fix: ender pearl delay
This commit is contained in:
parent
b0930dd6eb
commit
d33484d057
@ -6,7 +6,7 @@ use crate::data_component::DataComponent::{
|
|||||||
AttributeModifiers, BlocksAttacks, Consumable, CustomData, CustomName, Damage, DamageResistant,
|
AttributeModifiers, BlocksAttacks, Consumable, CustomData, CustomName, Damage, DamageResistant,
|
||||||
DeathProtection, Enchantments, Equippable, FireworkExplosion, Fireworks, Food, ItemModel,
|
DeathProtection, Enchantments, Equippable, FireworkExplosion, Fireworks, Food, ItemModel,
|
||||||
ItemName, JukeboxPlayable, MaxDamage, MaxStackSize, PotionContents, StoredEnchantments, Tool,
|
ItemName, JukeboxPlayable, MaxDamage, MaxStackSize, PotionContents, StoredEnchantments, Tool,
|
||||||
Unbreakable, Weapon,
|
Unbreakable, UseCooldown, Weapon,
|
||||||
};
|
};
|
||||||
use crate::effect::{self, StatusEffect};
|
use crate::effect::{self, StatusEffect};
|
||||||
use crate::entity_type::EntityType;
|
use crate::entity_type::EntityType;
|
||||||
@ -68,6 +68,7 @@ pub fn read_data(id: DataComponent, data: &NbtTag) -> Option<Box<dyn DataCompone
|
|||||||
Consumable => Some(ConsumableImpl::read_data(data)?.to_dyn()),
|
Consumable => Some(ConsumableImpl::read_data(data)?.to_dyn()),
|
||||||
Equippable => Some(EquippableImpl::read_data(data)?.to_dyn()),
|
Equippable => Some(EquippableImpl::read_data(data)?.to_dyn()),
|
||||||
StoredEnchantments => Some(StoredEnchantmentsImpl::read_data(data)?.to_dyn()),
|
StoredEnchantments => Some(StoredEnchantmentsImpl::read_data(data)?.to_dyn()),
|
||||||
|
UseCooldown => Some(UseCooldownImpl::read_data(data)?.to_dyn()),
|
||||||
_ => None,
|
_ => None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -720,8 +721,61 @@ impl Hash for ConsumableImpl {
|
|||||||
}
|
}
|
||||||
#[derive(Clone, Debug, Hash, PartialEq, Eq)]
|
#[derive(Clone, Debug, Hash, PartialEq, Eq)]
|
||||||
pub struct UseRemainderImpl;
|
pub struct UseRemainderImpl;
|
||||||
#[derive(Clone, Debug, Hash, PartialEq, Eq)]
|
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
|
||||||
pub struct UseCooldownImpl;
|
pub struct UseCooldownImpl {
|
||||||
|
pub seconds: f32,
|
||||||
|
pub cooldown_group: Option<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl UseCooldownImpl {
|
||||||
|
pub fn new(seconds: f32, cooldown_group: Option<String>) -> Self {
|
||||||
|
Self {
|
||||||
|
seconds,
|
||||||
|
cooldown_group,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn read_data(data: &NbtTag) -> Option<Self> {
|
||||||
|
let compound = data.extract_compound()?;
|
||||||
|
let seconds = compound.get_float("seconds")?;
|
||||||
|
let cooldown_group = compound.get_string("cooldown_group").map(|s| s.to_string());
|
||||||
|
Some(Self {
|
||||||
|
seconds,
|
||||||
|
cooldown_group,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl DataComponentImpl for UseCooldownImpl {
|
||||||
|
fn write_data(&self) -> NbtTag {
|
||||||
|
let mut compound = NbtCompound::new();
|
||||||
|
compound.put_float("seconds", self.seconds);
|
||||||
|
if let Some(group) = &self.cooldown_group {
|
||||||
|
compound.put_string("cooldown_group", group.clone());
|
||||||
|
}
|
||||||
|
NbtTag::Compound(compound)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_hash(&self) -> i32 {
|
||||||
|
let mut digest = Digest::new(Crc32Iscsi);
|
||||||
|
digest.update(&get_f32_hash(self.seconds).to_le_bytes());
|
||||||
|
if let Some(group) = &self.cooldown_group {
|
||||||
|
digest.update(&get_str_hash(group).to_le_bytes());
|
||||||
|
}
|
||||||
|
digest.finalize() as i32
|
||||||
|
}
|
||||||
|
|
||||||
|
default_impl!(UseCooldown);
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Eq for UseCooldownImpl {}
|
||||||
|
|
||||||
|
impl Hash for UseCooldownImpl {
|
||||||
|
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
|
||||||
|
self.seconds.to_bits().hash(state);
|
||||||
|
self.cooldown_group.hash(state);
|
||||||
|
}
|
||||||
|
}
|
||||||
#[derive(Clone, Copy, Debug, Hash, PartialEq, Eq)]
|
#[derive(Clone, Copy, Debug, Hash, PartialEq, Eq)]
|
||||||
pub enum DamageResistantType {
|
pub enum DamageResistantType {
|
||||||
/// Damage always dealt to ender dragons
|
/// Damage always dealt to ender dragons
|
||||||
|
|||||||
@ -2,8 +2,8 @@ use crate::data_component::DataComponent;
|
|||||||
use crate::data_component::DataComponent::Enchantments;
|
use crate::data_component::DataComponent::Enchantments;
|
||||||
use crate::data_component_impl::{
|
use crate::data_component_impl::{
|
||||||
BlocksAttacksImpl, ConsumableImpl, DamageImpl, DataComponentImpl, EnchantmentsImpl, IDSet,
|
BlocksAttacksImpl, ConsumableImpl, DamageImpl, DataComponentImpl, EnchantmentsImpl, IDSet,
|
||||||
MaxDamageImpl, MaxStackSizeImpl, ToolImpl, UnbreakableImpl, get, get_mut, read_data,
|
MaxDamageImpl, MaxStackSizeImpl, ToolImpl, UnbreakableImpl, UseCooldownImpl, get, get_mut,
|
||||||
read_data_pnbt,
|
read_data, read_data_pnbt,
|
||||||
};
|
};
|
||||||
use crate::item::Item;
|
use crate::item::Item;
|
||||||
use crate::recipes::RecipeResultStruct;
|
use crate::recipes::RecipeResultStruct;
|
||||||
@ -131,6 +131,11 @@ impl ItemStack {
|
|||||||
.map(|value| value.max_damage)
|
.map(|value| value.max_damage)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[must_use]
|
||||||
|
pub fn get_use_cooldown(&self) -> Option<&UseCooldownImpl> {
|
||||||
|
self.get_data_component::<UseCooldownImpl>()
|
||||||
|
}
|
||||||
|
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn get_damage(&self) -> i32 {
|
pub fn get_damage(&self) -> i32 {
|
||||||
self.get_data_component::<DamageImpl>()
|
self.get_data_component::<DamageImpl>()
|
||||||
|
|||||||
@ -10,4 +10,5 @@ pub mod raknet;
|
|||||||
pub mod request_chunk_radius;
|
pub mod request_chunk_radius;
|
||||||
pub mod request_network_settings;
|
pub mod request_network_settings;
|
||||||
pub mod resource_pack_response;
|
pub mod resource_pack_response;
|
||||||
|
pub mod set_local_player_as_initialized;
|
||||||
pub mod text;
|
pub mod text;
|
||||||
|
|||||||
@ -0,0 +1,16 @@
|
|||||||
|
use crate::{codec::var_ulong::VarULong, serial::PacketRead};
|
||||||
|
use pumpkin_macros::packet;
|
||||||
|
use std::io::{Error, Read};
|
||||||
|
|
||||||
|
#[packet(113)]
|
||||||
|
pub struct SSetLocalPlayerAsInitialized {
|
||||||
|
pub runtime_entity_id: VarULong,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PacketRead for SSetLocalPlayerAsInitialized {
|
||||||
|
fn read<R: Read>(reader: &mut R) -> Result<Self, Error> {
|
||||||
|
Ok(Self {
|
||||||
|
runtime_entity_id: VarULong::read(reader)?,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -7,7 +7,8 @@ use pumpkin_data::data_component_impl::{
|
|||||||
ConsumableImpl, ConsumeAnimation, ConsumeEffect, CustomNameImpl, DamageImpl, DataComponentImpl,
|
ConsumableImpl, ConsumeAnimation, ConsumeEffect, CustomNameImpl, DamageImpl, DataComponentImpl,
|
||||||
EnchantmentsImpl, EquipmentSlot, EquippableImpl, FireworkExplosionImpl, FireworkExplosionShape,
|
EnchantmentsImpl, EquipmentSlot, EquippableImpl, FireworkExplosionImpl, FireworkExplosionShape,
|
||||||
FireworksImpl, IDSet, IDSetContent, IdOr, ItemModelImpl, MaxStackSizeImpl, PotionContentsImpl,
|
FireworksImpl, IDSet, IDSetContent, IdOr, ItemModelImpl, MaxStackSizeImpl, PotionContentsImpl,
|
||||||
SoundEvent, StatusEffectInstance, StoredEnchantmentsImpl, UnbreakableImpl, get,
|
SoundEvent, StatusEffectInstance, StoredEnchantmentsImpl, UnbreakableImpl, UseCooldownImpl,
|
||||||
|
get,
|
||||||
};
|
};
|
||||||
use pumpkin_data::effect::StatusEffect;
|
use pumpkin_data::effect::StatusEffect;
|
||||||
use pumpkin_data::entity::EntityType;
|
use pumpkin_data::entity::EntityType;
|
||||||
@ -837,6 +838,7 @@ pub fn deserialize<'a, A: SeqAccess<'a>>(
|
|||||||
DataComponent::Consumable => Ok(ConsumableImpl::deserialize(seq)?.to_dyn()),
|
DataComponent::Consumable => Ok(ConsumableImpl::deserialize(seq)?.to_dyn()),
|
||||||
DataComponent::Equippable => Ok(EquippableImpl::deserialize(seq)?.to_dyn()),
|
DataComponent::Equippable => Ok(EquippableImpl::deserialize(seq)?.to_dyn()),
|
||||||
DataComponent::StoredEnchantments => Ok(StoredEnchantmentsImpl::deserialize(seq)?.to_dyn()),
|
DataComponent::StoredEnchantments => Ok(StoredEnchantmentsImpl::deserialize(seq)?.to_dyn()),
|
||||||
|
DataComponent::UseCooldown => Ok(UseCooldownImpl::deserialize(seq)?.to_dyn()),
|
||||||
_ => Err(serde::de::Error::custom(format!("{id:?} (TODO)"))),
|
_ => Err(serde::de::Error::custom(format!("{id:?} (TODO)"))),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -858,6 +860,29 @@ pub fn serialize<T: SerializeStruct>(
|
|||||||
DataComponent::Consumable => get::<ConsumableImpl>(value).serialize(seq),
|
DataComponent::Consumable => get::<ConsumableImpl>(value).serialize(seq),
|
||||||
DataComponent::Equippable => get::<EquippableImpl>(value).serialize(seq),
|
DataComponent::Equippable => get::<EquippableImpl>(value).serialize(seq),
|
||||||
DataComponent::StoredEnchantments => get::<StoredEnchantmentsImpl>(value).serialize(seq),
|
DataComponent::StoredEnchantments => get::<StoredEnchantmentsImpl>(value).serialize(seq),
|
||||||
|
DataComponent::UseCooldown => get::<UseCooldownImpl>(value).serialize(seq),
|
||||||
_ => todo!("{} not yet implemented", id.to_name()),
|
_ => todo!("{} not yet implemented", id.to_name()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl DataComponentCodec<Self> for UseCooldownImpl {
|
||||||
|
fn serialize<T: SerializeStruct>(&self, seq: &mut T) -> Result<(), T::Error> {
|
||||||
|
seq.serialize_field::<f32>("", &self.seconds)?;
|
||||||
|
seq.serialize_field::<Option<String>>("", &self.cooldown_group)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn deserialize<'a, A: SeqAccess<'a>>(seq: &mut A) -> Result<Self, A::Error> {
|
||||||
|
let seconds = seq
|
||||||
|
.next_element::<f32>()?
|
||||||
|
.ok_or(de::Error::custom("No UseCooldownImpl seconds float!"))?;
|
||||||
|
let cooldown_group = seq
|
||||||
|
.next_element::<Option<String>>()?
|
||||||
|
.ok_or(de::Error::custom(
|
||||||
|
"No UseCooldownImpl cooldown_group optional string!",
|
||||||
|
))?;
|
||||||
|
Ok(Self {
|
||||||
|
seconds,
|
||||||
|
cooldown_group,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
19
pumpkin-protocol/src/java/client/play/item_cooldown.rs
Normal file
19
pumpkin-protocol/src/java/client/play/item_cooldown.rs
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
use pumpkin_data::packet::clientbound::PLAY_COOLDOWN;
|
||||||
|
|
||||||
|
use crate::codec::var_int::VarInt;
|
||||||
|
use pumpkin_macros::java_packet;
|
||||||
|
use serde::Serialize;
|
||||||
|
|
||||||
|
#[derive(Serialize)]
|
||||||
|
#[java_packet(PLAY_COOLDOWN)]
|
||||||
|
pub struct CItemCooldown {
|
||||||
|
pub group: String,
|
||||||
|
pub cooldown: VarInt,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl CItemCooldown {
|
||||||
|
#[must_use]
|
||||||
|
pub const fn new(group: String, cooldown: VarInt) -> Self {
|
||||||
|
Self { group, cooldown }
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -33,6 +33,7 @@ mod game_event;
|
|||||||
mod head_rot;
|
mod head_rot;
|
||||||
mod hurt_animation;
|
mod hurt_animation;
|
||||||
mod initialize_world_border;
|
mod initialize_world_border;
|
||||||
|
mod item_cooldown;
|
||||||
mod keep_alive;
|
mod keep_alive;
|
||||||
mod level_event;
|
mod level_event;
|
||||||
mod light_update;
|
mod light_update;
|
||||||
@ -133,6 +134,7 @@ pub use game_event::*;
|
|||||||
pub use head_rot::*;
|
pub use head_rot::*;
|
||||||
pub use hurt_animation::*;
|
pub use hurt_animation::*;
|
||||||
pub use initialize_world_border::*;
|
pub use initialize_world_border::*;
|
||||||
|
pub use item_cooldown::*;
|
||||||
pub use keep_alive::*;
|
pub use keep_alive::*;
|
||||||
pub use level_event::*;
|
pub use level_event::*;
|
||||||
pub use light_update::*;
|
pub use light_update::*;
|
||||||
|
|||||||
@ -183,13 +183,13 @@ impl DynamicLightEngine {
|
|||||||
// No self-emission, darken it completely and continue propagation
|
// No self-emission, darken it completely and continue propagation
|
||||||
self.set_block_light_level(level, &neighbor_pos, 0)
|
self.set_block_light_level(level, &neighbor_pos, 0)
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.ok();
|
||||||
self.queue_block_light_decrease(neighbor_pos, neighbor_light);
|
self.queue_block_light_decrease(neighbor_pos, neighbor_light);
|
||||||
} else {
|
} else {
|
||||||
// Has self-emission, set to its own light and re-propagate from it
|
// Has self-emission, set to its own light and re-propagate from it
|
||||||
self.set_block_light_level(level, &neighbor_pos, neighbor_luminance)
|
self.set_block_light_level(level, &neighbor_pos, neighbor_luminance)
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.ok();
|
||||||
self.queue_block_light_increase(neighbor_pos, neighbor_luminance);
|
self.queue_block_light_increase(neighbor_pos, neighbor_luminance);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@ -204,11 +204,11 @@ impl DynamicLightEngine {
|
|||||||
pub async fn check_block_light_updates(&self, level: &Arc<Level>, pos: BlockPos) {
|
pub async fn check_block_light_updates(&self, level: &Arc<Level>, pos: BlockPos) {
|
||||||
match level.lighting_config {
|
match level.lighting_config {
|
||||||
LightingEngineConfig::Full => {
|
LightingEngineConfig::Full => {
|
||||||
self.set_block_light_level(level, &pos, 15).await.unwrap();
|
self.set_block_light_level(level, &pos, 15).await.ok();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
LightingEngineConfig::Dark => {
|
LightingEngineConfig::Dark => {
|
||||||
self.set_block_light_level(level, &pos, 0).await.unwrap();
|
self.set_block_light_level(level, &pos, 0).await.ok();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
LightingEngineConfig::Default => {}
|
LightingEngineConfig::Default => {}
|
||||||
@ -223,13 +223,13 @@ impl DynamicLightEngine {
|
|||||||
// Set to expected value immediately, then queue decrease to darken neighbors
|
// Set to expected value immediately, then queue decrease to darken neighbors
|
||||||
self.set_block_light_level(level, &pos, expected_light)
|
self.set_block_light_level(level, &pos, expected_light)
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.ok();
|
||||||
self.queue_block_light_decrease(pos, current_light);
|
self.queue_block_light_decrease(pos, current_light);
|
||||||
} else if expected_light > current_light {
|
} else if expected_light > current_light {
|
||||||
// Handle light increase (placing light source)
|
// Handle light increase (placing light source)
|
||||||
self.set_block_light_level(level, &pos, expected_light)
|
self.set_block_light_level(level, &pos, expected_light)
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.ok();
|
||||||
self.queue_block_light_increase(pos, expected_light);
|
self.queue_block_light_increase(pos, expected_light);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -318,7 +318,7 @@ impl DynamicLightEngine {
|
|||||||
if new_light > neighbor_light {
|
if new_light > neighbor_light {
|
||||||
self.set_sky_light_level(level, &neighbor_pos, new_light)
|
self.set_sky_light_level(level, &neighbor_pos, new_light)
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.ok();
|
||||||
|
|
||||||
if new_light > 0 {
|
if new_light > 0 {
|
||||||
self.queue_sky_light_increase(neighbor_pos, new_light);
|
self.queue_sky_light_increase(neighbor_pos, new_light);
|
||||||
@ -353,9 +353,7 @@ impl DynamicLightEngine {
|
|||||||
|
|
||||||
if neighbor_light == expected || neighbor_light < removed_light {
|
if neighbor_light == expected || neighbor_light < removed_light {
|
||||||
// This neighbor was lit by us, darken it
|
// This neighbor was lit by us, darken it
|
||||||
self.set_sky_light_level(level, &neighbor_pos, 0)
|
self.set_sky_light_level(level, &neighbor_pos, 0).await.ok();
|
||||||
.await
|
|
||||||
.unwrap();
|
|
||||||
self.queue_sky_light_decrease(neighbor_pos, neighbor_light);
|
self.queue_sky_light_decrease(neighbor_pos, neighbor_light);
|
||||||
} else if neighbor_light > removed_light {
|
} else if neighbor_light > removed_light {
|
||||||
// Neighbor has brighter light from another source
|
// Neighbor has brighter light from another source
|
||||||
@ -368,7 +366,7 @@ impl DynamicLightEngine {
|
|||||||
pub async fn check_sky_light_updates(&self, level: &Arc<Level>, pos: BlockPos) {
|
pub async fn check_sky_light_updates(&self, level: &Arc<Level>, pos: BlockPos) {
|
||||||
match level.lighting_config {
|
match level.lighting_config {
|
||||||
LightingEngineConfig::Full => {
|
LightingEngineConfig::Full => {
|
||||||
self.set_sky_light_level(level, &pos, 15).await.unwrap();
|
self.set_sky_light_level(level, &pos, 15).await.ok();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
LightingEngineConfig::Dark => {
|
LightingEngineConfig::Dark => {
|
||||||
@ -423,13 +421,13 @@ impl DynamicLightEngine {
|
|||||||
// Light decreased
|
// Light decreased
|
||||||
self.set_sky_light_level(level, &pos, expected_light)
|
self.set_sky_light_level(level, &pos, expected_light)
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.ok();
|
||||||
self.queue_sky_light_decrease(pos, current_light);
|
self.queue_sky_light_decrease(pos, current_light);
|
||||||
} else if expected_light > current_light {
|
} else if expected_light > current_light {
|
||||||
// Light increased
|
// Light increased
|
||||||
self.set_sky_light_level(level, &pos, expected_light)
|
self.set_sky_light_level(level, &pos, expected_light)
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.ok();
|
||||||
self.queue_sky_light_increase(pos, expected_light);
|
self.queue_sky_light_increase(pos, expected_light);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -2203,6 +2203,16 @@ impl EntityBase for LivingEntity {
|
|||||||
item_lock.decrement_unless_creative(player.gamemode.load(), 1);
|
item_lock.decrement_unless_creative(player.gamemode.load(), 1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if let Some(cooldown) = item.get_use_cooldown() {
|
||||||
|
let group = cooldown
|
||||||
|
.cooldown_group
|
||||||
|
.clone()
|
||||||
|
.unwrap_or_else(|| item.item.registry_key.to_string());
|
||||||
|
player
|
||||||
|
.start_cooldown(group, (cooldown.seconds * 20.0) as i32)
|
||||||
|
.await;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
self.clear_active_hand().await;
|
self.clear_active_hand().await;
|
||||||
|
|||||||
@ -69,7 +69,7 @@ use pumpkin_nbt::pnbt::PNbtCompound;
|
|||||||
impl NBTStorage for BatEntity {
|
impl NBTStorage for BatEntity {
|
||||||
fn write_nbt<'a>(&'a self, nbt: &'a mut PNbtCompound) -> NbtFuture<'a, ()> {
|
fn write_nbt<'a>(&'a self, nbt: &'a mut PNbtCompound) -> NbtFuture<'a, ()> {
|
||||||
Box::pin(async move {
|
Box::pin(async move {
|
||||||
self.mob_entity.living_entity.entity.write_nbt(nbt).await;
|
self.mob_entity.living_entity.write_nbt(nbt).await;
|
||||||
let flags: u8 = if self.is_roosting() { ROOSTING_FLAG } else { 0 };
|
let flags: u8 = if self.is_roosting() { ROOSTING_FLAG } else { 0 };
|
||||||
nbt.put_byte(flags as i8);
|
nbt.put_byte(flags as i8);
|
||||||
})
|
})
|
||||||
@ -77,11 +77,7 @@ impl NBTStorage for BatEntity {
|
|||||||
|
|
||||||
fn read_nbt_non_mut<'a>(&'a self, nbt: &'a mut PNbtCompound) -> NbtFuture<'a, ()> {
|
fn read_nbt_non_mut<'a>(&'a self, nbt: &'a mut PNbtCompound) -> NbtFuture<'a, ()> {
|
||||||
Box::pin(async move {
|
Box::pin(async move {
|
||||||
self.mob_entity
|
self.mob_entity.living_entity.read_nbt_non_mut(nbt).await;
|
||||||
.living_entity
|
|
||||||
.entity
|
|
||||||
.read_nbt_non_mut(nbt)
|
|
||||||
.await;
|
|
||||||
let flags = nbt.get_byte().unwrap_or(0) as u8;
|
let flags = nbt.get_byte().unwrap_or(0) as u8;
|
||||||
let roosting = (flags & ROOSTING_FLAG) != 0;
|
let roosting = (flags & ROOSTING_FLAG) != 0;
|
||||||
self.set_roosting(roosting).await;
|
self.set_roosting(roosting).await;
|
||||||
|
|||||||
@ -121,7 +121,7 @@ use pumpkin_nbt::pnbt::PNbtCompound;
|
|||||||
impl NBTStorage for CreeperEntity {
|
impl NBTStorage for CreeperEntity {
|
||||||
fn write_nbt<'a>(&'a self, nbt: &'a mut PNbtCompound) -> NbtFuture<'a, ()> {
|
fn write_nbt<'a>(&'a self, nbt: &'a mut PNbtCompound) -> NbtFuture<'a, ()> {
|
||||||
Box::pin(async {
|
Box::pin(async {
|
||||||
self.mob_entity.living_entity.entity.write_nbt(nbt).await;
|
self.mob_entity.living_entity.write_nbt(nbt).await;
|
||||||
nbt.put_bool(self.charged.load(Ordering::Relaxed));
|
nbt.put_bool(self.charged.load(Ordering::Relaxed));
|
||||||
nbt.put_short(self.fuse_time.load(Ordering::Relaxed) as i16);
|
nbt.put_short(self.fuse_time.load(Ordering::Relaxed) as i16);
|
||||||
nbt.put_byte(self.explosion_radius.load(Ordering::Relaxed) as i8);
|
nbt.put_byte(self.explosion_radius.load(Ordering::Relaxed) as i8);
|
||||||
@ -131,11 +131,7 @@ impl NBTStorage for CreeperEntity {
|
|||||||
|
|
||||||
fn read_nbt_non_mut<'a>(&'a self, nbt: &'a mut PNbtCompound) -> NbtFuture<'a, ()> {
|
fn read_nbt_non_mut<'a>(&'a self, nbt: &'a mut PNbtCompound) -> NbtFuture<'a, ()> {
|
||||||
Box::pin(async {
|
Box::pin(async {
|
||||||
self.mob_entity
|
self.mob_entity.living_entity.read_nbt_non_mut(nbt).await;
|
||||||
.living_entity
|
|
||||||
.entity
|
|
||||||
.read_nbt_non_mut(nbt)
|
|
||||||
.await;
|
|
||||||
self.charged
|
self.charged
|
||||||
.store(nbt.get_bool().unwrap_or(false), Ordering::Relaxed);
|
.store(nbt.get_bool().unwrap_or(false), Ordering::Relaxed);
|
||||||
self.fuse_time
|
self.fuse_time
|
||||||
|
|||||||
@ -322,7 +322,7 @@ impl ShulkerEntity {
|
|||||||
impl NBTStorage for ShulkerEntity {
|
impl NBTStorage for ShulkerEntity {
|
||||||
fn write_nbt<'a>(&'a self, nbt: &'a mut PNbtCompound) -> NbtFuture<'a, ()> {
|
fn write_nbt<'a>(&'a self, nbt: &'a mut PNbtCompound) -> NbtFuture<'a, ()> {
|
||||||
Box::pin(async move {
|
Box::pin(async move {
|
||||||
self.mob_entity.living_entity.entity.write_nbt(nbt).await;
|
self.mob_entity.living_entity.write_nbt(nbt).await;
|
||||||
nbt.put_byte(self.attach_face.load(Ordering::Relaxed) as i8);
|
nbt.put_byte(self.attach_face.load(Ordering::Relaxed) as i8);
|
||||||
nbt.put_byte(self.peek_amount.load(Ordering::Relaxed) as i8);
|
nbt.put_byte(self.peek_amount.load(Ordering::Relaxed) as i8);
|
||||||
nbt.put_byte(self.color.load(Ordering::Relaxed) as i8);
|
nbt.put_byte(self.color.load(Ordering::Relaxed) as i8);
|
||||||
@ -331,11 +331,7 @@ impl NBTStorage for ShulkerEntity {
|
|||||||
|
|
||||||
fn read_nbt_non_mut<'a>(&'a self, nbt: &'a mut PNbtCompound) -> NbtFuture<'a, ()> {
|
fn read_nbt_non_mut<'a>(&'a self, nbt: &'a mut PNbtCompound) -> NbtFuture<'a, ()> {
|
||||||
Box::pin(async move {
|
Box::pin(async move {
|
||||||
self.mob_entity
|
self.mob_entity.living_entity.read_nbt_non_mut(nbt).await;
|
||||||
.living_entity
|
|
||||||
.entity
|
|
||||||
.read_nbt_non_mut(nbt)
|
|
||||||
.await;
|
|
||||||
if let Ok(face) = nbt.get_byte() {
|
if let Ok(face) = nbt.get_byte() {
|
||||||
self.attach_face.store(face as u8, Ordering::Relaxed);
|
self.attach_face.store(face as u8, Ordering::Relaxed);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,4 +1,3 @@
|
|||||||
use std::sync::atomic::Ordering::Relaxed;
|
|
||||||
use std::sync::{Arc, Weak};
|
use std::sync::{Arc, Weak};
|
||||||
|
|
||||||
use pumpkin_data::entity::EntityType;
|
use pumpkin_data::entity::EntityType;
|
||||||
@ -64,19 +63,13 @@ use pumpkin_nbt::pnbt::PNbtCompound;
|
|||||||
impl NBTStorage for SlimeEntity {
|
impl NBTStorage for SlimeEntity {
|
||||||
fn write_nbt<'a>(&'a self, nbt: &'a mut PNbtCompound) -> NbtFuture<'a, ()> {
|
fn write_nbt<'a>(&'a self, nbt: &'a mut PNbtCompound) -> NbtFuture<'a, ()> {
|
||||||
Box::pin(async move {
|
Box::pin(async move {
|
||||||
self.entity.living_entity.entity.write_nbt(nbt).await;
|
self.entity.living_entity.write_nbt(nbt).await;
|
||||||
nbt.put_int(self.entity.living_entity.entity.data.load(Relaxed));
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn read_nbt_non_mut<'a>(&'a self, nbt: &'a mut PNbtCompound) -> NbtFuture<'a, ()> {
|
fn read_nbt_non_mut<'a>(&'a self, nbt: &'a mut PNbtCompound) -> NbtFuture<'a, ()> {
|
||||||
Box::pin(async move {
|
Box::pin(async move {
|
||||||
self.entity.living_entity.entity.read_nbt_non_mut(nbt).await;
|
self.entity.living_entity.read_nbt_non_mut(nbt).await;
|
||||||
self.entity
|
|
||||||
.living_entity
|
|
||||||
.entity
|
|
||||||
.data
|
|
||||||
.store(nbt.get_int().unwrap_or(0), Relaxed);
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -2694,7 +2694,7 @@ impl NBTStorage for Entity {
|
|||||||
fn write_nbt<'a>(&'a self, nbt: &'a mut PNbtCompound) -> NbtFuture<'a, ()> {
|
fn write_nbt<'a>(&'a self, nbt: &'a mut PNbtCompound) -> NbtFuture<'a, ()> {
|
||||||
Box::pin(async move {
|
Box::pin(async move {
|
||||||
let position = self.pos.load();
|
let position = self.pos.load();
|
||||||
nbt.put_string(&format!("minecraft:{}", self.entity_type.resource_name));
|
nbt.put_string(self.entity_type.resource_name);
|
||||||
nbt.put_uuid(&self.entity_uuid);
|
nbt.put_uuid(&self.entity_uuid);
|
||||||
|
|
||||||
// Pos
|
// Pos
|
||||||
|
|||||||
@ -110,7 +110,7 @@ use pumpkin_nbt::pnbt::PNbtCompound;
|
|||||||
impl NBTStorage for SheepEntity {
|
impl NBTStorage for SheepEntity {
|
||||||
fn write_nbt<'a>(&'a self, nbt: &'a mut PNbtCompound) -> NbtFuture<'a, ()> {
|
fn write_nbt<'a>(&'a self, nbt: &'a mut PNbtCompound) -> NbtFuture<'a, ()> {
|
||||||
Box::pin(async {
|
Box::pin(async {
|
||||||
self.mob_entity.living_entity.entity.write_nbt(nbt).await;
|
self.mob_entity.living_entity.write_nbt(nbt).await;
|
||||||
nbt.put_bool(self.is_sheared());
|
nbt.put_bool(self.is_sheared());
|
||||||
nbt.put_byte(self.get_color() as i8);
|
nbt.put_byte(self.get_color() as i8);
|
||||||
})
|
})
|
||||||
@ -118,11 +118,7 @@ impl NBTStorage for SheepEntity {
|
|||||||
|
|
||||||
fn read_nbt_non_mut<'a>(&'a self, nbt: &'a mut PNbtCompound) -> NbtFuture<'a, ()> {
|
fn read_nbt_non_mut<'a>(&'a self, nbt: &'a mut PNbtCompound) -> NbtFuture<'a, ()> {
|
||||||
Box::pin(async {
|
Box::pin(async {
|
||||||
self.mob_entity
|
self.mob_entity.living_entity.read_nbt_non_mut(nbt).await;
|
||||||
.living_entity
|
|
||||||
.entity
|
|
||||||
.read_nbt_non_mut(nbt)
|
|
||||||
.await;
|
|
||||||
let sheared = nbt.get_bool().unwrap_or(false);
|
let sheared = nbt.get_bool().unwrap_or(false);
|
||||||
let color = nbt.get_byte().unwrap_or(0) as u8;
|
let color = nbt.get_byte().unwrap_or(0) as u8;
|
||||||
let byte = (color & 0x0F) | if sheared { 0x10 } else { 0 };
|
let byte = (color & 0x0F) | if sheared { 0x10 } else { 0 };
|
||||||
|
|||||||
@ -15,10 +15,12 @@ use pumpkin_data::meta_data_type::MetaDataType;
|
|||||||
use pumpkin_data::tracked_data::TrackedData;
|
use pumpkin_data::tracked_data::TrackedData;
|
||||||
use pumpkin_inventory::player::ender_chest_inventory::EnderChestInventory;
|
use pumpkin_inventory::player::ender_chest_inventory::EnderChestInventory;
|
||||||
use pumpkin_protocol::bedrock::client::level_chunk::CLevelChunk;
|
use pumpkin_protocol::bedrock::client::level_chunk::CLevelChunk;
|
||||||
|
use pumpkin_protocol::bedrock::client::play_status::CPlayStatus;
|
||||||
use pumpkin_protocol::bedrock::client::set_time::CSetTime;
|
use pumpkin_protocol::bedrock::client::set_time::CSetTime;
|
||||||
use pumpkin_protocol::bedrock::client::update_abilities::{
|
use pumpkin_protocol::bedrock::client::update_abilities::{
|
||||||
Ability, AbilityLayer, CUpdateAbilities,
|
Ability, AbilityLayer, CUpdateAbilities,
|
||||||
};
|
};
|
||||||
|
use pumpkin_protocol::bedrock::frame_set::FrameSet;
|
||||||
use pumpkin_protocol::bedrock::server::text::SText;
|
use pumpkin_protocol::bedrock::server::text::SText;
|
||||||
use pumpkin_protocol::codec::item_stack_seralizer::ItemStackSerializer;
|
use pumpkin_protocol::codec::item_stack_seralizer::ItemStackSerializer;
|
||||||
use pumpkin_world::chunk::{ChunkData, ChunkEntityData};
|
use pumpkin_world::chunk::{ChunkData, ChunkEntityData};
|
||||||
@ -56,8 +58,8 @@ use pumpkin_protocol::codec::var_int::VarInt;
|
|||||||
use pumpkin_protocol::java::client::play::{
|
use pumpkin_protocol::java::client::play::{
|
||||||
Animation, CAcknowledgeBlockChange, CActionBar, CChangeDifficulty, CChunkBatchEnd,
|
Animation, CAcknowledgeBlockChange, CActionBar, CChangeDifficulty, CChunkBatchEnd,
|
||||||
CChunkBatchStart, CChunkData, CCloseContainer, CCombatDeath, CCustomPayload,
|
CChunkBatchStart, CChunkData, CCloseContainer, CCombatDeath, CCustomPayload,
|
||||||
CDisguisedChatMessage, CEntityAnimation, CEntityPositionSync, CGameEvent, CKeepAlive,
|
CDisguisedChatMessage, CEntityAnimation, CEntityPositionSync, CGameEvent, CItemCooldown,
|
||||||
COpenScreen, CParticle, CPlayerAbilities, CPlayerInfoUpdate, CPlayerPosition,
|
CKeepAlive, COpenScreen, CParticle, CPlayerAbilities, CPlayerInfoUpdate, CPlayerPosition,
|
||||||
CPlayerSpawnPosition, CRespawn, CSetContainerContent, CSetContainerProperty, CSetContainerSlot,
|
CPlayerSpawnPosition, CRespawn, CSetContainerContent, CSetContainerProperty, CSetContainerSlot,
|
||||||
CSetCursorItem, CSetEquipment, CSetExperience, CSetHealth, CSetPlayerInventory,
|
CSetCursorItem, CSetEquipment, CSetExperience, CSetHealth, CSetPlayerInventory,
|
||||||
CSetSelectedSlot, CSoundEffect, CStopSound, CSubtitle, CSystemChatMessage, CTabList,
|
CSetSelectedSlot, CSoundEffect, CStopSound, CSubtitle, CSystemChatMessage, CTabList,
|
||||||
@ -365,6 +367,12 @@ impl ChunkManager {
|
|||||||
/// Represents a Minecraft player entity.
|
/// Represents a Minecraft player entity.
|
||||||
///
|
///
|
||||||
/// A `Player` is a special type of entity that represents a human player connected to the server.
|
/// A `Player` is a special type of entity that represents a human player connected to the server.
|
||||||
|
#[derive(Clone, Copy, Debug)]
|
||||||
|
pub struct ItemCooldown {
|
||||||
|
pub start_tick: i32,
|
||||||
|
pub duration: i32,
|
||||||
|
}
|
||||||
|
|
||||||
pub struct Player {
|
pub struct Player {
|
||||||
/// The underlying living entity object that represents the player.
|
/// The underlying living entity object that represents the player.
|
||||||
pub living_entity: LivingEntity,
|
pub living_entity: LivingEntity,
|
||||||
@ -437,6 +445,7 @@ pub struct Player {
|
|||||||
pub permission_lvl: AtomicCell<PermissionLvl>,
|
pub permission_lvl: AtomicCell<PermissionLvl>,
|
||||||
/// Whether the client has reported that it has loaded.
|
/// Whether the client has reported that it has loaded.
|
||||||
pub client_loaded: AtomicBool,
|
pub client_loaded: AtomicBool,
|
||||||
|
pub bedrock_spawned: AtomicBool,
|
||||||
/// The amount of time (in ticks) the client has to report having finished loading before being timed out.
|
/// The amount of time (in ticks) the client has to report having finished loading before being timed out.
|
||||||
pub client_loaded_timeout: AtomicU32,
|
pub client_loaded_timeout: AtomicU32,
|
||||||
/// Item usage tracking for bows, crossbows, etc.
|
/// Item usage tracking for bows, crossbows, etc.
|
||||||
@ -449,6 +458,7 @@ pub struct Player {
|
|||||||
pub experience_progress: AtomicCell<f32>,
|
pub experience_progress: AtomicCell<f32>,
|
||||||
/// The player's total experience points.
|
/// The player's total experience points.
|
||||||
pub experience_points: AtomicI32,
|
pub experience_points: AtomicI32,
|
||||||
|
pub item_cooldowns: Mutex<HashMap<String, ItemCooldown>>,
|
||||||
pub experience_pick_up_delay: Mutex<u32>,
|
pub experience_pick_up_delay: Mutex<u32>,
|
||||||
pub chunk_manager: Mutex<ChunkManager>,
|
pub chunk_manager: Mutex<ChunkManager>,
|
||||||
pub has_played_before: AtomicBool,
|
pub has_played_before: AtomicBool,
|
||||||
@ -551,6 +561,7 @@ impl Player {
|
|||||||
ping: AtomicU32::new(0),
|
ping: AtomicU32::new(0),
|
||||||
last_attacked_ticks: AtomicU32::new(0),
|
last_attacked_ticks: AtomicU32::new(0),
|
||||||
client_loaded: AtomicBool::new(false),
|
client_loaded: AtomicBool::new(false),
|
||||||
|
bedrock_spawned: AtomicBool::new(false),
|
||||||
client_loaded_timeout: AtomicU32::new(60),
|
client_loaded_timeout: AtomicU32::new(60),
|
||||||
// Item usage tracking
|
// Item usage tracking
|
||||||
using_item: AtomicBool::new(false),
|
using_item: AtomicBool::new(false),
|
||||||
@ -573,6 +584,7 @@ impl Player {
|
|||||||
experience_level: AtomicI32::new(0),
|
experience_level: AtomicI32::new(0),
|
||||||
experience_progress: AtomicCell::new(0.0),
|
experience_progress: AtomicCell::new(0.0),
|
||||||
experience_points: AtomicI32::new(0),
|
experience_points: AtomicI32::new(0),
|
||||||
|
item_cooldowns: Mutex::new(HashMap::new()),
|
||||||
// Default to sending 16 chunks per tick.
|
// Default to sending 16 chunks per tick.
|
||||||
chunk_manager: Mutex::new(ChunkManager::new(
|
chunk_manager: Mutex::new(ChunkManager::new(
|
||||||
16,
|
16,
|
||||||
@ -608,6 +620,44 @@ impl Player {
|
|||||||
.await;
|
.await;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub async fn start_cooldown(&self, group: String, duration: i32) {
|
||||||
|
let mut cooldowns = self.item_cooldowns.lock().await;
|
||||||
|
cooldowns.insert(
|
||||||
|
group.clone(),
|
||||||
|
ItemCooldown {
|
||||||
|
start_tick: self.tick_counter.load(Ordering::Relaxed),
|
||||||
|
duration,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
self.client
|
||||||
|
.send_packet_now(&CItemCooldown::new(group, VarInt(duration)))
|
||||||
|
.await;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn get_cooldown(&self, group: &str) -> f32 {
|
||||||
|
let cooldowns = self.item_cooldowns.lock().await;
|
||||||
|
if let Some(cooldown) = cooldowns.get(group) {
|
||||||
|
let current_tick = self.tick_counter.load(Ordering::Relaxed);
|
||||||
|
let elapsed = current_tick - cooldown.start_tick;
|
||||||
|
if elapsed < cooldown.duration {
|
||||||
|
return 1.0 - (elapsed as f32 / cooldown.duration as f32);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
0.0
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn is_on_cooldown(&self, group: &str) -> bool {
|
||||||
|
let mut cooldowns = self.item_cooldowns.lock().await;
|
||||||
|
if let Some(cooldown) = cooldowns.get(group) {
|
||||||
|
let current_tick = self.tick_counter.load(Ordering::Relaxed);
|
||||||
|
if current_tick - cooldown.start_tick < cooldown.duration {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
cooldowns.remove(group);
|
||||||
|
}
|
||||||
|
false
|
||||||
|
}
|
||||||
|
|
||||||
pub async fn set_display_name(&self, display_name: Option<TextComponent>) {
|
pub async fn set_display_name(&self, display_name: Option<TextComponent>) {
|
||||||
*self.display_name.lock().await = display_name.clone();
|
*self.display_name.lock().await = display_name.clone();
|
||||||
// Update the tab list for everyone
|
// Update the tab list for everyone
|
||||||
@ -1612,6 +1662,16 @@ impl Player {
|
|||||||
})
|
})
|
||||||
.await;
|
.await;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if !self.bedrock_spawned.load(Ordering::Relaxed) && chunk_count > 4 {
|
||||||
|
let mut frame_set = FrameSet::default();
|
||||||
|
|
||||||
|
bedrock_client
|
||||||
|
.write_game_packet_to_set(&CPlayStatus::PlayerSpawn, &mut frame_set)
|
||||||
|
.await;
|
||||||
|
bedrock_client.send_frame_set(frame_set, 0x84).await;
|
||||||
|
self.bedrock_spawned.store(true, Ordering::Relaxed);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -26,11 +26,27 @@ impl ItemRegistry {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn on_use(&self, item: &Item, player: &Player) {
|
pub async fn on_use(&self, stack: &ItemStack, player: &Player) {
|
||||||
|
let item = stack.item;
|
||||||
|
let cooldown = stack.get_use_cooldown();
|
||||||
|
let cooldown_group = cooldown
|
||||||
|
.and_then(|c| c.cooldown_group.clone())
|
||||||
|
.unwrap_or_else(|| item.registry_key.to_string());
|
||||||
|
|
||||||
|
if player.is_on_cooldown(&cooldown_group).await {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
let pumpkin_item = self.get_pumpkin_item(item.id);
|
let pumpkin_item = self.get_pumpkin_item(item.id);
|
||||||
if let Some(pumpkin_item) = pumpkin_item {
|
if let Some(pumpkin_item) = pumpkin_item {
|
||||||
pumpkin_item.normal_use(item, player).await;
|
pumpkin_item.normal_use(item, player).await;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if let Some(cooldown) = cooldown {
|
||||||
|
player
|
||||||
|
.start_cooldown(cooldown_group, (cooldown.seconds * 20.0) as i32)
|
||||||
|
.await;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn on_stopped_using(&self, stack: &ItemStack, player: &Player) {
|
pub async fn on_stopped_using(&self, stack: &ItemStack, player: &Player) {
|
||||||
@ -59,12 +75,28 @@ impl ItemRegistry {
|
|||||||
block: &Block,
|
block: &Block,
|
||||||
server: &Server,
|
server: &Server,
|
||||||
) {
|
) {
|
||||||
|
let cooldown = stack.get_use_cooldown().cloned();
|
||||||
|
let cooldown_group = cooldown
|
||||||
|
.as_ref()
|
||||||
|
.and_then(|c| c.cooldown_group.clone())
|
||||||
|
.unwrap_or_else(|| stack.item.registry_key.to_string());
|
||||||
|
|
||||||
|
if player.is_on_cooldown(&cooldown_group).await {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
let pumpkin_item = self.get_pumpkin_item(stack.item.id);
|
let pumpkin_item = self.get_pumpkin_item(stack.item.id);
|
||||||
if let Some(pumpkin_item) = pumpkin_item {
|
if let Some(pumpkin_item) = pumpkin_item {
|
||||||
pumpkin_item
|
pumpkin_item
|
||||||
.use_on_block(stack, player, location, face, cursor_pos, block, server)
|
.use_on_block(stack, player, location, face, cursor_pos, block, server)
|
||||||
.await;
|
.await;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if let Some(cooldown) = cooldown {
|
||||||
|
player
|
||||||
|
.start_cooldown(cooldown_group, (cooldown.seconds * 20.0) as i32)
|
||||||
|
.await;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn use_on_entity(
|
pub async fn use_on_entity(
|
||||||
@ -73,10 +105,26 @@ impl ItemRegistry {
|
|||||||
player: &Player,
|
player: &Player,
|
||||||
entity: Arc<dyn EntityBase>,
|
entity: Arc<dyn EntityBase>,
|
||||||
) {
|
) {
|
||||||
|
let cooldown = stack.get_use_cooldown().cloned();
|
||||||
|
let cooldown_group = cooldown
|
||||||
|
.as_ref()
|
||||||
|
.and_then(|c| c.cooldown_group.clone())
|
||||||
|
.unwrap_or_else(|| stack.item.registry_key.to_string());
|
||||||
|
|
||||||
|
if player.is_on_cooldown(&cooldown_group).await {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
let pumpkin_item = self.get_pumpkin_item(stack.item.id);
|
let pumpkin_item = self.get_pumpkin_item(stack.item.id);
|
||||||
if let Some(pumpkin_item) = pumpkin_item {
|
if let Some(pumpkin_item) = pumpkin_item {
|
||||||
pumpkin_item.use_on_entity(stack, player, entity).await;
|
pumpkin_item.use_on_entity(stack, player, entity).await;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if let Some(cooldown) = cooldown {
|
||||||
|
player
|
||||||
|
.start_cooldown(cooldown_group, (cooldown.seconds * 20.0) as i32)
|
||||||
|
.await;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn can_mine(&self, item: &Item, player: &Player) -> bool {
|
pub fn can_mine(&self, item: &Item, player: &Player) -> bool {
|
||||||
|
|||||||
@ -26,7 +26,6 @@ use pumpkin_protocol::{
|
|||||||
command_request::SCommandRequest,
|
command_request::SCommandRequest,
|
||||||
container_close::SContainerClose,
|
container_close::SContainerClose,
|
||||||
interaction::SInteraction,
|
interaction::SInteraction,
|
||||||
loading_screen::SLoadingScreen,
|
|
||||||
login::SLogin,
|
login::SLogin,
|
||||||
player_action::SPlayerAction,
|
player_action::SPlayerAction,
|
||||||
player_auth_input::SPlayerAuthInput,
|
player_auth_input::SPlayerAuthInput,
|
||||||
@ -40,6 +39,7 @@ use pumpkin_protocol::{
|
|||||||
request_chunk_radius::SRequestChunkRadius,
|
request_chunk_radius::SRequestChunkRadius,
|
||||||
request_network_settings::SRequestNetworkSettings,
|
request_network_settings::SRequestNetworkSettings,
|
||||||
resource_pack_response::SResourcePackResponse,
|
resource_pack_response::SResourcePackResponse,
|
||||||
|
set_local_player_as_initialized::SSetLocalPlayerAsInitialized,
|
||||||
text::SText,
|
text::SText,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@ -294,7 +294,7 @@ impl BedrockClient {
|
|||||||
let mut packet_buf = Vec::new();
|
let mut packet_buf = Vec::new();
|
||||||
match self.write_game_packet(packet, &mut packet_buf).await {
|
match self.write_game_packet(packet, &mut packet_buf).await {
|
||||||
Ok(()) => {
|
Ok(()) => {
|
||||||
self.send_framed_packet_data(packet_buf, RakReliability::Unreliable)
|
self.send_framed_packet_data(packet_buf, RakReliability::ReliableOrdered)
|
||||||
.await;
|
.await;
|
||||||
}
|
}
|
||||||
Err(err) => error!("Failed to write game packet: {err}"),
|
Err(err) => error!("Failed to write game packet: {err}"),
|
||||||
@ -688,11 +688,6 @@ impl BedrockClient {
|
|||||||
self.player_pos_update(player, SPlayerAuthInput::read(reader)?, server)
|
self.player_pos_update(player, SPlayerAuthInput::read(reader)?, server)
|
||||||
.await;
|
.await;
|
||||||
}
|
}
|
||||||
SLoadingScreen::PACKET_ID => {
|
|
||||||
if SLoadingScreen::read(reader)?.is_loading_done() {
|
|
||||||
player.set_client_loaded(true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
SRequestChunkRadius::PACKET_ID => {
|
SRequestChunkRadius::PACKET_ID => {
|
||||||
self.handle_request_chunk_radius(player, SRequestChunkRadius::read(reader)?)
|
self.handle_request_chunk_radius(player, SRequestChunkRadius::read(reader)?)
|
||||||
.await;
|
.await;
|
||||||
@ -713,6 +708,12 @@ impl BedrockClient {
|
|||||||
self.handle_chat_command(player, server, SCommandRequest::read(reader)?)
|
self.handle_chat_command(player, server, SCommandRequest::read(reader)?)
|
||||||
.await;
|
.await;
|
||||||
}
|
}
|
||||||
|
SSetLocalPlayerAsInitialized::PACKET_ID => {
|
||||||
|
self.handle_set_local_player_as_initialized(
|
||||||
|
player,
|
||||||
|
&SSetLocalPlayerAsInitialized::read(reader)?,
|
||||||
|
);
|
||||||
|
}
|
||||||
SPlayerAction::PACKET_ID => {
|
SPlayerAction::PACKET_ID => {
|
||||||
self.handle_player_action(player, server, SPlayerAction::read(reader)?)
|
self.handle_player_action(player, server, SPlayerAction::read(reader)?)
|
||||||
.await;
|
.await;
|
||||||
|
|||||||
@ -14,6 +14,7 @@ use pumpkin_protocol::{
|
|||||||
player_action::{Action as PlayerAction, SPlayerAction},
|
player_action::{Action as PlayerAction, SPlayerAction},
|
||||||
player_auth_input::{InputData, SPlayerAuthInput},
|
player_auth_input::{InputData, SPlayerAuthInput},
|
||||||
request_chunk_radius::SRequestChunkRadius,
|
request_chunk_radius::SRequestChunkRadius,
|
||||||
|
set_local_player_as_initialized::SSetLocalPlayerAsInitialized,
|
||||||
text::SText,
|
text::SText,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@ -73,13 +74,24 @@ impl BedrockClient {
|
|||||||
old_vd
|
old_vd
|
||||||
};
|
};
|
||||||
|
|
||||||
if old_view_distance.get() != view_distance as u8 {
|
|
||||||
debug!(
|
debug!(
|
||||||
"Player {} updated their render distance: {} -> {}.",
|
"Player {} updated their render distance: {} -> {}.",
|
||||||
player.gameprofile.name, old_view_distance, view_distance
|
player.gameprofile.name, old_view_distance, view_distance
|
||||||
);
|
);
|
||||||
chunker::update_position(player).await;
|
chunker::update_position(player).await;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn handle_set_local_player_as_initialized(
|
||||||
|
&self,
|
||||||
|
player: &Arc<Player>,
|
||||||
|
packet: &SSetLocalPlayerAsInitialized,
|
||||||
|
) {
|
||||||
|
debug!(
|
||||||
|
"Player {} initialized (Runtime ID: {})",
|
||||||
|
player.gameprofile.name, packet.runtime_entity_id.0
|
||||||
|
);
|
||||||
|
// This is sent when the client has finished loading and rendering the world.
|
||||||
|
player.set_client_loaded(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn player_pos_update(
|
pub async fn player_pos_update(
|
||||||
|
|||||||
@ -2278,9 +2278,9 @@ impl JavaClient {
|
|||||||
self.prepare_hand_item_for_use(player, hand, &item_in_hand)
|
self.prepare_hand_item_for_use(player, hand, &item_in_hand)
|
||||||
.await;
|
.await;
|
||||||
|
|
||||||
let item_for_use = {
|
let (item_for_use, stack_for_use) = {
|
||||||
let held = item_in_hand.lock().await;
|
let held = item_in_hand.lock().await;
|
||||||
held.item
|
(held.item, held.clone())
|
||||||
};
|
};
|
||||||
|
|
||||||
if !self
|
if !self
|
||||||
@ -2294,7 +2294,7 @@ impl JavaClient {
|
|||||||
server;
|
server;
|
||||||
event;
|
event;
|
||||||
'after: {
|
'after: {
|
||||||
server.item_registry.on_use(item_for_use, player).await;
|
server.item_registry.on_use(&stack_for_use, player).await;
|
||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
}
|
}
|
||||||
@ -2307,6 +2307,17 @@ impl JavaClient {
|
|||||||
) {
|
) {
|
||||||
let inventory = player.inventory();
|
let inventory = player.inventory();
|
||||||
let mut held = item_in_hand.lock().await;
|
let mut held = item_in_hand.lock().await;
|
||||||
|
|
||||||
|
if let Some(cooldown) = held.get_use_cooldown() {
|
||||||
|
let group = cooldown
|
||||||
|
.cooldown_group
|
||||||
|
.clone()
|
||||||
|
.unwrap_or_else(|| held.item.registry_key.to_string());
|
||||||
|
if player.is_on_cooldown(&group).await {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if held.get_data_component::<ConsumableImpl>().is_some() {
|
if held.get_data_component::<ConsumableImpl>().is_some() {
|
||||||
// If its food we want to make sure we can actually consume it
|
// If its food we want to make sure we can actually consume it
|
||||||
if let Some(food) = held.get_data_component::<FoodImpl>() {
|
if let Some(food) = held.get_data_component::<FoodImpl>() {
|
||||||
|
|||||||
@ -78,7 +78,6 @@ use pumpkin_protocol::{
|
|||||||
client::{
|
client::{
|
||||||
creative_content::{CreativeContent, Group},
|
creative_content::{CreativeContent, Group},
|
||||||
gamerules_changed::GameRules,
|
gamerules_changed::GameRules,
|
||||||
play_status::CPlayStatus,
|
|
||||||
start_game::{Experiments, GamePublishSetting, LevelSettings},
|
start_game::{Experiments, GamePublishSetting, LevelSettings},
|
||||||
update_artributes::{Attribute, CUpdateAttributes},
|
update_artributes::{Attribute, CUpdateAttributes},
|
||||||
},
|
},
|
||||||
@ -1806,9 +1805,6 @@ impl World {
|
|||||||
&mut frame_set,
|
&mut frame_set,
|
||||||
)
|
)
|
||||||
.await;
|
.await;
|
||||||
client
|
|
||||||
.write_game_packet_to_set(&CPlayStatus::PlayerSpawn, &mut frame_set)
|
|
||||||
.await;
|
|
||||||
client.send_frame_set(frame_set, 0x84).await;
|
client.send_frame_set(frame_set, 0x84).await;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user