init commit

This commit is contained in:
MeexReay 2025-04-28 14:07:46 +03:00
parent 0475b6a6cf
commit c0f077a10e
16 changed files with 1595 additions and 7 deletions

1
.gitignore vendored Normal file
View File

@ -0,0 +1 @@
target/

289
Cargo.lock generated Normal file
View File

@ -0,0 +1,289 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
version = 4
[[package]]
name = "addr2line"
version = "0.24.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dfbe277e56a376000877090da837660b4427aad530e3028d44e0bffe4f89a1c1"
dependencies = [
"gimli",
]
[[package]]
name = "adler2"
version = "2.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "512761e0bb2578dd7380c6baaa0f4ce03e84f95e960231d1dec8bf4d7d6e2627"
[[package]]
name = "backtrace"
version = "0.3.74"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8d82cb332cdfaed17ae235a638438ac4d4839913cc2af585c3c6746e8f8bee1a"
dependencies = [
"addr2line",
"cfg-if",
"libc",
"miniz_oxide",
"object",
"rustc-demangle",
"windows-targets",
]
[[package]]
name = "bytes"
version = "1.10.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d71b6127be86fdcfddb610f7182ac57211d4b18a3e9c82eb2d17662f2227ad6a"
[[package]]
name = "cfg-if"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
[[package]]
name = "crc32fast"
version = "1.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b3855a8a784b474f333699ef2bbca9db2c4a1f6d9088a90a2d25b1eb53111eaa"
dependencies = [
"cfg-if",
]
[[package]]
name = "flate2"
version = "1.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7ced92e76e966ca2fd84c8f7aa01a4aea65b0eb6648d72f7c8f3e2764a67fece"
dependencies = [
"crc32fast",
"miniz_oxide",
]
[[package]]
name = "gimli"
version = "0.31.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "07e28edb80900c19c28f1072f2e8aeca7fa06b23cd4169cefe1af5aa3260783f"
[[package]]
name = "libc"
version = "0.2.172"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d750af042f7ef4f724306de029d18836c26c1765a54a6a3f094cbd23a7267ffa"
[[package]]
name = "memchr"
version = "2.7.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3"
[[package]]
name = "miniz_oxide"
version = "0.8.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3be647b768db090acb35d5ec5db2b0e1f1de11133ca123b9eacf5137868f892a"
dependencies = [
"adler2",
]
[[package]]
name = "mio"
version = "1.0.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2886843bf800fba2e3377cff24abf6379b4c4d5c6681eaf9ea5b0d15090450bd"
dependencies = [
"libc",
"wasi",
"windows-sys",
]
[[package]]
name = "object"
version = "0.36.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "62948e14d923ea95ea2c7c86c71013138b66525b86bdc08d2dcc262bdb497b87"
dependencies = [
"memchr",
]
[[package]]
name = "pin-project-lite"
version = "0.2.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3b3cff922bd51709b605d9ead9aa71031d81447142d828eb4a6eba76fe619f9b"
[[package]]
name = "proc-macro2"
version = "1.0.95"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "02b3e5e68a3a1a02aad3ec490a98007cbc13c37cbe84a3cd7b8e406d76e7f778"
dependencies = [
"unicode-ident",
]
[[package]]
name = "quote"
version = "1.0.40"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1885c039570dc00dcb4ff087a89e185fd56bae234ddc7f056a945bf36467248d"
dependencies = [
"proc-macro2",
]
[[package]]
name = "rust_mc_proto_tokio"
version = "0.1.18"
dependencies = [
"flate2",
"tokio",
"uuid",
]
[[package]]
name = "rustc-demangle"
version = "0.1.24"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f"
[[package]]
name = "socket2"
version = "0.5.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4f5fd57c80058a56cf5c777ab8a126398ece8e442983605d280a44ce79d0edef"
dependencies = [
"libc",
"windows-sys",
]
[[package]]
name = "syn"
version = "2.0.101"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8ce2b7fc941b3a24138a0a7cf8e858bfc6a992e7978a068a5c760deb0ed43caf"
dependencies = [
"proc-macro2",
"quote",
"unicode-ident",
]
[[package]]
name = "tokio"
version = "1.44.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e6b88822cbe49de4185e3a4cbf8321dd487cf5fe0c5c65695fef6346371e9c48"
dependencies = [
"backtrace",
"bytes",
"libc",
"mio",
"pin-project-lite",
"socket2",
"tokio-macros",
"windows-sys",
]
[[package]]
name = "tokio-macros"
version = "2.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6e06d43f1345a3bcd39f6a56dbb7dcab2ba47e68e8ac134855e7e2bdbaf8cab8"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "unicode-ident"
version = "1.0.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512"
[[package]]
name = "uuid"
version = "1.16.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "458f7a779bf54acc9f347480ac654f68407d3aab21269a6e3c9f922acd9e2da9"
[[package]]
name = "wasi"
version = "0.11.0+wasi-snapshot-preview1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
[[package]]
name = "windows-sys"
version = "0.52.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d"
dependencies = [
"windows-targets",
]
[[package]]
name = "windows-targets"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973"
dependencies = [
"windows_aarch64_gnullvm",
"windows_aarch64_msvc",
"windows_i686_gnu",
"windows_i686_gnullvm",
"windows_i686_msvc",
"windows_x86_64_gnu",
"windows_x86_64_gnullvm",
"windows_x86_64_msvc",
]
[[package]]
name = "windows_aarch64_gnullvm"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3"
[[package]]
name = "windows_aarch64_msvc"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469"
[[package]]
name = "windows_i686_gnu"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b"
[[package]]
name = "windows_i686_gnullvm"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66"
[[package]]
name = "windows_i686_msvc"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66"
[[package]]
name = "windows_x86_64_gnu"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78"
[[package]]
name = "windows_x86_64_gnullvm"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d"
[[package]]
name = "windows_x86_64_msvc"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec"

16
Cargo.toml Normal file
View File

@ -0,0 +1,16 @@
[package]
name = "rust_mc_proto_tokio"
description = "lightweight minecraft protocol support in pure rust"
repository = "https://git.meex.lol/MeexReay/rust_mc_proto"
license-file = "LICENSE"
readme = "README.md"
keywords = ["minecraft", "protocol", "packets", "lightweight", "tokio"]
version = "0.1.18"
edition = "2024"
[dependencies]
flate2 = "1.1.1"
uuid = "1.16.0"
tokio = { version = "1", features = ["io-util", "rt", "macros", "net", "rt-multi-thread"] }

