Initial commit
commit
277c84c5af
|
@ -0,0 +1 @@
|
|||
/target
|
|
@ -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",
|
||||
]
|
|
@ -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"] }
|
|
@ -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)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,2 @@
|
|||
pub mod varint;
|
||||
pub mod lenght_prefixed_string;
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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,
|
||||
}
|
||||
|
|
@ -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}
|
||||
}
|
|
@ -0,0 +1,3 @@
|
|||
pub mod status;
|
||||
pub mod login;
|
||||
|
|
@ -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}
|
||||
}
|
|
@ -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(())
|
||||
}
|
||||
}
|
|
@ -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}
|
||||
}
|
|
@ -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}
|
||||
}
|
|
@ -0,0 +1,4 @@
|
|||
pub mod handshake;
|
||||
pub mod status;
|
||||
pub mod login;
|
||||
|
|
@ -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}
|
||||
}
|
Loading…
Reference in New Issue