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,
|
||||
DeathProtection, Enchantments, Equippable, FireworkExplosion, Fireworks, Food, ItemModel,
|
||||
ItemName, JukeboxPlayable, MaxDamage, MaxStackSize, PotionContents, StoredEnchantments, Tool,
|
||||
Unbreakable, Weapon,
|
||||
Unbreakable, UseCooldown, Weapon,
|
||||
};
|
||||
use crate::effect::{self, StatusEffect};
|
||||
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()),
|
||||
Equippable => Some(EquippableImpl::read_data(data)?.to_dyn()),
|
||||
StoredEnchantments => Some(StoredEnchantmentsImpl::read_data(data)?.to_dyn()),
|
||||
UseCooldown => Some(UseCooldownImpl::read_data(data)?.to_dyn()),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
@ -720,8 +721,61 @@ impl Hash for ConsumableImpl {
|
||||
}
|
||||
#[derive(Clone, Debug, Hash, PartialEq, Eq)]
|
||||
pub struct UseRemainderImpl;
|
||||
#[derive(Clone, Debug, Hash, PartialEq, Eq)]
|
||||
pub struct UseCooldownImpl;
|
||||
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
|
||||
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)]
|
||||
pub enum DamageResistantType {
|
||||
/// 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_impl::{
|
||||
BlocksAttacksImpl, ConsumableImpl, DamageImpl, DataComponentImpl, EnchantmentsImpl, IDSet,
|
||||
MaxDamageImpl, MaxStackSizeImpl, ToolImpl, UnbreakableImpl, get, get_mut, read_data,
|
||||
read_data_pnbt,
|
||||
MaxDamageImpl, MaxStackSizeImpl, ToolImpl, UnbreakableImpl, UseCooldownImpl, get, get_mut,
|
||||
read_data, read_data_pnbt,
|
||||
};
|
||||
use crate::item::Item;
|
||||
use crate::recipes::RecipeResultStruct;
|
||||
@ -131,6 +131,11 @@ impl ItemStack {
|
||||
.map(|value| value.max_damage)
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn get_use_cooldown(&self) -> Option<&UseCooldownImpl> {
|
||||
self.get_data_component::<UseCooldownImpl>()
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn get_damage(&self) -> i32 {
|
||||
self.get_data_component::<DamageImpl>()
|
||||
|
||||
@ -10,4 +10,5 @@ pub mod raknet;
|
||||
pub mod request_chunk_radius;
|
||||
pub mod request_network_settings;
|
||||
pub mod resource_pack_response;
|
||||
pub mod set_local_player_as_initialized;
|
||||
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,
|
||||
EnchantmentsImpl, EquipmentSlot, EquippableImpl, FireworkExplosionImpl, FireworkExplosionShape,
|
||||
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::entity::EntityType;
|
||||
@ -837,6 +838,7 @@ pub fn deserialize<'a, A: SeqAccess<'a>>(
|
||||
DataComponent::Consumable => Ok(ConsumableImpl::deserialize(seq)?.to_dyn()),
|
||||
DataComponent::Equippable => Ok(EquippableImpl::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)"))),
|
||||
}
|
||||
}
|
||||
@ -858,6 +860,29 @@ pub fn serialize<T: SerializeStruct>(
|
||||
DataComponent::Consumable => get::<ConsumableImpl>(value).serialize(seq),
|
||||
DataComponent::Equippable => get::<EquippableImpl>(value).serialize(seq),
|
||||
DataComponent::StoredEnchantments => get::<StoredEnchantmentsImpl>(value).serialize(seq),
|
||||
DataComponent::UseCooldown => get::<UseCooldownImpl>(value).serialize(seq),
|
||||
_ => 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 hurt_animation;
|
||||
mod initialize_world_border;
|
||||
mod item_cooldown;
|
||||
mod keep_alive;
|
||||
mod level_event;
|
||||
mod light_update;
|
||||
@ -133,6 +134,7 @@ pub use game_event::*;
|
||||
pub use head_rot::*;
|
||||
pub use hurt_animation::*;
|
||||
pub use initialize_world_border::*;
|
||||
pub use item_cooldown::*;
|
||||
pub use keep_alive::*;
|
||||
pub use level_event::*;
|
||||
pub use light_update::*;
|
||||
|
||||
@ -183,13 +183,13 @@ impl DynamicLightEngine {
|
||||
// No self-emission, darken it completely and continue propagation
|
||||
self.set_block_light_level(level, &neighbor_pos, 0)
|
||||
.await
|
||||
.unwrap();
|
||||
.ok();
|
||||
self.queue_block_light_decrease(neighbor_pos, neighbor_light);
|
||||
} else {
|
||||
// Has self-emission, set to its own light and re-propagate from it
|
||||
self.set_block_light_level(level, &neighbor_pos, neighbor_luminance)
|
||||
.await
|
||||
.unwrap();
|
||||
.ok();
|
||||
self.queue_block_light_increase(neighbor_pos, neighbor_luminance);
|
||||
}
|
||||
} else {
|
||||
@ -204,11 +204,11 @@ impl DynamicLightEngine {
|
||||
pub async fn check_block_light_updates(&self, level: &Arc<Level>, pos: BlockPos) {
|
||||
match level.lighting_config {
|
||||
LightingEngineConfig::Full => {
|
||||
self.set_block_light_level(level, &pos, 15).await.unwrap();
|
||||
self.set_block_light_level(level, &pos, 15).await.ok();
|
||||
return;
|
||||
}
|
||||
LightingEngineConfig::Dark => {
|
||||
self.set_block_light_level(level, &pos, 0).await.unwrap();
|
||||
self.set_block_light_level(level, &pos, 0).await.ok();
|
||||
return;
|
||||
}
|
||||
LightingEngineConfig::Default => {}
|
||||
@ -223,13 +223,13 @@ impl DynamicLightEngine {
|
||||
// Set to expected value immediately, then queue decrease to darken neighbors
|
||||
self.set_block_light_level(level, &pos, expected_light)
|
||||
.await
|
||||
.unwrap();
|
||||
.ok();
|
||||
self.queue_block_light_decrease(pos, current_light);
|
||||
} else if expected_light > current_light {
|
||||
// Handle light increase (placing light source)
|
||||
self.set_block_light_level(level, &pos, expected_light)
|
||||
.await
|
||||
.unwrap();
|
||||
.ok();
|
||||
self.queue_block_light_increase(pos, expected_light);
|
||||
}
|
||||
|
||||
@ -318,7 +318,7 @@ impl DynamicLightEngine {
|
||||
if new_light > neighbor_light {
|
||||
self.set_sky_light_level(level, &neighbor_pos, new_light)
|
||||
.await
|
||||
.unwrap();
|
||||
.ok();
|
||||
|
||||
if new_light > 0 {
|
||||
self.queue_sky_light_increase(neighbor_pos, new_light);
|
||||
@ -353,9 +353,7 @@ impl DynamicLightEngine {
|
||||
|
||||
if neighbor_light == expected || neighbor_light < removed_light {
|
||||
// This neighbor was lit by us, darken it
|
||||
self.set_sky_light_level(level, &neighbor_pos, 0)
|
||||
.await
|
||||
.unwrap();
|
||||
self.set_sky_light_level(level, &neighbor_pos, 0).await.ok();
|
||||
self.queue_sky_light_decrease(neighbor_pos, neighbor_light);
|
||||
} else if neighbor_light > removed_light {
|
||||
// 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) {
|
||||
match level.lighting_config {
|
||||
LightingEngineConfig::Full => {
|
||||
self.set_sky_light_level(level, &pos, 15).await.unwrap();
|
||||
self.set_sky_light_level(level, &pos, 15).await.ok();
|
||||
return;
|
||||
}
|
||||
LightingEngineConfig::Dark => {
|
||||
@ -423,13 +421,13 @@ impl DynamicLightEngine {
|
||||
// Light decreased
|
||||
self.set_sky_light_level(level, &pos, expected_light)
|
||||
.await
|
||||
.unwrap();
|
||||
.ok();
|
||||
self.queue_sky_light_decrease(pos, current_light);
|
||||
} else if expected_light > current_light {
|
||||
// Light increased
|
||||
self.set_sky_light_level(level, &pos, expected_light)
|
||||
.await
|
||||
.unwrap();
|
||||
.ok();
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
@ -69,7 +69,7 @@ use pumpkin_nbt::pnbt::PNbtCompound;
|
||||
impl NBTStorage for BatEntity {
|
||||
fn write_nbt<'a>(&'a self, nbt: &'a mut PNbtCompound) -> NbtFuture<'a, ()> {
|
||||
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 };
|
||||
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, ()> {
|
||||
Box::pin(async move {
|
||||
self.mob_entity
|
||||
.living_entity
|
||||
.entity
|
||||
.read_nbt_non_mut(nbt)
|
||||
.await;
|
||||
self.mob_entity.living_entity.read_nbt_non_mut(nbt).await;
|
||||
let flags = nbt.get_byte().unwrap_or(0) as u8;
|
||||
let roosting = (flags & ROOSTING_FLAG) != 0;
|
||||
self.set_roosting(roosting).await;
|
||||
|
||||
@ -121,7 +121,7 @@ use pumpkin_nbt::pnbt::PNbtCompound;
|
||||
impl NBTStorage for CreeperEntity {
|
||||
fn write_nbt<'a>(&'a self, nbt: &'a mut PNbtCompound) -> NbtFuture<'a, ()> {
|
||||
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_short(self.fuse_time.load(Ordering::Relaxed) as i16);
|
||||
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, ()> {
|
||||
Box::pin(async {
|
||||
self.mob_entity
|
||||
.living_entity
|
||||
.entity
|
||||
.read_nbt_non_mut(nbt)
|
||||
.await;
|
||||
self.mob_entity.living_entity.read_nbt_non_mut(nbt).await;
|
||||
self.charged
|
||||
.store(nbt.get_bool().unwrap_or(false), Ordering::Relaxed);
|
||||
self.fuse_time
|
||||
|
||||
@ -322,7 +322,7 @@ impl ShulkerEntity {
|
||||
impl NBTStorage for ShulkerEntity {
|
||||
fn write_nbt<'a>(&'a self, nbt: &'a mut PNbtCompound) -> NbtFuture<'a, ()> {
|
||||
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.peek_amount.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, ()> {
|
||||
Box::pin(async move {
|
||||
self.mob_entity
|
||||
.living_entity
|
||||
.entity
|
||||
.read_nbt_non_mut(nbt)
|
||||
.await;
|
||||
self.mob_entity.living_entity.read_nbt_non_mut(nbt).await;
|
||||
if let Ok(face) = nbt.get_byte() {
|
||||
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 pumpkin_data::entity::EntityType;
|
||||
@ -64,19 +63,13 @@ use pumpkin_nbt::pnbt::PNbtCompound;
|
||||
impl NBTStorage for SlimeEntity {
|
||||
fn write_nbt<'a>(&'a self, nbt: &'a mut PNbtCompound) -> NbtFuture<'a, ()> {
|
||||
Box::pin(async move {
|
||||
self.entity.living_entity.entity.write_nbt(nbt).await;
|
||||
nbt.put_int(self.entity.living_entity.entity.data.load(Relaxed));
|
||||
self.entity.living_entity.write_nbt(nbt).await;
|
||||
})
|
||||
}
|
||||
|
||||
fn read_nbt_non_mut<'a>(&'a self, nbt: &'a mut PNbtCompound) -> NbtFuture<'a, ()> {
|
||||
Box::pin(async move {
|
||||
self.entity.living_entity.entity.read_nbt_non_mut(nbt).await;
|
||||
self.entity
|
||||
.living_entity
|
||||
.entity
|
||||
.data
|
||||
.store(nbt.get_int().unwrap_or(0), Relaxed);
|
||||
self.entity.living_entity.read_nbt_non_mut(nbt).await;
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@ -2694,7 +2694,7 @@ impl NBTStorage for Entity {
|
||||
fn write_nbt<'a>(&'a self, nbt: &'a mut PNbtCompound) -> NbtFuture<'a, ()> {
|
||||
Box::pin(async move {
|
||||
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);
|
||||
|
||||
// Pos
|
||||
|
||||
@ -110,7 +110,7 @@ use pumpkin_nbt::pnbt::PNbtCompound;
|
||||
impl NBTStorage for SheepEntity {
|
||||
fn write_nbt<'a>(&'a self, nbt: &'a mut PNbtCompound) -> NbtFuture<'a, ()> {
|
||||
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_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, ()> {
|
||||
Box::pin(async {
|
||||
self.mob_entity
|
||||
.living_entity
|
||||
.entity
|
||||
.read_nbt_non_mut(nbt)
|
||||
.await;
|
||||
self.mob_entity.living_entity.read_nbt_non_mut(nbt).await;
|
||||
let sheared = nbt.get_bool().unwrap_or(false);
|
||||
let color = nbt.get_byte().unwrap_or(0) as u8;
|
||||
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_inventory::player::ender_chest_inventory::EnderChestInventory;
|
||||
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::update_abilities::{
|
||||
Ability, AbilityLayer, CUpdateAbilities,
|
||||
};
|
||||
use pumpkin_protocol::bedrock::frame_set::FrameSet;
|
||||
use pumpkin_protocol::bedrock::server::text::SText;
|
||||
use pumpkin_protocol::codec::item_stack_seralizer::ItemStackSerializer;
|
||||
use pumpkin_world::chunk::{ChunkData, ChunkEntityData};
|
||||
@ -56,8 +58,8 @@ use pumpkin_protocol::codec::var_int::VarInt;
|
||||
use pumpkin_protocol::java::client::play::{
|
||||
Animation, CAcknowledgeBlockChange, CActionBar, CChangeDifficulty, CChunkBatchEnd,
|
||||
CChunkBatchStart, CChunkData, CCloseContainer, CCombatDeath, CCustomPayload,
|
||||
CDisguisedChatMessage, CEntityAnimation, CEntityPositionSync, CGameEvent, CKeepAlive,
|
||||
COpenScreen, CParticle, CPlayerAbilities, CPlayerInfoUpdate, CPlayerPosition,
|
||||
CDisguisedChatMessage, CEntityAnimation, CEntityPositionSync, CGameEvent, CItemCooldown,
|
||||
CKeepAlive, COpenScreen, CParticle, CPlayerAbilities, CPlayerInfoUpdate, CPlayerPosition,
|
||||
CPlayerSpawnPosition, CRespawn, CSetContainerContent, CSetContainerProperty, CSetContainerSlot,
|
||||
CSetCursorItem, CSetEquipment, CSetExperience, CSetHealth, CSetPlayerInventory,
|
||||
CSetSelectedSlot, CSoundEffect, CStopSound, CSubtitle, CSystemChatMessage, CTabList,
|
||||
@ -365,6 +367,12 @@ impl ChunkManager {
|
||||
/// Represents a Minecraft player entity.
|
||||
///
|
||||
/// 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 {
|
||||
/// The underlying living entity object that represents the player.
|
||||
pub living_entity: LivingEntity,
|
||||
@ -437,6 +445,7 @@ pub struct Player {
|
||||
pub permission_lvl: AtomicCell<PermissionLvl>,
|
||||
/// Whether the client has reported that it has loaded.
|
||||
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.
|
||||
pub client_loaded_timeout: AtomicU32,
|
||||
/// Item usage tracking for bows, crossbows, etc.
|
||||
@ -449,6 +458,7 @@ pub struct Player {
|
||||
pub experience_progress: AtomicCell<f32>,
|
||||
/// The player's total experience points.
|
||||
pub experience_points: AtomicI32,
|
||||
pub item_cooldowns: Mutex<HashMap<String, ItemCooldown>>,
|
||||
pub experience_pick_up_delay: Mutex<u32>,
|
||||
pub chunk_manager: Mutex<ChunkManager>,
|
||||
pub has_played_before: AtomicBool,
|
||||
@ -551,6 +561,7 @@ impl Player {
|
||||
ping: AtomicU32::new(0),
|
||||
last_attacked_ticks: AtomicU32::new(0),
|
||||
client_loaded: AtomicBool::new(false),
|
||||
bedrock_spawned: AtomicBool::new(false),
|
||||
client_loaded_timeout: AtomicU32::new(60),
|
||||
// Item usage tracking
|
||||
using_item: AtomicBool::new(false),
|
||||
@ -573,6 +584,7 @@ impl Player {
|
||||
experience_level: AtomicI32::new(0),
|
||||
experience_progress: AtomicCell::new(0.0),
|
||||
experience_points: AtomicI32::new(0),
|
||||
item_cooldowns: Mutex::new(HashMap::new()),
|
||||
// Default to sending 16 chunks per tick.
|
||||
chunk_manager: Mutex::new(ChunkManager::new(
|
||||
16,
|
||||
@ -608,6 +620,44 @@ impl Player {
|
||||
.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>) {
|
||||
*self.display_name.lock().await = display_name.clone();
|
||||
// Update the tab list for everyone
|
||||
@ -1612,6 +1662,16 @@ impl Player {
|
||||
})
|
||||
.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);
|
||||
if let Some(pumpkin_item) = pumpkin_item {
|
||||
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) {
|
||||
@ -59,12 +75,28 @@ impl ItemRegistry {
|
||||
block: &Block,
|
||||
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);
|
||||
if let Some(pumpkin_item) = pumpkin_item {
|
||||
pumpkin_item
|
||||
.use_on_block(stack, player, location, face, cursor_pos, block, server)
|
||||
.await;
|
||||
}
|
||||
|
||||
if let Some(cooldown) = cooldown {
|
||||
player
|
||||
.start_cooldown(cooldown_group, (cooldown.seconds * 20.0) as i32)
|
||||
.await;
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn use_on_entity(
|
||||
@ -73,10 +105,26 @@ impl ItemRegistry {
|
||||
player: &Player,
|
||||
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);
|
||||
if let Some(pumpkin_item) = pumpkin_item {
|
||||
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 {
|
||||
|
||||
@ -26,7 +26,6 @@ use pumpkin_protocol::{
|
||||
command_request::SCommandRequest,
|
||||
container_close::SContainerClose,
|
||||
interaction::SInteraction,
|
||||
loading_screen::SLoadingScreen,
|
||||
login::SLogin,
|
||||
player_action::SPlayerAction,
|
||||
player_auth_input::SPlayerAuthInput,
|
||||
@ -40,6 +39,7 @@ use pumpkin_protocol::{
|
||||
request_chunk_radius::SRequestChunkRadius,
|
||||
request_network_settings::SRequestNetworkSettings,
|
||||
resource_pack_response::SResourcePackResponse,
|
||||
set_local_player_as_initialized::SSetLocalPlayerAsInitialized,
|
||||
text::SText,
|
||||
},
|
||||
},
|
||||
@ -294,7 +294,7 @@ impl BedrockClient {
|
||||
let mut packet_buf = Vec::new();
|
||||
match self.write_game_packet(packet, &mut packet_buf).await {
|
||||
Ok(()) => {
|
||||
self.send_framed_packet_data(packet_buf, RakReliability::Unreliable)
|
||||
self.send_framed_packet_data(packet_buf, RakReliability::ReliableOrdered)
|
||||
.await;
|
||||
}
|
||||
Err(err) => error!("Failed to write game packet: {err}"),
|
||||
@ -688,11 +688,6 @@ impl BedrockClient {
|
||||
self.player_pos_update(player, SPlayerAuthInput::read(reader)?, server)
|
||||
.await;
|
||||
}
|
||||
SLoadingScreen::PACKET_ID => {
|
||||
if SLoadingScreen::read(reader)?.is_loading_done() {
|
||||
player.set_client_loaded(true);
|
||||
}
|
||||
}
|
||||
SRequestChunkRadius::PACKET_ID => {
|
||||
self.handle_request_chunk_radius(player, SRequestChunkRadius::read(reader)?)
|
||||
.await;
|
||||
@ -713,6 +708,12 @@ impl BedrockClient {
|
||||
self.handle_chat_command(player, server, SCommandRequest::read(reader)?)
|
||||
.await;
|
||||
}
|
||||
SSetLocalPlayerAsInitialized::PACKET_ID => {
|
||||
self.handle_set_local_player_as_initialized(
|
||||
player,
|
||||
&SSetLocalPlayerAsInitialized::read(reader)?,
|
||||
);
|
||||
}
|
||||
SPlayerAction::PACKET_ID => {
|
||||
self.handle_player_action(player, server, SPlayerAction::read(reader)?)
|
||||
.await;
|
||||
|
||||
@ -14,6 +14,7 @@ use pumpkin_protocol::{
|
||||
player_action::{Action as PlayerAction, SPlayerAction},
|
||||
player_auth_input::{InputData, SPlayerAuthInput},
|
||||
request_chunk_radius::SRequestChunkRadius,
|
||||
set_local_player_as_initialized::SSetLocalPlayerAsInitialized,
|
||||
text::SText,
|
||||
},
|
||||
},
|
||||
@ -73,13 +74,24 @@ impl BedrockClient {
|
||||
old_vd
|
||||
};
|
||||
|
||||
if old_view_distance.get() != view_distance as u8 {
|
||||
debug!(
|
||||
"Player {} updated their render distance: {} -> {}.",
|
||||
player.gameprofile.name, old_view_distance, view_distance
|
||||
);
|
||||
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(
|
||||
|
||||
@ -2278,9 +2278,9 @@ impl JavaClient {
|
||||
self.prepare_hand_item_for_use(player, hand, &item_in_hand)
|
||||
.await;
|
||||
|
||||
let item_for_use = {
|
||||
let (item_for_use, stack_for_use) = {
|
||||
let held = item_in_hand.lock().await;
|
||||
held.item
|
||||
(held.item, held.clone())
|
||||
};
|
||||
|
||||
if !self
|
||||
@ -2294,7 +2294,7 @@ impl JavaClient {
|
||||
server;
|
||||
event;
|
||||
'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 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 its food we want to make sure we can actually consume it
|
||||
if let Some(food) = held.get_data_component::<FoodImpl>() {
|
||||
|
||||
@ -78,7 +78,6 @@ use pumpkin_protocol::{
|
||||
client::{
|
||||
creative_content::{CreativeContent, Group},
|
||||
gamerules_changed::GameRules,
|
||||
play_status::CPlayStatus,
|
||||
start_game::{Experiments, GamePublishSetting, LevelSettings},
|
||||
update_artributes::{Attribute, CUpdateAttributes},
|
||||
},
|
||||
@ -1806,9 +1805,6 @@ impl World {
|
||||
&mut frame_set,
|
||||
)
|
||||
.await;
|
||||
client
|
||||
.write_game_packet_to_set(&CPlayStatus::PlayerSpawn, &mut frame_set)
|
||||
.await;
|
||||
client.send_frame_set(frame_set, 0x84).await;
|
||||
}
|
||||
|
||||
|
||||
Loading…
Reference in New Issue
Block a user