14
LICENSE
View File

@ -1,11 +1,13 @@
DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
Version 2, December 2004
DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
Version 2, December 2004
Copyright (C) 2004 Sam Hocevar <sam@hocevar.net>
Everyone is permitted to copy and distribute verbatim or modified copies of this license document, and changing it is allowed as long as the name is changed.
Everyone is permitted to copy and distribute verbatim or modified
copies of this license document, and changing it is allowed as long
as the name is changed.
DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
0. You just DO WHAT THE FUCK YOU WANT TO.
0. You just DO WHAT THE FUCK YOU WANT TO.

View File

@ -1,3 +1,49 @@
# rust_mc_proto_tokio
Async minecraft packets protocol in pure rust
Lightweight minecraft packets protocol support in pure rust \
Has compression (`MinecraftConnection::set_compression`) \
This crate can be used for a server on rust idk -_-
## Setup
```toml
rust_mc_proto_tokio = "0.1.18" # stable version
rust_mc_proto_tokio = { git = "https://git.meex.lol/MeexReay/rust_mc_proto_tokio" } # unstable version
```
Features:
- `atomic_clone` - Atomic clone of MinecraftConnection
## How to use
Example of receiving motd:
```rust
use rust_mc_proto_tokio::{prelude::*, MCConnTcp, Packet, ProtocolError};
#[tokio::main]
async fn main() -> Result<(), ProtocolError> {
let mut conn = MCConnTcp::connect("localhost:25565").await?; // connecting
let mut packet = Packet::empty(0x00);
packet.write_u16_varint(765).await?; // protocol_version
packet.write_string("localhost").await?; // server_address
packet.write_unsigned_short(25565).await?; // server_port
packet.write_u8_varint(1).await?; // next_state
conn.write_packet(&packet).await?;
conn.write_packet(&Packet::empty(0x00)).await?; // status request packet
Ok(println!("motd: {}", conn.read_packet().await?.read_string().await?)) // status response packet
}
```
[More examples](https://git.meex.lol/MeexReay/rust_mc_proto/src/branch/main/examples) \
[Documentation](https://docs.rs/rust_mc_proto/)
### Contributing
If you would like to contribute to the project, feel free to fork the repository and submit a pull request.
### License
This project is licensed under the WTFPL License

43
examples/recv_motd.rs Normal file
View File

@ -0,0 +1,43 @@
use rust_mc_proto_tokio::{prelude::*, MCConnTcp, Packet, ProtocolError};
/*
Example of receiving motd from the server
Sends handshake, status request and receiving one
*/
async fn send_handshake(
conn: &mut MCConnTcp,
protocol_version: u16,
server_address: &str,
server_port: u16,
next_state: u8
) -> Result<(), ProtocolError> {
let mut packet = Packet::empty(0x00);
packet.write_u16_varint(protocol_version).await?;
packet.write_string(server_address).await?;
packet.write_unsigned_short(server_port).await?;
packet.write_u8_varint(next_state).await?;
conn.write_packet(&packet).await
}
async fn send_status_request(conn: &mut MCConnTcp) -> Result<(), ProtocolError> {
conn.write_packet(&Packet::empty(0x00)).await
}
async fn read_status_response(conn: &mut MCConnTcp) -> Result<String, ProtocolError> {
conn.read_packet().await?.read_string().await
}
#[tokio::main]
async fn main() {
let mut conn = MCConnTcp::connect("mc.hypixel.net:25565").await.unwrap();
send_handshake(&mut conn, 765, "mc.hypixel.net", 25565, 1).await.unwrap();
send_status_request(&mut conn).await.unwrap();
let motd = read_status_response(&mut conn).await.unwrap();
dbg!(motd);
}

View File

@ -0,0 +1,17 @@
use rust_mc_proto_tokio::{DataReader, DataWriter, MCConnTcp, Packet, ProtocolError};
#[tokio::main]
async fn main() -> Result<(), ProtocolError> {
let mut conn = MCConnTcp::connect("localhost:25565").await?; // connecting
let mut packet = Packet::empty(0x00);
packet.write_u16_varint(765).await?; // protocol_version
packet.write_string("localhost").await?; // server_address
packet.write_unsigned_short(25565).await?; // server_port
packet.write_u8_varint(1).await?; // next_state
conn.write_packet(&packet).await?;
conn.write_packet(&Packet::empty(0x00)).await?; // status request packet
Ok(println!("motd: {}", conn.read_packet().await?.read_string().await?)) // status response packet
}

122
examples/status_server.rs Normal file
View File

@ -0,0 +1,122 @@
use tokio::net::TcpListener;
use std::sync::Arc;
use rust_mc_proto_tokio::{prelude::*, MCConnTcp, MinecraftConnection, Packet, ProtocolError};
/*
Example of simple server that sends motd
to client like a vanilla minecraft server
*/
struct MinecraftServer {
server_ip: String,
server_port: u16,
motd: String
}
impl MinecraftServer {
fn new(server_ip: &str,
server_port: u16,
motd: &str) -> Self {
MinecraftServer {
server_ip: server_ip.to_string(),
server_port,
motd: motd.to_string()
}
}
async fn start(self) {
let addr = self.server_ip.clone() + ":" + &self.server_port.to_string();
let listener = TcpListener::bind(addr).await.unwrap();
let this = Arc::new(self);
while let Ok((stream, _)) = listener.accept().await {
tokio::spawn({
let this = this.clone();
async move {
Self::accept_client(this, MinecraftConnection::new(stream)).await.unwrap();
}
});
}
}
async fn accept_client(self: Arc<Self>, mut conn: MCConnTcp) -> Result<(), ProtocolError> {
let mut handshake = false;
loop {
let Ok(mut packet) = conn.read_packet().await else { break; };
if handshake {
if packet.id() == 0x00 {
let motd = self.motd.clone();
let mut status = Packet::empty(0x00);
status.write_string(&motd).await?;
conn.write_packet(&status).await?;
} else if packet.id() == 0x01 {
let mut status = Packet::empty(0x01);
status.write_long(packet.read_long().await?).await?;
conn.write_packet(&status).await?;
}
} else if packet.id() == 0x00 {
let protocol_version = packet.read_i32_varint().await?;
let server_address = packet.read_string().await?;
let server_port = packet.read_unsigned_short().await?;
let next_state = packet.read_u8_varint().await?;
if next_state != 1 { break; }
println!(
"{} > protocol: {} server: {}:{}",
conn.get_ref().peer_addr().unwrap(),
protocol_version,
server_address,
server_port
);
handshake = true;
} else {
break;
}
}
conn.close().await;
Ok(())
}
}
#[tokio::main]
async fn main() {
let server = MinecraftServer::new(
"127.0.0.1",
25565,
"{
\"version\":{
\"protocol\":765,
\"name\":\"Version name\"
},
\"players\":{
\"online\":0,
\"max\":1,
\"sample\":[
{
\"uuid\": \"\",
\"name\": \"Notch\"
}
]
},
\"description\": {
\"text\": \"Hello World!\",
\"color\": \"red\",
\"bold\": true
},
\"favicon\": \"data:image/png;base64,R0lGODlhAQABAIAAAP///wAAACwAAAAAAQABAAACAkQBADs=\"
}"
);
server.start().await;
}

