From 339f0cb300de509d46973f1d8417a6dcf581b699 Mon Sep 17 00:00:00 2001 From: zani Date: Mon, 13 Jan 2025 15:14:19 -0700 Subject: [PATCH] wip feat: velocity forwarding --- src/args.rs | 3 ++ src/net/player.rs | 25 +++++++++---- src/protocol/datatypes/string.rs | 57 +++++++++++++++++++++++++++++ src/protocol/packets/login/login.rs | 23 +++++++++++- src/state.rs | 2 + 5 files changed, 101 insertions(+), 9 deletions(-) diff --git a/src/args.rs b/src/args.rs index 4ee9653..62e8561 100644 --- a/src/args.rs +++ b/src/args.rs @@ -30,6 +30,9 @@ pub struct Args { /// The port to serve crawlspace on. Defaults to 25565 if not set. #[arg(short, long, default_value = "25565", env = "LIMBO_PORT")] pub port: u16, + // Whether or not to enable Velocity forwarding. + #[arg(short, long, default_value = "true", env = "LIMBO_VELOCITY_FORWARDING")] + pub velocity_forwarding: bool, /// The x coordinate of the spawnpoint. #[arg(short = 'x', long, default_value = "0", env = "LIMBO_SPAWN_X")] pub spawn_x: f64, diff --git a/src/net/player.rs b/src/net/player.rs index 8d8e96f..fd3eff8 100644 --- a/src/net/player.rs +++ b/src/net/player.rs @@ -38,7 +38,7 @@ use uuid::Uuid; use crate::{ protocol::{ - datatypes::{Bounded, Slot, VarInt}, + datatypes::{Bounded, Bytes, Rest, Slot, VarInt}, packets::{ login::*, play::{ @@ -230,8 +230,16 @@ impl SharedPlayer { let uuid = login.player_uuid; let username = login.name.0.to_owned(); - #[cfg(feature = "encryption")] - self.login_velocity(&username).await?; + if state.velocity_forwarding { + let understood = self.login_velocity().await?; + + if !understood { + warn!( + "Velocity forwarding is on, but client {} did not properly respond to our forwarding request. This will kick in future.", + self.0.id + ) + } + } let success = LoginSuccessC { uuid, @@ -262,17 +270,20 @@ impl SharedPlayer { Ok(()) } - #[cfg(feature = "encryption")] - async fn login_velocity(&self, _username: &str) -> Result<()> { + async fn login_velocity(&self) -> Result { let req = PluginRequestC { message_id: VarInt(0), channel: Bounded("velocity:player_info"), - data: Bounded(Bytes(&[3])), + data: Rest(Bytes(&[3])), }; self.0.io.tx(&req).await?; - Ok(()) + let res = self.0.io.rx::().await?; + let res: PluginResponseS = res.decode()?; + + // todo: replace with a profile maybe? + Ok(res.data.is_some() && req.message_id.0 == res.message_id.0) } async fn begin_play(&self) -> Result<()> { diff --git a/src/protocol/datatypes/string.rs b/src/protocol/datatypes/string.rs index 09873a7..f778c1f 100644 --- a/src/protocol/datatypes/string.rs +++ b/src/protocol/datatypes/string.rs @@ -106,3 +106,60 @@ impl<'a, const BOUND: usize> Decode<'a> for Bounded, BOUND> { Ok(Bounded(content)) } } + +#[derive(Debug)] +pub struct Rest(pub T); + +impl<'a, const BOUND: usize> Decode<'a> for Rest<&'a str, BOUND> { + fn decode(r: &mut &'a [u8]) -> Result { + let (content, rest) = r.split_at(r.len()); + let content = std::str::from_utf8(content)?; + let utf16_len = content.encode_utf16().count(); + + ensure!( + utf16_len <= BOUND, + "utf-16 encoded string exceeds {BOUND} chars (is {utf16_len})" + ); + + *r = rest; + + Ok(Rest(content)) + } +} + +impl<'a, const BOUND: usize> Encode for Rest<&'a str, BOUND> { + fn encode(&self, mut w: impl std::io::Write) -> Result<()> { + let len = self.0.encode_utf16().count(); + + ensure!(len < BOUND, "length of string {len} exceeds bound {BOUND}"); + + Ok(w.write_all(self.0.as_bytes())?) + } +} + +impl<'a, const BOUND: usize> Encode for Rest, BOUND> { + fn encode(&self, mut w: impl std::io::Write) -> Result<()> { + let len = self.0.0.len(); + + ensure!(len < BOUND, "length of bytes {len} exceeds bound {BOUND}"); + + self.0.encode(&mut w) + } +} + +impl<'a, const BOUND: usize> Decode<'a> for Rest, BOUND> { + fn decode(r: &mut &'a [u8]) -> Result { + let (mut content, rest) = r.split_at(r.len()); + let content = Bytes::decode(&mut content)?; + let len = content.0.len(); + + ensure!( + len <= BOUND, + "raw byte length exceeds {BOUND} chars (is {len})" + ); + + *r = rest; + + Ok(Rest(content)) + } +} \ No newline at end of file diff --git a/src/protocol/packets/login/login.rs b/src/protocol/packets/login/login.rs index 02ef277..3ae3ba7 100644 --- a/src/protocol/packets/login/login.rs +++ b/src/protocol/packets/login/login.rs @@ -21,7 +21,7 @@ use color_eyre::eyre::Result; use uuid::Uuid; use crate::protocol::{ - datatypes::{Bounded, Bytes, VarInt}, + datatypes::{Bounded, Bytes, Rest, VarInt}, Decode, Encode, Packet, Property, }; @@ -74,7 +74,7 @@ impl<'a> Encode for LoginSuccessC<'a> { pub struct PluginRequestC<'a> { pub message_id: VarInt, pub channel: Bounded<&'a str, 32767>, - pub data: Bounded, 1048576>, + pub data: Rest, 1048576>, } impl Packet for PluginRequestC<'_> { @@ -91,6 +91,25 @@ impl<'a> Encode for PluginRequestC<'a> { } } +#[derive(Debug)] +pub struct PluginResponseS<'a> { + pub message_id: VarInt, + pub data: Option, 1048576>> +} + +impl Packet for PluginResponseS<'_> { + const ID: i32 = 0x02; +} + +impl<'a> Decode<'a> for PluginResponseS<'a> { + fn decode(r: &mut &'a [u8]) -> Result { + Ok(Self { + message_id: VarInt::decode(r)?, + data: if bool::decode(r)? { Some(Rest::, 1048576>::decode(r)?) } else { None } + }) + } +} + #[derive(Debug)] pub struct LoginAckS; diff --git a/src/state.rs b/src/state.rs index 46abb4e..848946f 100644 --- a/src/state.rs +++ b/src/state.rs @@ -38,6 +38,7 @@ pub struct State { pub version_number: i32, pub addr: String, pub port: u16, + pub velocity_forwarding: bool, pub registry_cache: RegistryCache, @@ -74,6 +75,7 @@ impl State { version_number: version_number.to_owned(), addr: args.addr, port: args.port, + velocity_forwarding: args.velocity_forwarding, registry_cache: RegistryCache::from(&*ALL_REGISTRIES), -- 2.51.2