Spectator style movement + chunk unloading
This commit is contained in:
parent
8d141b1310
commit
e6477bd186
|
@ -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);
|
||||
|
|
14
src/main.rs
14
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);
|
||||
}
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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),
|
||||
|
|
Loading…
Reference in New Issue