<div align="center">
<img src="assets/logo.svg" alt="rust-mc-status logo" width="160" height="160">
<h1>rust-mc-status</h1>
<p>
<strong>Высокопроизводительный асинхронный клиент статуса серверов Minecraft для Rust — Java и Bedrock Edition.</strong>
</p>
<p>
[](https://crates.io/crates/rust-mc-status)
[](https://docs.rs/rust-mc-status/3.0.0)


[](https://deps.rs/crate/rust-mc-status/3.0.0)
<br />

</p>
</div>
---
**Документация**: [https://docs.rs/rust-mc-status](https://docs.rs/rust-mc-status/3.0.0)
**Исходный код**: [https://github.com/NameOfShadow/rust-mc-status](https://github.com/NameOfShadow/rust-mc-status)
---
## 🎉 v3.0.0 — выпуск 2026-07-01
Крупный рефакторинг с акцентом на эргономику, производительность и расширяемость. **Ломающие изменения** относительно 2.x — см. [руководство по миграции](CHANGELOG.md#migration-guide-2x--300).
**Главное:**
- **Builder API** — `McClient::builder().timeout(...).response_cache(...).build()` вместо разрозненных `with_*`.
- **Fluent-пинги** — `client.java("addr").timeout(3s).await?` и `.is_online()` на каждый запрос.
- **Response-кэш с дедупликацией in-flight запросов** — одновременные пинги одного сервера сходятся в один сетевой запрос; попадание в кэш — ~0 мс.
- **Двухуровневая иерархия ошибок** — `McError::Network(NetworkError::Timeout)` и т.п. для точного матчинга и retry-политик.
- **Поддержка прокси** *(feature = "proxy")* — SOCKS5 (опционально с UDP для Bedrock) и HTTP CONNECT, с авторизацией.
- **Tower-интеграция** *(feature = "tower")* — `McService` и `McRetryPolicy` встраиваются в экосистему Tower.
- **Слоистая структура `src/`** — `client/`, `core/`, `models/`, `protocol/`, `proxy/`, `status/` для удобной навигации.
- **Без оверхеда по умолчанию** — без опциональных фич не подтягиваются tower, pin-project и tokio-socks.
- **149 тестов в 8 интеграционных файлах** — парсинг DNS, декодеры Bedrock/Java, иерархия ошибок, response-кэш, конфиг прокси, форматирование MOTD.
Полные релиз-ноты и руководство по миграции — в [CHANGELOG.md](CHANGELOG.md).
---
## Возможности
- **Поддержка двух протоколов** — пинг Java Edition (порт `25565`) и Bedrock Edition (порт `19132`)
- **Удобный Builder API** — `client.java("addr").timeout(3s).await?` с таймаутом на уровне запроса
- **Типизированные обёртки** — `JavaServerStatus` и `BedrockServerStatus` с методами для каждой редакции
- **Фасадные функции** — `ping_java("addr").await?` без создания клиента
- **Tower Middleware** *(опциональная фича)* — rate limiting, retry, буферизация, трейсинг через экосистему Tower
- **Async/Await** — построена на Tokio для неблокирующих операций и высокой параллельности
- **Массовые запросы** — параллельная проверка множества серверов с настраиваемым лимитом
- **LRU DNS-кэш** — DNS и SRV-записи кэшируются с автоматическим вытеснением (по умолчанию 1024 записи)
- **Поддержка SRV-записей** — автоматический SRV-запрос для Java-серверов, как в официальном клиенте
- **JSON-сериализация** — все типы статуса реализуют `serde::Serialize`; favicon и raw data исключены автоматически
- **Форматирование MOTD** — `motd_clean()` убирает `§`-коды; `truncate_str()` безопасно обрезает Unicode
- **Zero-Panic дизайн** — все данные от сервера валидируются перед использованием
- **SmallVec оптимизация** — список игроков, плагинов и модов хранится на стеке для малых размеров
- **Zero-cost без Tower** — стандартная сборка не тянет tower, async-trait и pin-project
## Установка
```toml
[dependencies]
rust-mc-status = "3.0.0"
tokio = { version = "*", features = ["full"] }
```
Для Tower middleware (rate limiting, retry, буферизация, трейсинг):
```toml
rust-mc-status = { version = "3.0.0", features = ["tower"] }
```
## Быстрый старт
### Одна строка — без клиента
```rust
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 без §-кодов
Ok(())
}
```
### Клиент с настройками
```rust
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);
// Типизированный статус Java
let java = client.java("mc.hypixel.net").await?;
println!("Версия : {}", java.version());
println!("Игроки : {}", java.display_players());
println!("Задержка : {:.0} мс", java.latency_ms());
println!("MOTD : {}", java.motd_clean());
// Таймаут на уровне запроса — конфиг клиента не меняется
let fast = client.java("mc.hypixel.net")
.timeout(Duration::from_secs(3))
.await?;
println!("Быстрая проверка: {} мс", fast.latency_ms() as u32);
// Типизированный статус Bedrock
let bedrock = client.bedrock("geo.hivebedrock.network:19132").await?;
println!("Редакция : {}", bedrock.edition());
println!("Игроки : {}", bedrock.display_players());
Ok(())
}
```
### Массовые запросы
```rust
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!("{}: онлайн ({:.0} мс)", server.address, s.latency),
Err(e) => println!("{}: {}", server.address, e),
}
}
Ok(())
}
```
### JSON-сериализация
```rust
let status = client.java("mc.hypixel.net").await?;
let json = serde_json::to_string(&status)?; // компактный
let json = serde_json::to_string_pretty(&status)?; // форматированный
// favicon и raw_data исключены автоматически
```
### Выбор редакции в рантайме
```rust
let edition: ServerEdition = "java".parse()?;
// Полный статус
let status = client.server("mc.hypixel.net", edition).await?;
// Только проверка доступности — без парсинга статуса
let online = client.server("mc.hypixel.net", edition)
.timeout(Duration::from_secs(3))
.is_online()
.await;
```
### Кастомные типы адресов
```rust
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
Подключается через `features = ["tower"]`. Преобразует `McClient` в стандартный `tower::Service`, совместимый со всей экосистемой Tower — rate limiting, retry, буферизация, `tower-http` трейсинг, любой `tower::Layer`.
```rust
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 + параллельность
.rate_limit(100, Duration::from_secs(1)) // 100 запросов/сек
.retry(McRetryPolicy::new(3)) // 3 попытки при таймауте/ошибке соединения
.service(McClient::new().into_service());
svc.ready().await?;
let resp = svc.call(PingRequestTower::java("mc.hypixel.net")).await?;
println!("{:.0} мс", resp.status.latency);
Ok(())
}
```
### Кастомный Tower Layer
Реализуй `tower::Layer` для логирования, метрик или любого другого middleware:
```rust
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} мс", resp.status.latency);
Ok(resp)
})
}
}
// Использование
let mut svc = ServiceBuilder::new()
.layer(LogLayer)
.service(McClient::new().into_service());
```
## Поддержка SRV-записей
При пинге Java-сервера **без явного порта** библиотека запрашивает `_minecraft._tcp.{hostname}` — точно так же, как официальный клиент Minecraft. Результаты кэшируются на 5 минут.
```rust
// SRV-запрос выполняется автоматически
let status = client.java("hypixel.net").await?;
// Явный порт — SRV-запрос пропускается
let status = client.java("mc.hypixel.net:25565").await?;
```
## Форматирование MOTD
```rust
use rust_mc_status::strip_formatting;
let raw = "§aHypixel §c[1.8/1.21]";
println!("{}", strip_formatting(raw)); // "Hypixel [1.8/1.21]"
// Или через метод обёртки
let status = client.java("mc.hypixel.net").await?;
println!("{}", status.motd_clean()); // тот же результат
```
## Примеры
| `basic_usage` | Базовый API: фасад, типизированный статус, таймаут на запрос, сериализация |
| `advanced_usage` | Массовые запросы, плагины, моды, сохранение favicon |
| `cache_management` | Статистика LRU-кэша, cold/warm замеры, `clear_caches()` |
| `tower_usage` | Tower middleware: rate limiting, retry, buffer, кастомный Layer |
| `srv_lookup_example` | Поведение SRV-записей |
| `performance_test` | Бенчмарк (запускать с `--release`) |
```bash
cargo run --example basic_usage
cargo run --example cache_management
cargo run --example tower_usage --features tower
cargo run --example performance_test --release
```
## Справочник API
### Builder-методы `McClient`
| `new()` | Настройки по умолчанию (10 с таймаут, 10 параллельных, 1024 кэш) |
| `with_timeout(Duration)` | Глобальный таймаут запроса |
| `with_max_parallel(usize)` | Максимум параллельных пингов в `ping_many` |
| `with_cache_size(usize)` | Ёмкость LRU-кэша для DNS и SRV |
| `into_service()` *(tower)* | Преобразовать в `McService` для Tower middleware |
### Методы пинга
| `client.java(addr)` | `JavaPingBuilder` | Поддерживает `.timeout(d).await` |
| `client.bedrock(addr)` | `BedrockPingBuilder` | Поддерживает `.timeout(d).await` |
| `client.server(addr, edition)` | `ServerPingBuilder` | Рантайм-редакция; `.is_online().await` |
| `client.ping_many(&[ServerInfo])` | `Vec<(ServerInfo, Result<ServerStatus>)>` | Параллельный батч |
| `ping_java(addr)` | `Result<JavaServerStatus>` | Глобальный клиент, без настройки |
| `ping_bedrock(addr)` | `Result<BedrockServerStatus>` | Глобальный клиент, без настройки |
### Tower-типы *(feature = "tower")*
| `McService` | `tower::Service<PingRequestTower>` — обёртка над `McClient` |
| `McRetryPolicy` | Retry при `Timeout` / `ConnectionError`; настраиваемое число попыток |
| `PingRequestTower` | Тип запроса с конструкторами `.java(addr)` / `.bedrock(addr)` |
| `PingResponseTower` | Ответ, содержащий `status: ServerStatus` |
### Методы `JavaServerStatus`
`motd()`, `motd_clean()`, `version()`, `players_online()`, `players_max()`, `display_players()`, `favicon()`, `save_favicon(path)`, `ip()`, `port()`, `hostname()`, `latency_ms()`, `is_online()`, `raw()`
### Методы `BedrockServerStatus`
`motd()`, `motd_clean()`, `motd2()`, `motd2_clean()`, `edition()`, `version()`, `players_online()`, `players_max()`, `display_players()`, `game_mode()`, `ip()`, `port()`, `hostname()`, `latency_ms()`, `is_online()`, `raw()`
### Утилиты
| `strip_formatting(s)` | Убрать `§X`-коды Minecraft, схлопнуть пробелы |
| `truncate_str(s, n)` | Обрезать до `n` Unicode-символов (без паник) |
### Методы кэша (все `async`)
`cache_stats().await`, `clear_caches().await`
## Обработка ошибок
```rust
use rust_mc_status::McError;
match client.java("server.com").await {
Ok(s) => println!("онлайн: {}", s.display_players()),
Err(McError::Timeout) => println!("таймаут"),
Err(McError::DnsError(msg)) => println!("DNS: {}", msg),
Err(McError::ConnectionError(msg)) => println!("соединение: {}", msg),
Err(e) => println!("ошибка: {}", e),
}
```
## Производительность
- DNS и SRV-записи кэшируются на 5 минут с LRU-вытеснением — без утечек памяти
- `tokio::sync::RwLock` позволяет одновременное чтение с минимальной конкуренцией
- `SmallVec` для списка игроков (≤ 12), плагинов (≤ 8) и модов (≤ 8) — хранение на стеке
- `.timeout()` на уровне запроса снижает хвостовую латентность без изменения конфига клиента
- `ping_bedrock` использует UDP; `ping_java` использует TCP с `TCP_NODELAY`
- Стандартная сборка не включает tower/async-trait/pin-project — только с `features = ["tower"]`
## Лицензия
MIT — см. [LICENSE](LICENSE).
## Список изменений
См. [CHANGELOG.md](CHANGELOG.md).
## Версия
Текущая версия: **3.0.0** (выпущена 2026-07-01) — см. [CHANGELOG.md](CHANGELOG.md) для полной истории и руководства по миграции.