Initial commit

main
kalle 2023-04-09 00:08:03 +02:00
commit 277c84c5af
15 changed files with 883 additions and 0 deletions

1
.gitignore vendored Normal file
View File

@ -0,0 +1 @@
/target

150
Cargo.lock generated Normal file
View File

@ -0,0 +1,150 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
version = 3
[[package]]
name = "anyhow"
version = "1.0.68"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2cb2f989d18dd141ab8ae82f64d1a8cdd37e0840f73a406896cf5e99502fab61"
[[package]]
name = "byteorder"
version = "1.4.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610"
[[package]]
name = "either"
version = "1.8.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7fcaabb2fef8c910e7f4c7ce9f67a1283a1715879a7c230ca9d6d1ae31f16d91"
[[package]]
name = "itertools"
version = "0.10.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473"
dependencies = [
"either",
]
[[package]]
name = "itoa"
version = "1.0.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fad582f4b9e86b6caa621cabeb0963332d92eea04729ab12892c2533951e6440"
[[package]]
name = "minecraft-rust-server"
version = "0.1.0"
dependencies = [
"anyhow",
"byteorder",
"itertools",
"serde",
"serde_json",
"thiserror",
"uuid",
]
[[package]]
name = "proc-macro2"
version = "1.0.50"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6ef7d57beacfaf2d8aee5937dab7b7f28de3cb8b1828479bb5de2a7106f2bae2"
dependencies = [
"unicode-ident",
]
[[package]]
name = "quote"
version = "1.0.23"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8856d8364d252a14d474036ea1358d63c9e6965c8e5c1885c18f73d70bff9c7b"
dependencies = [
"proc-macro2",
]
[[package]]
name = "ryu"
version = "1.0.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7b4b9743ed687d4b4bcedf9ff5eaa7398495ae14e61cba0a295704edbc7decde"
[[package]]
name = "serde"
version = "1.0.152"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bb7d1f0d3021d347a83e556fc4683dea2ea09d87bccdf88ff5c12545d89d5efb"
dependencies = [
"serde_derive",
]
[[package]]
name = "serde_derive"
version = "1.0.152"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "af487d118eecd09402d70a5d72551860e788df87b464af30e5ea6a38c75c541e"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "serde_json"
version = "1.0.91"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "877c235533714907a8c2464236f5c4b2a17262ef1bd71f38f35ea592c8da6883"
dependencies = [
"itoa",
"ryu",
"serde",
]
[[package]]
name = "syn"
version = "1.0.107"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1f4064b5b16e03ae50984a5a8ed5d4f8803e6bc1fd170a3cda91a1be4b18e3f5"
dependencies = [
"proc-macro2",
"quote",
"unicode-ident",
]
[[package]]
name = "thiserror"
version = "1.0.38"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6a9cd18aa97d5c45c6603caea1da6628790b37f7a34b6ca89522331c5180fed0"
dependencies = [
"thiserror-impl",
]
[[package]]
name = "thiserror-impl"
version = "1.0.38"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1fb327af4685e4d03fa8cbcf1716380da910eeb2bb8be417e7f9fd3fb164f36f"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "unicode-ident"
version = "1.0.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "84a22b9f218b40614adcb3f4ff08b703773ad44fa9423e4e0d346d5db86e4ebc"
[[package]]
name = "uuid"
version = "1.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "422ee0de9031b5b948b97a8fc04e3aa35230001a722ddd27943e0be31564ce4c"
dependencies = [
"serde",
]

15
Cargo.toml Normal file
View File

@ -0,0 +1,15 @@
[package]
name = "minecraft-rust-server"
version = "0.1.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
anyhow = "1.0.68"
byteorder = "1.4.3"
itertools = "0.10.5"
serde = { version = "1.0.152", features = ["serde_derive", "derive"] }
serde_json = "1.0.91"
thiserror = "1.0.38"
uuid = { version = "1.2.2", features = ["serde"] }

View File

