Skip to content

Commit

Permalink
Implement velocity modern forwarding support
Browse files Browse the repository at this point in the history
  • Loading branch information
StackDoubleFlow committed Aug 19, 2024
1 parent 43019f5 commit 9d12cef
Show file tree
Hide file tree
Showing 7 changed files with 169 additions and 16 deletions.
12 changes: 12 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions crates/core/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,8 @@ impls = "1"
bincode = "1.3"
once_cell = "1.14.0"
rustc-hash = "2.0"
hmac = "0.12"
sha2 = "0.10"
mchprs_save_data = { path = "../save_data" }
mchprs_blocks = { path = "../blocks" }
mchprs_world = { path = "../world" }
Expand Down
9 changes: 8 additions & 1 deletion crates/core/src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -73,5 +73,12 @@ gen_config! {
schemati: bool = false,
luckperms: Option<PermissionsConfig> = None,
block_in_hitbox: bool = true,
auto_redpiler: bool = true
auto_redpiler: bool = true,
velocity: Option<VelocityConfig> = None
}

#[derive(Serialize, Deserialize)]
pub struct VelocityConfig {
pub enabled: bool,
pub secret: String,
}
100 changes: 85 additions & 15 deletions crates/core/src/server.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,16 +6,18 @@ use crate::utils::HyphenatedUUID;
use crate::{permissions, utils};
use backtrace::Backtrace;
use bus::Bus;
use hmac::{Hmac, Mac};
use mchprs_network::packets::clientbound::{
CConfigurationPluginMessage, CDisconnectLogin, CFinishConfiguration, CGameEvent,
CGameEventType, CLogin, CLoginSuccess, CPlayerInfoActions, CPlayerInfoAddPlayer,
CPlayerInfoUpdate, CPlayerInfoUpdatePlayer, CPong, CRegistryBiome, CRegistryBiomeEffects,
CRegistryData, CRegistryDataCodec, CRegistryDimensionType, CResponse, CSetCompression,
CSetContainerContent, CSetHeldItem, CSynchronizePlayerPosition, ClientBoundPacket, UpdateTime,
CGameEventType, CLogin, CLoginPluginRequest, CLoginSuccess, CPlayerInfoActions,
CPlayerInfoAddPlayer, CPlayerInfoUpdate, CPlayerInfoUpdatePlayer, CPong, CRegistryBiome,
CRegistryBiomeEffects, CRegistryData, CRegistryDataCodec, CRegistryDimensionType, CResponse,
CSetCompression, CSetContainerContent, CSetHeldItem, CSynchronizePlayerPosition,
ClientBoundPacket, UpdateTime,
};
use mchprs_network::packets::serverbound::{
SAcknowledgeFinishConfiguration, SHandshake, SLoginAcknowledged, SLoginStart, SPing, SRequest,
ServerBoundPacketHandler,
SAcknowledgeFinishConfiguration, SHandshake, SLoginAcknowledged, SLoginPluginResponse,
SLoginStart, SPing, SRequest, ServerBoundPacketHandler, VelocityResponseData,
};
use mchprs_network::packets::{PacketEncoderExt, SlotData, COMPRESSION_THRESHOLD};
use mchprs_network::{NetworkServer, NetworkState, PlayerPacketSender};
Expand All @@ -24,7 +26,9 @@ use mchprs_utils::map;
use rustc_hash::FxHashMap;
use serde::{Deserialize, Serialize};
use serde_json::json;
use sha2::Sha256;
use std::fs::{self, File};
use std::io::Cursor;
use std::path::Path;
use std::sync::mpsc::{self, Receiver, Sender};
use std::time::{Duration, Instant};
Expand Down Expand Up @@ -419,10 +423,33 @@ impl MinecraftServer {
.unwrap();
}

