rust-mc-status 3.0.0

High-performance asynchronous Rust library for querying Minecraft server status (Java & Bedrock)
Documentation

crates.io Documentation MSRV MIT licensed Dependency Status downloads


Documentation: https://docs.rs/rust-mc-status

Source Code: https://github.com/NameOfShadow/rust-mc-status


๐ŸŽ‰ v3.0.0 โ€” Released 2026-07-01

A major rewrite focused on ergonomics, performance, and extensibility. Breaking changes from 2.x โ€” see the Migration Guide.

Headline changes:

  • Builder API โ€” McClient::builder().timeout(...).response_cache(...).build() replaces ad-hoc with_* chaining.
  • Per-request fluent pings โ€” client.java("addr").timeout(3s).await? with optional .is_online().
  • Response cache with in-flight deduplication โ€” repeated pings to the same server collapse into one network request; cache hits return in ~0 ms.
  • Two-level error hierarchy โ€” McError::Network(NetworkError::Timeout) etc. for fine-grained matching and retry policy.
  • Proxy support (feature = "proxy") โ€” SOCKS5 (with optional UDP for Bedrock) and HTTP CONNECT, with auth.
  • Tower integration (feature = "tower") โ€” McService + McRetryPolicy plug into the Tower middleware ecosystem.
  • Layer-based source tree โ€” client/, core/, models/, protocol/, proxy/, status/ for cleaner navigation.
  • Zero-cost defaults โ€” without optional features, no extra tower / pin-project / tokio-socks dependencies are pulled in.
  • 149 tests across 8 integration test files โ€” DNS parsing, Bedrock/Java decoders, error hierarchy, response cache, proxy config, MOTD formatting.

Full release notes and the migration guide are in CHANGELOG.md.


Features

  • Dual Protocol Support โ€” ping both Java Edition (port 25565) and Bedrock Edition (port 19132)
  • Ergonomic Builder API โ€” client.java("addr").timeout(3s).await? with per-request timeout override
  • Typed Status Wrappers โ€” JavaServerStatus and BedrockServerStatus with edition-specific methods
  • Facade Functions โ€” ping_java("addr").await? without creating a client
  • Tower Middleware (optional feature) โ€” rate limiting, retries, buffering, tracing via the Tower ecosystem
  • Async/Await โ€” built on Tokio for non-blocking, high-concurrency operation
  • Batch Queries โ€” ping multiple servers in parallel with configurable concurrency
  • LRU DNS Cache โ€” DNS and SRV lookups cached with automatic eviction (default 1024 entries)
  • SRV Record Support โ€” automatic SRV lookup for Java servers, mimicking the official client
  • JSON Serialisation โ€” all status types implement serde::Serialize; favicon and raw data excluded automatically
  • MOTD Formatting โ€” motd_clean() strips Minecraft ยง-color codes; truncate_str() safely slices Unicode
  • Zero-Panic Design โ€” all server-supplied data validated before use; no silent index panics
  • SmallVec Optimisation โ€” player sample, plugins, and mods use stack allocation for small lists
  • Zero-cost without Tower โ€” the default build pulls in no tower, async-trait, or pin-project dependency

Installation

[dependencies]
rust-mc-status = "3.0.0"
tokio = { version = "*", features = ["full"] }

To enable Tower middleware (rate limiting, retries, buffering, tracing):

rust-mc-status = { version = "3.0.0", features = ["tower"] }

Quick Start

One-liner (no client needed)

use rust_mc_status::{ping_java, ping_bedrock, StatusExt};

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let status = ping_java("mc.hypixel.net").await?;
    println!("{}", status);                    // "mc.hypixel.net [42 ms] 32000/200000 โ€” Hypixel Network"
    println!("{}", status.display_players());  // "32000/200000"
    println!("{}", status.motd_clean());       // MOTD without ยง-codes
    Ok(())
}

Client with custom settings

