diff --git a/Cargo.lock b/Cargo.lock index 6311637..561b7db 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -249,7 +249,7 @@ checksum = "308cc39be01b73d0d18f82a0e7b2a3df85245f84af96fdddc5d202d27e47b86a" [[package]] name = "minecraft-protocol" version = "0.1.0" -source = "git+https://github.com/timvisee/minecraft-protocol?rev=c181117#c1811171c20a88bc7ecac1cda3257fbaa9f9c10c" +source = "git+https://github.com/timvisee/minecraft-protocol?rev=4348c27#4348c27fa95d304b154c66b414738f41927db23e" dependencies = [ "byteorder", "minecraft-protocol-derive", @@ -262,7 +262,7 @@ dependencies = [ [[package]] name = "minecraft-protocol-derive" version = "0.0.0" -source = "git+https://github.com/timvisee/minecraft-protocol?rev=c181117#c1811171c20a88bc7ecac1cda3257fbaa9f9c10c" +source = "git+https://github.com/timvisee/minecraft-protocol?rev=4348c27#4348c27fa95d304b154c66b414738f41927db23e" dependencies = [ "proc-macro2", "quote", diff --git a/Cargo.toml b/Cargo.toml index 4a68253..9dbd9b0 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -22,7 +22,7 @@ edition = "2021" bytes = "1.1" futures = { version = "0.3", default-features = false } libc = "0.2" -minecraft-protocol = { git = "https://github.com/timvisee/minecraft-protocol", rev = "c181117" } +minecraft-protocol = { git = "https://github.com/timvisee/minecraft-protocol", rev = "4348c27" } tokio = { version = "1", default-features = false, features = ["rt", "rt-multi-thread", "io-util", "net", "macros", "time", "process", "signal"] } dotenv = "0.15" diff --git a/src/main.rs b/src/main.rs index 0df2a4f..b79910a 100644 --- a/src/main.rs +++ b/src/main.rs @@ -24,7 +24,7 @@ use tokio::io::AsyncWriteExt; use tokio::net::{TcpListener, TcpStream}; use config::*; -use proto::{Client, ClientState, RawPacket}; +use proto::{Client, ClientState, RawPacket, PROTO_DEFAULT_PROTOCOL, PROTO_DEFAULT_VERSION}; use server::ServerState; #[tokio::main] @@ -157,22 +157,32 @@ async fn serve_status( // Hijack server status packet if client.state() == ClientState::Status && packet.id == proto::STATUS_PACKET_ID_STATUS { - // Build status response - // TODO: grab latest protocol version from online server! + // Select version and player max from last known server status + let (version, max) = match server.clone_status() { + Some(status) => (status.version, status.players.max), + None => ( + ServerVersion { + name: String::from(PROTO_DEFAULT_VERSION), + protocol: PROTO_DEFAULT_PROTOCOL, + }, + 0, + ), + }; + + // Select description let description = if server.starting() { LABEL_SERVER_STARTING } else { LABEL_SERVER_SLEEPING }; + + // Build status resposne let server_status = ServerStatus { - version: ServerVersion { - name: String::from("1.16.5"), - protocol: 754, - }, + version, description: Message::new(Payload::text(description)), players: OnlinePlayers { online: 0, - max: 0, + max, sample: vec![], }, }; diff --git a/src/monitor.rs b/src/monitor.rs index ed750ab..b061556 100644 --- a/src/monitor.rs +++ b/src/monitor.rs @@ -13,12 +13,9 @@ use minecraft_protocol::version::v1_14_4::status::StatusResponse; use tokio::io::AsyncWriteExt; use tokio::net::TcpStream; -use crate::proto::{self, ClientState, RawPacket}; +use crate::proto::{self, ClientState, RawPacket, PROTO_DEFAULT_PROTOCOL}; use crate::server::ServerState; -/// Minecraft protocol version used when polling server status. -const PROTOCOL_VERSION: i32 = 754; - /// Monitor ping inverval in seconds. const MONITOR_PING_INTERVAL: u64 = 2; @@ -37,6 +34,7 @@ pub async fn monitor_server(addr: SocketAddr, state: Arc) { state.set_status(status); } + // TODO: use interval instead, for a more reliable polling interval? tokio::time::sleep(Duration::from_secs(MONITOR_PING_INTERVAL)).await; } } @@ -60,7 +58,7 @@ async fn fetch_status(addr: SocketAddr) -> Result { /// Send handshake. async fn send_handshake(stream: &mut TcpStream, addr: SocketAddr) -> Result<(), ()> { let handshake = Handshake { - protocol_version: PROTOCOL_VERSION, + protocol_version: PROTO_DEFAULT_PROTOCOL as i32, server_addr: addr.ip().to_string(), server_port: addr.port(), next_state: ClientState::Status.to_id(), @@ -72,7 +70,6 @@ async fn send_handshake(stream: &mut TcpStream, addr: SocketAddr) -> Result<(), let raw = RawPacket::new(proto::HANDSHAKE_PACKET_ID_HANDSHAKE, packet) .encode() .map_err(|_| ())?; - stream.write_all(&raw).await.map_err(|_| ())?; Ok(()) diff --git a/src/proto.rs b/src/proto.rs index 35f1609..cf184bd 100644 --- a/src/proto.rs +++ b/src/proto.rs @@ -7,13 +7,31 @@ use tokio::net::tcp::ReadHalf; use crate::types; +/// Default minecraft protocol version name. +/// +/// Send to clients when the server is sleeping when the real server version is not known yet. +pub const PROTO_DEFAULT_VERSION: &str = "1.16.5"; + +/// Default minecraft protocol version. +/// +/// Send to clients when the server is sleeping when the real server version is not known yet, and +/// with server status polling requests. +pub const PROTO_DEFAULT_PROTOCOL: u32 = 754; + +/// Handshake state, handshake packet ID. pub const HANDSHAKE_PACKET_ID_HANDSHAKE: i32 = 0; + +/// Status state, status packet ID. pub const STATUS_PACKET_ID_STATUS: i32 = 0; + +/// Status state, ping packet ID. pub const STATUS_PACKET_ID_PING: i32 = 1; + +/// Login state, login start packet ID. pub const LOGIN_PACKET_ID_LOGIN_START: i32 = 0; /// Client state. -// TODO: add encryption/compression state +// TODO: add encryption/compression state? #[derive(Debug, Default)] pub struct Client { /// Current client state. @@ -120,6 +138,10 @@ impl RawPacket { } /// Read raw packet from stream. +/// +/// Note: this does not support reading compressed/encrypted packets. +/// We should never need this though, as we're done reading user packets before any of this is +/// enabled. See: https://wiki.vg/Protocol#Packet_format pub async fn read_packet<'a>( buf: &mut BytesMut, stream: &mut ReadHalf<'a>, diff --git a/src/server.rs b/src/server.rs index 25c14e8..edd7c91 100644 --- a/src/server.rs +++ b/src/server.rs @@ -21,7 +21,8 @@ pub struct ServerState { /// Last known server status. /// /// Once set, this will remain set, and isn't cleared when the server goes offline. - status: Mutex>, + // TODO: make this private? + pub status: Mutex>, } impl ServerState { @@ -61,6 +62,11 @@ impl ServerState { *self.pid.lock().unwrap() = pid; } + /// Clone the last known server status. + pub fn clone_status(&self) -> Option { + self.status.lock().unwrap().clone() + } + /// Update the server status. pub fn set_status(&self, status: ServerStatus) { self.status.lock().unwrap().replace(status);