mirror of
https://gitlab.com/veloren/veloren.git
synced 2024-08-30 18:12:32 +00:00
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:
parent
4022937da7
commit
8d0b776f18
@ -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"
|
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"
|
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-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"
|
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"
|
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"
|
server = "run --bin veloren-server-cli"
|
||||||
|
@ -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
|
/// There's no guard, but really this is actually the guard
|
||||||
pub struct GuardlessSpan {
|
pub struct GuardlessSpan {
|
||||||
span: tracing::Span,
|
span: tracing::Span,
|
||||||
|
@ -253,7 +253,10 @@ where
|
|||||||
fn run(&mut self, data: Self::SystemData) {
|
fn run(&mut self, data: Self::SystemData) {
|
||||||
common_base::span!(_guard, "run", &format!("{}::Sys::run", T::NAME));
|
common_base::span!(_guard, "run", &format!("{}::Sys::run", T::NAME));
|
||||||
self.cpu_stats.reset();
|
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();
|
self.cpu_stats.end();
|
||||||
data.1
|
data.1
|
||||||
.stats
|
.stats
|
||||||
|
@ -18,6 +18,11 @@
|
|||||||
trait_alias,
|
trait_alias,
|
||||||
type_alias_impl_trait
|
type_alias_impl_trait
|
||||||
)]
|
)]
|
||||||
|
|
||||||
|
/// Re-exported crates
|
||||||
|
pub use uuid;
|
||||||
|
|
||||||
|
// modules
|
||||||
#[cfg(not(target_arch = "wasm32"))]
|
#[cfg(not(target_arch = "wasm32"))]
|
||||||
pub mod assets;
|
pub mod assets;
|
||||||
#[cfg(not(target_arch = "wasm32"))] pub mod astar;
|
#[cfg(not(target_arch = "wasm32"))] pub mod astar;
|
||||||
|
@ -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
|
// TerrainChunkMeta
|
||||||
|
|
||||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||||
|
@ -10,7 +10,7 @@ use common::{
|
|||||||
uid::Uid,
|
uid::Uid,
|
||||||
vol::ReadVol,
|
vol::ReadVol,
|
||||||
};
|
};
|
||||||
use common_base::span;
|
use common_base::{prof_span, span};
|
||||||
use common_ecs::{Job, Origin, ParMode, Phase, PhysicsMetrics, System};
|
use common_ecs::{Job, Origin, ParMode, Phase, PhysicsMetrics, System};
|
||||||
use rayon::iter::ParallelIterator;
|
use rayon::iter::ParallelIterator;
|
||||||
use specs::{Entities, Join, ParJoin, Read, ReadExpect, ReadStorage, WriteExpect, WriteStorage};
|
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)
|
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))
|
.map(|(e, p, v, vd, m, c, _, _, ph, pr, c_s)| (e, p, v, vd, m, c, ph, pr, c_s))
|
||||||
.fold(
|
.map_init(
|
||||||
PhysicsMetrics::default,
|
|| {
|
||||||
|mut metrics,
|
prof_span!(guard, "physics e<>e rayon job");
|
||||||
|
guard
|
||||||
|
},
|
||||||
|
|_guard,
|
||||||
(
|
(
|
||||||
entity,
|
entity,
|
||||||
pos,
|
pos,
|
||||||
@ -256,6 +259,9 @@ impl<'a> System<'a> for Sys {
|
|||||||
|
|
||||||
let mut vel_delta = Vec3::zero();
|
let mut vel_delta = Vec3::zero();
|
||||||
|
|
||||||
|
let mut entity_entity_collision_checks = 0;
|
||||||
|
let mut entity_entity_collisions = 0;
|
||||||
|
|
||||||
for (
|
for (
|
||||||
entity_other,
|
entity_other,
|
||||||
other,
|
other,
|
||||||
@ -307,7 +313,7 @@ impl<'a> System<'a> for Sys {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
metrics.entity_entity_collision_checks += 1;
|
entity_entity_collision_checks += 1;
|
||||||
|
|
||||||
const MIN_COLLISION_DIST: f32 = 0.3;
|
const MIN_COLLISION_DIST: f32 = 0.3;
|
||||||
let increments = ((previous_cache.velocity_dt
|
let increments = ((previous_cache.velocity_dt
|
||||||
@ -334,7 +340,7 @@ impl<'a> System<'a> for Sys {
|
|||||||
{
|
{
|
||||||
if !collided {
|
if !collided {
|
||||||
physics.touch_entities.push(*other);
|
physics.touch_entities.push(*other);
|
||||||
metrics.entity_entity_collisions += 1;
|
entity_entity_collisions += 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Don't apply repulsive force to projectiles
|
// Don't apply repulsive force to projectiles
|
||||||
@ -353,7 +359,12 @@ impl<'a> System<'a> for Sys {
|
|||||||
|
|
||||||
// Change velocity
|
// Change velocity
|
||||||
vel.0 += vel_delta * dt.0;
|
vel.0 += vel_delta * dt.0;
|
||||||
metrics
|
|
||||||
|
// Metrics
|
||||||
|
PhysicsMetrics {
|
||||||
|
entity_entity_collision_checks,
|
||||||
|
entity_entity_collisions,
|
||||||
|
}
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
.reduce(PhysicsMetrics::default, |old, new| PhysicsMetrics {
|
.reduce(PhysicsMetrics::default, |old, new| PhysicsMetrics {
|
||||||
@ -380,13 +391,19 @@ impl<'a> System<'a> for Sys {
|
|||||||
!&mountings,
|
!&mountings,
|
||||||
)
|
)
|
||||||
.par_join()
|
.par_join()
|
||||||
.fold(Vec::new, |
|
.map_init(
|
||||||
mut land_on_grounds,
|
|| {
|
||||||
|
prof_span!(guard, "physics e<>t rayon job");
|
||||||
|
guard
|
||||||
|
},
|
||||||
|
|_guard,
|
||||||
(entity, _scale, sticky, collider, mut pos, mut vel, _ori, mut physics_state, _),
|
(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() {
|
if sticky.is_some() && physics_state.on_surface().is_some() {
|
||||||
vel.0 = Vec3::zero();
|
vel.0 = Vec3::zero();
|
||||||
return land_on_grounds;
|
return landed_on_ground;
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Use this
|
// TODO: Use this
|
||||||
@ -593,7 +610,7 @@ impl<'a> System<'a> for Sys {
|
|||||||
on_ground = true;
|
on_ground = true;
|
||||||
|
|
||||||
if !was_on_ground {
|
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 {
|
} else if resolve_dir.z < 0.0 && vel.0.z >= 0.0 {
|
||||||
on_ceiling = true;
|
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| {
|
}).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.append(&mut land_on_grounds_b);
|
||||||
land_on_grounds_a
|
land_on_grounds_a
|
||||||
|
@ -16,12 +16,12 @@ pub fn admin_subcommand(
|
|||||||
match sub_m.subcommand() {
|
match sub_m.subcommand() {
|
||||||
("add", Some(sub_m)) => {
|
("add", Some(sub_m)) => {
|
||||||
if let Some(username) = sub_m.value_of("username") {
|
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)) => {
|
("remove", Some(sub_m)) => {
|
||||||
if let Some(username) = sub_m.value_of("username") {
|
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?
|
// TODO: can clap enforce this?
|
||||||
|
140
server-cli/src/cmd.rs
Normal file
140
server-cli/src/cmd.rs
Normal 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -1,6 +1,5 @@
|
|||||||
use crate::tuilog::TuiLog;
|
use crate::tuilog::TuiLog;
|
||||||
use termcolor::{ColorChoice, StandardStream};
|
use termcolor::{ColorChoice, StandardStream};
|
||||||
use tracing::Level;
|
|
||||||
use tracing_subscriber::{filter::LevelFilter, EnvFilter, FmtSubscriber};
|
use tracing_subscriber::{filter::LevelFilter, EnvFilter, FmtSubscriber};
|
||||||
#[cfg(feature = "tracy")]
|
#[cfg(feature = "tracy")]
|
||||||
use tracing_subscriber::{layer::SubscriberExt, prelude::*};
|
use tracing_subscriber::{layer::SubscriberExt, prelude::*};
|
||||||
@ -19,10 +18,10 @@ pub fn init(basic: bool) {
|
|||||||
.add_directive("hyper=info".parse().unwrap())
|
.add_directive("hyper=info".parse().unwrap())
|
||||||
.add_directive("prometheus_hyper=info".parse().unwrap())
|
.add_directive("prometheus_hyper=info".parse().unwrap())
|
||||||
.add_directive("mio::pool=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("h2=info".parse().unwrap())
|
||||||
.add_directive("tokio_util=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_network_protocol=info".parse().unwrap())
|
||||||
.add_directive(
|
.add_directive(
|
||||||
"veloren_server::persistence::character=info"
|
"veloren_server::persistence::character=info"
|
||||||
@ -32,7 +31,6 @@ pub fn init(basic: bool) {
|
|||||||
.add_directive(LevelFilter::INFO.into())
|
.add_directive(LevelFilter::INFO.into())
|
||||||
};
|
};
|
||||||
|
|
||||||
#[cfg(not(feature = "tracy"))]
|
|
||||||
let filter = match std::env::var_os(RUST_LOG_ENV).map(|s| s.into_string()) {
|
let filter = match std::env::var_os(RUST_LOG_ENV).map(|s| s.into_string()) {
|
||||||
Some(Ok(env)) => {
|
Some(Ok(env)) => {
|
||||||
let mut filter = base_exceptions(EnvFilter::new(""));
|
let mut filter = base_exceptions(EnvFilter::new(""));
|
||||||
@ -49,6 +47,7 @@ pub fn init(basic: bool) {
|
|||||||
|
|
||||||
#[cfg(feature = "tracy")]
|
#[cfg(feature = "tracy")]
|
||||||
tracing_subscriber::registry()
|
tracing_subscriber::registry()
|
||||||
|
.with(filter)
|
||||||
.with(tracing_tracy::TracyLayer::new().with_stackdepth(0))
|
.with(tracing_tracy::TracyLayer::new().with_stackdepth(0))
|
||||||
.init();
|
.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
|
// TODO: when tracing gets per Layer filters re-enable this when the tracy feature is being
|
||||||
// used (and do the same in voxygen)
|
// used (and do the same in voxygen)
|
||||||
{
|
{
|
||||||
let subscriber = FmtSubscriber::builder()
|
let subscriber = FmtSubscriber::builder().with_env_filter(filter);
|
||||||
.with_max_level(Level::ERROR)
|
|
||||||
.with_env_filter(filter);
|
|
||||||
|
|
||||||
if basic {
|
if basic {
|
||||||
subscriber
|
subscriber
|
||||||
|
@ -3,16 +3,16 @@
|
|||||||
#![feature(bool_to_option)]
|
#![feature(bool_to_option)]
|
||||||
|
|
||||||
mod admin;
|
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 logging;
|
||||||
mod settings;
|
mod settings;
|
||||||
mod shutdown_coordinator;
|
mod shutdown_coordinator;
|
||||||
mod tui_runner;
|
mod tui_runner;
|
||||||
mod tuilog;
|
mod tuilog;
|
||||||
|
|
||||||
use crate::{
|
use crate::{cmd::Message, shutdown_coordinator::ShutdownCoordinator, tui_runner::Tui};
|
||||||
shutdown_coordinator::ShutdownCoordinator,
|
|
||||||
tui_runner::{Message, Tui},
|
|
||||||
};
|
|
||||||
use clap::{App, Arg, SubCommand};
|
use clap::{App, Arg, SubCommand};
|
||||||
use common::clock::Clock;
|
use common::clock::Clock;
|
||||||
use common_base::span;
|
use common_base::span;
|
||||||
@ -201,6 +201,9 @@ fn main() -> io::Result<()> {
|
|||||||
Message::RemoveAdmin(username) => {
|
Message::RemoveAdmin(username) => {
|
||||||
server.remove_admin(&username);
|
server.remove_admin(&username);
|
||||||
},
|
},
|
||||||
|
Message::LoadArea(view_distance) => {
|
||||||
|
server.create_centered_persister(view_distance);
|
||||||
|
},
|
||||||
},
|
},
|
||||||
Err(mpsc::TryRecvError::Empty) | Err(mpsc::TryRecvError::Disconnected) => {},
|
Err(mpsc::TryRecvError::Empty) | Err(mpsc::TryRecvError::Disconnected) => {},
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
use crate::logging::LOG;
|
use crate::{cmd, logging::LOG, Message};
|
||||||
use crossterm::{
|
use crossterm::{
|
||||||
event::{DisableMouseCapture, EnableMouseCapture},
|
event::{DisableMouseCapture, EnableMouseCapture},
|
||||||
execute,
|
execute,
|
||||||
@ -12,7 +12,7 @@ use std::{
|
|||||||
},
|
},
|
||||||
time::Duration,
|
time::Duration,
|
||||||
};
|
};
|
||||||
use tracing::{debug, error, info, warn};
|
use tracing::{debug, error, warn};
|
||||||
use tui::{
|
use tui::{
|
||||||
backend::CrosstermBackend,
|
backend::CrosstermBackend,
|
||||||
layout::Rect,
|
layout::Rect,
|
||||||
@ -21,89 +21,6 @@ use tui::{
|
|||||||
Terminal,
|
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 struct Tui {
|
||||||
pub msg_r: mpsc::Receiver<Message>,
|
pub msg_r: mpsc::Receiver<Message>,
|
||||||
background: Option<std::thread::JoinHandle<()>>,
|
background: Option<std::thread::JoinHandle<()>>,
|
||||||
@ -129,7 +46,7 @@ impl Tui {
|
|||||||
},
|
},
|
||||||
KeyCode::Enter => {
|
KeyCode::Enter => {
|
||||||
debug!(?input, "tui mode: command entered");
|
debug!(?input, "tui mode: command entered");
|
||||||
parse_command(input, msg_s);
|
cmd::parse_command(input, msg_s);
|
||||||
|
|
||||||
*input = String::new();
|
*input = String::new();
|
||||||
},
|
},
|
||||||
@ -163,7 +80,7 @@ impl Tui {
|
|||||||
},
|
},
|
||||||
Ok(_) => {
|
Ok(_) => {
|
||||||
debug!(?line, "basic mode: command entered");
|
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);
|
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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
@ -305,14 +305,10 @@ impl Server {
|
|||||||
.min_by_key(|site_pos| site_pos.distance_squared(center_chunk))
|
.min_by_key(|site_pos| site_pos.distance_squared(center_chunk))
|
||||||
.unwrap_or(center_chunk);
|
.unwrap_or(center_chunk);
|
||||||
|
|
||||||
// calculate the absolute position of the chunk in the world
|
// Calculate the middle of the chunk in the world
|
||||||
// (we could add TerrainChunkSize::RECT_SIZE / 2 here, to spawn in the middle of
|
let spawn_wpos = TerrainChunkSize::center_wpos(spawn_chunk);
|
||||||
// the chunk)
|
|
||||||
let spawn_wpos = spawn_chunk.map2(TerrainChunkSize::RECT_SIZE, |e, sz| {
|
|
||||||
e as i32 * sz as i32 + sz as i32 / 2
|
|
||||||
});
|
|
||||||
|
|
||||||
// unwrapping because generate_chunk only returns err when should_continue evals
|
// Unwrapping because generate_chunk only returns err when should_continue evals
|
||||||
// to true
|
// to true
|
||||||
let (tc, _cs) = world.generate_chunk(index, spawn_chunk, || false).unwrap();
|
let (tc, _cs) = world.generate_chunk(index, spawn_chunk, || false).unwrap();
|
||||||
let min_z = tc.get_min_z();
|
let min_z = tc.get_min_z();
|
||||||
@ -345,7 +341,7 @@ impl Server {
|
|||||||
#[cfg(not(feature = "worldgen"))]
|
#[cfg(not(feature = "worldgen"))]
|
||||||
let spawn_point = Vec3::new(0.0, 0.0, 256.0);
|
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));
|
state.ecs_mut().insert(SpawnPoint(spawn_point));
|
||||||
|
|
||||||
// Insert the world into the ECS (todo: Maybe not an Arc?)
|
// 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
|
self.state.ecs().read_storage::<Client>().join().count() as i64
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: add Admin comp if ingame
|
|
||||||
pub fn add_admin(&self, username: &str) {
|
pub fn add_admin(&self, username: &str) {
|
||||||
let mut editable_settings = self.editable_settings_mut();
|
let mut editable_settings = self.editable_settings_mut();
|
||||||
let login_provider = self.state.ecs().fetch::<LoginProvider>();
|
let login_provider = self.state.ecs().fetch::<LoginProvider>();
|
||||||
let data_dir = self.data_dir();
|
let data_dir = self.data_dir();
|
||||||
add_admin(
|
if let Some(entity) = add_admin(
|
||||||
username,
|
username,
|
||||||
&login_provider,
|
&login_provider,
|
||||||
&mut editable_settings,
|
&mut editable_settings,
|
||||||
&data_dir.path,
|
&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) {
|
pub fn remove_admin(&self, username: &str) {
|
||||||
let mut editable_settings = self.editable_settings_mut();
|
let mut editable_settings = self.editable_settings_mut();
|
||||||
let login_provider = self.state.ecs().fetch::<LoginProvider>();
|
let login_provider = self.state.ecs().fetch::<LoginProvider>();
|
||||||
let data_dir = self.data_dir();
|
let data_dir = self.data_dir();
|
||||||
remove_admin(
|
if let Some(entity) = remove_admin(
|
||||||
username,
|
username,
|
||||||
&login_provider,
|
&login_provider,
|
||||||
&mut editable_settings,
|
&mut editable_settings,
|
||||||
&data_dir.path,
|
&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(
|
pub fn add_admin(
|
||||||
username: &str,
|
username: &str,
|
||||||
login_provider: &LoginProvider,
|
login_provider: &LoginProvider,
|
||||||
editable_settings: &mut EditableSettings,
|
editable_settings: &mut EditableSettings,
|
||||||
data_dir: &std::path::Path,
|
data_dir: &std::path::Path,
|
||||||
) {
|
) -> Option<common::uuid::Uuid> {
|
||||||
use crate::settings::EditableSetting;
|
use crate::settings::EditableSetting;
|
||||||
match login_provider.username_to_uuid(username) {
|
match login_provider.username_to_uuid(username) {
|
||||||
Ok(uuid) => editable_settings.admins.edit(data_dir, |admins| {
|
Ok(uuid) => editable_settings.admins.edit(data_dir, |admins| {
|
||||||
if admins.insert(uuid) {
|
if admins.insert(uuid) {
|
||||||
info!("Successfully added {} ({}) as an admin!", username, uuid);
|
info!("Successfully added {} ({}) as an admin!", username, uuid);
|
||||||
|
Some(uuid)
|
||||||
} else {
|
} else {
|
||||||
info!("{} ({}) is already an admin!", username, uuid);
|
info!("{} ({}) is already an admin!", username, uuid);
|
||||||
|
None
|
||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
Err(err) => error!(
|
Err(err) => {
|
||||||
?err,
|
error!(
|
||||||
"Could not find uuid for this name either the user does not exist or there was an \
|
?err,
|
||||||
error communicating with the auth server."
|
"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(
|
pub fn remove_admin(
|
||||||
username: &str,
|
username: &str,
|
||||||
login_provider: &LoginProvider,
|
login_provider: &LoginProvider,
|
||||||
editable_settings: &mut EditableSettings,
|
editable_settings: &mut EditableSettings,
|
||||||
data_dir: &std::path::Path,
|
data_dir: &std::path::Path,
|
||||||
) {
|
) -> Option<common::uuid::Uuid> {
|
||||||
use crate::settings::EditableSetting;
|
use crate::settings::EditableSetting;
|
||||||
match login_provider.username_to_uuid(username) {
|
match login_provider.username_to_uuid(username) {
|
||||||
Ok(uuid) => editable_settings.admins.edit(data_dir, |admins| {
|
Ok(uuid) => editable_settings.admins.edit(data_dir, |admins| {
|
||||||
@ -1003,14 +1039,19 @@ pub fn remove_admin(
|
|||||||
"Successfully removed {} ({}) from the admins",
|
"Successfully removed {} ({}) from the admins",
|
||||||
username, uuid
|
username, uuid
|
||||||
);
|
);
|
||||||
|
Some(uuid)
|
||||||
} else {
|
} else {
|
||||||
info!("{} ({}) is not an admin!", username, uuid);
|
info!("{} ({}) is not an admin!", username, uuid);
|
||||||
|
None
|
||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
Err(err) => error!(
|
Err(err) => {
|
||||||
?err,
|
error!(
|
||||||
"Could not find uuid for this name either the user does not exist or there was an \
|
?err,
|
||||||
error communicating with the auth server."
|
"Could not find uuid for this name either the user does not exist or there was an \
|
||||||
),
|
error communicating with the auth server."
|
||||||
|
);
|
||||||
|
None
|
||||||
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -63,6 +63,18 @@ pub trait StateExt {
|
|||||||
pos: comp::Pos,
|
pos: comp::Pos,
|
||||||
ori: comp::Ori,
|
ori: comp::Ori,
|
||||||
) -> EcsEntityBuilder;
|
) -> 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
|
/// Insert common/default components for a new character joining the server
|
||||||
fn initialize_character_data(&mut self, entity: EcsEntity, character_id: CharacterId);
|
fn initialize_character_data(&mut self, entity: EcsEntity, character_id: CharacterId);
|
||||||
/// Update the components associated with the entity's current character.
|
/// 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) {
|
fn initialize_character_data(&mut self, entity: EcsEntity, character_id: CharacterId) {
|
||||||
let spawn_point = self.ecs().read_resource::<SpawnPoint>().0;
|
let spawn_point = self.ecs().read_resource::<SpawnPoint>().0;
|
||||||
|
|
||||||
|
@ -23,6 +23,7 @@ use common::{
|
|||||||
util::Dir,
|
util::Dir,
|
||||||
vol::ReadVol,
|
vol::ReadVol,
|
||||||
};
|
};
|
||||||
|
use common_base::prof_span;
|
||||||
use common_ecs::{Job, Origin, ParMode, Phase, System};
|
use common_ecs::{Job, Origin, ParMode, Phase, System};
|
||||||
use rand::{thread_rng, Rng};
|
use rand::{thread_rng, Rng};
|
||||||
use rayon::iter::ParallelIterator;
|
use rayon::iter::ParallelIterator;
|
||||||
@ -141,8 +142,13 @@ impl<'a> System<'a> for Sys {
|
|||||||
.map(|ms| *ms == MountState::Unmounted)
|
.map(|ms| *ms == MountState::Unmounted)
|
||||||
.unwrap_or(true)
|
.unwrap_or(true)
|
||||||
})
|
})
|
||||||
.for_each(
|
.for_each_init(
|
||||||
|(
|
|| {
|
||||||
|
prof_span!(guard, "agent rayon job");
|
||||||
|
guard
|
||||||
|
},
|
||||||
|
|_guard,
|
||||||
|
(
|
||||||
entity,
|
entity,
|
||||||
(energy, health),
|
(energy, health),
|
||||||
pos,
|
pos,
|
||||||
|
@ -43,10 +43,10 @@ pub fn init(settings: &Settings) -> Vec<impl Drop> {
|
|||||||
env.add_directive("dot_vox::parser=warn".parse().unwrap())
|
env.add_directive("dot_vox::parser=warn".parse().unwrap())
|
||||||
.add_directive("gfx_device_gl=warn".parse().unwrap())
|
.add_directive("gfx_device_gl=warn".parse().unwrap())
|
||||||
.add_directive("prometheus_hyper=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("h2=info".parse().unwrap())
|
||||||
.add_directive("tokio_util=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_network_protocol=info".parse().unwrap())
|
||||||
.add_directive(
|
.add_directive(
|
||||||
"veloren_server::persistence::character=info"
|
"veloren_server::persistence::character=info"
|
||||||
|
Loading…
Reference in New Issue
Block a user