From 87a7d7afd17c87788d6c0fd722b77fae8ce14210 Mon Sep 17 00:00:00 2001 From: Imbris Date: Sat, 13 Mar 2021 01:48:30 -0500 Subject: [PATCH] Move server-cli commands to separate file, tracy profiling par_join improvements, misc improvements - remove overwritten logging setting in server-cli - add server-cli command to load a random area for testing without a client - make admin add/remove commands modify ingame players instead of needing to reconnect - add spans to par_join jobs - added test command that loads up an area of the world - add tracy-world-server alias - set debug directives to info for logging --- .cargo/config | 1 + common/base/src/lib.rs | 19 +++++ common/ecs/src/system.rs | 5 +- common/src/lib.rs | 5 ++ common/src/terrain/mod.rs | 21 ++++++ common/sys/src/phys.rs | 46 +++++++++--- server-cli/src/admin.rs | 4 +- server-cli/src/cmd.rs | 140 +++++++++++++++++++++++++++++++++++ server-cli/src/logging.rs | 11 +-- server-cli/src/main.rs | 11 ++- server-cli/src/tui_runner.rs | 129 +------------------------------- server/src/lib.rs | 93 ++++++++++++++++------- server/src/state_ext.rs | 56 ++++++++++++++ server/src/sys/agent.rs | 10 ++- voxygen/src/logging.rs | 4 +- 15 files changed, 374 insertions(+), 181 deletions(-) create mode 100644 server-cli/src/cmd.rs diff --git a/.cargo/config b/.cargo/config index 09fa64d2ea..f1923f273e 100644 --- a/.cargo/config +++ b/.cargo/config @@ -8,6 +8,7 @@ csv-export = "run --manifest-path common/Cargo.toml --features=bin_csv --bin csv csv-import = "run --manifest-path common/Cargo.toml --features=bin_csv --bin csv_import" test-server = "-Zpackage-features run --bin veloren-server-cli --no-default-features -- -b" tracy-server = "-Zunstable-options -Zpackage-features run --bin veloren-server-cli --no-default-features --features tracy,simd --profile no_overflow" +tracy-world-server = "-Zunstable-options -Zpackage-features run --bin veloren-server-cli --features tracy,simd --profile no_overflow -- -bi" test-voxygen = "-Zpackage-features run --bin veloren-voxygen --no-default-features --features gl,simd" tracy-voxygen = "-Zunstable-options -Zpackage-features run --bin veloren-voxygen --no-default-features --features tracy,gl,simd --profile no_overflow" server = "run --bin veloren-server-cli" diff --git a/common/base/src/lib.rs b/common/base/src/lib.rs index e08ac271ec..c1e1704880 100644 --- a/common/base/src/lib.rs +++ b/common/base/src/lib.rs @@ -41,6 +41,25 @@ macro_rules! span { }; } +/// Like the span macro but only used when profiling and not in regular tracing +/// operations +#[macro_export] +macro_rules! prof_span { + ($guard_name:tt, $name:expr) => { + #[cfg(feature = "tracy")] + let $guard_name = $crate::tracy_client::Span::new( + $name, + "", + module_path!(), + line!(), + // No callstack since this has significant overhead + 0, + ); + #[cfg(not(feature = "tracy"))] + let $guard_name = (); + }; +} + /// There's no guard, but really this is actually the guard pub struct GuardlessSpan { span: tracing::Span, diff --git a/common/ecs/src/system.rs b/common/ecs/src/system.rs index f1459967f4..033ed7818d 100644 --- a/common/ecs/src/system.rs +++ b/common/ecs/src/system.rs @@ -253,7 +253,10 @@ where fn run(&mut self, data: Self::SystemData) { common_base::span!(_guard, "run", &format!("{}::Sys::run", T::NAME)); self.cpu_stats.reset(); - T::run(self, data.0); + { + common_base::span!(_guard, "run inner", &format!("{}::Sys::run inner", T::NAME)); + T::run(self, data.0); + } self.cpu_stats.end(); data.1 .stats diff --git a/common/src/lib.rs b/common/src/lib.rs index cd1c62c9bd..3f7a5f35a9 100644 --- a/common/src/lib.rs +++ b/common/src/lib.rs @@ -18,6 +18,11 @@ trait_alias, type_alias_impl_trait )] + +/// Re-exported crates +pub use uuid; + +// modules #[cfg(not(target_arch = "wasm32"))] pub mod assets; #[cfg(not(target_arch = "wasm32"))] pub mod astar; diff --git a/common/src/terrain/mod.rs b/common/src/terrain/mod.rs index e1a18f2dbc..b108d302c3 100644 --- a/common/src/terrain/mod.rs +++ b/common/src/terrain/mod.rs @@ -49,6 +49,27 @@ impl RectVolSize for TerrainChunkSize { }; } +impl TerrainChunkSize { + #[inline(always)] + /// Convert dimensions in terms of chunks into dimensions in terms of blocks + /// ``` + /// assert_eq!(TerrainChunkSize::blocks(Vec2::new(3, 2)), Vec2::new(96, 64)); + /// ``` + pub fn blocks(chunks: Vec2) -> Vec2 { chunks * Self::RECT_SIZE } + + /// Calculate the world position (i.e. in blocks) at the center of this + /// chunk + /// ``` + /// assert_eq!( + /// TerrainChunkSize::center_wpos(Vec2::new(0, 2)), + /// Vec2::new(16, 80) + /// ); + /// ``` + pub fn center_wpos(chunk_pos: Vec2) -> Vec2 { + chunk_pos * Self::RECT_SIZE.as_::() + Self::RECT_SIZE.as_::() / 2 + } +} + // TerrainChunkMeta #[derive(Debug, Clone, Serialize, Deserialize)] diff --git a/common/sys/src/phys.rs b/common/sys/src/phys.rs index e58197ed09..cb0ee3cd51 100644 --- a/common/sys/src/phys.rs +++ b/common/sys/src/phys.rs @@ -10,7 +10,7 @@ use common::{ uid::Uid, vol::ReadVol, }; -use common_base::span; +use common_base::{prof_span, span}; use common_ecs::{Job, Origin, ParMode, Phase, PhysicsMetrics, System}; use rayon::iter::ParallelIterator; use specs::{Entities, Join, ParJoin, Read, ReadExpect, ReadStorage, WriteExpect, WriteStorage}; @@ -232,9 +232,12 @@ impl<'a> System<'a> for Sys { sticky.is_none() || (physics.on_wall.is_none() && !physics.on_ground) }) .map(|(e, p, v, vd, m, c, _, _, ph, pr, c_s)| (e, p, v, vd, m, c, ph, pr, c_s)) - .fold( - PhysicsMetrics::default, - |mut metrics, + .map_init( + || { + prof_span!(guard, "physics e<>e rayon job"); + guard + }, + |_guard, ( entity, pos, @@ -256,6 +259,9 @@ impl<'a> System<'a> for Sys { let mut vel_delta = Vec3::zero(); + let mut entity_entity_collision_checks = 0; + let mut entity_entity_collisions = 0; + for ( entity_other, other, @@ -307,7 +313,7 @@ impl<'a> System<'a> for Sys { continue; } - metrics.entity_entity_collision_checks += 1; + entity_entity_collision_checks += 1; const MIN_COLLISION_DIST: f32 = 0.3; let increments = ((previous_cache.velocity_dt @@ -334,7 +340,7 @@ impl<'a> System<'a> for Sys { { if !collided { physics.touch_entities.push(*other); - metrics.entity_entity_collisions += 1; + entity_entity_collisions += 1; } // Don't apply repulsive force to projectiles @@ -353,7 +359,12 @@ impl<'a> System<'a> for Sys { // Change velocity vel.0 += vel_delta * dt.0; - metrics + + // Metrics + PhysicsMetrics { + entity_entity_collision_checks, + entity_entity_collisions, + } }, ) .reduce(PhysicsMetrics::default, |old, new| PhysicsMetrics { @@ -380,13 +391,19 @@ impl<'a> System<'a> for Sys { !&mountings, ) .par_join() - .fold(Vec::new, | - mut land_on_grounds, + .map_init( + || { + prof_span!(guard, "physics e<>t rayon job"); + guard + }, + |_guard, (entity, _scale, sticky, collider, mut pos, mut vel, _ori, mut physics_state, _), | { + let mut landed_on_ground = None; + if sticky.is_some() && physics_state.on_surface().is_some() { vel.0 = Vec3::zero(); - return land_on_grounds; + return landed_on_ground; } // TODO: Use this @@ -593,7 +610,7 @@ impl<'a> System<'a> for Sys { on_ground = true; if !was_on_ground { - land_on_grounds.push((entity, *vel)); + landed_on_ground = Some((entity, *vel)); } } else if resolve_dir.z < 0.0 && vel.0.z >= 0.0 { on_ceiling = true; @@ -770,7 +787,12 @@ impl<'a> System<'a> for Sys { }, } - land_on_grounds + landed_on_ground + }).fold(Vec::new, |mut lands_on_grounds, landed_on_ground| { + if let Some(land_on_ground) = landed_on_ground { + lands_on_grounds.push(land_on_ground); + } + lands_on_grounds }).reduce(Vec::new, |mut land_on_grounds_a, mut land_on_grounds_b| { land_on_grounds_a.append(&mut land_on_grounds_b); land_on_grounds_a diff --git a/server-cli/src/admin.rs b/server-cli/src/admin.rs index 281bc83ca8..9a9fef99cb 100644 --- a/server-cli/src/admin.rs +++ b/server-cli/src/admin.rs @@ -16,12 +16,12 @@ pub fn admin_subcommand( match sub_m.subcommand() { ("add", Some(sub_m)) => { if let Some(username) = sub_m.value_of("username") { - server::add_admin(username, &login_provider, editable_settings, data_dir) + server::add_admin(username, &login_provider, editable_settings, data_dir); } }, ("remove", Some(sub_m)) => { if let Some(username) = sub_m.value_of("username") { - server::remove_admin(username, &login_provider, editable_settings, data_dir) + server::remove_admin(username, &login_provider, editable_settings, data_dir); } }, // TODO: can clap enforce this? diff --git a/server-cli/src/cmd.rs b/server-cli/src/cmd.rs new file mode 100644 index 0000000000..953546b4e3 --- /dev/null +++ b/server-cli/src/cmd.rs @@ -0,0 +1,140 @@ +use core::time::Duration; +use std::sync::mpsc::Sender; +use tracing::{error, info, warn}; + +#[derive(Debug, Clone)] +pub enum Message { + AbortShutdown, + Shutdown { grace_period: Duration }, + Quit, + AddAdmin(String), + RemoveAdmin(String), + LoadArea(u32), +} + +struct Command<'a> { + name: &'a str, + description: &'a str, + // Whether or not the command splits the arguments on whitespace + split_spaces: bool, + args: usize, + cmd: fn(Vec, &mut Sender), +} + +// TODO: maybe we could be using clap here? +const COMMANDS: [Command; 6] = [ + Command { + name: "quit", + description: "Closes the server", + split_spaces: true, + args: 0, + cmd: |_, sender| sender.send(Message::Quit).unwrap(), + }, + Command { + name: "shutdown", + description: "Initiates a graceful shutdown of the server, waiting the specified number \ + of seconds before shutting down", + split_spaces: true, + args: 1, + cmd: |args, sender| { + if let Ok(grace_period) = args.first().unwrap().parse::() { + sender + .send(Message::Shutdown { + grace_period: Duration::from_secs(grace_period), + }) + .unwrap() + } else { + error!("Grace period must an integer") + } + }, + }, + Command { + name: "loadarea", + description: "Loads up the chunks in a random area and adds a entity that mimics a player \ + to keep them from despawning", + split_spaces: true, + args: 1, + cmd: |args, sender| { + if let Ok(view_distance) = args.first().unwrap().parse::() { + sender.send(Message::LoadArea(view_distance)).unwrap(); + } else { + error!("View distance must be an integer"); + } + }, + }, + Command { + name: "abortshutdown", + description: "Aborts a shutdown if one is in progress", + split_spaces: false, + args: 0, + cmd: |_, sender| sender.send(Message::AbortShutdown).unwrap(), + }, + Command { + name: "admin", + description: "Add or remove an admin via \'admin add/remove \'", + split_spaces: true, + args: 2, + cmd: |args, sender| match args.get(..2) { + Some([op, username]) if op == "add" => { + sender.send(Message::AddAdmin(username.clone())).unwrap() + }, + Some([op, username]) if op == "remove" => { + sender.send(Message::RemoveAdmin(username.clone())).unwrap() + }, + Some(_) => error!("First arg must be add or remove"), + _ => error!("Not enough args, should be unreachable"), + }, + }, + Command { + name: "help", + description: "List all command available", + split_spaces: true, + args: 0, + cmd: |_, _| { + info!("===== Help ====="); + for command in COMMANDS.iter() { + info!("{} - {}", command.name, command.description) + } + info!("================"); + }, + }, +]; + +pub fn parse_command(input: &str, msg_s: &mut Sender) { + let mut args = input.split_whitespace(); + + if let Some(cmd_name) = args.next() { + if let Some(cmd) = COMMANDS.iter().find(|cmd| cmd.name == cmd_name) { + let args = args.collect::>(); + + let (arg_len, args) = if cmd.split_spaces { + ( + args.len(), + args.into_iter() + .map(|s| s.to_string()) + .collect::>(), + ) + } else { + (0, vec![args.into_iter().collect::()]) + }; + + use core::cmp::Ordering; + match arg_len.cmp(&cmd.args) { + Ordering::Less => error!("{} takes {} arguments", cmd_name, cmd.args), + Ordering::Greater => { + warn!("{} only takes {} arguments", cmd_name, cmd.args); + let cmd = cmd.cmd; + + cmd(args, msg_s) + }, + Ordering::Equal => { + let cmd = cmd.cmd; + + cmd(args, msg_s) + }, + } + } else { + error!("{} not found", cmd_name); + } + } +} diff --git a/server-cli/src/logging.rs b/server-cli/src/logging.rs index fb26085d0f..356e9938b2 100644 --- a/server-cli/src/logging.rs +++ b/server-cli/src/logging.rs @@ -1,6 +1,5 @@ use crate::tuilog::TuiLog; use termcolor::{ColorChoice, StandardStream}; -use tracing::Level; use tracing_subscriber::{filter::LevelFilter, EnvFilter, FmtSubscriber}; #[cfg(feature = "tracy")] use tracing_subscriber::{layer::SubscriberExt, prelude::*}; @@ -19,10 +18,10 @@ pub fn init(basic: bool) { .add_directive("hyper=info".parse().unwrap()) .add_directive("prometheus_hyper=info".parse().unwrap()) .add_directive("mio::pool=info".parse().unwrap()) - .add_directive("mio::sys::windows=debug".parse().unwrap()) + .add_directive("mio::sys::windows=info".parse().unwrap()) .add_directive("h2=info".parse().unwrap()) .add_directive("tokio_util=info".parse().unwrap()) - .add_directive("rustls=debug".parse().unwrap()) + .add_directive("rustls=info".parse().unwrap()) .add_directive("veloren_network_protocol=info".parse().unwrap()) .add_directive( "veloren_server::persistence::character=info" @@ -32,7 +31,6 @@ pub fn init(basic: bool) { .add_directive(LevelFilter::INFO.into()) }; - #[cfg(not(feature = "tracy"))] let filter = match std::env::var_os(RUST_LOG_ENV).map(|s| s.into_string()) { Some(Ok(env)) => { let mut filter = base_exceptions(EnvFilter::new("")); @@ -49,6 +47,7 @@ pub fn init(basic: bool) { #[cfg(feature = "tracy")] tracing_subscriber::registry() + .with(filter) .with(tracing_tracy::TracyLayer::new().with_stackdepth(0)) .init(); @@ -56,9 +55,7 @@ pub fn init(basic: bool) { // TODO: when tracing gets per Layer filters re-enable this when the tracy feature is being // used (and do the same in voxygen) { - let subscriber = FmtSubscriber::builder() - .with_max_level(Level::ERROR) - .with_env_filter(filter); + let subscriber = FmtSubscriber::builder().with_env_filter(filter); if basic { subscriber diff --git a/server-cli/src/main.rs b/server-cli/src/main.rs index fbd390fc36..550709a4d7 100644 --- a/server-cli/src/main.rs +++ b/server-cli/src/main.rs @@ -3,16 +3,16 @@ #![feature(bool_to_option)] mod admin; +/// `server-cli` interface commands not to be confused with the commands sent +/// from the client to the server +mod cmd; mod logging; mod settings; mod shutdown_coordinator; mod tui_runner; mod tuilog; -use crate::{ - shutdown_coordinator::ShutdownCoordinator, - tui_runner::{Message, Tui}, -}; +use crate::{cmd::Message, shutdown_coordinator::ShutdownCoordinator, tui_runner::Tui}; use clap::{App, Arg, SubCommand}; use common::clock::Clock; use common_base::span; @@ -201,6 +201,9 @@ fn main() -> io::Result<()> { Message::RemoveAdmin(username) => { server.remove_admin(&username); }, + Message::LoadArea(view_distance) => { + server.create_centered_persister(view_distance); + }, }, Err(mpsc::TryRecvError::Empty) | Err(mpsc::TryRecvError::Disconnected) => {}, } diff --git a/server-cli/src/tui_runner.rs b/server-cli/src/tui_runner.rs index 136c2d94f2..e215496bb8 100644 --- a/server-cli/src/tui_runner.rs +++ b/server-cli/src/tui_runner.rs @@ -1,4 +1,4 @@ -use crate::logging::LOG; +use crate::{cmd, logging::LOG, Message}; use crossterm::{ event::{DisableMouseCapture, EnableMouseCapture}, execute, @@ -12,7 +12,7 @@ use std::{ }, time::Duration, }; -use tracing::{debug, error, info, warn}; +use tracing::{debug, error, warn}; use tui::{ backend::CrosstermBackend, layout::Rect, @@ -21,89 +21,6 @@ use tui::{ Terminal, }; -#[derive(Debug, Clone)] -pub enum Message { - AbortShutdown, - Shutdown { grace_period: Duration }, - Quit, - AddAdmin(String), - RemoveAdmin(String), -} - -pub struct Command<'a> { - pub name: &'a str, - pub description: &'a str, - // Whether or not the command splits the arguments on whitespace - pub split_spaces: bool, - pub args: usize, - pub cmd: fn(Vec, &mut mpsc::Sender), -} - -// TODO: mabye we could be using clap here? -pub const COMMANDS: [Command; 5] = [ - Command { - name: "quit", - description: "Closes the server", - split_spaces: true, - args: 0, - cmd: |_, sender| sender.send(Message::Quit).unwrap(), - }, - Command { - name: "shutdown", - description: "Initiates a graceful shutdown of the server, waiting the specified number \ - of seconds before shutting down", - split_spaces: true, - args: 1, - cmd: |args, sender| { - if let Ok(grace_period) = args.first().unwrap().parse::() { - sender - .send(Message::Shutdown { - grace_period: Duration::from_secs(grace_period), - }) - .unwrap() - } else { - error!("Grace period must an integer") - } - }, - }, - Command { - name: "abortshutdown", - description: "Aborts a shutdown if one is in progress", - split_spaces: false, - args: 0, - cmd: |_, sender| sender.send(Message::AbortShutdown).unwrap(), - }, - Command { - name: "admin", - description: "Add or remove an admin via \'admin add/remove \'", - split_spaces: true, - args: 2, - cmd: |args, sender| match args.get(..2) { - Some([op, username]) if op == "add" => { - sender.send(Message::AddAdmin(username.clone())).unwrap() - }, - Some([op, username]) if op == "remove" => { - sender.send(Message::RemoveAdmin(username.clone())).unwrap() - }, - Some(_) => error!("First arg must be add or remove"), - _ => error!("Not enough args, should be unreachable"), - }, - }, - Command { - name: "help", - description: "List all command available", - split_spaces: true, - args: 0, - cmd: |_, _| { - info!("===== Help ====="); - for command in COMMANDS.iter() { - info!("{} - {}", command.name, command.description) - } - info!("================"); - }, - }, -]; - pub struct Tui { pub msg_r: mpsc::Receiver, background: Option>, @@ -129,7 +46,7 @@ impl Tui { }, KeyCode::Enter => { debug!(?input, "tui mode: command entered"); - parse_command(input, msg_s); + cmd::parse_command(input, msg_s); *input = String::new(); }, @@ -163,7 +80,7 @@ impl Tui { }, Ok(_) => { debug!(?line, "basic mode: command entered"); - parse_command(&line, &mut msg_s); + crate::cmd::parse_command(&line, &mut msg_s); }, } } @@ -263,41 +180,3 @@ impl Drop for Tui { Tui::shutdown(self.basic); } } - -fn parse_command(input: &str, msg_s: &mut mpsc::Sender) { - let mut args = input.split_whitespace(); - - if let Some(cmd_name) = args.next() { - if let Some(cmd) = COMMANDS.iter().find(|cmd| cmd.name == cmd_name) { - let args = args.collect::>(); - - let (arg_len, args) = if cmd.split_spaces { - ( - args.len(), - args.into_iter() - .map(|s| s.to_string()) - .collect::>(), - ) - } else { - (0, vec![args.into_iter().collect::()]) - }; - - match arg_len.cmp(&cmd.args) { - std::cmp::Ordering::Less => error!("{} takes {} arguments", cmd_name, cmd.args), - std::cmp::Ordering::Greater => { - warn!("{} only takes {} arguments", cmd_name, cmd.args); - let cmd = cmd.cmd; - - cmd(args, msg_s) - }, - std::cmp::Ordering::Equal => { - let cmd = cmd.cmd; - - cmd(args, msg_s) - }, - } - } else { - error!("{} not found", cmd_name); - } - } -} diff --git a/server/src/lib.rs b/server/src/lib.rs index 4187108814..51f4968f38 100644 --- a/server/src/lib.rs +++ b/server/src/lib.rs @@ -305,14 +305,10 @@ impl Server { .min_by_key(|site_pos| site_pos.distance_squared(center_chunk)) .unwrap_or(center_chunk); - // calculate the absolute position of the chunk in the world - // (we could add TerrainChunkSize::RECT_SIZE / 2 here, to spawn in the middle of - // the chunk) - let spawn_wpos = spawn_chunk.map2(TerrainChunkSize::RECT_SIZE, |e, sz| { - e as i32 * sz as i32 + sz as i32 / 2 - }); + // Calculate the middle of the chunk in the world + let spawn_wpos = TerrainChunkSize::center_wpos(spawn_chunk); - // unwrapping because generate_chunk only returns err when should_continue evals + // Unwrapping because generate_chunk only returns err when should_continue evals // to true let (tc, _cs) = world.generate_chunk(index, spawn_chunk, || false).unwrap(); let min_z = tc.get_min_z(); @@ -345,7 +341,7 @@ impl Server { #[cfg(not(feature = "worldgen"))] let spawn_point = Vec3::new(0.0, 0.0, 256.0); - // set the spawn point we calculated above + // Set the spawn point we calculated above state.ecs_mut().insert(SpawnPoint(spawn_point)); // Insert the world into the ECS (todo: Maybe not an Arc?) @@ -931,30 +927,63 @@ impl Server { self.state.ecs().read_storage::().join().count() as i64 } - // TODO: add Admin comp if ingame pub fn add_admin(&self, username: &str) { let mut editable_settings = self.editable_settings_mut(); let login_provider = self.state.ecs().fetch::(); let data_dir = self.data_dir(); - add_admin( + if let Some(entity) = add_admin( username, &login_provider, &mut editable_settings, &data_dir.path, - ); + ).and_then(|uuid| { + let state = &self.state; + (&state.ecs().entities(), &state.read_storage::()) + .join() + .find(|(_, player)| player.uuid() == uuid) + .map(|(e, _)| e) + }) { + // Add admin component if the player is ingame + let _ = self.state.ecs().write_storage().insert(entity, comp::Admin); + + }; } - // TODO: remove Admin comp if ingame pub fn remove_admin(&self, username: &str) { let mut editable_settings = self.editable_settings_mut(); let login_provider = self.state.ecs().fetch::(); let data_dir = self.data_dir(); - remove_admin( + if let Some(entity) = remove_admin( username, &login_provider, &mut editable_settings, &data_dir.path, - ); + ).and_then(|uuid| { + let state = &self.state; + (&state.ecs().entities(), &state.read_storage::()) + .join() + .find(|(_, player)| player.uuid() == uuid) + .map(|(e, _)| e) + }) { + // Remove admin component if the player is ingame + let _ = self.state.ecs().write_storage::().remove(entity); + }; + } + + /// Useful for testing without a client + /// view_distance: distance in chunks that are persisted, this acts like the player view + /// distance so it is actually a bit farther due to a buffer zone + pub fn create_centered_persister(&mut self, view_distance: u32) { + let world_dims_chunks = self.world.sim().get_size(); + let world_dims_blocks = TerrainChunkSize::blocks(world_dims_chunks); + // NOTE: origin is in the corner of the map + // TODO: extend this function to have picking a random position or specifiying a position + // as options + //let mut rng = rand::thread_rng(); + // // Pick a random position but not to close to the edge + // let rand_pos = world_dims_blocks.map(|e| e as i32).map(|e| e / 2 + rng.gen_range(-e/2..e/2 + 1)); + let pos = comp::Pos(Vec3::from(world_dims_blocks.map(|e| e as f32 / 2.0))); + self.state.create_persister(pos, view_distance, &self.world, &self.index, &self.runtime).build(); } } @@ -966,35 +995,42 @@ impl Drop for Server { } } +/// If successful returns the Some(uuid) of the added admin pub fn add_admin( username: &str, login_provider: &LoginProvider, editable_settings: &mut EditableSettings, data_dir: &std::path::Path, -) { +) -> Option { use crate::settings::EditableSetting; match login_provider.username_to_uuid(username) { Ok(uuid) => editable_settings.admins.edit(data_dir, |admins| { if admins.insert(uuid) { info!("Successfully added {} ({}) as an admin!", username, uuid); + Some(uuid) } else { info!("{} ({}) is already an admin!", username, uuid); + None } }), - Err(err) => error!( - ?err, - "Could not find uuid for this name either the user does not exist or there was an \ - error communicating with the auth server." - ), + Err(err) => { + error!( + ?err, + "Could not find uuid for this name either the user does not exist or there was an \ + error communicating with the auth server." + ); + None + }, } } +/// If successful returns the Some(uuid) of the removed admin pub fn remove_admin( username: &str, login_provider: &LoginProvider, editable_settings: &mut EditableSettings, data_dir: &std::path::Path, -) { +) -> Option { use crate::settings::EditableSetting; match login_provider.username_to_uuid(username) { Ok(uuid) => editable_settings.admins.edit(data_dir, |admins| { @@ -1003,14 +1039,19 @@ pub fn remove_admin( "Successfully removed {} ({}) from the admins", username, uuid ); + Some(uuid) } else { info!("{} ({}) is not an admin!", username, uuid); + None } }), - Err(err) => error!( - ?err, - "Could not find uuid for this name either the user does not exist or there was an \ - error communicating with the auth server." - ), + Err(err) => { + error!( + ?err, + "Could not find uuid for this name either the user does not exist or there was an \ + error communicating with the auth server." + ); + None + }, } } diff --git a/server/src/state_ext.rs b/server/src/state_ext.rs index 379fb920e5..a596b31cbb 100644 --- a/server/src/state_ext.rs +++ b/server/src/state_ext.rs @@ -63,6 +63,18 @@ pub trait StateExt { pos: comp::Pos, ori: comp::Ori, ) -> EcsEntityBuilder; + // NOTE: currently only used for testing + /// Queues chunk generation in the view distance of the persister, this + /// entity must be built before those chunks are received (the builder + /// borrows the ecs world so that is kind of impossible in practice) + fn create_persister( + &mut self, + pos: comp::Pos, + view_distance: u32, + world: &std::sync::Arc, + index: &world::IndexOwned, + runtime: &tokio::runtime::Runtime, + ) -> EcsEntityBuilder; /// Insert common/default components for a new character joining the server fn initialize_character_data(&mut self, entity: EcsEntity, character_id: CharacterId); /// Update the components associated with the entity's current character. @@ -257,6 +269,50 @@ impl StateExt for State { }) } + // NOTE: currently only used for testing + /// Queues chunk generation in the view distance of the persister, this + /// entity must be built before those chunks are received (the builder + /// borrows the ecs world so that is kind of impossible in practice) + fn create_persister( + &mut self, + pos: comp::Pos, + view_distance: u32, + world: &std::sync::Arc, + index: &world::IndexOwned, + runtime: &tokio::runtime::Runtime, + ) -> EcsEntityBuilder { + use common::{terrain::TerrainChunkSize, vol::RectVolSize}; + use std::sync::Arc; + // Request chunks + { + let mut chunk_generator = self + .ecs() + .write_resource::(); + let chunk_pos = self.terrain().pos_key(pos.0.map(|e| e as i32)); + (-(view_distance as i32)..view_distance as i32 + 1) + .flat_map(|x| { + (-(view_distance as i32)..view_distance as i32 + 1).map(move |y| Vec2::new(x, y)) + }) + .map(|offset| offset + chunk_pos) + // Filter chunks outside the view distance + // Note: calculation from client chunk request filtering + .filter(|chunk_key| { + pos.0.xy().map(|e| e as f64).distance( + chunk_key.map(|e| e as f64 + 0.5) * TerrainChunkSize::RECT_SIZE.map(|e| e as f64), + ) < (view_distance as f64 - 1.0 + 2.5 * 2.0_f64.sqrt()) + * TerrainChunkSize::RECT_SIZE.x as f64 + }) + .for_each(|chunk_key| { + chunk_generator.generate_chunk(None, chunk_key, runtime, Arc::clone(world), index.clone()); + }); + } + + self.ecs_mut() + .create_entity_synced() + .with(pos) + .with(Presence::new(view_distance, PresenceKind::Spectator)) + } + fn initialize_character_data(&mut self, entity: EcsEntity, character_id: CharacterId) { let spawn_point = self.ecs().read_resource::().0; diff --git a/server/src/sys/agent.rs b/server/src/sys/agent.rs index dd029b3546..40059f5a28 100644 --- a/server/src/sys/agent.rs +++ b/server/src/sys/agent.rs @@ -23,6 +23,7 @@ use common::{ util::Dir, vol::ReadVol, }; +use common_base::prof_span; use common_ecs::{Job, Origin, ParMode, Phase, System}; use rand::{thread_rng, Rng}; use rayon::iter::ParallelIterator; @@ -141,8 +142,13 @@ impl<'a> System<'a> for Sys { .map(|ms| *ms == MountState::Unmounted) .unwrap_or(true) }) - .for_each( - |( + .for_each_init( + || { + prof_span!(guard, "agent rayon job"); + guard + }, + |_guard, + ( entity, (energy, health), pos, diff --git a/voxygen/src/logging.rs b/voxygen/src/logging.rs index e8569be402..5039c94316 100644 --- a/voxygen/src/logging.rs +++ b/voxygen/src/logging.rs @@ -43,10 +43,10 @@ pub fn init(settings: &Settings) -> Vec { env.add_directive("dot_vox::parser=warn".parse().unwrap()) .add_directive("gfx_device_gl=warn".parse().unwrap()) .add_directive("prometheus_hyper=warn".parse().unwrap()) - .add_directive("mio::sys::windows=debug".parse().unwrap()) + .add_directive("mio::sys::windows=info".parse().unwrap()) .add_directive("h2=info".parse().unwrap()) .add_directive("tokio_util=info".parse().unwrap()) - .add_directive("rustls=debug".parse().unwrap()) + .add_directive("rustls=info".parse().unwrap()) .add_directive("veloren_network_protocol=info".parse().unwrap()) .add_directive( "veloren_server::persistence::character=info"