fix: 26.1 attack packet
This commit is contained in:
parent
7f95d43982
commit
8a6698cd21
@ -110,6 +110,13 @@ impl ItemStack {
|
|||||||
patch: Vec::new(),
|
patch: Vec::new(),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
pub fn split_off(&mut self, amount: u8) -> Self {
|
||||||
|
let count = amount.min(self.item_count);
|
||||||
|
let result = self.copy_with_count(count);
|
||||||
|
self.decrement(count);
|
||||||
|
result
|
||||||
|
}
|
||||||
|
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn get_max_stack_size(&self) -> u8 {
|
pub fn get_max_stack_size(&self) -> u8 {
|
||||||
self.get_data_component::<MaxStackSizeImpl>()
|
self.get_data_component::<MaxStackSizeImpl>()
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
use std::{any::Any, pin::Pin, sync::Arc};
|
use std::{any::Any, pin::Pin, sync::Arc};
|
||||||
|
|
||||||
use pumpkin_data::screen::WindowType;
|
|
||||||
use pumpkin_data::tag::Taggable;
|
use pumpkin_data::tag::Taggable;
|
||||||
|
use pumpkin_data::{screen::WindowType, tag};
|
||||||
use pumpkin_world::{block::entities::PropertyDelegate, inventory::Inventory};
|
use pumpkin_world::{block::entities::PropertyDelegate, inventory::Inventory};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
@ -122,10 +122,7 @@ impl ScreenHandler for BrewingScreenHandler {
|
|||||||
.is_some();
|
.is_some();
|
||||||
|
|
||||||
// Check if item is brewing fuel (for slot 4)
|
// Check if item is brewing fuel (for slot 4)
|
||||||
let is_fuel = stack
|
let is_fuel = stack.get_item().has_tag(&tag::Item::MINECRAFT_BREWING_FUEL);
|
||||||
.get_item()
|
|
||||||
.is_tagged_with("minecraft:brewing_fuel")
|
|
||||||
.is_some_and(|b| b);
|
|
||||||
|
|
||||||
if has_potion_contents {
|
if has_potion_contents {
|
||||||
// Try to insert into potion slots (0-2)
|
// Try to insert into potion slots (0-2)
|
||||||
|
|||||||
@ -13,9 +13,9 @@ use serde::{
|
|||||||
};
|
};
|
||||||
|
|
||||||
#[derive(Clone, Copy)]
|
#[derive(Clone, Copy)]
|
||||||
pub struct Velocity(pub Vector3<f64>);
|
pub struct LpVector3d(pub Vector3<f64>);
|
||||||
|
|
||||||
impl Velocity {
|
impl LpVector3d {
|
||||||
pub fn write<W: std::io::Write>(&self, writer: &mut W) -> Result<(), WritingError> {
|
pub fn write<W: std::io::Write>(&self, writer: &mut W) -> Result<(), WritingError> {
|
||||||
let velocity = self.0;
|
let velocity = self.0;
|
||||||
|
|
||||||
@ -153,7 +153,7 @@ fn from_long(quantized: i64, scale: f64) -> f64 {
|
|||||||
(normalized / 0.5) * scale
|
(normalized / 0.5) * scale
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Serialize for Velocity {
|
impl Serialize for LpVector3d {
|
||||||
fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
|
fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
|
||||||
let mut buf = Vec::new();
|
let mut buf = Vec::new();
|
||||||
self.write(&mut buf).unwrap();
|
self.write(&mut buf).unwrap();
|
||||||
@ -161,7 +161,7 @@ impl Serialize for Velocity {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'de> de::Deserialize<'de> for Velocity {
|
impl<'de> de::Deserialize<'de> for LpVector3d {
|
||||||
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
||||||
where
|
where
|
||||||
D: Deserializer<'de>,
|
D: Deserializer<'de>,
|
||||||
@ -169,7 +169,7 @@ impl<'de> de::Deserialize<'de> for Velocity {
|
|||||||
struct VelocityVisitor;
|
struct VelocityVisitor;
|
||||||
|
|
||||||
impl Visitor<'_> for VelocityVisitor {
|
impl Visitor<'_> for VelocityVisitor {
|
||||||
type Value = Velocity;
|
type Value = LpVector3d;
|
||||||
|
|
||||||
fn expecting(&self, formatter: &mut Formatter) -> std::fmt::Result {
|
fn expecting(&self, formatter: &mut Formatter) -> std::fmt::Result {
|
||||||
formatter.write_str("a byte array representing bit-packed velocity")
|
formatter.write_str("a byte array representing bit-packed velocity")
|
||||||
@ -180,7 +180,7 @@ impl<'de> de::Deserialize<'de> for Velocity {
|
|||||||
E: de::Error,
|
E: de::Error,
|
||||||
{
|
{
|
||||||
let mut cursor = std::io::Cursor::new(v);
|
let mut cursor = std::io::Cursor::new(v);
|
||||||
Velocity::read(&mut cursor).map_err(de::Error::custom)
|
LpVector3d::read(&mut cursor).map_err(de::Error::custom)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -190,7 +190,7 @@ impl<'de> de::Deserialize<'de> for Velocity {
|
|||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::{Velocity, encode_legacy_velocity_component};
|
use super::{LpVector3d, encode_legacy_velocity_component};
|
||||||
use pumpkin_util::math::vector3::Vector3;
|
use pumpkin_util::math::vector3::Vector3;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@ -203,7 +203,7 @@ mod tests {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn write_legacy_writes_three_i16_be_components() {
|
fn write_legacy_writes_three_i16_be_components() {
|
||||||
let velocity = Velocity(Vector3::new(0.5, -0.5, 0.0));
|
let velocity = LpVector3d(Vector3::new(0.5, -0.5, 0.0));
|
||||||
let mut buf = Vec::new();
|
let mut buf = Vec::new();
|
||||||
velocity.write_legacy(&mut buf).unwrap();
|
velocity.write_legacy(&mut buf).unwrap();
|
||||||
assert_eq!(buf, vec![0x0F, 0xA0, 0xF0, 0x60, 0x00, 0x00]);
|
assert_eq!(buf, vec![0x0F, 0xA0, 0xF0, 0x60, 0x00, 0x00]);
|
||||||
@ -4,6 +4,7 @@ pub mod bitset;
|
|||||||
pub mod data_component;
|
pub mod data_component;
|
||||||
pub mod item_stack_seralizer;
|
pub mod item_stack_seralizer;
|
||||||
pub mod little_endian;
|
pub mod little_endian;
|
||||||
|
pub mod lp_vector_3d;
|
||||||
pub mod optional_int;
|
pub mod optional_int;
|
||||||
mod u24_type;
|
mod u24_type;
|
||||||
pub mod uuid;
|
pub mod uuid;
|
||||||
@ -11,6 +12,5 @@ pub mod var_int;
|
|||||||
pub mod var_long;
|
pub mod var_long;
|
||||||
pub mod var_uint;
|
pub mod var_uint;
|
||||||
pub mod var_ulong;
|
pub mod var_ulong;
|
||||||
pub mod velocity;
|
|
||||||
|
|
||||||
pub use u24_type::u24;
|
pub use u24_type::u24;
|
||||||
|
|||||||
@ -4,7 +4,9 @@ use pumpkin_data::packet::clientbound::PLAY_SET_ENTITY_MOTION;
|
|||||||
use pumpkin_macros::java_packet;
|
use pumpkin_macros::java_packet;
|
||||||
use pumpkin_util::{math::vector3::Vector3, version::MinecraftVersion};
|
use pumpkin_util::{math::vector3::Vector3, version::MinecraftVersion};
|
||||||
|
|
||||||
use crate::{ClientPacket, VarInt, WritingError, codec::velocity::Velocity, ser::NetworkWriteExt};
|
use crate::{
|
||||||
|
ClientPacket, VarInt, WritingError, codec::lp_vector_3d::LpVector3d, ser::NetworkWriteExt,
|
||||||
|
};
|
||||||
|
|
||||||
/// Updates the velocity of an entity.
|
/// Updates the velocity of an entity.
|
||||||
///
|
///
|
||||||
@ -15,7 +17,7 @@ pub struct CEntityVelocity {
|
|||||||
/// The Entity ID of the entity whose velocity is being set
|
/// The Entity ID of the entity whose velocity is being set
|
||||||
pub entity_id: VarInt,
|
pub entity_id: VarInt,
|
||||||
/// The velocity vector
|
/// The velocity vector
|
||||||
pub velocity: Velocity,
|
pub velocity: LpVector3d,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl CEntityVelocity {
|
impl CEntityVelocity {
|
||||||
@ -23,7 +25,7 @@ impl CEntityVelocity {
|
|||||||
pub const fn new(entity_id: VarInt, velocity: Vector3<f64>) -> Self {
|
pub const fn new(entity_id: VarInt, velocity: Vector3<f64>) -> Self {
|
||||||
Self {
|
Self {
|
||||||
entity_id,
|
entity_id,
|
||||||
velocity: Velocity(velocity),
|
velocity: LpVector3d(velocity),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -56,7 +58,7 @@ mod tests {
|
|||||||
use super::CEntityVelocity;
|
use super::CEntityVelocity;
|
||||||
use crate::{
|
use crate::{
|
||||||
ClientPacket, VarInt,
|
ClientPacket, VarInt,
|
||||||
codec::velocity::{Velocity, encode_legacy_velocity_component},
|
codec::lp_vector_3d::{LpVector3d, encode_legacy_velocity_component},
|
||||||
};
|
};
|
||||||
|
|
||||||
fn encode_packet(version: MinecraftVersion) -> Vec<u8> {
|
fn encode_packet(version: MinecraftVersion) -> Vec<u8> {
|
||||||
@ -95,7 +97,7 @@ mod tests {
|
|||||||
|
|
||||||
// Ensure the packed bytes can still be decoded back to a velocity.
|
// Ensure the packed bytes can still be decoded back to a velocity.
|
||||||
let mut cursor = std::io::Cursor::new(&encoded[1..]);
|
let mut cursor = std::io::Cursor::new(&encoded[1..]);
|
||||||
let decoded = Velocity::read(&mut cursor).unwrap();
|
let decoded = LpVector3d::read(&mut cursor).unwrap();
|
||||||
assert!(decoded.0.x.is_finite());
|
assert!(decoded.0.x.is_finite());
|
||||||
assert!(decoded.0.y.is_finite());
|
assert!(decoded.0.y.is_finite());
|
||||||
assert!(decoded.0.z.is_finite());
|
assert!(decoded.0.z.is_finite());
|
||||||
|
|||||||
@ -9,7 +9,7 @@ use pumpkin_util::{math::vector3::Vector3, version::MinecraftVersion};
|
|||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
ClientPacket, VarInt,
|
ClientPacket, VarInt,
|
||||||
codec::velocity::Velocity,
|
codec::lp_vector_3d::LpVector3d,
|
||||||
ser::{NetworkWriteExt, WritingError},
|
ser::{NetworkWriteExt, WritingError},
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -19,7 +19,7 @@ pub struct CSpawnEntity {
|
|||||||
pub entity_uuid: uuid::Uuid,
|
pub entity_uuid: uuid::Uuid,
|
||||||
pub r#type: VarInt,
|
pub r#type: VarInt,
|
||||||
pub position: Vector3<f64>,
|
pub position: Vector3<f64>,
|
||||||
pub velocity: Velocity,
|
pub velocity: LpVector3d,
|
||||||
pub pitch: u8, // angle
|
pub pitch: u8, // angle
|
||||||
pub yaw: u8, // angle
|
pub yaw: u8, // angle
|
||||||
pub head_yaw: u8, // angle
|
pub head_yaw: u8, // angle
|
||||||
@ -49,7 +49,7 @@ impl CSpawnEntity {
|
|||||||
yaw: (yaw.rem_euclid(360.0) * 256.0 / 360.0).floor() as u8,
|
yaw: (yaw.rem_euclid(360.0) * 256.0 / 360.0).floor() as u8,
|
||||||
head_yaw: (head_yaw.rem_euclid(360.0) * 256.0 / 360.0).floor() as u8,
|
head_yaw: (head_yaw.rem_euclid(360.0) * 256.0 / 360.0).floor() as u8,
|
||||||
data,
|
data,
|
||||||
velocity: Velocity(velocity),
|
velocity: LpVector3d(velocity),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -100,7 +100,7 @@ impl ClientPacket for CSpawnEntity {
|
|||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::CSpawnEntity;
|
use super::CSpawnEntity;
|
||||||
use crate::{ClientPacket, VarInt, codec::velocity::encode_legacy_velocity_component};
|
use crate::{ClientPacket, VarInt, codec::lp_vector_3d::encode_legacy_velocity_component};
|
||||||
use pumpkin_util::version::MinecraftVersion;
|
use pumpkin_util::version::MinecraftVersion;
|
||||||
|
|
||||||
fn legacy_tail(velocity: pumpkin_util::math::vector3::Vector3<f64>) -> [u8; 6] {
|
fn legacy_tail(velocity: pumpkin_util::math::vector3::Vector3<f64>) -> [u8; 6] {
|
||||||
|
|||||||
11
pumpkin-protocol/src/java/server/play/attack.rs
Normal file
11
pumpkin-protocol/src/java/server/play/attack.rs
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
use pumpkin_data::packet::serverbound::PLAY_ATTACK;
|
||||||
|
use pumpkin_macros::java_packet;
|
||||||
|
use serde::Deserialize;
|
||||||
|
|
||||||
|
use crate::codec::var_int::VarInt;
|
||||||
|
|
||||||
|
#[derive(Deserialize)]
|
||||||
|
#[java_packet(PLAY_ATTACK)]
|
||||||
|
pub struct SAttack {
|
||||||
|
pub entity_id: VarInt,
|
||||||
|
}
|
||||||
@ -6,7 +6,7 @@ use pumpkin_util::{math::vector3::Vector3, version::MinecraftVersion};
|
|||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
ServerPacket,
|
ServerPacket,
|
||||||
codec::var_int::VarInt,
|
codec::{lp_vector_3d::LpVector3d, var_int::VarInt},
|
||||||
ser::{NetworkReadExt, ReadingError},
|
ser::{NetworkReadExt, ReadingError},
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -14,26 +14,42 @@ use crate::{
|
|||||||
pub struct SInteract {
|
pub struct SInteract {
|
||||||
pub entity_id: VarInt,
|
pub entity_id: VarInt,
|
||||||
pub r#type: VarInt,
|
pub r#type: VarInt,
|
||||||
pub target_position: Option<Vector3<f32>>,
|
pub target_position: Option<Vector3<f64>>,
|
||||||
pub hand: Option<VarInt>,
|
pub hand: Option<VarInt>,
|
||||||
pub sneaking: bool,
|
pub sneaking: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
// Great job Mojang ;D
|
// Great job Mojang ;D
|
||||||
impl ServerPacket for SInteract {
|
impl ServerPacket for SInteract {
|
||||||
fn read(mut read: impl Read, _version: &MinecraftVersion) -> Result<Self, ReadingError> {
|
fn read(mut read: impl Read, version: &MinecraftVersion) -> Result<Self, ReadingError> {
|
||||||
|
// 26.1+ removes the 'type' field and uses doubles for location
|
||||||
|
if version >= &MinecraftVersion::V_26_1 {
|
||||||
|
let entity_id = read.get_var_int()?;
|
||||||
|
let hand = Some(read.get_var_int()?);
|
||||||
|
let target_position = Some(LpVector3d::read(&mut read)?.0);
|
||||||
|
let sneaking = read.get_bool()?;
|
||||||
|
|
||||||
|
return Ok(Self {
|
||||||
|
entity_id,
|
||||||
|
r#type: VarInt(2), // InteractAt for compatibility
|
||||||
|
target_position,
|
||||||
|
hand,
|
||||||
|
sneaking,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
let entity_id = read.get_var_int()?;
|
let entity_id = read.get_var_int()?;
|
||||||
let r#type = read.get_var_int()?;
|
let r#type = read.get_var_int()?;
|
||||||
let action = ActionType::try_from(r#type.0)
|
let action = ActionType::try_from(r#type.0)
|
||||||
.map_err(|_| ReadingError::Message("invalid action type".to_string()))?;
|
.map_err(|_| ReadingError::Message("invalid action type".to_string()))?;
|
||||||
let target_position: Option<Vector3<f32>> = match action {
|
|
||||||
|
let target_position: Option<Vector3<f64>> = match action {
|
||||||
ActionType::Interact | ActionType::Attack => None,
|
ActionType::Interact | ActionType::Attack => None,
|
||||||
ActionType::InteractAt => Some(Vector3::new(
|
ActionType::InteractAt => Some(
|
||||||
read.get_f32_be()?,
|
Vector3::new(read.get_f32_be()?, read.get_f32_be()?, read.get_f32_be()?).to_f64(),
|
||||||
read.get_f32_be()?,
|
),
|
||||||
read.get_f32_be()?,
|
|
||||||
)),
|
|
||||||
};
|
};
|
||||||
|
|
||||||
let hand = match action {
|
let hand = match action {
|
||||||
ActionType::Interact | ActionType::InteractAt => Some(read.get_var_int()?),
|
ActionType::Interact | ActionType::InteractAt => Some(read.get_var_int()?),
|
||||||
ActionType::Attack => None,
|
ActionType::Attack => None,
|
||||||
|
|||||||
@ -1,3 +1,4 @@
|
|||||||
|
mod attack;
|
||||||
mod change_game_mode;
|
mod change_game_mode;
|
||||||
mod chat_command;
|
mod chat_command;
|
||||||
mod chat_message;
|
mod chat_message;
|
||||||
@ -35,6 +36,7 @@ mod update_sign;
|
|||||||
mod use_item;
|
mod use_item;
|
||||||
mod use_item_on;
|
mod use_item_on;
|
||||||
|
|
||||||
|
pub use attack::*;
|
||||||
pub use change_game_mode::*;
|
pub use change_game_mode::*;
|
||||||
pub use chat_command::*;
|
pub use chat_command::*;
|
||||||
pub use chat_message::*;
|
pub use chat_message::*;
|
||||||
|
|||||||
@ -15,7 +15,7 @@ use pumpkin_data::item::Item;
|
|||||||
use pumpkin_data::item_stack::ItemStack;
|
use pumpkin_data::item_stack::ItemStack;
|
||||||
use pumpkin_data::potion_brewing::{ITEM_RECIPES, POTION_RECIPES};
|
use pumpkin_data::potion_brewing::{ITEM_RECIPES, POTION_RECIPES};
|
||||||
use pumpkin_data::sound::{Sound, SoundCategory};
|
use pumpkin_data::sound::{Sound, SoundCategory};
|
||||||
use pumpkin_data::tag::Taggable;
|
use pumpkin_data::tag::{self, Taggable};
|
||||||
use pumpkin_nbt::compound::NbtCompound;
|
use pumpkin_nbt::compound::NbtCompound;
|
||||||
use pumpkin_util::math::position::BlockPos;
|
use pumpkin_util::math::position::BlockPos;
|
||||||
use pumpkin_util::math::vector3::Vector3;
|
use pumpkin_util::math::vector3::Vector3;
|
||||||
@ -294,20 +294,14 @@ impl crate::inventory::Inventory for BrewingStandBlockEntity {
|
|||||||
// Slot 3 - ingredient (must be tagged as brewable)
|
// Slot 3 - ingredient (must be tagged as brewable)
|
||||||
3 => {
|
3 => {
|
||||||
// Check if item is a valid brewing ingredient
|
// Check if item is a valid brewing ingredient
|
||||||
if let Some(is_brewing_fuel) =
|
if stack.get_item().has_tag(&tag::Item::MINECRAFT_BREWING_FUEL) {
|
||||||
stack.get_item().is_tagged_with("minecraft:brewing_fuel")
|
|
||||||
&& is_brewing_fuel
|
|
||||||
{
|
|
||||||
return false; // Fuel should not go in ingredient slot
|
return false; // Fuel should not go in ingredient slot
|
||||||
}
|
}
|
||||||
// Allow any item that's not fuel (ingredient validation happens during brewing)
|
// Allow any item that's not fuel (ingredient validation happens during brewing)
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
// Slot 4 - fuel
|
// Slot 4 - fuel
|
||||||
4 => stack
|
4 => stack.get_item().has_tag(&tag::Item::MINECRAFT_BREWING_FUEL),
|
||||||
.get_item()
|
|
||||||
.is_tagged_with("minecraft:brewing_fuel")
|
|
||||||
.is_some_and(|b| b),
|
|
||||||
_ => false,
|
_ => false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -422,8 +416,7 @@ impl crate::block::entities::BlockEntity for BrewingStandBlockEntity {
|
|||||||
if !fuel_stack.is_empty()
|
if !fuel_stack.is_empty()
|
||||||
&& fuel_stack
|
&& fuel_stack
|
||||||
.get_item()
|
.get_item()
|
||||||
.is_tagged_with("minecraft:brewing_fuel")
|
.has_tag(&tag::Item::MINECRAFT_BREWING_FUEL)
|
||||||
.is_some_and(|b| b)
|
|
||||||
{
|
{
|
||||||
self.fuel.store(20, Ordering::Relaxed);
|
self.fuel.store(20, Ordering::Relaxed);
|
||||||
fuel_stack.decrement(1);
|
fuel_stack.decrement(1);
|
||||||
|
|||||||
@ -14,7 +14,6 @@ pub mod positions;
|
|||||||
pub mod proto_chunk;
|
pub mod proto_chunk;
|
||||||
pub mod proto_chunk_test;
|
pub mod proto_chunk_test;
|
||||||
pub mod rule;
|
pub mod rule;
|
||||||
mod rule_test;
|
|
||||||
pub mod structure;
|
pub mod structure;
|
||||||
mod surface;
|
mod surface;
|
||||||
|
|
||||||
|
|||||||
@ -1,41 +0,0 @@
|
|||||||
use pumpkin_data::{Block, tag::Taggable};
|
|
||||||
use serde::Deserialize;
|
|
||||||
|
|
||||||
/// Rule tests are used in structure or features generation to check if a block state matches some condition.
|
|
||||||
#[derive(Deserialize)]
|
|
||||||
pub enum RuleTest {
|
|
||||||
TagMatch(TagMatchTest),
|
|
||||||
BlockMatch(BlockMatchRuleTest),
|
|
||||||
// TODO: add more
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct AlwaysTrueRuleTest;
|
|
||||||
|
|
||||||
impl AlwaysTrueRuleTest {
|
|
||||||
pub const fn test() -> bool {
|
|
||||||
true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Deserialize)]
|
|
||||||
pub struct BlockMatchRuleTest {
|
|
||||||
block: String,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl BlockMatchRuleTest {
|
|
||||||
pub fn test(&self, block: &'static Block) -> bool {
|
|
||||||
let test_block = Block::from_registry_key(&self.block).expect("Failed to find block");
|
|
||||||
test_block == block
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Deserialize)]
|
|
||||||
pub struct TagMatchTest {
|
|
||||||
tag: String,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl TagMatchTest {
|
|
||||||
pub fn test(&self, block: &'static Block) -> bool {
|
|
||||||
block.is_tagged_with(&self.tag).unwrap()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
26
pumpkin/src/entity/boss/ender_dragon.rs
Normal file
26
pumpkin/src/entity/boss/ender_dragon.rs
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
use std::sync::Arc;
|
||||||
|
|
||||||
|
use crate::entity::{
|
||||||
|
Entity, NBTStorage,
|
||||||
|
mob::{Mob, MobEntity},
|
||||||
|
};
|
||||||
|
|
||||||
|
pub struct EnderDragonEntity {
|
||||||
|
pub mob_entity: MobEntity,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl EnderDragonEntity {
|
||||||
|
pub fn new(entity: Entity) -> Arc<Self> {
|
||||||
|
let mob_entity = MobEntity::new(entity);
|
||||||
|
let dragon = Self { mob_entity };
|
||||||
|
Arc::new(dragon)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl NBTStorage for EnderDragonEntity {}
|
||||||
|
|
||||||
|
impl Mob for EnderDragonEntity {
|
||||||
|
fn get_mob_entity(&self) -> &MobEntity {
|
||||||
|
&self.mob_entity
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1 +1,2 @@
|
|||||||
|
pub mod ender_dragon;
|
||||||
pub mod wither;
|
pub mod wither;
|
||||||
|
|||||||
@ -4,6 +4,7 @@ use pumpkin_data::entity::EntityType;
|
|||||||
use pumpkin_util::math::vector3::Vector3;
|
use pumpkin_util::math::vector3::Vector3;
|
||||||
use uuid::Uuid;
|
use uuid::Uuid;
|
||||||
|
|
||||||
|
use crate::entity::boss::ender_dragon::EnderDragonEntity;
|
||||||
use crate::entity::mob::zombie::zombie_villager::ZombieVillagerEntity;
|
use crate::entity::mob::zombie::zombie_villager::ZombieVillagerEntity;
|
||||||
use crate::{
|
use crate::{
|
||||||
entity::{
|
entity::{
|
||||||
@ -66,7 +67,10 @@ pub async fn from_type(
|
|||||||
id if id == EntityType::IRON_GOLEM.id => IronGolemEntity::new(entity).await,
|
id if id == EntityType::IRON_GOLEM.id => IronGolemEntity::new(entity).await,
|
||||||
id if id == EntityType::SHEEP.id => SheepEntity::new(entity).await,
|
id if id == EntityType::SHEEP.id => SheepEntity::new(entity).await,
|
||||||
id if id == EntityType::WOLF.id => WolfEntity::new(entity).await,
|
id if id == EntityType::WOLF.id => WolfEntity::new(entity).await,
|
||||||
|
|
||||||
id if id == EntityType::WITHER.id => WitherEntity::new(entity).await,
|
id if id == EntityType::WITHER.id => WitherEntity::new(entity).await,
|
||||||
|
id if id == EntityType::ENDER_DRAGON.id => EnderDragonEntity::new(entity),
|
||||||
|
|
||||||
id if id == EntityType::AREA_EFFECT_CLOUD.id => {
|
id if id == EntityType::AREA_EFFECT_CLOUD.id => {
|
||||||
crate::entity::area_effect_cloud::AreaEffectCloudEntity::new(entity)
|
crate::entity::area_effect_cloud::AreaEffectCloudEntity::new(entity)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -6,7 +6,7 @@ use crossbeam::atomic::AtomicCell;
|
|||||||
use pumpkin_config::networking::compression::CompressionInfo;
|
use pumpkin_config::networking::compression::CompressionInfo;
|
||||||
use pumpkin_data::packet::CURRENT_MC_VERSION;
|
use pumpkin_data::packet::CURRENT_MC_VERSION;
|
||||||
use pumpkin_protocol::java::server::play::{
|
use pumpkin_protocol::java::server::play::{
|
||||||
SChangeGameMode, SChatCommand, SChatMessage, SChunkBatch, SClickSlot, SClientCommand,
|
SAttack, SChangeGameMode, SChatCommand, SChatMessage, SChunkBatch, SClickSlot, SClientCommand,
|
||||||
SClientInformationPlay, SClientTickEnd, SCloseContainer, SCommandSuggestion, SConfirmTeleport,
|
SClientInformationPlay, SClientTickEnd, SCloseContainer, SCommandSuggestion, SConfirmTeleport,
|
||||||
SCookieResponse as SPCookieResponse, SCustomPayload, SInteract, SKeepAlive, SMoveVehicle,
|
SCookieResponse as SPCookieResponse, SCustomPayload, SInteract, SKeepAlive, SMoveVehicle,
|
||||||
SPaddleBoat, SPickItemFromBlock, SPlayPingRequest, SPlayerAbilities, SPlayerAction,
|
SPaddleBoat, SPickItemFromBlock, SPlayPingRequest, SPlayerAbilities, SPlayerAction,
|
||||||
@ -727,6 +727,10 @@ impl JavaClient {
|
|||||||
self.handle_interact(player, SInteract::read(payload, &version)?, server)
|
self.handle_interact(player, SInteract::read(payload, &version)?, server)
|
||||||
.await;
|
.await;
|
||||||
}
|
}
|
||||||
|
id if id == SAttack::to_id(version) => {
|
||||||
|
self.handle_attack(player, SAttack::read(payload, &version)?, server)
|
||||||
|
.await;
|
||||||
|
}
|
||||||
id if id == SKeepAlive::to_id(version) => {
|
id if id == SKeepAlive::to_id(version) => {
|
||||||
self.handle_keep_alive(player, SKeepAlive::read(payload, &version)?)
|
self.handle_keep_alive(player, SKeepAlive::read(payload, &version)?)
|
||||||
.await;
|
.await;
|
||||||
|
|||||||
@ -52,7 +52,7 @@ use pumpkin_protocol::java::client::play::{
|
|||||||
CUpdateEntityPos, CUpdateEntityPosRot, CUpdateEntityRot, InitChat, PlayerAction,
|
CUpdateEntityPos, CUpdateEntityPosRot, CUpdateEntityRot, InitChat, PlayerAction,
|
||||||
};
|
};
|
||||||
use pumpkin_protocol::java::server::play::{
|
use pumpkin_protocol::java::server::play::{
|
||||||
Action, ActionType, CommandBlockMode, FLAG_ON_GROUND, SChangeGameMode, SChatCommand,
|
Action, ActionType, CommandBlockMode, FLAG_ON_GROUND, SAttack, SChangeGameMode, SChatCommand,
|
||||||
SChatMessage, SChunkBatch, SClientCommand, SClientInformationPlay, SCloseContainer,
|
SChatMessage, SChunkBatch, SClientCommand, SClientInformationPlay, SCloseContainer,
|
||||||
SCommandSuggestion, SConfirmTeleport, SCookieResponse as SPCookieResponse, SInteract,
|
SCommandSuggestion, SConfirmTeleport, SCookieResponse as SPCookieResponse, SInteract,
|
||||||
SKeepAlive, SMoveVehicle, SPaddleBoat, SPickItemFromBlock, SPlayPingRequest, SPlayerAbilities,
|
SKeepAlive, SMoveVehicle, SPaddleBoat, SPickItemFromBlock, SPlayPingRequest, SPlayerAbilities,
|
||||||
@ -1249,6 +1249,60 @@ impl JavaClient {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub async fn handle_attack(&self, player: &Arc<Player>, attack: SAttack, server: &Arc<Server>) {
|
||||||
|
if !player.has_client_loaded() {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
player.update_last_action_time();
|
||||||
|
let entity_id = attack.entity_id;
|
||||||
|
let player_entity = &player.living_entity.entity;
|
||||||
|
let world = player_entity.world.load_full();
|
||||||
|
|
||||||
|
let config = &server.advanced_config.pvp;
|
||||||
|
if !config.enabled {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if entity_id.0 == player.entity_id() {
|
||||||
|
self.kick(TextComponent::translate(
|
||||||
|
translation::MULTIPLAYER_DISCONNECT_INVALID_ENTITY_ATTACKED,
|
||||||
|
[],
|
||||||
|
))
|
||||||
|
.await;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let player_target = world.get_player_by_id(entity_id.0);
|
||||||
|
let target: Option<Arc<dyn EntityBase>> = player_target
|
||||||
|
.as_ref()
|
||||||
|
.map(|p| Arc::clone(p) as Arc<dyn EntityBase>)
|
||||||
|
.or_else(|| world.get_entity_by_id(entity_id.0));
|
||||||
|
let Some(target) = target else {
|
||||||
|
self.kick(TextComponent::translate(
|
||||||
|
translation::MULTIPLAYER_DISCONNECT_INVALID_ENTITY_ATTACKED,
|
||||||
|
[],
|
||||||
|
))
|
||||||
|
.await;
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
if let Some(player_victim) = &player_target {
|
||||||
|
if player_victim.living_entity.health.load() <= 0.0 {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if config.protect_creative && player_victim.gamemode.load() == GameMode::Creative {
|
||||||
|
world
|
||||||
|
.play_sound(
|
||||||
|
Sound::EntityPlayerAttackNodamage,
|
||||||
|
SoundCategory::Players,
|
||||||
|
&player_victim.position(),
|
||||||
|
)
|
||||||
|
.await;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
player.attack(target).await;
|
||||||
|
}
|
||||||
|
|
||||||
pub async fn handle_interact(
|
pub async fn handle_interact(
|
||||||
&self,
|
&self,
|
||||||
player: &Arc<Player>,
|
player: &Arc<Player>,
|
||||||
|
|||||||
@ -26,7 +26,7 @@ pub struct PlayerInteractEntityEvent {
|
|||||||
pub action: ActionType,
|
pub action: ActionType,
|
||||||
|
|
||||||
/// The position on the entity that was clicked (only for `InteractAt`).
|
/// The position on the entity that was clicked (only for `InteractAt`).
|
||||||
pub target_position: Option<Vector3<f32>>,
|
pub target_position: Option<Vector3<f64>>,
|
||||||
|
|
||||||
/// Whether the player was sneaking during the interaction.
|
/// Whether the player was sneaking during the interaction.
|
||||||
pub sneaking: bool,
|
pub sneaking: bool,
|
||||||
@ -37,7 +37,7 @@ impl PlayerInteractEntityEvent {
|
|||||||
player: &Arc<Player>,
|
player: &Arc<Player>,
|
||||||
target: Arc<dyn EntityBase>,
|
target: Arc<dyn EntityBase>,
|
||||||
action: ActionType,
|
action: ActionType,
|
||||||
target_position: Option<Vector3<f32>>,
|
target_position: Option<Vector3<f64>>,
|
||||||
sneaking: bool,
|
sneaking: bool,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
Self {
|
Self {
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user