diff --git a/src/entity/player.rs b/src/entity/player.rs index a2b15f2..29852f0 100644 --- a/src/entity/player.rs +++ b/src/entity/player.rs @@ -1,5 +1,5 @@ -use crate::world::location::Location; use crate::server::NetworkClient; +use crate::world::location::Location; pub struct Player { position: Location, diff --git a/src/server/mod.rs b/src/server/mod.rs index e3f923c..c9fbd2a 100644 --- a/src/server/mod.rs +++ b/src/server/mod.rs @@ -1,13 +1,13 @@ /// Definitions for all the packets in the Minecraft protocol. pub mod packets; +use crate::entity::player::Player; use crate::{mctypes::*, CONFIG, FAVICON}; use log::{debug, info}; use packets::*; use serde_json::json; use std::sync::mpsc::{self, Receiver, TryRecvError}; use tokio::net::{TcpListener, TcpStream, ToSocketAddrs}; -use crate::entity::player::Player; /// The struct containing all the data and running all the updates. pub struct Server { @@ -29,7 +29,7 @@ impl Server { .await .expect("Network receiver disconnected"); tx.send(NetworkClient::new(stream, id as u128)) - .expect("Network receiver disconnected"); + .expect("Network receiver disconnected"); id += 1; } }); @@ -202,24 +202,35 @@ impl NetworkClient { self.username = Some(loginsuccess.username.clone().into()); self.state = NetworkClientState::Play; let joingame = JoinGame::new(); - /// TODO: Fill out `joingame` with actual information. + // TODO: Fill out `joingame` with actual information. joingame.write(&mut self.stream).await.unwrap(); debug!("{:?}", joingame); - let (packet_length, packet_id) = + let (_packet_length, _packet_id) = read_packet_header(&mut self.stream).await.unwrap(); let clientsettings = ClientSettings::read(&mut self.stream).await.unwrap(); + // TODO: Actualy use client settings. debug!("{:?}", clientsettings); - // TODO: S->C Held Item Change - // TODO: S->C Declare Recipes - // TODO: S->C Tags - // TODO: S->C Entity Status - // TODO: S->C Declare Commands - // TODO: S->C Unlock Recipes + + // All good up to here. + + let helditemchange = HeldItemChange::new(); + // TODO: Retrieve selected slot from storage. + helditemchange.write(&mut self.stream).await.unwrap(); + debug!("{:?}", helditemchange); + // TODO: S->C Declare Recipes (1.16?) + // TODO: S->C Tags (1.16?) + // TODO: S->C Entity Status (optional?) + // TODO: S->C Declare Commands (1.16?) + // TODO: S->C Unlock Recipes (1.16?) // TODO: S->C Player Position and Look - // TODO: S->C Player Info (Add Player action) - // TODO: S->C Player Info (Update latency action) - // TODO: S->C Update View Position - // TODO: S->C Update Light + let playerpositionandlook = PlayerPositionAndLook::new(); + // TODO: Retrieve player position from storage. + // playerpositionandlook.write(&mut self.stream).await.unwrap(); + debug!("{:?}", playerpositionandlook); + // 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?) + // TODO: S->C Update Light (1.16?) // TODO: S->C Chunk Data // TODO: S->C World Border // TODO: S->C Spawn Position diff --git a/src/server/packets/clientbound.rs b/src/server/packets/clientbound.rs index daab897..1c3eec8 100644 --- a/src/server/packets/clientbound.rs +++ b/src/server/packets/clientbound.rs @@ -167,12 +167,12 @@ impl LoginDisconnect { #[derive(Debug, Clone)] pub struct JoinGame { - entity_id: MCInt, // The player's Entity ID (EID) + 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 + 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 + 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. } impl Into> for JoinGame { @@ -201,7 +201,7 @@ impl JoinGame { pub fn new() -> Self { JoinGame { entity_id: 0.into(), - gamemode: 1.into(), // Default to creative mode. + gamemode: 1.into(), // Default to creative mode. dimension: 0.into(), // Default to overworld. difficulty: 2.into(), max_players: (CONFIG.max_players as u8).into(), @@ -227,3 +227,167 @@ impl JoinGame { Ok(()) } } + +#[derive(Debug, Clone)] +pub struct HeldItemChange { + selected_slot: MCByte, +} +impl Into> for HeldItemChange { + fn into(self) -> Vec { + let mut out = vec![]; + let mut temp: Vec = MCVarInt::from(0x09).into(); // 0x09 Held Item Change. + temp.extend_from_slice(&Into::>::into(self.selected_slot)); + out.extend_from_slice(&Into::>::into(MCVarInt::from(temp.len() as i32))); + out.extend_from_slice(&temp); + out + } +} +impl TryFrom> for HeldItemChange { + type Error = &'static str; + fn try_from(_bytes: Vec) -> Result { + Err("unimplemented") + } +} +impl HeldItemChange { + pub fn new() -> Self { + HeldItemChange { + selected_slot: 0.into(), + } + } + pub 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<()> { + for b in Into::>::into(self.clone()) { + write_byte(t, b).await?; + } + Ok(()) + } +} + +#[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 +} +impl Into> for EntityStatus { + fn into(self) -> Vec { + let mut out = vec![]; + let mut temp: Vec = MCVarInt::from(0x1a).into(); // 0x1a Entity Status. + temp.extend_from_slice(&Into::>::into(self.entity_id)); + temp.extend_from_slice(&Into::>::into(self.entity_status)); + out.extend_from_slice(&Into::>::into(MCVarInt::from(temp.len() as i32))); + out.extend_from_slice(&temp); + out + } +} +impl TryFrom> for EntityStatus { + type Error = &'static str; + fn try_from(_bytes: Vec) -> Result { + Err("unimplemented") + } +} +impl EntityStatus { + pub fn new() -> Self { + EntityStatus { + entity_id: 0.into(), + entity_status: 0.into(), + } + } + pub 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<()> { + for b in Into::>::into(self.clone()) { + write_byte(t, b).await?; + } + Ok(()) + } +} + +#[derive(Debug, Clone)] +pub struct PlayerPositionAndLook { + x: MCDouble, + y: MCDouble, + z: MCDouble, + yaw: MCFloat, + pitch: MCFloat, + flags: MCByte, +} +impl Into> for PlayerPositionAndLook { + fn into(self) -> Vec { + let mut out = vec![]; + let mut temp: Vec = MCVarInt::from(0x08).into(); // 0x08 Player Position and Look. + temp.extend_from_slice(&Into::>::into(self.x)); + temp.extend_from_slice(&Into::>::into(self.y)); + temp.extend_from_slice(&Into::>::into(self.z)); + temp.extend_from_slice(&Into::>::into(self.yaw)); + temp.extend_from_slice(&Into::>::into(self.pitch)); + temp.extend_from_slice(&Into::>::into(self.flags)); + out.extend_from_slice(&Into::>::into(MCVarInt::from(temp.len() as i32))); + out.extend_from_slice(&temp); + out + } +} +impl TryFrom> for PlayerPositionAndLook { + type Error = &'static str; + fn try_from(_bytes: Vec) -> Result { + Err("unimplemented") + } +} +impl PlayerPositionAndLook { + pub fn new() -> Self { + PlayerPositionAndLook { + x: 0.0.into(), + y: 0.0.into(), + z: 0.0.into(), + yaw: 0.0.into(), + pitch: 0.0.into(), + flags: 0x00.into(), + } + } + pub 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?; + playerpositionandlook.z = MCDouble::read(t).await?; + playerpositionandlook.yaw = MCFloat::read(t).await?; + playerpositionandlook.pitch = MCFloat::read(t).await?; + playerpositionandlook.flags = MCByte::read(t).await?; + Ok(playerpositionandlook) + } + pub async fn write(&self, t: &mut TcpStream) -> tokio::io::Result<()> { + for b in Into::>::into(self.clone()) { + write_byte(t, b).await?; + } + Ok(()) + } +} diff --git a/src/server/packets/serverbound.rs b/src/server/packets/serverbound.rs index d6a5d5f..acfbfe6 100644 --- a/src/server/packets/serverbound.rs +++ b/src/server/packets/serverbound.rs @@ -170,14 +170,14 @@ pub struct ClientSettings { pub chat_mode: MCVarInt, // 0: enabled, 1: commands only, 2: hidden. pub chat_colors: MCBoolean, pub displayed_skin_parts: MCUnsignedByte, // Bit mask - // Displayed skin parts flags: - // Bit 0 (0x01): Cape enabled - // Bit 1 (0x02): Jacket enabled - // Bit 2 (0x04): Left Sleeve enabled - // Bit 3 (0x08): Right Sleeve enabled - // Bit 4 (0x10): Left Pants Leg enabled - // Bit 5 (0x20): Right Pants Leg enabled - // Bit 6 (0x40): Hat enabled + // Displayed skin parts flags: + // Bit 0 (0x01): Cape enabled + // Bit 1 (0x02): Jacket enabled + // Bit 2 (0x04): Left Sleeve enabled + // Bit 3 (0x08): Right Sleeve enabled + // Bit 4 (0x10): Left Pants Leg enabled + // Bit 5 (0x20): Right Pants Leg enabled + // Bit 6 (0x40): Hat enabled } impl Into> for ClientSettings { fn into(self) -> Vec { @@ -204,7 +204,7 @@ impl ClientSettings { ClientSettings { locale: "en_US".into(), view_distance: 8.into(), // 8 chunks. - chat_mode: 0.into(), // All chat enabled. + chat_mode: 0.into(), // All chat enabled. chat_colors: true.into(), displayed_skin_parts: 0xff.into(), // Enable all parts. }