mirror of
https://github.com/tribufu/rust-gamedig
synced 2026-06-01 09:42:41 +00:00
[Crate] Refactor: Buffer (#62)
* merge: local -> fresh pr * fix: utf16 bugs + clean up * docs: buffer * [feat/buffer] Replaced errors, partial valve protocol ported * fix: change buffer name + add endian switch * chore: update module * refactor: valve_master * refactor: mc bedrock * refactor: mc java * refactor: mc types * refactor: mc legacy 1.8 * refactor: mc legacy 1.4 * refactor: valve * refactor: valve types * refactor: quake * refactor: mc legacy 1.6 * refactor: gamespy 1 * fix: make switch endian move cursor * fix: reset cursor on switch * chore: add switch endian tests * chore: remove todo comment * chore: clean up buffer generic types * refactor: prop len when switching in mc bedrock * fix: tests and current pos fn * refactor: ffow * refactor: jc2mp * refactor: gs 3 * refactor: gs 2 * fix: mc bedrock prop on move + move data * fix: mc java lifetime error * fix: mc legacy 1.6 using pub not pub crate * fix: quake client lifetime * fix: quake 2 clippy warning * fix: valve lifetime issue * fix: buffer test * chore: format to keep ci happy * fix: buffer move_cursor * fix: quake client * feat: GameSpy 1 small optimization * fix: incomplete gamespy 3 fix * fix: gamespy 3 fix * fix: minecraft java * fix: minecraft bedrock * feat: update the CHANGELOG to mention the buffer rewrite and thank @cainthebest for it * fix: minecraft legacy 1.6 --------- Co-authored-by: CosminPerRam <cosmin.p@live.com>
This commit is contained in:
parent
a8342296d6
commit
66cc39eb26
20 changed files with 859 additions and 568 deletions
|
|
@ -1,6 +1,9 @@
|
|||
use byteorder::LittleEndian;
|
||||
|
||||
use crate::buffer::Utf8Decoder;
|
||||
use crate::protocols::gamespy::common::has_password;
|
||||
use crate::{
|
||||
bufferer::{Bufferer, Endianess},
|
||||
buffer::Buffer,
|
||||
protocols::{
|
||||
gamespy::one::{Player, Response},
|
||||
types::TimeoutSettings,
|
||||
|
|
@ -29,9 +32,9 @@ fn get_server_values(
|
|||
|
||||
while !is_finished {
|
||||
let data = socket.receive(None)?;
|
||||
let mut bufferer = Bufferer::new_with_data(Endianess::Little, &data);
|
||||
let mut bufferer = Buffer::<LittleEndian>::new(&data);
|
||||
|
||||
let mut as_string = bufferer.get_string_utf8_unended()?;
|
||||
let mut as_string = bufferer.read_string::<Utf8Decoder>(None)?;
|
||||
as_string.remove(0);
|
||||
|
||||
let splited: Vec<String> = as_string.split('\\').map(str::to_string).collect();
|
||||
|
|
@ -47,8 +50,7 @@ fn get_server_values(
|
|||
server_values.insert(key, value);
|
||||
}
|
||||
|
||||
is_finished = server_values.contains_key("final");
|
||||
server_values.remove("final");
|
||||
is_finished = server_values.remove("final").is_some();
|
||||
|
||||
let query_data = server_values.get("queryid");
|
||||
|
||||
|
|
|
|||
|
|
@ -1,4 +1,6 @@
|
|||
use crate::bufferer::{Bufferer, Endianess};
|
||||
use byteorder::{BigEndian, LittleEndian};
|
||||
|
||||
use crate::buffer::{Buffer, Utf8Decoder};
|
||||
use crate::protocols::gamespy::common::has_password;
|
||||
use crate::protocols::gamespy::three::{Player, Response, Team};
|
||||
use crate::protocols::types::TimeoutSettings;
|
||||
|
|
@ -73,19 +75,19 @@ impl GameSpy3 {
|
|||
})
|
||||
}
|
||||
|
||||
fn receive(&mut self, size: Option<usize>, kind: u8) -> GDResult<Bufferer> {
|
||||
fn receive(&mut self, size: Option<usize>, kind: u8) -> GDResult<Vec<u8>> {
|
||||
let received = self.socket.receive(size.or(Some(PACKET_SIZE)))?;
|
||||
let mut buf = Bufferer::new_with_data(Endianess::Big, &received);
|
||||
let mut buf = Buffer::<BigEndian>::new(&received);
|
||||
|
||||
if buf.get_u8()? != kind {
|
||||
if buf.read::<u8>()? != kind {
|
||||
return Err(GDError::PacketBad);
|
||||
}
|
||||
|
||||
if buf.get_u32()? != THIS_SESSION_ID {
|
||||
if buf.read::<u32>()? != THIS_SESSION_ID {
|
||||
return Err(GDError::PacketBad);
|
||||
}
|
||||
|
||||
Ok(buf)
|
||||
Ok(buf.remaining_bytes().to_vec())
|
||||
}
|
||||
|
||||
fn make_initial_handshake(&mut self) -> GDResult<Option<i32>> {
|
||||
|
|
@ -100,9 +102,10 @@ impl GameSpy3 {
|
|||
.to_bytes(),
|
||||
)?;
|
||||
|
||||
let mut buf = self.receive(Some(16), 9)?;
|
||||
let data = self.receive(Some(16), 9)?;
|
||||
let mut buf = Buffer::<LittleEndian>::new(&data);
|
||||
|
||||
let challenge_as_string = buf.get_string_utf8()?;
|
||||
let challenge_as_string = buf.read_string::<Utf8Decoder>(None)?;
|
||||
let challenge = challenge_as_string
|
||||
.parse()
|
||||
.map_err(|_| GDError::TypeParse)?;
|
||||
|
|
@ -135,21 +138,22 @@ impl GameSpy3 {
|
|||
let mut expected_number_of_packets: Option<usize> = None;
|
||||
|
||||
while expected_number_of_packets.is_none() || values.len() != expected_number_of_packets.unwrap() {
|
||||
let mut buf = self.receive(None, 0)?;
|
||||
let received_data = self.receive(None, 0)?;
|
||||
let mut buf = Buffer::<BigEndian>::new(&received_data);
|
||||
|
||||
if self.single_packets {
|
||||
buf.move_position_ahead(11);
|
||||
return Ok(vec![buf.remaining_data().to_vec()]);
|
||||
buf.move_cursor(11)?;
|
||||
return Ok(vec![buf.remaining_bytes().to_vec()]);
|
||||
}
|
||||
|
||||
if buf.get_string_utf8()? != "splitnum" {
|
||||
if buf.read_string::<Utf8Decoder>(None)? != "splitnum" {
|
||||
return Err(GDError::PacketBad);
|
||||
}
|
||||
|
||||
let id = buf.get_u8()?;
|
||||
let id = buf.read::<u8>()?;
|
||||
let is_last = (id & 0x80) > 0;
|
||||
let packet_id = (id & 0x7f) as usize;
|
||||
buf.move_position_ahead(1); //unknown byte regarding packet no.
|
||||
buf.move_cursor(1)?; //unknown byte regarding packet no.
|
||||
|
||||
if is_last {
|
||||
expected_number_of_packets = Some(packet_id + 1);
|
||||
|
|
@ -159,7 +163,7 @@ impl GameSpy3 {
|
|||
values.push(Vec::new());
|
||||
}
|
||||
|
||||
values[packet_id] = buf.remaining_data().to_vec();
|
||||
values[packet_id] = buf.remaining_bytes().to_vec();
|
||||
}
|
||||
|
||||
if values.iter().any(|v| v.is_empty()) {
|
||||
|
|
@ -173,19 +177,19 @@ impl GameSpy3 {
|
|||
pub(crate) fn data_to_map(packet: &[u8]) -> GDResult<(HashMap<String, String>, Vec<u8>)> {
|
||||
let mut vars = HashMap::new();
|
||||
|
||||
let mut buf = Bufferer::new_with_data(Endianess::Big, packet);
|
||||
while !buf.is_remaining_empty() {
|
||||
let key = buf.get_string_utf8()?;
|
||||
let mut buf = Buffer::<BigEndian>::new(packet);
|
||||
while buf.remaining_length() != 0 {
|
||||
let key = buf.read_string::<Utf8Decoder>(None)?;
|
||||
if key.is_empty() {
|
||||
break;
|
||||
}
|
||||
|
||||
let value = buf.get_string_utf8_optional()?;
|
||||
let value = buf.read_string::<Utf8Decoder>(None)?;
|
||||
|
||||
vars.insert(key, value);
|
||||
}
|
||||
|
||||
Ok((vars, buf.remaining_data().to_vec()))
|
||||
Ok((vars, buf.remaining_bytes().to_vec()))
|
||||
}
|
||||
|
||||
/// If there are parsing problems using the `query` function, you can directly
|
||||
|
|
@ -212,16 +216,16 @@ fn parse_players_and_teams(packets: Vec<Vec<u8>>) -> GDResult<(Vec<Player>, Vec<
|
|||
let mut teams_data: Vec<HashMap<String, String>> = vec![HashMap::new()];
|
||||
|
||||
for packet in packets {
|
||||
let mut buf = Bufferer::new_with_data(Endianess::Little, &packet);
|
||||
let mut buf = Buffer::<LittleEndian>::new(&packet);
|
||||
|
||||
while !buf.is_remaining_empty() {
|
||||
if buf.get_u8()? < 3 {
|
||||
while buf.remaining_length() != 0 {
|
||||
if buf.read::<u8>()? < 3 {
|
||||
continue;
|
||||
}
|
||||
|
||||
buf.move_position_backward(1);
|
||||
buf.move_cursor(1)?;
|
||||
|
||||
let field = buf.get_string_utf8()?;
|
||||
let field = buf.read_string::<Utf8Decoder>(None)?;
|
||||
if field.is_empty() {
|
||||
continue;
|
||||
}
|
||||
|
|
@ -248,15 +252,15 @@ fn parse_players_and_teams(packets: Vec<Vec<u8>>) -> GDResult<(Vec<Player>, Vec<
|
|||
}
|
||||
};
|
||||
|
||||
let mut offset = buf.get_u8()? as usize;
|
||||
let mut offset = buf.read::<u8>()? as usize;
|
||||
|
||||
let data = match field_type.is_none() {
|
||||
true => &mut players_data,
|
||||
false => &mut teams_data,
|
||||
};
|
||||
|
||||
while !buf.is_remaining_empty() {
|
||||
let item = buf.get_string_utf8()?;
|
||||
while buf.remaining_length() != 0 {
|
||||
let item = buf.read_string::<Utf8Decoder>(None)?;
|
||||
if item.is_empty() {
|
||||
break;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,8 +1,9 @@
|
|||
use crate::bufferer::{Bufferer, Endianess};
|
||||
use crate::buffer::{Buffer, Utf8Decoder};
|
||||
use crate::protocols::gamespy::two::{Player, Response, Team};
|
||||
use crate::protocols::types::TimeoutSettings;
|
||||
use crate::socket::{Socket, UdpSocket};
|
||||
use crate::{GDError, GDResult};
|
||||
use byteorder::BigEndian;
|
||||
use std::collections::HashMap;
|
||||
use std::net::SocketAddr;
|
||||
|
||||
|
|
@ -28,12 +29,12 @@ macro_rules! table_extract_parse {
|
|||
};
|
||||
}
|
||||
|
||||
fn data_as_table(data: &mut Bufferer) -> GDResult<(HashMap<String, Vec<String>>, usize)> {
|
||||
if data.get_u8()? != 0 {
|
||||
fn data_as_table(data: &mut Buffer<BigEndian>) -> GDResult<(HashMap<String, Vec<String>>, usize)> {
|
||||
if data.read::<u8>()? != 0 {
|
||||
Err(GDError::PacketBad)?
|
||||
}
|
||||
|
||||
let rows = data.get_u8()? as usize;
|
||||
let rows = data.read::<u8>()? as usize;
|
||||
|
||||
if rows == 0 {
|
||||
return Ok((HashMap::new(), 0));
|
||||
|
|
@ -41,21 +42,28 @@ fn data_as_table(data: &mut Bufferer) -> GDResult<(HashMap<String, Vec<String>>,
|
|||
|
||||
let mut column_heads = Vec::new();
|
||||
|
||||
let mut current_column = data.get_string_utf8()?;
|
||||
let mut current_column = data.read_string::<Utf8Decoder>(None)?;
|
||||
while !current_column.is_empty() {
|
||||
column_heads.push(current_column);
|
||||
current_column = data.get_string_utf8()?;
|
||||
current_column = data.read_string::<Utf8Decoder>(None)?
|
||||
}
|
||||
|
||||
let columns = column_heads.len();
|
||||
let mut table = HashMap::with_capacity(columns);
|
||||
for head in &column_heads {
|
||||
table.insert(head.clone(), Vec::new()); // TODO: This doesn't look good nor it is performant, fix later
|
||||
// TODO: This doesn't look good nor it is performant, fix later
|
||||
// By using &column_heads in the for loop instead of cloning column_heads, you
|
||||
// avoid creating an unnecessary copy. However, column_heads is a
|
||||
// Vec<String> and head is a &String (a reference to a string). Hence, to use
|
||||
// head as a key to the HashMap, we still need to call clone(). This is because
|
||||
// HashMap takes ownership of its keys and we cannot give it a reference to a
|
||||
// local variable (head) that will be dropped at the end of the function.
|
||||
table.insert(head.clone(), Vec::new());
|
||||
}
|
||||
|
||||
for _ in 0 .. rows {
|
||||
for column in column_heads.iter() {
|
||||
let value = data.get_string_utf8()?;
|
||||
let value = data.read_string::<Utf8Decoder>(None)?;
|
||||
table.get_mut(column).ok_or(GDError::PacketBad)?.push(value);
|
||||
}
|
||||
}
|
||||
|
|
@ -71,36 +79,33 @@ impl GameSpy2 {
|
|||
Ok(Self { socket })
|
||||
}
|
||||
|
||||
fn request_data(&mut self) -> GDResult<Bufferer> {
|
||||
fn request_data(&mut self) -> GDResult<(Vec<u8>, usize)> {
|
||||
self.socket
|
||||
.send(&[0xFE, 0xFD, 0x00, 0x00, 0x00, 0x00, 0x01, 0xFF, 0xFF, 0xFF])?;
|
||||
|
||||
let received = self.socket.receive(None)?;
|
||||
let mut buf = Bufferer::new_with_data(Endianess::Big, &received);
|
||||
|
||||
if buf.get_u8()? != 0 {
|
||||
let mut buf = Buffer::<BigEndian>::new(&received);
|
||||
if buf.read::<u8>()? != 0 || buf.read::<u32>()? != 1 {
|
||||
return Err(GDError::PacketBad);
|
||||
}
|
||||
|
||||
if buf.get_u32()? != 1 {
|
||||
return Err(GDError::PacketBad);
|
||||
}
|
||||
|
||||
Ok(buf)
|
||||
let buf_index = buf.current_position();
|
||||
Ok((received, buf_index))
|
||||
}
|
||||
}
|
||||
|
||||
fn get_server_vars(bufferer: &mut Bufferer) -> GDResult<HashMap<String, String>> {
|
||||
fn get_server_vars(bufferer: &mut Buffer<BigEndian>) -> GDResult<HashMap<String, String>> {
|
||||
let mut values = HashMap::new();
|
||||
|
||||
let mut done_processing_vars = false;
|
||||
while !done_processing_vars && !bufferer.is_remaining_empty() {
|
||||
let key = bufferer.get_string_utf8()?;
|
||||
let value = bufferer.get_string_utf8_optional()?;
|
||||
while !done_processing_vars && bufferer.remaining_length() != 0 {
|
||||
let key = bufferer.read_string::<Utf8Decoder>(None)?;
|
||||
let value = bufferer.read_string::<Utf8Decoder>(None)?;
|
||||
|
||||
if key.is_empty() {
|
||||
if value.is_empty() {
|
||||
bufferer.move_position_backward(1);
|
||||
bufferer.move_cursor(-1)?;
|
||||
done_processing_vars = true;
|
||||
}
|
||||
|
||||
|
|
@ -113,7 +118,7 @@ fn get_server_vars(bufferer: &mut Bufferer) -> GDResult<HashMap<String, String>>
|
|||
Ok(values)
|
||||
}
|
||||
|
||||
fn get_teams(bufferer: &mut Bufferer) -> GDResult<Vec<Team>> {
|
||||
fn get_teams(bufferer: &mut Buffer<BigEndian>) -> GDResult<Vec<Team>> {
|
||||
let mut teams = Vec::new();
|
||||
|
||||
let (table, entries) = data_as_table(bufferer)?;
|
||||
|
|
@ -128,7 +133,7 @@ fn get_teams(bufferer: &mut Bufferer) -> GDResult<Vec<Team>> {
|
|||
Ok(teams)
|
||||
}
|
||||
|
||||
fn get_players(bufferer: &mut Bufferer) -> GDResult<Vec<Player>> {
|
||||
fn get_players(bufferer: &mut Buffer<BigEndian>) -> GDResult<Vec<Player>> {
|
||||
let mut players = Vec::new();
|
||||
|
||||
let (table, entries) = data_as_table(bufferer)?;
|
||||
|
|
@ -147,10 +152,13 @@ fn get_players(bufferer: &mut Bufferer) -> GDResult<Vec<Player>> {
|
|||
|
||||
pub fn query(address: &SocketAddr, timeout_settings: Option<TimeoutSettings>) -> GDResult<Response> {
|
||||
let mut client = GameSpy2::new(address, timeout_settings)?;
|
||||
let mut data = client.request_data()?;
|
||||
let (data, buf_index) = client.request_data()?;
|
||||
|
||||
let mut server_vars = get_server_vars(&mut data)?;
|
||||
let players = get_players(&mut data)?;
|
||||
let mut buffer = Buffer::<BigEndian>::new(&data);
|
||||
buffer.move_cursor(buf_index as isize)?;
|
||||
|
||||
let mut server_vars = get_server_vars(&mut buffer)?;
|
||||
let players = get_players(&mut buffer)?;
|
||||
|
||||
let players_online = match server_vars.remove("numplayers") {
|
||||
None => players.len(),
|
||||
|
|
@ -171,7 +179,7 @@ pub fn query(address: &SocketAddr, timeout_settings: Option<TimeoutSettings>) ->
|
|||
name: server_vars.remove("hostname").ok_or(GDError::PacketBad)?,
|
||||
map: server_vars.remove("mapname").ok_or(GDError::PacketBad)?,
|
||||
has_password: server_vars.remove("password").ok_or(GDError::PacketBad)? == "1",
|
||||
teams: get_teams(&mut data)?,
|
||||
teams: get_teams(&mut buffer)?,
|
||||
players_maximum: server_vars
|
||||
.remove("maxplayers")
|
||||
.ok_or(GDError::PacketBad)?
|
||||
|
|
|
|||
|
|
@ -1,8 +1,7 @@
|
|||
// 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::{
|
||||
bufferer::{Bufferer, Endianess},
|
||||
buffer::{Buffer, Utf8Decoder},
|
||||
protocols::{
|
||||
minecraft::{BedrockResponse, GameMode, Server},
|
||||
types::TimeoutSettings,
|
||||
|
|
@ -12,8 +11,11 @@ use crate::{
|
|||
GDError::{PacketBad, TypeParse},
|
||||
GDResult,
|
||||
};
|
||||
|
||||
use std::net::SocketAddr;
|
||||
|
||||
use byteorder::LittleEndian;
|
||||
|
||||
pub struct Bedrock {
|
||||
socket: UdpSocket,
|
||||
}
|
||||
|
|
@ -40,35 +42,36 @@ impl Bedrock {
|
|||
fn get_info(&mut self) -> GDResult<BedrockResponse> {
|
||||
self.send_status_request()?;
|
||||
|
||||
let mut buffer = Bufferer::new_with_data(Endianess::Little, &self.socket.receive(None)?);
|
||||
let received = self.socket.receive(None)?;
|
||||
let mut buffer = Buffer::<LittleEndian>::new(&received);
|
||||
|
||||
if buffer.get_u8()? != 0x1c {
|
||||
if buffer.read::<u8>()? != 0x1c {
|
||||
return Err(PacketBad);
|
||||
}
|
||||
|
||||
// Checking for our nonce directly from a u64 (as the nonce is 8 bytes).
|
||||
if buffer.get_u64()? != 9833440827789222417 {
|
||||
if buffer.read::<u64>()? != 9833440827789222417 {
|
||||
return Err(PacketBad);
|
||||
}
|
||||
|
||||
// These 8 bytes are identical to the serverId string we receive in decimal
|
||||
// below
|
||||
buffer.move_position_ahead(8);
|
||||
buffer.move_cursor(8)?;
|
||||
|
||||
// Verifying the magic value (as we need 16 bytes, cast to two u64 values)
|
||||
if buffer.get_u64()? != 18374403896610127616 {
|
||||
if buffer.read::<u64>()? != 18374403896610127616 {
|
||||
return Err(PacketBad);
|
||||
}
|
||||
|
||||
if buffer.get_u64()? != 8671175388723805693 {
|
||||
if buffer.read::<u64>()? != 8671175388723805693 {
|
||||
return Err(PacketBad);
|
||||
}
|
||||
|
||||
let remaining_length = buffer.as_endianess(Endianess::Big).get_u16()? as usize;
|
||||
buffer.move_position_ahead(2);
|
||||
let remaining_length = buffer.switch_endian_chunk(2)?.read::<u16>()? as usize;
|
||||
|
||||
error_by_expected_size(remaining_length, buffer.remaining_length())?;
|
||||
|
||||
let binding = buffer.get_string_utf8_unended()?;
|
||||
let binding = buffer.read_string::<Utf8Decoder>(None)?;
|
||||
let status: Vec<&str> = binding.split(';').collect();
|
||||
|
||||
// We must have at least 6 values
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
use crate::{
|
||||
bufferer::{Bufferer, Endianess},
|
||||
buffer::Buffer,
|
||||
protocols::{
|
||||
minecraft::{as_varint, get_string, get_varint, JavaResponse, Player, Server},
|
||||
types::TimeoutSettings,
|
||||
|
|
@ -8,8 +8,10 @@ use crate::{
|
|||
GDError::{JsonParse, PacketBad},
|
||||
GDResult,
|
||||
};
|
||||
|
||||
use std::net::SocketAddr;
|
||||
|
||||
use byteorder::LittleEndian;
|
||||
use serde_json::Value;
|
||||
|
||||
#[rustfmt::skip]
|
||||
|
|
@ -43,14 +45,15 @@ impl Java {
|
|||
.send(&[as_varint(data.len() as i32), data].concat())
|
||||
}
|
||||
|
||||
fn receive(&mut self) -> GDResult<Bufferer> {
|
||||
let mut buffer = Bufferer::new_with_data(Endianess::Little, &self.socket.receive(None)?);
|
||||
fn receive(&mut self) -> GDResult<Vec<u8>> {
|
||||
let data = &self.socket.receive(None)?;
|
||||
let mut buffer = Buffer::<LittleEndian>::new(data);
|
||||
|
||||
let _packet_length = get_varint(&mut buffer)? as usize;
|
||||
// this declared 'packet length' from within the packet might be wrong (?), not
|
||||
// checking with it...
|
||||
|
||||
Ok(buffer)
|
||||
Ok(buffer.remaining_bytes().to_vec())
|
||||
}
|
||||
|
||||
fn send_handshake(&mut self) -> GDResult<()> {
|
||||
|
|
@ -82,7 +85,8 @@ impl Java {
|
|||
self.send_status_request()?;
|
||||
self.send_ping_request()?;
|
||||
|
||||
let mut buffer = self.receive()?;
|
||||
let socket_data = self.receive()?;
|
||||
let mut buffer = Buffer::<LittleEndian>::new(&socket_data);
|
||||
|
||||
if get_varint(&mut buffer)? != 0 {
|
||||
// first var int is the packet id
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
use crate::{
|
||||
bufferer::{Bufferer, Endianess},
|
||||
buffer::{Buffer, Utf16Decoder},
|
||||
protocols::{
|
||||
minecraft::{JavaResponse, LegacyGroup, Server},
|
||||
types::TimeoutSettings,
|
||||
|
|
@ -9,8 +9,11 @@ use crate::{
|
|||
GDError::{PacketBad, ProtocolFormat},
|
||||
GDResult,
|
||||
};
|
||||
|
||||
use std::net::SocketAddr;
|
||||
|
||||
use byteorder::BigEndian;
|
||||
|
||||
pub struct LegacyBV1_8 {
|
||||
socket: TcpSocket,
|
||||
}
|
||||
|
|
@ -29,16 +32,16 @@ impl LegacyBV1_8 {
|
|||
self.send_initial_request()?;
|
||||
|
||||
let data = self.socket.receive(None)?;
|
||||
let mut buffer = Bufferer::new_with_data(Endianess::Big, &data);
|
||||
let mut buffer = Buffer::<BigEndian>::new(&data);
|
||||
|
||||
if buffer.get_u8()? != 0xFF {
|
||||
if buffer.read::<u8>()? != 0xFF {
|
||||
return Err(ProtocolFormat);
|
||||
}
|
||||
|
||||
let length = buffer.get_u16()? * 2;
|
||||
let length = buffer.read::<u16>()? * 2;
|
||||
error_by_expected_size((length + 3) as usize, data.len())?;
|
||||
|
||||
let packet_string = buffer.get_string_utf16()?;
|
||||
let packet_string = buffer.read_string::<Utf16Decoder<BigEndian>>(None)?;
|
||||
|
||||
let split: Vec<&str> = packet_string.split('§').collect();
|
||||
error_by_expected_size(3, split.len())?;
|
||||
|
|
|
|||
|
|
@ -1,5 +1,7 @@
|
|||
use byteorder::BigEndian;
|
||||
|
||||
use crate::{
|
||||
bufferer::{Bufferer, Endianess},
|
||||
buffer::{Buffer, Utf16Decoder},
|
||||
protocols::{
|
||||
minecraft::{protocol::legacy_v1_6::LegacyV1_6, JavaResponse, LegacyGroup, Server},
|
||||
types::TimeoutSettings,
|
||||
|
|
@ -29,20 +31,20 @@ impl LegacyV1_4 {
|
|||
self.send_initial_request()?;
|
||||
|
||||
let data = self.socket.receive(None)?;
|
||||
let mut buffer = Bufferer::new_with_data(Endianess::Big, &data);
|
||||
let mut buffer = Buffer::<BigEndian>::new(&data);
|
||||
|
||||
if buffer.get_u8()? != 0xFF {
|
||||
if buffer.read::<u8>()? != 0xFF {
|
||||
return Err(ProtocolFormat);
|
||||
}
|
||||
|
||||
let length = buffer.get_u16()? * 2;
|
||||
let length = buffer.read::<u16>()? * 2;
|
||||
error_by_expected_size((length + 3) as usize, data.len())?;
|
||||
|
||||
if LegacyV1_6::is_protocol(&mut buffer)? {
|
||||
return LegacyV1_6::get_response(&mut buffer);
|
||||
}
|
||||
|
||||
let packet_string = buffer.get_string_utf16()?;
|
||||
let packet_string = buffer.read_string::<Utf16Decoder<BigEndian>>(None)?;
|
||||
|
||||
let split: Vec<&str> = packet_string.split('§').collect();
|
||||
error_by_expected_size(3, split.len())?;
|
||||
|
|
|
|||
|
|
@ -1,5 +1,7 @@
|
|||
use byteorder::BigEndian;
|
||||
|
||||
use crate::{
|
||||
bufferer::{Bufferer, Endianess},
|
||||
buffer::{Buffer, Utf16Decoder},
|
||||
protocols::{
|
||||
minecraft::{JavaResponse, LegacyGroup, Server},
|
||||
types::TimeoutSettings,
|
||||
|
|
@ -36,29 +38,34 @@ impl LegacyV1_6 {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
pub fn is_protocol(buffer: &mut Bufferer) -> GDResult<bool> {
|
||||
pub(crate) fn is_protocol(buffer: &mut Buffer<BigEndian>) -> GDResult<bool> {
|
||||
let state = buffer
|
||||
.remaining_data()
|
||||
.remaining_bytes()
|
||||
.starts_with(&[0x00, 0xA7, 0x00, 0x31, 0x00, 0x00]);
|
||||
|
||||
if state {
|
||||
buffer.move_position_ahead(6);
|
||||
buffer.move_cursor(6)?;
|
||||
}
|
||||
|
||||
Ok(state)
|
||||
}
|
||||
|
||||
pub fn get_response(buffer: &mut Bufferer) -> GDResult<JavaResponse> {
|
||||
let packet_string = buffer.get_string_utf16()?;
|
||||
|
||||
let split: Vec<&str> = packet_string.split('\x00').collect();
|
||||
error_by_expected_size(5, split.len())?;
|
||||
|
||||
let version_protocol = split[0].parse().map_err(|_| PacketBad)?;
|
||||
let version_name = split[1].to_string();
|
||||
let description = split[2].to_string();
|
||||
let online_players = split[3].parse().map_err(|_| PacketBad)?;
|
||||
let max_players = split[4].parse().map_err(|_| PacketBad)?;
|
||||
pub(crate) fn get_response(buffer: &mut Buffer<BigEndian>) -> GDResult<JavaResponse> {
|
||||
// This is a specific order!
|
||||
let version_protocol = buffer
|
||||
.read_string::<Utf16Decoder<BigEndian>>(None)?
|
||||
.parse()
|
||||
.map_err(|_| PacketBad)?;
|
||||
let version_name = buffer.read_string::<Utf16Decoder<BigEndian>>(None)?;
|
||||
let description = buffer.read_string::<Utf16Decoder<BigEndian>>(None)?;
|
||||
let online_players = buffer
|
||||
.read_string::<Utf16Decoder<BigEndian>>(None)?
|
||||
.parse()
|
||||
.map_err(|_| PacketBad)?;
|
||||
let max_players = buffer
|
||||
.read_string::<Utf16Decoder<BigEndian>>(None)?
|
||||
.parse()
|
||||
.map_err(|_| PacketBad)?;
|
||||
|
||||
Ok(JavaResponse {
|
||||
version_name,
|
||||
|
|
@ -78,13 +85,13 @@ impl LegacyV1_6 {
|
|||
self.send_initial_request()?;
|
||||
|
||||
let data = self.socket.receive(None)?;
|
||||
let mut buffer = Bufferer::new_with_data(Endianess::Big, &data);
|
||||
let mut buffer = Buffer::<BigEndian>::new(&data);
|
||||
|
||||
if buffer.get_u8()? != 0xFF {
|
||||
if buffer.read::<u8>()? != 0xFF {
|
||||
return Err(ProtocolFormat);
|
||||
}
|
||||
|
||||
let length = buffer.get_u16()? * 2;
|
||||
let length = buffer.read::<u16>()? * 2;
|
||||
error_by_expected_size((length + 3) as usize, data.len())?;
|
||||
|
||||
if !LegacyV1_6::is_protocol(&mut buffer)? {
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@
|
|||
// https://github.com/thisjaiden/golden_apple/blob/master/src/lib.rs
|
||||
|
||||
use crate::{
|
||||
bufferer::Bufferer,
|
||||
buffer::Buffer,
|
||||
protocols::{
|
||||
types::{CommonPlayer, CommonResponse, GenericPlayer},
|
||||
GenericResponse,
|
||||
|
|
@ -12,6 +12,7 @@ use crate::{
|
|||
GDResult,
|
||||
};
|
||||
|
||||
use byteorder::ByteOrder;
|
||||
#[cfg(feature = "serde")]
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
|
|
@ -179,14 +180,14 @@ impl GameMode {
|
|||
}
|
||||
}
|
||||
|
||||
pub(crate) fn get_varint(buffer: &mut Bufferer) -> GDResult<i32> {
|
||||
pub(crate) fn get_varint<B: ByteOrder>(buffer: &mut Buffer<B>) -> GDResult<i32> {
|
||||
let mut result = 0;
|
||||
|
||||
let msb: u8 = 0b10000000;
|
||||
let mask: u8 = !msb;
|
||||
|
||||
for i in 0 .. 5 {
|
||||
let current_byte = buffer.get_u8()?;
|
||||
let current_byte = buffer.read::<u8>()?;
|
||||
|
||||
result |= ((current_byte & mask) as i32) << (7 * i);
|
||||
|
||||
|
|
@ -227,12 +228,12 @@ pub(crate) fn as_varint(value: i32) -> Vec<u8> {
|
|||
bytes
|
||||
}
|
||||
|
||||
pub(crate) fn get_string(buffer: &mut Bufferer) -> GDResult<String> {
|
||||
pub(crate) fn get_string<B: ByteOrder>(buffer: &mut Buffer<B>) -> GDResult<String> {
|
||||
let length = get_varint(buffer)? as usize;
|
||||
let mut text = Vec::with_capacity(length);
|
||||
|
||||
for _ in 0 .. length {
|
||||
text.push(buffer.get_u8()?)
|
||||
text.push(buffer.read::<u8>()?)
|
||||
}
|
||||
|
||||
String::from_utf8(text).map_err(|_| PacketBad)
|
||||
|
|
|
|||
|
|
@ -1,4 +1,6 @@
|
|||
use crate::bufferer::{Bufferer, Endianess};
|
||||
use byteorder::LittleEndian;
|
||||
|
||||
use crate::buffer::{Buffer, Utf8Decoder};
|
||||
use crate::protocols::quake::types::Response;
|
||||
use crate::protocols::types::TimeoutSettings;
|
||||
use crate::socket::{Socket, UdpSocket};
|
||||
|
|
@ -15,10 +17,7 @@ pub(crate) trait QuakeClient {
|
|||
fn parse_player_string(data: Iter<&str>) -> GDResult<Self::Player>;
|
||||
}
|
||||
|
||||
fn get_data<Client: QuakeClient>(
|
||||
address: &SocketAddr,
|
||||
timeout_settings: Option<TimeoutSettings>,
|
||||
) -> GDResult<Bufferer> {
|
||||
fn get_data<Client: QuakeClient>(address: &SocketAddr, timeout_settings: Option<TimeoutSettings>) -> GDResult<Vec<u8>> {
|
||||
let mut socket = UdpSocket::new(address)?;
|
||||
socket.apply_timeout(timeout_settings)?;
|
||||
|
||||
|
|
@ -32,24 +31,24 @@ fn get_data<Client: QuakeClient>(
|
|||
)?;
|
||||
|
||||
let data = socket.receive(None)?;
|
||||
let mut bufferer = Bufferer::new_with_data(Endianess::Little, &data);
|
||||
let mut bufferer = Buffer::<LittleEndian>::new(&data);
|
||||
|
||||
if bufferer.get_u32()? != 4294967295 {
|
||||
if bufferer.read::<u32>()? != 4294967295 {
|
||||
return Err(GDError::PacketBad);
|
||||
}
|
||||
|
||||
let response_header = Client::get_response_header().as_bytes();
|
||||
if !bufferer.remaining_data().starts_with(response_header) {
|
||||
if !bufferer.remaining_bytes().starts_with(response_header) {
|
||||
Err(GDError::PacketBad)?
|
||||
}
|
||||
|
||||
bufferer.move_position_ahead(response_header.len());
|
||||
bufferer.move_cursor(response_header.len() as isize)?;
|
||||
|
||||
Ok(bufferer)
|
||||
Ok(bufferer.remaining_bytes().to_vec()) //TODO: Maybe fix?
|
||||
}
|
||||
|
||||
fn get_server_values(bufferer: &mut Bufferer) -> GDResult<HashMap<String, String>> {
|
||||
let data = bufferer.get_string_utf8_newline()?;
|
||||
fn get_server_values(bufferer: &mut Buffer<LittleEndian>) -> GDResult<HashMap<String, String>> {
|
||||
let data = bufferer.read_string::<Utf8Decoder>(Some([0x0A]))?;
|
||||
let mut data_split = data.split('\\').collect::<Vec<&str>>();
|
||||
if let Some(first) = data_split.first() {
|
||||
if first == &"" {
|
||||
|
|
@ -74,11 +73,14 @@ fn get_server_values(bufferer: &mut Bufferer) -> GDResult<HashMap<String, String
|
|||
Ok(vars)
|
||||
}
|
||||
|
||||
fn get_players<Client: QuakeClient>(bufferer: &mut Bufferer) -> GDResult<Vec<Client::Player>> {
|
||||
fn get_players<Client: QuakeClient>(bufferer: &mut Buffer<LittleEndian>) -> GDResult<Vec<Client::Player>> {
|
||||
let mut players: Vec<Client::Player> = Vec::new();
|
||||
|
||||
while !bufferer.is_remaining_empty() && bufferer.remaining_data() != [0x00] {
|
||||
let data = bufferer.get_string_utf8_newline()?;
|
||||
// this needs to be looked at again as theres no way to check if the buffer has
|
||||
// a remaining null byte the original code was:
|
||||
// while !bufferer.is_remaining_empty() && bufferer.remaining_data() != [0x00]
|
||||
while !bufferer.remaining_length() == 0 {
|
||||
let data = bufferer.read_string::<Utf8Decoder>(Some([0x0A]))?;
|
||||
let data_split = data.split(' ').collect::<Vec<&str>>();
|
||||
let data_iter = data_split.iter();
|
||||
|
||||
|
|
@ -92,7 +94,8 @@ pub(crate) fn client_query<Client: QuakeClient>(
|
|||
address: &SocketAddr,
|
||||
timeout_settings: Option<TimeoutSettings>,
|
||||
) -> GDResult<Response<Client::Player>> {
|
||||
let mut bufferer = get_data::<Client>(address, timeout_settings)?;
|
||||
let data = get_data::<Client>(address, timeout_settings)?;
|
||||
let mut bufferer = Buffer::<LittleEndian>::new(&data);
|
||||
|
||||
let mut server_vars = get_server_values(&mut bufferer)?;
|
||||
let players = get_players::<Client>(&mut bufferer)?;
|
||||
|
|
|
|||
|
|
@ -56,10 +56,7 @@ impl QuakeClient for QuakeTwo {
|
|||
None => Err(GDError::PacketBad)?,
|
||||
Some(v) => remove_wrapping_quotes(v).to_string(),
|
||||
},
|
||||
address: match data.next() {
|
||||
None => None,
|
||||
Some(v) => Some(remove_wrapping_quotes(v).to_string()),
|
||||
},
|
||||
address: data.next().map(|v| remove_wrapping_quotes(v).to_string()),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
use crate::{
|
||||
bufferer::{Bufferer, Endianess},
|
||||
buffer::Buffer,
|
||||
protocols::{
|
||||
types::TimeoutSettings,
|
||||
valve::{
|
||||
|
|
@ -27,7 +27,9 @@ use crate::{
|
|||
|
||||
use bzip2_rs::decoder::Decoder;
|
||||
|
||||
use crate::buffer::Utf8Decoder;
|
||||
use crate::protocols::valve::Packet;
|
||||
use byteorder::LittleEndian;
|
||||
use std::collections::HashMap;
|
||||
use std::net::SocketAddr;
|
||||
|
||||
|
|
@ -46,26 +48,27 @@ struct SplitPacket {
|
|||
}
|
||||
|
||||
impl SplitPacket {
|
||||
fn new(engine: &Engine, protocol: u8, buffer: &mut Bufferer) -> GDResult<Self> {
|
||||
let header = buffer.get_u32()?;
|
||||
let id = buffer.get_u32()?;
|
||||
fn new(engine: &Engine, protocol: u8, buffer: &mut Buffer<LittleEndian>) -> GDResult<Self> {
|
||||
let header = buffer.read()?; //buffer.get_u32()?;
|
||||
let id = buffer.read()?;
|
||||
let (total, number, size, compressed, decompressed_size, uncompressed_crc32) = match engine {
|
||||
Engine::GoldSrc(_) => {
|
||||
let (lower, upper) = u8_lower_upper(buffer.get_u8()?);
|
||||
let (lower, upper) = u8_lower_upper(buffer.read()?);
|
||||
(lower, upper, 0, false, None, None)
|
||||
}
|
||||
Engine::Source(_) => {
|
||||
let total = buffer.get_u8()?;
|
||||
let number = buffer.get_u8()?;
|
||||
let total = buffer.read()?;
|
||||
let number = buffer.read()?;
|
||||
let size = match protocol == 7 && (*engine == SteamApp::CSS.as_engine()) {
|
||||
// certain apps with protocol = 7 dont have this field
|
||||
false => buffer.get_u16()?,
|
||||
false => buffer.read()?,
|
||||
true => 1248,
|
||||
};
|
||||
let compressed = ((id >> 31) & 1) == 1;
|
||||
let compressed = ((id >> 31) & 1u32) == 1u32;
|
||||
|
||||
let (decompressed_size, uncompressed_crc32) = match compressed {
|
||||
false => (None, None),
|
||||
true => (Some(buffer.get_u32()?), Some(buffer.get_u32()?)),
|
||||
true => (Some(buffer.read()?), Some(buffer.read()?)),
|
||||
};
|
||||
(
|
||||
total,
|
||||
|
|
@ -87,7 +90,7 @@ impl SplitPacket {
|
|||
compressed,
|
||||
decompressed_size,
|
||||
uncompressed_crc32,
|
||||
payload: buffer.remaining_data().to_vec(),
|
||||
payload: buffer.remaining_bytes().to_vec(),
|
||||
})
|
||||
}
|
||||
|
||||
|
|
@ -133,10 +136,10 @@ impl ValveProtocol {
|
|||
|
||||
fn receive(&mut self, engine: &Engine, protocol: u8, buffer_size: usize) -> GDResult<Packet> {
|
||||
let data = self.socket.receive(Some(buffer_size))?;
|
||||
let mut buffer = Bufferer::new_with_data(Endianess::Little, &data);
|
||||
let mut buffer = Buffer::<LittleEndian>::new(&data);
|
||||
|
||||
let header = buffer.get_u8()?;
|
||||
buffer.move_position_backward(1);
|
||||
let header: u8 = buffer.read()?;
|
||||
buffer.move_cursor(-1)?;
|
||||
if header == 0xFE {
|
||||
// the packet is split
|
||||
let mut main_packet = SplitPacket::new(engine, protocol, &mut buffer)?;
|
||||
|
|
@ -144,7 +147,7 @@ impl ValveProtocol {
|
|||
|
||||
for _ in 1 .. main_packet.total {
|
||||
let new_data = self.socket.receive(Some(buffer_size))?;
|
||||
buffer = Bufferer::new_with_data(Endianess::Little, &new_data);
|
||||
buffer = Buffer::<LittleEndian>::new(&new_data);
|
||||
let chunk_packet = SplitPacket::new(engine, protocol, &mut buffer)?;
|
||||
chunk_packets.push(chunk_packet);
|
||||
}
|
||||
|
|
@ -155,25 +158,21 @@ impl ValveProtocol {
|
|||
main_packet.payload.extend(chunk_packet.payload);
|
||||
}
|
||||
|
||||
let mut new_packet_buffer = Bufferer::new_with_data(Endianess::Little, &main_packet.get_payload()?);
|
||||
let payload = main_packet.get_payload()?; // Creating a non-temporary value here
|
||||
let mut new_packet_buffer = Buffer::<LittleEndian>::new(&payload); // Using the non-temporary value here
|
||||
Ok(Packet::new_from_bufferer(&mut new_packet_buffer)?)
|
||||
} else {
|
||||
Packet::new_from_bufferer(&mut buffer)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_kind_request_data(&mut self, engine: &Engine, protocol: u8, kind: Request) -> GDResult<Bufferer> {
|
||||
self.get_request_data(engine, protocol, kind as u8, kind.get_default_payload())
|
||||
pub fn get_kind_request_data(&mut self, engine: &Engine, protocol: u8, kind: Request) -> GDResult<Vec<u8>> {
|
||||
let data = self.get_request_data(engine, protocol, kind as u8, kind.get_default_payload())?;
|
||||
Ok(data)
|
||||
}
|
||||
|
||||
/// Ask for a specific request only.
|
||||
pub fn get_request_data(
|
||||
&mut self,
|
||||
engine: &Engine,
|
||||
protocol: u8,
|
||||
kind: u8,
|
||||
payload: Vec<u8>,
|
||||
) -> GDResult<Bufferer> {
|
||||
pub fn get_request_data(&mut self, engine: &Engine, protocol: u8, kind: u8, payload: Vec<u8>) -> GDResult<Vec<u8>> {
|
||||
let request_initial_packet = Packet::new(kind, payload).to_bytes();
|
||||
self.socket.send(&request_initial_packet)?;
|
||||
|
||||
|
|
@ -182,7 +181,7 @@ impl ValveProtocol {
|
|||
// 'A'
|
||||
let challenge = packet.payload;
|
||||
|
||||
const INFO: u8 = Request::Info as u8; // hmm, this could be unwanted and problematic
|
||||
const INFO: u8 = Request::Info as u8;
|
||||
let challenge_packet = Packet::new(
|
||||
kind,
|
||||
match kind {
|
||||
|
|
@ -197,48 +196,47 @@ impl ValveProtocol {
|
|||
packet = self.receive(engine, protocol, PACKET_SIZE)?;
|
||||
}
|
||||
|
||||
let data = packet.payload;
|
||||
Ok(Bufferer::new_with_data(Endianess::Little, &data))
|
||||
Ok(packet.payload)
|
||||
}
|
||||
|
||||
fn get_goldsrc_server_info(buffer: &mut Bufferer) -> GDResult<ServerInfo> {
|
||||
buffer.get_u8()?; //get the header (useless info)
|
||||
buffer.get_string_utf8()?; //get the server address (useless info)
|
||||
let name = buffer.get_string_utf8()?;
|
||||
let map = buffer.get_string_utf8()?;
|
||||
let folder = buffer.get_string_utf8()?;
|
||||
let game = buffer.get_string_utf8()?;
|
||||
let players = buffer.get_u8()?;
|
||||
let max_players = buffer.get_u8()?;
|
||||
let protocol = buffer.get_u8()?;
|
||||
let server_type = match buffer.get_u8()? {
|
||||
fn get_goldsrc_server_info(buffer: &mut Buffer<LittleEndian>) -> GDResult<ServerInfo> {
|
||||
let _header: u8 = buffer.read()?; //get the header (useless info)
|
||||
let _address: String = buffer.read_string::<Utf8Decoder>(None)?; //get the server address (useless info)
|
||||
let name = buffer.read_string::<Utf8Decoder>(None)?;
|
||||
let map = buffer.read_string::<Utf8Decoder>(None)?;
|
||||
let folder = buffer.read_string::<Utf8Decoder>(None)?;
|
||||
let game = buffer.read_string::<Utf8Decoder>(None)?;
|
||||
let players = buffer.read()?;
|
||||
let max_players = buffer.read()?;
|
||||
let protocol = buffer.read()?;
|
||||
let server_type = match buffer.read::<u8>()? {
|
||||
68 => Server::Dedicated, //'D'
|
||||
76 => Server::NonDedicated, //'L'
|
||||
80 => Server::TV, //'P'
|
||||
_ => Err(UnknownEnumCast)?,
|
||||
};
|
||||
let environment_type = match buffer.get_u8()? {
|
||||
let environment_type = match buffer.read::<u8>()? {
|
||||
76 => Environment::Linux, //'L'
|
||||
87 => Environment::Windows, //'W'
|
||||
_ => Err(UnknownEnumCast)?,
|
||||
};
|
||||
let has_password = buffer.get_u8()? == 1;
|
||||
let is_mod = buffer.get_u8()? == 1;
|
||||
let has_password = buffer.read::<u8>()? == 1;
|
||||
let is_mod = buffer.read::<u8>()? == 1;
|
||||
let mod_data = match is_mod {
|
||||
false => None,
|
||||
true => {
|
||||
Some(ModData {
|
||||
link: buffer.get_string_utf8()?,
|
||||
download_link: buffer.get_string_utf8()?,
|
||||
version: buffer.get_u32()?,
|
||||
size: buffer.get_u32()?,
|
||||
multiplayer_only: buffer.get_u8()? == 1,
|
||||
has_own_dll: buffer.get_u8()? == 1,
|
||||
link: buffer.read_string::<Utf8Decoder>(None)?,
|
||||
download_link: buffer.read_string::<Utf8Decoder>(None)?,
|
||||
version: buffer.read()?,
|
||||
size: buffer.read()?,
|
||||
multiplayer_only: buffer.read::<u8>()? == 1,
|
||||
has_own_dll: buffer.read::<u8>()? == 1,
|
||||
})
|
||||
}
|
||||
};
|
||||
let vac_secured = buffer.get_u8()? == 1;
|
||||
let bots = buffer.get_u8()?;
|
||||
let vac_secured = buffer.read::<u8>()? == 1;
|
||||
let bots = buffer.read::<u8>()?;
|
||||
|
||||
Ok(ServerInfo {
|
||||
protocol,
|
||||
|
|
@ -264,7 +262,8 @@ impl ValveProtocol {
|
|||
|
||||
/// Get the server information's.
|
||||
fn get_server_info(&mut self, engine: &Engine) -> GDResult<ServerInfo> {
|
||||
let mut buffer = self.get_kind_request_data(engine, 0, Request::Info)?;
|
||||
let data = self.get_kind_request_data(engine, 0, Request::Info)?;
|
||||
let mut buffer = Buffer::<LittleEndian>::new(&data);
|
||||
|
||||
if let Engine::GoldSrc(force) = engine {
|
||||
if *force {
|
||||
|
|
@ -272,58 +271,58 @@ impl ValveProtocol {
|
|||
}
|
||||
}
|
||||
|
||||
let protocol = buffer.get_u8()?;
|
||||
let name = buffer.get_string_utf8()?;
|
||||
let map = buffer.get_string_utf8()?;
|
||||
let folder = buffer.get_string_utf8()?;
|
||||
let game = buffer.get_string_utf8()?;
|
||||
let mut appid = buffer.get_u16()? as u32;
|
||||
let players = buffer.get_u8()?;
|
||||
let max_players = buffer.get_u8()?;
|
||||
let bots = buffer.get_u8()?;
|
||||
let server_type = Server::from_gldsrc(buffer.get_u8()?)?;
|
||||
let environment_type = Environment::from_gldsrc(buffer.get_u8()?)?;
|
||||
let has_password = buffer.get_u8()? == 1;
|
||||
let vac_secured = buffer.get_u8()? == 1;
|
||||
let protocol = buffer.read()?;
|
||||
let name = buffer.read_string::<Utf8Decoder>(None)?;
|
||||
let map = buffer.read_string::<Utf8Decoder>(None)?;
|
||||
let folder = buffer.read_string::<Utf8Decoder>(None)?;
|
||||
let game = buffer.read_string::<Utf8Decoder>(None)?;
|
||||
let mut appid = buffer.read::<u16>()? as u32;
|
||||
let players = buffer.read()?;
|
||||
let max_players = buffer.read()?;
|
||||
let bots = buffer.read()?;
|
||||
let server_type = Server::from_gldsrc(buffer.read()?)?;
|
||||
let environment_type = Environment::from_gldsrc(buffer.read()?)?;
|
||||
let has_password = buffer.read::<u8>()? == 1;
|
||||
let vac_secured = buffer.read::<u8>()? == 1;
|
||||
let the_ship = match *engine == SteamApp::TS.as_engine() {
|
||||
false => None,
|
||||
true => {
|
||||
Some(TheShip {
|
||||
mode: buffer.get_u8()?,
|
||||
witnesses: buffer.get_u8()?,
|
||||
duration: buffer.get_u8()?,
|
||||
mode: buffer.read()?,
|
||||
witnesses: buffer.read()?,
|
||||
duration: buffer.read()?,
|
||||
})
|
||||
}
|
||||
};
|
||||
let version = buffer.get_string_utf8()?;
|
||||
let extra_data = match buffer.get_u8() {
|
||||
let version = buffer.read_string::<Utf8Decoder>(None)?;
|
||||
let extra_data = match buffer.read::<u8>() {
|
||||
Err(_) => None,
|
||||
Ok(value) => {
|
||||
Some(ExtraData {
|
||||
port: match (value & 0x80) > 0 {
|
||||
false => None,
|
||||
true => Some(buffer.get_u16()?),
|
||||
true => Some(buffer.read()?),
|
||||
},
|
||||
steam_id: match (value & 0x10) > 0 {
|
||||
false => None,
|
||||
true => Some(buffer.get_u64()?),
|
||||
true => Some(buffer.read()?),
|
||||
},
|
||||
tv_port: match (value & 0x40) > 0 {
|
||||
false => None,
|
||||
true => Some(buffer.get_u16()?),
|
||||
true => Some(buffer.read()?),
|
||||
},
|
||||
tv_name: match (value & 0x40) > 0 {
|
||||
false => None,
|
||||
true => Some(buffer.get_string_utf8()?),
|
||||
true => Some(buffer.read_string::<Utf8Decoder>(None)?),
|
||||
},
|
||||
keywords: match (value & 0x20) > 0 {
|
||||
false => None,
|
||||
true => Some(buffer.get_string_utf8()?),
|
||||
true => Some(buffer.read_string::<Utf8Decoder>(None)?),
|
||||
},
|
||||
game_id: match (value & 0x01) > 0 {
|
||||
false => None,
|
||||
true => {
|
||||
let gid = buffer.get_u64()?;
|
||||
let gid = buffer.read()?;
|
||||
appid = (gid & ((1 << 24) - 1)) as u32;
|
||||
|
||||
Some(gid)
|
||||
|
|
@ -357,25 +356,26 @@ impl ValveProtocol {
|
|||
|
||||
/// Get the server player's.
|
||||
fn get_server_players(&mut self, engine: &Engine, protocol: u8) -> GDResult<Vec<ServerPlayer>> {
|
||||
let mut buffer = self.get_kind_request_data(engine, protocol, Request::Players)?;
|
||||
let data = self.get_kind_request_data(engine, protocol, Request::Players)?;
|
||||
let mut buffer = Buffer::<LittleEndian>::new(&data);
|
||||
|
||||
let count = buffer.get_u8()? as usize;
|
||||
let count = buffer.read::<u8>()? as usize;
|
||||
let mut players: Vec<ServerPlayer> = Vec::with_capacity(count);
|
||||
|
||||
for _ in 0 .. count {
|
||||
buffer.move_position_ahead(1); //skip the index byte
|
||||
buffer.move_cursor(1)?; //skip the index byte
|
||||
|
||||
players.push(ServerPlayer {
|
||||
name: buffer.get_string_utf8()?,
|
||||
score: buffer.get_u32()?,
|
||||
duration: buffer.get_f32()?,
|
||||
name: buffer.read_string::<Utf8Decoder>(None)?,
|
||||
score: buffer.read()?,
|
||||
duration: buffer.read()?,
|
||||
deaths: match *engine == SteamApp::TS.as_engine() {
|
||||
false => None,
|
||||
true => Some(buffer.get_u32()?),
|
||||
true => Some(buffer.read()?),
|
||||
},
|
||||
money: match *engine == SteamApp::TS.as_engine() {
|
||||
false => None,
|
||||
true => Some(buffer.get_u32()?),
|
||||
true => Some(buffer.read()?),
|
||||
},
|
||||
});
|
||||
}
|
||||
|
|
@ -385,14 +385,15 @@ impl ValveProtocol {
|
|||
|
||||
/// Get the server's rules.
|
||||
fn get_server_rules(&mut self, engine: &Engine, protocol: u8) -> GDResult<HashMap<String, String>> {
|
||||
let mut buffer = self.get_kind_request_data(engine, protocol, Request::Rules)?;
|
||||
let data = self.get_kind_request_data(engine, protocol, Request::Rules)?;
|
||||
let mut buffer = Buffer::<LittleEndian>::new(&data);
|
||||
|
||||
let count = buffer.get_u16()? as usize;
|
||||
let count = buffer.read::<u16>()? as usize;
|
||||
let mut rules: HashMap<String, String> = HashMap::with_capacity(count);
|
||||
|
||||
for _ in 0 .. count {
|
||||
let name = buffer.get_string_utf8()?;
|
||||
let value = buffer.get_string_utf8()?;
|
||||
let name = buffer.read_string::<Utf8Decoder>(None)?;
|
||||
let value = buffer.read_string::<Utf8Decoder>(None)?;
|
||||
|
||||
rules.insert(name, value);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,7 +3,8 @@ use std::collections::HashMap;
|
|||
use crate::protocols::types::{CommonPlayer, CommonResponse, GenericPlayer};
|
||||
use crate::GDError::UnknownEnumCast;
|
||||
use crate::GDResult;
|
||||
use crate::{bufferer::Bufferer, protocols::GenericResponse};
|
||||
use crate::{buffer::Buffer, protocols::GenericResponse};
|
||||
use byteorder::LittleEndian;
|
||||
#[cfg(feature = "serde")]
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
|
|
@ -209,11 +210,11 @@ impl Packet {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn new_from_bufferer(buffer: &mut Bufferer) -> GDResult<Self> {
|
||||
pub fn new_from_bufferer(buffer: &mut Buffer<LittleEndian>) -> GDResult<Self> {
|
||||
Ok(Self {
|
||||
header: buffer.get_u32()?,
|
||||
kind: buffer.get_u8()?,
|
||||
payload: buffer.remaining_data().to_vec(),
|
||||
header: buffer.read::<u32>()?,
|
||||
kind: buffer.read::<u8>()?,
|
||||
payload: buffer.remaining_bytes().to_vec(),
|
||||
})
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue