mirror of
https://gitlab.com/veloren/veloren.git
synced 2024-08-30 18:12:32 +00:00
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:
commit
07698f46ba
@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "veloren-chat-cli"
|
name = "veloren-chat-cli"
|
||||||
version = "0.1.0"
|
version = "0.2.0"
|
||||||
authors = ["Joshua Barretto <joshua.s.barretto@gmail.com>"]
|
authors = ["Joshua Barretto <joshua.s.barretto@gmail.com>"]
|
||||||
edition = "2018"
|
edition = "2018"
|
||||||
|
|
||||||
|
@ -1,7 +1,10 @@
|
|||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
use log::info;
|
use log::info;
|
||||||
use client::{Input, Client, Event};
|
use client::{Input, Client, Event};
|
||||||
use common::clock::Clock;
|
use common::{
|
||||||
|
comp,
|
||||||
|
clock::Clock,
|
||||||
|
};
|
||||||
|
|
||||||
const FPS: u64 = 60;
|
const FPS: u64 = 60;
|
||||||
|
|
||||||
@ -15,7 +18,7 @@ fn main() {
|
|||||||
let mut clock = Clock::new();
|
let mut clock = Clock::new();
|
||||||
|
|
||||||
// Create client
|
// Create client
|
||||||
let mut client = Client::new(([127, 0, 0, 1], 59003))
|
let mut client = Client::new(([127, 0, 0, 1], 59003), comp::Player::new("test".to_string()), None, 300)
|
||||||
.expect("Failed to create client instance");
|
.expect("Failed to create client instance");
|
||||||
|
|
||||||
client.send_chat("Hello!".to_string());
|
client.send_chat("Hello!".to_string());
|
||||||
|
@ -1,12 +1,11 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "veloren-client"
|
name = "veloren-client"
|
||||||
version = "0.1.0"
|
version = "0.2.0"
|
||||||
authors = ["Joshua Barretto <joshua.s.barretto@gmail.com>"]
|
authors = ["Joshua Barretto <joshua.s.barretto@gmail.com>"]
|
||||||
edition = "2018"
|
edition = "2018"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
common = { package = "veloren-common", path = "../common" }
|
common = { package = "veloren-common", path = "../common" }
|
||||||
world = { package = "veloren-world", path = "../world" }
|
|
||||||
|
|
||||||
specs = "0.14"
|
specs = "0.14"
|
||||||
vek = "0.9"
|
vek = "0.9"
|
||||||
|
@ -3,6 +3,7 @@ use common::net::PostError;
|
|||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub enum Error {
|
pub enum Error {
|
||||||
Network(PostError),
|
Network(PostError),
|
||||||
|
ServerWentMad,
|
||||||
ServerTimeout,
|
ServerTimeout,
|
||||||
ServerShutdown,
|
ServerShutdown,
|
||||||
Other(String),
|
Other(String),
|
||||||
@ -10,9 +11,6 @@ pub enum Error {
|
|||||||
|
|
||||||
impl From<PostError> for Error {
|
impl From<PostError> for Error {
|
||||||
fn from(err: PostError) -> Self {
|
fn from(err: PostError) -> Self {
|
||||||
match err {
|
Error::Network(err)
|
||||||
PostError::Disconnect => Error::ServerShutdown,
|
|
||||||
err => Error::Network(err),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -13,9 +13,10 @@ pub use crate::{
|
|||||||
use std::{
|
use std::{
|
||||||
time::Duration,
|
time::Duration,
|
||||||
net::SocketAddr,
|
net::SocketAddr,
|
||||||
|
collections::HashSet,
|
||||||
};
|
};
|
||||||
use vek::*;
|
use vek::*;
|
||||||
use threadpool;
|
use threadpool::ThreadPool;
|
||||||
use specs::Builder;
|
use specs::Builder;
|
||||||
use common::{
|
use common::{
|
||||||
comp,
|
comp,
|
||||||
@ -24,7 +25,6 @@ use common::{
|
|||||||
net::PostBox,
|
net::PostBox,
|
||||||
msg::{ClientMsg, ServerMsg},
|
msg::{ClientMsg, ServerMsg},
|
||||||
};
|
};
|
||||||
use world::World;
|
|
||||||
|
|
||||||
const SERVER_TIMEOUT: f64 = 5.0; // Seconds
|
const SERVER_TIMEOUT: f64 = 5.0; // Seconds
|
||||||
|
|
||||||
@ -33,7 +33,7 @@ pub enum Event {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub struct Client {
|
pub struct Client {
|
||||||
thread_pool: threadpool::ThreadPool,
|
thread_pool: ThreadPool,
|
||||||
|
|
||||||
last_ping: f64,
|
last_ping: f64,
|
||||||
postbox: PostBox<ClientMsg, ServerMsg>,
|
postbox: PostBox<ClientMsg, ServerMsg>,
|
||||||
@ -41,19 +41,38 @@ pub struct Client {
|
|||||||
tick: u64,
|
tick: u64,
|
||||||
state: State,
|
state: State,
|
||||||
player: Option<EcsEntity>,
|
player: Option<EcsEntity>,
|
||||||
|
view_distance: u64,
|
||||||
|
|
||||||
// Testing
|
pending_chunks: HashSet<Vec3<i32>>,
|
||||||
world: World,
|
|
||||||
pub chunk: Option<TerrainChunk>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Client {
|
impl Client {
|
||||||
/// Create a new `Client`.
|
/// Create a new `Client`.
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
pub fn new<A: Into<SocketAddr>>(addr: A) -> Result<Self, Error> {
|
pub fn new<A: Into<SocketAddr>>(
|
||||||
let state = State::new();
|
addr: A,
|
||||||
|
player: comp::Player,
|
||||||
|
character: Option<comp::Character>,
|
||||||
|
view_distance: u64,
|
||||||
|
) -> Result<Self, Error> {
|
||||||
|
|
||||||
let mut postbox = PostBox::to_server(addr)?;
|
let mut postbox = PostBox::to(addr)?;
|
||||||
|
|
||||||
|
// Send connection request
|
||||||
|
postbox.send_message(ClientMsg::Connect {
|
||||||
|
player,
|
||||||
|
character,
|
||||||
|
});
|
||||||
|
|
||||||
|
// Wait for handshake from server
|
||||||
|
let (state, player) = match postbox.next_message() {
|
||||||
|
Some(ServerMsg::Handshake { ecs_state, player_entity }) => {
|
||||||
|
let mut state = State::from_state_package(ecs_state);
|
||||||
|
let player_entity = state.ecs().entity_from_uid(player_entity);
|
||||||
|
(state, player_entity)
|
||||||
|
},
|
||||||
|
_ => return Err(Error::ServerWentMad),
|
||||||
|
};
|
||||||
|
|
||||||
Ok(Self {
|
Ok(Self {
|
||||||
thread_pool: threadpool::Builder::new()
|
thread_pool: threadpool::Builder::new()
|
||||||
@ -65,11 +84,10 @@ impl Client {
|
|||||||
|
|
||||||
tick: 0,
|
tick: 0,
|
||||||
state,
|
state,
|
||||||
player: None,
|
player,
|
||||||
|
view_distance,
|
||||||
|
|
||||||
// Testing
|
pending_chunks: HashSet::new(),
|
||||||
world: World::new(),
|
|
||||||
chunk: None,
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -79,18 +97,6 @@ impl Client {
|
|||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
pub fn thread_pool(&self) -> &threadpool::ThreadPool { &self.thread_pool }
|
pub fn thread_pool(&self) -> &threadpool::ThreadPool { &self.thread_pool }
|
||||||
|
|
||||||
// TODO: Get rid of this
|
|
||||||
pub fn with_test_state(mut self) -> Self {
|
|
||||||
self.chunk = Some(self.world.generate_chunk(Vec3::zero()));
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: Get rid of this
|
|
||||||
pub fn load_chunk(&mut self, pos: Vec3<i32>) {
|
|
||||||
self.state.terrain_mut().insert(pos, self.world.generate_chunk(pos));
|
|
||||||
self.state.changes_mut().new_chunks.push(pos);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Get a reference to the client's game state.
|
/// Get a reference to the client's game state.
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
pub fn state(&self) -> &State { &self.state }
|
pub fn state(&self) -> &State { &self.state }
|
||||||
@ -114,7 +120,7 @@ impl Client {
|
|||||||
/// Send a chat message to the server
|
/// Send a chat message to the server
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
pub fn send_chat(&mut self, msg: String) {
|
pub fn send_chat(&mut self, msg: String) {
|
||||||
self.postbox.send(ClientMsg::Chat(msg))
|
self.postbox.send_message(ClientMsg::Chat(msg))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Execute a single client tick, handle input and update the game state by the given duration
|
/// Execute a single client tick, handle input and update the game state by the given duration
|
||||||
@ -138,12 +144,19 @@ impl Client {
|
|||||||
// Handle new messages from the server
|
// Handle new messages from the server
|
||||||
frontend_events.append(&mut self.handle_new_messages()?);
|
frontend_events.append(&mut self.handle_new_messages()?);
|
||||||
|
|
||||||
|
self.state.terrain().iter().for_each(|(k, _)| {
|
||||||
|
println!("Chunk at {:?}", k);
|
||||||
|
});
|
||||||
|
|
||||||
// Step 1
|
// Step 1
|
||||||
if let Some(ecs_entity) = self.player {
|
if let Some(ecs_entity) = self.player {
|
||||||
// TODO: remove this
|
// TODO: remove this
|
||||||
const PLAYER_VELOCITY: f32 = 100.0;
|
const PLAYER_VELOCITY: f32 = 100.0;
|
||||||
// TODO: Set acceleration instead
|
// TODO: Set acceleration instead
|
||||||
self.state.write_component(ecs_entity, comp::phys::Vel(Vec3::from(input.move_dir * PLAYER_VELOCITY)));
|
self.state.write_component(ecs_entity, comp::phys::Vel(Vec3::from(input.move_dir * PLAYER_VELOCITY) * 0.1));
|
||||||
|
if input.move_dir.magnitude() > 0.01 {
|
||||||
|
self.state.write_component(ecs_entity, comp::phys::Dir(input.move_dir.normalized().into()));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Tick the client's LocalState (step 3)
|
// Tick the client's LocalState (step 3)
|
||||||
@ -157,12 +170,31 @@ impl Client {
|
|||||||
self.state.read_storage().get(ecs_entity).cloned(),
|
self.state.read_storage().get(ecs_entity).cloned(),
|
||||||
) {
|
) {
|
||||||
(Some(pos), Some(vel), Some(dir)) => {
|
(Some(pos), Some(vel), Some(dir)) => {
|
||||||
self.postbox.send(ClientMsg::PlayerPhysics { pos, vel, dir });
|
self.postbox.send_message(ClientMsg::PlayerPhysics { pos, vel, dir });
|
||||||
},
|
},
|
||||||
_ => {},
|
_ => {},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Request chunks from the server
|
||||||
|
if let Some(player_entity) = self.player {
|
||||||
|
if let Some(pos) = self.state.read_storage::<comp::phys::Pos>().get(player_entity) {
|
||||||
|
let chunk_pos = self.state.terrain().pos_key(pos.0.map(|e| e as i32));
|
||||||
|
|
||||||
|
for i in chunk_pos.x - 0..chunk_pos.x + 1 {
|
||||||
|
for j in chunk_pos.y - 0..chunk_pos.y + 1 {
|
||||||
|
for k in 0..3 {
|
||||||
|
let key = chunk_pos + Vec3::new(i, j, k);
|
||||||
|
if self.state.terrain().get_key(key).is_none() && !self.pending_chunks.contains(&key) {
|
||||||
|
self.postbox.send_message(ClientMsg::TerrainChunkRequest { key });
|
||||||
|
self.pending_chunks.insert(key);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Finish the tick, pass control back to the frontend (step 6)
|
// Finish the tick, pass control back to the frontend (step 6)
|
||||||
self.tick += 1;
|
self.tick += 1;
|
||||||
Ok(frontend_events)
|
Ok(frontend_events)
|
||||||
@ -187,28 +219,16 @@ impl Client {
|
|||||||
|
|
||||||
for msg in new_msgs {
|
for msg in new_msgs {
|
||||||
match msg {
|
match msg {
|
||||||
|
ServerMsg::Handshake { .. } => return Err(Error::ServerWentMad),
|
||||||
ServerMsg::Shutdown => return Err(Error::ServerShutdown),
|
ServerMsg::Shutdown => return Err(Error::ServerShutdown),
|
||||||
ServerMsg::Ping => self.postbox.send(ClientMsg::Pong),
|
ServerMsg::Ping => self.postbox.send_message(ClientMsg::Pong),
|
||||||
ServerMsg::Pong => {},
|
ServerMsg::Pong => {},
|
||||||
ServerMsg::Chat(msg) => frontend_events.push(Event::Chat(msg)),
|
ServerMsg::Chat(msg) => frontend_events.push(Event::Chat(msg)),
|
||||||
ServerMsg::SetPlayerEntity(uid) => {
|
ServerMsg::SetPlayerEntity(uid) => self.player = Some(self.state.ecs().entity_from_uid(uid).unwrap()), // TODO: Don't unwrap here!
|
||||||
let ecs_entity = self.state
|
ServerMsg::EcsSync(sync_package) => self.state.ecs_mut().sync_with_package(sync_package),
|
||||||
.get_entity(uid)
|
ServerMsg::TerrainChunkUpdate { key, chunk } => {
|
||||||
.unwrap_or_else(|| self.state.build_uid_entity_with_uid(uid).build());
|
self.state.insert_chunk(key, *chunk);
|
||||||
|
self.pending_chunks.remove(&key);
|
||||||
self.player = Some(ecs_entity);
|
|
||||||
},
|
|
||||||
ServerMsg::EntityPhysics { uid, pos, vel, dir } => {
|
|
||||||
let ecs_entity = self.state
|
|
||||||
.get_entity(uid)
|
|
||||||
.unwrap_or_else(|| self.state.build_uid_entity_with_uid(uid).build());
|
|
||||||
|
|
||||||
self.state.write_component(ecs_entity, pos);
|
|
||||||
self.state.write_component(ecs_entity, vel);
|
|
||||||
self.state.write_component(ecs_entity, dir);
|
|
||||||
},
|
|
||||||
ServerMsg::EntityDeleted(uid) => {
|
|
||||||
self.state.delete_entity(uid);
|
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -218,7 +238,7 @@ impl Client {
|
|||||||
return Err(Error::ServerTimeout);
|
return Err(Error::ServerTimeout);
|
||||||
} else if self.state.get_time() - self.last_ping > SERVER_TIMEOUT * 0.5 {
|
} else if self.state.get_time() - self.last_ping > SERVER_TIMEOUT * 0.5 {
|
||||||
// Try pinging the server if the timeout is nearing
|
// Try pinging the server if the timeout is nearing
|
||||||
self.postbox.send(ClientMsg::Ping);
|
self.postbox.send_message(ClientMsg::Ping);
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(frontend_events)
|
Ok(frontend_events)
|
||||||
@ -227,6 +247,6 @@ impl Client {
|
|||||||
|
|
||||||
impl Drop for Client {
|
impl Drop for Client {
|
||||||
fn drop(&mut self) {
|
fn drop(&mut self) {
|
||||||
self.postbox.send(ClientMsg::Disconnect);
|
self.postbox.send_message(ClientMsg::Disconnect);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,10 +1,12 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "veloren-common"
|
name = "veloren-common"
|
||||||
version = "0.1.0"
|
version = "0.2.0"
|
||||||
authors = ["Joshua Barretto <joshua.s.barretto@gmail.com>", "Maciej Ćwięka <mckol363@gmail.com>"]
|
authors = ["Joshua Barretto <joshua.s.barretto@gmail.com>", "Maciej Ćwięka <mckol363@gmail.com>"]
|
||||||
edition = "2018"
|
edition = "2018"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
|
sphynx = { git = "https://gitlab.com/veloren/sphynx.git", features = ["serde1"] }
|
||||||
|
|
||||||
specs = { version = "0.14", features = ["serde"] }
|
specs = { version = "0.14", features = ["serde"] }
|
||||||
shred = { version = "0.7", features = ["nightly"] }
|
shred = { version = "0.7", features = ["nightly"] }
|
||||||
vek = { version = "0.9", features = ["serde"] }
|
vek = { version = "0.9", features = ["serde"] }
|
||||||
@ -16,4 +18,3 @@ serde = "1.0"
|
|||||||
serde_derive = "1.0"
|
serde_derive = "1.0"
|
||||||
bincode = "1.0"
|
bincode = "1.0"
|
||||||
log = "0.4"
|
log = "0.4"
|
||||||
pretty_env_logger = "0.3"
|
|
||||||
|
@ -1,9 +1,8 @@
|
|||||||
// Library
|
use specs::{Component, VecStorage, FlaggedStorage};
|
||||||
use specs::{Component, VecStorage};
|
|
||||||
use vek::*;
|
use vek::*;
|
||||||
|
|
||||||
#[derive(Copy, Clone, Debug, Serialize, Deserialize)]
|
#[derive(Copy, Clone, Debug, Serialize, Deserialize)]
|
||||||
enum Race {
|
pub enum Race {
|
||||||
Danari,
|
Danari,
|
||||||
Dwarf,
|
Dwarf,
|
||||||
Elf,
|
Elf,
|
||||||
@ -13,7 +12,7 @@ enum Race {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Copy, Clone, Debug, Serialize, Deserialize)]
|
#[derive(Copy, Clone, Debug, Serialize, Deserialize)]
|
||||||
pub struct Gender {
|
pub enum Gender {
|
||||||
Female,
|
Female,
|
||||||
Male,
|
Male,
|
||||||
Unspecified,
|
Unspecified,
|
||||||
@ -30,6 +29,21 @@ pub struct Character {
|
|||||||
feet: (),
|
feet: (),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Component for Character {
|
impl Character {
|
||||||
type Storage = VecStorage<Self>;
|
// TODO: Remove this
|
||||||
|
pub fn test() -> Self {
|
||||||
|
Self {
|
||||||
|
race: Race::Human,
|
||||||
|
gender: Gender::Unspecified,
|
||||||
|
head: (),
|
||||||
|
chest: (),
|
||||||
|
belt: (),
|
||||||
|
arms: (),
|
||||||
|
feet: (),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Component for Character {
|
||||||
|
type Storage = FlaggedStorage<Self, VecStorage<Self>>;
|
||||||
}
|
}
|
||||||
|
@ -1,20 +1,7 @@
|
|||||||
|
pub mod character;
|
||||||
|
pub mod player;
|
||||||
pub mod phys;
|
pub mod phys;
|
||||||
pub mod uid;
|
|
||||||
pub mod util;
|
|
||||||
|
|
||||||
// Reexports
|
// Reexports
|
||||||
pub use uid::{Uid, UidAllocator};
|
pub use character::Character;
|
||||||
|
pub use player::Player;
|
||||||
use specs::World as EcsWorld;
|
|
||||||
|
|
||||||
pub fn register_local_components(ecs_world: &mut EcsWorld) {
|
|
||||||
ecs_world.register::<Uid>();
|
|
||||||
ecs_world.add_resource(UidAllocator::new());
|
|
||||||
|
|
||||||
ecs_world.register::<util::New>();
|
|
||||||
|
|
||||||
ecs_world.register::<phys::Pos>();
|
|
||||||
ecs_world.register::<phys::Vel>();
|
|
||||||
ecs_world.register::<phys::Dir>();
|
|
||||||
ecs_world.register::<phys::UpdateKind>();
|
|
||||||
}
|
|
||||||
|
@ -1,5 +1,4 @@
|
|||||||
// Library
|
use specs::{Component, VecStorage, FlaggedStorage};
|
||||||
use specs::{Component, VecStorage};
|
|
||||||
use vek::*;
|
use vek::*;
|
||||||
|
|
||||||
// Pos
|
// Pos
|
||||||
@ -8,7 +7,7 @@ use vek::*;
|
|||||||
pub struct Pos(pub Vec3<f32>);
|
pub struct Pos(pub Vec3<f32>);
|
||||||
|
|
||||||
impl Component for Pos {
|
impl Component for Pos {
|
||||||
type Storage = VecStorage<Self>;
|
type Storage = FlaggedStorage<Self, VecStorage<Self>>;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Vel
|
// Vel
|
||||||
@ -17,7 +16,7 @@ impl Component for Pos {
|
|||||||
pub struct Vel(pub Vec3<f32>);
|
pub struct Vel(pub Vec3<f32>);
|
||||||
|
|
||||||
impl Component for Vel {
|
impl Component for Vel {
|
||||||
type Storage = VecStorage<Self>;
|
type Storage = FlaggedStorage<Self, VecStorage<Self>>;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Dir
|
// Dir
|
||||||
@ -26,17 +25,5 @@ impl Component for Vel {
|
|||||||
pub struct Dir(pub Vec3<f32>);
|
pub struct Dir(pub Vec3<f32>);
|
||||||
|
|
||||||
impl Component for Dir {
|
impl Component for Dir {
|
||||||
type Storage = VecStorage<Self>;
|
type Storage = FlaggedStorage<Self, VecStorage<Self>>;
|
||||||
}
|
|
||||||
|
|
||||||
// UpdateKind
|
|
||||||
|
|
||||||
#[derive(Copy, Clone, Debug)]
|
|
||||||
pub enum UpdateKind {
|
|
||||||
Passive,
|
|
||||||
Force,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Component for UpdateKind {
|
|
||||||
type Storage = VecStorage<Self>;
|
|
||||||
}
|
}
|
||||||
|
18
common/src/comp/player.rs
Normal file
18
common/src/comp/player.rs
Normal 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>>;
|
||||||
|
}
|
@ -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();
|
|
||||||
}
|
|
||||||
}
|
|
@ -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>;
|
|
||||||
}
|
|
@ -1,4 +1,4 @@
|
|||||||
#![feature(euclidean_division, duration_float, try_from, trait_alias)]
|
#![feature(euclidean_division, duration_float, trait_alias, bind_by_move_pattern_guards)]
|
||||||
|
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
extern crate serde_derive;
|
extern crate serde_derive;
|
||||||
|
@ -1,17 +1,22 @@
|
|||||||
use crate::comp::{
|
use vek::*;
|
||||||
Uid,
|
use crate::comp;
|
||||||
phys,
|
|
||||||
};
|
|
||||||
|
|
||||||
#[derive(Clone, Debug, Serialize, Deserialize)]
|
#[derive(Clone, Serialize, Deserialize)]
|
||||||
pub enum ClientMsg {
|
pub enum ClientMsg {
|
||||||
|
Connect {
|
||||||
|
player: comp::Player,
|
||||||
|
character: Option<comp::Character>,
|
||||||
|
},
|
||||||
Ping,
|
Ping,
|
||||||
Pong,
|
Pong,
|
||||||
Chat(String),
|
Chat(String),
|
||||||
PlayerPhysics {
|
PlayerPhysics {
|
||||||
pos: phys::Pos,
|
pos: comp::phys::Pos,
|
||||||
vel: phys::Vel,
|
vel: comp::phys::Vel,
|
||||||
dir: phys::Dir,
|
dir: comp::phys::Dir,
|
||||||
|
},
|
||||||
|
TerrainChunkRequest {
|
||||||
|
key: Vec3<i32>,
|
||||||
},
|
},
|
||||||
Disconnect,
|
Disconnect,
|
||||||
}
|
}
|
||||||
|
29
common/src/msg/ecs_packet.rs
Normal file
29
common/src/msg/ecs_packet.rs
Normal 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;
|
||||||
|
}
|
@ -1,6 +1,8 @@
|
|||||||
|
pub mod ecs_packet;
|
||||||
pub mod server;
|
pub mod server;
|
||||||
pub mod client;
|
pub mod client;
|
||||||
|
|
||||||
// Reexports
|
// Reexports
|
||||||
pub use server::ServerMsg;
|
pub use self::server::ServerMsg;
|
||||||
pub use client::ClientMsg;
|
pub use self::client::ClientMsg;
|
||||||
|
pub use self::ecs_packet::EcsPacket;
|
||||||
|
@ -1,20 +1,21 @@
|
|||||||
use crate::comp::{
|
use vek::*;
|
||||||
Uid,
|
use crate::terrain::TerrainChunk;
|
||||||
phys,
|
use super::EcsPacket;
|
||||||
};
|
|
||||||
|
|
||||||
#[derive(Clone, Debug, Serialize, Deserialize)]
|
#[derive(Clone, Serialize, Deserialize)]
|
||||||
pub enum ServerMsg {
|
pub enum ServerMsg {
|
||||||
|
Handshake {
|
||||||
|
ecs_state: sphynx::StatePackage<EcsPacket>,
|
||||||
|
player_entity: u64,
|
||||||
|
},
|
||||||
Shutdown,
|
Shutdown,
|
||||||
Ping,
|
Ping,
|
||||||
Pong,
|
Pong,
|
||||||
Chat(String),
|
Chat(String),
|
||||||
SetPlayerEntity(Uid),
|
SetPlayerEntity(u64),
|
||||||
EntityPhysics {
|
EcsSync(sphynx::SyncPackage<EcsPacket>),
|
||||||
uid: Uid,
|
TerrainChunkUpdate {
|
||||||
pos: phys::Pos,
|
key: Vec3<i32>,
|
||||||
vel: phys::Vel,
|
chunk: Box<TerrainChunk>,
|
||||||
dir: phys::Dir,
|
|
||||||
},
|
},
|
||||||
EntityDeleted(Uid),
|
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,8 @@
|
|||||||
pub mod data;
|
pub mod data;
|
||||||
pub mod post;
|
//pub mod post;
|
||||||
|
pub mod post2;
|
||||||
|
|
||||||
|
pub use post2 as post;
|
||||||
|
|
||||||
// Reexports
|
// Reexports
|
||||||
pub use self::{
|
pub use self::{
|
||||||
|
@ -23,6 +23,7 @@ use mio_extras::channel::{
|
|||||||
Sender,
|
Sender,
|
||||||
};
|
};
|
||||||
use bincode;
|
use bincode;
|
||||||
|
use middleman::Middleman;
|
||||||
|
|
||||||
#[derive(Clone, Debug, PartialEq)]
|
#[derive(Clone, Debug, PartialEq)]
|
||||||
pub enum Error {
|
pub enum Error {
|
||||||
@ -56,14 +57,15 @@ impl<T> From<mio_extras::channel::SendError<T>> for Error {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait PostSend = 'static + serde::Serialize + Send + fmt::Debug;
|
pub trait PostSend = 'static + serde::Serialize + Send + middleman::Message;
|
||||||
pub trait PostRecv = 'static + serde::de::DeserializeOwned + Send + fmt::Debug;
|
pub trait PostRecv = 'static + serde::de::DeserializeOwned + Send + middleman::Message;
|
||||||
|
|
||||||
const TCP_TOK: Token = Token(0);
|
const TCP_TOK: Token = Token(0);
|
||||||
const CTRL_TOK: Token = Token(1);
|
const CTRL_TOK: Token = Token(1);
|
||||||
const POSTBOX_TOK: Token = Token(2);
|
const POSTBOX_TOK: Token = Token(2);
|
||||||
const SEND_TOK: Token = Token(3);
|
const SEND_TOK: Token = Token(3);
|
||||||
const RECV_TOK: Token = Token(4);
|
const RECV_TOK: Token = Token(4);
|
||||||
|
const MIDDLEMAN_TOK: Token = Token(5);
|
||||||
|
|
||||||
const MAX_MSG_BYTES: usize = 1 << 20;
|
const MAX_MSG_BYTES: usize = 1 << 20;
|
||||||
|
|
||||||
@ -218,7 +220,7 @@ impl<S: PostSend, R: PostRecv> PostBox<S, R> {
|
|||||||
let (recv_tx, recv_rx) = channel();
|
let (recv_tx, recv_rx) = channel();
|
||||||
|
|
||||||
let worker_poll = Poll::new()?;
|
let worker_poll = Poll::new()?;
|
||||||
worker_poll.register(&tcp_stream, TCP_TOK, Ready::readable(), PollOpt::edge())?;
|
worker_poll.register(&tcp_stream, TCP_TOK, Ready::readable() | Ready::writable(), PollOpt::edge())?;
|
||||||
worker_poll.register(&ctrl_rx, CTRL_TOK, Ready::readable(), PollOpt::edge())?;
|
worker_poll.register(&ctrl_rx, CTRL_TOK, Ready::readable(), PollOpt::edge())?;
|
||||||
worker_poll.register(&send_rx, SEND_TOK, Ready::readable(), PollOpt::edge())?;
|
worker_poll.register(&send_rx, SEND_TOK, Ready::readable(), PollOpt::edge())?;
|
||||||
|
|
||||||
@ -251,6 +253,45 @@ impl<S: PostSend, R: PostRecv> PostBox<S, R> {
|
|||||||
let _ = self.send_tx.send(data);
|
let _ = self.send_tx.send(data);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: This method is super messy
|
||||||
|
pub fn next_message(&mut self) -> Option<R> {
|
||||||
|
if self.err.is_some() {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
|
loop {
|
||||||
|
let mut events = Events::with_capacity(10);
|
||||||
|
if let Err(err) = self.poll.poll(
|
||||||
|
&mut events,
|
||||||
|
Some(Duration::new(0, 0)),
|
||||||
|
) {
|
||||||
|
self.err = Some(err.into());
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
|
for event in events {
|
||||||
|
match event.token() {
|
||||||
|
// Keep reading new messages from the channel
|
||||||
|
RECV_TOK => loop {
|
||||||
|
match self.recv_rx.try_recv() {
|
||||||
|
Ok(Ok(msg)) => return Some(msg),
|
||||||
|
Err(TryRecvError::Empty) => break,
|
||||||
|
Err(err) => {
|
||||||
|
self.err = Some(err.into());
|
||||||
|
return None;
|
||||||
|
},
|
||||||
|
Ok(Err(err)) => {
|
||||||
|
self.err = Some(err);
|
||||||
|
return None;
|
||||||
|
},
|
||||||
|
}
|
||||||
|
},
|
||||||
|
tok => panic!("Unexpected event token '{:?}'", tok),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn new_messages(&mut self) -> impl ExactSizeIterator<Item=R> {
|
pub fn new_messages(&mut self) -> impl ExactSizeIterator<Item=R> {
|
||||||
let mut msgs = VecDeque::new();
|
let mut msgs = VecDeque::new();
|
||||||
|
|
||||||
@ -306,12 +347,38 @@ fn postbox_worker<S: PostSend, R: PostRecv>(
|
|||||||
send_rx: Receiver<S>,
|
send_rx: Receiver<S>,
|
||||||
recv_tx: Sender<Result<R, Error>>,
|
recv_tx: Sender<Result<R, Error>>,
|
||||||
) -> Result<(), Error> {
|
) -> Result<(), Error> {
|
||||||
|
fn try_tcp_send(tcp_stream: &mut TcpStream, chunks: &mut VecDeque<Vec<u8>>) -> Result<(), Error> {
|
||||||
|
loop {
|
||||||
|
let chunk = match chunks.pop_front() {
|
||||||
|
Some(chunk) => chunk,
|
||||||
|
None => break,
|
||||||
|
};
|
||||||
|
|
||||||
|
match tcp_stream.write_all(&chunk) {
|
||||||
|
Ok(()) => {},
|
||||||
|
Err(err) if err.kind() == io::ErrorKind::WouldBlock => {
|
||||||
|
chunks.push_front(chunk);
|
||||||
|
break;
|
||||||
|
},
|
||||||
|
Err(err) => {
|
||||||
|
println!("Error: {:?}", err);
|
||||||
|
return Err(err.into());
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
enum RecvState {
|
enum RecvState {
|
||||||
ReadHead(Vec<u8>),
|
ReadHead(Vec<u8>),
|
||||||
ReadBody(usize, Vec<u8>),
|
ReadBody(usize, Vec<u8>),
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut recv_state = RecvState::ReadHead(Vec::with_capacity(8));
|
let mut recv_state = RecvState::ReadHead(Vec::new());
|
||||||
|
let mut chunks = VecDeque::new();
|
||||||
|
|
||||||
|
//let mut recv_state = RecvState::ReadHead(Vec::with_capacity(8));
|
||||||
let mut events = Events::with_capacity(64);
|
let mut events = Events::with_capacity(64);
|
||||||
|
|
||||||
'work: loop {
|
'work: loop {
|
||||||
@ -344,18 +411,23 @@ fn postbox_worker<S: PostSend, R: PostRecv>(
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut packet = msg_bytes
|
let mut bytes = msg_bytes
|
||||||
.len()
|
.len()
|
||||||
.to_le_bytes()
|
.to_le_bytes()
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.to_vec();
|
.to_vec();
|
||||||
packet.append(&mut msg_bytes);
|
bytes.append(&mut msg_bytes);
|
||||||
|
|
||||||
match tcp_stream.write_all(&packet) {
|
bytes
|
||||||
Ok(()) => {},
|
.chunks(1024)
|
||||||
|
.map(|chunk| chunk.to_vec())
|
||||||
|
.for_each(|chunk| chunks.push_back(chunk));
|
||||||
|
|
||||||
|
match try_tcp_send(&mut tcp_stream, &mut chunks) {
|
||||||
|
Ok(_) => {},
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
recv_tx.send(Err(err.into()))?;
|
recv_tx.send(Err(err.into()))?;
|
||||||
break 'work;
|
return Err(Error::Network);
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -363,61 +435,75 @@ fn postbox_worker<S: PostSend, R: PostRecv>(
|
|||||||
Err(err) => Err(err)?,
|
Err(err) => Err(err)?,
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
TCP_TOK => loop {
|
TCP_TOK => {
|
||||||
match tcp_stream.take_error() {
|
loop {
|
||||||
Ok(None) => {},
|
// Check TCP error
|
||||||
Ok(Some(err)) => {
|
match tcp_stream.take_error() {
|
||||||
recv_tx.send(Err(err.into()))?;
|
Ok(None) => {},
|
||||||
break 'work;
|
Ok(Some(err)) => {
|
||||||
},
|
recv_tx.send(Err(err.into()))?;
|
||||||
|
break 'work;
|
||||||
|
},
|
||||||
|
Err(err) => {
|
||||||
|
recv_tx.send(Err(err.into()))?;
|
||||||
|
break 'work;
|
||||||
|
},
|
||||||
|
}
|
||||||
|
match &mut recv_state {
|
||||||
|
RecvState::ReadHead(head) => if head.len() == 8 {
|
||||||
|
let len = usize::from_le_bytes(<[u8; 8]>::try_from(head.as_slice()).unwrap());
|
||||||
|
if len > MAX_MSG_BYTES {
|
||||||
|
println!("TOO BIG! {:x}", len);
|
||||||
|
recv_tx.send(Err(Error::InvalidMsg))?;
|
||||||
|
break 'work;
|
||||||
|
} else if len == 0 {
|
||||||
|
recv_state = RecvState::ReadHead(Vec::with_capacity(8));
|
||||||
|
break;
|
||||||
|
} else {
|
||||||
|
recv_state = RecvState::ReadBody(
|
||||||
|
len,
|
||||||
|
Vec::new(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
let mut b = [0; 1];
|
||||||
|
match tcp_stream.read(&mut b) {
|
||||||
|
Ok(0) => {},
|
||||||
|
Ok(_) => head.push(b[0]),
|
||||||
|
Err(_) => break,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
RecvState::ReadBody(len, body) => if body.len() == *len {
|
||||||
|
match bincode::deserialize(&body) {
|
||||||
|
Ok(msg) => {
|
||||||
|
recv_tx.send(Ok(msg))?;
|
||||||
|
recv_state = RecvState::ReadHead(Vec::with_capacity(8));
|
||||||
|
},
|
||||||
|
Err(err) => {
|
||||||
|
recv_tx.send(Err((*err).into()))?;
|
||||||
|
break 'work;
|
||||||
|
},
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
let left = *len - body.len();
|
||||||
|
let mut buf = vec![0; left];
|
||||||
|
match tcp_stream.read(&mut buf) {
|
||||||
|
Ok(_) => body.append(&mut buf),
|
||||||
|
Err(err) => {
|
||||||
|
recv_tx.send(Err(err.into()))?;
|
||||||
|
break 'work;
|
||||||
|
},
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Now, try sending TCP stuff
|
||||||
|
match try_tcp_send(&mut tcp_stream, &mut chunks) {
|
||||||
|
Ok(_) => {},
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
recv_tx.send(Err(err.into()))?;
|
recv_tx.send(Err(err.into()))?;
|
||||||
break 'work;
|
return Err(Error::Network);
|
||||||
},
|
|
||||||
}
|
|
||||||
match &mut recv_state {
|
|
||||||
RecvState::ReadHead(head) => if head.len() == 8 {
|
|
||||||
let len = usize::from_le_bytes(<[u8; 8]>::try_from(head.as_slice()).unwrap());
|
|
||||||
if len > MAX_MSG_BYTES {
|
|
||||||
recv_tx.send(Err(Error::InvalidMsg))?;
|
|
||||||
break 'work;
|
|
||||||
} else if len == 0 {
|
|
||||||
recv_state = RecvState::ReadHead(Vec::with_capacity(8));
|
|
||||||
break;
|
|
||||||
} else {
|
|
||||||
recv_state = RecvState::ReadBody(
|
|
||||||
len,
|
|
||||||
Vec::new(),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
let mut b = [0; 1];
|
|
||||||
match tcp_stream.read(&mut b) {
|
|
||||||
Ok(_) => head.push(b[0]),
|
|
||||||
Err(_) => break,
|
|
||||||
}
|
|
||||||
},
|
|
||||||
RecvState::ReadBody(len, body) => if body.len() == *len {
|
|
||||||
match bincode::deserialize(&body) {
|
|
||||||
Ok(msg) => {
|
|
||||||
recv_tx.send(Ok(msg))?;
|
|
||||||
recv_state = RecvState::ReadHead(Vec::with_capacity(8));
|
|
||||||
},
|
|
||||||
Err(err) => {
|
|
||||||
recv_tx.send(Err((*err).into()))?;
|
|
||||||
break 'work;
|
|
||||||
},
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
let left = *len - body.len();
|
|
||||||
let mut buf = vec![0; left];
|
|
||||||
match tcp_stream.read(&mut buf) {
|
|
||||||
Ok(_) => body.append(&mut buf),
|
|
||||||
Err(err) => {
|
|
||||||
recv_tx.send(Err(err.into()))?;
|
|
||||||
break 'work;
|
|
||||||
},
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -426,24 +512,28 @@ fn postbox_worker<S: PostSend, R: PostRecv>(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
tcp_stream.shutdown(Shutdown::Both)?;
|
//tcp_stream.shutdown(Shutdown::Both)?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
// TESTS
|
// TESTS
|
||||||
|
|
||||||
|
/*
|
||||||
|
#[derive(Serialize, Deserialize)]
|
||||||
|
struct TestMsg<T>(T);
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn connect() {
|
fn connect() {
|
||||||
let srv_addr = ([127, 0, 0, 1], 12345);
|
let srv_addr = ([127, 0, 0, 1], 12345);
|
||||||
|
|
||||||
let mut postoffice = PostOffice::<u32, f32>::bind(srv_addr).unwrap();
|
let mut postoffice = PostOffice::<TestMsg<u32>, TestMsg<f32>>::bind(srv_addr).unwrap();
|
||||||
|
|
||||||
// We should start off with 0 incoming connections
|
// We should start off with 0 incoming connections
|
||||||
thread::sleep(Duration::from_millis(250));
|
thread::sleep(Duration::from_millis(250));
|
||||||
assert_eq!(postoffice.new_connections().len(), 0);
|
assert_eq!(postoffice.new_connections().len(), 0);
|
||||||
assert_eq!(postoffice.error(), None);
|
assert_eq!(postoffice.error(), None);
|
||||||
|
|
||||||
let postbox = PostBox::<f32, u32>::to_server(srv_addr).unwrap();
|
let postbox = PostBox::<TestMsg<f32>, TestMsg<u32>>::to_server(srv_addr).unwrap();
|
||||||
|
|
||||||
// Now a postbox has been created, we should have 1 new
|
// Now a postbox has been created, we should have 1 new
|
||||||
thread::sleep(Duration::from_millis(250));
|
thread::sleep(Duration::from_millis(250));
|
||||||
@ -457,21 +547,21 @@ fn connect_fail() {
|
|||||||
let listen_addr = ([0; 4], 12345);
|
let listen_addr = ([0; 4], 12345);
|
||||||
let connect_addr = ([127, 0, 0, 1], 12212);
|
let connect_addr = ([127, 0, 0, 1], 12212);
|
||||||
|
|
||||||
let mut postoffice = PostOffice::<u32, f32>::bind(listen_addr).unwrap();
|
let mut postoffice = PostOffice::<TestMsg<u32>, TestMsg<f32>>::bind(listen_addr).unwrap();
|
||||||
|
|
||||||
// We should start off with 0 incoming connections
|
// We should start off with 0 incoming connections
|
||||||
thread::sleep(Duration::from_millis(250));
|
thread::sleep(Duration::from_millis(250));
|
||||||
assert_eq!(postoffice.new_connections().len(), 0);
|
assert_eq!(postoffice.new_connections().len(), 0);
|
||||||
assert_eq!(postoffice.error(), None);
|
assert_eq!(postoffice.error(), None);
|
||||||
|
|
||||||
assert!(PostBox::<f32, u32>::to_server(connect_addr).is_err());
|
assert!(PostBox::<TestMsg<f32>, TestMsg<u32>>::to_server(connect_addr).is_err());
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn connection_count() {
|
fn connection_count() {
|
||||||
let srv_addr = ([127, 0, 0, 1], 12346);
|
let srv_addr = ([127, 0, 0, 1], 12346);
|
||||||
|
|
||||||
let mut postoffice = PostOffice::<u32, f32>::bind(srv_addr).unwrap();
|
let mut postoffice = PostOffice::<TestMsg<u32>, TestMsg<f32>>::bind(srv_addr).unwrap();
|
||||||
let mut postboxes = Vec::new();
|
let mut postboxes = Vec::new();
|
||||||
|
|
||||||
// We should start off with 0 incoming connections
|
// We should start off with 0 incoming connections
|
||||||
@ -480,7 +570,7 @@ fn connection_count() {
|
|||||||
assert_eq!(postoffice.error(), None);
|
assert_eq!(postoffice.error(), None);
|
||||||
|
|
||||||
for _ in 0..5 {
|
for _ in 0..5 {
|
||||||
postboxes.push(PostBox::<f32, u32>::to_server(srv_addr).unwrap());
|
postboxes.push(PostBox::<TestMsg<f32>, TestMsg<u32>>::to_server(srv_addr).unwrap());
|
||||||
}
|
}
|
||||||
|
|
||||||
// 5 postboxes created, we should have 5
|
// 5 postboxes created, we should have 5
|
||||||
@ -494,10 +584,10 @@ fn connection_count() {
|
|||||||
fn disconnect() {
|
fn disconnect() {
|
||||||
let srv_addr = ([127, 0, 0, 1], 12347);
|
let srv_addr = ([127, 0, 0, 1], 12347);
|
||||||
|
|
||||||
let mut postoffice = PostOffice::<u32, f32>::bind(srv_addr).unwrap();
|
let mut postoffice = PostOffice::<TestMsg<u32>, TestMsg<f32>>::bind(srv_addr).unwrap();
|
||||||
|
|
||||||
let mut server_postbox = {
|
let mut server_postbox = {
|
||||||
let mut client_postbox = PostBox::<f32, u32>::to_server(srv_addr).unwrap();
|
let mut client_postbox = PostBox::<TestMsg<f32>, TestMsg<u32>>::to_server(srv_addr).unwrap();
|
||||||
|
|
||||||
thread::sleep(Duration::from_millis(250));
|
thread::sleep(Duration::from_millis(250));
|
||||||
let mut incoming = postoffice.new_connections();
|
let mut incoming = postoffice.new_connections();
|
||||||
@ -519,52 +609,53 @@ fn disconnect() {
|
|||||||
fn client_to_server() {
|
fn client_to_server() {
|
||||||
let srv_addr = ([127, 0, 0, 1], 12348);
|
let srv_addr = ([127, 0, 0, 1], 12348);
|
||||||
|
|
||||||
let mut po = PostOffice::<u32, f32>::bind(srv_addr).unwrap();
|
let mut po = PostOffice::<TestMsg<u32>, TestMsg<f32>>::bind(srv_addr).unwrap();
|
||||||
|
|
||||||
let mut client_pb = PostBox::<f32, u32>::to_server(srv_addr).unwrap();
|
let mut client_pb = PostBox::<TestMsg<f32>, TestMsg<u32>>::to_server(srv_addr).unwrap();
|
||||||
|
|
||||||
thread::sleep(Duration::from_millis(250));
|
thread::sleep(Duration::from_millis(250));
|
||||||
|
|
||||||
let mut server_pb = po.new_connections().next().unwrap();
|
let mut server_pb = po.new_connections().next().unwrap();
|
||||||
|
|
||||||
client_pb.send(1337.0);
|
client_pb.send(TestMsg(1337.0));
|
||||||
client_pb.send(9821.0);
|
client_pb.send(TestMsg(9821.0));
|
||||||
client_pb.send(-3.2);
|
client_pb.send(TestMsg(-3.2));
|
||||||
client_pb.send(17.0);
|
client_pb.send(TestMsg(17.0));
|
||||||
|
|
||||||
thread::sleep(Duration::from_millis(250));
|
thread::sleep(Duration::from_millis(250));
|
||||||
|
|
||||||
let mut incoming_msgs = server_pb.new_messages();
|
let mut incoming_msgs = server_pb.new_messages();
|
||||||
assert_eq!(incoming_msgs.len(), 4);
|
assert_eq!(incoming_msgs.len(), 4);
|
||||||
assert_eq!(incoming_msgs.next().unwrap(), 1337.0);
|
assert_eq!(incoming_msgs.next().unwrap(), TestMsg(1337.0));
|
||||||
assert_eq!(incoming_msgs.next().unwrap(), 9821.0);
|
assert_eq!(incoming_msgs.next().unwrap(), TestMsg(9821.0));
|
||||||
assert_eq!(incoming_msgs.next().unwrap(), -3.2);
|
assert_eq!(incoming_msgs.next().unwrap(), TestMsg(-3.2));
|
||||||
assert_eq!(incoming_msgs.next().unwrap(), 17.0);
|
assert_eq!(incoming_msgs.next().unwrap(), TestMsg(17.0));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn server_to_client() {
|
fn server_to_client() {
|
||||||
let srv_addr = ([127, 0, 0, 1], 12349);
|
let srv_addr = ([127, 0, 0, 1], 12349);
|
||||||
|
|
||||||
let mut po = PostOffice::<u32, f32>::bind(srv_addr).unwrap();
|
let mut po = PostOffice::<TestMsg<u32>, TestMsg<f32>>::bind(srv_addr).unwrap();
|
||||||
|
|
||||||
let mut client_pb = PostBox::<f32, u32>::to_server(srv_addr).unwrap();
|
let mut client_pb = PostBox::<TestMsg<f32>, TestMsg<u32>>::to_server(srv_addr).unwrap();
|
||||||
|
|
||||||
thread::sleep(Duration::from_millis(250));
|
thread::sleep(Duration::from_millis(250));
|
||||||
|
|
||||||
let mut server_pb = po.new_connections().next().unwrap();
|
let mut server_pb = po.new_connections().next().unwrap();
|
||||||
|
|
||||||
server_pb.send(1337);
|
server_pb.send(TestMsg(1337));
|
||||||
server_pb.send(9821);
|
server_pb.send(TestMsg(9821));
|
||||||
server_pb.send(39999999);
|
server_pb.send(TestMsg(39999999));
|
||||||
server_pb.send(17);
|
server_pb.send(TestMsg(17));
|
||||||
|
|
||||||
thread::sleep(Duration::from_millis(250));
|
thread::sleep(Duration::from_millis(250));
|
||||||
|
|
||||||
let mut incoming_msgs = client_pb.new_messages();
|
let mut incoming_msgs = client_pb.new_messages();
|
||||||
assert_eq!(incoming_msgs.len(), 4);
|
assert_eq!(incoming_msgs.len(), 4);
|
||||||
assert_eq!(incoming_msgs.next().unwrap(), 1337);
|
assert_eq!(incoming_msgs.next().unwrap(), TestMsg(1337));
|
||||||
assert_eq!(incoming_msgs.next().unwrap(), 9821);
|
assert_eq!(incoming_msgs.next().unwrap(), TestMsg(9821));
|
||||||
assert_eq!(incoming_msgs.next().unwrap(), 39999999);
|
assert_eq!(incoming_msgs.next().unwrap(), TestMsg(39999999));
|
||||||
assert_eq!(incoming_msgs.next().unwrap(), 17);
|
assert_eq!(incoming_msgs.next().unwrap(), TestMsg(17));
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
|
404
common/src/net/post2.rs
Normal file
404
common/src/net/post2.rs
Normal 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -1,4 +1,7 @@
|
|||||||
use std::time::Duration;
|
use std::{
|
||||||
|
time::Duration,
|
||||||
|
collections::HashSet,
|
||||||
|
};
|
||||||
use shred::{Fetch, FetchMut};
|
use shred::{Fetch, FetchMut};
|
||||||
use specs::{
|
use specs::{
|
||||||
Builder,
|
Builder,
|
||||||
@ -6,18 +9,22 @@ use specs::{
|
|||||||
DispatcherBuilder,
|
DispatcherBuilder,
|
||||||
EntityBuilder as EcsEntityBuilder,
|
EntityBuilder as EcsEntityBuilder,
|
||||||
Entity as EcsEntity,
|
Entity as EcsEntity,
|
||||||
World as EcsWorld,
|
|
||||||
storage::{
|
storage::{
|
||||||
Storage as EcsStorage,
|
Storage as EcsStorage,
|
||||||
MaskedStorage as EcsMaskedStorage,
|
MaskedStorage as EcsMaskedStorage,
|
||||||
},
|
},
|
||||||
saveload::{MarkedBuilder, MarkerAllocator},
|
saveload::{MarkedBuilder, MarkerAllocator},
|
||||||
};
|
};
|
||||||
|
use sphynx;
|
||||||
use vek::*;
|
use vek::*;
|
||||||
use crate::{
|
use crate::{
|
||||||
comp,
|
comp,
|
||||||
sys,
|
sys,
|
||||||
terrain::TerrainMap,
|
terrain::{
|
||||||
|
TerrainMap,
|
||||||
|
TerrainChunk,
|
||||||
|
},
|
||||||
|
msg::EcsPacket,
|
||||||
};
|
};
|
||||||
|
|
||||||
/// How much faster should an in-game day be compared to a real day?
|
/// How much faster should an in-game day be compared to a real day?
|
||||||
@ -35,17 +42,17 @@ struct Time(f64);
|
|||||||
pub struct DeltaTime(pub f64);
|
pub struct DeltaTime(pub f64);
|
||||||
|
|
||||||
pub struct Changes {
|
pub struct Changes {
|
||||||
pub new_chunks: Vec<Vec3<i32>>,
|
pub new_chunks: HashSet<Vec3<i32>>,
|
||||||
pub changed_chunks: Vec<Vec3<i32>>,
|
pub changed_chunks: HashSet<Vec3<i32>>,
|
||||||
pub removed_chunks: Vec<Vec3<i32>>,
|
pub removed_chunks: HashSet<Vec3<i32>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Changes {
|
impl Changes {
|
||||||
pub fn default() -> Self {
|
pub fn default() -> Self {
|
||||||
Self {
|
Self {
|
||||||
new_chunks: vec![],
|
new_chunks: HashSet::new(),
|
||||||
changed_chunks: vec![],
|
changed_chunks: HashSet::new(),
|
||||||
removed_chunks: vec![],
|
removed_chunks: HashSet::new(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -59,95 +66,74 @@ impl Changes {
|
|||||||
/// A type used to represent game state stored on both the client and the server. This includes
|
/// A type used to represent game state stored on both the client and the server. This includes
|
||||||
/// things like entity components, terrain data, and global state like weather, time of day, etc.
|
/// things like entity components, terrain data, and global state like weather, time of day, etc.
|
||||||
pub struct State {
|
pub struct State {
|
||||||
ecs_world: EcsWorld,
|
ecs: sphynx::World<EcsPacket>,
|
||||||
changes: Changes,
|
changes: Changes,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl State {
|
impl State {
|
||||||
/// Create a new `State`.
|
/// Create a new `State`.
|
||||||
pub fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
let mut ecs_world = EcsWorld::new();
|
|
||||||
|
|
||||||
// Register resources used by the ECS
|
|
||||||
ecs_world.add_resource(TimeOfDay(0.0));
|
|
||||||
ecs_world.add_resource(Time(0.0));
|
|
||||||
ecs_world.add_resource(DeltaTime(0.0));
|
|
||||||
ecs_world.add_resource(TerrainMap::new());
|
|
||||||
|
|
||||||
// Register common components with the state
|
|
||||||
comp::register_local_components(&mut ecs_world);
|
|
||||||
|
|
||||||
Self {
|
Self {
|
||||||
ecs_world,
|
ecs: sphynx::World::new(specs::World::new(), Self::setup_sphynx_world),
|
||||||
changes: Changes::default(),
|
changes: Changes::default(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Create a new `State` from an ECS state package
|
||||||
|
pub fn from_state_package(state_package: sphynx::StatePackage<EcsPacket>) -> Self {
|
||||||
|
Self {
|
||||||
|
ecs: sphynx::World::from_state_package(specs::World::new(), Self::setup_sphynx_world, state_package),
|
||||||
|
changes: Changes::default(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create a new Sphynx ECS world
|
||||||
|
fn setup_sphynx_world(ecs: &mut sphynx::World<EcsPacket>) {
|
||||||
|
// Register synced components
|
||||||
|
ecs.register_synced::<comp::phys::Pos>();
|
||||||
|
ecs.register_synced::<comp::phys::Vel>();
|
||||||
|
ecs.register_synced::<comp::phys::Dir>();
|
||||||
|
ecs.register_synced::<comp::Character>();
|
||||||
|
ecs.register_synced::<comp::Player>();
|
||||||
|
|
||||||
|
// Register resources used by the ECS
|
||||||
|
ecs.internal_mut().add_resource(TimeOfDay(0.0));
|
||||||
|
ecs.internal_mut().add_resource(Time(0.0));
|
||||||
|
ecs.internal_mut().add_resource(DeltaTime(0.0));
|
||||||
|
ecs.internal_mut().add_resource(TerrainMap::new());
|
||||||
|
}
|
||||||
|
|
||||||
/// Register a component with the state's ECS
|
/// Register a component with the state's ECS
|
||||||
pub fn with_component<T: Component>(mut self) -> Self
|
pub fn with_component<T: Component>(mut self) -> Self
|
||||||
where <T as Component>::Storage: Default
|
where <T as Component>::Storage: Default
|
||||||
{
|
{
|
||||||
self.ecs_world.register::<T>();
|
self.ecs.internal_mut().register::<T>();
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Build a new entity with a generated UID
|
|
||||||
pub fn build_uid_entity(&mut self) -> EcsEntityBuilder {
|
|
||||||
self.ecs_world.create_entity()
|
|
||||||
.with(comp::util::New)
|
|
||||||
.marked::<comp::Uid>()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Build an entity with a specific UID
|
|
||||||
pub fn build_uid_entity_with_uid(&mut self, uid: comp::Uid) -> EcsEntityBuilder {
|
|
||||||
let builder = self.build_uid_entity();
|
|
||||||
|
|
||||||
builder.world
|
|
||||||
.write_resource::<comp::UidAllocator>()
|
|
||||||
.allocate(builder.entity, Some(uid.into()));
|
|
||||||
|
|
||||||
builder
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Get an entity from its UID, if it exists
|
|
||||||
pub fn get_entity(&self, uid: comp::Uid) -> Option<EcsEntity> {
|
|
||||||
// Find the ECS entity from its UID
|
|
||||||
self.ecs_world
|
|
||||||
.read_resource::<comp::UidAllocator>()
|
|
||||||
.retrieve_entity_internal(uid.into())
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Delete an entity from the state's ECS, if it exists
|
|
||||||
pub fn delete_entity(&mut self, uid: comp::Uid) {
|
|
||||||
// Find the ECS entity from its UID
|
|
||||||
let ecs_entity = self.ecs_world
|
|
||||||
.read_resource::<comp::UidAllocator>()
|
|
||||||
.retrieve_entity_internal(uid.into());
|
|
||||||
|
|
||||||
// Delete the ECS entity, if it exists
|
|
||||||
if let Some(ecs_entity) = ecs_entity {
|
|
||||||
let _ = self.ecs_world.delete_entity(ecs_entity);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Write a component attributed to a particular entity
|
/// Write a component attributed to a particular entity
|
||||||
pub fn write_component<C: Component>(&mut self, entity: EcsEntity, comp: C) {
|
pub fn write_component<C: Component>(&mut self, entity: EcsEntity, comp: C) {
|
||||||
let _ = self.ecs_world.write_storage().insert(entity, comp);
|
let _ = self.ecs.internal_mut().write_storage().insert(entity, comp);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Read a component attributed to a particular entity
|
||||||
|
pub fn read_component_cloned<C: Component + Clone>(&self, entity: EcsEntity) -> Option<C> {
|
||||||
|
self.ecs.internal().read_storage().get(entity).cloned()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get a read-only reference to the storage of a particular component type
|
/// Get a read-only reference to the storage of a particular component type
|
||||||
pub fn read_storage<C: Component>(&self) -> EcsStorage<C, Fetch<EcsMaskedStorage<C>>> {
|
pub fn read_storage<C: Component>(&self) -> EcsStorage<C, Fetch<EcsMaskedStorage<C>>> {
|
||||||
self.ecs_world.read_storage::<C>()
|
self.ecs.internal().read_storage::<C>()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get a reference to the internal ECS world
|
/// Get a reference to the internal ECS world
|
||||||
pub fn ecs_world(&self) -> &EcsWorld {
|
pub fn ecs(&self) -> &sphynx::World<EcsPacket> {
|
||||||
&self.ecs_world
|
&self.ecs
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get a mutable reference to the internal ECS world
|
/// Get a mutable reference to the internal ECS world
|
||||||
pub fn ecs_world_mut(&mut self) -> &mut EcsWorld {
|
pub fn ecs_mut(&mut self) -> &mut sphynx::World<EcsPacket> {
|
||||||
&mut self.ecs_world
|
&mut self.ecs
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get a reference to the `Changes` structure of the state. This contains
|
/// Get a reference to the `Changes` structure of the state. This contains
|
||||||
@ -156,54 +142,57 @@ impl State {
|
|||||||
&self.changes
|
&self.changes
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Get rid of this since it shouldn't be needed
|
|
||||||
pub fn changes_mut(&mut self) -> &mut Changes {
|
|
||||||
&mut self.changes
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Get the current in-game time of day.
|
/// Get the current in-game time of day.
|
||||||
///
|
///
|
||||||
/// Note that this should not be used for physics, animations or other such localised timings.
|
/// Note that this should not be used for physics, animations or other such localised timings.
|
||||||
pub fn get_time_of_day(&self) -> f64 {
|
pub fn get_time_of_day(&self) -> f64 {
|
||||||
self.ecs_world.read_resource::<TimeOfDay>().0
|
self.ecs.internal().read_resource::<TimeOfDay>().0
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get the current in-game time.
|
/// Get the current in-game time.
|
||||||
///
|
///
|
||||||
/// Note that this does not correspond to the time of day.
|
/// Note that this does not correspond to the time of day.
|
||||||
pub fn get_time(&self) -> f64 {
|
pub fn get_time(&self) -> f64 {
|
||||||
self.ecs_world.read_resource::<Time>().0
|
self.ecs.internal().read_resource::<Time>().0
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get a reference to this state's terrain.
|
/// Get a reference to this state's terrain.
|
||||||
pub fn terrain(&self) -> Fetch<TerrainMap> {
|
pub fn terrain(&self) -> Fetch<TerrainMap> {
|
||||||
self.ecs_world.read_resource::<TerrainMap>()
|
self.ecs
|
||||||
|
.internal()
|
||||||
|
.read_resource::<TerrainMap>()
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Get rid of this since it shouldn't be needed
|
/// Insert the provided chunk into this state's terrain.
|
||||||
pub fn terrain_mut(&mut self) -> FetchMut<TerrainMap> {
|
pub fn insert_chunk(&mut self, key: Vec3<i32>, chunk: TerrainChunk) {
|
||||||
self.ecs_world.write_resource::<TerrainMap>()
|
if self.ecs
|
||||||
|
.internal_mut()
|
||||||
|
.write_resource::<TerrainMap>()
|
||||||
|
.insert(key, chunk)
|
||||||
|
.is_some()
|
||||||
|
{
|
||||||
|
self.changes.changed_chunks.insert(key);
|
||||||
|
} else {
|
||||||
|
self.changes.new_chunks.insert(key);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Execute a single tick, simulating the game state by the given duration.
|
/// Execute a single tick, simulating the game state by the given duration.
|
||||||
pub fn tick(&mut self, dt: Duration) {
|
pub fn tick(&mut self, dt: Duration) {
|
||||||
// First, wipe all temporary marker components
|
|
||||||
self.ecs_world.write_storage::<comp::util::New>().clear();
|
|
||||||
|
|
||||||
// Change the time accordingly
|
// Change the time accordingly
|
||||||
self.ecs_world.write_resource::<TimeOfDay>().0 += dt.as_secs_f64() * DAY_CYCLE_FACTOR;
|
self.ecs.internal_mut().write_resource::<TimeOfDay>().0 += dt.as_secs_f64() * DAY_CYCLE_FACTOR;
|
||||||
self.ecs_world.write_resource::<Time>().0 += dt.as_secs_f64();
|
self.ecs.internal_mut().write_resource::<Time>().0 += dt.as_secs_f64();
|
||||||
|
|
||||||
// Run systems to update the world
|
// Run systems to update the world
|
||||||
self.ecs_world.write_resource::<DeltaTime>().0 = dt.as_secs_f64();
|
self.ecs.internal_mut().write_resource::<DeltaTime>().0 = dt.as_secs_f64();
|
||||||
|
|
||||||
// Create and run dispatcher for ecs systems
|
// Create and run dispatcher for ecs systems
|
||||||
let mut dispatch_builder = DispatcherBuilder::new();
|
let mut dispatch_builder = DispatcherBuilder::new();
|
||||||
sys::add_local_systems(&mut dispatch_builder);
|
sys::add_local_systems(&mut dispatch_builder);
|
||||||
// This dispatches all the systems in parallel
|
// This dispatches all the systems in parallel
|
||||||
dispatch_builder.build().dispatch(&self.ecs_world.res);
|
dispatch_builder.build().dispatch(&self.ecs.internal_mut().res);
|
||||||
|
|
||||||
self.ecs_world.maintain();
|
self.ecs.internal_mut().maintain();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Clean up the state after a tick
|
/// Clean up the state after a tick
|
||||||
|
@ -1,3 +1,6 @@
|
|||||||
|
use serde_derive::{Serialize, Deserialize};
|
||||||
|
|
||||||
|
#[derive(Clone, Serialize, Deserialize)]
|
||||||
pub enum BiomeKind {
|
pub enum BiomeKind {
|
||||||
Void,
|
Void,
|
||||||
Grassland,
|
Grassland,
|
||||||
|
@ -1,10 +1,10 @@
|
|||||||
// Library
|
|
||||||
use vek::*;
|
use vek::*;
|
||||||
|
use serde_derive::{Serialize, Deserialize};
|
||||||
|
|
||||||
// Crate
|
// Crate
|
||||||
use crate::vol::Vox;
|
use crate::vol::Vox;
|
||||||
|
|
||||||
#[derive(Copy, Clone, Debug)]
|
#[derive(Copy, Clone, Debug, Serialize, Deserialize)]
|
||||||
pub struct Block {
|
pub struct Block {
|
||||||
kind: u8,
|
kind: u8,
|
||||||
color: [u8; 3],
|
color: [u8; 3],
|
||||||
|
@ -7,10 +7,8 @@ pub use self::{
|
|||||||
biome::BiomeKind,
|
biome::BiomeKind,
|
||||||
};
|
};
|
||||||
|
|
||||||
// Library
|
|
||||||
use vek::*;
|
use vek::*;
|
||||||
|
use serde_derive::{Serialize, Deserialize};
|
||||||
// Crate
|
|
||||||
use crate::{
|
use crate::{
|
||||||
vol::VolSize,
|
vol::VolSize,
|
||||||
volumes::{
|
volumes::{
|
||||||
@ -21,6 +19,7 @@ use crate::{
|
|||||||
|
|
||||||
// TerrainChunkSize
|
// TerrainChunkSize
|
||||||
|
|
||||||
|
#[derive(Clone, Serialize, Deserialize)]
|
||||||
pub struct TerrainChunkSize;
|
pub struct TerrainChunkSize;
|
||||||
|
|
||||||
impl VolSize for TerrainChunkSize {
|
impl VolSize for TerrainChunkSize {
|
||||||
@ -29,6 +28,7 @@ impl VolSize for TerrainChunkSize {
|
|||||||
|
|
||||||
// TerrainChunkMeta
|
// TerrainChunkMeta
|
||||||
|
|
||||||
|
#[derive(Clone, Serialize, Deserialize)]
|
||||||
pub struct TerrainChunkMeta {
|
pub struct TerrainChunkMeta {
|
||||||
biome: BiomeKind,
|
biome: BiomeKind,
|
||||||
}
|
}
|
||||||
|
@ -3,6 +3,7 @@ use std::marker::PhantomData;
|
|||||||
|
|
||||||
// Library
|
// Library
|
||||||
use vek::*;
|
use vek::*;
|
||||||
|
use serde_derive::{Serialize, Deserialize};
|
||||||
|
|
||||||
// Local
|
// Local
|
||||||
use crate::vol::{
|
use crate::vol::{
|
||||||
@ -23,6 +24,7 @@ pub enum ChunkErr {
|
|||||||
// V = Voxel
|
// V = Voxel
|
||||||
// S = Size (replace when const generics are a thing)
|
// S = Size (replace when const generics are a thing)
|
||||||
// M = Metadata
|
// M = Metadata
|
||||||
|
#[derive(Clone, Serialize, Deserialize)]
|
||||||
pub struct Chunk<V: Vox, S: VolSize, M> {
|
pub struct Chunk<V: Vox, S: VolSize, M> {
|
||||||
vox: Vec<V>,
|
vox: Vec<V>,
|
||||||
meta: M,
|
meta: M,
|
||||||
|
@ -126,7 +126,37 @@ impl<V: Vox, S: VolSize, M> VolMap<V, S, M> {
|
|||||||
self.chunks.insert(key, chunk)
|
self.chunks.insert(key, chunk)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn remove(&mut self, key: &Vec3<i32>) -> Option<Chunk<V, S, M>> {
|
pub fn get_key(&self, key: Vec3<i32>) -> Option<&Chunk<V, S, M>> {
|
||||||
self.chunks.remove(key)
|
self.chunks.get(&key)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn remove(&mut self, key: Vec3<i32>) -> Option<Chunk<V, S, M>> {
|
||||||
|
self.chunks.remove(&key)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn key_pos(&self, key: Vec3<i32>) -> Vec3<i32> {
|
||||||
|
key * S::SIZE.map(|e| e as i32)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn pos_key(&self, pos: Vec3<i32>) -> Vec3<i32> {
|
||||||
|
Self::chunk_key(pos)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn iter<'a>(&'a self) -> ChunkIter<'a, V, S, M> {
|
||||||
|
ChunkIter {
|
||||||
|
iter: self.chunks.iter(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct ChunkIter<'a, V: Vox, S: VolSize, M> {
|
||||||
|
iter: std::collections::hash_map::Iter<'a, Vec3<i32>, Chunk<V, S, M>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a, V: Vox, S: VolSize, M> Iterator for ChunkIter<'a, V, S, M> {
|
||||||
|
type Item = (Vec3<i32>, &'a Chunk<V, S, M>);
|
||||||
|
|
||||||
|
fn next(&mut self) -> Option<Self::Item> {
|
||||||
|
self.iter.next().map(|(k, c)| (*k, c))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "veloren-server-cli"
|
name = "veloren-server-cli"
|
||||||
version = "0.1.0"
|
version = "0.2.0"
|
||||||
authors = ["Joshua Barretto <joshua.s.barretto@gmail.com>"]
|
authors = ["Joshua Barretto <joshua.s.barretto@gmail.com>"]
|
||||||
edition = "2018"
|
edition = "2018"
|
||||||
|
|
||||||
|
@ -24,9 +24,9 @@ fn main() {
|
|||||||
|
|
||||||
for event in events {
|
for event in events {
|
||||||
match event {
|
match event {
|
||||||
Event::ClientConnected { ecs_entity } => info!("Client connected!"),
|
Event::ClientConnected { entity } => info!("Client connected!"),
|
||||||
Event::ClientDisconnected { ecs_entity } => info!("Client disconnected!"),
|
Event::ClientDisconnected { entity } => info!("Client disconnected!"),
|
||||||
Event::Chat { ecs_entity, msg } => info!("[Client] {}", msg),
|
Event::Chat { entity, msg } => info!("[Client] {}", msg),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "veloren-server"
|
name = "veloren-server"
|
||||||
version = "0.1.0"
|
version = "0.2.0"
|
||||||
authors = ["Joshua Barretto <joshua.s.barretto@gmail.com>"]
|
authors = ["Joshua Barretto <joshua.s.barretto@gmail.com>"]
|
||||||
edition = "2018"
|
edition = "2018"
|
||||||
|
|
||||||
@ -10,3 +10,4 @@ world = { package = "veloren-world", path = "../world" }
|
|||||||
|
|
||||||
specs = "0.14"
|
specs = "0.14"
|
||||||
vek = "0.9"
|
vek = "0.9"
|
||||||
|
threadpool = "1.7"
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
use std::collections::HashMap;
|
||||||
use specs::Entity as EcsEntity;
|
use specs::Entity as EcsEntity;
|
||||||
use common::{
|
use common::{
|
||||||
comp,
|
comp,
|
||||||
@ -6,41 +7,61 @@ use common::{
|
|||||||
};
|
};
|
||||||
use crate::Error;
|
use crate::Error;
|
||||||
|
|
||||||
|
#[derive(PartialEq)]
|
||||||
|
pub enum ClientState {
|
||||||
|
Connecting,
|
||||||
|
Connected,
|
||||||
|
}
|
||||||
|
|
||||||
pub struct Client {
|
pub struct Client {
|
||||||
pub ecs_entity: EcsEntity,
|
pub state: ClientState,
|
||||||
pub postbox: PostBox<ServerMsg, ClientMsg>,
|
pub postbox: PostBox<ServerMsg, ClientMsg>,
|
||||||
pub last_ping: f64,
|
pub last_ping: f64,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Client {
|
||||||
|
pub fn notify(&mut self, msg: ServerMsg) {
|
||||||
|
self.postbox.send_message(msg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub struct Clients {
|
pub struct Clients {
|
||||||
clients: Vec<Client>,
|
clients: HashMap<EcsEntity, Client>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Clients {
|
impl Clients {
|
||||||
pub fn empty() -> Self {
|
pub fn empty() -> Self {
|
||||||
Self {
|
Self {
|
||||||
clients: Vec::new(),
|
clients: HashMap::new(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn add(&mut self, client: Client) {
|
pub fn add(&mut self, entity: EcsEntity, client: Client) {
|
||||||
self.clients.push(client);
|
self.clients.insert(entity, client);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn remove_if<F: FnMut(&mut Client) -> bool>(&mut self, f: F) {
|
pub fn remove_if<F: FnMut(EcsEntity, &mut Client) -> bool>(&mut self, mut f: F) {
|
||||||
self.clients.drain_filter(f);
|
self.clients.retain(|entity, client| !f(*entity, client));
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn notify_all(&mut self, msg: ServerMsg) {
|
pub fn notify(&mut self, entity: EcsEntity, msg: ServerMsg) {
|
||||||
for client in &mut self.clients {
|
if let Some(client) = self.clients.get_mut(&entity) {
|
||||||
client.postbox.send(msg.clone());
|
client.notify(msg);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn notify_all_except(&mut self, ecs_entity: EcsEntity, msg: ServerMsg) {
|
pub fn notify_connected(&mut self, msg: ServerMsg) {
|
||||||
for client in &mut self.clients {
|
for client in self.clients.values_mut() {
|
||||||
if client.ecs_entity != ecs_entity {
|
if client.state == ClientState::Connected {
|
||||||
client.postbox.send(msg.clone());
|
client.notify(msg.clone());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn notify_connected_except(&mut self, except_entity: EcsEntity, msg: ServerMsg) {
|
||||||
|
for (entity, client) in self.clients.iter_mut() {
|
||||||
|
if client.state == ClientState::Connected && *entity != except_entity {
|
||||||
|
client.notify(msg.clone());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -13,6 +13,8 @@ pub use crate::{
|
|||||||
use std::{
|
use std::{
|
||||||
time::Duration,
|
time::Duration,
|
||||||
net::SocketAddr,
|
net::SocketAddr,
|
||||||
|
sync::mpsc,
|
||||||
|
collections::HashSet,
|
||||||
};
|
};
|
||||||
use specs::{
|
use specs::{
|
||||||
Entity as EcsEntity,
|
Entity as EcsEntity,
|
||||||
@ -22,14 +24,17 @@ use specs::{
|
|||||||
saveload::MarkedBuilder,
|
saveload::MarkedBuilder,
|
||||||
};
|
};
|
||||||
use vek::*;
|
use vek::*;
|
||||||
|
use threadpool::ThreadPool;
|
||||||
use common::{
|
use common::{
|
||||||
comp,
|
comp,
|
||||||
state::State,
|
state::State,
|
||||||
net::PostOffice,
|
net::PostOffice,
|
||||||
msg::{ServerMsg, ClientMsg},
|
msg::{ServerMsg, ClientMsg},
|
||||||
|
terrain::TerrainChunk,
|
||||||
};
|
};
|
||||||
use world::World;
|
use world::World;
|
||||||
use crate::client::{
|
use crate::client::{
|
||||||
|
ClientState,
|
||||||
Client,
|
Client,
|
||||||
Clients,
|
Clients,
|
||||||
};
|
};
|
||||||
@ -38,13 +43,13 @@ const CLIENT_TIMEOUT: f64 = 5.0; // Seconds
|
|||||||
|
|
||||||
pub enum Event {
|
pub enum Event {
|
||||||
ClientConnected {
|
ClientConnected {
|
||||||
ecs_entity: EcsEntity,
|
entity: EcsEntity,
|
||||||
},
|
},
|
||||||
ClientDisconnected {
|
ClientDisconnected {
|
||||||
ecs_entity: EcsEntity,
|
entity: EcsEntity,
|
||||||
},
|
},
|
||||||
Chat {
|
Chat {
|
||||||
ecs_entity: EcsEntity,
|
entity: EcsEntity,
|
||||||
msg: String,
|
msg: String,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
@ -55,18 +60,32 @@ pub struct Server {
|
|||||||
|
|
||||||
postoffice: PostOffice<ServerMsg, ClientMsg>,
|
postoffice: PostOffice<ServerMsg, ClientMsg>,
|
||||||
clients: Clients,
|
clients: Clients,
|
||||||
|
|
||||||
|
thread_pool: ThreadPool,
|
||||||
|
chunk_tx: mpsc::Sender<(Vec3<i32>, TerrainChunk)>,
|
||||||
|
chunk_rx: mpsc::Receiver<(Vec3<i32>, TerrainChunk)>,
|
||||||
|
pending_chunks: HashSet<Vec3<i32>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Server {
|
impl Server {
|
||||||
/// Create a new `Server`.
|
/// Create a new `Server`.
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
pub fn new() -> Result<Self, Error> {
|
pub fn new() -> Result<Self, Error> {
|
||||||
|
let (chunk_tx, chunk_rx) = mpsc::channel();
|
||||||
|
|
||||||
Ok(Self {
|
Ok(Self {
|
||||||
state: State::new(),
|
state: State::new(),
|
||||||
world: World::new(),
|
world: World::new(),
|
||||||
|
|
||||||
postoffice: PostOffice::bind(SocketAddr::from(([0; 4], 59003)))?,
|
postoffice: PostOffice::bind(SocketAddr::from(([0; 4], 59003)))?,
|
||||||
clients: Clients::empty(),
|
clients: Clients::empty(),
|
||||||
|
|
||||||
|
thread_pool: threadpool::Builder::new()
|
||||||
|
.thread_name("veloren-worker".into())
|
||||||
|
.build(),
|
||||||
|
chunk_tx,
|
||||||
|
chunk_rx,
|
||||||
|
pending_chunks: HashSet::new(),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -77,15 +96,6 @@ impl Server {
|
|||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
pub fn state_mut(&mut self) -> &mut State { &mut self.state }
|
pub fn state_mut(&mut self) -> &mut State { &mut self.state }
|
||||||
|
|
||||||
/// Build a new player with a generated UID
|
|
||||||
pub fn build_player(&mut self) -> EcsEntityBuilder {
|
|
||||||
self.state.build_uid_entity()
|
|
||||||
.with(comp::phys::Pos(Vec3::zero()))
|
|
||||||
.with(comp::phys::Vel(Vec3::zero()))
|
|
||||||
.with(comp::phys::Dir(Vec3::unit_y()))
|
|
||||||
.with(comp::phys::UpdateKind::Passive)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Get a reference to the server's world.
|
/// Get a reference to the server's world.
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
pub fn world(&self) -> &World { &self.world }
|
pub fn world(&self) -> &World { &self.world }
|
||||||
@ -127,6 +137,29 @@ impl Server {
|
|||||||
// Tick the client's LocalState (step 3)
|
// Tick the client's LocalState (step 3)
|
||||||
self.state.tick(dt);
|
self.state.tick(dt);
|
||||||
|
|
||||||
|
// Fetch any generated `TerrainChunk`s and insert them into the terrain
|
||||||
|
// Also, send the chunk data to anybody that is close by
|
||||||
|
for (key, chunk) in self.chunk_rx.try_iter() {
|
||||||
|
// Send the chunk to all nearby players
|
||||||
|
for (entity, player, pos) in (
|
||||||
|
&self.state.ecs().internal().entities(),
|
||||||
|
&self.state.ecs().internal().read_storage::<comp::Player>(),
|
||||||
|
&self.state.ecs().internal().read_storage::<comp::phys::Pos>(),
|
||||||
|
).join() {
|
||||||
|
// TODO: Distance check
|
||||||
|
// if self.state.terrain().key_pos(key)
|
||||||
|
|
||||||
|
/*
|
||||||
|
self.clients.notify(entity, ServerMsg::TerrainChunkUpdate {
|
||||||
|
key,
|
||||||
|
chunk: Box::new(chunk.clone()),
|
||||||
|
});
|
||||||
|
*/
|
||||||
|
}
|
||||||
|
|
||||||
|
self.state.insert_chunk(key, chunk);
|
||||||
|
}
|
||||||
|
|
||||||
// Synchronise clients with the new state of the world
|
// Synchronise clients with the new state of the world
|
||||||
self.sync_clients();
|
self.sync_clients();
|
||||||
|
|
||||||
@ -145,24 +178,20 @@ impl Server {
|
|||||||
fn handle_new_connections(&mut self) -> Result<Vec<Event>, Error> {
|
fn handle_new_connections(&mut self) -> Result<Vec<Event>, Error> {
|
||||||
let mut frontend_events = Vec::new();
|
let mut frontend_events = Vec::new();
|
||||||
|
|
||||||
for mut postbox in self.postoffice.new_connections() {
|
for mut postbox in self.postoffice.new_postboxes() {
|
||||||
let ecs_entity = self.build_player()
|
let entity = self.state
|
||||||
// When the player is first created, force a physics notification to everyone
|
.ecs_mut()
|
||||||
// including themselves.
|
.create_entity_synced()
|
||||||
.with(comp::phys::UpdateKind::Force)
|
|
||||||
.build();
|
.build();
|
||||||
let uid = self.state.read_storage().get(ecs_entity).cloned().unwrap();
|
|
||||||
|
|
||||||
postbox.send(ServerMsg::SetPlayerEntity(uid));
|
self.clients.add(entity, Client {
|
||||||
|
state: ClientState::Connecting,
|
||||||
self.clients.add(Client {
|
|
||||||
ecs_entity,
|
|
||||||
postbox,
|
postbox,
|
||||||
last_ping: self.state.get_time(),
|
last_ping: self.state.get_time(),
|
||||||
});
|
});
|
||||||
|
|
||||||
frontend_events.push(Event::ClientConnected {
|
frontend_events.push(Event::ClientConnected {
|
||||||
ecs_entity,
|
entity,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -176,9 +205,10 @@ impl Server {
|
|||||||
let state = &mut self.state;
|
let state = &mut self.state;
|
||||||
let mut new_chat_msgs = Vec::new();
|
let mut new_chat_msgs = Vec::new();
|
||||||
let mut disconnected_clients = Vec::new();
|
let mut disconnected_clients = Vec::new();
|
||||||
|
let mut requested_chunks = Vec::new();
|
||||||
|
|
||||||
self.clients.remove_if(|client| {
|
self.clients.remove_if(|entity, client| {
|
||||||
let mut disconnected = false;
|
let mut disconnect = false;
|
||||||
let new_msgs = client.postbox.new_messages();
|
let new_msgs = client.postbox.new_messages();
|
||||||
|
|
||||||
// Update client ping
|
// Update client ping
|
||||||
@ -187,30 +217,66 @@ impl Server {
|
|||||||
|
|
||||||
// Process incoming messages
|
// Process incoming messages
|
||||||
for msg in new_msgs {
|
for msg in new_msgs {
|
||||||
match msg {
|
match client.state {
|
||||||
ClientMsg::Ping => client.postbox.send(ServerMsg::Pong),
|
ClientState::Connecting => match msg {
|
||||||
ClientMsg::Pong => {},
|
ClientMsg::Connect { player, character } => {
|
||||||
ClientMsg::Chat(msg) => new_chat_msgs.push((client.ecs_entity, msg)),
|
|
||||||
ClientMsg::PlayerPhysics { pos, vel, dir } => {
|
// Write client components
|
||||||
state.write_component(client.ecs_entity, pos);
|
state.write_component(entity, player);
|
||||||
state.write_component(client.ecs_entity, vel);
|
state.write_component(entity, comp::phys::Pos(Vec3::zero()));
|
||||||
state.write_component(client.ecs_entity, dir);
|
state.write_component(entity, comp::phys::Vel(Vec3::zero()));
|
||||||
|
state.write_component(entity, comp::phys::Dir(Vec3::unit_y()));
|
||||||
|
if let Some(character) = character {
|
||||||
|
state.write_component(entity, character);
|
||||||
|
}
|
||||||
|
|
||||||
|
client.state = ClientState::Connected;
|
||||||
|
|
||||||
|
// Return a handshake with the state of the current world
|
||||||
|
client.notify(ServerMsg::Handshake {
|
||||||
|
ecs_state: state.ecs().gen_state_package(),
|
||||||
|
player_entity: state
|
||||||
|
.ecs()
|
||||||
|
.uid_from_entity(entity)
|
||||||
|
.unwrap()
|
||||||
|
.into(),
|
||||||
|
});
|
||||||
|
},
|
||||||
|
_ => disconnect = true,
|
||||||
|
},
|
||||||
|
ClientState::Connected => match msg {
|
||||||
|
ClientMsg::Connect { .. } => disconnect = true, // Not allowed when already connected
|
||||||
|
ClientMsg::Disconnect => disconnect = true,
|
||||||
|
ClientMsg::Ping => client.postbox.send_message(ServerMsg::Pong),
|
||||||
|
ClientMsg::Pong => {},
|
||||||
|
ClientMsg::Chat(msg) => new_chat_msgs.push((entity, msg)),
|
||||||
|
ClientMsg::PlayerPhysics { pos, vel, dir } => {
|
||||||
|
state.write_component(entity, pos);
|
||||||
|
state.write_component(entity, vel);
|
||||||
|
state.write_component(entity, dir);
|
||||||
|
},
|
||||||
|
ClientMsg::TerrainChunkRequest { key } => match state.terrain().get_key(key) {
|
||||||
|
Some(chunk) => {}, /*client.postbox.send_message(ServerMsg::TerrainChunkUpdate {
|
||||||
|
key,
|
||||||
|
chunk: Box::new(chunk.clone()),
|
||||||
|
}),*/
|
||||||
|
None => requested_chunks.push(key),
|
||||||
|
},
|
||||||
},
|
},
|
||||||
ClientMsg::Disconnect => disconnected = true,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if
|
} else if
|
||||||
state.get_time() - client.last_ping > CLIENT_TIMEOUT || // Timeout
|
state.get_time() - client.last_ping > CLIENT_TIMEOUT || // Timeout
|
||||||
client.postbox.error().is_some() // Postbox error
|
client.postbox.error().is_some() // Postbox error
|
||||||
{
|
{
|
||||||
disconnected = true;
|
disconnect = true;
|
||||||
} else if state.get_time() - client.last_ping > CLIENT_TIMEOUT * 0.5 {
|
} else if state.get_time() - client.last_ping > CLIENT_TIMEOUT * 0.5 {
|
||||||
// Try pinging the client if the timeout is nearing
|
// Try pinging the client if the timeout is nearing
|
||||||
client.postbox.send(ServerMsg::Ping);
|
client.postbox.send_message(ServerMsg::Ping);
|
||||||
}
|
}
|
||||||
|
|
||||||
if disconnected {
|
if disconnect {
|
||||||
disconnected_clients.push(client.ecs_entity);
|
disconnected_clients.push(entity);
|
||||||
true
|
true
|
||||||
} else {
|
} else {
|
||||||
false
|
false
|
||||||
@ -218,24 +284,35 @@ impl Server {
|
|||||||
});
|
});
|
||||||
|
|
||||||
// Handle new chat messages
|
// Handle new chat messages
|
||||||
for (ecs_entity, msg) in new_chat_msgs {
|
for (entity, msg) in new_chat_msgs {
|
||||||
self.clients.notify_all(ServerMsg::Chat(msg.clone()));
|
self.clients.notify_connected(ServerMsg::Chat(match state
|
||||||
|
.ecs()
|
||||||
|
.internal()
|
||||||
|
.read_storage::<comp::Player>()
|
||||||
|
.get(entity)
|
||||||
|
{
|
||||||
|
Some(player) => format!("[{}] {}", &player.alias, msg),
|
||||||
|
None => format!("[<anon>] {}", msg),
|
||||||
|
}));
|
||||||
|
|
||||||
frontend_events.push(Event::Chat {
|
frontend_events.push(Event::Chat {
|
||||||
ecs_entity,
|
entity,
|
||||||
msg,
|
msg,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// Handle client disconnects
|
// Handle client disconnects
|
||||||
for ecs_entity in disconnected_clients {
|
for entity in disconnected_clients {
|
||||||
self.clients.notify_all(ServerMsg::EntityDeleted(state.read_storage().get(ecs_entity).cloned().unwrap()));
|
state.ecs_mut().delete_entity_synced(entity);
|
||||||
|
|
||||||
frontend_events.push(Event::ClientDisconnected {
|
frontend_events.push(Event::ClientDisconnected {
|
||||||
ecs_entity,
|
entity,
|
||||||
});
|
});
|
||||||
|
}
|
||||||
|
|
||||||
state.ecs_world_mut().delete_entity(ecs_entity);
|
// Generate requested chunks
|
||||||
|
for key in requested_chunks {
|
||||||
|
self.generate_chunk(key);
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(frontend_events)
|
Ok(frontend_events)
|
||||||
@ -243,36 +320,19 @@ impl Server {
|
|||||||
|
|
||||||
/// Sync client states with the most up to date information
|
/// Sync client states with the most up to date information
|
||||||
fn sync_clients(&mut self) {
|
fn sync_clients(&mut self) {
|
||||||
for (entity, &uid, &pos, &vel, &dir, update_kind) in (
|
self.clients.notify_connected(ServerMsg::EcsSync(self.state.ecs_mut().next_sync_package()));
|
||||||
&self.state.ecs_world().entities(),
|
}
|
||||||
&self.state.ecs_world().read_storage::<comp::Uid>(),
|
|
||||||
&self.state.ecs_world().read_storage::<comp::phys::Pos>(),
|
|
||||||
&self.state.ecs_world().read_storage::<comp::phys::Vel>(),
|
|
||||||
&self.state.ecs_world().read_storage::<comp::phys::Dir>(),
|
|
||||||
&mut self.state.ecs_world().write_storage::<comp::phys::UpdateKind>(),
|
|
||||||
).join() {
|
|
||||||
let msg = ServerMsg::EntityPhysics {
|
|
||||||
uid,
|
|
||||||
pos,
|
|
||||||
vel,
|
|
||||||
dir,
|
|
||||||
};
|
|
||||||
|
|
||||||
// Sometimes we need to force updated (i.e: teleporting players). This involves sending
|
pub fn generate_chunk(&mut self, key: Vec3<i32>) {
|
||||||
// everyone, including the player themselves, of their new physics information.
|
if self.pending_chunks.insert(key) {
|
||||||
match update_kind {
|
let chunk_tx = self.chunk_tx.clone();
|
||||||
comp::phys::UpdateKind::Force => self.clients.notify_all(msg),
|
self.thread_pool.execute(move || chunk_tx.send((key, World::generate_chunk(key))).unwrap());
|
||||||
comp::phys::UpdateKind::Passive => self.clients.notify_all_except(entity, msg),
|
|
||||||
}
|
|
||||||
|
|
||||||
// Now that the update has occured, default to a passive update
|
|
||||||
*update_kind = comp::phys::UpdateKind::Passive;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Drop for Server {
|
impl Drop for Server {
|
||||||
fn drop(&mut self) {
|
fn drop(&mut self) {
|
||||||
self.clients.notify_all(ServerMsg::Shutdown);
|
self.clients.notify_connected(ServerMsg::Shutdown);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "voxygen"
|
name = "veloren-voxygen"
|
||||||
version = "0.1.0"
|
version = "0.2.0"
|
||||||
authors = ["Joshua Barretto <joshua.s.barretto@gmail.com>"]
|
authors = ["Joshua Barretto <joshua.s.barretto@gmail.com>"]
|
||||||
edition = "2018"
|
edition = "2018"
|
||||||
|
|
||||||
|
@ -24,27 +24,28 @@ impl Animation for RunAnimation {
|
|||||||
let wave_slow = (time as f32 * 6.0 + PI).sin();
|
let wave_slow = (time as f32 * 6.0 + PI).sin();
|
||||||
let wave_dip = (wave_slow.abs() - 0.5).abs();
|
let wave_dip = (wave_slow.abs() - 0.5).abs();
|
||||||
|
|
||||||
skeleton.head.offset = Vec3::unit_z() * 13.0;
|
skeleton.head.offset = Vec3::unit_z() * 13.0 / 11.0;
|
||||||
skeleton.head.ori = Quaternion::rotation_z(wave * 0.3);
|
skeleton.head.ori = Quaternion::rotation_z(wave * 0.3);
|
||||||
|
|
||||||
skeleton.chest.offset = Vec3::unit_z() * 9.0;
|
skeleton.chest.offset = Vec3::unit_z() * 9.0 / 11.0;
|
||||||
skeleton.chest.ori = Quaternion::rotation_z(wave * 0.3);
|
skeleton.chest.ori = Quaternion::rotation_z(wave * 0.3);
|
||||||
|
|
||||||
skeleton.belt.offset = Vec3::unit_z() * 7.0;
|
skeleton.belt.offset = Vec3::unit_z() * 7.0 / 11.0;
|
||||||
skeleton.belt.ori = Quaternion::rotation_z(wave * 0.2);
|
skeleton.belt.ori = Quaternion::rotation_z(wave * 0.2);
|
||||||
|
|
||||||
skeleton.shorts.offset = Vec3::unit_z() * 4.0;
|
skeleton.shorts.offset = Vec3::unit_z() * 4.0 / 11.0;
|
||||||
skeleton.shorts.ori = Quaternion::rotation_z(wave * 0.1);
|
skeleton.shorts.ori = Quaternion::rotation_z(wave * 0.1);
|
||||||
|
|
||||||
skeleton.l_hand.offset = Vec3::new(-6.0 - wave_dip * 6.0, wave * 5.0, 11.0 - wave_dip * 6.0);
|
skeleton.l_hand.offset = Vec3::new(-6.0 - wave_dip * 6.0, wave * 5.0, 11.0 - wave_dip * 6.0) / 11.0;
|
||||||
skeleton.r_hand.offset = Vec3::new(6.0 + wave_dip * 6.0, -wave * 5.0, 11.0 - wave_dip * 6.0);
|
skeleton.r_hand.offset = Vec3::new(6.0 + wave_dip * 6.0, -wave * 5.0, 11.0 - wave_dip * 6.0) / 11.0;
|
||||||
|
|
||||||
skeleton.l_foot.offset = Vec3::new(-3.5, 1.0 - wave * 8.0, 3.5 - wave_dip * 4.0);
|
skeleton.l_foot.offset = Vec3::new(-3.5, 1.0 - wave * 8.0, 3.5 - wave_dip * 4.0) / 11.0;
|
||||||
skeleton.l_foot.ori = Quaternion::rotation_x(-wave + 1.0);
|
skeleton.l_foot.ori = Quaternion::rotation_x(-wave + 1.0);
|
||||||
skeleton.r_foot.offset = Vec3::new(3.5, 1.0 + wave * 8.0, 3.5 - wave_dip * 4.0);
|
skeleton.r_foot.offset = Vec3::new(3.5, 1.0 + wave * 8.0, 3.5 - wave_dip * 4.0) / 11.0;
|
||||||
skeleton.r_foot.ori = Quaternion::rotation_x(wave + 1.0);
|
skeleton.r_foot.ori = Quaternion::rotation_x(wave + 1.0);
|
||||||
|
|
||||||
skeleton.back.offset = Vec3::new(-9.0, 5.0, 18.0);
|
skeleton.back.offset = Vec3::new(-9.0, 5.0, 18.0);
|
||||||
skeleton.back.ori = Quaternion::rotation_y(2.5);
|
skeleton.back.ori = Quaternion::rotation_y(2.5);
|
||||||
|
skeleton.back.scale = Vec3::one();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -10,6 +10,7 @@ use crate::render::FigureBoneData;
|
|||||||
pub struct Bone {
|
pub struct Bone {
|
||||||
pub offset: Vec3<f32>,
|
pub offset: Vec3<f32>,
|
||||||
pub ori: Quaternion<f32>,
|
pub ori: Quaternion<f32>,
|
||||||
|
pub scale: Vec3<f32>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Bone {
|
impl Bone {
|
||||||
@ -17,15 +18,16 @@ impl Bone {
|
|||||||
Self {
|
Self {
|
||||||
offset: Vec3::zero(),
|
offset: Vec3::zero(),
|
||||||
ori: Quaternion::identity(),
|
ori: Quaternion::identity(),
|
||||||
|
scale: Vec3::broadcast(1.0 / 11.0),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn compute_base_matrix(&self) -> Mat4<f32> {
|
pub fn compute_base_matrix(&self) -> Mat4<f32> {
|
||||||
Mat4::<f32>::translation_3d(self.offset) * Mat4::from(self.ori)
|
Mat4::<f32>::translation_3d(self.offset) * Mat4::scaling_3d(self.scale) * Mat4::from(self.ori)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait Skeleton {
|
pub trait Skeleton: Send + Sync + 'static {
|
||||||
fn compute_matrices(&self) -> [FigureBoneData; 16];
|
fn compute_matrices(&self) -> [FigureBoneData; 16];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -6,7 +6,10 @@ use crate::{
|
|||||||
GlobalState, PlayState, PlayStateResult,
|
GlobalState, PlayState, PlayStateResult,
|
||||||
};
|
};
|
||||||
use client::{self, Client};
|
use client::{self, Client};
|
||||||
use common::clock::Clock;
|
use common::{
|
||||||
|
comp,
|
||||||
|
clock::Clock,
|
||||||
|
};
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
use ui::{Event as MainMenuEvent, MainMenuUi};
|
use ui::{Event as MainMenuEvent, MainMenuUi};
|
||||||
use vek::*;
|
use vek::*;
|
||||||
@ -55,7 +58,7 @@ impl PlayState for MainMenuState {
|
|||||||
|
|
||||||
global_state.window.renderer_mut().clear(BG_COLOR);
|
global_state.window.renderer_mut().clear(BG_COLOR);
|
||||||
|
|
||||||
// Maintain the UI
|
// Maintain the UI (TODO: Maybe clean this up a little to avoid rightward drift?)
|
||||||
for event in self.main_menu_ui.maintain(global_state.window.renderer_mut()) {
|
for event in self.main_menu_ui.maintain(global_state.window.renderer_mut()) {
|
||||||
match event {
|
match event {
|
||||||
MainMenuEvent::LoginAttempt{ username, server_address } => {
|
MainMenuEvent::LoginAttempt{ username, server_address } => {
|
||||||
@ -67,12 +70,12 @@ impl PlayState for MainMenuState {
|
|||||||
Ok(mut socket_adders) => {
|
Ok(mut socket_adders) => {
|
||||||
while let Some(socket_addr) = socket_adders.next() {
|
while let Some(socket_addr) = socket_adders.next() {
|
||||||
// TODO: handle error
|
// TODO: handle error
|
||||||
match Client::new(socket_addr) {
|
match Client::new(socket_addr, comp::Player::new(username.clone()), Some(comp::Character::test()), 300) {
|
||||||
Ok(client) => {
|
Ok(client) => {
|
||||||
return PlayStateResult::Push(
|
return PlayStateResult::Push(
|
||||||
Box::new(CharSelectionState::new(
|
Box::new(CharSelectionState::new(
|
||||||
&mut global_state.window,
|
&mut global_state.window,
|
||||||
std::rc::Rc::new(std::cell::RefCell::new(client.with_test_state())) // <--- TODO: Remove this
|
std::rc::Rc::new(std::cell::RefCell::new(client)) // <--- TODO: Remove this
|
||||||
))
|
))
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,14 @@
|
|||||||
use specs::{Component, VecStorage};
|
use std::{
|
||||||
|
collections::HashMap,
|
||||||
|
f32,
|
||||||
|
};
|
||||||
|
use specs::{Entity as EcsEntity, Component, VecStorage, Join};
|
||||||
use vek::*;
|
use vek::*;
|
||||||
|
use client::Client;
|
||||||
|
use common::{
|
||||||
|
comp,
|
||||||
|
figure::Segment,
|
||||||
|
};
|
||||||
use crate::{
|
use crate::{
|
||||||
Error,
|
Error,
|
||||||
render::{
|
render::{
|
||||||
@ -12,42 +21,50 @@ use crate::{
|
|||||||
FigureBoneData,
|
FigureBoneData,
|
||||||
FigureLocals,
|
FigureLocals,
|
||||||
},
|
},
|
||||||
anim::Skeleton,
|
anim::{
|
||||||
|
Animation,
|
||||||
|
Skeleton,
|
||||||
|
character::{
|
||||||
|
CharacterSkeleton,
|
||||||
|
RunAnimation,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
mesh::Meshable,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub struct Figure<S: Skeleton> {
|
pub struct Figures {
|
||||||
// GPU data
|
test_model: Model<FigurePipeline>,
|
||||||
model: Model<FigurePipeline>,
|
states: HashMap<EcsEntity, FigureState<CharacterSkeleton>>,
|
||||||
bone_consts: Consts<FigureBoneData>,
|
|
||||||
locals: Consts<FigureLocals>,
|
|
||||||
|
|
||||||
// CPU data
|
|
||||||
bone_meshes: [Option<Mesh<FigurePipeline>>; 16],
|
|
||||||
pub skeleton: S,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<S: Skeleton> Figure<S> {
|
impl Figures {
|
||||||
pub fn new(
|
pub fn new(renderer: &mut Renderer) -> Self {
|
||||||
renderer: &mut Renderer,
|
// TODO: Make a proper asset loading system
|
||||||
bone_meshes: [Option<Mesh<FigurePipeline>>; 16],
|
fn load_segment(filename: &'static str) -> Segment {
|
||||||
skeleton: S,
|
Segment::from(dot_vox::load(&(concat!(env!("CARGO_MANIFEST_DIR"), "/../assets/voxygen/voxel/").to_string() + filename)).unwrap())
|
||||||
) -> Result<Self, Error> {
|
}
|
||||||
let mut this = Self {
|
|
||||||
model: renderer.create_model(&Mesh::new())?,
|
|
||||||
bone_consts: renderer.create_consts(&skeleton.compute_matrices())?,
|
|
||||||
locals: renderer.create_consts(&[FigureLocals::default()])?,
|
|
||||||
|
|
||||||
bone_meshes,
|
let bone_meshes = [
|
||||||
skeleton,
|
Some(load_segment("head.vox").generate_mesh(Vec3::new(-7.0, -6.5, -6.0))),
|
||||||
};
|
Some(load_segment("chest.vox").generate_mesh(Vec3::new(-6.0, -3.0, 0.0))),
|
||||||
this.update_model(renderer)?;
|
Some(load_segment("belt.vox").generate_mesh(Vec3::new(-5.0, -3.0, 0.0))),
|
||||||
Ok(this)
|
Some(load_segment("pants.vox").generate_mesh(Vec3::new(-5.0, -3.0, 0.0))),
|
||||||
}
|
Some(load_segment("hand.vox").generate_mesh(Vec3::new(-2.0, -2.0, -1.0))),
|
||||||
|
Some(load_segment("hand.vox").generate_mesh(Vec3::new(-2.0, -2.0, -1.0))),
|
||||||
|
Some(load_segment("foot.vox").generate_mesh(Vec3::new(-2.5, -3.0, -2.0))),
|
||||||
|
Some(load_segment("foot.vox").generate_mesh(Vec3::new(-2.5, -3.0, -2.0))),
|
||||||
|
Some(load_segment("sword.vox").generate_mesh(Vec3::new(-6.5, -1.0, 0.0))),
|
||||||
|
None,
|
||||||
|
None,
|
||||||
|
None,
|
||||||
|
None,
|
||||||
|
None,
|
||||||
|
None,
|
||||||
|
None,
|
||||||
|
];
|
||||||
|
|
||||||
pub fn update_model(&mut self, renderer: &mut Renderer) -> Result<(), Error> {
|
|
||||||
let mut mesh = Mesh::new();
|
let mut mesh = Mesh::new();
|
||||||
|
bone_meshes
|
||||||
self.bone_meshes
|
|
||||||
.iter()
|
.iter()
|
||||||
.enumerate()
|
.enumerate()
|
||||||
.filter_map(|(i, bm)| bm.as_ref().map(|bm| (i, bm)))
|
.filter_map(|(i, bm)| bm.as_ref().map(|bm| (i, bm)))
|
||||||
@ -55,39 +72,69 @@ impl<S: Skeleton> Figure<S> {
|
|||||||
mesh.push_mesh_map(bone_mesh, |vert| vert.with_bone_idx(i as u8))
|
mesh.push_mesh_map(bone_mesh, |vert| vert.with_bone_idx(i as u8))
|
||||||
});
|
});
|
||||||
|
|
||||||
self.model = renderer.create_model(&mesh)?;
|
Self {
|
||||||
Ok(())
|
test_model: renderer.create_model(&mesh).unwrap(),
|
||||||
|
states: HashMap::new(),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn update_skeleton(&mut self, renderer: &mut Renderer) -> Result<(), Error> {
|
pub fn maintain(&mut self, renderer: &mut Renderer, client: &mut Client) {
|
||||||
renderer.update_consts(&mut self.bone_consts, &self.skeleton.compute_matrices())?;
|
let time = client.state().get_time();
|
||||||
Ok(())
|
let ecs = client.state_mut().ecs_mut().internal_mut();
|
||||||
|
for (entity, pos, dir, character) in (
|
||||||
|
&ecs.entities(),
|
||||||
|
&ecs.read_storage::<comp::phys::Pos>(),
|
||||||
|
&ecs.read_storage::<comp::phys::Dir>(),
|
||||||
|
&ecs.read_storage::<comp::Character>(),
|
||||||
|
).join() {
|
||||||
|
let state = self.states
|
||||||
|
.entry(entity)
|
||||||
|
.or_insert_with(|| FigureState::new(renderer, CharacterSkeleton::new()));
|
||||||
|
|
||||||
|
RunAnimation::update_skeleton(&mut state.skeleton, time);
|
||||||
|
|
||||||
|
state.update(renderer, pos.0, dir.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
self.states.retain(|entity, _| ecs.entities().is_alive(*entity));
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn update_locals(&mut self, renderer: &mut Renderer, locals: FigureLocals) -> Result<(), Error> {
|
pub fn render(&self, renderer: &mut Renderer, client: &Client, globals: &Consts<Globals>) {
|
||||||
renderer.update_consts(&mut self.locals, &[locals])?;
|
for state in self.states.values() {
|
||||||
Ok(())
|
renderer.render_figure(
|
||||||
}
|
&self.test_model,
|
||||||
|
globals,
|
||||||
pub fn render(&self, renderer: &mut Renderer, globals: &Consts<Globals>) {
|
&state.locals,
|
||||||
renderer.render_figure(
|
&state.bone_consts,
|
||||||
&self.model,
|
);
|
||||||
globals,
|
}
|
||||||
&self.locals,
|
|
||||||
&self.bone_consts,
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
pub struct FigureState<S: Skeleton> {
|
||||||
#[derive(Copy, Clone, Debug)]
|
|
||||||
pub struct Figure<S: Skeleton> {
|
|
||||||
bone_consts: Consts<FigureBoneData>,
|
bone_consts: Consts<FigureBoneData>,
|
||||||
locals: Consts<FigureLocals>,
|
locals: Consts<FigureLocals>,
|
||||||
skeleton: S,
|
skeleton: S,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<S: Skeleton> Component for Figure<S> {
|
impl<S: Skeleton> FigureState<S> {
|
||||||
type Storage = VecStorage<Self>;
|
pub fn new(renderer: &mut Renderer, skeleton: S) -> Self {
|
||||||
|
Self {
|
||||||
|
bone_consts: renderer.create_consts(&skeleton.compute_matrices()).unwrap(),
|
||||||
|
locals: renderer.create_consts(&[FigureLocals::default()]).unwrap(),
|
||||||
|
skeleton,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn update(&mut self, renderer: &mut Renderer, pos: Vec3<f32>, dir: Vec3<f32>) {
|
||||||
|
let mat =
|
||||||
|
Mat4::<f32>::identity() *
|
||||||
|
Mat4::translation_3d(pos) *
|
||||||
|
Mat4::rotation_z(dir.y.atan2(dir.x) + f32::consts::PI / 2.0);
|
||||||
|
|
||||||
|
let locals = FigureLocals::new(mat);
|
||||||
|
renderer.update_consts(&mut self.locals, &[locals]).unwrap();
|
||||||
|
|
||||||
|
renderer.update_consts(&mut self.bone_consts, &self.skeleton.compute_matrices()).unwrap();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
*/
|
|
||||||
|
@ -29,7 +29,7 @@ use crate::{
|
|||||||
};
|
};
|
||||||
use self::{
|
use self::{
|
||||||
camera::Camera,
|
camera::Camera,
|
||||||
figure::Figure,
|
figure::Figures,
|
||||||
terrain::Terrain,
|
terrain::Terrain,
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -47,13 +47,7 @@ pub struct Scene {
|
|||||||
|
|
||||||
skybox: Skybox,
|
skybox: Skybox,
|
||||||
terrain: Terrain,
|
terrain: Terrain,
|
||||||
|
figures: Figures,
|
||||||
test_figure: Figure<CharacterSkeleton>,
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: Make a proper asset loading system
|
|
||||||
fn load_segment(filename: &'static str) -> Segment {
|
|
||||||
Segment::from(dot_vox::load(&(concat!(env!("CARGO_MANIFEST_DIR"), "/../assets/voxygen/voxel/").to_string() + filename)).unwrap())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Scene {
|
impl Scene {
|
||||||
@ -74,30 +68,7 @@ impl Scene {
|
|||||||
.unwrap(),
|
.unwrap(),
|
||||||
},
|
},
|
||||||
terrain: Terrain::new(),
|
terrain: Terrain::new(),
|
||||||
|
figures: Figures::new(renderer),
|
||||||
test_figure: Figure::new(
|
|
||||||
renderer,
|
|
||||||
[
|
|
||||||
Some(load_segment("head.vox").generate_mesh(Vec3::new(-7.0, -6.5, -6.0))),
|
|
||||||
Some(load_segment("chest.vox").generate_mesh(Vec3::new(-6.0, -3.0, 0.0))),
|
|
||||||
Some(load_segment("belt.vox").generate_mesh(Vec3::new(-5.0, -3.0, 0.0))),
|
|
||||||
Some(load_segment("pants.vox").generate_mesh(Vec3::new(-5.0, -3.0, 0.0))),
|
|
||||||
Some(load_segment("hand.vox").generate_mesh(Vec3::new(-2.0, -2.0, -1.0))),
|
|
||||||
Some(load_segment("hand.vox").generate_mesh(Vec3::new(-2.0, -2.0, -1.0))),
|
|
||||||
Some(load_segment("foot.vox").generate_mesh(Vec3::new(-2.5, -3.0, -2.0))),
|
|
||||||
Some(load_segment("foot.vox").generate_mesh(Vec3::new(-2.5, -3.0, -2.0))),
|
|
||||||
Some(load_segment("sword.vox").generate_mesh(Vec3::new(-6.5, -1.0, 0.0))),
|
|
||||||
None,
|
|
||||||
None,
|
|
||||||
None,
|
|
||||||
None,
|
|
||||||
None,
|
|
||||||
None,
|
|
||||||
None,
|
|
||||||
],
|
|
||||||
CharacterSkeleton::new(),
|
|
||||||
)
|
|
||||||
.unwrap(),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -133,13 +104,14 @@ impl Scene {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Maintain data such as GPU constant buffers, models, etc. To be called once per tick.
|
/// Maintain data such as GPU constant buffers, models, etc. To be called once per tick.
|
||||||
pub fn maintain(&mut self, renderer: &mut Renderer, client: &Client) {
|
pub fn maintain(&mut self, renderer: &mut Renderer, client: &mut Client) {
|
||||||
// Get player position
|
// Get player position
|
||||||
let player_pos = client
|
let player_pos = client
|
||||||
.player()
|
.player()
|
||||||
.and_then(|ent| client
|
.and_then(|ent| client
|
||||||
.state()
|
.state()
|
||||||
.ecs_world()
|
.ecs()
|
||||||
|
.internal()
|
||||||
.read_storage::<comp::phys::Pos>()
|
.read_storage::<comp::phys::Pos>()
|
||||||
.get(ent)
|
.get(ent)
|
||||||
.map(|pos| pos.0)
|
.map(|pos| pos.0)
|
||||||
@ -164,24 +136,13 @@ impl Scene {
|
|||||||
)])
|
)])
|
||||||
.expect("Failed to update global constants");
|
.expect("Failed to update global constants");
|
||||||
|
|
||||||
// Maintain the terrain
|
// Maintain the terrain and figures
|
||||||
self.terrain.maintain(renderer, client);
|
self.terrain.maintain(renderer, client);
|
||||||
|
self.figures.maintain(renderer, client);
|
||||||
// TODO: Don't do this here
|
|
||||||
RunAnimation::update_skeleton(
|
|
||||||
&mut self.test_figure.skeleton,
|
|
||||||
client.state().get_time(),
|
|
||||||
);
|
|
||||||
|
|
||||||
// Calculate player model matrix
|
|
||||||
let model_mat = Mat4::<f32>::translation_3d(player_pos);
|
|
||||||
|
|
||||||
self.test_figure.update_locals(renderer, FigureLocals::new(model_mat)).unwrap();
|
|
||||||
self.test_figure.update_skeleton(renderer).unwrap();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Render the scene using the provided `Renderer`
|
/// Render the scene using the provided `Renderer`
|
||||||
pub fn render_to(&self, renderer: &mut Renderer) {
|
pub fn render(&self, renderer: &mut Renderer, client: &Client) {
|
||||||
// Render the skybox first (it appears over everything else so must be rendered first)
|
// Render the skybox first (it appears over everything else so must be rendered first)
|
||||||
renderer.render_skybox(
|
renderer.render_skybox(
|
||||||
&self.skybox.model,
|
&self.skybox.model,
|
||||||
@ -189,10 +150,8 @@ impl Scene {
|
|||||||
&self.skybox.locals,
|
&self.skybox.locals,
|
||||||
);
|
);
|
||||||
|
|
||||||
// Render terrain
|
// Render terrain and figures
|
||||||
self.terrain.render(renderer, &self.globals);
|
self.terrain.render(renderer, &self.globals);
|
||||||
|
self.figures.render(renderer, client, &self.globals);
|
||||||
// Render the test figure
|
|
||||||
self.test_figure.render(renderer, &self.globals);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -87,7 +87,7 @@ impl SessionState {
|
|||||||
renderer.clear(BG_COLOR);
|
renderer.clear(BG_COLOR);
|
||||||
|
|
||||||
// Render the screen using the global renderer
|
// Render the screen using the global renderer
|
||||||
self.scene.render_to(renderer);
|
self.scene.render(renderer, &self.client.borrow());
|
||||||
// Draw the UI to the screen
|
// Draw the UI to the screen
|
||||||
self.hud.render(renderer);
|
self.hud.render(renderer);
|
||||||
|
|
||||||
@ -105,6 +105,7 @@ impl PlayState for SessionState {
|
|||||||
let mut clock = Clock::new();
|
let mut clock = Clock::new();
|
||||||
|
|
||||||
// Load a few chunks TODO: Remove this
|
// Load a few chunks TODO: Remove this
|
||||||
|
/*
|
||||||
for x in -6..7 {
|
for x in -6..7 {
|
||||||
for y in -6..7 {
|
for y in -6..7 {
|
||||||
for z in -1..2 {
|
for z in -1..2 {
|
||||||
@ -112,6 +113,7 @@ impl PlayState for SessionState {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
// Game loop
|
// Game loop
|
||||||
loop {
|
loop {
|
||||||
@ -155,7 +157,7 @@ impl PlayState for SessionState {
|
|||||||
.expect("Failed to tick the scene");
|
.expect("Failed to tick the scene");
|
||||||
|
|
||||||
// Maintain the scene
|
// Maintain the scene
|
||||||
self.scene.maintain(global_state.window.renderer_mut(), &self.client.borrow());
|
self.scene.maintain(global_state.window.renderer_mut(), &mut self.client.borrow_mut());
|
||||||
// Maintain the UI
|
// Maintain the UI
|
||||||
for event in self.hud.maintain(global_state.window.renderer_mut()) {
|
for event in self.hud.maintain(global_state.window.renderer_mut()) {
|
||||||
match event {
|
match event {
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "veloren-world"
|
name = "veloren-world"
|
||||||
version = "0.1.0"
|
version = "0.2.0"
|
||||||
authors = ["Joshua Barretto <joshua.s.barretto@gmail.com>"]
|
authors = ["Joshua Barretto <joshua.s.barretto@gmail.com>"]
|
||||||
edition = "2018"
|
edition = "2018"
|
||||||
|
|
||||||
|
@ -24,7 +24,7 @@ impl World {
|
|||||||
Self
|
Self
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn generate_chunk(&self, chunk_pos: Vec3<i32>) -> TerrainChunk {
|
pub fn generate_chunk(chunk_pos: Vec3<i32>) -> TerrainChunk {
|
||||||
// TODO: This is all test code, remove/improve this later
|
// TODO: This is all test code, remove/improve this later
|
||||||
|
|
||||||
let mut chunk = TerrainChunk::filled(Block::empty(), TerrainChunkMeta::void());
|
let mut chunk = TerrainChunk::filled(Block::empty(), TerrainChunkMeta::void());
|
||||||
|
Loading…
Reference in New Issue
Block a user