diff --git a/Cargo.lock b/Cargo.lock index b27aa7160e..ef8a66c119 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -5443,7 +5443,7 @@ dependencies = [ "veloren-common-frontend", "veloren-common-net", "veloren-common-state", - "veloren-common-sys", + "veloren-common-systems", "veloren-network", ] @@ -5557,7 +5557,7 @@ dependencies = [ ] [[package]] -name = "veloren-common-sys" +name = "veloren-common-systems" version = "0.9.0" dependencies = [ "hashbrown", @@ -5682,7 +5682,7 @@ dependencies = [ "veloren-common-ecs", "veloren-common-net", "veloren-common-state", - "veloren-common-sys", + "veloren-common-systems", "veloren-network", "veloren-plugin-api", "veloren-world", @@ -5770,7 +5770,7 @@ dependencies = [ "veloren-common-frontend", "veloren-common-net", "veloren-common-state", - "veloren-common-sys", + "veloren-common-systems", "veloren-server", "veloren-voxygen-anim", "veloren-world", diff --git a/Cargo.toml b/Cargo.toml index 2eb30662d7..d1cfa92648 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -7,7 +7,7 @@ members = [ "common/ecs", "common/net", "common/state", - "common/sys", + "common/systems", "common/frontend", "client", "plugin/api", diff --git a/client/Cargo.toml b/client/Cargo.toml index 8f7619c628..e0113e12f2 100644 --- a/client/Cargo.toml +++ b/client/Cargo.toml @@ -5,7 +5,7 @@ authors = ["Joshua Barretto "] edition = "2018" [features] -tracy = ["common/tracy", "common-base/tracy", "common-sys/tracy", "common-net/tracy", "common-frontend/tracy"] +tracy = ["common/tracy", "common-base/tracy", "common-state/tracy", "common-systems/tracy", "common-net/tracy", "common-frontend/tracy"] simd = ["vek/platform_intrinsics"] plugins = ["common-state/plugins"] bin_bot = ["common-ecs", "serde", "ron", "clap", "rustyline", "common-frontend", "async-channel"] @@ -16,7 +16,7 @@ default = ["simd"] common = { package = "veloren-common", path = "../common", features = ["no-assets"] } common-base = { package = "veloren-common-base", path = "../common/base" } common-state = { package = "veloren-common-state", path = "../common/state", default-features = false } -common-sys = { package = "veloren-common-sys", path = "../common/sys", default-features = false } +common-systems = { package = "veloren-common-systems", path = "../common/systems", default-features = false } common-net = { package = "veloren-common-net", path = "../common/net" } network = { package = "veloren-network", path = "../network", features = ["compression"], default-features = false } @@ -32,6 +32,7 @@ vek = { version = "=0.14.1", features = ["serde"] } hashbrown = { version = "0.9", features = ["rayon", "serde", "nightly"] } authc = { git = "https://gitlab.com/veloren/auth.git", rev = "fb3dcbc4962b367253f8f2f92760ef44d2679c9a" } +#TODO: put bot in a different crate #bot only async-channel = { version = "1.6", optional = true } common-ecs = { package = "veloren-common-ecs", path = "../common/ecs", optional = true } diff --git a/client/src/lib.rs b/client/src/lib.rs index 5f225758bc..347326ad81 100644 --- a/client/src/lib.rs +++ b/client/src/lib.rs @@ -55,7 +55,7 @@ use common_net::{ sync::WorldSyncExt, }; use common_state::State; -use common_sys::add_local_systems; +use common_systems::add_local_systems; use comp::BuffKind; use futures_util::FutureExt; use hashbrown::{HashMap, HashSet}; diff --git a/common/state/src/lib.rs b/common/state/src/lib.rs index c633e60771..8c1ce6bbcb 100644 --- a/common/state/src/lib.rs +++ b/common/state/src/lib.rs @@ -1,589 +1,8 @@ +//! This crate contains the [`State`] and shared between +//! server (`veloren-server`) and the client (`veloren-client`) + #[cfg(feature = "plugins")] pub mod plugin; +mod state; -#[cfg(feature = "plugins")] -use crate::plugin::memory_manager::EcsWorld; -#[cfg(feature = "plugins")] -use crate::plugin::PluginMgr; -#[cfg(feature = "plugins")] -use common::uid::UidAllocator; -use common::{ - comp, - depot::{Depot, Id}, - event::{EventBus, LocalEvent, ServerEvent}, - outcome::Outcome, - region::RegionMap, - resources::{DeltaTime, GameMode, PlayerEntity, PlayerPhysicsSettings, Time, TimeOfDay}, - slowjob::SlowJobPool, - terrain::{Block, TerrainChunk, TerrainGrid}, - time::DayPeriod, - trade::Trades, - vol::{ReadVol, WriteVol}, -}; -use common_base::span; -use common_ecs::{PhysicsMetrics, SysMetrics}; -use common_net::sync::{interpolation as sync_interp, WorldSyncExt}; -use core::{convert::identity, time::Duration}; -use hashbrown::{hash_map, 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; -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; - -/// 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; - -/// NOTE: Please don't add `Deserialize` without checking to make sure we -/// can guarantee the invariant that every entry in `area_names` points to a -/// valid id in `areas`. -#[derive(Default)] -pub struct BuildAreas { - areas: Depot>, - area_names: HashMap>>, -} - -pub enum BuildAreaError { - /// This build area name is reserved by the system. - Reserved, - /// The build area name was not found. - NotFound, -} - -/// Build area names that can only be inserted, not removed. -const RESERVED_BUILD_AREA_NAMES: &[&str] = &["world"]; - -impl BuildAreas { - pub fn areas(&self) -> &Depot> { &self.areas } - - pub fn area_names(&self) -> &HashMap>> { &self.area_names } - - /// If the area_name is already in the map, returns Err(area_name). - pub fn insert(&mut self, area_name: String, area: Aabb) -> Result>, String> { - let area_name_entry = match self.area_names.entry(area_name) { - hash_map::Entry::Occupied(o) => return Err(o.replace_key()), - hash_map::Entry::Vacant(v) => v, - }; - let bb_id = self.areas.insert(area.made_valid()); - area_name_entry.insert(bb_id); - Ok(bb_id) - } - - pub fn remove(&mut self, area_name: &str) -> Result, BuildAreaError> { - if RESERVED_BUILD_AREA_NAMES.contains(&area_name) { - return Err(BuildAreaError::Reserved); - } - let bb_id = self - .area_names - .remove(area_name) - .ok_or(BuildAreaError::NotFound)?; - let area = self.areas.remove(bb_id).expect( - "Entries in `areas` are added before entries in `area_names` in `insert`, and that is \ - the only exposed way to add elements to `area_names`.", - ); - Ok(area) - } -} - -#[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 - } - } - - pub fn clear(&mut self) { self.blocks.clear(); } -} - -#[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(Copy, Clone)] -pub enum ExecMode { - Server, - Client, - Singleplayer, -} - -/// 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, -} - -impl State { - /// Create a new `State` in client mode. - pub fn client() -> Self { Self::new(GameMode::Client) } - - /// Create a new `State` in server mode. - pub fn server() -> Self { Self::new(GameMode::Server) } - - pub fn new(game_mode: GameMode) -> Self { - let thread_name_infix = match game_mode { - GameMode::Server => "s", - GameMode::Client => "c", - GameMode::Singleplayer => "sp", - }; - - let thread_pool = Arc::new( - ThreadPoolBuilder::new() - .thread_name(move |i| format!("rayon-{}-{}", thread_name_infix, i)) - .build() - .unwrap(), - ); - Self { - ecs: Self::setup_ecs_world(game_mode, &thread_pool), - thread_pool, - } - } - - /// 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) -> specs::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::(); - - // 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)); - - // Register unsynced resources used by the ECS. - ecs.insert(Time(0.0)); - ecs.insert(DeltaTime(0.0)); - ecs.insert(PlayerEntity(None)); - ecs.insert(TerrainGrid::new().unwrap()); - ecs.insert(BlockChange::default()); - ecs.insert(BuildAreas::default()); - ecs.insert(TerrainChanges::default()); - ecs.insert(EventBus::::default()); - ecs.insert(game_mode); - ecs.insert(Vec::::new()); - - let slow_limit = thread_pool.current_num_threads().max(2) as u64; - let slow_limit = slow_limit / 2 + slow_limit / 4; - tracing::trace!(?slow_limit, "Slow Thread limit"); - ecs.insert(SlowJobPool::new(slow_limit, Arc::clone(&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()); - - // 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. - 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 } - - /// 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 day period (period of the day/night cycle) - /// 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::