diff --git a/Cargo.lock b/Cargo.lock index 744918dae..8fd3b3c60 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -287,6 +287,22 @@ dependencies = [ "powerfmt", ] +[[package]] +name = "dhat" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "98cd11d84628e233de0ce467de10b8633f4ddaecafadefc86e13b84b8739b827" +dependencies = [ + "backtrace", + "lazy_static", + "mintex", + "parking_lot", + "rustc-hash", + "serde", + "serde_json", + "thousands", +] + [[package]] name = "digest" version = "0.10.7" @@ -785,6 +801,16 @@ version = "0.4.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "78b3ae25bc7c8c38cec158d1f2757ee79e9b3740fbc7ccf0e59e4b08d793fa89" +[[package]] +name = "lock_api" +version = "0.4.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07af8b9cdd281b7915f413fa73f29ebd5d55d0d3f0155584dade1ff18cea1b17" +dependencies = [ + "autocfg", + "scopeguard", +] + [[package]] name = "log" version = "0.4.22" @@ -829,6 +855,12 @@ dependencies = [ "simd-adler32", ] +[[package]] +name = "mintex" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9bec4598fddb13cc7b528819e697852653252b760f1228b7642679bf2ff2cd07" + [[package]] name = "mio" version = "1.0.1" @@ -1011,6 +1043,29 @@ dependencies = [ "vcpkg", ] +[[package]] +name = "parking_lot" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1bf18183cf54e8d6059647fc3063646a1801cf30896933ec2311622cc4b9a27" +dependencies = [ + "lock_api", + "parking_lot_core", +] + +[[package]] +name = "parking_lot_core" +version = "0.9.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e401f977ab385c9e4e3ab30627d6f26d00e2c73eef317493c4ec6d468726cf8" +dependencies = [ + "cfg-if", + "libc", + "redox_syscall", + "smallvec", + "windows-targets 0.52.6", +] + [[package]] name = "pem-rfc7468" version = "0.7.0" @@ -1129,6 +1184,7 @@ dependencies = [ "base64", "bytes", "crossbeam-channel", + "dhat", "digest 0.11.0-pre.9", "image", "log", @@ -1143,6 +1199,7 @@ dependencies = [ "pumpkin-text", "pumpkin-world", "rand", + "rayon", "reqwest", "rsa", "rsa-der", @@ -1300,6 +1357,15 @@ dependencies = [ "crossbeam-utils", ] +[[package]] +name = "redox_syscall" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2a908a6e00f1fdd0dfd9c0eb08ce85126f6d8bbda50017e74bc4a4b7d4a926a4" +dependencies = [ + "bitflags 2.6.0", +] + [[package]] name = "reqwest" version = "0.12.5" @@ -1393,6 +1459,12 @@ version = "0.1.24" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f" +[[package]] +name = "rustc-hash" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" + [[package]] name = "rustix" version = "0.38.34" @@ -1461,6 +1533,12 @@ dependencies = [ "windows-sys 0.52.0", ] +[[package]] +name = "scopeguard" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" + [[package]] name = "security-framework" version = "2.11.1" @@ -1721,6 +1799,12 @@ dependencies = [ "syn", ] +[[package]] +name = "thousands" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3bf63baf9f5039dadc247375c29eb13706706cfde997d0330d05aa63a77d8820" + [[package]] name = "time" version = "0.3.36" diff --git a/Cargo.toml b/Cargo.toml index 79cd76031..112838e17 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -7,9 +7,10 @@ version = "0.1.0" edition = "2021" [profile.release] -strip = true +debug = 1 lto = true codegen-units = 1 [workspace.dependencies] tokio = { version = "1.39.2", features = ["net", "macros", "rt-multi-thread", "fs", "io-util"] } +rayon = "1.10.0" diff --git a/pumpkin/Cargo.toml b/pumpkin/Cargo.toml index b4b956829..b8f29a314 100644 --- a/pumpkin/Cargo.toml +++ b/pumpkin/Cargo.toml @@ -4,7 +4,15 @@ version = "0.1.0-dev" description = "Empowering everyone to host fast and efficient Minecraft servers." edition = "2021" +[features] +default = [] +dhat-heap = ["dhat"] + [dependencies] +# memory analysis +dhat = { version = "0.3.3", optional = true } + +# pumpkin pumpkin-text = { path = "../pumpkin-text" } pumpkin-inventory = { path = "../pumpkin-inventory"} pumpkin-world = { path = "../pumpkin-world"} @@ -51,4 +59,5 @@ crossbeam-channel = "0.5.13" uuid = { version = "1.10", features = ["serde", "v3"]} -tokio.workspace = true \ No newline at end of file +tokio.workspace = true +rayon.workspace = true diff --git a/pumpkin/src/main.rs b/pumpkin/src/main.rs index 171971dc8..79a19884f 100644 --- a/pumpkin/src/main.rs +++ b/pumpkin/src/main.rs @@ -22,141 +22,156 @@ pub mod rcon; pub mod server; pub mod util; +#[cfg(feature = "dhat-heap")] +#[global_allocator] +static ALLOC: dhat::Alloc = dhat::Alloc; + #[cfg(not(target_os = "wasi"))] -#[tokio::main] -async fn main() -> io::Result<()> { - const SERVER: Token = Token(0); - use std::{cell::RefCell, time::Instant}; +fn main() -> io::Result<()> { + #[cfg(feature = "dhat-heap")] + let _profiler = dhat::Profiler::new_heap(); + #[cfg(feature = "dhat-heap")] + println!("Using a memory profiler"); + let rt = tokio::runtime::Builder::new_multi_thread() + .enable_all() + .build() + .unwrap(); + // ensure rayon is built outside of tokio scope + rayon::ThreadPoolBuilder::new().build_global().unwrap(); + rt.block_on(async { + const SERVER: Token = Token(0); + use std::{cell::RefCell, time::Instant}; - use rcon::RCONServer; + use rcon::RCONServer; - let time = Instant::now(); - let basic_config = BasicConfiguration::load("configuration.toml"); + let time = Instant::now(); + let basic_config = BasicConfiguration::load("configuration.toml"); - let advanced_configuration = AdvancedConfiguration::load("features.toml"); + let advanced_configuration = AdvancedConfiguration::load("features.toml"); - simple_logger::SimpleLogger::new().init().unwrap(); + simple_logger::SimpleLogger::new().init().unwrap(); - // Create a poll instance. - let mut poll = Poll::new()?; - // Create storage for events. - let mut events = Events::with_capacity(128); + // Create a poll instance. + let mut poll = Poll::new()?; + // Create storage for events. + let mut events = Events::with_capacity(128); - // Setup the TCP server socket. + // Setup the TCP server socket. - let addr = format!( - "{}:{}", - basic_config.server_address, basic_config.server_port - ) - .parse() - .unwrap(); + let addr = format!( + "{}:{}", + basic_config.server_address, basic_config.server_port + ) + .parse() + .unwrap(); - let mut listener = TcpListener::bind(addr)?; + let mut listener = TcpListener::bind(addr)?; - // Register the server with poll we can receive events for it. - poll.registry() - .register(&mut listener, SERVER, Interest::READABLE)?; + // Register the server with poll we can receive events for it. + poll.registry() + .register(&mut listener, SERVER, Interest::READABLE)?; - // Unique token for each incoming connection. - let mut unique_token = Token(SERVER.0 + 1); + // Unique token for each incoming connection. + let mut unique_token = Token(SERVER.0 + 1); - let use_console = advanced_configuration.commands.use_console; - let rcon = advanced_configuration.rcon.clone(); + let use_console = advanced_configuration.commands.use_console; + let rcon = advanced_configuration.rcon.clone(); - let mut connections: HashMap>> = HashMap::new(); + let mut connections: HashMap>> = HashMap::new(); - let mut server = Server::new((basic_config, advanced_configuration)); - log::info!("Started Server took {}ms", time.elapsed().as_millis()); - log::info!("You now can connect to the server"); + let mut server = Server::new((basic_config, advanced_configuration)); + log::info!("Started Server took {}ms", time.elapsed().as_millis()); + log::info!("You now can connect to the server"); - if use_console { - thread::spawn(move || { - let stdin = std::io::stdin(); - loop { - let mut out = String::new(); - stdin - .read_line(&mut out) - .expect("Failed to read console line"); - handle_command(&mut commands::CommandSender::Console, &out); - } - }); - } - if rcon.enabled { - tokio::spawn(async move { - RCONServer::new(&rcon).await.unwrap(); - }); - } - loop { - if let Err(err) = poll.poll(&mut events, None) { - if interrupted(&err) { - continue; - } - return Err(err); + if use_console { + thread::spawn(move || { + let stdin = std::io::stdin(); + loop { + let mut out = String::new(); + stdin + .read_line(&mut out) + .expect("Failed to read console line"); + handle_command(&mut commands::CommandSender::Console, &out); + } + }); } + if rcon.enabled { + tokio::spawn(async move { + RCONServer::new(&rcon).await.unwrap(); + }); + } + loop { + if let Err(err) = poll.poll(&mut events, None) { + if interrupted(&err) { + continue; + } + return Err(err); + } - for event in events.iter() { - match event.token() { - SERVER => loop { - // Received an event for the TCP server socket, which - // indicates we can accept an connection. - let (mut connection, address) = match listener.accept() { - Ok((connection, address)) => (connection, address), - Err(e) if e.kind() == io::ErrorKind::WouldBlock => { - // If we get a `WouldBlock` error we know our - // listener has no more incoming connections queued, - // so we can return to polling and wait for some - // more. - break; + for event in events.iter() { + match event.token() { + SERVER => loop { + // Received an event for the TCP server socket, which + // indicates we can accept an connection. + let (mut connection, address) = match listener.accept() { + Ok((connection, address)) => (connection, address), + Err(e) if e.kind() == io::ErrorKind::WouldBlock => { + // If we get a `WouldBlock` error we know our + // listener has no more incoming connections queued, + // so we can return to polling and wait for some + // more. + break; + } + Err(e) => { + // If it was any other kind of error, something went + // wrong and we terminate with an error. + return Err(e); + } + }; + if let Err(e) = connection.set_nodelay(true) { + log::warn!("failed to set TCP_NODELAY {e}"); } - Err(e) => { - // If it was any other kind of error, something went - // wrong and we terminate with an error. - return Err(e); - } - }; - if let Err(e) = connection.set_nodelay(true) { - log::warn!("failed to set TCP_NODELAY {e}"); - } - log::info!("Accepted connection from: {}", address); + log::info!("Accepted connection from: {}", address); - let token = next(&mut unique_token); - poll.registry().register( - &mut connection, - token, - Interest::READABLE.add(Interest::WRITABLE), - )?; - let rc_token = Rc::new(token); - let client = Rc::new(RefCell::new(Client::new( - Rc::clone(&rc_token), - connection, - addr, - ))); - server.add_client(rc_token, Rc::clone(&client)); - connections.insert(token, client); - }, + let token = next(&mut unique_token); + poll.registry().register( + &mut connection, + token, + Interest::READABLE.add(Interest::WRITABLE), + )?; + let rc_token = Rc::new(token); + let client = Rc::new(RefCell::new(Client::new( + Rc::clone(&rc_token), + connection, + addr, + ))); + server.add_client(rc_token, Rc::clone(&client)); + connections.insert(token, client); + }, - token => { - // Maybe received an event for a TCP connection. - let done = if let Some(client) = connections.get_mut(&token) { - let mut client = client.borrow_mut(); - client.poll(&mut server, event).await; - client.closed - } else { - // Sporadic events happen, we can safely ignore them. - false - }; - if done { - if let Some(client) = connections.remove(&token) { - server.remove_client(&token); + token => { + // Maybe received an event for a TCP connection. + let done = if let Some(client) = connections.get_mut(&token) { let mut client = client.borrow_mut(); - poll.registry().deregister(&mut client.connection)?; + client.poll(&mut server, event).await; + client.closed + } else { + // Sporadic events happen, we can safely ignore them. + false + }; + if done { + if let Some(client) = connections.remove(&token) { + server.remove_client(&token); + let mut client = client.borrow_mut(); + poll.registry().deregister(&mut client.connection)?; + } } } } } } - } + }) } fn next(current: &mut Token) -> Token {