8
src/data/mod.rs Normal file
View File

@ -0,0 +1,8 @@
//! `DataReader` and `DataWriter` traits for reading and writing primitive types in the Minecraft protocol
pub mod reader;
pub mod varint;
pub mod writer;
pub use reader::*;
pub use writer::*;

222
src/data/reader.rs Normal file
View File

@ -0,0 +1,222 @@
use crate::{
data::varint::{read_varint, size_varint},
zigzag::Zigzag,
ProtocolError,
};
use tokio::io::AsyncReadExt;
use uuid::Uuid;
/// Packet data reader trait
pub trait DataReader {
/// Read bytes
fn read_bytes(&mut self, size: usize) -> impl Future<Output = Result<Vec<u8>, ProtocolError>>;
/// Read byte
fn read_byte(&mut self) -> impl Future<Output = Result<u8, ProtocolError>> {
async {Ok(self.read_bytes(1).await?[0])}
}
/// Read String
fn read_string(&mut self) -> impl Future<Output = Result<String, ProtocolError>> {
async {
let size = self.read_usize_varint().await?;
match String::from_utf8(self.read_bytes(size).await?) {
Ok(i) => Ok(i),
Err(_) => Err(ProtocolError::StringParseError),
}
}
}
/// Read Unsigned Short as u16
fn read_unsigned_short(&mut self) -> impl Future<Output = Result<u16, ProtocolError>> {
async {match self.read_bytes(2).await?.try_into() {
Ok(i) => Ok(u16::from_be_bytes(i)),
Err(_) => Err(ProtocolError::ReadError),
}}
}
/// Read Boolean
fn read_boolean(&mut self) -> impl Future<Output = Result<bool, ProtocolError>> {
async {Ok(self.read_byte().await? == 0x01)}
}
/// Read Short as i16
fn read_short(&mut self) -> impl Future<Output = Result<i16, ProtocolError>> {
async {match self.read_bytes(2).await?.try_into() {
Ok(i) => Ok(i16::from_be_bytes(i)),
Err(_) => Err(ProtocolError::ReadError),
}}
}
/// Read Long as i64
fn read_long(&mut self) -> impl Future<Output = Result<i64, ProtocolError>> {
async {match self.read_bytes(8).await?.try_into() {
Ok(i) => Ok(i64::from_be_bytes(i)),
Err(_) => Err(ProtocolError::ReadError),
}}
}
/// Read Float as f32
fn read_float(&mut self) -> impl Future<Output = Result<f32, ProtocolError>> {
async {match self.read_bytes(4).await?.try_into() {
Ok(i) => Ok(f32::from_be_bytes(i)),
Err(_) => Err(ProtocolError::ReadError),
}}
}
/// Read Double as f64
fn read_double(&mut self) -> impl Future<Output = Result<f64, ProtocolError>> {
async {match self.read_bytes(8).await?.try_into() {
Ok(i) => Ok(f64::from_be_bytes(i)),
Err(_) => Err(ProtocolError::ReadError),
}}
}
/// Read Int as i32
fn read_int(&mut self) -> impl Future<Output = Result<i32, ProtocolError>> {
async {match self.read_bytes(4).await?.try_into() {
Ok(i) => Ok(i32::from_be_bytes(i)),
Err(_) => Err(ProtocolError::ReadError),
}}
}
/// Read UUID
fn read_uuid(&mut self) -> impl Future<Output = Result<Uuid, ProtocolError>> {
async {match self.read_bytes(16).await?.try_into() {
Ok(i) => Ok(Uuid::from_bytes(i)),
Err(_) => Err(ProtocolError::ReadError),
}}
}
/// Read VarInt as usize with size in bytes (varint, size)
fn read_usize_varint_size(&mut self) -> impl Future<Output = Result<(usize, usize), ProtocolError>> {
async {size_varint!(usize, self)}
}
/// Read VarInt as u8 with size in bytes (varint, size)
fn read_u8_varint_size(&mut self) -> impl Future<Output = Result<(u8, usize), ProtocolError>> {
async {size_varint!(u8, self)}
}
/// Read VarInt as u16 with size in bytes (varint, size)
fn read_u16_varint_size(&mut self) -> impl Future<Output = Result<(u16, usize), ProtocolError>> {
async {size_varint!(u16, self)}
}
/// Read VarInt as u32 with size in bytes (varint, size)
fn read_u32_varint_size(&mut self) -> impl Future<Output = Result<(u32, usize), ProtocolError>> {
async {size_varint!(u32, self)}
}
/// Read VarInt as u64 with size in bytes (varint, size)
fn read_u64_varint_size(&mut self) -> impl Future<Output = Result<(u64, usize), ProtocolError>> {
async {size_varint!(u64, self)}
}
/// Read VarInt as u128 with size in bytes (varint, size)
fn read_u128_varint_size(&mut self) -> impl Future<Output = Result<(u128, usize), ProtocolError>> {
async {size_varint!(u128, self)}
}
/// Read VarInt as isize with size in bytes (varint, size)
fn read_isize_varint_size(&mut self) -> impl Future<Output = Result<(isize, usize), ProtocolError>> {
async {Ok({
let i = self.read_usize_varint_size().await?;
(i.0.zigzag(), i.1)
})}
}
/// Read VarInt as i8 with size in bytes (varint, size)
fn read_i8_varint_size(&mut self) -> impl Future<Output = Result<(i8, usize), ProtocolError>> {
async {Ok({
let i = self.read_u8_varint_size().await?;
(i.0.zigzag(), i.1)
})}
}
/// Read VarInt as i16 with size in bytes (varint, size)
fn read_i16_varint_size(&mut self) -> impl Future<Output = Result<(i16, usize), ProtocolError>> {
async {Ok({
let i = self.read_u16_varint_size().await?;
(i.0.zigzag(), i.1)
})}
}
/// Read VarInt as i32 with size in bytes (varint, size)
fn read_i32_varint_size(&mut self) -> impl Future<Output = Result<(i32, usize), ProtocolError>> {
async {Ok({
let i = self.read_u32_varint_size().await?;
(i.0.zigzag(), i.1)
})}
}
/// Read VarInt as i64 with size in bytes (varint, size)
fn read_i64_varint_size(&mut self) -> impl Future<Output = Result<(i64, usize), ProtocolError>> {
async {Ok({
let i = self.read_u64_varint_size().await?;
(i.0.zigzag(), i.1)
})}
}
/// Read VarInt as i128 with size in bytes (varint, size)
fn read_i128_varint_size(&mut self) -> impl Future<Output = Result<(i128, usize), ProtocolError>> {
async {Ok({
let i = self.read_u128_varint_size().await?;
(i.0.zigzag(), i.1)
})}
}
/// Read VarInt as usize
fn read_usize_varint(&mut self) -> impl Future<Output = Result<usize, ProtocolError>> {
async {read_varint!(usize, self)}
}
/// Read VarInt as u8
fn read_u8_varint(&mut self) -> impl Future<Output = Result<u8, ProtocolError>> {
async {read_varint!(u8, self)}
}
/// Read VarInt as u16
fn read_u16_varint(&mut self) -> impl Future<Output = Result<u16, ProtocolError>> {
async {read_varint!(u16, self)}
}
/// Read VarInt as u32
fn read_u32_varint(&mut self) -> impl Future<Output = Result<u32, ProtocolError>> {
async {read_varint!(u32, self)}
}
/// Read VarInt as u64
fn read_u64_varint(&mut self) -> impl Future<Output = Result<u64, ProtocolError>> {
async {read_varint!(u64, self)}
}
/// Read VarInt as u128
fn read_u128_varint(&mut self) -> impl Future<Output = Result<u128, ProtocolError>> {
async {read_varint!(u128, self)}
}
/// Read VarInt as isize
fn read_isize_varint(&mut self) -> impl Future<Output = Result<isize, ProtocolError>> {
async {Ok(self.read_usize_varint().await?.zigzag())}
}
/// Read VarInt as i8
fn read_i8_varint(&mut self) -> impl Future<Output = Result<i8, ProtocolError>> {
async {Ok(self.read_u8_varint().await?.zigzag())}
}
/// Read VarInt as i16
fn read_i16_varint(&mut self) -> impl Future<Output = Result<i16, ProtocolError>> {
async {Ok(self.read_u16_varint().await?.zigzag())}
}
/// Read VarInt as i32
fn read_i32_varint(&mut self) -> impl Future<Output = Result<i32, ProtocolError>> {
async {Ok(self.read_u32_varint().await?.zigzag())}
}
/// Read VarInt as i64
fn read_i64_varint(&mut self) -> impl Future<Output = Result<i64, ProtocolError>> {
async {Ok(self.read_u64_varint().await?.zigzag())}
}
/// Read VarInt as i128
fn read_i128_varint(&mut self) -> impl Future<Output = Result<i128, ProtocolError>> {
async {Ok(self.read_u128_varint().await?.zigzag())}
}
}
impl<R: AsyncReadExt + Unpin> DataReader for R {
async fn read_bytes(&mut self, size: usize) -> Result<Vec<u8>, ProtocolError> {
let mut data = Vec::new();
while data.len() < size {
let mut buf = vec![0; size - data.len()];
let n = self.read(&mut buf).await
.map_err(|_| ProtocolError::ReadError)?;
if n == 0 {
return Err(ProtocolError::ConnectionClosedError);
}
buf.truncate(n);
data.append(&mut buf);
}
Ok(data)
}
}

70
src/data/varint.rs Normal file
View File

@ -0,0 +1,70 @@
macro_rules! size_varint {
($type:ty, $self:expr) => {{
let mut shift: $type = 0;
let mut decoded: $type = 0;
let mut size: usize = 0;
loop {
let next = DataReader::read_byte($self).await?;
size += 1;
if shift >= (std::mem::size_of::<$type>() * 8) as $type {
return Err(ProtocolError::VarIntError);
}
decoded |= ((next & 0b01111111) as $type) << shift;
if next & 0b10000000 == 0b10000000 {
shift += 7;
} else {
return Ok((decoded, size));
}
}
}};
}
macro_rules! read_varint {
($type:ty, $self:expr) => {{
let mut shift: $type = 0;
let mut decoded: $type = 0;
loop {
let next = DataReader::read_byte($self).await?;
if shift >= (std::mem::size_of::<$type>() * 8) as $type {
return Err(ProtocolError::VarIntError);
}
decoded |= ((next & 0b01111111) as $type) << shift;
if next & 0b10000000 == 0b10000000 {
shift += 7;
} else {
return Ok(decoded);
}
}
}};
}
macro_rules! write_varint {
($type:ty, $self:expr, $value:expr) => {{
let mut value: $type = $value;
if value == 0 {
DataWriter::write_byte($self, 0).await
} else {
while value >= 0b10000000 {
let next: u8 = ((value & 0b01111111) as u8) | 0b10000000;
value >>= 7;
DataWriter::write_byte($self, next).await?;
}
DataWriter::write_byte($self, (value & 0b01111111) as u8).await
}
}};
}
pub(crate) use read_varint;
pub(crate) use size_varint;
pub(crate) use write_varint;

110
src/data/writer.rs Normal file
View File

