diff --git a/common/sys/src/state.rs b/common/sys/src/state.rs index 9719fd3069..e60b63d2aa 100644 --- a/common/sys/src/state.rs +++ b/common/sys/src/state.rs @@ -5,8 +5,7 @@ use common::{ event::{EventBus, LocalEvent, ServerEvent}, metrics::{PhysicsMetrics, SysMetrics}, region::RegionMap, - resources, - resources::{DeltaTime, Time, TimeOfDay}, + resources::{DeltaTime, GameMode, Time, TimeOfDay}, span, terrain::{Block, TerrainChunk, TerrainGrid}, time::DayPeriod, @@ -90,22 +89,34 @@ pub struct State { impl State { /// Create a new `State` in client mode. - pub fn client() -> Self { Self::new(resources::GameMode::Client) } + pub fn client() -> Self { Self::new(GameMode::Client) } /// Create a new `State` in server mode. - pub fn server() -> Self { Self::new(resources::GameMode::Server) } + pub fn server() -> Self { Self::new(GameMode::Server) } - pub fn new(game_mode: resources::GameMode) -> Self { + pub fn new(game_mode: GameMode) -> Self { + let thread_name_infix = match game_mode { + GameMode::Server => "s", + GameMode::Client => "c", + GameMode::Singleplayer => "sp", + }; + + let thread_pool = Arc::new( + ThreadPoolBuilder::new() + .thread_name(move |i| format!("rayon-{}-{}", thread_name_infix, i)) + .build() + .unwrap(), + ); Self { ecs: Self::setup_ecs_world(game_mode), - thread_pool: Arc::new(ThreadPoolBuilder::new().build().unwrap()), + thread_pool, } } /// Creates ecs world and registers all the common components and resources // TODO: Split up registering into server and client (e.g. move // EventBus to the server) - fn setup_ecs_world(game_mode: resources::GameMode) -> specs::World { + fn setup_ecs_world(game_mode: GameMode) -> specs::World { let mut ecs = specs::World::new(); // Uids for sync ecs.register_sync_marker(); diff --git a/network/tests/helper.rs b/network/tests/helper.rs index bc3b1c58a6..46def7020d 100644 --- a/network/tests/helper.rs +++ b/network/tests/helper.rs @@ -22,7 +22,6 @@ pub fn setup(tracing: bool, sleep: u64) -> (u64, u64) { let _subscriber = if tracing { let filter = EnvFilter::from_default_env() .add_directive("trace".parse().unwrap()) - .add_directive("async_std::task::block_on=warn".parse().unwrap()) .add_directive("veloren_network::tests=trace".parse().unwrap()) .add_directive("veloren_network::controller=trace".parse().unwrap()) .add_directive("veloren_network::channel=trace".parse().unwrap()) diff --git a/server/src/connection_handler.rs b/server/src/connection_handler.rs index e8697991a3..e5db83c021 100644 --- a/server/src/connection_handler.rs +++ b/server/src/connection_handler.rs @@ -26,7 +26,7 @@ pub(crate) struct ConnectionHandler { /// to the Server main thread sometimes though to get the current server_info /// and time impl ConnectionHandler { - pub fn new(network: Network, runtime: Arc) -> Self { + pub fn new(network: Network, runtime: &Runtime) -> Self { let network = Arc::new(network); let network_clone = Arc::clone(&network); let (stop_sender, stop_receiver) = oneshot::channel(); diff --git a/server/src/lib.rs b/server/src/lib.rs index bffdf79430..c4c2e7e164 100644 --- a/server/src/lib.rs +++ b/server/src/lib.rs @@ -376,7 +376,7 @@ impl Server { .await }); runtime.block_on(network.listen(ProtocolAddr::Tcp(settings.gameserver_address)))?; - let connection_handler = ConnectionHandler::new(network, Arc::clone(&runtime)); + let connection_handler = ConnectionHandler::new(network, &runtime); // Initiate real-time world simulation #[cfg(feature = "worldgen")] diff --git a/voxygen/src/menu/main/client_init.rs b/voxygen/src/menu/main/client_init.rs index 07f1b41fd1..4dca251c8c 100644 --- a/voxygen/src/menu/main/client_init.rs +++ b/voxygen/src/menu/main/client_init.rs @@ -6,7 +6,7 @@ use crossbeam::channel::{unbounded, Receiver, Sender, TryRecvError}; use std::{ net::SocketAddr, sync::{ - atomic::{AtomicBool, Ordering}, + atomic::{AtomicBool, AtomicUsize, Ordering}, Arc, }, time::Duration, @@ -50,6 +50,7 @@ impl ClientInit { username: String, view_distance: Option, password: String, + runtime: Option>, ) -> Self { let (server_address, port, prefer_ipv6) = connection_args; @@ -58,15 +59,21 @@ impl ClientInit { let cancel = Arc::new(AtomicBool::new(false)); let cancel2 = Arc::clone(&cancel); - let cores = num_cpus::get(); - - let runtime = Arc::new( - runtime::Builder::new_multi_thread() - .enable_all() - .worker_threads(if cores > 4 { cores - 1 } else { cores }) - .build() - .unwrap(), - ); + let runtime = runtime.unwrap_or_else(|| { + let cores = num_cpus::get(); + Arc::new( + runtime::Builder::new_multi_thread() + .enable_all() + .worker_threads(if cores > 4 { cores - 1 } else { cores }) + .thread_name_fn(|| { + static ATOMIC_ID: AtomicUsize = AtomicUsize::new(0); + let id = ATOMIC_ID.fetch_add(1, Ordering::SeqCst); + format!("tokio-voxygen-{}", id) + }) + .build() + .unwrap(), + ) + }); let runtime2 = Arc::clone(&runtime); runtime.spawn(async move { @@ -85,9 +92,7 @@ impl ClientInit { break; } for socket_addr in &addresses { - match Client::new(socket_addr.clone(), view_distance, Arc::clone(&runtime2)) - .await - { + match Client::new(*socket_addr, view_distance, Arc::clone(&runtime2)).await { Ok(mut client) => { if let Err(e) = client .register(username, password, |auth_server| { @@ -140,23 +145,26 @@ impl ClientInit { } } + /// Parse ip address or resolves hostname. + /// Note: if you use an ipv6 address, the number after the last colon will + /// be used as the port unless you use [] around the address. async fn resolve( server_address: String, port: u16, prefer_ipv6: bool, ) -> Result, std::io::Error> { - //1. try if server_address already contains a port + // 1. try if server_address already contains a port if let Ok(addr) = server_address.parse::() { warn!("please don't add port directly to server_address"); return Ok(vec![addr]); } - //2, try server_address and port + // 2, try server_address and port if let Ok(addr) = format!("{}:{}", server_address, port).parse::() { return Ok(vec![addr]); } - //3. do DNS call + // 3. do DNS call let (mut first_addrs, mut second_addrs) = match lookup_host(server_address).await { Ok(s) => s.partition::, _>(|a| a.is_ipv6() == prefer_ipv6), Err(e) => { diff --git a/voxygen/src/menu/main/mod.rs b/voxygen/src/menu/main/mod.rs index 8567047987..341422c137 100644 --- a/voxygen/src/menu/main/mod.rs +++ b/voxygen/src/menu/main/mod.rs @@ -13,6 +13,8 @@ use crate::{ }; use client_init::{ClientInit, Error as InitError, Msg as InitMsg}; use common::{assets::AssetExt, comp, span}; +use std::sync::Arc; +use tokio::runtime; use tracing::error; use ui::{Event as MainMenuEvent, MainMenuUi}; @@ -63,14 +65,8 @@ impl PlayState for MainMenuState { #[cfg(feature = "singleplayer")] { if let Some(singleplayer) = &global_state.singleplayer { - if let Ok(result) = singleplayer.receiver.try_recv() { - if let Err(error) = result { - tracing::error!(?error, "Could not start server"); - global_state.singleplayer = None; - self.client_init = None; - self.main_menu_ui.cancel_connection(); - self.main_menu_ui.show_info(format!("Error: {:?}", error)); - } else { + match singleplayer.receiver.try_recv() { + Ok(Ok(runtime)) => { let server_settings = singleplayer.settings(); // Attempt login after the server is finished initializing attempt_login( @@ -81,8 +77,17 @@ impl PlayState for MainMenuState { server_settings.gameserver_address.ip().to_string(), server_settings.gameserver_address.port(), &mut self.client_init, + Some(runtime), ); - } + }, + Ok(Err(e)) => { + error!(?e, "Could not start server"); + global_state.singleplayer = None; + self.client_init = None; + self.main_menu_ui.cancel_connection(); + self.main_menu_ui.show_info(format!("Error: {:?}", e)); + }, + Err(_) => (), } } } @@ -245,6 +250,7 @@ impl PlayState for MainMenuState { server_address, DEFAULT_PORT, &mut self.client_init, + None, ); }, MainMenuEvent::CancelLoginAttempt => { @@ -270,7 +276,7 @@ impl PlayState for MainMenuState { }, #[cfg(feature = "singleplayer")] MainMenuEvent::StartSingleplayer => { - let singleplayer = Singleplayer::new(None); // TODO: Make client and server use the same thread pool + let singleplayer = Singleplayer::new(); global_state.singleplayer = Some(singleplayer); }, @@ -318,6 +324,7 @@ fn attempt_login( server_address: String, server_port: u16, client_init: &mut Option, + runtime: Option>, ) { if comp::Player::alias_is_valid(&username) { // Don't try to connect if there is already a connection in progress. @@ -327,6 +334,7 @@ fn attempt_login( username, Some(settings.graphics.view_distance), password, + runtime, )); } } else { diff --git a/voxygen/src/scene/figure/cache.rs b/voxygen/src/scene/figure/cache.rs index aa5d047e80..38a6f11086 100644 --- a/voxygen/src/scene/figure/cache.rs +++ b/voxygen/src/scene/figure/cache.rs @@ -312,7 +312,7 @@ where tick: u64, camera_mode: CameraMode, character_state: Option<&CharacterState>, - runtime: &Arc, + runtime: &Runtime, ) -> (FigureModelEntryLod<'c>, &'c Skel::Attr) where for<'a> &'a Skel::Body: Into, diff --git a/voxygen/src/scene/mod.rs b/voxygen/src/scene/mod.rs index 06a9b57af8..339917b173 100644 --- a/voxygen/src/scene/mod.rs +++ b/voxygen/src/scene/mod.rs @@ -36,7 +36,6 @@ use common_sys::state::State; use comp::item::Reagent; use num::traits::{Float, FloatConst}; use specs::{Entity as EcsEntity, Join, WorldExt}; -use std::sync::Arc; use tokio::runtime::Runtime; use vek::*; @@ -116,7 +115,7 @@ pub struct SceneData<'a> { pub loaded_distance: f32, pub view_distance: u32, pub tick: u64, - pub runtime: &'a Arc, + pub runtime: &'a Runtime, pub gamma: f32, pub exposure: f32, pub ambiance: f32, diff --git a/voxygen/src/scene/simple.rs b/voxygen/src/scene/simple.rs index 9e4838ad25..6dad94e70b 100644 --- a/voxygen/src/scene/simple.rs +++ b/voxygen/src/scene/simple.rs @@ -29,7 +29,6 @@ use common::{ terrain::BlockKind, vol::{BaseVol, ReadVol}, }; -use std::sync::Arc; use tokio::runtime::Runtime; use tracing::error; use vek::*; @@ -98,7 +97,7 @@ pub struct SceneData<'a> { pub time: f64, pub delta_time: f32, pub tick: u64, - pub runtime: &'a Arc, + pub runtime: &'a Runtime, pub body: Option, pub gamma: f32, pub exposure: f32, diff --git a/voxygen/src/scene/terrain.rs b/voxygen/src/scene/terrain.rs index 3b143283a2..8767b639cd 100644 --- a/voxygen/src/scene/terrain.rs +++ b/voxygen/src/scene/terrain.rs @@ -638,7 +638,11 @@ impl Terrain { // Limit ourselves to u16::MAX even if larger textures are supported. let max_texture_size = renderer.max_texture_size(); - let cores = num_cpus::get(); + let meshing_cores = match num_cpus::get() as u64 { + n if n < 4 => 1, + n if n < 8 => n - 3, + n => n - 4, + }; span!(guard, "Queue meshing from todo list"); for (todo, chunk) in self @@ -655,7 +659,7 @@ impl Terrain { .cloned()?)) }) { - if self.mesh_todos_active.load(Ordering::Relaxed) > (cores as u64 - 1).max(1) { + if self.mesh_todos_active.load(Ordering::Relaxed) > meshing_cores { break; } diff --git a/voxygen/src/singleplayer.rs b/voxygen/src/singleplayer.rs index 8c2b76baff..78b20aa47c 100644 --- a/voxygen/src/singleplayer.rs +++ b/voxygen/src/singleplayer.rs @@ -1,29 +1,25 @@ -use client::Client; use common::clock::Clock; use crossbeam::channel::{bounded, unbounded, Receiver, Sender, TryRecvError}; use server::{Error as ServerError, Event, Input, Server}; use std::{ sync::{ - atomic::{AtomicBool, Ordering}, + atomic::{AtomicBool, AtomicUsize, Ordering}, Arc, }, thread::{self, JoinHandle}, time::Duration, }; -use tracing::{error, info, warn, trace, debug}; +use tokio::runtime::Runtime; +use tracing::{debug, error, info, trace, warn}; const TPS: u64 = 30; -enum Msg { - Stop, -} - /// Used to start and stop the background thread running the server /// when in singleplayer mode. pub struct Singleplayer { _server_thread: JoinHandle<()>, - sender: Sender, - pub receiver: Receiver>, + stop_server_s: Sender<()>, + pub receiver: Receiver, ServerError>>, // Wether the server is stopped or not paused: Arc, // Settings that the server was started with @@ -31,8 +27,9 @@ pub struct Singleplayer { } impl Singleplayer { - pub fn new(client: Option<&Client>) -> Self { - let (sender, receiver) = unbounded(); + #[allow(clippy::new_without_default)] + pub fn new() -> Self { + let (stop_server_s, stop_server_r) = unbounded(); // Determine folder to save server data in let server_data_dir = { @@ -81,19 +78,21 @@ impl Singleplayer { let settings = server::Settings::singleplayer(&server_data_dir); let editable_settings = server::EditableSettings::singleplayer(&server_data_dir); - let runtime = if let Some(c) = client { - Arc::clone(&c.runtime()) - } else { - let cores = num_cpus::get(); - debug!("creating a new runtime for server"); - Arc::new( - tokio::runtime::Builder::new_multi_thread() - .enable_all() - .worker_threads(if cores > 4 { cores - 1 } else { cores }) - .build() - .unwrap(), - ) - }; + let cores = num_cpus::get(); + debug!("Creating a new runtime for server"); + let runtime = Arc::new( + tokio::runtime::Builder::new_multi_thread() + .enable_all() + .worker_threads(if cores > 4 { cores - 1 } else { cores }) + .thread_name_fn(|| { + static ATOMIC_ID: AtomicUsize = AtomicUsize::new(0); + let id = ATOMIC_ID.fetch_add(1, Ordering::SeqCst); + format!("tokio-sp-{}", id) + }) + .build() + .unwrap(), + ); + let settings2 = settings.clone(); let paused = Arc::new(AtomicBool::new(false)); @@ -107,10 +106,15 @@ impl Singleplayer { trace!("starting singleplayer server thread"); let mut server = None; if let Err(e) = result_sender.send( - match Server::new(settings2, editable_settings, &server_data_dir, runtime) { + match Server::new( + settings2, + editable_settings, + &server_data_dir, + Arc::clone(&runtime), + ) { Ok(s) => { server = Some(s); - Ok(()) + Ok(runtime) }, Err(e) => Err(e), }, @@ -128,14 +132,14 @@ impl Singleplayer { None => return, }; - run_server(server, receiver, paused1); + run_server(server, stop_server_r, paused1); trace!("ending singleplayer server thread"); }) .unwrap(); Singleplayer { _server_thread: thread, - sender, + stop_server_s, receiver: result_receiver, paused, settings, @@ -156,11 +160,11 @@ impl Singleplayer { impl Drop for Singleplayer { fn drop(&mut self) { // Ignore the result - let _ = self.sender.send(Msg::Stop); + let _ = self.stop_server_s.send(()); } } -fn run_server(mut server: Server, rec: Receiver, paused: Arc) { +fn run_server(mut server: Server, stop_server_r: Receiver<()>, paused: Arc) { info!("Starting server-cli..."); // Set up an fps clock @@ -168,14 +172,10 @@ fn run_server(mut server: Server, rec: Receiver, paused: Arc) { loop { // Check any event such as stopping and pausing - match rec.try_recv() { - Ok(msg) => match msg { - Msg::Stop => break, - }, - Err(err) => match err { - TryRecvError::Empty => (), - TryRecvError::Disconnected => break, - }, + match stop_server_r.try_recv() { + Ok(()) => break, + Err(TryRecvError::Disconnected) => break, + Err(TryRecvError::Empty) => (), } // Wait for the next tick.