use rust_mc_status::{McClient, StatusExt};
use std::time::Duration;

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let client = McClient::new()
        .with_timeout(Duration::from_secs(10))
        .with_max_parallel(20)
        .with_cache_size(2048);

    // Typed Java status
    let java = client.java("mc.hypixel.net").await?;
    println!("Version : {}", java.version());
    println!("Players : {}", java.display_players());
    println!("Latency : {:.0} ms", java.latency_ms());
    println!("MOTD    : {}", java.motd_clean());

    // Per-request timeout override โ€” client config unchanged
    let fast = client.java("mc.hypixel.net")
        .timeout(Duration::from_secs(3))
        .await?;
    println!("Fast check: {} ms", fast.latency_ms() as u32);

    // Typed Bedrock status
    let bedrock = client.bedrock("geo.hivebedrock.network:19132").await?;
    println!("Edition : {}", bedrock.edition());
    println!("Players : {}", bedrock.display_players());

    Ok(())
}

Batch queries

use rust_mc_status::{McClient, ServerEdition, ServerInfo};

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let client = McClient::new();
    let servers = vec![
        ServerInfo { address: "mc.hypixel.net".into(),                edition: ServerEdition::Java },
        ServerInfo { address: "geo.hivebedrock.network:19132".into(), edition: ServerEdition::Bedrock },
    ];

    for (server, result) in client.ping_many(&servers).await {
        match result {
            Ok(s)  => println!("{}: online ({:.0} ms)", server.address, s.latency),
            Err(e) => println!("{}: {}", server.address, e),
        }
    }
    Ok(())
}

JSON serialisation

let status = client.java("mc.hypixel.net").await?;
let json = serde_json::to_string(&status)?;         // compact
let json = serde_json::to_string_pretty(&status)?;  // pretty-printed
// favicon and raw_data are excluded automatically

Runtime edition selection

let edition: ServerEdition = "java".parse()?;

// Full status
let status = client.server("mc.hypixel.net", edition).await?;

// Lightweight reachability check only
let online = client.server("mc.hypixel.net", edition)
    .timeout(Duration::from_secs(3))
    .is_online()
    .await;

Custom address types

struct ServerId(String);
impl From<ServerId> for String { fn from(s: ServerId) -> Self { s.0 } }

let status = client.java(ServerId("mc.hypixel.net".into())).await?;

Tower Middleware

Enable with features = ["tower"]. Converts McClient into a standard tower::Service that works with the entire Tower ecosystem โ€” rate limiting, retries, buffering, tower-http tracing, and any custom tower::Layer.

use tower::{ServiceBuilder, ServiceExt};
use rust_mc_status::{McClient, McRetryPolicy, PingRequestTower};
use std::time::Duration;

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let mut svc = ServiceBuilder::new()
        .buffer(64)                                      // Clone + concurrent
        .rate_limit(100, Duration::from_secs(1))         // 100 req/s
        .retry(McRetryPolicy::new(3))                    // 3 attempts on timeout/connection error
        .service(McClient::new().into_service());

    svc.ready().await?;
    let resp = svc.call(PingRequestTower::java("mc.hypixel.net")).await?;
    println!("{:.0} ms", resp.status.latency);
    Ok(())
}

Custom Tower Layer

Implement tower::Layer for logging, metrics, or any middleware:

use std::future::Future;
use std::pin::Pin;
use std::task::{Context, Poll};
use tower::{Layer, Service};
use rust_mc_status::{McError, PingRequestTower, PingResponseTower};

#[derive(Clone)]
struct LogLayer;

impl<S> Layer<S> for LogLayer {
    type Service = LogService<S>;
    fn layer(&self, inner: S) -> LogService<S> { LogService { inner } }
}

#[derive(Clone)]
struct LogService<S> { inner: S }

impl<S> Service<PingRequestTower> for LogService<S>
where
    S: Service<PingRequestTower, Response = PingResponseTower, Error = McError> + Send,
    S::Future: Send + 'static,
{
    type Response = PingResponseTower;
    type Error    = McError;
    type Future   = Pin<Box<dyn Future<Output = Result<PingResponseTower, McError>> + Send>>;

    fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll<Result<(), McError>> {
        self.inner.poll_ready(cx)
    }

    fn call(&mut self, req: PingRequestTower) -> Self::Future {
        println!("โ†’ {}", req.address);
        let fut = self.inner.call(req);
        Box::pin(async move {
            let resp = fut.await?;
            println!("{:.0} ms", resp.status.latency);
            Ok(resp)
        })
    }
}

// Usage
let mut svc = ServiceBuilder::new()
    .layer(LogLayer)
    .service(McClient::new().into_service());

SRV Record Lookup

