Merge branch 'master' into 'master'

Added multi-character rendering, integrated Sphynx, network overhaul

See merge request veloren/veloren!19

Former-commit-id: df34b5df61bea22193b465e883d563a56632174c
This commit is contained in:
Joshua Barretto 2019-04-14 13:39:55 +00:00
commit 07698f46ba
40 changed files with 1209 additions and 623 deletions

View File

@ -1,6 +1,6 @@
[package] [package]
name = "veloren-chat-cli" name = "veloren-chat-cli"
version = "0.1.0" version = "0.2.0"
authors = ["Joshua Barretto <joshua.s.barretto@gmail.com>"] authors = ["Joshua Barretto <joshua.s.barretto@gmail.com>"]
edition = "2018" edition = "2018"

View File

@ -1,7 +1,10 @@
use std::time::Duration; use std::time::Duration;
use log::info; use log::info;
use client::{Input, Client, Event}; use client::{Input, Client, Event};
use common::clock::Clock; use common::{
comp,
clock::Clock,
};
const FPS: u64 = 60; const FPS: u64 = 60;
@ -15,7 +18,7 @@ fn main() {
let mut clock = Clock::new(); let mut clock = Clock::new();
// Create client // Create client
let mut client = Client::new(([127, 0, 0, 1], 59003)) let mut client = Client::new(([127, 0, 0, 1], 59003), comp::Player::new("test".to_string()), None, 300)
.expect("Failed to create client instance"); .expect("Failed to create client instance");
client.send_chat("Hello!".to_string()); client.send_chat("Hello!".to_string());

View File

@ -1,12 +1,11 @@
[package] [package]
name = "veloren-client" name = "veloren-client"
version = "0.1.0" version = "0.2.0"
authors = ["Joshua Barretto <joshua.s.barretto@gmail.com>"] authors = ["Joshua Barretto <joshua.s.barretto@gmail.com>"]
edition = "2018" edition = "2018"
[dependencies] [dependencies]
common = { package = "veloren-common", path = "../common" } common = { package = "veloren-common", path = "../common" }
world = { package = "veloren-world", path = "../world" }
specs = "0.14" specs = "0.14"
vek = "0.9" vek = "0.9"

View File

@ -3,6 +3,7 @@ use common::net::PostError;
#[derive(Debug)] #[derive(Debug)]
pub enum Error { pub enum Error {
Network(PostError), Network(PostError),
ServerWentMad,
ServerTimeout, ServerTimeout,
ServerShutdown, ServerShutdown,
Other(String), Other(String),
@ -10,9 +11,6 @@ pub enum Error {
impl From<PostError> for Error { impl From<PostError> for Error {
fn from(err: PostError) -> Self { fn from(err: PostError) -> Self {
match err { Error::Network(err)
PostError::Disconnect => Error::ServerShutdown,
err => Error::Network(err),
}
} }
} }

View File

@ -13,9 +13,10 @@ pub use crate::{
use std::{ use std::{
time::Duration, time::Duration,
net::SocketAddr, net::SocketAddr,
collections::HashSet,
}; };
use vek::*; use vek::*;
use threadpool; use threadpool::ThreadPool;
use specs::Builder; use specs::Builder;
use common::{ use common::{
comp, comp,
@ -24,7 +25,6 @@ use common::{
net::PostBox, net::PostBox,
msg::{ClientMsg, ServerMsg}, msg::{ClientMsg, ServerMsg},
}; };
use world::World;
const SERVER_TIMEOUT: f64 = 5.0; // Seconds const SERVER_TIMEOUT: f64 = 5.0; // Seconds
@ -33,7 +33,7 @@ pub enum Event {
} }
pub struct Client { pub struct Client {
thread_pool: threadpool::ThreadPool, thread_pool: ThreadPool,
last_ping: f64, last_ping: f64,
postbox: PostBox<ClientMsg, ServerMsg>, postbox: PostBox<ClientMsg, ServerMsg>,
@ -41,19 +41,38 @@ pub struct Client {
tick: u64, tick: u64,
state: State, state: State,
player: Option<EcsEntity>, player: Option<EcsEntity>,
view_distance: u64,
// Testing pending_chunks: HashSet<Vec3<i32>>,
world: World,
pub chunk: Option<TerrainChunk>,
} }
impl Client { impl Client {
/// Create a new `Client`. /// Create a new `Client`.
#[allow(dead_code)] #[allow(dead_code)]
pub fn new<A: Into<SocketAddr>>(addr: A) -> Result<Self, Error> { pub fn new<A: Into<SocketAddr>>(
let state = State::new(); addr: A,
player: comp::Player,
character: Option<comp::Character>,
view_distance: u64,
) -> Result<Self, Error> {
let mut postbox = PostBox::to_server(addr)?; let mut postbox = PostBox::to(addr)?;
// Send connection request
postbox.send_message(ClientMsg::Connect {
player,
character,
});
// Wait for handshake from server
let (state, player) = match postbox.next_message() {
Some(ServerMsg::Handshake { ecs_state, player_entity }) => {
let mut state = State::from_state_package(ecs_state);
let player_entity = state.ecs().entity_from_uid(player_entity);
(state, player_entity)
},
_ => return Err(Error::ServerWentMad),
};
Ok(Self { Ok(Self {
thread_pool: threadpool::Builder::new() thread_pool: threadpool::Builder::new()
@ -65,11 +84,10 @@ impl Client {
tick: 0, tick: 0,
state, state,
player: None, player,
view_distance,
// Testing pending_chunks: HashSet::new(),
world: World::new(),
chunk: None,
}) })
} }
@ -79,18 +97,6 @@ impl Client {
#[allow(dead_code)] #[allow(dead_code)]
pub fn thread_pool(&self) -> &threadpool::ThreadPool { &self.thread_pool } pub fn thread_pool(&self) -> &threadpool::ThreadPool { &self.thread_pool }
// TODO: Get rid of this
pub fn with_test_state(mut self) -> Self {
self.chunk = Some(self.world.generate_chunk(Vec3::zero()));
self
}
// TODO: Get rid of this
pub fn load_chunk(&mut self, pos: Vec3<i32>) {
self.state.terrain_mut().insert(pos, self.world.generate_chunk(pos));
self.state.changes_mut().new_chunks.push(pos);
}
/// Get a reference to the client's game state. /// Get a reference to the client's game state.
#[allow(dead_code)] #[allow(dead_code)]
pub fn state(&self) -> &State { &self.state } pub fn state(&self) -> &State { &self.state }
@ -114,7 +120,7 @@ impl Client {
/// Send a chat message to the server /// Send a chat message to the server
#[allow(dead_code)] #[allow(dead_code)]
pub fn send_chat(&mut self, msg: String) { pub fn send_chat(&mut self, msg: String) {
self.postbox.send(ClientMsg::Chat(msg)) self.postbox.send_message(ClientMsg::Chat(msg))
} }
/// Execute a single client tick, handle input and update the game state by the given duration /// Execute a single client tick, handle input and update the game state by the given duration
@ -138,12 +144,19 @@ 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, _)| {
println!("Chunk at {:?}", k);
});
// Step 1 // Step 1
if let Some(ecs_entity) = self.player { if let Some(ecs_entity) = self.player {
// TODO: remove this // TODO: remove this
const PLAYER_VELOCITY: f32 = 100.0; const PLAYER_VELOCITY: f32 = 100.0;
// TODO: Set acceleration instead // TODO: Set acceleration instead
self.state.write_component(ecs_entity, comp::phys::Vel(Vec3::from(input.move_dir * PLAYER_VELOCITY))); self.state.write_component(ecs_entity, comp::phys::Vel(Vec3::from(input.move_dir * PLAYER_VELOCITY) * 0.1));
if input.move_dir.magnitude() > 0.01 {
self.state.write_component(ecs_entity, comp::phys::Dir(input.move_dir.normalized().into()));
}
} }
// Tick the client's LocalState (step 3) // Tick the client's LocalState (step 3)
@ -157,12 +170,31 @@ impl Client {
self.state.read_storage().get(ecs_entity).cloned(), self.state.read_storage().get(ecs_entity).cloned(),
) { ) {
(Some(pos), Some(vel), Some(dir)) => { (Some(pos), Some(vel), Some(dir)) => {
self.postbox.send(ClientMsg::PlayerPhysics { pos, vel, dir }); self.postbox.send_message(ClientMsg::PlayerPhysics { pos, vel, dir });
}, },
_ => {}, _ => {},
} }
} }
// Request chunks from the server
if let Some(player_entity) = self.player {
if let Some(pos) = self.state.read_storage::<comp::phys::Pos>().get(player_entity) {
let chunk_pos = self.state.terrain().pos_key(pos.0.map(|e| e as i32));
for i in chunk_pos.x - 0..chunk_pos.x + 1 {
for j in chunk_pos.y - 0..chunk_pos.y + 1 {
for k in 0..3 {
let key = chunk_pos + Vec3::new(i, j, k);
if self.state.terrain().get_key(key).is_none() && !self.pending_chunks.contains(&key) {
self.postbox.send_message(ClientMsg::TerrainChunkRequest { key });
self.pending_chunks.insert(key);
}
}
}
}
}
}
// Finish the tick, pass control back to the frontend (step 6) // Finish the tick, pass control back to the frontend (step 6)
self.tick += 1; self.tick += 1;
Ok(frontend_events) Ok(frontend_events)
@ -187,28 +219,16 @@ impl Client {
for msg in new_msgs { for msg in new_msgs {
match msg { match msg {
ServerMsg::Handshake { .. } => return Err(Error::ServerWentMad),
ServerMsg::Shutdown => return Err(Error::ServerShutdown), ServerMsg::Shutdown => return Err(Error::ServerShutdown),
ServerMsg::Ping => self.postbox.send(ClientMsg::Pong), ServerMsg::Ping => self.postbox.send_message(ClientMsg::Pong),
ServerMsg::Pong => {}, ServerMsg::Pong => {},
ServerMsg::Chat(msg) => frontend_events.push(Event::Chat(msg)), ServerMsg::Chat(msg) => frontend_events.push(Event::Chat(msg)),
ServerMsg::SetPlayerEntity(uid) => { ServerMsg::SetPlayerEntity(uid) => self.player = Some(self.state.ecs().entity_from_uid(uid).unwrap()), // TODO: Don't unwrap here!
let ecs_entity = self.state ServerMsg::EcsSync(sync_package) => self.state.ecs_mut().sync_with_package(sync_package),
.get_entity(uid) ServerMsg::TerrainChunkUpdate { key, chunk } => {
.unwrap_or_else(|| self.state.build_uid_entity_with_uid(uid).build()); self.state.insert_chunk(key, *chunk);
self.pending_chunks.remove(&key);
self.player = Some(ecs_entity);
},
ServerMsg::EntityPhysics { uid, pos, vel, dir } => {
let ecs_entity = self.state
.get_entity(uid)
.unwrap_or_else(|| self.state.build_uid_entity_with_uid(uid).build());
self.state.write_component(ecs_entity, pos);
self.state.write_component(ecs_entity, vel);
self.state.write_component(ecs_entity, dir);
},
ServerMsg::EntityDeleted(uid) => {
self.state.delete_entity(uid);
}, },
} }
} }
@ -218,7 +238,7 @@ impl Client {
return Err(Error::ServerTimeout); return Err(Error::ServerTimeout);
} else if self.state.get_time() - self.last_ping > SERVER_TIMEOUT * 0.5 { } else if self.state.get_time() - self.last_ping > SERVER_TIMEOUT * 0.5 {
// Try pinging the server if the timeout is nearing // Try pinging the server if the timeout is nearing
self.postbox.send(ClientMsg::Ping); self.postbox.send_message(ClientMsg::Ping);
} }
Ok(frontend_events) Ok(frontend_events)
@ -227,6 +247,6 @@ impl Client {
impl Drop for Client { impl Drop for Client {
fn drop(&mut self) { fn drop(&mut self) {
self.postbox.send(ClientMsg::Disconnect); self.postbox.send_message(ClientMsg::Disconnect);
} }
} }

View File

@ -1,10 +1,12 @@
[package] [package]
name = "veloren-common" name = "veloren-common"
version = "0.1.0" version = "0.2.0"
authors = ["Joshua Barretto <joshua.s.barretto@gmail.com>", "Maciej Ćwięka <mckol363@gmail.com>"] authors = ["Joshua Barretto <joshua.s.barretto@gmail.com>", "Maciej Ćwięka <mckol363@gmail.com>"]
edition = "2018" edition = "2018"
[dependencies] [dependencies]
sphynx = { git = "https://gitlab.com/veloren/sphynx.git", features = ["serde1"] }
specs = { version = "0.14", features = ["serde"] } specs = { version = "0.14", features = ["serde"] }
shred = { version = "0.7", features = ["nightly"] } shred = { version = "0.7", features = ["nightly"] }
vek = { version = "0.9", features = ["serde"] } vek = { version = "0.9", features = ["serde"] }
@ -16,4 +18,3 @@ serde = "1.0"
serde_derive = "1.0" serde_derive = "1.0"
bincode = "1.0" bincode = "1.0"
log = "0.4" log = "0.4"
pretty_env_logger = "0.3"

View File

@ -1,9 +1,8 @@
// Library use specs::{Component, VecStorage, FlaggedStorage};
use specs::{Component, VecStorage};
use vek::*; use vek::*;
#[derive(Copy, Clone, Debug, Serialize, Deserialize)] #[derive(Copy, Clone, Debug, Serialize, Deserialize)]
enum Race { pub enum Race {
Danari, Danari,
Dwarf, Dwarf,
Elf, Elf,
@ -13,7 +12,7 @@ enum Race {
} }
#[derive(Copy, Clone, Debug, Serialize, Deserialize)] #[derive(Copy, Clone, Debug, Serialize, Deserialize)]
pub struct Gender { pub enum Gender {
Female, Female,
Male, Male,
Unspecified, Unspecified,
@ -30,6 +29,21 @@ pub struct Character {
feet: (), feet: (),
} }
impl Component for Character { impl Character {
type Storage = VecStorage<Self>; // TODO: Remove this
pub fn test() -> Self {
Self {
race: Race::Human,
gender: Gender::Unspecified,
head: (),
chest: (),
belt: (),
arms: (),
feet: (),
}
}
}
impl Component for Character {
type Storage = FlaggedStorage<Self, VecStorage<Self>>;
} }

View File

@ -1,20 +1,7 @@
pub mod character;
pub mod player;
pub mod phys; pub mod phys;
pub mod uid;
pub mod util;
// Reexports // Reexports
pub use uid::{Uid, UidAllocator}; pub use character::Character;
pub use player::Player;
use specs::World as EcsWorld;
pub fn register_local_components(ecs_world: &mut EcsWorld) {
ecs_world.register::<Uid>();
ecs_world.add_resource(UidAllocator::new());
ecs_world.register::<util::New>();
ecs_world.register::<phys::Pos>();
ecs_world.register::<phys::Vel>();
ecs_world.register::<phys::Dir>();
ecs_world.register::<phys::UpdateKind>();
}

View File

@ -1,5 +1,4 @@
// Library use specs::{Component, VecStorage, FlaggedStorage};
use specs::{Component, VecStorage};
use vek::*; use vek::*;
// Pos // Pos
@ -8,7 +7,7 @@ use vek::*;
pub struct Pos(pub Vec3<f32>); pub struct Pos(pub Vec3<f32>);
impl Component for Pos { impl Component for Pos {
type Storage = VecStorage<Self>; type Storage = FlaggedStorage<Self, VecStorage<Self>>;
} }
// Vel // Vel
@ -17,7 +16,7 @@ impl Component for Pos {
pub struct Vel(pub Vec3<f32>); pub struct Vel(pub Vec3<f32>);
impl Component for Vel { impl Component for Vel {
type Storage = VecStorage<Self>; type Storage = FlaggedStorage<Self, VecStorage<Self>>;
} }
// Dir // Dir
@ -26,17 +25,5 @@ impl Component for Vel {
pub struct Dir(pub Vec3<f32>); pub struct Dir(pub Vec3<f32>);
impl Component for Dir { impl Component for Dir {
type Storage = VecStorage<Self>; type Storage = FlaggedStorage<Self, VecStorage<Self>>;
}
// UpdateKind
#[derive(Copy, Clone, Debug)]
pub enum UpdateKind {
Passive,
Force,
}
impl Component for UpdateKind {
type Storage = VecStorage<Self>;
} }

18
common/src/comp/player.rs Normal file
View File

@ -0,0 +1,18 @@
use specs::{Component, VecStorage, FlaggedStorage};
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct Player {
pub alias: String,
}
impl Player {
pub fn new(alias: String) -> Self {
Self {
alias,
}
}
}
impl Component for Player {
type Storage = FlaggedStorage<Self, VecStorage<Self>>;
}

View File

@ -1,84 +0,0 @@
use std::{
collections::HashMap,
ops::Range,
u64,
fmt,
};
use specs::{
saveload::{Marker, MarkerAllocator},
world::EntitiesRes,
Component,
VecStorage,
Entity,
Join,
ReadStorage,
};
#[derive(Copy, Clone, Debug, Eq, Hash, PartialEq, Serialize, Deserialize)]
pub struct Uid(pub u64);
impl Into<u64> for Uid {
fn into(self) -> u64 {
self.0
}
}
impl fmt::Display for Uid {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}", self.0)
}
}
impl Component for Uid {
type Storage = VecStorage<Self>;
}
impl Marker for Uid {
type Identifier = u64;
type Allocator = UidAllocator;
fn id(&self) -> u64 { self.0 }
fn update(&mut self, update: Self) {
assert_eq!(self.0, update.0);
}
}
pub struct UidAllocator {
pub(crate) range: Range<u64>,
pub(crate) mapping: HashMap<u64, Entity>,
}
impl UidAllocator {
pub fn new() -> Self {
Self {
range: 0..u64::MAX,
mapping: HashMap::new(),
}
}
}
impl MarkerAllocator<Uid> for UidAllocator {
fn allocate(&mut self, entity: Entity, id: Option<u64>) -> Uid {
let id = id.unwrap_or_else(|| {
self.range.next().expect("
Id range must be effectively endless.
Somehow, you ran this program for longer than the lifetime of the universe.
It's probably time to stop playing and prepare for your imminent extinction.
")
});
self.mapping.insert(id, entity);
Uid(id)
}
fn retrieve_entity_internal(&self, id: u64) -> Option<Entity> {
self.mapping.get(&id).cloned()
}
fn maintain(&mut self, entities: &EntitiesRes, storage: &ReadStorage<Uid>) {
self.mapping = (&*entities, storage)
.join()
.map(|(e, m)| (m.id(), e))
.collect();
}
}

View File

@ -1,12 +0,0 @@
// Library
use specs::{Component, NullStorage};
use vek::*;
// Pos
#[derive(Copy, Clone, Debug, Default)]
pub struct New;
impl Component for New {
type Storage = NullStorage<Self>;
}

View File

@ -1,4 +1,4 @@
#![feature(euclidean_division, duration_float, try_from, trait_alias)] #![feature(euclidean_division, duration_float, trait_alias, bind_by_move_pattern_guards)]
#[macro_use] #[macro_use]
extern crate serde_derive; extern crate serde_derive;

View File

@ -1,17 +1,22 @@
use crate::comp::{ use vek::*;
Uid, use crate::comp;
phys,
};
#[derive(Clone, Debug, Serialize, Deserialize)] #[derive(Clone, Serialize, Deserialize)]
pub enum ClientMsg { pub enum ClientMsg {
Connect {
player: comp::Player,
character: Option<comp::Character>,
},
Ping, Ping,
Pong, Pong,
Chat(String), Chat(String),
PlayerPhysics { PlayerPhysics {
pos: phys::Pos, pos: comp::phys::Pos,
vel: phys::Vel, vel: comp::phys::Vel,
dir: phys::Dir, dir: comp::phys::Dir,
},
TerrainChunkRequest {
key: Vec3<i32>,
}, },
Disconnect, Disconnect,
} }

View File

@ -0,0 +1,29 @@
use std::marker::PhantomData;
use serde_derive::{Serialize, Deserialize};
use crate::comp;
// Automatically derive From<T> for Packet for each variant Packet::T(T)
sphynx::sum_type! {
#[derive(Clone, Debug, Serialize, Deserialize)]
pub enum EcsPacket {
Pos(comp::phys::Pos),
Vel(comp::phys::Vel),
Dir(comp::phys::Dir),
Character(comp::Character),
Player(comp::Player),
}
}
// Automatically derive From<T> for Phantom for each variant Phantom::T(PhantomData<T>)
sphynx::sum_type! {
#[derive(Clone, Debug, Serialize, Deserialize)]
pub enum EcsPhantom {
Pos(PhantomData<comp::phys::Pos>),
Vel(PhantomData<comp::phys::Vel>),
Dir(PhantomData<comp::phys::Dir>),
Character(PhantomData<comp::Character>),
Player(PhantomData<comp::Player>),
}
}
impl sphynx::Packet for EcsPacket {
type Phantom = EcsPhantom;
}

View File

@ -1,6 +1,8 @@
pub mod ecs_packet;
pub mod server; pub mod server;
pub mod client; pub mod client;
// Reexports // Reexports
pub use server::ServerMsg; pub use self::server::ServerMsg;
pub use client::ClientMsg; pub use self::client::ClientMsg;
pub use self::ecs_packet::EcsPacket;

View File

@ -1,20 +1,21 @@
use crate::comp::{ use vek::*;
Uid, use crate::terrain::TerrainChunk;
phys, use super::EcsPacket;
};
#[derive(Clone, Debug, Serialize, Deserialize)] #[derive(Clone, Serialize, Deserialize)]
pub enum ServerMsg { pub enum ServerMsg {
Handshake {
ecs_state: sphynx::StatePackage<EcsPacket>,
player_entity: u64,
},
Shutdown, Shutdown,
Ping, Ping,
Pong, Pong,
Chat(String), Chat(String),
SetPlayerEntity(Uid), SetPlayerEntity(u64),
EntityPhysics { EcsSync(sphynx::SyncPackage<EcsPacket>),
uid: Uid, TerrainChunkUpdate {
pos: phys::Pos, key: Vec3<i32>,
vel: phys::Vel, chunk: Box<TerrainChunk>,
dir: phys::Dir,
}, },
EntityDeleted(Uid),
} }

View File

@ -1,5 +1,8 @@
pub mod data; pub mod data;
pub mod post; //pub mod post;
pub mod post2;
pub use post2 as post;
// Reexports // Reexports
pub use self::{ pub use self::{

View File

@ -23,6 +23,7 @@ use mio_extras::channel::{
Sender, Sender,
}; };
use bincode; use bincode;
use middleman::Middleman;
#[derive(Clone, Debug, PartialEq)] #[derive(Clone, Debug, PartialEq)]
pub enum Error { pub enum Error {
@ -56,14 +57,15 @@ impl<T> From<mio_extras::channel::SendError<T>> for Error {
} }
} }
pub trait PostSend = 'static + serde::Serialize + Send + fmt::Debug; pub trait PostSend = 'static + serde::Serialize + Send + middleman::Message;
pub trait PostRecv = 'static + serde::de::DeserializeOwned + Send + fmt::Debug; pub trait PostRecv = 'static + serde::de::DeserializeOwned + Send + middleman::Message;
const TCP_TOK: Token = Token(0); const TCP_TOK: Token = Token(0);
const CTRL_TOK: Token = Token(1); const CTRL_TOK: Token = Token(1);
const POSTBOX_TOK: Token = Token(2); const POSTBOX_TOK: Token = Token(2);
const SEND_TOK: Token = Token(3); const SEND_TOK: Token = Token(3);
const RECV_TOK: Token = Token(4); const RECV_TOK: Token = Token(4);
const MIDDLEMAN_TOK: Token = Token(5);
const MAX_MSG_BYTES: usize = 1 << 20; const MAX_MSG_BYTES: usize = 1 << 20;
@ -218,7 +220,7 @@ impl<S: PostSend, R: PostRecv> PostBox<S, R> {
let (recv_tx, recv_rx) = channel(); let (recv_tx, recv_rx) = channel();
let worker_poll = Poll::new()?; let worker_poll = Poll::new()?;
worker_poll.register(&tcp_stream, TCP_TOK, Ready::readable(), PollOpt::edge())?; worker_poll.register(&tcp_stream, TCP_TOK, Ready::readable() | Ready::writable(), PollOpt::edge())?;
worker_poll.register(&ctrl_rx, CTRL_TOK, Ready::readable(), PollOpt::edge())?; worker_poll.register(&ctrl_rx, CTRL_TOK, Ready::readable(), PollOpt::edge())?;
worker_poll.register(&send_rx, SEND_TOK, Ready::readable(), PollOpt::edge())?; worker_poll.register(&send_rx, SEND_TOK, Ready::readable(), PollOpt::edge())?;
@ -251,6 +253,45 @@ impl<S: PostSend, R: PostRecv> PostBox<S, R> {
let _ = self.send_tx.send(data); let _ = self.send_tx.send(data);
} }
// TODO: This method is super messy
pub fn next_message(&mut self) -> Option<R> {
if self.err.is_some() {
return None;
}
loop {
let mut events = Events::with_capacity(10);
if let Err(err) = self.poll.poll(
&mut events,
Some(Duration::new(0, 0)),
) {
self.err = Some(err.into());
return None;
}
for event in events {
match event.token() {
// Keep reading new messages from the channel
RECV_TOK => loop {
match self.recv_rx.try_recv() {
Ok(Ok(msg)) => return Some(msg),
Err(TryRecvError::Empty) => break,
Err(err) => {
self.err = Some(err.into());
return None;
},
Ok(Err(err)) => {
self.err = Some(err);
return None;
},
}
},
tok => panic!("Unexpected event token '{:?}'", tok),
}
}
}
}
pub fn new_messages(&mut self) -> impl ExactSizeIterator<Item=R> { pub fn new_messages(&mut self) -> impl ExactSizeIterator<Item=R> {
let mut msgs = VecDeque::new(); let mut msgs = VecDeque::new();
@ -306,12 +347,38 @@ fn postbox_worker<S: PostSend, R: PostRecv>(
send_rx: Receiver<S>, send_rx: Receiver<S>,
recv_tx: Sender<Result<R, Error>>, recv_tx: Sender<Result<R, Error>>,
) -> Result<(), Error> { ) -> Result<(), Error> {
fn try_tcp_send(tcp_stream: &mut TcpStream, chunks: &mut VecDeque<Vec<u8>>) -> Result<(), Error> {
loop {
let chunk = match chunks.pop_front() {
Some(chunk) => chunk,
None => break,
};
match tcp_stream.write_all(&chunk) {
Ok(()) => {},
Err(err) if err.kind() == io::ErrorKind::WouldBlock => {
chunks.push_front(chunk);
break;
},
Err(err) => {
println!("Error: {:?}", err);
return Err(err.into());
},
}
}
Ok(())
}
enum RecvState { enum RecvState {
ReadHead(Vec<u8>), ReadHead(Vec<u8>),
ReadBody(usize, Vec<u8>), ReadBody(usize, Vec<u8>),
} }
let mut recv_state = RecvState::ReadHead(Vec::with_capacity(8)); let mut recv_state = RecvState::ReadHead(Vec::new());
let mut chunks = VecDeque::new();
//let mut recv_state = RecvState::ReadHead(Vec::with_capacity(8));
let mut events = Events::with_capacity(64); let mut events = Events::with_capacity(64);
'work: loop { 'work: loop {
@ -344,18 +411,23 @@ fn postbox_worker<S: PostSend, R: PostRecv>(
}, },
}; };
let mut packet = msg_bytes let mut bytes = msg_bytes
.len() .len()
.to_le_bytes() .to_le_bytes()
.as_ref() .as_ref()
.to_vec(); .to_vec();
packet.append(&mut msg_bytes); bytes.append(&mut msg_bytes);
match tcp_stream.write_all(&packet) { bytes
Ok(()) => {}, .chunks(1024)
.map(|chunk| chunk.to_vec())
.for_each(|chunk| chunks.push_back(chunk));
match try_tcp_send(&mut tcp_stream, &mut chunks) {
Ok(_) => {},
Err(err) => { Err(err) => {
recv_tx.send(Err(err.into()))?; recv_tx.send(Err(err.into()))?;
break 'work; return Err(Error::Network);
}, },
} }
}, },
@ -363,61 +435,75 @@ fn postbox_worker<S: PostSend, R: PostRecv>(
Err(err) => Err(err)?, Err(err) => Err(err)?,
} }
}, },
TCP_TOK => loop { TCP_TOK => {
match tcp_stream.take_error() { loop {
Ok(None) => {}, // Check TCP error
Ok(Some(err)) => { match tcp_stream.take_error() {
recv_tx.send(Err(err.into()))?; Ok(None) => {},
break 'work; Ok(Some(err)) => {
}, recv_tx.send(Err(err.into()))?;
break 'work;
},
Err(err) => {
recv_tx.send(Err(err.into()))?;
break 'work;
},
}
match &mut recv_state {
RecvState::ReadHead(head) => if head.len() == 8 {
let len = usize::from_le_bytes(<[u8; 8]>::try_from(head.as_slice()).unwrap());
if len > MAX_MSG_BYTES {
println!("TOO BIG! {:x}", len);
recv_tx.send(Err(Error::InvalidMsg))?;
break 'work;
} else if len == 0 {
recv_state = RecvState::ReadHead(Vec::with_capacity(8));
break;
} else {
recv_state = RecvState::ReadBody(
len,
Vec::new(),
);
}
} else {
let mut b = [0; 1];
match tcp_stream.read(&mut b) {
Ok(0) => {},
Ok(_) => head.push(b[0]),
Err(_) => break,
}
},
RecvState::ReadBody(len, body) => if body.len() == *len {
match bincode::deserialize(&body) {
Ok(msg) => {
recv_tx.send(Ok(msg))?;
recv_state = RecvState::ReadHead(Vec::with_capacity(8));
},
Err(err) => {
recv_tx.send(Err((*err).into()))?;
break 'work;
},
}
} else {
let left = *len - body.len();
let mut buf = vec![0; left];
match tcp_stream.read(&mut buf) {
Ok(_) => body.append(&mut buf),
Err(err) => {
recv_tx.send(Err(err.into()))?;
break 'work;
},
}
},
}
}
// Now, try sending TCP stuff
match try_tcp_send(&mut tcp_stream, &mut chunks) {
Ok(_) => {},
Err(err) => { Err(err) => {
recv_tx.send(Err(err.into()))?; recv_tx.send(Err(err.into()))?;
break 'work; return Err(Error::Network);
},
}
match &mut recv_state {
RecvState::ReadHead(head) => if head.len() == 8 {
let len = usize::from_le_bytes(<[u8; 8]>::try_from(head.as_slice()).unwrap());
if len > MAX_MSG_BYTES {
recv_tx.send(Err(Error::InvalidMsg))?;
break 'work;
} else if len == 0 {
recv_state = RecvState::ReadHead(Vec::with_capacity(8));
break;
} else {
recv_state = RecvState::ReadBody(
len,
Vec::new(),
);
}
} else {
let mut b = [0; 1];
match tcp_stream.read(&mut b) {
Ok(_) => head.push(b[0]),
Err(_) => break,
}
},
RecvState::ReadBody(len, body) => if body.len() == *len {
match bincode::deserialize(&body) {
Ok(msg) => {
recv_tx.send(Ok(msg))?;
recv_state = RecvState::ReadHead(Vec::with_capacity(8));
},
Err(err) => {
recv_tx.send(Err((*err).into()))?;
break 'work;
},
}
} else {
let left = *len - body.len();
let mut buf = vec![0; left];
match tcp_stream.read(&mut buf) {
Ok(_) => body.append(&mut buf),
Err(err) => {
recv_tx.send(Err(err.into()))?;
break 'work;
},
}
}, },
} }
}, },
@ -426,24 +512,28 @@ fn postbox_worker<S: PostSend, R: PostRecv>(
} }
} }
tcp_stream.shutdown(Shutdown::Both)?; //tcp_stream.shutdown(Shutdown::Both)?;
Ok(()) Ok(())
} }
// TESTS // TESTS
/*
#[derive(Serialize, Deserialize)]
struct TestMsg<T>(T);
#[test] #[test]
fn connect() { fn connect() {
let srv_addr = ([127, 0, 0, 1], 12345); let srv_addr = ([127, 0, 0, 1], 12345);
let mut postoffice = PostOffice::<u32, f32>::bind(srv_addr).unwrap(); let mut postoffice = PostOffice::<TestMsg<u32>, TestMsg<f32>>::bind(srv_addr).unwrap();
// We should start off with 0 incoming connections // We should start off with 0 incoming connections
thread::sleep(Duration::from_millis(250)); thread::sleep(Duration::from_millis(250));
assert_eq!(postoffice.new_connections().len(), 0); assert_eq!(postoffice.new_connections().len(), 0);
assert_eq!(postoffice.error(), None); assert_eq!(postoffice.error(), None);
let postbox = PostBox::<f32, u32>::to_server(srv_addr).unwrap(); let postbox = PostBox::<TestMsg<f32>, TestMsg<u32>>::to_server(srv_addr).unwrap();
// Now a postbox has been created, we should have 1 new // Now a postbox has been created, we should have 1 new
thread::sleep(Duration::from_millis(250)); thread::sleep(Duration::from_millis(250));
@ -457,21 +547,21 @@ fn connect_fail() {
let listen_addr = ([0; 4], 12345); let listen_addr = ([0; 4], 12345);
let connect_addr = ([127, 0, 0, 1], 12212); let connect_addr = ([127, 0, 0, 1], 12212);
let mut postoffice = PostOffice::<u32, f32>::bind(listen_addr).unwrap(); let mut postoffice = PostOffice::<TestMsg<u32>, TestMsg<f32>>::bind(listen_addr).unwrap();
// We should start off with 0 incoming connections // We should start off with 0 incoming connections
thread::sleep(Duration::from_millis(250)); thread::sleep(Duration::from_millis(250));
assert_eq!(postoffice.new_connections().len(), 0); assert_eq!(postoffice.new_connections().len(), 0);
assert_eq!(postoffice.error(), None); assert_eq!(postoffice.error(), None);
assert!(PostBox::<f32, u32>::to_server(connect_addr).is_err()); assert!(PostBox::<TestMsg<f32>, TestMsg<u32>>::to_server(connect_addr).is_err());
} }
#[test] #[test]
fn connection_count() { fn connection_count() {
let srv_addr = ([127, 0, 0, 1], 12346); let srv_addr = ([127, 0, 0, 1], 12346);
let mut postoffice = PostOffice::<u32, f32>::bind(srv_addr).unwrap(); let mut postoffice = PostOffice::<TestMsg<u32>, TestMsg<f32>>::bind(srv_addr).unwrap();
let mut postboxes = Vec::new(); let mut postboxes = Vec::new();
// We should start off with 0 incoming connections // We should start off with 0 incoming connections
@ -480,7 +570,7 @@ fn connection_count() {
assert_eq!(postoffice.error(), None); assert_eq!(postoffice.error(), None);
for _ in 0..5 { for _ in 0..5 {
postboxes.push(PostBox::<f32, u32>::to_server(srv_addr).unwrap()); postboxes.push(PostBox::<TestMsg<f32>, TestMsg<u32>>::to_server(srv_addr).unwrap());
} }
// 5 postboxes created, we should have 5 // 5 postboxes created, we should have 5
@ -494,10 +584,10 @@ fn connection_count() {
fn disconnect() { fn disconnect() {
let srv_addr = ([127, 0, 0, 1], 12347); let srv_addr = ([127, 0, 0, 1], 12347);
let mut postoffice = PostOffice::<u32, f32>::bind(srv_addr).unwrap(); let mut postoffice = PostOffice::<TestMsg<u32>, TestMsg<f32>>::bind(srv_addr).unwrap();
let mut server_postbox = { let mut server_postbox = {
let mut client_postbox = PostBox::<f32, u32>::to_server(srv_addr).unwrap(); let mut client_postbox = PostBox::<TestMsg<f32>, TestMsg<u32>>::to_server(srv_addr).unwrap();
thread::sleep(Duration::from_millis(250)); thread::sleep(Duration::from_millis(250));
let mut incoming = postoffice.new_connections(); let mut incoming = postoffice.new_connections();
@ -519,52 +609,53 @@ fn disconnect() {
fn client_to_server() { fn client_to_server() {
let srv_addr = ([127, 0, 0, 1], 12348); let srv_addr = ([127, 0, 0, 1], 12348);
let mut po = PostOffice::<u32, f32>::bind(srv_addr).unwrap(); let mut po = PostOffice::<TestMsg<u32>, TestMsg<f32>>::bind(srv_addr).unwrap();
let mut client_pb = PostBox::<f32, u32>::to_server(srv_addr).unwrap(); let mut client_pb = PostBox::<TestMsg<f32>, TestMsg<u32>>::to_server(srv_addr).unwrap();
thread::sleep(Duration::from_millis(250)); thread::sleep(Duration::from_millis(250));
let mut server_pb = po.new_connections().next().unwrap(); let mut server_pb = po.new_connections().next().unwrap();
client_pb.send(1337.0); client_pb.send(TestMsg(1337.0));
client_pb.send(9821.0); client_pb.send(TestMsg(9821.0));
client_pb.send(-3.2); client_pb.send(TestMsg(-3.2));
client_pb.send(17.0); client_pb.send(TestMsg(17.0));
thread::sleep(Duration::from_millis(250)); thread::sleep(Duration::from_millis(250));
let mut incoming_msgs = server_pb.new_messages(); let mut incoming_msgs = server_pb.new_messages();
assert_eq!(incoming_msgs.len(), 4); assert_eq!(incoming_msgs.len(), 4);
assert_eq!(incoming_msgs.next().unwrap(), 1337.0); assert_eq!(incoming_msgs.next().unwrap(), TestMsg(1337.0));
assert_eq!(incoming_msgs.next().unwrap(), 9821.0); assert_eq!(incoming_msgs.next().unwrap(), TestMsg(9821.0));
assert_eq!(incoming_msgs.next().unwrap(), -3.2); assert_eq!(incoming_msgs.next().unwrap(), TestMsg(-3.2));
assert_eq!(incoming_msgs.next().unwrap(), 17.0); assert_eq!(incoming_msgs.next().unwrap(), TestMsg(17.0));
} }
#[test] #[test]
fn server_to_client() { fn server_to_client() {
let srv_addr = ([127, 0, 0, 1], 12349); let srv_addr = ([127, 0, 0, 1], 12349);
let mut po = PostOffice::<u32, f32>::bind(srv_addr).unwrap(); let mut po = PostOffice::<TestMsg<u32>, TestMsg<f32>>::bind(srv_addr).unwrap();
let mut client_pb = PostBox::<f32, u32>::to_server(srv_addr).unwrap(); let mut client_pb = PostBox::<TestMsg<f32>, TestMsg<u32>>::to_server(srv_addr).unwrap();
thread::sleep(Duration::from_millis(250)); thread::sleep(Duration::from_millis(250));
let mut server_pb = po.new_connections().next().unwrap(); let mut server_pb = po.new_connections().next().unwrap();
server_pb.send(1337); server_pb.send(TestMsg(1337));
server_pb.send(9821); server_pb.send(TestMsg(9821));
server_pb.send(39999999); server_pb.send(TestMsg(39999999));
server_pb.send(17); server_pb.send(TestMsg(17));
thread::sleep(Duration::from_millis(250)); thread::sleep(Duration::from_millis(250));
let mut incoming_msgs = client_pb.new_messages(); let mut incoming_msgs = client_pb.new_messages();
assert_eq!(incoming_msgs.len(), 4); assert_eq!(incoming_msgs.len(), 4);
assert_eq!(incoming_msgs.next().unwrap(), 1337); assert_eq!(incoming_msgs.next().unwrap(), TestMsg(1337));
assert_eq!(incoming_msgs.next().unwrap(), 9821); assert_eq!(incoming_msgs.next().unwrap(), TestMsg(9821));
assert_eq!(incoming_msgs.next().unwrap(), 39999999); assert_eq!(incoming_msgs.next().unwrap(), TestMsg(39999999));
assert_eq!(incoming_msgs.next().unwrap(), 17); assert_eq!(incoming_msgs.next().unwrap(), TestMsg(17));
} }
*/

