use std::any::Any; use std::net::{Ipv4Addr, SocketAddr, SocketAddrV4}; use std::panic::{RefUnwindSafe, UnwindSafe}; use async_trait::async_trait; use tokio::runtime::Handle as TokioHandle; use crate::client::ClientMut; use crate::{Biome, Dimension, NewClientData, Server, Text, Ticks, WorldId, WorldsMut}; /// A trait containing callbacks which are invoked by the running Minecraft /// server. /// /// The config is used from multiple threads and must therefore implement /// `Send` and `Sync`. From within a single thread, methods are never invoked /// recursively by the library. In other words, a mutex can be aquired at /// the beginning of a method and released at the end without risk of /// deadlocking. /// /// This trait uses the [async_trait](https://docs.rs/async-trait/latest/async_trait/) attribute macro. /// This will be removed once `impl Trait` in return position in traits is /// available in stable rust. #[async_trait] #[allow(unused_variables)] pub trait Config: Any + Send + Sync + UnwindSafe + RefUnwindSafe { /// Called once at startup to get the maximum number of connections allowed /// to the server. Note that this includes all connections, not just those /// past the login stage. /// /// You will want this value to be somewhere above the maximum number of /// players, since status pings should still succeed even when the server is /// full. fn max_connections(&self) -> usize; /// Called once at startup to get the socket address the server will /// be bound to. /// /// # Default Implementation /// Returns `127.0.0.1:25565`. fn address(&self) -> SocketAddr { SocketAddrV4::new(Ipv4Addr::new(127, 0, 0, 1), 25565).into() } /// Called once at startup to get the tick rate, which is the number of game /// updates that should occur in one second. /// /// On each game update (tick), the server is expected to update game logic /// and respond to packets from clients. Once this is complete, the server /// will sleep for any remaining time until a full tick has passed. /// /// The tick rate must be greater than zero. /// /// Note that the official Minecraft client only processes packets at 20hz, /// so there is little benefit to a tick rate higher than 20. /// /// # Default Implementation /// Returns `20`, which is the same as Minecraft's official server. fn tick_rate(&self) -> Ticks { 20 } /// Called once at startup to get the "online mode" option, which determines /// if client authentication and encryption should take place. /// /// When online mode is disabled, malicious clients can give themselves any /// username and UUID they want, potentially gaining privileges they /// might not otherwise have. Additionally, encryption is only enabled in /// online mode. For these reasons online mode should only be disabled /// for development purposes and enabled on servers exposed to the /// internet. /// /// # Default Implementation /// Returns `true`. fn online_mode(&self) -> bool { true } /// Called once at startup to get the capacity of the buffer used to /// hold incoming packets. /// /// A larger capcity reduces the chance of packet loss but increases /// potential memory usage. /// /// # Default Implementation /// An unspecified value is returned that should be adequate in most /// situations. fn incoming_packet_capacity(&self) -> usize { 32 } /// Called once at startup to get the capacity of the buffer used to /// hold outgoing packets. /// /// A larger capcity reduces the chance of packet loss due to a full buffer /// but increases potential memory usage. /// /// # Default Implementation /// An unspecified value is returned that should be adequate in most /// situations. fn outgoing_packet_capacity(&self) -> usize { 512 } /// Called once at startup to get a handle to the tokio runtime the server /// will use. /// /// If a handle is not provided, the server will create its own tokio /// runtime. /// /// # Default Implementation /// Returns `None`. fn tokio_handle(&self) -> Option { None } /// Called once at startup to get the list of [`Dimension`]s usable on the /// server. /// /// The dimensions traversed by [`Server::dimensions`] will be in the same /// order as the `Vec` returned by this function. /// /// The number of elements in the returned `Vec` must be in \[1, u16::MAX]. /// Additionally, the documented requirements on the fields of [`Dimension`] /// must be met. /// /// # Default Implementation /// Returns `vec![Dimension::default()]`. fn dimensions(&self) -> Vec { vec![Dimension::default()] } /// Called once at startup to get the list of [`Biome`]s usable on the /// server. /// /// The biomes traversed by [`Server::biomes`] will be in the same /// order as the `Vec` returned by this function. /// /// The number of elements in the returned `Vec` must be in \[1, u16::MAX]. /// Additionally, the documented requirements on the fields of [`Biome`] /// must be met. /// /// # Default Implementation /// Returns `vec![Dimension::default()]`. fn biomes(&self) -> Vec { vec![Biome::default()] } /// Called when the server receives a Server List Ping query. /// Data for the response can be provided or the query can be ignored. /// /// This method is called from within a tokio runtime. /// /// # Default Implementation /// The query is ignored. async fn server_list_ping(&self, server: &Server, remote_addr: SocketAddr) -> ServerListPing { ServerListPing::Ignore } /// Called asynchronously for each client after successful authentication /// (if online mode is enabled) to determine if they can continue to join /// the server. On success, [`Config::join`] is called with the new /// client. If this method returns with `Err(reason)`, then the client is /// immediately disconnected with the given reason. /// /// This method is the appropriate place to perform asynchronous /// operations such as database queries which may take some time to /// complete. If you need access to the worlds on the server and don't need /// async, see [`Config::join`]. /// /// This method is called from within a tokio runtime. /// /// # Default Implementation /// The client is allowed to join unconditionally. async fn login(&self, server: &Server, ncd: &NewClientData) -> Result<(), Text> { Ok(()) } /// Called after a successful [`Config::login`] to determine what world the /// new client should join. If this method returns with `Err(reason)`, then /// the client is immediately disconnected with the given reason. /// /// If the returned [`WorldId`] is invalid, then the client is disconnected. /// /// This method is called from within a tokio runtime. fn join(&self, server: &Server, client: ClientMut, worlds: WorldsMut) -> Result; /// Called after the server is created, but prior to accepting connections /// and entering the update loop. /// /// This is useful for performing initialization work with a guarantee that /// no connections to the server will be made until this function returns. /// /// This method is called from within a tokio runtime. fn init(&self, server: &Server, worlds: WorldsMut) {} /// Called once at the beginning of every server update (also known as /// a "tick"). /// /// The frequency of server updates can be configured by `update_duration` /// in [`ServerConfig`]. /// /// This method is called from within a tokio runtime. /// /// # Default Implementation /// The default implementation does nothing. fn update(&self, server: &Server, worlds: WorldsMut); } /// The result of the [`server_list_ping`](Handler::server_list_ping) callback. #[derive(Debug)] pub enum ServerListPing<'a> { /// Responds to the server list ping with the given information. Respond { online_players: i32, max_players: i32, description: Text, /// The server's icon as the bytes of a PNG image. /// The image must be 64x64 pixels. /// /// No icon is used if the value is `None`. favicon_png: Option<&'a [u8]>, }, /// Ignores the query and disconnects from the client. Ignore, }