feat: more entity work

This commit is contained in:
Alexander Medvedev 2026-09-02 15:16:42 +02:00
parent d1cfc0d181
commit 4e614c9eeb
162 changed files with 11867 additions and 1553 deletions

View File

@ -42,7 +42,9 @@ impl ClientPacket for CConfigAddResourcePack<'_> {
mut write: impl std::io::Write,
version: &JavaMinecraftVersion,
) -> Result<(), crate::ser::WritingError> {
write.write_uuid(self.uuid)?;
if *version >= JavaMinecraftVersion::V_1_20_3 {
write.write_uuid(self.uuid)?;
}
write.write_string(self.url)?;
write.write_string(self.hash)?;
write.write_bool(self.forced)?;

View File

@ -41,15 +41,19 @@ impl ClientPacket for CAddResourcePack<'_> {
mut write: impl std::io::Write,
version: &JavaMinecraftVersion,
) -> Result<(), crate::ser::WritingError> {
write.write_uuid(self.uuid)?;
if *version >= JavaMinecraftVersion::V_1_20_3 {
write.write_uuid(self.uuid)?;
}
write.write_string(self.url)?;
write.write_string(self.hash)?;
write.write_bool(self.forced)?;
if let Some(prompt) = &self.prompt_message {
write.write_bool(true)?;
write.write_component(prompt, version)?;
} else {
write.write_bool(false)?;
if *version >= JavaMinecraftVersion::V_1_17 {
write.write_bool(self.forced)?;
if let Some(prompt) = &self.prompt_message {
write.write_bool(true)?;
write.write_component(prompt, version)?;
} else {
write.write_bool(false)?;
}
}
Ok(())
}

View File

@ -81,12 +81,19 @@ impl ClientPacket for CCommandSuggestions {
mut write: impl std::io::Write,
version: &JavaMinecraftVersion,
) -> Result<(), crate::ser::WritingError> {
write.write_var_int(&self.id)?;
write.write_var_int(&self.start)?;
write.write_var_int(&self.length)?;
write.write_var_int(&VarInt(self.matches.len() as i32))?;
for match_ in &self.matches {
match_.write(&mut write, version)?;
if *version >= JavaMinecraftVersion::V_1_13 {
write.write_var_int(&self.id)?;
write.write_var_int(&self.start)?;
write.write_var_int(&self.length)?;
write.write_var_int(&VarInt(self.matches.len() as i32))?;
for match_ in &self.matches {
match_.write(&mut write, version)?;
}
} else {
write.write_var_int(&VarInt(self.matches.len() as i32))?;
for match_ in &self.matches {
write.write_string(&match_.suggestion)?;
}
}
Ok(())
}

View File

@ -24,9 +24,9 @@ impl ClientPacket for CPlaceGhostRecipe<'_> {
fn write_packet_data(
&self,
mut write: impl std::io::Write,
_version: &JavaMinecraftVersion,
version: &JavaMinecraftVersion,
) -> Result<(), crate::ser::WritingError> {
write.write_u8(self.window_id)?;
write.write_container_id(&crate::VarInt(i32::from(self.window_id)), version)?;
write.write_string(self.recipe_id)?;
Ok(())
}

View File

@ -21,10 +21,16 @@ impl ClientPacket for CPlayerRotation {
fn write_packet_data(
&self,
mut write: impl std::io::Write,
_version: &JavaMinecraftVersion,
version: &JavaMinecraftVersion,
) -> Result<(), crate::ser::WritingError> {
write.write_f32_be(self.yaw)?;
if *version >= JavaMinecraftVersion::V_1_21_9 {
write.write_bool(false)?;
}
write.write_f32_be(self.pitch)?;
if *version >= JavaMinecraftVersion::V_1_21_9 {
write.write_bool(false)?;
}
Ok(())
}
}

View File

@ -28,12 +28,14 @@ impl ClientPacket for CProjectilePower {
fn write_packet_data(
&self,
mut write: impl std::io::Write,
_version: &JavaMinecraftVersion,
version: &JavaMinecraftVersion,
) -> Result<(), crate::ser::WritingError> {
write.write_var_int(&self.entity_id)?;
write.write_f64_be(self.x_power)?;
write.write_f64_be(self.y_power)?;
write.write_f64_be(self.z_power)?;
if *version < JavaMinecraftVersion::V_1_21 {
write.write_f64_be(self.y_power)?;
write.write_f64_be(self.z_power)?;
}
Ok(())
}
}

View File

@ -26,10 +26,19 @@ impl ClientPacket for CRemoveMobEffect {
fn write_packet_data(
&self,
mut write: impl std::io::Write,
_version: &JavaMinecraftVersion,
version: &JavaMinecraftVersion,
) -> Result<(), crate::ser::WritingError> {
write.write_var_int(&self.entity_id)?;
write.write_var_int(&self.effect_id)?;
if *version <= JavaMinecraftVersion::V_1_7_6 {
write.write_i32_be(self.entity_id.0)?;
} else {
write.write_var_int(&self.entity_id)?;
}
if *version >= JavaMinecraftVersion::V_1_18_2 {
write.write_var_int(&self.effect_id)?;
} else {
write.write_u8(self.effect_id.0 as u8)?;
}
Ok(())
}
}

View File

@ -24,12 +24,36 @@ impl ClientPacket for CServerData<'_> {
mut write: impl std::io::Write,
version: &JavaMinecraftVersion,
) -> Result<(), crate::ser::WritingError> {
write.write_component(self.motd, version)?;
if let Some(icon) = self.icon_base64 {
write.write_bool(true)?;
write.write_string(icon)?;
if *version >= JavaMinecraftVersion::V_1_19_4 {
write.write_component(self.motd, version)?;
if let Some(icon) = self.icon_base64 {
write.write_bool(true)?;
let raw_b64 = icon.strip_prefix("data:image/png;base64,").unwrap_or(icon);
if let Ok(bytes) = pumpkin_util::jwt::decode_b64_standard(raw_b64) {
write.write_slice(&bytes)?;
} else {
write.write_slice(&[])?;
}
} else {
write.write_bool(false)?;
}
} else {
write.write_bool(false)?;
write.write_bool(true)?;
write.write_component(self.motd, version)?;
if let Some(icon) = self.icon_base64 {
write.write_bool(true)?;
write.write_string(icon)?;
} else {
write.write_bool(false)?;
}
if *version < JavaMinecraftVersion::V_1_19_3 {
write.write_bool(false)?;
}
if *version >= JavaMinecraftVersion::V_1_19_1
&& *version < JavaMinecraftVersion::V_1_20_5
{
write.write_bool(false)?;
}
}
Ok(())
}

View File

@ -20,9 +20,13 @@ impl ClientPacket for CSetSelectedSlot {
fn write_packet_data(
&self,
mut write: impl std::io::Write,
_version: &JavaMinecraftVersion,
version: &JavaMinecraftVersion,
) -> Result<(), crate::ser::WritingError> {
write.write_i8(self.slot)?;
if *version >= JavaMinecraftVersion::V_1_21_4 {
write.write_var_int(&crate::VarInt(i32::from(self.slot)))?;
} else {
write.write_i8(self.slot)?;
}
Ok(())
}
}

View File

@ -38,13 +38,40 @@ impl ClientPacket for CUpdateMobEffect {
fn write_packet_data(
&self,
mut write: impl std::io::Write,
_version: &JavaMinecraftVersion,
version: &JavaMinecraftVersion,
) -> Result<(), crate::ser::WritingError> {
write.write_var_int(&self.entity_id)?;
write.write_var_int(&self.effect_id)?;
write.write_var_int(&self.amplifier)?;
write.write_var_int(&self.duration)?;
write.write_i8(self.flags)?;
if *version <= JavaMinecraftVersion::V_1_7_6 {
write.write_i32_be(self.entity_id.0)?;
} else {
write.write_var_int(&self.entity_id)?;
}
if *version >= JavaMinecraftVersion::V_1_18_2 {
write.write_var_int(&self.effect_id)?;
} else {
write.write_u8(self.effect_id.0 as u8)?;
}
if *version >= JavaMinecraftVersion::V_1_20_5 {
write.write_var_int(&self.amplifier)?;
} else {
write.write_i8(self.amplifier.0 as i8)?;
}
if *version <= JavaMinecraftVersion::V_1_7_6 {
write.write_i16_be(self.duration.0 as i16)?;
} else {
write.write_var_int(&self.duration)?;
}
if *version > JavaMinecraftVersion::V_1_7_6 {
write.write_i8(self.flags)?;
}
if *version >= JavaMinecraftVersion::V_1_19 && *version < JavaMinecraftVersion::V_1_20_5 {
write.write_bool(false)?;
}
Ok(())
}
}

View File

@ -45,27 +45,38 @@ impl ClientPacket for CUpdateObjectives {
write.write_string(&self.objective_name)?;
write.write_u8(self.mode)?;
if self.mode == 0 || self.mode == 2 {
write.write_component(&self.display_name, version)?;
write.write_var_int(&self.render_type)?;
write.write_option(&self.number_format, |p, v| {
match v {
NumberFormat::Blank => p.write_var_int(&VarInt(0)),
NumberFormat::Styled(_style) => {
p.write_var_int(&VarInt(1))?;
let comp = pumpkin_nbt::compound::NbtCompound::new();
// Write style properties if any
let bytes = pumpkin_nbt::Nbt::from(comp).write_unnamed();
p.write_all(&bytes).map_err(WritingError::IoError)
}
NumberFormat::Fixed(text_component) => {
p.write_var_int(&VarInt(2))?;
p.write_component(text_component, version)
}
if *version < JavaMinecraftVersion::V_1_13 {
write.write_string(&self.display_name.clone().to_pretty_console())?;
let render_str = if self.render_type.0 == 1 {
"hearts"
} else {
"integer"
};
write.write_string(render_str)?;
} else {
write.write_component(&self.display_name, version)?;
write.write_var_int(&self.render_type)?;
if *version >= JavaMinecraftVersion::V_1_20_3 {
write.write_option(&self.number_format, |p, v| {
match v {
NumberFormat::Blank => p.write_var_int(&VarInt(0)),
NumberFormat::Styled(_style) => {
p.write_var_int(&VarInt(1))?;
let comp = pumpkin_nbt::compound::NbtCompound::new();
// Write style properties if any
let bytes = pumpkin_nbt::Nbt::from(comp).write_unnamed();
p.write_all(&bytes).map_err(WritingError::IoError)
}
NumberFormat::Fixed(text_component) => {
p.write_var_int(&VarInt(2))?;
p.write_component(text_component, version)
}
}
})?;
}
})
} else {
Ok(())
}
}
Ok(())
}
}

View File

@ -66,9 +66,52 @@ impl ClientPacket for CUpdateScore {
version: &pumpkin_util::version::JavaMinecraftVersion,
) -> Result<(), WritingError> {
write.write_string(&self.entity_name)?;
write.write_string(&self.objective_name)?;
write.write_var_int(&self.value)?;
write.write_option(&self.display_name, |w, t| w.write_component(t, version))?;
write.write_option(&self.number_format, |w, n| n.write(w))
if *version >= pumpkin_util::version::JavaMinecraftVersion::V_1_20_3 {
write.write_string(&self.objective_name)?;
write.write_var_int(&self.value)?;
write.write_option(&self.display_name, |w, t| w.write_component(t, version))?;
write.write_option(&self.number_format, |w, n| n.write(w))
} else if *version <= pumpkin_util::version::JavaMinecraftVersion::V_1_7_6 {
write.write_u8(0)?;
write.write_string(&self.objective_name)?;
write.write_i32_be(self.value.0)
} else {
write.write_u8(0)?;
write.write_string(&self.objective_name)?;
write.write_var_int(&self.value)
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use pumpkin_util::version::JavaMinecraftVersion;
#[test]
fn update_score_serialization() {
let packet = CUpdateScore::new("Alex".into(), "kills".into(), VarInt(42), None, None);
// Modern 1.20.3+
let mut buf_modern = Vec::new();
packet
.write_packet_data(&mut buf_modern, &JavaMinecraftVersion::V_1_20_3)
.unwrap();
// 1.8 - 1.20.2
let mut buf_legacy = Vec::new();
packet
.write_packet_data(&mut buf_legacy, &JavaMinecraftVersion::V_1_8)
.unwrap();
// 1.7.6
let mut buf_v1_7 = Vec::new();
packet
.write_packet_data(&mut buf_v1_7, &JavaMinecraftVersion::V_1_7_6)
.unwrap();
assert!(!buf_modern.is_empty());
assert!(!buf_legacy.is_empty());
assert!(!buf_v1_7.is_empty());
}
}

View File

@ -3,7 +3,7 @@ use pumpkin_macros::java_packet;
use crate::{
ServerPacket,
ser::{NetworkReadExt, ReadingError},
ser::{NetworkReadExt, NetworkReadSliceExt, ReadingError},
};
use pumpkin_util::version::JavaMinecraftVersion;
@ -34,11 +34,17 @@ pub struct SConfigResourcePack {
}
impl<'a> ServerPacket<'a> for SConfigResourcePack {
fn read(bytebuf: &mut &'a [u8], _version: &JavaMinecraftVersion) -> Result<Self, ReadingError> {
Ok(Self {
uuid: bytebuf.get_uuid()?,
result: bytebuf.get_var_int()?,
})
fn read(bytebuf: &mut &'a [u8], version: &JavaMinecraftVersion) -> Result<Self, ReadingError> {
let uuid = if *version >= JavaMinecraftVersion::V_1_20_3 {
bytebuf.get_uuid()?
} else {
uuid::Uuid::nil()
};
if *version < JavaMinecraftVersion::V_1_10 {
let _hash = bytebuf.get_str_bounded_borrowed(40)?;
}
let result = bytebuf.get_var_int()?;
Ok(Self { uuid, result })
}
}

View File

@ -16,11 +16,28 @@ pub struct SCommandSuggestion<'a> {
}
impl<'a> ServerPacket<'a> for SCommandSuggestion<'a> {
fn read(bytebuf: &mut &'a [u8], _version: &JavaMinecraftVersion) -> Result<Self, ReadingError> {
Ok(Self {
id: bytebuf.get_var_int()?,
command: bytebuf.get_str_borrowed()?,
})
fn read(bytebuf: &mut &'a [u8], version: &JavaMinecraftVersion) -> Result<Self, ReadingError> {
if *version >= JavaMinecraftVersion::V_1_13 {
Ok(Self {
id: bytebuf.get_var_int()?,
command: bytebuf.get_str_borrowed()?,
})
} else {
let command = bytebuf.get_str_borrowed()?;
if *version >= JavaMinecraftVersion::V_1_9 {
let _assume_command = bytebuf.get_bool()?;
}
if *version >= JavaMinecraftVersion::V_1_8 {
let has_pos = bytebuf.get_bool()?;
if has_pos {
let _ = bytebuf.get_block_pos(version)?;
}
}
Ok(Self {
id: VarInt(0),
command,
})
}
}
}

View File

@ -16,21 +16,49 @@ pub struct SEditBook<'a> {
}
impl<'a> ServerPacket<'a> for SEditBook<'a> {
fn read(read: &mut &'a [u8], _version: &JavaMinecraftVersion) -> Result<Self, ReadingError> {
let slot = read.get_var_int()?;
let count = read.get_var_int()?.0 as usize;
let count = count.min(100);
let mut pages = Vec::with_capacity(count);
for _ in 0..count {
pages.push(read.get_str_bounded_borrowed(1024)?);
}
let has_title = read.get_bool()?;
let title = if has_title {
Some(read.get_str_bounded_borrowed(128)?)
fn read(read: &mut &'a [u8], version: &JavaMinecraftVersion) -> Result<Self, ReadingError> {
if *version >= JavaMinecraftVersion::V_1_17_1 {
let slot = read.get_var_int()?;
let count = read.get_var_int()?.0 as usize;
let max_pages = if *version >= JavaMinecraftVersion::V_1_21_2 {
100
} else {
200
};
let count = count.min(max_pages);
let char_limit = if *version >= JavaMinecraftVersion::V_1_21_2 {
1024
} else {
8192
};
let mut pages = Vec::with_capacity(count);
for _ in 0..count {
pages.push(read.get_str_bounded_borrowed(char_limit)?);
}
let has_title = read.get_bool()?;
let title_limit = if *version >= JavaMinecraftVersion::V_1_21_2 {
32
} else {
128
};
let title = if has_title {
Some(read.get_str_bounded_borrowed(title_limit)?)
} else {
None
};
Ok(Self { slot, pages, title })
} else {
None
};
Ok(Self { slot, pages, title })
let _item = crate::codec::item_stack_seralizer::ItemStackSerializer::read_with_version(
read, version,
)?;
let _signing = read.get_bool()?;
let slot = read.get_var_int()?;
Ok(Self {
slot,
pages: Vec::new(),
title: None,
})
}
}
}

View File

@ -1,6 +1,6 @@
use crate::{
ServerPacket,
ser::{NetworkReadExt, ReadingError},
ser::{NetworkReadExt, NetworkReadSliceExt, ReadingError},
};
use pumpkin_data::packet::serverbound::play::PLACE_RECIPE;
use pumpkin_macros::java_packet;
@ -16,12 +16,35 @@ pub struct SPlaceRecipe {
}
impl<'a> ServerPacket<'a> for SPlaceRecipe {
fn read(bytebuf: &mut &'a [u8], _version: &JavaMinecraftVersion) -> Result<Self, ReadingError> {
Ok(Self {
container_id: bytebuf.get_i8()?,
recipe_display_id: bytebuf.get_var_int()?,
use_max_items: bytebuf.get_bool()?,
})
fn read(bytebuf: &mut &'a [u8], version: &JavaMinecraftVersion) -> Result<Self, ReadingError> {
if *version >= JavaMinecraftVersion::V_1_21_2 {
let container_id = bytebuf.get_container_id(version)?.0 as i8;
let recipe_display_id = bytebuf.get_var_int()?;
let use_max_items = bytebuf.get_bool()?;
Ok(Self {
container_id,
recipe_display_id,
use_max_items,
})
} else if *version >= JavaMinecraftVersion::V_1_13 {
let container_id = bytebuf.get_i8()?;
let _recipe_key = bytebuf.get_str_borrowed()?;
let use_max_items = bytebuf.get_bool()?;
Ok(Self {
container_id,
recipe_display_id: VarInt(0),
use_max_items,
})
} else {
let container_id = bytebuf.get_i8()?;
let recipe_display_id = bytebuf.get_var_int()?;
let use_max_items = bytebuf.get_bool()?;
Ok(Self {
container_id,
recipe_display_id,
use_max_items,
})
}
}
}

View File

@ -1,6 +1,6 @@
use crate::{
ServerPacket,
ser::{NetworkReadExt, ReadingError},
ser::{NetworkReadExt, NetworkReadSliceExt, ReadingError},
};
use pumpkin_data::packet::serverbound::play::RECIPE_BOOK_SEEN_RECIPE;
use pumpkin_macros::java_packet;
@ -14,10 +14,17 @@ pub struct SRecipeBookSeenRecipe {
}
impl<'a> ServerPacket<'a> for SRecipeBookSeenRecipe {
fn read(bytebuf: &mut &'a [u8], _version: &JavaMinecraftVersion) -> Result<Self, ReadingError> {
Ok(Self {
recipe_display_id: bytebuf.get_var_int()?,
})
fn read(bytebuf: &mut &'a [u8], version: &JavaMinecraftVersion) -> Result<Self, ReadingError> {
if *version >= JavaMinecraftVersion::V_1_21_2 {
Ok(Self {
recipe_display_id: bytebuf.get_var_int()?,
})
} else {
let _recipe = bytebuf.get_str_borrowed()?;
Ok(Self {
recipe_display_id: VarInt(0),
})
}
}
}

View File

@ -4,7 +4,7 @@ use pumpkin_macros::java_packet;
use crate::{
ServerPacket,
codec::var_int::VarInt,
ser::{NetworkReadExt, ReadingError},
ser::{NetworkReadExt, NetworkReadSliceExt, ReadingError},
};
use pumpkin_util::version::JavaMinecraftVersion;
@ -45,11 +45,17 @@ impl SPlayResourcePack {
}
impl<'a> ServerPacket<'a> for SPlayResourcePack {
fn read(bytebuf: &mut &'a [u8], _version: &JavaMinecraftVersion) -> Result<Self, ReadingError> {
Ok(Self {
uuid: bytebuf.get_uuid()?,
result: bytebuf.get_var_int()?,
})
fn read(bytebuf: &mut &'a [u8], version: &JavaMinecraftVersion) -> Result<Self, ReadingError> {
let uuid = if *version >= JavaMinecraftVersion::V_1_20_3 {
bytebuf.get_uuid()?
} else {
uuid::Uuid::nil()
};
if *version < JavaMinecraftVersion::V_1_10 {
let _hash = bytebuf.get_str_bounded_borrowed(40)?;
}
let result = bytebuf.get_var_int()?;
Ok(Self { uuid, result })
}
}

View File

@ -15,11 +15,22 @@ pub struct SSetBeacon {
}
impl<'a> ServerPacket<'a> for SSetBeacon {
fn read(bytebuf: &mut &'a [u8], _version: &JavaMinecraftVersion) -> Result<Self, ReadingError> {
Ok(Self {
primary_effect: bytebuf.get_option(NetworkReadExt::get_var_int)?,
secondary_effect: bytebuf.get_option(NetworkReadExt::get_var_int)?,
})
fn read(bytebuf: &mut &'a [u8], version: &JavaMinecraftVersion) -> Result<Self, ReadingError> {
if *version >= JavaMinecraftVersion::V_1_19 {
Ok(Self {
primary_effect: bytebuf.get_option(NetworkReadExt::get_var_int)?,
secondary_effect: bytebuf.get_option(NetworkReadExt::get_var_int)?,
})
} else {
let p = bytebuf.get_var_int()?;
let s = bytebuf.get_var_int()?;
let primary_effect = if p.0 == -1 { None } else { Some(p) };
let secondary_effect = if s.0 == -1 { None } else { Some(s) };
Ok(Self {
primary_effect,
secondary_effect,
})
}
}
}

View File

@ -474,6 +474,18 @@ impl<T: Math + Copy> Mul<T> for Vector3<T> {
}
}
impl<T: Math + Copy> Div<T> for Vector3<T> {
type Output = Self;
fn div(self, scalar: T) -> Self {
Self {
x: self.x / scalar,
y: self.y / scalar,
z: self.z / scalar,
}
}
}
impl<T: Math + Copy> Add for Vector3<T> {
type Output = Self;

View File

@ -2,12 +2,12 @@ use crate::block::blocks::falling::FallingBlock;
use crate::block::registry::BlockActionResult;
use crate::block::{
BlockBehaviour, GetStateForNeighborUpdateArgs, NormalUseArgs, OnPlaceArgs, OnScheduledTickArgs,
PlacedArgs,
PathComputationType, PlacedArgs,
};
use pumpkin_data::block_properties::WallTorchLikeProperties;
use pumpkin_data::translation;
use pumpkin_data::{Block, BlockStateId};
use pumpkin_data::{Block, BlockState, BlockStateId};
use pumpkin_inventory::anvil::AnvilScreenHandler;
use pumpkin_inventory::player::player_inventory::PlayerInventory;
use pumpkin_inventory::screen_handler::{
@ -79,6 +79,10 @@ impl BlockBehaviour for AnvilBlock {
) -> BlockStateId {
FallingBlock::get_state_for_neighbor_update(&FallingBlock, args)
}
fn is_pathfindable(&self, _state: &BlockState, _computation_type: PathComputationType) -> bool {
false
}
}
struct AnvilScreenFactory;

View File

@ -1,12 +1,11 @@
use std::sync::Arc;
use crate::block::entities::bed::BedBlockEntity;
use pumpkin_data::Block;
use pumpkin_data::BlockStateId;
use pumpkin_data::block_properties::BedPart;
use pumpkin_data::dimension::Dimension;
use pumpkin_data::entity::EntityType;
use pumpkin_data::translation;
use pumpkin_data::{Block, BlockState, BlockStateId};
use pumpkin_macros::pumpkin_block_from_tag;
use pumpkin_util::GameMode;
use pumpkin_util::math::position::BlockPos;
@ -18,7 +17,7 @@ use crate::block::bounce_entity_after_fall;
use crate::block::registry::BlockActionResult;
use crate::block::{
BlockBehaviour, BrokenArgs, CanPlaceAtArgs, NormalUseArgs, OnPlaceArgs, OnStateReplacedArgs,
PlacedArgs, PlayerPlacedArgs,
PathComputationType, PlacedArgs, PlayerPlacedArgs,
};
use crate::entity::{Entity, EntityBase, player::Player};
use crate::world::World;
@ -188,6 +187,10 @@ impl BlockBehaviour for BedBlock {
fn normal_use(&self, args: NormalUseArgs<'_>) -> BlockActionResult {
Self::use_bed(args.world, args.player, args.block, args.position)
}
fn is_pathfindable(&self, _state: &BlockState, _computation_type: PathComputationType) -> bool {
false
}
}
impl BedBlock {

View File

@ -1,13 +1,14 @@
use std::sync::Arc;
use std::sync::Mutex;
use crate::block::{GetComparatorOutputArgs, PlacedArgs};
use crate::block::{GetComparatorOutputArgs, PathComputationType, PlacedArgs};
use crate::block::{
registry::BlockActionResult,
{BlockBehaviour, NormalUseArgs},
};
use crate::block::entities::brewing_stand::BrewingStandBlockEntity;
use pumpkin_data::BlockState;
use pumpkin_data::translation;
use pumpkin_inventory::player::player_inventory::PlayerInventory;
use pumpkin_inventory::screen_handler::{
@ -85,4 +86,8 @@ impl BlockBehaviour for BrewingStandBlock {
None
}
}
fn is_pathfindable(&self, _state: &BlockState, _computation_type: PathComputationType) -> bool {
false
}
}

View File

@ -3,7 +3,7 @@ use std::sync::Arc;
use crate::{
block::{
BlockBehaviour, CanPlaceAtArgs, GetComparatorOutputArgs, GetStateForNeighborUpdateArgs,
NormalUseArgs, OnPlaceArgs, OnScheduledTickArgs, UseWithItemArgs,
NormalUseArgs, OnPlaceArgs, OnScheduledTickArgs, PathComputationType, UseWithItemArgs,
blocks::candle_cakes::cake_from_candle, registry::BlockActionResult,
},
entity::player::Player,
@ -11,7 +11,7 @@ use crate::{
};
use pumpkin_data::item::Item;
use pumpkin_data::{
Block, BlockStateId,
Block, BlockState, BlockStateId,
block_properties::CakeLikeProperties,
sound::{Sound, SoundCategory},
};
@ -169,6 +169,10 @@ impl BlockBehaviour for CakeBlock {
}
}
}
fn is_pathfindable(&self, _state: &BlockState, _computation_type: PathComputationType) -> bool {
false
}
}
fn can_place_at(world: &dyn BlockAccessor, position: &BlockPos) -> bool {

View File

@ -1,6 +1,7 @@
use pumpkin_data::{
Block, BlockDirection, BlockStateId, Enchantment, block_properties::CampfireLikeProperties,
damage::DamageType, data_component_impl::EquipmentSlot, effect::StatusEffect, fluid::Fluid,
Block, BlockDirection, BlockState, BlockStateId, Enchantment,
block_properties::CampfireLikeProperties, damage::DamageType,
data_component_impl::EquipmentSlot, effect::StatusEffect, fluid::Fluid,
};
use pumpkin_macros::pumpkin_block_from_tag;
use pumpkin_world::tick::TickPriority;
@ -9,7 +10,7 @@ use crate::block::entities::campfire::CampfireBlockEntity;
use crate::{
block::{
BlockBehaviour, BlockIsReplacing, GetStateForNeighborUpdateArgs, OnEntityCollisionArgs,
OnPlaceArgs, PlacedArgs,
OnPlaceArgs, PathComputationType, PlacedArgs,
},
entity::EntityBase,
};
@ -95,6 +96,10 @@ impl BlockBehaviour for CampfireBlock {
props.to_state_id(args.block)
}
fn is_pathfindable(&self, _state: &BlockState, _computation_type: PathComputationType) -> bool {
false
}
// TODO: onProjectileHit
}

View File

@ -1,6 +1,6 @@
use std::sync::Arc;
use pumpkin_data::{Block, BlockStateId, item::Item, item_stack::ItemStack};
use pumpkin_data::{Block, BlockState, BlockStateId, item::Item, item_stack::ItemStack};
use pumpkin_macros::pumpkin_block_from_tag;
use pumpkin_util::{GameMode, math::position::BlockPos};
use pumpkin_world::{
@ -11,7 +11,7 @@ use pumpkin_world::{
use crate::{
block::{
BlockBehaviour, GetStateForNeighborUpdateArgs, NormalUseArgs, OnScheduledTickArgs,
UseWithItemArgs, blocks::cake::CakeBlock, registry::BlockActionResult,
PathComputationType, UseWithItemArgs, blocks::cake::CakeBlock, registry::BlockActionResult,
},
entity::player::Player,
world::World,
@ -121,6 +121,10 @@ impl BlockBehaviour for CandleCakeBlock {
}
args.state_id
}
fn is_pathfindable(&self, _state: &BlockState, _computation_type: PathComputationType) -> bool {
false
}
}
fn can_place_at(world: &dyn BlockAccessor, position: &BlockPos) -> bool {

View File

@ -1,13 +1,14 @@
use std::sync::Arc;
use crate::block::registry::BlockActionResult;
use crate::block::{BlockBehaviour, BlockMetadata, GetComparatorOutputArgs, UseWithItemArgs};
use pumpkin_data::Block;
use pumpkin_data::BlockId;
use crate::block::{
BlockBehaviour, BlockMetadata, GetComparatorOutputArgs, PathComputationType, UseWithItemArgs,
};
use pumpkin_data::block_properties::WaterCauldronLikeProperties;
use pumpkin_data::item::Item;
use pumpkin_data::item_stack::ItemStack;
use pumpkin_data::sound::{Sound, SoundCategory};
use pumpkin_data::{Block, BlockId, BlockState};
use pumpkin_world::world::BlockFlags;
pub struct CauldronBlock;
@ -216,4 +217,8 @@ impl BlockBehaviour for CauldronBlock {
_ => Some(0),
}
}
fn is_pathfindable(&self, _state: &BlockState, _computation_type: PathComputationType) -> bool {
false
}
}

View File

@ -1,7 +1,6 @@
use crate::block::{BlockBehaviour, OnPlaceArgs};
use pumpkin_data::BlockDirection;
use pumpkin_data::BlockStateId;
use crate::block::{BlockBehaviour, OnPlaceArgs, PathComputationType};
use pumpkin_data::block_properties::Axis;
use pumpkin_data::{BlockDirection, BlockState, BlockStateId};
use pumpkin_macros::pumpkin_block;
#[pumpkin_block("minecraft:iron_chain")]
@ -20,4 +19,8 @@ impl BlockBehaviour for ChainBlock {
props.to_state_id(args.block)
}
fn is_pathfindable(&self, _state: &BlockState, _computation_type: PathComputationType) -> bool {
false
}
}

View File

@ -22,17 +22,15 @@ use pumpkin_world::world::BlockFlags;
use std::sync::Mutex;
use crate::block::{
BrokenArgs, EmitsRedstonePowerArgs, GetComparatorOutputArgs, GetRedstonePowerArgs,
NormalUseArgs, OnPlaceArgs, OnSyncedBlockEventArgs, PlacedArgs, PlayerPlacedArgs,
RandomTickArgs,
BlockBehaviour, BrokenArgs, EmitsRedstonePowerArgs, GetComparatorOutputArgs,
GetRedstonePowerArgs, NormalUseArgs, OnPlaceArgs, OnSyncedBlockEventArgs, PathComputationType,
PlacedArgs, PlayerPlacedArgs, RandomTickArgs, registry::BlockActionResult,
};
use crate::entity::EntityBase;
use crate::entity::player::Player;
use crate::world::World;
use crate::world::loot::fill_chest_inventory;
use crate::{
block::{BlockBehaviour, registry::BlockActionResult},
entity::player::Player,
};
use pumpkin_data::BlockState;
struct ChestScreenFactory(Arc<dyn Inventory>);
@ -297,6 +295,10 @@ impl BlockBehaviour for ChestBlock {
fn get_comparator_output(&self, args: GetComparatorOutputArgs<'_>) -> Option<u8> {
get_chest_comparator_output(&args)
}
fn is_pathfindable(&self, _state: &BlockState, _computation_type: PathComputationType) -> bool {
false
}
}
/// Copper chests have the same behavior as wooden chests but also oxidize over time.
@ -389,6 +391,10 @@ impl BlockBehaviour for CopperChestBlock {
fn get_comparator_output(&self, args: GetComparatorOutputArgs<'_>) -> Option<u8> {
get_chest_comparator_output(&args)
}
fn is_pathfindable(&self, _state: &BlockState, _computation_type: PathComputationType) -> bool {
false
}
}
/// Trapped chests have the same behavior as wooden chests but also emit redstone power based on viewer count.
@ -455,6 +461,10 @@ impl BlockBehaviour for TrappedChestBlock {
fn get_comparator_output(&self, args: GetComparatorOutputArgs<'_>) -> Option<u8> {
get_chest_comparator_output(&args)
}
fn is_pathfindable(&self, _state: &BlockState, _computation_type: PathComputationType) -> bool {
false
}
}
fn compute_chest_props(

View File

@ -3,13 +3,13 @@ use std::sync::Arc;
use crate::{
block::{
BlockBehaviour, GetComparatorOutputArgs, NormalUseArgs, OnScheduledTickArgs,
UseWithItemArgs, registry::BlockActionResult,
PathComputationType, UseWithItemArgs, registry::BlockActionResult,
},
entity::{Entity, item::ItemEntity},
world::World,
};
use pumpkin_data::{
Block, BlockStateId, block_properties::ComposterLikeProperties,
Block, BlockState, BlockStateId, block_properties::ComposterLikeProperties,
composter_increase_chance::get_composter_increase_chance_from_item_id, entity::EntityType,
item::Item, item_stack::ItemStack, world::WorldEvent,
};
@ -93,6 +93,10 @@ impl BlockBehaviour for ComposterBlock {
Some(props.level)
}
}
fn is_pathfindable(&self, _state: &BlockState, _computation_type: PathComputationType) -> bool {
false
}
}
impl ComposterBlock {

View File

@ -1,6 +1,6 @@
use crate::block::entities::conduit::ConduitBlockEntity;
use crate::block::{BlockBehaviour, OnPlaceArgs, PlacedArgs};
use pumpkin_data::BlockStateId;
use crate::block::{BlockBehaviour, OnPlaceArgs, PathComputationType, PlacedArgs};
use pumpkin_data::{BlockState, BlockStateId};
use pumpkin_macros::pumpkin_block;
use std::sync::Arc;
@ -22,4 +22,8 @@ impl BlockBehaviour for ConduitBlock {
args.world.add_block_entity(Arc::new(entity));
}
}
fn is_pathfindable(&self, _state: &BlockState, _computation_type: PathComputationType) -> bool {
false
}
}

View File

@ -1,17 +1,17 @@
use std::sync::Arc;
use pumpkin_data::BlockStateId;
use pumpkin_data::block_properties::DecoratedPotLikeProperties;
use pumpkin_data::item::Item;
use pumpkin_data::item_stack::ItemStack;
use pumpkin_data::sound::{Sound, SoundCategory};
use pumpkin_data::{BlockState, BlockStateId};
use pumpkin_macros::pumpkin_block;
use crate::block::entities::decorated_pot::DecoratedPotBlockEntity;
use crate::block::registry::BlockActionResult;
use crate::block::{
BlockBehaviour, BrokenArgs, GetComparatorOutputArgs, NormalUseArgs, OnPlaceArgs, PlacedArgs,
UseWithItemArgs,
BlockBehaviour, BrokenArgs, GetComparatorOutputArgs, NormalUseArgs, OnPlaceArgs,
PathComputationType, PlacedArgs, UseWithItemArgs,
};
#[pumpkin_block("minecraft:decorated_pot")]
@ -109,4 +109,8 @@ impl BlockBehaviour for DecoratedPotBlock {
Some(0)
}
}
fn is_pathfindable(&self, _state: &BlockState, _computation_type: PathComputationType) -> bool {
false
}
}

