Skip to content

Commit

Permalink
Merge pull request #143 from kralverde/icon
Browse files Browse the repository at this point in the history
Fix server icons + add server icon to config
  • Loading branch information
Snowiiii authored Oct 18, 2024
2 parents 5ac45b0 + 037315b commit 9d55896
Show file tree
Hide file tree
Showing 5 changed files with 69 additions and 22 deletions.
File renamed without changes
16 changes: 16 additions & 0 deletions docs/config/basic.md
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,22 @@ The server's description displayed on the status screen.
motd=true
```

## Use favicon

Whether to use a server favicon or not

```toml
use_favicon=true
```

## Favicon path

The path to the server's favicon

```toml
favicon_path=./icon.png
```

## Default gamemode

The default game mode for players
Expand Down
8 changes: 8 additions & 0 deletions pumpkin-config/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,12 @@ pub struct BasicConfiguration {
/// Whether to remove IPs from logs or not
#[serde_inline_default(true)]
pub scrub_ips: bool,
/// Whether to use a server favicon
#[serde_inline_default(true)]
pub use_favicon: bool,
/// Path to server favicon
#[serde_inline_default("icon.png".to_string())]
pub favicon_path: String,
}

fn default_server_address() -> SocketAddr {
Expand All @@ -119,6 +125,8 @@ impl Default for BasicConfiguration {
motd: "A Blazing fast Pumpkin Server!".to_string(),
default_gamemode: GameMode::Survival,
scrub_ips: true,
use_favicon: true,
favicon_path: "icon.png".to_string(),
}
}
}
Expand Down
2 changes: 1 addition & 1 deletion pumpkin/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ thiserror = "1.0"

# icon loading
base64 = "0.22.1"
png = "0.17.14"
png = "0.17.14"

# logging
simple_logger = { version = "5.0.0", features = ["threads"] }
Expand Down
65 changes: 44 additions & 21 deletions pumpkin/src/server/connection_cache.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,10 @@
use std::{fs::File, path::Path};
use core::error;
use std::{
fs::File,
io::{Cursor, Read},
path::Path,
sync::LazyLock,
};

use base64::{engine::general_purpose, Engine as _};
use pumpkin_config::{BasicConfiguration, BASIC_CONFIG};
Expand All @@ -9,6 +15,29 @@ use pumpkin_protocol::{

use super::CURRENT_MC_VERSION;

static DEFAULT_ICON: LazyLock<&[u8]> =
LazyLock::new(|| include_bytes!("../../../assets/default_icon.png"));

fn load_icon_from_file<P: AsRef<Path>>(path: P) -> Result<String, Box<dyn error::Error>> {
let mut icon_file = File::open(path)?;
let mut buf = Vec::new();
icon_file.read_to_end(&mut buf)?;
load_icon_from_bytes(&buf)
}

fn load_icon_from_bytes(png_data: &[u8]) -> Result<String, Box<dyn error::Error>> {
let icon = png::Decoder::new(Cursor::new(&png_data));
let reader = icon.read_info()?;
let info = reader.info();
assert!(info.width == 64, "Icon width must be 64");
assert!(info.height == 64, "Icon height must be 64");

// Reader consumes the image. Once we verify dimensions, we want to encode the entire raw image
let mut result = "data:image/png;base64,".to_owned();
general_purpose::STANDARD.encode_string(png_data, &mut result);
Ok(result)
}

pub struct CachedStatus {
_status_response: StatusResponse,
// We cache the json response here so we don't parse it every time someone makes a Status request.
Expand Down Expand Up @@ -57,10 +86,21 @@ impl CachedStatus {
}

pub fn build_response(config: &BasicConfiguration) -> StatusResponse {
let icon_path = "/icon.png";
let icon = if Path::new(icon_path).exists() {
Some(Self::load_icon(icon_path))
let icon = if config.use_favicon {
let icon_path = &config.favicon_path;
log::info!("Loading server favicon from '{}'", icon_path);
match load_icon_from_file(icon_path).or_else(|err| {
log::warn!("Failed to load icon from '{}': {}", icon_path, err);
load_icon_from_bytes(DEFAULT_ICON.as_ref())
}) {
Ok(result) => Some(result),
Err(err) => {
log::warn!("Failed to load default icon: {}", err);
None
}
}
} else {
log::info!("Not using a server favicon");
None
};

Expand All @@ -82,21 +122,4 @@ impl CachedStatus {
enforce_secure_chat: false,
}
}

fn load_icon<P: AsRef<Path>>(path: P) -> String {
let icon = png::Decoder::new(File::open(path).expect("Failed to load icon"));
let mut reader = icon.read_info().unwrap();
let info = reader.info();
assert!(info.width == 64, "Icon width must be 64");
assert!(info.height == 64, "Icon height must be 64");
// Allocate the output buffer.
let mut buf = vec![0; reader.output_buffer_size()];
// Read the next frame. An APNG might contain multiple frames.
let info = reader.next_frame(&mut buf).unwrap();
// Grab the bytes of the image.
let bytes = &buf[..info.buffer_size()];
let mut result = "data:image/png;base64,".to_owned();
general_purpose::STANDARD.encode_string(bytes, &mut result);
result
}
}

0 comments on commit 9d55896

Please sign in to comment.