404
common/src/net/post2.rs Normal file
View File

@ -0,0 +1,404 @@
use std::{
io::{self, Read, Write},
net::{TcpListener, TcpStream, SocketAddr, Shutdown},
time::{Instant, Duration},
marker::PhantomData,
sync::{mpsc, Arc, atomic::{AtomicBool, Ordering}},
thread,
collections::VecDeque,
convert::TryFrom,
};
use serde::{Serialize, de::DeserializeOwned};
#[derive(Clone, Debug)]
pub enum Error {
Io, //Io(io::Error),
Bincode, //Bincode(bincode::Error),
ChannelFailure,
InvalidMessage,
}
impl From<io::Error> for Error {
fn from(err: io::Error) -> Self {
Error::Io //(err)
}
}
impl From<bincode::Error> for Error {
fn from(err: bincode::Error) -> Self {
Error::Bincode //(err)
}
}
impl From<mpsc::TryRecvError> for Error {
fn from(error: mpsc::TryRecvError) -> Self {
Error::ChannelFailure
}
}
pub trait PostMsg = Serialize + DeserializeOwned + 'static + Send;
const MAX_MSG_SIZE: usize = 1 << 20;
pub struct PostOffice<S: PostMsg, R: PostMsg> {
listener: TcpListener,
error: Option<Error>,
phantom: PhantomData<(S, R)>,
}
impl<S: PostMsg, R: PostMsg> PostOffice<S, R> {
pub fn bind<A: Into<SocketAddr>>(addr: A) -> Result<Self, Error> {
let mut listener = TcpListener::bind(addr.into())?;
listener.set_nonblocking(true)?;
Ok(Self {
listener,
error: None,
phantom: PhantomData,
})
}
pub fn error(&self) -> Option<Error> {
self.error.clone()
}
pub fn new_postboxes(&mut self) -> impl ExactSizeIterator<Item=PostBox<S, R>> {
let mut new = Vec::new();
if self.error.is_some() {
return new.into_iter();
}
loop {
match self.listener.accept() {
Ok((stream, sock)) => new.push(PostBox::from_stream(stream).unwrap()),
Err(e) if e.kind() == io::ErrorKind::WouldBlock => break,
Err(e) => {
self.error = Some(e.into());
break;
},
}
}
new.into_iter()
}
}
pub struct PostBox<S: PostMsg, R: PostMsg> {
send_tx: mpsc::Sender<S>,
recv_rx: mpsc::Receiver<Result<R, Error>>,
worker: Option<thread::JoinHandle<()>>,
running: Arc<AtomicBool>,
error: Option<Error>,
}
impl<S: PostMsg, R: PostMsg> PostBox<S, R> {
pub fn to<A: Into<SocketAddr>>(addr: A) -> Result<Self, Error> {
Self::from_stream(TcpStream::connect(addr.into())?)
}
fn from_stream(stream: TcpStream) -> Result<Self, Error> {
stream.set_nonblocking(true)?;
let running = Arc::new(AtomicBool::new(true));
let worker_running = running.clone();
let (send_tx, send_rx) = mpsc::channel();
let (recv_tx, recv_rx) = mpsc::channel();
let worker = thread::spawn(move || Self::worker(stream, send_rx, recv_tx, worker_running));
Ok(Self {
send_tx,
recv_rx,
worker: Some(worker),
running,
error: None,
})
}
pub fn error(&self) -> Option<Error> {
self.error.clone()
}
pub fn send_message(&mut self, msg: S) {
let _ = self.send_tx.send(msg);
}
pub fn next_message(&mut self) -> Option<R> {
if self.error.is_some() {
return None;
}
match self.recv_rx.recv().ok()? {
Ok(msg) => Some(msg),
Err(e) => {
self.error = Some(e);
None
},
}
}
pub fn new_messages(&mut self) -> impl ExactSizeIterator<Item=R> {
let mut new = Vec::new();
if self.error.is_some() {
return new.into_iter();
}
loop {
match self.recv_rx.try_recv() {
Ok(Ok(msg)) => new.push(msg),
Err(mpsc::TryRecvError::Empty) => break,
Err(e) => {
self.error = Some(e.into());
break;
},
Ok(Err(e)) => {
self.error = Some(e);
break;
},
}
}
new.into_iter()
}
fn worker(mut stream: TcpStream, send_rx: mpsc::Receiver<S>, recv_tx: mpsc::Sender<Result<R, Error>>, running: Arc<AtomicBool>) {
let mut outgoing_chunks = VecDeque::new();
let mut incoming_buf = Vec::new();
'work: while running.load(Ordering::Relaxed) {
// Get stream errors
match stream.take_error() {
Ok(Some(e)) | Err(e) => {
recv_tx.send(Err(e.into())).unwrap();
break 'work;
},
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;
},
}
}
// Try sending bytes through the TCP stream
for _ in 0..10 {
//println!("HERE! Outgoing len: {}", outgoing_chunks.len());
match outgoing_chunks.pop_front() {
Some(chunk) => match stream.write_all(&chunk) {
Ok(()) => {},
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..10 {
let mut buf = [0; 1024];
match stream.read(&mut buf) {
Ok(n) => incoming_buf.extend_from_slice(&buf[0..n]),
Err(e) if e.kind() == io::ErrorKind::WouldBlock => break,
// Worker error
Err(e) => {
recv_tx.send(Err(e.into())).unwrap();
break 'work;
},
}
}
// Try turning bytes into messages
for _ in 0..10 {
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
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()));
incoming_buf = incoming_buf.split_off(len + 8);
} else {
break;
}
},
None => break,
}
}
thread::sleep(Duration::from_millis(10));
}
stream.shutdown(Shutdown::Both);
}
}
impl<S: PostMsg, R: PostMsg> Drop for PostBox<S, R> {
fn drop(&mut self) {
self.running.store(false, Ordering::Relaxed);
self.worker.take().map(|handle| handle.join());
}
}
#[cfg(test)]
mod tests {
use super::*;
fn create_postoffice<S: PostMsg, R: PostMsg>(id: u16) -> Result<(PostOffice<S, R>, SocketAddr), Error> {
let sock = ([0; 4], 12345 + id).into();
Ok((PostOffice::bind(sock)?, sock))
}
fn loop_for<F: FnMut()>(duration: Duration, mut f: F) {
let start = Instant::now();
while start.elapsed() < duration {
f();
}
}
#[test]
fn connect() {
let (mut postoffice, sock) = create_postoffice::<(), ()>(0).unwrap();
let _client0 = PostBox::<(), ()>::to(sock).unwrap();
let _client1 = PostBox::<(), ()>::to(sock).unwrap();
let _client2 = PostBox::<(), ()>::to(sock).unwrap();
let mut new_clients = 0;
loop_for(Duration::from_millis(250), || {
new_clients += postoffice.new_postboxes().count();
});
assert_eq!(new_clients, 3);
}
/*
#[test]
fn disconnect() {
let (mut postoffice, sock) = create_postoffice::<(), ()>(1).unwrap();
let mut client = PostBox::<i32, ()>::to(sock).unwrap();
loop_for(Duration::from_millis(250), || ());
let mut server = postoffice.new_postboxes().unwrap().next().unwrap();
drop(client);
loop_for(Duration::from_millis(300), || ());
assert!(server.new_messages().is_err());
}
*/
#[test]
fn send_recv() {
let (mut postoffice, sock) = create_postoffice::<(), i32>(2).unwrap();
let test_msgs = vec![1, 1337, 42, -48];
let mut client = PostBox::<i32, ()>::to(sock).unwrap();
loop_for(Duration::from_millis(250), || ());
let mut server = postoffice.new_postboxes().next().unwrap();
for msg in &test_msgs {
client.send_message(msg.clone());
}
let mut recv_msgs = Vec::new();
loop_for(Duration::from_millis(250), || server
.new_messages()
.for_each(|msg| recv_msgs.push(msg)));
assert_eq!(test_msgs, recv_msgs);
}
#[test]
fn send_recv_huge() {
let (mut postoffice, sock) = create_postoffice::<(), Vec<i32>>(3).unwrap();
let test_msgs: Vec<Vec<i32>> = (0..5).map(|i| (0..100000).map(|j| i * 2 + j).collect()).collect();
let mut client = PostBox::<Vec<i32>, ()>::to(sock).unwrap();
loop_for(Duration::from_millis(250), || ());
let mut server = postoffice.new_postboxes().next().unwrap();
for msg in &test_msgs {
client.send_message(msg.clone());
}
let mut recv_msgs = Vec::new();
loop_for(Duration::from_millis(3000), || server
.new_messages()
.for_each(|msg| recv_msgs.push(msg)));
assert_eq!(test_msgs.len(), recv_msgs.len());
assert!(test_msgs == recv_msgs);
}
#[test]
fn send_recv_both() {
let (mut postoffice, sock) = create_postoffice::<u32, u32>(4).unwrap();
let test_msgs = vec![1, 1337, 42, -48];
let mut client = PostBox::<u32, u32>::to(sock).unwrap();
loop_for(Duration::from_millis(250), || ());
let mut server = postoffice.new_postboxes().next().unwrap();
let test_msgs = vec![
(0xDEADBEAD, 0xBEEEEEEF),
(0x1BADB002, 0xBAADF00D),
(0xBAADA555, 0xC0DED00D),
(0xCAFEBABE, 0xDEADC0DE),
];
for (to, from) in test_msgs {
client.send_message(to);
server.send_message(from);
loop_for(Duration::from_millis(250), || ());
assert_eq!(client.new_messages().next().unwrap(), from);
assert_eq!(server.new_messages().next().unwrap(), to);
}
}
}