View File

@ -1,11 +1,8 @@
use crate::block::BlockBehaviour;
use crate::block::CanPlaceAtArgs;
use crate::block::GetStateForNeighborUpdateArgs;
use crate::block::OnPlaceArgs;
use crate::block::OnScheduledTickArgs;
use pumpkin_data::Block;
use pumpkin_data::BlockDirection;
use pumpkin_data::BlockStateId;
use crate::block::{
BlockBehaviour, CanPlaceAtArgs, GetStateForNeighborUpdateArgs, OnPlaceArgs,
OnScheduledTickArgs, PathComputationType,
};
use pumpkin_data::{Block, BlockDirection, BlockState, BlockStateId};
use pumpkin_macros::pumpkin_block;
use pumpkin_util::math::position::BlockPos;
use pumpkin_world::tick::TickPriority;
@ -47,6 +44,10 @@ impl BlockBehaviour for DirtPathBlock {
fn can_place_at(&self, args: CanPlaceAtArgs<'_>) -> bool {
can_place_at(args.block_accessor, args.position)
}
fn is_pathfindable(&self, _state: &BlockState, _computation_type: PathComputationType) -> bool {
false
}
}
fn can_place_at(world: &dyn BlockAccessor, block_pos: &BlockPos) -> bool {

View File

@ -7,7 +7,7 @@ use pumpkin_data::block_properties::HorizontalFacing;
use pumpkin_data::sound::Sound;
use pumpkin_data::sound::SoundCategory;
use pumpkin_data::tag::Taggable;
use pumpkin_data::{Block, tag};
use pumpkin_data::{Block, BlockState, tag};
use pumpkin_macros::pumpkin_block_from_tag;
use pumpkin_util::math::position::BlockPos;
use pumpkin_world::world::BlockAccessor;
@ -22,6 +22,7 @@ use crate::block::NormalUseArgs;
use crate::block::OnNeighborUpdateArgs;
use crate::block::OnPlaceArgs;
use crate::block::OnStateReplacedArgs;
use crate::block::PathComputationType;
use crate::block::PlacedArgs;
use crate::block::blocks::redstone::block_receives_redstone_power;
use crate::block::registry::BlockActionResult;
@ -391,6 +392,15 @@ impl BlockBehaviour for DoorBlock {
}
}
}
fn is_pathfindable(&self, state: &BlockState, computation_type: PathComputationType) -> bool {
match computation_type {
PathComputationType::Land | PathComputationType::Air => {
DoorProperties::from_state_id(state.id).open
}
PathComputationType::Water => false,
}
}
}
fn has_support(world: &dyn BlockAccessor, block_pos: &BlockPos) -> bool {

View File

@ -1,7 +1,10 @@
use crate::block::blocks::falling::FallingBlock;
use crate::block::registry::BlockActionResult;
use crate::block::{BlockBehaviour, BrokenArgs, NormalUseArgs, PlacedArgs};
use crate::block::{
BlockBehaviour, BrokenArgs, NormalUseArgs, OnScheduledTickArgs, PathComputationType, PlacedArgs,
};
use crate::world::World;
use pumpkin_data::BlockState;
use pumpkin_macros::pumpkin_block;
use pumpkin_util::math::position::BlockPos;
use pumpkin_world::tick::TickPriority;
@ -56,7 +59,11 @@ impl BlockBehaviour for DragonEggBlock {
Self::teleport(args.world, args.position);
}
fn on_scheduled_tick(&self, args: crate::block::OnScheduledTickArgs<'_>) {
fn on_scheduled_tick(&self, args: OnScheduledTickArgs<'_>) {
FallingBlock::on_scheduled_tick(&FallingBlock, args);
}
fn is_pathfindable(&self, _state: &BlockState, _computation_type: PathComputationType) -> bool {
false
}
}

View File

@ -3,13 +3,13 @@ use std::sync::Arc;
use crate::{
block::{
BlockBehaviour, BrokenArgs, CanPlaceAtArgs, GetStateForNeighborUpdateArgs, OnPlaceArgs,
PlacedArgs,
PathComputationType, PlacedArgs,
},
entity::player::Player,
world::World,
};
use pumpkin_data::{
Block, BlockDirection, BlockStateId,
Block, BlockDirection, BlockState, BlockStateId,
block_properties::{PointedDripstoneLikeProperties, SpeleothemThickness, VerticalDirection},
};
use pumpkin_macros::pumpkin_block;
@ -114,6 +114,10 @@ impl BlockBehaviour for DripstoneBlock {
}
args.state_id
}
fn is_pathfindable(&self, _state: &BlockState, _computation_type: PathComputationType) -> bool {
false
}
}
fn update_stalagmite(world: &Arc<World>, stalagmite_len: u8, tip_pos: &BlockPos) {
let block_above = world.get_block(&tip_pos.up());

View File

@ -3,8 +3,8 @@ use std::sync::Mutex;
use crate::block::entities::enchanting_table::EnchantingTableBlockEntity;
use crate::block::registry::BlockActionResult;
use crate::block::{BlockBehaviour, NormalUseArgs, PlacedArgs};
use pumpkin_data::{Block, BlockStateId, translation};
use crate::block::{BlockBehaviour, NormalUseArgs, PathComputationType, PlacedArgs};
use pumpkin_data::{Block, BlockState, BlockStateId, translation};
use pumpkin_inventory::enchanting::enchanting_screen_handler::EnchantingTableScreenHandler;
use pumpkin_inventory::player::player_inventory::PlayerInventory;
use pumpkin_inventory::screen_handler::{
@ -79,6 +79,10 @@ impl BlockBehaviour for EnchantingTableBlock {
);
BlockActionResult::Success
}
fn is_pathfindable(&self, _state: &BlockState, _computation_type: PathComputationType) -> bool {
false
}
}
impl EnchantingTableBlock {

View File

@ -1,8 +1,8 @@
use pumpkin_data::BlockStateId;
use pumpkin_data::{BlockState, BlockStateId};
use pumpkin_macros::pumpkin_block;
use crate::{
block::{BlockBehaviour, OnPlaceArgs},
block::{BlockBehaviour, OnPlaceArgs, PathComputationType},
entity::EntityBase,
};
@ -18,4 +18,8 @@ impl BlockBehaviour for EndPortalFrameBlock {
end_portal_frame_props.to_state_id(args.block)
}
fn is_pathfindable(&self, _state: &BlockState, _computation_type: PathComputationType) -> bool {
false
}
}

