diff --git a/CHANGELOG.md b/CHANGELOG.md
index a1dafc3..4970d55 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -2,6 +2,12 @@
Who knows what the future holds...
# 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
[Minecraft](https://www.minecraft.com) support (bedrock not supported yet).
diff --git a/GAMES.md b/GAMES.md
index 223f9c0..712cd02 100644
--- a/GAMES.md
+++ b/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.
# Supported games:
-| Game | Use name | Protocol | Notes |
-|------------------------------------|----------|---------------------------|-------------------------------------------------------------------------------------------|
-| Team Fortress 2 | TF2 | Valve Protocol | |
-| 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: Source | CSS | Valve Protocol | |
-| Day of Defeat: Source | DODS | Valve Protocol | |
-| Left 4 Dead | L4D | Valve Protocol | |
-| Left 4 Dead 2 | L4D2 | Valve Protocol | |
-| Half-Life 2 Deathmatch | HL2DM | Valve Protocol | |
-| Alien Swarm | ALIENS | Valve Protocol | |
-| Alien Swarm: Reactive Drop | ASRD | Valve Protocol | |
-| Insurgency | INS | Valve Protocol | |
-| Insurgency: Sandstorm | INSS | Valve Protocol | Use the query port. |
-| Insurgency: Modern Infantry Combat | INSMIC | Valve Protocol | |
-| Counter-Strike: Condition Zero | CSCZ | Valve Protocol (GoldSrc) | |
-| Day of Defeat | DOD | Valve Protocol (GoldSrc) | |
-| Minecraft | MC | Proprietary | Bedrock not supported yet. |
-| 7 Days To Die | SDTD | Valve Protocol | |
-| ARK: Survival Evolved | ASE | Valve Protocol | |
-| Unturned | UNTURNED | Valve Protocol | |
-| The Forest | TF | Valve Protocol (GoldSrc) | Use the query port. |
-| Team Fortress Classic | TFC | Valve Protocol | |
-| Sven Co-op | SC | Valve Protocol (GoldSrc) | |
-| Rust | RUST | Valve Protocol | |
-| Counter-Strike | CS | Valve Protocol (GoldSrc) | |
-| Arma 2: Operation Arrowhead | ARMA2OA | Valve Protocol | Use the query port. |
-| Day of Infamy | DOI | Valve Protocol | |
-| Half-Life Deathmatch: Source | HLDMS | Valve Protocol | |
+| Game | Use name | Protocol | Notes |
+|------------------------------------|----------|---------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
+| Team Fortress 2 | TF2 | Valve Protocol | |
+| 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: Source | CSS | Valve Protocol | |
+| Day of Defeat: Source | DODS | Valve Protocol | |
+| Left 4 Dead | L4D | Valve Protocol | |
+| Left 4 Dead 2 | L4D2 | Valve Protocol | |
+| Half-Life 2 Deathmatch | HL2DM | Valve Protocol | |
+| Alien Swarm | ALIENS | Valve Protocol | |
+| Alien Swarm: Reactive Drop | ASRD | Valve Protocol | |
+| Insurgency | INS | Valve Protocol | |
+| Insurgency: Sandstorm | INSS | Valve Protocol | Use the query port. |
+| Insurgency: Modern Infantry Combat | INSMIC | Valve Protocol | |
+| Counter-Strike: Condition Zero | CSCZ | Valve Protocol (GoldSrc) | |
+| Day of Defeat | DOD | Valve Protocol (GoldSrc) | |
+| 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 | |
+| ARK: Survival Evolved | ASE | Valve Protocol | |
+| Unturned | UNTURNED | Valve Protocol | |
+| The Forest | TF | Valve Protocol (GoldSrc) | Use the query port. |
+| Team Fortress Classic | TFC | Valve Protocol | |
+| Sven Co-op | SC | Valve Protocol (GoldSrc) | |
+| Rust | RUST | Valve Protocol | |
+| Counter-Strike | CS | Valve Protocol (GoldSrc) | |
+| Arma 2: Operation Arrowhead | ARMA2OA | Valve Protocol | Use the query port. |
+| Day of Infamy | DOI | Valve Protocol | |
+| Half-Life Deathmatch: Source | HLDMS | Valve Protocol | |
## Planned to add support:
_
diff --git a/PROTOCOLS.md b/PROTOCOLS.md
index 06f3d26..0422ebd 100644
--- a/PROTOCOLS.md
+++ b/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:
-| Name | For | Proprietary? | Documentation reference | Notes |
-|----------------|-------|--------------|---------------------------------------------------------------------------|----------------------------------------|
-| 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. |
+| Name | For | Proprietary? | Documentation reference | Notes |
+|----------------|-------|--------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|----------------------------------------|
+| Valve Protocol | Games | No | [Server Queries](https://developer.valvesoftware.com/wiki/Server_queries) | Multi-packet decompression not tested. |
+| Minecraft | Games | Yes | Java: [List Server Protocol](https://wiki.vg/Server_List_Ping)
Bedrock: [Node-GameDig Source](https://github.com/gamedig/node-gamedig/blob/master/protocols/minecraftbedrock.js) | |
## Planned to add support:
_
diff --git a/README.md b/README.md
index 49bd483..61760c7 100644
--- a/README.md
+++ b/README.md
@@ -18,7 +18,7 @@ Team Fortress 2 query example:
use gamedig::games::tf2;
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 {
Err(error) => println!("Couldn't query, error: {error}"),
Ok(r) => println!("{:#?}", r)
diff --git a/examples/master_querant.rs b/examples/master_querant.rs
index 4ab5de0..dd181ba 100644
--- a/examples/master_querant.rs
+++ b/examples/master_querant.rs
@@ -1,7 +1,7 @@
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::protocols::minecraft::{LegacyGroup, Server};
+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;
use gamedig::protocols::valve;
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)?),
"mc" => println!("{:#?}", mc::query(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_vb1_8" => println!("{:#?}", protocols::minecraft::query_specific(Server::Legacy(LegacyGroup::VB1_8), ip, port.unwrap(), None)?),
- "_mc_legacy_v1_4" => println!("{:#?}", protocols::minecraft::query_specific(Server::Legacy(LegacyGroup::V1_4), ip, port.unwrap(), None)?),
- "_mc_legacy_v1_6" => println!("{:#?}", protocols::minecraft::query_specific(Server::Legacy(LegacyGroup::V1_6), ip, port.unwrap(), None)?),
+ "mc_legacy_vb1_8" => println!("{:#?}", mc::query_legacy_specific(LegacyGroup::VB1_8, ip, port)?),
+ "mc_legacy_v1_4" => println!("{:#?}", mc::query_legacy_specific(LegacyGroup::V1_4, ip, port)?),
+ "mc_legacy_v1_6" => println!("{:#?}", mc::query_legacy_specific(LegacyGroup::V1_6, ip, port)?),
"7dtd" => println!("{:#?}", sdtd::query(ip, port)?),
"ase" => println!("{:#?}", ase::query(ip, port)?),
"unturned" => println!("{:#?}", unturned::query(ip, port)?),
diff --git a/examples/minecraft.rs b/examples/minecraft.rs
index 59d34e1..f675508 100644
--- a/examples/minecraft.rs
+++ b/examples/minecraft.rs
@@ -2,7 +2,9 @@
use gamedig::games::mc;
fn main() {
- let response = mc::query("localhost", None); //or Some(25565), None is the default protocol port (which is 25565)
+ //or Some(), 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 {
Err(error) => println!("Couldn't query, error: {error}"),
Ok(r) => println!("{:#?}", r)
diff --git a/examples/tf2.rs b/examples/tf2.rs
index e79cb7f..f901d01 100644
--- a/examples/tf2.rs
+++ b/examples/tf2.rs
@@ -2,7 +2,7 @@
use gamedig::games::tf2;
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 {
Err(error) => println!("Couldn't query, error: {error}"),
Ok(r) => println!("{:#?}", r)
diff --git a/src/errors.rs b/src/errors.rs
index 148aa7d..5f5cae4 100644
--- a/src/errors.rs
+++ b/src/errors.rs
@@ -38,6 +38,8 @@ pub enum GDError {
AutoQuery,
/// A protocol-defined expected format was not met.
ProtocolFormat(String),
+ /// Couldn't parse a value.
+ TypeParse(String),
}
impl fmt::Display for GDError {
@@ -57,6 +59,7 @@ impl fmt::Display for GDError {
GDError::JsonParse(details) => write!(f, "Json parse: {details}"),
GDError::AutoQuery => write!(f, "Auto query failed."),
GDError::ProtocolFormat(details) => write!(f, "Protocol rule: {details}"),
+ GDError::TypeParse(details) => write!(f, "Type parse: {details}"),
}
}
}
diff --git a/src/games/mc.rs b/src/games/mc.rs
index e9b68d8..32443e7 100644
--- a/src/games/mc.rs
+++ b/src/games/mc.rs
@@ -1,39 +1,54 @@
use crate::{GDError, GDResult};
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) -> GDResult {
- minecraft::query(address, port_or_default(port), None)
-}
-
-/// Query a Java Server.
-pub fn query_java(address: &str, port: Option) -> GDResult {
- 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) -> GDResult {
- let unwrapped_port = port_or_default(port);
-
- if let Ok(response) = minecraft::query_specific(Server::Legacy(LegacyGroup::V1_6), address, unwrapped_port, None) {
+ if let Ok(response) = query_java(address, port) {
return Ok(response);
}
- if let Ok(response) = minecraft::query_specific(Server::Legacy(LegacyGroup::V1_4), address, unwrapped_port, None) {
- return Ok(response);
+ if let Ok(response) = query_bedrock(address, port) {
+ 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);
}
Err(GDError::AutoQuery)
}
-fn port_or_default(port: Option) -> u16 {
+/// Query a Java Server.
+pub fn query_java(address: &str, port: Option) -> GDResult {
+ 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) -> GDResult {
+ 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) -> GDResult {
+ minecraft::query_legacy_specific(group, address, port_or_java_default(port), None)
+}
+
+/// Query a Bedrock Server.
+pub fn query_bedrock(address: &str, port: Option) -> GDResult {
+ minecraft::query_bedrock(address, port_or_bedrock_default(port), None)
+}
+
+fn port_or_java_default(port: Option) -> u16 {
match port {
None => 25565,
Some(port) => port
}
}
+
+fn port_or_bedrock_default(port: Option) -> u16 {
+ match port {
+ None => 19132,
+ Some(port) => port
+ }
+}
diff --git a/src/protocols/minecraft/protocol/bedrock.rs b/src/protocols/minecraft/protocol/bedrock.rs
new file mode 100644
index 0000000..f406f4f
--- /dev/null
+++ b/src/protocols/minecraft/protocol/bedrock.rs
@@ -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) -> GDResult {
+ 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 {
+ 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) -> GDResult {
+ Bedrock::new(address, port, timeout_settings)?.get_info()
+ }
+}
diff --git a/src/protocols/minecraft/protocol/mod.rs b/src/protocols/minecraft/protocol/mod.rs
index bcd7c87..a3ae26a 100644
--- a/src/protocols/minecraft/protocol/mod.rs
+++ b/src/protocols/minecraft/protocol/mod.rs
@@ -1,5 +1,6 @@
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::legacy_v1_4::LegacyV1_4;
use crate::protocols::minecraft::protocol::legacy_v1_6::LegacyV1_6;
@@ -10,36 +11,57 @@ mod java;
mod legacy_v1_4;
mod legacy_v1_6;
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) -> GDResult {
- 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);
}
- if let Ok(response) = query_specific(Server::Legacy(LegacyGroup::V1_6), address, port, timeout_settings.clone()) {
- return Ok(response);
+ if let Ok(response) = query_bedrock(address, port, timeout_settings.clone()) {
+ return Ok(Response::from_bedrock_response(response));
}
- if let Ok(response) = query_specific(Server::Legacy(LegacyGroup::V1_4), address, port, timeout_settings.clone()) {
- return Ok(response);
- }
-
- if let Ok(response) = query_specific(Server::Legacy(LegacyGroup::VB1_8), address, port, timeout_settings.clone()) {
+ if let Ok(response) = query_legacy(address, port, timeout_settings) {
return Ok(response);
}
Err(GDError::AutoQuery)
}
-/// Queries a specific Minecraft Server type.
-pub fn query_specific(mc_type: Server, address: &str, port: u16, timeout_settings: Option) -> GDResult {
- match mc_type {
- Server::Java => Java::query(address, port, timeout_settings),
- Server::Legacy(category) => match category {
- 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 Java Server.
+pub fn query_java(address: &str, port: u16, timeout_settings: Option) -> GDResult {
+ Java::query(address, port, timeout_settings)
+}
+
+/// Query a (Java) Legacy Server (1.6 -> 1.4 -> Beta 1.8).
+pub fn query_legacy(address: &str, port: u16, timeout_settings: Option) -> GDResult {
+ 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) -> GDResult {
+ 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) -> GDResult {
+ Bedrock::query(address, port, timeout_settings)
+}
diff --git a/src/protocols/minecraft/types.rs b/src/protocols/minecraft/types.rs
index 46cfa0c..05999a4 100644
--- a/src/protocols/minecraft/types.rs
+++ b/src/protocols/minecraft/types.rs
@@ -1,31 +1,8 @@
/*
-
-This file contains lightly modified versions of the original code. (using only the varint parts)
-Code reference: 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.
-
+Although its a lightly modified version, this file contains code
+by Jaiden Bernard (2021-2022 - MIT) from
+https://github.com/thisjaiden/golden_apple/blob/master/src/lib.rs
*/
use crate::{GDError, GDResult};
@@ -37,7 +14,9 @@ pub enum Server {
/// Java Edition.
Java,
/// Legacy Java.
- Legacy(LegacyGroup)
+ Legacy(LegacyGroup),
+ /// Bedrock Edition.
+ Bedrock
}
/// Legacy Java (Versions) Groups.
@@ -83,6 +62,67 @@ pub struct Response {
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,
+ /// The map.
+ pub map: Option,
+ /// Game mode.
+ pub game_mode: Option,
+ /// 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 {
+ 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 {
let mut result = 0;
diff --git a/src/utils.rs b/src/utils.rs
index 1f90729..8f4c7f5 100644
--- a/src/utils.rs
+++ b/src/utils.rs
@@ -98,6 +98,19 @@ pub mod buffer {
Ok(value)
}
+ pub fn get_string_utf8_le_unended(buf: &[u8], pos: &mut usize) -> GDResult {
+ 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 {
let sub_buf = &buf[*pos..];
if sub_buf.len() == 0 {
@@ -199,6 +212,16 @@ mod tests {
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]
fn get_string_utf16_be_test() {
let data = [0x00, 0x48, 0x00, 0x65, 0x00, 0x6c, 0x00, 0x6c, 0x00, 0x6f];