@ -0,0 +1,110 @@
use crate::{data::varint::write_varint, zigzag::Zigzag, ProtocolError};
use tokio::io::AsyncWriteExt;
use uuid::Uuid;
/// Packet data writer trait
pub trait DataWriter {
/// Write bytes
fn write_bytes(&mut self, bytes: &[u8]) -> impl Future<Output = Result<(), ProtocolError>>;
/// Write byte
fn write_byte(&mut self, byte: u8) -> impl Future<Output = Result<(), ProtocolError>> {
async move { self.write_bytes(&[byte]).await }
}
/// Write String
fn write_string(&mut self, val: &str) -> impl Future<Output = Result<(), ProtocolError>> {
async move {
let bytes = val.as_bytes();
self.write_usize_varint(bytes.len()).await?;
self.write_bytes(bytes).await
}
}
/// Write UUID
fn write_uuid(&mut self, val: &Uuid) -> impl Future<Output = Result<(), ProtocolError>> {
async move { self.write_bytes(val.as_bytes()).await }
}
/// Write Unsigned Short as u16
fn write_unsigned_short(&mut self, val: u16) -> impl Future<Output = Result<(), ProtocolError>> {
async move { self.write_bytes(&val.to_be_bytes()).await.map_err(|_| ProtocolError::UnsignedShortError) }
}
/// Write Boolean
fn write_boolean(&mut self, val: bool) -> impl Future<Output = Result<(), ProtocolError>> {
async move { self.write_byte(if val { 0x01 } else { 0x00 }).await.map_err(|_| ProtocolError::UnsignedShortError) }
}
/// Write Short as i16
fn write_short(&mut self, val: i16) -> impl Future<Output = Result<(), ProtocolError>> {
async move { self.write_bytes(&val.to_be_bytes()).await.map_err(|_| ProtocolError::UnsignedShortError) }
}
/// Write Long as i64
fn write_long(&mut self, val: i64) -> impl Future<Output = Result<(), ProtocolError>> {
async move { self.write_bytes(&val.to_be_bytes()).await.map_err(|_| ProtocolError::UnsignedShortError) }
}
/// Write Float as f32
fn write_float(&mut self, val: f32) -> impl Future<Output = Result<(), ProtocolError>> {
async move { self.write_bytes(&val.to_be_bytes()).await.map_err(|_| ProtocolError::UnsignedShortError) }
}
/// Write Double as f64
fn write_double(&mut self, val: f64) -> impl Future<Output = Result<(), ProtocolError>> {
async move { self.write_bytes(&val.to_be_bytes()).await.map_err(|_| ProtocolError::UnsignedShortError) }
}
/// Write Int as i32
fn write_int(&mut self, val: i32) -> impl Future<Output = Result<(), ProtocolError>> {
async move { self.write_bytes(&val.to_be_bytes()).await.map_err(|_| ProtocolError::UnsignedShortError) }
}
/// Write VarInt as usize
fn write_usize_varint(&mut self, val: usize) -> impl Future<Output = Result<(), ProtocolError>> {
async move { write_varint!(usize, self, val) }
}
/// Write VarInt as u8
fn write_u8_varint(&mut self, val: u8) -> impl Future<Output = Result<(), ProtocolError>> {
async move { write_varint!(u8, self, val) }
}
/// Write VarInt as u16
fn write_u16_varint(&mut self, val: u16) -> impl Future<Output = Result<(), ProtocolError>> {
async move { write_varint!(u16, self, val) }
}
/// Write VarInt as u32
fn write_u32_varint(&mut self, val: u32) -> impl Future<Output = Result<(), ProtocolError>> {
async move { write_varint!(u32, self, val) }
}
/// Write VarInt as u64
fn write_u64_varint(&mut self, val: u64) -> impl Future<Output = Result<(), ProtocolError>> {
async move { write_varint!(u64, self, val) }
}
/// Write VarInt as u128
fn write_u128_varint(&mut self, val: u128) -> impl Future<Output = Result<(), ProtocolError>> {
async move { write_varint!(u128, self, val) }
}
/// Write VarInt as isize
fn write_isize_varint(&mut self, val: isize) -> impl Future<Output = Result<(), ProtocolError>> {
async move { self.write_usize_varint(val.zigzag()).await }
}
/// Write VarInt as i8
fn write_i8_varint(&mut self, val: i8) -> impl Future<Output = Result<(), ProtocolError>> {
async move { self.write_u8_varint(val.zigzag()).await }
}
/// Write VarInt as i16
fn write_i16_varint(&mut self, val: i16) -> impl Future<Output = Result<(), ProtocolError>> {
async move { self.write_u16_varint(val.zigzag()).await }
}
/// Write VarInt as i32
fn write_i32_varint(&mut self, val: i32) -> impl Future<Output = Result<(), ProtocolError>> {
async move { self.write_u32_varint(val.zigzag()).await }
}
/// Write VarInt as i64
fn write_i64_varint(&mut self, val: i64) -> impl Future<Output = Result<(), ProtocolError>> {
async move { self.write_u64_varint(val.zigzag()).await }
}
/// Write VarInt as i128
fn write_i128_varint(&mut self, val: i128) -> impl Future<Output = Result<(), ProtocolError>> {
async move { self.write_u128_varint(val.zigzag()).await }
}
}
impl<W: AsyncWriteExt + Unpin> DataWriter for W {
async fn write_bytes(&mut self, bytes: &[u8]) -> Result<(), ProtocolError> {
self.write_all(bytes).await.map_err(|_| ProtocolError::WriteError)
}
}

292
src/lib.rs Normal file
View File

