From 5f17aead7e4ace54b268296159ccecc094489ee3 Mon Sep 17 00:00:00 2001 From: Thinkofname Date: Fri, 18 Mar 2016 22:24:30 +0000 Subject: [PATCH] Base implementation for worlds/blocks --- src/macros.rs | 25 ++++++ src/main.rs | 10 ++- src/protocol/mod.rs | 23 ----- src/server.rs | 42 +++++++++ src/types/bit/map.rs | 4 +- src/types/mod.rs | 1 + src/types/nibble.rs | 45 ++++++++++ src/world/block.rs | 48 +++++++++++ src/world/mod.rs | 199 +++++++++++++++++++++++++++++++++++++++++++ 9 files changed, 370 insertions(+), 27 deletions(-) create mode 100644 src/macros.rs create mode 100644 src/server.rs create mode 100644 src/types/nibble.rs create mode 100644 src/world/block.rs create mode 100644 src/world/mod.rs diff --git a/src/macros.rs b/src/macros.rs new file mode 100644 index 0000000..bf2b7a2 --- /dev/null +++ b/src/macros.rs @@ -0,0 +1,25 @@ + + +#[doc(hidden)] +#[macro_export] +macro_rules! create_ids { + ($t:ty, ) => (); + ($t:ty, prev($prev:ident), $name:ident) => ( + #[allow(non_upper_case_globals)] + pub const $name: $t = $prev + 1; + ); + ($t:ty, prev($prev:ident), $name:ident, $($n:ident),+) => ( + #[allow(non_upper_case_globals)] + pub const $name: $t = $prev + 1; + create_ids!($t, prev($name), $($n),+); + ); + ($t:ty, $name:ident, $($n:ident),+) => ( + #[allow(non_upper_case_globals)] + pub const $name: $t = 0; + create_ids!($t, prev($name), $($n),+); + ); + ($t:ty, $name:ident) => ( + #[allow(non_upper_case_globals)] + pub const $name: $t = 0; + ); +} diff --git a/src/main.rs b/src/main.rs index f7ab49f..5f70fdd 100644 --- a/src/main.rs +++ b/src/main.rs @@ -14,6 +14,8 @@ #![recursion_limit="200"] +#[macro_use] +pub mod macros; pub mod ecs; pub mod protocol; pub mod format; @@ -27,6 +29,8 @@ pub mod ui; pub mod screen; #[macro_use] pub mod console; +pub mod server; +pub mod world; extern crate glutin; extern crate image; @@ -62,6 +66,8 @@ pub struct Game { console: Arc>, should_close: bool, mouse_pos: (i32, i32), + + server: server::Server, } fn main() { @@ -78,8 +84,7 @@ fn main() { log::set_logger(|max_log_level| { max_log_level.set(log::LogLevelFilter::Trace); Box::new(proxy) - }) - .unwrap(); + }).unwrap(); info!("Starting steven"); @@ -121,6 +126,7 @@ fn main() { console: con, should_close: false, mouse_pos: (0, 0), + server: server::Server::dummy_server(), }; while !game.should_close { diff --git a/src/protocol/mod.rs b/src/protocol/mod.rs index b2ae6e5..dbd9572 100644 --- a/src/protocol/mod.rs +++ b/src/protocol/mod.rs @@ -34,29 +34,6 @@ use time; pub const SUPPORTED_PROTOCOL: i32 = 74; -#[doc(hidden)] -macro_rules! create_ids { - ($t:ty, ) => (); - ($t:ty, prev($prev:ident), $name:ident) => ( - #[allow(non_upper_case_globals)] - pub const $name: $t = $prev + 1; - ); - ($t:ty, prev($prev:ident), $name:ident, $($n:ident),+) => ( - #[allow(non_upper_case_globals)] - pub const $name: $t = $prev + 1; - create_ids!($t, prev($name), $($n),+); - ); - ($t:ty, $name:ident, $($n:ident),+) => ( - #[allow(non_upper_case_globals)] - pub const $name: $t = 0; - create_ids!($t, prev($name), $($n),+); - ); - ($t:ty, $name:ident) => ( - #[allow(non_upper_case_globals)] - pub const $name: $t = 0; - ); -} - /// Helper macro for defining packets #[macro_export] diff --git a/src/server.rs b/src/server.rs new file mode 100644 index 0000000..6a21f32 --- /dev/null +++ b/src/server.rs @@ -0,0 +1,42 @@ +// 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. + +use protocol; +use world; +use world::block; +use rand::{self, Rng}; + +pub struct Server { + conn: Option, + world: world::World, +} + +impl Server { + pub fn dummy_server() -> Server { + let mut world = world::World::new(); + let mut rng = rand::thread_rng(); + for x in -7*16 .. 7*16 { + for z in -7*16 .. 7*16 { + let h = rng.gen_range(3, 10); + for y in 0 .. h { + world.set_block(x, y, z, block::MISSING); + } + } + } + Server { + conn: None, + world: world, + } + } +} diff --git a/src/types/bit/map.rs b/src/types/bit/map.rs index 15136a9..a847bb3 100644 --- a/src/types/bit/map.rs +++ b/src/types/bit/map.rs @@ -14,7 +14,7 @@ pub struct Map { bits: Vec, - bit_size: usize, + pub bit_size: usize, length: usize, } @@ -60,7 +60,7 @@ impl Map { map } - pub fn resize(self, size: usize) -> Map { + pub fn resize(&self, size: usize) -> Map { let mut n = Map::new(self.length, size); for i in 0..self.length { n.set(i, self.get(i)); diff --git a/src/types/mod.rs b/src/types/mod.rs index 11e4d7c..682180e 100644 --- a/src/types/mod.rs +++ b/src/types/mod.rs @@ -19,3 +19,4 @@ mod metadata; pub use self::metadata::*; pub mod bit; +pub mod nibble; diff --git a/src/types/nibble.rs b/src/types/nibble.rs new file mode 100644 index 0000000..1b62d2e --- /dev/null +++ b/src/types/nibble.rs @@ -0,0 +1,45 @@ +// 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 struct Array { + data: Vec, +} + +impl Array { + pub fn new(size: usize) -> Array { + Array { + data: vec![0; (size + 1) >> 1], + } + } + + pub fn get(&self, idx: usize) -> u8 { + let val = self.data[idx>>1]; + if idx&1 == 0 { + val & 0xF + } else { + val >> 4 + } + } + + pub fn set(&mut self, idx: usize, val: u8) { + let i = idx >> 1; + let old = self.data[i]; + if idx&1 == 0 { + self.data[i] = (old & 0xF0) | (val & 0xF); + } else { + self.data[i] = (old & 0x0F) | ((val & 0xF) << 4); + } + } +} diff --git a/src/world/block.rs b/src/world/block.rs new file mode 100644 index 0000000..3b88e1e --- /dev/null +++ b/src/world/block.rs @@ -0,0 +1,48 @@ + +#[derive(Debug, PartialEq, Eq, Hash)] +pub struct Block { + pub render: bool, +} + +macro_rules! define_blocks { + ( + $( + $name:ident $bl:expr + )* + ) => ( + const BLOCKS: &'static [Block] = &[ + $( + $bl + ),* + ]; + mod internal_ids { create_ids!(usize, $($name),*); } + $( + pub const $name: &'static Block = &BLOCKS[internal_ids::$name]; + )* + + impl Block { + pub fn get_id(&self) -> usize { + $( + if self == $name { return internal_ids::$name; } + )* + unreachable!() + } + } + ) +} + +define_blocks! { + AIR Block { + render: false, + } + MISSING Block { + render: true, + } +} + +pub fn get_block_by_id(id: usize) -> &'static Block { + if id >= BLOCKS.len() { + return MISSING; + } + &BLOCKS[id] +} diff --git a/src/world/mod.rs b/src/world/mod.rs new file mode 100644 index 0000000..91848c7 --- /dev/null +++ b/src/world/mod.rs @@ -0,0 +1,199 @@ +// 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 types::bit; +use types::nibble; + +pub struct World { + chunks: HashMap, +} + +impl World { + pub fn new() -> World { + World { + chunks: HashMap::new(), + } + } + + pub fn set_block(&mut self, x: i32, y: i32, z: i32, b: &'static block::Block) { + let cpos = CPos(x >> 4, z >> 4); + if !self.chunks.contains_key(&cpos) { + self.chunks.insert(cpos, Chunk::new(cpos)); + } + let chunk = self.chunks.get_mut(&cpos).unwrap(); + chunk.set_block(x & 0xF, y, z & 0xF, b); + } + + pub fn get_block(&self, x: i32, y: i32, z: i32) -> &'static block::Block { + match self.chunks.get(&CPos(x >> 4, z >> 4)) { + Some(ref chunk) => chunk.get_block(x & 0xF, y, z & 0xF), + None => block::AIR, + } + } +} + +#[derive(PartialEq, Eq, Hash, Clone, Copy)] +pub struct CPos(i32, i32); + +pub struct Chunk { + position: CPos, + + sections: [Option
; 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, + ], + biomes: [0; 16 * 16], + } + } + + fn set_block(&mut self, x: i32, y: i32, z: i32, b: &'static 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 b == block::AIR { + return; + } + self.sections[s_idx as usize] = Some(Section::new(s_idx as u8)); + } + 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) -> &'static block::Block { + let s_idx = y >> 4; + if s_idx < 0 || s_idx > 15 { + return block::AIR; + } + match self.sections[s_idx as usize].as_ref() { + Some(sec) => sec.get_block(x, y & 0xF, z), + None => block::AIR, + } + } +} + +struct Section { + y: u8, + + blocks: bit::Map, + block_map: Vec<(&'static block::Block, u32)>, + rev_block_map: HashMap<&'static block::Block, usize>, + + block_light: nibble::Array, + sky_light: nibble::Array, + + dirty: bool, +} + +impl Section { + fn new(y: u8) -> Section { + let mut section = Section { + y: y, + + blocks: bit::Map::new(4096, 4), + block_map: vec![ + (block::AIR, 0xFFFFFFFF) + ], + rev_block_map: HashMap::new(), + + block_light: nibble::Array::new(16 * 16 * 16), + sky_light: nibble::Array::new(16 * 16 * 16), + + dirty: 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) -> &'static 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: &'static block::Block) { + 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 !self.rev_block_map.contains_key(b) { + let mut found = false; + for (i, ref mut info) in self.block_map.iter_mut().enumerate() { + if info.1 == 0 { + info.0 = b; + self.rev_block_map.insert(b, 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.rev_block_map.insert(b, self.block_map.len()); + 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); + } +}