Spectator style movement + chunk unloading

This commit is contained in:
Thinkofname 2016-03-21 22:34:57 +00:00
parent 8d141b1310
commit e6477bd186
5 changed files with 145 additions and 31 deletions

View File

@ -14,7 +14,7 @@ pub struct ChunkBuilder {
free_builders: Vec<usize>,
built_recv: mpsc::Receiver<(usize, BuildReply)>,
sections: Vec<(i32, i32, i32)>,
sections: Vec<(i32, i32, i32, Arc<world::SectionKey>)>,
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<world::SectionKey>,
}
struct BuildReply {
position: (i32, i32, i32),
solid_buffer: Vec<u8>,
solid_count: usize,
key: Arc<world::SectionKey>,
}
fn build_func(id: usize, textures: Arc<RwLock<render::TextureManager>>, work_recv: mpsc::Receiver<BuildReq>, 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<RwLock<render::TextureManager>>, 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<RwLock<render::TextureManager>>, 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<RwLock<render::TextureManager>>, 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);

View File

@ -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);
}
}
_ => (),
}

View File

@ -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<world::SectionKey>,
position: (i32, i32, i32),
solid: Option<ChunkRenderInfo>,
@ -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<world::SectionKey>, 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;

View File

@ -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<protocol::Conn>,
@ -39,6 +41,8 @@ pub struct Server {
pub yaw: f64,
pub pitch: f64,
pressed_keys: HashMap<VirtualKeyCode, bool>,
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<T: protocol::PacketType>(&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);
}
}

View File

@ -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<SectionKey>)> {
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<u8>) -> 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<SectionKey>,
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),