From 15832078aff7bb0a99fc10b388537c3e853bbe2f Mon Sep 17 00:00:00 2001 From: Snowiiii Date: Wed, 16 Oct 2024 21:51:09 +0200 Subject: [PATCH] Add: Bungeecord support --- .github/FUNDING.yml | 1 + README.md | 4 +- docs/about/introduction.md | 1 - docs/config/advanced.md | 7 +++ docs/index.md | 4 +- package-lock.json | 36 +++++++-------- pumpkin-config/src/proxy.rs | 15 +++++++ pumpkin/src/client/authentication.rs | 6 +++ pumpkin/src/client/client_packet.rs | 52 ++++++++++++++-------- pumpkin/src/client/mod.rs | 3 ++ pumpkin/src/proxy/bungeecord.rs | 65 ++++++++++++++++++++++++++++ pumpkin/src/proxy/mod.rs | 3 ++ 12 files changed, 155 insertions(+), 42 deletions(-) create mode 100644 pumpkin/src/proxy/bungeecord.rs diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml index cc9d0dd1b..5f80fd995 100644 --- a/.github/FUNDING.yml +++ b/.github/FUNDING.yml @@ -1,3 +1,4 @@ # These are supported funding model platforms github: [ Snowiiii ] +custom: ["https://www.paypal.me/alexxmedvedev"] \ No newline at end of file diff --git a/README.md b/README.md index 63d715212..27504834b 100644 --- a/README.md +++ b/README.md @@ -24,7 +24,6 @@ and customizable experience. It prioritizes performance and player enjoyment whi ## What Pumpkin will not -- Be a drop-in replacement for Vanilla or other servers - Be compatible with plugins or mods for other servers - Function as a framework for building a server from scratch. @@ -68,6 +67,7 @@ and customizable experience. It prioritizes performance and player enjoyment whi - [x] Chat - [x] Commands - Proxy + - [x] Bungeecord - [ ] Velocity Check out our [Github Project](https://github.com/users/Snowiiii/projects/12/views/3) to see current progress @@ -90,7 +90,7 @@ Consider joining our [discord](https://discord.gg/wT8XjrjKkf) to stay up-to-date ## Funding -If you want to fund me and help the project, Check out my [GitHub sponsors](https://github.com/sponsors/Snowiiii) +If you want to fund me and help the project, Check out my [GitHub sponsors](https://github.com/sponsors/Snowiiii) or my [PayPal](https://www.paypal.me/alexxmedvedev) ## Thanks diff --git a/docs/about/introduction.md b/docs/about/introduction.md index ab9174d46..8f4c216b2 100644 --- a/docs/about/introduction.md +++ b/docs/about/introduction.md @@ -15,7 +15,6 @@ and customizable experience. It prioritizes performance and player enjoyment whi ## What Pumpkin will not -- Be a drop-in replacement for vanilla or other servers - Be compatible with plugins or mods for other servers - Function as a framework for building a server from scratch. diff --git a/docs/config/advanced.md b/docs/config/advanced.md index 6d69f6b7c..48a331430 100644 --- a/docs/config/advanced.md +++ b/docs/config/advanced.md @@ -31,6 +31,13 @@ This secret is used to ensure that player info forwarded by Velocity comes from secret= ``` +### Bungeecord +`proxy.bungeecord` + +```toml +enabled=false +``` + ## Authentication `authentication` diff --git a/docs/index.md b/docs/index.md index 2359e0c8d..e7665b3b6 100644 --- a/docs/index.md +++ b/docs/index.md @@ -22,6 +22,6 @@ features: details: Pumpkin is written 100% in Rust, ensuring memory safety and unmatched performance. - title: Feature complete details: With all vanilla features supported, you will have no issues. - - title: Extensible - details: Using Extism you can extend Pumpkin to your needs. Play your way! + - title: Flexibility + details: Highly configurable, with the ability to disable unnecessary features. --- diff --git a/package-lock.json b/package-lock.json index df845bdb2..ab8ce843f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1280,24 +1280,24 @@ } }, "node_modules/@vue/devtools-api": { - "version": "7.4.6", - "resolved": "https://registry.npmjs.org/@vue/devtools-api/-/devtools-api-7.4.6.tgz", - "integrity": "sha512-XipBV5k0/IfTr0sNBDTg7OBUCp51cYMMXyPxLXJZ4K/wmUeMqt8cVdr2ZZGOFq+si/jTyCYnNxeKoyev5DOUUA==", + "version": "7.5.2", + "resolved": "https://registry.npmjs.org/@vue/devtools-api/-/devtools-api-7.5.2.tgz", + "integrity": "sha512-VxPbAQxJrYSIkoGVvQ2oOoKW8u4CMpvRLySTxhoJA38z8bQEGy9GO33eoRY/DulJbSFRfjZFNvH+dh8B4qpesQ==", "dev": true, "license": "MIT", "dependencies": { - "@vue/devtools-kit": "^7.4.6" + "@vue/devtools-kit": "^7.5.2" } }, "node_modules/@vue/devtools-kit": { - "version": "7.4.6", - "resolved": "https://registry.npmjs.org/@vue/devtools-kit/-/devtools-kit-7.4.6.tgz", - "integrity": "sha512-NbYBwPWgEic1AOd9bWExz9weBzFdjiIfov0yRn4DrRfR+EQJCI9dn4I0XS7IxYGdkmUJi8mFW42LLk18WsGqew==", + "version": "7.5.2", + "resolved": "https://registry.npmjs.org/@vue/devtools-kit/-/devtools-kit-7.5.2.tgz", + "integrity": "sha512-0leUOE2HBfl8sHf9ePKzxqnCFskkU22tWWqd9OfeSlslAKE30/TViYvWcF4vgQmPlJnAAdHU0WfW5dYlCeOiuw==", "dev": true, "license": "MIT", "dependencies": { - "@vue/devtools-shared": "^7.4.6", - "birpc": "^0.2.17", + "@vue/devtools-shared": "^7.5.2", + "birpc": "^0.2.19", "hookable": "^5.5.3", "mitt": "^3.0.1", "perfect-debounce": "^1.0.0", @@ -1306,9 +1306,9 @@ } }, "node_modules/@vue/devtools-shared": { - "version": "7.4.6", - "resolved": "https://registry.npmjs.org/@vue/devtools-shared/-/devtools-shared-7.4.6.tgz", - "integrity": "sha512-rPeSBzElnHYMB05Cc056BQiJpgocQjY8XVulgni+O9a9Gr9tNXgPteSzFFD+fT/iWMxNuUgGKs9CuW5DZewfIg==", + "version": "7.5.2", + "resolved": "https://registry.npmjs.org/@vue/devtools-shared/-/devtools-shared-7.5.2.tgz", + "integrity": "sha512-+zmcixnD6TAo+zwm30YuwZckhL9iIi4u+gFwbq9C8zpm3SMndTlEYZtNhAHUhOXB+bCkzyunxw80KQ/T0trF4w==", "dev": true, "license": "MIT", "dependencies": { @@ -2101,9 +2101,9 @@ } }, "node_modules/preact": { - "version": "10.24.2", - "resolved": "https://registry.npmjs.org/preact/-/preact-10.24.2.tgz", - "integrity": "sha512-1cSoF0aCC8uaARATfrlz4VCBqE8LwZwRfLgkxJOQwAlQt6ayTmi0D9OF7nXid1POI5SZidFuG9CnlXbDfLqY/Q==", + "version": "10.24.3", + "resolved": "https://registry.npmjs.org/preact/-/preact-10.24.3.tgz", + "integrity": "sha512-Z2dPnBnMUfyQfSQ+GBdsGa16hz35YmLmtTLhM169uW944hYL6xzTYkJjC07j+Wosz733pMWx0fgON3JNw1jJQA==", "dev": true, "license": "MIT", "funding": { @@ -2386,9 +2386,9 @@ } }, "node_modules/vite": { - "version": "5.4.8", - "resolved": "https://registry.npmjs.org/vite/-/vite-5.4.8.tgz", - "integrity": "sha512-FqrItQ4DT1NC4zCUqMB4c4AZORMKIa0m8/URVCZ77OZ/QSNeJ54bU1vrFADbDsuwfIPcgknRkmqakQcgnL4GiQ==", + "version": "5.4.9", + "resolved": "https://registry.npmjs.org/vite/-/vite-5.4.9.tgz", + "integrity": "sha512-20OVpJHh0PAM0oSOELa5GaZNWeDjcAvQjGXy2Uyr+Tp+/D2/Hdz6NLgpJLsarPTA2QJ6v8mX2P1ZfbsSKvdMkg==", "dev": true, "license": "MIT", "dependencies": { diff --git a/pumpkin-config/src/proxy.rs b/pumpkin-config/src/proxy.rs index 1d1e44336..49113654e 100644 --- a/pumpkin-config/src/proxy.rs +++ b/pumpkin-config/src/proxy.rs @@ -1,16 +1,31 @@ use serde::{Deserialize, Serialize}; +use serde_inline_default::serde_inline_default; +#[serde_inline_default] #[derive(Deserialize, Serialize, Default)] #[serde(default)] pub struct ProxyConfig { + #[serde_inline_default(false)] pub enabled: bool, pub velocity: VelocityConfig, + pub bungeecord: BungeeCordConfig, } +#[serde_inline_default] +#[derive(Deserialize, Serialize, Default)] +#[serde(default)] +pub struct BungeeCordConfig { + #[serde_inline_default(false)] + pub enabled: bool, +} + +#[serde_inline_default] #[derive(Deserialize, Serialize)] #[serde(default)] pub struct VelocityConfig { + #[serde_inline_default(false)] pub enabled: bool, + #[serde_inline_default("".to_string())] pub secret: String, } diff --git a/pumpkin/src/client/authentication.rs b/pumpkin/src/client/authentication.rs index d6aaa5963..097a6a891 100644 --- a/pumpkin/src/client/authentication.rs +++ b/pumpkin/src/client/authentication.rs @@ -6,6 +6,8 @@ use pumpkin_core::ProfileAction; use pumpkin_protocol::Property; use reqwest::{StatusCode, Url}; use serde::Deserialize; +use sha1::Digest; +use sha2::Sha256; use thiserror::Error; use uuid::Uuid; @@ -119,6 +121,10 @@ pub fn is_texture_url_valid(url: Url, config: &TextureConfig) -> Result<(), Text Ok(()) } +pub fn offline_uuid(username: &str) -> Result { + Uuid::from_slice(&Sha256::digest(username)[..16]) +} + #[derive(Error, Debug)] pub enum AuthError { #[error("Missing auth client")] diff --git a/pumpkin/src/client/client_packet.rs b/pumpkin/src/client/client_packet.rs index d185438e6..b2cfd5bb5 100644 --- a/pumpkin/src/client/client_packet.rs +++ b/pumpkin/src/client/client_packet.rs @@ -20,7 +20,7 @@ use uuid::Uuid; use crate::{ client::authentication::{self, validate_textures, GameProfile}, entity::player::{ChatMode, Hand}, - proxy::velocity::velocity_login, + proxy::{bungeecord::bungeecord_login, velocity::velocity_login}, server::{Server, CURRENT_MC_VERSION}, }; @@ -36,6 +36,7 @@ impl Client { let version = handshake.protocol_version.0; self.protocol_version .store(version, std::sync::atomic::Ordering::Relaxed); + *self.server_address.lock() = handshake.server_address; self.connection_state.store(handshake.next_state); if self.connection_state.load() != ConnectionState::Status { @@ -79,23 +80,32 @@ impl Client { // default game profile, when no online mode // TODO: make offline uuid let mut gameprofile = self.gameprofile.lock(); - *gameprofile = Some(GameProfile { - id: login_start.uuid, - name: login_start.name, - properties: vec![], - profile_actions: None, - }); let proxy = &ADVANCED_CONFIG.proxy; if proxy.enabled { if proxy.velocity.enabled { - velocity_login(self) + velocity_login(self); + } else if proxy.bungeecord.enabled { + match bungeecord_login(self, login_start.name) { + Ok((_ip, profile)) => { + // self.address.lock() = ip; + self.finish_login(&profile); + *gameprofile = Some(profile); + } + Err(error) => self.kick(&error.to_string()), + } } - return; - } + } else { + *gameprofile = Some(GameProfile { + id: login_start.uuid, + name: login_start.name, + properties: vec![], + profile_actions: None, + }); - // TODO: check config for encryption - let verify_token: [u8; 4] = rand::random(); - self.send_packet(&server.encryption_request(&verify_token, BASIC_CONFIG.online_mode)); + // TODO: check config for encryption + let verify_token: [u8; 4] = rand::random(); + self.send_packet(&server.encryption_request(&verify_token, BASIC_CONFIG.online_mode)); + } } pub async fn handle_encryption_response( @@ -122,6 +132,14 @@ impl Client { } } + if let Some(profile) = gameprofile.as_ref() { + self.finish_login(profile); + } else { + self.kick("No Game profile"); + } + } + + fn finish_login(&self, profile: &GameProfile) { // enable compression if ADVANCED_CONFIG.packet_compression.enabled { let compression = ADVANCED_CONFIG.packet_compression.compression_info.clone(); @@ -129,12 +147,8 @@ impl Client { self.set_compression(Some(compression)); } - if let Some(profile) = gameprofile.as_ref() { - let packet = CLoginSuccess::new(&profile.id, &profile.name, &profile.properties, false); - self.send_packet(&packet); - } else { - self.kick("game profile is none"); - } + let packet = CLoginSuccess::new(&profile.id, &profile.name, &profile.properties, false); + self.send_packet(&packet); } async fn autenticate( diff --git a/pumpkin/src/client/mod.rs b/pumpkin/src/client/mod.rs index 83e61abaa..cf0cc8b74 100644 --- a/pumpkin/src/client/mod.rs +++ b/pumpkin/src/client/mod.rs @@ -93,6 +93,8 @@ pub struct Client { pub brand: Mutex>, /// The minecraft protocol version used by the client. pub protocol_version: AtomicI32, + /// The Address used to connect to the Server, Send in the Handshake + pub server_address: Mutex, /// The current connection state of the client (e.g., Handshaking, Status, Play). pub connection_state: AtomicCell, /// Whether encryption is enabled for the connection. @@ -132,6 +134,7 @@ impl Client { gameprofile: Mutex::new(None), config: Mutex::new(None), brand: Mutex::new(None), + server_address: Mutex::new("".to_string()), id, address: Mutex::new(address), connection_state: AtomicCell::new(ConnectionState::HandShake), diff --git a/pumpkin/src/proxy/bungeecord.rs b/pumpkin/src/proxy/bungeecord.rs new file mode 100644 index 000000000..76a2e130a --- /dev/null +++ b/pumpkin/src/proxy/bungeecord.rs @@ -0,0 +1,65 @@ +use std::net::IpAddr; + +use pumpkin_protocol::Property; +use thiserror::Error; + +use crate::{ + client::authentication::{offline_uuid, GameProfile}, + Client, +}; + +#[derive(Error, Debug)] +pub enum BungeeCordError { + #[error("Failed to parse Address")] + FailedParseAddress, + #[error("Failed to parse UUID")] + FailedParseUUID, + #[error("Failed to parse Properties")] + FailedParseProperties, + #[error("Failed to make offline UUID")] + FailedMakeOfflineUUID, +} + +pub fn bungeecord_login( + client: &Client, + username: String, +) -> Result<(IpAddr, GameProfile), BungeeCordError> { + let server_address = client.server_address.lock(); + let data = server_address.split('\0').take(4).collect::>(); + + // Ip of player, only given if ip_forward on bungee is true + let ip = match data.get(1) { + Some(ip) => ip + .parse() + .map_err(|_| BungeeCordError::FailedParseAddress)?, + None => client.address.lock().ip(), + }; + + // Uuid of player, only given if ip_forward on bungee is true + let id = match data.get(2) { + Some(uuid) => uuid.parse().map_err(|_| BungeeCordError::FailedParseUUID)?, + None => { + offline_uuid(username.as_str()).map_err(|_| BungeeCordError::FailedMakeOfflineUUID)? + } + }; + + // Read properties and get textures + // Properties of player's game profile, only given if ip_forward and online_mode + // on bungee both are true + let properties: Vec = match data.get(3) { + Some(properties) => { + serde_json::from_str(properties).map_err(|_| BungeeCordError::FailedParseProperties)? + } + None => vec![], + }; + + Ok(( + ip, + GameProfile { + id, + name: username, + properties, + profile_actions: None, + }, + )) +} diff --git a/pumpkin/src/proxy/mod.rs b/pumpkin/src/proxy/mod.rs index 70249d623..31c8418a9 100644 --- a/pumpkin/src/proxy/mod.rs +++ b/pumpkin/src/proxy/mod.rs @@ -1 +1,4 @@ +pub mod bungeecord; pub mod velocity; + +// TODO: Maybe make a trait for proxies