mirror of
https://gitlab.com/veloren/veloren.git
synced 2024-08-30 18:12:32 +00:00
Merge branch 'master' into 'master'
Terrain Closes #5 See merge request veloren/veloren!66 Former-commit-id: 01d1eb1e0aaae62d0c2a53390fd847db524d217e
This commit is contained in:
commit
f1b63732af
@ -146,10 +146,8 @@ impl Client {
|
|||||||
// Handle new messages from the server
|
// Handle new messages from the server
|
||||||
frontend_events.append(&mut self.handle_new_messages()?);
|
frontend_events.append(&mut self.handle_new_messages()?);
|
||||||
|
|
||||||
self.state.terrain().iter().for_each(|(k, _)| {
|
// Pass character control from frontend input to the player's entity
|
||||||
println!("Chunk at {:?}", k);
|
// TODO: Only do this if the entity already has a Control component!
|
||||||
});
|
|
||||||
|
|
||||||
self.state.write_component(
|
self.state.write_component(
|
||||||
self.entity,
|
self.entity,
|
||||||
comp::Control {
|
comp::Control {
|
||||||
@ -186,18 +184,30 @@ impl Client {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Request chunks from the server
|
let pos = self
|
||||||
if let Some(pos) = self
|
|
||||||
.state
|
.state
|
||||||
.read_storage::<comp::phys::Pos>()
|
.read_storage::<comp::phys::Pos>()
|
||||||
.get(self.entity)
|
.get(self.entity)
|
||||||
{
|
.cloned();
|
||||||
|
if let Some(pos) = pos {
|
||||||
let chunk_pos = self.state.terrain().pos_key(pos.0.map(|e| e as i32));
|
let chunk_pos = self.state.terrain().pos_key(pos.0.map(|e| e as i32));
|
||||||
|
|
||||||
for i in chunk_pos.x - 1..chunk_pos.x + 1 {
|
// Remove chunks that are too far from the player
|
||||||
for j in chunk_pos.y - 1..chunk_pos.y + 1 {
|
let mut chunks_to_remove = Vec::new();
|
||||||
for k in -1..3 {
|
self.state.terrain().iter().for_each(|(key, _)| {
|
||||||
let key = chunk_pos + Vec3::new(i, j, k);
|
if (chunk_pos - key).map(|e| e.abs()).reduce_max() > 3 {
|
||||||
|
chunks_to_remove.push(key);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
for key in chunks_to_remove {
|
||||||
|
self.state.remove_chunk(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Request chunks from the server
|
||||||
|
for i in chunk_pos.x - 1..chunk_pos.x + 2 {
|
||||||
|
for j in chunk_pos.y - 1..chunk_pos.y + 2 {
|
||||||
|
for k in 0..2 {
|
||||||
|
let key = Vec3::new(i, j, k);
|
||||||
if self.state.terrain().get_key(key).is_none()
|
if self.state.terrain().get_key(key).is_none()
|
||||||
&& !self.pending_chunks.contains(&key)
|
&& !self.pending_chunks.contains(&key)
|
||||||
{
|
{
|
||||||
|
@ -16,6 +16,8 @@ pub mod terrain;
|
|||||||
pub mod util;
|
pub mod util;
|
||||||
pub mod volumes;
|
pub mod volumes;
|
||||||
pub mod vol;
|
pub mod vol;
|
||||||
|
pub mod ray;
|
||||||
|
|
||||||
// TODO: unignore the code here, for some reason it refuses to compile here while has no problems copy-pasted elsewhere
|
// TODO: unignore the code here, for some reason it refuses to compile here while has no problems copy-pasted elsewhere
|
||||||
/// The networking module containing high-level wrappers of `TcpListener` and `TcpStream` (`PostOffice` and `PostBox` respectively) and data types used by both the server and client
|
/// The networking module containing high-level wrappers of `TcpListener` and `TcpStream` (`PostOffice` and `PostBox` respectively) and data types used by both the server and client
|
||||||
/// # Examples
|
/// # Examples
|
||||||
|
@ -73,6 +73,7 @@ impl<S: PostMsg, R: PostMsg> PostOffice<S, R> {
|
|||||||
match self.listener.accept() {
|
match self.listener.accept() {
|
||||||
Ok((stream, sock)) => new.push(PostBox::from_stream(stream).unwrap()),
|
Ok((stream, sock)) => new.push(PostBox::from_stream(stream).unwrap()),
|
||||||
Err(e) if e.kind() == io::ErrorKind::WouldBlock => break,
|
Err(e) if e.kind() == io::ErrorKind::WouldBlock => break,
|
||||||
|
Err(e) if e.kind() == io::ErrorKind::Interrupted => {},
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
self.error = Some(e.into());
|
self.error = Some(e.into());
|
||||||
break;
|
break;
|
||||||
@ -169,104 +170,110 @@ impl<S: PostMsg, R: PostMsg> PostBox<S, R> {
|
|||||||
let mut incoming_buf = Vec::new();
|
let mut incoming_buf = Vec::new();
|
||||||
|
|
||||||
'work: while running.load(Ordering::Relaxed) {
|
'work: while running.load(Ordering::Relaxed) {
|
||||||
// Get stream errors
|
for _ in 0..30 {
|
||||||
match stream.take_error() {
|
// Get stream errors
|
||||||
Ok(Some(e)) | Err(e) => {
|
match stream.take_error() {
|
||||||
recv_tx.send(Err(e.into())).unwrap();
|
Ok(Some(e)) | Err(e) => {
|
||||||
break 'work;
|
recv_tx.send(Err(e.into())).unwrap();
|
||||||
},
|
|
||||||
Ok(None) => {},
|
|
||||||
}
|
|
||||||
|
|
||||||
// Try getting messages from the send channel
|
|
||||||
for _ in 0..10 {
|
|
||||||
match send_rx.try_recv() {
|
|
||||||
Ok(send_msg) => {
|
|
||||||
// Serialize message
|
|
||||||
let mut msg_bytes = bincode::serialize(&send_msg).unwrap();
|
|
||||||
|
|
||||||
// Assemble into packet
|
|
||||||
let mut packet_bytes = msg_bytes
|
|
||||||
.len()
|
|
||||||
.to_le_bytes()
|
|
||||||
.as_ref()
|
|
||||||
.to_vec();
|
|
||||||
packet_bytes.append(&mut msg_bytes);
|
|
||||||
|
|
||||||
// Split packet into chunks
|
|
||||||
packet_bytes
|
|
||||||
.chunks(4096)
|
|
||||||
.map(|chunk| chunk.to_vec())
|
|
||||||
.for_each(|chunk| outgoing_chunks.push_back(chunk))
|
|
||||||
},
|
|
||||||
Err(mpsc::TryRecvError::Empty) => break,
|
|
||||||
// Worker error
|
|
||||||
Err(e) => {
|
|
||||||
let _ = recv_tx.send(Err(e.into()));
|
|
||||||
break 'work;
|
break 'work;
|
||||||
},
|
},
|
||||||
|
Ok(None) => {},
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// Try sending bytes through the TCP stream
|
// Try getting messages from the send channel
|
||||||
for _ in 0..10 {
|
for _ in 0..100 {
|
||||||
//println!("HERE! Outgoing len: {}", outgoing_chunks.len());
|
match send_rx.try_recv() {
|
||||||
match outgoing_chunks.pop_front() {
|
Ok(send_msg) => {
|
||||||
Some(chunk) => match stream.write_all(&chunk) {
|
// Serialize message
|
||||||
Ok(()) => {},
|
let mut msg_bytes = bincode::serialize(&send_msg).unwrap();
|
||||||
Err(e) if e.kind() == io::ErrorKind::WouldBlock => {
|
|
||||||
// Return chunk to the queue to try again later
|
// Assemble into packet
|
||||||
outgoing_chunks.push_front(chunk);
|
let mut packet_bytes = msg_bytes
|
||||||
break;
|
.len()
|
||||||
|
.to_le_bytes()
|
||||||
|
.as_ref()
|
||||||
|
.to_vec();
|
||||||
|
packet_bytes.append(&mut msg_bytes);
|
||||||
|
|
||||||
|
// Split packet into chunks
|
||||||
|
packet_bytes
|
||||||
|
.chunks(4096)
|
||||||
|
.map(|chunk| chunk.to_vec())
|
||||||
|
.for_each(|chunk| outgoing_chunks.push_back(chunk))
|
||||||
},
|
},
|
||||||
|
Err(mpsc::TryRecvError::Empty) => break,
|
||||||
|
// Worker error
|
||||||
|
Err(e) => {
|
||||||
|
let _ = recv_tx.send(Err(e.into()));
|
||||||
|
break 'work;
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Try sending bytes through the TCP stream
|
||||||
|
for _ in 0..100 {
|
||||||
|
match outgoing_chunks.pop_front() {
|
||||||
|
Some(mut chunk) => match stream.write(&chunk) {
|
||||||
|
Ok(n) => if n == chunk.len() {},
|
||||||
|
Ok(n) => {
|
||||||
|
outgoing_chunks.push_front(chunk.split_off(n));
|
||||||
|
break;
|
||||||
|
},
|
||||||
|
Err(e) if e.kind() == io::ErrorKind::WouldBlock => {
|
||||||
|
// Return chunk to the queue to try again later
|
||||||
|
outgoing_chunks.push_front(chunk);
|
||||||
|
break;
|
||||||
|
},
|
||||||
|
// Worker error
|
||||||
|
Err(e) => {
|
||||||
|
recv_tx.send(Err(e.into())).unwrap();
|
||||||
|
break 'work;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
None => break,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Try receiving bytes from the TCP stream
|
||||||
|
for _ in 0..100 {
|
||||||
|
let mut buf = [0; 4096];
|
||||||
|
|
||||||
|
match stream.read(&mut buf) {
|
||||||
|
Ok(n) => incoming_buf.extend_from_slice(&buf[0..n]),
|
||||||
|
Err(e) if e.kind() == io::ErrorKind::WouldBlock => break,
|
||||||
|
Err(e) if e.kind() == io::ErrorKind::Interrupted => {},
|
||||||
// Worker error
|
// Worker error
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
recv_tx.send(Err(e.into())).unwrap();
|
recv_tx.send(Err(e.into())).unwrap();
|
||||||
break 'work;
|
break 'work;
|
||||||
},
|
},
|
||||||
},
|
}
|
||||||
None => break,
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// Try receiving bytes from the TCP stream
|
// Try turning bytes into messages
|
||||||
for _ in 0..10 {
|
for _ in 0..100 {
|
||||||
let mut buf = [0; 1024];
|
match incoming_buf.get(0..8) {
|
||||||
|
Some(len_bytes) => {
|
||||||
|
let len = usize::from_le_bytes(<[u8; 8]>::try_from(len_bytes).unwrap()); // Can't fail
|
||||||
|
|
||||||
match stream.read(&mut buf) {
|
if len > MAX_MSG_SIZE {
|
||||||
Ok(n) => incoming_buf.extend_from_slice(&buf[0..n]),
|
recv_tx.send(Err(Error::InvalidMessage)).unwrap();
|
||||||
Err(e) if e.kind() == io::ErrorKind::WouldBlock => break,
|
break 'work;
|
||||||
// Worker error
|
} else if incoming_buf.len() >= len + 8 {
|
||||||
Err(e) => {
|
match bincode::deserialize(&incoming_buf[8..len + 8]) {
|
||||||
recv_tx.send(Err(e.into())).unwrap();
|
Ok(msg) => recv_tx.send(Ok(msg)).unwrap(),
|
||||||
break 'work;
|
Err(err) => {
|
||||||
},
|
recv_tx.send(Err(err.into())).unwrap()
|
||||||
}
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
// Try turning bytes into messages
|
incoming_buf = incoming_buf.split_off(len + 8);
|
||||||
for _ in 0..10 {
|
} else {
|
||||||
match incoming_buf.get(0..8) {
|
break;
|
||||||
Some(len_bytes) => {
|
|
||||||
let len = usize::from_le_bytes(<[u8; 8]>::try_from(len_bytes).unwrap()); // Can't fail
|
|
||||||
|
|
||||||
if len > MAX_MSG_SIZE {
|
|
||||||
recv_tx.send(Err(Error::InvalidMessage)).unwrap();
|
|
||||||
break 'work;
|
|
||||||
} else if incoming_buf.len() >= len + 8 {
|
|
||||||
let deserialize_result = bincode::deserialize(&incoming_buf[8..len + 8]);
|
|
||||||
|
|
||||||
if let Err(e) = &deserialize_result {
|
|
||||||
println!("DESERIALIZE ERROR: {:?}", e);
|
|
||||||
}
|
}
|
||||||
|
},
|
||||||
recv_tx.send(deserialize_result.map_err(|e| e.into()));
|
None => break,
|
||||||
incoming_buf = incoming_buf.split_off(len + 8);
|
}
|
||||||
} else {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
None => break,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
74
common/src/ray.rs
Normal file
74
common/src/ray.rs
Normal file
@ -0,0 +1,74 @@
|
|||||||
|
use vek::*;
|
||||||
|
use crate::vol::{
|
||||||
|
Vox,
|
||||||
|
ReadVol,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub trait RayUntil<V: Vox> = FnMut(&V) -> bool;
|
||||||
|
|
||||||
|
pub struct Ray<'a, V: ReadVol, F: RayUntil<V::Vox>> {
|
||||||
|
vol: &'a V,
|
||||||
|
from: Vec3<f32>,
|
||||||
|
to: Vec3<f32>,
|
||||||
|
until: F,
|
||||||
|
max_iter: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a, V: ReadVol, F: RayUntil<V::Vox>> Ray<'a, V, F> {
|
||||||
|
pub fn new(vol: &'a V, from: Vec3<f32>, to: Vec3<f32>, until: F) -> Self {
|
||||||
|
Self {
|
||||||
|
vol,
|
||||||
|
from,
|
||||||
|
to,
|
||||||
|
until,
|
||||||
|
max_iter: 100,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn until(self, f: F) -> Ray<'a, V, F> {
|
||||||
|
Ray {
|
||||||
|
until: f,
|
||||||
|
..self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn max_iter(mut self, max_iter: usize) -> Self {
|
||||||
|
self.max_iter = max_iter;
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn cast(mut self) -> (f32, Result<Option<&'a V::Vox>, V::Err>) {
|
||||||
|
// TODO: Fully test this!
|
||||||
|
|
||||||
|
const PLANCK: f32 = 0.001;
|
||||||
|
|
||||||
|
let mut dist = 0.0;
|
||||||
|
let dir = (self.to - self.from).normalized();
|
||||||
|
|
||||||
|
let mut pos = self.from;
|
||||||
|
let mut ipos = pos.map(|e| e as i32);
|
||||||
|
|
||||||
|
for _ in 0..self.max_iter {
|
||||||
|
pos = self.from + dir * dist;
|
||||||
|
ipos = pos.map(|e| e as i32);
|
||||||
|
|
||||||
|
match self.vol
|
||||||
|
.get(ipos)
|
||||||
|
.map(|vox| (vox, (self.until)(vox)))
|
||||||
|
{
|
||||||
|
Ok((vox, true)) => return (dist, Ok(Some(vox))),
|
||||||
|
Ok((_, false)) => {},
|
||||||
|
Err(err) => return (dist, Err(err)),
|
||||||
|
}
|
||||||
|
|
||||||
|
let deltas = (
|
||||||
|
dir.map(|e| if e < 0.0 { 0.0 } else { 1.0 }) -
|
||||||
|
pos.map(|e| e.fract())
|
||||||
|
) / dir;
|
||||||
|
|
||||||
|
dist += deltas.reduce(f32::min).max(PLANCK);
|
||||||
|
}
|
||||||
|
|
||||||
|
(dist, Ok(None))
|
||||||
|
}
|
||||||
|
}
|
@ -183,6 +183,17 @@ impl State {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Remove the chunk with the given key from this state's terrain, if it exists
|
||||||
|
pub fn remove_chunk(&mut self, key: Vec3<i32>) {
|
||||||
|
if self.ecs
|
||||||
|
.write_resource::<TerrainMap>()
|
||||||
|
.remove(key)
|
||||||
|
.is_some()
|
||||||
|
{
|
||||||
|
self.changes.removed_chunks.insert(key);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Execute a single tick, simulating the game state by the given duration.
|
/// Execute a single tick, simulating the game state by the given duration.
|
||||||
pub fn tick(&mut self, dt: Duration) {
|
pub fn tick(&mut self, dt: Duration) {
|
||||||
// Change the time accordingly
|
// Change the time accordingly
|
||||||
|
@ -1,25 +1,45 @@
|
|||||||
// Library
|
use vek::*;
|
||||||
use specs::{Join, Read, ReadStorage, System, WriteStorage};
|
use specs::{Join, Read, ReadStorage, System, WriteStorage, ReadExpect};
|
||||||
|
|
||||||
// Crate
|
|
||||||
use crate::{
|
use crate::{
|
||||||
comp::phys::{Pos, Vel},
|
comp::phys::{Pos, Vel},
|
||||||
state::DeltaTime,
|
state::DeltaTime,
|
||||||
|
terrain::TerrainMap,
|
||||||
|
vol::{Vox, ReadVol},
|
||||||
};
|
};
|
||||||
|
|
||||||
// Basic ECS physics system
|
// Basic ECS physics system
|
||||||
pub struct Sys;
|
pub struct Sys;
|
||||||
|
|
||||||
|
const GRAVITY: f32 = 9.81 * 2.0;
|
||||||
|
|
||||||
impl<'a> System<'a> for Sys {
|
impl<'a> System<'a> for Sys {
|
||||||
type SystemData = (
|
type SystemData = (
|
||||||
WriteStorage<'a, Pos>,
|
ReadExpect<'a, TerrainMap>,
|
||||||
ReadStorage<'a, Vel>,
|
|
||||||
Read<'a, DeltaTime>,
|
Read<'a, DeltaTime>,
|
||||||
|
WriteStorage<'a, Pos>,
|
||||||
|
WriteStorage<'a, Vel>,
|
||||||
);
|
);
|
||||||
|
|
||||||
fn run(&mut self, (mut positions, velocities, dt): Self::SystemData) {
|
fn run(&mut self, (terrain, dt, mut positions, mut velocities): Self::SystemData) {
|
||||||
(&mut positions, &velocities)
|
for (pos, vel) in (&mut positions, &mut velocities).join() {
|
||||||
.join() // this can be parallelized with par_join()
|
// Gravity
|
||||||
.for_each(|(pos, vel)| pos.0 += vel.0 * dt.0 as f32);
|
vel.0.z -= GRAVITY * dt.0 as f32;
|
||||||
|
|
||||||
|
// Movement
|
||||||
|
pos.0 += vel.0 * dt.0 as f32;
|
||||||
|
|
||||||
|
// Basic collision with terrain
|
||||||
|
let mut i = 0;
|
||||||
|
while terrain
|
||||||
|
.get(pos.0.map(|e| e.floor() as i32))
|
||||||
|
.map(|vox| !vox.is_empty())
|
||||||
|
.unwrap_or(false) &&
|
||||||
|
i < 80
|
||||||
|
{
|
||||||
|
pos.0.z += 0.005;
|
||||||
|
vel.0.z = 0.0;
|
||||||
|
i += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
// Library
|
|
||||||
use vek::*;
|
use vek::*;
|
||||||
|
use crate::ray::{Ray, RayUntil};
|
||||||
|
|
||||||
/// A voxel
|
/// A voxel
|
||||||
pub trait Vox {
|
pub trait Vox {
|
||||||
@ -64,6 +64,12 @@ pub trait ReadVol: BaseVol {
|
|||||||
/// Get a reference to the voxel at the provided position in the volume.
|
/// Get a reference to the voxel at the provided position in the volume.
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
fn get(&self, pos: Vec3<i32>) -> Result<&Self::Vox, Self::Err>;
|
fn get(&self, pos: Vec3<i32>) -> Result<&Self::Vox, Self::Err>;
|
||||||
|
|
||||||
|
fn ray(&self, from: Vec3<f32>, to: Vec3<f32>) -> Ray<Self, fn(&Self::Vox) -> bool>
|
||||||
|
where Self: Sized
|
||||||
|
{
|
||||||
|
Ray::new(self, from, to, |vox| !vox.is_empty())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A volume that provides the ability to sample (i.e: clone a section of) its voxel data.
|
/// A volume that provides the ability to sample (i.e: clone a section of) its voxel data.
|
||||||
|
@ -73,6 +73,7 @@ impl<V: Vox + Clone, S: VolSize, M> SampleVol for VolMap<V, S, M> {
|
|||||||
/// Note that the resultant volume does not carry forward metadata from the original chunks.
|
/// Note that the resultant volume does not carry forward metadata from the original chunks.
|
||||||
fn sample(&self, range: Aabb<i32>) -> Result<Self::Sample, VolMapErr> {
|
fn sample(&self, range: Aabb<i32>) -> Result<Self::Sample, VolMapErr> {
|
||||||
// Return early if we don't have all the needed chunks that we need!
|
// Return early if we don't have all the needed chunks that we need!
|
||||||
|
/*
|
||||||
let min_chunk = Self::chunk_key(range.min);
|
let min_chunk = Self::chunk_key(range.min);
|
||||||
let max_chunk = Self::chunk_key(range.max - Vec3::one());
|
let max_chunk = Self::chunk_key(range.max - Vec3::one());
|
||||||
for x in min_chunk.x..=max_chunk.x {
|
for x in min_chunk.x..=max_chunk.x {
|
||||||
@ -84,6 +85,7 @@ impl<V: Vox + Clone, S: VolSize, M> SampleVol for VolMap<V, S, M> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
let mut sample = Dyna::filled(
|
let mut sample = Dyna::filled(
|
||||||
range.size().map(|e| e as u32).into(),
|
range.size().map(|e| e as u32).into(),
|
||||||
@ -92,7 +94,7 @@ impl<V: Vox + Clone, S: VolSize, M> SampleVol for VolMap<V, S, M> {
|
|||||||
);
|
);
|
||||||
|
|
||||||
for pos in sample.iter_positions() {
|
for pos in sample.iter_positions() {
|
||||||
sample.set(pos, self.get(range.min + pos)?.clone())
|
sample.set(pos, self.get(range.min + pos).map(|v| v.clone()).unwrap_or(V::empty()))
|
||||||
.map_err(|err| VolMapErr::DynaErr(err))?;
|
.map_err(|err| VolMapErr::DynaErr(err))?;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -8,10 +8,20 @@ pub mod input;
|
|||||||
// Reexports
|
// Reexports
|
||||||
pub use crate::{error::Error, input::Input};
|
pub use crate::{error::Error, input::Input};
|
||||||
|
|
||||||
use crate::{
|
use std::{
|
||||||
client::{Client, Clients},
|
collections::HashSet,
|
||||||
cmd::CHAT_COMMANDS,
|
net::SocketAddr,
|
||||||
|
sync::mpsc,
|
||||||
|
time::Duration,
|
||||||
|
i32,
|
||||||
};
|
};
|
||||||
|
use specs::{
|
||||||
|
join::Join, saveload::MarkedBuilder, world::EntityBuilder as EcsEntityBuilder, Builder,
|
||||||
|
Entity as EcsEntity,
|
||||||
|
};
|
||||||
|
use threadpool::ThreadPool;
|
||||||
|
use vek::*;
|
||||||
|
use world::World;
|
||||||
use common::{
|
use common::{
|
||||||
comp,
|
comp,
|
||||||
comp::character::Animation,
|
comp::character::Animation,
|
||||||
@ -20,14 +30,10 @@ use common::{
|
|||||||
state::{State, Uid},
|
state::{State, Uid},
|
||||||
terrain::TerrainChunk,
|
terrain::TerrainChunk,
|
||||||
};
|
};
|
||||||
use specs::{
|
use crate::{
|
||||||
join::Join, saveload::MarkedBuilder, world::EntityBuilder as EcsEntityBuilder, Builder,
|
client::{Client, Clients},
|
||||||
Entity as EcsEntity,
|
cmd::CHAT_COMMANDS,
|
||||||
};
|
};
|
||||||
use std::{collections::HashSet, net::SocketAddr, sync::mpsc, time::Duration};
|
|
||||||
use threadpool::ThreadPool;
|
|
||||||
use vek::*;
|
|
||||||
use world::World;
|
|
||||||
|
|
||||||
const CLIENT_TIMEOUT: f64 = 20.0; // Seconds
|
const CLIENT_TIMEOUT: f64 = 20.0; // Seconds
|
||||||
|
|
||||||
@ -112,7 +118,7 @@ impl Server {
|
|||||||
self.state
|
self.state
|
||||||
.ecs_mut()
|
.ecs_mut()
|
||||||
.create_entity_synced()
|
.create_entity_synced()
|
||||||
.with(comp::phys::Pos(Vec3::zero()))
|
.with(comp::phys::Pos(Vec3::new(0.0, 0.0, 64.0)))
|
||||||
.with(comp::phys::Vel(Vec3::zero()))
|
.with(comp::phys::Vel(Vec3::zero()))
|
||||||
.with(comp::phys::Dir(Vec3::unit_y()))
|
.with(comp::phys::Dir(Vec3::unit_y()))
|
||||||
.with(character)
|
.with(character)
|
||||||
@ -125,7 +131,7 @@ impl Server {
|
|||||||
character: comp::Character,
|
character: comp::Character,
|
||||||
) {
|
) {
|
||||||
state.write_component(entity, character);
|
state.write_component(entity, character);
|
||||||
state.write_component(entity, comp::phys::Pos(Vec3::zero()));
|
state.write_component(entity, comp::phys::Pos(Vec3::new(0.0, 0.0, 64.0)));
|
||||||
state.write_component(entity, comp::phys::Vel(Vec3::zero()));
|
state.write_component(entity, comp::phys::Vel(Vec3::zero()));
|
||||||
state.write_component(entity, comp::phys::Dir(Vec3::unit_y()));
|
state.write_component(entity, comp::phys::Dir(Vec3::unit_y()));
|
||||||
// Make sure everything is accepted
|
// Make sure everything is accepted
|
||||||
@ -187,21 +193,43 @@ impl Server {
|
|||||||
&self.state.ecs().entities(),
|
&self.state.ecs().entities(),
|
||||||
&self.state.ecs().read_storage::<comp::Player>(),
|
&self.state.ecs().read_storage::<comp::Player>(),
|
||||||
&self.state.ecs().read_storage::<comp::phys::Pos>(),
|
&self.state.ecs().read_storage::<comp::phys::Pos>(),
|
||||||
)
|
).join() {
|
||||||
.join()
|
let chunk_pos = self.state.terrain().pos_key(pos.0.map(|e| e as i32));
|
||||||
{
|
let dist = (chunk_pos - key).map(|e| e.abs()).reduce_max();
|
||||||
// TODO: Distance check
|
|
||||||
// if self.state.terrain().key_pos(key)
|
|
||||||
|
|
||||||
/*
|
if dist < 4 {
|
||||||
self.clients.notify(entity, ServerMsg::TerrainChunkUpdate {
|
self.clients.notify(entity, ServerMsg::TerrainChunkUpdate {
|
||||||
key,
|
key,
|
||||||
chunk: Box::new(chunk.clone()),
|
chunk: Box::new(chunk.clone()),
|
||||||
});
|
});
|
||||||
*/
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
self.state.insert_chunk(key, chunk);
|
self.state.insert_chunk(key, chunk);
|
||||||
|
self.pending_chunks.remove(&key);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remove chunks that are too far from players
|
||||||
|
let mut chunks_to_remove = Vec::new();
|
||||||
|
self.state.terrain().iter().for_each(|(key, _)| {
|
||||||
|
let mut min_dist = i32::MAX;
|
||||||
|
|
||||||
|
// For each player with a position, calculate the distance
|
||||||
|
for (_, pos) in (
|
||||||
|
&self.state.ecs().read_storage::<comp::Player>(),
|
||||||
|
&self.state.ecs().read_storage::<comp::phys::Pos>(),
|
||||||
|
).join() {
|
||||||
|
let chunk_pos = self.state.terrain().pos_key(pos.0.map(|e| e as i32));
|
||||||
|
let dist = (chunk_pos - key).map(|e| e.abs()).reduce_max();
|
||||||
|
min_dist = min_dist.min(dist);
|
||||||
|
}
|
||||||
|
|
||||||
|
if min_dist > 3 {
|
||||||
|
chunks_to_remove.push(key);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
for key in chunks_to_remove {
|
||||||
|
self.state.remove_chunk(key);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Synchronise clients with the new state of the world
|
// Synchronise clients with the new state of the world
|
||||||
@ -322,10 +350,10 @@ impl Server {
|
|||||||
}
|
}
|
||||||
ClientState::Spectator | ClientState::Character => {
|
ClientState::Spectator | ClientState::Character => {
|
||||||
match state.terrain().get_key(key) {
|
match state.terrain().get_key(key) {
|
||||||
Some(chunk) => {} /*client.postbox.send_message(ServerMsg::TerrainChunkUpdate {
|
Some(chunk) => client.postbox.send_message(ServerMsg::TerrainChunkUpdate {
|
||||||
key,
|
key,
|
||||||
chunk: Box::new(chunk.clone()),
|
chunk: Box::new(chunk.clone()),
|
||||||
}),*/
|
}),
|
||||||
None => requested_chunks.push(key),
|
None => requested_chunks.push(key),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -67,8 +67,7 @@ impl Animation for IdleAnimation {
|
|||||||
next.weapon.ori = Quaternion::rotation_x(2.5);
|
next.weapon.ori = Quaternion::rotation_x(2.5);
|
||||||
next.weapon.scale = Vec3::one();
|
next.weapon.scale = Vec3::one();
|
||||||
|
|
||||||
|
next.torso.offset = Vec3::new(-0.5, 0.0, 0.0);
|
||||||
next.torso.offset = Vec3::new(0.0, 0.0, 0.0);
|
|
||||||
next.torso.ori = Quaternion::rotation_y(0.0);
|
next.torso.ori = Quaternion::rotation_y(0.0);
|
||||||
next.torso.scale = Vec3::one() / 11.0;
|
next.torso.scale = Vec3::one() / 11.0;
|
||||||
|
|
||||||
|
@ -67,7 +67,7 @@ impl Animation for RunAnimation {
|
|||||||
next.weapon.ori = Quaternion::rotation_x(2.5);
|
next.weapon.ori = Quaternion::rotation_x(2.5);
|
||||||
next.weapon.scale = Vec3::one();
|
next.weapon.scale = Vec3::one();
|
||||||
|
|
||||||
next.torso.offset = Vec3::new(0.0, 0.0, 0.0);
|
next.torso.offset = Vec3::new(-0.5, 0.0, 0.0);
|
||||||
next.torso.ori = Quaternion::rotation_y(0.25 + wavecos * 0.1);
|
next.torso.ori = Quaternion::rotation_y(0.25 + wavecos * 0.1);
|
||||||
next.torso.scale = Vec3::one() / 11.0;
|
next.torso.scale = Vec3::one() / 11.0;
|
||||||
|
|
||||||
|
@ -54,6 +54,8 @@ impl<M> Meshable for Dyna<Block, M> {
|
|||||||
.filter(|pos| pos.map(|e| e >= 1).reduce_and())
|
.filter(|pos| pos.map(|e| e >= 1).reduce_and())
|
||||||
.filter(|pos| pos.map2(self.get_size(), |e, sz| e < sz as i32 - 1).reduce_and())
|
.filter(|pos| pos.map2(self.get_size(), |e, sz| e < sz as i32 - 1).reduce_and())
|
||||||
{
|
{
|
||||||
|
let offs = pos.map(|e| e as f32 - 1.0);
|
||||||
|
|
||||||
if let Some(col) = self
|
if let Some(col) = self
|
||||||
.get(pos)
|
.get(pos)
|
||||||
.ok()
|
.ok()
|
||||||
@ -67,9 +69,9 @@ impl<M> Meshable for Dyna<Block, M> {
|
|||||||
.unwrap_or(true)
|
.unwrap_or(true)
|
||||||
{
|
{
|
||||||
mesh.push_quad(create_quad(
|
mesh.push_quad(create_quad(
|
||||||
Vec3::one() + pos.map(|e| e as f32) + Vec3::unit_y(),
|
offs,
|
||||||
-Vec3::unit_y(),
|
|
||||||
Vec3::unit_z(),
|
Vec3::unit_z(),
|
||||||
|
Vec3::unit_y(),
|
||||||
-Vec3::unit_x(),
|
-Vec3::unit_x(),
|
||||||
col,
|
col,
|
||||||
));
|
));
|
||||||
@ -80,7 +82,7 @@ impl<M> Meshable for Dyna<Block, M> {
|
|||||||
.unwrap_or(true)
|
.unwrap_or(true)
|
||||||
{
|
{
|
||||||
mesh.push_quad(create_quad(
|
mesh.push_quad(create_quad(
|
||||||
Vec3::one() + pos.map(|e| e as f32) + Vec3::unit_x(),
|
offs + Vec3::unit_x(),
|
||||||
Vec3::unit_y(),
|
Vec3::unit_y(),
|
||||||
Vec3::unit_z(),
|
Vec3::unit_z(),
|
||||||
Vec3::unit_x(),
|
Vec3::unit_x(),
|
||||||
@ -93,7 +95,7 @@ impl<M> Meshable for Dyna<Block, M> {
|
|||||||
.unwrap_or(true)
|
.unwrap_or(true)
|
||||||
{
|
{
|
||||||
mesh.push_quad(create_quad(
|
mesh.push_quad(create_quad(
|
||||||
Vec3::one() + pos.map(|e| e as f32),
|
offs,
|
||||||
Vec3::unit_x(),
|
Vec3::unit_x(),
|
||||||
Vec3::unit_z(),
|
Vec3::unit_z(),
|
||||||
-Vec3::unit_y(),
|
-Vec3::unit_y(),
|
||||||
@ -106,7 +108,7 @@ impl<M> Meshable for Dyna<Block, M> {
|
|||||||
.unwrap_or(true)
|
.unwrap_or(true)
|
||||||
{
|
{
|
||||||
mesh.push_quad(create_quad(
|
mesh.push_quad(create_quad(
|
||||||
Vec3::one() + pos.map(|e| e as f32) + Vec3::unit_y(),
|
offs + Vec3::unit_y(),
|
||||||
Vec3::unit_z(),
|
Vec3::unit_z(),
|
||||||
Vec3::unit_x(),
|
Vec3::unit_x(),
|
||||||
Vec3::unit_y(),
|
Vec3::unit_y(),
|
||||||
@ -119,7 +121,7 @@ impl<M> Meshable for Dyna<Block, M> {
|
|||||||
.unwrap_or(true)
|
.unwrap_or(true)
|
||||||
{
|
{
|
||||||
mesh.push_quad(create_quad(
|
mesh.push_quad(create_quad(
|
||||||
Vec3::one() + pos.map(|e| e as f32),
|
offs,
|
||||||
Vec3::unit_y(),
|
Vec3::unit_y(),
|
||||||
Vec3::unit_x(),
|
Vec3::unit_x(),
|
||||||
-Vec3::unit_z(),
|
-Vec3::unit_z(),
|
||||||
@ -132,7 +134,7 @@ impl<M> Meshable for Dyna<Block, M> {
|
|||||||
.unwrap_or(true)
|
.unwrap_or(true)
|
||||||
{
|
{
|
||||||
mesh.push_quad(create_quad(
|
mesh.push_quad(create_quad(
|
||||||
Vec3::one() + pos.map(|e| e as f32) + Vec3::unit_z(),
|
offs + Vec3::unit_z(),
|
||||||
Vec3::unit_x(),
|
Vec3::unit_x(),
|
||||||
Vec3::unit_y(),
|
Vec3::unit_y(),
|
||||||
Vec3::unit_z(),
|
Vec3::unit_z(),
|
||||||
|
@ -257,7 +257,7 @@ impl<S: Skeleton> FigureState<S> {
|
|||||||
let mat =
|
let mat =
|
||||||
Mat4::<f32>::identity() *
|
Mat4::<f32>::identity() *
|
||||||
Mat4::translation_3d(pos) *
|
Mat4::translation_3d(pos) *
|
||||||
Mat4::rotation_z(dir.y.atan2(dir.x));// + f32//::consts)::PI / 2.0);
|
Mat4::rotation_z(-dir.x.atan2(dir.y) + f32::consts::PI / 2.0);
|
||||||
|
|
||||||
let locals = FigureLocals::new(mat);
|
let locals = FigureLocals::new(mat);
|
||||||
renderer.update_consts(&mut self.locals, &[locals]).unwrap();
|
renderer.update_consts(&mut self.locals, &[locals]).unwrap();
|
||||||
|
@ -99,14 +99,24 @@ impl Terrain {
|
|||||||
// What happens if the block on the edge of a chunk gets modified? We need to spawn
|
// What happens if the block on the edge of a chunk gets modified? We need to spawn
|
||||||
// a mesh worker to remesh its neighbour(s) too since their ambient occlusion and face
|
// a mesh worker to remesh its neighbour(s) too since their ambient occlusion and face
|
||||||
// elision information changes too!
|
// elision information changes too!
|
||||||
match self.mesh_todo.iter_mut().find(|todo| todo.pos == *pos) {
|
for i in -1..2 {
|
||||||
Some(todo) => todo.started_tick = current_tick,
|
for j in -1..2 {
|
||||||
// The chunk it's queued yet, add it to the queue
|
for k in -1..2 {
|
||||||
None => self.mesh_todo.push_back(ChunkMeshState {
|
let pos = pos + Vec3::new(i, j, k);
|
||||||
pos: *pos,
|
|
||||||
started_tick: current_tick,
|
if client.state().terrain().get_key(pos).is_some() {
|
||||||
active_worker: false,
|
match self.mesh_todo.iter_mut().find(|todo| todo.pos == pos) {
|
||||||
}),
|
//Some(todo) => todo.started_tick = current_tick,
|
||||||
|
// The chunk it's queued yet, add it to the queue
|
||||||
|
_ /* None */ => self.mesh_todo.push_back(ChunkMeshState {
|
||||||
|
pos,
|
||||||
|
started_tick: current_tick,
|
||||||
|
active_worker: false,
|
||||||
|
}),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -155,8 +165,10 @@ impl Terrain {
|
|||||||
todo.active_worker = true;
|
todo.active_worker = true;
|
||||||
});
|
});
|
||||||
|
|
||||||
// Receive chunk meshes from worker threads, upload them to the GPU and then store them
|
// Receive a chunk mesh from a worker thread, upload it to the GPU and then store it
|
||||||
while let Ok(response) = self.mesh_recv.recv_timeout(Duration::new(0, 0)) {
|
// Only pull out one chunk per frame to avoid an unacceptable amount of blocking lag due
|
||||||
|
// to the GPU upload. That still gives us a 60 chunks / second budget to play with.
|
||||||
|
if let Ok(response) = self.mesh_recv.recv_timeout(Duration::new(0, 0)) {
|
||||||
match self.mesh_todo.iter().find(|todo| todo.pos == response.pos) {
|
match self.mesh_todo.iter().find(|todo| todo.pos == response.pos) {
|
||||||
// It's the mesh we want, insert the newly finished model into the terrain model
|
// It's the mesh we want, insert the newly finished model into the terrain model
|
||||||
// data structure (convert the mesh to a model first of course)
|
// data structure (convert the mesh to a model first of course)
|
||||||
@ -170,7 +182,7 @@ impl Terrain {
|
|||||||
},
|
},
|
||||||
// Chunk must have been removed, or it was spawned on an old tick. Drop the mesh
|
// Chunk must have been removed, or it was spawned on an old tick. Drop the mesh
|
||||||
// since it's either out of date or no longer needed
|
// since it's either out of date or no longer needed
|
||||||
_ => continue,
|
_ => {},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -32,7 +32,8 @@ impl World {
|
|||||||
let air = Block::empty();
|
let air = Block::empty();
|
||||||
let stone = Block::new(1, Rgb::new(200, 220, 255));
|
let stone = Block::new(1, Rgb::new(200, 220, 255));
|
||||||
let grass = Block::new(2, Rgb::new(50, 255, 0));
|
let grass = Block::new(2, Rgb::new(50, 255, 0));
|
||||||
let sand = Block::new(3, Rgb::new(180, 150, 50));
|
let dirt = Block::new(3, Rgb::new(128, 90, 0));
|
||||||
|
let sand = Block::new(4, Rgb::new(180, 150, 50));
|
||||||
|
|
||||||
let perlin_nz = Perlin::new();
|
let perlin_nz = Perlin::new();
|
||||||
|
|
||||||
@ -40,17 +41,22 @@ impl World {
|
|||||||
let wpos = lpos + chunk_pos * chunk.get_size().map(|e| e as i32);
|
let wpos = lpos + chunk_pos * chunk.get_size().map(|e| e as i32);
|
||||||
let wposf = wpos.map(|e| e as f64);
|
let wposf = wpos.map(|e| e as f64);
|
||||||
|
|
||||||
let freq = 1.0 / 32.0;
|
let freq = 1.0 / 128.0;
|
||||||
let ampl = 16.0;
|
let ampl = 32.0;
|
||||||
let offs = 16.0;
|
let small_freq = 1.0 / 32.0;
|
||||||
let height = perlin_nz.get(Vec2::from(wposf * freq).into_array()) * ampl + offs;
|
let small_ampl = 6.0;
|
||||||
|
let offs = 32.0;
|
||||||
|
let height =
|
||||||
|
perlin_nz.get(Vec2::from(wposf * freq).into_array()) * ampl
|
||||||
|
+ perlin_nz.get(Vec2::from(wposf * small_freq).into_array()) * small_ampl
|
||||||
|
+ offs;
|
||||||
|
|
||||||
chunk.set(lpos, if wposf.z < height {
|
chunk.set(lpos, if wposf.z < height - 4.0 {
|
||||||
if wposf.z < height - 1.0 {
|
stone
|
||||||
stone
|
} else if wposf.z < height - 1.0 {
|
||||||
} else {
|
dirt
|
||||||
sand
|
} else if wposf.z < height {
|
||||||
}
|
grass
|
||||||
} else {
|
} else {
|
||||||
air
|
air
|
||||||
}).unwrap();
|
}).unwrap();
|
||||||
|
Loading…
x
Reference in New Issue
Block a user