init commit
This commit is contained in:
parent
0475b6a6cf
commit
c0f077a10e
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
@ -0,0 +1 @@
|
||||
target/
|
289
Cargo.lock
generated
Normal file
289
Cargo.lock
generated
Normal 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
16
Cargo.toml
Normal 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
14
LICENSE
@ -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.
|
48
README.md
48
README.md
@ -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
43
examples/recv_motd.rs
Normal 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);
|
||||
}
|
17
examples/recv_motd_dirty.rs
Normal file
17
examples/recv_motd_dirty.rs
Normal 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
122
examples/status_server.rs
Normal 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
8
src/data/mod.rs
Normal 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
222
src/data/reader.rs
Normal 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
70
src/data/varint.rs
Normal 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
110
src/data/writer.rs
Normal 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
292
src/lib.rs
Normal 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
123
src/packet.rs
Normal 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
162
src/tests.rs
Normal 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
65
src/zigzag.rs
Normal 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))
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user