@ -0,0 +1,45 @@
use std::{io::{Cursor, Seek, SeekFrom}, fmt::Display};
use anyhow::Result;
use crate::packets::{Decode, Encode};
use super::varint::VarInt;
#[derive(Debug, PartialEq, Eq)]
pub struct LenghtPrefixedString {
pub value: String,
}
impl Decode for LenghtPrefixedString {
fn decode(cursor: &mut Cursor<&[u8]>) -> Result<LenghtPrefixedString> {
let lenght: i32 = VarInt::decode(cursor)?.into();
let value = String::from_utf8(cursor.remaining_slice()[..lenght as usize].to_owned())?;
cursor.seek(SeekFrom::Current(lenght as i64))?;
Ok(LenghtPrefixedString { value })
}
}
impl Encode for LenghtPrefixedString {
fn encode(&self, buffer: &mut Vec<u8>) -> Result<()> {
let lenght = VarInt {value: self.value.len() as i32};
lenght.encode(buffer)?;
buffer.extend_from_slice(self.value.as_bytes());
Ok(())
}
}
impl From<LenghtPrefixedString> for String {
fn from(val: LenghtPrefixedString) -> Self {
val.value
}
}
impl Display for LenghtPrefixedString {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
self.value.fmt(f)
}
}

2
src/datatypes/mod.rs Normal file
View File

@ -0,0 +1,2 @@
pub mod varint;
pub mod lenght_prefixed_string;

130
src/datatypes/varint.rs Normal file
View File

@ -0,0 +1,130 @@
use std::{io::{Read, Cursor}, fmt::Display};
use anyhow::Result;
use crate::packets::{Decode, Encode};
#[derive(Debug, PartialEq, Eq)]
pub struct VarInt {
pub value: i32,
}
impl Decode for VarInt {
fn decode(cursor: &mut Cursor<&[u8]>) -> Result<VarInt> {
let mut value: i32 = 0;
let mut pos = 0;
let mut current_byte = vec![0u8; 1];
loop {
cursor.read_exact(&mut current_byte)?;
value |= (current_byte[0] as i32 & 0x7F) << pos;
if current_byte[0] & 0x80 == 0 {
break;
}
pos += 7;
}
Ok(VarInt { value })
}
}
impl Encode for VarInt {
fn encode(&self, buffer: &mut Vec<u8>) -> Result<()> {
let mut value = self.value as u32;
loop {
if value & !0x7F == 0 {
buffer.push(value as u8);
break;
}
buffer.push(((value & 0x7F) | 0x80) as u8);
value >>= 7;
}
Ok(())
}
}
impl From<VarInt> for i32 {
fn from(val: VarInt) -> Self {
val.value
}
}
impl Display for VarInt {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
self.value.fmt(f)
}
}
#[cfg(test)]
mod test {
use std::io::Cursor;
use crate::datatypes::varint::VarInt;
use crate::packets::{Decode, Encode};
#[test]
fn small_number_decode_works() {
let data = vec![0x10];
let mut cursor = Cursor::new(&data[..]);
let result = VarInt::decode(&mut cursor).unwrap();
assert_eq!(16, result.value);
assert_eq!(1, cursor.position());
}
#[test]
fn large_number_decode_works() {
let data = vec![0xff, 0xff, 0x7f];
let mut cursor = Cursor::new(&data[..]);
let result = VarInt::decode(&mut cursor).unwrap();
assert_eq!(2097151, result.value);
assert_eq!(3, cursor.position());
}
#[test]
fn negative_number_decode_works() {
let data = vec![0xff, 0xff, 0xff, 0xff, 0x0f];
let mut cursor = Cursor::new(&data[..]);
let result = VarInt::decode(&mut cursor).unwrap();
assert_eq!(-1, result.value);
assert_eq!(5, cursor.position());
}
#[test]
fn small_number_encode_works() {
let mut data = Vec::new();
let to_encode = VarInt {value: 12};
to_encode.encode(&mut data);
assert_eq!(vec![12], data);
}
#[test]
fn large_number_encode_works() {
let mut data = Vec::new();
let to_encode = VarInt {value: 2097151};
to_encode.encode(&mut data);
assert_eq!(vec![0xff, 0xff, 0x7f], data);
}
#[test]
fn negative_number_encode_works() {
let mut data = Vec::new();
let to_encode = VarInt {value: -1};
to_encode.encode(&mut data);
assert_eq!(vec![0xff, 0xff, 0xff, 0xff, 0x0f], data);
}
}

