diff --git a/pumpkin-protocol/src/client/play/c_hurt_animation.rs b/pumpkin-protocol/src/client/play/c_hurt_animation.rs new file mode 100644 index 000000000..e58580a96 --- /dev/null +++ b/pumpkin-protocol/src/client/play/c_hurt_animation.rs @@ -0,0 +1,17 @@ +use pumpkin_macros::packet; +use serde::Serialize; + +use crate::VarInt; + +#[derive(Serialize)] +#[packet(0x24)] +pub struct CHurtAnimation { + entitiy_id: VarInt, + yaw: f32, +} + +impl CHurtAnimation { + pub fn new(entitiy_id: VarInt, yaw: f32) -> Self { + Self { entitiy_id, yaw } + } +} diff --git a/pumpkin-protocol/src/client/play/mod.rs b/pumpkin-protocol/src/client/play/mod.rs index 9bb6b2861..576951143 100644 --- a/pumpkin-protocol/src/client/play/mod.rs +++ b/pumpkin-protocol/src/client/play/mod.rs @@ -7,6 +7,7 @@ mod c_entity_animation; mod c_entity_metadata; mod c_game_event; mod c_head_rot; +mod c_hurt_animation; mod c_login; mod c_open_screen; mod c_play_disconnect; @@ -35,6 +36,7 @@ pub use c_entity_animation::*; pub use c_entity_metadata::*; pub use c_game_event::*; pub use c_head_rot::*; +pub use c_hurt_animation::*; pub use c_login::*; pub use c_open_screen::*; pub use c_play_disconnect::*; diff --git a/pumpkin-protocol/src/server/play/c_interact.rs b/pumpkin-protocol/src/server/play/c_interact.rs new file mode 100644 index 000000000..ec54a626f --- /dev/null +++ b/pumpkin-protocol/src/server/play/c_interact.rs @@ -0,0 +1,25 @@ +use pumpkin_macros::packet; +use serde::Deserialize; + +use crate::{ServerPacket, VarInt}; + +#[packet(0x16)] +pub struct SInteract { + pub entity_id: VarInt, + pub typ: VarInt, + pub target_x: Option, + // don't ask me why, adding more values does not work :c +} + +// TODO +impl ServerPacket for SInteract { + fn read( + bytebuf: &mut crate::bytebuf::ByteBuffer, + ) -> Result { + Ok(Self { + entity_id: bytebuf.get_var_int(), + typ: bytebuf.get_var_int(), + target_x: bytebuf.get_option(|v| v.get_f32()), + }) + } +} diff --git a/pumpkin-protocol/src/server/play/mod.rs b/pumpkin-protocol/src/server/play/mod.rs index 977320703..8394c0d67 100644 --- a/pumpkin-protocol/src/server/play/mod.rs +++ b/pumpkin-protocol/src/server/play/mod.rs @@ -1,4 +1,5 @@ mod c_client_information; +mod c_interact; mod s_chat_command; mod s_chat_message; mod s_confirm_teleport; @@ -9,6 +10,7 @@ mod s_player_rotation; mod s_swing_arm; pub use c_client_information::*; +pub use c_interact::*; pub use s_chat_command::*; pub use s_chat_message::*; pub use s_confirm_teleport::*; diff --git a/pumpkin-protocol/src/server/play/s_chat_message.rs b/pumpkin-protocol/src/server/play/s_chat_message.rs index d2e34de03..51d47bd17 100644 --- a/pumpkin-protocol/src/server/play/s_chat_message.rs +++ b/pumpkin-protocol/src/server/play/s_chat_message.rs @@ -16,6 +16,7 @@ pub struct SChatMessage { // acknowledged: BitSet, } +// TODO impl ServerPacket for SChatMessage { fn read(bytebuf: &mut ByteBuffer) -> Result { Ok(Self { diff --git a/pumpkin-world/src/block_registry.rs b/pumpkin-world/src/block_registry.rs index b429ec079..600642a64 100644 --- a/pumpkin-world/src/block_registry.rs +++ b/pumpkin-world/src/block_registry.rs @@ -4,7 +4,7 @@ use lazy_static::lazy_static; use crate::world::WorldError; -const BLOCKS_JSON: &'static str = include_str!("../blocks.json"); +const BLOCKS_JSON: &str = include_str!("../blocks.json"); #[derive(serde::Deserialize, Debug, Clone, PartialEq, Eq)] struct BlockDefinition { diff --git a/pumpkin-world/src/chunk.rs b/pumpkin-world/src/chunk.rs index c3ebeddba..60729c83f 100644 --- a/pumpkin-world/src/chunk.rs +++ b/pumpkin-world/src/chunk.rs @@ -25,10 +25,9 @@ // } // } -use std::{collections::HashMap, io::Write}; +use std::collections::HashMap; use fastnbt::LongArray; -use itertools::Itertools; use crate::{world::WorldError, WORLD_HEIGHT}; @@ -157,7 +156,7 @@ impl ChunkData { // } // dbg!("{}", blocks.iter().filter(|v| **v == 2005).collect_vec().len()); Ok(ChunkData { - blocks: blocks, + blocks, position: at, heightmaps: chunk_data.heightmaps, }) diff --git a/pumpkin-world/src/world.rs b/pumpkin-world/src/world.rs index 7f5d2ce88..f41582b51 100644 --- a/pumpkin-world/src/world.rs +++ b/pumpkin-world/src/world.rs @@ -1,15 +1,15 @@ -use std::{collections::VecDeque, future, io::Read, path::PathBuf, sync::Arc}; +use std::{io::Read, path::PathBuf, sync::Arc}; use flate2::bufread::ZlibDecoder; use itertools::Itertools; use thiserror::Error; use tokio::{ fs::File, - io::{AsyncReadExt, AsyncSeekExt, AsyncWriteExt}, + io::{AsyncReadExt, AsyncSeekExt}, sync::Mutex, }; -use crate::{chunk::ChunkData, dimension::Dimension}; +use crate::chunk::ChunkData; pub struct Level { root_folder: PathBuf, diff --git a/pumpkin/src/client/mod.rs b/pumpkin/src/client/mod.rs index a7cd44f53..68404b971 100644 --- a/pumpkin/src/client/mod.rs +++ b/pumpkin/src/client/mod.rs @@ -27,8 +27,8 @@ use pumpkin_protocol::{ handshake::SHandShake, login::{SEncryptionResponse, SLoginAcknowledged, SLoginPluginResponse, SLoginStart}, play::{ - SChatCommand, SChatMessage, SClientInformationPlay, SConfirmTeleport, SPlayerCommand, - SPlayerPosition, SPlayerPositionRotation, SPlayerRotation, SSwingArm, + SChatCommand, SChatMessage, SClientInformationPlay, SConfirmTeleport, SInteract, + SPlayerCommand, SPlayerPosition, SPlayerPositionRotation, SPlayerRotation, SSwingArm, }, status::{SPingRequest, SStatusRequest}, }, @@ -159,6 +159,8 @@ impl Client { } pub fn set_gamemode(&mut self, gamemode: GameMode) { + let player = self.player.as_mut().unwrap(); + player.gamemode = gamemode; self.send_packet(&CGameEvent::new(3, gamemode.to_f32().unwrap())); } @@ -282,6 +284,9 @@ impl Client { server, SClientInformationPlay::read(bytebuf).unwrap(), ), + SInteract::PACKET_ID => { + self.handle_interact(server, SInteract::read(bytebuf).unwrap()); + } _ => 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 615cd15f8..a96c8abee 100644 --- a/pumpkin/src/client/player_packet.rs +++ b/pumpkin/src/client/player_packet.rs @@ -1,13 +1,14 @@ use num_traits::FromPrimitive; +use pumpkin_entity::EntityId; use pumpkin_inventory::WindowType; use pumpkin_protocol::{ client::play::{ - Animation, CEntityAnimation, CHeadRot, COpenScreen, CUpdateEntityPos, CUpdateEntityPosRot, - CUpdateEntityRot, + Animation, CEntityAnimation, CHeadRot, CHurtAnimation, COpenScreen, CUpdateEntityPos, + CUpdateEntityPosRot, CUpdateEntityRot, }, server::play::{ - SChatCommand, SChatMessage, SClientInformationPlay, SConfirmTeleport, SPlayerCommand, - SPlayerPosition, SPlayerPositionRotation, SPlayerRotation, SSwingArm, + SChatCommand, SChatMessage, SClientInformationPlay, SConfirmTeleport, SInteract, + SPlayerCommand, SPlayerPosition, SPlayerPositionRotation, SPlayerRotation, SSwingArm, }, VarInt, }; @@ -15,7 +16,7 @@ use pumpkin_text::TextComponent; use crate::{ commands::{handle_command, CommandSender}, - entity::player::{ChatMode, Hand}, + entity::player::{ChatMode, GameMode, Hand}, server::Server, util::math::wrap_degrees, }; @@ -256,4 +257,24 @@ impl Client { server_listing: client_information.server_listing, }); } + + pub fn handle_interact(&mut self, server: &mut Server, interact: SInteract) { + // TODO: do validation and stuff + let config = &server.advanced_config.pvp; + if config.enabled { + let attacked_client = server.get_by_entityid(self, interact.entity_id.0 as EntityId); + if let Some(client) = attacked_client { + if config.protect_creative + && client.player.as_ref().unwrap().gamemode == GameMode::Creative + { + return; + } + drop(client); + if config.hurt_animation { + // todo + server.broadcast_packet(self, &CHurtAnimation::new(interact.entity_id, 10.0)) + } + } + } + } } diff --git a/pumpkin/src/config/auth_config.rs b/pumpkin/src/config/auth_config.rs index 936ed9dda..09c4188ff 100644 --- a/pumpkin/src/config/auth_config.rs +++ b/pumpkin/src/config/auth_config.rs @@ -3,7 +3,7 @@ use serde::{Deserialize, Serialize}; use crate::client::authentication::ProfileAction; #[derive(Deserialize, Serialize)] -pub struct Authentication { +pub struct AuthenticationConfig { /// Whether to use Mojang authentication. pub enabled: bool, @@ -81,7 +81,7 @@ impl Default for TextureTypes { } } -impl Default for Authentication { +impl Default for AuthenticationConfig { fn default() -> Self { Self { enabled: true, diff --git a/pumpkin/src/config/mod.rs b/pumpkin/src/config/mod.rs index ee84669e0..f4dc19219 100644 --- a/pumpkin/src/config/mod.rs +++ b/pumpkin/src/config/mod.rs @@ -1,7 +1,7 @@ use std::path::Path; -use auth_config::Authentication; -use resource_pack::ResourcePack; +use auth_config::AuthenticationConfig; +use resource_pack::ResourcePackConfig; use serde::{Deserialize, Serialize}; use crate::{entity::player::GameMode, server::Difficulty}; @@ -17,11 +17,12 @@ 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 { - pub commands: Commands, - pub authentication: Authentication, - pub packet_compression: Compression, - pub resource_pack: ResourcePack, + pub commands: CommandsConfig, + pub authentication: AuthenticationConfig, + pub packet_compression: CompressionConfig, + pub resource_pack: ResourcePackConfig, pub rcon: RCONConfig, + pub pvp: PVPConfig, } #[derive(Deserialize, Serialize, Clone)] @@ -44,26 +45,52 @@ impl Default for RCONConfig { } #[derive(Deserialize, Serialize)] -pub struct Commands { +pub struct CommandsConfig { /// Are commands from the Console accepted ? pub use_console: bool, // TODO: commands... } +impl Default for CommandsConfig { + fn default() -> Self { + Self { use_console: true } + } +} + +#[derive(Deserialize, Serialize)] +pub struct PVPConfig { + /// Is PVP enabled ? + pub enabled: bool, + /// Do we want to have the Red hurt animation & fov bobbing + pub hurt_animation: bool, + /// Should players in creative be protected against PVP + pub protect_creative: bool, +} + +impl Default for PVPConfig { + fn default() -> Self { + Self { + enabled: true, + hurt_animation: true, + protect_creative: true, + } + } +} + #[derive(Deserialize, Serialize)] // Packet compression -pub struct Compression { - // Is compression enabled ? +pub struct CompressionConfig { + /// Is compression enabled ? pub enabled: bool, - // The compression threshold used when compression is enabled + /// The compression threshold used when compression is enabled pub compression_threshold: u32, - // A value between 0..9 - // 1 = Optimize for the best speed of encoding. - // 9 = Optimize for the size of data being encoded. + /// A value between 0..9 + /// 1 = Optimize for the best speed of encoding. + /// 9 = Optimize for the size of data being encoded. pub compression_level: u32, } -impl Default for Compression { +impl Default for CompressionConfig { fn default() -> Self { Self { enabled: true, @@ -73,21 +100,16 @@ impl Default for Compression { } } -impl Default for Commands { - fn default() -> Self { - Self { use_console: true } - } -} - /// Important: The Configuration should match Vanilla by default impl Default for AdvancedConfiguration { fn default() -> Self { Self { - authentication: Authentication::default(), - commands: Commands::default(), - packet_compression: Compression::default(), - resource_pack: ResourcePack::default(), + authentication: AuthenticationConfig::default(), + commands: CommandsConfig::default(), + packet_compression: CompressionConfig::default(), + resource_pack: ResourcePackConfig::default(), rcon: RCONConfig::default(), + pvp: PVPConfig::default(), } } } diff --git a/pumpkin/src/config/resource_pack.rs b/pumpkin/src/config/resource_pack.rs index 25e97d759..f3479240e 100644 --- a/pumpkin/src/config/resource_pack.rs +++ b/pumpkin/src/config/resource_pack.rs @@ -1,7 +1,7 @@ use serde::{Deserialize, Serialize}; #[derive(Deserialize, Serialize)] -pub struct ResourcePack { +pub struct ResourcePackConfig { pub enabled: bool, /// The path to the resource pack. pub resource_pack_url: String, @@ -13,7 +13,7 @@ pub struct ResourcePack { pub force: bool, } -impl ResourcePack { +impl ResourcePackConfig { pub fn validate(&self) { assert_eq!( !self.resource_pack_url.is_empty(), @@ -27,7 +27,7 @@ impl ResourcePack { } } -impl Default for ResourcePack { +impl Default for ResourcePackConfig { fn default() -> Self { Self { enabled: false, diff --git a/pumpkin/src/entity/player.rs b/pumpkin/src/entity/player.rs index 23c690789..07ccab380 100644 --- a/pumpkin/src/entity/player.rs +++ b/pumpkin/src/entity/player.rs @@ -7,6 +7,8 @@ use serde::{Deserialize, Serialize}; pub struct Player { pub entity: Entity, + // current gamemode + pub gamemode: GameMode, // Client side value, Should be not trusted pub on_ground: bool, @@ -19,13 +21,14 @@ pub struct Player { } impl Player { - pub fn new(entity_id: EntityId) -> Self { + pub fn new(entity_id: EntityId, gamemode: GameMode) -> Self { Self { entity: Entity::new(entity_id, EntityType::Player), on_ground: false, awaiting_teleport: None, sneaking: false, sprinting: false, + gamemode, } } diff --git a/pumpkin/src/server.rs b/pumpkin/src/server.rs index 636f053d8..578aab5d2 100644 --- a/pumpkin/src/server.rs +++ b/pumpkin/src/server.rs @@ -1,5 +1,5 @@ use std::{ - cell::RefCell, + cell::{RefCell, RefMut}, collections::HashMap, io::Cursor, rc::Rc, @@ -23,7 +23,7 @@ use pumpkin_protocol::{ }, }, uuid::UUID, - BitSet, ClientPacket, Players, Sample, StatusResponse, VarInt, Version, CURRENT_MC_PROTOCOL, + ClientPacket, Players, Sample, StatusResponse, VarInt, Version, CURRENT_MC_PROTOCOL, }; use pumpkin_world::dimension::Dimension; @@ -146,8 +146,12 @@ impl Server { pub async fn spawn_player(&mut self, client: &mut Client) { // This code follows the vanilla packet order let entity_id = self.new_entity_id(); + let gamemode = match self.base_config.default_gamemode { + GameMode::Undefined => GameMode::Survival, + game_mode => game_mode, + }; log::debug!("spawning player, entity id {}", entity_id); - let player = Player::new(entity_id); + let player = Player::new(entity_id, gamemode); client.player = Some(player); // login packet for our new player @@ -164,12 +168,7 @@ impl Server { 0.into(), "minecraft:overworld", 0, // seed - match self.base_config.default_gamemode { - GameMode::Undefined => GameMode::Survival, - game_mode => game_mode, - } - .to_u8() - .unwrap(), + gamemode.to_u8().unwrap(), self.base_config.default_gamemode.to_i8().unwrap(), false, false, @@ -291,8 +290,20 @@ impl Server { Server::spawn_test_chunk(client).await; } + /// TODO: This definitly should be in world + pub fn get_by_entityid(&self, from: &Client, id: EntityId) -> Option> { + for (_, client) in self.current_clients.iter().filter(|c| c.0 != &from.token) { + // Check if client is a player + let client = client.borrow_mut(); + if client.is_player() && client.player.as_ref().unwrap().entity_id() == id { + return Some(client); + } + } + None + } + /// Sends a Packet to all Players - pub fn broadcast_packet

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

(&self, from: &mut Client, packet: &P) where P: ClientPacket, { @@ -308,7 +319,7 @@ impl Server { } } - pub fn broadcast_packet_expect

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

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