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
This commit is contained in:
Imbris 2021-03-13 01:48:30 -05:00
parent 4022937da7
commit 8d0b776f18
15 changed files with 374 additions and 181 deletions

View File

@ -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"

View File

@ -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,

View File

@ -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

View File

@ -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;

View File

@ -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<u32>) -> Vec2<u32> { 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<i32>) -> Vec2<i32> {
chunk_pos * Self::RECT_SIZE.as_::<i32>() + Self::RECT_SIZE.as_::<i32>() / 2
}
}
// TerrainChunkMeta
#[derive(Debug, Clone, Serialize, Deserialize)]

View File

@ -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

View File

@ -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?

140
server-cli/src/cmd.rs Normal file
View File

@ -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<String>, &mut Sender<Message>),
}
// 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::<u64>() {
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::<u32>() {
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 <username>\'",
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<Message>) {
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::<Vec<_>>();
let (arg_len, args) = if cmd.split_spaces {
(
args.len(),
args.into_iter()
.map(|s| s.to_string())
.collect::<Vec<String>>(),
)
} else {
(0, vec![args.into_iter().collect::<String>()])
};
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);
}
}
}

View File

@ -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

View File

@ -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) => {},
}

View File

@ -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<String>, &mut mpsc::Sender<Message>),
}
// 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::<u64>() {
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 <username>\'",
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<Message>,
background: Option<std::thread::JoinHandle<()>>,
@ -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<Message>) {
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::<Vec<_>>();
let (arg_len, args) = if cmd.split_spaces {
(
args.len(),
args.into_iter()
.map(|s| s.to_string())
.collect::<Vec<String>>(),
)
} else {
(0, vec![args.into_iter().collect::<String>()])
};
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);
}
}
}

View File

@ -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::<Client>().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::<LoginProvider>();
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::<comp::Player>())
.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::<LoginProvider>();
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::<comp::Player>())
.join()
.find(|(_, player)| player.uuid() == uuid)
.map(|(e, _)| e)
}) {
// Remove admin component if the player is ingame
let _ = self.state.ecs().write_storage::<comp::Admin>().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<common::uuid::Uuid> {
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<common::uuid::Uuid> {
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
},
}
}

View File

@ -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<world::World>,
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<world::World>,
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::<crate::chunk_generator::ChunkGenerator>();
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::<SpawnPoint>().0;

View File

@ -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,

View File

@ -43,10 +43,10 @@ pub fn init(settings: &Settings) -> Vec<impl Drop> {
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"