aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authormat <github@matdoes.dev>2021-12-10 00:54:58 -0600
committermat <github@matdoes.dev>2021-12-10 00:54:58 -0600
commitbe762fc5d37ba48386996afb4c5ba0c94aaf5883 (patch)
treea7c96227c51e291e4e59eab7a66c6bc223973e06
parentf64750afdd9b18379f706df66c32806b6d21bbe8 (diff)
downloadazalea-drasl-be762fc5d37ba48386996afb4c5ba0c94aaf5883.tar.xz
rust is driving me insane
-rw-r--r--Cargo.lock1
-rw-r--r--minecraft-chat/Cargo.toml1
-rw-r--r--minecraft-chat/src/lib.rs6
-rw-r--r--minecraft-chat/src/style.rs50
-rw-r--r--minecraft-protocol/src/connection.rs4
-rw-r--r--minecraft-protocol/src/mc_buf.rs4
-rw-r--r--minecraft-protocol/src/packets/handshake/client_intention_packet.rs13
-rw-r--r--minecraft-protocol/src/packets/mod.rs93
-rw-r--r--minecraft-protocol/src/packets/status/clientbound_status_response_packet.rs19
-rw-r--r--minecraft-protocol/src/packets/status/serverbound_status_request_packet.rs13
-rw-r--r--minecraft-protocol/src/server_status_pinger.rs25
11 files changed, 164 insertions, 65 deletions
diff --git a/Cargo.lock b/Cargo.lock
index 0c25e106..08d718a1 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -288,6 +288,7 @@ checksum = "308cc39be01b73d0d18f82a0e7b2a3df85245f84af96fdddc5d202d27e47b86a"
name = "minecraft-chat"
version = "0.1.0"
dependencies = [
+ "lazy_static",
"serde_json",
]
diff --git a/minecraft-chat/Cargo.toml b/minecraft-chat/Cargo.toml
index 2578a535..81f676af 100644
--- a/minecraft-chat/Cargo.toml
+++ b/minecraft-chat/Cargo.toml
@@ -6,4 +6,5 @@ version = "0.1.0"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
+lazy_static = "1.4.0"
serde_json = "^1.0.72"
diff --git a/minecraft-chat/src/lib.rs b/minecraft-chat/src/lib.rs
index 2fbad937..b7035e13 100644
--- a/minecraft-chat/src/lib.rs
+++ b/minecraft-chat/src/lib.rs
@@ -1,4 +1,8 @@
-//! Things for working with Minecraft chat messages, inspired by the Minecraft source code and prismarine-chat.
+//! Things for working with Minecraft chat messages.
+//! This was inspired by Minecraft and prismarine-chat.
+
+#[macro_use]
+extern crate lazy_static;
pub mod base_component;
pub mod component;
diff --git a/minecraft-chat/src/style.rs b/minecraft-chat/src/style.rs
index c9d419a2..5d5cc8a5 100644
--- a/minecraft-chat/src/style.rs
+++ b/minecraft-chat/src/style.rs
@@ -9,8 +9,26 @@ pub struct TextColor {
}
impl TextColor {
- // hopefully rust/llvm optimizes this so it's just calculated once
- fn calculate_legacy_format_to_color() -> HashMap<&'static ChatFormatting<'static>, TextColor> {
+ pub fn parse(value: String) -> Result<TextColor, String> {
+ if value.starts_with("#") {
+ let n = value.chars().skip(1).collect::<String>();
+ let n = u32::from_str_radix(&n, 16).unwrap();
+ return Ok(TextColor::from_rgb(n));
+ }
+ let color = NAMED_COLORS.get(&value.to_ascii_uppercase());
+ if color.is_some() {
+ return Ok(color.unwrap().clone());
+ }
+ Err(format!("Invalid color {}", value))
+ }
+
+ fn from_rgb(value: u32) -> TextColor {
+ TextColor { value, name: None }
+ }
+}
+
+lazy_static! {
+ static ref LEGACY_FORMAT_TO_COLOR: HashMap<&'static ChatFormatting<'static>, TextColor> = {
let mut legacy_format_to_color = HashMap::new();
for formatter in &ChatFormatting::FORMATTERS {
if !formatter.is_format && *formatter != ChatFormatting::RESET {
@@ -24,34 +42,14 @@ impl TextColor {
}
}
legacy_format_to_color
- }
-
- fn calculate_named_colors() -> HashMap<String, TextColor> {
- let legacy_format_to_color = Self::calculate_legacy_format_to_color();
+ };
+ static ref NAMED_COLORS: HashMap<String, TextColor> = {
let mut named_colors = HashMap::new();
- for color in legacy_format_to_color.values() {
+ for color in LEGACY_FORMAT_TO_COLOR.values() {
named_colors.insert(color.name.clone().unwrap(), color.clone());
}
named_colors
- }
-
- pub fn parse(value: String) -> Result<TextColor, String> {
- if value.starts_with("#") {
- let n = value.chars().skip(1).collect::<String>();
- let n = u32::from_str_radix(&n, 16).unwrap();
- return Ok(TextColor::from_rgb(n));
- }
- let named_colors = Self::calculate_named_colors();
- let color = named_colors.get(&value.to_ascii_uppercase());
- if color.is_some() {
- return Ok(color.unwrap().clone());
- }
- Err(format!("Invalid color {}", value))
- }
-
- fn from_rgb(value: u32) -> TextColor {
- TextColor { value, name: None }
- }
+ };
}
/// The weird S character Minecraft used to use for chat formatting
diff --git a/minecraft-protocol/src/connection.rs b/minecraft-protocol/src/connection.rs
index e5784eb6..ba27cdad 100644
--- a/minecraft-protocol/src/connection.rs
+++ b/minecraft-protocol/src/connection.rs
@@ -35,7 +35,7 @@ impl Connection {
.expect("Error enabling tcp_nodelay");
Ok(Connection {
- state: ConnectionProtocol::Handshaking,
+ state: ConnectionProtocol::Handshake,
flow: PacketFlow::ClientToServer,
stream,
})
@@ -80,7 +80,7 @@ impl Connection {
// write the packet id
let mut id_and_data_buf = vec![];
- mc_buf::write_varint(&mut id_and_data_buf, packet.get_id() as i32);
+ mc_buf::write_varint(&mut id_and_data_buf, packet.id() as i32);
packet.write(&mut id_and_data_buf);
// write the packet data
diff --git a/minecraft-protocol/src/mc_buf.rs b/minecraft-protocol/src/mc_buf.rs
index 087e66a1..b83a9599 100644
--- a/minecraft-protocol/src/mc_buf.rs
+++ b/minecraft-protocol/src/mc_buf.rs
@@ -146,7 +146,9 @@ pub fn write_utf_with_len(buf: &mut Vec<u8>, string: &String, len: usize) {
write_bytes(buf, string.as_bytes());
}
-pub async fn read_utf<T: AsyncRead + std::marker::Unpin>(buf: &mut T) -> Result<String, String> {
+pub async fn read_utf<T: AsyncRead + std::marker::Unpin>(
+ buf: &mut BufReader<T>,
+) -> Result<String, String> {
read_utf_with_len(buf, MAX_STRING_LENGTH.into()).await
}
diff --git a/minecraft-protocol/src/packets/handshake/client_intention_packet.rs b/minecraft-protocol/src/packets/handshake/client_intention_packet.rs
index 7dd358eb..401dbbac 100644
--- a/minecraft-protocol/src/packets/handshake/client_intention_packet.rs
+++ b/minecraft-protocol/src/packets/handshake/client_intention_packet.rs
@@ -1,5 +1,8 @@
use std::hash::Hash;
+use async_trait::async_trait;
+use tokio::io::BufReader;
+
use crate::{
mc_buf,
packets::{ConnectionProtocol, Packet, PacketTrait},
@@ -14,9 +17,10 @@ pub struct ClientIntentionPacket<'a> {
pub intention: ConnectionProtocol,
}
+#[async_trait]
impl<'a> PacketTrait for ClientIntentionPacket<'a> {
fn get(&self) -> Packet {
- Packet::ClientIntentionPacket(self)
+ Packet::ClientIntentionPacket(*self)
}
fn write(&self, buf: &mut Vec<u8>) {
@@ -26,5 +30,10 @@ impl<'a> PacketTrait for ClientIntentionPacket<'a> {
mc_buf::write_varint(buf, self.intention.clone() as i32);
}
- fn parse<T: tokio::io::AsyncRead + std::marker::Unpin>(&self, buf: T) -> () {}
+ async fn read<T: tokio::io::AsyncRead + std::marker::Unpin + std::marker::Send>(
+ buf: &mut BufReader<T>,
+ ) -> Result<Packet<'_>, String> {
+ Err("ClientIntentionPacket::parse not implemented".to_string())
+ // Ok(ClientIntentionPacket {}.get())
+ }
}
diff --git a/minecraft-protocol/src/packets/mod.rs b/minecraft-protocol/src/packets/mod.rs
index 5fb34743..76f9128e 100644
--- a/minecraft-protocol/src/packets/mod.rs
+++ b/minecraft-protocol/src/packets/mod.rs
@@ -1,10 +1,12 @@
-mod game;
-mod handshake;
-mod login;
-mod status;
+pub mod game;
+pub mod handshake;
+pub mod login;
+pub mod status;
use async_trait::async_trait;
-use tokio::io::AsyncRead;
+use tokio::io::{AsyncRead, BufReader};
+
+use crate::connection::PacketFlow;
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub enum ConnectionProtocol {
@@ -16,24 +18,89 @@ pub enum ConnectionProtocol {
pub enum Packet<'a> {
// game
+
// handshake
- ClientIntentionPacket(&'a handshake::client_intention_packet::ClientIntentionPacket<'a>),
+ ClientIntentionPacket(handshake::client_intention_packet::ClientIntentionPacket<'a>),
+
// login
+
// status
ServerboundStatusRequestPacket(
- &'a status::serverbound_status_request_packet::ServerboundStatusRequestPacket,
+ status::serverbound_status_request_packet::ServerboundStatusRequestPacket,
),
- ClientboundStatusRequestPacket(
- &'a status::clientbound_status_response_packet::ClientboundStatusRequestPacket,
+ ClientboundStatusResponsePacket(
+ status::clientbound_status_response_packet::ClientboundStatusResponsePacket,
),
}
+// TODO: do all this with macros so it's less repetitive
+impl Packet<'_> {
+ fn get_inner_packet(&self) -> &dyn PacketTrait {
+ match self {
+ Packet::ClientIntentionPacket(packet) => packet,
+ Packet::ServerboundStatusRequestPacket(packet) => packet,
+ Packet::ClientboundStatusResponsePacket(packet) => packet,
+ }
+ }
+
+ pub fn id(&self) -> u32 {
+ match self {
+ Packet::ClientIntentionPacket(packet) => 0x00,
+ Packet::ServerboundStatusRequestPacket(packet) => 0x00,
+ Packet::ClientboundStatusResponsePacket(packet) => 0x00,
+ }
+ }
+
+ /// Read a packet by its id, ConnectionProtocol, and flow
+ pub async fn read<T: tokio::io::AsyncRead + std::marker::Unpin + std::marker::Send>(
+ id: u32,
+ protocol: ConnectionProtocol,
+ flow: PacketFlow,
+ buf: &mut BufReader<T>,
+ ) -> Result<Packet<'_>, String> {
+ match protocol {
+ ConnectionProtocol::Handshake => match id {
+ 0x00 => Ok(
+ handshake::client_intention_packet::ClientIntentionPacket::read(buf).await?,
+ ),
+ _ => Err(format!("Unknown packet id: {}", id)),
+ },
+ ConnectionProtocol::Game => Err("Game protocol not implemented yet".to_string()),
+ ConnectionProtocol::Status => match flow {
+ PacketFlow::ServerToClient => match id {
+ 0x00 => Ok(
+ status::clientbound_status_response_packet::ClientboundStatusResponsePacket
+ ::read(buf)
+ .await?,
+ ),
+ _ => Err(format!("Unknown packet id: {}", id)),
+ },
+ PacketFlow::ClientToServer => match id {
+ 0x00 => Ok(
+ status::serverbound_status_request_packet::ServerboundStatusRequestPacket
+ ::read(buf)
+ .await?,
+ ),
+ _ => Err(format!("Unknown packet id: {}", id)),
+ },
+ },
+ ConnectionProtocol::Login => Err("Login protocol not implemented yet".to_string()),
+ }
+ }
+
+ pub fn write(&self, buf: &mut Vec<u8>) {
+ self.get_inner_packet().write(buf);
+ }
+}
+
+#[async_trait]
pub trait PacketTrait {
/// Return a version of the packet that you can actually use for stuff
fn get(&self) -> Packet;
fn write(&self, buf: &mut Vec<u8>) -> ();
- fn parse<T: AsyncRead + std::marker::Unpin>(
- buf: &mut T,
- // is using a static lifetime here a good idea? idk
- ) -> Result<Packet<'_>, String>;
+ async fn read<T: AsyncRead + std::marker::Unpin + std::marker::Send>(
+ buf: &mut BufReader<T>,
+ ) -> Result<Packet<'_>, String>
+ where
+ Self: Sized;
}
diff --git a/minecraft-protocol/src/packets/status/clientbound_status_response_packet.rs b/minecraft-protocol/src/packets/status/clientbound_status_response_packet.rs
index ada86667..38747cc8 100644
--- a/minecraft-protocol/src/packets/status/clientbound_status_response_packet.rs
+++ b/minecraft-protocol/src/packets/status/clientbound_status_response_packet.rs
@@ -1,28 +1,31 @@
use async_trait::async_trait;
use std::hash::Hash;
+use tokio::io::BufReader;
use crate::{
mc_buf,
packets::{Packet, PacketTrait},
};
-#[derive(Hash)]
-pub struct ClientboundStatusRequestPacket {
+#[derive(Hash, Clone)]
+pub struct ClientboundStatusResponsePacket {
status: String,
}
-impl PacketTrait for ClientboundStatusRequestPacket {
+#[async_trait]
+impl PacketTrait for ClientboundStatusResponsePacket {
fn get(&self) -> Packet {
- Packet::ClientboundStatusRequestPacket(self)
+ Packet::ClientboundStatusResponsePacket(self.clone())
}
fn write(&self, _buf: &mut Vec<u8>) {}
- fn parse<T: tokio::io::AsyncRead + std::marker::Unpin>(
- buf: &mut T,
+ async fn read<T: tokio::io::AsyncRead + std::marker::Unpin + std::marker::Send>(
+ buf: &mut BufReader<T>,
) -> Result<Packet<'_>, String> {
- let status = mc_buf::read_utf(&mut buf).await?;
+ let status = mc_buf::read_utf(buf).await?;
// this.status = GsonHelper.fromJson(GSON, friendlyByteBuf.readUtf(32767), ServerStatus.class);
- Ok(ClientboundStatusRequestPacket { status }.get())
+ let packet = ClientboundStatusResponsePacket { status }.get();
+ Ok(packet)
}
}
diff --git a/minecraft-protocol/src/packets/status/serverbound_status_request_packet.rs b/minecraft-protocol/src/packets/status/serverbound_status_request_packet.rs
index f2e6fe9a..b3c87968 100644
--- a/minecraft-protocol/src/packets/status/serverbound_status_request_packet.rs
+++ b/minecraft-protocol/src/packets/status/serverbound_status_request_packet.rs
@@ -1,4 +1,6 @@
+use async_trait::async_trait;
use std::hash::Hash;
+use tokio::io::BufReader;
use crate::{
mc_buf,
@@ -8,11 +10,16 @@ use crate::{
#[derive(Hash)]
pub struct ServerboundStatusRequestPacket {}
-// implement "Packet" for "ClientIntentionPacket"
+#[async_trait]
impl PacketTrait for ServerboundStatusRequestPacket {
fn get(&self) -> Packet {
- Packet::ServerboundStatusRequestPacket(self)
+ Packet::ServerboundStatusRequestPacket(*self)
}
fn write(&self, _buf: &mut Vec<u8>) {}
- fn parse<T: tokio::io::AsyncRead + std::marker::Unpin>(&self, buf: T) -> () {}
+
+ async fn read<T: tokio::io::AsyncRead + std::marker::Unpin + std::marker::Send>(
+ buf: &mut BufReader<T>,
+ ) -> Result<Packet<'_>, String> {
+ Err("ServerboundStatusRequestPacket::read not implemented".to_string())
+ }
}
diff --git a/minecraft-protocol/src/server_status_pinger.rs b/minecraft-protocol/src/server_status_pinger.rs
index 342c4f44..1e523511 100644
--- a/minecraft-protocol/src/server_status_pinger.rs
+++ b/minecraft-protocol/src/server_status_pinger.rs
@@ -1,7 +1,10 @@
use crate::{
connection::Connection,
- mc_buf,
- packets::{ClientIntentionPacket, ConnectionProtocol, ServerboundStatusRequestPacket},
+ packets::{
+ handshake::client_intention_packet::ClientIntentionPacket,
+ status::serverbound_status_request_packet::ServerboundStatusRequestPacket,
+ ConnectionProtocol, PacketTrait,
+ },
resolver, ServerAddress,
};
@@ -14,17 +17,21 @@ pub async fn ping_server(address: &ServerAddress) -> Result<(), String> {
println!("writing intention packet {}", address.host);
// send the client intention packet and switch to the status state
- conn.send_packet(&ClientIntentionPacket {
- protocol_version: 757,
- hostname: &address.host,
- port: address.port,
- intention: ConnectionProtocol::Status,
- })
+ conn.send_packet(
+ ClientIntentionPacket {
+ protocol_version: 757,
+ hostname: &address.host,
+ port: address.port,
+ intention: ConnectionProtocol::Status,
+ }
+ .get(),
+ )
.await;
conn.switch_state(ConnectionProtocol::Status);
// send the empty status request packet
- conn.send_packet(&ServerboundStatusRequestPacket {}).await;
+ conn.send_packet(ServerboundStatusRequestPacket {}.get())
+ .await;
conn.read_packet().await.unwrap();