View File

@ -1,7 +1,6 @@
use crate::block::{BlockBehaviour, OnPlaceArgs};
use pumpkin_data::Block;
use pumpkin_data::BlockStateId;
use crate::block::{BlockBehaviour, OnPlaceArgs, PathComputationType};
use pumpkin_data::block_properties::EndRodLikeProperties;
use pumpkin_data::{Block, BlockState, BlockStateId};
use pumpkin_macros::pumpkin_block;
#[pumpkin_block("minecraft:end_rod")]
@ -26,4 +25,8 @@ impl BlockBehaviour for EndRodBlock {
props.to_state_id(args.block)
}
fn is_pathfindable(&self, _state: &BlockState, _computation_type: PathComputationType) -> bool {
false
}
}

View File

@ -3,13 +3,12 @@ use std::sync::Mutex;
use crate::block::entities::ender_chest::EnderChestBlockEntity;
use crate::block::{
BlockBehaviour, NormalUseArgs, OnPlaceArgs, OnSyncedBlockEventArgs, PlacedArgs,
registry::BlockActionResult,
BlockBehaviour, NormalUseArgs, OnPlaceArgs, OnSyncedBlockEventArgs, PathComputationType,
PlacedArgs, registry::BlockActionResult,
};
use crate::world::World;
use pumpkin_data::BlockStateId;
use pumpkin_data::block_properties::LadderLikeProperties;
use pumpkin_data::translation;
use pumpkin_data::{BlockState, BlockStateId, translation};
use pumpkin_inventory::{
generic_container_screen_handler::create_generic_9x3,
player::ender_chest_inventory::EnderChestInventory,
@ -112,6 +111,10 @@ impl BlockBehaviour for EnderChestBlock {
let block_entity = EnderChestBlockEntity::new(*args.position);
args.world.add_block_entity(Arc::new(block_entity));
}
fn is_pathfindable(&self, _state: &BlockState, _computation_type: PathComputationType) -> bool {
false
}
}
fn is_chest_blocked(world: &World, block_pos: &BlockPos) -> bool {

View File

@ -1,18 +1,14 @@
use std::sync::Arc;
use crate::block::BlockBehaviour;
use crate::block::CanPlaceAtArgs;
use crate::block::GetStateForNeighborUpdateArgs;
use crate::block::OnPlaceArgs;
use crate::block::OnScheduledTickArgs;
use crate::block::RandomTickArgs;
use crate::block::{
BlockBehaviour, CanPlaceAtArgs, GetStateForNeighborUpdateArgs, OnPlaceArgs,
OnScheduledTickArgs, PathComputationType, RandomTickArgs,
};
use crate::world::World;
use pumpkin_data::Block;
use pumpkin_data::BlockDirection;
use pumpkin_data::BlockStateId;
use pumpkin_data::block_properties::FarmlandLikeProperties;
use pumpkin_data::tag;
use pumpkin_data::tag::Taggable;
use pumpkin_data::{Block, BlockDirection, BlockState, BlockStateId};
use pumpkin_macros::pumpkin_block;
use pumpkin_util::math::position::BlockPos;
use pumpkin_util::math::vector3::Vector3;
@ -93,6 +89,10 @@ impl BlockBehaviour for FarmlandBlock {
}
}
}
fn is_pathfindable(&self, _state: &BlockState, _computation_type: PathComputationType) -> bool {
false
}
}
fn can_place_at(world: &dyn BlockAccessor, block_pos: &BlockPos) -> bool {

View File

@ -3,16 +3,16 @@ use std::sync::Arc;
use crate::block::blocks::redstone::block_receives_redstone_power;
use crate::block::registry::BlockActionResult;
use crate::block::{
BlockBehaviour, GetStateForNeighborUpdateArgs, NormalUseArgs, OnNeighborUpdateArgs, OnPlaceArgs,
BlockBehaviour, GetStateForNeighborUpdateArgs, NormalUseArgs, OnNeighborUpdateArgs,
OnPlaceArgs, PathComputationType,
};
use crate::entity::EntityBase;
use crate::entity::player::Player;
use crate::world::World;
use pumpkin_data::Block;
use pumpkin_data::BlockStateId;
use pumpkin_data::sound::{Sound, SoundCategory};
use pumpkin_data::tag;
use pumpkin_data::tag::Taggable;
use pumpkin_data::{Block, BlockState, BlockStateId};
use pumpkin_macros::pumpkin_block_from_tag;
use pumpkin_util::math::position::BlockPos;
use pumpkin_world::world::BlockFlags;
@ -134,6 +134,15 @@ impl BlockBehaviour for FenceGateBlock {
);
}
}
fn is_pathfindable(&self, state: &BlockState, computation_type: PathComputationType) -> bool {
match computation_type {
PathComputationType::Land | PathComputationType::Air => {
FenceGateProperties::from_state_id(state.id).open
}
PathComputationType::Water => false,
}
}
}
fn is_in_wall(args: &GetStateForNeighborUpdateArgs<'_>) -> FenceGateProperties {

View File

@ -1,21 +1,16 @@
use crate::block::GetStateForNeighborUpdateArgs;
use crate::block::OnPlaceArgs;
use pumpkin_data::BlockDirection;
use pumpkin_data::BlockState;
use pumpkin_data::BlockStateId;
use pumpkin_data::HorizontalFacingExt;
use crate::block::{
BlockBehaviour, GetStateForNeighborUpdateArgs, OnPlaceArgs, PathComputationType,
};
use crate::world::World;
use pumpkin_data::block_properties::HorizontalFacing;
use pumpkin_data::tag::Taggable;
use pumpkin_data::{Block, tag};
use pumpkin_data::{Block, BlockDirection, BlockState, BlockStateId, HorizontalFacingExt, tag};
use pumpkin_macros::pumpkin_block_from_tag;
use pumpkin_util::math::position::BlockPos;
type FenceGateProperties = pumpkin_data::block_properties::OakFenceGateLikeProperties;
type FenceProperties = pumpkin_data::block_properties::OakFenceLikeProperties;
use crate::block::BlockBehaviour;
use crate::world::World;
#[pumpkin_block_from_tag("minecraft:fences")]
pub struct FenceBlock;
@ -34,6 +29,10 @@ impl BlockBehaviour for FenceBlock {
let fence_props = FenceProperties::from_state_id(args.state_id);
compute_fence_state(fence_props, args.world, args.block, args.position)
}
fn is_pathfindable(&self, _state: &BlockState, _computation_type: PathComputationType) -> bool {
false
}
}
pub fn compute_fence_state(

View File

@ -1,8 +1,8 @@
use crate::block::registry::BlockActionResult;
use crate::block::{BlockBehaviour, RandomTickArgs, UseWithItemArgs};
use crate::block::{BlockBehaviour, PathComputationType, RandomTickArgs, UseWithItemArgs};
use pumpkin_data::dimension::Dimension;
use pumpkin_data::flower_pot_transformations::get_potted_item;
use pumpkin_data::{Block, BlockId};
use pumpkin_data::{Block, BlockId, BlockState};
use pumpkin_macros::pumpkin_block_from_tag;
use pumpkin_world::world::BlockFlags;
@ -61,4 +61,8 @@ impl BlockBehaviour for FlowerPotBlock {
);
}
}
fn is_pathfindable(&self, _state: &BlockState, _computation_type: PathComputationType) -> bool {
false
}
}

View File