238
src/main.rs Normal file
View File

@ -0,0 +1,238 @@
#![feature(cursor_remaining)]
use std::fmt::Display;
use std::io::{Read, ErrorKind, Cursor, Write};
use std::net::{TcpListener, TcpStream, SocketAddr};
use std::thread;
use anyhow::{Result, bail};
use datatypes::lenght_prefixed_string::LenghtPrefixedString;
use itertools::Itertools;
use packets::serverbound::login::LoginStartPacket;
use packets::serverbound::status::StatusPingRequestPacket;
use packets::{Packet, Encode};
use packets::serverbound::handshake::HandshakePacket;
use packets::clientbound::status::{StatusResponsePacket, StatusPongResponsePacket};
use packets::clientbound::login::LoginSuccessPacket;
use serde::Serialize;
use thiserror::Error;
use uuid::Uuid;
use crate::datatypes::varint::VarInt;
use crate::packets::Decode;
pub mod datatypes;
pub mod packets;
fn main() {
let listener = TcpListener::bind("127.0.0.1:25565").unwrap();
println!("listening started, ready to accept");
for stream in listener.incoming() {
match stream {
Err(e) => println!("Accept ERROR: {}", e),
Ok(stream) => drop(thread::spawn(|| handle_client(stream))),
}
}
}
fn handle_client(stream: TcpStream) -> Result<()> {
let mut client = Client::new(stream)?;
loop {
client.read_packet()?;
}
}
#[derive(Debug, Clone, Copy)]
enum ConnectionState {
Handshake,
Status,
Login,
Play,
}
impl Display for ConnectionState {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
ConnectionState::Handshake => f.write_str("Handshake"),
ConnectionState::Status => f.write_str("Status"),
ConnectionState::Login => f.write_str("Login"),
ConnectionState::Play => f.write_str("Play"),
}
}
}
struct Client {
state: ConnectionState,
address: SocketAddr,
stream: TcpStream,
}
impl Client {
fn new(stream: TcpStream) -> Result<Client> {
let address = stream.peer_addr()?;
Ok(Client {
state: ConnectionState::Handshake,
address, stream,
})
}
fn send_packet<T: Packet>(&mut self, packet: T) -> Result<()> {
let mut data: Vec<u8> = Vec::new();
// Encode packet ID
VarInt {value: packet.get_id()}.encode(&mut data)?;
// Encode packet data
packet.encode(&mut data)?;
// Prepend packet data
let mut lenght: Vec<u8> = Vec::new();
VarInt {value: data.len() as i32}.encode(&mut lenght)?;
lenght.append(&mut data);
// Data is empty now
let data = lenght;
self.stream.write_all(&data)?;
Ok(())
}
fn read_packet(&mut self) -> Result<()> {
let mut buffer = vec![0u8; 256];
let read_bytes = self.stream.read(&mut buffer)?;
if read_bytes == 0 {
return Err(std::io::Error::new(ErrorKind::UnexpectedEof, "Read 0 bytes").into());
}
let bytes = &buffer[..read_bytes];
let mut cursor = Cursor::new(bytes);
let length: i32 = VarInt::decode(&mut cursor)?.into();
let id: i32 = VarInt::decode(&mut cursor)?.into();
self.handle_packet(id, length, cursor)
}
fn handle_packet(&mut self, id: i32, length: i32, cursor: Cursor<&[u8]>) -> Result<()> {
match self.state {
ConnectionState::Handshake => self.handle_handshake_packets(id, length, cursor),
ConnectionState::Status => self.handle_status_packets(id, length, cursor),
ConnectionState::Login => self.handle_login_packet(id, length, cursor),
ConnectionState::Play => todo!(),
}
}
fn handle_handshake_packets(&mut self, id: i32, length: i32, mut cursor: Cursor<&[u8]>) -> Result<()> {
match id {
0 => {
let packet = HandshakePacket::decode(&mut cursor)?;
println!("[HANDSHAKE] <- {}: Handshake request: IP: {}:{}", self.address, packet.server_address, packet.server_port);
self.change_state(packet.next_state.into())
},
_ => {
println!("[HANDSHAKE] <- {}: [ID: {}, Length: {}] {:02x}", self.address, id, length, cursor.remaining_slice().iter().format(" "));
Ok(())
//bail!(Error::InvalidPacketIdError {id, state: self.state}),
}
}
}
fn handle_status_packets(&mut self, id: i32, length: i32, mut cursor: Cursor<&[u8]>) -> Result<()> {
match id {
0 => {
let response_data = ServerStatusResponseData {
version: ServerStatusResponseDataVersion { name: "Rust 1.19".to_owned(), protocol: 761 },
players: ServerStatusResponseDataPlayers { max: 100, online: 0 },
description: ServerStatusResponseDataDescription { text: "A rust server!".to_owned() },
};
let response = StatusResponsePacket {json: LenghtPrefixedString {value: serde_json::to_string(&response_data)?}};
self.send_packet(response)?;
Ok(())
},
1 => {
let packet = StatusPingRequestPacket::decode(&mut cursor)?;
let response = StatusPongResponsePacket {payload: packet.payload};
self.send_packet(response)?;
Ok(())
},
_ => {
println!("[STATUS] <- {}: [ID: {}, Length: {}] {:02x}", self.address, id, length, cursor.remaining_slice().iter().format(" "));
Ok(())
//bail!(Error::InvalidPacketIdError {id, state: self.state}),
}
}
}
fn handle_login_packet(&mut self, id: i32, length: i32, mut cursor: Cursor<&[u8]>) -> Result<()> {
match id {
0 => {
let packet = LoginStartPacket::decode(&mut cursor)?;
let response = LoginSuccessPacket {
uuid: Uuid::from_u128(0xd15b7518f4d747f8b385f10bffedf643),
username: LenghtPrefixedString {value: "test".to_owned()},
property_length: VarInt {value: 0},
};
self.send_packet(response)?;
self.change_state(3)?;
Ok(())
},
_ => {
println!("[LOGIN] <- {}: [ID: {}, Length: {}] {:02x}", self.address, id, length, cursor.remaining_slice().iter().format(" "));
Ok(())
//bail!(Error::InvalidPacketIdError {id, state: self.state}),
}
}
}
fn change_state(&mut self, state_index: i32) -> Result<()> {
println!("<- {}: Moving connection to state: {}", self.address, state_index);
match state_index {
1 => self.state = ConnectionState::Status,
2 => self.state = ConnectionState::Login,
3 => self.state = ConnectionState::Play,
_ => bail!(Error::InvalidNextState {next_state: state_index})
}
Ok(())
}
}
#[derive(Error, Debug)]
enum Error {
#[error("Packet has invalid packet id {id} for current state {state}")]
InvalidPacketIdError {id: i32, state: ConnectionState},
#[error("Requested to move to invalid next state {next_state}.")]
InvalidNextState {next_state: i32},
}
#[derive(Debug, Serialize)]
struct ServerStatusResponseData {
version: ServerStatusResponseDataVersion,
players: ServerStatusResponseDataPlayers,
description: ServerStatusResponseDataDescription,
}
#[derive(Debug, Serialize)]
struct ServerStatusResponseDataVersion {
name: String,
protocol: i32,
}
#[derive(Debug, Serialize)]
struct ServerStatusResponseDataPlayers {
max: i32,
online: i32,
}
#[derive(Debug, Serialize)]
struct ServerStatusResponseDataDescription {
text: String,
}

