Check if player can destroy block using an tool
Prevents players to destroy blocks when holding a sword or trident in creative
This commit is contained in:
parent
cccdf53e7c
commit
e055dd740c
3285
assets/blocks.json
3285
assets/blocks.json
File diff suppressed because it is too large
Load Diff
@ -315,14 +315,15 @@ pub(crate) fn build() -> TokenStream {
|
||||
serde_json::from_str(self.components.item_name.unwrap()).expect("Could not parse item name.")
|
||||
}
|
||||
|
||||
#[doc = r" Try to parse a Item from a resource location string"]
|
||||
#[doc = "Try to parse a Item from a resource location string"]
|
||||
pub fn from_registry_key(name: &str) -> Option<Self> {
|
||||
match name {
|
||||
#type_from_name
|
||||
_ => None
|
||||
}
|
||||
}
|
||||
#[doc = r" Try to parse a Item from a raw id"]
|
||||
|
||||
#[doc = "Try to parse a Item from a raw id"]
|
||||
pub const fn from_id(id: u16) -> Option<Self> {
|
||||
match id {
|
||||
#type_from_raw_id_arms
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
use heck::{ToPascalCase, ToSnakeCase};
|
||||
use proc_macro::TokenStream;
|
||||
use pumpkin_data::item::Item;
|
||||
use quote::quote;
|
||||
use syn::{self};
|
||||
use syn::{
|
||||
Block, Expr, Field, Fields, ItemStruct, Stmt,
|
||||
parse::{Nothing, Parser},
|
||||
@ -198,29 +198,6 @@ pub fn pumpkin_block(input: TokenStream, item: TokenStream) -> TokenStream {
|
||||
code.into()
|
||||
}
|
||||
|
||||
#[proc_macro_attribute]
|
||||
pub fn pumpkin_item(input: TokenStream, item: TokenStream) -> TokenStream {
|
||||
let ast: syn::DeriveInput = syn::parse(item.clone()).unwrap();
|
||||
let name = &ast.ident;
|
||||
let (impl_generics, ty_generics, _) = ast.generics.split_for_impl();
|
||||
|
||||
let input_string = input.to_string();
|
||||
let packet_name = input_string.trim_matches('"');
|
||||
let item_id = Item::from_registry_key(packet_name).unwrap();
|
||||
let id = item_id.id;
|
||||
|
||||
let item: proc_macro2::TokenStream = item.into();
|
||||
|
||||
let code = quote! {
|
||||
#item
|
||||
impl #impl_generics crate::item::pumpkin_item::ItemMetadata for #name #ty_generics {
|
||||
const ID: u16 = #id;
|
||||
}
|
||||
};
|
||||
|
||||
code.into()
|
||||
}
|
||||
|
||||
#[proc_macro_attribute]
|
||||
pub fn block_property(input: TokenStream, item: TokenStream) -> TokenStream {
|
||||
let ast: syn::DeriveInput = syn::parse(item.clone()).unwrap();
|
||||
|
||||
@ -19,6 +19,13 @@ impl BoundingBox {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn expand(&self, x: f64, y: f64, z: f64) -> Self {
|
||||
Self {
|
||||
min: Vector3::new(self.min.x - x, self.min.y - y, self.min.z - z),
|
||||
max: Vector3::new(self.max.x + x, self.max.y + y, self.max.z + z),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn offset(&self, other: Self) -> Self {
|
||||
Self {
|
||||
min: self.min.add(&other.min),
|
||||
|
||||
@ -122,6 +122,9 @@ pub struct Block {
|
||||
pub experience: Option<Experience>,
|
||||
pub blast_resistance: f32,
|
||||
pub wall_variant_id: Option<u16>,
|
||||
pub slipperiness: f32,
|
||||
pub velocity_multiplier: f32,
|
||||
pub jump_velocity_multiplier: f32,
|
||||
pub translation_key: String,
|
||||
pub name: String,
|
||||
pub loot_table: Option<LootTable>,
|
||||
|
||||
@ -12,14 +12,7 @@ impl AlternativeEntry {
|
||||
let mut items = vec![];
|
||||
for child in &self.children {
|
||||
if let Some(conditions) = &child.conditions {
|
||||
let mut conditions_met = true;
|
||||
for condition in conditions {
|
||||
if !condition.test() {
|
||||
conditions_met = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if !conditions_met {
|
||||
if !conditions.iter().all(|condition| condition.test()) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
@ -41,14 +41,7 @@ impl LootPool {
|
||||
for _ in 0..i {
|
||||
for entry in &self.entries {
|
||||
if let Some(conditions) = &entry.conditions {
|
||||
let mut conditions_met = true;
|
||||
for condition in conditions {
|
||||
if !condition.test() {
|
||||
conditions_met = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if !conditions_met {
|
||||
if !conditions.iter().all(|condition| condition.test()) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
@ -80,11 +80,8 @@ async fn drop_stack(world: &Arc<World>, pos: &BlockPos, stack: ItemStack) {
|
||||
);
|
||||
|
||||
let entity = world.create_entity(pos, EntityType::ITEM);
|
||||
let item_entity = Arc::new(ItemEntity::new(
|
||||
entity,
|
||||
stack.item.id,
|
||||
u32::from(stack.item_count),
|
||||
));
|
||||
let item_entity =
|
||||
Arc::new(ItemEntity::new(entity, stack.item.id, u32::from(stack.item_count)).await);
|
||||
world.spawn_entity(item_entity.clone()).await;
|
||||
item_entity.send_meta_packet().await;
|
||||
}
|
||||
|
||||
@ -64,7 +64,9 @@ impl ExperienceOrbEntity {
|
||||
|
||||
#[async_trait]
|
||||
impl EntityBase for ExperienceOrbEntity {
|
||||
async fn tick(&self, _: &Server) {
|
||||
async fn tick(&self, server: &Server) {
|
||||
self.entity.tick(server).await;
|
||||
|
||||
let age = self
|
||||
.orb_age
|
||||
.fetch_add(1, std::sync::atomic::Ordering::Relaxed);
|
||||
|
||||
@ -6,6 +6,7 @@ use pumpkin_protocol::{
|
||||
client::play::{CTakeItemEntity, MetaDataType, Metadata},
|
||||
codec::slot::Slot,
|
||||
};
|
||||
use pumpkin_util::math::vector3::Vector3;
|
||||
use pumpkin_world::item::ItemStack;
|
||||
use tokio::sync::Mutex;
|
||||
|
||||
@ -24,7 +25,14 @@ pub struct ItemEntity {
|
||||
}
|
||||
|
||||
impl ItemEntity {
|
||||
pub fn new(entity: Entity, item_id: u16, count: u32) -> Self {
|
||||
pub async fn new(entity: Entity, item_id: u16, count: u32) -> Self {
|
||||
entity
|
||||
.set_velocity(Vector3::new(
|
||||
rand::random::<f64>() * 0.2 - 0.1,
|
||||
0.2,
|
||||
rand::random::<f64>() * 0.2 - 0.1,
|
||||
))
|
||||
.await;
|
||||
entity.yaw.store(rand::random::<f32>() * 360.0);
|
||||
Self {
|
||||
entity,
|
||||
@ -44,7 +52,8 @@ impl ItemEntity {
|
||||
|
||||
#[async_trait]
|
||||
impl EntityBase for ItemEntity {
|
||||
async fn tick(&self, _server: &Server) {
|
||||
async fn tick(&self, server: &Server) {
|
||||
self.entity.tick(server).await;
|
||||
{
|
||||
let mut delay = self.pickup_delay.lock().await;
|
||||
*delay = delay.saturating_sub(1);
|
||||
|
||||
@ -242,7 +242,9 @@ impl LivingEntity {
|
||||
|
||||
#[async_trait]
|
||||
impl EntityBase for LivingEntity {
|
||||
async fn tick(&self, _server: &Server) {
|
||||
async fn tick(&self, server: &Server) {
|
||||
self.entity.tick(server).await;
|
||||
|
||||
if self
|
||||
.time_until_regen
|
||||
.load(std::sync::atomic::Ordering::Relaxed)
|
||||
|
||||
@ -427,6 +427,33 @@ impl Entity {
|
||||
self.invulnerable.load(std::sync::atomic::Ordering::Relaxed)
|
||||
|| self.damage_immunities.contains(damage_type)
|
||||
}
|
||||
|
||||
async fn velocity_multiplier(&self, _pos: Vector3<f64>) -> f32 {
|
||||
let world = self.world.read().await;
|
||||
let block = world.get_block(&self.block_pos.load()).await.unwrap();
|
||||
block.velocity_multiplier
|
||||
// if velo_multiplier == 1.0 {
|
||||
// const VELOCITY_OFFSET: f64 = 0.500001; // Vanilla
|
||||
// let pos_with_y_offset = BlockPos(Vector3::new(
|
||||
// pos.x.floor() as i32,
|
||||
// (pos.y - VELOCITY_OFFSET).floor() as i32,
|
||||
// pos.z.floor() as i32,
|
||||
// ));
|
||||
// let block = world.get_block(&pos_with_y_offset).await.unwrap();
|
||||
// block.velocity_multiplier
|
||||
// } else {
|
||||
// }
|
||||
}
|
||||
|
||||
async fn tick_move(&self) {
|
||||
let velo = self.velocity.load();
|
||||
let pos = self.pos.load();
|
||||
self.pos
|
||||
.store(Vector3::new(pos.x + velo.x, pos.y + velo.y, pos.z + velo.z));
|
||||
let multiplier = f64::from(self.velocity_multiplier(pos).await);
|
||||
self.velocity
|
||||
.store(velo.multiply(multiplier, 1.0, multiplier));
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
@ -435,7 +462,9 @@ impl EntityBase for Entity {
|
||||
false
|
||||
}
|
||||
|
||||
async fn tick(&self, _: &Server) {}
|
||||
async fn tick(&self, _: &Server) {
|
||||
self.tick_move().await;
|
||||
}
|
||||
|
||||
fn get_entity(&self) -> &Entity {
|
||||
self
|
||||
|
||||
@ -1009,7 +1009,7 @@ impl Player {
|
||||
.create_entity(self.living_entity.entity.pos.load(), EntityType::ITEM);
|
||||
|
||||
// TODO: Merge stacks together
|
||||
let item_entity = Arc::new(ItemEntity::new(entity, item_id, count));
|
||||
let item_entity = Arc::new(ItemEntity::new(entity, item_id, count).await);
|
||||
self.world().await.spawn_entity(item_entity.clone()).await;
|
||||
item_entity.send_meta_packet().await;
|
||||
}
|
||||
|
||||
@ -2,16 +2,18 @@ use std::sync::Arc;
|
||||
|
||||
use crate::entity::player::Player;
|
||||
use crate::entity::projectile::ThrownItemEntity;
|
||||
use crate::item::pumpkin_item::PumpkinItem;
|
||||
use crate::item::pumpkin_item::{ItemMetadata, PumpkinItem};
|
||||
use async_trait::async_trait;
|
||||
use pumpkin_data::entity::EntityType;
|
||||
use pumpkin_data::item::Item;
|
||||
use pumpkin_data::sound::Sound;
|
||||
use pumpkin_macros::pumpkin_item;
|
||||
|
||||
#[pumpkin_item("egg")]
|
||||
pub struct EggItem;
|
||||
|
||||
impl ItemMetadata for EggItem {
|
||||
const IDS: &'static [u16] = &[Item::EGG.id];
|
||||
}
|
||||
|
||||
const POWER: f32 = 1.5;
|
||||
|
||||
#[async_trait]
|
||||
|
||||
@ -1,2 +1,24 @@
|
||||
pub mod egg;
|
||||
pub mod snowball;
|
||||
mod egg;
|
||||
mod snowball;
|
||||
mod sword;
|
||||
mod trident;
|
||||
|
||||
use std::sync::Arc;
|
||||
|
||||
use egg::EggItem;
|
||||
use snowball::SnowBallItem;
|
||||
use sword::SwordItem;
|
||||
use trident::TridentItem;
|
||||
|
||||
use super::registry::ItemRegistry;
|
||||
#[must_use]
|
||||
pub fn default_registry() -> Arc<ItemRegistry> {
|
||||
let mut manager = ItemRegistry::default();
|
||||
|
||||
manager.register(SnowBallItem);
|
||||
manager.register(EggItem);
|
||||
manager.register(SwordItem);
|
||||
manager.register(TridentItem);
|
||||
|
||||
Arc::new(manager)
|
||||
}
|
||||
|
||||
@ -2,16 +2,18 @@ use std::sync::Arc;
|
||||
|
||||
use crate::entity::player::Player;
|
||||
use crate::entity::projectile::ThrownItemEntity;
|
||||
use crate::item::pumpkin_item::PumpkinItem;
|
||||
use crate::item::pumpkin_item::{ItemMetadata, PumpkinItem};
|
||||
use async_trait::async_trait;
|
||||
use pumpkin_data::entity::EntityType;
|
||||
use pumpkin_data::item::Item;
|
||||
use pumpkin_data::sound::Sound;
|
||||
use pumpkin_macros::pumpkin_item;
|
||||
|
||||
#[pumpkin_item("snowball")]
|
||||
pub struct SnowBallItem;
|
||||
|
||||
impl ItemMetadata for SnowBallItem {
|
||||
const IDS: &'static [u16] = &[Item::SNOWBALL.id];
|
||||
}
|
||||
|
||||
const POWER: f32 = 1.5;
|
||||
|
||||
#[async_trait]
|
||||
|
||||
24
pumpkin/src/item/items/sword.rs
Normal file
24
pumpkin/src/item/items/sword.rs
Normal file
@ -0,0 +1,24 @@
|
||||
use crate::entity::player::Player;
|
||||
use crate::item::pumpkin_item::{ItemMetadata, PumpkinItem};
|
||||
use async_trait::async_trait;
|
||||
use pumpkin_data::item::Item;
|
||||
use pumpkin_util::GameMode;
|
||||
|
||||
pub struct SwordItem;
|
||||
|
||||
impl ItemMetadata for SwordItem {
|
||||
const IDS: &'static [u16] = &[
|
||||
Item::WOODEN_SWORD.id,
|
||||
Item::STONE_SWORD.id,
|
||||
Item::GOLDEN_SWORD.id,
|
||||
Item::DIAMOND_SWORD.id,
|
||||
Item::NETHERITE_SWORD.id,
|
||||
];
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl PumpkinItem for SwordItem {
|
||||
fn can_mine(&self, player: &Player) -> bool {
|
||||
player.gamemode.load() != GameMode::Creative
|
||||
}
|
||||
}
|
||||
21
pumpkin/src/item/items/trident.rs
Normal file
21
pumpkin/src/item/items/trident.rs
Normal file
@ -0,0 +1,21 @@
|
||||
use async_trait::async_trait;
|
||||
use pumpkin_data::item::Item;
|
||||
use pumpkin_util::GameMode;
|
||||
|
||||
use crate::{
|
||||
entity::player::Player,
|
||||
item::pumpkin_item::{ItemMetadata, PumpkinItem},
|
||||
};
|
||||
|
||||
pub struct TridentItem;
|
||||
|
||||
impl ItemMetadata for TridentItem {
|
||||
const IDS: &'static [u16] = &[Item::TRIDENT.id];
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl PumpkinItem for TridentItem {
|
||||
fn can_mine(&self, player: &Player) -> bool {
|
||||
player.gamemode.load() != GameMode::Creative
|
||||
}
|
||||
}
|
||||
@ -1,18 +1,3 @@
|
||||
use items::{egg::EggItem, snowball::SnowBallItem};
|
||||
use registry::ItemRegistry;
|
||||
|
||||
use std::sync::Arc;
|
||||
|
||||
mod items;
|
||||
pub mod items;
|
||||
pub mod pumpkin_item;
|
||||
pub mod registry;
|
||||
|
||||
#[must_use]
|
||||
pub fn default_registry() -> Arc<ItemRegistry> {
|
||||
let mut manager = ItemRegistry::default();
|
||||
|
||||
manager.register(SnowBallItem);
|
||||
manager.register(EggItem);
|
||||
|
||||
Arc::new(manager)
|
||||
}
|
||||
|
||||
@ -6,7 +6,7 @@ use pumpkin_util::math::position::BlockPos;
|
||||
use pumpkin_world::block::registry::Block;
|
||||
|
||||
pub trait ItemMetadata {
|
||||
const ID: u16;
|
||||
const IDS: &'static [u16];
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
@ -21,4 +21,8 @@ pub trait PumpkinItem: Send + Sync {
|
||||
_server: &Server,
|
||||
) {
|
||||
}
|
||||
|
||||
fn can_mine(&self, _player: &Player) -> bool {
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
@ -10,12 +10,12 @@ use super::pumpkin_item::{ItemMetadata, PumpkinItem};
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct ItemRegistry {
|
||||
items: HashMap<u16, Arc<dyn PumpkinItem>>,
|
||||
items: HashMap<&'static [u16], Arc<dyn PumpkinItem>>,
|
||||
}
|
||||
|
||||
impl ItemRegistry {
|
||||
pub fn register<T: PumpkinItem + ItemMetadata + 'static>(&mut self, item: T) {
|
||||
self.items.insert(T::ID, Arc::new(item));
|
||||
self.items.insert(T::IDS, Arc::new(item));
|
||||
}
|
||||
|
||||
pub async fn on_use(&self, item: &Item, player: &Player) {
|
||||
@ -41,8 +41,18 @@ impl ItemRegistry {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn can_mine(&self, item: &Item, player: &Player) -> bool {
|
||||
let pumpkin_block = self.get_pumpkin_item(item.id);
|
||||
if let Some(pumpkin_block) = pumpkin_block {
|
||||
return pumpkin_block.can_mine(player);
|
||||
}
|
||||
true
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn get_pumpkin_item(&self, item_id: u16) -> Option<&Arc<dyn PumpkinItem>> {
|
||||
self.items.get(&item_id)
|
||||
self.items
|
||||
.iter()
|
||||
.find_map(|(ids, item)| ids.contains(&item_id).then_some(item))
|
||||
}
|
||||
}
|
||||
|
||||
@ -25,7 +25,7 @@ use pumpkin_inventory::player::{
|
||||
};
|
||||
use pumpkin_macros::block_entity;
|
||||
use pumpkin_protocol::client::play::{
|
||||
CBlockEntityData, COpenSignEditor, CSetContainerSlot, CSetHeldItem, EquipmentSlot,
|
||||
CBlockEntityData, CBlockUpdate, COpenSignEditor, CSetContainerSlot, CSetHeldItem, EquipmentSlot,
|
||||
};
|
||||
use pumpkin_protocol::codec::slot::Slot;
|
||||
use pumpkin_protocol::codec::var_int::VarInt;
|
||||
@ -835,9 +835,22 @@ impl Player {
|
||||
let location = player_action.location;
|
||||
let entity = &self.living_entity.entity;
|
||||
let world = &entity.world.read().await;
|
||||
let block = world.get_block(&location).await;
|
||||
let block = world.get_block(&location).await.unwrap();
|
||||
let state = world.get_block_state(&location).await.unwrap();
|
||||
|
||||
if let Some(held) = self.inventory.lock().await.held_item() {
|
||||
if !server.item_registry.can_mine(&held.item, &self) {
|
||||
self.client
|
||||
.send_packet(&CBlockUpdate::new(
|
||||
&location,
|
||||
VarInt(i32::from(state.id)),
|
||||
))
|
||||
.await;
|
||||
self.update_sequence(player_action.sequence.0);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: do validation
|
||||
// TODO: Config
|
||||
if self.gamemode.load() == GameMode::Creative {
|
||||
@ -846,37 +859,33 @@ impl Player {
|
||||
world
|
||||
.break_block(&location, Some(self.clone()), false)
|
||||
.await;
|
||||
if let Ok(block) = block {
|
||||
server
|
||||
.block_registry
|
||||
.broken(block, &self, location, server)
|
||||
.await;
|
||||
}
|
||||
server
|
||||
.block_registry
|
||||
.broken(block, &self, location, server)
|
||||
.await;
|
||||
return;
|
||||
}
|
||||
self.start_mining_time.store(
|
||||
self.tick_counter.load(std::sync::atomic::Ordering::Relaxed),
|
||||
std::sync::atomic::Ordering::Relaxed,
|
||||
);
|
||||
if let Ok(block) = block {
|
||||
if !state.air {
|
||||
let speed = block::calc_block_breaking(&self, state, &block.name).await;
|
||||
// Instant break
|
||||
if speed >= 1.0 {
|
||||
world.break_block(&location, Some(self.clone()), true).await;
|
||||
server
|
||||
.block_registry
|
||||
.broken(block, &self, location, server)
|
||||
.await;
|
||||
} else {
|
||||
self.mining
|
||||
.store(true, std::sync::atomic::Ordering::Relaxed);
|
||||
*self.mining_pos.lock().await = location;
|
||||
let progress = (speed * 10.0) as i32;
|
||||
world.set_block_breaking(entity, location, progress).await;
|
||||
self.current_block_destroy_stage
|
||||
.store(progress, std::sync::atomic::Ordering::Relaxed);
|
||||
}
|
||||
if !state.air {
|
||||
let speed = block::calc_block_breaking(&self, state, &block.name).await;
|
||||
// Instant break
|
||||
if speed >= 1.0 {
|
||||
world.break_block(&location, Some(self.clone()), true).await;
|
||||
server
|
||||
.block_registry
|
||||
.broken(block, &self, location, server)
|
||||
.await;
|
||||
} else {
|
||||
self.mining
|
||||
.store(true, std::sync::atomic::Ordering::Relaxed);
|
||||
*self.mining_pos.lock().await = location;
|
||||
let progress = (speed * 10.0) as i32;
|
||||
world.set_block_breaking(entity, location, progress).await;
|
||||
self.current_block_destroy_stage
|
||||
.store(progress, std::sync::atomic::Ordering::Relaxed);
|
||||
}
|
||||
}
|
||||
self.update_sequence(player_action.sequence.0);
|
||||
@ -1022,14 +1031,13 @@ impl Player {
|
||||
let Ok(block) = world.get_block(&location).await else {
|
||||
return Err(BlockPlacingError::NoBaseBlock.into());
|
||||
};
|
||||
|
||||
let sneaking = self
|
||||
.living_entity
|
||||
.entity
|
||||
.sneaking
|
||||
.load(std::sync::atomic::Ordering::Relaxed);
|
||||
let Some(stack) = held_item else {
|
||||
if !self
|
||||
.living_entity
|
||||
.entity
|
||||
.sneaking
|
||||
.load(std::sync::atomic::Ordering::Relaxed)
|
||||
{
|
||||
if !sneaking {
|
||||
// Using block with empty hand
|
||||
server
|
||||
.block_registry
|
||||
@ -1044,12 +1052,7 @@ impl Player {
|
||||
}
|
||||
return Ok(());
|
||||
};
|
||||
if !self
|
||||
.living_entity
|
||||
.entity
|
||||
.sneaking
|
||||
.load(std::sync::atomic::Ordering::Relaxed)
|
||||
{
|
||||
if !sneaking {
|
||||
let action_result = server
|
||||
.block_registry
|
||||
.use_with_item(block, self, location, &stack.item, server)
|
||||
|
||||
@ -121,7 +121,7 @@ impl Server {
|
||||
],
|
||||
command_dispatcher,
|
||||
block_registry: super::block::default_registry(),
|
||||
item_registry: super::item::default_registry(),
|
||||
item_registry: super::item::items::default_registry(),
|
||||
block_properties_manager: default_block_properties_manager(),
|
||||
auth_client,
|
||||
key_store: KeyStore::new(),
|
||||
|
||||
@ -306,6 +306,8 @@ impl World {
|
||||
.entity
|
||||
.bounding_box
|
||||
.load()
|
||||
// This is vanilla, but TODO: change this when is in a Vehicle
|
||||
.expand(1.0, 0.5, 1.0)
|
||||
.intersects(&entity.get_entity().bounding_box.load())
|
||||
{
|
||||
collied_player = Some(player.clone());
|
||||
@ -402,7 +404,7 @@ impl World {
|
||||
// also send his info to everyone else
|
||||
log::debug!("Broadcasting player info for {}", player.gameprofile.name);
|
||||
self.broadcast_packet_all(&CPlayerInfoUpdate::new(
|
||||
0x01 | 0x08,
|
||||
0x01 | 0x04 | 0x08,
|
||||
&[pumpkin_protocol::client::play::Player {
|
||||
uuid: gameprofile.id,
|
||||
actions: vec![
|
||||
@ -411,6 +413,7 @@ impl World {
|
||||
properties: &gameprofile.properties,
|
||||
},
|
||||
PlayerAction::UpdateListed(true),
|
||||
PlayerAction::UpdateGameMode(VarInt(gamemode as i32)),
|
||||
],
|
||||
}],
|
||||
))
|
||||
|
||||
Loading…
Reference in New Issue
Block a user