@ -1,19 +1,15 @@
use crate::block::GetStateForNeighborUpdateArgs;
use crate::block::OnPlaceArgs;
use pumpkin_data::BlockDirection;
use pumpkin_data::BlockStateId;
use pumpkin_data::HorizontalFacingExt;
use crate::block::{
BlockBehaviour, GetStateForNeighborUpdateArgs, OnPlaceArgs, PathComputationType,
};
use crate::world::World;
use pumpkin_data::block_properties::HorizontalFacing;
use pumpkin_data::tag::Taggable;
use pumpkin_data::{Block, tag};
use pumpkin_data::{Block, BlockDirection, BlockState, BlockStateId, HorizontalFacingExt, tag};
use pumpkin_macros::pumpkin_block_from_tag;
use pumpkin_util::math::position::BlockPos;
type GlassPaneProperties = pumpkin_data::block_properties::OakFenceLikeProperties;
use crate::block::BlockBehaviour;
use crate::world::World;
#[pumpkin_block_from_tag("c:glass_panes")]
pub struct GlassPaneBlock;
@ -32,6 +28,10 @@ impl BlockBehaviour for GlassPaneBlock {
let pane_props = GlassPaneProperties::from_state_id(args.state_id);
compute_pane_state(pane_props, args.world, args.block, args.position)
}
fn is_pathfindable(&self, _state: &BlockState, _computation_type: PathComputationType) -> bool {
false
}
}
pub fn compute_pane_state(

View File

@ -17,10 +17,12 @@ use pumpkin_util::text::TextComponent;
use pumpkin_world::inventory::SimpleInventory;
use pumpkin_world::world::BlockAccessor;
use crate::block::BlockBehaviour;
use crate::block::CanPlaceAtArgs;
use crate::block::registry::BlockActionResult;
use crate::block::{GetStateForNeighborUpdateArgs, NormalUseArgs, OnPlaceArgs};
use crate::block::{
BlockBehaviour, CanPlaceAtArgs, GetStateForNeighborUpdateArgs, NormalUseArgs, OnPlaceArgs,
PathComputationType,
};
use pumpkin_data::BlockState;
use super::abstract_wall_mounting::WallMountedBlock;
@ -63,6 +65,10 @@ impl BlockBehaviour for GrindstoneBlock {
) -> BlockStateId {
WallMountedBlock::get_state_for_neighbor_update(self, args)
}
fn is_pathfindable(&self, _state: &BlockState, _computation_type: PathComputationType) -> bool {
false
}
}
impl WallMountedBlock for GrindstoneBlock {

View File

@ -2,7 +2,9 @@ use std::sync::Arc;
use std::sync::Mutex;
use crate::block::blocks::redstone::block_receives_redstone_power;
use crate::block::{GetComparatorOutputArgs, OnNeighborUpdateArgs, OnPlaceArgs, PlacedArgs};
use crate::block::{
GetComparatorOutputArgs, OnNeighborUpdateArgs, OnPlaceArgs, PathComputationType, PlacedArgs,
};
use crate::block::{
registry::BlockActionResult,
{BlockBehaviour, NormalUseArgs},
@ -10,9 +12,8 @@ use crate::block::{
use crate::world::World;
use crate::block::entities::hopper::HopperBlockEntity;
use pumpkin_data::BlockStateId;
use pumpkin_data::block_properties::FacingHopper;
use pumpkin_data::{Block, BlockDirection, translation};
use pumpkin_data::{Block, BlockDirection, BlockState, BlockStateId, translation};
use pumpkin_inventory::generic_container_screen_handler::create_hopper;
use pumpkin_inventory::player::player_inventory::PlayerInventory;
use pumpkin_inventory::screen_handler::{
@ -111,6 +112,10 @@ impl BlockBehaviour for HopperBlock {
None
}
}
fn is_pathfindable(&self, _state: &BlockState, _computation_type: PathComputationType) -> bool {
false
}
}
fn check_powered_state(world: &Arc<World>, pos: &BlockPos, state_id: BlockStateId, block: &Block) {

View File

@ -1,19 +1,15 @@
use crate::block::GetStateForNeighborUpdateArgs;
use crate::block::OnPlaceArgs;
use pumpkin_data::BlockDirection;
use pumpkin_data::BlockStateId;
use pumpkin_data::HorizontalFacingExt;
use crate::block::{
BlockBehaviour, GetStateForNeighborUpdateArgs, OnPlaceArgs, PathComputationType,
};
use crate::world::World;
use pumpkin_data::block_properties::HorizontalFacing;
use pumpkin_data::tag::Taggable;
use pumpkin_data::{Block, tag};
use pumpkin_data::{Block, BlockDirection, BlockState, BlockStateId, HorizontalFacingExt, tag};
use pumpkin_macros::pumpkin_block;
use pumpkin_util::math::position::BlockPos;
type IronBarsProperties = pumpkin_data::block_properties::OakFenceLikeProperties;
use crate::block::BlockBehaviour;
use crate::world::World;
#[pumpkin_block("minecraft:iron_bars")]
pub struct IronBarsBlock;
@ -32,6 +28,10 @@ impl BlockBehaviour for IronBarsBlock {
let bars_props = IronBarsProperties::from_state_id(args.state_id);
compute_bars_state(bars_props, args.world, args.block, args.position)
}
fn is_pathfindable(&self, _state: &BlockState, _computation_type: PathComputationType) -> bool {
false
}
}
pub fn compute_bars_state(

View File

@ -1,10 +1,10 @@
use crate::block::{
BlockBehaviour, CanPlaceAtArgs, GetStateForNeighborUpdateArgs, OnPlaceArgs, OnScheduledTickArgs,
BlockBehaviour, CanPlaceAtArgs, GetStateForNeighborUpdateArgs, OnPlaceArgs,
OnScheduledTickArgs, PathComputationType,
};
use crate::world::World;
use pumpkin_data::BlockStateId;
use pumpkin_data::tag::Taggable;
use pumpkin_data::{BlockDirection, tag};
use pumpkin_data::{BlockDirection, BlockState, BlockStateId, tag};
use pumpkin_macros::pumpkin_block_from_tag;
use pumpkin_util::math::position::BlockPos;
use pumpkin_world::tick::TickPriority;
@ -48,6 +48,10 @@ impl BlockBehaviour for LanternBlock {
.break_block(args.position, None, BlockFlags::empty());
}
}
fn is_pathfindable(&self, _state: &BlockState, _computation_type: PathComputationType) -> bool {
false
}
}
fn can_place_at(world: &World, position: &BlockPos) -> bool {

View File

@ -6,7 +6,7 @@ use crate::block::registry::BlockActionResult;
use crate::block::{
BlockBehaviour, BrokenArgs, EmitsRedstonePowerArgs, GetComparatorOutputArgs,
GetRedstonePowerArgs, NormalUseArgs, OnPlaceArgs, OnScheduledTickArgs, OnStateReplacedArgs,
PlacedArgs, UseWithItemArgs,
PathComputationType, PlacedArgs, UseWithItemArgs,
};
use crate::entity::Entity;
use crate::entity::item::ItemEntity;
@ -16,7 +16,7 @@ use pumpkin_data::entity::EntityType;
use pumpkin_data::sound::{Sound, SoundCategory};
use pumpkin_data::tag::Taggable;
use pumpkin_data::world::WorldEvent;
use pumpkin_data::{Block, BlockDirection, BlockStateId, tag, translation};
use pumpkin_data::{Block, BlockDirection, BlockState, BlockStateId, tag, translation};
use pumpkin_inventory::lectern_screen_handler::{LecternController, LecternScreenHandler};
use pumpkin_inventory::player::player_inventory::PlayerInventory;
use pumpkin_inventory::screen_handler::{
@ -296,4 +296,8 @@ impl BlockBehaviour for LecternBlock {
Some(0)
}
}
fn is_pathfindable(&self, _state: &BlockState, _computation_type: PathComputationType) -> bool {
false
}
}

View File

@ -17,7 +17,7 @@ use rustc_hash::FxHashMap;
use crate::{
block::{
BlockBehaviour, BlockMetadata, BrokenArgs, OnNeighborUpdateArgs, OnPlaceArgs,
OnSyncedBlockEventArgs, PlacedArgs,
OnSyncedBlockEventArgs, PathComputationType, PlacedArgs,
blocks::{piston::piston_head::PistonHeadProperties, redstone::is_emitting_redstone_power},
},
world::World,
@ -121,6 +121,10 @@ impl BlockBehaviour for PistonBlock {
let block = Block::from_id(block_id);
Self::handle_synced_block_event(block, args.world, args.position, args.r#type, args.data)
}
fn is_pathfindable(&self, _state: &BlockState, _computation_type: PathComputationType) -> bool {
false
}
}
impl PistonBlock {

View File

@ -1,9 +1,8 @@
use pumpkin_data::{Block, FacingExt};
use pumpkin_data::{Block, BlockState, FacingExt};
use pumpkin_macros::pumpkin_block;
use pumpkin_world::world::BlockFlags;
use crate::block::BlockBehaviour;
use crate::block::BrokenArgs;
use crate::block::{BlockBehaviour, BrokenArgs, PathComputationType};
use super::piston::PistonProps;
@ -29,4 +28,8 @@ impl BlockBehaviour for PistonExtensionBlock {
}
}
}
fn is_pathfindable(&self, _state: &BlockState, _computation_type: PathComputationType) -> bool {
false
}
}

View File

@ -1,11 +1,10 @@
use pumpkin_data::block_properties::Facing;
use pumpkin_data::{Block, FacingExt};
use pumpkin_data::{Block, BlockState, FacingExt};
use pumpkin_macros::pumpkin_block;
use pumpkin_world::world::BlockFlags;
use crate::block::BlockBehaviour;
use crate::block::blocks::piston::piston::try_move;
use crate::block::{BrokenArgs, OnNeighborUpdateArgs};
use crate::block::{BlockBehaviour, BrokenArgs, OnNeighborUpdateArgs, PathComputationType};
use super::piston::PistonProps;
@ -54,4 +53,8 @@ impl BlockBehaviour for PistonHeadBlock {
}
}
}
fn is_pathfindable(&self, _state: &BlockState, _computation_type: PathComputationType) -> bool {
false
}
}

View File

@ -1,20 +1,19 @@
use std::sync::Arc;
use pumpkin_data::BlockStateId;
use pumpkin_data::block_properties::{BambooLeaves, BambooLikeProperties};
use pumpkin_data::tag::Block::MINECRAFT_SUPPORTS_BAMBOO;
use pumpkin_data::tag::Taggable;
use pumpkin_data::tag::{self};
use pumpkin_data::{Block, BlockDirection};
use pumpkin_data::{Block, BlockDirection, BlockState, BlockStateId};
use pumpkin_macros::pumpkin_block;
use pumpkin_util::math::position::BlockPos;
use pumpkin_world::tick::TickPriority;
use pumpkin_world::world::{BlockAccessor, BlockFlags};
use rand::RngExt;
use crate::block::{BlockBehaviour, CanPlaceAtArgs, blocks::plant::PlantBlockBase};
use crate::block::{
GetStateForNeighborUpdateArgs, OnPlaceArgs, OnScheduledTickArgs, RandomTickArgs,
BlockBehaviour, CanPlaceAtArgs, GetStateForNeighborUpdateArgs, OnPlaceArgs,
OnScheduledTickArgs, PathComputationType, RandomTickArgs, blocks::plant::PlantBlockBase,
};
use crate::world::World;
@ -111,6 +110,10 @@ impl BlockBehaviour for BambooBlock {
update_leaves_and_grow(args.world, args.position);
}
}
fn is_pathfindable(&self, _state: &BlockState, _computation_type: PathComputationType) -> bool {
false
}
}
fn update_leaves_and_grow(world: &Arc<World>, position: &BlockPos) {

View File

@ -1,8 +1,7 @@
use pumpkin_data::BlockStateId;
use pumpkin_data::block_properties::CactusLikeProperties;
use pumpkin_data::damage::DamageType;
use pumpkin_data::tag::Taggable;
use pumpkin_data::{Block, BlockDirection, tag};
use pumpkin_data::{Block, BlockDirection, BlockState, BlockStateId, tag};
use pumpkin_macros::pumpkin_block;
use pumpkin_util::math::position::BlockPos;
use pumpkin_world::tick::TickPriority;
@ -11,7 +10,7 @@ use rand::RngExt;
use crate::block::{
BlockBehaviour, CanPlaceAtArgs, GetStateForNeighborUpdateArgs, OnEntityCollisionArgs,
OnScheduledTickArgs, RandomTickArgs,
OnScheduledTickArgs, PathComputationType, RandomTickArgs,
};
#[pumpkin_block("minecraft:cactus")]
@ -94,6 +93,10 @@ impl BlockBehaviour for CactusBlock {
fn can_place_at(&self, args: CanPlaceAtArgs<'_>) -> bool {
can_place_at(args.block_accessor, args.position)
}
fn is_pathfindable(&self, _state: &BlockState, _computation_type: PathComputationType) -> bool {
false
}
}
fn can_place_at(world: &dyn BlockAccessor, block_pos: &BlockPos) -> bool {

View File

@ -1,5 +1,5 @@
use pumpkin_data::{
Block, BlockDirection, BlockStateId,
Block, BlockDirection, BlockState, BlockStateId,
block_properties::BrownMushroomBlockLikeProperties,
tag::{self, Taggable},
};
@ -11,7 +11,8 @@ use pumpkin_world::{
};
use crate::block::{
BlockBehaviour, CanPlaceAtArgs, GetStateForNeighborUpdateArgs, OnPlaceArgs, OnScheduledTickArgs,
BlockBehaviour, CanPlaceAtArgs, GetStateForNeighborUpdateArgs, OnPlaceArgs,
OnScheduledTickArgs, PathComputationType,
};
#[pumpkin_block("minecraft:chorus_plant")]
@ -65,6 +66,10 @@ impl BlockBehaviour for ChorusPlantBlock {
.break_block(args.position, None, BlockFlags::empty());
}
}
fn is_pathfindable(&self, _state: &BlockState, _computation_type: PathComputationType) -> bool {
false
}
}
#[must_use]

View File

@ -9,7 +9,7 @@ use pumpkin_world::world::{BlockAccessor, BlockFlags};
use crate::block::{
BlockBehaviour, BonemealArgs, CanPlaceAtArgs, GetStateForNeighborUpdateArgs, OnPlaceArgs,
RandomTickArgs,
PathComputationType, RandomTickArgs,
};
use crate::entity::EntityBase;
@ -137,4 +137,8 @@ impl BlockBehaviour for CocoaBlock {
props.facing = mirror.mirror_horizontal(props.facing);
BlockState::from_id(props.to_state_id(block))
}
fn is_pathfindable(&self, _state: &BlockState, _computation_type: PathComputationType) -> bool {
false
}
}

View File

@ -3,14 +3,13 @@ use crate::block::blocks::plant::PlantBlockBase;
use crate::block::registry::BlockActionResult;
use crate::block::{
BlockBehaviour, CanPlaceAtArgs, CanUpdateAtArgs, GetStateForNeighborUpdateArgs, OnPlaceArgs,
UseWithItemArgs,
PathComputationType, UseWithItemArgs,
};
use crate::entity::EntityBase;
use pumpkin_data::BlockStateId;
use pumpkin_data::entity::EntityPose;
use pumpkin_data::item::Item;
use pumpkin_data::tag::Taggable;
use pumpkin_data::{Block, BlockDirection, tag};
use pumpkin_data::{Block, BlockDirection, BlockState, BlockStateId, tag};
use pumpkin_macros::pumpkin_block;
use pumpkin_util::math::position::BlockPos;
use pumpkin_world::world::BlockFlags;
@ -130,6 +129,10 @@ impl BlockBehaviour for SeaPickleBlock {
args.state_id,
)
}
fn is_pathfindable(&self, _state: &BlockState, _computation_type: PathComputationType) -> bool {
false
}
}
impl PlantBlockBase for SeaPickleBlock {}

View File

@ -1,3 +1,4 @@
use pumpkin_data::BlockState;
use pumpkin_data::data_component_impl::EquipmentSlot;
use pumpkin_data::entity::EntityType;
use pumpkin_data::item::Item;
@ -9,7 +10,7 @@ use pumpkin_util::math::position::BlockPos;
use pumpkin_util::math::vector3::Vector3;
use pumpkin_world::world::BlockFlags;
use crate::block::{BlockBehaviour, OnEntityCollisionArgs, OnLandedUponArgs};
use crate::block::{BlockBehaviour, OnEntityCollisionArgs, OnLandedUponArgs, PathComputationType};
use crate::entity::EntityBase;
#[pumpkin_block("minecraft:powder_snow")]
@ -117,4 +118,8 @@ impl BlockBehaviour for PowderSnowBlock {
entity.extinguish();
}
fn is_pathfindable(&self, _state: &BlockState, _computation_type: PathComputationType) -> bool {
true
}
}

View File

@ -5,16 +5,15 @@ use crate::block::entities::bell::BellBlockEntity;
use crate::block::registry::BlockActionResult;
use crate::block::{
BlockBehaviour, BlockHitResult, BrokenArgs, CanPlaceAtArgs, NormalUseArgs,
OnNeighborUpdateArgs, OnPlaceArgs, PlacedArgs,
OnNeighborUpdateArgs, OnPlaceArgs, PathComputationType, PlacedArgs,
};
use crate::world::World;
use pumpkin_data::BlockStateId;
use pumpkin_data::block_properties::HorizontalFacing;
use pumpkin_data::block_properties::{AttachFace, BellAttachment, BellLikeProperties};
use pumpkin_data::sound::Sound;
use pumpkin_data::sound::SoundCategory;
use pumpkin_data::tag::Taggable;
use pumpkin_data::{Block, BlockDirection};
use pumpkin_data::{Block, BlockDirection, BlockState, BlockStateId};
use pumpkin_data::{HorizontalFacingExt, tag};
use pumpkin_macros::pumpkin_block;
use pumpkin_util::math::position::BlockPos;
@ -215,4 +214,8 @@ impl BlockBehaviour for BellBlock {
}
}
}
fn is_pathfindable(&self, _state: &BlockState, _computation_type: PathComputationType) -> bool {
false
}
}

View File

@ -2,10 +2,11 @@ use std::sync::Arc;
use crate::block::{
BlockBehaviour, EmitsRedstonePowerArgs, GetRedstonePowerArgs, OnPlaceArgs, OnScheduledTickArgs,
PathComputationType,
};
use crate::world::World;
use pumpkin_data::block_properties::LightningRodLikeProperties;
use pumpkin_data::{BlockStateId, FacingExt};
use pumpkin_data::{BlockState, BlockStateId, FacingExt};
use pumpkin_macros::pumpkin_block;
use pumpkin_util::math::position::BlockPos;
use pumpkin_world::tick::TickPriority;
@ -75,4 +76,8 @@ impl BlockBehaviour for LightningRodBlock {
);
}
}
fn is_pathfindable(&self, _state: &BlockState, _computation_type: PathComputationType) -> bool {
false
}
}

View File

