Skip to content

Commit

Permalink
Merge pull request #89 from StripedMonkey/connection_refactor
Browse files Browse the repository at this point in the history
Networking Refactor Pt. 2
  • Loading branch information
Snowiiii authored Sep 12, 2024
2 parents f9bf750 + d68a25b commit e9b43fc
Show file tree
Hide file tree
Showing 13 changed files with 260 additions and 179 deletions.
6 changes: 2 additions & 4 deletions pumpkin-protocol/src/client/config/c_add_resource_pack.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,10 @@ use pumpkin_core::text::TextComponent;
use pumpkin_macros::packet;
use serde::Serialize;

use crate::uuid::UUID;

#[derive(Serialize)]
#[packet(0x09)]
pub struct CConfigAddResourcePack<'a> {
uuid: UUID,
uuid: uuid::Uuid,
url: &'a str,
hash: &'a str, // max 40
forced: bool,
Expand All @@ -16,7 +14,7 @@ pub struct CConfigAddResourcePack<'a> {

impl<'a> CConfigAddResourcePack<'a> {
pub fn new(
uuid: UUID,
uuid: uuid::Uuid,
url: &'a str,
hash: &'a str,
forced: bool,
Expand Down
7 changes: 4 additions & 3 deletions pumpkin-protocol/src/client/play/c_player_chat_message.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,12 @@ use pumpkin_core::text::TextComponent;
use pumpkin_macros::packet;
use serde::Serialize;

use crate::{uuid::UUID, BitSet, VarInt};
use crate::{BitSet, VarInt};
#[derive(Serialize)]
#[packet(0x39)]
pub struct CPlayerChatMessage<'a> {
sender: UUID,
#[serde(with = "uuid::serde::compact")]
sender: uuid::Uuid,
index: VarInt,
message_signature: Option<&'a [u8]>,
message: &'a str,
Expand All @@ -24,7 +25,7 @@ pub struct CPlayerChatMessage<'a> {
impl<'a> CPlayerChatMessage<'a> {
#[expect(clippy::too_many_arguments)]
pub fn new(
sender: UUID,
sender: uuid::Uuid,
index: VarInt,
message_signature: Option<&'a [u8]>,
message: &'a str,
Expand Down
20 changes: 16 additions & 4 deletions pumpkin-protocol/src/client/play/c_player_remove.rs
Original file line number Diff line number Diff line change
@@ -1,20 +1,32 @@
use pumpkin_macros::packet;
use serde::Serialize;
use serde::{ser::SerializeSeq, Serialize};

use crate::{uuid::UUID, VarInt};
use crate::VarInt;

#[derive(Serialize)]
#[packet(0x3D)]
pub struct CRemovePlayerInfo<'a> {
players_count: VarInt,
players: &'a [UUID],
#[serde(serialize_with = "serialize_slice_uuids")]
players: &'a [uuid::Uuid],
}

impl<'a> CRemovePlayerInfo<'a> {
pub fn new(players_count: VarInt, players: &'a [UUID]) -> Self {
pub fn new(players_count: VarInt, players: &'a [uuid::Uuid]) -> Self {
Self {
players_count,
players,
}
}
}

fn serialize_slice_uuids<S: serde::Serializer>(
uuids: &[uuid::Uuid],
serializer: S,
) -> Result<S::Ok, S::Error> {
let mut seq = serializer.serialize_seq(Some(uuids.len()))?;
for uuid in uuids {
seq.serialize_element(uuid.as_bytes())?;
}
seq.end()
}
7 changes: 4 additions & 3 deletions pumpkin-protocol/src/client/play/c_spawn_player.rs
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
use pumpkin_macros::packet;
use serde::Serialize;

use crate::{uuid::UUID, VarInt};
use crate::VarInt;

#[derive(Serialize)]
#[packet(0x01)]
pub struct CSpawnEntity {
entity_id: VarInt,
entity_uuid: UUID,
#[serde(with = "uuid::serde::compact")]
entity_uuid: uuid::Uuid,
typ: VarInt,
x: f64,
y: f64,
Expand All @@ -25,7 +26,7 @@ impl CSpawnEntity {
#[expect(clippy::too_many_arguments)]
pub fn new(
entity_id: VarInt,
entity_uuid: UUID,
entity_uuid: uuid::Uuid,
typ: VarInt,
x: f64,
y: f64,
Expand Down
1 change: 0 additions & 1 deletion pumpkin-protocol/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ pub mod packet_decoder;
pub mod packet_encoder;
pub mod server;
pub mod slot;
pub mod uuid;

/// To current Minecraft protocol
/// Don't forget to change this when porting
Expand Down
15 changes: 0 additions & 15 deletions pumpkin-protocol/src/uuid.rs

This file was deleted.

5 changes: 0 additions & 5 deletions pumpkin/src/client/authentication.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
use std::{collections::HashMap, net::IpAddr, sync::Arc};

use base64::{engine::general_purpose, Engine};
use num_bigint::BigInt;
use pumpkin_config::{auth::TextureConfig, ADVANCED_CONFIG};
use pumpkin_core::ProfileAction;
use pumpkin_protocol::Property;
Expand Down Expand Up @@ -98,10 +97,6 @@ pub fn unpack_textures(property: Property, config: &TextureConfig) -> Result<(),
Ok(())
}

pub fn auth_digest(bytes: &[u8]) -> String {
BigInt::from_signed_bytes_be(bytes).to_str_radix(16)
}

pub fn is_texture_url_valid(url: Url, config: &TextureConfig) -> Result<(), TextureError> {
let scheme = url.scheme();
if !config.allowed_url_schemes.contains(&scheme.to_string()) {
Expand Down
61 changes: 22 additions & 39 deletions pumpkin/src/client/client_packet.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@ use pumpkin_core::text::TextComponent;
use pumpkin_protocol::{
client::{
config::{CConfigAddResourcePack, CFinishConfig, CKnownPacks, CRegistryData},
login::{CEncryptionRequest, CLoginSuccess, CSetCompression},
status::{CPingResponse, CStatusResponse},
login::{CLoginSuccess, CSetCompression},
status::CPingResponse,
},
server::{
config::{SAcknowledgeFinishConfig, SClientInformationConfig, SKnownPacks, SPluginMessage},
Expand All @@ -17,8 +17,7 @@ use pumpkin_protocol::{
},
ConnectionState, KnownPack, CURRENT_MC_PROTOCOL,
};
use rsa::Pkcs1v15Encrypt;
use sha1::{Digest, Sha1};
use uuid::Uuid;

use crate::{
client::authentication::{self, GameProfile},
Expand All @@ -27,10 +26,7 @@ use crate::{
server::{Server, CURRENT_MC_VERSION},
};

use super::{
authentication::{auth_digest, unpack_textures},
Client, EncryptionError, PlayerConfig,
};
use super::{authentication::unpack_textures, Client, PlayerConfig};

/// Processes incoming Packets from the Client to the Server
/// Implements the `Client` Packets
Expand Down Expand Up @@ -59,7 +55,7 @@ impl Client {
}

pub fn handle_status_request(&self, server: &Arc<Server>, _status_request: SStatusRequest) {
self.send_packet(&CStatusResponse::new(&server.status_response_json));
self.send_packet(&server.get_status());
}

pub fn handle_ping_request(&self, _server: &Arc<Server>, ping_request: SStatusPingRequest) {
Expand Down Expand Up @@ -101,37 +97,23 @@ impl Client {

// TODO: check config for encryption
let verify_token: [u8; 4] = rand::random();
let public_key_der = &server.public_key_der;
let packet = CEncryptionRequest::new(
"",
public_key_der,
&verify_token,
BASIC_CONFIG.online_mode, // TODO
);
self.send_packet(&packet);
self.send_packet(&server.encryption_request(&verify_token, BASIC_CONFIG.online_mode));
}

pub async fn handle_encryption_response(
&self,
server: &Arc<Server>,
encryption_response: SEncryptionResponse,
) {
let shared_secret = server
.private_key
.decrypt(Pkcs1v15Encrypt, &encryption_response.shared_secret)
.map_err(|_| EncryptionError::FailedDecrypt)
.unwrap();
let shared_secret = server.decrypt(&encryption_response.shared_secret).unwrap();

self.enable_encryption(&shared_secret)
.unwrap_or_else(|e| self.kick(&e.to_string()));

let mut gameprofile = self.gameprofile.lock();

if BASIC_CONFIG.online_mode {
let hash = Sha1::new()
.chain_update(&shared_secret)
.chain_update(&server.public_key_der)
.finalize();
let hash = auth_digest(&hash);
let hash = server.digest_secret(&shared_secret);
let ip = self.address.lock().ip();
match authentication::authenticate(
&gameprofile.as_ref().unwrap().name,
Expand Down Expand Up @@ -204,25 +186,26 @@ impl Client {
_login_acknowledged: SLoginAcknowledged,
) {
self.connection_state.store(ConnectionState::Config);
server.send_brand(self);
self.send_packet(&server.get_branding());

let resource_config = &ADVANCED_CONFIG.resource_pack;
if resource_config.enabled {
let prompt_message = if resource_config.prompt_message.is_empty() {
None
} else {
Some(TextComponent::text(&resource_config.prompt_message))
};
self.send_packet(&CConfigAddResourcePack::new(
pumpkin_protocol::uuid::UUID(uuid::Uuid::new_v3(
let resource_pack = CConfigAddResourcePack::new(
Uuid::new_v3(
&uuid::Uuid::NAMESPACE_DNS,
resource_config.resource_pack_url.as_bytes(),
)),
),
&resource_config.resource_pack_url,
&resource_config.resource_pack_sha1,
resource_config.force,
prompt_message,
));
if !resource_config.prompt_message.is_empty() {
Some(TextComponent::text(&resource_config.prompt_message))
} else {
None
},
);

self.send_packet(&resource_pack);
}

// known data packs
Expand All @@ -231,7 +214,7 @@ impl Client {
id: "core",
version: "1.21",
}]));
dbg!("login achnowlaged");
dbg!("login acknowledged");
}
pub fn handle_client_information_config(
&self,
Expand Down
2 changes: 1 addition & 1 deletion pumpkin/src/client/player_packet.rs
Original file line number Diff line number Diff line change
Expand Up @@ -316,7 +316,7 @@ impl Player {
let entity = &self.entity;
let world = &entity.world;
world.broadcast_packet_all(&CPlayerChatMessage::new(
pumpkin_protocol::uuid::UUID(gameprofile.id),
gameprofile.id,
1.into(),
chat_message.signature.as_deref(),
&message,
Expand Down
102 changes: 102 additions & 0 deletions pumpkin/src/server/connection_cache.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
use std::{io::Cursor, path::Path};

use base64::{engine::general_purpose, Engine as _};
use image::GenericImageView as _;
use pumpkin_config::{BasicConfiguration, BASIC_CONFIG};
use pumpkin_protocol::{
client::{config::CPluginMessage, status::CStatusResponse},
Players, Sample, StatusResponse, VarInt, Version, CURRENT_MC_PROTOCOL,
};

use super::CURRENT_MC_VERSION;

pub struct CachedStatus {
_status_response: StatusResponse,
// We cache the json response here so we don't parse it every time someone makes a Status request.
// Keep in mind that we must parse this again, when the StatusResponse changes which usually happen when a player joins or leaves
status_response_json: String,
}

pub struct CachedBranding {
/// Cached Server brand buffer so we don't have to rebuild them every time a player joins
cached_server_brand: Vec<u8>,
}

impl CachedBranding {
pub fn new() -> CachedBranding {
let cached_server_brand = Self::build_brand();
CachedBranding {
cached_server_brand,
}
}
pub fn get_branding(&self) -> CPluginMessage {
CPluginMessage::new("minecraft:brand", &self.cached_server_brand)
}
fn build_brand() -> Vec<u8> {
let brand = "Pumpkin";
let mut buf = vec![];
let _ = VarInt(brand.len() as i32).encode(&mut buf);
buf.extend_from_slice(brand.as_bytes());
buf
}
}

impl CachedStatus {
pub fn new() -> Self {
let status_response = Self::build_response(&BASIC_CONFIG);
let status_response_json = serde_json::to_string(&status_response)
.expect("Failed to parse Status response into JSON");

CachedStatus {
_status_response: status_response,
status_response_json,
}
}

pub fn get_status(&self) -> CStatusResponse<'_> {
CStatusResponse::new(&self.status_response_json)
}

pub fn build_response(config: &BasicConfiguration) -> StatusResponse {
let icon_path = concat!(env!("CARGO_MANIFEST_DIR"), "/icon.png");
let icon = if Path::new(icon_path).exists() {
Some(Self::load_icon(icon_path))
} else {
None
};

StatusResponse {
version: Some(Version {
name: CURRENT_MC_VERSION.into(),
protocol: CURRENT_MC_PROTOCOL,
}),
players: Some(Players {
max: config.max_players,
online: 0,
sample: vec![Sample {
name: "".into(),
id: "".into(),
}],
}),
description: config.motd.clone(),
favicon: icon,
enforece_secure_chat: false,
}
}

fn load_icon(path: &str) -> String {
let icon = match image::open(path).map_err(|e| panic!("error loading icon: {}", e)) {
Ok(icon) => icon,
Err(_) => return "".into(),
};
let dimension = icon.dimensions();
assert!(dimension.0 == 64, "Icon width must be 64");
assert!(dimension.1 == 64, "Icon height must be 64");
let mut image = Vec::with_capacity(64 * 64 * 4);
icon.write_to(&mut Cursor::new(&mut image), image::ImageFormat::Png)
.unwrap();
let mut result = "data:image/png;base64,".to_owned();
general_purpose::STANDARD.encode_string(image, &mut result);
result
}
}
Loading

0 comments on commit e9b43fc

Please sign in to comment.