mirror of
https://github.com/tribufu/rust-gamedig
synced 2026-06-01 09:42:41 +00:00
Minecraft bedrock support (#7)
* Added needed ground stuff * Minecraft bedrock support! * Documentation acknowledgements! * Added utf8_le_undended test, some docs and modified master_querant * Modified query function to comply with the others Before: game query -> protocol query (get port or default port) After: game query (get port or default port) -> protocol query * Modified md files
This commit is contained in:
parent
ae14e37e60
commit
91f8bbb9fe
13 changed files with 319 additions and 107 deletions
|
|
@ -2,6 +2,12 @@
|
||||||
Who knows what the future holds...
|
Who knows what the future holds...
|
||||||
|
|
||||||
# 0.0.7 - ??/??/2022
|
# 0.0.7 - ??/??/2022
|
||||||
|
### Changes:
|
||||||
|
[Minecraft](https://www.minecraft.com) bedrock edition support.
|
||||||
|
Also added a `query_legacy_specific` method to the mc game queries.
|
||||||
|
|
||||||
|
### Breaking:
|
||||||
|
Removed `query_specific` from the mc protocol in favor of `query_java`, `query_legacy` and `query_legacy_specific`.
|
||||||
|
|
||||||
# 0.0.6 - 28/11/2022
|
# 0.0.6 - 28/11/2022
|
||||||
[Minecraft](https://www.minecraft.com) support (bedrock not supported yet).
|
[Minecraft](https://www.minecraft.com) support (bedrock not supported yet).
|
||||||
|
|
|
||||||
58
GAMES.md
58
GAMES.md
|
|
@ -1,35 +1,35 @@
|
||||||
A supported game is defined as a game that has been successfully tested, other games that use the implemented protocols might work too, but it isn't guaranteed.
|
A supported game is defined as a game that has been successfully tested, other games that use the implemented protocols might work too, but it isn't guaranteed.
|
||||||
|
|
||||||
# Supported games:
|
# Supported games:
|
||||||
| Game | Use name | Protocol | Notes |
|
| Game | Use name | Protocol | Notes |
|
||||||
|------------------------------------|----------|---------------------------|-------------------------------------------------------------------------------------------|
|
|------------------------------------|----------|---------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||||
| Team Fortress 2 | TF2 | Valve Protocol | |
|
| Team Fortress 2 | TF2 | Valve Protocol | |
|
||||||
| The Ship | TS | Valve Protocol (*Altered) | |
|
| The Ship | TS | Valve Protocol (*Altered) | |
|
||||||
| Counter-Strike: Global Offensive | CSGO | Valve Protocol | The server must have the cvar `host_players_show` set to `2` to get the full player list. |
|
| Counter-Strike: Global Offensive | CSGO | Valve Protocol | The server must have the cvar `host_players_show` set to `2` to get the full player list. |
|
||||||
| Counter-Strike: Source | CSS | Valve Protocol | |
|
| Counter-Strike: Source | CSS | Valve Protocol | |
|
||||||
| Day of Defeat: Source | DODS | Valve Protocol | |
|
| Day of Defeat: Source | DODS | Valve Protocol | |
|
||||||
| Left 4 Dead | L4D | Valve Protocol | |
|
| Left 4 Dead | L4D | Valve Protocol | |
|
||||||
| Left 4 Dead 2 | L4D2 | Valve Protocol | |
|
| Left 4 Dead 2 | L4D2 | Valve Protocol | |
|
||||||
| Half-Life 2 Deathmatch | HL2DM | Valve Protocol | |
|
| Half-Life 2 Deathmatch | HL2DM | Valve Protocol | |
|
||||||
| Alien Swarm | ALIENS | Valve Protocol | |
|
| Alien Swarm | ALIENS | Valve Protocol | |
|
||||||
| Alien Swarm: Reactive Drop | ASRD | Valve Protocol | |
|
| Alien Swarm: Reactive Drop | ASRD | Valve Protocol | |
|
||||||
| Insurgency | INS | Valve Protocol | |
|
| Insurgency | INS | Valve Protocol | |
|
||||||
| Insurgency: Sandstorm | INSS | Valve Protocol | Use the query port. |
|
| Insurgency: Sandstorm | INSS | Valve Protocol | Use the query port. |
|
||||||
| Insurgency: Modern Infantry Combat | INSMIC | Valve Protocol | |
|
| Insurgency: Modern Infantry Combat | INSMIC | Valve Protocol | |
|
||||||
| Counter-Strike: Condition Zero | CSCZ | Valve Protocol (GoldSrc) | |
|
| Counter-Strike: Condition Zero | CSCZ | Valve Protocol (GoldSrc) | |
|
||||||
| Day of Defeat | DOD | Valve Protocol (GoldSrc) | |
|
| Day of Defeat | DOD | Valve Protocol (GoldSrc) | |
|
||||||
| Minecraft | MC | Proprietary | Bedrock not supported yet. |
|
| Minecraft | MC | Proprietary | Bedrock edition provides a different response compared to the Java edition, query specifically for bedrock to get them, otherwise, only matching fields will be provided. |
|
||||||
| 7 Days To Die | SDTD | Valve Protocol | |
|
| 7 Days To Die | SDTD | Valve Protocol | |
|
||||||
| ARK: Survival Evolved | ASE | Valve Protocol | |
|
| ARK: Survival Evolved | ASE | Valve Protocol | |
|
||||||
| Unturned | UNTURNED | Valve Protocol | |
|
| Unturned | UNTURNED | Valve Protocol | |
|
||||||
| The Forest | TF | Valve Protocol (GoldSrc) | Use the query port. |
|
| The Forest | TF | Valve Protocol (GoldSrc) | Use the query port. |
|
||||||
| Team Fortress Classic | TFC | Valve Protocol | |
|
| Team Fortress Classic | TFC | Valve Protocol | |
|
||||||
| Sven Co-op | SC | Valve Protocol (GoldSrc) | |
|
| Sven Co-op | SC | Valve Protocol (GoldSrc) | |
|
||||||
| Rust | RUST | Valve Protocol | |
|
| Rust | RUST | Valve Protocol | |
|
||||||
| Counter-Strike | CS | Valve Protocol (GoldSrc) | |
|
| Counter-Strike | CS | Valve Protocol (GoldSrc) | |
|
||||||
| Arma 2: Operation Arrowhead | ARMA2OA | Valve Protocol | Use the query port. |
|
| Arma 2: Operation Arrowhead | ARMA2OA | Valve Protocol | Use the query port. |
|
||||||
| Day of Infamy | DOI | Valve Protocol | |
|
| Day of Infamy | DOI | Valve Protocol | |
|
||||||
| Half-Life Deathmatch: Source | HLDMS | Valve Protocol | |
|
| Half-Life Deathmatch: Source | HLDMS | Valve Protocol | |
|
||||||
|
|
||||||
## Planned to add support:
|
## Planned to add support:
|
||||||
_
|
_
|
||||||
|
|
|
||||||
10
PROTOCOLS.md
10
PROTOCOLS.md
|
|
@ -1,10 +1,10 @@
|
||||||
A protocol is defined as proprietary if it is being used only for a single scope.
|
A protocol is defined as proprietary if it is being used only for a single scope (or series, like Minecraft).
|
||||||
|
|
||||||
# Supported protocols:
|
# Supported protocols:
|
||||||
| Name | For | Proprietary? | Documentation reference | Notes |
|
| Name | For | Proprietary? | Documentation reference | Notes |
|
||||||
|----------------|-------|--------------|---------------------------------------------------------------------------|----------------------------------------|
|
|----------------|-------|--------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|----------------------------------------|
|
||||||
| Valve Protocol | Games | No | [Server Queries](https://developer.valvesoftware.com/wiki/Server_queries) | Multi-packet decompression not tested. |
|
| Valve Protocol | Games | No | [Server Queries](https://developer.valvesoftware.com/wiki/Server_queries) | Multi-packet decompression not tested. |
|
||||||
| Minecraft | Games | Yes | [List Server Protocol](https://wiki.vg/Server_List_Ping) | Bedrock not yet supported. |
|
| Minecraft | Games | Yes | Java: [List Server Protocol](https://wiki.vg/Server_List_Ping) <br> Bedrock: [Node-GameDig Source](https://github.com/gamedig/node-gamedig/blob/master/protocols/minecraftbedrock.js) | |
|
||||||
|
|
||||||
## Planned to add support:
|
## Planned to add support:
|
||||||
_
|
_
|
||||||
|
|
|
||||||
|
|
@ -18,7 +18,7 @@ Team Fortress 2 query example:
|
||||||
use gamedig::games::tf2;
|
use gamedig::games::tf2;
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
let response = tf2::query("localhost", None); //or Some(27015), None is the default protocol port
|
let response = tf2::query("127.0.0.1", None); //or Some(27015), None is the default protocol port
|
||||||
match response {
|
match response {
|
||||||
Err(error) => println!("Couldn't query, error: {error}"),
|
Err(error) => println!("Couldn't query, error: {error}"),
|
||||||
Ok(r) => println!("{:#?}", r)
|
Ok(r) => println!("{:#?}", r)
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
|
|
||||||
use std::env;
|
use std::env;
|
||||||
use gamedig::{aliens, arma2oa, ase, asrd, cs, cscz, csgo, css, dod, dods, doi, GDResult, gm, hl2dm, hldms, ins, insmic, inss, l4d, l4d2, mc, protocols, rust, sc, sdtd, tf, tf2, tfc, ts, unturned};
|
use gamedig::{aliens, arma2oa, ase, asrd, cs, cscz, csgo, css, dod, dods, doi, GDResult, gm, hl2dm, hldms, ins, insmic, inss, l4d, l4d2, mc, rust, sc, sdtd, tf, tf2, tfc, ts, unturned};
|
||||||
use gamedig::protocols::minecraft::{LegacyGroup, Server};
|
use gamedig::protocols::minecraft::LegacyGroup;
|
||||||
use gamedig::protocols::valve;
|
use gamedig::protocols::valve;
|
||||||
use gamedig::protocols::valve::App;
|
use gamedig::protocols::valve::App;
|
||||||
|
|
||||||
|
|
@ -53,10 +53,11 @@ fn main() -> GDResult<()> {
|
||||||
"_gld_f" => println!("{:#?}", valve::query(ip, port.unwrap(), App::GoldSrc(true), None, None)?),
|
"_gld_f" => println!("{:#?}", valve::query(ip, port.unwrap(), App::GoldSrc(true), None, None)?),
|
||||||
"mc" => println!("{:#?}", mc::query(ip, port)?),
|
"mc" => println!("{:#?}", mc::query(ip, port)?),
|
||||||
"mc_java" => println!("{:#?}", mc::query_java(ip, port)?),
|
"mc_java" => println!("{:#?}", mc::query_java(ip, port)?),
|
||||||
|
"mc_bedrock" => println!("{:#?}", mc::query_bedrock(ip, port)?),
|
||||||
"mc_legacy" => println!("{:#?}", mc::query_legacy(ip, port)?),
|
"mc_legacy" => println!("{:#?}", mc::query_legacy(ip, port)?),
|
||||||
"_mc_legacy_vb1_8" => println!("{:#?}", protocols::minecraft::query_specific(Server::Legacy(LegacyGroup::VB1_8), ip, port.unwrap(), None)?),
|
"mc_legacy_vb1_8" => println!("{:#?}", mc::query_legacy_specific(LegacyGroup::VB1_8, ip, port)?),
|
||||||
"_mc_legacy_v1_4" => println!("{:#?}", protocols::minecraft::query_specific(Server::Legacy(LegacyGroup::V1_4), ip, port.unwrap(), None)?),
|
"mc_legacy_v1_4" => println!("{:#?}", mc::query_legacy_specific(LegacyGroup::V1_4, ip, port)?),
|
||||||
"_mc_legacy_v1_6" => println!("{:#?}", protocols::minecraft::query_specific(Server::Legacy(LegacyGroup::V1_6), ip, port.unwrap(), None)?),
|
"mc_legacy_v1_6" => println!("{:#?}", mc::query_legacy_specific(LegacyGroup::V1_6, ip, port)?),
|
||||||
"7dtd" => println!("{:#?}", sdtd::query(ip, port)?),
|
"7dtd" => println!("{:#?}", sdtd::query(ip, port)?),
|
||||||
"ase" => println!("{:#?}", ase::query(ip, port)?),
|
"ase" => println!("{:#?}", ase::query(ip, port)?),
|
||||||
"unturned" => println!("{:#?}", unturned::query(ip, port)?),
|
"unturned" => println!("{:#?}", unturned::query(ip, port)?),
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,9 @@
|
||||||
use gamedig::games::mc;
|
use gamedig::games::mc;
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
let response = mc::query("localhost", None); //or Some(25565), None is the default protocol port (which is 25565)
|
//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", None);
|
||||||
|
|
||||||
match response {
|
match response {
|
||||||
Err(error) => println!("Couldn't query, error: {error}"),
|
Err(error) => println!("Couldn't query, error: {error}"),
|
||||||
Ok(r) => println!("{:#?}", r)
|
Ok(r) => println!("{:#?}", r)
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@
|
||||||
use gamedig::games::tf2;
|
use gamedig::games::tf2;
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
let response = tf2::query("localhost", None); //or Some(27015), None is the default protocol port (which is 27015)
|
let response = tf2::query("127.0.0.1", None); //or Some(27015), None is the default protocol port (which is 27015)
|
||||||
match response {
|
match response {
|
||||||
Err(error) => println!("Couldn't query, error: {error}"),
|
Err(error) => println!("Couldn't query, error: {error}"),
|
||||||
Ok(r) => println!("{:#?}", r)
|
Ok(r) => println!("{:#?}", r)
|
||||||
|
|
|
||||||
|
|
@ -38,6 +38,8 @@ pub enum GDError {
|
||||||
AutoQuery,
|
AutoQuery,
|
||||||
/// A protocol-defined expected format was not met.
|
/// A protocol-defined expected format was not met.
|
||||||
ProtocolFormat(String),
|
ProtocolFormat(String),
|
||||||
|
/// Couldn't parse a value.
|
||||||
|
TypeParse(String),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl fmt::Display for GDError {
|
impl fmt::Display for GDError {
|
||||||
|
|
@ -57,6 +59,7 @@ impl fmt::Display for GDError {
|
||||||
GDError::JsonParse(details) => write!(f, "Json parse: {details}"),
|
GDError::JsonParse(details) => write!(f, "Json parse: {details}"),
|
||||||
GDError::AutoQuery => write!(f, "Auto query failed."),
|
GDError::AutoQuery => write!(f, "Auto query failed."),
|
||||||
GDError::ProtocolFormat(details) => write!(f, "Protocol rule: {details}"),
|
GDError::ProtocolFormat(details) => write!(f, "Protocol rule: {details}"),
|
||||||
|
GDError::TypeParse(details) => write!(f, "Type parse: {details}"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,39 +1,54 @@
|
||||||
use crate::{GDError, GDResult};
|
use crate::{GDError, GDResult};
|
||||||
use crate::protocols::minecraft;
|
use crate::protocols::minecraft;
|
||||||
use crate::protocols::minecraft::{Server, Response, LegacyGroup};
|
use crate::protocols::minecraft::{Response, LegacyGroup, BedrockResponse};
|
||||||
|
|
||||||
/// Query with all the protocol variants one by one (Java -> Legacy (1.6 -> 1.4 -> Beta 1.8)).
|
/// Query with all the protocol variants one by one (Java -> Bedrock -> Legacy (1.6 -> 1.4 -> Beta 1.8)).
|
||||||
pub fn query(address: &str, port: Option<u16>) -> GDResult<Response> {
|
pub fn query(address: &str, port: Option<u16>) -> GDResult<Response> {
|
||||||
minecraft::query(address, port_or_default(port), None)
|
if let Ok(response) = query_java(address, port) {
|
||||||
}
|
|
||||||
|
|
||||||
/// Query a Java Server.
|
|
||||||
pub fn query_java(address: &str, port: Option<u16>) -> GDResult<Response> {
|
|
||||||
minecraft::query_specific(Server::Java, address, port_or_default(port), None)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Query a (Java) Legacy Server (1.6 -> 1.4 -> Beta 1.8).
|
|
||||||
pub fn query_legacy(address: &str, port: Option<u16>) -> GDResult<Response> {
|
|
||||||
let unwrapped_port = port_or_default(port);
|
|
||||||
|
|
||||||
if let Ok(response) = minecraft::query_specific(Server::Legacy(LegacyGroup::V1_6), address, unwrapped_port, None) {
|
|
||||||
return Ok(response);
|
return Ok(response);
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Ok(response) = minecraft::query_specific(Server::Legacy(LegacyGroup::V1_4), address, unwrapped_port, None) {
|
if let Ok(response) = query_bedrock(address, port) {
|
||||||
return Ok(response);
|
return Ok(Response::from_bedrock_response(response));
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Ok(response) = minecraft::query_specific(Server::Legacy(LegacyGroup::VB1_8), address, unwrapped_port, None) {
|
if let Ok(response) = query_legacy(address, port) {
|
||||||
return Ok(response);
|
return Ok(response);
|
||||||
}
|
}
|
||||||
|
|
||||||
Err(GDError::AutoQuery)
|
Err(GDError::AutoQuery)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn port_or_default(port: Option<u16>) -> u16 {
|
/// Query a Java Server.
|
||||||
|
pub fn query_java(address: &str, port: Option<u16>) -> GDResult<Response> {
|
||||||
|
minecraft::query_java(address, port_or_java_default(port), None)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Query a (Java) Legacy Server (1.6 -> 1.4 -> Beta 1.8).
|
||||||
|
pub fn query_legacy(address: &str, port: Option<u16>) -> GDResult<Response> {
|
||||||
|
minecraft::query_legacy(address, port_or_java_default(port), None)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Query a specific (Java) Legacy Server.
|
||||||
|
pub fn query_legacy_specific(group: LegacyGroup, address: &str, port: Option<u16>) -> GDResult<Response> {
|
||||||
|
minecraft::query_legacy_specific(group, address, port_or_java_default(port), None)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Query a Bedrock Server.
|
||||||
|
pub fn query_bedrock(address: &str, port: Option<u16>) -> GDResult<BedrockResponse> {
|
||||||
|
minecraft::query_bedrock(address, port_or_bedrock_default(port), None)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn port_or_java_default(port: Option<u16>) -> u16 {
|
||||||
match port {
|
match port {
|
||||||
None => 25565,
|
None => 25565,
|
||||||
Some(port) => port
|
Some(port) => port
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn port_or_bedrock_default(port: Option<u16>) -> u16 {
|
||||||
|
match port {
|
||||||
|
None => 19132,
|
||||||
|
Some(port) => port
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
100
src/protocols/minecraft/protocol/bedrock.rs
Normal file
100
src/protocols/minecraft/protocol/bedrock.rs
Normal file
|
|
@ -0,0 +1,100 @@
|
||||||
|
|
||||||
|
/*
|
||||||
|
This file has code that has been documented by the NodeJS GameDig library (MIT) from
|
||||||
|
https://github.com/gamedig/node-gamedig/blob/master/protocols/minecraftbedrock.js
|
||||||
|
*/
|
||||||
|
|
||||||
|
use crate::{GDError, GDResult};
|
||||||
|
use crate::protocols::minecraft::{BedrockResponse, GameMode, Server};
|
||||||
|
use crate::protocols::types::TimeoutSettings;
|
||||||
|
use crate::socket::{Socket, UdpSocket};
|
||||||
|
use crate::utils::buffer::{get_string_utf8_le_unended, get_u16_be, get_u64_le, get_u8};
|
||||||
|
use crate::utils::error_by_expected_size;
|
||||||
|
|
||||||
|
pub struct Bedrock {
|
||||||
|
socket: UdpSocket
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Bedrock {
|
||||||
|
fn new(address: &str, port: u16, timeout_settings: Option<TimeoutSettings>) -> GDResult<Self> {
|
||||||
|
let socket = UdpSocket::new(address, port)?;
|
||||||
|
socket.apply_timeout(timeout_settings)?;
|
||||||
|
|
||||||
|
Ok(Self {
|
||||||
|
socket
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn send_status_request(&mut self) -> GDResult<()> {
|
||||||
|
self.socket.send(&[
|
||||||
|
// Message ID, ID_UNCONNECTED_PING
|
||||||
|
0x01,
|
||||||
|
// Nonce / timestamp
|
||||||
|
0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88,
|
||||||
|
// Magic
|
||||||
|
0x00, 0xff, 0xff, 0x00, 0xfe, 0xfe, 0xfe, 0xfe, 0xfd, 0xfd, 0xfd, 0xfd, 0x12, 0x34, 0x56, 0x78,
|
||||||
|
// Client GUID
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00])?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_info(&mut self) -> GDResult<BedrockResponse> {
|
||||||
|
self.send_status_request()?;
|
||||||
|
|
||||||
|
let buf = self.socket.receive(None)?;
|
||||||
|
let mut pos = 0;
|
||||||
|
|
||||||
|
if get_u8(&buf, &mut pos)? != 0x1c {
|
||||||
|
return Err(GDError::PacketBad("Invalid message id.".to_string()));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Checking for our nonce directly from a u64 (as the nonce is 8 bytes).
|
||||||
|
if get_u64_le(&buf, &mut pos)? != 9833440827789222417 {
|
||||||
|
return Err(GDError::PacketBad("Invalid nonce.".to_string()));
|
||||||
|
}
|
||||||
|
|
||||||
|
// These 8 bytes are identical to the serverId string we receive in decimal below
|
||||||
|
pos += 8;
|
||||||
|
|
||||||
|
// Verifying the magic value (as we need 16 bytes, cast to two u64 values)
|
||||||
|
if get_u64_le(&buf, &mut pos)? != 18374403896610127616 {
|
||||||
|
return Err(GDError::PacketBad("Invalid magic (part 1).".to_string()));
|
||||||
|
}
|
||||||
|
|
||||||
|
if get_u64_le(&buf, &mut pos)? != 8671175388723805693 {
|
||||||
|
return Err(GDError::PacketBad("Invalid magic (part 2).".to_string()));
|
||||||
|
}
|
||||||
|
|
||||||
|
let remaining_length = get_u16_be(&buf, &mut pos)? as usize;
|
||||||
|
error_by_expected_size(remaining_length, buf.len() - pos)?;
|
||||||
|
|
||||||
|
let binding = get_string_utf8_le_unended(&buf, &mut pos)?;
|
||||||
|
let status: Vec<&str> = binding.split(";").collect();
|
||||||
|
|
||||||
|
// We must have at least 6 values
|
||||||
|
if status.len() < 6 {
|
||||||
|
return Err(GDError::PacketBad("Not enough status parts.".to_string()));
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(BedrockResponse {
|
||||||
|
edition: status[0].to_string(),
|
||||||
|
name: status[1].to_string(),
|
||||||
|
version_name: status[3].to_string(),
|
||||||
|
version_protocol: status[2].to_string(),
|
||||||
|
max_players: status[5].parse().map_err(|_| GDError::TypeParse("couldn't parse.".to_string()))?,
|
||||||
|
online_players: status[4].parse().map_err(|_| GDError::TypeParse("couldn't parse.".to_string()))?,
|
||||||
|
id: status.get(6).and_then(|v| Some(v.to_string())),
|
||||||
|
map: status.get(7).and_then(|v| Some(v.to_string())),
|
||||||
|
game_mode: match status.get(8) {
|
||||||
|
None => None,
|
||||||
|
Some(v) => Some(GameMode::from_bedrock(v)?)
|
||||||
|
},
|
||||||
|
server_type: Server::Bedrock
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn query(address: &str, port: u16, timeout_settings: Option<TimeoutSettings>) -> GDResult<BedrockResponse> {
|
||||||
|
Bedrock::new(address, port, timeout_settings)?.get_info()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
use crate::{GDError, GDResult};
|
use crate::{GDError, GDResult};
|
||||||
use crate::protocols::minecraft::{LegacyGroup, Response, Server};
|
use crate::protocols::minecraft::{BedrockResponse, LegacyGroup, Response};
|
||||||
|
use crate::protocols::minecraft::protocol::bedrock::Bedrock;
|
||||||
use crate::protocols::minecraft::protocol::java::Java;
|
use crate::protocols::minecraft::protocol::java::Java;
|
||||||
use crate::protocols::minecraft::protocol::legacy_v1_4::LegacyV1_4;
|
use crate::protocols::minecraft::protocol::legacy_v1_4::LegacyV1_4;
|
||||||
use crate::protocols::minecraft::protocol::legacy_v1_6::LegacyV1_6;
|
use crate::protocols::minecraft::protocol::legacy_v1_6::LegacyV1_6;
|
||||||
|
|
@ -10,36 +11,57 @@ mod java;
|
||||||
mod legacy_v1_4;
|
mod legacy_v1_4;
|
||||||
mod legacy_v1_6;
|
mod legacy_v1_6;
|
||||||
mod legacy_bv1_8;
|
mod legacy_bv1_8;
|
||||||
|
mod bedrock;
|
||||||
|
|
||||||
/// Queries a Minecraft server with all the protocol variants one by one (Java -> Legacy (1.6 -> 1.4 -> Beta 1.8)).
|
/// 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: &str, port: u16, timeout_settings: Option<TimeoutSettings>) -> GDResult<Response> {
|
pub fn query(address: &str, port: u16, timeout_settings: Option<TimeoutSettings>) -> GDResult<Response> {
|
||||||
if let Ok(response) = query_specific(Server::Java, address, port, timeout_settings.clone()) {
|
if let Ok(response) = query_java(address, port, timeout_settings.clone()) {
|
||||||
return Ok(response);
|
return Ok(response);
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Ok(response) = query_specific(Server::Legacy(LegacyGroup::V1_6), address, port, timeout_settings.clone()) {
|
if let Ok(response) = query_bedrock(address, port, timeout_settings.clone()) {
|
||||||
return Ok(response);
|
return Ok(Response::from_bedrock_response(response));
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Ok(response) = query_specific(Server::Legacy(LegacyGroup::V1_4), address, port, timeout_settings.clone()) {
|
if let Ok(response) = query_legacy(address, port, timeout_settings) {
|
||||||
return Ok(response);
|
|
||||||
}
|
|
||||||
|
|
||||||
if let Ok(response) = query_specific(Server::Legacy(LegacyGroup::VB1_8), address, port, timeout_settings.clone()) {
|
|
||||||
return Ok(response);
|
return Ok(response);
|
||||||
}
|
}
|
||||||
|
|
||||||
Err(GDError::AutoQuery)
|
Err(GDError::AutoQuery)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Queries a specific Minecraft Server type.
|
/// Query a Java Server.
|
||||||
pub fn query_specific(mc_type: Server, address: &str, port: u16, timeout_settings: Option<TimeoutSettings>) -> GDResult<Response> {
|
pub fn query_java(address: &str, port: u16, timeout_settings: Option<TimeoutSettings>) -> GDResult<Response> {
|
||||||
match mc_type {
|
Java::query(address, port, timeout_settings)
|
||||||
Server::Java => Java::query(address, port, timeout_settings),
|
}
|
||||||
Server::Legacy(category) => match category {
|
|
||||||
LegacyGroup::V1_6 => LegacyV1_6::query(address, port, timeout_settings),
|
/// Query a (Java) Legacy Server (1.6 -> 1.4 -> Beta 1.8).
|
||||||
LegacyGroup::V1_4 => LegacyV1_4::query(address, port, timeout_settings),
|
pub fn query_legacy(address: &str, port: u16, timeout_settings: Option<TimeoutSettings>) -> GDResult<Response> {
|
||||||
LegacyGroup::VB1_8 => LegacyBV1_8::query(address, port, timeout_settings),
|
if let Ok(response) = query_legacy_specific(LegacyGroup::V1_6, address, port, timeout_settings.clone()) {
|
||||||
}
|
return Ok(response);
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Ok(response) = query_legacy_specific(LegacyGroup::V1_4, address, port, timeout_settings.clone()) {
|
||||||
|
return Ok(response);
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Ok(response) = query_legacy_specific(LegacyGroup::VB1_8, address, port, timeout_settings) {
|
||||||
|
return Ok(response);
|
||||||
|
}
|
||||||
|
|
||||||
|
Err(GDError::AutoQuery)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Query a specific (Java) Legacy Server.
|
||||||
|
pub fn query_legacy_specific(group: LegacyGroup, address: &str, port: u16, timeout_settings: Option<TimeoutSettings>) -> GDResult<Response> {
|
||||||
|
match group {
|
||||||
|
LegacyGroup::V1_6 => LegacyV1_6::query(address, port, timeout_settings),
|
||||||
|
LegacyGroup::V1_4 => LegacyV1_4::query(address, port, timeout_settings),
|
||||||
|
LegacyGroup::VB1_8 => LegacyBV1_8::query(address, port, timeout_settings)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Query a Bedrock Server.
|
||||||
|
pub fn query_bedrock(address: &str, port: u16, timeout_settings: Option<TimeoutSettings>) -> GDResult<BedrockResponse> {
|
||||||
|
Bedrock::query(address, port, timeout_settings)
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,31 +1,8 @@
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
Although its a lightly modified version, this file contains code
|
||||||
This file contains lightly modified versions of the original code. (using only the varint parts)
|
by Jaiden Bernard (2021-2022 - MIT) from
|
||||||
Code reference: https://github.com/thisjaiden/golden_apple/blob/master/src/lib.rs
|
https://github.com/thisjaiden/golden_apple/blob/master/src/lib.rs
|
||||||
|
|
||||||
MIT License
|
|
||||||
|
|
||||||
Copyright (c) 2021-2022 Jaiden Bernard
|
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
of this software and associated documentation files (the "Software"), to deal
|
|
||||||
in the Software without restriction, including without limitation the rights
|
|
||||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
copies of the Software, and to permit persons to whom the Software is
|
|
||||||
furnished to do so, subject to the following conditions:
|
|
||||||
|
|
||||||
The above copyright notice and this permission notice shall be included in all
|
|
||||||
copies or substantial portions of the Software.
|
|
||||||
|
|
||||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
||||||
SOFTWARE.
|
|
||||||
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
use crate::{GDError, GDResult};
|
use crate::{GDError, GDResult};
|
||||||
|
|
@ -37,7 +14,9 @@ pub enum Server {
|
||||||
/// Java Edition.
|
/// Java Edition.
|
||||||
Java,
|
Java,
|
||||||
/// Legacy Java.
|
/// Legacy Java.
|
||||||
Legacy(LegacyGroup)
|
Legacy(LegacyGroup),
|
||||||
|
/// Bedrock Edition.
|
||||||
|
Bedrock
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Legacy Java (Versions) Groups.
|
/// Legacy Java (Versions) Groups.
|
||||||
|
|
@ -83,6 +62,67 @@ pub struct Response {
|
||||||
pub server_type: Server
|
pub server_type: Server
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// A Bedrock Edition query response.
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct BedrockResponse {
|
||||||
|
/// Server edition.
|
||||||
|
pub edition: String,
|
||||||
|
/// Server name.
|
||||||
|
pub name: String,
|
||||||
|
/// Version name, example: "1.19.40".
|
||||||
|
pub version_name: String,
|
||||||
|
/// Version protocol, example: 760 (for 1.19.2).
|
||||||
|
pub version_protocol: String,
|
||||||
|
/// Number of server capacity.
|
||||||
|
pub max_players: u32,
|
||||||
|
/// Number of online players.
|
||||||
|
pub online_players: u32,
|
||||||
|
/// Server id.
|
||||||
|
pub id: Option<String>,
|
||||||
|
/// The map.
|
||||||
|
pub map: Option<String>,
|
||||||
|
/// Game mode.
|
||||||
|
pub game_mode: Option<GameMode>,
|
||||||
|
/// Tell's the server type.
|
||||||
|
pub server_type: Server
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Response {
|
||||||
|
pub fn from_bedrock_response(response: BedrockResponse) -> Self {
|
||||||
|
Self {
|
||||||
|
version_name: response.version_name,
|
||||||
|
version_protocol: 0,
|
||||||
|
max_players: response.max_players,
|
||||||
|
online_players: response.online_players,
|
||||||
|
sample_players: None,
|
||||||
|
description: response.name,
|
||||||
|
favicon: None,
|
||||||
|
previews_chat: None,
|
||||||
|
enforces_secure_chat: None,
|
||||||
|
server_type: Server::Bedrock
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A server's game mode (used only by Bedrock servers).
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub enum GameMode {
|
||||||
|
Survival, Creative, Hardcore, Spectator, Adventure
|
||||||
|
}
|
||||||
|
|
||||||
|
impl GameMode {
|
||||||
|
pub fn from_bedrock(value: &&str) -> GDResult<Self> {
|
||||||
|
match *value {
|
||||||
|
"Survival" => Ok(GameMode::Survival),
|
||||||
|
"Creative" => Ok(GameMode::Creative),
|
||||||
|
"Hardcore" => Ok(GameMode::Hardcore),
|
||||||
|
"Spectator" => Ok(GameMode::Spectator),
|
||||||
|
"Adventure" => Ok(GameMode::Adventure),
|
||||||
|
_ => Err(GDError::UnknownEnumCast)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn get_varint(buf: &[u8], pos: &mut usize) -> GDResult<i32> {
|
pub fn get_varint(buf: &[u8], pos: &mut usize) -> GDResult<i32> {
|
||||||
let mut result = 0;
|
let mut result = 0;
|
||||||
|
|
||||||
|
|
|
||||||
23
src/utils.rs
23
src/utils.rs
|
|
@ -98,6 +98,19 @@ pub mod buffer {
|
||||||
Ok(value)
|
Ok(value)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn get_string_utf8_le_unended(buf: &[u8], pos: &mut usize) -> GDResult<String> {
|
||||||
|
let sub_buf = &buf[*pos..];
|
||||||
|
if sub_buf.len() == 0 {
|
||||||
|
return Err(GDError::PacketUnderflow("Unexpectedly short packet for getting an utf8 LE string.".to_string()));
|
||||||
|
}
|
||||||
|
|
||||||
|
let value = std::str::from_utf8(&sub_buf)
|
||||||
|
.map_err(|_| GDError::PacketBad("Badly formatted utf8 LE string.".to_string()))?.to_string();
|
||||||
|
|
||||||
|
*pos += value.len();
|
||||||
|
Ok(value)
|
||||||
|
}
|
||||||
|
|
||||||
pub fn get_string_utf16_be(buf: &[u8], pos: &mut usize) -> GDResult<String> {
|
pub fn get_string_utf16_be(buf: &[u8], pos: &mut usize) -> GDResult<String> {
|
||||||
let sub_buf = &buf[*pos..];
|
let sub_buf = &buf[*pos..];
|
||||||
if sub_buf.len() == 0 {
|
if sub_buf.len() == 0 {
|
||||||
|
|
@ -199,6 +212,16 @@ mod tests {
|
||||||
assert_eq!(pos, 6);
|
assert_eq!(pos, 6);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn get_string_utf8_le_unended_test() {
|
||||||
|
let data = [72, 101, 108, 108, 111];
|
||||||
|
let mut pos = 0;
|
||||||
|
assert_eq!(buffer::get_string_utf8_le_unended(&data, &mut pos).unwrap(), "Hello");
|
||||||
|
assert_eq!(pos, 5);
|
||||||
|
assert!(buffer::get_string_utf8_le_unended(&data, &mut pos).is_err());
|
||||||
|
assert_eq!(pos, 5);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn get_string_utf16_be_test() {
|
fn get_string_utf16_be_test() {
|
||||||
let data = [0x00, 0x48, 0x00, 0x65, 0x00, 0x6c, 0x00, 0x6c, 0x00, 0x6f];
|
let data = [0x00, 0x48, 0x00, 0x65, 0x00, 0x6c, 0x00, 0x6c, 0x00, 0x6f];
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue