diff --git a/src/chunk_builder.rs b/src/chunk_builder.rs index ba35b28..e7d17fb 100644 --- a/src/chunk_builder.rs +++ b/src/chunk_builder.rs @@ -14,7 +14,7 @@ pub struct ChunkBuilder { free_builders: Vec, built_recv: mpsc::Receiver<(usize, BuildReply)>, - sections: Vec<(i32, i32, i32)>, + sections: Vec<(i32, i32, i32, Arc)>, next_collection: f64, } @@ -52,7 +52,7 @@ impl ChunkBuilder { while let Ok((id, val)) = self.built_recv.try_recv() { world.reset_building_flag(val.position); - renderer.update_chunk_solid(val.position, &val.solid_buffer, val.solid_count); + renderer.update_chunk_solid(val.position, val.key, &val.solid_buffer, val.solid_count); self.free_builders.push(id); } @@ -82,7 +82,7 @@ impl ChunkBuilder { self.sections = sections; self.next_collection = 60.0; } - while let Some((x, y, z)) = self.sections.pop() { + while let Some((x, y, z, key)) = self.sections.pop() { let t_id = self.free_builders.pop().unwrap(); world.set_building_flag((x, y, z)); let (cx, cy, cz) = (x << 4, y << 4, z << 4); @@ -91,6 +91,7 @@ impl ChunkBuilder { self.threads[t_id].0.send(BuildReq { snapshot: snapshot, position: (x, y, z), + key: key, }).unwrap(); if self.free_builders.is_empty() { return; @@ -102,20 +103,22 @@ impl ChunkBuilder { struct BuildReq { snapshot: world::Snapshot, position: (i32, i32, i32), + key: Arc, } struct BuildReply { position: (i32, i32, i32), solid_buffer: Vec, solid_count: usize, + key: Arc, } fn build_func(id: usize, textures: Arc>, work_recv: mpsc::Receiver, built_send: mpsc::Sender<(usize, BuildReply)>) { - use rand::{self, Rng}; loop { let BuildReq { snapshot, position, + key, } = match work_recv.recv() { Ok(val) => val, Err(_) => return, @@ -124,8 +127,6 @@ fn build_func(id: usize, textures: Arc>, work_rec let mut solid_buffer = vec![]; let mut solid_count = 0; - let mut rng = rand::thread_rng(); - for y in 0 .. 16 { for x in 0 .. 16 { for z in 0 .. 16 { @@ -149,12 +150,7 @@ fn build_func(id: usize, textures: Arc>, work_rec cb = ((cb as f64) * 0.8) as u8; } - let stone = render::Renderer::get_texture(&textures, rng.choose(&[ - "minecraft:blocks/lava_flow", - "minecraft:blocks/stone", - "minecraft:blocks/melon_side", - "minecraft:blocks/sand", - ]).unwrap()); + let stone = render::Renderer::get_texture(&textures, "minecraft:blocks/stone"); solid_count += 6; for vert in dir.get_verts() { let mut vert = vert.clone(); @@ -201,6 +197,7 @@ fn build_func(id: usize, textures: Arc>, work_rec position: position, solid_buffer: solid_buffer, solid_count: solid_count, + key: key, })).unwrap(); } } @@ -209,8 +206,11 @@ fn calculate_light(snapshot: &world::Snapshot, orig_x: i32, orig_y: i32, orig_z: x: f64, y: f64, z: f64, face: Direction, smooth: bool, force: bool) -> (u16, u16) { use std::cmp::max; use world::block; - let (ox, oy, oz) = face.get_offset(); - // TODO: Cull against check + let (ox, oy, oz) = if !snapshot.get_block(orig_x, orig_y, orig_z).renderable() { // TODO: cull check + (0, 0, 0) + } else { + face.get_offset() + }; let s_block_light = snapshot.get_block_light(orig_x + ox, orig_y + oy, orig_z + oz); let s_sky_light = snapshot.get_sky_light(orig_x + ox, orig_y + oy, orig_z + oz); diff --git a/src/main.rs b/src/main.rs index a2a36d0..8db6396 100644 --- a/src/main.rs +++ b/src/main.rs @@ -14,6 +14,7 @@ #![recursion_limit="200"] #![feature(const_fn)] +#![feature(arc_counts)] extern crate glutin; extern crate image; @@ -107,7 +108,6 @@ impl Game { match server { Ok(val) => { self.screen_sys.pop_screen(); - self.renderer.clear_chunks(); self.chunk_builder.wait_for_builders(); self.focused = true; self.server = val; @@ -292,10 +292,18 @@ fn handle_window_event(window: &glutin::Window, game.console.lock().unwrap().toggle(); } Event::KeyboardInput(state, key, virt) => { - ui_container.key_press(game, virt, key, state == glutin::ElementState::Pressed); + if game.focused { + if let Some(virt) = virt { + game.server.key_press(state == glutin::ElementState::Pressed, virt); + } + } else { + ui_container.key_press(game, virt, key, state == glutin::ElementState::Pressed); + } } Event::ReceivedCharacter(c) => { - ui_container.key_type(game, c); + if !game.focused { + ui_container.key_type(game, c); + } } _ => (), } diff --git a/src/render/mod.rs b/src/render/mod.rs index 0d60dd4..a5e76eb 100644 --- a/src/render/mod.rs +++ b/src/render/mod.rs @@ -28,6 +28,7 @@ use image::GenericImage; use byteorder::{WriteBytesExt, NativeEndian}; use serde_json; use cgmath::{self, Vector, Point}; +use world; const ATLAS_SIZE: usize = 1024; @@ -67,9 +68,12 @@ pub struct Renderer { last_width: u32, last_height: u32, + + chunk_gc_timer: f64, } struct ChunkBuffer { + key: Arc, position: (i32, i32, i32), solid: Option, @@ -199,6 +203,8 @@ impl Renderer { perspective_matrix: cgmath::Matrix4::zero(), trans: None, + + chunk_gc_timer: 120.0, } } @@ -232,6 +238,20 @@ impl Renderer { self.init_trans(width, height); } + self.chunk_gc_timer -= delta; + if self.chunk_gc_timer <= 0.0 { + self.chunk_gc_timer = 120.0; + let mut unload_queue = vec![]; + for (pos, info) in &self.chunks { + if Arc::strong_count(&info.key) == 1 { + unload_queue.push(*pos); + } + } + for unload in unload_queue { + self.chunks.remove(&unload); + } + } + let trans = self.trans.as_mut().unwrap(); trans.main.bind(); @@ -317,10 +337,6 @@ impl Renderer { self.ui.tick(width, height); } - pub fn clear_chunks(&mut self) { - self.chunks.clear(); - } - fn ensure_element_buffer(&mut self, size: usize) { if self.element_buffer_size < size { let (data, ty) = self::generate_element_buffer(size); @@ -331,13 +347,15 @@ impl Renderer { } } - pub fn update_chunk_solid(&mut self, pos: (i32, i32, i32), data: &[u8], count: usize) { + pub fn update_chunk_solid(&mut self, pos: (i32, i32, i32), key: Arc, data: &[u8], count: usize) { self.ensure_element_buffer(count); - let buffer = self.chunks.entry(pos).or_insert(ChunkBuffer { + let buffer = self.chunks.entry(pos).or_insert_with(||ChunkBuffer { + key: key.clone(), position: pos, solid: None, trans: None, }); + buffer.key = key; if count == 0 { if buffer.solid.is_some() { buffer.solid = None; diff --git a/src/server.rs b/src/server.rs index e88b8ba..d6ba33d 100644 --- a/src/server.rs +++ b/src/server.rs @@ -19,12 +19,14 @@ use rand::{self, Rng}; use std::sync::{Arc, RwLock, Mutex}; use std::sync::mpsc; use std::thread; +use std::collections::HashMap; use resources; use openssl; use console; use render; use auth; use cgmath::{self, Vector}; +use glutin::VirtualKeyCode; pub struct Server { conn: Option, @@ -39,6 +41,8 @@ pub struct Server { pub yaw: f64, pub pitch: f64, + pressed_keys: HashMap, + tick_timer: f64, } @@ -146,6 +150,8 @@ impl Server { console: console, version: version, + pressed_keys: HashMap::new(), + position: cgmath::Vector3::zero(), yaw: 0.0, pitch: 0.0, @@ -175,6 +181,8 @@ impl Server { resources: resources, console: console, + pressed_keys: HashMap::new(), + position: cgmath::Vector3::new(0.5, 13.2, 0.5), yaw: 0.0, pitch: 0.0, @@ -201,6 +209,7 @@ impl Server { self pck { KeepAliveClientbound => on_keep_alive, ChunkData => on_chunk_data, + ChunkUnload => on_chunk_unload, TeleportPlayer => on_teleport, } }, @@ -210,6 +219,26 @@ impl Server { self.read_queue = Some(rx); } + let (forward, yaw) = self.calculate_movement(); + + if self.world.is_chunk_loaded((self.position.x as i32) >> 4, (self.position.z as i32) >> 4) { + let mut speed = 4.317 / 60.0; + if self.is_key_pressed(VirtualKeyCode::LShift) { + speed = 5.612 / 60.0; + } + // TODO: only do this for flying + speed *= 2.5; + + if self.is_key_pressed(VirtualKeyCode::Space) { + self.position.y += speed * delta; + } + if self.is_key_pressed(VirtualKeyCode::LControl) { + self.position.y -= speed * delta; + } + self.position.x += forward * yaw.cos() * delta * speed; + self.position.z -= forward * yaw.sin() * delta * speed; + } + self.tick_timer += delta; while self.tick_timer >= 3.0 && self.is_connected() { self.minecraft_tick(); @@ -222,6 +251,35 @@ impl Server { renderer.camera.pitch = self.pitch; } + fn calculate_movement(&self) -> (f64, f64) { + use std::f64::consts::PI; + let mut forward = 0.0f64; + let mut yaw = self.yaw - (PI/2.0); + if self.is_key_pressed(VirtualKeyCode::W) || self.is_key_pressed(VirtualKeyCode::S) { + forward = 1.0; + if self.is_key_pressed(VirtualKeyCode::S) { + yaw += PI; + } + } + let mut change = 0.0; + if self.is_key_pressed(VirtualKeyCode::A) { + change = (PI / 2.0) / (forward.abs() + 1.0); + } + if self.is_key_pressed(VirtualKeyCode::D) { + change = -(PI / 2.0) / (forward.abs() + 1.0); + } + if self.is_key_pressed(VirtualKeyCode::A) || self.is_key_pressed(VirtualKeyCode::D) { + forward = 1.0; + } + if self.is_key_pressed(VirtualKeyCode::S) { + yaw -= change; + } else { + yaw += change; + } + + (forward, yaw) + } + pub fn minecraft_tick(&mut self) { // Sync our position to the server @@ -236,17 +294,25 @@ impl Server { self.write_packet(packet); } + pub fn key_press(&mut self, down: bool, key: VirtualKeyCode) { + self.pressed_keys.insert(key, down); + } + + fn is_key_pressed(&self, key: VirtualKeyCode) -> bool { + self.pressed_keys.get(&key).map(|v| *v).unwrap_or(false) + } + pub fn write_packet(&mut self, p: T) { self.conn.as_mut().unwrap().write_packet(p).unwrap(); // TODO handle errors } - pub fn on_keep_alive(&mut self, keep_alive: packet::play::clientbound::KeepAliveClientbound) { + fn on_keep_alive(&mut self, keep_alive: packet::play::clientbound::KeepAliveClientbound) { self.write_packet(packet::play::serverbound::KeepAliveServerbound { id: keep_alive.id, }); } - pub fn on_teleport(&mut self, teleport: packet::play::clientbound::TeleportPlayer) { + fn on_teleport(&mut self, teleport: packet::play::clientbound::TeleportPlayer) { // TODO: relative teleports self.position.x = teleport.x; self.position.y = teleport.y; @@ -264,7 +330,7 @@ impl Server { }); } - pub fn on_chunk_data(&mut self, chunk_data: packet::play::clientbound::ChunkData) { + fn on_chunk_data(&mut self, chunk_data: packet::play::clientbound::ChunkData) { self.world.load_chunk( chunk_data.chunk_x, chunk_data.chunk_z, @@ -273,4 +339,8 @@ impl Server { chunk_data.data.data ).unwrap(); } + + fn on_chunk_unload(&mut self, chunk_unload: packet::play::clientbound::ChunkUnload) { + self.world.unload_chunk(chunk_unload.x, chunk_unload.z); + } } diff --git a/src/world/mod.rs b/src/world/mod.rs index 96653e3..944b55c 100644 --- a/src/world/mod.rs +++ b/src/world/mod.rs @@ -15,6 +15,7 @@ pub mod block; use self::block::BlockSet; +use std::sync::Arc; use std::collections::HashMap; use types::bit; use types::nibble; @@ -31,6 +32,10 @@ impl World { } } + 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: &'static block::Block) { let cpos = CPos(x >> 4, z >> 4); if !self.chunks.contains_key(&cpos) { @@ -47,13 +52,13 @@ 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, Arc)> { 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.push((chunk.position.0, sec.y as i32, chunk.position.1, sec.key.clone())); } } } @@ -158,6 +163,10 @@ impl World { 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; @@ -182,7 +191,7 @@ impl World { continue; } if chunk.sections[i].is_none() { - chunk.sections[i] = Some(Section::new(i as u8)); + chunk.sections[i] = Some(Section::new(x, i as u8, z)); } let section = chunk.sections[i as usize].as_mut().unwrap(); section.dirty = true; @@ -332,7 +341,7 @@ impl Chunk { if b.in_set(&*block::AIR) { return; } - self.sections[s_idx as usize] = Some(Section::new(s_idx as u8)); + 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); @@ -350,7 +359,13 @@ impl Chunk { } } +#[derive(PartialEq, Eq, Hash)] +pub struct SectionKey { + pos: (i32, u8, i32), +} + struct Section { + key: Arc, y: u8, blocks: bit::Map, @@ -365,8 +380,11 @@ struct Section { } impl Section { - fn new(y: u8) -> Section { + fn new(x: i32, y: u8, z: i32) -> Section { let mut section = Section { + key: Arc::new(SectionKey{ + pos: (x, y, z), + }), y: y, blocks: bit::Map::new(4096, 4),