// Reexports pub use sphynx::Uid; use crate::{ comp, msg::{EcsCompPacket, EcsResPacket}, sys, terrain::{Block, TerrainChunk, TerrainMap}, vol::WriteVol, }; use fxhash::{FxHashMap, FxHashSet}; use rayon::{ThreadPool, ThreadPoolBuilder}; use serde_derive::{Deserialize, Serialize}; use specs::{ shred::{Fetch, FetchMut}, storage::{MaskedStorage as EcsMaskedStorage, Storage as EcsStorage}, Component, DispatcherBuilder, Entity as EcsEntity, }; use sphynx; use std::{sync::Arc, time::Duration}; use vek::*; /// How much faster should an in-game day be compared to a real day? // TODO: Don't hard-code this. const DAY_CYCLE_FACTOR: f64 = 24.0 * 2.0; /// A resource that stores the time of day. #[derive(Clone, Debug, Serialize, Deserialize)] pub struct TimeOfDay(pub f64); /// A resource that stores the tick (i.e: physics) time. #[derive(Copy, Clone, Debug, Default, Serialize, Deserialize)] pub struct Time(pub f64); /// A resource that stores the time since the previous tick. #[derive(Default)] pub struct DeltaTime(pub f32); /// At what point should we stop speeding up physics to compensate for lag? If we speed physics up /// too fast, we'd skip important physics events like collisions. This constant determines the /// upper limit. If delta time exceeds this value, the game's physics will begin to produce time /// lag. Ideally, we'd avoid such a situation. const MAX_DELTA_TIME: f32 = 1.0; pub struct BlockChange { blocks: FxHashMap, Block>, } impl Default for BlockChange { fn default() -> Self { Self { blocks: FxHashMap::default(), } } } impl BlockChange { pub fn set(&mut self, pos: Vec3, block: Block) { self.blocks.insert(pos, block); } pub fn clear(&mut self) { self.blocks.clear(); } } pub struct TerrainChanges { pub new_chunks: FxHashSet>, pub modified_chunks: FxHashSet>, pub removed_chunks: FxHashSet>, pub modified_blocks: FxHashMap, Block>, } impl Default for TerrainChanges { fn default() -> Self { Self { new_chunks: FxHashSet::default(), modified_chunks: FxHashSet::default(), removed_chunks: FxHashSet::default(), modified_blocks: FxHashMap::default(), } } } impl TerrainChanges { pub fn clear(&mut self) { self.new_chunks.clear(); self.modified_chunks.clear(); self.removed_chunks.clear(); } } /// 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 states like weather, time of day, etc. pub struct State { ecs: sphynx::World, // Avoid lifetime annotation by storing a thread pool instead of the whole dispatcher thread_pool: Arc, } impl Default for State { /// Create a new `State`. fn default() -> Self { Self { ecs: sphynx::World::new(specs::World::new(), Self::setup_sphynx_world), thread_pool: Arc::new(ThreadPoolBuilder::new().build().unwrap()), } } } impl State { /// 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, ), thread_pool: Arc::new(ThreadPoolBuilder::new().build().unwrap()), } } // Create a new Sphynx ECS world. fn setup_sphynx_world(ecs: &mut sphynx::World) { // Register server -> all clients synced components. ecs.register_synced::(); ecs.register_synced::(); ecs.register_synced::(); ecs.register_synced::(); ecs.register_synced::(); ecs.register_synced::(); // Register components send from clients -> server ecs.register::(); // Register components send directly from server -> all but one client ecs.register::(); // Register components synced from client -> server -> all other clients ecs.register::(); ecs.register::(); ecs.register::(); ecs.register::(); // Register client-local components ecs.register::(); ecs.register::(); // Register server-local components ecs.register::>(); ecs.register::>(); ecs.register::>(); ecs.register::>(); ecs.register::(); ecs.register::(); ecs.register::(); ecs.register::(); ecs.register::(); ecs.register::(); // Controller effects ecs.register::(); ecs.register::(); ecs.register::(); ecs.register::(); ecs.register::(); ecs.register::(); // Register synced resources used by the ECS. ecs.add_resource_synced(TimeOfDay(0.0)); // Register unsynced resources used by the ECS. ecs.add_resource(Time(0.0)); ecs.add_resource(DeltaTime(0.0)); ecs.add_resource(TerrainMap::new().unwrap()); ecs.add_resource(BlockChange::default()); ecs.add_resource(TerrainChanges::default()); } /// Register a component with the state's ECS. pub fn with_component(mut self) -> Self where ::Storage: Default, { self.ecs.register::(); self } /// Write a component attributed to a particular entity. pub fn write_component(&mut self, entity: EcsEntity, comp: C) { let _ = self.ecs.write_storage().insert(entity, comp); } /// Read a component attributed to a particular entity. pub fn read_component_cloned(&self, entity: EcsEntity) -> Option { self.ecs.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.read_storage::() } /// Get a reference to the internal ECS world. pub fn ecs(&self) -> &sphynx::World { &self.ecs } /// Get a mutable reference to the internal ECS world. pub fn ecs_mut(&mut self) -> &mut sphynx::World { &mut self.ecs } /// Get a reference to the `TerrainChanges` structure of the state. This contains /// information about terrain state that has changed since the last game tick. pub fn terrain_changes(&self) -> Fetch { self.ecs.read_resource() } /// 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.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.read_resource::