mirror of
https://gitlab.com/veloren/veloren.git
synced 2024-08-30 18:12:32 +00:00
Merge branch 'master' into 'master'
Networked entities See merge request veloren/fresh!12 Former-commit-id: 0aab14fdc18f0567c662589dda5d2cf518072431
This commit is contained in:
commit
a32d977b32
@ -19,8 +19,13 @@ fn main() {
|
||||
.expect("Failed to create client instance");
|
||||
|
||||
loop {
|
||||
let events = client.tick(Input::default(), clock.get_last_delta())
|
||||
.expect("Failed to tick client");
|
||||
let events = match client.tick(Input::default(), clock.get_last_delta()) {
|
||||
Ok(events) => events,
|
||||
Err(err) => {
|
||||
println!("Error: {:?}", err);
|
||||
break;
|
||||
},
|
||||
};
|
||||
|
||||
for event in events {
|
||||
match event {
|
||||
|
@ -3,6 +3,7 @@ use common::net::PostError;
|
||||
#[derive(Debug)]
|
||||
pub enum Error {
|
||||
Network(PostError),
|
||||
ServerTimeout,
|
||||
ServerShutdown,
|
||||
Other(String),
|
||||
}
|
||||
@ -10,7 +11,7 @@ pub enum Error {
|
||||
impl From<PostError> for Error {
|
||||
fn from(err: PostError) -> Self {
|
||||
match err {
|
||||
PostError::Disconnected => Error::ServerShutdown,
|
||||
PostError::Disconnect => Error::ServerShutdown,
|
||||
err => Error::Network(err),
|
||||
}
|
||||
}
|
||||
|
@ -1,3 +1,5 @@
|
||||
#![feature(label_break_value)]
|
||||
|
||||
pub mod error;
|
||||
pub mod input;
|
||||
|
||||
@ -24,6 +26,8 @@ use common::{
|
||||
};
|
||||
use world::World;
|
||||
|
||||
const SERVER_TIMEOUT: f64 = 5.0; // Seconds
|
||||
|
||||
pub enum Event {
|
||||
Chat(String),
|
||||
}
|
||||
@ -50,7 +54,6 @@ impl Client {
|
||||
let state = State::new();
|
||||
|
||||
let mut postbox = PostBox::to_server(addr)?;
|
||||
postbox.send(ClientMsg::Chat(String::from("Hello, world!")));
|
||||
|
||||
Ok(Self {
|
||||
thread_pool: threadpool::Builder::new()
|
||||
@ -79,7 +82,6 @@ impl Client {
|
||||
// TODO: Get rid of this
|
||||
pub fn with_test_state(mut self) -> Self {
|
||||
self.chunk = Some(self.world.generate_chunk(Vec3::zero()));
|
||||
self.player = Some(self.state.new_test_player());
|
||||
self
|
||||
}
|
||||
|
||||
@ -97,13 +99,6 @@ impl Client {
|
||||
#[allow(dead_code)]
|
||||
pub fn state_mut(&mut self) -> &mut State { &mut self.state }
|
||||
|
||||
/// Get an entity from its UID, creating it if it does not exists
|
||||
pub fn get_or_create_entity(&mut self, uid: u64) -> EcsEntity {
|
||||
self.state.ecs_world_mut().create_entity()
|
||||
.with(comp::Uid(uid))
|
||||
.build()
|
||||
}
|
||||
|
||||
/// Get the player entity
|
||||
#[allow(dead_code)]
|
||||
pub fn player(&self) -> Option<EcsEntity> {
|
||||
@ -118,8 +113,8 @@ impl Client {
|
||||
|
||||
/// Send a chat message to the server
|
||||
#[allow(dead_code)]
|
||||
pub fn send_chat(&mut self, msg: String) -> Result<(), Error> {
|
||||
Ok(self.postbox.send(ClientMsg::Chat(msg))?)
|
||||
pub fn send_chat(&mut self, msg: String) {
|
||||
self.postbox.send(ClientMsg::Chat(msg))
|
||||
}
|
||||
|
||||
/// Execute a single client tick, handle input and update the game state by the given duration
|
||||
@ -144,17 +139,31 @@ impl Client {
|
||||
frontend_events.append(&mut self.handle_new_messages()?);
|
||||
|
||||
// Step 3
|
||||
if let Some(p) = self.player {
|
||||
if let Some(ecs_entity) = self.player {
|
||||
// TODO: remove this
|
||||
const PLAYER_VELOCITY: f32 = 100.0;
|
||||
|
||||
// TODO: Set acceleration instead
|
||||
self.state.write_component(p, 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)));
|
||||
}
|
||||
|
||||
// Tick the client's LocalState (step 3)
|
||||
self.state.tick(dt);
|
||||
|
||||
// Update the server about the player's physics attributes
|
||||
if let Some(ecs_entity) = self.player {
|
||||
match (
|
||||
self.state.read_storage().get(ecs_entity).cloned(),
|
||||
self.state.read_storage().get(ecs_entity).cloned(),
|
||||
self.state.read_storage().get(ecs_entity).cloned(),
|
||||
) {
|
||||
(Some(pos), Some(vel), Some(dir)) => {
|
||||
self.postbox.send(ClientMsg::PlayerPhysics { pos, vel, dir });
|
||||
},
|
||||
_ => {},
|
||||
}
|
||||
}
|
||||
|
||||
// Finish the tick, pass control back to the frontend (step 6)
|
||||
self.tick += 1;
|
||||
Ok(frontend_events)
|
||||
@ -178,20 +187,39 @@ impl Client {
|
||||
self.last_ping = self.state.get_time();
|
||||
|
||||
for msg in new_msgs {
|
||||
println!("Received message");
|
||||
match msg {
|
||||
ServerMsg::Shutdown => return Err(Error::ServerShutdown),
|
||||
ServerMsg::Ping => self.postbox.send(ClientMsg::Pong),
|
||||
ServerMsg::Pong => {},
|
||||
ServerMsg::Chat(msg) => frontend_events.push(Event::Chat(msg)),
|
||||
ServerMsg::SetPlayerEntity(uid) => {
|
||||
let ecs_entity = self.state
|
||||
.get_entity(uid)
|
||||
.unwrap_or_else(|| self.state.build_uid_entity_with_uid(uid).build());
|
||||
|
||||
self.player = Some(ecs_entity);
|
||||
},
|
||||
ServerMsg::EntityPhysics { uid, pos, vel, dir } => {
|
||||
let ecs_entity = self.get_or_create_entity(uid);
|
||||
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);
|
||||
},
|
||||
}
|
||||
}
|
||||
} else if let Some(err) = self.postbox.status() {
|
||||
} else if let Some(err) = self.postbox.error() {
|
||||
return Err(err.into());
|
||||
} else if self.state.get_time() - self.last_ping > SERVER_TIMEOUT {
|
||||
return Err(Error::ServerTimeout);
|
||||
} else if self.state.get_time() - self.last_ping > SERVER_TIMEOUT * 0.5 {
|
||||
// Try pinging the server if the timeout is nearing
|
||||
self.postbox.send(ClientMsg::Ping);
|
||||
}
|
||||
|
||||
Ok(frontend_events)
|
||||
@ -200,6 +228,6 @@ impl Client {
|
||||
|
||||
impl Drop for Client {
|
||||
fn drop(&mut self) {
|
||||
self.postbox.send(ClientMsg::Disconnect).unwrap();
|
||||
self.postbox.send(ClientMsg::Disconnect);
|
||||
}
|
||||
}
|
||||
|
@ -6,7 +6,7 @@ edition = "2018"
|
||||
|
||||
[dependencies]
|
||||
specs = { version = "0.14", features = ["serde"] }
|
||||
shred = "0.7"
|
||||
shred = { version = "0.7", features = ["nightly"] }
|
||||
vek = { version = "0.9", features = ["serde"] }
|
||||
dot_vox = "1.0"
|
||||
threadpool = "1.7"
|
||||
|
35
common/src/comp/character.rs
Normal file
35
common/src/comp/character.rs
Normal file
@ -0,0 +1,35 @@
|
||||
// Library
|
||||
use specs::{Component, VecStorage};
|
||||
use vek::*;
|
||||
|
||||
#[derive(Copy, Clone, Debug, Serialize, Deserialize)]
|
||||
enum Race {
|
||||
Danari,
|
||||
Dwarf,
|
||||
Elf,
|
||||
Human,
|
||||
Orc,
|
||||
Undead,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, Serialize, Deserialize)]
|
||||
pub struct Gender {
|
||||
Female,
|
||||
Male,
|
||||
Unspecified,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, Serialize, Deserialize)]
|
||||
pub struct Character {
|
||||
race: Race,
|
||||
gender: Gender,
|
||||
head: (),
|
||||
chest: (),
|
||||
belt: (),
|
||||
arms: (),
|
||||
feet: (),
|
||||
}
|
||||
|
||||
impl Component for Character {
|
||||
type Storage = VecStorage<Self>;
|
||||
}
|
@ -1,5 +1,6 @@
|
||||
pub mod phys;
|
||||
pub mod uid;
|
||||
pub mod util;
|
||||
|
||||
// Reexports
|
||||
pub use uid::{Uid, UidAllocator};
|
||||
@ -8,8 +9,12 @@ 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>();
|
||||
}
|
||||
|
@ -28,3 +28,15 @@ pub struct Dir(pub Vec3<f32>);
|
||||
impl Component for Dir {
|
||||
type Storage = VecStorage<Self>;
|
||||
}
|
||||
|
||||
// UpdateKind
|
||||
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
pub enum UpdateKind {
|
||||
Passive,
|
||||
Force,
|
||||
}
|
||||
|
||||
impl Component for UpdateKind {
|
||||
type Storage = VecStorage<Self>;
|
||||
}
|
||||
|
@ -2,6 +2,7 @@ use std::{
|
||||
collections::HashMap,
|
||||
ops::Range,
|
||||
u64,
|
||||
fmt,
|
||||
};
|
||||
use specs::{
|
||||
saveload::{Marker, MarkerAllocator},
|
||||
@ -22,6 +23,12 @@ impl Into<u64> for Uid {
|
||||
}
|
||||
}
|
||||
|
||||
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>;
|
||||
}
|
||||
|
12
common/src/comp/util.rs
Normal file
12
common/src/comp/util.rs
Normal file
@ -0,0 +1,12 @@
|
||||
// Library
|
||||
use specs::{Component, NullStorage};
|
||||
use vek::*;
|
||||
|
||||
// Pos
|
||||
|
||||
#[derive(Copy, Clone, Debug, Default)]
|
||||
pub struct New;
|
||||
|
||||
impl Component for New {
|
||||
type Storage = NullStorage<Self>;
|
||||
}
|
@ -1,5 +1,17 @@
|
||||
use crate::comp::{
|
||||
Uid,
|
||||
phys,
|
||||
};
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||
pub enum ClientMsg {
|
||||
Ping,
|
||||
Pong,
|
||||
Chat(String),
|
||||
PlayerPhysics {
|
||||
pos: phys::Pos,
|
||||
vel: phys::Vel,
|
||||
dir: phys::Dir,
|
||||
},
|
||||
Disconnect,
|
||||
}
|
||||
|
@ -1,13 +1,20 @@
|
||||
use crate::comp::phys;
|
||||
use crate::comp::{
|
||||
Uid,
|
||||
phys,
|
||||
};
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||
pub enum ServerMsg {
|
||||
Shutdown,
|
||||
Ping,
|
||||
Pong,
|
||||
Chat(String),
|
||||
SetPlayerEntity(Uid),
|
||||
EntityPhysics {
|
||||
uid: u64,
|
||||
uid: Uid,
|
||||
pos: phys::Pos,
|
||||
vel: phys::Vel,
|
||||
dir: phys::Dir,
|
||||
},
|
||||
EntityDeleted(Uid),
|
||||
}
|
||||
|
@ -1,15 +1,17 @@
|
||||
pub mod data;
|
||||
pub mod error;
|
||||
pub mod post;
|
||||
pub mod postbox;
|
||||
pub mod postoffice;
|
||||
mod test;
|
||||
|
||||
// Reexports
|
||||
pub use self::{
|
||||
data::{ClientMsg, ServerMsg},
|
||||
error::PostError,
|
||||
postbox::PostBox,
|
||||
postoffice::PostOffice,
|
||||
post::{
|
||||
Error as PostError,
|
||||
PostBox,
|
||||
PostOffice,
|
||||
},
|
||||
};
|
||||
|
||||
pub trait PostSend = 'static + serde::Serialize + std::marker::Send + std::fmt::Debug;
|
||||
|
556
common/src/net/post.rs
Normal file
556
common/src/net/post.rs
Normal file
@ -0,0 +1,556 @@
|
||||
use std::{
|
||||
fmt,
|
||||
thread,
|
||||
net::{SocketAddr, Shutdown},
|
||||
sync::mpsc::TryRecvError,
|
||||
io::{self, Read, Write},
|
||||
collections::VecDeque,
|
||||
time::Duration,
|
||||
convert::TryFrom,
|
||||
};
|
||||
use serde;
|
||||
use mio::{
|
||||
net::{TcpListener, TcpStream},
|
||||
Events,
|
||||
Poll,
|
||||
PollOpt,
|
||||
Ready,
|
||||
Token,
|
||||
};
|
||||
use mio_extras::channel::{
|
||||
channel,
|
||||
Receiver,
|
||||
Sender,
|
||||
};
|
||||
use bincode;
|
||||
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub enum Error {
|
||||
Disconnect,
|
||||
Network,
|
||||
InvalidMsg,
|
||||
Internal,
|
||||
}
|
||||
|
||||
impl From<io::Error> for Error {
|
||||
fn from(err: io::Error) -> Self {
|
||||
Error::Network
|
||||
}
|
||||
}
|
||||
|
||||
impl From<TryRecvError> for Error {
|
||||
fn from(err: TryRecvError) -> Self {
|
||||
Error::Internal
|
||||
}
|
||||
}
|
||||
|
||||
impl From<bincode::ErrorKind> for Error {
|
||||
fn from(err: bincode::ErrorKind) -> Self {
|
||||
Error::InvalidMsg
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> From<mio_extras::channel::SendError<T>> for Error {
|
||||
fn from(err: mio_extras::channel::SendError<T>) -> Self {
|
||||
Error::Internal
|
||||
}
|
||||
}
|
||||
|
||||
pub trait PostSend = 'static + serde::Serialize + Send + fmt::Debug;
|
||||
pub trait PostRecv = 'static + serde::de::DeserializeOwned + Send + fmt::Debug;
|
||||
|
||||
const TCP_TOK: Token = Token(0);
|
||||
const CTRL_TOK: Token = Token(1);
|
||||
const POSTBOX_TOK: Token = Token(2);
|
||||
const SEND_TOK: Token = Token(3);
|
||||
const RECV_TOK: Token = Token(4);
|
||||
|
||||
const MAX_MSG_BYTES: usize = 1 << 20;
|
||||
|
||||
enum CtrlMsg {
|
||||
Shutdown,
|
||||
}
|
||||
|
||||
pub struct PostOffice<S: PostSend, R: PostRecv> {
|
||||
worker: Option<thread::JoinHandle<Result<(), Error>>>,
|
||||
ctrl_tx: Sender<CtrlMsg>,
|
||||
postbox_rx: Receiver<Result<PostBox<S, R>, Error>>,
|
||||
poll: Poll,
|
||||
err: Option<Error>,
|
||||
}
|
||||
|
||||
impl<S: PostSend, R: PostRecv> PostOffice<S, R> {
|
||||
pub fn bind<A: Into<SocketAddr>>(addr: A) -> Result<Self, Error> {
|
||||
let tcp_listener = TcpListener::bind(&addr.into())?;
|
||||
|
||||
let (ctrl_tx, ctrl_rx) = channel();
|
||||
let (postbox_tx, postbox_rx) = channel();
|
||||
|
||||
let worker_poll = Poll::new()?;
|
||||
worker_poll.register(&tcp_listener, TCP_TOK, Ready::readable(), PollOpt::edge())?;
|
||||
worker_poll.register(&ctrl_rx, CTRL_TOK, Ready::readable(), PollOpt::edge())?;
|
||||
|
||||
let office_poll = Poll::new()?;
|
||||
office_poll.register(&postbox_rx, POSTBOX_TOK, Ready::readable(), PollOpt::edge())?;
|
||||
|
||||
let worker = thread::spawn(move || office_worker(
|
||||
worker_poll,
|
||||
tcp_listener,
|
||||
ctrl_rx,
|
||||
postbox_tx,
|
||||
));
|
||||
|
||||
Ok(Self {
|
||||
worker: Some(worker),
|
||||
ctrl_tx,
|
||||
postbox_rx,
|
||||
poll: office_poll,
|
||||
err: None,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn error(&self) -> Option<Error> {
|
||||
self.err.clone()
|
||||
}
|
||||
|
||||
pub fn new_connections(&mut self) -> impl ExactSizeIterator<Item=PostBox<S, R>> {
|
||||
let mut conns = VecDeque::new();
|
||||
|
||||
if let Some(_) = self.err {
|
||||
return conns.into_iter();
|
||||
}
|
||||
|
||||
let mut events = Events::with_capacity(64);
|
||||
if let Err(err) = self.poll.poll(
|
||||
&mut events,
|
||||
Some(Duration::new(0, 0)),
|
||||
) {
|
||||
self.err = Some(err.into());
|
||||
return conns.into_iter();
|
||||
}
|
||||
|
||||
for event in events {
|
||||
match event.token() {
|
||||
// Keep reading new postboxes from the channel
|
||||
POSTBOX_TOKEN => loop {
|
||||
match self.postbox_rx.try_recv() {
|
||||
Ok(Ok(conn)) => conns.push_back(conn),
|
||||
Err(TryRecvError::Empty) => break,
|
||||
Err(err) => {
|
||||
self.err = Some(err.into());
|
||||
return conns.into_iter();
|
||||
},
|
||||
Ok(Err(err)) => {
|
||||
self.err = Some(err.into());
|
||||
return conns.into_iter();
|
||||
},
|
||||
}
|
||||
},
|
||||
tok => panic!("Unexpected event token '{:?}'", tok),
|
||||
}
|
||||
}
|
||||
|
||||
conns.into_iter()
|
||||
}
|
||||
}
|
||||
|
||||
impl<S: PostSend, R: PostRecv> Drop for PostOffice<S, R> {
|
||||
fn drop(&mut self) {
|
||||
let _ = self.ctrl_tx.send(CtrlMsg::Shutdown);
|
||||
let _ = self.worker.take().map(|w| w.join());
|
||||
}
|
||||
}
|
||||
|
||||
fn office_worker<S: PostSend, R: PostRecv>(
|
||||
poll: Poll,
|
||||
tcp_listener: TcpListener,
|
||||
ctrl_rx: Receiver<CtrlMsg>,
|
||||
postbox_tx: Sender<Result<PostBox<S, R>, Error>>,
|
||||
) -> Result<(), Error> {
|
||||
let mut events = Events::with_capacity(64);
|
||||
loop {
|
||||
if let Err(err) = poll.poll(&mut events, None) {
|
||||
postbox_tx.send(Err(err.into()))?;
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
for event in &events {
|
||||
match event.token() {
|
||||
CTRL_TOK => loop {
|
||||
match ctrl_rx.try_recv() {
|
||||
Ok(CtrlMsg::Shutdown) => return Ok(()),
|
||||
Err(TryRecvError::Empty) => {},
|
||||
Err(err) => {
|
||||
postbox_tx.send(Err(err.into()))?;
|
||||
return Ok(());
|
||||
},
|
||||
}
|
||||
},
|
||||
TCP_TOK => postbox_tx.send(
|
||||
match tcp_listener.accept() {
|
||||
Ok((stream, _)) => PostBox::from_tcpstream(stream),
|
||||
Err(err) => Err(err.into()),
|
||||
}
|
||||
)?,
|
||||
tok => panic!("Unexpected event token '{:?}'", tok),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct PostBox<S: PostSend, R: PostRecv> {
|
||||
worker: Option<thread::JoinHandle<Result<(), Error>>>,
|
||||
ctrl_tx: Sender<CtrlMsg>,
|
||||
send_tx: Sender<S>,
|
||||
recv_rx: Receiver<Result<R, Error>>,
|
||||
poll: Poll,
|
||||
err: Option<Error>,
|
||||
}
|
||||
|
||||
impl<S: PostSend, R: PostRecv> PostBox<S, R> {
|
||||
pub fn to_server<A: Into<SocketAddr>>(addr: A) -> Result<Self, Error> {
|
||||
Self::from_tcpstream(TcpStream::connect(&addr.into())?)
|
||||
}
|
||||
|
||||
fn from_tcpstream(tcp_stream: TcpStream) -> Result<Self, Error> {
|
||||
let (ctrl_tx, ctrl_rx) = channel();
|
||||
let (send_tx, send_rx) = channel();
|
||||
let (recv_tx, recv_rx) = channel();
|
||||
|
||||
let worker_poll = Poll::new()?;
|
||||
worker_poll.register(&tcp_stream, TCP_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())?;
|
||||
|
||||
let postbox_poll = Poll::new()?;
|
||||
postbox_poll.register(&recv_rx, RECV_TOK, Ready::readable(), PollOpt::edge())?;
|
||||
|
||||
let worker = thread::spawn(move || postbox_worker(
|
||||
worker_poll,
|
||||
tcp_stream,
|
||||
ctrl_rx,
|
||||
send_rx,
|
||||
recv_tx,
|
||||
));
|
||||
|
||||
Ok(Self {
|
||||
worker: Some(worker),
|
||||
ctrl_tx,
|
||||
send_tx,
|
||||
recv_rx,
|
||||
poll: postbox_poll,
|
||||
err: None,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn error(&self) -> Option<Error> {
|
||||
self.err.clone()
|
||||
}
|
||||
|
||||
pub fn send(&mut self, data: S) {
|
||||
let _ = self.send_tx.send(data);
|
||||
}
|
||||
|
||||
pub fn new_messages(&mut self) -> impl ExactSizeIterator<Item=R> {
|
||||
let mut msgs = VecDeque::new();
|
||||
|
||||
if let Some(_) = self.err {
|
||||
return msgs.into_iter();
|
||||
}
|
||||
|
||||
let mut events = Events::with_capacity(64);
|
||||
if let Err(err) = self.poll.poll(
|
||||
&mut events,
|
||||
Some(Duration::new(0, 0)),
|
||||
) {
|
||||
self.err = Some(err.into());
|
||||
return msgs.into_iter();
|
||||
}
|
||||
|
||||
for event in events {
|
||||
match event.token() {
|
||||
// Keep reading new messages from the channel
|
||||
RECV_TOKEN => loop {
|
||||
match self.recv_rx.try_recv() {
|
||||
Ok(Ok(msg)) => msgs.push_back(msg),
|
||||
Err(TryRecvError::Empty) => break,
|
||||
Err(err) => {
|
||||
self.err = Some(err.into());
|
||||
return msgs.into_iter();
|
||||
},
|
||||
Ok(Err(err)) => {
|
||||
self.err = Some(err.into());
|
||||
return msgs.into_iter();
|
||||
},
|
||||
}
|
||||
},
|
||||
tok => panic!("Unexpected event token '{:?}'", tok),
|
||||
}
|
||||
}
|
||||
|
||||
msgs.into_iter()
|
||||
}
|
||||
}
|
||||
|
||||
impl<S: PostSend, R: PostRecv> Drop for PostBox<S, R> {
|
||||
fn drop(&mut self) {
|
||||
let _ = self.ctrl_tx.send(CtrlMsg::Shutdown);
|
||||
let _ = self.worker.take().map(|w| w.join());
|
||||
}
|
||||
}
|
||||
|
||||
fn postbox_worker<S: PostSend, R: PostRecv>(
|
||||
poll: Poll,
|
||||
mut tcp_stream: TcpStream,
|
||||
ctrl_rx: Receiver<CtrlMsg>,
|
||||
send_rx: Receiver<S>,
|
||||
recv_tx: Sender<Result<R, Error>>,
|
||||
) -> Result<(), Error> {
|
||||
enum RecvState {
|
||||
ReadHead(Vec<u8>),
|
||||
ReadBody(usize, Vec<u8>),
|
||||
}
|
||||
|
||||
let mut recv_state = RecvState::ReadHead(Vec::with_capacity(8));
|
||||
let mut events = Events::with_capacity(64);
|
||||
|
||||
'work: loop {
|
||||
if let Err(err) = poll.poll(&mut events, None) {
|
||||
recv_tx.send(Err(err.into()))?;
|
||||
break 'work;
|
||||
}
|
||||
|
||||
for event in &events {
|
||||
match event.token() {
|
||||
CTRL_TOK => loop {
|
||||
match ctrl_rx.try_recv() {
|
||||
Ok(CtrlMsg::Shutdown) => {
|
||||
break 'work;
|
||||
},
|
||||
Err(TryRecvError::Empty) => break,
|
||||
Err(err) => {
|
||||
recv_tx.send(Err(err.into()))?;
|
||||
break 'work;
|
||||
},
|
||||
}
|
||||
},
|
||||
SEND_TOK => loop {
|
||||
match send_rx.try_recv() {
|
||||
Ok(outgoing_msg) => {
|
||||
let mut msg_bytes = match bincode::serialize(&outgoing_msg) {
|
||||
Ok(bytes) => bytes,
|
||||
Err(err) => {
|
||||
recv_tx.send(Err((*err).into()));
|
||||
break 'work;
|
||||
},
|
||||
};
|
||||
|
||||
let mut packet = msg_bytes
|
||||
.len()
|
||||
.to_le_bytes()
|
||||
.as_ref()
|
||||
.to_vec();
|
||||
packet.append(&mut msg_bytes);
|
||||
|
||||
match tcp_stream.write_all(&packet) {
|
||||
Ok(()) => {},
|
||||
Err(err) => {
|
||||
recv_tx.send(Err(err.into()));
|
||||
break 'work;
|
||||
},
|
||||
}
|
||||
},
|
||||
Err(TryRecvError::Empty) => break,
|
||||
Err(err) => Err(err)?,
|
||||
}
|
||||
},
|
||||
TCP_TOK => loop {
|
||||
match tcp_stream.take_error() {
|
||||
Ok(None) => {},
|
||||
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 {
|
||||
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;
|
||||
},
|
||||
}
|
||||
},
|
||||
}
|
||||
},
|
||||
tok => panic!("Unexpected event token '{:?}'", tok),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
tcp_stream.shutdown(Shutdown::Both);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// TESTS
|
||||
|
||||
#[test]
|
||||
fn connect() {
|
||||
let srv_addr = ([127, 0, 0, 1], 12345);
|
||||
|
||||
let mut postoffice = PostOffice::<u32, f32>::bind(srv_addr).unwrap();
|
||||
|
||||
// We should start off with 0 incoming connections
|
||||
thread::sleep(Duration::from_millis(250));
|
||||
assert_eq!(postoffice.new_connections().len(), 0);
|
||||
assert_eq!(postoffice.error(), None);
|
||||
|
||||
let postbox = PostBox::<f32, u32>::to_server(srv_addr).unwrap();
|
||||
|
||||
// Now a postbox has been created, we should have 1 new
|
||||
thread::sleep(Duration::from_millis(250));
|
||||
let incoming = postoffice.new_connections();
|
||||
assert_eq!(incoming.len(), 1);
|
||||
assert_eq!(postoffice.error(), None);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn connection_count() {
|
||||
let srv_addr = ([127, 0, 0, 1], 12346);
|
||||
|
||||
let mut postoffice = PostOffice::<u32, f32>::bind(srv_addr).unwrap();
|
||||
let mut postboxes = Vec::new();
|
||||
|
||||
// We should start off with 0 incoming connections
|
||||
thread::sleep(Duration::from_millis(250));
|
||||
assert_eq!(postoffice.new_connections().len(), 0);
|
||||
assert_eq!(postoffice.error(), None);
|
||||
|
||||
for _ in 0..5 {
|
||||
postboxes.push(PostBox::<f32, u32>::to_server(srv_addr).unwrap());
|
||||
}
|
||||
|
||||
// 10 postboxes created, we should have 10
|
||||
thread::sleep(Duration::from_millis(3500));
|
||||
let incoming = postoffice.new_connections();
|
||||
assert_eq!(incoming.len(), 5);
|
||||
assert_eq!(postoffice.error(), None);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn disconnect() {
|
||||
let srv_addr = ([127, 0, 0, 1], 12347);
|
||||
|
||||
let mut postoffice = PostOffice::<u32, f32>::bind(srv_addr).unwrap();
|
||||
|
||||
let mut server_postbox = {
|
||||
let mut client_postbox = PostBox::<f32, u32>::to_server(srv_addr).unwrap();
|
||||
|
||||
thread::sleep(Duration::from_millis(250));
|
||||
let mut incoming = postoffice.new_connections();
|
||||
assert_eq!(incoming.len(), 1);
|
||||
assert_eq!(postoffice.error(), None);
|
||||
|
||||
incoming.next().unwrap()
|
||||
};
|
||||
|
||||
// The client postbox has since been disconnected
|
||||
thread::sleep(Duration::from_millis(2050));
|
||||
let incoming_msgs = server_postbox.new_messages();
|
||||
assert_eq!(incoming_msgs.len(), 0);
|
||||
// TODO
|
||||
// assert_eq!(server_postbox.error(), Some(Error::Disconnect));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn client_to_server() {
|
||||
let srv_addr = ([127, 0, 0, 1], 12348);
|
||||
|
||||
let mut po = PostOffice::<u32, f32>::bind(srv_addr).unwrap();
|
||||
|
||||
let mut client_pb = PostBox::<f32, u32>::to_server(srv_addr).unwrap();
|
||||
|
||||
thread::sleep(Duration::from_millis(250));
|
||||
|
||||
let mut server_pb = po.new_connections().next().unwrap();
|
||||
|
||||
client_pb.send(1337.0);
|
||||
client_pb.send(9821.0);
|
||||
client_pb.send(-3.2);
|
||||
client_pb.send(17.0);
|
||||
|
||||
thread::sleep(Duration::from_millis(250));
|
||||
|
||||
let mut incoming_msgs = server_pb.new_messages();
|
||||
assert_eq!(incoming_msgs.len(), 4);
|
||||
assert_eq!(incoming_msgs.next().unwrap(), 1337.0);
|
||||
assert_eq!(incoming_msgs.next().unwrap(), 9821.0);
|
||||
assert_eq!(incoming_msgs.next().unwrap(), -3.2);
|
||||
assert_eq!(incoming_msgs.next().unwrap(), 17.0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn server_to_client() {
|
||||
let srv_addr = ([127, 0, 0, 1], 12349);
|
||||
|
||||
let mut po = PostOffice::<u32, f32>::bind(srv_addr).unwrap();
|
||||
|
||||
let mut client_pb = PostBox::<f32, u32>::to_server(srv_addr).unwrap();
|
||||
|
||||
thread::sleep(Duration::from_millis(250));
|
||||
|
||||
let mut server_pb = po.new_connections().next().unwrap();
|
||||
|
||||
server_pb.send(1337);
|
||||
server_pb.send(9821);
|
||||
server_pb.send(39999999);
|
||||
server_pb.send(17);
|
||||
|
||||
thread::sleep(Duration::from_millis(250));
|
||||
|
||||
let mut incoming_msgs = client_pb.new_messages();
|
||||
assert_eq!(incoming_msgs.len(), 4);
|
||||
assert_eq!(incoming_msgs.next().unwrap(), 1337);
|
||||
assert_eq!(incoming_msgs.next().unwrap(), 9821);
|
||||
assert_eq!(incoming_msgs.next().unwrap(), 39999999);
|
||||
assert_eq!(incoming_msgs.next().unwrap(), 17);
|
||||
}
|
@ -9,6 +9,7 @@ use std::{
|
||||
net::SocketAddr,
|
||||
thread,
|
||||
time::Duration,
|
||||
sync::mpsc::TryRecvError,
|
||||
};
|
||||
|
||||
// External
|
||||
@ -126,14 +127,18 @@ where
|
||||
|
||||
for event in events {
|
||||
match event.token() {
|
||||
DATA_TOKEN => match self.recv.try_recv() {
|
||||
Ok(Ok(item)) => items.push_back(item),
|
||||
Err(err) => self.err = Some(err.into()),
|
||||
Ok(Err(err)) => self.err = Some(err.into()),
|
||||
DATA_TOKEN => loop {
|
||||
match self.recv.try_recv() {
|
||||
Ok(Ok(item)) => items.push_back(item),
|
||||
Err(TryRecvError::Empty) => break,
|
||||
Err(err) => self.err = Some(err.into()),
|
||||
Ok(Err(err)) => self.err = Some(err.into()),
|
||||
}
|
||||
},
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
|
||||
items.into_iter()
|
||||
}
|
||||
}
|
||||
@ -148,15 +153,17 @@ fn postbox_thread<S, R>(
|
||||
S: PostSend,
|
||||
R: PostRecv,
|
||||
{
|
||||
let mut events = Events::with_capacity(64);
|
||||
// Receiving related variables
|
||||
let mut events = Events::with_capacity(64);
|
||||
let mut recv_buff = Vec::new();
|
||||
let mut recv_nextlen: u64 = 0;
|
||||
loop {
|
||||
let mut disconnected = false;
|
||||
poll.poll(&mut events, None)
|
||||
poll.poll(&mut events, Some(Duration::from_millis(20)))
|
||||
.expect("Failed to execute poll(), most likely fault of the OS");
|
||||
println!("FINISHED POLL!");
|
||||
for event in events.iter() {
|
||||
println!("EVENT!");
|
||||
match event.token() {
|
||||
CTRL_TOKEN => match ctrl_rx.try_recv().unwrap() {
|
||||
ControlMsg::Shutdown => return,
|
||||
@ -165,16 +172,17 @@ fn postbox_thread<S, R>(
|
||||
Ok(_) => {}
|
||||
// Returned when all the data has been read
|
||||
Err(ref e) if e.kind() == ErrorKind::WouldBlock => {}
|
||||
Err(e) => {
|
||||
recv_tx.send(Err(e.into())).unwrap();
|
||||
}
|
||||
Err(e) => recv_tx.send(Err(e.into())).unwrap(),
|
||||
},
|
||||
DATA_TOKEN => {
|
||||
let mut packet = bincode::serialize(&send_rx.try_recv().unwrap()).unwrap();
|
||||
let msg = send_rx.try_recv().unwrap();
|
||||
println!("Send: {:?}", msg);
|
||||
let mut packet = bincode::serialize(&msg).unwrap();
|
||||
packet.splice(0..0, (packet.len() as u64).to_be_bytes().iter().cloned());
|
||||
match connection.write_bufs(&[packet.as_slice().into()]) {
|
||||
Ok(_) => {}
|
||||
Ok(_) => { println!("Sent!"); }
|
||||
Err(e) => {
|
||||
println!("Send error!");
|
||||
recv_tx.send(Err(e.into())).unwrap();
|
||||
}
|
||||
};
|
||||
@ -184,9 +192,9 @@ fn postbox_thread<S, R>(
|
||||
}
|
||||
loop {
|
||||
if recv_nextlen == 0 && recv_buff.len() >= 8 {
|
||||
println!("Read nextlen");
|
||||
recv_nextlen = u64::from_be_bytes(
|
||||
<[u8; 8]>::try_from(recv_buff.drain(0..8).collect::<Vec<u8>>().as_slice())
|
||||
.unwrap(),
|
||||
<[u8; 8]>::try_from(recv_buff.drain(0..8).collect::<Vec<u8>>().as_slice()).unwrap(),
|
||||
);
|
||||
if recv_nextlen > MESSAGE_SIZE_CAP {
|
||||
recv_tx.send(Err(PostErrorInternal::MsgSizeLimitExceeded)).unwrap();
|
||||
@ -204,16 +212,17 @@ fn postbox_thread<S, R>(
|
||||
)
|
||||
.collect::<Vec<u8>>()
|
||||
.as_slice()) {
|
||||
Ok(ok) => {
|
||||
Ok(msg) => {
|
||||
println!("Recv: {:?}", msg);
|
||||
recv_tx
|
||||
.send(Ok(ok))
|
||||
.send(Ok(msg))
|
||||
.unwrap();
|
||||
recv_nextlen = 0;
|
||||
}
|
||||
Err(e) => {
|
||||
println!("Recv error: {:?}", e);
|
||||
recv_tx.send(Err(e.into())).unwrap();
|
||||
recv_nextlen = 0;
|
||||
continue
|
||||
}
|
||||
}
|
||||
} else {
|
||||
|
@ -4,6 +4,7 @@ use std::{
|
||||
collections::VecDeque,
|
||||
net::SocketAddr,
|
||||
thread,
|
||||
sync::mpsc::TryRecvError,
|
||||
};
|
||||
|
||||
// External
|
||||
@ -93,11 +94,13 @@ where
|
||||
|
||||
for event in events {
|
||||
match event.token() {
|
||||
// Ignore recv error
|
||||
DATA_TOKEN => match self.recv.try_recv() {
|
||||
Ok(Ok(conn)) => conns.push_back(conn),
|
||||
Err(err) => self.err = Some(err.into()),
|
||||
Ok(Err(err)) => self.err = Some(err.into()),
|
||||
DATA_TOKEN => loop {
|
||||
match self.recv.try_recv() {
|
||||
Ok(Ok(conn)) => conns.push_back(conn),
|
||||
Err(TryRecvError::Empty) => break,
|
||||
Err(err) => self.err = Some(err.into()),
|
||||
Ok(Err(err)) => self.err = Some(err.into()),
|
||||
}
|
||||
},
|
||||
_ => (),
|
||||
}
|
||||
|
@ -1,78 +0,0 @@
|
||||
use std::{
|
||||
io::Write,
|
||||
str::FromStr,
|
||||
net::SocketAddr,
|
||||
thread,
|
||||
time::Duration,
|
||||
};
|
||||
|
||||
use mio::{net::TcpStream, Events, Poll, PollOpt, Ready, Token};
|
||||
|
||||
use super::{error::PostError, PostBox, PostOffice};
|
||||
|
||||
fn new_local_addr(n: u16) -> SocketAddr {
|
||||
SocketAddr::from(([127, 0, 0, 1], 12345 + n))
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn basic_run() {
|
||||
let srv_addr = new_local_addr(0);
|
||||
let mut server: PostOffice<String, String> = PostOffice::new(srv_addr).unwrap();
|
||||
let mut client: PostBox<String, String> = PostBox::to_server(srv_addr).unwrap();
|
||||
std::thread::sleep(std::time::Duration::from_millis(10));
|
||||
let mut scon = server.new_connections().next().unwrap();
|
||||
std::thread::sleep(std::time::Duration::from_millis(10));
|
||||
scon.send(String::from("foo")).unwrap();
|
||||
client.send(String::from("bar")).unwrap();
|
||||
std::thread::sleep(std::time::Duration::from_millis(10));
|
||||
assert_eq!("foo", client.new_messages().next().unwrap());
|
||||
assert_eq!("bar", scon.new_messages().next().unwrap());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn huge_size_header() {
|
||||
let srv_addr = new_local_addr(1);
|
||||
|
||||
let mut server: PostOffice<String, String> = PostOffice::new(srv_addr).unwrap();
|
||||
let mut client = TcpStream::connect(&srv_addr).unwrap();
|
||||
std::thread::sleep(std::time::Duration::from_millis(10));
|
||||
let mut scon = server.new_connections().next().unwrap();
|
||||
std::thread::sleep(std::time::Duration::from_millis(10));
|
||||
client.write(&[0xffu8; 64]).unwrap();
|
||||
std::thread::sleep(std::time::Duration::from_millis(10));
|
||||
assert_eq!(scon.new_messages().next(), None);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn disconnect() {
|
||||
let srv_addr = new_local_addr(2);
|
||||
|
||||
let mut server = PostOffice::<_, String>::new(srv_addr)
|
||||
.unwrap();
|
||||
|
||||
// Create then close client
|
||||
{
|
||||
PostBox::<String, String>::to_server(srv_addr).unwrap();
|
||||
}
|
||||
|
||||
std::thread::sleep(std::time::Duration::from_millis(10));
|
||||
|
||||
let mut to_client = server
|
||||
.new_connections()
|
||||
.next()
|
||||
.unwrap();
|
||||
|
||||
to_client.send(String::from("foo")).unwrap();
|
||||
|
||||
thread::sleep(Duration::from_millis(10));
|
||||
|
||||
match to_client.new_messages().next() {
|
||||
None => {},
|
||||
_ => panic!("Unexpected message!"),
|
||||
}
|
||||
|
||||
match to_client.status() {
|
||||
Some(PostError::Disconnected) => {},
|
||||
s => panic!("Did not expect {:?}", s),
|
||||
}
|
||||
}
|
@ -1,23 +1,24 @@
|
||||
// Standard
|
||||
use std::time::Duration;
|
||||
|
||||
// Library
|
||||
use shred::{Fetch, FetchMut};
|
||||
use specs::{
|
||||
Builder,
|
||||
Component,
|
||||
DispatcherBuilder,
|
||||
EntityBuilder as EcsEntityBuilder,
|
||||
Entity as EcsEntity,
|
||||
World as EcsWorld,
|
||||
storage::{
|
||||
Storage as EcsStorage,
|
||||
MaskedStorage as EcsMaskedStorage,
|
||||
},
|
||||
saveload::{MarkedBuilder, MarkerAllocator},
|
||||
};
|
||||
use vek::*;
|
||||
|
||||
// Crate
|
||||
use crate::{comp, sys, terrain::TerrainMap};
|
||||
use crate::{
|
||||
comp,
|
||||
sys,
|
||||
terrain::TerrainMap,
|
||||
};
|
||||
|
||||
/// How much faster should an in-game day be compared to a real day?
|
||||
// TODO: Don't hard-code this
|
||||
@ -90,19 +91,43 @@ impl State {
|
||||
self
|
||||
}
|
||||
|
||||
/// Delete an entity from the state's ECS, if it exists
|
||||
pub fn delete_entity(&mut self, entity: EcsEntity) {
|
||||
let _ = self.ecs_world.delete_entity(entity);
|
||||
/// 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>()
|
||||
}
|
||||
|
||||
// TODO: Get rid of this
|
||||
pub fn new_test_player(&mut self) -> EcsEntity {
|
||||
/// 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
|
||||
.create_entity()
|
||||
.with(comp::phys::Pos(Vec3::default()))
|
||||
.with(comp::phys::Vel(Vec3::default()))
|
||||
.with(comp::phys::Dir(Vec3::default()))
|
||||
.build()
|
||||
.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
|
||||
@ -110,11 +135,6 @@ impl State {
|
||||
let _ = self.ecs_world.write_storage().insert(entity, comp);
|
||||
}
|
||||
|
||||
/// Read a clone of a component attributed to a particular entity
|
||||
pub fn read_component<C: Component + Clone>(&self, entity: EcsEntity) -> Option<C> {
|
||||
self.ecs_world.read_storage::<C>().get(entity).cloned()
|
||||
}
|
||||
|
||||
/// 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>>> {
|
||||
self.ecs_world.read_storage::<C>()
|
||||
@ -167,6 +187,9 @@ impl State {
|
||||
|
||||
/// Execute a single tick, simulating the game state by the given 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
|
||||
self.ecs_world.write_resource::<TimeOfDay>().0 += dt.as_secs_f64() * DAY_CYCLE_FACTOR;
|
||||
self.ecs_world.write_resource::<Time>().0 += dt.as_secs_f64();
|
||||
|
@ -3,7 +3,7 @@ use log::info;
|
||||
use server::{Input, Event, Server};
|
||||
use common::clock::Clock;
|
||||
|
||||
const FPS: u64 = 60;
|
||||
const TPS: u64 = 30;
|
||||
|
||||
fn main() {
|
||||
// Init logging
|
||||
@ -24,9 +24,9 @@ fn main() {
|
||||
|
||||
for event in events {
|
||||
match event {
|
||||
Event::ClientConnected { ecs_entity } => println!("Client connected!"),
|
||||
Event::ClientDisconnected { ecs_entity } => println!("Client disconnected!"),
|
||||
Event::Chat { msg, .. } => println!("[chat] {}", msg),
|
||||
Event::ClientConnected { ecs_entity } => info!("Client connected!"),
|
||||
Event::ClientDisconnected { ecs_entity } => info!("Client disconnected!"),
|
||||
Event::Chat { ecs_entity, msg } => info!("[Client] {}", msg),
|
||||
}
|
||||
}
|
||||
|
||||
@ -34,6 +34,6 @@ fn main() {
|
||||
server.cleanup();
|
||||
|
||||
// Wait for the next tick
|
||||
clock.tick(Duration::from_millis(1000 / FPS));
|
||||
clock.tick(Duration::from_millis(1000 / TPS));
|
||||
}
|
||||
}
|
||||
|
@ -1,5 +1,6 @@
|
||||
use specs::Entity as EcsEntity;
|
||||
use common::{
|
||||
comp,
|
||||
msg::{ServerMsg, ClientMsg},
|
||||
net::PostBox,
|
||||
};
|
||||
@ -32,9 +33,15 @@ impl Clients {
|
||||
|
||||
pub fn notify_all(&mut self, msg: ServerMsg) {
|
||||
for client in &mut self.clients {
|
||||
// Consume any errors, deal with them later
|
||||
let _ = client.postbox.send(msg.clone());
|
||||
println!("Sending message...");
|
||||
client.postbox.send(msg.clone());
|
||||
}
|
||||
}
|
||||
|
||||
pub fn notify_all_except(&mut self, ecs_entity: EcsEntity, msg: ServerMsg) {
|
||||
for client in &mut self.clients {
|
||||
if client.ecs_entity != ecs_entity {
|
||||
client.postbox.send(msg.clone());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -61,15 +61,11 @@ impl Server {
|
||||
/// Create a new `Server`.
|
||||
#[allow(dead_code)]
|
||||
pub fn new() -> Result<Self, Error> {
|
||||
let mut state = State::new();
|
||||
|
||||
state.ecs_world_mut().add_resource(comp::UidAllocator::new());
|
||||
|
||||
Ok(Self {
|
||||
state,
|
||||
state: State::new(),
|
||||
world: World::new(),
|
||||
|
||||
postoffice: PostOffice::new(SocketAddr::from(([0; 4], 59003)))?,
|
||||
postoffice: PostOffice::bind(SocketAddr::from(([0; 4], 59003)))?,
|
||||
clients: Clients::empty(),
|
||||
})
|
||||
}
|
||||
@ -81,18 +77,13 @@ impl Server {
|
||||
#[allow(dead_code)]
|
||||
pub fn state_mut(&mut self) -> &mut State { &mut self.state }
|
||||
|
||||
/// Build a new entity with a generated UID
|
||||
pub fn build_entity(&mut self) -> EcsEntityBuilder {
|
||||
self.state.ecs_world_mut().create_entity()
|
||||
.marked::<comp::Uid>()
|
||||
}
|
||||
|
||||
/// Build a new player with a generated UID
|
||||
pub fn build_player(&mut self) -> EcsEntityBuilder {
|
||||
self.build_entity()
|
||||
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.
|
||||
@ -123,7 +114,7 @@ impl Server {
|
||||
let mut frontend_events = Vec::new();
|
||||
|
||||
// If networking has problems, handle them
|
||||
if let Some(err) = self.postoffice.status() {
|
||||
if let Some(err) = self.postoffice.error() {
|
||||
return Err(err.into());
|
||||
}
|
||||
|
||||
@ -154,19 +145,25 @@ impl Server {
|
||||
fn handle_new_connections(&mut self) -> Result<Vec<Event>, Error> {
|
||||
let mut frontend_events = Vec::new();
|
||||
|
||||
for postbox in self.postoffice.new_connections() {
|
||||
for mut postbox in self.postoffice.new_connections() {
|
||||
let ecs_entity = self.build_player()
|
||||
// When the player is first created, force a physics notification to everyone
|
||||
// including themselves.
|
||||
.with(comp::phys::UpdateKind::Force)
|
||||
.build();
|
||||
let uid = self.state.read_storage().get(ecs_entity).cloned().unwrap();
|
||||
|
||||
frontend_events.push(Event::ClientConnected {
|
||||
ecs_entity,
|
||||
});
|
||||
postbox.send(ServerMsg::SetPlayerEntity(uid));
|
||||
|
||||
self.clients.add(Client {
|
||||
ecs_entity,
|
||||
postbox,
|
||||
last_ping: self.state.get_time(),
|
||||
});
|
||||
|
||||
frontend_events.push(Event::ClientConnected {
|
||||
ecs_entity,
|
||||
});
|
||||
}
|
||||
|
||||
Ok(frontend_events)
|
||||
@ -178,6 +175,7 @@ impl Server {
|
||||
|
||||
let state = &mut self.state;
|
||||
let mut new_chat_msgs = Vec::new();
|
||||
let mut disconnected_clients = Vec::new();
|
||||
|
||||
self.clients.remove_if(|client| {
|
||||
let mut disconnected = false;
|
||||
@ -190,22 +188,29 @@ impl Server {
|
||||
// Process incoming messages
|
||||
for msg in new_msgs {
|
||||
match msg {
|
||||
ClientMsg::Ping => client.postbox.send(ServerMsg::Pong),
|
||||
ClientMsg::Pong => {},
|
||||
ClientMsg::Chat(msg) => new_chat_msgs.push((client.ecs_entity, msg)),
|
||||
ClientMsg::PlayerPhysics { pos, vel, dir } => {
|
||||
state.write_component(client.ecs_entity, pos);
|
||||
state.write_component(client.ecs_entity, vel);
|
||||
state.write_component(client.ecs_entity, dir);
|
||||
},
|
||||
ClientMsg::Disconnect => disconnected = true,
|
||||
}
|
||||
}
|
||||
} else if
|
||||
state.get_time() - client.last_ping > CLIENT_TIMEOUT || // Timeout
|
||||
client.postbox.status().is_some() // Postbox eror
|
||||
client.postbox.error().is_some() // Postbox error
|
||||
{
|
||||
disconnected = true;
|
||||
} else if state.get_time() - client.last_ping > CLIENT_TIMEOUT * 0.5 {
|
||||
// Try pinging the client if the timeout is nearing
|
||||
client.postbox.send(ServerMsg::Ping);
|
||||
}
|
||||
|
||||
if disconnected {
|
||||
state.delete_entity(client.ecs_entity);
|
||||
frontend_events.push(Event::ClientDisconnected {
|
||||
ecs_entity: client.ecs_entity,
|
||||
});
|
||||
disconnected_clients.push(client.ecs_entity);
|
||||
true
|
||||
} else {
|
||||
false
|
||||
@ -222,23 +227,52 @@ impl Server {
|
||||
});
|
||||
}
|
||||
|
||||
// Handle client disconnects
|
||||
for ecs_entity in disconnected_clients {
|
||||
self.clients.notify_all(ServerMsg::EntityDeleted(state.read_storage().get(ecs_entity).cloned().unwrap()));
|
||||
|
||||
frontend_events.push(Event::ClientDisconnected {
|
||||
ecs_entity,
|
||||
});
|
||||
|
||||
state.ecs_world_mut().delete_entity(ecs_entity);
|
||||
}
|
||||
|
||||
Ok(frontend_events)
|
||||
}
|
||||
|
||||
/// Sync client states with the most up to date information
|
||||
fn sync_clients(&mut self) {
|
||||
for (&uid, &pos, &vel, &dir) in (
|
||||
for (entity, &uid, &pos, &vel, &dir, update_kind) in (
|
||||
&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() {
|
||||
self.clients.notify_all(ServerMsg::EntityPhysics {
|
||||
uid: uid.into(),
|
||||
let msg = ServerMsg::EntityPhysics {
|
||||
uid,
|
||||
pos,
|
||||
vel,
|
||||
dir,
|
||||
});
|
||||
};
|
||||
|
||||
// Sometimes we need to force updated (i.e: teleporting players). This involves sending
|
||||
// everyone, including the player themselves, of their new physics information.
|
||||
match update_kind {
|
||||
comp::phys::UpdateKind::Force => self.clients.notify_all(msg),
|
||||
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 {
|
||||
fn drop(&mut self) {
|
||||
self.clients.notify_all(ServerMsg::Shutdown);
|
||||
}
|
||||
}
|
||||
|
@ -19,6 +19,9 @@ gfx_device_gl = { version = "0.15", optional = true }
|
||||
gfx_window_glutin = "0.28"
|
||||
glutin = "0.19"
|
||||
|
||||
# ECS
|
||||
specs = "0.14"
|
||||
|
||||
# Mathematics
|
||||
vek = "0.9"
|
||||
|
||||
|
@ -15,10 +15,10 @@ use super::{
|
||||
pub struct CharacterSkeleton {
|
||||
head: Bone,
|
||||
chest: Bone,
|
||||
bl_foot: Bone,
|
||||
br_foot: Bone,
|
||||
r_hand: Bone,
|
||||
belt: Bone,
|
||||
shorts: Bone,
|
||||
l_hand: Bone,
|
||||
r_hand: Bone,
|
||||
l_foot: Bone,
|
||||
r_foot: Bone,
|
||||
back: Bone,
|
||||
@ -29,10 +29,10 @@ impl CharacterSkeleton {
|
||||
Self {
|
||||
head: Bone::default(),
|
||||
chest: Bone::default(),
|
||||
br_foot: Bone::default(),
|
||||
bl_foot: Bone::default(),
|
||||
r_hand: Bone::default(),
|
||||
belt: Bone::default(),
|
||||
shorts: Bone::default(),
|
||||
l_hand: Bone::default(),
|
||||
r_hand: Bone::default(),
|
||||
l_foot: Bone::default(),
|
||||
r_foot: Bone::default(),
|
||||
back: Bone::default(),
|
||||
@ -47,10 +47,10 @@ impl Skeleton for CharacterSkeleton {
|
||||
[
|
||||
FigureBoneData::new(self.head.compute_base_matrix()),
|
||||
FigureBoneData::new(chest_mat),
|
||||
FigureBoneData::new(self.bl_foot.compute_base_matrix()),
|
||||
FigureBoneData::new(self.br_foot.compute_base_matrix()),
|
||||
FigureBoneData::new(self.r_hand.compute_base_matrix()),
|
||||
FigureBoneData::new(self.belt.compute_base_matrix()),
|
||||
FigureBoneData::new(self.shorts.compute_base_matrix()),
|
||||
FigureBoneData::new(self.l_hand.compute_base_matrix()),
|
||||
FigureBoneData::new(self.r_hand.compute_base_matrix()),
|
||||
FigureBoneData::new(self.l_foot.compute_base_matrix()),
|
||||
FigureBoneData::new(self.r_foot.compute_base_matrix()),
|
||||
FigureBoneData::new(chest_mat * self.back.compute_base_matrix()),
|
||||
|
@ -21,34 +21,30 @@ impl Animation for RunAnimation {
|
||||
time: f64,
|
||||
) {
|
||||
let wave = (time as f32 * 12.0).sin();
|
||||
let wavecos = (time as f32 * 12.0).cos();
|
||||
let wave_slow = (time as f32 * 6.0 + PI).sin();
|
||||
let wavecos_slow = (time as f32 * 6.0 + PI).cos();
|
||||
let wave_dip = (wave_slow.abs() - 0.5).abs();
|
||||
|
||||
skeleton.head.offset = Vec3::new(0.0, 0.0, 0.0);
|
||||
skeleton.head.ori = Quaternion::rotation_x(0.0);
|
||||
skeleton.head.offset = Vec3::unit_z() * 13.0;
|
||||
skeleton.head.ori = Quaternion::rotation_z(wave * 0.3);
|
||||
|
||||
skeleton.chest.offset = Vec3::new(0.0, 0.0, 0.0);
|
||||
skeleton.chest.ori = Quaternion::rotation_x(0.0);
|
||||
skeleton.chest.offset = Vec3::unit_z() * 9.0;
|
||||
skeleton.chest.ori = Quaternion::rotation_z(wave * 0.3);
|
||||
|
||||
//skeleton.br_foot.offset = Vec3::new(0.0, wavecos_slow * 1.0, wave_slow * 2.0 + wave_dip * 1.0);
|
||||
//skeleton.br_foot.ori = Quaternion::rotation_x(0.0 + wave_slow * 10.1);
|
||||
skeleton.belt.offset = Vec3::unit_z() * 7.0;
|
||||
skeleton.belt.ori = Quaternion::rotation_z(wave * 0.2);
|
||||
|
||||
skeleton.bl_foot.offset = Vec3::new(0.0, 0.0, 0.0);
|
||||
skeleton.bl_foot.ori = Quaternion::rotation_x(wave_slow * 2.0);
|
||||
//skeleton.bl_foot.offset = Vec3::new(0.0, wavecos_slow * 1.0, wave_slow * 2.0 + wave_dip * 1.0);
|
||||
//skeleton.bl_foot.ori = Quaternion::rotation_x(0.5 + wave_slow * 0.1);
|
||||
skeleton.shorts.offset = Vec3::unit_z() * 4.0;
|
||||
skeleton.shorts.ori = Quaternion::rotation_z(wave * 0.1);
|
||||
|
||||
//skeleton.r_hand.offset = Vec3::new(0.0, wavecos_slow * 1.0, wave_slow * 2.0 + wave_dip * 1.0);
|
||||
//skeleton.r_hand.ori = Quaternion::rotation_x(0.5 + wave_slow * 0.1);
|
||||
|
||||
skeleton.l_hand.offset = Vec3::new(0.0, 0.0, 0.0);
|
||||
skeleton.l_hand.ori = Quaternion::rotation_x(wave_slow * 2.0);
|
||||
|
||||
//skeleton.l_hand.offset = Vec3::new(0.0, wavecos_slow * 1.0, wave_slow * 2.0 + wave_dip * 1.0);
|
||||
//skeleton.l_hand.ori = Quaternion::rotation_x(0.5 + wave_slow * 0.1);
|
||||
skeleton.l_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);
|
||||
|
||||
skeleton.l_foot.offset = Vec3::new(-3.5, 1.0 - wave * 8.0, 3.5 - wave_dip * 4.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.ori = Quaternion::rotation_x(wave + 1.0);
|
||||
|
||||
skeleton.back.offset = Vec3::new(-9.0, 5.0, 18.0);
|
||||
skeleton.back.ori = Quaternion::rotation_y(2.5);
|
||||
}
|
||||
}
|
||||
|
@ -1,4 +1,5 @@
|
||||
// Crate
|
||||
use specs::{Component, VecStorage};
|
||||
use vek::*;
|
||||
use crate::{
|
||||
Error,
|
||||
render::{
|
||||
@ -77,3 +78,16 @@ impl<S: Skeleton> Figure<S> {
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
pub struct Figure<S: Skeleton> {
|
||||
bone_consts: Consts<FigureBoneData>,
|
||||
locals: Consts<FigureLocals>,
|
||||
skeleton: S,
|
||||
}
|
||||
|
||||
impl<S: Skeleton> Component for Figure<S> {
|
||||
type Storage = VecStorage<Self>;
|
||||
}
|
||||
*/
|
||||
|
@ -2,15 +2,13 @@ pub mod camera;
|
||||
pub mod figure;
|
||||
pub mod terrain;
|
||||
|
||||
// Library
|
||||
use vek::*;
|
||||
use dot_vox;
|
||||
|
||||
// Project
|
||||
use common::figure::Segment;
|
||||
use common::{
|
||||
comp,
|
||||
figure::Segment,
|
||||
};
|
||||
use client::Client;
|
||||
|
||||
// Crate
|
||||
use crate::{
|
||||
render::{
|
||||
Consts,
|
||||
@ -29,8 +27,6 @@ use crate::{
|
||||
character::{CharacterSkeleton, RunAnimation},
|
||||
},
|
||||
};
|
||||
|
||||
// Local
|
||||
use self::{
|
||||
camera::Camera,
|
||||
figure::Figure,
|
||||
@ -82,15 +78,15 @@ impl Scene {
|
||||
test_figure: Figure::new(
|
||||
renderer,
|
||||
[
|
||||
Some(load_segment("dragonhead.vox").generate_mesh(Vec3::new(2.0, -12.0, 2.0))),
|
||||
Some(load_segment("dragon_body.vox").generate_mesh(Vec3::new(0.0, 0.0, 0.0))),
|
||||
Some(load_segment("dragon_lfoot.vox").generate_mesh(Vec3::new(0.0, 10.0, -4.0))),
|
||||
Some(load_segment("dragon_rfoot.vox").generate_mesh(Vec3::new(0.0, 10.0, -4.0))),
|
||||
Some(load_segment("dragon_rfoot.vox").generate_mesh(Vec3::new(0.0, -10.0, -4.0))),
|
||||
Some(load_segment("dragon_lfoot.vox").generate_mesh(Vec3::new(0.0, 0.0, 0.0))),
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
Some(load_segment("head.vox").generate_mesh(Vec3::new(-7.0, -5.5, -1.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,
|
||||
@ -138,6 +134,21 @@ impl Scene {
|
||||
|
||||
/// 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) {
|
||||
// Get player position
|
||||
let player_pos = client
|
||||
.player()
|
||||
.and_then(|ent| client
|
||||
.state()
|
||||
.ecs_world()
|
||||
.read_storage::<comp::phys::Pos>()
|
||||
.get(ent)
|
||||
.map(|pos| pos.0)
|
||||
)
|
||||
.unwrap_or(Vec3::zero());
|
||||
|
||||
// Alter camera position to match player
|
||||
self.camera.set_focus_pos(player_pos);
|
||||
|
||||
// Compute camera matrices
|
||||
let (view_mat, proj_mat, cam_pos) = self.camera.compute_dependents();
|
||||
|
||||
@ -161,7 +172,11 @@ impl Scene {
|
||||
&mut self.test_figure.skeleton,
|
||||
client.state().get_time(),
|
||||
);
|
||||
self.test_figure.update_locals(renderer, FigureLocals::default()).unwrap();
|
||||
|
||||
// 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();
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user