210 lines
6.1 KiB
Rust
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(())
|
|
}
|