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]]
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",

View File

@ -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)

View File

@ -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<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`.
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?;
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>`.
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<()> {
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::<Handshake>().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::<StatusRequest>().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::<StatusPing>().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::<LoginStart>().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::<ClientSettings>().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<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<()> {
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::<KeepAlivePong>().await?;
debug!("{:?}", serverboundkeepalive);
self.last_keep_alive = Instant::now();
Ok(())

View File

@ -1,3 +1,4 @@
use super::PacketCommon;
use crate::mctypes::*;
use crate::CONFIG;
use std::convert::{Into, TryFrom};
@ -23,18 +24,19 @@ impl TryFrom<Vec<u8>> 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<Self> {
async fn read(t: &'_ mut TcpStream) -> tokio::io::Result<Self> {
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::<Vec<u8>>::into(self.clone()) {
write_byte(t, b).await?;
}
@ -62,16 +64,17 @@ impl TryFrom<Vec<u8>> 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<Self> {
async fn read(t: &mut TcpStream) -> tokio::io::Result<Self> {
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::<Vec<u8>>::into(self.clone()) {
write_byte(t, b).await?;
}
@ -101,20 +104,21 @@ impl TryFrom<Vec<u8>> 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<Self> {
async fn read(t: &mut TcpStream) -> tokio::io::Result<Self> {
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::<Vec<u8>>::into(self.clone()) {
write_byte(t, b).await?;
}
@ -142,22 +146,23 @@ impl TryFrom<Vec<u8>> 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<Self> {
async fn read(t: &mut TcpStream) -> tokio::io::Result<Self> {
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::<Vec<u8>>::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<Vec<u8>> for JoinGame {
fn into(self) -> Vec<u8> {
@ -197,8 +202,9 @@ impl TryFrom<Vec<u8>> 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<Self> {
async fn read(t: &mut TcpStream) -> tokio::io::Result<Self> {
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::<Vec<u8>>::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<Vec<u8>> for HeldItemChange {
fn into(self) -> Vec<u8> {
@ -248,18 +254,19 @@ impl TryFrom<Vec<u8>> 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<Self> {
async fn read(t: &mut TcpStream) -> tokio::io::Result<Self> {
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::<Vec<u8>>::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<Vec<u8>> for EntityStatus {
fn into(self) -> Vec<u8> {
@ -312,20 +319,21 @@ impl TryFrom<Vec<u8>> 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<Self> {
async fn read(t: &mut TcpStream) -> tokio::io::Result<Self> {
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::<Vec<u8>>::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<Vec<u8>> for PlayerPositionAndLook {
fn into(self) -> Vec<u8> {
@ -363,8 +371,9 @@ impl TryFrom<Vec<u8>> 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<Self> {
async fn read(t: &mut TcpStream) -> tokio::io::Result<Self> {
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::<Vec<u8>>::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<Vec<u8>> for SpawnPosition {
fn into(self) -> Vec<u8> {
@ -414,18 +423,19 @@ impl TryFrom<Vec<u8>> 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<Self> {
async fn read(t: &mut TcpStream) -> tokio::io::Result<Self> {
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::<Vec<u8>>::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<Vec<u8>> for KeepAlivePing {
fn into(self) -> Vec<u8> {
@ -453,16 +463,17 @@ impl TryFrom<Vec<u8>> 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<Self> {
async fn read(t: &mut TcpStream) -> tokio::io::Result<Self> {
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::<Vec<u8>>::into(self.clone()) {
write_byte(t, b).await?;
}
@ -490,20 +501,21 @@ impl TryFrom<Vec<u8>> 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<Self> {
async fn read(t: &mut TcpStream) -> tokio::io::Result<Self> {
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::<Vec<u8>>::into(self.clone()) {
write_byte(t, b).await?;
}
@ -533,20 +545,21 @@ impl TryFrom<Vec<u8>> 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<Self> {
async fn read(t: &mut TcpStream) -> tokio::io::Result<Self> {
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::<Vec<u8>>::into(self.clone()) {
write_byte(t, b).await?;
}

View File

@ -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<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 std::convert::{Into, TryFrom};
use tokio::net::TcpStream;
@ -29,8 +30,9 @@ impl TryFrom<Vec<u8>> 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<Self> {
async fn read(t: &mut TcpStream) -> tokio::io::Result<Self> {
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::<Vec<u8>>::into(self.clone()) {
write_byte(t, b).await?;
}
@ -71,15 +73,16 @@ impl TryFrom<Vec<u8>> 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<Self> {
async fn read(_t: &mut TcpStream) -> tokio::io::Result<Self> {
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::<Vec<u8>>::into(self.clone()) {
write_byte(t, b).await?;
}
@ -107,16 +110,17 @@ impl TryFrom<Vec<u8>> 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<Self> {
async fn read(t: &mut TcpStream) -> tokio::io::Result<Self> {
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::<Vec<u8>>::into(self.clone()) {
write_byte(t, b).await?;
}
@ -144,18 +148,19 @@ impl TryFrom<Vec<u8>> 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<Self> {
async fn read(t: &mut TcpStream) -> tokio::io::Result<Self> {
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::<Vec<u8>>::into(self.clone()) {
write_byte(t, b).await?;
}
@ -199,8 +204,9 @@ impl TryFrom<Vec<u8>> 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<Self> {
async fn read(t: &mut TcpStream) -> tokio::io::Result<Self> {
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::<Vec<u8>>::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<Vec<u8>> for KeepAlivePong {
fn into(self) -> Vec<u8> {
@ -246,16 +252,17 @@ impl TryFrom<Vec<u8>> 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<Self> {
async fn read(t: &mut TcpStream) -> tokio::io::Result<Self> {
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::<Vec<u8>>::into(self.clone()) {
write_byte(t, b).await?;
}