mirror of
https://github.com/Pumpkin-MC/Pumpkin.git
synced 2026-08-30 20:14:23 +00:00
feat: make totems work
This commit is contained in:
@@ -39,6 +39,8 @@ pub struct ItemComponents {
|
||||
pub consumable: Option<Consumable>,
|
||||
#[serde(rename = "minecraft:blocks_attacks")]
|
||||
pub blocks_attacks: Option<BlocksAttacks>,
|
||||
#[serde(rename = "minecraft:death_protection")]
|
||||
pub death_protection: Option<DeathProtection>,
|
||||
}
|
||||
|
||||
impl ToTokens for ItemComponents {
|
||||
@@ -237,6 +239,10 @@ impl ToTokens for ItemComponents {
|
||||
tokens.extend(quote! { (BlocksAttacks, &BlocksAttacksImpl), });
|
||||
};
|
||||
|
||||
if self.death_protection.is_some() {
|
||||
tokens.extend(quote! { (DeathProtection, &DeathProtectionImpl), });
|
||||
};
|
||||
|
||||
if let Some(equippable) = &self.equippable {
|
||||
let slot = match equippable.slot.as_str() {
|
||||
"mainhand" => quote! { &EquipmentSlot::MAIN_HAND },
|
||||
@@ -397,6 +403,11 @@ pub struct Consumable {
|
||||
consume_seconds: Option<f32>, // TODO
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Clone, Debug)]
|
||||
pub struct DeathProtection {
|
||||
// TODO
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Clone, Debug)]
|
||||
pub struct BlocksAttacks {
|
||||
// TODO
|
||||
|
||||
@@ -571,6 +571,9 @@ pub struct GliderImpl;
|
||||
pub struct TooltipStyleImpl;
|
||||
#[derive(Clone, Debug, Hash, PartialEq)]
|
||||
pub struct DeathProtectionImpl;
|
||||
impl DataComponentImpl for DeathProtectionImpl {
|
||||
default_impl!(DeathProtection);
|
||||
}
|
||||
#[derive(Clone, Debug, Hash, PartialEq)]
|
||||
pub struct BlocksAttacksImpl;
|
||||
|
||||
|
||||
@@ -12,4 +12,33 @@ pub mod slot;
|
||||
pub mod sync_handler;
|
||||
pub mod window_property;
|
||||
|
||||
use std::collections::HashMap;
|
||||
|
||||
pub use error::InventoryError;
|
||||
use pumpkin_data::data_component_impl::EquipmentSlot;
|
||||
|
||||
use crate::player::player_inventory::PlayerInventory;
|
||||
|
||||
pub fn build_equipment_slots() -> HashMap<usize, EquipmentSlot> {
|
||||
let mut equipment_slots = HashMap::new();
|
||||
equipment_slots.insert(
|
||||
EquipmentSlot::FEET.get_offset_entity_slot_id(PlayerInventory::MAIN_SIZE as i32) as usize,
|
||||
EquipmentSlot::FEET,
|
||||
);
|
||||
equipment_slots.insert(
|
||||
EquipmentSlot::LEGS.get_offset_entity_slot_id(PlayerInventory::MAIN_SIZE as i32) as usize,
|
||||
EquipmentSlot::LEGS,
|
||||
);
|
||||
equipment_slots.insert(
|
||||
EquipmentSlot::CHEST.get_offset_entity_slot_id(PlayerInventory::MAIN_SIZE as i32) as usize,
|
||||
EquipmentSlot::CHEST,
|
||||
);
|
||||
equipment_slots.insert(
|
||||
EquipmentSlot::HEAD.get_offset_entity_slot_id(PlayerInventory::MAIN_SIZE as i32) as usize,
|
||||
EquipmentSlot::HEAD,
|
||||
);
|
||||
|
||||
equipment_slots.insert(PlayerInventory::OFF_HAND_SLOT, EquipmentSlot::OFF_HAND);
|
||||
equipment_slots.insert(PlayerInventory::OFF_HAND_SLOT, EquipmentSlot::OFF_HAND);
|
||||
equipment_slots
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@ use crate::screen_handler::InventoryPlayer;
|
||||
use async_trait::async_trait;
|
||||
use pumpkin_data::data_component_impl::EquipmentSlot;
|
||||
use pumpkin_protocol::java::client::play::CSetPlayerInventory;
|
||||
use pumpkin_util::Hand;
|
||||
use pumpkin_world::inventory::split_stack;
|
||||
use pumpkin_world::inventory::{Clearable, Inventory};
|
||||
use pumpkin_world::item::ItemStack;
|
||||
@@ -16,22 +17,25 @@ use tokio::sync::Mutex;
|
||||
#[derive(Debug)]
|
||||
pub struct PlayerInventory {
|
||||
pub main_inventory: [Arc<Mutex<ItemStack>>; Self::MAIN_SIZE],
|
||||
pub equipment_slots: HashMap<usize, EquipmentSlot>,
|
||||
pub equipment_slots: Arc<HashMap<usize, EquipmentSlot>>,
|
||||
selected_slot: AtomicU8,
|
||||
pub entity_equipment: Arc<Mutex<EntityEquipment>>,
|
||||
}
|
||||
|
||||
impl PlayerInventory {
|
||||
const MAIN_SIZE: usize = 36;
|
||||
pub const MAIN_SIZE: usize = 36;
|
||||
const HOTBAR_SIZE: usize = 9;
|
||||
const OFF_HAND_SLOT: usize = 40;
|
||||
pub const OFF_HAND_SLOT: usize = 40;
|
||||
|
||||
// TODO: Add inventory load from nbt
|
||||
pub fn new(entity_equipment: Arc<Mutex<EntityEquipment>>) -> Self {
|
||||
pub fn new(
|
||||
entity_equipment: Arc<Mutex<EntityEquipment>>,
|
||||
equipment_slots: Arc<HashMap<usize, EquipmentSlot>>,
|
||||
) -> Self {
|
||||
Self {
|
||||
// Normal syntax can't be used here because Arc doesn't implement Copy
|
||||
main_inventory: from_fn(|_| Arc::new(Mutex::new(ItemStack::EMPTY.clone()))),
|
||||
equipment_slots: Self::build_equipment_slots(),
|
||||
equipment_slots,
|
||||
selected_slot: AtomicU8::new(0),
|
||||
entity_equipment,
|
||||
}
|
||||
@@ -45,6 +49,13 @@ impl PlayerInventory {
|
||||
.clone()
|
||||
}
|
||||
|
||||
pub async fn get_stack_in_hand(&self, hand: Hand) -> Arc<Mutex<ItemStack>> {
|
||||
match hand {
|
||||
Hand::Left => self.off_hand_item().await,
|
||||
Hand::Right => self.held_item(),
|
||||
}
|
||||
}
|
||||
|
||||
/// getOffHandStack in source
|
||||
pub async fn off_hand_item(&self) -> Arc<Mutex<ItemStack>> {
|
||||
let slot = self
|
||||
@@ -71,28 +82,6 @@ impl PlayerInventory {
|
||||
slot < Self::HOTBAR_SIZE
|
||||
}
|
||||
|
||||
fn build_equipment_slots() -> HashMap<usize, EquipmentSlot> {
|
||||
let mut equipment_slots = HashMap::new();
|
||||
equipment_slots.insert(
|
||||
EquipmentSlot::FEET.get_offset_entity_slot_id(Self::MAIN_SIZE as i32) as usize,
|
||||
EquipmentSlot::FEET,
|
||||
);
|
||||
equipment_slots.insert(
|
||||
EquipmentSlot::LEGS.get_offset_entity_slot_id(Self::MAIN_SIZE as i32) as usize,
|
||||
EquipmentSlot::LEGS,
|
||||
);
|
||||
equipment_slots.insert(
|
||||
EquipmentSlot::CHEST.get_offset_entity_slot_id(Self::MAIN_SIZE as i32) as usize,
|
||||
EquipmentSlot::CHEST,
|
||||
);
|
||||
equipment_slots.insert(
|
||||
EquipmentSlot::HEAD.get_offset_entity_slot_id(Self::MAIN_SIZE as i32) as usize,
|
||||
EquipmentSlot::HEAD,
|
||||
);
|
||||
equipment_slots.insert(PlayerInventory::OFF_HAND_SLOT, EquipmentSlot::OFF_HAND);
|
||||
equipment_slots
|
||||
}
|
||||
|
||||
async fn add_stack(&self, stack: ItemStack) -> usize {
|
||||
let mut slot_index = self.get_occupied_slot_with_room_for_stack(&stack).await;
|
||||
|
||||
|
||||
@@ -61,10 +61,10 @@ struct AdditionsSound {
|
||||
tick_chance: f64,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
struct Music {
|
||||
sound: String,
|
||||
min_delay: i32,
|
||||
max_delay: i32,
|
||||
replace_current_music: bool,
|
||||
}
|
||||
// #[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
// struct Music {
|
||||
// sound: String,
|
||||
// min_delay: i32,
|
||||
// max_delay: i32,
|
||||
// replace_current_music: bool,
|
||||
// }
|
||||
|
||||
@@ -73,12 +73,6 @@ pub struct SyncedRegistry {
|
||||
instrument: IndexMap<String, Instrument>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
struct DataPool<T> {
|
||||
data: T,
|
||||
weight: i32,
|
||||
}
|
||||
|
||||
// TODO: remove in favor of numerical registry ids for `minecraft:dimension_type`
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub enum VanillaDimensionType {
|
||||
|
||||
@@ -140,3 +140,32 @@ macro_rules! assert_eq_delta {
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/// Represents the player's dominant hand.
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub enum Hand {
|
||||
/// Usually the player's off-hand.
|
||||
Left,
|
||||
/// Usually the player's primary hand.
|
||||
Right,
|
||||
}
|
||||
|
||||
impl Hand {
|
||||
pub fn all() -> [Self; 2] {
|
||||
[Self::Right, Self::Left]
|
||||
}
|
||||
}
|
||||
|
||||
pub struct InvalidHand;
|
||||
|
||||
impl TryFrom<i32> for Hand {
|
||||
type Error = InvalidHand;
|
||||
|
||||
fn try_from(value: i32) -> Result<Self, Self::Error> {
|
||||
match value {
|
||||
0 => Ok(Self::Left),
|
||||
1 => Ok(Self::Right),
|
||||
_ => Err(InvalidHand),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,7 +2,6 @@ use async_trait::async_trait;
|
||||
use pumpkin_data::block_properties::{
|
||||
BlockProperties, CactusLikeProperties, EnumVariants, Integer0To15,
|
||||
};
|
||||
use pumpkin_data::damage::DamageType;
|
||||
use pumpkin_data::tag::Taggable;
|
||||
use pumpkin_data::{Block, BlockDirection, tag};
|
||||
use pumpkin_macros::pumpkin_block;
|
||||
@@ -68,8 +67,9 @@ impl BlockBehaviour for CactusBlock {
|
||||
}
|
||||
}
|
||||
|
||||
async fn on_entity_collision(&self, args: OnEntityCollisionArgs<'_>) {
|
||||
args.entity.damage(1.0, DamageType::CACTUS).await;
|
||||
async fn on_entity_collision(&self, _args: OnEntityCollisionArgs<'_>) {
|
||||
// TODO
|
||||
//args.entity.damage(1.0, DamageType::CACTUS).await;
|
||||
}
|
||||
|
||||
async fn get_state_for_neighbor_update(
|
||||
|
||||
@@ -2,7 +2,6 @@ use async_trait::async_trait;
|
||||
use pumpkin_data::{
|
||||
Block, BlockDirection,
|
||||
block_properties::{BlockProperties, CampfireLikeProperties},
|
||||
damage::DamageType,
|
||||
fluid::Fluid,
|
||||
};
|
||||
use pumpkin_world::{BlockStateId, tick::TickPriority};
|
||||
@@ -37,7 +36,8 @@ impl BlockBehaviour for CampfireBlock {
|
||||
if CampfireLikeProperties::from_state_id(args.state.id, args.block).lit
|
||||
&& args.entity.get_living_entity().is_some()
|
||||
{
|
||||
args.entity.damage(1.0, DamageType::CAMPFIRE).await;
|
||||
// TODO
|
||||
//args.entity.damage(args.entity, 1.0, DamageType::CAMPFIRE).await;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -58,7 +58,7 @@ impl ArgumentConsumer for CommandTreeArgumentConsumer {
|
||||
.commands
|
||||
.keys()
|
||||
.filter(|suggestion| suggestion.starts_with(input))
|
||||
.map(|suggestion| CommandSuggestion::new(suggestion.to_string(), None))
|
||||
.map(|suggestion| CommandSuggestion::new(suggestion.clone(), None))
|
||||
.collect();
|
||||
Ok(Some(suggestions))
|
||||
}
|
||||
|
||||
@@ -59,7 +59,7 @@ impl CommandExecutor for ReasonExecutor {
|
||||
return Err(InvalidConsumption(Some(ARG_REASON.into())));
|
||||
};
|
||||
|
||||
ban_player(sender, &targets[0], Some(reason.to_string())).await;
|
||||
ban_player(sender, &targets[0], Some(reason.clone())).await;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -72,7 +72,7 @@ impl CommandExecutor for ReasonExecutor {
|
||||
return Err(InvalidConsumption(Some(ARG_REASON.into())));
|
||||
};
|
||||
|
||||
ban_ip(sender, server, target, Some(reason.to_string())).await;
|
||||
ban_ip(sender, server, target, Some(reason.clone())).await;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -77,7 +77,7 @@ impl CommandExecutor for AddExecuter {
|
||||
sender,
|
||||
TextComponent::translate(
|
||||
"commands.bossbar.create.failed",
|
||||
[TextComponent::text(namespace.to_string())],
|
||||
[TextComponent::text(namespace.clone())],
|
||||
),
|
||||
)
|
||||
.await;
|
||||
@@ -89,12 +89,12 @@ impl CommandExecutor for AddExecuter {
|
||||
.bossbars
|
||||
.lock()
|
||||
.await
|
||||
.create_bossbar(namespace.to_string(), bossbar.clone());
|
||||
.create_bossbar(namespace.clone(), bossbar.clone());
|
||||
|
||||
sender
|
||||
.send_message(TextComponent::translate(
|
||||
"commands.bossbar.create.success",
|
||||
[bossbar_prefix(bossbar.title.clone(), namespace.to_string())],
|
||||
[bossbar_prefix(bossbar.title.clone(), namespace.clone())],
|
||||
))
|
||||
.await;
|
||||
|
||||
@@ -119,7 +119,7 @@ impl CommandExecutor for GetExecuter {
|
||||
let Some(bossbar) = server.bossbars.lock().await.get_bossbar(&namespace) else {
|
||||
handle_bossbar_error(
|
||||
sender,
|
||||
BossbarUpdateError::InvalidResourceLocation(namespace.to_string()),
|
||||
BossbarUpdateError::InvalidResourceLocation(namespace.clone()),
|
||||
)
|
||||
.await;
|
||||
return Ok(());
|
||||
@@ -131,10 +131,7 @@ impl CommandExecutor for GetExecuter {
|
||||
.send_message(TextComponent::translate(
|
||||
"commands.bossbar.get.max",
|
||||
[
|
||||
bossbar_prefix(
|
||||
bossbar.bossbar_data.title.clone(),
|
||||
namespace.to_string(),
|
||||
),
|
||||
bossbar_prefix(bossbar.bossbar_data.title.clone(), namespace.clone()),
|
||||
TextComponent::text(bossbar.max.to_string()),
|
||||
],
|
||||
))
|
||||
@@ -147,10 +144,7 @@ impl CommandExecutor for GetExecuter {
|
||||
.send_message(TextComponent::translate(
|
||||
"commands.bossbar.get.value",
|
||||
[
|
||||
bossbar_prefix(
|
||||
bossbar.bossbar_data.title.clone(),
|
||||
namespace.to_string(),
|
||||
),
|
||||
bossbar_prefix(bossbar.bossbar_data.title.clone(), namespace.clone()),
|
||||
TextComponent::text(bossbar.value.to_string()),
|
||||
],
|
||||
))
|
||||
@@ -168,7 +162,7 @@ impl CommandExecutor for GetExecuter {
|
||||
state,
|
||||
[bossbar_prefix(
|
||||
bossbar.bossbar_data.title.clone(),
|
||||
namespace.to_string(),
|
||||
namespace.clone(),
|
||||
)],
|
||||
))
|
||||
.await;
|
||||
@@ -215,13 +209,13 @@ impl CommandExecutor for ListExecuter {
|
||||
if i == 0 {
|
||||
bossbars_text = bossbars_text.add_child(bossbar_prefix(
|
||||
bossbar.bossbar_data.title.clone(),
|
||||
bossbar.namespace.to_string(),
|
||||
bossbar.namespace.clone(),
|
||||
));
|
||||
} else {
|
||||
bossbars_text =
|
||||
bossbars_text.add_child(TextComponent::text(", ").add_child(bossbar_prefix(
|
||||
bossbar.bossbar_data.title.clone(),
|
||||
bossbar.namespace.to_string(),
|
||||
bossbar.namespace.clone(),
|
||||
)));
|
||||
}
|
||||
}
|
||||
@@ -267,7 +261,7 @@ impl CommandExecutor for RemoveExecuter {
|
||||
"commands.bossbar.remove.success",
|
||||
[bossbar_prefix(
|
||||
bossbar.bossbar_data.title.clone(),
|
||||
namespace.to_string(),
|
||||
namespace.clone(),
|
||||
)],
|
||||
))
|
||||
.await;
|
||||
@@ -276,7 +270,7 @@ impl CommandExecutor for RemoveExecuter {
|
||||
.bossbars
|
||||
.lock()
|
||||
.await
|
||||
.remove_bossbar(server, namespace.to_string())
|
||||
.remove_bossbar(server, namespace.clone())
|
||||
.await
|
||||
{
|
||||
Ok(()) => {}
|
||||
@@ -470,7 +464,7 @@ impl CommandExecutor for SetExecuter {
|
||||
namespace.to_string(),
|
||||
),
|
||||
TextComponent::text(count.to_string()),
|
||||
TextComponent::text(player_names.join(", ").to_string()),
|
||||
TextComponent::text(player_names.join(", ").clone()),
|
||||
],
|
||||
))
|
||||
.await;
|
||||
|
||||
@@ -89,7 +89,14 @@ impl CommandExecutor for LocationExecutor {
|
||||
let location = Position3DArgumentConsumer::find_arg(args, ARG_LOCATION)?;
|
||||
|
||||
let success = target
|
||||
.damage_with_context(amount, damage_type, Some(location), None, None)
|
||||
.damage_with_context(
|
||||
target.clone(),
|
||||
amount,
|
||||
damage_type,
|
||||
Some(location),
|
||||
None,
|
||||
None,
|
||||
)
|
||||
.await;
|
||||
|
||||
send_damage_result(sender, success, amount, target.get_display_name().await).await;
|
||||
@@ -134,6 +141,7 @@ impl CommandExecutor for EntityExecutor {
|
||||
|
||||
let success = target
|
||||
.damage_with_context(
|
||||
target.clone(),
|
||||
amount,
|
||||
damage_type,
|
||||
None,
|
||||
|
||||
@@ -154,9 +154,7 @@ pub fn snbt_colorful_display(tag: &NbtTag, depth: usize) -> Result<TextComponent
|
||||
let item_display = snbt_colorful_display(item, depth + 1)
|
||||
.map_err(|string| format!("Error displaying item.{key}: {string}"))?;
|
||||
content = content
|
||||
.add_child(
|
||||
TextComponent::text(key.to_string()).color_named(NamedColor::Aqua),
|
||||
)
|
||||
.add_child(TextComponent::text(key.clone()).color_named(NamedColor::Aqua))
|
||||
.add_child(TextComponent::text(": "))
|
||||
.add_child(item_display);
|
||||
|
||||
|
||||
@@ -30,7 +30,7 @@ impl CommandExecutor for Executor {
|
||||
|
||||
let target_count = targets.len();
|
||||
for target in targets {
|
||||
target.kill().await;
|
||||
target.kill(target.clone()).await;
|
||||
}
|
||||
|
||||
let msg = if target_count == 1 {
|
||||
@@ -62,7 +62,7 @@ impl CommandExecutor for SelfExecutor {
|
||||
_args: &ConsumedArgs<'a>,
|
||||
) -> Result<(), CommandError> {
|
||||
let target = sender.as_player().ok_or(CommandError::InvalidRequirement)?;
|
||||
target.kill().await;
|
||||
target.kill(target.clone()).await;
|
||||
|
||||
sender
|
||||
.send_message(TextComponent::translate(
|
||||
|
||||
@@ -411,15 +411,13 @@ impl CommandDispatcher {
|
||||
|
||||
for name in names {
|
||||
self.commands
|
||||
.insert(name.to_string(), Command::Alias(primary_name.to_string()));
|
||||
self.permissions
|
||||
.insert(name.to_string(), permission.clone());
|
||||
.insert(name.clone(), Command::Alias(primary_name.clone()));
|
||||
self.permissions.insert(name.clone(), permission.clone());
|
||||
}
|
||||
|
||||
self.permissions
|
||||
.insert(primary_name.to_string(), permission);
|
||||
self.permissions.insert(primary_name.clone(), permission);
|
||||
self.commands
|
||||
.insert(primary_name.to_string(), Command::Tree(tree));
|
||||
.insert(primary_name.clone(), Command::Tree(tree));
|
||||
}
|
||||
|
||||
/// Remove a command from the dispatcher by its primary name.
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
use core::f32;
|
||||
use std::sync::atomic::Ordering;
|
||||
use std::sync::{Arc, atomic::Ordering};
|
||||
|
||||
use crate::entity::{Entity, EntityBase, NBTStorage, living::LivingEntity};
|
||||
use async_trait::async_trait;
|
||||
@@ -41,6 +41,7 @@ impl EntityBase for PaintingEntity {
|
||||
|
||||
async fn damage_with_context(
|
||||
&self,
|
||||
_caller: Arc<dyn EntityBase>,
|
||||
_amount: f32,
|
||||
_damage_type: DamageType,
|
||||
_position: Option<Vector3<f64>>,
|
||||
|
||||
@@ -5,7 +5,7 @@ use std::sync::{
|
||||
};
|
||||
|
||||
use async_trait::async_trait;
|
||||
use pumpkin_data::{damage::DamageType, entity::EntityType};
|
||||
use pumpkin_data::entity::EntityType;
|
||||
use pumpkin_util::math::vector3::Vector3;
|
||||
use uuid::Uuid;
|
||||
|
||||
@@ -125,17 +125,6 @@ impl EntityBase for ExperienceOrbEntity {
|
||||
}
|
||||
}
|
||||
|
||||
async fn damage_with_context(
|
||||
&self,
|
||||
_amount: f32,
|
||||
_damage_type: DamageType,
|
||||
_position: Option<Vector3<f64>>,
|
||||
_source: Option<&dyn EntityBase>,
|
||||
_cause: Option<&dyn EntityBase>,
|
||||
) -> bool {
|
||||
false
|
||||
}
|
||||
|
||||
fn get_living_entity(&self) -> Option<&LivingEntity> {
|
||||
None
|
||||
}
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
use async_trait::async_trait;
|
||||
use pumpkin_data::Block;
|
||||
use pumpkin_data::{damage::DamageType, entity::EntityType};
|
||||
use pumpkin_data::{entity::EntityType};
|
||||
use pumpkin_protocol::java::client::play::{MetaDataType, Metadata};
|
||||
use pumpkin_util::math::{position::BlockPos, vector3::Vector3};
|
||||
use pumpkin_util::math::{position::BlockPos};
|
||||
use pumpkin_world::{BlockStateId, world::BlockFlags};
|
||||
use std::sync::{Arc, atomic::Ordering};
|
||||
use uuid::Uuid;
|
||||
@@ -97,17 +97,6 @@ impl EntityBase for FallingEntity {
|
||||
.await;
|
||||
}
|
||||
|
||||
async fn damage_with_context(
|
||||
&self,
|
||||
_amount: f32,
|
||||
_damage_type: DamageType,
|
||||
_position: Option<Vector3<f64>>,
|
||||
_source: Option<&dyn EntityBase>,
|
||||
_cause: Option<&dyn EntityBase>,
|
||||
) -> bool {
|
||||
false
|
||||
}
|
||||
|
||||
fn get_entity(&self) -> &Entity {
|
||||
&self.entity
|
||||
}
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
use std::sync::Arc;
|
||||
|
||||
use super::{EntityBase, NBTStorage, NBTStorageInit, player::Player};
|
||||
use async_trait::async_trait;
|
||||
use crossbeam::atomic::AtomicCell;
|
||||
@@ -27,7 +29,7 @@ impl Default for HungerManager {
|
||||
}
|
||||
|
||||
impl HungerManager {
|
||||
pub async fn tick(&self, player: &Player) {
|
||||
pub async fn tick(&self, player: &Arc<Player>) {
|
||||
let saturation = self.saturation.load();
|
||||
let level = self.level.load();
|
||||
let exhaustion = self.exhaustion.load();
|
||||
@@ -68,7 +70,7 @@ impl HungerManager {
|
||||
|| (difficulty == Difficulty::Hard)
|
||||
|| (health > 1.0 && difficulty == Difficulty::Normal)
|
||||
{
|
||||
player.damage(1.0, DamageType::STARVE).await;
|
||||
player.damage(player.clone(), 1.0, DamageType::STARVE).await;
|
||||
}
|
||||
self.tick_timer.store(0);
|
||||
}
|
||||
|
||||
@@ -312,7 +312,7 @@ impl EntityBase for ItemEntity {
|
||||
2
|
||||
};
|
||||
|
||||
if age % n == 0 && self.can_merge().await {
|
||||
if age.is_multiple_of(n) && self.can_merge().await {
|
||||
self.try_merge().await;
|
||||
}
|
||||
}
|
||||
@@ -352,6 +352,7 @@ impl EntityBase for ItemEntity {
|
||||
|
||||
async fn damage_with_context(
|
||||
&self,
|
||||
_caller: Arc<dyn EntityBase>,
|
||||
amount: f32,
|
||||
_damage_type: DamageType,
|
||||
_position: Option<Vector3<f64>>,
|
||||
@@ -366,7 +367,12 @@ impl EntityBase for ItemEntity {
|
||||
true
|
||||
}
|
||||
|
||||
async fn damage(&self, _amount: f32, _damage_type: DamageType) -> bool {
|
||||
async fn damage(
|
||||
&self,
|
||||
_caller: Arc<dyn EntityBase>,
|
||||
_amount: f32,
|
||||
_damage_type: DamageType,
|
||||
) -> bool {
|
||||
false
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,7 @@
|
||||
use pumpkin_data::potion::Effect;
|
||||
use pumpkin_inventory::build_equipment_slots;
|
||||
use pumpkin_inventory::player::player_inventory::PlayerInventory;
|
||||
use pumpkin_util::Hand;
|
||||
use pumpkin_util::math::position::BlockPos;
|
||||
use std::sync::Arc;
|
||||
use std::sync::atomic::Ordering;
|
||||
@@ -10,18 +13,17 @@ use std::{collections::HashMap, sync::atomic::AtomicI32};
|
||||
|
||||
use super::{Entity, NBTStorage};
|
||||
use super::{EntityBase, NBTStorageInit};
|
||||
use crate::entity::player::Hand;
|
||||
use crate::server::Server;
|
||||
use crate::world::loot::{LootContextParameters, LootTableExt};
|
||||
use async_trait::async_trait;
|
||||
use crossbeam::atomic::AtomicCell;
|
||||
use pumpkin_config::advanced_config;
|
||||
use pumpkin_data::Block;
|
||||
use pumpkin_data::damage::DeathMessageType;
|
||||
use pumpkin_data::data_component_impl::{EquipmentSlot, FoodImpl};
|
||||
use pumpkin_data::data_component_impl::{DeathProtectionImpl, EquipmentSlot, FoodImpl};
|
||||
use pumpkin_data::effect::StatusEffect;
|
||||
use pumpkin_data::entity::{EntityPose, EntityStatus, EntityType};
|
||||
use pumpkin_data::sound::SoundCategory;
|
||||
use pumpkin_data::Block;
|
||||
use pumpkin_data::{damage::DamageType, sound::Sound};
|
||||
use pumpkin_inventory::entity_equipment::EntityEquipment;
|
||||
use pumpkin_nbt::compound::NbtCompound;
|
||||
@@ -59,6 +61,7 @@ pub struct LivingEntity {
|
||||
pub active_effects: Mutex<HashMap<&'static StatusEffect, Effect>>,
|
||||
pub entity_equipment: Arc<Mutex<EntityEquipment>>,
|
||||
pub movement_input: AtomicCell<Vector3<f64>>,
|
||||
pub equipment_slots: Arc<HashMap<usize, EquipmentSlot>>,
|
||||
|
||||
pub movement_speed: AtomicCell<f64>,
|
||||
|
||||
@@ -112,6 +115,7 @@ impl LivingEntity {
|
||||
livings_flags: AtomicU8::new(0),
|
||||
active_effects: Mutex::new(HashMap::new()),
|
||||
entity_equipment: Arc::new(Mutex::new(EntityEquipment::new())),
|
||||
equipment_slots: Arc::new(build_equipment_slots()),
|
||||
jumping: AtomicBool::new(false),
|
||||
jumping_cooldown: AtomicU8::new(0),
|
||||
climbing: AtomicBool::new(false),
|
||||
@@ -336,7 +340,7 @@ impl LivingEntity {
|
||||
let suffocating = self.entity.tick_block_collisions(&caller, server).await;
|
||||
|
||||
if suffocating {
|
||||
self.damage(1.0, DamageType::IN_WALL).await;
|
||||
self.damage(caller, 1.0, DamageType::IN_WALL).await;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -680,6 +684,7 @@ impl LivingEntity {
|
||||
|
||||
pub async fn update_fall_distance(
|
||||
&self,
|
||||
caller: Arc<dyn EntityBase>,
|
||||
height_difference: f64,
|
||||
ground: bool,
|
||||
dont_damage: bool,
|
||||
@@ -700,7 +705,7 @@ impl LivingEntity {
|
||||
|
||||
// TODO: Play block fall sound
|
||||
if damage > 0.0 {
|
||||
let check_damage = self.damage(damage, DamageType::FALL).await; // Fall
|
||||
let check_damage = self.damage(caller, damage, DamageType::FALL).await; // Fall
|
||||
if check_damage {
|
||||
self.entity
|
||||
.play_sound(Self::get_fall_sound(fall_distance as i32))
|
||||
@@ -843,6 +848,56 @@ impl LivingEntity {
|
||||
}
|
||||
}
|
||||
|
||||
async fn try_use_death_protector(&self, caller: &Arc<dyn EntityBase>) -> bool {
|
||||
for hand in Hand::all() {
|
||||
let stack = self.get_stack_in_hand(caller, hand).await;
|
||||
let mut stack = stack.lock().await;
|
||||
// TODO: effects...
|
||||
if stack.get_data_component::<DeathProtectionImpl>().is_some() {
|
||||
stack.decrement(1);
|
||||
self.set_health(1.0).await;
|
||||
self.entity
|
||||
.world
|
||||
.send_entity_status(&self.entity, EntityStatus::UseTotemOfUndying)
|
||||
.await;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
false
|
||||
}
|
||||
|
||||
pub async fn held_item(&self, caller: &Arc<dyn EntityBase>) -> Arc<Mutex<ItemStack>> {
|
||||
if let Some(player) = caller.get_player() {
|
||||
return player.inventory.held_item();
|
||||
}
|
||||
let slot = self
|
||||
.equipment_slots
|
||||
.get(&PlayerInventory::OFF_HAND_SLOT)
|
||||
.unwrap();
|
||||
self.entity_equipment.lock().await.get(slot)
|
||||
}
|
||||
|
||||
pub async fn get_stack_in_hand(
|
||||
&self,
|
||||
caller: &Arc<dyn EntityBase>,
|
||||
hand: Hand,
|
||||
) -> Arc<Mutex<ItemStack>> {
|
||||
match hand {
|
||||
Hand::Left => self.off_hand_item().await,
|
||||
Hand::Right => self.held_item(caller).await,
|
||||
}
|
||||
}
|
||||
|
||||
/// getOffHandStack in source
|
||||
pub async fn off_hand_item(&self) -> Arc<Mutex<ItemStack>> {
|
||||
let slot = self
|
||||
.equipment_slots
|
||||
.get(&PlayerInventory::OFF_HAND_SLOT)
|
||||
.unwrap();
|
||||
self.entity_equipment.lock().await.get(slot)
|
||||
}
|
||||
|
||||
pub fn is_part_of_game(&self) -> bool {
|
||||
self.is_spectator() && self.entity.is_alive()
|
||||
}
|
||||
@@ -914,6 +969,7 @@ impl NBTStorage for LivingEntity {
|
||||
impl EntityBase for LivingEntity {
|
||||
async fn damage_with_context(
|
||||
&self,
|
||||
caller: Arc<dyn EntityBase>,
|
||||
amount: f32,
|
||||
damage_type: DamageType,
|
||||
position: Option<Vector3<f64>>,
|
||||
@@ -996,7 +1052,7 @@ impl EntityBase for LivingEntity {
|
||||
self.set_health(new_health).await;
|
||||
}
|
||||
|
||||
if new_health <= 0.0 {
|
||||
if new_health <= 0.0 && !self.try_use_death_protector(&caller).await {
|
||||
self.on_death(damage_type, source, cause).await;
|
||||
}
|
||||
|
||||
|
||||
@@ -113,6 +113,7 @@ where
|
||||
|
||||
async fn damage_with_context(
|
||||
&self,
|
||||
caller: Arc<dyn EntityBase>,
|
||||
amount: f32,
|
||||
damage_type: DamageType,
|
||||
position: Option<Vector3<f64>>,
|
||||
@@ -121,7 +122,7 @@ where
|
||||
) -> bool {
|
||||
self.get_mob_entity()
|
||||
.living_entity
|
||||
.damage_with_context(amount, damage_type, position, source, cause)
|
||||
.damage_with_context(caller, amount, damage_type, position, source, cause)
|
||||
.await
|
||||
}
|
||||
|
||||
|
||||
@@ -106,8 +106,13 @@ pub trait EntityBase: Send + Sync + NBTStorage {
|
||||
}
|
||||
|
||||
/// Returns if damage was successful or not
|
||||
async fn damage(&self, amount: f32, damage_type: DamageType) -> bool {
|
||||
self.damage_with_context(amount, damage_type, None, None, None)
|
||||
async fn damage(
|
||||
&self,
|
||||
caller: Arc<dyn EntityBase>,
|
||||
amount: f32,
|
||||
damage_type: DamageType,
|
||||
) -> bool {
|
||||
self.damage_with_context(caller, amount, damage_type, None, None, None)
|
||||
.await
|
||||
}
|
||||
|
||||
@@ -129,12 +134,16 @@ pub trait EntityBase: Send + Sync + NBTStorage {
|
||||
|
||||
async fn damage_with_context(
|
||||
&self,
|
||||
amount: f32,
|
||||
damage_type: DamageType,
|
||||
position: Option<Vector3<f64>>,
|
||||
source: Option<&dyn EntityBase>,
|
||||
cause: Option<&dyn EntityBase>,
|
||||
) -> bool;
|
||||
_caller: Arc<dyn EntityBase>,
|
||||
_amount: f32,
|
||||
_damage_type: DamageType,
|
||||
_position: Option<Vector3<f64>>,
|
||||
_source: Option<&dyn EntityBase>,
|
||||
_cause: Option<&dyn EntityBase>,
|
||||
) -> bool {
|
||||
// Just do nothing
|
||||
false
|
||||
}
|
||||
|
||||
/// Called when a player collides with a entity
|
||||
async fn on_player_collision(&self, _player: &Arc<Player>) {}
|
||||
@@ -181,9 +190,11 @@ pub trait EntityBase: Send + Sync + NBTStorage {
|
||||
}
|
||||
|
||||
/// Kills the Entity.
|
||||
async fn kill(&self) {
|
||||
async fn kill(&self, caller: Arc<dyn EntityBase>) {
|
||||
if let Some(living) = self.get_living_entity() {
|
||||
living.damage(f32::MAX, DamageType::GENERIC_KILL).await;
|
||||
living
|
||||
.damage(caller, f32::MAX, DamageType::GENERIC_KILL)
|
||||
.await;
|
||||
} else {
|
||||
// TODO this should be removed once all entities are implemented
|
||||
self.get_entity().remove().await;
|
||||
@@ -1174,7 +1185,12 @@ impl Entity {
|
||||
|
||||
if let Some(living) = caller.get_living_entity() {
|
||||
living
|
||||
.update_fall_distance(final_move.y, self.on_ground.load(Ordering::SeqCst), false)
|
||||
.update_fall_distance(
|
||||
caller.clone(),
|
||||
final_move.y,
|
||||
self.on_ground.load(Ordering::SeqCst),
|
||||
false,
|
||||
)
|
||||
.await;
|
||||
}
|
||||
}
|
||||
@@ -1694,10 +1710,12 @@ impl Entity {
|
||||
vehicle.is_some()
|
||||
}
|
||||
|
||||
pub async fn check_out_of_world(&self, dyn_self: &dyn EntityBase) {
|
||||
pub async fn check_out_of_world(&self, dyn_self: Arc<dyn EntityBase>) {
|
||||
if self.pos.load().y < f64::from(self.world.generation_settings().shape.min_y) - 64.0 {
|
||||
// Tick out of world damage
|
||||
dyn_self.damage(4.0, DamageType::OUT_OF_WORLD).await;
|
||||
dyn_self
|
||||
.damage(dyn_self.clone(), 4.0, DamageType::OUT_OF_WORLD)
|
||||
.await;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1792,21 +1810,10 @@ impl NBTStorage for Entity {
|
||||
|
||||
#[async_trait]
|
||||
impl EntityBase for Entity {
|
||||
async fn damage_with_context(
|
||||
&self,
|
||||
_amount: f32,
|
||||
_damage_type: DamageType,
|
||||
_position: Option<Vector3<f64>>,
|
||||
_source: Option<&dyn EntityBase>,
|
||||
_cause: Option<&dyn EntityBase>,
|
||||
) -> bool {
|
||||
false
|
||||
}
|
||||
|
||||
async fn tick(&self, caller: Arc<dyn EntityBase>, _server: &Server) {
|
||||
self.tick_portal(&caller).await;
|
||||
self.update_fluid_state(&caller).await;
|
||||
self.check_out_of_world(&*caller).await;
|
||||
self.check_out_of_world(caller.clone()).await;
|
||||
let fire_ticks = self.fire_ticks.load(Ordering::Relaxed);
|
||||
if fire_ticks > 0 {
|
||||
if self.entity_type.fire_immune {
|
||||
@@ -1816,7 +1823,9 @@ impl EntityBase for Entity {
|
||||
}
|
||||
} else {
|
||||
if fire_ticks % 20 == 0 {
|
||||
caller.damage(1.0, DamageType::ON_FIRE).await;
|
||||
caller
|
||||
.damage(caller.clone(), 1.0, DamageType::ON_FIRE)
|
||||
.await;
|
||||
}
|
||||
|
||||
self.fire_ticks.store(fire_ticks - 1, Ordering::Relaxed);
|
||||
|
||||
@@ -53,7 +53,6 @@ use pumpkin_protocol::java::client::play::{
|
||||
};
|
||||
use pumpkin_protocol::java::server::play::SClickSlot;
|
||||
use pumpkin_registry::VanillaDimensionType;
|
||||
use pumpkin_util::GameMode;
|
||||
use pumpkin_util::math::{
|
||||
boundingbox::BoundingBox, experience, position::BlockPos, vector2::Vector2, vector3::Vector3,
|
||||
};
|
||||
@@ -62,6 +61,7 @@ use pumpkin_util::resource_location::ResourceLocation;
|
||||
use pumpkin_util::text::TextComponent;
|
||||
use pumpkin_util::text::click::ClickEvent;
|
||||
use pumpkin_util::text::hover::HoverEvent;
|
||||
use pumpkin_util::{GameMode, Hand};
|
||||
use pumpkin_world::biome;
|
||||
use pumpkin_world::cylindrical_chunk_iterator::Cylindrical;
|
||||
use pumpkin_world::entity::entity_data_flags::{
|
||||
@@ -308,7 +308,10 @@ impl Player {
|
||||
matches!(gamemode, GameMode::Creative | GameMode::Spectator),
|
||||
));
|
||||
|
||||
let inventory = Arc::new(PlayerInventory::new(living_entity.entity_equipment.clone()));
|
||||
let inventory = Arc::new(PlayerInventory::new(
|
||||
living_entity.entity_equipment.clone(),
|
||||
living_entity.equipment_slots.clone(),
|
||||
));
|
||||
|
||||
let player_screen_handler = Arc::new(Mutex::new(
|
||||
PlayerScreenHandler::new(&inventory, None, 0).await,
|
||||
@@ -492,6 +495,7 @@ impl Player {
|
||||
|
||||
if !victim
|
||||
.damage_with_context(
|
||||
victim.clone(),
|
||||
damage as f32,
|
||||
DamageType::PLAYER_ATTACK,
|
||||
None,
|
||||
@@ -829,7 +833,7 @@ impl Player {
|
||||
self.last_attacked_ticks.fetch_add(1, Ordering::Relaxed);
|
||||
|
||||
self.living_entity.tick(self.clone(), server).await;
|
||||
self.hunger_manager.tick(self.as_ref()).await;
|
||||
self.hunger_manager.tick(self).await;
|
||||
|
||||
// experience handling
|
||||
self.tick_experience().await;
|
||||
@@ -2052,6 +2056,7 @@ impl NBTStorageInit for PlayerInventory {}
|
||||
impl EntityBase for Player {
|
||||
async fn damage_with_context(
|
||||
&self,
|
||||
caller: Arc<dyn EntityBase>,
|
||||
amount: f32,
|
||||
damage_type: DamageType,
|
||||
position: Option<Vector3<f64>>,
|
||||
@@ -2070,7 +2075,7 @@ impl EntityBase for Player {
|
||||
.expect("Entity not found in world");
|
||||
let result = self
|
||||
.living_entity
|
||||
.damage_with_context(amount, damage_type, position, source, cause)
|
||||
.damage_with_context(caller, amount, damage_type, position, source, cause)
|
||||
.await;
|
||||
if result {
|
||||
let health = self.living_entity.health.load();
|
||||
@@ -2258,29 +2263,6 @@ impl Abilities {
|
||||
}
|
||||
}
|
||||
|
||||
/// Represents the player's dominant hand.
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub enum Hand {
|
||||
/// Usually the player's off-hand.
|
||||
Left,
|
||||
/// Usually the player's primary hand.
|
||||
Right,
|
||||
}
|
||||
|
||||
pub struct InvalidHand;
|
||||
|
||||
impl TryFrom<i32> for Hand {
|
||||
type Error = InvalidHand;
|
||||
|
||||
fn try_from(value: i32) -> Result<Self, Self::Error> {
|
||||
match value {
|
||||
0 => Ok(Self::Left),
|
||||
1 => Ok(Self::Right),
|
||||
_ => Err(InvalidHand),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Represents the player's respawn point.
|
||||
#[derive(Copy, Debug, Clone, PartialEq)]
|
||||
pub struct RespawnPoint {
|
||||
|
||||
@@ -5,7 +5,6 @@ use std::{
|
||||
|
||||
use super::{Entity, EntityBase, NBTStorage, living::LivingEntity};
|
||||
use async_trait::async_trait;
|
||||
use pumpkin_data::damage::DamageType;
|
||||
use pumpkin_util::math::vector3::Vector3;
|
||||
|
||||
pub struct ThrownItemEntity {
|
||||
@@ -85,17 +84,6 @@ impl EntityBase for ThrownItemEntity {
|
||||
&self.entity
|
||||
}
|
||||
|
||||
async fn damage_with_context(
|
||||
&self,
|
||||
_amount: f32,
|
||||
_damage_type: DamageType,
|
||||
_position: Option<Vector3<f64>>,
|
||||
_source: Option<&dyn EntityBase>,
|
||||
_cause: Option<&dyn EntityBase>,
|
||||
) -> bool {
|
||||
false
|
||||
}
|
||||
|
||||
fn get_living_entity(&self) -> Option<&LivingEntity> {
|
||||
None
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@ use super::{Entity, EntityBase, NBTStorage, living::LivingEntity};
|
||||
use crate::server::Server;
|
||||
use async_trait::async_trait;
|
||||
use core::f32;
|
||||
use pumpkin_data::{Block, damage::DamageType};
|
||||
use pumpkin_data::{Block};
|
||||
use pumpkin_protocol::{
|
||||
codec::var_int::VarInt,
|
||||
java::client::play::{MetaDataType, Metadata},
|
||||
@@ -96,17 +96,6 @@ impl EntityBase for TNTEntity {
|
||||
.await;
|
||||
}
|
||||
|
||||
async fn damage_with_context(
|
||||
&self,
|
||||
_amount: f32,
|
||||
_damage_type: DamageType,
|
||||
_position: Option<Vector3<f64>>,
|
||||
_source: Option<&dyn EntityBase>,
|
||||
_cause: Option<&dyn EntityBase>,
|
||||
) -> bool {
|
||||
false
|
||||
}
|
||||
|
||||
fn get_entity(&self) -> &Entity {
|
||||
&self.entity
|
||||
}
|
||||
|
||||
@@ -18,7 +18,6 @@
|
||||
#![deny(clippy::needless_pass_by_ref_mut)]
|
||||
#![deny(clippy::needless_collect)]
|
||||
#![deny(clippy::redundant_clone)]
|
||||
#![deny(clippy::branches_sharing_code)]
|
||||
#![deny(clippy::set_contains_or_insert)]
|
||||
#![deny(clippy::significant_drop_in_scrutinee)]
|
||||
// use log crate
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
use std::{num::NonZeroU8, sync::Arc};
|
||||
|
||||
use crate::{
|
||||
entity::player::{ChatMode, Hand},
|
||||
entity::player::ChatMode,
|
||||
net::{ClientPlatform, PlayerConfig, can_not_join, java::JavaClient},
|
||||
server::Server,
|
||||
};
|
||||
@@ -15,7 +15,7 @@ use pumpkin_protocol::{
|
||||
SConfigResourcePack, SKnownPacks, SPluginMessage,
|
||||
},
|
||||
};
|
||||
use pumpkin_util::text::TextComponent;
|
||||
use pumpkin_util::{Hand, text::TextComponent};
|
||||
|
||||
impl JavaClient {
|
||||
pub async fn handle_client_information_config(
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
use pumpkin_protocol::bedrock::server::text::SText;
|
||||
use pumpkin_util::PermissionLvl;
|
||||
use pumpkin_util::{Hand, PermissionLvl};
|
||||
use rsa::pkcs1v15::{Signature as RsaPkcs1v15Signature, VerifyingKey};
|
||||
use rsa::signature::Verifier;
|
||||
use sha1::Sha1;
|
||||
@@ -14,7 +14,7 @@ use crate::block::registry::BlockActionResult;
|
||||
use crate::block::{self, BlockIsReplacing};
|
||||
use crate::command::CommandSender;
|
||||
use crate::entity::EntityBase;
|
||||
use crate::entity::player::{ChatMode, ChatSession, Hand, Player};
|
||||
use crate::entity::player::{ChatMode, ChatSession, Player};
|
||||
use crate::entity::r#type::from_type;
|
||||
use crate::error::PumpkinError;
|
||||
use crate::net::PlayerConfig;
|
||||
@@ -327,6 +327,7 @@ impl JavaClient {
|
||||
if !player.abilities.lock().await.flying {
|
||||
player.living_entity
|
||||
.update_fall_distance(
|
||||
player.clone(),
|
||||
height_difference,
|
||||
packet.collision & FLAG_ON_GROUND != 0,
|
||||
player.gamemode.load() == GameMode::Creative,
|
||||
@@ -451,6 +452,7 @@ impl JavaClient {
|
||||
if !player.abilities.lock().await.flying {
|
||||
player.living_entity
|
||||
.update_fall_distance(
|
||||
player.clone(),
|
||||
height_difference,
|
||||
(packet.collision & FLAG_ON_GROUND) != 0,
|
||||
player.gamemode.load() == GameMode::Creative,
|
||||
|
||||
@@ -9,13 +9,13 @@ use crate::{
|
||||
banned_ip_data::BANNED_IP_LIST, banned_player_data::BANNED_PLAYER_LIST,
|
||||
op_data::OPERATOR_CONFIG, whitelist_data::WHITELIST_CONFIG,
|
||||
},
|
||||
entity::player::{ChatMode, Hand},
|
||||
entity::player::ChatMode,
|
||||
net::{bedrock::BedrockClient, java::JavaClient},
|
||||
server::Server,
|
||||
};
|
||||
|
||||
use pumpkin_protocol::{ClientPacket, Property};
|
||||
use pumpkin_util::{ProfileAction, text::TextComponent};
|
||||
use pumpkin_util::{Hand, ProfileAction, text::TextComponent};
|
||||
use serde::Deserialize;
|
||||
use sha1::Digest;
|
||||
use sha2::Sha256;
|
||||
|
||||
Reference in New Issue
Block a user