View File

@ -0,0 +1,39 @@
use uuid::Uuid;
use crate::{datatypes::{lenght_prefixed_string::LenghtPrefixedString, varint::VarInt}, packets::{Decode, Encode, Packet}};
pub struct LoginSuccessPacket {
pub uuid: Uuid,
pub username: LenghtPrefixedString,
pub property_length: VarInt,
// TODO: properties...
}
impl Decode for LoginSuccessPacket {
fn decode(cursor: &mut std::io::Cursor<&[u8]>) -> anyhow::Result<Self> {
let uuid = Uuid::decode(cursor)?;
let username = LenghtPrefixedString::decode(cursor)?;
let property_length = VarInt::decode(cursor)?;
Ok(LoginSuccessPacket {
uuid,
username,
property_length
})
}
}
impl Encode for LoginSuccessPacket {
fn encode(&self, buffer: &mut Vec<u8>) -> anyhow::Result<()> {
self.uuid.encode(buffer)?;
self.username.encode(buffer)?;
self.property_length.encode(buffer)?;
Ok(())
}
}
impl Packet for LoginSuccessPacket {
fn get_id(&self) -> i32 {0x02}
}

View File

@ -0,0 +1,3 @@
pub mod status;
pub mod login;

View File

@ -0,0 +1,51 @@
use std::io::Cursor;
use anyhow::Result;
use crate::{datatypes::lenght_prefixed_string::LenghtPrefixedString, packets::{Decode, Encode, Packet}};
pub struct StatusResponsePacket {
pub json: LenghtPrefixedString,
}
impl Decode for StatusResponsePacket {
fn decode(cursor: &mut Cursor<&[u8]>) -> Result<Self> {
let json = LenghtPrefixedString::decode(cursor)?;
Ok(StatusResponsePacket { json })
}
}
impl Encode for StatusResponsePacket {
fn encode(&self, buffer: &mut Vec<u8>) -> Result<()> {
self.json.encode(buffer)
}
}
impl Packet for StatusResponsePacket {
fn get_id(&self) -> i32 {0x00}
}
pub struct StatusPongResponsePacket {
pub payload: i64,
}
impl Decode for StatusPongResponsePacket {
fn decode(cursor: &mut Cursor<&[u8]>) -> Result<Self> {
let payload = i64::decode(cursor)?;
Ok(StatusPongResponsePacket { payload })
}
}
impl Encode for StatusPongResponsePacket {
fn encode(&self, buffer: &mut Vec<u8>) -> anyhow::Result<()> {
self.payload.encode(buffer)
}
}
impl Packet for StatusPongResponsePacket {
fn get_id(&self) -> i32 {0x01}
}