fn handle_player_login(&mut self, client_idx: usize, login_start: SLoginStart) {
fn handle_player_login_start(&mut self, client_idx: usize, login_start: SLoginStart) {
let clients = &mut self.network.handshaking_clients;
let username = login_start.name;
clients[client_idx].username = Some(username.clone());

if let Some(velocity_config) = &CONFIG.velocity {
if velocity_config.enabled {
let message_id = rand::random();
clients[client_idx].forwarding_message_id = Some(message_id);
let plugin_message = CLoginPluginRequest {
channel: "velocity:player_info".to_string(),
message_id,
data: vec![1], // MODERN_DEFAULT
}
.encode();
clients[client_idx].send_packet(&plugin_message);
return;
}
}

self.complete_player_login(client_idx);
}

fn complete_player_login(&mut self, client_idx: usize) {
let clients = &mut self.network.handshaking_clients;
let username = clients[client_idx].username.clone().unwrap();

let set_compression = CSetCompression {
threshold: COMPRESSION_THRESHOLD as i32,
}
Expand All @@ -431,11 +458,11 @@ impl MinecraftServer {
clients[client_idx].set_compressed(true);

if let Some(whitelist) = &self.whitelist {
// uuid will only be present if bungeecord is enabled in config
// uuid will only be present if velocity is enabled in config
let whitelisted = if let Some(uuid) = clients[client_idx].uuid {
whitelist.iter().any(|entry| entry.uuid.0 == uuid)
} else {
whitelist.iter().any(|entry| entry.name == username)
whitelist.iter().any(|entry| entry.name == *username)
};
if !whitelisted {
let disconnect = CDisconnectLogin {
Expand All @@ -449,14 +476,19 @@ impl MinecraftServer {
}
}

let uuid = clients[client_idx]
.uuid
.unwrap_or_else(|| Player::generate_offline_uuid(&username));
clients[client_idx].uuid = Some(uuid);
// Set generate uuid if it doesn't exist yet
let uuid = match clients[client_idx].uuid {
Some(uuid) => uuid,
None => {
let uuid = Player::generate_offline_uuid(&username);
clients[client_idx].uuid = Some(uuid);
uuid
}
};

let login_success = CLoginSuccess {
uuid,
username: username.clone(),
username,
// TODO: send player properties
properties: Vec::new(),
}
Expand Down Expand Up @@ -673,7 +705,7 @@ impl ServerBoundPacketHandler for MinecraftServer {
}

fn handle_login_start(&mut self, login_start: SLoginStart, client_idx: usize) {
self.handle_player_login(client_idx, login_start);
self.handle_player_login_start(client_idx, login_start);
}

fn handle_login_acknowledged(
Expand Down Expand Up @@ -763,4 +795,42 @@ impl ServerBoundPacketHandler for MinecraftServer {
) {
self.handle_player_enter_play(client_idx);
}

fn handle_login_plugin_response(&mut self, packet: SLoginPluginResponse, client_idx: usize) {
let clients = &mut self.network.handshaking_clients;

if Some(packet.message_id) != clients[client_idx].forwarding_message_id {
error!(
"Unknown login plugin response with message id: {}",
packet.message_id
);
return;
}

if !packet.successful {
error!("Velocity forwarding channel not understood by client");
return;
}

let velocity_response = VelocityResponseData::decode(&mut Cursor::new(&packet.data));

let velocity_response = match velocity_response {
Ok(velocity_response) => velocity_response,
Err(err) => {
error!("Could not decode velocity reponse data: {:?}", err);
return;
}
};

let secret = CONFIG.velocity.as_ref().unwrap().secret.as_bytes();
let mut mac = <Hmac<Sha256>>::new_from_slice(secret).unwrap();
mac.update(&packet.data[32..]);
if mac.verify_slice(&packet.data[..32]).is_err() {
error!("Failed to verify velocity secret!");
return;
};

clients[client_idx].uuid = Some(velocity_response.uuid);
self.complete_player_login(client_idx);
}
}
2 changes: 2 additions & 0 deletions crates/network/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ pub struct HandshakingConn {
client: NetworkClient,
pub username: Option<String>,
pub uuid: Option<u128>,
pub forwarding_message_id: Option<i32>,
}

impl HandshakingConn {
Expand Down Expand Up @@ -209,6 +210,7 @@ impl NetworkServer {
client,
username: None,
uuid: None,
forwarding_message_id: None,
}),
Err(mpsc::TryRecvError::Empty) => break,
Err(mpsc::TryRecvError::Disconnected) => {
Expand Down
5 changes: 5 additions & 0 deletions crates/network/src/packets/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,7 @@ fn read_decompressed<T: PacketDecoderExt>(
NetworkState::Status if packet_id == 0x00 => Box::new(SRequest::decode(reader)?),
NetworkState::Status if packet_id == 0x01 => Box::new(SPing::decode(reader)?),
NetworkState::Login if packet_id == 0x00 => Box::new(SLoginStart::decode(reader)?),
NetworkState::Login if packet_id == 0x02 => Box::new(SLoginPluginResponse::decode(reader)?),
NetworkState::Login if packet_id == 0x03 => {
*state = NetworkState::Configuration;
Box::new(SLoginAcknowledged::decode(reader)?)
Expand Down Expand Up @@ -259,6 +260,10 @@ pub trait PacketDecoderExt: Read + Sized {
Ok((x as i32, y as i32, z as i32))
}

fn read_uuid(&mut self) -> DecodeResult<u128> {
Ok(self.read_u128::<BigEndian>()?)
}

fn read_nbt_compound(&mut self) -> DecodeResult<Option<NBTCompound>> {
let id = self.read_byte()? as u8;
if id == 0 {
Expand Down
55 changes: 55 additions & 0 deletions crates/network/src/packets/serverbound.rs
Original file line number Diff line number Diff line change
Expand Up @@ -162,6 +162,61 @@ impl ServerBoundPacket for SLoginPluginResponse {
}
}

#[derive(Debug)]
pub struct VelocityGameProfileProperty {
pub name: String,
pub value: String,
pub signature: Option<String>,
}

#[derive(Debug)]
pub struct VelocityResponseData {
pub signature: Vec<u8>,
pub version: i32,
pub address: String,
pub uuid: u128,
pub username: String,
pub properties: Vec<VelocityGameProfileProperty>,
}

impl VelocityResponseData {
pub fn decode<T: PacketDecoderExt>(decoder: &mut T) -> DecodeResult<Self> {
let signature = decoder.read_bytes(32)?;
let version = decoder.read_varint()?;
let address = decoder.read_string()?;
let uuid = decoder.read_uuid()?;
let username = decoder.read_string()?;

Ok(VelocityResponseData {
signature,
version,
address,
uuid,
username,
properties: {
let mut properties = Vec::new();
let len = decoder.read_varint()?;
for _ in 0..len {
let name = decoder.read_string()?;
let value = decoder.read_string()?;
let has_signature = decoder.read_bool()?;
let signature = if has_signature {
Some(decoder.read_string()?)
} else {
None
};
properties.push(VelocityGameProfileProperty {
name,
value,
signature,
});
}
properties
},
})
}
}

#[derive(Debug)]
pub struct SLoginAcknowledged;

Expand Down

0 comments on commit 9d12cef

Please sign in to comment.