View File

@ -1,4 +1,7 @@
use std::time::Duration; use std::{
time::Duration,
collections::HashSet,
};
use shred::{Fetch, FetchMut}; use shred::{Fetch, FetchMut};
use specs::{ use specs::{
Builder, Builder,
@ -6,18 +9,22 @@ use specs::{
DispatcherBuilder, DispatcherBuilder,
EntityBuilder as EcsEntityBuilder, EntityBuilder as EcsEntityBuilder,
Entity as EcsEntity, Entity as EcsEntity,
World as EcsWorld,
storage::{ storage::{
Storage as EcsStorage, Storage as EcsStorage,
MaskedStorage as EcsMaskedStorage, MaskedStorage as EcsMaskedStorage,
}, },
saveload::{MarkedBuilder, MarkerAllocator}, saveload::{MarkedBuilder, MarkerAllocator},
}; };
use sphynx;
use vek::*; use vek::*;
use crate::{ use crate::{
comp, comp,
sys, sys,
terrain::TerrainMap, terrain::{
TerrainMap,
TerrainChunk,
},
msg::EcsPacket,
}; };
/// How much faster should an in-game day be compared to a real day? /// How much faster should an in-game day be compared to a real day?
@ -35,17 +42,17 @@ struct Time(f64);
pub struct DeltaTime(pub f64); pub struct DeltaTime(pub f64);
pub struct Changes { pub struct Changes {
pub new_chunks: Vec<Vec3<i32>>, pub new_chunks: HashSet<Vec3<i32>>,
pub changed_chunks: Vec<Vec3<i32>>, pub changed_chunks: HashSet<Vec3<i32>>,
pub removed_chunks: Vec<Vec3<i32>>, pub removed_chunks: HashSet<Vec3<i32>>,
} }
impl Changes { impl Changes {
pub fn default() -> Self { pub fn default() -> Self {
Self { Self {
new_chunks: vec![], new_chunks: HashSet::new(),
changed_chunks: vec![], changed_chunks: HashSet::new(),
removed_chunks: vec![], removed_chunks: HashSet::new(),
} }
} }
@ -59,95 +66,74 @@ impl Changes {
/// A type used to represent game state stored on both the client and the server. This includes /// A type used to represent game state stored on both the client and the server. This includes
/// things like entity components, terrain data, and global state like weather, time of day, etc. /// things like entity components, terrain data, and global state like weather, time of day, etc.
pub struct State { pub struct State {
ecs_world: EcsWorld, ecs: sphynx::World<EcsPacket>,
changes: Changes, changes: Changes,
} }
impl State { impl State {
/// Create a new `State`. /// Create a new `State`.
pub fn new() -> Self { pub fn new() -> Self {
let mut ecs_world = EcsWorld::new();
// Register resources used by the ECS
ecs_world.add_resource(TimeOfDay(0.0));
ecs_world.add_resource(Time(0.0));
ecs_world.add_resource(DeltaTime(0.0));
ecs_world.add_resource(TerrainMap::new());
// Register common components with the state
comp::register_local_components(&mut ecs_world);
Self { Self {
ecs_world, ecs: sphynx::World::new(specs::World::new(), Self::setup_sphynx_world),
changes: Changes::default(), changes: Changes::default(),
} }
} }
/// Create a new `State` from an ECS state package
pub fn from_state_package(state_package: sphynx::StatePackage<EcsPacket>) -> Self {
Self {
ecs: sphynx::World::from_state_package(specs::World::new(), Self::setup_sphynx_world, state_package),
changes: Changes::default(),
}
}
// Create a new Sphynx ECS world
fn setup_sphynx_world(ecs: &mut sphynx::World<EcsPacket>) {
// Register synced components
ecs.register_synced::<comp::phys::Pos>();
ecs.register_synced::<comp::phys::Vel>();
ecs.register_synced::<comp::phys::Dir>();
ecs.register_synced::<comp::Character>();
ecs.register_synced::<comp::Player>();
// Register resources used by the ECS
ecs.internal_mut().add_resource(TimeOfDay(0.0));
ecs.internal_mut().add_resource(Time(0.0));
ecs.internal_mut().add_resource(DeltaTime(0.0));
ecs.internal_mut().add_resource(TerrainMap::new());
}
/// Register a component with the state's ECS /// Register a component with the state's ECS
pub fn with_component<T: Component>(mut self) -> Self pub fn with_component<T: Component>(mut self) -> Self
where <T as Component>::Storage: Default where <T as Component>::Storage: Default
{ {
self.ecs_world.register::<T>(); self.ecs.internal_mut().register::<T>();
self self
} }
/// Build a new entity with a generated UID
pub fn build_uid_entity(&mut self) -> EcsEntityBuilder {
self.ecs_world.create_entity()
.with(comp::util::New)
.marked::<comp::Uid>()
}
/// Build an entity with a specific UID
pub fn build_uid_entity_with_uid(&mut self, uid: comp::Uid) -> EcsEntityBuilder {
let builder = self.build_uid_entity();
builder.world
.write_resource::<comp::UidAllocator>()
.allocate(builder.entity, Some(uid.into()));
builder
}
/// Get an entity from its UID, if it exists
pub fn get_entity(&self, uid: comp::Uid) -> Option<EcsEntity> {
// Find the ECS entity from its UID
self.ecs_world
.read_resource::<comp::UidAllocator>()
.retrieve_entity_internal(uid.into())
}
/// Delete an entity from the state's ECS, if it exists
pub fn delete_entity(&mut self, uid: comp::Uid) {
// Find the ECS entity from its UID
let ecs_entity = self.ecs_world
.read_resource::<comp::UidAllocator>()
.retrieve_entity_internal(uid.into());
// Delete the ECS entity, if it exists
if let Some(ecs_entity) = ecs_entity {
let _ = self.ecs_world.delete_entity(ecs_entity);
}
}
/// Write a component attributed to a particular entity /// Write a component attributed to a particular entity
pub fn write_component<C: Component>(&mut self, entity: EcsEntity, comp: C) { pub fn write_component<C: Component>(&mut self, entity: EcsEntity, comp: C) {
let _ = self.ecs_world.write_storage().insert(entity, comp); let _ = self.ecs.internal_mut().write_storage().insert(entity, comp);
}
/// Read a component attributed to a particular entity
pub fn read_component_cloned<C: Component + Clone>(&self, entity: EcsEntity) -> Option<C> {
self.ecs.internal().read_storage().get(entity).cloned()
} }
/// Get a read-only reference to the storage of a particular component type /// Get a read-only reference to the storage of a particular component type
pub fn read_storage<C: Component>(&self) -> EcsStorage<C, Fetch<EcsMaskedStorage<C>>> { pub fn read_storage<C: Component>(&self) -> EcsStorage<C, Fetch<EcsMaskedStorage<C>>> {
self.ecs_world.read_storage::<C>() self.ecs.internal().read_storage::<C>()
} }
/// Get a reference to the internal ECS world /// Get a reference to the internal ECS world
pub fn ecs_world(&self) -> &EcsWorld { pub fn ecs(&self) -> &sphynx::World<EcsPacket> {
&self.ecs_world &self.ecs
} }
/// Get a mutable reference to the internal ECS world /// Get a mutable reference to the internal ECS world
pub fn ecs_world_mut(&mut self) -> &mut EcsWorld { pub fn ecs_mut(&mut self) -> &mut sphynx::World<EcsPacket> {
&mut self.ecs_world &mut self.ecs
} }
/// Get a reference to the `Changes` structure of the state. This contains /// Get a reference to the `Changes` structure of the state. This contains
@ -156,54 +142,57 @@ impl State {
&self.changes &self.changes
} }
// TODO: Get rid of this since it shouldn't be needed
pub fn changes_mut(&mut self) -> &mut Changes {
&mut self.changes
}
/// Get the current in-game time of day. /// Get the current in-game time of day.
/// ///
/// Note that this should not be used for physics, animations or other such localised timings. /// Note that this should not be used for physics, animations or other such localised timings.
pub fn get_time_of_day(&self) -> f64 { pub fn get_time_of_day(&self) -> f64 {
self.ecs_world.read_resource::<TimeOfDay>().0 self.ecs.internal().read_resource::<TimeOfDay>().0
} }
/// Get the current in-game time. /// Get the current in-game time.
/// ///
/// Note that this does not correspond to the time of day. /// Note that this does not correspond to the time of day.
pub fn get_time(&self) -> f64 { pub fn get_time(&self) -> f64 {
self.ecs_world.read_resource::<Time>().0 self.ecs.internal().read_resource::<Time>().0
} }
/// Get a reference to this state's terrain. /// Get a reference to this state's terrain.
pub fn terrain(&self) -> Fetch<TerrainMap> { pub fn terrain(&self) -> Fetch<TerrainMap> {
self.ecs_world.read_resource::<TerrainMap>() self.ecs
.internal()
.read_resource::<TerrainMap>()
} }
// TODO: Get rid of this since it shouldn't be needed /// Insert the provided chunk into this state's terrain.
pub fn terrain_mut(&mut self) -> FetchMut<TerrainMap> { pub fn insert_chunk(&mut self, key: Vec3<i32>, chunk: TerrainChunk) {
self.ecs_world.write_resource::<TerrainMap>() if self.ecs
.internal_mut()
.write_resource::<TerrainMap>()
.insert(key, chunk)
.is_some()
{
self.changes.changed_chunks.insert(key);
} else {
self.changes.new_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) {
// First, wipe all temporary marker components
self.ecs_world.write_storage::<comp::util::New>().clear();
// Change the time accordingly // Change the time accordingly
self.ecs_world.write_resource::<TimeOfDay>().0 += dt.as_secs_f64() * DAY_CYCLE_FACTOR; self.ecs.internal_mut().write_resource::<TimeOfDay>().0 += dt.as_secs_f64() * DAY_CYCLE_FACTOR;
self.ecs_world.write_resource::<Time>().0 += dt.as_secs_f64(); self.ecs.internal_mut().write_resource::<Time>().0 += dt.as_secs_f64();
// Run systems to update the world // Run systems to update the world
self.ecs_world.write_resource::<DeltaTime>().0 = dt.as_secs_f64(); self.ecs.internal_mut().write_resource::<DeltaTime>().0 = dt.as_secs_f64();
// Create and run dispatcher for ecs systems // Create and run dispatcher for ecs systems
let mut dispatch_builder = DispatcherBuilder::new(); let mut dispatch_builder = DispatcherBuilder::new();
sys::add_local_systems(&mut dispatch_builder); sys::add_local_systems(&mut dispatch_builder);
// This dispatches all the systems in parallel // This dispatches all the systems in parallel
dispatch_builder.build().dispatch(&self.ecs_world.res); dispatch_builder.build().dispatch(&self.ecs.internal_mut().res);
self.ecs_world.maintain(); self.ecs.internal_mut().maintain();
} }
/// Clean up the state after a tick /// Clean up the state after a tick

View File

@ -1,3 +1,6 @@
use serde_derive::{Serialize, Deserialize};
#[derive(Clone, Serialize, Deserialize)]
pub enum BiomeKind { pub enum BiomeKind {
Void, Void,
Grassland, Grassland,

View File

@ -1,10 +1,10 @@
// Library
use vek::*; use vek::*;
use serde_derive::{Serialize, Deserialize};
// Crate // Crate
use crate::vol::Vox; use crate::vol::Vox;
#[derive(Copy, Clone, Debug)] #[derive(Copy, Clone, Debug, Serialize, Deserialize)]
pub struct Block { pub struct Block {
kind: u8, kind: u8,
color: [u8; 3], color: [u8; 3],

View File

@ -7,10 +7,8 @@ pub use self::{
biome::BiomeKind, biome::BiomeKind,
}; };
// Library
use vek::*; use vek::*;
use serde_derive::{Serialize, Deserialize};
// Crate
use crate::{ use crate::{
vol::VolSize, vol::VolSize,
volumes::{ volumes::{
@ -21,6 +19,7 @@ use crate::{
// TerrainChunkSize // TerrainChunkSize
#[derive(Clone, Serialize, Deserialize)]
pub struct TerrainChunkSize; pub struct TerrainChunkSize;
impl VolSize for TerrainChunkSize { impl VolSize for TerrainChunkSize {
@ -29,6 +28,7 @@ impl VolSize for TerrainChunkSize {
// TerrainChunkMeta // TerrainChunkMeta
#[derive(Clone, Serialize, Deserialize)]
pub struct TerrainChunkMeta { pub struct TerrainChunkMeta {
biome: BiomeKind, biome: BiomeKind,
} }

View File

@ -3,6 +3,7 @@ use std::marker::PhantomData;
// Library // Library
use vek::*; use vek::*;
use serde_derive::{Serialize, Deserialize};
// Local // Local
use crate::vol::{ use crate::vol::{
@ -23,6 +24,7 @@ pub enum ChunkErr {
// V = Voxel // V = Voxel
// S = Size (replace when const generics are a thing) // S = Size (replace when const generics are a thing)
// M = Metadata // M = Metadata
#[derive(Clone, Serialize, Deserialize)]
pub struct Chunk<V: Vox, S: VolSize, M> { pub struct Chunk<V: Vox, S: VolSize, M> {
vox: Vec<V>, vox: Vec<V>,
meta: M, meta: M,

View File

@ -126,7 +126,37 @@ impl<V: Vox, S: VolSize, M> VolMap<V, S, M> {
self.chunks.insert(key, chunk) self.chunks.insert(key, chunk)
} }
pub fn remove(&mut self, key: &Vec3<i32>) -> Option<Chunk<V, S, M>> { pub fn get_key(&self, key: Vec3<i32>) -> Option<&Chunk<V, S, M>> {
self.chunks.remove(key) self.chunks.get(&key)
}
pub fn remove(&mut self, key: Vec3<i32>) -> Option<Chunk<V, S, M>> {
self.chunks.remove(&key)
}
pub fn key_pos(&self, key: Vec3<i32>) -> Vec3<i32> {
key * S::SIZE.map(|e| e as i32)
}
pub fn pos_key(&self, pos: Vec3<i32>) -> Vec3<i32> {
Self::chunk_key(pos)
}
pub fn iter<'a>(&'a self) -> ChunkIter<'a, V, S, M> {
ChunkIter {
iter: self.chunks.iter(),
}
}
}
pub struct ChunkIter<'a, V: Vox, S: VolSize, M> {
iter: std::collections::hash_map::Iter<'a, Vec3<i32>, Chunk<V, S, M>>,
}
impl<'a, V: Vox, S: VolSize, M> Iterator for ChunkIter<'a, V, S, M> {
type Item = (Vec3<i32>, &'a Chunk<V, S, M>);
fn next(&mut self) -> Option<Self::Item> {
self.iter.next().map(|(k, c)| (*k, c))
} }
} }

View File

@ -1,6 +1,6 @@
[package] [package]
name = "veloren-server-cli" name = "veloren-server-cli"
version = "0.1.0" version = "0.2.0"
authors = ["Joshua Barretto <joshua.s.barretto@gmail.com>"] authors = ["Joshua Barretto <joshua.s.barretto@gmail.com>"]
edition = "2018" edition = "2018"

View File

@ -24,9 +24,9 @@ fn main() {
for event in events { for event in events {
match event { match event {
Event::ClientConnected { ecs_entity } => info!("Client connected!"), Event::ClientConnected { entity } => info!("Client connected!"),
Event::ClientDisconnected { ecs_entity } => info!("Client disconnected!"), Event::ClientDisconnected { entity } => info!("Client disconnected!"),
Event::Chat { ecs_entity, msg } => info!("[Client] {}", msg), Event::Chat { entity, msg } => info!("[Client] {}", msg),
} }
} }

View File

@ -1,6 +1,6 @@
[package] [package]
name = "veloren-server" name = "veloren-server"
version = "0.1.0" version = "0.2.0"
authors = ["Joshua Barretto <joshua.s.barretto@gmail.com>"] authors = ["Joshua Barretto <joshua.s.barretto@gmail.com>"]
edition = "2018" edition = "2018"
@ -10,3 +10,4 @@ world = { package = "veloren-world", path = "../world" }
specs = "0.14" specs = "0.14"
vek = "0.9" vek = "0.9"
threadpool = "1.7"

View File

@ -1,3 +1,4 @@
use std::collections::HashMap;
use specs::Entity as EcsEntity; use specs::Entity as EcsEntity;
use common::{ use common::{
comp, comp,
@ -6,41 +7,61 @@ use common::{
}; };
use crate::Error; use crate::Error;
#[derive(PartialEq)]
pub enum ClientState {
Connecting,
Connected,
}
pub struct Client { pub struct Client {
pub ecs_entity: EcsEntity, pub state: ClientState,
pub postbox: PostBox<ServerMsg, ClientMsg>, pub postbox: PostBox<ServerMsg, ClientMsg>,
pub last_ping: f64, pub last_ping: f64,
} }
impl Client {
pub fn notify(&mut self, msg: ServerMsg) {
self.postbox.send_message(msg);
}
}
pub struct Clients { pub struct Clients {
clients: Vec<Client>, clients: HashMap<EcsEntity, Client>,
} }
impl Clients { impl Clients {
pub fn empty() -> Self { pub fn empty() -> Self {
Self { Self {
clients: Vec::new(), clients: HashMap::new(),
} }
} }
pub fn add(&mut self, client: Client) { pub fn add(&mut self, entity: EcsEntity, client: Client) {
self.clients.push(client); self.clients.insert(entity, client);
} }
pub fn remove_if<F: FnMut(&mut Client) -> bool>(&mut self, f: F) { pub fn remove_if<F: FnMut(EcsEntity, &mut Client) -> bool>(&mut self, mut f: F) {
self.clients.drain_filter(f); self.clients.retain(|entity, client| !f(*entity, client));
} }
pub fn notify_all(&mut self, msg: ServerMsg) { pub fn notify(&mut self, entity: EcsEntity, msg: ServerMsg) {
for client in &mut self.clients { if let Some(client) = self.clients.get_mut(&entity) {
client.postbox.send(msg.clone()); client.notify(msg);
} }
} }
pub fn notify_all_except(&mut self, ecs_entity: EcsEntity, msg: ServerMsg) { pub fn notify_connected(&mut self, msg: ServerMsg) {
for client in &mut self.clients { for client in self.clients.values_mut() {
if client.ecs_entity != ecs_entity { if client.state == ClientState::Connected {
client.postbox.send(msg.clone()); client.notify(msg.clone());
}
}
}
pub fn notify_connected_except(&mut self, except_entity: EcsEntity, msg: ServerMsg) {
for (entity, client) in self.clients.iter_mut() {
if client.state == ClientState::Connected && *entity != except_entity {
client.notify(msg.clone());
} }
} }
} }

View File

@ -13,6 +13,8 @@ pub use crate::{
use std::{ use std::{
time::Duration, time::Duration,
net::SocketAddr, net::SocketAddr,
sync::mpsc,
collections::HashSet,
}; };
use specs::{ use specs::{
Entity as EcsEntity, Entity as EcsEntity,
@ -22,14 +24,17 @@ use specs::{
saveload::MarkedBuilder, saveload::MarkedBuilder,
}; };
use vek::*; use vek::*;
use threadpool::ThreadPool;
use common::{ use common::{
comp, comp,
state::State, state::State,
net::PostOffice, net::PostOffice,
msg::{ServerMsg, ClientMsg}, msg::{ServerMsg, ClientMsg},
terrain::TerrainChunk,
}; };
use world::World; use world::World;
use crate::client::{ use crate::client::{
ClientState,
Client, Client,
Clients, Clients,
}; };
@ -38,13 +43,13 @@ const CLIENT_TIMEOUT: f64 = 5.0; // Seconds
pub enum Event { pub enum Event {
ClientConnected { ClientConnected {
ecs_entity: EcsEntity, entity: EcsEntity,
}, },
ClientDisconnected { ClientDisconnected {
ecs_entity: EcsEntity, entity: EcsEntity,
}, },
Chat { Chat {
ecs_entity: EcsEntity, entity: EcsEntity,
msg: String, msg: String,
}, },
} }
@ -55,18 +60,32 @@ pub struct Server {
postoffice: PostOffice<ServerMsg, ClientMsg>, postoffice: PostOffice<ServerMsg, ClientMsg>,
clients: Clients, clients: Clients,
thread_pool: ThreadPool,
chunk_tx: mpsc::Sender<(Vec3<i32>, TerrainChunk)>,
chunk_rx: mpsc::Receiver<(Vec3<i32>, TerrainChunk)>,
pending_chunks: HashSet<Vec3<i32>>,
} }
impl Server { impl Server {
/// Create a new `Server`. /// Create a new `Server`.
#[allow(dead_code)] #[allow(dead_code)]
pub fn new() -> Result<Self, Error> { pub fn new() -> Result<Self, Error> {
let (chunk_tx, chunk_rx) = mpsc::channel();
Ok(Self { Ok(Self {
state: State::new(), state: State::new(),
world: World::new(), world: World::new(),
postoffice: PostOffice::bind(SocketAddr::from(([0; 4], 59003)))?, postoffice: PostOffice::bind(SocketAddr::from(([0; 4], 59003)))?,
clients: Clients::empty(), clients: Clients::empty(),
thread_pool: threadpool::Builder::new()
.thread_name("veloren-worker".into())
.build(),
chunk_tx,
chunk_rx,
pending_chunks: HashSet::new(),
}) })
} }
@ -77,15 +96,6 @@ impl Server {
#[allow(dead_code)] #[allow(dead_code)]
pub fn state_mut(&mut self) -> &mut State { &mut self.state } pub fn state_mut(&mut self) -> &mut State { &mut self.state }
/// Build a new player with a generated UID
pub fn build_player(&mut self) -> EcsEntityBuilder {
self.state.build_uid_entity()
.with(comp::phys::Pos(Vec3::zero()))
.with(comp::phys::Vel(Vec3::zero()))
.with(comp::phys::Dir(Vec3::unit_y()))
.with(comp::phys::UpdateKind::Passive)
}
/// Get a reference to the server's world. /// Get a reference to the server's world.
#[allow(dead_code)] #[allow(dead_code)]
pub fn world(&self) -> &World { &self.world } pub fn world(&self) -> &World { &self.world }
@ -127,6 +137,29 @@ impl Server {
// Tick the client's LocalState (step 3) // Tick the client's LocalState (step 3)
self.state.tick(dt); self.state.tick(dt);
// Fetch any generated `TerrainChunk`s and insert them into the terrain
// Also, send the chunk data to anybody that is close by
for (key, chunk) in self.chunk_rx.try_iter() {
// Send the chunk to all nearby players
for (entity, player, pos) in (
&self.state.ecs().internal().entities(),
&self.state.ecs().internal().read_storage::<comp::Player>(),
&self.state.ecs().internal().read_storage::<comp::phys::Pos>(),
).join() {
// TODO: Distance check
// if self.state.terrain().key_pos(key)
/*
self.clients.notify(entity, ServerMsg::TerrainChunkUpdate {
key,
chunk: Box::new(chunk.clone()),
});
*/
}
self.state.insert_chunk(key, chunk);
}
// Synchronise clients with the new state of the world // Synchronise clients with the new state of the world
self.sync_clients(); self.sync_clients();
@ -145,24 +178,20 @@ impl Server {
fn handle_new_connections(&mut self) -> Result<Vec<Event>, Error> { fn handle_new_connections(&mut self) -> Result<Vec<Event>, Error> {
let mut frontend_events = Vec::new(); let mut frontend_events = Vec::new();
for mut postbox in self.postoffice.new_connections() { for mut postbox in self.postoffice.new_postboxes() {
let ecs_entity = self.build_player() let entity = self.state
// When the player is first created, force a physics notification to everyone .ecs_mut()
// including themselves. .create_entity_synced()
.with(comp::phys::UpdateKind::Force)
.build(); .build();
let uid = self.state.read_storage().get(ecs_entity).cloned().unwrap();
postbox.send(ServerMsg::SetPlayerEntity(uid)); self.clients.add(entity, Client {
state: ClientState::Connecting,
self.clients.add(Client {
ecs_entity,
postbox, postbox,
last_ping: self.state.get_time(), last_ping: self.state.get_time(),
}); });
frontend_events.push(Event::ClientConnected { frontend_events.push(Event::ClientConnected {
ecs_entity, entity,
}); });
} }
@ -176,9 +205,10 @@ impl Server {
let state = &mut self.state; let state = &mut self.state;
let mut new_chat_msgs = Vec::new(); let mut new_chat_msgs = Vec::new();
let mut disconnected_clients = Vec::new(); let mut disconnected_clients = Vec::new();
let mut requested_chunks = Vec::new();
self.clients.remove_if(|client| { self.clients.remove_if(|entity, client| {
let mut disconnected = false; let mut disconnect = false;
let new_msgs = client.postbox.new_messages(); let new_msgs = client.postbox.new_messages();
// Update client ping // Update client ping
@ -187,30 +217,66 @@ impl Server {
// Process incoming messages // Process incoming messages
for msg in new_msgs { for msg in new_msgs {
match msg { match client.state {
ClientMsg::Ping => client.postbox.send(ServerMsg::Pong), ClientState::Connecting => match msg {
ClientMsg::Pong => {}, ClientMsg::Connect { player, character } => {
ClientMsg::Chat(msg) => new_chat_msgs.push((client.ecs_entity, msg)),
ClientMsg::PlayerPhysics { pos, vel, dir } => { // Write client components
state.write_component(client.ecs_entity, pos); state.write_component(entity, player);
state.write_component(client.ecs_entity, vel); state.write_component(entity, comp::phys::Pos(Vec3::zero()));
state.write_component(client.ecs_entity, dir); state.write_component(entity, comp::phys::Vel(Vec3::zero()));
state.write_component(entity, comp::phys::Dir(Vec3::unit_y()));
if let Some(character) = character {
state.write_component(entity, character);
}
client.state = ClientState::Connected;
// Return a handshake with the state of the current world
client.notify(ServerMsg::Handshake {
ecs_state: state.ecs().gen_state_package(),
player_entity: state
.ecs()
.uid_from_entity(entity)
.unwrap()
.into(),
});
},
_ => disconnect = true,
},
ClientState::Connected => match msg {
ClientMsg::Connect { .. } => disconnect = true, // Not allowed when already connected
ClientMsg::Disconnect => disconnect = true,
ClientMsg::Ping => client.postbox.send_message(ServerMsg::Pong),
ClientMsg::Pong => {},
ClientMsg::Chat(msg) => new_chat_msgs.push((entity, msg)),
ClientMsg::PlayerPhysics { pos, vel, dir } => {
state.write_component(entity, pos);
state.write_component(entity, vel);
state.write_component(entity, dir);
},
ClientMsg::TerrainChunkRequest { key } => match state.terrain().get_key(key) {
Some(chunk) => {}, /*client.postbox.send_message(ServerMsg::TerrainChunkUpdate {
key,
chunk: Box::new(chunk.clone()),
}),*/
None => requested_chunks.push(key),
},
}, },
ClientMsg::Disconnect => disconnected = true,
} }
} }
} else if } else if
state.get_time() - client.last_ping > CLIENT_TIMEOUT || // Timeout state.get_time() - client.last_ping > CLIENT_TIMEOUT || // Timeout
client.postbox.error().is_some() // Postbox error client.postbox.error().is_some() // Postbox error
{ {
disconnected = true; disconnect = true;
} else if state.get_time() - client.last_ping > CLIENT_TIMEOUT * 0.5 { } else if state.get_time() - client.last_ping > CLIENT_TIMEOUT * 0.5 {
// Try pinging the client if the timeout is nearing // Try pinging the client if the timeout is nearing
client.postbox.send(ServerMsg::Ping); client.postbox.send_message(ServerMsg::Ping);
} }
if disconnected { if disconnect {
disconnected_clients.push(client.ecs_entity); disconnected_clients.push(entity);
true true
} else { } else {
false false
@ -218,24 +284,35 @@ impl Server {
}); });
// Handle new chat messages // Handle new chat messages
for (ecs_entity, msg) in new_chat_msgs { for (entity, msg) in new_chat_msgs {
self.clients.notify_all(ServerMsg::Chat(msg.clone())); self.clients.notify_connected(ServerMsg::Chat(match state
.ecs()
.internal()
.read_storage::<comp::Player>()
.get(entity)
{
Some(player) => format!("[{}] {}", &player.alias, msg),
None => format!("[<anon>] {}", msg),
}));
frontend_events.push(Event::Chat { frontend_events.push(Event::Chat {
ecs_entity, entity,
msg, msg,
}); });
} }
// Handle client disconnects // Handle client disconnects
for ecs_entity in disconnected_clients { for entity in disconnected_clients {
self.clients.notify_all(ServerMsg::EntityDeleted(state.read_storage().get(ecs_entity).cloned().unwrap())); state.ecs_mut().delete_entity_synced(entity);
frontend_events.push(Event::ClientDisconnected { frontend_events.push(Event::ClientDisconnected {
ecs_entity, entity,
}); });
}
state.ecs_world_mut().delete_entity(ecs_entity); // Generate requested chunks
for key in requested_chunks {
self.generate_chunk(key);
} }
Ok(frontend_events) Ok(frontend_events)
@ -243,36 +320,19 @@ impl Server {
/// Sync client states with the most up to date information /// Sync client states with the most up to date information
fn sync_clients(&mut self) { fn sync_clients(&mut self) {
for (entity, &uid, &pos, &vel, &dir, update_kind) in ( self.clients.notify_connected(ServerMsg::EcsSync(self.state.ecs_mut().next_sync_package()));
&self.state.ecs_world().entities(), }
&self.state.ecs_world().read_storage::<comp::Uid>(),
&self.state.ecs_world().read_storage::<comp::phys::Pos>(),
&self.state.ecs_world().read_storage::<comp::phys::Vel>(),
&self.state.ecs_world().read_storage::<comp::phys::Dir>(),
&mut self.state.ecs_world().write_storage::<comp::phys::UpdateKind>(),
).join() {
let msg = ServerMsg::EntityPhysics {
uid,
pos,
vel,
dir,
};
// Sometimes we need to force updated (i.e: teleporting players). This involves sending pub fn generate_chunk(&mut self, key: Vec3<i32>) {
// everyone, including the player themselves, of their new physics information. if self.pending_chunks.insert(key) {
match update_kind { let chunk_tx = self.chunk_tx.clone();
comp::phys::UpdateKind::Force => self.clients.notify_all(msg), self.thread_pool.execute(move || chunk_tx.send((key, World::generate_chunk(key))).unwrap());
comp::phys::UpdateKind::Passive => self.clients.notify_all_except(entity, msg),
}
// Now that the update has occured, default to a passive update
*update_kind = comp::phys::UpdateKind::Passive;
} }
} }
} }
impl Drop for Server { impl Drop for Server {
fn drop(&mut self) { fn drop(&mut self) {
self.clients.notify_all(ServerMsg::Shutdown); self.clients.notify_connected(ServerMsg::Shutdown);
} }
} }

View File

@ -1,6 +1,6 @@
[package] [package]
name = "voxygen" name = "veloren-voxygen"
version = "0.1.0" version = "0.2.0"
authors = ["Joshua Barretto <joshua.s.barretto@gmail.com>"] authors = ["Joshua Barretto <joshua.s.barretto@gmail.com>"]
edition = "2018" edition = "2018"

View File

@ -24,27 +24,28 @@ impl Animation for RunAnimation {
let wave_slow = (time as f32 * 6.0 + PI).sin(); let wave_slow = (time as f32 * 6.0 + PI).sin();
let wave_dip = (wave_slow.abs() - 0.5).abs(); let wave_dip = (wave_slow.abs() - 0.5).abs();
skeleton.head.offset = Vec3::unit_z() * 13.0; skeleton.head.offset = Vec3::unit_z() * 13.0 / 11.0;
skeleton.head.ori = Quaternion::rotation_z(wave * 0.3); skeleton.head.ori = Quaternion::rotation_z(wave * 0.3);
skeleton.chest.offset = Vec3::unit_z() * 9.0; skeleton.chest.offset = Vec3::unit_z() * 9.0 / 11.0;
skeleton.chest.ori = Quaternion::rotation_z(wave * 0.3); skeleton.chest.ori = Quaternion::rotation_z(wave * 0.3);
skeleton.belt.offset = Vec3::unit_z() * 7.0; skeleton.belt.offset = Vec3::unit_z() * 7.0 / 11.0;
skeleton.belt.ori = Quaternion::rotation_z(wave * 0.2); skeleton.belt.ori = Quaternion::rotation_z(wave * 0.2);
skeleton.shorts.offset = Vec3::unit_z() * 4.0; skeleton.shorts.offset = Vec3::unit_z() * 4.0 / 11.0;
skeleton.shorts.ori = Quaternion::rotation_z(wave * 0.1); skeleton.shorts.ori = Quaternion::rotation_z(wave * 0.1);
skeleton.l_hand.offset = Vec3::new(-6.0 - wave_dip * 6.0, wave * 5.0, 11.0 - wave_dip * 6.0); skeleton.l_hand.offset = Vec3::new(-6.0 - wave_dip * 6.0, wave * 5.0, 11.0 - wave_dip * 6.0) / 11.0;
skeleton.r_hand.offset = Vec3::new(6.0 + wave_dip * 6.0, -wave * 5.0, 11.0 - wave_dip * 6.0); skeleton.r_hand.offset = Vec3::new(6.0 + wave_dip * 6.0, -wave * 5.0, 11.0 - wave_dip * 6.0) / 11.0;
skeleton.l_foot.offset = Vec3::new(-3.5, 1.0 - wave * 8.0, 3.5 - wave_dip * 4.0); skeleton.l_foot.offset = Vec3::new(-3.5, 1.0 - wave * 8.0, 3.5 - wave_dip * 4.0) / 11.0;
skeleton.l_foot.ori = Quaternion::rotation_x(-wave + 1.0); skeleton.l_foot.ori = Quaternion::rotation_x(-wave + 1.0);
skeleton.r_foot.offset = Vec3::new(3.5, 1.0 + wave * 8.0, 3.5 - wave_dip * 4.0); skeleton.r_foot.offset = Vec3::new(3.5, 1.0 + wave * 8.0, 3.5 - wave_dip * 4.0) / 11.0;
skeleton.r_foot.ori = Quaternion::rotation_x(wave + 1.0); skeleton.r_foot.ori = Quaternion::rotation_x(wave + 1.0);
skeleton.back.offset = Vec3::new(-9.0, 5.0, 18.0); skeleton.back.offset = Vec3::new(-9.0, 5.0, 18.0);
skeleton.back.ori = Quaternion::rotation_y(2.5); skeleton.back.ori = Quaternion::rotation_y(2.5);
skeleton.back.scale = Vec3::one();
} }
} }

View File

@ -10,6 +10,7 @@ use crate::render::FigureBoneData;
pub struct Bone { pub struct Bone {
pub offset: Vec3<f32>, pub offset: Vec3<f32>,
pub ori: Quaternion<f32>, pub ori: Quaternion<f32>,
pub scale: Vec3<f32>,
} }
impl Bone { impl Bone {
@ -17,15 +18,16 @@ impl Bone {
Self { Self {
offset: Vec3::zero(), offset: Vec3::zero(),
ori: Quaternion::identity(), ori: Quaternion::identity(),
scale: Vec3::broadcast(1.0 / 11.0),
} }
} }
pub fn compute_base_matrix(&self) -> Mat4<f32> { pub fn compute_base_matrix(&self) -> Mat4<f32> {
Mat4::<f32>::translation_3d(self.offset) * Mat4::from(self.ori) Mat4::<f32>::translation_3d(self.offset) * Mat4::scaling_3d(self.scale) * Mat4::from(self.ori)
} }
} }
pub trait Skeleton { pub trait Skeleton: Send + Sync + 'static {
fn compute_matrices(&self) -> [FigureBoneData; 16]; fn compute_matrices(&self) -> [FigureBoneData; 16];
} }

View File

@ -6,7 +6,10 @@ use crate::{
GlobalState, PlayState, PlayStateResult, GlobalState, PlayState, PlayStateResult,
}; };
use client::{self, Client}; use client::{self, Client};
use common::clock::Clock; use common::{
comp,
clock::Clock,
};
use std::time::Duration; use std::time::Duration;
use ui::{Event as MainMenuEvent, MainMenuUi}; use ui::{Event as MainMenuEvent, MainMenuUi};
use vek::*; use vek::*;
@ -55,7 +58,7 @@ impl PlayState for MainMenuState {
global_state.window.renderer_mut().clear(BG_COLOR); global_state.window.renderer_mut().clear(BG_COLOR);
// Maintain the UI // Maintain the UI (TODO: Maybe clean this up a little to avoid rightward drift?)
for event in self.main_menu_ui.maintain(global_state.window.renderer_mut()) { for event in self.main_menu_ui.maintain(global_state.window.renderer_mut()) {
match event { match event {
MainMenuEvent::LoginAttempt{ username, server_address } => { MainMenuEvent::LoginAttempt{ username, server_address } => {
@ -67,12 +70,12 @@ impl PlayState for MainMenuState {
Ok(mut socket_adders) => { Ok(mut socket_adders) => {
while let Some(socket_addr) = socket_adders.next() { while let Some(socket_addr) = socket_adders.next() {
// TODO: handle error // TODO: handle error
match Client::new(socket_addr) { match Client::new(socket_addr, comp::Player::new(username.clone()), Some(comp::Character::test()), 300) {
Ok(client) => { Ok(client) => {
return PlayStateResult::Push( return PlayStateResult::Push(
Box::new(CharSelectionState::new( Box::new(CharSelectionState::new(
&mut global_state.window, &mut global_state.window,
std::rc::Rc::new(std::cell::RefCell::new(client.with_test_state())) // <--- TODO: Remove this std::rc::Rc::new(std::cell::RefCell::new(client)) // <--- TODO: Remove this
)) ))
); );
} }

View File

@ -1,5 +1,14 @@
use specs::{Component, VecStorage}; use std::{
collections::HashMap,
f32,
};
use specs::{Entity as EcsEntity, Component, VecStorage, Join};
use vek::*; use vek::*;
use client::Client;
use common::{
comp,
figure::Segment,
};
use crate::{ use crate::{
Error, Error,
render::{ render::{
@ -12,42 +21,50 @@ use crate::{
FigureBoneData, FigureBoneData,
FigureLocals, FigureLocals,
}, },
anim::Skeleton, anim::{
Animation,
Skeleton,
character::{
CharacterSkeleton,
RunAnimation,
},
},
mesh::Meshable,
}; };
pub struct Figure<S: Skeleton> { pub struct Figures {
// GPU data test_model: Model<FigurePipeline>,
model: Model<FigurePipeline>, states: HashMap<EcsEntity, FigureState<CharacterSkeleton>>,
bone_consts: Consts<FigureBoneData>,
locals: Consts<FigureLocals>,
// CPU data
bone_meshes: [Option<Mesh<FigurePipeline>>; 16],
pub skeleton: S,
} }
impl<S: Skeleton> Figure<S> { impl Figures {
pub fn new( pub fn new(renderer: &mut Renderer) -> Self {
renderer: &mut Renderer, // TODO: Make a proper asset loading system
bone_meshes: [Option<Mesh<FigurePipeline>>; 16], fn load_segment(filename: &'static str) -> Segment {
skeleton: S, Segment::from(dot_vox::load(&(concat!(env!("CARGO_MANIFEST_DIR"), "/../assets/voxygen/voxel/").to_string() + filename)).unwrap())
) -> Result<Self, Error> { }
let mut this = Self {
model: renderer.create_model(&Mesh::new())?,
bone_consts: renderer.create_consts(&skeleton.compute_matrices())?,
locals: renderer.create_consts(&[FigureLocals::default()])?,
bone_meshes, let bone_meshes = [
skeleton, Some(load_segment("head.vox").generate_mesh(Vec3::new(-7.0, -6.5, -6.0))),
}; Some(load_segment("chest.vox").generate_mesh(Vec3::new(-6.0, -3.0, 0.0))),
this.update_model(renderer)?; Some(load_segment("belt.vox").generate_mesh(Vec3::new(-5.0, -3.0, 0.0))),
Ok(this) Some(load_segment("pants.vox").generate_mesh(Vec3::new(-5.0, -3.0, 0.0))),
} Some(load_segment("hand.vox").generate_mesh(Vec3::new(-2.0, -2.0, -1.0))),
Some(load_segment("hand.vox").generate_mesh(Vec3::new(-2.0, -2.0, -1.0))),
Some(load_segment("foot.vox").generate_mesh(Vec3::new(-2.5, -3.0, -2.0))),
Some(load_segment("foot.vox").generate_mesh(Vec3::new(-2.5, -3.0, -2.0))),
Some(load_segment("sword.vox").generate_mesh(Vec3::new(-6.5, -1.0, 0.0))),
None,
None,
None,
None,
None,
None,
None,
];
pub fn update_model(&mut self, renderer: &mut Renderer) -> Result<(), Error> {
let mut mesh = Mesh::new(); let mut mesh = Mesh::new();
bone_meshes
self.bone_meshes
.iter() .iter()
.enumerate() .enumerate()
.filter_map(|(i, bm)| bm.as_ref().map(|bm| (i, bm))) .filter_map(|(i, bm)| bm.as_ref().map(|bm| (i, bm)))
@ -55,39 +72,69 @@ impl<S: Skeleton> Figure<S> {
mesh.push_mesh_map(bone_mesh, |vert| vert.with_bone_idx(i as u8)) mesh.push_mesh_map(bone_mesh, |vert| vert.with_bone_idx(i as u8))
}); });
self.model = renderer.create_model(&mesh)?; Self {
Ok(()) test_model: renderer.create_model(&mesh).unwrap(),
states: HashMap::new(),
}
} }
pub fn update_skeleton(&mut self, renderer: &mut Renderer) -> Result<(), Error> { pub fn maintain(&mut self, renderer: &mut Renderer, client: &mut Client) {
renderer.update_consts(&mut self.bone_consts, &self.skeleton.compute_matrices())?; let time = client.state().get_time();
Ok(()) let ecs = client.state_mut().ecs_mut().internal_mut();
for (entity, pos, dir, character) in (
&ecs.entities(),
&ecs.read_storage::<comp::phys::Pos>(),
&ecs.read_storage::<comp::phys::Dir>(),
&ecs.read_storage::<comp::Character>(),
).join() {
let state = self.states
.entry(entity)
.or_insert_with(|| FigureState::new(renderer, CharacterSkeleton::new()));
RunAnimation::update_skeleton(&mut state.skeleton, time);
state.update(renderer, pos.0, dir.0);
}
self.states.retain(|entity, _| ecs.entities().is_alive(*entity));
} }
pub fn update_locals(&mut self, renderer: &mut Renderer, locals: FigureLocals) -> Result<(), Error> { pub fn render(&self, renderer: &mut Renderer, client: &Client, globals: &Consts<Globals>) {
renderer.update_consts(&mut self.locals, &[locals])?; for state in self.states.values() {
Ok(()) renderer.render_figure(
} &self.test_model,
globals,
pub fn render(&self, renderer: &mut Renderer, globals: &Consts<Globals>) { &state.locals,
renderer.render_figure( &state.bone_consts,
&self.model, );
globals, }
&self.locals,
&self.bone_consts,
);
} }
} }
/* pub struct FigureState<S: Skeleton> {
#[derive(Copy, Clone, Debug)]
pub struct Figure<S: Skeleton> {
bone_consts: Consts<FigureBoneData>, bone_consts: Consts<FigureBoneData>,
locals: Consts<FigureLocals>, locals: Consts<FigureLocals>,
skeleton: S, skeleton: S,
} }
impl<S: Skeleton> Component for Figure<S> { impl<S: Skeleton> FigureState<S> {
type Storage = VecStorage<Self>; pub fn new(renderer: &mut Renderer, skeleton: S) -> Self {
Self {
bone_consts: renderer.create_consts(&skeleton.compute_matrices()).unwrap(),
locals: renderer.create_consts(&[FigureLocals::default()]).unwrap(),
skeleton,
}
}
fn update(&mut self, renderer: &mut Renderer, pos: Vec3<f32>, dir: Vec3<f32>) {
let mat =
Mat4::<f32>::identity() *
Mat4::translation_3d(pos) *
Mat4::rotation_z(dir.y.atan2(dir.x) + f32::consts::PI / 2.0);
let locals = FigureLocals::new(mat);
renderer.update_consts(&mut self.locals, &[locals]).unwrap();
renderer.update_consts(&mut self.bone_consts, &self.skeleton.compute_matrices()).unwrap();
}
} }
*/

View File

@ -29,7 +29,7 @@ use crate::{
}; };
use self::{ use self::{
camera::Camera, camera::Camera,
figure::Figure, figure::Figures,
terrain::Terrain, terrain::Terrain,
}; };
@ -47,13 +47,7 @@ pub struct Scene {
skybox: Skybox, skybox: Skybox,
terrain: Terrain, terrain: Terrain,
figures: Figures,
test_figure: Figure<CharacterSkeleton>,
}
// TODO: Make a proper asset loading system
fn load_segment(filename: &'static str) -> Segment {
Segment::from(dot_vox::load(&(concat!(env!("CARGO_MANIFEST_DIR"), "/../assets/voxygen/voxel/").to_string() + filename)).unwrap())
} }
impl Scene { impl Scene {
@ -74,30 +68,7 @@ impl Scene {
.unwrap(), .unwrap(),
}, },
terrain: Terrain::new(), terrain: Terrain::new(),
figures: Figures::new(renderer),
test_figure: Figure::new(
renderer,
[
Some(load_segment("head.vox").generate_mesh(Vec3::new(-7.0, -6.5, -6.0))),
Some(load_segment("chest.vox").generate_mesh(Vec3::new(-6.0, -3.0, 0.0))),
Some(load_segment("belt.vox").generate_mesh(Vec3::new(-5.0, -3.0, 0.0))),
Some(load_segment("pants.vox").generate_mesh(Vec3::new(-5.0, -3.0, 0.0))),
Some(load_segment("hand.vox").generate_mesh(Vec3::new(-2.0, -2.0, -1.0))),
Some(load_segment("hand.vox").generate_mesh(Vec3::new(-2.0, -2.0, -1.0))),
Some(load_segment("foot.vox").generate_mesh(Vec3::new(-2.5, -3.0, -2.0))),
Some(load_segment("foot.vox").generate_mesh(Vec3::new(-2.5, -3.0, -2.0))),
Some(load_segment("sword.vox").generate_mesh(Vec3::new(-6.5, -1.0, 0.0))),
None,
None,
None,
None,
None,
None,
None,
],
CharacterSkeleton::new(),
)
.unwrap(),
} }
} }
@ -133,13 +104,14 @@ impl Scene {
} }
/// Maintain data such as GPU constant buffers, models, etc. To be called once per tick. /// Maintain data such as GPU constant buffers, models, etc. To be called once per tick.
pub fn maintain(&mut self, renderer: &mut Renderer, client: &Client) { pub fn maintain(&mut self, renderer: &mut Renderer, client: &mut Client) {
// Get player position // Get player position
let player_pos = client let player_pos = client
.player() .player()
.and_then(|ent| client .and_then(|ent| client
.state() .state()
.ecs_world() .ecs()
.internal()
.read_storage::<comp::phys::Pos>() .read_storage::<comp::phys::Pos>()
.get(ent) .get(ent)
.map(|pos| pos.0) .map(|pos| pos.0)
@ -164,24 +136,13 @@ impl Scene {
)]) )])
.expect("Failed to update global constants"); .expect("Failed to update global constants");
// Maintain the terrain // Maintain the terrain and figures
self.terrain.maintain(renderer, client); self.terrain.maintain(renderer, client);
self.figures.maintain(renderer, client);
// TODO: Don't do this here
RunAnimation::update_skeleton(
&mut self.test_figure.skeleton,
client.state().get_time(),
);
// Calculate player model matrix
let model_mat = Mat4::<f32>::translation_3d(player_pos);
self.test_figure.update_locals(renderer, FigureLocals::new(model_mat)).unwrap();
self.test_figure.update_skeleton(renderer).unwrap();
} }
/// Render the scene using the provided `Renderer` /// Render the scene using the provided `Renderer`
pub fn render_to(&self, renderer: &mut Renderer) { pub fn render(&self, renderer: &mut Renderer, client: &Client) {
// Render the skybox first (it appears over everything else so must be rendered first) // Render the skybox first (it appears over everything else so must be rendered first)
renderer.render_skybox( renderer.render_skybox(
&self.skybox.model, &self.skybox.model,
@ -189,10 +150,8 @@ impl Scene {
&self.skybox.locals, &self.skybox.locals,
); );
// Render terrain // Render terrain and figures
self.terrain.render(renderer, &self.globals); self.terrain.render(renderer, &self.globals);
self.figures.render(renderer, client, &self.globals);
// Render the test figure
self.test_figure.render(renderer, &self.globals);
} }
} }

View File

@ -87,7 +87,7 @@ impl SessionState {
renderer.clear(BG_COLOR); renderer.clear(BG_COLOR);
// Render the screen using the global renderer // Render the screen using the global renderer
self.scene.render_to(renderer); self.scene.render(renderer, &self.client.borrow());
// Draw the UI to the screen // Draw the UI to the screen
self.hud.render(renderer); self.hud.render(renderer);
@ -105,6 +105,7 @@ impl PlayState for SessionState {
let mut clock = Clock::new(); let mut clock = Clock::new();
// Load a few chunks TODO: Remove this // Load a few chunks TODO: Remove this
/*
for x in -6..7 { for x in -6..7 {
for y in -6..7 { for y in -6..7 {
for z in -1..2 { for z in -1..2 {
@ -112,6 +113,7 @@ impl PlayState for SessionState {
} }
} }
} }
*/
// Game loop // Game loop
loop { loop {
@ -155,7 +157,7 @@ impl PlayState for SessionState {
.expect("Failed to tick the scene"); .expect("Failed to tick the scene");
// Maintain the scene // Maintain the scene
self.scene.maintain(global_state.window.renderer_mut(), &self.client.borrow()); self.scene.maintain(global_state.window.renderer_mut(), &mut self.client.borrow_mut());
// Maintain the UI // Maintain the UI
for event in self.hud.maintain(global_state.window.renderer_mut()) { for event in self.hud.maintain(global_state.window.renderer_mut()) {
match event { match event {

View File

@ -1,6 +1,6 @@
[package] [package]
name = "veloren-world" name = "veloren-world"
version = "0.1.0" version = "0.2.0"
authors = ["Joshua Barretto <joshua.s.barretto@gmail.com>"] authors = ["Joshua Barretto <joshua.s.barretto@gmail.com>"]
edition = "2018" edition = "2018"

View File

@ -24,7 +24,7 @@ impl World {
Self Self
} }
pub fn generate_chunk(&self, chunk_pos: Vec3<i32>) -> TerrainChunk { pub fn generate_chunk(chunk_pos: Vec3<i32>) -> TerrainChunk {
// TODO: This is all test code, remove/improve this later // TODO: This is all test code, remove/improve this later
let mut chunk = TerrainChunk::filled(Block::empty(), TerrainChunkMeta::void()); let mut chunk = TerrainChunk::filled(Block::empty(), TerrainChunkMeta::void());