Generic way to send/get packets

This commit is contained in:
Garen Tyler 2021-03-19 18:33:14 -06:00
parent 3279aeae23
commit 17d953fc0c
7 changed files with 286 additions and 164 deletions

8
Cargo.lock generated
View File

@ -8,9 +8,9 @@ checksum = "a4c527152e37cf757a3f78aae5a06fbeefdb07ccc535c980a3208ee3060dd544"
[[package]] [[package]]
name = "async-trait" name = "async-trait"
version = "0.1.42" version = "0.1.48"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8d3a45e77e34375a7923b1e8febb049bb011f064714a8e17a1a616fef01da13d" checksum = "36ea56748e10732c49404c153638a15ec3d6211ec5ff35d9bb20e13b93576adf"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
@ -374,9 +374,9 @@ dependencies = [
[[package]] [[package]]
name = "syn" name = "syn"
version = "1.0.60" version = "1.0.64"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c700597eca8a5a762beb35753ef6b94df201c81cca676604f547495a0d7f0081" checksum = "3fd9d1e9976102a03c542daa2eff1b43f9d72306342f3f8b3ed5fb8908195d6f"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",

View File

@ -13,7 +13,7 @@ serde_json = "1.0.59"
toml = "0.5" toml = "0.5"
radix64 = "0.3.0" radix64 = "0.3.0"
tokio = { version = "1", features = ["full"] } tokio = { version = "1", features = ["full"] }
async-trait = "0.1.42" async-trait = "0.1.48"
lazy_static = "1.4.0" lazy_static = "1.4.0"
# colorful = "0.2.1" # colorful = "0.2.1"
# ozelot = "0.9.0" # Ozelot 0.9.0 supports protocol version 578 (1.15.2) # ozelot = "0.9.0" # Ozelot 0.9.0 supports protocol version 578 (1.15.2)

View File

@ -21,10 +21,25 @@ pub mod functions {
t.read_exact(&mut buffer).await?; t.read_exact(&mut buffer).await?;
Ok(buffer[0]) Ok(buffer[0])
} }
/// Read `l` bytes from the given `TcpStream`.
pub async fn read_bytes(t: &mut TcpStream, l: usize) -> tokio::io::Result<Vec<u8>> {
let mut buffer = vec![];
for _ in 0..l {
buffer.push(read_byte(t).await?);
}
Ok(buffer)
}
/// Write a single byte to the given `TcpStream`. /// Write a single byte to the given `TcpStream`.
pub async fn write_byte(t: &mut TcpStream, value: u8) -> tokio::io::Result<u8> { pub async fn write_byte(t: &mut TcpStream, value: u8) -> tokio::io::Result<()> {
t.write(&[value]).await?; 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<u8>`. /// Take `l` bytes from the given `Vec<u8>`.
pub fn get_bytes(v: Vec<u8>, l: usize) -> Box<[u8]> { pub fn get_bytes(v: Vec<u8>, l: usize) -> Box<[u8]> {

View File

@ -128,8 +128,7 @@ impl NetworkClient {
pub async fn update(&mut self, num_players: usize) -> tokio::io::Result<()> { pub async fn update(&mut self, num_players: usize) -> tokio::io::Result<()> {
match self.state { match self.state {
NetworkClientState::Handshake => { NetworkClientState::Handshake => {
let (_packet_length, _packet_id) = read_packet_header(&mut self.stream).await?; let handshake = self.get_packet::<Handshake>().await?;
let handshake = Handshake::read(&mut self.stream).await?;
// Minecraft versions 1.8 - 1.8.9 use protocol version 47. // Minecraft versions 1.8 - 1.8.9 use protocol version 47.
let compatible_versions = handshake.protocol_version == 47; let compatible_versions = handshake.protocol_version == 47;
let next_state = match handshake.next_state.into() { let next_state = match handshake.next_state.into() {
@ -144,15 +143,14 @@ impl NetworkClient {
logindisconnect.reason = MCChat { logindisconnect.reason = MCChat {
text: MCString::from("Incompatible client! Server is on 1.8.9"), 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; self.state = NetworkClientState::Disconnected;
} }
debug!("Got handshake: {:?}", handshake); debug!("{:?}", handshake);
} }
NetworkClientState::Status => { NetworkClientState::Status => {
let (_packet_length, _packet_id) = read_packet_header(&mut self.stream).await?; let statusrequest = self.get_packet::<StatusRequest>().await?;
let statusrequest = StatusRequest::read(&mut self.stream).await?; debug!("{:?}", statusrequest);
debug!("Got status request: {:?}", statusrequest);
let mut statusresponse = StatusResponse::new(); let mut statusresponse = StatusResponse::new();
statusresponse.json_response = json!({ statusresponse.json_response = json!({
"version": { "version": {
@ -176,20 +174,17 @@ impl NetworkClient {
}) })
.to_string() .to_string()
.into(); .into();
statusresponse.write(&mut self.stream).await?; self.send_packet(statusresponse).await?;
debug!("Sending status response: StatusResponse");
let (_packet_length, _packet_id) = read_packet_header(&mut self.stream).await?; let statusping = self.get_packet::<StatusPing>().await?;
let statusping = StatusPing::read(&mut self.stream).await?; debug!("{:?}", statusping);
debug!("Got status ping: {:?}", statusping);
let mut statuspong = StatusPong::new(); let mut statuspong = StatusPong::new();
statuspong.payload = statusping.payload; statuspong.payload = statusping.payload;
statuspong.write(&mut self.stream).await?; self.send_packet(statuspong).await?;
debug!("Sending status pong: {:?}", statuspong);
self.state = NetworkClientState::Disconnected; self.state = NetworkClientState::Disconnected;
} }
NetworkClientState::Login => { NetworkClientState::Login => {
let (_packet_length, _packet_id) = read_packet_header(&mut self.stream).await?; let loginstart = self.get_packet::<LoginStart>().await?;
let loginstart = LoginStart::read(&mut self.stream).await?;
debug!("{:?}", loginstart); debug!("{:?}", loginstart);
// Offline mode skips encryption and compression. // Offline mode skips encryption and compression.
// TODO: Encryption and compression // TODO: Encryption and compression
@ -198,26 +193,20 @@ impl NetworkClient {
// TODO: Get uuid and username from Mojang servers. // TODO: Get uuid and username from Mojang servers.
loginsuccess.uuid = "00000000-0000-3000-0000-000000000000".into(); loginsuccess.uuid = "00000000-0000-3000-0000-000000000000".into();
loginsuccess.username = loginstart.player_name; loginsuccess.username = loginstart.player_name;
loginsuccess.write(&mut self.stream).await?;
debug!("{:?}", loginsuccess);
self.uuid = Some(loginsuccess.uuid.clone().into()); self.uuid = Some(loginsuccess.uuid.clone().into());
self.username = Some(loginsuccess.username.clone().into()); self.username = Some(loginsuccess.username.clone().into());
self.send_packet(loginsuccess).await?;
self.state = NetworkClientState::Play; self.state = NetworkClientState::Play;
let joingame = JoinGame::new(); let joingame = JoinGame::new();
// TODO: Fill out `joingame` with actual information. // TODO: Fill out `joingame` with actual information.
joingame.write(&mut self.stream).await?; self.send_packet(joingame).await?;
debug!("{:?}", joingame);
let (_packet_length, _packet_id) = read_packet_header(&mut self.stream).await?; let clientsettings = self.get_packet::<ClientSettings>().await?;
let clientsettings = ClientSettings::read(&mut self.stream).await?; // TODO: Actually use client settings.
// TODO: Actualy use client settings.
debug!("{:?}", clientsettings); debug!("{:?}", clientsettings);
// All good up to here.
let helditemchange = HeldItemChange::new(); let helditemchange = HeldItemChange::new();
// TODO: Retrieve selected slot from storage. // TODO: Retrieve selected slot from storage.
helditemchange.write(&mut self.stream).await?; self.send_packet(helditemchange).await?;
debug!("{:?}", helditemchange);
// TODO: S->C Declare Recipes (1.16?) // TODO: S->C Declare Recipes (1.16?)
// TODO: S->C Tags (1.16?) // TODO: S->C Tags (1.16?)
// TODO: S->C Entity Status (optional?) // TODO: S->C Entity Status (optional?)
@ -226,8 +215,7 @@ impl NetworkClient {
// TODO: S->C Player Position and Look // TODO: S->C Player Position and Look
let playerpositionandlook = PlayerPositionAndLook::new(); let playerpositionandlook = PlayerPositionAndLook::new();
// TODO: Retrieve player position from storage. // TODO: Retrieve player position from storage.
playerpositionandlook.write(&mut self.stream).await?; self.send_packet(playerpositionandlook).await?;
debug!("{:?}", playerpositionandlook);
// TODO: S->C Player Info (Add Player action) (1.16?) // TODO: S->C Player Info (Add Player action) (1.16?)
// TODO: S->C Player Info (Update latency 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 View Position (1.16?)
@ -236,8 +224,7 @@ impl NetworkClient {
// TODO: S->C World Border // TODO: S->C World Border
// TODO: S->C Spawn Position // TODO: S->C Spawn Position
let spawnposition = SpawnPosition::new(); let spawnposition = SpawnPosition::new();
spawnposition.write(&mut self.stream).await?; self.send_packet(spawnposition).await?;
debug!("{:?}", spawnposition);
// Send initial keep alive. // Send initial keep alive.
self.send_chat_message("keep alive").await?; self.send_chat_message("keep alive").await?;
self.keep_alive().await?; self.keep_alive().await?;
@ -267,36 +254,56 @@ impl NetworkClient {
Ok(()) Ok(())
} }
/// Send a generic packet to the client.
pub async fn send_packet<P: Into<Packet> + core::fmt::Debug>(
&mut self,
packet: P,
) -> tokio::io::Result<()> {
debug!("{:?}", packet);
Into::<Packet>::into(packet).write(&mut self.stream).await
}
/// Read a generic packet from the network.
pub async fn get_packet<T: PacketCommon>(&mut self) -> tokio::io::Result<T> {
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<C: Into<MCChat>>(&mut self, message: C) -> tokio::io::Result<()> { async fn send_chat_message<C: Into<MCChat>>(&mut self, message: C) -> tokio::io::Result<()> {
let mut chatmessage = ClientboundChatMessage::new(); let mut chatmessage = ClientboundChatMessage::new();
chatmessage.text = message.into(); chatmessage.text = message.into();
chatmessage.write(&mut self.stream).await?; self.send_packet(chatmessage).await?;
debug!("{:?}", chatmessage);
Ok(()) 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<()> { 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(); let mut disconnect = Disconnect::new();
disconnect.reason.text = reason.unwrap_or("Disconnected").into(); disconnect.reason.text = reason.unwrap_or("Disconnected").into();
disconnect.write(&mut self.stream).await?; self.send_packet(disconnect).await?;
debug!("{:?}", disconnect);
// Give the client 10 seconds to disconnect before forcing it. // Give the client 10 seconds to disconnect before forcing it.
tokio::time::sleep(Duration::from_secs(10)).await; tokio::time::sleep(Duration::from_secs(10)).await;
self.force_disconnect();
Ok(()) 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. /// Send a keep alive packet to the client.
async fn keep_alive(&mut self) -> tokio::io::Result<()> { async fn keep_alive(&mut self) -> tokio::io::Result<()> {
// Keep alive ping to client. // Keep alive ping to client.
let clientboundkeepalive = KeepAlivePing::new(); let clientboundkeepalive = KeepAlivePing::new();
clientboundkeepalive.write(&mut self.stream).await?; self.send_packet(clientboundkeepalive).await?;
debug!("{:?}", clientboundkeepalive);
// Keep alive pong to server. // 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::<KeepAlivePong>().await?;
debug!("{:?}", serverboundkeepalive); debug!("{:?}", serverboundkeepalive);
self.last_keep_alive = Instant::now(); self.last_keep_alive = Instant::now();
Ok(()) Ok(())

View File

@ -1,3 +1,4 @@
use super::PacketCommon;
use crate::mctypes::*; use crate::mctypes::*;
use crate::CONFIG; use crate::CONFIG;
use std::convert::{Into, TryFrom}; use std::convert::{Into, TryFrom};
@ -23,18 +24,19 @@ impl TryFrom<Vec<u8>> for StatusResponse {
Err("unimplemented") Err("unimplemented")
} }
} }
impl StatusResponse { #[async_trait::async_trait]
pub fn new() -> Self { impl PacketCommon for StatusResponse {
fn new() -> Self {
StatusResponse { StatusResponse {
json_response: MCString::from(""), json_response: MCString::from(""),
} }
} }
pub async fn read(t: &'_ mut TcpStream) -> tokio::io::Result<Self> { async fn read(t: &'_ mut TcpStream) -> tokio::io::Result<Self> {
let mut statusresponse = StatusResponse::new(); let mut statusresponse = StatusResponse::new();
statusresponse.json_response = MCString::read(t).await?; statusresponse.json_response = MCString::read(t).await?;
Ok(statusresponse) 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::<Vec<u8>>::into(self.clone()) { for b in Into::<Vec<u8>>::into(self.clone()) {
write_byte(t, b).await?; write_byte(t, b).await?;
} }
@ -62,16 +64,17 @@ impl TryFrom<Vec<u8>> for StatusPong {
Err("unimplemented") Err("unimplemented")
} }
} }
impl StatusPong { #[async_trait::async_trait]
pub fn new() -> Self { impl PacketCommon for StatusPong {
fn new() -> Self {
StatusPong { payload: 0.into() } StatusPong { payload: 0.into() }
} }
pub async fn read(t: &mut TcpStream) -> tokio::io::Result<Self> { async fn read(t: &mut TcpStream) -> tokio::io::Result<Self> {
let mut statuspong = StatusPong::new(); let mut statuspong = StatusPong::new();
statuspong.payload = MCLong::read(t).await?; statuspong.payload = MCLong::read(t).await?;
Ok(statuspong) 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::<Vec<u8>>::into(self.clone()) { for b in Into::<Vec<u8>>::into(self.clone()) {
write_byte(t, b).await?; write_byte(t, b).await?;
} }
@ -101,20 +104,21 @@ impl TryFrom<Vec<u8>> for LoginSuccess {
Err("unimplemented") Err("unimplemented")
} }
} }
impl LoginSuccess { #[async_trait::async_trait]
pub fn new() -> Self { impl PacketCommon for LoginSuccess {
fn new() -> Self {
LoginSuccess { LoginSuccess {
uuid: MCString::from(""), uuid: MCString::from(""),
username: MCString::from(""), username: MCString::from(""),
} }
} }
pub async fn read(t: &mut TcpStream) -> tokio::io::Result<Self> { async fn read(t: &mut TcpStream) -> tokio::io::Result<Self> {
let mut loginsuccess = LoginSuccess::new(); let mut loginsuccess = LoginSuccess::new();
loginsuccess.uuid = MCString::read(t).await?; loginsuccess.uuid = MCString::read(t).await?;
loginsuccess.username = MCString::read(t).await?; loginsuccess.username = MCString::read(t).await?;
Ok(loginsuccess) 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::<Vec<u8>>::into(self.clone()) { for b in Into::<Vec<u8>>::into(self.clone()) {
write_byte(t, b).await?; write_byte(t, b).await?;
} }
@ -142,22 +146,23 @@ impl TryFrom<Vec<u8>> for LoginDisconnect {
Err("unimplemented") Err("unimplemented")
} }
} }
impl LoginDisconnect { #[async_trait::async_trait]
pub fn new() -> Self { impl PacketCommon for LoginDisconnect {
fn new() -> Self {
LoginDisconnect { LoginDisconnect {
reason: MCChat { reason: MCChat {
text: MCString::from(""), text: MCString::from(""),
}, },
} }
} }
pub async fn read(t: &mut TcpStream) -> tokio::io::Result<Self> { async fn read(t: &mut TcpStream) -> tokio::io::Result<Self> {
let mut logindisconnect = LoginDisconnect::new(); let mut logindisconnect = LoginDisconnect::new();
logindisconnect.reason = MCChat { logindisconnect.reason = MCChat {
text: MCString::read(t).await?, text: MCString::read(t).await?,
}; };
Ok(logindisconnect) 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::<Vec<u8>>::into(self.clone()) { for b in Into::<Vec<u8>>::into(self.clone()) {
write_byte(t, b).await?; write_byte(t, b).await?;
} }
@ -167,13 +172,13 @@ impl LoginDisconnect {
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub struct JoinGame { pub struct JoinGame {
entity_id: MCInt, // The player's Entity ID (EID) pub 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. pub 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 pub dimension: MCByte, // -1: Nether, 0: Overworld, 1: End
difficulty: MCUnsignedByte, // 0: Peaceful, 1: Easy, 2: Normal, 3: Hard pub difficulty: MCUnsignedByte, // 0: Peaceful, 1: Easy, 2: Normal, 3: Hard
max_players: MCUnsignedByte, // Used by the client to draw the player list pub max_players: MCUnsignedByte, // Used by the client to draw the player list
level_type: MCString, // default, flat, largeBiomes, amplified, default_1_1 pub 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 reduced_debug_info: MCBoolean, // If true, a Notchian client shows reduced information on the debug screen.
} }
impl Into<Vec<u8>> for JoinGame { impl Into<Vec<u8>> for JoinGame {
fn into(self) -> Vec<u8> { fn into(self) -> Vec<u8> {
@ -197,8 +202,9 @@ impl TryFrom<Vec<u8>> for JoinGame {
Err("unimplemented") Err("unimplemented")
} }
} }
impl JoinGame { #[async_trait::async_trait]
pub fn new() -> Self { impl PacketCommon for JoinGame {
fn new() -> Self {
JoinGame { JoinGame {
entity_id: 0.into(), entity_id: 0.into(),
gamemode: 1.into(), // Default to creative mode. gamemode: 1.into(), // Default to creative mode.
@ -209,7 +215,7 @@ impl JoinGame {
reduced_debug_info: false.into(), // The debug info should be useful. reduced_debug_info: false.into(), // The debug info should be useful.
} }
} }
pub async fn read(t: &mut TcpStream) -> tokio::io::Result<Self> { async fn read(t: &mut TcpStream) -> tokio::io::Result<Self> {
let mut joingame = JoinGame::new(); let mut joingame = JoinGame::new();
joingame.entity_id = MCInt::read(t).await?; joingame.entity_id = MCInt::read(t).await?;
joingame.gamemode = MCUnsignedByte::read(t).await?; joingame.gamemode = MCUnsignedByte::read(t).await?;
@ -220,7 +226,7 @@ impl JoinGame {
joingame.reduced_debug_info = MCBoolean::read(t).await?; joingame.reduced_debug_info = MCBoolean::read(t).await?;
Ok(joingame) 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::<Vec<u8>>::into(self.clone()) { for b in Into::<Vec<u8>>::into(self.clone()) {
write_byte(t, b).await?; write_byte(t, b).await?;
} }
@ -230,7 +236,7 @@ impl JoinGame {
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub struct HeldItemChange { pub struct HeldItemChange {
selected_slot: MCByte, pub selected_slot: MCByte,
} }
impl Into<Vec<u8>> for HeldItemChange { impl Into<Vec<u8>> for HeldItemChange {
fn into(self) -> Vec<u8> { fn into(self) -> Vec<u8> {
@ -248,18 +254,19 @@ impl TryFrom<Vec<u8>> for HeldItemChange {
Err("unimplemented") Err("unimplemented")
} }
} }
impl HeldItemChange { #[async_trait::async_trait]
pub fn new() -> Self { impl PacketCommon for HeldItemChange {
fn new() -> Self {
HeldItemChange { HeldItemChange {
selected_slot: 0.into(), selected_slot: 0.into(),
} }
} }
pub async fn read(t: &mut TcpStream) -> tokio::io::Result<Self> { async fn read(t: &mut TcpStream) -> tokio::io::Result<Self> {
let mut helditemchange = HeldItemChange::new(); let mut helditemchange = HeldItemChange::new();
helditemchange.selected_slot = MCByte::read(t).await?; helditemchange.selected_slot = MCByte::read(t).await?;
Ok(helditemchange) 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::<Vec<u8>>::into(self.clone()) { for b in Into::<Vec<u8>>::into(self.clone()) {
write_byte(t, b).await?; write_byte(t, b).await?;
} }
@ -269,31 +276,31 @@ impl HeldItemChange {
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub struct EntityStatus { pub struct EntityStatus {
entity_id: MCInt, pub entity_id: MCInt,
entity_status: MCByte, // See table below. pub entity_status: MCByte, // See table below.
// 1: Sent when resetting a mob spawn minecart's timer / Rabbit jump animation // 1: Sent when resetting a mob spawn minecart's timer / Rabbit jump animation
// 2: Living Entity hurt // 2: Living Entity hurt
// 3: Living Entity dead // 3: Living Entity dead
// 4: Iron Golem throwing up arms // 4: Iron Golem throwing up arms
// 6: Wolf/Ocelot/Horse taming — Spawn “heart” particles // 6: Wolf/Ocelot/Horse taming — Spawn “heart” particles
// 7: Wolf/Ocelot/Horse tamed — Spawn “smoke” particles // 7: Wolf/Ocelot/Horse tamed — Spawn “smoke” particles
// 8: Wolf shaking water — Trigger the shaking animation // 8: Wolf shaking water — Trigger the shaking animation
// 9: (of self) Eating accepted by server // 9: (of self) Eating accepted by server
// 10: Sheep eating grass // 10: Sheep eating grass
// 10: Play TNT ignite sound // 10: Play TNT ignite sound
// 11: Iron Golem handing over a rose // 11: Iron Golem handing over a rose
// 12: Villager mating — Spawn “heart” particles // 12: Villager mating — Spawn “heart” particles
// 13: Spawn particles indicating that a villager is angry and seeking revenge // 13: Spawn particles indicating that a villager is angry and seeking revenge
// 14: Spawn happy particles near a villager // 14: Spawn happy particles near a villager
// 15: Witch animation — Spawn “magic” particles // 15: Witch animation — Spawn “magic” particles
// 16: Play zombie converting into a villager sound // 16: Play zombie converting into a villager sound
// 17: Firework exploding // 17: Firework exploding
// 18: Animal in love (ready to mate) — Spawn “heart” particles // 18: Animal in love (ready to mate) — Spawn “heart” particles
// 19: Reset squid rotation // 19: Reset squid rotation
// 20: Spawn explosion particle — works for some living entities // 20: Spawn explosion particle — works for some living entities
// 21: Play guardian sound — works for only for guardians // 21: Play guardian sound — works for only for guardians
// 22: Enables reduced debug for players // 22: Enables reduced debug for players
// 23: Disables reduced debug for players // 23: Disables reduced debug for players
} }
impl Into<Vec<u8>> for EntityStatus { impl Into<Vec<u8>> for EntityStatus {
fn into(self) -> Vec<u8> { fn into(self) -> Vec<u8> {
@ -312,20 +319,21 @@ impl TryFrom<Vec<u8>> for EntityStatus {
Err("unimplemented") Err("unimplemented")
} }
} }
impl EntityStatus { #[async_trait::async_trait]
pub fn new() -> Self { impl PacketCommon for EntityStatus {
fn new() -> Self {
EntityStatus { EntityStatus {
entity_id: 0.into(), entity_id: 0.into(),
entity_status: 0.into(), entity_status: 0.into(),
} }
} }
pub async fn read(t: &mut TcpStream) -> tokio::io::Result<Self> { async fn read(t: &mut TcpStream) -> tokio::io::Result<Self> {
let mut entitystatus = EntityStatus::new(); let mut entitystatus = EntityStatus::new();
entitystatus.entity_id = MCInt::read(t).await?; entitystatus.entity_id = MCInt::read(t).await?;
entitystatus.entity_status = MCByte::read(t).await?; entitystatus.entity_status = MCByte::read(t).await?;
Ok(entitystatus) 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::<Vec<u8>>::into(self.clone()) { for b in Into::<Vec<u8>>::into(self.clone()) {
write_byte(t, b).await?; write_byte(t, b).await?;
} }
@ -335,12 +343,12 @@ impl EntityStatus {
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub struct PlayerPositionAndLook { pub struct PlayerPositionAndLook {
x: MCDouble, pub x: MCDouble,
y: MCDouble, pub y: MCDouble,
z: MCDouble, pub z: MCDouble,
yaw: MCFloat, pub yaw: MCFloat,
pitch: MCFloat, pub pitch: MCFloat,
flags: MCByte, pub flags: MCByte,
} }
impl Into<Vec<u8>> for PlayerPositionAndLook { impl Into<Vec<u8>> for PlayerPositionAndLook {
fn into(self) -> Vec<u8> { fn into(self) -> Vec<u8> {
@ -363,8 +371,9 @@ impl TryFrom<Vec<u8>> for PlayerPositionAndLook {
Err("unimplemented") Err("unimplemented")
} }
} }
impl PlayerPositionAndLook { #[async_trait::async_trait]
pub fn new() -> Self { impl PacketCommon for PlayerPositionAndLook {
fn new() -> Self {
PlayerPositionAndLook { PlayerPositionAndLook {
x: 0.0.into(), x: 0.0.into(),
y: 0.0.into(), y: 0.0.into(),
@ -374,7 +383,7 @@ impl PlayerPositionAndLook {
flags: 0x00.into(), flags: 0x00.into(),
} }
} }
pub async fn read(t: &mut TcpStream) -> tokio::io::Result<Self> { async fn read(t: &mut TcpStream) -> tokio::io::Result<Self> {
let mut playerpositionandlook = PlayerPositionAndLook::new(); let mut playerpositionandlook = PlayerPositionAndLook::new();
playerpositionandlook.x = MCDouble::read(t).await?; playerpositionandlook.x = MCDouble::read(t).await?;
playerpositionandlook.y = MCDouble::read(t).await?; playerpositionandlook.y = MCDouble::read(t).await?;
@ -384,7 +393,7 @@ impl PlayerPositionAndLook {
playerpositionandlook.flags = MCByte::read(t).await?; playerpositionandlook.flags = MCByte::read(t).await?;
Ok(playerpositionandlook) 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::<Vec<u8>>::into(self.clone()) { for b in Into::<Vec<u8>>::into(self.clone()) {
write_byte(t, b).await?; write_byte(t, b).await?;
} }
@ -395,7 +404,7 @@ impl PlayerPositionAndLook {
// TODO: Actually send the position. // TODO: Actually send the position.
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub struct SpawnPosition { pub struct SpawnPosition {
position: MCPosition, pub position: MCPosition,
} }
impl Into<Vec<u8>> for SpawnPosition { impl Into<Vec<u8>> for SpawnPosition {
fn into(self) -> Vec<u8> { fn into(self) -> Vec<u8> {
@ -414,18 +423,19 @@ impl TryFrom<Vec<u8>> for SpawnPosition {
Err("unimplemented") Err("unimplemented")
} }
} }
impl SpawnPosition { #[async_trait::async_trait]
pub fn new() -> Self { impl PacketCommon for SpawnPosition {
fn new() -> Self {
SpawnPosition { SpawnPosition {
position: MCPosition::new(), position: MCPosition::new(),
} }
} }
pub async fn read(t: &mut TcpStream) -> tokio::io::Result<Self> { async fn read(t: &mut TcpStream) -> tokio::io::Result<Self> {
let mut spawnposition = SpawnPosition::new(); let mut spawnposition = SpawnPosition::new();
spawnposition.position = MCPosition::read(t).await?; spawnposition.position = MCPosition::read(t).await?;
Ok(spawnposition) 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::<Vec<u8>>::into(self.clone()) { for b in Into::<Vec<u8>>::into(self.clone()) {
write_byte(t, b).await?; write_byte(t, b).await?;
} }
@ -435,7 +445,7 @@ impl SpawnPosition {
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub struct KeepAlivePing { pub struct KeepAlivePing {
payload: MCVarInt, pub payload: MCVarInt,
} }
impl Into<Vec<u8>> for KeepAlivePing { impl Into<Vec<u8>> for KeepAlivePing {
fn into(self) -> Vec<u8> { fn into(self) -> Vec<u8> {
@ -453,16 +463,17 @@ impl TryFrom<Vec<u8>> for KeepAlivePing {
Err("unimplemented") Err("unimplemented")
} }
} }
impl KeepAlivePing { #[async_trait::async_trait]
pub fn new() -> Self { impl PacketCommon for KeepAlivePing {
fn new() -> Self {
KeepAlivePing { payload: 0.into() } KeepAlivePing { payload: 0.into() }
} }
pub async fn read(t: &mut TcpStream) -> tokio::io::Result<Self> { async fn read(t: &mut TcpStream) -> tokio::io::Result<Self> {
let mut keepalive = KeepAlivePing::new(); let mut keepalive = KeepAlivePing::new();
keepalive.payload = MCVarInt::read(t).await?; keepalive.payload = MCVarInt::read(t).await?;
Ok(keepalive) 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::<Vec<u8>>::into(self.clone()) { for b in Into::<Vec<u8>>::into(self.clone()) {
write_byte(t, b).await?; write_byte(t, b).await?;
} }
@ -490,20 +501,21 @@ impl TryFrom<Vec<u8>> for Disconnect {
Err("unimplemented") Err("unimplemented")
} }
} }
impl Disconnect { #[async_trait::async_trait]
pub fn new() -> Self { impl PacketCommon for Disconnect {
fn new() -> Self {
Disconnect { Disconnect {
reason: MCChat { reason: MCChat {
text: "Disconnected".into(), text: "Disconnected".into(),
}, },
} }
} }
pub async fn read(t: &mut TcpStream) -> tokio::io::Result<Self> { async fn read(t: &mut TcpStream) -> tokio::io::Result<Self> {
let mut keepalive = Disconnect::new(); let mut keepalive = Disconnect::new();
keepalive.reason = MCChat::read(t).await?; keepalive.reason = MCChat::read(t).await?;
Ok(keepalive) 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::<Vec<u8>>::into(self.clone()) { for b in Into::<Vec<u8>>::into(self.clone()) {
write_byte(t, b).await?; write_byte(t, b).await?;
} }
@ -533,20 +545,21 @@ impl TryFrom<Vec<u8>> for ClientboundChatMessage {
Err("unimplemented") Err("unimplemented")
} }
} }
impl ClientboundChatMessage { #[async_trait::async_trait]
pub fn new() -> Self { impl PacketCommon for ClientboundChatMessage {
fn new() -> Self {
ClientboundChatMessage { ClientboundChatMessage {
text: MCChat { text: "".into() }, text: MCChat { text: "".into() },
position: 0.into(), position: 0.into(),
} }
} }
pub async fn read(t: &mut TcpStream) -> tokio::io::Result<Self> { async fn read(t: &mut TcpStream) -> tokio::io::Result<Self> {
let mut clientboundchatmessage = ClientboundChatMessage::new(); let mut clientboundchatmessage = ClientboundChatMessage::new();
clientboundchatmessage.text = MCChat::read(t).await?; clientboundchatmessage.text = MCChat::read(t).await?;
clientboundchatmessage.position = MCByte::read(t).await?; clientboundchatmessage.position = MCByte::read(t).await?;
Ok(clientboundchatmessage) 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::<Vec<u8>>::into(self.clone()) { for b in Into::<Vec<u8>>::into(self.clone()) {
write_byte(t, b).await?; write_byte(t, b).await?;
} }

View File

@ -5,6 +5,7 @@ pub mod serverbound;
use crate::mctypes::MCVarInt; use crate::mctypes::MCVarInt;
pub use clientbound::*; pub use clientbound::*;
use core::convert::TryFrom;
pub use serverbound::*; pub use serverbound::*;
use tokio::net::TcpStream; 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?; let id = MCVarInt::read(t).await?;
Ok((length, id)) 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<Packet> for $name {
fn into(self) -> Packet {
Packet::$name(self.clone())
}
}
impl TryFrom<Packet> for $name {
type Error = &'static str;
fn try_from(p: Packet) -> Result<Self, Self::Error> {
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<Self>;
async fn write(&self, t: &'_ mut TcpStream) -> tokio::io::Result<()>;
}

View File

@ -1,3 +1,4 @@
use super::PacketCommon;
use crate::mctypes::*; use crate::mctypes::*;
use std::convert::{Into, TryFrom}; use std::convert::{Into, TryFrom};
use tokio::net::TcpStream; use tokio::net::TcpStream;
@ -29,8 +30,9 @@ impl TryFrom<Vec<u8>> for Handshake {
Err("unimplemented") Err("unimplemented")
} }
} }
impl Handshake { #[async_trait::async_trait]
pub fn new() -> Self { impl PacketCommon for Handshake {
fn new() -> Self {
Handshake { Handshake {
protocol_version: 0.into(), protocol_version: 0.into(),
server_address: "".into(), server_address: "".into(),
@ -38,7 +40,7 @@ impl Handshake {
next_state: 0.into(), next_state: 0.into(),
} }
} }
pub async fn read(t: &mut TcpStream) -> tokio::io::Result<Self> { async fn read(t: &mut TcpStream) -> tokio::io::Result<Self> {
let mut handshake = Handshake::new(); let mut handshake = Handshake::new();
handshake.protocol_version = MCVarInt::read(t).await?; handshake.protocol_version = MCVarInt::read(t).await?;
handshake.server_address = MCString::read(t).await?; handshake.server_address = MCString::read(t).await?;
@ -46,7 +48,7 @@ impl Handshake {
handshake.next_state = MCVarInt::read(t).await?; handshake.next_state = MCVarInt::read(t).await?;
Ok(handshake) 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::<Vec<u8>>::into(self.clone()) { for b in Into::<Vec<u8>>::into(self.clone()) {
write_byte(t, b).await?; write_byte(t, b).await?;
} }
@ -71,15 +73,16 @@ impl TryFrom<Vec<u8>> for StatusRequest {
Err("unimplemented") Err("unimplemented")
} }
} }
impl StatusRequest { #[async_trait::async_trait]
pub fn new() -> Self { impl PacketCommon for StatusRequest {
fn new() -> Self {
StatusRequest {} StatusRequest {}
} }
pub async fn read(_t: &mut TcpStream) -> tokio::io::Result<Self> { async fn read(_t: &mut TcpStream) -> tokio::io::Result<Self> {
let statusrequest = StatusRequest::new(); let statusrequest = StatusRequest::new();
Ok(statusrequest) 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::<Vec<u8>>::into(self.clone()) { for b in Into::<Vec<u8>>::into(self.clone()) {
write_byte(t, b).await?; write_byte(t, b).await?;
} }
@ -107,16 +110,17 @@ impl TryFrom<Vec<u8>> for StatusPing {
Err("unimplemented") Err("unimplemented")
} }
} }
impl StatusPing { #[async_trait::async_trait]
pub fn new() -> Self { impl PacketCommon for StatusPing {
fn new() -> Self {
StatusPing { payload: 0.into() } StatusPing { payload: 0.into() }
} }
pub async fn read(t: &mut TcpStream) -> tokio::io::Result<Self> { async fn read(t: &mut TcpStream) -> tokio::io::Result<Self> {
let mut statusping = StatusPing::new(); let mut statusping = StatusPing::new();
statusping.payload = MCLong::read(t).await?; statusping.payload = MCLong::read(t).await?;
Ok(statusping) 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::<Vec<u8>>::into(self.clone()) { for b in Into::<Vec<u8>>::into(self.clone()) {
write_byte(t, b).await?; write_byte(t, b).await?;
} }
@ -144,18 +148,19 @@ impl TryFrom<Vec<u8>> for LoginStart {
Err("unimplemented") Err("unimplemented")
} }
} }
impl LoginStart { #[async_trait::async_trait]
pub fn new() -> Self { impl PacketCommon for LoginStart {
fn new() -> Self {
LoginStart { LoginStart {
player_name: "".into(), player_name: "".into(),
} }
} }
pub async fn read(t: &mut TcpStream) -> tokio::io::Result<Self> { async fn read(t: &mut TcpStream) -> tokio::io::Result<Self> {
let mut loginstart = LoginStart::new(); let mut loginstart = LoginStart::new();
loginstart.player_name = MCString::read(t).await?; loginstart.player_name = MCString::read(t).await?;
Ok(loginstart) 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::<Vec<u8>>::into(self.clone()) { for b in Into::<Vec<u8>>::into(self.clone()) {
write_byte(t, b).await?; write_byte(t, b).await?;
} }
@ -199,8 +204,9 @@ impl TryFrom<Vec<u8>> for ClientSettings {
Err("unimplemented") Err("unimplemented")
} }
} }
impl ClientSettings { #[async_trait::async_trait]
pub fn new() -> Self { impl PacketCommon for ClientSettings {
fn new() -> Self {
ClientSettings { ClientSettings {
locale: "en_US".into(), locale: "en_US".into(),
view_distance: 8.into(), // 8 chunks. view_distance: 8.into(), // 8 chunks.
@ -209,7 +215,7 @@ impl ClientSettings {
displayed_skin_parts: 0xff.into(), // Enable all parts. displayed_skin_parts: 0xff.into(), // Enable all parts.
} }
} }
pub async fn read(t: &mut TcpStream) -> tokio::io::Result<Self> { async fn read(t: &mut TcpStream) -> tokio::io::Result<Self> {
let mut clientsettings = ClientSettings::new(); let mut clientsettings = ClientSettings::new();
clientsettings.locale = MCString::read(t).await?; clientsettings.locale = MCString::read(t).await?;
clientsettings.view_distance = MCByte::read(t).await?; clientsettings.view_distance = MCByte::read(t).await?;
@ -218,7 +224,7 @@ impl ClientSettings {
clientsettings.displayed_skin_parts = MCUnsignedByte::read(t).await?; clientsettings.displayed_skin_parts = MCUnsignedByte::read(t).await?;
Ok(clientsettings) 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::<Vec<u8>>::into(self.clone()) { for b in Into::<Vec<u8>>::into(self.clone()) {
write_byte(t, b).await?; write_byte(t, b).await?;
} }
@ -228,7 +234,7 @@ impl ClientSettings {
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub struct KeepAlivePong { pub struct KeepAlivePong {
payload: MCVarInt, pub payload: MCVarInt,
} }
impl Into<Vec<u8>> for KeepAlivePong { impl Into<Vec<u8>> for KeepAlivePong {
fn into(self) -> Vec<u8> { fn into(self) -> Vec<u8> {
@ -246,16 +252,17 @@ impl TryFrom<Vec<u8>> for KeepAlivePong {
Err("unimplemented") Err("unimplemented")
} }
} }
impl KeepAlivePong { #[async_trait::async_trait]
pub fn new() -> Self { impl PacketCommon for KeepAlivePong {
fn new() -> Self {
KeepAlivePong { payload: 0.into() } KeepAlivePong { payload: 0.into() }
} }
pub async fn read(t: &mut TcpStream) -> tokio::io::Result<Self> { async fn read(t: &mut TcpStream) -> tokio::io::Result<Self> {
let mut keepalive = KeepAlivePong::new(); let mut keepalive = KeepAlivePong::new();
keepalive.payload = MCVarInt::read(t).await?; keepalive.payload = MCVarInt::read(t).await?;
Ok(keepalive) 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::<Vec<u8>>::into(self.clone()) { for b in Into::<Vec<u8>>::into(self.clone()) {
write_byte(t, b).await?; write_byte(t, b).await?;
} }