#[cfg(feature = "plugins")] use crate::plugin::memory_manager::EcsWorld; #[cfg(feature = "plugins")] use crate::plugin::PluginMgr; use crate::{BuildArea, NoDurabilityArea}; #[cfg(feature = "plugins")] use common::uid::UidAllocator; use common::{ calendar::Calendar, comp, event::{EventBus, LocalEvent, ServerEvent}, link::Is, mounting::{Mount, Rider, VolumeRider, VolumeRiders}, outcome::Outcome, region::RegionMap, resources::{ DeltaTime, EntitiesDiedLastTick, GameMode, PlayerEntity, PlayerPhysicsSettings, Time, TimeOfDay, }, shared_server_config::ServerConstants, slowjob::SlowJobPool, terrain::{Block, MapSizeLg, TerrainChunk, TerrainGrid}, time::DayPeriod, trade::Trades, vol::{ReadVol, WriteVol}, weather::{Weather, WeatherGrid}, }; use common_base::{prof_span, span}; use common_ecs::{PhysicsMetrics, SysMetrics}; use common_net::sync::{interpolation as sync_interp, WorldSyncExt}; use core::{convert::identity, time::Duration}; use hashbrown::{HashMap, HashSet}; use rayon::{ThreadPool, ThreadPoolBuilder}; use specs::{ prelude::Resource, shred::{Fetch, FetchMut}, storage::{MaskedStorage as EcsMaskedStorage, Storage as EcsStorage}, Component, DispatcherBuilder, Entity as EcsEntity, WorldExt, }; use std::{sync::Arc, time::Instant}; use timer_queue::TimerQueue; use vek::*; /// 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; /// convert seconds to milliseconds to use in TimerQueue const SECONDS_TO_MILLISECONDS: f64 = 1000.0; #[derive(Default)] pub struct BlockChange { blocks: HashMap, Block>, } impl BlockChange { pub fn set(&mut self, pos: Vec3, block: Block) { self.blocks.insert(pos, block); } pub fn try_set(&mut self, pos: Vec3, block: Block) -> Option<()> { if !self.blocks.contains_key(&pos) { self.blocks.insert(pos, block); Some(()) } else { None } } /// Check if the block at given position `pos` has already been modified /// this tick. pub fn can_set_block(&self, pos: Vec3) -> bool { !self.blocks.contains_key(&pos) } pub fn clear(&mut self) { self.blocks.clear(); } } #[derive(Default)] pub struct ScheduledBlockChange { changes: TimerQueue, Block>>, outcomes: TimerQueue, Block>>, last_poll_time: u64, } impl ScheduledBlockChange { pub fn set(&mut self, pos: Vec3, block: Block, replace_time: f64) { let timer = self.changes.insert( (replace_time * SECONDS_TO_MILLISECONDS) as u64, HashMap::new(), ); self.changes.get_mut(timer).insert(pos, block); } pub fn outcome_set(&mut self, pos: Vec3, block: Block, replace_time: f64) { let outcome_timer = self.outcomes.insert( (replace_time * SECONDS_TO_MILLISECONDS) as u64, HashMap::new(), ); self.outcomes.get_mut(outcome_timer).insert(pos, block); } } #[derive(Default)] pub struct TerrainChanges { pub new_chunks: HashSet>, pub modified_chunks: HashSet>, pub removed_chunks: HashSet>, pub modified_blocks: HashMap, Block>, } impl TerrainChanges { pub fn clear(&mut self) { self.new_chunks.clear(); self.modified_chunks.clear(); self.removed_chunks.clear(); } } #[derive(Clone)] pub struct BlockDiff { pub wpos: Vec3, pub old: Block, pub new: Block, } /// 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: specs::World, // Avoid lifetime annotation by storing a thread pool instead of the whole dispatcher thread_pool: Arc, } pub type Pools = Arc; impl State { pub fn pools(game_mode: GameMode) -> Pools { let thread_name_infix = match game_mode { GameMode::Server => "s", GameMode::Client => "c", GameMode::Singleplayer => "sp", }; Arc::new( ThreadPoolBuilder::new() .num_threads(num_cpus::get().max(common::consts::MIN_RECOMMENDED_RAYON_THREADS)) .thread_name(move |i| format!("rayon-{}-{}", thread_name_infix, i)) .build() .unwrap(), ) } /// Create a new `State` in client mode. pub fn client(pools: Pools, map_size_lg: MapSizeLg, default_chunk: Arc) -> Self { Self::new(GameMode::Client, pools, map_size_lg, default_chunk) } /// Create a new `State` in server mode. pub fn server(pools: Pools, map_size_lg: MapSizeLg, default_chunk: Arc) -> Self { Self::new(GameMode::Server, pools, map_size_lg, default_chunk) } pub fn new( game_mode: GameMode, pools: Pools, map_size_lg: MapSizeLg, default_chunk: Arc, ) -> Self { Self { ecs: Self::setup_ecs_world(game_mode, Arc::clone(&pools), map_size_lg, default_chunk), thread_pool: pools, } } /// Creates ecs world and registers all the common components and resources // TODO: Split up registering into server and client (e.g. move // EventBus to the server) fn setup_ecs_world( game_mode: GameMode, thread_pool: Arc, map_size_lg: MapSizeLg, default_chunk: Arc, ) -> specs::World { prof_span!("State::setup_ecs_world"); let mut ecs = specs::World::new(); // Uids for sync ecs.register_sync_marker(); // Register server -> all clients synced components. ecs.register::(); ecs.register::(); ecs.register::(); ecs.register::(); ecs.register::(); ecs.register::(); ecs.register::(); ecs.register::(); ecs.register::(); ecs.register::(); ecs.register::(); ecs.register::(); ecs.register::(); ecs.register::(); ecs.register::(); ecs.register::>(); ecs.register::>(); ecs.register::>(); ecs.register::(); ecs.register::(); ecs.register::(); ecs.register::(); ecs.register::(); ecs.register::(); ecs.register::(); ecs.register::(); ecs.register::(); ecs.register::(); ecs.register::(); ecs.register::(); ecs.register::(); ecs.register::(); ecs.register::(); ecs.register::(); // 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 common unsynced components ecs.register::(); ecs.register::(); // Register client-local components // TODO: only register on the client ecs.register::(); ecs.register::>(); ecs.register::>(); ecs.register::>(); // Register server-local components // TODO: only register on the server ecs.register::>(); ecs.register::>(); ecs.register::>(); ecs.register::(); ecs.register::(); ecs.register::(); ecs.register::(); ecs.register::(); ecs.register::(); ecs.register::(); ecs.register::(); ecs.register::(); ecs.register::(); ecs.register::(); ecs.register::(); ecs.register::(); ecs.register::(); ecs.register::(); // Register synced resources used by the ECS. ecs.insert(TimeOfDay(0.0)); ecs.insert(Calendar::default()); ecs.insert(WeatherGrid::new(Vec2::zero())); ecs.insert(Time(0.0)); // Register unsynced resources used by the ECS. ecs.insert(DeltaTime(0.0)); ecs.insert(PlayerEntity(None)); ecs.insert(TerrainGrid::new(map_size_lg, default_chunk).unwrap()); ecs.insert(BlockChange::default()); ecs.insert(ScheduledBlockChange::default()); ecs.insert(crate::special_areas::AreasContainer::::default()); ecs.insert(crate::special_areas::AreasContainer::::default()); ecs.insert(TerrainChanges::default()); ecs.insert(EventBus::::default()); ecs.insert(game_mode); ecs.insert(EventBus::::default()); ecs.insert(common::CachedSpatialGrid::default()); ecs.insert(EntitiesDiedLastTick::default()); let num_cpu = num_cpus::get() as u64; let slow_limit = (num_cpu / 2 + num_cpu / 4).max(1); tracing::trace!(?slow_limit, "Slow Thread limit"); ecs.insert(SlowJobPool::new(slow_limit, 10_000, thread_pool)); // TODO: only register on the server ecs.insert(EventBus::::default()); ecs.insert(comp::group::GroupManager::default()); ecs.insert(RegionMap::new()); ecs.insert(SysMetrics::default()); ecs.insert(PhysicsMetrics::default()); ecs.insert(Trades::default()); ecs.insert(PlayerPhysicsSettings::default()); ecs.insert(VolumeRiders::default()); // Load plugins from asset directory #[cfg(feature = "plugins")] ecs.insert(match PluginMgr::from_assets() { Ok(plugin_mgr) => { let ecs_world = EcsWorld { entities: &ecs.entities(), health: ecs.read_component().into(), uid: ecs.read_component().into(), uid_allocator: &ecs.read_resource::().into(), player: ecs.read_component().into(), }; if let Err(e) = plugin_mgr .execute_event(&ecs_world, &plugin_api::event::PluginLoadEvent { game_mode, }) { tracing::debug!(?e, "Failed to run plugin init"); tracing::info!("Plugins disabled, enable debug logging for more information."); PluginMgr::default() } else { plugin_mgr } }, Err(e) => { tracing::debug!(?e, "Failed to read plugins from assets"); tracing::info!("Plugins disabled, enable debug logging for more information."); PluginMgr::default() }, }); ecs } /// Register a component with the state's ECS. #[must_use] pub fn with_component(mut self) -> Self where ::Storage: Default, { self.ecs.register::(); self } /// Write a component attributed to a particular entity, ignoring errors. /// /// This should be used *only* when we can guarantee that the rest of the /// code does not rely on the insert having succeeded (meaning the /// entity is no longer alive!). /// /// Returns None if the entity was dead or there was no previous entry for /// this component; otherwise, returns Some(old_component). pub fn write_component_ignore_entity_dead( &mut self, entity: EcsEntity, comp: C, ) -> Option { self.ecs .write_storage() .insert(entity, comp) .ok() .and_then(identity) } /// Delete a component attributed to a particular entity. pub fn delete_component(&mut self, entity: EcsEntity) -> Option { self.ecs.write_storage().remove(entity) } /// Read a component attributed to a particular entity. pub fn read_component_cloned(&self, entity: EcsEntity) -> Option { self.ecs.read_storage().get(entity).cloned() } /// Read a component attributed to a particular entity. pub fn read_component_copied(&self, entity: EcsEntity) -> Option { self.ecs.read_storage().get(entity).copied() } /// Given mutable access to the resource R, assuming the resource /// component exists (this is already the behavior of functions like `fetch` /// and `write_component_ignore_entity_dead`). Since all of our resources /// are generated up front, any failure here is definitely a code bug. pub fn mut_resource(&mut self) -> &mut R { self.ecs.get_mut::().expect( "Tried to fetch an invalid resource even though all our resources should be known at \ compile time.", ) } /// 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) -> &specs::World { &self.ecs } /// Get a mutable reference to the internal ECS world. pub fn ecs_mut(&mut self) -> &mut specs::World { &mut self.ecs } pub fn thread_pool(&self) -> &Arc { &self.thread_pool } /// 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 a reference the current in-game weather grid. pub fn weather_grid(&self) -> Fetch { self.ecs.read_resource() } /// Get a mutable reference the current in-game weather grid. pub fn weather_grid_mut(&mut self) -> FetchMut { self.ecs.write_resource() } /// Get the current weather at a position in worldspace. pub fn weather_at(&self, pos: Vec2) -> Weather { self.weather_grid().get_interpolated(pos) } /// Get the max weather near a position in worldspace. pub fn max_weather_near(&self, pos: Vec2) -> Weather { self.weather_grid().get_max_near(pos) } /// 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 day period (period of the day/night cycle) pub fn get_day_period(&self) -> DayPeriod { self.get_time_of_day().into() } /// 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::