@ -0,0 +1,292 @@
#[cfg(test)]
mod tests;
pub mod data;
pub mod packet;
pub mod zigzag;
pub mod prelude {
pub use crate::{DataReader, DataWriter};
}
pub use crate::{
data::{DataReader, DataWriter},
packet::Packet,
};
use flate2::{read::ZlibDecoder, write::ZlibEncoder, Compression};
use tokio::{io::{AsyncReadExt, AsyncWriteExt}, net::TcpStream};
use std::{
error::Error, fmt, io::{Read, Write}, net::ToSocketAddrs, usize
};
/// Minecraft protocol error
#[derive(Debug)]
pub enum ProtocolError {
AddressParseError,
DataRanOutError,
StringParseError,
StreamConnectError,
VarIntError,
ReadError,
WriteError,
ZlibError,
UnsignedShortError,
CloneError,
ConnectionClosedError
}
impl fmt::Display for ProtocolError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "An protocol error occured")
}
}
impl Error for ProtocolError {}
/// Minecraft connection, wrapper for stream with compression
pub struct MinecraftConnection<T: AsyncReadExt + AsyncWriteExt + Unpin> {
stream: T,
compression: Option<usize>,
compression_type: u32,
is_alive: bool,
}
impl MinecraftConnection<TcpStream> {
/// Connect to Minecraft Server with TcpStream
pub async fn connect(addr: &str) -> Result<MinecraftConnection<TcpStream>, ProtocolError> {
let addr = match addr.to_socket_addrs() {
Ok(mut i) => match i.next() {
Some(i) => i,
None => return Err(ProtocolError::AddressParseError),
},
Err(_) => return Err(ProtocolError::AddressParseError),
};
let stream = TcpStream::connect(&addr).await
.map_err(|_| ProtocolError::StreamConnectError)?;
Ok(MinecraftConnection {
stream,
compression: None,
is_alive: true,
compression_type: 1,
})
}
/// Close TcpStream
pub async fn close(&mut self) {
let _ = self.stream.shutdown().await;
self.is_alive = false;
}
}
impl<T: AsyncReadExt + AsyncWriteExt + Unpin> DataReader for MinecraftConnection<T> {
async fn read_bytes(&mut self, size: usize) -> Result<Vec<u8>, ProtocolError> {
let mut buf = vec![0; size];
match self.stream.read_exact(&mut buf).await {
Ok(_) => Ok(buf),
Err(_) => Err(ProtocolError::ReadError),
}
}
}
impl<T: AsyncReadExt + AsyncWriteExt + Unpin> DataWriter for MinecraftConnection<T> {
async fn write_bytes(&mut self, bytes: &[u8]) -> Result<(), ProtocolError> {
self.stream.write_all(bytes).await.map_err(|_| ProtocolError::WriteError)
}
}
impl<T: AsyncReadExt + AsyncWriteExt + Unpin> MinecraftConnection<T> {
/// Create new MinecraftConnection from stream
pub fn new(stream: T) -> MinecraftConnection<T> {
MinecraftConnection {
stream,
compression: None,
is_alive: true,
compression_type: 1,
}
}
/// Set alive state
fn set_alive(&mut self, state: bool) {
self.is_alive = state;
}
/// Is connection alive
pub fn is_alive(&self) -> bool {
self.is_alive
}
/// Set compression threshold
pub fn set_compression(&mut self, threshold: Option<usize>) {
self.compression = threshold;
}
/// Get compression threshold
pub fn compression(&self) -> Option<usize> {
self.compression
}
/// Set compression type
///
/// `compression_type` is integer from 0 (none) to 9 (longest)
/// 1 is fast compression
/// 6 is normal compression
pub fn set_compression_type(&mut self, compression_type: u32) {
self.compression_type = compression_type;
}
/// Get compression type
///
/// `compression_type` is integer from 0 (none) to 9 (longest)
/// 1 is fast compression
/// 6 is normal compression
pub fn compression_type(&self) -> u32 {
self.compression_type
}
/// Get mutable reference of stream
pub fn get_mut(&mut self) -> &mut T {
&mut self.stream
}
/// Get immutable reference of stream
pub fn get_ref(&self) -> &T {
&self.stream
}
/// Read [`Packet`](Packet) from connection
pub async fn read_packet(&mut self) -> Result<Packet, ProtocolError> {
if !self.is_alive() {
return Err(ProtocolError::ConnectionClosedError);
}
match read_packet(&mut self.stream, self.compression).await {
Err(ProtocolError::ConnectionClosedError) => {
self.set_alive(false);
Err(ProtocolError::ConnectionClosedError)
},
i => i
}
}
/// Write [`Packet`](Packet) to connection
pub async fn write_packet(&mut self, packet: &Packet) -> Result<(), ProtocolError> {
if !self.is_alive() {
return Err(ProtocolError::ConnectionClosedError);
}
write_packet(&mut self.stream, self.compression, self.compression_type, packet).await
}
}
impl<T: AsyncReadExt + AsyncWriteExt + Unpin + Clone> MinecraftConnection<T> {
/// Clone MinecraftConnection with compression and stream
pub fn clone(&mut self) -> MinecraftConnection<T> {
MinecraftConnection {
stream: self.stream.clone(),
compression: self.compression.clone(),
is_alive: self.is_alive.clone(),
compression_type: self.compression_type,
}
}
}
fn compress_zlib(bytes: &[u8], compression: u32) -> Result<Vec<u8>, ProtocolError> {
let mut encoder = ZlibEncoder::new(Vec::new(), Compression::new(compression));
encoder.write_all(bytes).or(Err(ProtocolError::ZlibError))?;
encoder.finish().or(Err(ProtocolError::ZlibError))
}
fn decompress_zlib(bytes: &[u8]) -> Result<Vec<u8>, ProtocolError> {
let mut decoder = ZlibDecoder::new(bytes);
let mut output = Vec::new();
decoder
.read_to_end(&mut output)
.or(Err(ProtocolError::ZlibError))?;
Ok(output)
}
/// MinecraftConnection shorter alias
pub type MCConn<T> = MinecraftConnection<T>;
/// MinecraftConnection\<TcpStream\> shorter alias
pub type MCConnTcp = MinecraftConnection<TcpStream>;
/// Read [`Packet`](Packet) from stream
///
/// `compression` here is atomic usize
/// usize::MAX means that compression is disabled
///
/// `ordering` is order how to load atomic
pub async fn read_packet<T: AsyncReadExt + Unpin>(
stream: &mut T,
compression: Option<usize>
) -> Result<Packet, ProtocolError> {
let mut data: Vec<u8>;
let packet_length = stream.read_usize_varint_size().await?;
if compression.is_some() {
let data_length = stream.read_usize_varint_size().await?;
data = stream.read_bytes(packet_length.0 - data_length.1).await?;
if data_length.0 != 0 {
data = decompress_zlib(&data)?;
}
} else {
data = stream.read_bytes(packet_length.0).await?;
}
Ok(Packet::from_data(&data).await?)
}
/// Write [`Packet`](Packet) to stream
///
/// `compression` here is usize
/// usize::MAX means that compression is disabled
///
/// `ordering` is order how to load atomic
///
/// `compression_type` is integer from 0 (none) to 9 (longest)
/// 1 is fast compression
/// 6 is normal compression
pub async fn write_packet<T: AsyncWriteExt + Unpin>(
stream: &mut T,
compression: Option<usize>,
compression_type: u32,
packet: &Packet,
) -> Result<(), ProtocolError> {
let mut buf = Vec::new();
let mut data_buf = Vec::new();
data_buf.write_u8_varint(packet.id()).await?;
data_buf.write_bytes(packet.get_bytes()).await?;
if let Some(compression) = compression {
let mut packet_buf = Vec::new();
if data_buf.len() >= compression {
let compressed_data = compress_zlib(&data_buf, compression_type)?;
packet_buf.write_usize_varint(data_buf.len()).await?;
Write::write_all(&mut packet_buf, &compressed_data)
.or(Err(ProtocolError::WriteError))?;
} else {
packet_buf.write_usize_varint(0).await?;
packet_buf.write_bytes(&data_buf).await?;
}
buf.write_usize_varint(packet_buf.len()).await?;
buf.write_bytes(&packet_buf).await?;
} else {
buf.write_usize_varint(data_buf.len()).await?;
buf.write_bytes(&data_buf).await?;
}
stream.write_bytes(&buf).await?;
Ok(())
}

