Add Basic Combat

This commit is contained in:
Snowiiii 2024-08-14 22:26:27 +02:00
parent 03cc991bfd
commit 6459aef027
12 changed files with 222 additions and 32 deletions

View File

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

View File

@ -5,13 +5,13 @@ use crate::VarInt;
#[derive(Serialize)] #[derive(Serialize)]
#[packet(0x24)] #[packet(0x24)]
pub struct CHurtAnimation { pub struct CHurtAnimation<'a> {
entitiy_id: VarInt, entitiy_id: &'a VarInt,
yaw: f32, yaw: f32,
} }
impl CHurtAnimation { impl<'a> CHurtAnimation<'a> {
pub fn new(entitiy_id: VarInt, yaw: f32) -> Self { pub fn new(entitiy_id: &'a VarInt, yaw: f32) -> Self {
Self { entitiy_id, yaw } Self { entitiy_id, yaw }
} }
} }

View File

@ -5,6 +5,7 @@ mod c_chunk_data;
mod c_disguised_chat_message; mod c_disguised_chat_message;
mod c_entity_animation; mod c_entity_animation;
mod c_entity_metadata; mod c_entity_metadata;
mod c_entity_velocity;
mod c_game_event; mod c_game_event;
mod c_head_rot; mod c_head_rot;
mod c_hurt_animation; mod c_hurt_animation;
@ -34,6 +35,7 @@ pub use c_chunk_data::*;
pub use c_disguised_chat_message::*; pub use c_disguised_chat_message::*;
pub use c_entity_animation::*; pub use c_entity_animation::*;
pub use c_entity_metadata::*; pub use c_entity_metadata::*;
pub use c_entity_velocity::*;
pub use c_game_event::*; pub use c_game_event::*;
pub use c_head_rot::*; pub use c_head_rot::*;
pub use c_hurt_animation::*; pub use c_hurt_animation::*;

View File

@ -1,4 +1,3 @@
pub struct RadialIterator { pub struct RadialIterator {
radius: i32, radius: i32,
direction: usize, direction: usize,

View File

@ -161,6 +161,13 @@ impl Client {
self.send_packet(&CSyncPlayerPostion::new(x, y, z, yaw, pitch, 0, id.into())); 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) { pub fn set_gamemode(&mut self, gamemode: GameMode) {
let player = self.player.as_mut().unwrap(); let player = self.player.as_mut().unwrap();
player.gamemode = gamemode; player.gamemode = gamemode;

View File

@ -3,8 +3,8 @@ use pumpkin_entity::EntityId;
use pumpkin_inventory::WindowType; use pumpkin_inventory::WindowType;
use pumpkin_protocol::{ use pumpkin_protocol::{
client::play::{ client::play::{
Animation, CEntityAnimation, CHeadRot, CHurtAnimation, COpenScreen, CUpdateEntityPos, Animation, CEntityAnimation, CEntityVelocity, CHeadRot, CHurtAnimation, COpenScreen,
CUpdateEntityPosRot, CUpdateEntityRot, CUpdateEntityPos, CUpdateEntityPosRot, CUpdateEntityRot,
}, },
server::play::{ server::play::{
SChatCommand, SChatMessage, SClientInformationPlay, SConfirmTeleport, SInteract, SChatCommand, SChatMessage, SClientInformationPlay, SConfirmTeleport, SInteract,
@ -114,8 +114,8 @@ impl Client {
entity.x = Self::clamp_horizontal(position_rotation.x); entity.x = Self::clamp_horizontal(position_rotation.x);
entity.y = Self::clamp_vertical(position_rotation.feet_y); entity.y = Self::clamp_vertical(position_rotation.feet_y);
entity.z = Self::clamp_horizontal(position_rotation.z); entity.z = Self::clamp_horizontal(position_rotation.z);
entity.yaw = wrap_degrees(position_rotation.yaw); entity.yaw = wrap_degrees(position_rotation.yaw).clamp(-90.0, 90.0) % 360.0;
entity.pitch = wrap_degrees(position_rotation.pitch); entity.pitch = wrap_degrees(position_rotation.pitch) % 360.0;
// send new position to all other players // send new position to all other players
let on_ground = player.on_ground; let on_ground = player.on_ground;
@ -149,8 +149,8 @@ impl Client {
} }
let player = self.player.as_mut().unwrap(); let player = self.player.as_mut().unwrap();
let entity = &mut player.entity; let entity = &mut player.entity;
entity.yaw = wrap_degrees(rotation.yaw); entity.yaw = wrap_degrees(rotation.yaw).clamp(-90.0, 90.0) % 360.0;
entity.pitch = wrap_degrees(rotation.pitch); entity.pitch = wrap_degrees(rotation.pitch) % 360.0;
// send new position to all other players // send new position to all other players
let on_ground = player.on_ground; let on_ground = player.on_ground;
let entity_id = entity.entity_id; let entity_id = entity.entity_id;
@ -195,7 +195,10 @@ impl Client {
}; };
let player = self.player.as_mut().unwrap(); let player = self.player.as_mut().unwrap();
let id = player.entity_id(); 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) { 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) { 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 // TODO: do validation and stuff
let config = &server.advanced_config.pvp; let config = &server.advanced_config.pvp;
if config.enabled { if config.enabled {
let attacked_client = server.get_by_entityid(self, interact.entity_id.0 as EntityId); let attacked_client = server.get_by_entityid(self, entity_id.0 as EntityId);
if let Some(client) = attacked_client { if let Some(mut client) = attacked_client {
if config.protect_creative let token = client.token.clone();
&& client.player.as_ref().unwrap().gamemode == GameMode::Creative let player = client.player.as_mut().unwrap();
{ let velo = player.velocity;
if config.protect_creative && player.gamemode == GameMode::Creative {
return; 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 { if config.hurt_animation {
// TODO // 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")
} }
} }
} }

View File

@ -32,13 +32,14 @@ impl<'a> Command<'a> for GamemodeCommand {
} }
Err(_) => { Err(_) => {
// try to parse from number // try to parse from number
if let Ok(i) = mode_str.parse::<u8>() { if let Some(mode) = GameMode::from_u8(i) { if let Ok(i) = mode_str.parse::<u8>() {
if let Some(mode) = GameMode::from_u8(i) {
player.set_gamemode(mode); player.set_gamemode(mode);
player.send_system_message( player
format!("Set own game mode to {:?}", mode).into(), .send_system_message(format!("Set own game mode to {:?}", mode).into());
);
return; return;
} } }
}
player.send_system_message( player.send_system_message(
TextComponent::from("Invalid gamemode") TextComponent::from("Invalid gamemode")

View File

@ -65,6 +65,8 @@ pub struct PVPConfig {
pub hurt_animation: bool, pub hurt_animation: bool,
/// Should players in creative be protected against PVP /// Should players in creative be protected against PVP
pub protect_creative: bool, pub protect_creative: bool,
/// Has PVP Knockback?
pub knockback: bool,
} }
impl Default for PVPConfig { impl Default for PVPConfig {
@ -73,6 +75,7 @@ impl Default for PVPConfig {
enabled: true, enabled: true,
hurt_animation: true, hurt_animation: true,
protect_creative: true, protect_creative: true,
knockback: true,
} }
} }
} }

View File

@ -5,10 +5,16 @@ use pumpkin_entity::{entity_type::EntityType, Entity, EntityId};
use pumpkin_protocol::VarInt; use pumpkin_protocol::VarInt;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use crate::util::vec3::Vec3;
pub struct Player { pub struct Player {
pub entity: Entity, pub entity: Entity,
// current gamemode // current gamemode
pub gamemode: 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 // Client side value, Should be not trusted
pub on_ground: bool, pub on_ground: bool,
@ -16,6 +22,8 @@ pub struct Player {
pub sneaking: bool, pub sneaking: bool,
pub sprinting: bool, pub sprinting: bool,
pub velocity: Vec3,
// Current awaiting teleport id, None if did not teleport // Current awaiting teleport id, None if did not teleport
pub awaiting_teleport: Option<VarInt>, pub awaiting_teleport: Option<VarInt>,
} }
@ -28,6 +36,11 @@ impl Player {
awaiting_teleport: None, awaiting_teleport: None,
sneaking: false, sneaking: false,
sprinting: 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, gamemode,
} }
} }
@ -35,6 +48,28 @@ impl Player {
pub fn entity_id(&self) -> EntityId { pub fn entity_id(&self) -> EntityId {
self.entity.entity_id 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::<f64>() - rand::random::<f64>()) * 0.01;
z = (rand::random::<f64>() - rand::random::<f64>()) * 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)] #[derive(FromPrimitive)]

View File

@ -134,8 +134,11 @@ impl Server {
if client.is_player() { if client.is_player() {
let id = client.player.as_ref().unwrap().entity_id(); let id = client.player.as_ref().unwrap().entity_id();
let uuid = client.gameprofile.as_ref().unwrap().id; let uuid = client.gameprofile.as_ref().unwrap().id;
self.broadcast_packet_expect(&client, &CRemovePlayerInfo::new(1.into(), &[UUID(uuid)])); self.broadcast_packet_expect(
self.broadcast_packet_expect(&client, &CRemoveEntities::new(&[id.into()])) &[&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 // spawn player for every client
self.broadcast_packet_expect( self.broadcast_packet_expect(
client, &[&client.token],
// TODO: add velo // TODO: add velo
&CSpawnEntity::new( &CSpawnEntity::new(
entity_id.into(), entity_id.into(),
@ -316,11 +319,15 @@ impl Server {
} }
} }
pub fn broadcast_packet_expect<P>(&self, from: &Client, packet: &P) pub fn broadcast_packet_expect<P>(&self, from: &[&Token], packet: &P)
where where
P: ClientPacket, 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 // Check if client is a player
let mut client = client.borrow_mut(); let mut client = client.borrow_mut();
if client.is_player() { if client.is_player() {
@ -339,7 +346,7 @@ impl Server {
"./world".parse().unwrap(), "./world".parse().unwrap(),
); );
level level
.read_chunks(RadialIterator::new(2).collect(), sender) .read_chunks(RadialIterator::new(7).collect(), sender)
.await; .await;
}); });

View File

@ -1 +1,2 @@
pub mod math; pub mod math;
pub mod vec3;

79
pumpkin/src/util/vec3.rs Normal file
View File

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