@ -4,14 +4,14 @@ use crate::block::entities::calibrated_sculk_sensor::CalibratedSculkSensorBlockE
use crate::block::entities::sculk_sensor::SculkSensorBlockEntity;
use crate::block::{
BlockBehaviour, BlockMetadata, EmitsRedstonePowerArgs, GetComparatorOutputArgs,
GetRedstonePowerArgs, OnPlaceArgs, OnScheduledTickArgs, PlacedArgs,
GetRedstonePowerArgs, OnPlaceArgs, OnScheduledTickArgs, PathComputationType, PlacedArgs,
};
use crate::world::World;
use pumpkin_data::block_properties::{
CalibratedSculkSensorLikeProperties, HorizontalFacing, SculkSensorLikeProperties,
SculkSensorPhase,
};
use pumpkin_data::{Block, BlockDirection, BlockId, BlockStateId};
use pumpkin_data::{Block, BlockDirection, BlockId, BlockState, BlockStateId};
use pumpkin_util::math::position::BlockPos;
use pumpkin_world::tick::TickPriority;
use pumpkin_world::world::BlockFlags;
@ -224,4 +224,8 @@ impl BlockBehaviour for SculkSensorBlock {
}
}
}
fn is_pathfindable(&self, _state: &BlockState, _computation_type: PathComputationType) -> bool {
false
}
}

View File

@ -2,12 +2,12 @@ use pumpkin_data::block_properties::RespawnAnchorLikeProperties;
use pumpkin_data::dimension::Dimension;
use pumpkin_data::item::Item;
use pumpkin_data::sound::{Sound, SoundCategory};
use pumpkin_data::translation;
use pumpkin_data::{BlockState, translation};
use pumpkin_macros::pumpkin_block;
use pumpkin_world::world::BlockFlags;
use crate::block::registry::BlockActionResult;
use crate::block::{BlockBehaviour, NormalUseArgs, UseWithItemArgs};
use crate::block::{BlockBehaviour, NormalUseArgs, PathComputationType, UseWithItemArgs};
use crate::entity::EntityBase;
#[pumpkin_block("minecraft:respawn_anchor")]
@ -91,4 +91,8 @@ impl BlockBehaviour for RespawnAnchorBlock {
BlockActionResult::SuccessServer
}
fn is_pathfindable(&self, _state: &BlockState, _computation_type: PathComputationType) -> bool {
false
}
}

View File

@ -1,9 +1,9 @@
use pumpkin_data::BlockStateId;
use pumpkin_data::block_properties::AcaciaShelfLikeProperties;
use pumpkin_data::{BlockState, BlockStateId};
use pumpkin_macros::pumpkin_block_from_tag;
use crate::block::entities::shelf::ShelfBlockEntity;
use crate::block::{BlockBehaviour, OnPlaceArgs, PlacedArgs};
use crate::block::{BlockBehaviour, OnPlaceArgs, PathComputationType, PlacedArgs};
use crate::entity::EntityBase;
use std::sync::Arc;
@ -26,4 +26,8 @@ impl BlockBehaviour for ShelfBlock {
args.world.add_block_entity(Arc::new(entity));
}
}
fn is_pathfindable(&self, state: &BlockState, computation_type: PathComputationType) -> bool {
computation_type == PathComputationType::Water && state.is_waterlogged()
}
}

View File

@ -1,11 +1,14 @@
use crate::block::blocks::redstone::block_receives_redstone_power;
use crate::block::{BlockBehaviour, BlockMetadata, OnNeighborUpdateArgs, OnPlaceArgs};
use crate::block::entities::skull::SkullBlockEntity;
use crate::block::{
BlockBehaviour, BlockMetadata, OnNeighborUpdateArgs, OnPlaceArgs, PathComputationType,
PlacedArgs,
};
use crate::entity::EntityBase;
use pumpkin_data::BlockId;
use pumpkin_data::BlockStateId;
use pumpkin_data::block_properties::SkeletonSkullLikeProperties;
use pumpkin_data::{BlockId, BlockState, BlockStateId};
use pumpkin_world::world::BlockFlags;
type SkeletonSkullLikeProperties = pumpkin_data::block_properties::SkeletonSkullLikeProperties;
use std::sync::Arc;
pub struct SkullBlock;
@ -23,10 +26,6 @@ impl BlockMetadata for SkullBlock {
}
}
use crate::block::PlacedArgs;
use crate::block::entities::skull::SkullBlockEntity;
use std::sync::Arc;
impl BlockBehaviour for SkullBlock {
fn placed(&self, args: PlacedArgs<'_>) {
{
@ -57,4 +56,8 @@ impl BlockBehaviour for SkullBlock {
}
}
}
fn is_pathfindable(&self, _state: &BlockState, _computation_type: PathComputationType) -> bool {
false
}
}

View File

@ -1,12 +1,10 @@
use pumpkin_data::BlockDirection;
use pumpkin_data::BlockStateId;
use pumpkin_data::block_properties::SlabType;
use pumpkin_data::{BlockDirection, BlockState, BlockStateId};
use pumpkin_macros::pumpkin_block_from_tag;
use crate::block::BlockBehaviour;
use crate::block::BlockIsReplacing;
use crate::block::CanUpdateAtArgs;
use crate::block::OnPlaceArgs;
use crate::block::{
BlockBehaviour, BlockIsReplacing, CanUpdateAtArgs, OnPlaceArgs, PathComputationType,
};
type SlabProperties = pumpkin_data::block_properties::ResinBrickSlabLikeProperties;
@ -49,4 +47,11 @@ impl BlockBehaviour for SlabBlock {
},
}
}
fn is_pathfindable(&self, state: &BlockState, computation_type: PathComputationType) -> bool {
match computation_type {
PathComputationType::Water => state.is_waterlogged(),
PathComputationType::Land | PathComputationType::Air => false,
}
}
}

View File

@ -2,12 +2,14 @@ use pumpkin_data::block_properties::SnifferEggLikeProperties;
use pumpkin_data::item::Item;
use pumpkin_data::item_stack::ItemStack;
use pumpkin_data::sound::{Sound, SoundCategory};
use pumpkin_data::{BlockId, BlockStateId};
use pumpkin_data::{BlockId, BlockState, BlockStateId};
use pumpkin_macros::pumpkin_block;
use pumpkin_world::tick::TickPriority;
use pumpkin_world::world::BlockFlags;
use crate::block::{BlockBehaviour, BrokenArgs, OnPlaceArgs, OnScheduledTickArgs, PlacedArgs};
use crate::block::{
BlockBehaviour, BrokenArgs, OnPlaceArgs, OnScheduledTickArgs, PathComputationType, PlacedArgs,
};
#[pumpkin_block("minecraft:sniffer_egg")]
pub struct SnifferEggBlock;
@ -94,4 +96,8 @@ impl BlockBehaviour for SnifferEggBlock {
.drop_stack(args.position, ItemStack::new(1, &Item::SNIFFER_EGG));
}
}
fn is_pathfindable(&self, _state: &BlockState, _computation_type: PathComputationType) -> bool {
false
}
}

View File

@ -1,5 +1,7 @@
use pumpkin_data::tag::Taggable;
use pumpkin_data::{Block, BlockStateId, block_properties::SnowLikeProperties, item::Item, tag};
use pumpkin_data::{
Block, BlockState, BlockStateId, block_properties::SnowLikeProperties, item::Item, tag,
};
use pumpkin_macros::pumpkin_block;
use pumpkin_util::math::position::BlockPos;
use pumpkin_world::{
@ -9,7 +11,7 @@ use pumpkin_world::{
use crate::block::{
BlockBehaviour, GetStateForNeighborUpdateArgs, OnPlaceArgs, OnScheduledTickArgs,
RandomTickArgs, UseWithItemArgs, registry::BlockActionResult,
PathComputationType, RandomTickArgs, UseWithItemArgs, registry::BlockActionResult,
};
#[pumpkin_block("minecraft:snow")]
@ -90,6 +92,11 @@ impl BlockBehaviour for LayeredSnowBlock {
}
args.state_id
}
fn is_pathfindable(&self, state: &BlockState, computation_type: PathComputationType) -> bool {
computation_type == PathComputationType::Land
&& SnowLikeProperties::from_state_id(state.id).layers < 5
}
}
fn can_place_at(block_accessor: &dyn BlockAccessor, position: &BlockPos) -> bool {

View File

@ -8,9 +8,7 @@ use pumpkin_macros::pumpkin_block_from_tag;
use pumpkin_util::math::position::BlockPos;
use pumpkin_world::world::BlockFlags;
use crate::block::BlockBehaviour;
use crate::block::OnNeighborUpdateArgs;
use crate::block::OnPlaceArgs;
use crate::block::{BlockBehaviour, OnNeighborUpdateArgs, OnPlaceArgs, PathComputationType};
use crate::world::World;
type StairsProperties = pumpkin_data::block_properties::OakStairsLikeProperties;
@ -117,6 +115,10 @@ impl BlockBehaviour for StairBlock {
BlockState::from_id(state_id)
}
fn is_pathfindable(&self, _state: &BlockState, _computation_type: PathComputationType) -> bool {
false
}
}
fn compute_stair_shape(

View File

@ -1,7 +1,7 @@
use crate::block::registry::BlockActionResult;
use crate::block::{BlockBehaviour, NormalUseArgs};
use crate::block::{BlockBehaviour, NormalUseArgs, PathComputationType};
use pumpkin_data::translation;
use pumpkin_data::{BlockState, translation};
use pumpkin_inventory::player::player_inventory::PlayerInventory;
use pumpkin_inventory::screen_handler::{
InventoryPlayer, ScreenHandlerFactory, SharedScreenHandler,
@ -28,6 +28,10 @@ impl BlockBehaviour for StonecutterBlock {
BlockActionResult::Success
}
fn is_pathfindable(&self, _state: &BlockState, _computation_type: PathComputationType) -> bool {
false
}
}
struct StonecutterScreenFactory;

View File

@ -1,15 +1,16 @@
use crate::block::blocks::redstone::block_receives_redstone_power;
use crate::block::registry::BlockActionResult;
use crate::block::{BlockBehaviour, NormalUseArgs, OnNeighborUpdateArgs, OnPlaceArgs};
use crate::block::{
BlockBehaviour, NormalUseArgs, OnNeighborUpdateArgs, OnPlaceArgs, PathComputationType,
};
use crate::entity::EntityBase;
use crate::entity::player::Player;
use crate::world::World;
use pumpkin_data::BlockDirection;
use pumpkin_data::BlockStateId;
use pumpkin_data::block_properties::Half;
use pumpkin_data::sound::{Sound, SoundCategory};
use pumpkin_data::tag::Taggable;
use pumpkin_data::{Block, tag};
use pumpkin_data::{Block, BlockState, BlockStateId, tag};
use pumpkin_macros::pumpkin_block_from_tag;
use pumpkin_util::math::position::BlockPos;
use pumpkin_world::world::BlockFlags;
@ -135,4 +136,12 @@ impl BlockBehaviour for TrapDoorBlock {
);
}
}
fn is_pathfindable(&self, state: &BlockState, computation_type: PathComputationType) -> bool {
let props = TrapDoorProperties::from_state_id(state.id);
match computation_type {
PathComputationType::Land | PathComputationType::Air => props.open,
PathComputationType::Water => props.waterlogged,
}
}
}

View File

@ -1,20 +1,17 @@
use crate::block::GetStateForNeighborUpdateArgs;
use crate::block::OnPlaceArgs;
use pumpkin_data::BlockDirection;
use pumpkin_data::BlockState;
use pumpkin_data::BlockStateId;
use crate::block::{
BlockBehaviour, GetStateForNeighborUpdateArgs, OnPlaceArgs, PathComputationType,
};
use crate::world::World;
use pumpkin_data::block_properties::EastWall;
use pumpkin_data::block_properties::HorizontalFacing;
use pumpkin_data::block_properties::NorthWall;
use pumpkin_data::block_properties::SouthWall;
use pumpkin_data::block_properties::WestWall;
use pumpkin_data::tag::Taggable;
use pumpkin_data::{Block, tag};
use pumpkin_data::{Block, BlockDirection, BlockState, BlockStateId, tag};
use pumpkin_macros::pumpkin_block_from_tag;
use pumpkin_util::math::position::BlockPos;
use crate::block::BlockBehaviour;
use crate::world::World;
type FenceGateProperties = pumpkin_data::block_properties::OakFenceGateLikeProperties;
type FenceLikeProperties = pumpkin_data::block_properties::OakFenceLikeProperties;
type WallProperties = pumpkin_data::block_properties::ResinBrickWallLikeProperties;
@ -37,6 +34,10 @@ impl BlockBehaviour for WallBlock {
let wall_props = WallProperties::from_state_id(args.state_id);
compute_wall_state(wall_props, args.world, args.block, args.position)
}
fn is_pathfindable(&self, _state: &BlockState, _computation_type: PathComputationType) -> bool {
false
}
}
pub fn compute_wall_state(

View File

@ -20,7 +20,7 @@ use crate::block::registry::BlockActionResult;
use crate::block::{
BlockBehaviour, BlockMetadata, BrokenArgs, CanPlaceAtArgs, CanUpdateAtArgs,
GetStateForNeighborUpdateArgs, NormalUseArgs, OnNeighborUpdateArgs, OnPlaceArgs,
OnStateReplacedArgs, PlacedArgs, RandomTickArgs,
OnStateReplacedArgs, PathComputationType, PlacedArgs, RandomTickArgs,
};
use crate::world::World;
@ -710,6 +710,10 @@ impl BlockBehaviour for WeatheringCopperStairBlock {
fn random_tick(&self, args: RandomTickArgs<'_>) {
change_over_time(args.world, args.position, args.block);
}
fn is_pathfindable(&self, state: &BlockState, computation_type: PathComputationType) -> bool {
StairBlock.is_pathfindable(state, computation_type)
}
}
/// Weathering copper trapdoor blocks.
@ -768,6 +772,10 @@ impl BlockBehaviour for WeatheringCopperTrapDoorBlock {
fn random_tick(&self, args: RandomTickArgs<'_>) {
change_over_time(args.world, args.position, args.block);
}
fn is_pathfindable(&self, state: &BlockState, computation_type: PathComputationType) -> bool {
TrapDoorBlock.is_pathfindable(state, computation_type)
}
}
/// Weathering copper slab blocks.
@ -822,6 +830,10 @@ impl BlockBehaviour for WeatheringCopperSlabBlock {
fn random_tick(&self, args: RandomTickArgs<'_>) {
change_over_time(args.world, args.position, args.block);
}
fn is_pathfindable(&self, state: &BlockState, computation_type: PathComputationType) -> bool {
SlabBlock.is_pathfindable(state, computation_type)
}
}
/// Weathering copper door blocks.
@ -907,6 +919,10 @@ impl BlockBehaviour for WeatheringCopperDoorBlock {
change_over_time(args.world, args.position, args.block);
}
}
fn is_pathfindable(&self, state: &BlockState, computation_type: PathComputationType) -> bool {
DoorBlock.is_pathfindable(state, computation_type)
}
}
/// Weathering copper grate blocks.

View File

@ -1,9 +1,14 @@
use pumpkin_data::{Block, BlockDirection, BlockStateId, entity::EntityType, world::WorldEvent};
use pumpkin_data::{
Block, BlockDirection, BlockState, BlockStateId, entity::EntityType, world::WorldEvent,
};
use pumpkin_macros::pumpkin_block;
use pumpkin_world::world::BlockFlags;
use crate::{
block::{BlockBehaviour, OnPlaceArgs, PlacedArgs, blocks::skull_block::SkullBlock},
block::{
BlockBehaviour, OnPlaceArgs, PathComputationType, PlacedArgs,
blocks::skull_block::SkullBlock,
},
entity::{Entity, boss::wither::WitherEntity},
};
@ -92,4 +97,8 @@ impl BlockBehaviour for WitherSkeletonSkullBlock {
}
}
}
fn is_pathfindable(&self, _state: &BlockState, _computation_type: PathComputationType) -> bool {
false
}
}

View File

@ -1,3 +1,5 @@
use pumpkin_data::fluid::Fluid;
use pumpkin_data::tag::{self, Taggable};
use pumpkin_data::{Block, BlockId, BlockState};
use pumpkin_data::BlockStateId;
@ -28,6 +30,13 @@ use pumpkin_util::math::boundingbox::BoundingBox;
use pumpkin_util::math::vector3::Vector3;
use pumpkin_world::world::{BlockAccessor, BlockFlags};
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum PathComputationType {
Land,
Water,
Air,
}
pub trait BlockMetadata {
fn ids() -> Box<[BlockId]>;
}
@ -185,6 +194,17 @@ pub trait BlockBehaviour: Send + Sync {
) -> &'static BlockState {
block.rotate(state_id, rotation)
}
fn is_pathfindable(&self, state: &BlockState, computation_type: PathComputationType) -> bool {
match computation_type {
PathComputationType::Water => {
state.is_waterlogged()
|| Fluid::from_state_id(state.id)
.is_some_and(|f| f.has_tag(&tag::Fluid::MINECRAFT_WATER))
}
PathComputationType::Land | PathComputationType::Air => !state.is_full_cube(),
}
}
}
#[derive(Clone, Copy)]

View File

