diff --git a/Cargo.lock b/Cargo.lock index c9fa11e..76bbcec 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -55,7 +55,7 @@ version = "0.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fcb51a0695d8f838b1ee009b3fbf66bda078cd64590202a864a8f3e8c4315c47" dependencies = [ - "getrandom", + "getrandom 0.2.6", "once_cell", "version_check", ] @@ -119,6 +119,12 @@ dependencies = [ "rustc-demangle", ] +[[package]] +name = "base64" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3441f0f7b02788e948e47f457ca01f1d7e6d92c693bc132c22b087d3141c03ff" + [[package]] name = "base64" version = "0.13.0" @@ -514,16 +520,6 @@ dependencies = [ "syn", ] -[[package]] -name = "derive_packet" -version = "0.1.0" -dependencies = [ - "darling", - "proc-macro2", - "quote", - "syn", -] - [[package]] name = "dispatch" version = "0.2.0" @@ -797,6 +793,17 @@ version = "0.3.21" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "21163e139fa306126e6eedaf49ecdb4588f939600f0b1e770f4205ee4b7fa868" +[[package]] +name = "getrandom" +version = "0.1.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8fc3cb4d91f53b50155bdcfd23f6a4c39ae1969c2ae85982b135750cccaf5fce" +dependencies = [ + "cfg-if 1.0.0", + "libc", + "wasi 0.9.0+wasi-snapshot-preview1", +] + [[package]] name = "getrandom" version = "0.2.6" @@ -806,7 +813,7 @@ dependencies = [ "cfg-if 1.0.0", "js-sys", "libc", - "wasi", + "wasi 0.10.0+wasi-snapshot-preview1", "wasm-bindgen", ] @@ -1140,12 +1147,15 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a3e378b66a060d48947b590737b30a1be76706c8dd7b8ba0f2fe3989c68a853f" [[package]] -name = "mcnetwork" -version = "0.1.0" +name = "mcproto-rs" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95da7392378178142f90c2b47681104ad96b99c43e6818ac09ed46087a9777ab" dependencies = [ - "derive_packet", - "quartz_nbt", - "rand", + "base64 0.12.3", + "rand 0.7.3", + "serde", + "serde_json", ] [[package]] @@ -1177,7 +1187,7 @@ name = "minecraft" version = "0.1.0" dependencies = [ "Inflector", - "base64", + "base64 0.13.0", "chrono", "egui", "egui-winit", @@ -1190,10 +1200,10 @@ dependencies = [ "image", "lazy_static", "log", - "mcnetwork", + "mcproto-rs", "miniz_oxide 0.5.0", "quartz_nbt", - "rand", + "rand 0.8.4", "serde", "serde_json", ] @@ -1251,7 +1261,7 @@ version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6a51313c5820b0b02bd422f4b44776fbf47961755c74ce64afc73bfad10226c3" dependencies = [ - "getrandom", + "getrandom 0.2.6", ] [[package]] @@ -1647,6 +1657,19 @@ dependencies = [ "proc-macro2", ] +[[package]] +name = "rand" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a6b1679d49b24bbfe0c803429aa1874472f50d9b363131f0e89fc356b544d03" +dependencies = [ + "getrandom 0.1.16", + "libc", + "rand_chacha 0.2.2", + "rand_core 0.5.1", + "rand_hc 0.2.0", +] + [[package]] name = "rand" version = "0.8.4" @@ -1654,9 +1677,19 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2e7573632e6454cf6b99d7aac4ccca54be06da05aca2ef7423d22d27d4d4bcd8" dependencies = [ "libc", - "rand_chacha", - "rand_core", - "rand_hc", + "rand_chacha 0.3.1", + "rand_core 0.6.3", + "rand_hc 0.3.1", +] + +[[package]] +name = "rand_chacha" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f4c8ed856279c9737206bf725bf36935d8666ead7aa69b52be55af369d193402" +dependencies = [ + "ppv-lite86", + "rand_core 0.5.1", ] [[package]] @@ -1666,7 +1699,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" dependencies = [ "ppv-lite86", - "rand_core", + "rand_core 0.6.3", +] + +[[package]] +name = "rand_core" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19" +dependencies = [ + "getrandom 0.1.16", ] [[package]] @@ -1675,7 +1717,16 @@ version = "0.6.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d34f1408f55294453790c48b2f1ebbb1c5b4b7563eb1f418bcfcfdbb06ebb4e7" dependencies = [ - "getrandom", + "getrandom 0.2.6", +] + +[[package]] +name = "rand_hc" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca3129af7b92a17112d59ad498c6f81eaf463253766b90396d39ea7a39d6613c" +dependencies = [ + "rand_core 0.5.1", ] [[package]] @@ -1684,7 +1735,7 @@ version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d51e9f596de227fda2ea6c84607f5558e196eeaf43c986b724ba4fb8fdf497e7" dependencies = [ - "rand_core", + "rand_core 0.6.3", ] [[package]] @@ -1949,7 +2000,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6db9e6914ab8b1ae1c260a4ae7a49b6c5611b40328a735b21862567685e73255" dependencies = [ "libc", - "wasi", + "wasi 0.10.0+wasi-snapshot-preview1", "winapi", ] @@ -2065,6 +2116,12 @@ dependencies = [ "winapi-util", ] +[[package]] +name = "wasi" +version = "0.9.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519" + [[package]] name = "wasi" version = "0.10.0+wasi-snapshot-preview1" diff --git a/Cargo.toml b/Cargo.toml index 285e160..fbbf9b1 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -39,4 +39,4 @@ env_logger = "0.9.0" lazy_static = "*" -mcnetwork = { path = "./mcnetwork-rs" } \ No newline at end of file +mcproto-rs = { version = "0.2.0", features = ["v1_16_3"] } diff --git a/src/network.rs b/src/network.rs index 208be49..7bb03a7 100644 --- a/src/network.rs +++ b/src/network.rs @@ -1,15 +1,14 @@ -use egui::TextureHandle; + use log::debug; use log::{error, info, warn}; -use mcnetwork::packets; +use mcproto_rs::protocol::{RawPacket, Id, PacketErr}; +use mcproto_rs::types::{BytesSerializer, self, VarInt, TextComponent, BaseComponent}; +use mcproto_rs::{v1_16_3::*, status}; +use mcproto_rs::{protocol, v1_16_3, Serialize}; use miniz_oxide::{deflate::compress_to_vec_zlib, inflate::decompress_to_vec_zlib}; -use mcnetwork::packets::*; -use mcnetwork::types::*; -use serde_json::Value; - -use std::io::Cursor; -use std::time::{Duration, Instant, SystemTime, UNIX_EPOCH}; +use std::io::{Cursor, self, ErrorKind}; +use std::time::Instant; use std::{ io::{Error, Read, Write}, net::TcpStream, @@ -19,7 +18,8 @@ use std::{ use crate::server::*; -pub const PROTOCOL_1_17_1: VarInt = VarInt(756); +pub type PacketType = v1_16_3::Packet753; +pub type RawPacketType<'a> = v1_16_3::RawPacket753<'a>; pub struct NetworkManager { pub stream: TcpStream, @@ -29,7 +29,7 @@ pub struct NetworkManager { compress: bool, threshold: usize, - state: ServerState, + state: protocol::State, pub count: u32, } @@ -80,7 +80,7 @@ impl NetworkManager { threshold: 0, close: false, channel: NetworkChannel { send: ti, recv: ri }, - state: ServerState::Status, + state: protocol::State::Status, count: 0, }); @@ -124,14 +124,20 @@ impl NetworkManager { // Handles incoming packets while !self.close { match self.next_packet() { - Ok(PacketData::Empty) => { - break; - } - Ok(packet) => { - self.handle_packet(packet); + Ok(packet_result) => { + match packet_result { + Ok(packet) => self.handle_packet(packet), + Err(e) => { + log::error!("Couldn't deserialize packet: {}", e); + }, + } } Err(e) => { - panic!("Error handling packet: {:?}", e); + if e.kind() == ErrorKind::WouldBlock { + return; + } else { + panic!("Error handling packet: {:?}", e); + } } } } @@ -143,17 +149,14 @@ impl NetworkManager { /// /// Returns a Decoded Packet ready for processing, or Error if it failed. /// - fn next_packet(&mut self) -> Result> { + fn next_packet(&mut self) -> io::Result> { let mut check = [0u8]; match self.stream.peek(&mut check) { Ok(0) => { panic!("TcpStream ded???"); } Err(e) => { - if e.kind() == std::io::ErrorKind::WouldBlock { - return Ok(PacketData::Empty); - } - return Err(Box::new(e)); + return Err(e); } _ => {} } @@ -161,7 +164,7 @@ impl NetworkManager { self.stream .set_nonblocking(false) .expect("Failed to set TcpStream to blocking mode"); - let VarInt(len) = VarInt::read(&mut self.stream)?; + let len = read_varint(&mut self.stream)?; let mut buf = vec![0u8; len as usize]; self.stream.read_exact(&mut buf)?; @@ -171,18 +174,38 @@ impl NetworkManager { if self.compress { let mut cur = Cursor::new(&buf); - let VarInt(data_len) = VarInt::read(&mut cur)?; + let data_len = read_varint(&mut cur)?; if data_len == 0 { - return Ok(decode_packet_clientbound( - &buf[cur.position() as usize..], - &self.state, - )?); + let id = read_varint(&mut cur)?; + let id = Id { + id, + state: self.state, + direction: protocol::PacketDirection::ClientBound, + }; + return Ok( + match RawPacketType::create(id, &buf[cur.position() as usize..]) { + Ok(raw_packet) => raw_packet.deserialize(), + Err(e) => Err(e), + } + ); } match decompress_to_vec_zlib(&buf[cur.position() as usize..]) { Ok(uncompressed) => { - return Ok(decode_packet_clientbound(&uncompressed, &self.state)?) + let mut cur = Cursor::new(&mut uncompressed); + let id = read_varint(&mut cur)?; + let id = Id { + id, + state: self.state, + direction: protocol::PacketDirection::ClientBound, + }; + return Ok( + match RawPacketType::create(id, &uncompressed[cur.position() as usize..]) { + Ok(raw_packet) => raw_packet.deserialize(), + Err(e) => Err(e), + } + ); } Err(e) => { todo!("Properly decompression error handling"); @@ -190,7 +213,20 @@ impl NetworkManager { } } - Ok(decode_packet_clientbound(&buf, &self.state)?) + + let mut cur = Cursor::new(&mut buf); + let id = read_varint(&mut cur)?; + let id = Id { + id, + state: self.state, + direction: protocol::PacketDirection::ClientBound, + }; + return Ok( + match RawPacketType::create(id, &buf[cur.position() as usize..]) { + Ok(raw_packet) => raw_packet.deserialize(), + Err(e) => Err(e), + } + ); } /// Attempts to login to the server @@ -198,7 +234,7 @@ impl NetworkManager { /// # Returns /// /// * `Some(())` if it successfully logs in, `None` if it fails - fn login(&mut self, protocol: VarInt, port: u16, name: String) -> Option<()> { + fn login(&mut self, protocol: i32, port: u16, name: String) -> Option<()> { use std::net::SocketAddr; // Extracts local address from TcpStream @@ -213,70 +249,82 @@ impl NetworkManager { }; // Construct and send handshake and login packets - let handshake = Handshake { - protocol_version: protocol, - address: local_addr, - port, - next: VarInt(2), + let handshake = HandshakeSpec { + version: VarInt(protocol), + server_address: local_addr, + server_port: port, + next_state: HandshakeNextState::Login, }; - let login = LoginStart { name }; + let login = LoginStartSpec { + name, + }; self.send_packet(&encode(handshake)) .expect("Failed to send handshake"); - self.state = ServerState::Login; + self.state = protocol::State::Login; self.send_packet(&encode(login)) .expect("Failed to send login request"); // Handle all incoming packets until success or failure loop { match self.next_packet() { - Ok(PacketData::Empty) => {} Ok(packet) => { - match &packet { - // Please no - PacketData::EncryptionRequest(_) => { - panic!("I ain't implemented this shit yet"); - } - PacketData::SetCompression(pack) => { - if pack.threshold.0 <= 0 { - self.compress = false; - info!("Disabled Compression"); - } else { - self.compress = true; - self.threshold = pack.threshold.0 as usize; - info!("Set compression: {}", pack.threshold.0); - } - } - PacketData::Disconnect(_) => { - self.send_message(NetworkCommand::ReceivePacket(packet)); - self.close = true; - return None; - } - PacketData::LoginPluginRequest(_) => { - panic!("I don't want to think about LoginPlugin"); - } - PacketData::LoginSuccess(_) => { - warn!("Connecting to server with no authentication!"); - - self.state = ServerState::Play; - self.send_message(NetworkCommand::ReceivePacket(packet)); - - return Some(()); - } - _ => { - warn!("Got unexpected packet during login: {:?}", packet); + match packet { + Ok(packet) => { + match packet { + // Please no + PacketType::LoginEncryptionRequest(_) => { + panic!("I ain't implemented this shit yet"); + } + PacketType::LoginSetCompression(pack) => { + if pack.threshold.0 <= 0 { + self.compress = false; + info!("Disabled Compression"); + } else { + self.compress = true; + self.threshold = pack.threshold.0 as usize; + info!("Set compression: {}", pack.threshold.0); + } + } + PacketType::LoginDisconnect(_) => { + self.send_message(NetworkCommand::ReceivePacket(packet)); + self.close = true; + return None; + } + PacketType::LoginPluginRequest(_) => { + panic!("I don't want to think about LoginPlugin"); + } + PacketType::LoginSuccess(_) => { + warn!("Connecting to server with no authentication!"); + + self.state = protocol::State::Play; + self.send_message(NetworkCommand::ReceivePacket(packet)); + + return Some(()); + } + _ => { + warn!("Got unexpected packet during login: {:?}", packet); + } + }; + }, + Err(e) => { + panic!("Error decoding packet: {}", e); } } } Err(e) => { - panic!("Error reading packet: {:?}", e); + if e.kind() == ErrorKind::WouldBlock { + continue; + } else { + panic!("Error reading packet: {:?}", e); + } } } } } - fn status(&mut self) -> Option { + fn status(&mut self) -> Option { use std::net::SocketAddr; // Extracts local address from TcpStream @@ -291,106 +339,50 @@ impl NetworkManager { }; // Construct and send handshake and login packets - let handshake = Handshake { - protocol_version: VarInt(0), - address: local_addr, - port: 0, - next: VarInt(1), + let handshake = HandshakeSpec { + version: VarInt(0), + server_address: local_addr, + server_port: 0, + next_state: HandshakeNextState::Status, }; - let request = Request {}; - let now = Instant::now(); self.send_packet(&encode(handshake)) .expect("Failed to send handshake"); - self.send_packet(&encode(request)) + self.send_packet(&encode(StatusRequestSpec{})) .expect("Failed to send status request"); - self.send_packet(&encode(StatusPing { payload: 0 })) + self.send_packet(&encode(StatusPingSpec{ payload: 0 })) .expect("Failed to send Status Ping"); let ping; - let json_data; + let mut status: status::StatusSpec; loop { match self.next_packet() { - Ok(PacketData::Empty) => {} Ok(pack) => match pack { - PacketData::Response(Response { json }) => { - ping = (Instant::now() - now).as_millis() as u32; - json_data = json; - break; - } - _ => { - warn!( - "Got unexpected packet waiting for status response: {:?}", - pack - ); + Ok(pack) => match pack { + PacketType::StatusResponse(pack) => { + ping = (Instant::now() - now).as_millis() as u32; + status = pack.response; + break; + } + _ => { + warn!( + "Got unexpected packet waiting for status response: {:?}", + pack + ); + } + }, + Err(e) => { + panic!("Error decoding packet: {}", e); } }, Err(e) => { - error!("Couldn't get response from server status request: {:?}", e); - return None; - } - } - } - - let mut status = ServerStatus { - icon: None, - motd: String::new(), - version: String::new(), - num_players: 0, - max_players: 0, - online_players: Vec::new(), - ping, - }; - - let val: Value = serde_json::from_str(&json_data).expect("Couldn't read JSON data"); - - if let Value::Object(map) = val { - // MOTD - if let Some(Value::Object(motd)) = map.get("description") { - if let Some(Value::String(motd)) = motd.get("text") { - status.motd = motd.clone(); - } - } - - // Version - if let Some(Value::Object(version)) = map.get("version") { - if let Some(Value::String(version)) = version.get("name") { - status.version = version.clone(); - } - } - - // Players - if let Some(Value::Object(players)) = map.get("players") { - // Max - if let Some(Value::Number(max)) = players.get("max") { - status.max_players = max.as_u64().unwrap() as u32; - } - // Num online - if let Some(Value::Number(online)) = players.get("online") { - status.num_players = online.as_u64().unwrap() as u32; - } - // Players online - if let Some(Value::Array(sample)) = players.get("sample") { - for p in sample { - if let Value::Object(pp) = p { - if let Some(Value::String(name)) = pp.get("name") { - status.online_players.push(name.to_string()); - } - } - } - } - } - - // Favicon - if let Some(Value::String(favicon)) = map.get("favicon") { - match base64::decode(&(favicon.replace("\n", ""))[22..]) { - Ok(bytes) => { - status.icon = Some(bytes); - } - Err(e) => { - error!("Couldn't interpret bytes from server favicon"); + if e.kind() == ErrorKind::WouldBlock { + continue; + } else { + error!("Couldn't get response from server status request: {:?}", e); + return None; } } } @@ -416,23 +408,23 @@ impl NetworkManager { if self.compress { if packet.len() >= self.threshold { let mut data_length = Vec::new(); - VarInt(packet.len() as i32).write(&mut data_length)?; + write_varint(&mut data_length, packet.len() as i32)?; let compressed = compress_to_vec_zlib(packet, 0); let mut packet_length = Vec::new(); - VarInt((data_length.len() + compressed.len()) as i32).write(&mut packet_length)?; + write_varint(&mut packet_length, (data_length.len() + compressed.len()) as i32)?; s.write(&packet_length)?; s.write(&data_length)?; s.write(&compressed)?; } else { - VarInt((packet.len() + 1) as i32).write(s)?; + write_varint(s, (packet.len() + 1) as i32)?; s.write(&[0u8])?; s.write(packet)?; } return Ok(()); } - VarInt(packet.len() as i32).write(s)?; + write_varint(s, packet.len() as i32)?; s.write(packet)?; s.set_nonblocking(true) .expect("Failed to set TcpStream nonblocking"); @@ -447,8 +439,11 @@ impl NetworkManager { self.login(protocol, port, name); } NetworkCommand::Disconnect => { - self.send_packet(&encode(Disconnect { - reason: String::from("Player Disconnected"), + self.send_packet(&encode(PlayDisconnectSpec { + reason: types::Chat::Text(TextComponent { + text: String::from("Player Disconnected"), + base: BaseComponent::default(), + }), })) .expect("Failed to send packet"); self.close = true; @@ -470,32 +465,21 @@ impl NetworkManager { } /// Handles an incoming packet - fn handle_packet(&mut self, packet: PacketData) { - use PacketData::*; - + fn handle_packet(&mut self, packet: PacketType) { match &packet { - Unknown(_buf) => { - // println!("Got unknown packet: {:02x}", buf[0]); - } - KeepAliveClientbound(pack) => { - self.send_packet(&encode(packets::KeepAliveServerbound { - keep_alive_id: pack.keep_alive_id.clone(), - })) - .expect("Failed to send heartbeat"); - } - - SetCompression(pack) => { + PacketType::PlayClientKeepAlive(pack) => { + self.send_packet(&encode(PlayClientKeepAliveSpec{id: pack.id})).expect("Failed to send heartbeat."); + }, + PacketType::LoginSetCompression(pack) => { if pack.threshold.0 <= 0 { self.compress = false; - info!("Disabled Packet Compression."); + info!("Disabled packet compression."); } else { - info!("Set Packet Compression: {}", pack.threshold.0); + info!("Set Packet Compression: {}", pack.threshold); self.compress = true; self.threshold = pack.threshold.0 as usize; } } - - // Forward other packets to the main thread _ => { self.send_message(NetworkCommand::ReceivePacket(packet)); } @@ -506,8 +490,11 @@ impl NetworkManager { if let Err(_) = self.channel.send.send(comm) { error!("Couldn't communicated with main thread, assuming connection was closed and disconnecting from server."); self.close = true; - self.send_packet(&encode(Disconnect { - reason: String::from("Player Disconnected"), + self.send_packet(&encode(PlayDisconnectSpec { + reason: types::Chat::Text(types::TextComponent { + text: String::from("Player Disconnected"), + base: types::BaseComponent::default(), + }) })) .expect("Failed to send Disconnect packet"); } @@ -527,13 +514,59 @@ pub enum NetworkCommand { Error(Error), Disconnect, // Login(protocol, port, name) - Login(VarInt, u16, String), + Login(i32, u16, String), SendPacket(Vec), - ReceivePacket(PacketData), + ReceivePacket(PacketType), RequestStatus, - ReceiveStatus(ServerStatus), + ReceiveStatus(status::StatusSpec), Spawn, } + +fn read_varint(r: &mut R) -> io::Result { + const PART: u32 = 0x7F; + let mut size = 0; + let mut val = 0u32; + let mut byte: [u8; 1] = [0]; + + r.read_exact(&mut byte)?; + loop { + val |= (byte[0] as u32 & PART) << (size * 7); + size += 1; + + if (byte[0] & 0x80) == 0 { + break; + } + + r.read_exact(&mut byte)?; + } + + Ok(val as i32) +} + +fn write_varint(w: &mut W, val: i32) -> io::Result<()> { + let mut buf: Vec = Vec::new(); + + const PART: u32 = 0x7F; + let mut val = val as u32; + loop { + if (val & !PART) == 0 { + buf.push(val as u8); + break; + } + buf.push(val as u8 | !0x7F); + val >>= 7; + } + w.write(&buf)?; + Ok(()) +} + +fn encode(packet: S) -> Vec { + let mut serializer = BytesSerializer { + data: Vec::new(), + }; + packet.mc_serialize(&mut serializer); + serializer.into_bytes() +}