diff --git a/Cargo.lock b/Cargo.lock index a7a7a30..e9a516b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -163,6 +163,12 @@ version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a" +[[package]] +name = "base64" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "904dfeac50f3cdaba28fc6f57fdcddb75f49ed61346676a78c4ffe55877802fd" + [[package]] name = "bitflags" version = "1.3.2" @@ -750,6 +756,7 @@ name = "lazymc" version = "0.2.1" dependencies = [ "anyhow", + "base64", "bytes", "chrono", "clap", @@ -815,7 +822,7 @@ checksum = "308cc39be01b73d0d18f82a0e7b2a3df85245f84af96fdddc5d202d27e47b86a" [[package]] name = "minecraft-protocol" version = "0.1.0" -source = "git+https://github.com/timvisee/rust-minecraft-protocol?rev=a14b40e#a14b40ea9d9a9ed54a6f6546b6d19bc0db1b6c8c" +source = "git+https://github.com/timvisee/rust-minecraft-protocol?rev=356ea54#356ea5424374c5a7249be2f0f13fd3e0e2db5b58" dependencies = [ "byteorder", "minecraft-protocol-derive", @@ -828,7 +835,7 @@ dependencies = [ [[package]] name = "minecraft-protocol-derive" version = "0.0.0" -source = "git+https://github.com/timvisee/rust-minecraft-protocol?rev=a14b40e#a14b40ea9d9a9ed54a6f6546b6d19bc0db1b6c8c" +source = "git+https://github.com/timvisee/rust-minecraft-protocol?rev=356ea54#356ea5424374c5a7249be2f0f13fd3e0e2db5b58" dependencies = [ "proc-macro2", "quote", diff --git a/Cargo.toml b/Cargo.toml index 0870607..a5f4727 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -32,6 +32,7 @@ lobby = ["named-binary-tag", "quartz_nbt", "uuid"] [dependencies] anyhow = "1.0" +base64 = "0.13" bytes = "1.1" chrono = "0.4" clap = { version = "3.0.0-beta.5", default-features = false, features = [ "std", "cargo", "color", "env", "suggestions", "unicode" ]} @@ -41,7 +42,7 @@ dotenv = "0.15" flate2 = { version = "1.0", default-features = false, features = ["default"] } futures = { version = "0.3", default-features = false, features = ["executor"] } log = "0.4" -minecraft-protocol = { git = "https://github.com/timvisee/rust-minecraft-protocol", rev = "a14b40e" } +minecraft-protocol = { git = "https://github.com/timvisee/rust-minecraft-protocol", rev = "356ea54" } notify = "4.0" pretty_env_logger = "0.4" rand = "0.8" @@ -49,7 +50,7 @@ serde = "1.0" serde_json = "1.0" shlex = "1.1" thiserror = "1.0" -tokio = { version = "1", default-features = false, features = ["rt-multi-thread", "io-util", "net", "macros", "time", "process", "signal", "sync"] } +tokio = { version = "1", default-features = false, features = ["rt-multi-thread", "io-util", "net", "macros", "time", "process", "signal", "sync", "fs"] } toml = "0.5" version-compare = "0.1" diff --git a/README.md b/README.md index 49b68e3..2a82620 100644 --- a/README.md +++ b/README.md @@ -43,7 +43,7 @@ https://user-images.githubusercontent.com/856222/141378688-882082be-9efa-4cfe-81 - _Lobby: keep client in emulated server with lobby world, teleport to real server when ready ([experimental*](./docs/join-method-lobby.md))_ - Customizable MOTD and login messages - Automatically manages `server.properties` (host, port and RCON settings) -- Automatically handle banned IPs from server within `lazymc` +- Automatically block banned IPs from server within `lazymc` - Graceful server sleep/shutdown through RCON (with `SIGTERM` fallback on Linux/Unix) - Restart server on crash - Lockout mode diff --git a/src/status.rs b/src/status.rs index 3383cdc..6ef3010 100644 --- a/src/status.rs +++ b/src/status.rs @@ -8,6 +8,7 @@ use minecraft_protocol::encoder::Encoder; use minecraft_protocol::version::v1_14_4::handshake::Handshake; use minecraft_protocol::version::v1_14_4::login::LoginStart; use minecraft_protocol::version::v1_14_4::status::StatusResponse; +use tokio::fs; use tokio::io::AsyncWriteExt; use tokio::net::TcpStream; @@ -25,6 +26,9 @@ const BAN_MESSAGE_PREFIX: &str = "Your IP address is banned from this server.\nR /// Default ban reason if unknown. const DEFAULT_BAN_REASON: &str = "Banned by an operator."; +/// Server icon file path. +const SERVER_ICON_FILE: &str = "server-icon.png"; + /// Proxy the given inbound stream to a target address. // TODO: do not drop error here, return Box pub async fn serve( @@ -226,6 +230,13 @@ async fn server_status(config: &Config, server: &Server) -> ServerStatus { } }; + // Get server favicon + let favicon = if config.motd.from_server && status.is_some() { + status.as_ref().unwrap().favicon.clone() + } else { + favicon(&config).await + }; + // Build status resposne ServerStatus { version, @@ -235,5 +246,36 @@ async fn server_status(config: &Config, server: &Server) -> ServerStatus { max, sample: vec![], }, + favicon, } } + +/// Get server status favicon. +async fn favicon(config: &Config) -> Option { + // Get server dir + let dir = match config.server.directory.as_ref() { + Some(dir) => dir, + None => return None, + }; + + // Server icon file, ensure it exists + let path = dir.join(SERVER_ICON_FILE); + if !path.is_file() { + return None; + } + + // Read icon data + let data = fs::read(path) + .await + .map_err(|err| { + error!(target: "lazymc", "Failed to read favicon from {}: {}", SERVER_ICON_FILE, err); + }) + .ok()?; + + // Format and return favicon + Some(format!( + "{}{}", + "data:image/png;base64,", + base64::encode(data) + )) +}