Generic way to send/get packets
This commit is contained in:
parent
3279aeae23
commit
17d953fc0c
8
Cargo.lock
generated
8
Cargo.lock
generated
@ -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",
|
||||
|
@ -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)
|
||||
|
@ -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]> {
|
||||
|
@ -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(())
|
||||
|
@ -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?;
|
||||
}
|
||||
|
@ -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<()>;
|
||||
}
|
||||
|
@ -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?;
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user