123
src/packet.rs Normal file
View File

@ -0,0 +1,123 @@
//! Minecraft packet struct
use crate::data::{DataReader, DataWriter};
use crate::ProtocolError;
use std::io::Cursor;
/// Minecraft packet
#[derive(Debug, Clone)]
pub struct Packet {
id: u8,
cursor: Cursor<Vec<u8>>,
}
impl Packet {
/// Create new packet from id and buffer
pub fn new(id: u8, cursor: Cursor<Vec<u8>>) -> Packet {
Packet { id, cursor }
}
/// Create new packet from raw packet (id + data)
pub async fn from_data(data: &[u8]) -> Result<Packet, ProtocolError> {
let mut cursor = Cursor::new(data);
let (packet_id, packet_id_size) = cursor.read_u8_varint_size().await?;
let packet_data =
DataReader::read_bytes(&mut cursor, data.len() - packet_id_size as usize).await?;
Ok(Packet {
id: packet_id,
cursor: Cursor::new(packet_data),
})
}
/// Create new packet from id and bytes in buffer
pub fn from_bytes(id: u8, data: &[u8]) -> Packet {
Packet {
id,
cursor: Cursor::new(data.to_vec()),
}
}
/// Create new packet with id and empty buffer
pub fn empty(id: u8) -> Packet {
Packet {
id,
cursor: Cursor::new(Vec::new()),
}
}
/// Build packet with lambda
pub async fn build<F, Fut>(id: u8, builder: F) -> Result<Packet, ProtocolError>
where
F: FnOnce(&mut Packet) -> Fut,
Fut: Future<Output = Result<(), ProtocolError>> + Send
{
let mut packet = Self::empty(id);
builder(&mut packet).await?;
Ok(packet)
}
/// Get packet id
pub fn id(&self) -> u8 {
self.id
}
/// Set packet id
pub fn set_id(&mut self, id: u8) {
self.id = id;
}
/// Set packet cursor
pub fn set_cursor(&mut self, cursor: Cursor<Vec<u8>>) {
self.cursor = cursor;
}
/// Get cursor length
pub fn len(&self) -> usize {
self.cursor.get_ref().len() - self.cursor.position() as usize
}
/// Is cursor empty
pub fn is_empty(&self) -> bool {
self.len() == 0
}
/// Get cursor remaining bytes
pub fn get_bytes(&self) -> &[u8] {
&self.cursor.get_ref()
}
/// Get mutable reference to cursor
pub fn get_mut(&mut self) -> &mut Cursor<Vec<u8>> {
&mut self.cursor
}
/// Get immutable reference to cursor
pub fn get_ref(&self) -> &Cursor<Vec<u8>> {
&self.cursor
}
/// Get inner cursor
pub fn into_inner(self) -> Cursor<Vec<u8>> {
self.cursor
}
}
impl Into<Cursor<Vec<u8>>> for Packet {
fn into(self) -> Cursor<Vec<u8>> {
self.cursor
}
}
impl DataReader for Packet {
async fn read_bytes(&mut self, size: usize) -> Result<Vec<u8>, ProtocolError> {
self.cursor.read_bytes(size).await
}
}
impl DataWriter for Packet {
async fn write_bytes(&mut self, bytes: &[u8]) -> Result<(), ProtocolError> {
self.cursor.write_bytes(bytes).await
}
}

162
src/tests.rs Normal file
View File

