// Copyright 2015 Matthew Collins // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. pub mod block; use std::collections::HashMap; use std::hash::BuildHasherDefault; use types::bit; use types::nibble; use types::hash::FNVHash; use protocol; use render; use collision; use cgmath; use chunk_builder; pub mod biome; pub struct World { chunks: HashMap>, render_list: Vec<(i32, i32, i32)>, } impl World { pub fn new() -> World { World { chunks: HashMap::with_hasher(BuildHasherDefault::default()), render_list: vec![], } } pub fn is_chunk_loaded(&self, x: i32, z: i32) -> bool { self.chunks.contains_key(&CPos(x, z)) } pub fn set_block(&mut self, x: i32, y: i32, z: i32, b: block::Block) { self.set_block_raw(x, y, z, b); self.update_block(x, y, z); } fn set_block_raw(&mut self, x: i32, y: i32, z: i32, b: block::Block) { let cpos = CPos(x >> 4, z >> 4); let chunk = self.chunks.entry(cpos).or_insert_with(|| Chunk::new(cpos)); chunk.set_block(x & 0xF, y, z & 0xF, b); } pub fn update_block(&mut self, x: i32, y: i32, z: i32) { for yy in -1 .. 2 { for zz in -1 .. 2 { for xx in -1 .. 2 { let (bx, by, bz) = (x+xx, y+yy, z+zz); let current = self.get_block(bx, by, bz); let new = current.update_state(self, bx, by, bz); if current != new { self.set_block_raw(bx, by, bz, new); } } } } } fn update_range(&mut self, x1: i32, y1: i32, z1: i32, x2: i32, y2: i32, z2: i32) { for by in y1 .. y2 { for bz in z1 .. z2 { for bx in x1 .. x2 { let current = self.get_block(bx, by, bz); let new = current.update_state(self, bx, by, bz); if current != new { self.set_block_raw(bx, by, bz, new); } } } } } pub fn get_block(&self, x: i32, y: i32, z: i32) -> block::Block { match self.chunks.get(&CPos(x >> 4, z >> 4)) { Some(ref chunk) => chunk.get_block(x & 0xF, y, z & 0xF), None => block::Missing{}, } } pub fn compute_render_list(&mut self, renderer: &mut render::Renderer) { use chunk_builder; use types::Direction; use cgmath::Vector; use std::collections::VecDeque; self.render_list.clear(); let mut valid_dirs = [false; 6]; for dir in Direction::all() { let (ox, oy, oz) = dir.get_offset(); let dir_vec = cgmath::Vector3::new(ox as f32, oy as f32, oz as f32); valid_dirs[dir.index()] = renderer.view_vector.dot(dir_vec) > -0.8; } let start = ( ((renderer.camera.pos.x as i32) >> 4), ((renderer.camera.pos.y as i32) >> 4), ((renderer.camera.pos.z as i32) >> 4) ); let mut process_queue = VecDeque::with_capacity(self.chunks.len() * 16); process_queue.push_front((Direction::Invalid, start)); while let Some((from, pos)) = process_queue.pop_front() { let (exists, cull) = if let Some((sec, rendered_on)) = self.get_render_section_mut(pos.0, pos.1, pos.2) { if *rendered_on == renderer.frame_id { continue; } *rendered_on = renderer.frame_id; let min = cgmath::Point3::new(pos.0 as f32 * 16.0, -pos.1 as f32 * 16.0, pos.2 as f32 * 16.0); let bounds = collision::Aabb3::new(min, min + cgmath::Vector3::new(16.0, -16.0, 16.0)); if renderer.frustum.contains(bounds) == collision::Relation::Out { continue; } (sec.is_some(), sec.map_or(chunk_builder::CullInfo::all_vis(), |v| v.cull_info)) } else { continue; }; if exists { self.render_list.push(pos); } for dir in Direction::all() { let (ox, oy, oz) = dir.get_offset(); let opos = (pos.0 + ox, pos.1 + oy, pos.2 + oz); if let Some((_, rendered_on)) = self.get_render_section_mut(opos.0, opos.1, opos.2) { if *rendered_on == renderer.frame_id { continue; } if from == Direction::Invalid || (valid_dirs[dir.index()] && cull.is_visible(from, dir)) { process_queue.push_back((dir.opposite(), opos)); } } } } } pub fn get_render_list(&self) -> Vec<((i32, i32, i32), &render::ChunkBuffer)> { self.render_list.iter().map(|v| { let chunk = self.chunks.get(&CPos(v.0, v.2)).unwrap(); let sec = chunk.sections[v.1 as usize].as_ref().unwrap(); (*v, &sec.render_buffer) }).collect() } 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(sec) = chunk.sections[y as usize].as_mut() { return Some(sec); } } None } fn get_render_section_mut(&mut self, x: i32, y: i32, z: i32) -> Option<(Option<&mut Section>, &mut u32)> { if y < 0 || y > 15 { return None; } if let Some(chunk) = self.chunks.get_mut(&CPos(x, z)) { let rendered = &mut chunk.sections_rendered_on[y as usize]; if let Some(sec) = chunk.sections[y as usize].as_mut() { return Some((Some(sec), rendered)); } return Some((None, rendered)); } None } pub fn get_dirty_chunk_sections(&mut self) -> Vec<(i32, i32, i32)> { let mut out = vec![]; for (_, chunk) in &mut self.chunks { for sec in &mut chunk.sections { if let Some(sec) = sec.as_mut() { if !sec.building && sec.dirty { out.push((chunk.position.0, sec.y as i32, chunk.position.1)); } } } } out } 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(sec) = chunk.sections[pos.1 as usize].as_ref() { return sec.dirty && !sec.building; } } false } 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(sec) = chunk.sections[pos.1 as usize].as_mut() { sec.building = true; sec.dirty = false; } } } 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(section) = chunk.sections[pos.1 as usize].as_mut() { section.building = false; } } } pub fn flag_dirty_all(&mut self) { for (_, chunk) in &mut self.chunks { for sec in &mut chunk.sections { if let Some(sec) = sec.as_mut() { sec.dirty = true; } } } } pub fn capture_snapshot(&self, x: i32, y: i32, z: i32, w: i32, h: i32, d: i32) -> Snapshot { use std::cmp::{min, max}; let mut snapshot = Snapshot { blocks: vec![0; (w * h * d) as usize], block_light: nibble::Array::new((w * h * d) as usize), sky_light: nibble::Array::new((w * h * d) as usize), biomes: vec![0; (w * d) as usize], x: x, y: y, z: z, w: w, _h: h, d: d, }; for i in 0 .. (w * h * d) as usize { snapshot.sky_light.set(i, 0xF); snapshot.blocks[i] = block::Missing{}.get_steven_id() as u16; } let cx1 = x >> 4; let cy1 = y >> 4; let cz1 = z >> 4; let cx2 = (x + w + 15) >> 4; let cy2 = (y + h + 15) >> 4; let cz2 = (z + d + 15) >> 4; for cx in cx1 .. cx2 { for cz in cz1 .. cz2 { let chunk = match self.chunks.get(&CPos(cx, cz)) { Some(val) => val, None => continue, }; let x1 = min(16, max(0, x - (cx<<4))); let x2 = min(16, max(0, x + w - (cx<<4))); let z1 = min(16, max(0, z - (cz<<4))); let z2 = min(16, max(0, z + d - (cz<<4))); for cy in cy1 .. cy2 { if cy < 0 || cy > 15 { continue; } let section = &chunk.sections[cy as usize]; let y1 = min(16, max(0, y - (cy<<4))); let y2 = min(16, max(0, y + h - (cy<<4))); for yy in y1 .. y2 { for zz in z1 .. z2 { for xx in x1 .. x2 { let ox = xx + (cx << 4); let oy = yy + (cy << 4); let oz = zz + (cz << 4); match section.as_ref() { Some(sec) => { snapshot.set_block(ox, oy, oz, sec.get_block(xx, yy, zz)); snapshot.set_block_light(ox, oy, oz, sec.get_block_light(xx, yy, zz)); snapshot.set_sky_light(ox, oy, oz, sec.get_sky_light(xx, yy, zz)); }, None => { snapshot.set_block(ox, oy, oz, block::Air{}); }, } } } } } for zz in z1 .. z2 { for xx in x1 .. x2 { let ox = xx + (cx << 4); let oz = zz + (cz << 4); snapshot.set_biome(ox, oz, chunk.get_biome(xx, zz)); } } } } snapshot } pub fn unload_chunk(&mut self, x: i32, z: i32) { self.chunks.remove(&CPos(x, z)); } pub fn load_chunk(&mut self, x: i32, z: i32, new: bool, mask: u16, data: Vec) -> Result<(), protocol::Error> { use std::io::{Cursor, Read}; use byteorder::ReadBytesExt; use protocol::{VarInt, Serializable, LenPrefixed}; let mut data = Cursor::new(data); let cpos = CPos(x, z); { let chunk = if new { self.chunks.insert(cpos, Chunk::new(cpos)); self.chunks.get_mut(&cpos).unwrap() } else { if !self.chunks.contains_key(&cpos) { return Ok(()); } self.chunks.get_mut(&cpos).unwrap() }; for i in 0 .. 16 { if mask & (1 << i) == 0 { continue; } if chunk.sections[i].is_none() { chunk.sections[i] = Some(Section::new(x, i as u8, z)); } let section = chunk.sections[i as usize].as_mut().unwrap(); section.dirty = true; let mut bit_size = try!(data.read_u8()); let mut block_map = HashMap::with_hasher(BuildHasherDefault::::default()); if bit_size != 0 { let count = try!(VarInt::read_from(&mut data)).0; for i in 0 .. count { let id = try!(VarInt::read_from(&mut data)).0; block_map.insert(i as usize, id); } } else { bit_size = 13; } let bits = try!(LenPrefixed::::read_from(&mut data)).data; let m = bit::Map::from_raw(bits, bit_size as usize); for i in 0 .. 4096 { let val = m.get(i); let block_id = block_map.get(&val).map_or(val, |v| *v as usize); let block = block::Block::by_vanilla_id(block_id); let i = i as i32; section.set_block( i & 0xF, i >> 8, (i >> 4) & 0xF, block ); } try!(data.read_exact(&mut section.block_light.data)); try!(data.read_exact(&mut section.sky_light.data)); } if new { try!(data.read_exact(&mut chunk.biomes)); } } for i in 0 .. 16 { if mask & (1 << i) == 0 { continue; } for pos in [ (-1, 0, 0), (1, 0, 0), (0, -1, 0), (0, 1, 0), (0, 0, -1), (0, 0, 1)].into_iter() { self.flag_section_dirty(x + pos.0, i as i32 + pos.1, z + pos.2); } self.update_range( (x<<4) - 1, (i<<4) - 1, (z<<4) - 1, (x<<4) + 17, (i<<4) + 17, (z<<4) + 17 ); } Ok(()) } fn flag_section_dirty(&mut self, x: i32, y: i32, z: i32) { if y < 0 || y > 15 { return; } let cpos = CPos(x, z); if let Some(chunk) = self.chunks.get_mut(&cpos) { if let Some(sec) = chunk.sections[y as usize].as_mut() { sec.dirty = true; } } } } pub struct Snapshot { blocks: Vec, block_light: nibble::Array, sky_light: nibble::Array, biomes: Vec, x: i32, y: i32, z: i32, w: i32, _h: i32, d: i32, } impl Snapshot { pub fn make_relative(&mut self, x: i32, y: i32, z: i32) { self.x = x; self.y = y; self.z = z; } pub fn get_block(&self, x: i32, y: i32, z: i32) -> block::Block { block::Block::by_steven_id(self.blocks[self.index(x, y, z)] as usize) } pub fn set_block(&mut self, x: i32, y: i32, z: i32, b: block::Block) { let idx = self.index(x, y, z); self.blocks[idx] = b.get_steven_id() as u16; } pub fn get_block_light(&self, x: i32, y: i32, z: i32) -> u8 { self.block_light.get(self.index(x, y, z)) } pub fn set_block_light(&mut self, x: i32, y: i32, z: i32, l: u8) { let idx = self.index(x, y, z); self.block_light.set(idx, l); } pub fn get_sky_light(&self, x: i32, y: i32, z: i32) -> u8 { self.sky_light.get(self.index(x, y, z)) } pub fn set_sky_light(&mut self, x: i32, y: i32, z: i32, l: u8) { let idx = self.index(x, y, z); self.sky_light.set(idx, l); } pub fn get_biome(&self, x: i32, z: i32) -> biome::Biome { biome::Biome::by_id(self.biomes[((x - self.x) | ((z - self.z) << 4)) as usize] as usize) } pub fn set_biome(&mut self, x: i32, z: i32, b: biome::Biome) { self.biomes[((x - self.x) | ((z - self.z) << 4)) as usize] = b.id as u8; } #[inline] fn index(&self, x: i32, y: i32, z: i32) -> usize { ((x - self.x) + ((z - self.z) * self.w) + ((y - self.y) * self.w * self.d)) as usize } } #[derive(PartialEq, Eq, Hash, Clone, Copy)] pub struct CPos(i32, i32); pub struct Chunk { position: CPos, sections: [Option
; 16], sections_rendered_on: [u32; 16], biomes: [u8; 16 * 16], } impl Chunk { fn new(pos: CPos) -> Chunk { Chunk { position: pos, sections: [ None,None,None,None, None,None,None,None, None,None,None,None, None,None,None,None, ], sections_rendered_on: [0; 16], biomes: [0; 16 * 16], } } fn set_block(&mut self, x: i32, y: i32, z: i32, b: block::Block) { let s_idx = y >> 4; if s_idx < 0 || s_idx > 15 { return; } if self.sections[s_idx as usize].is_none() { if let block::Air {} = b { return; } self.sections[s_idx as usize] = Some(Section::new(self.position.0, s_idx as u8, self.position.1)); } let section = self.sections[s_idx as usize].as_mut().unwrap(); section.set_block(x, y & 0xF, z, b); } fn get_block(&self, x: i32, y: i32, z: i32) -> block::Block { let s_idx = y >> 4; if s_idx < 0 || s_idx > 15 { return block::Missing{}; } match self.sections[s_idx as usize].as_ref() { Some(sec) => sec.get_block(x, y & 0xF, z), None => block::Air{}, } } fn get_biome(&self, x: i32, z: i32) -> biome::Biome { biome::Biome::by_id(self.biomes[((z<<4)|x) as usize] as usize) } } #[derive(PartialEq, Eq, Hash)] pub struct SectionKey { pos: (i32, u8, i32), } pub struct Section { pub cull_info: chunk_builder::CullInfo, pub render_buffer: render::ChunkBuffer, y: u8, blocks: bit::Map, block_map: Vec<(block::Block, u32)>, rev_block_map: HashMap>, block_light: nibble::Array, sky_light: nibble::Array, dirty: bool, building: bool, } impl Section { fn new(_x: i32, y: u8, _z: i32) -> Section { let mut section = Section { cull_info: chunk_builder::CullInfo::all_vis(), 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()), block_light: nibble::Array::new(16 * 16 * 16), sky_light: nibble::Array::new(16 * 16 * 16), dirty: false, building: false, }; for i in 0 .. 16*16*16 { 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 } fn set_block(&mut self, x: i32, y: i32, z: i32, b: block::Block) { use std::collections::hash_map::Entry; let old = self.get_block(x, y, z); if old == b { return; } // 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; let new_blocks = self.blocks.resize(new_size); self.blocks = new_blocks; } 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; } fn get_block_light(&self, x: i32, y: i32, z: i32) -> u8 { self.block_light.get(((y << 8) | (z << 4) | x) as usize) } fn set_block_light(&mut self, x: i32, y: i32, z: i32, l: u8) { self.block_light.set(((y << 8) | (z << 4) | x) as usize, l); } fn get_sky_light(&self, x: i32, y: i32, z: i32) -> u8 { self.sky_light.get(((y << 8) | (z << 4) | x) as usize) } fn set_sky_light(&mut self, x: i32, y: i32, z: i32, l: u8) { self.sky_light.set(((y << 8) | (z << 4) | x) as usize, l); } }