diff --git a/chat-cli/Cargo.toml b/chat-cli/Cargo.toml index 61161cd537..90e7c0e5d4 100644 --- a/chat-cli/Cargo.toml +++ b/chat-cli/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "veloren-chat-cli" -version = "0.1.0" +version = "0.2.0" authors = ["Joshua Barretto "] edition = "2018" diff --git a/chat-cli/src/main.rs b/chat-cli/src/main.rs index fa93a3df36..2bbc51a388 100644 --- a/chat-cli/src/main.rs +++ b/chat-cli/src/main.rs @@ -1,7 +1,10 @@ use std::time::Duration; use log::info; use client::{Input, Client, Event}; -use common::clock::Clock; +use common::{ + comp, + clock::Clock, +}; const FPS: u64 = 60; @@ -15,7 +18,7 @@ fn main() { let mut clock = Clock::new(); // Create client - let mut client = Client::new(([127, 0, 0, 1], 59003)) + let mut client = Client::new(([127, 0, 0, 1], 59003), comp::Player::new("test".to_string()), None, 300) .expect("Failed to create client instance"); client.send_chat("Hello!".to_string()); diff --git a/client/Cargo.toml b/client/Cargo.toml index ac82be29e1..847431764b 100644 --- a/client/Cargo.toml +++ b/client/Cargo.toml @@ -1,12 +1,11 @@ [package] name = "veloren-client" -version = "0.1.0" +version = "0.2.0" authors = ["Joshua Barretto "] edition = "2018" [dependencies] common = { package = "veloren-common", path = "../common" } -world = { package = "veloren-world", path = "../world" } specs = "0.14" vek = "0.9" diff --git a/client/src/error.rs b/client/src/error.rs index e702a67b0f..1f74ae62b9 100644 --- a/client/src/error.rs +++ b/client/src/error.rs @@ -3,6 +3,7 @@ use common::net::PostError; #[derive(Debug)] pub enum Error { Network(PostError), + ServerWentMad, ServerTimeout, ServerShutdown, Other(String), @@ -10,9 +11,6 @@ pub enum Error { impl From for Error { fn from(err: PostError) -> Self { - match err { - PostError::Disconnect => Error::ServerShutdown, - err => Error::Network(err), - } + Error::Network(err) } } diff --git a/client/src/lib.rs b/client/src/lib.rs index 0e364d9701..a2e3648bea 100644 --- a/client/src/lib.rs +++ b/client/src/lib.rs @@ -13,9 +13,10 @@ pub use crate::{ use std::{ time::Duration, net::SocketAddr, + collections::HashSet, }; use vek::*; -use threadpool; +use threadpool::ThreadPool; use specs::Builder; use common::{ comp, @@ -24,7 +25,6 @@ use common::{ net::PostBox, msg::{ClientMsg, ServerMsg}, }; -use world::World; const SERVER_TIMEOUT: f64 = 5.0; // Seconds @@ -33,7 +33,7 @@ pub enum Event { } pub struct Client { - thread_pool: threadpool::ThreadPool, + thread_pool: ThreadPool, last_ping: f64, postbox: PostBox, @@ -41,19 +41,38 @@ pub struct Client { tick: u64, state: State, player: Option, + view_distance: u64, - // Testing - world: World, - pub chunk: Option, + pending_chunks: HashSet>, } impl Client { /// Create a new `Client`. #[allow(dead_code)] - pub fn new>(addr: A) -> Result { - let state = State::new(); + pub fn new>( + addr: A, + player: comp::Player, + character: Option, + view_distance: u64, + ) -> Result { - let mut postbox = PostBox::to_server(addr)?; + let mut postbox = PostBox::to(addr)?; + + // Send connection request + postbox.send_message(ClientMsg::Connect { + player, + character, + }); + + // Wait for handshake from server + let (state, player) = match postbox.next_message() { + Some(ServerMsg::Handshake { ecs_state, player_entity }) => { + let mut state = State::from_state_package(ecs_state); + let player_entity = state.ecs().entity_from_uid(player_entity); + (state, player_entity) + }, + _ => return Err(Error::ServerWentMad), + }; Ok(Self { thread_pool: threadpool::Builder::new() @@ -65,11 +84,10 @@ impl Client { tick: 0, state, - player: None, + player, + view_distance, - // Testing - world: World::new(), - chunk: None, + pending_chunks: HashSet::new(), }) } @@ -79,18 +97,6 @@ impl Client { #[allow(dead_code)] pub fn thread_pool(&self) -> &threadpool::ThreadPool { &self.thread_pool } - // TODO: Get rid of this - pub fn with_test_state(mut self) -> Self { - self.chunk = Some(self.world.generate_chunk(Vec3::zero())); - self - } - - // TODO: Get rid of this - pub fn load_chunk(&mut self, pos: Vec3) { - self.state.terrain_mut().insert(pos, self.world.generate_chunk(pos)); - self.state.changes_mut().new_chunks.push(pos); - } - /// Get a reference to the client's game state. #[allow(dead_code)] pub fn state(&self) -> &State { &self.state } @@ -114,7 +120,7 @@ impl Client { /// Send a chat message to the server #[allow(dead_code)] pub fn send_chat(&mut self, msg: String) { - self.postbox.send(ClientMsg::Chat(msg)) + self.postbox.send_message(ClientMsg::Chat(msg)) } /// Execute a single client tick, handle input and update the game state by the given duration @@ -138,12 +144,19 @@ impl Client { // Handle new messages from the server frontend_events.append(&mut self.handle_new_messages()?); + self.state.terrain().iter().for_each(|(k, _)| { + println!("Chunk at {:?}", k); + }); + // Step 1 if let Some(ecs_entity) = self.player { // TODO: remove this const PLAYER_VELOCITY: f32 = 100.0; // TODO: Set acceleration instead - self.state.write_component(ecs_entity, comp::phys::Vel(Vec3::from(input.move_dir * PLAYER_VELOCITY))); + self.state.write_component(ecs_entity, comp::phys::Vel(Vec3::from(input.move_dir * PLAYER_VELOCITY) * 0.1)); + if input.move_dir.magnitude() > 0.01 { + self.state.write_component(ecs_entity, comp::phys::Dir(input.move_dir.normalized().into())); + } } // Tick the client's LocalState (step 3) @@ -157,12 +170,31 @@ impl Client { self.state.read_storage().get(ecs_entity).cloned(), ) { (Some(pos), Some(vel), Some(dir)) => { - self.postbox.send(ClientMsg::PlayerPhysics { pos, vel, dir }); + self.postbox.send_message(ClientMsg::PlayerPhysics { pos, vel, dir }); }, _ => {}, } } + // Request chunks from the server + if let Some(player_entity) = self.player { + if let Some(pos) = self.state.read_storage::().get(player_entity) { + let chunk_pos = self.state.terrain().pos_key(pos.0.map(|e| e as i32)); + + for i in chunk_pos.x - 0..chunk_pos.x + 1 { + for j in chunk_pos.y - 0..chunk_pos.y + 1 { + for k in 0..3 { + let key = chunk_pos + Vec3::new(i, j, k); + if self.state.terrain().get_key(key).is_none() && !self.pending_chunks.contains(&key) { + self.postbox.send_message(ClientMsg::TerrainChunkRequest { key }); + self.pending_chunks.insert(key); + } + } + } + } + } + } + // Finish the tick, pass control back to the frontend (step 6) self.tick += 1; Ok(frontend_events) @@ -187,28 +219,16 @@ impl Client { for msg in new_msgs { match msg { + ServerMsg::Handshake { .. } => return Err(Error::ServerWentMad), ServerMsg::Shutdown => return Err(Error::ServerShutdown), - ServerMsg::Ping => self.postbox.send(ClientMsg::Pong), + ServerMsg::Ping => self.postbox.send_message(ClientMsg::Pong), ServerMsg::Pong => {}, ServerMsg::Chat(msg) => frontend_events.push(Event::Chat(msg)), - ServerMsg::SetPlayerEntity(uid) => { - let ecs_entity = self.state - .get_entity(uid) - .unwrap_or_else(|| self.state.build_uid_entity_with_uid(uid).build()); - - self.player = Some(ecs_entity); - }, - ServerMsg::EntityPhysics { uid, pos, vel, dir } => { - let ecs_entity = self.state - .get_entity(uid) - .unwrap_or_else(|| self.state.build_uid_entity_with_uid(uid).build()); - - self.state.write_component(ecs_entity, pos); - self.state.write_component(ecs_entity, vel); - self.state.write_component(ecs_entity, dir); - }, - ServerMsg::EntityDeleted(uid) => { - self.state.delete_entity(uid); + ServerMsg::SetPlayerEntity(uid) => self.player = Some(self.state.ecs().entity_from_uid(uid).unwrap()), // TODO: Don't unwrap here! + ServerMsg::EcsSync(sync_package) => self.state.ecs_mut().sync_with_package(sync_package), + ServerMsg::TerrainChunkUpdate { key, chunk } => { + self.state.insert_chunk(key, *chunk); + self.pending_chunks.remove(&key); }, } } @@ -218,7 +238,7 @@ impl Client { return Err(Error::ServerTimeout); } else if self.state.get_time() - self.last_ping > SERVER_TIMEOUT * 0.5 { // Try pinging the server if the timeout is nearing - self.postbox.send(ClientMsg::Ping); + self.postbox.send_message(ClientMsg::Ping); } Ok(frontend_events) @@ -227,6 +247,6 @@ impl Client { impl Drop for Client { fn drop(&mut self) { - self.postbox.send(ClientMsg::Disconnect); + self.postbox.send_message(ClientMsg::Disconnect); } } diff --git a/common/Cargo.toml b/common/Cargo.toml index 3fa8fd33a3..1b99516d23 100644 --- a/common/Cargo.toml +++ b/common/Cargo.toml @@ -1,10 +1,12 @@ [package] name = "veloren-common" -version = "0.1.0" +version = "0.2.0" authors = ["Joshua Barretto ", "Maciej Ćwięka "] edition = "2018" [dependencies] +sphynx = { git = "https://gitlab.com/veloren/sphynx.git", features = ["serde1"] } + specs = { version = "0.14", features = ["serde"] } shred = { version = "0.7", features = ["nightly"] } vek = { version = "0.9", features = ["serde"] } @@ -16,4 +18,3 @@ serde = "1.0" serde_derive = "1.0" bincode = "1.0" log = "0.4" -pretty_env_logger = "0.3" diff --git a/common/src/comp/character.rs b/common/src/comp/character.rs index 090f0e047a..02cef3eff6 100644 --- a/common/src/comp/character.rs +++ b/common/src/comp/character.rs @@ -1,9 +1,8 @@ -// Library -use specs::{Component, VecStorage}; +use specs::{Component, VecStorage, FlaggedStorage}; use vek::*; #[derive(Copy, Clone, Debug, Serialize, Deserialize)] -enum Race { +pub enum Race { Danari, Dwarf, Elf, @@ -13,7 +12,7 @@ enum Race { } #[derive(Copy, Clone, Debug, Serialize, Deserialize)] -pub struct Gender { +pub enum Gender { Female, Male, Unspecified, @@ -30,6 +29,21 @@ pub struct Character { feet: (), } -impl Component for Character { - type Storage = VecStorage; +impl Character { + // TODO: Remove this + pub fn test() -> Self { + Self { + race: Race::Human, + gender: Gender::Unspecified, + head: (), + chest: (), + belt: (), + arms: (), + feet: (), + } + } +} + +impl Component for Character { + type Storage = FlaggedStorage>; } diff --git a/common/src/comp/mod.rs b/common/src/comp/mod.rs index 776a67485c..f5fa9254b7 100644 --- a/common/src/comp/mod.rs +++ b/common/src/comp/mod.rs @@ -1,20 +1,7 @@ +pub mod character; +pub mod player; pub mod phys; -pub mod uid; -pub mod util; // Reexports -pub use uid::{Uid, UidAllocator}; - -use specs::World as EcsWorld; - -pub fn register_local_components(ecs_world: &mut EcsWorld) { - ecs_world.register::(); - ecs_world.add_resource(UidAllocator::new()); - - ecs_world.register::(); - - ecs_world.register::(); - ecs_world.register::(); - ecs_world.register::(); - ecs_world.register::(); -} +pub use character::Character; +pub use player::Player; diff --git a/common/src/comp/phys.rs b/common/src/comp/phys.rs index 3db813276d..20e2b59192 100644 --- a/common/src/comp/phys.rs +++ b/common/src/comp/phys.rs @@ -1,5 +1,4 @@ -// Library -use specs::{Component, VecStorage}; +use specs::{Component, VecStorage, FlaggedStorage}; use vek::*; // Pos @@ -8,7 +7,7 @@ use vek::*; pub struct Pos(pub Vec3); impl Component for Pos { - type Storage = VecStorage; + type Storage = FlaggedStorage>; } // Vel @@ -17,7 +16,7 @@ impl Component for Pos { pub struct Vel(pub Vec3); impl Component for Vel { - type Storage = VecStorage; + type Storage = FlaggedStorage>; } // Dir @@ -26,17 +25,5 @@ impl Component for Vel { pub struct Dir(pub Vec3); impl Component for Dir { - type Storage = VecStorage; -} - -// UpdateKind - -#[derive(Copy, Clone, Debug)] -pub enum UpdateKind { - Passive, - Force, -} - -impl Component for UpdateKind { - type Storage = VecStorage; + type Storage = FlaggedStorage>; } diff --git a/common/src/comp/player.rs b/common/src/comp/player.rs new file mode 100644 index 0000000000..46f1a048a4 --- /dev/null +++ b/common/src/comp/player.rs @@ -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>; +} diff --git a/common/src/comp/uid.rs b/common/src/comp/uid.rs deleted file mode 100644 index 6ea657dada..0000000000 --- a/common/src/comp/uid.rs +++ /dev/null @@ -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 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; -} - -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, - pub(crate) mapping: HashMap, -} - -impl UidAllocator { - pub fn new() -> Self { - Self { - range: 0..u64::MAX, - mapping: HashMap::new(), - } - } -} - -impl MarkerAllocator for UidAllocator { - fn allocate(&mut self, entity: Entity, id: Option) -> 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 { - self.mapping.get(&id).cloned() - } - - fn maintain(&mut self, entities: &EntitiesRes, storage: &ReadStorage) { - self.mapping = (&*entities, storage) - .join() - .map(|(e, m)| (m.id(), e)) - .collect(); - } -} diff --git a/common/src/comp/util.rs b/common/src/comp/util.rs deleted file mode 100644 index 0ea2d35ebc..0000000000 --- a/common/src/comp/util.rs +++ /dev/null @@ -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; -} diff --git a/common/src/lib.rs b/common/src/lib.rs index 9fc68777e4..146e272351 100644 --- a/common/src/lib.rs +++ b/common/src/lib.rs @@ -1,4 +1,4 @@ -#![feature(euclidean_division, duration_float, try_from, trait_alias)] +#![feature(euclidean_division, duration_float, trait_alias, bind_by_move_pattern_guards)] #[macro_use] extern crate serde_derive; diff --git a/common/src/msg/client.rs b/common/src/msg/client.rs index 149cd07ad4..5a599633d7 100644 --- a/common/src/msg/client.rs +++ b/common/src/msg/client.rs @@ -1,17 +1,22 @@ -use crate::comp::{ - Uid, - phys, -}; +use vek::*; +use crate::comp; -#[derive(Clone, Debug, Serialize, Deserialize)] +#[derive(Clone, Serialize, Deserialize)] pub enum ClientMsg { + Connect { + player: comp::Player, + character: Option, + }, Ping, Pong, Chat(String), PlayerPhysics { - pos: phys::Pos, - vel: phys::Vel, - dir: phys::Dir, + pos: comp::phys::Pos, + vel: comp::phys::Vel, + dir: comp::phys::Dir, + }, + TerrainChunkRequest { + key: Vec3, }, Disconnect, } diff --git a/common/src/msg/ecs_packet.rs b/common/src/msg/ecs_packet.rs new file mode 100644 index 0000000000..a3b4a9e463 --- /dev/null +++ b/common/src/msg/ecs_packet.rs @@ -0,0 +1,29 @@ +use std::marker::PhantomData; +use serde_derive::{Serialize, Deserialize}; +use crate::comp; + +// Automatically derive From 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 for Phantom for each variant Phantom::T(PhantomData) +sphynx::sum_type! { + #[derive(Clone, Debug, Serialize, Deserialize)] + pub enum EcsPhantom { + Pos(PhantomData), + Vel(PhantomData), + Dir(PhantomData), + Character(PhantomData), + Player(PhantomData), + } +} +impl sphynx::Packet for EcsPacket { + type Phantom = EcsPhantom; +} diff --git a/common/src/msg/mod.rs b/common/src/msg/mod.rs index ee18da8f4d..b38939eeb2 100644 --- a/common/src/msg/mod.rs +++ b/common/src/msg/mod.rs @@ -1,6 +1,8 @@ +pub mod ecs_packet; pub mod server; pub mod client; // Reexports -pub use server::ServerMsg; -pub use client::ClientMsg; +pub use self::server::ServerMsg; +pub use self::client::ClientMsg; +pub use self::ecs_packet::EcsPacket; diff --git a/common/src/msg/server.rs b/common/src/msg/server.rs index 871215138e..2f852072bf 100644 --- a/common/src/msg/server.rs +++ b/common/src/msg/server.rs @@ -1,20 +1,21 @@ -use crate::comp::{ - Uid, - phys, -}; +use vek::*; +use crate::terrain::TerrainChunk; +use super::EcsPacket; -#[derive(Clone, Debug, Serialize, Deserialize)] +#[derive(Clone, Serialize, Deserialize)] pub enum ServerMsg { + Handshake { + ecs_state: sphynx::StatePackage, + player_entity: u64, + }, Shutdown, Ping, Pong, Chat(String), - SetPlayerEntity(Uid), - EntityPhysics { - uid: Uid, - pos: phys::Pos, - vel: phys::Vel, - dir: phys::Dir, + SetPlayerEntity(u64), + EcsSync(sphynx::SyncPackage), + TerrainChunkUpdate { + key: Vec3, + chunk: Box, }, - EntityDeleted(Uid), } diff --git a/common/src/net/mod.rs b/common/src/net/mod.rs index c1c7fd4107..11829e32fa 100644 --- a/common/src/net/mod.rs +++ b/common/src/net/mod.rs @@ -1,5 +1,8 @@ pub mod data; -pub mod post; +//pub mod post; +pub mod post2; + +pub use post2 as post; // Reexports pub use self::{ diff --git a/common/src/net/post.rs b/common/src/net/post.rs index b9160ec35b..38a8533186 100644 --- a/common/src/net/post.rs +++ b/common/src/net/post.rs @@ -23,6 +23,7 @@ use mio_extras::channel::{ Sender, }; use bincode; +use middleman::Middleman; #[derive(Clone, Debug, PartialEq)] pub enum Error { @@ -56,14 +57,15 @@ impl From> for Error { } } -pub trait PostSend = 'static + serde::Serialize + Send + fmt::Debug; -pub trait PostRecv = 'static + serde::de::DeserializeOwned + Send + fmt::Debug; +pub trait PostSend = 'static + serde::Serialize + Send + middleman::Message; +pub trait PostRecv = 'static + serde::de::DeserializeOwned + Send + middleman::Message; -const TCP_TOK: Token = Token(0); -const CTRL_TOK: Token = Token(1); -const POSTBOX_TOK: Token = Token(2); -const SEND_TOK: Token = Token(3); -const RECV_TOK: Token = Token(4); +const TCP_TOK: Token = Token(0); +const CTRL_TOK: Token = Token(1); +const POSTBOX_TOK: Token = Token(2); +const SEND_TOK: Token = Token(3); +const RECV_TOK: Token = Token(4); +const MIDDLEMAN_TOK: Token = Token(5); const MAX_MSG_BYTES: usize = 1 << 20; @@ -218,7 +220,7 @@ impl PostBox { let (recv_tx, recv_rx) = channel(); let worker_poll = Poll::new()?; - worker_poll.register(&tcp_stream, TCP_TOK, Ready::readable(), PollOpt::edge())?; + worker_poll.register(&tcp_stream, TCP_TOK, Ready::readable() | Ready::writable(), PollOpt::edge())?; worker_poll.register(&ctrl_rx, CTRL_TOK, Ready::readable(), PollOpt::edge())?; worker_poll.register(&send_rx, SEND_TOK, Ready::readable(), PollOpt::edge())?; @@ -251,6 +253,45 @@ impl PostBox { let _ = self.send_tx.send(data); } + // TODO: This method is super messy + pub fn next_message(&mut self) -> Option { + 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 { let mut msgs = VecDeque::new(); @@ -306,12 +347,38 @@ fn postbox_worker( send_rx: Receiver, recv_tx: Sender>, ) -> Result<(), Error> { + fn try_tcp_send(tcp_stream: &mut TcpStream, chunks: &mut VecDeque>) -> Result<(), Error> { + loop { + let chunk = match chunks.pop_front() { + Some(chunk) => chunk, + None => break, + }; + + match tcp_stream.write_all(&chunk) { + Ok(()) => {}, + Err(err) if err.kind() == io::ErrorKind::WouldBlock => { + chunks.push_front(chunk); + break; + }, + Err(err) => { + println!("Error: {:?}", err); + return Err(err.into()); + }, + } + } + + Ok(()) + } + enum RecvState { ReadHead(Vec), ReadBody(usize, Vec), } - let mut recv_state = RecvState::ReadHead(Vec::with_capacity(8)); + let mut recv_state = RecvState::ReadHead(Vec::new()); + let mut chunks = VecDeque::new(); + + //let mut recv_state = RecvState::ReadHead(Vec::with_capacity(8)); let mut events = Events::with_capacity(64); 'work: loop { @@ -344,18 +411,23 @@ fn postbox_worker( }, }; - let mut packet = msg_bytes + let mut bytes = msg_bytes .len() .to_le_bytes() .as_ref() .to_vec(); - packet.append(&mut msg_bytes); + bytes.append(&mut msg_bytes); - match tcp_stream.write_all(&packet) { - Ok(()) => {}, + bytes + .chunks(1024) + .map(|chunk| chunk.to_vec()) + .for_each(|chunk| chunks.push_back(chunk)); + + match try_tcp_send(&mut tcp_stream, &mut chunks) { + Ok(_) => {}, Err(err) => { recv_tx.send(Err(err.into()))?; - break 'work; + return Err(Error::Network); }, } }, @@ -363,61 +435,75 @@ fn postbox_worker( Err(err) => Err(err)?, } }, - TCP_TOK => loop { - match tcp_stream.take_error() { - Ok(None) => {}, - Ok(Some(err)) => { - recv_tx.send(Err(err.into()))?; - break 'work; - }, + TCP_TOK => { + loop { + // Check TCP error + match tcp_stream.take_error() { + Ok(None) => {}, + Ok(Some(err)) => { + recv_tx.send(Err(err.into()))?; + break 'work; + }, + Err(err) => { + recv_tx.send(Err(err.into()))?; + break 'work; + }, + } + match &mut recv_state { + RecvState::ReadHead(head) => if head.len() == 8 { + let len = usize::from_le_bytes(<[u8; 8]>::try_from(head.as_slice()).unwrap()); + if len > MAX_MSG_BYTES { + println!("TOO BIG! {:x}", len); + recv_tx.send(Err(Error::InvalidMsg))?; + break 'work; + } else if len == 0 { + recv_state = RecvState::ReadHead(Vec::with_capacity(8)); + break; + } else { + recv_state = RecvState::ReadBody( + len, + Vec::new(), + ); + } + } else { + let mut b = [0; 1]; + match tcp_stream.read(&mut b) { + Ok(0) => {}, + Ok(_) => head.push(b[0]), + Err(_) => break, + } + }, + RecvState::ReadBody(len, body) => if body.len() == *len { + match bincode::deserialize(&body) { + Ok(msg) => { + recv_tx.send(Ok(msg))?; + recv_state = RecvState::ReadHead(Vec::with_capacity(8)); + }, + Err(err) => { + recv_tx.send(Err((*err).into()))?; + break 'work; + }, + } + } else { + let left = *len - body.len(); + let mut buf = vec![0; left]; + match tcp_stream.read(&mut buf) { + Ok(_) => body.append(&mut buf), + Err(err) => { + recv_tx.send(Err(err.into()))?; + break 'work; + }, + } + }, + } + } + + // Now, try sending TCP stuff + match try_tcp_send(&mut tcp_stream, &mut chunks) { + Ok(_) => {}, Err(err) => { recv_tx.send(Err(err.into()))?; - break 'work; - }, - } - match &mut recv_state { - RecvState::ReadHead(head) => if head.len() == 8 { - let len = usize::from_le_bytes(<[u8; 8]>::try_from(head.as_slice()).unwrap()); - if len > MAX_MSG_BYTES { - recv_tx.send(Err(Error::InvalidMsg))?; - break 'work; - } else if len == 0 { - recv_state = RecvState::ReadHead(Vec::with_capacity(8)); - break; - } else { - recv_state = RecvState::ReadBody( - len, - Vec::new(), - ); - } - } else { - let mut b = [0; 1]; - match tcp_stream.read(&mut b) { - Ok(_) => head.push(b[0]), - Err(_) => break, - } - }, - RecvState::ReadBody(len, body) => if body.len() == *len { - match bincode::deserialize(&body) { - Ok(msg) => { - recv_tx.send(Ok(msg))?; - recv_state = RecvState::ReadHead(Vec::with_capacity(8)); - }, - Err(err) => { - recv_tx.send(Err((*err).into()))?; - break 'work; - }, - } - } else { - let left = *len - body.len(); - let mut buf = vec![0; left]; - match tcp_stream.read(&mut buf) { - Ok(_) => body.append(&mut buf), - Err(err) => { - recv_tx.send(Err(err.into()))?; - break 'work; - }, - } + return Err(Error::Network); }, } }, @@ -426,24 +512,28 @@ fn postbox_worker( } } - tcp_stream.shutdown(Shutdown::Both)?; + //tcp_stream.shutdown(Shutdown::Both)?; Ok(()) } // TESTS +/* +#[derive(Serialize, Deserialize)] +struct TestMsg(T); + #[test] fn connect() { let srv_addr = ([127, 0, 0, 1], 12345); - let mut postoffice = PostOffice::::bind(srv_addr).unwrap(); + let mut postoffice = PostOffice::, TestMsg>::bind(srv_addr).unwrap(); // We should start off with 0 incoming connections thread::sleep(Duration::from_millis(250)); assert_eq!(postoffice.new_connections().len(), 0); assert_eq!(postoffice.error(), None); - let postbox = PostBox::::to_server(srv_addr).unwrap(); + let postbox = PostBox::, TestMsg>::to_server(srv_addr).unwrap(); // Now a postbox has been created, we should have 1 new thread::sleep(Duration::from_millis(250)); @@ -457,21 +547,21 @@ fn connect_fail() { let listen_addr = ([0; 4], 12345); let connect_addr = ([127, 0, 0, 1], 12212); - let mut postoffice = PostOffice::::bind(listen_addr).unwrap(); + let mut postoffice = PostOffice::, TestMsg>::bind(listen_addr).unwrap(); // We should start off with 0 incoming connections thread::sleep(Duration::from_millis(250)); assert_eq!(postoffice.new_connections().len(), 0); assert_eq!(postoffice.error(), None); - assert!(PostBox::::to_server(connect_addr).is_err()); + assert!(PostBox::, TestMsg>::to_server(connect_addr).is_err()); } #[test] fn connection_count() { let srv_addr = ([127, 0, 0, 1], 12346); - let mut postoffice = PostOffice::::bind(srv_addr).unwrap(); + let mut postoffice = PostOffice::, TestMsg>::bind(srv_addr).unwrap(); let mut postboxes = Vec::new(); // We should start off with 0 incoming connections @@ -480,7 +570,7 @@ fn connection_count() { assert_eq!(postoffice.error(), None); for _ in 0..5 { - postboxes.push(PostBox::::to_server(srv_addr).unwrap()); + postboxes.push(PostBox::, TestMsg>::to_server(srv_addr).unwrap()); } // 5 postboxes created, we should have 5 @@ -494,10 +584,10 @@ fn connection_count() { fn disconnect() { let srv_addr = ([127, 0, 0, 1], 12347); - let mut postoffice = PostOffice::::bind(srv_addr).unwrap(); + let mut postoffice = PostOffice::, TestMsg>::bind(srv_addr).unwrap(); let mut server_postbox = { - let mut client_postbox = PostBox::::to_server(srv_addr).unwrap(); + let mut client_postbox = PostBox::, TestMsg>::to_server(srv_addr).unwrap(); thread::sleep(Duration::from_millis(250)); let mut incoming = postoffice.new_connections(); @@ -519,52 +609,53 @@ fn disconnect() { fn client_to_server() { let srv_addr = ([127, 0, 0, 1], 12348); - let mut po = PostOffice::::bind(srv_addr).unwrap(); + let mut po = PostOffice::, TestMsg>::bind(srv_addr).unwrap(); - let mut client_pb = PostBox::::to_server(srv_addr).unwrap(); + let mut client_pb = PostBox::, TestMsg>::to_server(srv_addr).unwrap(); thread::sleep(Duration::from_millis(250)); let mut server_pb = po.new_connections().next().unwrap(); - client_pb.send(1337.0); - client_pb.send(9821.0); - client_pb.send(-3.2); - client_pb.send(17.0); + client_pb.send(TestMsg(1337.0)); + client_pb.send(TestMsg(9821.0)); + client_pb.send(TestMsg(-3.2)); + client_pb.send(TestMsg(17.0)); thread::sleep(Duration::from_millis(250)); let mut incoming_msgs = server_pb.new_messages(); assert_eq!(incoming_msgs.len(), 4); - assert_eq!(incoming_msgs.next().unwrap(), 1337.0); - assert_eq!(incoming_msgs.next().unwrap(), 9821.0); - assert_eq!(incoming_msgs.next().unwrap(), -3.2); - assert_eq!(incoming_msgs.next().unwrap(), 17.0); + assert_eq!(incoming_msgs.next().unwrap(), TestMsg(1337.0)); + assert_eq!(incoming_msgs.next().unwrap(), TestMsg(9821.0)); + assert_eq!(incoming_msgs.next().unwrap(), TestMsg(-3.2)); + assert_eq!(incoming_msgs.next().unwrap(), TestMsg(17.0)); } #[test] fn server_to_client() { let srv_addr = ([127, 0, 0, 1], 12349); - let mut po = PostOffice::::bind(srv_addr).unwrap(); + let mut po = PostOffice::, TestMsg>::bind(srv_addr).unwrap(); - let mut client_pb = PostBox::::to_server(srv_addr).unwrap(); + let mut client_pb = PostBox::, TestMsg>::to_server(srv_addr).unwrap(); thread::sleep(Duration::from_millis(250)); let mut server_pb = po.new_connections().next().unwrap(); - server_pb.send(1337); - server_pb.send(9821); - server_pb.send(39999999); - server_pb.send(17); + server_pb.send(TestMsg(1337)); + server_pb.send(TestMsg(9821)); + server_pb.send(TestMsg(39999999)); + server_pb.send(TestMsg(17)); thread::sleep(Duration::from_millis(250)); let mut incoming_msgs = client_pb.new_messages(); assert_eq!(incoming_msgs.len(), 4); - assert_eq!(incoming_msgs.next().unwrap(), 1337); - assert_eq!(incoming_msgs.next().unwrap(), 9821); - assert_eq!(incoming_msgs.next().unwrap(), 39999999); - assert_eq!(incoming_msgs.next().unwrap(), 17); + assert_eq!(incoming_msgs.next().unwrap(), TestMsg(1337)); + assert_eq!(incoming_msgs.next().unwrap(), TestMsg(9821)); + assert_eq!(incoming_msgs.next().unwrap(), TestMsg(39999999)); + assert_eq!(incoming_msgs.next().unwrap(), TestMsg(17)); } +*/ diff --git a/common/src/net/post2.rs b/common/src/net/post2.rs new file mode 100644 index 0000000000..25db57288b --- /dev/null +++ b/common/src/net/post2.rs @@ -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 for Error { + fn from(err: io::Error) -> Self { + Error::Io //(err) + } +} + +impl From for Error { + fn from(err: bincode::Error) -> Self { + Error::Bincode //(err) + } +} + +impl From 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 { + listener: TcpListener, + error: Option, + phantom: PhantomData<(S, R)>, +} + +impl PostOffice { + pub fn bind>(addr: A) -> Result { + let mut listener = TcpListener::bind(addr.into())?; + listener.set_nonblocking(true)?; + + Ok(Self { + listener, + error: None, + phantom: PhantomData, + }) + } + + pub fn error(&self) -> Option { + self.error.clone() + } + + pub fn new_postboxes(&mut self) -> impl ExactSizeIterator> { + 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 { + send_tx: mpsc::Sender, + recv_rx: mpsc::Receiver>, + worker: Option>, + running: Arc, + error: Option, +} + +impl PostBox { + pub fn to>(addr: A) -> Result { + Self::from_stream(TcpStream::connect(addr.into())?) + } + + fn from_stream(stream: TcpStream) -> Result { + 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 { + self.error.clone() + } + + pub fn send_message(&mut self, msg: S) { + let _ = self.send_tx.send(msg); + } + + pub fn next_message(&mut self) -> Option { + 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 { + 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, recv_tx: mpsc::Sender>, running: Arc) { + 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 Drop for PostBox { + 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(id: u16) -> Result<(PostOffice, SocketAddr), Error> { + let sock = ([0; 4], 12345 + id).into(); + Ok((PostOffice::bind(sock)?, sock)) + } + + fn loop_for(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::::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::::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>(3).unwrap(); + let test_msgs: Vec> = (0..5).map(|i| (0..100000).map(|j| i * 2 + j).collect()).collect(); + + let mut client = PostBox::, ()>::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::(4).unwrap(); + let test_msgs = vec![1, 1337, 42, -48]; + + let mut client = PostBox::::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); + } + } +} diff --git a/common/src/state.rs b/common/src/state.rs index b395dd35de..6c7a0ba9b9 100644 --- a/common/src/state.rs +++ b/common/src/state.rs @@ -1,4 +1,7 @@ -use std::time::Duration; +use std::{ + time::Duration, + collections::HashSet, +}; use shred::{Fetch, FetchMut}; use specs::{ Builder, @@ -6,18 +9,22 @@ use specs::{ DispatcherBuilder, EntityBuilder as EcsEntityBuilder, Entity as EcsEntity, - World as EcsWorld, storage::{ Storage as EcsStorage, MaskedStorage as EcsMaskedStorage, }, saveload::{MarkedBuilder, MarkerAllocator}, }; +use sphynx; use vek::*; use crate::{ comp, sys, - terrain::TerrainMap, + terrain::{ + TerrainMap, + TerrainChunk, + }, + msg::EcsPacket, }; /// How much faster should an in-game day be compared to a real day? @@ -35,17 +42,17 @@ struct Time(f64); pub struct DeltaTime(pub f64); pub struct Changes { - pub new_chunks: Vec>, - pub changed_chunks: Vec>, - pub removed_chunks: Vec>, + pub new_chunks: HashSet>, + pub changed_chunks: HashSet>, + pub removed_chunks: HashSet>, } impl Changes { pub fn default() -> Self { Self { - new_chunks: vec![], - changed_chunks: vec![], - removed_chunks: vec![], + new_chunks: HashSet::new(), + changed_chunks: HashSet::new(), + removed_chunks: HashSet::new(), } } @@ -59,95 +66,74 @@ impl Changes { /// A type used to represent game state stored on both the client and the server. This includes /// things like entity components, terrain data, and global state like weather, time of day, etc. pub struct State { - ecs_world: EcsWorld, + ecs: sphynx::World, changes: Changes, } impl State { /// Create a new `State`. pub fn new() -> Self { - let mut ecs_world = EcsWorld::new(); - - // Register resources used by the ECS - ecs_world.add_resource(TimeOfDay(0.0)); - ecs_world.add_resource(Time(0.0)); - ecs_world.add_resource(DeltaTime(0.0)); - ecs_world.add_resource(TerrainMap::new()); - - // Register common components with the state - comp::register_local_components(&mut ecs_world); - Self { - ecs_world, + ecs: sphynx::World::new(specs::World::new(), Self::setup_sphynx_world), changes: Changes::default(), } } + /// Create a new `State` from an ECS state package + pub fn from_state_package(state_package: sphynx::StatePackage) -> 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) { + // Register synced components + ecs.register_synced::(); + ecs.register_synced::(); + ecs.register_synced::(); + ecs.register_synced::(); + ecs.register_synced::(); + + // Register resources used by the ECS + ecs.internal_mut().add_resource(TimeOfDay(0.0)); + ecs.internal_mut().add_resource(Time(0.0)); + ecs.internal_mut().add_resource(DeltaTime(0.0)); + ecs.internal_mut().add_resource(TerrainMap::new()); + } + /// Register a component with the state's ECS pub fn with_component(mut self) -> Self where ::Storage: Default { - self.ecs_world.register::(); + self.ecs.internal_mut().register::(); 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::() - } - - /// 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::() - .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 { - // Find the ECS entity from its UID - self.ecs_world - .read_resource::() - .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::() - .retrieve_entity_internal(uid.into()); - - // Delete the ECS entity, if it exists - if let Some(ecs_entity) = ecs_entity { - let _ = self.ecs_world.delete_entity(ecs_entity); - } - } - /// Write a component attributed to a particular entity pub fn write_component(&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(&self, entity: EcsEntity) -> Option { + self.ecs.internal().read_storage().get(entity).cloned() } /// Get a read-only reference to the storage of a particular component type pub fn read_storage(&self) -> EcsStorage>> { - self.ecs_world.read_storage::() + self.ecs.internal().read_storage::() } /// Get a reference to the internal ECS world - pub fn ecs_world(&self) -> &EcsWorld { - &self.ecs_world + pub fn ecs(&self) -> &sphynx::World { + &self.ecs } /// Get a mutable reference to the internal ECS world - pub fn ecs_world_mut(&mut self) -> &mut EcsWorld { - &mut self.ecs_world + pub fn ecs_mut(&mut self) -> &mut sphynx::World { + &mut self.ecs } /// Get a reference to the `Changes` structure of the state. This contains @@ -156,54 +142,57 @@ impl State { &self.changes } - // TODO: Get rid of this since it shouldn't be needed - pub fn changes_mut(&mut self) -> &mut Changes { - &mut self.changes - } - /// Get the current in-game time of day. /// /// Note that this should not be used for physics, animations or other such localised timings. pub fn get_time_of_day(&self) -> f64 { - self.ecs_world.read_resource::().0 + self.ecs.internal().read_resource::().0 } /// Get the current in-game time. /// /// Note that this does not correspond to the time of day. pub fn get_time(&self) -> f64 { - self.ecs_world.read_resource::