diff --git a/Cargo.lock b/Cargo.lock index b191eac..2a8dfd1 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -8,9 +8,9 @@ checksum = "a4c527152e37cf757a3f78aae5a06fbeefdb07ccc535c980a3208ee3060dd544" [[package]] name = "async-trait" -version = "0.1.42" +version = "0.1.48" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8d3a45e77e34375a7923b1e8febb049bb011f064714a8e17a1a616fef01da13d" +checksum = "36ea56748e10732c49404c153638a15ec3d6211ec5ff35d9bb20e13b93576adf" dependencies = [ "proc-macro2", "quote", @@ -374,9 +374,9 @@ dependencies = [ [[package]] name = "syn" -version = "1.0.60" +version = "1.0.64" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c700597eca8a5a762beb35753ef6b94df201c81cca676604f547495a0d7f0081" +checksum = "3fd9d1e9976102a03c542daa2eff1b43f9d72306342f3f8b3ed5fb8908195d6f" dependencies = [ "proc-macro2", "quote", diff --git a/Cargo.toml b/Cargo.toml index 2c4532d..2c289e1 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -13,7 +13,7 @@ serde_json = "1.0.59" toml = "0.5" radix64 = "0.3.0" tokio = { version = "1", features = ["full"] } -async-trait = "0.1.42" +async-trait = "0.1.48" lazy_static = "1.4.0" # colorful = "0.2.1" # ozelot = "0.9.0" # Ozelot 0.9.0 supports protocol version 578 (1.15.2) diff --git a/src/mctypes.rs b/src/mctypes.rs index 348e7d1..954e8c8 100644 --- a/src/mctypes.rs +++ b/src/mctypes.rs @@ -21,10 +21,25 @@ pub mod functions { t.read_exact(&mut buffer).await?; Ok(buffer[0]) } + /// Read `l` bytes from the given `TcpStream`. + pub async fn read_bytes(t: &mut TcpStream, l: usize) -> tokio::io::Result> { + let mut buffer = vec![]; + for _ in 0..l { + buffer.push(read_byte(t).await?); + } + Ok(buffer) + } /// Write a single byte to the given `TcpStream`. - pub async fn write_byte(t: &mut TcpStream, value: u8) -> tokio::io::Result { + pub async fn write_byte(t: &mut TcpStream, value: u8) -> tokio::io::Result<()> { t.write(&[value]).await?; - Ok(value) + Ok(()) + } + /// Write multiple bytes to the given `TcpStream`. + pub async fn write_bytes(t: &mut TcpStream, bytes: &[u8]) -> tokio::io::Result<()> { + for b in bytes { + write_byte(t, *b).await?; + } + Ok(()) } /// Take `l` bytes from the given `Vec`. pub fn get_bytes(v: Vec, l: usize) -> Box<[u8]> { diff --git a/src/server/mod.rs b/src/server/mod.rs index 8956422..f60c0fc 100644 --- a/src/server/mod.rs +++ b/src/server/mod.rs @@ -128,8 +128,7 @@ impl NetworkClient { pub async fn update(&mut self, num_players: usize) -> tokio::io::Result<()> { match self.state { NetworkClientState::Handshake => { - let (_packet_length, _packet_id) = read_packet_header(&mut self.stream).await?; - let handshake = Handshake::read(&mut self.stream).await?; + let handshake = self.get_packet::().await?; // Minecraft versions 1.8 - 1.8.9 use protocol version 47. let compatible_versions = handshake.protocol_version == 47; let next_state = match handshake.next_state.into() { @@ -144,15 +143,14 @@ impl NetworkClient { logindisconnect.reason = MCChat { text: MCString::from("Incompatible client! Server is on 1.8.9"), }; - logindisconnect.write(&mut self.stream).await?; + self.send_packet(logindisconnect).await?; self.state = NetworkClientState::Disconnected; } - debug!("Got handshake: {:?}", handshake); + debug!("{:?}", handshake); } NetworkClientState::Status => { - let (_packet_length, _packet_id) = read_packet_header(&mut self.stream).await?; - let statusrequest = StatusRequest::read(&mut self.stream).await?; - debug!("Got status request: {:?}", statusrequest); + let statusrequest = self.get_packet::().await?; + debug!("{:?}", statusrequest); let mut statusresponse = StatusResponse::new(); statusresponse.json_response = json!({ "version": { @@ -176,20 +174,17 @@ impl NetworkClient { }) .to_string() .into(); - statusresponse.write(&mut self.stream).await?; - debug!("Sending status response: StatusResponse"); - let (_packet_length, _packet_id) = read_packet_header(&mut self.stream).await?; - let statusping = StatusPing::read(&mut self.stream).await?; - debug!("Got status ping: {:?}", statusping); + self.send_packet(statusresponse).await?; + + let statusping = self.get_packet::().await?; + debug!("{:?}", statusping); let mut statuspong = StatusPong::new(); statuspong.payload = statusping.payload; - statuspong.write(&mut self.stream).await?; - debug!("Sending status pong: {:?}", statuspong); + self.send_packet(statuspong).await?; self.state = NetworkClientState::Disconnected; } NetworkClientState::Login => { - let (_packet_length, _packet_id) = read_packet_header(&mut self.stream).await?; - let loginstart = LoginStart::read(&mut self.stream).await?; + let loginstart = self.get_packet::().await?; debug!("{:?}", loginstart); // Offline mode skips encryption and compression. // TODO: Encryption and compression @@ -198,26 +193,20 @@ impl NetworkClient { // TODO: Get uuid and username from Mojang servers. loginsuccess.uuid = "00000000-0000-3000-0000-000000000000".into(); loginsuccess.username = loginstart.player_name; - loginsuccess.write(&mut self.stream).await?; - debug!("{:?}", loginsuccess); self.uuid = Some(loginsuccess.uuid.clone().into()); self.username = Some(loginsuccess.username.clone().into()); + self.send_packet(loginsuccess).await?; self.state = NetworkClientState::Play; let joingame = JoinGame::new(); // TODO: Fill out `joingame` with actual information. - joingame.write(&mut self.stream).await?; - debug!("{:?}", joingame); - let (_packet_length, _packet_id) = read_packet_header(&mut self.stream).await?; - let clientsettings = ClientSettings::read(&mut self.stream).await?; - // TODO: Actualy use client settings. + self.send_packet(joingame).await?; + + let clientsettings = self.get_packet::().await?; + // TODO: Actually use client settings. debug!("{:?}", clientsettings); - - // All good up to here. - let helditemchange = HeldItemChange::new(); // TODO: Retrieve selected slot from storage. - helditemchange.write(&mut self.stream).await?; - debug!("{:?}", helditemchange); + self.send_packet(helditemchange).await?; // TODO: S->C Declare Recipes (1.16?) // TODO: S->C Tags (1.16?) // TODO: S->C Entity Status (optional?) @@ -226,8 +215,7 @@ impl NetworkClient { // TODO: S->C Player Position and Look let playerpositionandlook = PlayerPositionAndLook::new(); // TODO: Retrieve player position from storage. - playerpositionandlook.write(&mut self.stream).await?; - debug!("{:?}", playerpositionandlook); + self.send_packet(playerpositionandlook).await?; // TODO: S->C Player Info (Add Player action) (1.16?) // TODO: S->C Player Info (Update latency action) (1.16?) // TODO: S->C Update View Position (1.16?) @@ -236,8 +224,7 @@ impl NetworkClient { // TODO: S->C World Border // TODO: S->C Spawn Position let spawnposition = SpawnPosition::new(); - spawnposition.write(&mut self.stream).await?; - debug!("{:?}", spawnposition); + self.send_packet(spawnposition).await?; // Send initial keep alive. self.send_chat_message("keep alive").await?; self.keep_alive().await?; @@ -267,36 +254,56 @@ impl NetworkClient { Ok(()) } + /// Send a generic packet to the client. + pub async fn send_packet + core::fmt::Debug>( + &mut self, + packet: P, + ) -> tokio::io::Result<()> { + debug!("{:?}", packet); + Into::::into(packet).write(&mut self.stream).await + } + + /// Read a generic packet from the network. + pub async fn get_packet(&mut self) -> tokio::io::Result { + let (_packet_length, _packet_id) = read_packet_header(&mut self.stream).await?; + Ok(T::read(&mut self.stream).await?) + } + + /// Send the client a message in chat. async fn send_chat_message>(&mut self, message: C) -> tokio::io::Result<()> { let mut chatmessage = ClientboundChatMessage::new(); chatmessage.text = message.into(); - chatmessage.write(&mut self.stream).await?; - debug!("{:?}", chatmessage); + self.send_packet(chatmessage).await?; Ok(()) } + /// Disconnect the client. + /// + /// Sends `0x40 Disconnect` then waits 10 seconds before forcing the connection closed. async fn disconnect(&mut self, reason: Option<&str>) -> tokio::io::Result<()> { - self.connected = false; - self.state = NetworkClientState::Disconnected; - // Send 0x40 Disconnect. let mut disconnect = Disconnect::new(); disconnect.reason.text = reason.unwrap_or("Disconnected").into(); - disconnect.write(&mut self.stream).await?; - debug!("{:?}", disconnect); + self.send_packet(disconnect).await?; // Give the client 10 seconds to disconnect before forcing it. tokio::time::sleep(Duration::from_secs(10)).await; + self.force_disconnect(); Ok(()) } + /// Force disconnect the client by marking it for cleanup as disconnected. + async fn force_disconnect(&mut self) { + self.connected = false; + self.state = NetworkClientState::Disconnected; + } + /// Send a keep alive packet to the client. async fn keep_alive(&mut self) -> tokio::io::Result<()> { // Keep alive ping to client. let clientboundkeepalive = KeepAlivePing::new(); - clientboundkeepalive.write(&mut self.stream).await?; - debug!("{:?}", clientboundkeepalive); + self.send_packet(clientboundkeepalive).await?; // Keep alive pong to server. - let (_packet_length, _packet_id) = read_packet_header(&mut self.stream).await?; - let serverboundkeepalive = KeepAlivePong::read(&mut self.stream).await?; + + let serverboundkeepalive = self.get_packet::().await?; debug!("{:?}", serverboundkeepalive); self.last_keep_alive = Instant::now(); Ok(()) diff --git a/src/server/packets/clientbound.rs b/src/server/packets/clientbound.rs index 758e4c7..818ed7a 100644 --- a/src/server/packets/clientbound.rs +++ b/src/server/packets/clientbound.rs @@ -1,3 +1,4 @@ +use super::PacketCommon; use crate::mctypes::*; use crate::CONFIG; use std::convert::{Into, TryFrom}; @@ -23,18 +24,19 @@ impl TryFrom> for StatusResponse { Err("unimplemented") } } -impl StatusResponse { - pub fn new() -> Self { +#[async_trait::async_trait] +impl PacketCommon for StatusResponse { + fn new() -> Self { StatusResponse { json_response: MCString::from(""), } } - pub async fn read(t: &'_ mut TcpStream) -> tokio::io::Result { + async fn read(t: &'_ mut TcpStream) -> tokio::io::Result { let mut statusresponse = StatusResponse::new(); statusresponse.json_response = MCString::read(t).await?; Ok(statusresponse) } - pub async fn write(&self, t: &'_ mut TcpStream) -> tokio::io::Result<()> { + async fn write(&self, t: &'_ mut TcpStream) -> tokio::io::Result<()> { for b in Into::>::into(self.clone()) { write_byte(t, b).await?; } @@ -62,16 +64,17 @@ impl TryFrom> for StatusPong { Err("unimplemented") } } -impl StatusPong { - pub fn new() -> Self { +#[async_trait::async_trait] +impl PacketCommon for StatusPong { + fn new() -> Self { StatusPong { payload: 0.into() } } - pub async fn read(t: &mut TcpStream) -> tokio::io::Result { + async fn read(t: &mut TcpStream) -> tokio::io::Result { let mut statuspong = StatusPong::new(); statuspong.payload = MCLong::read(t).await?; Ok(statuspong) } - pub async fn write(&self, t: &mut TcpStream) -> tokio::io::Result<()> { + async fn write(&self, t: &mut TcpStream) -> tokio::io::Result<()> { for b in Into::>::into(self.clone()) { write_byte(t, b).await?; } @@ -101,20 +104,21 @@ impl TryFrom> for LoginSuccess { Err("unimplemented") } } -impl LoginSuccess { - pub fn new() -> Self { +#[async_trait::async_trait] +impl PacketCommon for LoginSuccess { + fn new() -> Self { LoginSuccess { uuid: MCString::from(""), username: MCString::from(""), } } - pub async fn read(t: &mut TcpStream) -> tokio::io::Result { + async fn read(t: &mut TcpStream) -> tokio::io::Result { let mut loginsuccess = LoginSuccess::new(); loginsuccess.uuid = MCString::read(t).await?; loginsuccess.username = MCString::read(t).await?; Ok(loginsuccess) } - pub async fn write(&self, t: &mut TcpStream) -> tokio::io::Result<()> { + async fn write(&self, t: &mut TcpStream) -> tokio::io::Result<()> { for b in Into::>::into(self.clone()) { write_byte(t, b).await?; } @@ -142,22 +146,23 @@ impl TryFrom> for LoginDisconnect { Err("unimplemented") } } -impl LoginDisconnect { - pub fn new() -> Self { +#[async_trait::async_trait] +impl PacketCommon for LoginDisconnect { + fn new() -> Self { LoginDisconnect { reason: MCChat { text: MCString::from(""), }, } } - pub async fn read(t: &mut TcpStream) -> tokio::io::Result { + async fn read(t: &mut TcpStream) -> tokio::io::Result { let mut logindisconnect = LoginDisconnect::new(); logindisconnect.reason = MCChat { text: MCString::read(t).await?, }; Ok(logindisconnect) } - pub async fn write(&self, t: &mut TcpStream) -> tokio::io::Result<()> { + async fn write(&self, t: &mut TcpStream) -> tokio::io::Result<()> { for b in Into::>::into(self.clone()) { write_byte(t, b).await?; } @@ -167,13 +172,13 @@ impl LoginDisconnect { #[derive(Debug, Clone)] pub struct JoinGame { - entity_id: MCInt, // The player's Entity ID (EID) - gamemode: MCUnsignedByte, // 0: Survival, 1: Creative, 2: Adventure, 3: Spectator. Bit 3 (0x8) is the hardcore flag. - dimension: MCByte, // -1: Nether, 0: Overworld, 1: End - difficulty: MCUnsignedByte, // 0: Peaceful, 1: Easy, 2: Normal, 3: Hard - max_players: MCUnsignedByte, // Used by the client to draw the player list - level_type: MCString, // default, flat, largeBiomes, amplified, default_1_1 - reduced_debug_info: MCBoolean, // If true, a Notchian client shows reduced information on the debug screen. + pub entity_id: MCInt, // The player's Entity ID (EID) + pub gamemode: MCUnsignedByte, // 0: Survival, 1: Creative, 2: Adventure, 3: Spectator. Bit 3 (0x8) is the hardcore flag. + pub dimension: MCByte, // -1: Nether, 0: Overworld, 1: End + pub difficulty: MCUnsignedByte, // 0: Peaceful, 1: Easy, 2: Normal, 3: Hard + pub max_players: MCUnsignedByte, // Used by the client to draw the player list + pub level_type: MCString, // default, flat, largeBiomes, amplified, default_1_1 + pub reduced_debug_info: MCBoolean, // If true, a Notchian client shows reduced information on the debug screen. } impl Into> for JoinGame { fn into(self) -> Vec { @@ -197,8 +202,9 @@ impl TryFrom> for JoinGame { Err("unimplemented") } } -impl JoinGame { - pub fn new() -> Self { +#[async_trait::async_trait] +impl PacketCommon for JoinGame { + fn new() -> Self { JoinGame { entity_id: 0.into(), gamemode: 1.into(), // Default to creative mode. @@ -209,7 +215,7 @@ impl JoinGame { reduced_debug_info: false.into(), // The debug info should be useful. } } - pub async fn read(t: &mut TcpStream) -> tokio::io::Result { + async fn read(t: &mut TcpStream) -> tokio::io::Result { let mut joingame = JoinGame::new(); joingame.entity_id = MCInt::read(t).await?; joingame.gamemode = MCUnsignedByte::read(t).await?; @@ -220,7 +226,7 @@ impl JoinGame { joingame.reduced_debug_info = MCBoolean::read(t).await?; Ok(joingame) } - pub async fn write(&self, t: &mut TcpStream) -> tokio::io::Result<()> { + async fn write(&self, t: &mut TcpStream) -> tokio::io::Result<()> { for b in Into::>::into(self.clone()) { write_byte(t, b).await?; } @@ -230,7 +236,7 @@ impl JoinGame { #[derive(Debug, Clone)] pub struct HeldItemChange { - selected_slot: MCByte, + pub selected_slot: MCByte, } impl Into> for HeldItemChange { fn into(self) -> Vec { @@ -248,18 +254,19 @@ impl TryFrom> for HeldItemChange { Err("unimplemented") } } -impl HeldItemChange { - pub fn new() -> Self { +#[async_trait::async_trait] +impl PacketCommon for HeldItemChange { + fn new() -> Self { HeldItemChange { selected_slot: 0.into(), } } - pub async fn read(t: &mut TcpStream) -> tokio::io::Result { + async fn read(t: &mut TcpStream) -> tokio::io::Result { let mut helditemchange = HeldItemChange::new(); helditemchange.selected_slot = MCByte::read(t).await?; Ok(helditemchange) } - pub async fn write(&self, t: &mut TcpStream) -> tokio::io::Result<()> { + async fn write(&self, t: &mut TcpStream) -> tokio::io::Result<()> { for b in Into::>::into(self.clone()) { write_byte(t, b).await?; } @@ -269,31 +276,31 @@ impl HeldItemChange { #[derive(Debug, Clone)] pub struct EntityStatus { - entity_id: MCInt, - entity_status: MCByte, // See table below. - // 1: Sent when resetting a mob spawn minecart's timer / Rabbit jump animation - // 2: Living Entity hurt - // 3: Living Entity dead - // 4: Iron Golem throwing up arms - // 6: Wolf/Ocelot/Horse taming — Spawn “heart” particles - // 7: Wolf/Ocelot/Horse tamed — Spawn “smoke” particles - // 8: Wolf shaking water — Trigger the shaking animation - // 9: (of self) Eating accepted by server - // 10: Sheep eating grass - // 10: Play TNT ignite sound - // 11: Iron Golem handing over a rose - // 12: Villager mating — Spawn “heart” particles - // 13: Spawn particles indicating that a villager is angry and seeking revenge - // 14: Spawn happy particles near a villager - // 15: Witch animation — Spawn “magic” particles - // 16: Play zombie converting into a villager sound - // 17: Firework exploding - // 18: Animal in love (ready to mate) — Spawn “heart” particles - // 19: Reset squid rotation - // 20: Spawn explosion particle — works for some living entities - // 21: Play guardian sound — works for only for guardians - // 22: Enables reduced debug for players - // 23: Disables reduced debug for players + pub entity_id: MCInt, + pub entity_status: MCByte, // See table below. + // 1: Sent when resetting a mob spawn minecart's timer / Rabbit jump animation + // 2: Living Entity hurt + // 3: Living Entity dead + // 4: Iron Golem throwing up arms + // 6: Wolf/Ocelot/Horse taming — Spawn “heart” particles + // 7: Wolf/Ocelot/Horse tamed — Spawn “smoke” particles + // 8: Wolf shaking water — Trigger the shaking animation + // 9: (of self) Eating accepted by server + // 10: Sheep eating grass + // 10: Play TNT ignite sound + // 11: Iron Golem handing over a rose + // 12: Villager mating — Spawn “heart” particles + // 13: Spawn particles indicating that a villager is angry and seeking revenge + // 14: Spawn happy particles near a villager + // 15: Witch animation — Spawn “magic” particles + // 16: Play zombie converting into a villager sound + // 17: Firework exploding + // 18: Animal in love (ready to mate) — Spawn “heart” particles + // 19: Reset squid rotation + // 20: Spawn explosion particle — works for some living entities + // 21: Play guardian sound — works for only for guardians + // 22: Enables reduced debug for players + // 23: Disables reduced debug for players } impl Into> for EntityStatus { fn into(self) -> Vec { @@ -312,20 +319,21 @@ impl TryFrom> for EntityStatus { Err("unimplemented") } } -impl EntityStatus { - pub fn new() -> Self { +#[async_trait::async_trait] +impl PacketCommon for EntityStatus { + fn new() -> Self { EntityStatus { entity_id: 0.into(), entity_status: 0.into(), } } - pub async fn read(t: &mut TcpStream) -> tokio::io::Result { + async fn read(t: &mut TcpStream) -> tokio::io::Result { let mut entitystatus = EntityStatus::new(); entitystatus.entity_id = MCInt::read(t).await?; entitystatus.entity_status = MCByte::read(t).await?; Ok(entitystatus) } - pub async fn write(&self, t: &mut TcpStream) -> tokio::io::Result<()> { + async fn write(&self, t: &mut TcpStream) -> tokio::io::Result<()> { for b in Into::>::into(self.clone()) { write_byte(t, b).await?; } @@ -335,12 +343,12 @@ impl EntityStatus { #[derive(Debug, Clone)] pub struct PlayerPositionAndLook { - x: MCDouble, - y: MCDouble, - z: MCDouble, - yaw: MCFloat, - pitch: MCFloat, - flags: MCByte, + pub x: MCDouble, + pub y: MCDouble, + pub z: MCDouble, + pub yaw: MCFloat, + pub pitch: MCFloat, + pub flags: MCByte, } impl Into> for PlayerPositionAndLook { fn into(self) -> Vec { @@ -363,8 +371,9 @@ impl TryFrom> for PlayerPositionAndLook { Err("unimplemented") } } -impl PlayerPositionAndLook { - pub fn new() -> Self { +#[async_trait::async_trait] +impl PacketCommon for PlayerPositionAndLook { + fn new() -> Self { PlayerPositionAndLook { x: 0.0.into(), y: 0.0.into(), @@ -374,7 +383,7 @@ impl PlayerPositionAndLook { flags: 0x00.into(), } } - pub async fn read(t: &mut TcpStream) -> tokio::io::Result { + async fn read(t: &mut TcpStream) -> tokio::io::Result { let mut playerpositionandlook = PlayerPositionAndLook::new(); playerpositionandlook.x = MCDouble::read(t).await?; playerpositionandlook.y = MCDouble::read(t).await?; @@ -384,7 +393,7 @@ impl PlayerPositionAndLook { playerpositionandlook.flags = MCByte::read(t).await?; Ok(playerpositionandlook) } - pub async fn write(&self, t: &mut TcpStream) -> tokio::io::Result<()> { + async fn write(&self, t: &mut TcpStream) -> tokio::io::Result<()> { for b in Into::>::into(self.clone()) { write_byte(t, b).await?; } @@ -395,7 +404,7 @@ impl PlayerPositionAndLook { // TODO: Actually send the position. #[derive(Debug, Clone)] pub struct SpawnPosition { - position: MCPosition, + pub position: MCPosition, } impl Into> for SpawnPosition { fn into(self) -> Vec { @@ -414,18 +423,19 @@ impl TryFrom> for SpawnPosition { Err("unimplemented") } } -impl SpawnPosition { - pub fn new() -> Self { +#[async_trait::async_trait] +impl PacketCommon for SpawnPosition { + fn new() -> Self { SpawnPosition { position: MCPosition::new(), } } - pub async fn read(t: &mut TcpStream) -> tokio::io::Result { + async fn read(t: &mut TcpStream) -> tokio::io::Result { let mut spawnposition = SpawnPosition::new(); spawnposition.position = MCPosition::read(t).await?; Ok(spawnposition) } - pub async fn write(&self, t: &mut TcpStream) -> tokio::io::Result<()> { + async fn write(&self, t: &mut TcpStream) -> tokio::io::Result<()> { for b in Into::>::into(self.clone()) { write_byte(t, b).await?; } @@ -435,7 +445,7 @@ impl SpawnPosition { #[derive(Debug, Clone)] pub struct KeepAlivePing { - payload: MCVarInt, + pub payload: MCVarInt, } impl Into> for KeepAlivePing { fn into(self) -> Vec { @@ -453,16 +463,17 @@ impl TryFrom> for KeepAlivePing { Err("unimplemented") } } -impl KeepAlivePing { - pub fn new() -> Self { +#[async_trait::async_trait] +impl PacketCommon for KeepAlivePing { + fn new() -> Self { KeepAlivePing { payload: 0.into() } } - pub async fn read(t: &mut TcpStream) -> tokio::io::Result { + async fn read(t: &mut TcpStream) -> tokio::io::Result { let mut keepalive = KeepAlivePing::new(); keepalive.payload = MCVarInt::read(t).await?; Ok(keepalive) } - pub async fn write(&self, t: &mut TcpStream) -> tokio::io::Result<()> { + async fn write(&self, t: &mut TcpStream) -> tokio::io::Result<()> { for b in Into::>::into(self.clone()) { write_byte(t, b).await?; } @@ -490,20 +501,21 @@ impl TryFrom> for Disconnect { Err("unimplemented") } } -impl Disconnect { - pub fn new() -> Self { +#[async_trait::async_trait] +impl PacketCommon for Disconnect { + fn new() -> Self { Disconnect { reason: MCChat { text: "Disconnected".into(), }, } } - pub async fn read(t: &mut TcpStream) -> tokio::io::Result { + async fn read(t: &mut TcpStream) -> tokio::io::Result { let mut keepalive = Disconnect::new(); keepalive.reason = MCChat::read(t).await?; Ok(keepalive) } - pub async fn write(&self, t: &mut TcpStream) -> tokio::io::Result<()> { + async fn write(&self, t: &mut TcpStream) -> tokio::io::Result<()> { for b in Into::>::into(self.clone()) { write_byte(t, b).await?; } @@ -533,20 +545,21 @@ impl TryFrom> for ClientboundChatMessage { Err("unimplemented") } } -impl ClientboundChatMessage { - pub fn new() -> Self { +#[async_trait::async_trait] +impl PacketCommon for ClientboundChatMessage { + fn new() -> Self { ClientboundChatMessage { text: MCChat { text: "".into() }, position: 0.into(), } } - pub async fn read(t: &mut TcpStream) -> tokio::io::Result { + async fn read(t: &mut TcpStream) -> tokio::io::Result { let mut clientboundchatmessage = ClientboundChatMessage::new(); clientboundchatmessage.text = MCChat::read(t).await?; clientboundchatmessage.position = MCByte::read(t).await?; Ok(clientboundchatmessage) } - pub async fn write(&self, t: &mut TcpStream) -> tokio::io::Result<()> { + async fn write(&self, t: &mut TcpStream) -> tokio::io::Result<()> { for b in Into::>::into(self.clone()) { write_byte(t, b).await?; } diff --git a/src/server/packets/mod.rs b/src/server/packets/mod.rs index 86dfd1f..f104f78 100644 --- a/src/server/packets/mod.rs +++ b/src/server/packets/mod.rs @@ -5,6 +5,7 @@ pub mod serverbound; use crate::mctypes::MCVarInt; pub use clientbound::*; +use core::convert::TryFrom; pub use serverbound::*; use tokio::net::TcpStream; @@ -14,3 +15,82 @@ pub async fn read_packet_header(t: &mut TcpStream) -> tokio::io::Result<(MCVarIn let id = MCVarInt::read(t).await?; Ok((length, id)) } + +/// A way to generically encode a packet. +macro_rules! register_packets { + ($($name:ident),*) => { + #[derive(Debug, Clone)] + pub enum Packet { + $($name($name),)* + Null, + } + impl Packet { + pub fn new() -> Packet { + Packet::Null + } + pub async fn write(&self, t: &mut TcpStream) -> tokio::io::Result<()> { + match self { + $( + Packet::$name(p) => p.write(t).await, + )* + Packet::Null => Ok(()) + } + } + } + $( + impl $name { + pub fn into_packet(&self) -> Packet { + Packet::$name(self.clone()) + } + } + impl Into for $name { + fn into(self) -> Packet { + Packet::$name(self.clone()) + } + } + impl TryFrom for $name { + type Error = &'static str; + fn try_from(p: Packet) -> Result { + match p { + Packet::$name(i) => Ok(i), + _ => Err("wrong kind"), + } + } + } + )* + }; +} + +// Register all the packets. +register_packets!( + // Clientbound. + StatusResponse, + StatusPong, + LoginSuccess, + LoginDisconnect, + JoinGame, + HeldItemChange, + EntityStatus, + PlayerPositionAndLook, + SpawnPosition, + KeepAlivePing, + Disconnect, + ClientboundChatMessage, + // Serverbound. + Handshake, + StatusRequest, + StatusPing, + LoginStart, + ClientSettings, + KeepAlivePong +); + +#[async_trait::async_trait] +pub trait PacketCommon +where + Self: Sized, +{ + fn new() -> Self; + async fn read(t: &'_ mut TcpStream) -> tokio::io::Result; + async fn write(&self, t: &'_ mut TcpStream) -> tokio::io::Result<()>; +} diff --git a/src/server/packets/serverbound.rs b/src/server/packets/serverbound.rs index 7e237bc..f34907f 100644 --- a/src/server/packets/serverbound.rs +++ b/src/server/packets/serverbound.rs @@ -1,3 +1,4 @@ +use super::PacketCommon; use crate::mctypes::*; use std::convert::{Into, TryFrom}; use tokio::net::TcpStream; @@ -29,8 +30,9 @@ impl TryFrom> for Handshake { Err("unimplemented") } } -impl Handshake { - pub fn new() -> Self { +#[async_trait::async_trait] +impl PacketCommon for Handshake { + fn new() -> Self { Handshake { protocol_version: 0.into(), server_address: "".into(), @@ -38,7 +40,7 @@ impl Handshake { next_state: 0.into(), } } - pub async fn read(t: &mut TcpStream) -> tokio::io::Result { + async fn read(t: &mut TcpStream) -> tokio::io::Result { let mut handshake = Handshake::new(); handshake.protocol_version = MCVarInt::read(t).await?; handshake.server_address = MCString::read(t).await?; @@ -46,7 +48,7 @@ impl Handshake { handshake.next_state = MCVarInt::read(t).await?; Ok(handshake) } - pub async fn write(&self, t: &mut TcpStream) -> tokio::io::Result<()> { + async fn write(&self, t: &mut TcpStream) -> tokio::io::Result<()> { for b in Into::>::into(self.clone()) { write_byte(t, b).await?; } @@ -71,15 +73,16 @@ impl TryFrom> for StatusRequest { Err("unimplemented") } } -impl StatusRequest { - pub fn new() -> Self { +#[async_trait::async_trait] +impl PacketCommon for StatusRequest { + fn new() -> Self { StatusRequest {} } - pub async fn read(_t: &mut TcpStream) -> tokio::io::Result { + async fn read(_t: &mut TcpStream) -> tokio::io::Result { let statusrequest = StatusRequest::new(); Ok(statusrequest) } - pub async fn write(&self, t: &mut TcpStream) -> tokio::io::Result<()> { + async fn write(&self, t: &mut TcpStream) -> tokio::io::Result<()> { for b in Into::>::into(self.clone()) { write_byte(t, b).await?; } @@ -107,16 +110,17 @@ impl TryFrom> for StatusPing { Err("unimplemented") } } -impl StatusPing { - pub fn new() -> Self { +#[async_trait::async_trait] +impl PacketCommon for StatusPing { + fn new() -> Self { StatusPing { payload: 0.into() } } - pub async fn read(t: &mut TcpStream) -> tokio::io::Result { + async fn read(t: &mut TcpStream) -> tokio::io::Result { let mut statusping = StatusPing::new(); statusping.payload = MCLong::read(t).await?; Ok(statusping) } - pub async fn write(&self, t: &mut TcpStream) -> tokio::io::Result<()> { + async fn write(&self, t: &mut TcpStream) -> tokio::io::Result<()> { for b in Into::>::into(self.clone()) { write_byte(t, b).await?; } @@ -144,18 +148,19 @@ impl TryFrom> for LoginStart { Err("unimplemented") } } -impl LoginStart { - pub fn new() -> Self { +#[async_trait::async_trait] +impl PacketCommon for LoginStart { + fn new() -> Self { LoginStart { player_name: "".into(), } } - pub async fn read(t: &mut TcpStream) -> tokio::io::Result { + async fn read(t: &mut TcpStream) -> tokio::io::Result { let mut loginstart = LoginStart::new(); loginstart.player_name = MCString::read(t).await?; Ok(loginstart) } - pub async fn write(&self, t: &mut TcpStream) -> tokio::io::Result<()> { + async fn write(&self, t: &mut TcpStream) -> tokio::io::Result<()> { for b in Into::>::into(self.clone()) { write_byte(t, b).await?; } @@ -199,8 +204,9 @@ impl TryFrom> for ClientSettings { Err("unimplemented") } } -impl ClientSettings { - pub fn new() -> Self { +#[async_trait::async_trait] +impl PacketCommon for ClientSettings { + fn new() -> Self { ClientSettings { locale: "en_US".into(), view_distance: 8.into(), // 8 chunks. @@ -209,7 +215,7 @@ impl ClientSettings { displayed_skin_parts: 0xff.into(), // Enable all parts. } } - pub async fn read(t: &mut TcpStream) -> tokio::io::Result { + async fn read(t: &mut TcpStream) -> tokio::io::Result { let mut clientsettings = ClientSettings::new(); clientsettings.locale = MCString::read(t).await?; clientsettings.view_distance = MCByte::read(t).await?; @@ -218,7 +224,7 @@ impl ClientSettings { clientsettings.displayed_skin_parts = MCUnsignedByte::read(t).await?; Ok(clientsettings) } - pub async fn write(&self, t: &mut TcpStream) -> tokio::io::Result<()> { + async fn write(&self, t: &mut TcpStream) -> tokio::io::Result<()> { for b in Into::>::into(self.clone()) { write_byte(t, b).await?; } @@ -228,7 +234,7 @@ impl ClientSettings { #[derive(Debug, Clone)] pub struct KeepAlivePong { - payload: MCVarInt, + pub payload: MCVarInt, } impl Into> for KeepAlivePong { fn into(self) -> Vec { @@ -246,16 +252,17 @@ impl TryFrom> for KeepAlivePong { Err("unimplemented") } } -impl KeepAlivePong { - pub fn new() -> Self { +#[async_trait::async_trait] +impl PacketCommon for KeepAlivePong { + fn new() -> Self { KeepAlivePong { payload: 0.into() } } - pub async fn read(t: &mut TcpStream) -> tokio::io::Result { + async fn read(t: &mut TcpStream) -> tokio::io::Result { let mut keepalive = KeepAlivePong::new(); keepalive.payload = MCVarInt::read(t).await?; Ok(keepalive) } - pub async fn write(&self, t: &mut TcpStream) -> tokio::io::Result<()> { + async fn write(&self, t: &mut TcpStream) -> tokio::io::Result<()> { for b in Into::>::into(self.clone()) { write_byte(t, b).await?; }