diff --git a/pumpkin-protocol/src/client/play/c_entity_velocity.rs b/pumpkin-protocol/src/client/play/c_entity_velocity.rs new file mode 100644 index 000000000..842fc1cda --- /dev/null +++ b/pumpkin-protocol/src/client/play/c_entity_velocity.rs @@ -0,0 +1,24 @@ +use pumpkin_macros::packet; +use serde::Serialize; + +use crate::VarInt; + +#[derive(Serialize)] +#[packet(0x5A)] +pub struct CEntityVelocity<'a> { + entitiy_id: &'a VarInt, + velocity_x: i16, + velocity_y: i16, + velocity_z: i16, +} + +impl<'a> CEntityVelocity<'a> { + pub fn new(entitiy_id: &'a VarInt, velocity_x: f32, velocity_y: f32, velocity_z: f32) -> Self { + Self { + entitiy_id, + velocity_x: (velocity_x.clamp(-3.9, 3.9) * 8000.0) as i16, + velocity_y: (velocity_y.clamp(-3.9, 3.9) * 8000.0) as i16, + velocity_z: (velocity_z.clamp(-3.9, 3.9) * 8000.0) as i16, + } + } +} diff --git a/pumpkin-protocol/src/client/play/c_hurt_animation.rs b/pumpkin-protocol/src/client/play/c_hurt_animation.rs index e58580a96..6166acbd3 100644 --- a/pumpkin-protocol/src/client/play/c_hurt_animation.rs +++ b/pumpkin-protocol/src/client/play/c_hurt_animation.rs @@ -5,13 +5,13 @@ use crate::VarInt; #[derive(Serialize)] #[packet(0x24)] -pub struct CHurtAnimation { - entitiy_id: VarInt, +pub struct CHurtAnimation<'a> { + entitiy_id: &'a VarInt, yaw: f32, } -impl CHurtAnimation { - pub fn new(entitiy_id: VarInt, yaw: f32) -> Self { +impl<'a> CHurtAnimation<'a> { + pub fn new(entitiy_id: &'a 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 576951143..122756990 100644 --- a/pumpkin-protocol/src/client/play/mod.rs +++ b/pumpkin-protocol/src/client/play/mod.rs @@ -5,6 +5,7 @@ mod c_chunk_data; mod c_disguised_chat_message; mod c_entity_animation; mod c_entity_metadata; +mod c_entity_velocity; mod c_game_event; mod c_head_rot; mod c_hurt_animation; @@ -34,6 +35,7 @@ pub use c_chunk_data::*; pub use c_disguised_chat_message::*; pub use c_entity_animation::*; pub use c_entity_metadata::*; +pub use c_entity_velocity::*; pub use c_game_event::*; pub use c_head_rot::*; pub use c_hurt_animation::*; diff --git a/pumpkin-world/src/radial_chunk_iterator.rs b/pumpkin-world/src/radial_chunk_iterator.rs index 4cf1ea6ad..5460d5a40 100644 --- a/pumpkin-world/src/radial_chunk_iterator.rs +++ b/pumpkin-world/src/radial_chunk_iterator.rs @@ -1,4 +1,3 @@ - pub struct RadialIterator { radius: i32, direction: usize, diff --git a/pumpkin/src/client/mod.rs b/pumpkin/src/client/mod.rs index 5bebd9c2c..1c30f0ea7 100644 --- a/pumpkin/src/client/mod.rs +++ b/pumpkin/src/client/mod.rs @@ -161,6 +161,13 @@ impl Client { self.send_packet(&CSyncPlayerPostion::new(x, y, z, yaw, pitch, 0, id.into())); } + pub fn update_health(&mut self, health: f32, food: i32, food_saturation: f32) { + let player = self.player.as_mut().unwrap(); + player.health = health; + player.food = food; + player.food_saturation = food_saturation; + } + pub fn set_gamemode(&mut self, gamemode: GameMode) { let player = self.player.as_mut().unwrap(); player.gamemode = gamemode; diff --git a/pumpkin/src/client/player_packet.rs b/pumpkin/src/client/player_packet.rs index d5d601203..f74acf9e4 100644 --- a/pumpkin/src/client/player_packet.rs +++ b/pumpkin/src/client/player_packet.rs @@ -3,8 +3,8 @@ use pumpkin_entity::EntityId; use pumpkin_inventory::WindowType; use pumpkin_protocol::{ client::play::{ - Animation, CEntityAnimation, CHeadRot, CHurtAnimation, COpenScreen, CUpdateEntityPos, - CUpdateEntityPosRot, CUpdateEntityRot, + Animation, CEntityAnimation, CEntityVelocity, CHeadRot, CHurtAnimation, COpenScreen, + CUpdateEntityPos, CUpdateEntityPosRot, CUpdateEntityRot, }, server::play::{ SChatCommand, SChatMessage, SClientInformationPlay, SConfirmTeleport, SInteract, @@ -114,8 +114,8 @@ impl Client { entity.x = Self::clamp_horizontal(position_rotation.x); entity.y = Self::clamp_vertical(position_rotation.feet_y); entity.z = Self::clamp_horizontal(position_rotation.z); - entity.yaw = wrap_degrees(position_rotation.yaw); - entity.pitch = wrap_degrees(position_rotation.pitch); + entity.yaw = wrap_degrees(position_rotation.yaw).clamp(-90.0, 90.0) % 360.0; + entity.pitch = wrap_degrees(position_rotation.pitch) % 360.0; // send new position to all other players let on_ground = player.on_ground; @@ -149,8 +149,8 @@ impl Client { } let player = self.player.as_mut().unwrap(); let entity = &mut player.entity; - entity.yaw = wrap_degrees(rotation.yaw); - entity.pitch = wrap_degrees(rotation.pitch); + entity.yaw = wrap_degrees(rotation.yaw).clamp(-90.0, 90.0) % 360.0; + entity.pitch = wrap_degrees(rotation.pitch) % 360.0; // send new position to all other players let on_ground = player.on_ground; let entity_id = entity.entity_id; @@ -195,7 +195,10 @@ impl Client { }; let player = self.player.as_mut().unwrap(); let id = player.entity_id(); - server.broadcast_packet_expect(self, &CEntityAnimation::new(id.into(), animation as u8)) + server.broadcast_packet_expect( + &[&self.token], + &CEntityAnimation::new(id.into(), animation as u8), + ) } pub fn handle_chat_message(&mut self, server: &mut Server, chat_message: SChatMessage) { @@ -264,21 +267,50 @@ impl Client { } pub fn handle_interact(&mut self, server: &mut Server, interact: SInteract) { + let attacker_player = self.player.as_ref().unwrap(); + let entity_id = interact.entity_id; // 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 - { + let attacked_client = server.get_by_entityid(self, entity_id.0 as EntityId); + if let Some(mut client) = attacked_client { + let token = client.token.clone(); + let player = client.player.as_mut().unwrap(); + let velo = player.velocity; + if config.protect_creative && player.gamemode == GameMode::Creative { return; } - drop(client); + if config.knockback { + let pitch = attacker_player.entity.pitch; + let strength = 1.0; + player.knockback( + strength * 0.5, + (pitch * 0.017453292).sin() as f64, + -(pitch * 0.017453292).cos() as f64, + ); + let packet = &CEntityVelocity::new( + &entity_id, + player.velocity.x as f32, + player.velocity.y as f32, + player.velocity.z as f32, + ); + player.velocity = velo; + client.send_packet(packet); + // attacker_player.velocity = attacker_player.velocity.multiply(0.6, 1.0, 0.6); + } if config.hurt_animation { // TODO - server.broadcast_packet(self, &CHurtAnimation::new(interact.entity_id, 10.0)) + // thats how we prevent borrow errors :c + let packet = &CHurtAnimation::new(&entity_id, 10.0); + self.send_packet(packet); + client.send_packet(packet); + server.broadcast_packet_expect( + &[self.token.as_ref(), token.as_ref()], + &CHurtAnimation::new(&entity_id, 10.0), + ) } + } else { + self.kick("Interacted with invalid entitiy id") } } } diff --git a/pumpkin/src/commands/gamemode.rs b/pumpkin/src/commands/gamemode.rs index 6432d58ba..fa00cbeb5 100644 --- a/pumpkin/src/commands/gamemode.rs +++ b/pumpkin/src/commands/gamemode.rs @@ -32,13 +32,14 @@ impl<'a> Command<'a> for GamemodeCommand { } Err(_) => { // try to parse from number - if let Ok(i) = mode_str.parse::() { if let Some(mode) = GameMode::from_u8(i) { - player.set_gamemode(mode); - player.send_system_message( - format!("Set own game mode to {:?}", mode).into(), - ); - return; - } } + if let Ok(i) = mode_str.parse::() { + if let Some(mode) = GameMode::from_u8(i) { + player.set_gamemode(mode); + player + .send_system_message(format!("Set own game mode to {:?}", mode).into()); + return; + } + } player.send_system_message( TextComponent::from("Invalid gamemode") diff --git a/pumpkin/src/config/mod.rs b/pumpkin/src/config/mod.rs index f4dc19219..5e76a8e14 100644 --- a/pumpkin/src/config/mod.rs +++ b/pumpkin/src/config/mod.rs @@ -65,6 +65,8 @@ pub struct PVPConfig { pub hurt_animation: bool, /// Should players in creative be protected against PVP pub protect_creative: bool, + /// Has PVP Knockback? + pub knockback: bool, } impl Default for PVPConfig { @@ -73,6 +75,7 @@ impl Default for PVPConfig { enabled: true, hurt_animation: true, protect_creative: true, + knockback: true, } } } diff --git a/pumpkin/src/entity/player.rs b/pumpkin/src/entity/player.rs index ae4b13472..52c02390d 100644 --- a/pumpkin/src/entity/player.rs +++ b/pumpkin/src/entity/player.rs @@ -5,10 +5,16 @@ use pumpkin_entity::{entity_type::EntityType, Entity, EntityId}; use pumpkin_protocol::VarInt; use serde::{Deserialize, Serialize}; +use crate::util::vec3::Vec3; + pub struct Player { pub entity: Entity, // current gamemode pub gamemode: GameMode, + // TODO: prbly should put this into an Living Entitiy or something + pub health: f32, + pub food: i32, + pub food_saturation: f32, // Client side value, Should be not trusted pub on_ground: bool, @@ -16,6 +22,8 @@ pub struct Player { pub sneaking: bool, pub sprinting: bool, + pub velocity: Vec3, + // Current awaiting teleport id, None if did not teleport pub awaiting_teleport: Option, } @@ -28,6 +36,11 @@ impl Player { awaiting_teleport: None, sneaking: false, sprinting: false, + // TODO: Load this from previous instance + health: 20.0, + food: 20, + food_saturation: 20.0, + velocity: Vec3::new(0.0, 0.0, 0.0), gamemode, } } @@ -35,6 +48,28 @@ impl Player { pub fn entity_id(&self) -> EntityId { self.entity.entity_id } + + pub fn knockback(&mut self, y: f64, x: f64, z: f64) { + // This has some vanilla magic + let mut x = x; + let mut z = z; + while x * x + z * z < 9.999999747378752E-6 { + x = (rand::random::() - rand::random::()) * 0.01; + z = (rand::random::() - rand::random::()) * 0.01; + } + + let var8 = Vec3::new(x, 0.0, z).normalize() * y; + let var7 = self.velocity; + self.velocity = Vec3::new( + var7.x / 2.0 - var8.x, + if self.on_ground { + (var7.y / 2.0 + x).min(0.4) + } else { + var7.y + }, + var7.z / 2.0 - var8.z, + ); + } } #[derive(FromPrimitive)] diff --git a/pumpkin/src/server.rs b/pumpkin/src/server.rs index b426a75e4..e395a68bc 100644 --- a/pumpkin/src/server.rs +++ b/pumpkin/src/server.rs @@ -134,8 +134,11 @@ impl Server { if client.is_player() { let id = client.player.as_ref().unwrap().entity_id(); let uuid = client.gameprofile.as_ref().unwrap().id; - self.broadcast_packet_expect(&client, &CRemovePlayerInfo::new(1.into(), &[UUID(uuid)])); - self.broadcast_packet_expect(&client, &CRemoveEntities::new(&[id.into()])) + self.broadcast_packet_expect( + &[&client.token], + &CRemovePlayerInfo::new(1.into(), &[UUID(uuid)]), + ); + self.broadcast_packet_expect(&[&client.token], &CRemoveEntities::new(&[id.into()])) } } @@ -232,7 +235,7 @@ impl Server { // spawn player for every client self.broadcast_packet_expect( - client, + &[&client.token], // TODO: add velo &CSpawnEntity::new( entity_id.into(), @@ -316,11 +319,15 @@ impl Server { } } - pub fn broadcast_packet_expect

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

(&self, from: &[&Token], packet: &P) where P: ClientPacket, { - for (_, client) in self.current_clients.iter().filter(|c| c.0 != &from.token) { + for (_, client) in self + .current_clients + .iter() + .filter(|c| !from.contains(&c.0.as_ref())) + { // Check if client is a player let mut client = client.borrow_mut(); if client.is_player() { @@ -339,7 +346,7 @@ impl Server { "./world".parse().unwrap(), ); level - .read_chunks(RadialIterator::new(2).collect(), sender) + .read_chunks(RadialIterator::new(7).collect(), sender) .await; }); diff --git a/pumpkin/src/util/mod.rs b/pumpkin/src/util/mod.rs index b8135c3c3..f538bb225 100644 --- a/pumpkin/src/util/mod.rs +++ b/pumpkin/src/util/mod.rs @@ -1 +1,2 @@ pub mod math; +pub mod vec3; diff --git a/pumpkin/src/util/vec3.rs b/pumpkin/src/util/vec3.rs new file mode 100644 index 000000000..0227302c7 --- /dev/null +++ b/pumpkin/src/util/vec3.rs @@ -0,0 +1,79 @@ +use std::ops::{Mul, Neg}; + +#[derive(Clone, Copy, Debug, PartialEq)] +pub struct Vec3 { + pub x: f64, + pub y: f64, + pub z: f64, +} + +impl Vec3 { + pub fn new(x: f64, y: f64, z: f64) -> Self { + Vec3 { x, y, z } + } + + pub fn length(&self) -> f64 { + (self.x * self.x + self.y * self.y + self.z * self.z).sqrt() + } + + pub fn normalize(&self) -> Self { + let length = self.length(); + Vec3 { + x: self.x / length, + y: self.y / length, + z: self.z / length, + } + } + + pub fn add(&self, other: &Vec3) -> Self { + Vec3 { + x: self.x + other.x, + y: self.y + other.y, + z: self.z + other.z, + } + } + + pub fn sub(&self, other: &Vec3) -> Self { + Vec3 { + x: self.x - other.x, + y: self.y - other.y, + z: self.z - other.z, + } + } + + pub fn multiply(self, x: f64, y: f64, z: f64) -> Self { + Self { + x: self.x * x, + y: self.y * y, + z: self.z * z, + } + } + + pub fn mul(self, scalar: f64) -> Self { + Vec3 { + x: self.x * scalar, + y: self.y * scalar, + z: self.z * scalar, + } + } +} + +impl Mul for Vec3 { + type Output = Self; + + fn mul(self, scalar: f64) -> Self { + self.mul(scalar) + } +} + +impl Neg for Vec3 { + type Output = Self; + + fn neg(self) -> Self { + Vec3 { + x: -self.x, + y: -self.y, + z: -self.z, + } + } +}