When pinging a Java server without an explicit port, the library queries _minecraft._tcp.{hostname} for SRV records โ€” exactly as the official Minecraft client does. Results are cached for 5 minutes.

// SRV lookup performed automatically
let status = client.java("hypixel.net").await?;

// Explicit port โ€” SRV lookup skipped
let status = client.java("mc.hypixel.net:25565").await?;

MOTD Formatting

use rust_mc_status::strip_formatting;

let raw = "ยงaHypixel ยงc[1.8/1.21]";
println!("{}", strip_formatting(raw)); // "Hypixel [1.8/1.21]"

// Or via the wrapper method
let status = client.java("mc.hypixel.net").await?;
println!("{}", status.motd_clean()); // same result

Examples

Example Description
basic_usage Core API: facade, typed status, per-request timeout, serialisation
advanced_usage Batch queries, plugins, mods, favicon saving
cache_management LRU cache stats, warm/cold timing, clear_caches()
tower_usage Tower middleware: rate limiting, retries, buffer, custom Layer
srv_lookup_example SRV record behaviour
performance_test Benchmarking (run with --release)
cargo run --example basic_usage
cargo run --example cache_management
cargo run --example tower_usage --features tower
cargo run --example performance_test --release

API Reference

McClient builder methods

Method Description
new() Default settings (10 s timeout, 10 parallel, 1024 cache)
with_timeout(Duration) Global request timeout
with_max_parallel(usize) Max concurrent pings in ping_many
with_cache_size(usize) LRU cache capacity for DNS and SRV
into_service() (tower) Convert to McService for Tower middleware

Ping methods

Method Returns Notes
client.java(addr) JavaPingBuilder Supports .timeout(d).await
client.bedrock(addr) BedrockPingBuilder Supports .timeout(d).await
client.server(addr, edition) ServerPingBuilder Runtime edition; .is_online().await
client.ping_many(&[ServerInfo]) Vec<(ServerInfo, Result<ServerStatus>)> Parallel batch
ping_java(addr) Result<JavaServerStatus> Global client, no setup needed
ping_bedrock(addr) Result<BedrockServerStatus> Global client, no setup needed

Tower types (feature = "tower")

Type Description
McService tower::Service<PingRequestTower> โ€” wraps McClient
McRetryPolicy Retries on Timeout / ConnectionError; configurable attempt count
PingRequestTower Request type with .java(addr) / .bedrock(addr) constructors
PingResponseTower Response containing status: ServerStatus

JavaServerStatus methods

motd(), motd_clean(), version(), players_online(), players_max(), display_players(), favicon(), save_favicon(path), ip(), port(), hostname(), latency_ms(), is_online(), raw()

BedrockServerStatus methods

motd(), motd_clean(), motd2(), motd2_clean(), edition(), version(), players_online(), players_max(), display_players(), game_mode(), ip(), port(), hostname(), latency_ms(), is_online(), raw()

Utility functions

Function Description
strip_formatting(s) Remove Minecraft ยงX codes, collapse whitespace
truncate_str(s, n) Truncate to n Unicode characters (panic-safe)

Cache methods (all async)

cache_stats().await, clear_caches().await

Error Handling

use rust_mc_status::McError;

match client.java("server.com").await {
    Ok(s)                              => println!("online: {}", s.display_players()),
    Err(McError::Timeout)              => println!("timed out"),
    Err(McError::DnsError(msg))        => println!("DNS: {}", msg),
    Err(McError::ConnectionError(msg)) => println!("connection: {}", msg),
    Err(e)                             => println!("error: {}", e),
}

Performance Notes

  • DNS and SRV results cached for 5 minutes with LRU eviction โ€” no memory leaks
  • tokio::sync::RwLock allows concurrent readers with minimal contention
  • SmallVec for player sample (โ‰ค 12), plugins (โ‰ค 8), and mods (โ‰ค 8) โ€” stack-allocated for common case
  • Per-request .timeout() reduces tail latency in batch scenarios without touching the client
  • ping_bedrock uses UDP; ping_java uses TCP with TCP_NODELAY
  • Default build has zero tower/async-trait dependency โ€” only pulled in with features = ["tower"]

License

MIT โ€” see LICENSE.

Changelog

See CHANGELOG.md.

Version

Current version: 3.0.0 (released 2026-07-01) โ€” see CHANGELOG.md for full history and migration guide.