95
src/packets/mod.rs Normal file
View File

@ -0,0 +1,95 @@
use std::io::Cursor;
use byteorder::{BigEndian, ReadBytesExt, WriteBytesExt};
use anyhow::Result;
use uuid::Uuid;
pub mod clientbound;
pub mod serverbound;
pub trait Decode: Sized {
fn decode(cursor: &mut Cursor<&[u8]>) -> Result<Self>;
}
pub trait Encode: Sized {
fn encode(&self, buffer: &mut Vec<u8>) -> Result<()>;
}
pub trait Packet: Decode + Encode {
fn get_id(&self) -> i32;
}
impl <T> Encode for Option<T>
where T: Encode {
fn encode(&self, buffer: &mut Vec<u8>) -> Result<()> {
match self {
Some(it) => it.encode(buffer),
None => Ok(())
}
}
}
macro_rules! number_impl {
($($type:ty, $read_fn:tt, $write_fn:tt),* $(,)?) => {
$(
impl Decode for $type {
fn decode(cursor: &mut Cursor<&[u8]>) -> Result<Self> {
cursor.$read_fn::<BigEndian>().map_err(anyhow::Error::from)
}
}
impl Encode for $type {
fn encode(&self, buffer: &mut Vec<u8>) -> Result<()> {
buffer.$write_fn::<BigEndian>(*self).map_err(anyhow::Error::from)
}
}
)*
}
}
number_impl! {
u16, read_u16, write_u16,
u32, read_u32, write_u32,
u64, read_u64, write_u64,
i16, read_i16, write_i16,
i32, read_i32, write_i32,
i64, read_i64, write_i64,
f32, read_f32, write_f32,
f64, read_f64, write_f64,
}
impl Decode for bool {
fn decode(cursor: &mut Cursor<&[u8]>) -> Result<Self> {
let data = cursor.read_u8()?;
Ok(data != 0x00)
}
}
impl Encode for bool {
fn encode(&self, buffer: &mut Vec<u8>) -> Result<()> {
if *self {
buffer.push(0x01);
} else {
buffer.push(0x00);
}
Ok(())
}
}
impl Decode for Uuid {
fn decode(cursor: &mut Cursor<&[u8]>) -> Result<Self> {
let data = cursor.read_u128::<BigEndian>()?;
Ok(Uuid::from_u128(data))
}
}
impl Encode for Uuid {
fn encode(&self, buffer: &mut Vec<u8>) -> Result<()> {
buffer.write_u128::<BigEndian>(self.as_u128())?;
Ok(())
}
}

