Merge branch 'master' into 'master'

Added multi-character rendering, integrated Sphynx, network overhaul

See merge request veloren/veloren!19

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -1,20 +1,7 @@
pub mod character;
pub mod player;
pub mod phys;
pub mod uid;
pub mod util;
// Reexports
pub use uid::{Uid, UidAllocator};
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>();
}
pub use character::Character;
pub use player::Player;

View File

@ -1,5 +1,4 @@
// Library
use specs::{Component, VecStorage};
use specs::{Component, VecStorage, FlaggedStorage};
use vek::*;
// Pos
@ -8,7 +7,7 @@ use vek::*;
pub struct Pos(pub Vec3<f32>);
impl Component for Pos {
type Storage = VecStorage<Self>;
type Storage = FlaggedStorage<Self, VecStorage<Self>>;
}
// Vel
@ -17,7 +16,7 @@ impl Component for Pos {
pub struct Vel(pub Vec3<f32>);
impl Component for Vel {
type Storage = VecStorage<Self>;
type Storage = FlaggedStorage<Self, VecStorage<Self>>;
}
// Dir
@ -26,17 +25,5 @@ impl Component for Vel {
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>;
type Storage = FlaggedStorage<Self, VecStorage<Self>>;
}

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

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

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

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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