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:
CosminPerRam 2022-12-05 18:47:35 +02:00 committed by GitHub
parent ae14e37e60
commit 91f8bbb9fe
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
13 changed files with 319 additions and 107 deletions

View file

@ -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).

View file

@ -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:
_

View file

@ -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) <br> Bedrock: [Node-GameDig Source](https://github.com/gamedig/node-gamedig/blob/master/protocols/minecraftbedrock.js) | |
## Planned to add support:
_

View file

@ -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)

View file

@ -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)?),

View file

@ -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(<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 {
Err(error) => println!("Couldn't query, error: {error}"),
Ok(r) => println!("{:#?}", r)

View file

@ -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)

View file

@ -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}"),
}
}
}

View file

@ -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<u16>) -> GDResult<Response> {
minecraft::query(address, port_or_default(port), None)
}
/// 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) {
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>) -> 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 {
None => 25565,
Some(port) => port
}
}
fn port_or_bedrock_default(port: Option<u16>) -> u16 {
match port {
None => 19132,
Some(port) => port
}
}

View 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()
}
}

View file

@ -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<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);
}
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<TimeoutSettings>) -> GDResult<Response> {
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<TimeoutSettings>) -> GDResult<Response> {
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<TimeoutSettings>) -> GDResult<Response> {
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)
}

View file

@ -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<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> {
let mut result = 0;

View file

@ -98,6 +98,19 @@ pub mod buffer {
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> {
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];