@ -0,0 +1,162 @@
use uuid::Uuid;
use tokio::net::TcpListener;
use std::io::Cursor;
use crate::*;
#[tokio::test]
async fn test_data_transfer() -> Result<(), ProtocolError> {
async fn server_thread() -> Result<(), ProtocolError> {
let listener =
TcpListener::bind("localhost:44447").await.or(Err(ProtocolError::StreamConnectError))?;
while let Ok((stream, _)) = listener.accept().await {
let mut stream = MCConnTcp::new(stream);
stream.set_compression(Some(5));
let mut packet = stream.read_packet().await?;
let mut pack = Packet::empty(packet.id());
pack.write_boolean(packet.read_boolean().await?).await?;
pack.write_byte(packet.read_byte().await?).await?;
pack.write_bytes(&packet.read_bytes(10).await?.clone()).await?;
pack.write_double(packet.read_double().await?).await?;
pack.write_float(packet.read_float().await?).await?;
pack.write_i128_varint(packet.read_i128_varint().await?).await?;
pack.write_u128_varint(packet.read_u128_varint().await?).await?;
pack.write_int(packet.read_int().await?).await?;
pack.write_long(packet.read_long().await?).await?;
pack.write_short(packet.read_short().await?).await?;
pack.write_uuid(&packet.read_uuid().await?).await?;
pack.write_string(&packet.read_string().await?.clone()).await?;
stream.write_packet(&pack).await?;
stream.set_compression(None);
let mut packet = stream.read_packet().await?;
let mut pack = Packet::empty(packet.id());
pack.write_boolean(packet.read_boolean().await?).await?;
pack.write_byte(packet.read_byte().await?).await?;
pack.write_bytes(&packet.read_bytes(10).await?.clone()).await?;
pack.write_double(packet.read_double().await?).await?;
pack.write_float(packet.read_float().await?).await?;
pack.write_i128_varint(packet.read_i128_varint().await?).await?;
pack.write_u128_varint(packet.read_u128_varint().await?).await?;
pack.write_int(packet.read_int().await?).await?;
pack.write_long(packet.read_long().await?).await?;
pack.write_short(packet.read_short().await?).await?;
pack.write_uuid(&packet.read_uuid().await?).await?;
pack.write_string(&packet.read_string().await?.clone()).await?;
stream.write_packet(&pack).await?;
}
Ok(())
}
tokio::spawn(async { let _ = server_thread().await; });
let conn = MCConnTcp::connect("localhost:44447").await;
while let Err(_) = conn {}
let mut conn = conn?;
conn.set_compression(Some(5));
let mut pack = Packet::empty(0xfe);
pack.write_boolean(true).await?;
pack.write_byte(0x12).await?;
pack.write_bytes(&vec![0x01, 0x56, 0x47, 0x48, 0xf5, 0xc2, 0x45, 0x98, 0xde, 0x99]).await?;
pack.write_double(123456789.123456789f64).await?;
pack.write_float(789456.44422f32).await?;
pack.write_i128_varint(468927513325566).await?;
pack.write_u128_varint(99859652365236523).await?;
pack.write_int(77861346i32).await?;
pack.write_long(789465123545678946i64).await?;
pack.write_short(1233i16).await?;
pack.write_uuid(&Uuid::try_parse("550e8400-e29b-41d4-a716-446655440000").map_err(|_| ProtocolError::CloneError)?).await?;
pack.write_string("&packet.read_string()?").await?;
conn.write_packet(&pack).await?;
let mut packet = conn.read_packet().await?;
assert_eq!(packet.read_boolean().await?, true);
assert_eq!(packet.read_byte().await?, 0x12);
assert_eq!(packet.read_bytes(10).await?, vec![0x01, 0x56, 0x47, 0x48, 0xf5, 0xc2, 0x45, 0x98, 0xde, 0x99]);
assert_eq!(packet.read_double().await?, 123456789.123456789f64);
assert_eq!(packet.read_float().await?, 789456.44422f32);
assert_eq!(packet.read_i128_varint().await?, 468927513325566);
assert_eq!(packet.read_u128_varint().await?, 99859652365236523);
assert_eq!(packet.read_int().await?, 77861346i32);
assert_eq!(packet.read_long().await?, 789465123545678946i64);
assert_eq!(packet.read_short().await?, 1233i16);
assert_eq!(packet.read_uuid().await?, Uuid::try_parse("550e8400-e29b-41d4-a716-446655440000").map_err(|_| ProtocolError::CloneError)?);
assert_eq!(packet.read_string().await?, "&packet.read_string()?");
conn.set_compression(None);
let mut pack = Packet::empty(0xfe);
pack.write_boolean(true).await?;
pack.write_byte(0x12).await?;
pack.write_bytes(&vec![0x01, 0x56, 0x47, 0x48, 0xf5, 0xc2, 0x45, 0x98, 0xde, 0x99]).await?;
pack.write_double(123456789.123456789f64).await?;
pack.write_float(789456.44422f32).await?;
pack.write_i128_varint(468927513325566).await?;
pack.write_u128_varint(99859652365236523).await?;
pack.write_int(77861346i32).await?;
pack.write_long(789465123545678946i64).await?;
pack.write_short(1233i16).await?;
pack.write_uuid(&Uuid::try_parse("550e8400-e29b-41d4-a716-446655440000").map_err(|_| ProtocolError::CloneError)?).await?;
pack.write_string("&packet.read_string()?").await?;
conn.write_packet(&pack).await?;
let mut packet = conn.read_packet().await?;
assert_eq!(packet.read_boolean().await?, true);
assert_eq!(packet.read_byte().await?, 0x12);
assert_eq!(packet.read_bytes(10).await?, vec![0x01, 0x56, 0x47, 0x48, 0xf5, 0xc2, 0x45, 0x98, 0xde, 0x99]);
assert_eq!(packet.read_double().await?, 123456789.123456789f64);
assert_eq!(packet.read_float().await?, 789456.44422f32);
assert_eq!(packet.read_i128_varint().await?, 468927513325566);
assert_eq!(packet.read_u128_varint().await?, 99859652365236523);
assert_eq!(packet.read_int().await?, 77861346i32);
assert_eq!(packet.read_long().await?, 789465123545678946i64);
assert_eq!(packet.read_short().await?, 1233i16);
assert_eq!(packet.read_uuid().await?, Uuid::try_parse("550e8400-e29b-41d4-a716-446655440000").map_err(|_| ProtocolError::CloneError)?);
assert_eq!(packet.read_string().await?, "&packet.read_string()?");
Ok(())
}
#[tokio::test]
async fn test_compression() -> Result<(), ProtocolError> {
let mut conn = MCConn::new(Cursor::new(Vec::new()));
conn.set_compression(Some(5));
let mut packet_1 = Packet::empty(0x12);
packet_1.write_bytes(b"1234567890qwertyuiopasdfghjklzxcvbnm").await?;
dbg!(&packet_1);
conn.write_packet(&packet_1).await?;
conn.get_mut().set_position(0);
let mut packet_2 = conn.read_packet().await?;
assert_eq!(
packet_2.read_bytes(36).await?,
b"1234567890qwertyuiopasdfghjklzxcvbnm"
);
Ok(())
}

65
src/zigzag.rs Normal file
View File

@ -0,0 +1,65 @@
//! VarInt reading helper
pub trait Zigzag<T> {
fn zigzag(&self) -> T;
}
impl Zigzag<u8> for i8 {
fn zigzag(&self) -> u8 {
((self << 1) ^ (self >> 7)) as u8
}
}
impl Zigzag<i8> for u8 {
fn zigzag(&self) -> i8 {
((self >> 1) as i8) ^ (-((self & 1) as i8))
}
}
impl Zigzag<u16> for i16 {
fn zigzag(&self) -> u16 {
((self << 1) ^ (self >> 15)) as u16
}
}
impl Zigzag<i16> for u16 {
fn zigzag(&self) -> i16 {
((self >> 1) as i16) ^ (-((self & 1) as i16))
}
}
impl Zigzag<u32> for i32 {
fn zigzag(&self) -> u32 {
((self << 1) ^ (self >> 31)) as u32
}
}
impl Zigzag<i32> for u32 {
fn zigzag(&self) -> i32 {
((self >> 1) as i32) ^ (-((self & 1) as i32))
}
}
impl Zigzag<u64> for i64 {
fn zigzag(&self) -> u64 {
((self << 1) ^ (self >> 63)) as u64
}
}
impl Zigzag<i64> for u64 {
fn zigzag(&self) -> i64 {
((self >> 1) as i64) ^ (-((self & 1) as i64))
}
}
impl Zigzag<u128> for i128 {
fn zigzag(&self) -> u128 {
((self << 1) ^ (self >> 127)) as u128
}
}
impl Zigzag<i128> for u128 {
fn zigzag(&self) -> i128 {
((self >> 1) as i128) ^ (-((self & 1) as i128))
}
}
impl Zigzag<usize> for isize {
fn zigzag(&self) -> usize {
((self << 1) ^ (self >> std::mem::size_of::<usize>() - 1)) as usize
}
}
impl Zigzag<isize> for usize {
fn zigzag(&self) -> isize {
((self >> 1) as isize) ^ (-((self & 1) as isize))
}
}