commit 419513d2e7b5118aa1c75fc87079e9dfc670dd9a Author: Snowiiii Date: Sun Jul 28 23:07:26 2024 +0200 initial commit diff --git a/.gitignore b/.gitignore new file mode 100644 index 000000000..ea8c4bf7f --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +/target diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 000000000..403f186f3 --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,389 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "byteorder" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" + +[[package]] +name = "colored" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cbf2150cce219b664a8a70df7a1f933836724b503f8a413af9365b4dcc4d90b8" +dependencies = [ + "lazy_static", + "windows-sys 0.48.0", +] + +[[package]] +name = "crossbeam-channel" +version = "0.5.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "33480d6946193aa8033910124896ca395333cae7e2d1113d1fef6c3272217df2" +dependencies = [ + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-utils" +version = "0.8.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "248e3bacc7dc6baa3b21e405ee045c3047101a49145e7e9eca583ab4c2ca5345" + +[[package]] +name = "deranged" +version = "0.3.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b42b6fa04a440b495c8b04d0e71b707c585f83cb9cb28cf8cd0d976c315e31b4" +dependencies = [ + "powerfmt", +] + +[[package]] +name = "hermit-abi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024" + +[[package]] +name = "itoa" +version = "1.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b" + +[[package]] +name = "lazy_static" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" + +[[package]] +name = "libc" +version = "0.2.155" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97b3888a4aecf77e811145cadf6eef5901f4782c53886191b2f693f24761847c" + +[[package]] +name = "log" +version = "0.4.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90ed8c1e510134f979dbc4f070f87d4313098b704861a105fe34231c70a3901c" + +[[package]] +name = "mio" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4569e456d394deccd22ce1c1913e6ea0e54519f577285001215d33557431afe4" +dependencies = [ + "hermit-abi", + "libc", + "log", + "wasi", + "windows-sys 0.52.0", +] + +[[package]] +name = "num-conv" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9" + +[[package]] +name = "num_threads" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c7398b9c8b70908f6371f47ed36737907c87c52af34c268fed0bf0ceb92ead9" +dependencies = [ + "libc", +] + +[[package]] +name = "powerfmt" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" + +[[package]] +name = "proc-macro2" +version = "1.0.86" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e719e8df665df0d1c8fbfd238015744736151d4445ec0836b8e628aae103b77" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "pumpkin" +version = "0.1.0" +dependencies = [ + "byteorder", + "crossbeam-channel", + "log", + "mio", + "serde", + "serde_json", + "simple_logger", + "uuid", +] + +[[package]] +name = "quote" +version = "1.0.36" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fa76aaf39101c457836aec0ce2316dbdc3ab723cdda1c6bd4e6ad4208acaca7" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "ryu" +version = "1.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f" + +[[package]] +name = "serde" +version = "1.0.204" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bc76f558e0cbb2a839d37354c575f1dc3fdc6546b5be373ba43d95f231bf7c12" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.204" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e0cd7e117be63d3c3678776753929474f3b04a43a080c744d6b0ae2a8c28e222" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "serde_json" +version = "1.0.120" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4e0d21c9a8cae1235ad58a00c11cb40d4b1e5c784f1ef2c537876ed6ffd8b7c5" +dependencies = [ + "itoa", + "ryu", + "serde", +] + +[[package]] +name = "simple_logger" +version = "5.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e8c5dfa5e08767553704aa0ffd9d9794d527103c736aba9854773851fd7497eb" +dependencies = [ + "colored", + "log", + "time", + "windows-sys 0.48.0", +] + +[[package]] +name = "syn" +version = "2.0.72" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc4b9b9bf2add8093d3f2c0204471e951b2285580335de42f9d2534f3ae7a8af" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "time" +version = "0.3.36" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5dfd88e563464686c916c7e46e623e520ddc6d79fa6641390f2e3fa86e83e885" +dependencies = [ + "deranged", + "itoa", + "libc", + "num-conv", + "num_threads", + "powerfmt", + "serde", + "time-core", + "time-macros", +] + +[[package]] +name = "time-core" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef927ca75afb808a4d64dd374f00a2adf8d0fcff8e7b184af886c3c87ec4a3f3" + +[[package]] +name = "time-macros" +version = "0.2.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f252a68540fde3a3877aeea552b832b40ab9a69e318efd078774a01ddee1ccf" +dependencies = [ + "num-conv", + "time-core", +] + +[[package]] +name = "unicode-ident" +version = "1.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" + +[[package]] +name = "uuid" +version = "1.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "81dfa00651efa65069b0b6b651f4aaa31ba9e3c3ce0137aaad053604ee7e0314" + +[[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.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" +dependencies = [ + "windows-targets 0.48.5", +] + +[[package]] +name = "windows-sys" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" +dependencies = [ + "windows-targets 0.52.5", +] + +[[package]] +name = "windows-targets" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" +dependencies = [ + "windows_aarch64_gnullvm 0.48.5", + "windows_aarch64_msvc 0.48.5", + "windows_i686_gnu 0.48.5", + "windows_i686_msvc 0.48.5", + "windows_x86_64_gnu 0.48.5", + "windows_x86_64_gnullvm 0.48.5", + "windows_x86_64_msvc 0.48.5", +] + +[[package]] +name = "windows-targets" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6f0713a46559409d202e70e28227288446bf7841d3211583a4b53e3f6d96e7eb" +dependencies = [ + "windows_aarch64_gnullvm 0.52.5", + "windows_aarch64_msvc 0.52.5", + "windows_i686_gnu 0.52.5", + "windows_i686_gnullvm", + "windows_i686_msvc 0.52.5", + "windows_x86_64_gnu 0.52.5", + "windows_x86_64_gnullvm 0.52.5", + "windows_x86_64_msvc 0.52.5", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7088eed71e8b8dda258ecc8bac5fb1153c5cffaf2578fc8ff5d61e23578d3263" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9985fd1504e250c615ca5f281c3f7a6da76213ebd5ccc9561496568a2752afb6" + +[[package]] +name = "windows_i686_gnu" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" + +[[package]] +name = "windows_i686_gnu" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "88ba073cf16d5372720ec942a8ccbf61626074c6d4dd2e745299726ce8b89670" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87f4261229030a858f36b459e748ae97545d6f1ec60e5e0d6a3d32e0dc232ee9" + +[[package]] +name = "windows_i686_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" + +[[package]] +name = "windows_i686_msvc" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db3c2bf3d13d5b658be73463284eaf12830ac9a26a90c717b7f771dfe97487bf" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4e4246f76bdeff09eb48875a0fd3e2af6aada79d409d33011886d3e1581517d9" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "852298e482cd67c356ddd9570386e2862b5673c85bd5f88df9ab6802b334c596" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bec47e5bfd1bff0eeaf6d8b485cc1074891a197ab4225d504cb7a1ab88b02bf0" diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 000000000..0897a4616 --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,12 @@ +[workspace] +resolver = "2" +members = [ + "pumpkin/"] + +[workspace.package] +version = "0.1.0" +edition = "2021" + +[profile.release] +lto = true + diff --git a/pumpkin/Cargo.toml b/pumpkin/Cargo.toml new file mode 100644 index 000000000..5c6caf2fc --- /dev/null +++ b/pumpkin/Cargo.toml @@ -0,0 +1,18 @@ +[package] +name = "pumpkin" +version = "0.1.0" +edition = "2021" + +[dependencies] + +serde = { version = "1.0", features = ["derive"] } +serde_json = "1.0" + +simple_logger = "5.0.0" +log = "0.4" +byteorder = "1" +mio = { version = "1.0.1", features = ["os-poll", "net"]} +crossbeam-channel = "0.5.13" +uuid = "1.10" + + diff --git a/pumpkin/src/client/client_packet.rs b/pumpkin/src/client/client_packet.rs new file mode 100644 index 000000000..98ff59aa8 --- /dev/null +++ b/pumpkin/src/client/client_packet.rs @@ -0,0 +1,46 @@ +use crate::protocol::{ + client::status::{CPingResponse, CStatusResponse}, + server::{ + handshake::SHandShake, + status::{SPingRequest, SStatusRequest}, + }, + StatusResponse, Version, +}; + +use super::Client; + +pub trait ClientPacketProcessor { + // Handshake + fn handle_handshake(&mut self, handshake: SHandShake); + // Status + fn handle_status_request(&mut self, status_request: SStatusRequest); + fn handle_ping_request(&mut self, ping_request: SPingRequest); +} + +impl ClientPacketProcessor for Client { + fn handle_handshake(&mut self, handshake: SHandShake) { + // TODO set protocol version and check protocol version + self.connection_state = handshake.next_state; + dbg!("handshake"); + } + + fn handle_status_request(&mut self, _status_request: SStatusRequest) { + dbg!("sending status"); + let response = StatusResponse { + version: Version { + version: "1.21".into(), + protocol: 767, + }, + description: "Pumpkin Server".into(), + }; + self.send_packet(CStatusResponse::new( + serde_json::to_string(&response).unwrap(), + )) + } + + fn handle_ping_request(&mut self, ping_request: SPingRequest) { + dbg!("ping"); + self.send_packet(CPingResponse::new(ping_request.payload)); + self.close(); + } +} diff --git a/pumpkin/src/client/mod.rs b/pumpkin/src/client/mod.rs new file mode 100644 index 000000000..608b6aa8e --- /dev/null +++ b/pumpkin/src/client/mod.rs @@ -0,0 +1,168 @@ +use std::{ + collections::VecDeque, + io::{self, Write}, +}; + +use crate::protocol::{ + server::{ + handshake::SHandShake, + status::{SPingRequest, SStatusRequest}, + }, + ClientPacket, RawPacket, +}; + +use crate::protocol::{bytebuf::buffer::ByteBuffer, ConnectionState}; +use mio::{event::Event, net::TcpStream, Registry}; +use std::io::Read; + +mod client_packet; +pub mod player; + +use client_packet::ClientPacketProcessor; + +pub struct Client { + pub connection_state: ConnectionState, + pub closed: bool, + pub connection: TcpStream, + // Packets coming from the client -> server + client_packets_queue: VecDeque, +} + +impl Client { + pub fn new(connection: TcpStream) -> Self { + Self { + connection_state: ConnectionState::HandShake, + connection, + closed: false, + client_packets_queue: VecDeque::new(), + } + } + + pub fn add_packet(&mut self, packet: RawPacket) { + self.client_packets_queue.push_back(packet); + } + + pub fn send_packet(&mut self, packet: P) { + dbg!("WRITING"); + let mut packet_buf = ByteBuffer::new(); + packet.write(&mut packet_buf); + // Creating empty packet's buffer + let mut packet = ByteBuffer::new(); + // Creating length's bytes buffer and fill it as VarInt + let mut len_bytes = ByteBuffer::new(); + len_bytes.write_var_int(P::PACKET_ID); + // Writing full packet's length(content + length's bytes) + packet.write_var_int((packet_buf.len() + len_bytes.len()) as i32); + // Writing length bytes + packet.write_all(len_bytes.as_bytes()).unwrap(); + // Drop(Free) length bytes buffer + drop(len_bytes); + // Writing some packet's content + packet.write_all(packet_buf.as_bytes()).unwrap(); + + self.connection.write_all(packet.as_bytes()).unwrap(); + self.connection.flush().unwrap(); // todo do not flush every time + } + + pub fn procress_packets(&mut self) { + let mut i = 0; + while i < self.client_packets_queue.len() { + let mut packet = self.client_packets_queue.remove(i).unwrap(); + self.handle_packet(&mut packet); + i += 1; + } + } + + pub fn handle_packet(&mut self, packet: &mut RawPacket) { + dbg!("Handling packet"); + let bytebuf = &mut packet.bytebuf; + match self.connection_state { + crate::protocol::ConnectionState::HandShake => match packet.id { + SHandShake::PACKET_ID => self.handle_handshake(SHandShake::read(bytebuf)), + _ => log::error!( + "Failed to handle packet id {} while in Handshake state", + packet.id + ), + }, + crate::protocol::ConnectionState::Status => match packet.id { + SStatusRequest::PACKET_ID => { + self.handle_status_request(SStatusRequest::read(bytebuf)) + } + SPingRequest::PACKET_ID => self.handle_ping_request(SPingRequest::read(bytebuf)), + _ => log::error!( + "Failed to handle packet id {} while in Status state", + packet.id + ), + }, + _ => log::error!("Invalid Connection state {:?}", self.connection_state), + } + } + + /// Returns `true` if the connection is done. + pub fn handle_connection_event( + &mut self, + _registry: &Registry, + event: &Event, + ) -> io::Result<()> { + if event.is_readable() { + let mut received_data = vec![0; 4096]; + let mut bytes_read = 0; + // We can (maybe) read from the connection. + loop { + match self.connection.read(&mut received_data[bytes_read..]) { + Ok(0) => { + // Reading 0 bytes means the other side has closed the + // connection or is done writing, then so are we. + self.closed = true; + break; + } + Ok(n) => { + bytes_read += n; + if bytes_read == received_data.len() { + received_data.resize(received_data.len() + 1024, 0); + } + } + // Would block "errors" are the OS's way of saying that the + // connection is not actually ready to perform this I/O operation. + Err(ref err) if would_block(err) => break, + Err(ref err) if interrupted(err) => continue, + // Other errors we'll consider fatal. + Err(err) => return Err(err), + } + } + + if bytes_read != 0 { + let received_data = &received_data[..bytes_read]; + let mut bytebuf = ByteBuffer::from_bytes(received_data); + let packet = RawPacket { + len: bytebuf.read_var_int().unwrap(), + id: bytebuf.read_var_int().unwrap(), + bytebuf, + }; + if packet.len + 1 != received_data.len() as i32 { + log::error!( + "Packet length does not match Data length: {} {}", + packet.len, + received_data.len() + ); + } + dbg!(&packet); + self.add_packet(packet); + self.procress_packets(); + } + } + Ok(()) + } + + pub fn close(&mut self) { + self.closed = true; + } +} + +fn would_block(err: &io::Error) -> bool { + err.kind() == io::ErrorKind::WouldBlock +} + +pub fn interrupted(err: &io::Error) -> bool { + err.kind() == io::ErrorKind::Interrupted +} diff --git a/pumpkin/src/client/player.rs b/pumpkin/src/client/player.rs new file mode 100644 index 000000000..55eea144c --- /dev/null +++ b/pumpkin/src/client/player.rs @@ -0,0 +1,12 @@ +use crate::protocol::RawPacket; + +use super::Client; + +pub struct Player { + // All networking stuff + pub client: Client, +} + +impl Player { + pub fn handle_packet(&mut self, _packet: &mut RawPacket) {} +} diff --git a/pumpkin/src/client/player_packet.rs b/pumpkin/src/client/player_packet.rs new file mode 100644 index 000000000..e69de29bb diff --git a/pumpkin/src/main.rs b/pumpkin/src/main.rs new file mode 100644 index 000000000..9e03653fb --- /dev/null +++ b/pumpkin/src/main.rs @@ -0,0 +1,109 @@ +use mio::net::TcpListener; +use mio::{Events, Interest, Poll, Token}; +use std::collections::HashMap; +use std::io::{self}; + +// Setup some tokens to allow us to identify which event is for which socket. +const SERVER: Token = Token(0); + +pub mod client; +pub mod protocol; + +#[cfg(not(target_os = "wasi"))] +fn main() -> io::Result<()> { + use client::{interrupted, Client}; + + simple_logger::SimpleLogger::new().init().unwrap(); + + // Create a poll instance. + let mut poll = Poll::new()?; + // Create storage for events. + let mut events = Events::with_capacity(128); + + // Setup the TCP server socket. + let addr = "127.0.0.1:25565".parse().unwrap(); + let mut server = TcpListener::bind(addr)?; + + // Register the server with poll we can receive events for it. + poll.registry() + .register(&mut server, SERVER, Interest::READABLE)?; + + // Map of `Token` -> `TcpStream`. + let mut connections = HashMap::new(); + // Unique token for each incoming connection. + let mut unique_token = Token(SERVER.0 + 1); + + log::info!("You can connect to the server using `nc`:"); + log::info!(" $ nc 127.0.0.1 9000"); + log::info!("You'll see our welcome message and anything you type will be printed here."); + + loop { + if let Err(err) = poll.poll(&mut events, None) { + if interrupted(&err) { + continue; + } + return Err(err); + } + + for event in events.iter() { + match event.token() { + SERVER => loop { + // Received an event for the TCP server socket, which + // indicates we can accept an connection. + let (mut connection, address) = match server.accept() { + Ok((connection, address)) => (connection, address), + Err(e) if e.kind() == io::ErrorKind::WouldBlock => { + // If we get a `WouldBlock` error we know our + // listener has no more incoming connections queued, + // so we can return to polling and wait for some + // more. + break; + } + Err(e) => { + // If it was any other kind of error, something went + // wrong and we terminate with an error. + return Err(e); + } + }; + + log::info!("Accepted connection from: {}", address); + + let token = next(&mut unique_token); + poll.registry().register( + &mut connection, + token, + Interest::READABLE.add(Interest::WRITABLE), + )?; + + connections.insert(token, Client::new(connection)); + }, + token => { + // Maybe received an event for a TCP connection. + let done = if let Some(client) = connections.get_mut(&token) { + client.handle_connection_event(poll.registry(), event)?; + client.closed + } else { + // Sporadic events happen, we can safely ignore them. + false + }; + if done { + if let Some(mut client) = connections.remove(&token) { + poll.registry().deregister(&mut client.connection)?; + } + } + } + } + } + } +} + +fn next(current: &mut Token) -> Token { + let next = current.0; + current.0 += 1; + Token(next) +} + +#[cfg(target_os = "wasi")] +fn main() { + panic!("can't bind to an address with wasi") +} diff --git a/pumpkin/src/network/connection.rs b/pumpkin/src/network/connection.rs new file mode 100644 index 000000000..c6ace2882 --- /dev/null +++ b/pumpkin/src/network/connection.rs @@ -0,0 +1,168 @@ +use std::{ + io::{self, Read}, + string::FromUtf8Error, + sync::Arc, +}; + +use crossbeam_channel::{Receiver, Sender}; +use mio::{net::TcpStream, Token, Waker}; + +use crate::player::JoinInfo; + +use super::{ + protocol::{clientbound, read::MessageReader, serverbound}, + ProtocolVersion, +}; + +pub struct Connection { + stream: TcpStream, + ver: Option, + + tx: Sender, + rx: Receiver, + + token: Token, + + incoming: Vec, + outgoing: Vec, + garbage: Vec, +} + +#[derive(Debug, Clone)] +pub struct ConnSender { + tx: Sender, + wake: Sender, + waker: Arc, + tok: Token, +} + +pub struct NewConn { + pub sender: ConnSender, + pub info: JoinInfo, +} + +#[derive(Debug)] +enum ParseError { + InvalidType(i32), + CannotHandleOutput, + InvalidLength, + NotLoggedIn, + AlreadyLoggedIn, + InvalidPassword, + IO(io::Error), + InvalidMessage(FromUtf8Error), +} + +impl Connection { + fn new(stream: TcpStream, token: Token) -> Self { + // For a 10 chunk render distance, we need to send 441 packets at once. So a + // limit of 512 means we don't block very much. + let (tx, rx) = crossbeam_channel::bounded(512); + Self { + stream, + rx, + tx, + token, + incoming: Vec::with_capacity(1024), + outgoing: Vec::with_capacity(1024), + garbage: vec![0; 256 * 1024], + } + } + pub fn send(&self, p: clientbound::Packet) { + if let Ok(()) = self.tx.send(p) {} + } + + pub fn read(&mut self) -> io::Result<(bool, Option, Vec)> { + let mut out = vec![]; + loop { + let n = match self.stream.read(&mut self.garbage) { + Ok(0) => return Ok((true, None, out)), + Ok(n) => n, + Err(e) if e.kind() == io::ErrorKind::WouldBlock => return Ok((false, None, out)), + Err(e) => return Err(e), + }; + self.incoming.extend_from_slice(&self.garbage[..n]); + let (new_conn, packets) = self.read_incoming()?; + if new_conn.is_some() { + return Ok((false, new_conn, packets)); + } + out.extend(packets); + } + } + + fn read_incoming(&mut self) -> io::Result<(Option, Vec)> { + let mut out = vec![]; + while !self.incoming.is_empty() { + let mut m = MessageReader::new(&self.incoming); + match m.read_u32() { + Ok(len) => { + let len = len as usize; + if len + m.index() <= self.incoming.len() { + // Remove the length varint at the start + let idx = m.index(); + self.incoming.drain(0..idx); + // We already handshaked + if self.ver.is_some() { + let mut m = MessageReader::new(&self.incoming[..len]); + let p = serverbound::Packet::read(&mut m).map_err(|err| { + io::Error::new( + io::ErrorKind::InvalidData, + format!("while reading packet got err: {err}"), + ) + })?; + let n = m.index(); + self.incoming.drain(0..n); + if n != len { + return Err(io::Error::new( + io::ErrorKind::InvalidData, + format!("packet did not parse enough bytes (expected {len}, only parsed {n})"), + )); + } + out.push(p); + } else { + // This is the first packet, so it must be a login packet. + let mut m = MessageReader::new(&self.incoming[..len]); + let info: JoinInfo = m.read().map_err(|e| { + io::Error::new( + io::ErrorKind::InvalidData, + format!("error reading handshake: {e}"), + ) + })?; + let n = m.index(); + self.incoming.drain(0..n); + if n != len { + return Err(io::Error::new( + io::ErrorKind::InvalidData, + format!("handshake did not parse enough bytes (expected {len}, only parsed {n})"), + )); + } + self.ver = Some(ProtocolVersion::from(info.ver as i32)); + // We rely on the caller to set the player using this value. + return Ok(( + Some(NewConn { + sender: self.sender(), + info, + }), + out, + )); + } + } else { + break; + } + } + // If this is an EOF, then we have a partial varint, so we are done reading. + Err(e) => { + if matches!(e, ReadError::Invalid(InvalidReadError::EOF)) { + return Ok((None, out)); + } else { + return Err(io::Error::new( + io::ErrorKind::InvalidData, + format!("error reading packet id: {e}"), + )); + } + } + } + } + Ok((None, out)) + } +} diff --git a/pumpkin/src/protocol/bytebuf/buffer.rs b/pumpkin/src/protocol/bytebuf/buffer.rs new file mode 100644 index 000000000..e7f381f72 --- /dev/null +++ b/pumpkin/src/protocol/bytebuf/buffer.rs @@ -0,0 +1,756 @@ +use crate::protocol::{VarInt, VarLong}; + +use super::{Endian, CONTINUE_BIT, SEGMENT_BITS}; +use byteorder::{BigEndian, ByteOrder, LittleEndian}; +use std::{ + fmt::Debug, + io::{Error, ErrorKind, Read, Result, Write}, +}; + +/// A byte buffer object specifically turned to easily read and write binary values +#[derive(Clone, PartialEq, Eq, Hash)] +pub struct ByteBuffer { + data: Vec, + wpos: usize, + rpos: usize, + wbit: usize, + rbit: usize, + endian: Endian, +} + +impl From<&[u8]> for ByteBuffer { + fn from(val: &[u8]) -> Self { + ByteBuffer::from_bytes(val) + } +} + +impl From> for ByteBuffer { + fn from(val: Vec) -> Self { + ByteBuffer::from_vec(val) + } +} + +impl From for Vec { + fn from(val: ByteBuffer) -> Self { + val.into_vec() + } +} + +impl Default for ByteBuffer { + fn default() -> Self { + Self::new() + } +} + +impl Read for ByteBuffer { + fn read(&mut self, buf: &mut [u8]) -> Result { + self.flush_bits(); + let read_len = std::cmp::min(self.data.len() - self.rpos, buf.len()); + let range = self.rpos..self.rpos + read_len; + for (i, val) in self.data[range].iter().enumerate() { + buf[i] = *val; + } + self.rpos += read_len; + Ok(read_len) + } +} + +impl Write for ByteBuffer { + fn write(&mut self, buf: &[u8]) -> Result { + self.write_bytes(buf); + Ok(buf.len()) + } + + fn flush(&mut self) -> Result<()> { + Ok(()) + } +} + +impl Debug for ByteBuffer { + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + let rpos = if self.rbit > 0 { + self.rpos + 1 + } else { + self.rpos + }; + + let read_len = self.data.len() - rpos; + let mut remaining_data = vec![0; read_len]; + let range = rpos..rpos + read_len; + for (i, val) in self.data[range].iter().enumerate() { + remaining_data[i] = *val; + } + + write!( + f, + "ByteBuffer {{ remaining_data: {:?}, total_data: {:?}, wpos: {:?}, rpos: {:?}, endian: {:?} }}", + remaining_data, self.data, self.wpos, self.rpos, self.endian + ) + } +} + +macro_rules! read_number { + ($self:ident, $name:ident, $offset:expr) => {{ + $self.flush_bits(); + if $self.rpos + $offset > $self.data.len() { + return Err(Error::new( + ErrorKind::UnexpectedEof, + "could not read enough bits from buffer", + )); + } + let range = $self.rpos..$self.rpos + $offset; + $self.rpos += $offset; + + Ok(match $self.endian { + Endian::BigEndian => BigEndian::$name(&$self.data[range]), + Endian::LittleEndian => LittleEndian::$name(&$self.data[range]), + }) + }}; +} + +impl ByteBuffer { + /// Construct a new, empty, ByteBuffer + pub fn new() -> ByteBuffer { + ByteBuffer { + data: vec![], + wpos: 0, + rpos: 0, + rbit: 0, + wbit: 0, + endian: Endian::BigEndian, + } + } + + /// Construct a new ByteBuffer filled with the data array. + pub fn from_bytes(bytes: &[u8]) -> ByteBuffer { + let mut buffer = ByteBuffer::new(); + buffer.write_bytes(bytes); + buffer + } + + /// Constructs a new ByteBuffer from an existing vector. This + /// function takes ownership of the vector + pub fn from_vec(vec: Vec) -> ByteBuffer { + let len = vec.len(); + ByteBuffer { + data: vec, + wpos: len, + rpos: 0, + rbit: 0, + wbit: 0, + endian: Endian::BigEndian, + } + } + + /// Return the buffer size + pub fn len(&self) -> usize { + self.data.len() + } + + pub fn is_empty(&self) -> bool { + self.data.is_empty() + } + + /// Clear the buffer and reinitialize the reading and writing cursors + pub fn clear(&mut self) { + self.data.clear(); + self.reset_cursors(); + self.reset_bits_cursors(); + } + + /// Reinitialize the reading and writing cursor + pub fn reset_cursors(&mut self) { + self.wpos = 0; + self.rpos = 0; + } + + /// Reinitialize the bit reading and bit writing cursor + pub fn reset_bits_cursors(&mut self) { + self.rbit = 0; + self.wbit = 0; + } + + /// Change the buffer size to size. + /// + /// _Note_: You cannot shrink a buffer with this method + pub fn resize(&mut self, size: usize) { + let diff = size - self.data.len(); + if diff > 0 { + self.data.extend(std::iter::repeat(0).take(diff)) + } + } + + /// Set the byte order of the buffer + /// + /// _Note_: By default the buffer uses big endian order + pub fn set_endian(&mut self, endian: Endian) { + self.endian = endian; + } + + /// Returns the current byte order of the buffer + pub fn endian(&self) -> Endian { + self.endian + } + + // Write operations + + /// Append a byte array to the buffer. The buffer is automatically extended if needed + /// _Note_: This method resets the read and write cursor for bitwise reading. + /// + /// #Example + /// + /// ``` + /// # use bytebuffer::*; + /// let mut buffer = ByteBuffer::new(); + /// buffer.write_bytes(&vec![0x1, 0xFF, 0x45]); // buffer contains [0x1, 0xFF, 0x45] + /// ``` + pub fn write_bytes(&mut self, bytes: &[u8]) { + self.flush_bits(); + + let size = bytes.len() + self.wpos; + + if size > self.data.len() { + self.resize(size); + } + + for v in bytes { + self.data[self.wpos] = *v; + self.wpos += 1; + } + } + + /// Append a byte (8 bits value) to the buffer + /// _Note_: This method resets the read and write cursor for bitwise reading. + /// + /// #Example + /// + /// ``` + /// # use bytebuffer::*; + /// let mut buffer = ByteBuffer::new(); + /// buffer.write_u8(1) // buffer contains [0x1] + /// ``` + pub fn write_u8(&mut self, val: u8) { + self.write_bytes(&[val]); + } + + /// Same as `write_u8()` but for signed values + /// _Note_: This method resets the read and write cursor for bitwise reading. + pub fn write_i8(&mut self, val: i8) { + self.write_u8(val as u8); + } + + /// Append a word (16 bits value) to the buffer + /// _Note_: This method resets the read and write cursor for bitwise reading. + /// + /// #Example + /// + /// ``` + /// # use bytebuffer::*; + /// let mut buffer = ByteBuffer::new(); + /// buffer.write_u16(1) // buffer contains [0x00, 0x1] if little endian + /// ``` + pub fn write_u16(&mut self, val: u16) { + let mut buf = [0; 2]; + + match self.endian { + Endian::BigEndian => BigEndian::write_u16(&mut buf, val), + Endian::LittleEndian => LittleEndian::write_u16(&mut buf, val), + }; + + self.write_bytes(&buf); + } + + /// Same as `write_u16()` but for signed values + /// _Note_: This method resets the read and write cursor for bitwise reading. + pub fn write_i16(&mut self, val: i16) { + self.write_u16(val as u16); + } + + /// Append a double word (32 bits value) to the buffer + /// _Note_: This method resets the read and write cursor for bitwise reading. + /// + /// #Example + /// + /// ``` + /// # use bytebuffer::*; + /// let mut buffer = ByteBuffer::new(); + /// buffer.write_u32(1) // buffer contains [0x00, 0x00, 0x00, 0x1] if little endian + /// ``` + pub fn write_u32(&mut self, val: u32) { + let mut buf = [0; 4]; + + match self.endian { + Endian::BigEndian => BigEndian::write_u32(&mut buf, val), + Endian::LittleEndian => LittleEndian::write_u32(&mut buf, val), + }; + + self.write_bytes(&buf); + } + + /// Same as `write_u32()` but for signed values + /// _Note_: This method resets the read and write cursor for bitwise reading. + pub fn write_i32(&mut self, val: i32) { + self.write_u32(val as u32); + } + + pub fn write_var_int(&mut self, value: VarInt) { + let mut val = value as u32; + for _ in 0..5 { + let mut b: u8 = val as u8 & 0b01111111; + val >>= 7; + if val != 0 { + b |= 0b10000000; + } + self.write_u8(b); + if val == 0 { + break; + } + } + } + + /// Append a quaddruple word (64 bits value) to the buffer + /// _Note_: This method resets the read and write cursor for bitwise reading. + /// + /// #Example + /// + /// ``` + /// # use bytebuffer::*; + /// let mut buffer = ByteBuffer::new(); + /// buffer.write_u64(1) // buffer contains [0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1] if little endian + /// ``` + pub fn write_u64(&mut self, val: u64) { + let mut buf = [0; 8]; + match self.endian { + Endian::BigEndian => BigEndian::write_u64(&mut buf, val), + Endian::LittleEndian => LittleEndian::write_u64(&mut buf, val), + }; + + self.write_bytes(&buf); + } + + /// Same as `write_u64()` but for signed values + /// _Note_: This method resets the read and write cursor for bitwise reading. + pub fn write_i64(&mut self, val: i64) { + self.write_u64(val as u64); + } + + /// Append a 32 bits floating point number to the buffer. + /// _Note_: This method resets the read and write cursor for bitwise reading. + /// + /// #Example + /// + /// ``` + /// # use bytebuffer::*; + /// let mut buffer = ByteBuffer::new(); + /// buffer.write_f32(0.1) + /// ``` + pub fn write_f32(&mut self, val: f32) { + let mut buf = [0; 4]; + + match self.endian { + Endian::BigEndian => BigEndian::write_f32(&mut buf, val), + Endian::LittleEndian => LittleEndian::write_f32(&mut buf, val), + }; + + self.write_bytes(&buf); + } + + /// Append a 64 bits floating point number to the buffer. + /// _Note_: This method resets the read and write cursor for bitwise reading. + /// + /// #Example + /// + /// ``` + /// # use bytebuffer::*; + /// let mut buffer = ByteBuffer::new(); + /// buffer.write_f64(0.1) + /// ``` + pub fn write_f64(&mut self, val: f64) { + let mut buf = [0; 8]; + + match self.endian { + Endian::BigEndian => BigEndian::write_f64(&mut buf, val), + Endian::LittleEndian => LittleEndian::write_f64(&mut buf, val), + }; + self.write_bytes(&buf); + } + + /// Append a string to the buffer. + /// _Note_: This method resets the read and write cursor for bitwise reading. + /// + /// *Format* The format is `(u32)size + size * (u8)characters` + /// + /// #Example + /// + /// ``` + /// # use bytebuffer::*; + /// let mut buffer = ByteBuffer::new(); + /// buffer.write_string("Hello") + /// ``` + pub fn write_string(&mut self, val: &str) { + self.write_var_int(val.len() as VarInt); + self.write_bytes(val.as_bytes()); + } + + // Read operations + + /// Read a defined amount of raw bytes, or return an IO error if not enough bytes are + /// available. + /// _Note_: This method resets the read and write cursor for bitwise reading. + pub fn read_bytes(&mut self, size: usize) -> Result> { + self.flush_bits(); + if self.rpos + size > self.data.len() { + return Err(Error::new( + ErrorKind::UnexpectedEof, + "could not read enough bytes from buffer", + )); + } + let range = self.rpos..self.rpos + size; + let mut res = Vec::::new(); + res.write_all(&self.data[range])?; + self.rpos += size; + Ok(res) + } + + /// Read one byte, or return an IO error if not enough bytes are available. + /// _Note_: This method resets the read and write cursor for bitwise reading. + /// + /// #Example + /// + /// ``` + /// # use bytebuffer::*; + /// let mut buffer = ByteBuffer::from_bytes(&vec![0x1]); + /// let value = buffer.read_u8().unwrap(); //Value contains 1 + /// ``` + pub fn read_u8(&mut self) -> Result { + self.flush_bits(); + if self.rpos >= self.data.len() { + return Err(Error::new( + ErrorKind::UnexpectedEof, + "could not read enough bits from buffer", + )); + } + let pos = self.rpos; + self.rpos += 1; + Ok(self.data[pos]) + } + + /// Same as `read_u8()` but for signed values + pub fn read_i8(&mut self) -> Result { + Ok(self.read_u8()? as i8) + } + + /// Read a 2-bytes long value, or return an IO error if not enough bytes are available. + /// _Note_: This method resets the read and write cursor for bitwise reading. + /// + /// #Example + /// + /// ``` + /// # use bytebuffer::*; + /// let mut buffer = ByteBuffer::from_bytes(&vec![0x0, 0x1]); + /// let value = buffer.read_u16().unwrap(); //Value contains 1 + /// ``` + pub fn read_u16(&mut self) -> Result { + read_number!(self, read_u16, 2) + } + + /// Same as `read_u16()` but for signed values + /// _Note_: This method resets the read and write cursor for bitwise reading. + pub fn read_i16(&mut self) -> Result { + Ok(self.read_u16()? as i16) + } + + /// Read a four-bytes long value, or return an IO error if not enough bytes are available. + /// _Note_: This method resets the read and write cursor for bitwise reading. + /// + /// #Example + /// + /// ``` + /// # use bytebuffer::*; + /// let mut buffer = ByteBuffer::from_bytes(&vec![0x0, 0x0, 0x0, 0x1]); + /// let value = buffer.read_u32().unwrap(); // Value contains 1 + /// ``` + pub fn read_u32(&mut self) -> Result { + read_number!(self, read_u32, 4) + } + + /// Same as `read_u32()` but for signed values + /// _Note_: This method resets the read and write cursor for bitwise reading. + pub fn read_i32(&mut self) -> Result { + Ok(self.read_u32()? as i32) + } + + /// Read an eight bytes long value, or return an IO error if not enough bytes are available. + /// _Note_: This method resets the read and write cursor for bitwise reading. + /// + /// #Example + /// + /// ``` + /// # use bytebuffer::*; + /// let mut buffer = ByteBuffer::from_bytes(&vec![0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1]); + /// let value = buffer.read_u64().unwrap(); //Value contains 1 + /// ``` + pub fn read_u64(&mut self) -> Result { + read_number!(self, read_u64, 8) + } + + pub fn read_var_int(&mut self) -> Result { + let mut value: i32 = 0; + let mut position: i32 = 0; + + loop { + let read = self.read_u8()?; + + value |= ((read & SEGMENT_BITS) as i32) << position; + + if read & CONTINUE_BIT == 0 { + break; + } + + position += 7; + + if position >= 32 { + return Err(Error::new(ErrorKind::InvalidData, "VarInt is too big")); + } + } + + Ok(value) + } + + pub fn read_var_long(&mut self) -> Result { + let mut value: i64 = 0; + let mut position: i64 = 0; + + loop { + let read = self.read_u8()?; + + value |= ((read & SEGMENT_BITS) as i64) << position; + + if read & CONTINUE_BIT == 0 { + break; + } + + position += 7; + + if position >= 64 { + return Err(Error::new(ErrorKind::InvalidData, "VarLong is too big")); + } + } + + Ok(value) + } + + /// Same as `read_u64()` but for signed values + /// _Note_: This method resets the read and write cursor for bitwise reading. + pub fn read_i64(&mut self) -> Result { + Ok(self.read_u64()? as i64) + } + + /// Read a 32 bits floating point value, or return an IO error if not enough bytes are available. + /// _Note_: This method resets the read and write cursor for bitwise reading. + pub fn read_f32(&mut self) -> Result { + read_number!(self, read_f32, 4) + } + + /// Read a 64 bits floating point value, or return an IO error if not enough bytes are available. + /// _Note_: This method resets the read and write cursor for bitwise reading. + pub fn read_f64(&mut self) -> Result { + read_number!(self, read_f64, 8) + } + + /// Read a string. + /// + /// _Note_: First it reads a 32 bits value representing the size, then 'size' raw bytes + /// that must be encoded as UTF8. + /// _Note_: This method resets the read and write cursor for bitwise reading. + pub fn read_string(&mut self) -> Result { + let size = self.read_var_int()?; + match String::from_utf8(self.read_bytes(size as usize)?) { + Ok(string_result) => Ok(string_result), + Err(e) => Err(Error::new(ErrorKind::InvalidData, e)), + } + } + + // Other + + /// Dump the byte buffer to a string. + pub fn to_hex_dump(&self) -> String { + let mut str = String::new(); + for b in &self.data { + str = str + &format!("0x{:01$x} ", b, 2); + } + str.pop(); + str + } + + /// Return the position of the reading cursor + pub fn get_rpos(&self) -> usize { + self.rpos + } + + /// Set the reading cursor position. + /// _Note_: Sets the reading cursor to `min(newPosition, self.len())` to prevent overflow + pub fn set_rpos(&mut self, rpos: usize) { + self.rpos = std::cmp::min(rpos, self.data.len()); + } + + /// Return the writing cursor position + pub fn get_wpos(&self) -> usize { + self.wpos + } + + /// Set the writing cursor position. + /// _Note_: Sets the writing cursor to `min(newPosition, self.len())` to prevent overflow + pub fn set_wpos(&mut self, wpos: usize) { + self.wpos = std::cmp::min(wpos, self.data.len()); + } + + /// Return the raw byte buffer bytes. + pub fn as_bytes(&self) -> &[u8] { + &self.data + } + + /// Return the raw byte buffer as a Vec. + #[deprecated( + since = "2.1.0", + note = "use `as_bytes().to_vec()` or `into_vec()` instead" + )] + pub fn into_bytes(&self) -> Vec { + self.data.to_vec() + } + + /// Return the raw byte buffer as a Vec. + pub fn into_vec(self) -> Vec { + self.data + } + + //Bit manipulation functions + + /// Read 1 bit. Return true if the bit is set to 1, otherwhise, return false. + /// + /// _Note_: Bits are read from left to right + /// + /// #Example + /// + /// ``` + /// # use bytebuffer::*; + /// let mut buffer = ByteBuffer::from_bytes(&vec![128]); // 10000000b + /// let value1 = buffer.read_bit().unwrap(); //value1 contains true (eg: bit is 1) + /// let value2 = buffer.read_bit().unwrap(); //value2 contains false (eg: bit is 0) + /// ``` + pub fn read_bit(&mut self) -> Result { + if self.rpos >= self.data.len() { + return Err(Error::new( + ErrorKind::UnexpectedEof, + "could not read enough bits from buffer", + )); + } + let bit = self.data[self.rpos] & (1 << (7 - self.rbit)) != 0; + self.rbit += 1; + if self.rbit > 7 { + self.flush_rbits(); + } + Ok(bit) + } + + /// Read n bits. an return the corresponding value an u64. + /// + /// _Note_: We cannot read more than 64 bits + /// + /// _Note_: Bits are read from left to right + /// + /// #Example + /// + /// ``` + /// # use bytebuffer::*; + /// let mut buffer = ByteBuffer::from_bytes(&vec![128]); // 10000000b + /// let value = buffer.read_bits(3).unwrap(); // value contains 4 (eg: 100b) + /// ``` + pub fn read_bits(&mut self, n: u8) -> Result { + if n > 64 { + return Err(Error::new( + ErrorKind::InvalidInput, + "cannot read more than 64 bits", + )); + } + + if n == 0 { + Ok(0) + } else { + Ok((u64::from(self.read_bit()?) << (n - 1)) | self.read_bits(n - 1)?) + } + } + + /// Discard all the pending bits available for reading or writing and place the corresponding cursor to the next byte. + /// + /// _Note_: If no bits are currently read or written, this function does nothing. + /// + /// #Example + /// + /// ```text + /// 10010010 | 00000001 + /// ^ + /// 10010010 | 00000001 // read_bit called + /// ^ + /// 10010010 | 00000001 // flush_bit() called + /// ^ + /// ``` + pub fn flush_bits(&mut self) { + if self.rbit > 0 { + self.flush_rbits(); + } + if self.wbit > 0 { + self.flush_wbits(); + } + } + + fn flush_rbits(&mut self) { + self.rpos += 1; + self.rbit = 0 + } + + fn flush_wbits(&mut self) { + self.wpos += 1; + self.wbit = 0 + } + + /// Append 1 bit value to the buffer. + /// The bit is appended like this : + /// + /// ```text + /// ...| XXXXXXXX | 10000000 |.... + /// ``` + pub fn write_bit(&mut self, bit: bool) { + let size = self.wpos + 1; + if size > self.data.len() { + self.resize(size); + } + + if bit { + self.data[self.wpos] |= 1 << (7 - self.wbit); + } + + self.wbit += 1; + + if self.wbit > 7 { + self.wbit = 0; + self.wpos += 1; + } + } + + /// Write the given value as a sequence of n bits + /// + /// #Example + /// + /// ``` + /// # use bytebuffer::*; + /// let mut buffer = ByteBuffer::new(); + /// buffer.write_bits(4, 3); // append 100b + /// ``` + pub fn write_bits(&mut self, value: u64, n: u8) { + if n > 0 { + self.write_bit((value >> (n - 1)) & 1 != 0); + self.write_bits(value, n - 1); + } + } +} diff --git a/pumpkin/src/protocol/bytebuf/mod.rs b/pumpkin/src/protocol/bytebuf/mod.rs new file mode 100644 index 000000000..93f65a020 --- /dev/null +++ b/pumpkin/src/protocol/bytebuf/mod.rs @@ -0,0 +1,11 @@ +pub mod buffer; +pub mod reader; + +const SEGMENT_BITS: u8 = 0x7F; +const CONTINUE_BIT: u8 = 0x80; +/// An enum to represent the byte order of the ByteBuffer object +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] +pub enum Endian { + BigEndian, + LittleEndian, +} diff --git a/pumpkin/src/protocol/bytebuf/reader.rs b/pumpkin/src/protocol/bytebuf/reader.rs new file mode 100644 index 000000000..461df7a39 --- /dev/null +++ b/pumpkin/src/protocol/bytebuf/reader.rs @@ -0,0 +1,368 @@ +use super::Endian; +use byteorder::{BigEndian, ByteOrder, LittleEndian}; +use std::{ + fmt::Debug, + io::{Error, ErrorKind, Read, Result, Write}, +}; + +/// A byte buffer object specifically turned to easily read and write binary values +#[derive(Clone, PartialEq, Eq, Hash)] +pub struct ByteReader<'a> { + data: &'a [u8], + rpos: usize, + rbit: usize, + endian: Endian, +} + +impl<'a> From<&'a [u8]> for ByteReader<'a> { + fn from(val: &'a [u8]) -> Self { + ByteReader::from_bytes(val) + } +} + +impl<'a> Read for ByteReader<'a> { + fn read(&mut self, buf: &mut [u8]) -> Result { + self.flush_bits(); + let read_len = std::cmp::min(self.data.len() - self.rpos, buf.len()); + let range = self.rpos..self.rpos + read_len; + for (i, val) in self.data[range].iter().enumerate() { + buf[i] = *val; + } + self.rpos += read_len; + Ok(read_len) + } +} + +impl<'a> Debug for ByteReader<'a> { + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + let rpos = if self.rbit > 0 { + self.rpos + 1 + } else { + self.rpos + }; + + let read_len = self.data.len() - rpos; + let mut remaining_data = vec![0; read_len]; + let range = rpos..rpos + read_len; + for (i, val) in self.data[range].iter().enumerate() { + remaining_data[i] = *val; + } + + write!( + f, + "ByteReader {{ remaining_data: {:?}, total_data: {:?}, rpos: {:?}, endian: {:?} }}", + remaining_data, self.data, self.rpos, self.endian + ) + } +} + +macro_rules! read_number { + ($self:ident, $name:ident, $offset:expr) => {{ + $self.flush_bits(); + if $self.rpos + $offset > $self.data.len() { + return Err(Error::new( + ErrorKind::UnexpectedEof, + "could not read enough bits from buffer", + )); + } + let range = $self.rpos..$self.rpos + $offset; + $self.rpos += $offset; + + Ok(match $self.endian { + Endian::BigEndian => BigEndian::$name(&$self.data[range]), + Endian::LittleEndian => LittleEndian::$name(&$self.data[range]), + }) + }}; +} + +impl<'a> ByteReader<'a> { + /// Construct a new ByteReader filled with the data array. + pub fn from_bytes(bytes: &[u8]) -> ByteReader { + ByteReader { + data: bytes, + rpos: 0, + rbit: 0, + endian: Endian::BigEndian, + } + } + + /// Return the buffer size + pub fn len(&self) -> usize { + self.data.len() + } + + pub fn is_empty(&self) -> bool { + self.data.is_empty() + } + + /// Reinitialize the reading cursor + pub fn reset_cursors(&mut self) { + self.rpos = 0; + } + + /// Reinitialize the bit reading cursor + pub fn reset_bits_cursors(&mut self) { + self.rbit = 0; + } + + /// Set the byte order of the buffer + /// + /// _Note_: By default the buffer uses big endian order + pub fn set_endian(&mut self, endian: Endian) { + self.endian = endian; + } + + /// Returns the current byte order of the buffer + pub fn endian(&self) -> Endian { + self.endian + } + + // Read operations + + /// Read a defined amount of raw bytes, or return an IO error if not enough bytes are + /// available. + /// _Note_: This method resets the read and write cursor for bitwise reading. + pub fn read_bytes(&mut self, size: usize) -> Result> { + self.flush_bits(); + if self.rpos + size > self.data.len() { + return Err(Error::new( + ErrorKind::UnexpectedEof, + "could not read enough bytes from buffer", + )); + } + let range = self.rpos..self.rpos + size; + let mut res = Vec::::new(); + res.write_all(&self.data[range])?; + self.rpos += size; + Ok(res) + } + + /// Read one byte, or return an IO error if not enough bytes are available. + /// _Note_: This method resets the read and write cursor for bitwise reading. + /// + /// #Example + /// + /// ``` + /// # use bytebuffer::*; + /// let data = vec![0x1]; + /// let mut buffer = ByteReader::from_bytes(&data); + /// let value = buffer.read_u8().unwrap(); //Value contains 1 + /// ``` + pub fn read_u8(&mut self) -> Result { + self.flush_bits(); + if self.rpos >= self.data.len() { + return Err(Error::new( + ErrorKind::UnexpectedEof, + "could not read enough bits from buffer", + )); + } + let pos = self.rpos; + self.rpos += 1; + Ok(self.data[pos]) + } + + /// Same as `read_u8()` but for signed values + pub fn read_i8(&mut self) -> Result { + Ok(self.read_u8()? as i8) + } + + /// Read a 2-bytes long value, or return an IO error if not enough bytes are available. + /// _Note_: This method resets the read and write cursor for bitwise reading. + /// + /// #Example + /// + /// ``` + /// # use bytebuffer::*; + /// let data = vec![0x0, 0x1]; + /// let mut buffer = ByteReader::from_bytes(&data); + /// let value = buffer.read_u16().unwrap(); //Value contains 1 + /// ``` + pub fn read_u16(&mut self) -> Result { + read_number!(self, read_u16, 2) + } + + /// Same as `read_u16()` but for signed values + /// _Note_: This method resets the read and write cursor for bitwise reading. + pub fn read_i16(&mut self) -> Result { + Ok(self.read_u16()? as i16) + } + + /// Read a four-bytes long value, or return an IO error if not enough bytes are available. + /// _Note_: This method resets the read and write cursor for bitwise reading. + /// + /// #Example + /// + /// ``` + /// # use bytebuffer::*; + /// let data = vec![0x0, 0x0, 0x0, 0x1]; + /// let mut buffer = ByteReader::from_bytes(&data); + /// let value = buffer.read_u32().unwrap(); // Value contains 1 + /// ``` + pub fn read_u32(&mut self) -> Result { + read_number!(self, read_u32, 4) + } + + /// Same as `read_u32()` but for signed values + /// _Note_: This method resets the read and write cursor for bitwise reading. + pub fn read_i32(&mut self) -> Result { + Ok(self.read_u32()? as i32) + } + + /// Read an eight bytes long value, or return an IO error if not enough bytes are available. + /// _Note_: This method resets the read and write cursor for bitwise reading. + /// + /// #Example + /// + /// ``` + /// # use bytebuffer::*; + /// let data = vec![0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1]; + /// let mut buffer = ByteReader::from_bytes(&data); + /// let value = buffer.read_u64().unwrap(); //Value contains 1 + /// ``` + pub fn read_u64(&mut self) -> Result { + read_number!(self, read_u64, 8) + } + + /// Same as `read_u64()` but for signed values + /// _Note_: This method resets the read and write cursor for bitwise reading. + pub fn read_i64(&mut self) -> Result { + Ok(self.read_u64()? as i64) + } + + /// Read a 32 bits floating point value, or return an IO error if not enough bytes are available. + /// _Note_: This method resets the read and write cursor for bitwise reading. + pub fn read_f32(&mut self) -> Result { + read_number!(self, read_f32, 4) + } + + /// Read a 64 bits floating point value, or return an IO error if not enough bytes are available. + /// _Note_: This method resets the read and write cursor for bitwise reading. + pub fn read_f64(&mut self) -> Result { + read_number!(self, read_f64, 8) + } + + /// Read a string. + /// + /// _Note_: First it reads a 32 bits value representing the size, then 'size' raw bytes + /// that must be encoded as UTF8. + /// _Note_: This method resets the read and write cursor for bitwise reading. + pub fn read_string(&mut self) -> Result { + let size = self.read_u32()?; + match String::from_utf8(self.read_bytes(size as usize)?) { + Ok(string_result) => Ok(string_result), + Err(e) => Err(Error::new(ErrorKind::InvalidData, e)), + } + } + + // Other + + /// Dump the byte buffer to a string. + pub fn to_hex_dump(&self) -> String { + let mut str = String::new(); + for b in self.data { + str = str + &format!("0x{:01$x} ", b, 2); + } + str.pop(); + str + } + + /// Return the position of the reading cursor + pub fn get_rpos(&self) -> usize { + self.rpos + } + + /// Set the reading cursor position. + /// _Note_: Sets the reading cursor to `min(newPosition, self.len())` to prevent overflow + pub fn set_rpos(&mut self, rpos: usize) { + self.rpos = std::cmp::min(rpos, self.data.len()); + } + + /// Return the raw byte buffer bytes. + pub fn as_bytes(&self) -> &[u8] { + self.data + } + + //Bit manipulation functions + + /// Read 1 bit. Return true if the bit is set to 1, otherwhise, return false. + /// + /// _Note_: Bits are read from left to right + /// + /// #Example + /// + /// ``` + /// # use bytebuffer::*; + /// let data = vec![128]; + /// let mut buffer = ByteReader::from_bytes(&data); // 10000000b + /// let value1 = buffer.read_bit().unwrap(); //value1 contains true (eg: bit is 1) + /// let value2 = buffer.read_bit().unwrap(); //value2 contains false (eg: bit is 0) + /// ``` + pub fn read_bit(&mut self) -> Result { + if self.rpos >= self.data.len() { + return Err(Error::new( + ErrorKind::UnexpectedEof, + "could not read enough bits from buffer", + )); + } + let bit = self.data[self.rpos] & (1 << (7 - self.rbit)) != 0; + self.rbit += 1; + if self.rbit > 7 { + self.flush_rbits(); + } + Ok(bit) + } + + /// Read n bits. an return the corresponding value an u64. + /// + /// _Note_: We cannot read more than 64 bits + /// + /// _Note_: Bits are read from left to right + /// + /// #Example + /// + /// ``` + /// # use bytebuffer::*; + /// let data = vec![128]; + /// let mut buffer = ByteReader::from_bytes(&data); // 10000000b + /// let value = buffer.read_bits(3).unwrap(); // value contains 4 (eg: 100b) + /// ``` + pub fn read_bits(&mut self, n: u8) -> Result { + if n > 64 { + return Err(Error::new( + ErrorKind::InvalidInput, + "cannot read more than 64 bits", + )); + } + + if n == 0 { + Ok(0) + } else { + Ok((u64::from(self.read_bit()?) << (n - 1)) | self.read_bits(n - 1)?) + } + } + + /// Discard all the pending bits available for reading and place the corresponding cursor to the next byte. + /// + /// _Note_: If no bits are currently read, this function does nothing. + /// + /// #Example + /// + /// ```text + /// 10010010 | 00000001 + /// ^ + /// 10010010 | 00000001 // read_bit called + /// ^ + /// 10010010 | 00000001 // flush_bit() called + /// ^ + /// ``` + pub fn flush_bits(&mut self) { + if self.rbit > 0 { + self.flush_rbits(); + } + } + + fn flush_rbits(&mut self) { + self.rpos += 1; + self.rbit = 0 + } +} diff --git a/pumpkin/src/protocol/client/mod.rs b/pumpkin/src/protocol/client/mod.rs new file mode 100644 index 000000000..7befc88e7 --- /dev/null +++ b/pumpkin/src/protocol/client/mod.rs @@ -0,0 +1,4 @@ +// Clientbound Packets +// Server -> Client +// +pub mod status; diff --git a/pumpkin/src/protocol/client/status/mod.rs b/pumpkin/src/protocol/client/status/mod.rs new file mode 100644 index 000000000..1d531b6e8 --- /dev/null +++ b/pumpkin/src/protocol/client/status/mod.rs @@ -0,0 +1,37 @@ +use crate::protocol::{bytebuf::buffer::ByteBuffer, ClientPacket, VarInt}; + +pub struct CPingResponse { + payload: i64, // must responde with the same as in `SPingRequest` +} + +impl CPingResponse { + pub fn new(payload: i64) -> Self { + Self { payload } + } +} + +impl ClientPacket for CPingResponse { + const PACKET_ID: VarInt = 1; + + fn write(&self, bytebuf: &mut ByteBuffer) { + bytebuf.write_i64(self.payload); + } +} + +pub struct CStatusResponse { + json_response: String, // 32767 +} + +impl CStatusResponse { + pub fn new(json_response: String) -> Self { + Self { json_response } + } +} + +impl ClientPacket for CStatusResponse { + const PACKET_ID: VarInt = 0; + + fn write(&self, bytebuf: &mut ByteBuffer) { + bytebuf.write_string(self.json_response.as_str()); + } +} diff --git a/pumpkin/src/protocol/mod.rs b/pumpkin/src/protocol/mod.rs new file mode 100644 index 000000000..ad5f2e82b --- /dev/null +++ b/pumpkin/src/protocol/mod.rs @@ -0,0 +1,54 @@ +use bytebuf::buffer::ByteBuffer; +use serde::{Deserialize, Serialize}; + +pub mod bytebuf; + +pub mod client; +pub mod server; + +type VarInt = i32; +type VarLong = i64; + +#[derive(Debug)] +pub enum ConnectionState { + HandShake, + Status, + Login, + Transfer, +} + +impl ConnectionState { + pub fn from_varint(var_int: VarInt) -> Self { + match var_int { + 1 => Self::Status, + 2 => Self::Login, + 3 => Self::Transfer, + _ => panic!("Unexpected Status {}", var_int), + } + } +} + +#[derive(Debug)] +pub struct RawPacket { + pub len: VarInt, + pub id: VarInt, + pub bytebuf: ByteBuffer, +} + +pub trait ClientPacket { + const PACKET_ID: VarInt; + + fn write(&self, bytebuf: &mut ByteBuffer); +} + +#[derive(Serialize, Deserialize)] +pub struct StatusResponse { + pub version: Version, + pub description: String, + // Players, favicon ... +} +#[derive(Serialize, Deserialize)] +pub struct Version { + pub version: String, + pub protocol: u32, +} diff --git a/pumpkin/src/protocol/server/handshake/mod.rs b/pumpkin/src/protocol/server/handshake/mod.rs new file mode 100644 index 000000000..d63482b53 --- /dev/null +++ b/pumpkin/src/protocol/server/handshake/mod.rs @@ -0,0 +1,21 @@ +use crate::protocol::{bytebuf::buffer::ByteBuffer, ConnectionState, VarInt}; + +pub struct SHandShake { + pub protocol_version: VarInt, + pub server_address: String, // 255 + pub server_port: u16, + pub next_state: ConnectionState, +} + +impl SHandShake { + pub const PACKET_ID: VarInt = 0; + + pub fn read(bytebuf: &mut ByteBuffer) -> Self { + Self { + protocol_version: bytebuf.read_var_int().unwrap(), + server_address: bytebuf.read_string().unwrap(), + server_port: bytebuf.read_u16().unwrap(), + next_state: ConnectionState::from_varint(bytebuf.read_var_int().unwrap()), + } + } +} diff --git a/pumpkin/src/protocol/server/mod.rs b/pumpkin/src/protocol/server/mod.rs new file mode 100644 index 000000000..70b6f91a9 --- /dev/null +++ b/pumpkin/src/protocol/server/mod.rs @@ -0,0 +1,4 @@ +// Serverbound Packets +// Client -> Server +pub mod handshake; +pub mod status; diff --git a/pumpkin/src/protocol/server/status/mod.rs b/pumpkin/src/protocol/server/status/mod.rs new file mode 100644 index 000000000..925a88634 --- /dev/null +++ b/pumpkin/src/protocol/server/status/mod.rs @@ -0,0 +1,27 @@ +use crate::protocol::{bytebuf::buffer::ByteBuffer, VarInt}; + +pub struct SStatusRequest { + // empty +} + +impl SStatusRequest { + pub const PACKET_ID: VarInt = 0; + + pub fn read(_bytebuf: &mut ByteBuffer) -> Self { + Self {} + } +} + +pub struct SPingRequest { + pub payload: i64, +} + +impl SPingRequest { + pub const PACKET_ID: VarInt = 1; + + pub fn read(bytebuf: &mut ByteBuffer) -> Self { + Self { + payload: bytebuf.read_i64().unwrap(), + } + } +}