Use server protocol version/player max once known for status responses

This commit is contained in:
timvisee 2021-11-08 13:07:49 +01:00
parent d4c0b4c146
commit 049fce78b7
No known key found for this signature in database
GPG Key ID: B8DB720BC383E172
6 changed files with 54 additions and 19 deletions

4
Cargo.lock generated
View File

@ -249,7 +249,7 @@ checksum = "308cc39be01b73d0d18f82a0e7b2a3df85245f84af96fdddc5d202d27e47b86a"
[[package]] [[package]]
name = "minecraft-protocol" name = "minecraft-protocol"
version = "0.1.0" 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 = [ dependencies = [
"byteorder", "byteorder",
"minecraft-protocol-derive", "minecraft-protocol-derive",
@ -262,7 +262,7 @@ dependencies = [
[[package]] [[package]]
name = "minecraft-protocol-derive" name = "minecraft-protocol-derive"
version = "0.0.0" 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 = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",

View File

@ -22,7 +22,7 @@ edition = "2021"
bytes = "1.1" bytes = "1.1"
futures = { version = "0.3", default-features = false } futures = { version = "0.3", default-features = false }
libc = "0.2" 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"] } tokio = { version = "1", default-features = false, features = ["rt", "rt-multi-thread", "io-util", "net", "macros", "time", "process", "signal"] }
dotenv = "0.15" dotenv = "0.15"

View File

@ -24,7 +24,7 @@ use tokio::io::AsyncWriteExt;
use tokio::net::{TcpListener, TcpStream}; use tokio::net::{TcpListener, TcpStream};
use config::*; use config::*;
use proto::{Client, ClientState, RawPacket}; use proto::{Client, ClientState, RawPacket, PROTO_DEFAULT_PROTOCOL, PROTO_DEFAULT_VERSION};
use server::ServerState; use server::ServerState;
#[tokio::main] #[tokio::main]
@ -157,22 +157,32 @@ async fn serve_status(
// Hijack server status packet // Hijack server status packet
if client.state() == ClientState::Status && packet.id == proto::STATUS_PACKET_ID_STATUS { if client.state() == ClientState::Status && packet.id == proto::STATUS_PACKET_ID_STATUS {
// Build status response // Select version and player max from last known server status
// TODO: grab latest protocol version from online server! 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() { let description = if server.starting() {
LABEL_SERVER_STARTING LABEL_SERVER_STARTING
} else { } else {
LABEL_SERVER_SLEEPING LABEL_SERVER_SLEEPING
}; };
// Build status resposne
let server_status = ServerStatus { let server_status = ServerStatus {
version: ServerVersion { version,
name: String::from("1.16.5"),
protocol: 754,
},
description: Message::new(Payload::text(description)), description: Message::new(Payload::text(description)),
players: OnlinePlayers { players: OnlinePlayers {
online: 0, online: 0,
max: 0, max,
sample: vec![], sample: vec![],
}, },
}; };

View File

@ -13,12 +13,9 @@ use minecraft_protocol::version::v1_14_4::status::StatusResponse;
use tokio::io::AsyncWriteExt; use tokio::io::AsyncWriteExt;
use tokio::net::TcpStream; use tokio::net::TcpStream;
use crate::proto::{self, ClientState, RawPacket}; use crate::proto::{self, ClientState, RawPacket, PROTO_DEFAULT_PROTOCOL};
use crate::server::ServerState; use crate::server::ServerState;
/// Minecraft protocol version used when polling server status.
const PROTOCOL_VERSION: i32 = 754;
/// Monitor ping inverval in seconds. /// Monitor ping inverval in seconds.
const MONITOR_PING_INTERVAL: u64 = 2; const MONITOR_PING_INTERVAL: u64 = 2;
@ -37,6 +34,7 @@ pub async fn monitor_server(addr: SocketAddr, state: Arc<ServerState>) {
state.set_status(status); state.set_status(status);
} }
// TODO: use interval instead, for a more reliable polling interval?
tokio::time::sleep(Duration::from_secs(MONITOR_PING_INTERVAL)).await; tokio::time::sleep(Duration::from_secs(MONITOR_PING_INTERVAL)).await;
} }
} }
@ -60,7 +58,7 @@ async fn fetch_status(addr: SocketAddr) -> Result<ServerStatus, ()> {
/// Send handshake. /// Send handshake.
async fn send_handshake(stream: &mut TcpStream, addr: SocketAddr) -> Result<(), ()> { async fn send_handshake(stream: &mut TcpStream, addr: SocketAddr) -> Result<(), ()> {
let handshake = Handshake { let handshake = Handshake {
protocol_version: PROTOCOL_VERSION, protocol_version: PROTO_DEFAULT_PROTOCOL as i32,
server_addr: addr.ip().to_string(), server_addr: addr.ip().to_string(),
server_port: addr.port(), server_port: addr.port(),
next_state: ClientState::Status.to_id(), 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) let raw = RawPacket::new(proto::HANDSHAKE_PACKET_ID_HANDSHAKE, packet)
.encode() .encode()
.map_err(|_| ())?; .map_err(|_| ())?;
stream.write_all(&raw).await.map_err(|_| ())?; stream.write_all(&raw).await.map_err(|_| ())?;
Ok(()) Ok(())

View File

@ -7,13 +7,31 @@ use tokio::net::tcp::ReadHalf;
use crate::types; 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; pub const HANDSHAKE_PACKET_ID_HANDSHAKE: i32 = 0;
/// Status state, status packet ID.
pub const STATUS_PACKET_ID_STATUS: i32 = 0; pub const STATUS_PACKET_ID_STATUS: i32 = 0;
/// Status state, ping packet ID.
pub const STATUS_PACKET_ID_PING: i32 = 1; pub const STATUS_PACKET_ID_PING: i32 = 1;
/// Login state, login start packet ID.
pub const LOGIN_PACKET_ID_LOGIN_START: i32 = 0; pub const LOGIN_PACKET_ID_LOGIN_START: i32 = 0;
/// Client state. /// Client state.
// TODO: add encryption/compression state // TODO: add encryption/compression state?
#[derive(Debug, Default)] #[derive(Debug, Default)]
pub struct Client { pub struct Client {
/// Current client state. /// Current client state.
@ -120,6 +138,10 @@ impl RawPacket {
} }
/// Read raw packet from stream. /// 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>( pub async fn read_packet<'a>(
buf: &mut BytesMut, buf: &mut BytesMut,
stream: &mut ReadHalf<'a>, stream: &mut ReadHalf<'a>,

View File

@ -21,7 +21,8 @@ pub struct ServerState {
/// Last known server status. /// Last known server status.
/// ///
/// Once set, this will remain set, and isn't cleared when the server goes offline. /// Once set, this will remain set, and isn't cleared when the server goes offline.
status: Mutex<Option<ServerStatus>>, // TODO: make this private?
pub status: Mutex<Option<ServerStatus>>,
} }
impl ServerState { impl ServerState {
@ -61,6 +62,11 @@ impl ServerState {
*self.pid.lock().unwrap() = pid; *self.pid.lock().unwrap() = pid;
} }
/// Clone the last known server status.
pub fn clone_status(&self) -> Option<ServerStatus> {
self.status.lock().unwrap().clone()
}
/// Update the server status. /// Update the server status.
pub fn set_status(&self, status: ServerStatus) { pub fn set_status(&self, status: ServerStatus) {
self.status.lock().unwrap().replace(status); self.status.lock().unwrap().replace(status);