@ -160,7 +160,8 @@ use crate::block::fluid::water::FlowingWater;
use crate::block::{
BlockBehaviour, BlockHitResult, BlockMetadata, BonemealArgs, FluidMetadata,
GetInsideCollisionShapeArgs, OnEntityCollisionArgs, OnEntityStepArgs, OnLandedUponArgs,
OnProjectileHitArgs, UpdateEntityMovementAfterFallOnArgs, stop_vertical_movement_after_fall,
OnProjectileHitArgs, PathComputationType, UpdateEntityMovementAfterFallOnArgs,
stop_vertical_movement_after_fall,
};
use crate::entity::EntityBase;
use crate::entity::player::Player;
@ -172,6 +173,7 @@ use pumpkin_data::data_component_impl::EquipmentSlot;
use pumpkin_data::fluid::Fluid;
use pumpkin_data::item::Item;
use pumpkin_data::item_stack::ItemStack;
use pumpkin_data::tag::{self, Taggable};
use pumpkin_data::{Block, BlockDirection, BlockId, BlockState};
use pumpkin_protocol::java::server::play::SUseItemOn;
use pumpkin_util::math::boundingbox::BoundingBox;
@ -1385,4 +1387,24 @@ impl BlockRegistry {
|pumpkin_block| pumpkin_block.rotate(block, state_id, rotation),
)
}
#[must_use]
pub fn is_pathfindable(
&self,
block: &Block,
state: &BlockState,
computation_type: PathComputationType,
) -> bool {
self.get_pumpkin_block(block.id).map_or_else(
|| match computation_type {
PathComputationType::Water => {
state.is_waterlogged()
|| Fluid::from_state_id(state.id)
.is_some_and(|f| f.has_tag(&tag::Fluid::MINECRAFT_WATER))
}
PathComputationType::Land | PathComputationType::Air => !state.is_full_cube(),
},
|pumpkin_block| pumpkin_block.is_pathfindable(state, computation_type),
)
}
}

View File

@ -0,0 +1,85 @@
use crate::entity::ai::control::Control;
use crate::entity::mob::Mob;
use pumpkin_util::math::rotate_if_necessary;
pub struct BodyRotationControl {
head_stable_time: i32,
last_stable_y_head_rot: f32,
}
impl Default for BodyRotationControl {
fn default() -> Self {
Self::new()
}
}
impl BodyRotationControl {
#[must_use]
pub const fn new() -> Self {
Self {
head_stable_time: 0,
last_stable_y_head_rot: 0.0,
}
}
pub fn client_tick(&mut self, mob: &dyn Mob) {
let mob_entity = mob.get_mob_entity();
let entity = &mob_entity.living_entity.entity;
if Self::is_moving(mob) {
entity.body_yaw.store(entity.yaw.load());
Self::rotate_head_if_necessary(mob);
self.last_stable_y_head_rot = entity.head_yaw.load();
self.head_stable_time = 0;
} else if (entity.head_yaw.load() - self.last_stable_y_head_rot).abs() > 15.0 {
self.head_stable_time = 0;
self.last_stable_y_head_rot = entity.head_yaw.load();
Self::rotate_body_if_necessary(mob);
} else {
self.head_stable_time += 1;
if self.head_stable_time > 10 {
self.rotate_head_towards_front(mob);
}
}
}
fn rotate_body_if_necessary(mob: &dyn Mob) {
let entity = &mob.get_mob_entity().living_entity.entity;
let max_head_y_rot = mob.get_max_head_rotation();
entity.body_yaw.store(rotate_if_necessary(
entity.body_yaw.load(),
entity.head_yaw.load(),
max_head_y_rot,
));
}
fn rotate_head_if_necessary(mob: &dyn Mob) {
let entity = &mob.get_mob_entity().living_entity.entity;
let max_head_y_rot = mob.get_max_head_rotation();
entity.head_yaw.store(rotate_if_necessary(
entity.head_yaw.load(),
entity.body_yaw.load(),
max_head_y_rot,
));
}
fn rotate_head_towards_front(&self, mob: &dyn Mob) {
let time_since_starting = self.head_stable_time - 10;
let face_forward_fraction = (time_since_starting as f32 / 10.0).clamp(0.0, 1.0);
let angle_remaining = mob.get_max_head_rotation() * (1.0 - face_forward_fraction);
let entity = &mob.get_mob_entity().living_entity.entity;
entity.body_yaw.store(rotate_if_necessary(
entity.body_yaw.load(),
entity.head_yaw.load(),
angle_remaining,
));
}
fn is_moving(mob: &dyn Mob) -> bool {
let entity = &mob.get_mob_entity().living_entity.entity;
let vel = entity.velocity.load();
vel.x * vel.x + vel.z * vel.z > 2.500_000_3e-7
}
}
impl Control for BodyRotationControl {}

View File

@ -0,0 +1,113 @@
use crate::entity::ai::control::move_control::Operation;
use crate::entity::ai::control::{Control, MoveControlTrait};
use crate::entity::mob::Mob;
use pumpkin_data::attributes::Attributes;
use pumpkin_util::math::vector3::Vector3;
use std::sync::atomic::Ordering;
pub struct FlyingMoveControl {
pub wanted_x: f64,
pub wanted_y: f64,
pub wanted_z: f64,
pub speed_modifier: f64,
pub max_turn: i32,
pub hovers_in_place: bool,
pub operation: Operation,
}
impl FlyingMoveControl {
#[must_use]
pub const fn new(max_turn: i32, hovers_in_place: bool) -> Self {
Self {
wanted_x: 0.0,
wanted_y: 0.0,
wanted_z: 0.0,
speed_modifier: 0.0,
max_turn,
hovers_in_place,
operation: Operation::Wait,
}
}
}
impl Control for FlyingMoveControl {}
impl MoveControlTrait for FlyingMoveControl {
fn tick(&mut self, mob: &dyn Mob) {
let mob_entity = mob.get_mob_entity();
let living_entity = &mob_entity.living_entity;
let entity = &living_entity.entity;
if self.operation == Operation::MoveTo {
self.operation = Operation::Wait;
entity.set_has_no_gravity(true);
let pos = entity.pos.load();
let xd = self.wanted_x - pos.x;
let yd = self.wanted_y - pos.y;
let zd = self.wanted_z - pos.z;
let dd = xd * xd + yd * yd + zd * zd;
if dd < 2.5000003E-7 {
living_entity
.movement_input
.store(Vector3::new(0.0, 0.0, 0.0));
return;
}
let y_rot_d = (zd.atan2(xd).to_degrees() as f32) - 90.0;
entity
.yaw
.store(self.change_angle(entity.yaw.load(), y_rot_d, 90.0));
let speed = if entity.on_ground.load(Ordering::Relaxed) {
let movement_speed = living_entity.get_attribute_value(&Attributes::MOVEMENT_SPEED);
(self.speed_modifier * movement_speed) as f32
} else {
let flying_speed = living_entity.get_attribute_value(&Attributes::FLYING_SPEED);
(self.speed_modifier * flying_speed) as f32
};
let sd = xd.hypot(zd);
if yd.abs() > 1.0E-5 || sd > 1.0E-5 {
let x_rot_d = -((yd.atan2(sd).to_degrees()) as f32);
entity.pitch.store(self.change_angle(
entity.pitch.load(),
x_rot_d,
self.max_turn as f32,
));
let yya = if yd > 0.0 {
speed as f64
} else {
-(speed as f64)
};
living_entity
.movement_input
.store(Vector3::new(0.0, yya, speed as f64));
} else {
living_entity
.movement_input
.store(Vector3::new(0.0, 0.0, speed as f64));
}
} else {
if !self.hovers_in_place {
entity.set_has_no_gravity(false);
}
living_entity
.movement_input
.store(Vector3::new(0.0, 0.0, 0.0));
}
}
fn set_wanted_position(&mut self, x: f64, y: f64, z: f64, speed_modifier: f64) {
self.wanted_x = x;
self.wanted_y = y;
self.wanted_z = z;
self.speed_modifier = speed_modifier;
self.operation = Operation::MoveTo;
}
fn has_wanted(&self) -> bool {
self.operation == Operation::MoveTo
}
}

View File

@ -0,0 +1,24 @@
use crate::entity::ai::control::Control;
use crate::entity::mob::Mob;
use std::sync::atomic::Ordering;
#[derive(Default)]
pub struct JumpControl {
jump: bool,
}
impl Control for JumpControl {}
impl JumpControl {
pub const fn jump(&mut self) {
self.jump = true;
}
pub fn tick(&mut self, mob: &dyn Mob) {
mob.get_mob_entity()
.living_entity
.jumping
.store(self.jump, Ordering::SeqCst);
self.jump = false;
}
}

View File

