Add: Bungeecord support

This commit is contained in:
Snowiiii 2024-10-16 21:51:09 +02:00
parent e725d1995e
commit 15832078af
12 changed files with 155 additions and 42 deletions

1
.github/FUNDING.yml vendored
View File

@ -1,3 +1,4 @@
# These are supported funding model platforms
github: [ Snowiiii ]
custom: ["https://www.paypal.me/alexxmedvedev"]

View File

@ -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

View File

@ -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.

View File

@ -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`

View File

@ -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.
---

36
package-lock.json generated
View File

@ -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": {

View File

@ -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,
}

View File

@ -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, uuid::Error> {
Uuid::from_slice(&Sha256::digest(username)[..16])
}
#[derive(Error, Debug)]
pub enum AuthError {
#[error("Missing auth client")]

View File

@ -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,24 +80,33 @@ impl Client {
// default game profile, when no online mode
// TODO: make offline uuid
let mut gameprofile = self.gameprofile.lock();
let proxy = &ADVANCED_CONFIG.proxy;
if proxy.enabled {
if proxy.velocity.enabled {
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()),
}
}
} else {
*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)
}
return;
}
// 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(
&self,
@ -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");
}
}
async fn autenticate(

View File

@ -93,6 +93,8 @@ pub struct Client {
pub brand: Mutex<Option<String>>,
/// 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<String>,
/// The current connection state of the client (e.g., Handshaking, Status, Play).
pub connection_state: AtomicCell<ConnectionState>,
/// 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),

View File

@ -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::<Vec<_>>();
// 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<Property> = 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,
},
))
}

View File

@ -1 +1,4 @@
pub mod bungeecord;
pub mod velocity;
// TODO: Maybe make a trait for proxies