Pumpkin/pumpkin/src/main.rs
Alexander Medvedev c1b6044457 Parse SocketAddr
this allows for Ipv6 addresses
2025-08-16 17:02:25 +02:00

210 lines
6.1 KiB
Rust

#![deny(clippy::all)]
#![deny(clippy::pedantic)]
// #![warn(clippy::restriction)]
#![deny(clippy::cargo)]
// to keep consistency
#![deny(clippy::if_then_some_else_none)]
#![deny(clippy::empty_enum_variants_with_brackets)]
#![deny(clippy::empty_structs_with_brackets)]
#![deny(clippy::separated_literal_suffix)]
#![deny(clippy::semicolon_outside_block)]
#![deny(clippy::non_zero_suggestions)]
#![deny(clippy::string_lit_chars_any)]
#![deny(clippy::use_self)]
#![deny(clippy::useless_let_if_seq)]
#![deny(clippy::branches_sharing_code)]
#![deny(clippy::equatable_if_let)]
#![deny(clippy::option_if_let_else)]
// use log crate
#![deny(clippy::print_stdout)]
#![deny(clippy::print_stderr)]
// REMOVE SOME WHEN RELEASE
#![expect(clippy::cargo_common_metadata)]
#![expect(clippy::cast_precision_loss)]
#![expect(clippy::multiple_crate_versions)]
#![expect(clippy::single_call_fn)]
#![expect(clippy::cast_sign_loss)]
#![expect(clippy::cast_possible_truncation)]
#![expect(clippy::cast_possible_wrap)]
#![expect(clippy::missing_panics_doc)]
#![expect(clippy::missing_errors_doc)]
#![expect(clippy::module_name_repetitions)]
#![expect(clippy::struct_excessive_bools)]
// Don't warn on event sending macros
#![expect(unused_labels)]
#[cfg(target_os = "wasi")]
compile_error!("Compiling for WASI targets is not supported!");
use plugin::PluginManager;
use pumpkin_config::BASIC_CONFIG;
use pumpkin_data::packet::CURRENT_MC_PROTOCOL;
use std::{
io::{self},
sync::{Arc, LazyLock},
};
#[cfg(not(unix))]
use tokio::signal::ctrl_c;
#[cfg(unix)]
use tokio::signal::unix::{SignalKind, signal};
use tokio::sync::RwLock;
use pumpkin::{LOGGER_IMPL, PumpkinServer, SHOULD_STOP, STOP_INTERRUPT, init_log, stop_server};
use pumpkin_util::{
permission::{PermissionManager, PermissionRegistry},
text::{TextComponent, color::NamedColor},
};
use std::time::Instant;
// Setup some tokens to allow us to identify which event is for which socket.
pub mod block;
pub mod command;
pub mod data;
pub mod entity;
pub mod error;
pub mod item;
pub mod net;
pub mod plugin;
pub mod server;
pub mod world;
#[cfg(feature = "dhat-heap")]
#[global_allocator]
static ALLOC: dhat::Alloc = dhat::Alloc;
#[cfg(feature = "dhat-heap")]
use pumpkin::HEAP_PROFILER;
pub static PLUGIN_MANAGER: LazyLock<Arc<PluginManager>> =
LazyLock::new(|| Arc::new(PluginManager::new()));
pub static PERMISSION_REGISTRY: LazyLock<Arc<RwLock<PermissionRegistry>>> =
LazyLock::new(|| Arc::new(RwLock::new(PermissionRegistry::new())));
pub static PERMISSION_MANAGER: LazyLock<Arc<RwLock<PermissionManager>>> = LazyLock::new(|| {
Arc::new(RwLock::new(PermissionManager::new(
PERMISSION_REGISTRY.clone(),
)))
});
const CARGO_PKG_VERSION: &str = env!("CARGO_PKG_VERSION");
// WARNING: All rayon calls from the tokio runtime must be non-blocking! This includes things
// like `par_iter`. These should be spawned in the the rayon pool and then passed to the tokio
// runtime with a channel! See `Level::fetch_chunks` as an example!
#[tokio::main]
async fn main() {
#[cfg(feature = "console-subscriber")]
console_subscriber::init();
#[cfg(feature = "dhat-heap")]
{
let profiler = dhat::Profiler::new_heap();
let mut static_loc = HEAP_PROFILER.lock().await;
*static_loc = Some(profiler);
};
let time = Instant::now();
init_log!();
let default_panic = std::panic::take_hook();
std::panic::set_hook(Box::new(move |info| {
if let Some((wrapper, _)) = LOGGER_IMPL.as_ref() {
// Drop readline to reset terminal state
let _ = wrapper.take_readline();
}
default_panic(info);
// TODO: Gracefully exit?
// We need to abide by the panic rules here.
std::process::exit(1);
}));
log::info!("Starting Pumpkin {CARGO_PKG_VERSION} Minecraft (Protocol {CURRENT_MC_PROTOCOL})",);
log::debug!(
"Build info: FAMILY: \"{}\", OS: \"{}\", ARCH: \"{}\", BUILD: \"{}\"",
std::env::consts::FAMILY,
std::env::consts::OS,
std::env::consts::ARCH,
if cfg!(debug_assertions) {
"Debug"
} else {
"Release"
}
);
log::warn!("Pumpkin is currently under heavy development!");
log::info!("Report issues on https://github.com/Pumpkin-MC/Pumpkin/issues");
log::info!("Join our Discord for community support: https://discord.com/invite/wT8XjrjKkf");
tokio::spawn(async {
setup_sighandler()
.await
.expect("Unable to setup signal handlers");
});
let pumpkin_server = PumpkinServer::new().await;
pumpkin_server.init_plugins().await;
log::info!("Started server; took {}ms", time.elapsed().as_millis());
log::info!(
"Server is now running. Connect using port: {}{}{}",
if BASIC_CONFIG.java_edition {
format!("Java Edition: {}", BASIC_CONFIG.java_edition_address)
} else {
String::new()
},
if BASIC_CONFIG.java_edition && BASIC_CONFIG.bedrock_edition {
" | " // Separator if both are enabled
} else {
""
},
if BASIC_CONFIG.bedrock_edition {
format!("Bedrock Edition: {}", BASIC_CONFIG.bedrock_edition_address)
} else {
String::new()
}
);
pumpkin_server.start().await;
log::info!("The server has stopped.");
}
fn handle_interrupt() {
log::warn!(
"{}",
TextComponent::text("Received interrupt signal; stopping server...")
.color_named(NamedColor::Red)
.to_pretty_console()
);
stop_server();
}
// Non-UNIX Ctrl-C handling
#[cfg(not(unix))]
async fn setup_sighandler() -> io::Result<()> {
if ctrl_c().await.is_ok() {
handle_interrupt();
}
Ok(())
}
// Unix signal handling
#[cfg(unix)]
async fn setup_sighandler() -> io::Result<()> {
if signal(SignalKind::interrupt())?.recv().await.is_some() {
handle_interrupt();
}
if signal(SignalKind::hangup())?.recv().await.is_some() {
handle_interrupt();
}
if signal(SignalKind::terminate())?.recv().await.is_some() {
handle_interrupt();
}
Ok(())
}