@ -1,8 +1,13 @@
use crate::entity::mob::Mob;
use pumpkin_util::math::subtract_angles;
pub mod body_rotation_control;
pub mod flying_move_control;
pub mod jump_control;
pub mod look_control;
pub mod move_control;
pub mod smooth_swimming_look_control;
pub mod smooth_swimming_move_control;
pub trait Control: Send + Sync {
fn change_angle(&self, start: f32, end: f32, max_change: f32) -> f32 {

View File

@ -0,0 +1,113 @@
use crate::entity::ai::control::Control;
use crate::entity::mob::Mob;
use pumpkin_util::math::vector3::Vector3;
use pumpkin_util::math::wrap_degrees;
pub struct SmoothSwimmingLookControl {
pub max_yaw_change: f32,
pub max_pitch_change: f32,
pub look_at_timer: i32,
pub position: Vector3<f64>,
pub max_y_rot_from_center: i32,
}
impl SmoothSwimmingLookControl {
#[must_use]
pub const fn new(max_y_rot_from_center: i32) -> Self {
Self {
max_yaw_change: 0.0,
max_pitch_change: 0.0,
look_at_timer: 0,
position: Vector3::new(0.0, 0.0, 0.0),
max_y_rot_from_center,
}
}
pub const fn look_at_with_range(
&mut self,
x: f64,
y: f64,
z: f64,
max_yaw_change: f32,
max_pitch_change: f32,
) {
self.position = Vector3::new(x, y, z);
self.max_yaw_change = max_yaw_change;
self.max_pitch_change = max_pitch_change;
self.look_at_timer = 2;
}
pub fn tick(&mut self, mob: &dyn Mob) {
let mob_entity = mob.get_mob_entity();
let entity = &mob_entity.living_entity.entity;
if self.look_at_timer > 0 {
self.look_at_timer -= 1;
if let Some(yaw) = self.get_target_yaw(mob) {
entity.head_yaw.store(self.change_angle(
entity.head_yaw.load(),
yaw + 20.0,
self.max_yaw_change,
));
}
if let Some(pitch) = self.get_target_pitch(mob) {
entity.set_pitch(self.change_angle(
entity.pitch.load(),
pitch + 10.0,
self.max_pitch_change,
));
}
} else {
let is_idle = mob_entity
.navigator
.try_lock()
.is_ok_and(|navigator| navigator.is_idle());
if is_idle {
entity.set_pitch(self.change_angle(entity.pitch.load(), 0.0, 5.0));
}
entity.head_yaw.store(self.change_angle(
entity.head_yaw.load(),
entity.body_yaw.load(),
self.max_yaw_change,
));
}
let head_diff_body = wrap_degrees(entity.head_yaw.load() - entity.body_yaw.load());
let max_rot = self.max_y_rot_from_center as f32;
if head_diff_body < -max_rot {
let body_yaw = entity.body_yaw.load();
entity.body_yaw.store(body_yaw - 4.0);
} else if head_diff_body > max_rot {
let body_yaw = entity.body_yaw.load();
entity.body_yaw.store(body_yaw + 4.0);
}
}
fn get_target_pitch(&self, mob: &dyn Mob) -> Option<f32> {
let position = self.position;
let mob_position = mob.get_entity().pos.load();
let d = position.x - mob_position.x;
let e = position.y - mob.get_entity().get_eye_y();
let f = position.z - mob_position.z;
let g = d.hypot(f);
if e.abs() <= 1.0E-5 && g.abs() <= 1.0E-5 {
None
} else {
Some(-(e.atan2(g) as f32).to_degrees())
}
}
fn get_target_yaw(&self, mob: &dyn Mob) -> Option<f32> {
let position = self.position;
let mob_position = mob.get_entity().pos.load();
let d = position.x - mob_position.x;
let e = position.z - mob_position.z;
if e.abs() <= 1.0E-5 && d.abs() <= 1.0E-5 {
None
} else {
Some((e.atan2(d) as f32).to_degrees() - 90.0)
}
}
}
impl Control for SmoothSwimmingLookControl {}

View File

@ -0,0 +1,138 @@
use crate::entity::ai::control::move_control::Operation;
use crate::entity::ai::control::{Control, MoveControlTrait};
use crate::entity::mob::Mob;
use pumpkin_data::attributes::Attributes;
use pumpkin_util::math::vector3::Vector3;
use pumpkin_util::math::wrap_degrees;
use std::sync::atomic::Ordering;
pub struct SmoothSwimmingMoveControl {
pub wanted_x: f64,
pub wanted_y: f64,
pub wanted_z: f64,
pub speed_modifier: f64,
pub max_turn_x: i32,
pub max_turn_y: i32,
pub in_water_speed_modifier: f32,
pub outside_water_speed_modifier: f32,
pub apply_gravity: bool,
pub operation: Operation,
}
impl SmoothSwimmingMoveControl {
#[must_use]
pub const fn new(
max_turn_x: i32,
max_turn_y: i32,
in_water_speed_modifier: f32,
outside_water_speed_modifier: f32,
apply_gravity: bool,
) -> Self {
Self {
wanted_x: 0.0,
wanted_y: 0.0,
wanted_z: 0.0,
speed_modifier: 0.0,
max_turn_x,
max_turn_y,
in_water_speed_modifier,
outside_water_speed_modifier,
apply_gravity,
operation: Operation::Wait,
}
}
fn get_turning_speed_factor(left_to_turn: f32) -> f32 {
1.0 - ((left_to_turn - 10.0) / 50.0).clamp(0.0, 1.0)
}
}
impl Control for SmoothSwimmingMoveControl {}
impl MoveControlTrait for SmoothSwimmingMoveControl {
fn tick(&mut self, mob: &dyn Mob) {
let mob_entity = mob.get_mob_entity();
let living_entity = &mob_entity.living_entity;
let entity = &living_entity.entity;
if self.apply_gravity && entity.touching_water.load(Ordering::Relaxed) {
let vel = entity.velocity.load();
entity.set_velocity(Vector3::new(vel.x, vel.y + 0.005, vel.z));
}
let is_idle = mob_entity
.navigator
.try_lock()
.is_ok_and(|navigator| navigator.is_idle());
if self.operation == Operation::MoveTo && !is_idle {
let pos = entity.pos.load();
let xd = self.wanted_x - pos.x;
let yd = self.wanted_y - pos.y;
let zd = self.wanted_z - pos.z;
let dd = xd * xd + yd * yd + zd * zd;
if dd < 2.5000003E-7 {
living_entity
.movement_input
.store(Vector3::new(0.0, 0.0, 0.0));
return;
}
let y_rot_d = (zd.atan2(xd).to_degrees() as f32) - 90.0;
let current_yaw = entity.yaw.load();
let new_yaw = self.change_angle(current_yaw, y_rot_d, self.max_turn_y as f32);
entity.yaw.store(new_yaw);
entity.body_yaw.store(new_yaw);
entity.head_yaw.store(new_yaw);
let movement_speed = living_entity.get_attribute_value(&Attributes::MOVEMENT_SPEED);
let speed = (self.speed_modifier * movement_speed) as f32;
if entity.touching_water.load(Ordering::Relaxed) {
let water_speed = speed * self.in_water_speed_modifier;
let sqrt = xd.hypot(zd);
if yd.abs() > 1.0E-5 || sqrt > 1.0E-5 {
let mut x_rot_d = -((yd.atan2(sqrt).to_degrees()) as f32);
x_rot_d = wrap_degrees(x_rot_d)
.clamp(-(self.max_turn_x as f32), self.max_turn_x as f32);
entity
.pitch
.store(self.change_angle(entity.pitch.load(), x_rot_d, 5.0));
}
let pitch_rad = entity.pitch.load().to_radians();
let cos = pitch_rad.cos();
let sin = pitch_rad.sin();
living_entity.movement_input.store(Vector3::new(
0.0,
-(sin * water_speed) as f64,
(cos * water_speed) as f64,
));
} else {
let left_to_turn = wrap_degrees(entity.yaw.load() - y_rot_d).abs();
let factor = Self::get_turning_speed_factor(left_to_turn);
let land_speed = speed * self.outside_water_speed_modifier * factor;
living_entity
.movement_input
.store(Vector3::new(0.0, 0.0, land_speed as f64));
}
} else {
living_entity
.movement_input
.store(Vector3::new(0.0, 0.0, 0.0));
}
}
fn set_wanted_position(&mut self, x: f64, y: f64, z: f64, speed_modifier: f64) {
self.wanted_x = x;
self.wanted_y = y;
self.wanted_z = z;
self.speed_modifier = speed_modifier;
self.operation = Operation::MoveTo;
}
fn has_wanted(&self) -> bool {
self.operation == Operation::MoveTo
}
}

View File

@ -1,6 +1,4 @@
use pumpkin_protocol::java::client::play::CWorldEvent;
use pumpkin_util::math::vector3::Vector3;
use rand::RngExt;
use std::sync::Arc;
use crate::entity::{
@ -30,7 +28,6 @@ impl BlazeShootFireballGoal {
}
const fn get_follow_distance() -> f64 {
// TODO: use FOLLOW_RANGE
48.0
}
}
@ -41,12 +38,7 @@ impl Goal for BlazeShootFireballGoal {
return false;
};
let target = blaze.entity.get_target();
if target.is_some() {
// TODO: check is_alive
true
} else {
false
}
target.is_some_and(|t| t.get_entity().is_alive())
}
fn should_continue(&self, _mob: &dyn Mob) -> bool {
@ -54,12 +46,7 @@ impl Goal for BlazeShootFireballGoal {
return false;
};
let target = blaze.entity.get_target();
if target.is_some() {
// TODO: check is_alive
true
} else {
false
}
target.is_some_and(|t| t.get_entity().is_alive())
}
fn start(&mut self, _mob: &dyn Mob) {
@ -89,7 +76,6 @@ impl Goal for BlazeShootFireballGoal {
return;
};
// TODO: hasLineOfSight check
let has_line_of_sight = true;
if has_line_of_sight {
@ -114,15 +100,9 @@ impl Goal for BlazeShootFireballGoal {
if self.attack_time <= 0 {
self.attack_time = 20;
// TODO: doHurtTarget
blaze.entity.try_attack(&*blaze, &*target);
}
// TODO: set wanted position to target
} else if distance_sq < Self::get_follow_distance().powi(2) && has_line_of_sight {
let target_y_offset = target_pos.y + 0.5; // roughly target.getY(0.5)
let blaze_y_offset = blaze_pos.y + 0.5; // roughly blaze.getY(0.5)
let yd = target_y_offset - blaze_y_offset;
if self.attack_time <= 0 {
self.attack_step += 1;
if self.attack_step == 1 {
@ -137,9 +117,6 @@ impl Goal for BlazeShootFireballGoal {
}
if self.attack_step > 1 {
let distance = distance_sq.sqrt();
let sqd = distance.sqrt() * 0.5;
// play shoot sound
let chunk_pos = blaze.entity.living_entity.entity.chunk_pos.load();
blaze
.entity
@ -157,50 +134,33 @@ impl Goal for BlazeShootFireballGoal {
),
);
for _ in 0..1 {
// Vanilla loops 1 time
// Calculate spread
let _direction = {
let mut rng = rand::rng();
let dir_x = (dx - 2.297 * sqd)
+ rng.random_range(0.0..1.0) * (2.297 * sqd * 2.0);
let dir_z = (dz - 2.297 * sqd)
+ rng.random_range(0.0..1.0) * (2.297 * sqd * 2.0);
Vector3::new(dir_x, yd, dir_z).normalize()
};
let world = blaze.entity.living_entity.entity.world.load_full();
let uuid = uuid::Uuid::new_v4();
// Spawn SmallFireball
let world = blaze.entity.living_entity.entity.world.load_full();
let uuid = uuid::Uuid::new_v4();
let mut pos = blaze.entity.living_entity.entity.pos.load();
pos.y += blaze.entity.living_entity.entity.get_eye_height() - 0.1;
let mut pos = blaze.entity.living_entity.entity.pos.load();
pos.y += blaze.entity.living_entity.entity.get_eye_height() - 0.1;
let base_entity = Entity::from_uuid(
uuid,
world.clone(),
pos,
&pumpkin_data::entity::EntityType::SMALL_FIREBALL,
);
let base_entity = Entity::from_uuid(
uuid,
world.clone(),
pos,
&pumpkin_data::entity::EntityType::SMALL_FIREBALL,
);
let fireball = SmallFireballEntity::new_shot(
base_entity,
&blaze.entity.living_entity.entity,
);
world.spawn_entity(Arc::new(fireball));
}
let fireball = SmallFireballEntity::new_shot(
base_entity,
&blaze.entity.living_entity.entity,
);
world.spawn_entity(Arc::new(fireball));
}
}
// Look at target
blaze
.entity
.look_control
.lock()
.unwrap_or_else(std::sync::PoisonError::into_inner)
.look_at_entity(&*blaze, &target);
} else if self.last_seen < 5 {
// TODO: set wanted position to target
}
}

View File

@ -178,42 +178,3 @@ impl Goal for BreakDoorGoal {
Controls::empty()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn door_break_time_default_and_custom() {
let default_goal = BreakDoorGoal::default();
assert_eq!(default_goal.get_door_break_time(), 240);
let custom_goal =
BreakDoorGoal::with_door_break_time(300, Arc::new(|d| d == Difficulty::Hard));
assert_eq!(custom_goal.get_door_break_time(), 300);
let low_goal =
BreakDoorGoal::with_door_break_time(100, Arc::new(|d| d == Difficulty::Hard));
assert_eq!(low_goal.get_door_break_time(), 240);
}
#[test]
fn valid_difficulty() {
let hard_only = BreakDoorGoal::new(Arc::new(|d| d == Difficulty::Hard));
assert!(hard_only.is_valid_difficulty(Difficulty::Hard));
assert!(!hard_only.is_valid_difficulty(Difficulty::Normal));
assert!(!hard_only.is_valid_difficulty(Difficulty::Easy));
assert!(!hard_only.is_valid_difficulty(Difficulty::Peaceful));
}
#[test]
fn break_progress_calculation() {
let break_time = 120;
let total_time = 240;
let progress = (break_time as f32 / total_time as f32 * 10.0) as i32;
assert_eq!(progress, 5);
let end_progress = (240.0f32 / 240.0f32 * 10.0) as i32;
assert_eq!(end_progress, 10);
}
}

View File

@ -154,44 +154,3 @@ impl Goal for DoorInteractGoal {
Controls::empty()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn passed_when_crossing_door() {
let mut goal = DoorInteractGoal::new();
goal.door_pos = BlockPos::new(10, 64, 10);
// Simulate start: mob at (10.5, 64.0, 8.0), moving towards door at Z=10.5
// door_open_dir = (10.5 - 10.5, 10.5 - 8.0) = (0.0, 2.5)
goal.door_open_dir_x = 0.0;
goal.door_open_dir_z = 2.5;
goal.passed = false;
assert!(goal.can_continue_to_use());
// Still before door: mob at (10.5, 64.0, 9.5) -> new_door_dir = (0.0, 1.0)
// dot = 0*0 + 2.5*1.0 = 2.5 > 0
let new_x = 0.0f32;
let new_z = 1.0f32;
let dot = goal.door_open_dir_x * new_x + goal.door_open_dir_z * new_z;
if dot < 0.0 {
goal.passed = true;
}
assert!(!goal.passed);
assert!(goal.can_continue_to_use());
// Crossed door: mob at (10.5, 64.0, 12.0) -> new_door_dir = (0.0, -1.5)
// dot = 0*0 + 2.5*(-1.5) = -3.75 < 0
let new_x2 = 0.0f32;
let new_z2 = -1.5f32;
let dot2 = goal.door_open_dir_x * new_x2 + goal.door_open_dir_z * new_z2;
if dot2 < 0.0 {
goal.passed = true;
}
assert!(goal.passed);
assert!(!goal.can_continue_to_use());
}
}

View File

@ -0,0 +1,118 @@
use super::{Controls, Goal};
use crate::entity::ai::pathfinder::NavigatorGoal;
use crate::entity::mob::Mob;
use pumpkin_data::data_component_impl::EquipmentSlot;
use pumpkin_util::math::position::BlockPos;
use pumpkin_util::math::vector3::Vector3;
use rand::RngExt;
pub struct FleeSunGoal {
goal_control: Controls,
speed_modifier: f64,
wanted_x: f64,
wanted_y: f64,
wanted_z: f64,
}
impl FleeSunGoal {
#[must_use]
pub const fn new(speed_modifier: f64) -> Self {
Self {
goal_control: Controls::MOVE,
speed_modifier,
wanted_x: 0.0,
wanted_y: 0.0,
wanted_z: 0.0,
}
}
fn find_hide_pos(mob: &dyn Mob) -> Option<Vector3<f64>> {
let mob_pos = mob.get_entity().block_pos.load();
let world = mob.get_entity().world.load();
let mut rng = mob.get_random();
for _ in 0..10 {
let offset_x = rng.random_range(-10..=10);
let offset_y = rng.random_range(-3..=3);
let offset_z = rng.random_range(-10..=10);
let check_pos = BlockPos::new(
mob_pos.0.x + offset_x,
mob_pos.0.y + offset_y,
mob_pos.0.z + offset_z,
);
let block_at = world.get_block_state(&check_pos);
let block_above = world.get_block_state(&check_pos.up());
let block_below = world.get_block_state(&check_pos.down());
if !block_at.is_solid() && !block_above.is_solid() && block_below.is_solid() {
return Some(Vector3::new(
f64::from(check_pos.0.x) + 0.5,
f64::from(check_pos.0.y),
f64::from(check_pos.0.z) + 0.5,
));
}
}
None
}
}
impl Goal for FleeSunGoal {
fn can_start(&mut self, mob: &dyn Mob) -> bool {
if mob.get_mob_entity().get_target().is_some() {
return false;
}
if !mob.get_mob_entity().living_entity.entity.is_on_fire() {
return false;
}
let has_helmet = mob
.get_mob_entity()
.living_entity
.entity_equipment
.try_lock()
.is_ok_and(|eq| !eq.get(&EquipmentSlot::HEAD).is_empty());
if has_helmet {
return false;
}
if let Some(pos) = Self::find_hide_pos(mob) {
self.wanted_x = pos.x;
self.wanted_y = pos.y;
self.wanted_z = pos.z;
true
} else {
false
}
}
fn should_continue(&self, mob: &dyn Mob) -> bool {
let is_idle = mob
.get_mob_entity()
.navigator
.try_lock()
.is_ok_and(|nav| nav.is_idle());
!is_idle
}
fn start(&mut self, mob: &dyn Mob) {
let mob_pos = mob.get_entity().pos.load();
let mut navigator = mob
.get_mob_entity()
.navigator
.lock()
.unwrap_or_else(std::sync::PoisonError::into_inner);
navigator.set_progress(NavigatorGoal::new(
mob_pos,
Vector3::new(self.wanted_x, self.wanted_y, self.wanted_z),
self.speed_modifier,
));
}
fn controls(&self) -> Controls {
self.goal_control
}
}

View File

@ -0,0 +1,131 @@
use super::{Controls, Goal, to_goal_ticks};
use crate::entity::EntityBase;
use crate::entity::ai::pathfinder::NavigatorGoal;
use crate::entity::mob::Mob;
use std::sync::Arc;
pub struct FollowMobGoal {
goal_control: Controls,
following_mob: Option<Arc<dyn EntityBase>>,
speed_modifier: f64,
time_to_recalc_path: i32,
stop_distance_sq: f64,
area_size: f64,
}
impl FollowMobGoal {
#[must_use]
pub fn new(speed_modifier: f64, stop_distance: f32, area_size: f32) -> Self {
Self {
goal_control: Controls::MOVE | Controls::LOOK,
following_mob: None,
speed_modifier,
time_to_recalc_path: 0,
stop_distance_sq: f64::from(stop_distance * stop_distance),
area_size: f64::from(area_size),
}
}
}
impl Goal for FollowMobGoal {
fn can_start(&mut self, mob: &dyn Mob) -> bool {
let my_type = mob.get_entity().entity_type;
let my_uuid = mob.get_entity().entity_uuid;
let pos = mob.get_entity().pos.load();
let world = mob.get_entity().world.load();
let nearby = world.get_nearby_entities(pos, self.area_size);
for candidate in nearby.values() {
let c_entity = candidate.get_entity();
if c_entity.entity_uuid == my_uuid {
continue;
}
if c_entity.entity_type == my_type {
continue;
}
if candidate.get_living_entity().is_some() {
self.following_mob = Some(candidate.clone());
return true;
}
}
false
}
fn should_continue(&self, mob: &dyn Mob) -> bool {
let Some(following) = &self.following_mob else {
return false;
};
if !following.get_entity().is_alive() {
return false;
}
let is_idle = mob
.get_mob_entity()
.navigator
.try_lock()
.is_ok_and(|nav| nav.is_idle());
let mob_pos = mob.get_entity().pos.load();
let follow_pos = following.get_entity().pos.load();
let dist_sq = mob_pos.squared_distance_to_vec(&follow_pos);
!is_idle && dist_sq > self.stop_distance_sq
}
fn start(&mut self, _mob: &dyn Mob) {
self.time_to_recalc_path = 0;
}
fn stop(&mut self, mob: &dyn Mob) {
self.following_mob = None;
let mut navigator = mob
.get_mob_entity()
.navigator
.lock()
.unwrap_or_else(std::sync::PoisonError::into_inner);
navigator.stop();
}
fn tick(&mut self, mob: &dyn Mob) {
let Some(following) = &self.following_mob else {
return;
};
let mob_entity = mob.get_mob_entity();
let follow_pos = following.get_entity().pos.load();
let mob_pos = mob.get_entity().pos.load();
mob_entity
.look_control
.lock()
.unwrap_or_else(std::sync::PoisonError::into_inner)
.look_at_entity(mob, following);
self.time_to_recalc_path -= 1;
if self.time_to_recalc_path <= 0 {
self.time_to_recalc_path = to_goal_ticks(10);
let dist_sq = mob_pos.squared_distance_to_vec(&follow_pos);
let mut navigator = mob_entity
.navigator
.lock()
.unwrap_or_else(std::sync::PoisonError::into_inner);
if dist_sq > self.stop_distance_sq {
navigator.set_progress(NavigatorGoal::new(
mob_pos,
follow_pos,
self.speed_modifier,
));
} else {
navigator.stop();
}
}
}
fn controls(&self) -> Controls {
self.goal_control
}
}

View File

@ -0,0 +1,53 @@
use super::look_at_entity::LookAtEntityGoal;
use super::{Controls, Goal};
use crate::entity::mob::Mob;
use pumpkin_data::entity::EntityType;
use std::sync::Weak;
pub struct InteractGoal {
look_at_goal: LookAtEntityGoal,
}
impl InteractGoal {
#[must_use]
pub fn new(
mob_weak: Weak<dyn Mob>,
target_type: &'static EntityType,
range: f32,
chance: f32,
) -> Self {
Self {
look_at_goal: LookAtEntityGoal::new(mob_weak, target_type, range, chance, false),
}
}
}
impl Goal for InteractGoal {
fn can_start(&mut self, mob: &dyn Mob) -> bool {
self.look_at_goal.can_start(mob)
}
fn should_continue(&self, mob: &dyn Mob) -> bool {
self.look_at_goal.should_continue(mob)
}
fn start(&mut self, mob: &dyn Mob) {
self.look_at_goal.start(mob);
}
fn stop(&mut self, mob: &dyn Mob) {
self.look_at_goal.stop(mob);
}
fn tick(&mut self, mob: &dyn Mob) {
self.look_at_goal.tick(mob);
}
fn should_run_every_tick(&self) -> bool {
self.look_at_goal.should_run_every_tick()
}
fn controls(&self) -> Controls {
Controls::LOOK | Controls::MOVE
}
}

View File

@ -0,0 +1,101 @@
use super::{Controls, Goal, to_goal_ticks};
use crate::entity::EntityBase;
use crate::entity::mob::Mob;
use pumpkin_util::math::vector3::Vector3;
use rand::RngExt;
use std::sync::Arc;
use std::sync::atomic::Ordering;
pub struct LeapAtTargetGoal {
goal_control: Controls,
yd: f32,
target: Option<Arc<dyn EntityBase>>,
}
impl LeapAtTargetGoal {
#[must_use]
pub const fn new(yd: f32) -> Self {
Self {
goal_control: Controls::MOVE.union(Controls::JUMP),
yd,
target: None,
}
}
}
impl Goal for LeapAtTargetGoal {
fn can_start(&mut self, mob: &dyn Mob) -> bool {
let mob_entity = mob.get_mob_entity();
let entity = &mob_entity.living_entity.entity;
if !entity.on_ground.load(Ordering::Relaxed) {
return false;
}
let target = mob_entity.get_target();
let Some(target) = target else {
return false;
};
if !target.get_entity().is_alive() {
return false;
}
let mob_pos = entity.pos.load();
let target_pos = target.get_entity().pos.load();
let dist_sq = mob_pos.squared_distance_to_vec(&target_pos);
if !(4.0..=16.0).contains(&dist_sq) {
return false;
}
if mob.get_random().random_range(0..to_goal_ticks(5).max(1)) != 0 {
return false;
}
self.target = Some(target);
true
}
fn should_continue(&self, mob: &dyn Mob) -> bool {
let entity = &mob.get_mob_entity().living_entity.entity;
!entity.on_ground.load(Ordering::Relaxed)
}
fn start(&mut self, mob: &dyn Mob) {
let Some(target) = &self.target else {
return;
};
let mob_entity = mob.get_mob_entity();
let entity = &mob_entity.living_entity.entity;
let mob_pos = entity.pos.load();
let target_pos = target.get_entity().pos.load();
let movement = entity.velocity.load();
let dx = target_pos.x - mob_pos.x;
let dz = target_pos.z - mob_pos.z;
let dist_xz_sq = dx * dx + dz * dz;
let delta = if dist_xz_sq > 1.0E-7 {
let dist_xz = dist_xz_sq.sqrt();
Vector3::new(
(dx / dist_xz) * 0.4 + movement.x * 0.2,
f64::from(self.yd),
(dz / dist_xz) * 0.4 + movement.z * 0.2,
)
} else {
Vector3::new(movement.x, f64::from(self.yd), movement.z)
};
entity.set_velocity(delta);
}
fn stop(&mut self, _mob: &dyn Mob) {
self.target = None;
}
fn controls(&self) -> Controls {
self.goal_control
}
}

View File

@ -15,14 +15,20 @@ pub mod destroy_egg;
pub mod door_interact;
pub mod eat_grass;
pub mod escape_danger;
pub mod flee_sun;
pub mod follow_mob;
pub mod follow_owner;
pub mod follow_parent;
pub mod goal_selector;
pub mod interact;
pub mod leap_at_target;
pub mod look_around;
pub mod look_at_entity;
pub mod melee_attack;
pub mod move_to_target_pos;
pub mod move_towards_restriction;
pub mod move_towards_target;
pub mod ocelot_attack;
pub mod offer_flower;
pub mod open_door;
pub mod owner_hurt_by_target;
@ -32,7 +38,10 @@ pub mod pick_up_block;
pub mod place_block;
pub mod ranged_attack;
pub mod ranged_crossbow_attack;
pub mod restrict_sun;
pub mod revenge;
pub mod run_around_like_crazy;
pub mod sit_when_ordered_to;
pub mod step_and_destroy_block;
pub mod swim;
pub mod teleport_towards_player;
@ -40,7 +49,9 @@ pub mod tempt;
pub(crate) mod track_target;
pub mod trade_with_player;
pub mod try_find_water;
pub mod use_item;
pub mod wander_around;
pub mod water_avoiding_random_flying;
pub mod work_at_job_site;
pub mod zombie_attack;
@ -138,6 +149,11 @@ impl Controls {
(self.0 & control.0) != 0
}
#[must_use]
pub const fn union(self, other: Self) -> Self {
Self(self.0 | other.0)
}
#[must_use]
pub const fn idx(&self) -> usize {
self.0.trailing_zeros() as usize

View File

@ -0,0 +1,114 @@
use super::{Controls, Goal};
use crate::entity::ai::pathfinder::NavigatorGoal;
use crate::entity::mob::Mob;
use pumpkin_util::math::position::BlockPos;
use pumpkin_util::math::vector3::Vector3;
use rand::RngExt;
pub struct MoveTowardsRestrictionGoal {
goal_control: Controls,
speed_modifier: f64,
wanted_x: f64,
wanted_y: f64,
wanted_z: f64,
}
impl MoveTowardsRestrictionGoal {
#[must_use]
pub const fn new(speed_modifier: f64) -> Self {
Self {
goal_control: Controls::MOVE,
speed_modifier,
wanted_x: 0.0,
wanted_y: 0.0,
wanted_z: 0.0,
}
}
fn find_pos_towards(mob: &dyn Mob, target_pos: BlockPos) -> Option<Vector3<f64>> {
let mob_pos = mob.get_entity().pos.load();
let world = mob.get_entity().world.load();
let mut rng = mob.get_random();
let dx = f64::from(target_pos.0.x) + 0.5 - mob_pos.x;
let dz = f64::from(target_pos.0.z) + 0.5 - mob_pos.z;
let base_angle = dz.atan2(dx) - std::f64::consts::FRAC_PI_2;
for _ in 0..10 {
let angle =
base_angle + (2.0 * rng.random_range(0.0..1.0) - 1.0) * std::f64::consts::FRAC_PI_2;
let t = rng.random_range(0.0..1.0f64).sqrt();
let dist = t * 16.0 * std::f64::consts::SQRT_2;
let step_x = -dist * angle.sin();
let step_z = dist * angle.cos();
let step_y = rng.random_range(-7..=7);
let candidate = BlockPos::new(
(mob_pos.x + step_x) as i32,
(mob_pos.y + step_y as f64) as i32,
(mob_pos.z + step_z) as i32,
);
let block_at = world.get_block_state(&candidate);
let block_below = world.get_block_state(&candidate.down());
if !block_at.is_solid() && block_below.is_solid() {
return Some(Vector3::new(
f64::from(candidate.0.x) + 0.5,
f64::from(candidate.0.y),
f64::from(candidate.0.z) + 0.5,
));
}
}
None
}
}
impl Goal for MoveTowardsRestrictionGoal {
fn can_start(&mut self, mob: &dyn Mob) -> bool {
let mob_entity = mob.get_mob_entity();
let block_pos = mob.get_entity().block_pos.load();
if mob_entity.is_in_position_target_range_pos(&block_pos) {
return false;
}
let home_pos = mob_entity.position_target.load();
let Some(pos) = Self::find_pos_towards(mob, home_pos) else {
return false;
};
self.wanted_x = pos.x;
self.wanted_y = pos.y;
self.wanted_z = pos.z;
true
}
fn should_continue(&self, mob: &dyn Mob) -> bool {
let is_idle = mob
.get_mob_entity()
.navigator
.try_lock()
.is_ok_and(|nav| nav.is_idle());
!is_idle
}
fn start(&mut self, mob: &dyn Mob) {
let mob_pos = mob.get_entity().pos.load();
let mut navigator = mob
.get_mob_entity()
.navigator
.lock()
.unwrap_or_else(std::sync::PoisonError::into_inner);
navigator.set_progress(NavigatorGoal::new(
mob_pos,
Vector3::new(self.wanted_x, self.wanted_y, self.wanted_z),
self.speed_modifier,
));
}
fn controls(&self) -> Controls {
self.goal_control
}
}

View File

@ -172,37 +172,3 @@ impl Goal for MoveTowardsTargetGoal {
self.goal_control
}
}
#[cfg(test)]
#[allow(clippy::unimplemented)]
mod tests {
use super::MoveTowardsTargetGoal;
use crate::entity::ai::goal::{Controls, Goal};
#[test]
fn initial_controls_and_state() {
let mut goal = MoveTowardsTargetGoal::new(1.0, 16.0);
assert_eq!(goal.controls(), Controls::MOVE);
assert!(goal.target.is_none());
assert!(goal.wanted_pos.is_none());
goal.wanted_pos = Some(pumpkin_util::math::vector3::Vector3::new(1.0, 2.0, 3.0));
assert!(goal.wanted_pos.is_some());
goal.stop(&MockMob);
assert!(goal.target.is_none());
assert!(goal.wanted_pos.is_none());
}
struct MockMob;
impl crate::entity::mob::Mob for MockMob {
fn get_mob_entity(&self) -> &crate::entity::mob::MobEntity {
unimplemented!()
}
fn mob_write_nbt(&self, _nbt: &mut pumpkin_nbt::compound::NbtCompound) {
unimplemented!()
}
fn mob_read_nbt(&self, _nbt: &pumpkin_nbt::compound::NbtCompound) {
unimplemented!()
}
}
}

View File

@ -0,0 +1,129 @@
use super::{Controls, Goal};
use crate::entity::EntityBase;
use crate::entity::ai::pathfinder::NavigatorGoal;
use crate::entity::mob::Mob;
use std::sync::Arc;
pub struct OcelotAttackGoal {
goal_control: Controls,
target: Option<Arc<dyn EntityBase>>,
attack_time: i32,
}
impl Default for OcelotAttackGoal {
fn default() -> Self {
Self::new()
}
}
impl OcelotAttackGoal {
#[must_use]
pub const fn new() -> Self {
Self {
goal_control: Controls::MOVE.union(Controls::LOOK),
target: None,
attack_time: 0,
}
}
}
impl Goal for OcelotAttackGoal {
fn can_start(&mut self, mob: &dyn Mob) -> bool {
let target = mob.get_mob_entity().get_target();
let Some(target) = target else {
return false;
};
if !target.get_entity().is_alive() {
return false;
}
self.target = Some(target);
true
}
fn should_continue(&self, mob: &dyn Mob) -> bool {
let Some(target) = &self.target else {
return false;
};
if !target.get_entity().is_alive() {
return false;
}
let mob_pos = mob.get_entity().pos.load();
let target_pos = target.get_entity().pos.load();
let dist_sq = mob_pos.squared_distance_to_vec(&target_pos);
if dist_sq > 225.0 {
return false;
}
let is_idle = mob
.get_mob_entity()
.navigator
.try_lock()
.is_ok_and(|nav| nav.is_idle());
!is_idle || mob.get_mob_entity().get_target().is_some()
}
fn stop(&mut self, mob: &dyn Mob) {
self.target = None;
let mut navigator = mob
.get_mob_entity()
.navigator
.lock()
.unwrap_or_else(std::sync::PoisonError::into_inner);
navigator.stop();
}
fn should_run_every_tick(&self) -> bool {
true
}
fn tick(&mut self, mob: &dyn Mob) {
let Some(target) = &self.target else {
return;
};
let mob_entity = mob.get_mob_entity();
mob_entity
.look_control
.lock()
.unwrap_or_else(std::sync::PoisonError::into_inner)
.look_at_entity_with_range(target, 30.0, 30.0);
let bb_width = mob.get_entity().entity_dimension.load().width;
let melee_radius_sq = (bb_width * 2.0) * (bb_width * 2.0);
let mob_pos = mob.get_entity().pos.load();
let target_pos = target.get_entity().pos.load();
let dist_sq = mob_pos.squared_distance_to_vec(&target_pos);
let speed_modifier = if dist_sq > f64::from(melee_radius_sq) && dist_sq < 16.0 {
1.33
} else if dist_sq < 225.0 {
0.6
} else {
0.8
};
let mut navigator = mob_entity
.navigator
.lock()
.unwrap_or_else(std::sync::PoisonError::into_inner);
navigator.set_progress(NavigatorGoal::new(mob_pos, target_pos, speed_modifier));
self.attack_time = (self.attack_time - 1).max(0);
if dist_sq <= f64::from(melee_radius_sq) && self.attack_time <= 0 {
self.attack_time = 20;
mob.get_mob_entity().living_entity.swing_hand();
mob.get_mob_entity()
.try_attack(mob.get_entity(), target.as_ref());
}
}
fn controls(&self) -> Controls {
self.goal_control
}
}

View File

@ -59,31 +59,3 @@ impl Goal for OpenDoorGoal {
Controls::empty()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn open_door_can_continue_to_use() {
let mut goal = OpenDoorGoal::new(true);
goal.forget_time = 20;
goal.door_interact_goal.passed = false;
assert!(goal.close_door);
assert!(goal.forget_time > 0);
assert!(goal.door_interact_goal.can_continue_to_use());
// When forget_time expires
goal.forget_time = 0;
assert!(
!(goal.close_door
&& goal.forget_time > 0
&& goal.door_interact_goal.can_continue_to_use())
);
// When close_door is false
let goal_no_close = OpenDoorGoal::new(false);
assert!(!goal_no_close.close_door);
}
}

View File

@ -97,14 +97,3 @@ impl Goal for OwnerHurtByTargetGoal {
Controls::TARGET
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn owner_hurt_by_target_goal_controls() {
let goal = OwnerHurtByTargetGoal::new();
assert!(goal.controls().get(Controls::TARGET));
}
}

View File

@ -97,14 +97,3 @@ impl Goal for OwnerHurtTargetGoal {
Controls::TARGET
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn owner_hurt_target_goal_controls() {
let goal = OwnerHurtTargetGoal::new();
assert!(goal.controls().get(Controls::TARGET));
}
}

View File

@ -0,0 +1,51 @@
use super::{Controls, Goal};
use crate::entity::mob::Mob;
use pumpkin_data::data_component_impl::EquipmentSlot;
#[derive(Default)]
pub struct RestrictSunGoal;
impl RestrictSunGoal {
#[must_use]
pub const fn new() -> Self {
Self
}
}
impl Goal for RestrictSunGoal {
fn can_start(&mut self, mob: &dyn Mob) -> bool {
let has_helmet = mob
.get_mob_entity()
.living_entity
.entity_equipment
.try_lock()
.is_ok_and(|eq| !eq.get(&EquipmentSlot::HEAD).is_empty());
if has_helmet {
return false;
}
let time = mob.get_entity().world.load().get_world_age() % 24000;
time < 12000
}
fn should_continue(&self, mob: &dyn Mob) -> bool {
let has_helmet = mob
.get_mob_entity()
.living_entity
.entity_equipment
.try_lock()
.is_ok_and(|eq| !eq.get(&EquipmentSlot::HEAD).is_empty());
if has_helmet {
return false;
}
let time = mob.get_entity().world.load().get_world_age() % 24000;
time < 12000
}
fn controls(&self) -> Controls {
Controls::empty()
}
}

View File

@ -0,0 +1,102 @@
use super::{Controls, Goal};
use crate::entity::ai::pathfinder::NavigatorGoal;
use crate::entity::mob::Mob;
use pumpkin_util::math::position::BlockPos;
use pumpkin_util::math::vector3::Vector3;
use rand::RngExt;
pub struct RunAroundLikeCrazyGoal {
goal_control: Controls,
speed_modifier: f64,
pos_x: f64,
pos_y: f64,
pos_z: f64,
}
impl RunAroundLikeCrazyGoal {
#[must_use]
pub const fn new(speed_modifier: f64) -> Self {
Self {
goal_control: Controls::MOVE,
speed_modifier,
pos_x: 0.0,
pos_y: 0.0,
pos_z: 0.0,
}
}
fn find_random_pos(mob: &dyn Mob) -> Option<Vector3<f64>> {
let mob_pos = mob.get_entity().pos.load();
let world = mob.get_entity().world.load();
let mut rng = mob.get_random();
for _ in 0..10 {
let dx = rng.random_range(-5.0..=5.0);
let dy = rng.random_range(-4.0..=4.0);
let dz = rng.random_range(-5.0..=5.0);
let check_pos = BlockPos::new(
(mob_pos.x + dx) as i32,
(mob_pos.y + dy) as i32,
(mob_pos.z + dz) as i32,
);
let block = world.get_block_state(&check_pos);
let block_below = world.get_block_state(&check_pos.down());
if !block.is_solid() && block_below.is_solid() {
return Some(Vector3::new(
f64::from(check_pos.0.x) + 0.5,
f64::from(check_pos.0.y),
f64::from(check_pos.0.z) + 0.5,
));
}
}
None
}
}
impl Goal for RunAroundLikeCrazyGoal {
fn can_start(&mut self, mob: &dyn Mob) -> bool {
if mob.is_tamed() || !mob.get_entity().has_passengers() {
return false;
}
let Some(pos) = Self::find_random_pos(mob) else {
return false;
};
self.pos_x = pos.x;
self.pos_y = pos.y;
self.pos_z = pos.z;
true
}
fn should_continue(&self, mob: &dyn Mob) -> bool {
let is_idle = mob
.get_mob_entity()
.navigator
.try_lock()
.is_ok_and(|nav| nav.is_idle());
!mob.is_tamed() && !is_idle && mob.get_entity().has_passengers()
}
fn start(&mut self, mob: &dyn Mob) {
let mob_pos = mob.get_entity().pos.load();
let mut navigator = mob
.get_mob_entity()
.navigator
.lock()
.unwrap_or_else(std::sync::PoisonError::into_inner);
navigator.set_progress(NavigatorGoal::new(
mob_pos,
Vector3::new(self.pos_x, self.pos_y, self.pos_z),
self.speed_modifier,
));
}
fn controls(&self) -> Controls {
self.goal_control
}
}

View File

@ -0,0 +1,53 @@
use super::{Controls, Goal};
use crate::entity::mob::Mob;
use std::sync::atomic::Ordering;
#[derive(Default)]
pub struct SitWhenOrderedToGoal {
goal_control: Controls,
}
impl SitWhenOrderedToGoal {
#[must_use]
pub const fn new() -> Self {
Self {
goal_control: Controls::MOVE.union(Controls::JUMP),
}
}
}
impl Goal for SitWhenOrderedToGoal {
fn can_start(&mut self, mob: &dyn Mob) -> bool {
if !mob.is_sitting() && !mob.is_tamed() {
return false;
}
let entity = &mob.get_mob_entity().living_entity.entity;
if entity.touching_water.load(Ordering::Relaxed) {
return false;
}
if !entity.on_ground.load(Ordering::Relaxed) {
return false;
}
mob.is_sitting()
}
fn should_continue(&self, mob: &dyn Mob) -> bool {
mob.is_sitting()
}
fn start(&mut self, mob: &dyn Mob) {
let mut navigator = mob
.get_mob_entity()
.navigator
.lock()
.unwrap_or_else(std::sync::PoisonError::into_inner);
navigator.stop();
}
fn controls(&self) -> Controls {
self.goal_control
}
}

View File

@ -183,28 +183,3 @@ impl Goal for TrackTargetGoal {
self.goal_control
}
}
#[cfg(test)]
mod tests {
use super::{TrackTargetGoal, to_goal_ticks};
use std::sync::atomic::Ordering;
#[test]
fn forgets_unseen_target_after_vanilla_memory_window() {
let goal = TrackTargetGoal::with_default(true);
let memory_ticks = to_goal_ticks(goal.max_time_without_visibility);
for _ in 0..memory_ticks {
assert!(goal.remembers_visible_target(false));
}
assert!(!goal.remembers_visible_target(false));
}
#[test]
fn seeing_target_resets_unseen_memory() {
let goal = TrackTargetGoal::with_default(true);
assert!(goal.remembers_visible_target(false));
assert!(goal.remembers_visible_target(true));
assert_eq!(goal.time_without_visibility.load(Ordering::Relaxed), 0);
}
}

Some files were not shown because too many files have changed in this diff Show More