mirror of
https://github.com/tribufu/rust-gamedig
synced 2026-05-06 15:27:28 +00:00
[Protocol] Fix Minecraft Java query not being able to specify a hostname (#91)
* Make initial fix * Fix imports * Rename query_address to hostname and add this to the mc example * Fix master_querant example not compiling * Add extra safety on converting strings to Minecraft Varint strings * Add docs to RequestSettings * Fix formatting
This commit is contained in:
parent
211cd5fd5f
commit
a56ca45de6
8 changed files with 118 additions and 28 deletions
|
|
@ -126,7 +126,7 @@ fn main() -> GDResult<()> {
|
|||
)
|
||||
}
|
||||
"mc" => println!("{:#?}", mc::query(ip, port)?),
|
||||
"mc_java" => println!("{:#?}", mc::query_java(ip, port)?),
|
||||
"mc_java" => println!("{:#?}", mc::query_java(ip, port, None)?),
|
||||
"mc_bedrock" => println!("{:#?}", mc::query_bedrock(ip, port)?),
|
||||
"mc_legacy" => println!("{:#?}", mc::query_legacy(ip, port)?),
|
||||
"mc_legacy_vb1_8" => {
|
||||
|
|
|
|||
|
|
@ -1,9 +1,28 @@
|
|||
use gamedig::games::mc;
|
||||
use gamedig::protocols::minecraft::RequestSettings;
|
||||
|
||||
fn main() {
|
||||
// or Some(<port>), None is the default protocol port (which is 25565 for java
|
||||
// and 19132 for bedrock)
|
||||
let response = mc::query(&"127.0.0.1".parse().unwrap(), None);
|
||||
// This will fail if no server is available locally!
|
||||
|
||||
match response {
|
||||
Err(error) => println!("Couldn't query, error: {}", error),
|
||||
Ok(r) => println!("{:#?}", r),
|
||||
}
|
||||
|
||||
// This is an example to query a server with a hostname to be specified in the
|
||||
// packet. Passing -1 on the protocol_version means anything, note that
|
||||
// an invalid value here might result in server not responding.
|
||||
let response = mc::query_java(
|
||||
&"209.222.114.62".parse().unwrap(),
|
||||
Some(25565),
|
||||
Some(RequestSettings {
|
||||
hostname: "mc.hypixel.net".to_string(),
|
||||
protocol_version: -1,
|
||||
}),
|
||||
);
|
||||
|
||||
match response {
|
||||
Err(error) => println!("Couldn't query, error: {}", error),
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
use crate::protocols::minecraft::RequestSettings;
|
||||
use crate::{
|
||||
protocols::minecraft::{self, BedrockResponse, JavaResponse, LegacyGroup},
|
||||
GDErrorKind,
|
||||
|
|
@ -8,7 +9,7 @@ use std::net::{IpAddr, SocketAddr};
|
|||
/// Query with all the protocol variants one by one (Java -> Bedrock -> Legacy
|
||||
/// (1.6 -> 1.4 -> Beta 1.8)).
|
||||
pub fn query(address: &IpAddr, port: Option<u16>) -> GDResult<JavaResponse> {
|
||||
if let Ok(response) = query_java(address, port) {
|
||||
if let Ok(response) = query_java(address, port, None) {
|
||||
return Ok(response);
|
||||
}
|
||||
|
||||
|
|
@ -24,8 +25,16 @@ pub fn query(address: &IpAddr, port: Option<u16>) -> GDResult<JavaResponse> {
|
|||
}
|
||||
|
||||
/// Query a Java Server.
|
||||
pub fn query_java(address: &IpAddr, port: Option<u16>) -> GDResult<JavaResponse> {
|
||||
minecraft::query_java(&SocketAddr::new(*address, port_or_java_default(port)), None)
|
||||
pub fn query_java(
|
||||
address: &IpAddr,
|
||||
port: Option<u16>,
|
||||
request_settings: Option<RequestSettings>,
|
||||
) -> GDResult<JavaResponse> {
|
||||
minecraft::query_java(
|
||||
&SocketAddr::new(*address, port_or_java_default(port)),
|
||||
None,
|
||||
request_settings,
|
||||
)
|
||||
}
|
||||
|
||||
/// Query a (Java) Legacy Server (1.6 -> 1.4 -> Beta 1.8).
|
||||
|
|
|
|||
|
|
@ -159,7 +159,7 @@ pub fn query_with_timeout(
|
|||
Protocol::Minecraft(version) => {
|
||||
match version {
|
||||
Some(protocols::minecraft::Server::Java) => {
|
||||
protocols::minecraft::query_java(&socket_addr, timeout_settings).map(Box::new)?
|
||||
protocols::minecraft::query_java(&socket_addr, timeout_settings, None).map(Box::new)?
|
||||
}
|
||||
Some(protocols::minecraft::Server::Bedrock) => {
|
||||
protocols::minecraft::query_bedrock(&socket_addr, timeout_settings).map(Box::new)?
|
||||
|
|
|
|||
|
|
@ -11,33 +11,28 @@ use crate::{
|
|||
|
||||
use std::net::SocketAddr;
|
||||
|
||||
use crate::protocols::minecraft::{as_string, RequestSettings};
|
||||
use byteorder::LittleEndian;
|
||||
use serde_json::Value;
|
||||
|
||||
#[rustfmt::skip]
|
||||
const PAYLOAD: [u8; 17] = [
|
||||
//Packet ID (0)
|
||||
0x00,
|
||||
//Protocol Version (-1 to determine version)
|
||||
0xFF, 0xFF, 0xFF, 0xFF, 0x0F,
|
||||
//Server address (can be anything)
|
||||
0x07, 0x47, 0x61, 0x6D, 0x65, 0x44, 0x69, 0x67,
|
||||
//Server port (can be anything)
|
||||
0x00, 0x00,
|
||||
//Next state (1 for status)
|
||||
0x01
|
||||
];
|
||||
|
||||
pub struct Java {
|
||||
socket: TcpSocket,
|
||||
request_settings: RequestSettings,
|
||||
}
|
||||
|
||||
impl Java {
|
||||
fn new(address: &SocketAddr, timeout_settings: Option<TimeoutSettings>) -> GDResult<Self> {
|
||||
fn new(
|
||||
address: &SocketAddr,
|
||||
timeout_settings: Option<TimeoutSettings>,
|
||||
request_settings: Option<RequestSettings>,
|
||||
) -> GDResult<Self> {
|
||||
let socket = TcpSocket::new(address)?;
|
||||
socket.apply_timeout(timeout_settings)?;
|
||||
|
||||
Ok(Self { socket })
|
||||
Ok(Self {
|
||||
socket,
|
||||
request_settings: request_settings.unwrap_or_default(),
|
||||
})
|
||||
}
|
||||
|
||||
fn send(&mut self, data: Vec<u8>) -> GDResult<()> {
|
||||
|
|
@ -57,7 +52,24 @@ impl Java {
|
|||
}
|
||||
|
||||
fn send_handshake(&mut self) -> GDResult<()> {
|
||||
self.send(PAYLOAD.to_vec())?;
|
||||
let handshake_payload = [
|
||||
&[
|
||||
// Packet ID (0)
|
||||
0x00,
|
||||
], // Protocol Version (-1 to determine version)
|
||||
as_varint(self.request_settings.protocol_version).as_slice(),
|
||||
// Server address (can be anything)
|
||||
as_string(&self.request_settings.hostname)?.as_slice(),
|
||||
// Server port (can be anything)
|
||||
&self.socket.port().to_le_bytes(),
|
||||
&[
|
||||
// Next state (1 for status)
|
||||
0x01,
|
||||
],
|
||||
]
|
||||
.concat();
|
||||
|
||||
self.send(handshake_payload)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
|
@ -143,7 +155,11 @@ impl Java {
|
|||
})
|
||||
}
|
||||
|
||||
pub fn query(address: &SocketAddr, timeout_settings: Option<TimeoutSettings>) -> GDResult<JavaResponse> {
|
||||
Self::new(address, timeout_settings)?.get_info()
|
||||
pub fn query(
|
||||
address: &SocketAddr,
|
||||
timeout_settings: Option<TimeoutSettings>,
|
||||
request_settings: Option<RequestSettings>,
|
||||
) -> GDResult<JavaResponse> {
|
||||
Self::new(address, timeout_settings, request_settings)?.get_info()
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
use crate::protocols::minecraft::types::RequestSettings;
|
||||
use crate::{
|
||||
protocols::minecraft::{
|
||||
protocol::{
|
||||
|
|
@ -26,7 +27,7 @@ mod legacy_v1_6;
|
|||
/// Queries a Minecraft server with all the protocol variants one by one (Java
|
||||
/// -> Bedrock -> Legacy (1.6 -> 1.4 -> Beta 1.8)).
|
||||
pub fn query(address: &SocketAddr, timeout_settings: Option<TimeoutSettings>) -> GDResult<JavaResponse> {
|
||||
if let Ok(response) = query_java(address, timeout_settings.clone()) {
|
||||
if let Ok(response) = query_java(address, timeout_settings.clone(), None) {
|
||||
return Ok(response);
|
||||
}
|
||||
|
||||
|
|
@ -42,8 +43,12 @@ pub fn query(address: &SocketAddr, timeout_settings: Option<TimeoutSettings>) ->
|
|||
}
|
||||
|
||||
/// Query a Java Server.
|
||||
pub fn query_java(address: &SocketAddr, timeout_settings: Option<TimeoutSettings>) -> GDResult<JavaResponse> {
|
||||
Java::query(address, timeout_settings)
|
||||
pub fn query_java(
|
||||
address: &SocketAddr,
|
||||
timeout_settings: Option<TimeoutSettings>,
|
||||
request_settings: Option<RequestSettings>,
|
||||
) -> GDResult<JavaResponse> {
|
||||
Java::query(address, timeout_settings, request_settings)
|
||||
}
|
||||
|
||||
/// Query a (Java) Legacy Server (1.6 -> 1.4 -> Beta 1.8).
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ use crate::{
|
|||
types::{CommonPlayer, CommonResponse, GenericPlayer},
|
||||
GenericResponse,
|
||||
},
|
||||
GDErrorKind::{PacketBad, UnknownEnumCast},
|
||||
GDErrorKind::{InvalidInput, PacketBad, UnknownEnumCast},
|
||||
GDResult,
|
||||
};
|
||||
|
||||
|
|
@ -88,6 +88,28 @@ pub struct JavaResponse {
|
|||
pub server_type: Server,
|
||||
}
|
||||
|
||||
/// Java-only additional request settings.
|
||||
pub struct RequestSettings {
|
||||
/// Some Minecraft servers do not respond as expected if this
|
||||
/// isn't a specific value, `mc.hypixel.net` is an example.
|
||||
pub hostname: String,
|
||||
/// Specifies the client [protocol version number](https://wiki.vg/Protocol_version_numbers),
|
||||
/// `-1` means anything.
|
||||
pub protocol_version: i32,
|
||||
}
|
||||
|
||||
impl Default for RequestSettings {
|
||||
/// `hostname`: "gamedig"
|
||||
///
|
||||
/// `protocol_version`: -1
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
hostname: "gamedig".to_string(),
|
||||
protocol_version: -1,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl CommonResponse for JavaResponse {
|
||||
fn as_original(&self) -> GenericResponse { GenericResponse::Minecraft(VersionedResponse::Java(self)) }
|
||||
|
||||
|
|
@ -238,3 +260,14 @@ pub(crate) fn get_string<B: ByteOrder>(buffer: &mut Buffer<B>) -> GDResult<Strin
|
|||
|
||||
String::from_utf8(text).map_err(|e| PacketBad.context(e))
|
||||
}
|
||||
|
||||
pub(crate) fn as_string(value: &str) -> GDResult<Vec<u8>> {
|
||||
let length = value
|
||||
.len()
|
||||
.try_into()
|
||||
.map_err(|e| InvalidInput.context(e))?;
|
||||
let mut buf = as_varint(length);
|
||||
buf.extend(value.as_bytes());
|
||||
|
||||
Ok(buf)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -20,16 +20,20 @@ pub trait Socket {
|
|||
|
||||
fn send(&mut self, data: &[u8]) -> GDResult<()>;
|
||||
fn receive(&mut self, size: Option<usize>) -> GDResult<Vec<u8>>;
|
||||
|
||||
fn port(&self) -> u16;
|
||||
}
|
||||
|
||||
pub struct TcpSocket {
|
||||
socket: net::TcpStream,
|
||||
address: SocketAddr,
|
||||
}
|
||||
|
||||
impl Socket for TcpSocket {
|
||||
fn new(address: &SocketAddr) -> GDResult<Self> {
|
||||
Ok(Self {
|
||||
socket: net::TcpStream::connect(address).map_err(|e| SocketConnect.context(e))?,
|
||||
address: *address,
|
||||
})
|
||||
}
|
||||
|
||||
|
|
@ -54,6 +58,8 @@ impl Socket for TcpSocket {
|
|||
|
||||
Ok(buf)
|
||||
}
|
||||
|
||||
fn port(&self) -> u16 { self.address.port() }
|
||||
}
|
||||
|
||||
pub struct UdpSocket {
|
||||
|
|
@ -96,6 +102,8 @@ impl Socket for UdpSocket {
|
|||
|
||||
Ok(buf[.. number_of_bytes_received].to_vec())
|
||||
}
|
||||
|
||||
fn port(&self) -> u16 { self.address.port() }
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue