mirror of
https://github.com/Pumpkin-MC/Pumpkin.git
synced 2026-08-31 08:22:33 +00:00
Add Basic Combat
This commit is contained in:
24
pumpkin-protocol/src/client/play/c_entity_velocity.rs
Normal file
24
pumpkin-protocol/src/client/play/c_entity_velocity.rs
Normal 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,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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 }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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::*;
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
|
||||
pub struct RadialIterator {
|
||||
radius: i32,
|
||||
direction: usize,
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -32,13 +32,14 @@ impl<'a> Command<'a> for GamemodeCommand {
|
||||
}
|
||||
Err(_) => {
|
||||
// try to parse from number
|
||||
if let Ok(i) = mode_str.parse::<u8>() { 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::<u8>() {
|
||||
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")
|
||||
|
||||
@@ -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,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<VarInt>,
|
||||
}
|
||||
@@ -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::<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)]
|
||||
|
||||
@@ -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<P>(&self, from: &Client, packet: &P)
|
||||
pub fn broadcast_packet_expect<P>(&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;
|
||||
});
|
||||
|
||||
|
||||
@@ -1 +1,2 @@
|
||||
pub mod math;
|
||||
pub mod vec3;
|
||||
|
||||
79
pumpkin/src/util/vec3.rs
Normal file
79
pumpkin/src/util/vec3.rs
Normal 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,
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user