init commit??
This commit is contained in:
parent
32c09ce144
commit
39df5454a2
73
Cargo.lock
generated
Normal file
73
Cargo.lock
generated
Normal file
@ -0,0 +1,73 @@
|
|||||||
|
# This file is automatically @generated by Cargo.
|
||||||
|
# It is not intended for manual editing.
|
||||||
|
version = 3
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "adler"
|
||||||
|
version = "1.0.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "bytebuffer"
|
||||||
|
version = "2.2.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "fa7bfaf7cd08cacd74cdc6b521c37ac39cbc92692e5ab5c21ed5657a749b577c"
|
||||||
|
dependencies = [
|
||||||
|
"byteorder",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "byteorder"
|
||||||
|
version = "1.5.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b"
|
||||||
|
|
||||||
|
[[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.0.30"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "5f54427cfd1c7829e2a139fcefea601bf088ebca651d2bf53ebc600eac295dae"
|
||||||
|
dependencies = [
|
||||||
|
"crc32fast",
|
||||||
|
"miniz_oxide",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "miniz_oxide"
|
||||||
|
version = "0.7.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "9d811f3e15f28568be3407c8e7fdb6514c1cda3cb30683f15b6a1a1dc4ea14a7"
|
||||||
|
dependencies = [
|
||||||
|
"adler",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rust_mc_proto"
|
||||||
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"bytebuffer",
|
||||||
|
"flate2",
|
||||||
|
"varint-rs",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "varint-rs"
|
||||||
|
version = "2.2.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "8f54a172d0620933a27a4360d3db3e2ae0dd6cceae9730751a036bbf182c4b23"
|
15
Cargo.toml
Normal file
15
Cargo.toml
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
[package]
|
||||||
|
name = "rust_mc_proto"
|
||||||
|
description = "Minecraft packets protocol in pure rust"
|
||||||
|
|
||||||
|
license-file = "LICENSE"
|
||||||
|
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2021"
|
||||||
|
|
||||||
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
varint-rs = "2.2.0"
|
||||||
|
flate2 = "1.0.30"
|
||||||
|
bytebuffer = "2.2.0"
|
13
LICENSE
Normal file
13
LICENSE
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
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.
|
||||||
|
|
||||||
|
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.
|
@ -1 +1,2 @@
|
|||||||
# rust_mc_proto
|
# rust_mc_proto
|
||||||
|
Minecraft packets protocol in pure rust
|
305
src/lib.rs
Normal file
305
src/lib.rs
Normal file
@ -0,0 +1,305 @@
|
|||||||
|
use std::io::{Write, Read};
|
||||||
|
use std::net::{SocketAddr, TcpStream, ToSocketAddrs};
|
||||||
|
use bytebuffer::ByteBuffer;
|
||||||
|
use varint_rs::{VarintWriter, VarintReader};
|
||||||
|
use flate2::{Compress, Compression, Decompress, FlushCompress, Status, FlushDecompress};
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub enum ProtocolError {
|
||||||
|
AddressParseError,
|
||||||
|
DataRanOutError,
|
||||||
|
StringParseError,
|
||||||
|
StreamConnectError,
|
||||||
|
VarIntError,
|
||||||
|
ReadError,
|
||||||
|
WriteError,
|
||||||
|
ZlibError,
|
||||||
|
UnsignedShortError
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct Packet {
|
||||||
|
pub id: u8,
|
||||||
|
pub buffer: ByteBuffer
|
||||||
|
}
|
||||||
|
|
||||||
|
impl VarintWriter for Packet {
|
||||||
|
type Error = ProtocolError;
|
||||||
|
|
||||||
|
fn write(&mut self, byte: u8) -> Result<(), Self::Error> {
|
||||||
|
match Write::write(&mut self.buffer, &[byte]) {
|
||||||
|
Ok(_) => Ok(()),
|
||||||
|
Err(_) => Err(ProtocolError::WriteError)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl VarintReader for Packet {
|
||||||
|
type Error = ProtocolError;
|
||||||
|
|
||||||
|
fn read(&mut self) -> Result<u8, Self::Error> {
|
||||||
|
let mut buf = vec![0; 1];
|
||||||
|
|
||||||
|
match Read::read(&mut self.buffer, &mut buf) {
|
||||||
|
Ok(i) => {
|
||||||
|
if i == 1 {
|
||||||
|
Ok(buf[0])
|
||||||
|
} else {
|
||||||
|
Err(ProtocolError::ReadError)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
Err(_) => Err(ProtocolError::ReadError),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Packet {
|
||||||
|
pub fn new(id: u8, buffer: ByteBuffer) -> Packet {
|
||||||
|
Packet {
|
||||||
|
id,
|
||||||
|
buffer
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn from_bytes(id: u8, data: &[u8]) -> Packet {
|
||||||
|
Packet {
|
||||||
|
id,
|
||||||
|
buffer: ByteBuffer::from_bytes(data)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn empty(id: u8) -> Packet {
|
||||||
|
Packet {
|
||||||
|
id,
|
||||||
|
buffer: ByteBuffer::new()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn write_string(&mut self, s: String) -> Result<(), ProtocolError> {
|
||||||
|
let bytes = s.as_bytes();
|
||||||
|
|
||||||
|
self.write_usize_varint(bytes.len())?;
|
||||||
|
|
||||||
|
for b in bytes {
|
||||||
|
self.write(*b)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn read_string(&mut self) -> Result<String, ProtocolError> {
|
||||||
|
let size = self.read_usize_varint()?;
|
||||||
|
let mut bytes: Vec<u8> = vec![0; size];
|
||||||
|
|
||||||
|
for _ in 0..size {
|
||||||
|
bytes.push(self.read()?);
|
||||||
|
}
|
||||||
|
|
||||||
|
match String::from_utf8(bytes) {
|
||||||
|
Ok(i) => Ok(i),
|
||||||
|
Err(_) => Err(ProtocolError::StringParseError)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn write_unsigned_short(&mut self, short: u16) -> Result<(), ProtocolError> {
|
||||||
|
match self.buffer.write_all(&short.to_be_bytes()) {
|
||||||
|
Ok(_) => Ok(()),
|
||||||
|
Err(_) => Err(ProtocolError::UnsignedShortError),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct Connection {
|
||||||
|
pub stream: TcpStream,
|
||||||
|
pub addr: SocketAddr,
|
||||||
|
compress: bool,
|
||||||
|
compress_threashold: usize
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Connection {
|
||||||
|
pub fn build(addr: &str) -> Result<Connection, 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 = match TcpStream::connect(&addr) {
|
||||||
|
Ok(i) => i,
|
||||||
|
Err(_) => { return Err(ProtocolError::StreamConnectError) },
|
||||||
|
};
|
||||||
|
|
||||||
|
Ok(Connection {
|
||||||
|
stream,
|
||||||
|
addr,
|
||||||
|
compress: false,
|
||||||
|
compress_threashold: 0
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn new(addr: &str) -> Connection {
|
||||||
|
Self::build(addr).unwrap()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_compression(&mut self, threashold: usize) {
|
||||||
|
self.compress = true;
|
||||||
|
self.compress_threashold = threashold;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn read_packet(&mut self) -> Result<Packet, ProtocolError> {
|
||||||
|
if !self.compress {
|
||||||
|
let length = self.read_usize_varint()?;
|
||||||
|
|
||||||
|
let packet_id = self.read_u8_varint()?;
|
||||||
|
let mut data: Vec<u8> = vec![0; length - 1];
|
||||||
|
match self.stream.read_exact(&mut data) {
|
||||||
|
Ok(i) => i,
|
||||||
|
Err(_) => { return Err(ProtocolError::ReadError) },
|
||||||
|
};
|
||||||
|
|
||||||
|
Ok(Packet::from_bytes(packet_id, &data))
|
||||||
|
} else {
|
||||||
|
let packet_length = self.read_usize_varint()?;
|
||||||
|
let data_length = self.read_usize_varint()?;
|
||||||
|
|
||||||
|
if data_length == 0 {
|
||||||
|
let mut data: Vec<u8> = vec![0; packet_length - 1];
|
||||||
|
match self.stream.read_exact(&mut data) {
|
||||||
|
Ok(i) => i,
|
||||||
|
Err(_) => { return Err(ProtocolError::ReadError) },
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut data_buf = ByteBuffer::from_vec(decompress_zlib(&data)?);
|
||||||
|
|
||||||
|
let packet_id = match data_buf.read_u8_varint() {
|
||||||
|
Ok(i) => i,
|
||||||
|
Err(_) => { return Err(ProtocolError::VarIntError) },
|
||||||
|
};
|
||||||
|
let mut data: Vec<u8> = vec![0; data_length - 1];
|
||||||
|
match data_buf.read_exact(&mut data) {
|
||||||
|
Ok(i) => i,
|
||||||
|
Err(_) => { return Err(ProtocolError::ReadError) },
|
||||||
|
};
|
||||||
|
|
||||||
|
Ok(Packet::from_bytes(packet_id, &data))
|
||||||
|
} else {
|
||||||
|
let packet_id = self.read_u8_varint()?;
|
||||||
|
let mut data: Vec<u8> = vec![0; data_length - 1];
|
||||||
|
match self.stream.read_exact(&mut data) {
|
||||||
|
Ok(i) => i,
|
||||||
|
Err(_) => { return Err(ProtocolError::ReadError) },
|
||||||
|
};
|
||||||
|
|
||||||
|
Ok(Packet::from_bytes(packet_id, &data))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn write_packet(&mut self, packet: &Packet) -> Result<(), ProtocolError> {
|
||||||
|
let mut buf = ByteBuffer::new();
|
||||||
|
|
||||||
|
if !self.compress {
|
||||||
|
match buf.write_usize_varint(packet.buffer.len() + 1) {
|
||||||
|
Ok(_) => {},
|
||||||
|
Err(_) => { return Err(ProtocolError::WriteError) },
|
||||||
|
};
|
||||||
|
match buf.write_u8_varint(packet.id) {
|
||||||
|
Ok(_) => {},
|
||||||
|
Err(_) => { return Err(ProtocolError::WriteError) },
|
||||||
|
};
|
||||||
|
match buf.write_all(packet.buffer.as_bytes()) {
|
||||||
|
Ok(_) => {},
|
||||||
|
Err(_) => { return Err(ProtocolError::WriteError) },
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
let mut pack = ByteBuffer::new();
|
||||||
|
|
||||||
|
if packet.buffer.len() < self.compress_threashold {
|
||||||
|
pack.write_usize_varint(0).unwrap();
|
||||||
|
pack.write_u8_varint(packet.id).unwrap();
|
||||||
|
pack.write_all(packet.buffer.as_bytes()).unwrap();
|
||||||
|
} else {
|
||||||
|
let mut data = ByteBuffer::new();
|
||||||
|
|
||||||
|
data.write_u8_varint(packet.id).unwrap();
|
||||||
|
data.write_all(packet.buffer.as_bytes()).unwrap();
|
||||||
|
|
||||||
|
let data = compress_zlib(data.as_bytes())?;
|
||||||
|
|
||||||
|
pack.write_usize_varint(packet.buffer.len() + 1).unwrap();
|
||||||
|
pack.write_all(&data).unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
buf.write_usize_varint(pack.len()).unwrap();
|
||||||
|
buf.write_all(pack.as_bytes()).unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
self.stream.write_all(buf.as_bytes()).unwrap();
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn close(&mut self) {
|
||||||
|
self.stream.shutdown(std::net::Shutdown::Both).unwrap();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl VarintWriter for Connection {
|
||||||
|
type Error = ProtocolError;
|
||||||
|
|
||||||
|
fn write(&mut self, byte: u8) -> Result<(), Self::Error> {
|
||||||
|
match Write::write(&mut self.stream, &[byte]) {
|
||||||
|
Ok(_) => Ok(()),
|
||||||
|
Err(_) => Err(ProtocolError::WriteError),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl VarintReader for Connection {
|
||||||
|
type Error = ProtocolError;
|
||||||
|
|
||||||
|
fn read(&mut self) -> Result<u8, Self::Error> {
|
||||||
|
let mut buf = vec![0; 1];
|
||||||
|
|
||||||
|
match Read::read(&mut self.stream, &mut buf) {
|
||||||
|
Ok(i) => {
|
||||||
|
if i == 1 {
|
||||||
|
Ok(buf[0])
|
||||||
|
} else {
|
||||||
|
Err(ProtocolError::ReadError)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
Err(_) => Err(ProtocolError::ReadError),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn compress_zlib(bytes: &[u8]) -> Result<Vec<u8>, ProtocolError> {
|
||||||
|
let mut compresser = Compress::new(Compression::best(), true);
|
||||||
|
let mut output: Vec<u8> = Vec::new();
|
||||||
|
match compresser.compress_vec(&bytes, &mut output, FlushCompress::Finish) {
|
||||||
|
Ok(i) => {
|
||||||
|
match i {
|
||||||
|
Status::Ok => Ok(output),
|
||||||
|
_ => Err(ProtocolError::ZlibError)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
Err(_) => Err(ProtocolError::ZlibError)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn decompress_zlib(bytes: &[u8]) -> Result<Vec<u8>, ProtocolError> {
|
||||||
|
let mut compresser = Decompress::new(true);
|
||||||
|
let mut output: Vec<u8> = Vec::new();
|
||||||
|
match compresser.decompress_vec(&bytes, &mut output, FlushDecompress::Finish) {
|
||||||
|
Ok(i) => {
|
||||||
|
match i {
|
||||||
|
Status::Ok => Ok(output),
|
||||||
|
_ => Err(ProtocolError::ZlibError)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
Err(_) => Err(ProtocolError::ZlibError)
|
||||||
|
}
|
||||||
|
}
|
37
src/main.rs
Normal file
37
src/main.rs
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
use rust_mc_proto::{Connection, Packet, ProtocolError};
|
||||||
|
use varint_rs::{VarintReader, VarintWriter};
|
||||||
|
use std::io::Write;
|
||||||
|
|
||||||
|
fn send_handshake(conn: &mut Connection,
|
||||||
|
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)?;
|
||||||
|
packet.write_string(server_address.to_string())?;
|
||||||
|
packet.write_unsigned_short(server_port)?;
|
||||||
|
packet.write_u8_varint(next_state)?;
|
||||||
|
|
||||||
|
packet.buffer.set_rpos(0);
|
||||||
|
packet.buffer.set_wpos(0);
|
||||||
|
|
||||||
|
conn.write_packet(&packet)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn send_status_request(conn: &mut Connection) -> Result<(), ProtocolError> {
|
||||||
|
conn.write_packet(&Packet::empty(0x00))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn read_status_response(conn: &mut Connection) -> Result<String, ProtocolError> {
|
||||||
|
let mut packet = conn.read_packet()?;
|
||||||
|
packet.read_string()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let mut conn = Connection::new("localhost:25565");
|
||||||
|
|
||||||
|
send_handshake(&mut conn, 765, "localhost", 25565, 1).unwrap();
|
||||||
|
send_status_request(&mut conn).unwrap();
|
||||||
|
println!("{}", read_status_response(&mut conn).unwrap());
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user