mirror of
https://gitlab.com/veloren/veloren.git
synced 2024-08-30 18:12:32 +00:00
Integrated Sphynx
Former-commit-id: 5d96983a385bb77a2876aa7439158252b7e2f0fc
This commit is contained in:
parent
bea4a73c2e
commit
62b91eb01b
@ -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)
|
||||||
.expect("Failed to create client instance");
|
.expect("Failed to create client instance");
|
||||||
|
|
||||||
client.send_chat("Hello!".to_string());
|
client.send_chat("Hello!".to_string());
|
||||||
|
@ -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),
|
||||||
|
@ -50,11 +50,31 @@ pub struct Client {
|
|||||||
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>,
|
||||||
|
) -> Result<Self, Error> {
|
||||||
|
|
||||||
let mut postbox = PostBox::to_server(addr)?;
|
let mut postbox = PostBox::to_server(addr)?;
|
||||||
|
|
||||||
|
// Send connection request
|
||||||
|
postbox.send(ClientMsg::Connect {
|
||||||
|
player,
|
||||||
|
character,
|
||||||
|
});
|
||||||
|
|
||||||
|
// Wait for handshake from server
|
||||||
|
let (state, player) = match postbox.next_message() {
|
||||||
|
Some(ServerMsg::Handshake { ecs_state, player_entity }) => {
|
||||||
|
println!("STATE PACKAGE! {:?}", ecs_state);
|
||||||
|
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()
|
||||||
.thread_name("veloren-worker".into())
|
.thread_name("veloren-worker".into())
|
||||||
@ -65,7 +85,7 @@ impl Client {
|
|||||||
|
|
||||||
tick: 0,
|
tick: 0,
|
||||||
state,
|
state,
|
||||||
player: None,
|
player,
|
||||||
|
|
||||||
// Testing
|
// Testing
|
||||||
world: World::new(),
|
world: World::new(),
|
||||||
@ -85,12 +105,6 @@ impl Client {
|
|||||||
self
|
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 }
|
||||||
@ -187,28 +201,15 @@ 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(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) => {
|
||||||
.get_entity(uid)
|
println!("SYNC PACKAGE! {:?}", sync_package);
|
||||||
.unwrap_or_else(|| self.state.build_uid_entity_with_uid(uid).build());
|
self.state.ecs_mut().sync_with_package(sync_package)
|
||||||
|
|
||||||
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);
|
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -5,6 +5,8 @@ authors = ["Joshua Barretto <joshua.s.barretto@gmail.com>", "Maciej Ćwięka <mc
|
|||||||
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"] }
|
||||||
|
@ -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)]
|
||||||
|
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
extern crate serde_derive;
|
extern crate serde_derive;
|
||||||
|
@ -1,17 +1,18 @@
|
|||||||
use crate::comp::{
|
use crate::comp;
|
||||||
Uid,
|
|
||||||
phys,
|
|
||||||
};
|
|
||||||
|
|
||||||
#[derive(Clone, Debug, Serialize, Deserialize)]
|
#[derive(Clone, Debug, 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,
|
||||||
},
|
},
|
||||||
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,15 @@
|
|||||||
use crate::comp::{
|
use super::EcsPacket;
|
||||||
Uid,
|
|
||||||
phys,
|
|
||||||
};
|
|
||||||
|
|
||||||
#[derive(Clone, Debug, Serialize, Deserialize)]
|
#[derive(Clone, Debug, 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,
|
|
||||||
pos: phys::Pos,
|
|
||||||
vel: phys::Vel,
|
|
||||||
dir: phys::Dir,
|
|
||||||
},
|
|
||||||
EntityDeleted(Uid),
|
|
||||||
}
|
}
|
||||||
|
@ -251,6 +251,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();
|
||||||
|
|
||||||
|
@ -6,18 +6,19 @@ 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,
|
||||||
|
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?
|
||||||
@ -59,95 +60,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 +136,46 @@ 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
|
// TODO: Get rid of this since it shouldn't be needed
|
||||||
pub fn terrain_mut(&mut self) -> FetchMut<TerrainMap> {
|
pub fn terrain_mut(&mut self) -> FetchMut<TerrainMap> {
|
||||||
self.ecs_world.write_resource::<TerrainMap>()
|
self.ecs.internal_mut().write_resource::<TerrainMap>()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// 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
|
||||||
|
@ -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),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -6,12 +6,25 @@ 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 entity: EcsEntity,
|
||||||
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(msg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub struct Clients {
|
pub struct Clients {
|
||||||
clients: Vec<Client>,
|
clients: Vec<Client>,
|
||||||
}
|
}
|
||||||
@ -31,15 +44,17 @@ impl Clients {
|
|||||||
self.clients.drain_filter(f);
|
self.clients.drain_filter(f);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn notify_all(&mut self, msg: ServerMsg) {
|
pub fn notify_connected(&mut self, msg: ServerMsg) {
|
||||||
for client in &mut self.clients {
|
for client in &mut self.clients {
|
||||||
client.postbox.send(msg.clone());
|
if client.state == ClientState::Connected {
|
||||||
|
client.postbox.send(msg.clone());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn notify_all_except(&mut self, ecs_entity: EcsEntity, msg: ServerMsg) {
|
pub fn notify_connected_except(&mut self, entity: EcsEntity, msg: ServerMsg) {
|
||||||
for client in &mut self.clients {
|
for client in &mut self.clients {
|
||||||
if client.ecs_entity != ecs_entity {
|
if client.entity != entity && client.state == ClientState::Connected {
|
||||||
client.postbox.send(msg.clone());
|
client.postbox.send(msg.clone());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -30,6 +30,7 @@ use common::{
|
|||||||
};
|
};
|
||||||
use world::World;
|
use world::World;
|
||||||
use crate::client::{
|
use crate::client::{
|
||||||
|
ClientState,
|
||||||
Client,
|
Client,
|
||||||
Clients,
|
Clients,
|
||||||
};
|
};
|
||||||
@ -38,13 +39,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,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
@ -77,15 +78,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 }
|
||||||
@ -146,23 +138,20 @@ impl Server {
|
|||||||
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_connections() {
|
||||||
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(Client {
|
self.clients.add(Client {
|
||||||
ecs_entity,
|
state: ClientState::Connecting,
|
||||||
|
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,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -178,7 +167,7 @@ impl Server {
|
|||||||
let mut disconnected_clients = Vec::new();
|
let mut disconnected_clients = Vec::new();
|
||||||
|
|
||||||
self.clients.remove_if(|client| {
|
self.clients.remove_if(|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 +176,56 @@ 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(client.entity, player);
|
||||||
state.write_component(client.ecs_entity, vel);
|
if let Some(character) = character {
|
||||||
state.write_component(client.ecs_entity, dir);
|
state.write_component(client.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(client.entity)
|
||||||
|
.unwrap()
|
||||||
|
.into(),
|
||||||
|
});
|
||||||
|
},
|
||||||
|
_ => disconnect = true,
|
||||||
|
},
|
||||||
|
ClientState::Connected => match msg {
|
||||||
|
ClientMsg::Connect { .. } => disconnect = true, // Not allowed when already connected
|
||||||
|
ClientMsg::Ping => client.postbox.send(ServerMsg::Pong),
|
||||||
|
ClientMsg::Pong => {},
|
||||||
|
ClientMsg::Chat(msg) => new_chat_msgs.push((client.entity, msg)),
|
||||||
|
ClientMsg::PlayerPhysics { pos, vel, dir } => {
|
||||||
|
state.write_component(client.entity, pos);
|
||||||
|
state.write_component(client.entity, vel);
|
||||||
|
state.write_component(client.entity, dir);
|
||||||
|
},
|
||||||
|
ClientMsg::Disconnect => disconnect = true,
|
||||||
},
|
},
|
||||||
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(ServerMsg::Ping);
|
||||||
}
|
}
|
||||||
|
|
||||||
if disconnected {
|
if disconnect {
|
||||||
disconnected_clients.push(client.ecs_entity);
|
disconnected_clients.push(client.entity);
|
||||||
true
|
true
|
||||||
} else {
|
} else {
|
||||||
false
|
false
|
||||||
@ -218,24 +233,30 @@ 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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(frontend_events)
|
Ok(frontend_events)
|
||||||
@ -243,36 +264,12 @@ 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
|
|
||||||
// everyone, including the player themselves, of their new physics information.
|
|
||||||
match update_kind {
|
|
||||||
comp::phys::UpdateKind::Force => self.clients.notify_all(msg),
|
|
||||||
comp::phys::UpdateKind::Passive => self.clients.notify_all_except(entity, msg),
|
|
||||||
}
|
|
||||||
|
|
||||||
// Now that the update has occured, default to a passive update
|
|
||||||
*update_kind = comp::phys::UpdateKind::Passive;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Drop for Server {
|
impl Drop for Server {
|
||||||
fn drop(&mut self) {
|
fn drop(&mut self) {
|
||||||
self.clients.notify_all(ServerMsg::Shutdown);
|
self.clients.notify_connected(ServerMsg::Shutdown);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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::*;
|
||||||
@ -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())) {
|
||||||
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
|
||||||
))
|
))
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -139,7 +139,8 @@ impl Scene {
|
|||||||
.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)
|
||||||
|
@ -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 {
|
||||||
|
Loading…
Reference in New Issue
Block a user