View File

@ -0,0 +1,40 @@
use std::io::Cursor;
use anyhow::Result;
use crate::{datatypes::{varint::VarInt, lenght_prefixed_string::LenghtPrefixedString}, packets::{Decode, Encode, Packet}};
#[derive(Debug)]
pub struct HandshakePacket {
pub protocal_version: VarInt,
pub server_address: LenghtPrefixedString,
pub server_port: u16,
pub next_state: VarInt,
}
impl Decode for HandshakePacket {
fn decode(cursor: &mut Cursor<&[u8]>) -> Result<HandshakePacket> {
let protocal_version = VarInt::decode(cursor)?;
let server_address = LenghtPrefixedString::decode(cursor)?;
let server_port = u16::decode(cursor)?;
let next_state = VarInt::decode(cursor)?;
Ok(HandshakePacket {
protocal_version,
server_address,
server_port,
next_state,
})
}
}
impl Encode for HandshakePacket {
fn encode(&self, buffer: &mut Vec<u8>) -> Result<()> {
todo!()
}
}
impl Packet for HandshakePacket {
fn get_id(&self) -> i32 {0x00}
}

View File

@ -0,0 +1,42 @@
use uuid::Uuid;
use crate::{datatypes::lenght_prefixed_string::LenghtPrefixedString, packets::{Decode, Encode, Packet}};
pub struct LoginStartPacket {
pub username: LenghtPrefixedString,
pub has_uuid: bool,
pub uuid: Option<Uuid>,
}
impl Decode for LoginStartPacket {
fn decode(cursor: &mut std::io::Cursor<&[u8]>) -> anyhow::Result<Self> {
let username = LenghtPrefixedString::decode(cursor)?;
let has_uuid = bool::decode(cursor)?;
let uuid = if has_uuid {
Some(Uuid::decode(cursor)?)
} else {
None
};
Ok(LoginStartPacket {
username,
has_uuid,
uuid,
})
}
}
impl Encode for LoginStartPacket {
fn encode(&self, buffer: &mut Vec<u8>) -> anyhow::Result<()> {
self.username.encode(buffer)?;
self.has_uuid.encode(buffer)?;
self.uuid.encode(buffer)?;
Ok(())
}
}
impl Packet for LoginStartPacket {
fn get_id(&self) -> i32 {0x00}
}

View File

@ -0,0 +1,4 @@
pub mod handshake;
pub mod status;
pub mod login;

View File

@ -0,0 +1,28 @@
use std::io::Cursor;
use anyhow::Result;
use crate::packets::{Encode, Packet, Decode};
pub struct StatusPingRequestPacket {
pub payload: i64,
}
impl Decode for StatusPingRequestPacket {
fn decode(cursor: &mut Cursor<&[u8]>) -> Result<Self> {
let payload = i64::decode(cursor)?;
Ok(StatusPingRequestPacket { payload })
}
}
impl Encode for StatusPingRequestPacket {
fn encode(&self, buffer: &mut Vec<u8>) -> Result<()> {
self.payload.encode(buffer)
}
}
impl Packet for StatusPingRequestPacket {
fn get_id(&self) -> i32 {0x01}
}