Merge branch 'master' into 'master'

Networked entities

See merge request veloren/fresh!12

Former-commit-id: 0aab14fdc18f0567c662589dda5d2cf518072431
This commit is contained in:
Joshua Barretto 2019-03-19 19:54:09 +00:00
commit a32d977b32
25 changed files with 938 additions and 230 deletions

View File

@ -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 {

View File

@ -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),
}
}

View File

@ -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);
}
}

View File

@ -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"

View 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>;
}

View File

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

View File

@ -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>;
}

View File

@ -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
View 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>;
}

View File

@ -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,
}

View File

@ -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),
}

View File

@ -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
View 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);
}

View File

@ -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 {

View File

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

View File

@ -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),
}
}

View File

@ -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();

View File

@ -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));
}
}

View File

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

View File

@ -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);
}
}

View File

@ -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"

View File

@ -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()),

View File

@ -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);
}
}

View File

@ -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>;
}
*/

View File

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