diff --git a/README.md b/README.md index 9a58292cb..b0608e17e 100644 --- a/README.md +++ b/README.md @@ -14,8 +14,10 @@ Pumpkin is currently under heavy development. ## What Pumpkin aims to be: - A fast, efficient, and scalable Minecraft server +- Use Multi-Threading - Compatible with the latest Minecraft server version - Adherent to Vanilla game mechanics +- Be Secure and prevent all Exploits found - Highly configurable with the ability to disable unnecessary features - A platform for plugin development @@ -51,7 +53,7 @@ Pumpkin is currently under heavy development. - [x] Player Client brand - [x] Player Teleport - [x] Player Movement - - [ ] Player Animation + - [x] Player Animation - [ ] Player Inventory - [ ] Player Attack - Server diff --git a/pumpkin-protocol/src/bytebuf/mod.rs b/pumpkin-protocol/src/bytebuf/mod.rs index c070d4cfa..17268c3fc 100644 --- a/pumpkin-protocol/src/bytebuf/mod.rs +++ b/pumpkin-protocol/src/bytebuf/mod.rs @@ -299,6 +299,17 @@ impl ByteBuffer { self.buffer.put_slice(src) } + pub fn put(&mut self, src: T) + where + Self: Sized, + { + self.buffer.put(src) + } + + pub fn reserve(&mut self, additional: usize) { + self.buffer.reserve(additional) + } + pub fn get_slice(&mut self) -> BytesMut { self.buffer.split() } diff --git a/pumpkin-protocol/src/bytebuf/serializer.rs b/pumpkin-protocol/src/bytebuf/serializer.rs index 025bad91b..f3c81f5d7 100644 --- a/pumpkin-protocol/src/bytebuf/serializer.rs +++ b/pumpkin-protocol/src/bytebuf/serializer.rs @@ -3,6 +3,8 @@ use std::fmt::Display; use serde::{ser, Serialize}; use thiserror::Error; +use crate::VarInt; + use super::ByteBuffer; pub struct Serializer { @@ -121,16 +123,22 @@ impl<'a> ser::Serializer for &'a mut Serializer { unimplemented!() } fn serialize_none(self) -> Result { - unimplemented!() + self.output.put_bool(false); + Ok(()) } - fn serialize_seq(self, _len: Option) -> Result { - unimplemented!() + fn serialize_seq(self, len: Option) -> Result { + if let Some(len) = len { + self.output.put_var_int(&VarInt(len as i32)); + } + Ok(self) } - fn serialize_some(self, _value: &T) -> Result + fn serialize_some(self, value: &T) -> Result where T: ?Sized + Serialize, { - unimplemented!() + self.output.put_bool(true); + dbg!("aa"); + value.serialize(self) } fn serialize_str(self, v: &str) -> Result { self.output.put_string(v); @@ -213,16 +221,16 @@ impl<'a> ser::SerializeSeq for &'a mut Serializer { type Error = SerializerError; // Serialize a single element of the sequence. - fn serialize_element(&mut self, _value: &T) -> Result<(), Self::Error> + fn serialize_element(&mut self, value: &T) -> Result<(), Self::Error> where T: ?Sized + Serialize, { - todo!() + value.serialize(&mut **self) } // Close the sequence. fn end(self) -> Result<(), Self::Error> { - todo!() + Ok(()) } } diff --git a/pumpkin-protocol/src/client/login/c_encryption_request.rs b/pumpkin-protocol/src/client/login/c_encryption_request.rs index 7636ca377..312b5cd28 100644 --- a/pumpkin-protocol/src/client/login/c_encryption_request.rs +++ b/pumpkin-protocol/src/client/login/c_encryption_request.rs @@ -1,11 +1,13 @@ use pumpkin_macros::packet; -use crate::{bytebuf::ByteBuffer, ClientPacket, VarIntType}; +use crate::{bytebuf::ByteBuffer, ClientPacket, VarInt}; #[packet(0x01)] pub struct CEncryptionRequest<'a> { server_id: &'a str, // 20 + public_key_length: VarInt, public_key: &'a [u8], + verify_token_length: VarInt, verify_token: &'a [u8], should_authenticate: bool, } @@ -19,7 +21,9 @@ impl<'a> CEncryptionRequest<'a> { ) -> Self { Self { server_id, + public_key_length: public_key.len().into(), public_key, + verify_token_length: verify_token.len().into(), verify_token, should_authenticate, } @@ -29,9 +33,9 @@ impl<'a> CEncryptionRequest<'a> { impl<'a> ClientPacket for CEncryptionRequest<'a> { fn write(&self, bytebuf: &mut ByteBuffer) { bytebuf.put_string(self.server_id); - bytebuf.put_var_int(&(self.public_key.len() as VarIntType).into()); + bytebuf.put_var_int(&self.public_key_length); bytebuf.put_slice(self.public_key); - bytebuf.put_var_int(&(self.verify_token.len() as VarIntType).into()); + bytebuf.put_var_int(&self.verify_token_length); bytebuf.put_slice(self.verify_token); bytebuf.put_bool(self.should_authenticate); } diff --git a/pumpkin-protocol/src/client/login/c_login_success.rs b/pumpkin-protocol/src/client/login/c_login_success.rs index 265527802..065dc5e10 100644 --- a/pumpkin-protocol/src/client/login/c_login_success.rs +++ b/pumpkin-protocol/src/client/login/c_login_success.rs @@ -33,10 +33,7 @@ impl<'a> ClientPacket for CLoginSuccess<'a> { bytebuf.put_list::(self.properties, |p, v| { p.put_string(&v.name); p.put_string(&v.value); - // has signature ? - // todo: for some reason we get "got too many bytes error when using a signature" - p.put_bool(false); - // p.put_option(&v.signature, |p,v| p.put_string(v)); + p.put_option(&v.signature, |p, v| p.put_string(v)); }); bytebuf.put_bool(self.strict_error_handling); } diff --git a/pumpkin-protocol/src/client/play/c_entity_animation.rs b/pumpkin-protocol/src/client/play/c_entity_animation.rs new file mode 100644 index 000000000..a4c7f36da --- /dev/null +++ b/pumpkin-protocol/src/client/play/c_entity_animation.rs @@ -0,0 +1,31 @@ +use num_derive::ToPrimitive; +use pumpkin_macros::packet; +use serde::Serialize; + +use crate::VarInt; + +#[derive(Serialize, Clone)] +#[packet(0x03)] +pub struct CEntityAnimation { + entity_id: VarInt, + /// See `Animation` + animation: u8, +} + +impl CEntityAnimation { + pub fn new(entity_id: VarInt, animation: u8) -> Self { + Self { + entity_id, + animation, + } + } +} + +#[derive(ToPrimitive)] +pub enum Animation { + SwingMainArm, + LeaveBed, + SwingOffhand, + CriticalEffect, + MagicCriticaleffect, +} diff --git a/pumpkin-protocol/src/client/play/c_entity_metadata.rs b/pumpkin-protocol/src/client/play/c_entity_metadata.rs new file mode 100644 index 000000000..506adcb32 --- /dev/null +++ b/pumpkin-protocol/src/client/play/c_entity_metadata.rs @@ -0,0 +1,33 @@ +use pumpkin_macros::packet; +use serde::Serialize; + +use crate::VarInt; + +#[derive(Serialize, Clone)] +#[packet(0x58)] +pub struct CSetEntityMetadata { + entity_id: VarInt, + metadata: Vec, +} + +impl CSetEntityMetadata { + pub fn new(entity_id: VarInt, metadata: Vec) -> Self { + Self { + entity_id, + metadata, + } + } +} + +#[derive(Serialize, Clone)] +pub struct Metadata { + index: u8, + typ: VarInt, + value: u8, +} + +impl Metadata { + pub fn new(index: u8, typ: VarInt, value: u8) -> Self { + Self { index, typ, value } + } +} diff --git a/pumpkin-protocol/src/client/play/c_login.rs b/pumpkin-protocol/src/client/play/c_login.rs index 0cb8fe79e..ad18d6e74 100644 --- a/pumpkin-protocol/src/client/play/c_login.rs +++ b/pumpkin-protocol/src/client/play/c_login.rs @@ -1,7 +1,9 @@ use pumpkin_macros::packet; +use serde::Serialize; -use crate::{bytebuf::ByteBuffer, ClientPacket, VarInt}; +use crate::VarInt; +#[derive(Serialize)] #[packet(0x2B)] pub struct CLogin { entity_id: i32, @@ -20,9 +22,7 @@ pub struct CLogin { previous_gamemode: i8, debug: bool, is_flat: bool, - has_death_loc: bool, - death_dimension_name: Option, - death_loc: Option, // POSITION NOT STRING + death_dimension_name: Option<(String, i64)>, // POSITION NOT STRING portal_cooldown: VarInt, enforce_secure_chat: bool, } @@ -46,9 +46,7 @@ impl CLogin { previous_gamemode: i8, debug: bool, is_flat: bool, - has_death_loc: bool, - death_dimension_name: Option, - death_loc: Option, // todo add block pos + death_dimension_name: Option<(String, i64)>, portal_cooldown: VarInt, enforce_secure_chat: bool, ) -> Self { @@ -69,39 +67,9 @@ impl CLogin { previous_gamemode, debug, is_flat, - has_death_loc, death_dimension_name, - death_loc, portal_cooldown, enforce_secure_chat, } } } - -impl ClientPacket for CLogin { - fn write(&self, bytebuf: &mut ByteBuffer) { - bytebuf.put_i32(self.entity_id); - bytebuf.put_bool(self.is_hardcore); - bytebuf.put_list(&self.dimension_names, |buf, v| buf.put_string(v)); - bytebuf.put_var_int(&self.max_players); - bytebuf.put_var_int(&self.view_distance); - bytebuf.put_var_int(&self.simulated_distance); - bytebuf.put_bool(self.reduced_debug_info); - bytebuf.put_bool(self.enabled_respawn_screen); - bytebuf.put_bool(self.limited_crafting); - bytebuf.put_var_int(&self.dimension_type); - bytebuf.put_string(&self.dimension_name); - bytebuf.put_i64(self.hashed_seed); - bytebuf.put_u8(self.game_mode); - bytebuf.put_i8(self.previous_gamemode); - bytebuf.put_bool(self.debug); - bytebuf.put_bool(self.is_flat); - bytebuf.put_bool(self.has_death_loc); - if self.has_death_loc { - bytebuf.put_string(self.death_dimension_name.as_ref().unwrap()); - bytebuf.put_i64(self.death_loc.unwrap()); - } - bytebuf.put_var_int(&self.portal_cooldown); - bytebuf.put_bool(self.enforce_secure_chat); - } -} diff --git a/pumpkin-protocol/src/client/play/c_player_abilities.rs b/pumpkin-protocol/src/client/play/c_player_abilities.rs index 65c4cbd08..a74139fac 100644 --- a/pumpkin-protocol/src/client/play/c_player_abilities.rs +++ b/pumpkin-protocol/src/client/play/c_player_abilities.rs @@ -1,7 +1,7 @@ use pumpkin_macros::packet; +use serde::Serialize; -use crate::{bytebuf::ByteBuffer, ClientPacket}; - +#[derive(Serialize)] #[packet(0x38)] pub struct CPlayerAbilities { flags: i8, @@ -18,11 +18,3 @@ impl CPlayerAbilities { } } } - -impl ClientPacket for CPlayerAbilities { - fn write(&self, bytebuf: &mut ByteBuffer) { - bytebuf.put_i8(self.flags); - bytebuf.put_f32(self.flying_speed); - bytebuf.put_f32(self.field_of_view); - } -} diff --git a/pumpkin-protocol/src/client/play/c_player_chat_message.rs b/pumpkin-protocol/src/client/play/c_player_chat_message.rs new file mode 100644 index 000000000..cfbdb63ca --- /dev/null +++ b/pumpkin-protocol/src/client/play/c_player_chat_message.rs @@ -0,0 +1,70 @@ +use num_derive::FromPrimitive; +use serde::Serialize; + +use crate::{text::TextComponent, VarInt}; + +#[derive(Serialize)] +pub struct PlayerChatMessage<'a> { + sender: uuid::Uuid, + index: VarInt, + message_signature: Option<&'a [u8]>, + message: String, + timestamp: i64, + salt: i64, + previous_messages: &'a [PreviousMessage<'a>], // max 20 + unsigned_content: Option, + /// See `FilterType` + filter_type: VarInt, + chat_type: VarInt, + sender_name: TextComponent, + target_name: Option, +} + +impl<'a> PlayerChatMessage<'a> { + #[allow(clippy::too_many_arguments)] + pub fn new( + sender: uuid::Uuid, + index: VarInt, + message_signature: Option<&'a [u8]>, + message: String, + timestamp: i64, + salt: i64, + previous_messages: &'a [PreviousMessage<'a>], + unsigned_content: Option, + filter_type: VarInt, + chat_type: VarInt, + sender_name: TextComponent, + target_name: Option, + ) -> Self { + Self { + sender, + index, + message_signature, + message, + timestamp, + salt, + previous_messages, + unsigned_content, + filter_type, + chat_type, + sender_name, + target_name, + } + } +} + +#[derive(Serialize)] +pub struct PreviousMessage<'a> { + message_id: VarInt, + signature: Option<&'a [u8]>, +} + +#[derive(FromPrimitive)] +pub enum FilterType { + /// Message is not filtered at all + PassThrough, + /// Message is fully filtered + FullyFiltered, + /// Only some characters in the message are filtered + PartiallyFiltered, +} diff --git a/pumpkin-protocol/src/client/play/c_player_info_update.rs b/pumpkin-protocol/src/client/play/c_player_info_update.rs index 23028c855..163c7ce97 100644 --- a/pumpkin-protocol/src/client/play/c_player_info_update.rs +++ b/pumpkin-protocol/src/client/play/c_player_info_update.rs @@ -34,10 +34,7 @@ impl<'a> ClientPacket for CPlayerInfoUpdate<'a> { p.put_list::(properties, |p, v| { p.put_string(&v.name); p.put_string(&v.value); - // has signature ? - // todo: for some reason we get "got too many bytes error when using a signature" - p.put_bool(false); - // todo signature + p.put_option(&v.signature, |p, v| p.put_string(v)); }); } PlayerAction::InitializeChat(_) => todo!(), diff --git a/pumpkin-protocol/src/client/play/mod.rs b/pumpkin-protocol/src/client/play/mod.rs index e807663cd..e7aa90e77 100644 --- a/pumpkin-protocol/src/client/play/mod.rs +++ b/pumpkin-protocol/src/client/play/mod.rs @@ -1,10 +1,13 @@ mod c_change_difficulty; mod c_chunk_data_update_light; +mod c_entity_animation; +mod c_entity_metadata; mod c_game_event; mod c_head_rot; mod c_login; mod c_play_disconnect; mod c_player_abilities; +mod c_player_chat_message; mod c_player_info_update; mod c_set_held_item; mod c_spawn_player; @@ -17,11 +20,14 @@ mod player_action; pub use c_change_difficulty::*; pub use c_chunk_data_update_light::*; +pub use c_entity_animation::*; +pub use c_entity_metadata::*; pub use c_game_event::*; pub use c_head_rot::*; pub use c_login::*; pub use c_play_disconnect::*; pub use c_player_abilities::*; +pub use c_player_chat_message::*; pub use c_player_info_update::*; pub use c_set_held_item::*; pub use c_spawn_player::*; diff --git a/pumpkin-protocol/src/client/status/c_ping_response.rs b/pumpkin-protocol/src/client/status/c_ping_response.rs index 333339a9b..a75f8616f 100644 --- a/pumpkin-protocol/src/client/status/c_ping_response.rs +++ b/pumpkin-protocol/src/client/status/c_ping_response.rs @@ -1,6 +1,7 @@ use pumpkin_macros::packet; +use serde::Serialize; -#[derive(serde::Serialize)] +#[derive(Serialize)] #[packet(0x01)] pub struct CPingResponse { payload: i64, // must responde with the same as in `SPingRequest` diff --git a/pumpkin-protocol/src/client/status/c_status_response.rs b/pumpkin-protocol/src/client/status/c_status_response.rs index 5f82c33e1..4589877f1 100644 --- a/pumpkin-protocol/src/client/status/c_status_response.rs +++ b/pumpkin-protocol/src/client/status/c_status_response.rs @@ -1,6 +1,7 @@ use pumpkin_macros::packet; +use serde::Serialize; -#[derive(serde::Serialize)] +#[derive(Serialize)] #[packet(0x00)] pub struct CStatusResponse<'a> { json_response: &'a str, // 32767 diff --git a/pumpkin-protocol/src/lib.rs b/pumpkin-protocol/src/lib.rs index d54642298..fe2eec998 100644 --- a/pumpkin-protocol/src/lib.rs +++ b/pumpkin-protocol/src/lib.rs @@ -213,7 +213,7 @@ pub struct Sample { } // basicly game profile -#[derive(Deserialize, Clone, Debug)] +#[derive(Serialize, Deserialize, Clone, Debug)] pub struct Property { pub name: String, // base 64 diff --git a/pumpkin/src/client/authentication.rs b/pumpkin/src/client/authentication.rs index b8f57c0fc..dcebe09a3 100644 --- a/pumpkin/src/client/authentication.rs +++ b/pumpkin/src/client/authentication.rs @@ -1,19 +1,49 @@ -use std::net::IpAddr; +use std::{collections::HashMap, net::IpAddr}; +use base64::{engine::general_purpose, Engine}; use num_bigint::BigInt; use pumpkin_protocol::Property; -use reqwest::StatusCode; -use serde::Deserialize; +use reqwest::{StatusCode, Url}; +use serde::{Deserialize, Serialize}; use thiserror::Error; use uuid::Uuid; -use crate::server::Server; +use crate::{config::auth_config::TextureConfig, server::Server}; + +#[derive(Deserialize, Clone, Debug)] +#[allow(non_snake_case)] +#[allow(dead_code)] +pub struct ProfileTextures { + timestamp: i64, + profileId: Uuid, + profileName: String, + signatureRequired: bool, + textures: HashMap, +} + +#[derive(Deserialize, Clone, Debug)] +#[allow(non_snake_case)] +#[allow(dead_code)] +pub struct Texture { + url: String, + metadata: Option>, +} + +#[derive(Deserialize, Serialize, Clone, Debug, PartialEq, Eq)] +pub enum ProfileAction { + #[serde(rename = "FORCED_NAME_CHANGE")] + ForcedNameChange, + #[serde(rename = "USING_BANNED_SKIN")] + UsingBannedSkin, +} #[derive(Deserialize, Clone, Debug)] pub struct GameProfile { pub id: Uuid, pub name: String, pub properties: Vec, + #[serde(rename = "profileActions")] + pub profile_actions: Option>, } pub fn authenticate( @@ -22,8 +52,13 @@ pub fn authenticate( ip: &IpAddr, server: &mut Server, ) -> Result { + assert!(server.advanced_config.authentication.use_authentication); assert!(server.auth_client.is_some()); - let address = if server.base_config.prevent_proxy_connections { + let address = if server + .advanced_config + .authentication + .prevent_proxy_connections + { format!("https://sessionserver.mojang.com/session/minecraft/hasJoined?username={username}&serverId={server_hash}&ip={ip}") } else { format!("https://sessionserver.mojang.com/session/minecraft/hasJoined?username={username}&serverId={server_hash}") @@ -44,10 +79,32 @@ pub fn authenticate( Ok(profile) } +pub fn unpack_textures(property: Property, config: &TextureConfig) { + // todo: no unwrap + let from64 = general_purpose::STANDARD.decode(property.value).unwrap(); + let textures: ProfileTextures = serde_json::from_slice(&from64).unwrap(); + dbg!(&textures); + for texture in textures.textures { + is_texture_url_valid(Url::parse(&texture.1.url).unwrap(), config); + } +} + 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) -> bool { + let scheme = url.scheme(); + if !config.allowed_url_schemes.contains(&scheme.to_string()) { + return false; + } + let domain = url.domain().unwrap_or(""); + if !config.allowed_url_domains.contains(&domain.to_string()) { + return false; + } + true +} + #[derive(Error, Debug)] pub enum AuthError { #[error("Authentication servers are down")] diff --git a/pumpkin/src/client/client_packet.rs b/pumpkin/src/client/client_packet.rs index 3e44cbce3..a62286c40 100644 --- a/pumpkin/src/client/client_packet.rs +++ b/pumpkin/src/client/client_packet.rs @@ -23,7 +23,10 @@ use crate::{ server::Server, }; -use super::{authentication::auth_digest, Client, EncryptionError, PlayerConfig}; +use super::{ + authentication::{auth_digest, unpack_textures}, + Client, EncryptionError, PlayerConfig, +}; /// Processes incoming Packets from the Client to the Server /// Implements the `Client` Packets @@ -45,12 +48,15 @@ impl Client { } pub fn handle_login_start(&mut self, server: &mut Server, login_start: SLoginStart) { + // todo: do basic name validation dbg!("login start"); // default game profile, when no online mode + // todo: make offline uuid self.gameprofile = Some(GameProfile { id: login_start.uuid, name: login_start.name, properties: vec![], + profile_actions: None, }); // todo: check config for encryption @@ -91,10 +97,40 @@ impl Client { &ip, server, ) { - Ok(p) => self.gameprofile = Some(p), + Ok(p) => { + // Check if player should join + if let Some(p) = &p.profile_actions { + if !server + .advanced_config + .authentication + .player_profile + .allow_banned_players + { + if !p.is_empty() { + self.kick("Your account can't join"); + } + } else { + for allowed in server + .advanced_config + .authentication + .player_profile + .allowed_actions + .clone() + { + if !p.contains(&allowed) { + self.kick("Your account can't join"); + } + } + } + } + self.gameprofile = Some(p); + } Err(e) => self.kick(&e.to_string()), } } + for ele in self.gameprofile.as_ref().unwrap().properties.clone() { + unpack_textures(ele, &server.advanced_config.authentication.textures); + } if let Some(profile) = self.gameprofile.as_ref().cloned() { let packet = CLoginSuccess::new(profile.id, profile.name, &profile.properties, false); diff --git a/pumpkin/src/client/mod.rs b/pumpkin/src/client/mod.rs index 604248e3a..e5ec6acc0 100644 --- a/pumpkin/src/client/mod.rs +++ b/pumpkin/src/client/mod.rs @@ -28,7 +28,7 @@ use pumpkin_protocol::{ login::{SEncryptionResponse, SLoginAcknowledged, SLoginPluginResponse, SLoginStart}, play::{ SChatCommand, SConfirmTeleport, SPlayerCommand, SPlayerPosition, - SPlayerPositionRotation, SPlayerRotation, + SPlayerPositionRotation, SPlayerRotation, SSwingArm, }, status::{SPingRequest, SStatusRequest}, }, @@ -39,7 +39,7 @@ use pumpkin_protocol::{ use std::io::Read; use thiserror::Error; -mod authentication; +pub mod authentication; mod client_packet; pub mod player_packet; @@ -258,6 +258,7 @@ impl Client { SPlayerCommand::PACKET_ID => { self.handle_player_command(server, SPlayerCommand::read(bytebuf).unwrap()) } + SSwingArm::PACKET_ID => self.handle_swing_arm(server, SSwingArm::read(bytebuf)), _ => log::error!("Failed to handle player packet id {}", packet.id.0), } } diff --git a/pumpkin/src/client/player_packet.rs b/pumpkin/src/client/player_packet.rs index 7801580f5..1850fd345 100644 --- a/pumpkin/src/client/player_packet.rs +++ b/pumpkin/src/client/player_packet.rs @@ -1,13 +1,18 @@ +use num_traits::FromPrimitive; use pumpkin_protocol::{ - client::play::{CHeadRot, CUpdateEntityPos, CUpdateEntityPosRot, CUpdateEntityRot}, + client::play::{ + Animation, CEntityAnimation, CHeadRot, CUpdateEntityPos, CUpdateEntityPosRot, + CUpdateEntityRot, + }, server::play::{ SChatCommand, SConfirmTeleport, SPlayerCommand, SPlayerPosition, SPlayerPositionRotation, - SPlayerRotation, + SPlayerRotation, SSwingArm, }, }; use crate::{ commands::{handle_command, CommandSender}, + entity::player::Hand, server::Server, }; @@ -156,4 +161,14 @@ impl Client { pumpkin_protocol::server::play::Action::StartFlyingElytra => todo!(), } } + + pub fn handle_swing_arm(&mut self, server: &mut Server, swing_arm: SSwingArm) { + let animation = match Hand::from_i32(swing_arm.hand.0).unwrap() { + Hand::Main => Animation::SwingMainArm, + Hand::Off => Animation::SwingOffhand, + }; + let player = self.player.as_mut().unwrap(); + let id = player.entity_id(); + server.broadcast_packet_expect(self, CEntityAnimation::new(id.into(), animation as u8)) + } } diff --git a/pumpkin/src/config/auth_config.rs b/pumpkin/src/config/auth_config.rs new file mode 100644 index 000000000..0fe373ca8 --- /dev/null +++ b/pumpkin/src/config/auth_config.rs @@ -0,0 +1,93 @@ +use serde::{Deserialize, Serialize}; + +use crate::client::authentication::ProfileAction; + +#[derive(Deserialize, Serialize)] +pub struct Authentication { + /// Whether to use Mojang authentication. + pub use_authentication: bool, + + /// Prevent proxy connections. + pub prevent_proxy_connections: bool, + + /// Player profile handling. + pub player_profile: PlayerProfileConfig, + + /// Texture handling. + pub textures: TextureConfig, +} + +#[derive(Deserialize, Serialize)] +pub struct PlayerProfileConfig { + /// Allow players flagged by Mojang (banned, forced name change). + pub allow_banned_players: bool, + /// Depends on the value above + pub allowed_actions: Vec, +} + +impl Default for PlayerProfileConfig { + fn default() -> Self { + Self { + allow_banned_players: false, + allowed_actions: vec![ + ProfileAction::ForcedNameChange, + ProfileAction::UsingBannedSkin, + ], + } + } +} + +#[derive(Deserialize, Serialize)] +pub struct TextureConfig { + /// Whether to use player textures. + pub enabled: bool, + + pub allowed_url_schemes: Vec, + pub allowed_url_domains: Vec, + + /// Specific texture types. + pub types: TextureTypes, +} + +impl Default for TextureConfig { + fn default() -> Self { + Self { + enabled: true, + allowed_url_schemes: vec!["http".into(), "https".into()], + allowed_url_domains: vec![".minecraft.net".into(), ".mojang.com".into()], + types: Default::default(), + } + } +} + +#[derive(Deserialize, Serialize)] +pub struct TextureTypes { + /// Use player skins. + pub skin: bool, + /// Use player capes. + pub cape: bool, + /// Use player elytras. + /// (i didn't know myself that there are custom elytras) + pub elytra: bool, +} + +impl Default for TextureTypes { + fn default() -> Self { + Self { + skin: true, + cape: true, + elytra: true, + } + } +} + +impl Default for Authentication { + fn default() -> Self { + Self { + use_authentication: true, + prevent_proxy_connections: true, + player_profile: Default::default(), + textures: Default::default(), + } + } +} diff --git a/pumpkin/src/configuration.rs b/pumpkin/src/config/mod.rs similarity index 92% rename from pumpkin/src/configuration.rs rename to pumpkin/src/config/mod.rs index 37fa53baa..3430a2330 100644 --- a/pumpkin/src/configuration.rs +++ b/pumpkin/src/config/mod.rs @@ -1,9 +1,12 @@ use std::path::Path; +use auth_config::Authentication; use serde::{Deserialize, Serialize}; use crate::{entity::player::GameMode, server::Difficulty}; +pub mod auth_config; + /// Current Config version of the Base Config const CURRENT_BASE_VERSION: &str = "1.0.0"; @@ -12,19 +15,15 @@ const CURRENT_BASE_VERSION: &str = "1.0.0"; /// This also allows you get some Performance or Resource boosts. /// Important: The Configuration should match Vanilla by default pub struct AdvancedConfiguration { - /// Requires Online mode - /// Should player have skins - pub use_skins: bool, - /// Should chat be enabled - pub enable_chat: bool, - pub commands: Commands, + pub authentication: Authentication, } #[derive(Deserialize, Serialize)] pub struct Commands { - // Are commands from the Console accepted ? + /// Are commands from the Console accepted ? pub use_console: bool, + // todo commands... } impl Default for Commands { @@ -37,8 +36,7 @@ impl Default for Commands { impl Default for AdvancedConfiguration { fn default() -> Self { Self { - use_skins: true, - enable_chat: true, + authentication: Authentication::default(), commands: Commands::default(), } } @@ -74,8 +72,6 @@ pub struct BasicConfiguration { pub online_mode: bool, /// Whether packet encryption is enabled. Required when online mode is enabled. pub encryption: bool, - /// Whether to prevent proxy connections. - pub prevent_proxy_connections: bool, /// The server's description displayed on the status screen. pub motd: String, /// The default game mode for players. @@ -99,7 +95,6 @@ impl Default for BasicConfiguration { hardcore: false, online_mode: true, encryption: true, - prevent_proxy_connections: true, motd: "A Blazing fast Pumpkin Server!".to_string(), default_gamemode: GameMode::Survival, } diff --git a/pumpkin/src/main.rs b/pumpkin/src/main.rs index 711700f70..0b5372ae2 100644 --- a/pumpkin/src/main.rs +++ b/pumpkin/src/main.rs @@ -4,12 +4,12 @@ use std::io::{self}; use client::Client; use commands::handle_command; -use configuration::AdvancedConfiguration; +use config::AdvancedConfiguration; use std::{collections::HashMap, rc::Rc, thread}; use client::interrupted; -use configuration::BasicConfiguration; +use config::BasicConfiguration; use server::Server; // Setup some tokens to allow us to identify which event is for which socket. @@ -17,7 +17,7 @@ const SERVER: Token = Token(0); pub mod client; pub mod commands; -pub mod configuration; +pub mod config; pub mod entity; pub mod server; pub mod util; @@ -104,6 +104,9 @@ fn main() -> io::Result<()> { return Err(e); } }; + if let Err(e) = connection.set_nodelay(true) { + log::warn!("failed to set TCP_NODELAY {e}"); + } log::info!("Accepted connection from: {}", address); diff --git a/pumpkin/src/server.rs b/pumpkin/src/server.rs index c60fa6ee2..dd7b19235 100644 --- a/pumpkin/src/server.rs +++ b/pumpkin/src/server.rs @@ -26,7 +26,7 @@ use serde::{Deserialize, Serialize}; use crate::{ client::Client, - configuration::{AdvancedConfiguration, BasicConfiguration}, + config::{AdvancedConfiguration, BasicConfiguration}, entity::player::{GameMode, Player}, }; @@ -144,8 +144,6 @@ impl Server { self.base_config.default_gamemode.to_i8().unwrap(), false, false, - false, // deth loc - None, None, 0.into(), false, @@ -160,19 +158,7 @@ impl Server { client.teleport(x, y, z, 10.0, 10.0); let gameprofile = client.gameprofile.as_ref().unwrap(); // first send info update to our new player, So he can see his Skin - // TODO: send more actions, (chat. list, ping) - client.send_packet(CPlayerInfoUpdate::new( - 0x01, - &[pumpkin_protocol::client::play::Player { - uuid: gameprofile.id, - actions: vec![PlayerAction::AddPlayer { - name: gameprofile.name.clone(), - properties: gameprofile.properties.clone(), - }], - }], - )); - let gameprofile = client.gameprofile.as_ref().unwrap(); - // send his info to everyone else + // also send his info to everyone else self.broadcast_packet( client, CPlayerInfoUpdate::new( @@ -210,7 +196,7 @@ impl Server { let gameprofile = client.gameprofile.as_ref().unwrap(); // spawn player for every client - self.broadcast_packet( + self.broadcast_packet_expect( client, CSpawnEntity::new( entity_id.into(), @@ -252,12 +238,37 @@ impl Server { )) } } + // entity meta data + /* if let Some(config) = &client.config { + self.broadcast_packet( + client, + CSetEntityMetadata::new(entity_id.into(), vec![Metadata::new(18, VarInt(0), 0)]), + ) + } + */ // Server::spawn_test_chunk(client); } /// Sends a Packet to all Players - pub fn broadcast_packet

(&mut self, from: &Client, packet: P) + pub fn broadcast_packet

(&mut self, from: &mut Client, packet: P) + where + P: ClientPacket, + P: Clone, + { + // we can't borrow twice at same time + from.send_packet(packet.clone()); + for (_, client) in self.current_clients.iter().filter(|c| c.0 != &from.token) { + // Check if client is a player + let mut client = client.borrow_mut(); + if client.is_player() { + // we need to clone, Because we send a new packet to every client + client.send_packet(packet.clone()); + } + } + } + + pub fn broadcast_packet_expect

(&mut self, from: &mut Client, packet: P) where P: ClientPacket, P: Clone,