Add Pressure Plates

This commit is contained in:
Alexander Medvedev 2025-05-14 20:11:06 +02:00
parent f79167cece
commit 81c45b7c78
11 changed files with 455 additions and 3 deletions

View File

@ -3,6 +3,8 @@ use std::ops::{Add, AddAssign, Div, Mul, Sub};
use num_traits::{Float, Num};
use super::vector2::Vector2;
#[derive(Clone, Copy, Debug, PartialEq, Hash, Eq, Default)]
pub struct Vector3<T> {
pub x: T,
@ -209,6 +211,15 @@ where
z: z.round() as i32,
}
}
pub fn to_vec2_i32(&self) -> Vector2<i32> {
let x: f64 = self.x.into();
let z: f64 = self.z.into();
Vector2 {
x: x.round() as i32,
z: z.round() as i32,
}
}
}
pub trait Math:

View File

@ -13,6 +13,7 @@ use crate::world::World;
pub mod buttons;
pub mod lever;
pub mod observer;
pub mod pressure_plate;
pub mod rails;
pub mod redstone_block;
pub mod redstone_lamp;

View File

@ -0,0 +1,84 @@
use std::sync::Arc;
use pumpkin_data::{Block, BlockState};
use pumpkin_util::math::position::BlockPos;
use pumpkin_world::{BlockStateId, chunk::TickPriority, world::BlockFlags};
use crate::world::World;
pub mod plate;
pub mod weighted;
pub trait PressurePlate {
async fn on_entity_collision_pp(
&self,
world: &Arc<World>,
pos: BlockPos,
block: Block,
state: BlockState,
) {
let output = self.get_redstone_output(&block, state.id);
if output == 0 {
self.update_plate_state(world, pos, &block, state, output)
.await;
}
}
async fn on_scheduled_tick_pp(&self, world: &Arc<World>, block: &Block, pos: &BlockPos) {
let state = world.get_block_state(pos).await.unwrap();
let output = self.get_redstone_output(block, state.id);
if output > 0 {
self.update_plate_state(world, *pos, block, state, output)
.await;
}
}
async fn on_state_replaced_pp(
&self,
world: &Arc<World>,
block: &Block,
pos: BlockPos,
old_state_id: BlockStateId,
moved: bool,
) {
if !moved && self.get_redstone_output(block, old_state_id) > 0 {
world.update_neighbors(&pos, None).await;
world.update_neighbors(&pos.down(), None).await;
}
}
async fn update_plate_state(
&self,
world: &Arc<World>,
pos: BlockPos,
block: &Block,
state: BlockState,
output: u8,
) {
let calc_output = self.calculate_redstone_output(world, block, &pos).await;
let has_output = calc_output > 0;
if calc_output != output {
let state = self.set_redstone_output(block, &state, calc_output);
world
.set_block_state(&pos, state, BlockFlags::NOTIFY_LISTENERS)
.await;
world.update_neighbors(&pos, None).await;
world.update_neighbors(&pos.down(), None).await;
}
if has_output {
world
.schedule_block_tick(block, pos, self.tick_rate(), TickPriority::Normal)
.await;
}
}
fn get_redstone_output(&self, block: &Block, state: BlockStateId) -> u8;
fn set_redstone_output(&self, block: &Block, state: &BlockState, output: u8) -> BlockStateId;
async fn calculate_redstone_output(&self, world: &World, block: &Block, pos: &BlockPos) -> u8;
fn tick_rate(&self) -> u16 {
20
}
}

View File

@ -0,0 +1,133 @@
use std::sync::Arc;
use async_trait::async_trait;
use pumpkin_data::{
Block, BlockState,
block_properties::BlockProperties,
tag::{RegistryKey, get_tag_values},
};
use pumpkin_util::math::{boundingbox::BoundingBox, position::BlockPos};
use pumpkin_world::{BlockStateId, block::BlockDirection};
use crate::{
block::pumpkin_block::{BlockMetadata, PumpkinBlock},
entity::Entity,
world::World,
};
use super::PressurePlate;
/// This is for Normal Pressure plates, so not Gold or Iron
pub struct PressurePlateBlock;
type PressurePlateProps = pumpkin_data::block_properties::StonePressurePlateLikeProperties;
impl BlockMetadata for PressurePlateBlock {
fn namespace(&self) -> &'static str {
"minecraft"
}
fn ids(&self) -> &'static [&'static str] {
let mut combined = Vec::new();
combined.extend_from_slice(
get_tag_values(RegistryKey::Block, "minecraft:wooden_pressure_plates").unwrap(),
);
combined.extend_from_slice(
get_tag_values(RegistryKey::Block, "minecraft:stone_pressure_plates").unwrap(),
);
combined.leak()
}
}
#[async_trait]
impl PumpkinBlock for PressurePlateBlock {
async fn on_entity_collision(
&self,
world: &Arc<World>,
_entity: &Entity,
pos: BlockPos,
block: Block,
state: BlockState,
) {
self.on_entity_collision_pp(world, pos, block, state).await;
}
async fn on_scheduled_tick(&self, world: &Arc<World>, block: &Block, pos: &BlockPos) {
self.on_scheduled_tick_pp(world, block, pos).await;
}
async fn on_state_replaced(
&self,
world: &Arc<World>,
block: &Block,
pos: BlockPos,
old_state_id: BlockStateId,
moved: bool,
) {
self.on_state_replaced_pp(world, block, pos, old_state_id, moved)
.await;
}
async fn get_weak_redstone_power(
&self,
block: &Block,
_world: &World,
_pos: &BlockPos,
state: &BlockState,
_direction: BlockDirection,
) -> u8 {
self.get_redstone_output(block, state.id)
}
async fn get_strong_redstone_power(
&self,
block: &Block,
_world: &World,
_pos: &BlockPos,
state: &BlockState,
direction: BlockDirection,
) -> u8 {
if direction == BlockDirection::Up {
return self.get_redstone_output(block, state.id);
}
0
}
async fn emits_redstone_power(
&self,
_block: &Block,
_state: &BlockState,
_direction: BlockDirection,
) -> bool {
true
}
}
impl PressurePlate for PressurePlateBlock {
fn get_redstone_output(&self, block: &Block, state: BlockStateId) -> u8 {
let props = PressurePlateProps::from_state_id(state, block);
if props.powered { 15 } else { 0 }
}
async fn calculate_redstone_output(&self, world: &World, _block: &Block, pos: &BlockPos) -> u8 {
// TODO: this is bad use real box
let aabb = BoundingBox::from_block(pos);
if !world.get_entities_at_box(&aabb).await.is_empty()
|| !world.get_players_at_box(&aabb).await.is_empty()
{
return 15;
}
0
}
fn set_redstone_output(
&self,
block: &Block,
state: &BlockState,
output: u8,
) -> pumpkin_world::BlockStateId {
let mut props = PressurePlateProps::from_state_id(state.id, block);
props.powered = output > 0;
props.to_state_id(block)
}
}

View File

@ -0,0 +1,145 @@
use std::sync::Arc;
use async_trait::async_trait;
use pumpkin_data::{
Block, BlockState,
block_properties::{BlockProperties, EnumVariants, Integer0To15},
};
use pumpkin_util::math::{boundingbox::BoundingBox, position::BlockPos};
use pumpkin_world::{BlockStateId, block::BlockDirection};
use crate::{
block::pumpkin_block::{BlockMetadata, PumpkinBlock},
entity::Entity,
world::World,
};
use super::PressurePlate;
/// This is for Gold and Iron Pressure Plate
pub struct WeightedPressurePlateBlock;
type PressurePlateProps = pumpkin_data::block_properties::LightWeightedPressurePlateLikeProperties;
impl BlockMetadata for WeightedPressurePlateBlock {
fn namespace(&self) -> &'static str {
"minecraft"
}
fn ids(&self) -> &'static [&'static str] {
// light = Gold
// heavy = Iron
&[
"light_weighted_pressure_plate",
"heavy_weighted_pressure_plate",
]
}
}
#[async_trait]
impl PumpkinBlock for WeightedPressurePlateBlock {
async fn on_entity_collision(
&self,
world: &Arc<World>,
_entity: &Entity,
pos: BlockPos,
block: Block,
state: BlockState,
) {
self.on_entity_collision_pp(world, pos, block, state).await;
}
async fn on_scheduled_tick(&self, world: &Arc<World>, block: &Block, pos: &BlockPos) {
self.on_scheduled_tick_pp(world, block, pos).await;
}
async fn on_state_replaced(
&self,
world: &Arc<World>,
block: &Block,
pos: BlockPos,
old_state_id: BlockStateId,
moved: bool,
) {
self.on_state_replaced_pp(world, block, pos, old_state_id, moved)
.await;
}
async fn get_weak_redstone_power(
&self,
block: &Block,
_world: &World,
_pos: &BlockPos,
state: &BlockState,
_direction: BlockDirection,
) -> u8 {
self.get_redstone_output(block, state.id)
}
async fn get_strong_redstone_power(
&self,
block: &Block,
_world: &World,
_pos: &BlockPos,
state: &BlockState,
direction: BlockDirection,
) -> u8 {
if direction == BlockDirection::Up {
return self.get_redstone_output(block, state.id);
}
0
}
async fn emits_redstone_power(
&self,
_block: &Block,
_state: &BlockState,
_direction: BlockDirection,
) -> bool {
true
}
}
impl PressurePlate for WeightedPressurePlateBlock {
fn get_redstone_output(&self, block: &Block, state: BlockStateId) -> u8 {
let props = PressurePlateProps::from_state_id(state, block);
props.power.to_index() as u8
}
async fn calculate_redstone_output(&self, world: &World, block: &Block, pos: &BlockPos) -> u8 {
// light = Gold
// heavy = Iron
let weight = if block == &Block::LIGHT_WEIGHTED_PRESSURE_PLATE {
// Gold
15
} else {
// Iron
150
};
// TODO: this is bad use real box
let aabb = BoundingBox::from_block(pos);
let len = world.get_entities_at_box(&aabb).await.len()
+ world.get_players_at_box(&aabb).await.len();
let len = len.min(weight);
if len > 0 {
let f = (weight.min(len) / weight) as f32;
return (f * 15.0).ceil() as u8;
}
0
}
fn set_redstone_output(
&self,
block: &Block,
state: &BlockState,
output: u8,
) -> pumpkin_world::BlockStateId {
let mut props = PressurePlateProps::from_state_id(state.id, block);
props.power = Integer0To15::from_index(u16::from(output));
props.to_state_id(block)
}
fn tick_rate(&self) -> u16 {
10
}
}

View File

@ -19,6 +19,8 @@ use blocks::piston::piston_head::PistonHeadBlock;
use blocks::pumpkin::PumpkinBlock;
use blocks::redstone::buttons::ButtonBlock;
use blocks::redstone::observer::ObserverBlock;
use blocks::redstone::pressure_plate::plate::PressurePlateBlock;
use blocks::redstone::pressure_plate::weighted::WeightedPressurePlateBlock;
use blocks::redstone::rails::activator_rail::ActivatorRailBlock;
use blocks::redstone::rails::detector_rail::DetectorRailBlock;
use blocks::redstone::rails::powered_rail::PoweredRailBlock;
@ -94,6 +96,8 @@ pub fn default_registry() -> Arc<BlockRegistry> {
manager.register(NetherPortalBlock);
manager.register(NoteBlock);
manager.register(PumpkinBlock);
manager.register(PressurePlateBlock);
manager.register(WeightedPressurePlateBlock);
// Fire
manager.register(SoulFireBlock);

View File

@ -1,4 +1,5 @@
use crate::block::registry::BlockActionResult;
use crate::entity::Entity;
use crate::entity::player::Player;
use crate::server::Server;
use crate::world::World;
@ -49,6 +50,16 @@ pub trait PumpkinBlock: Send + Sync {
BlockActionResult::Continue
}
async fn on_entity_collision(
&self,
_world: &Arc<World>,
_entity: &Entity,
_pos: BlockPos,
_block: Block,
_state: BlockState,
) {
}
fn should_drop_items_on_explosion(&self) -> bool {
true
}

View File

@ -1,4 +1,5 @@
use crate::block::pumpkin_block::{BlockMetadata, PumpkinBlock};
use crate::entity::Entity;
use crate::entity::player::Player;
use crate::server::Server;
use crate::world::World;
@ -55,6 +56,22 @@ impl BlockRegistry {
false
}
pub async fn on_entity_collision(
&self,
block: Block,
world: &Arc<World>,
entity: &Entity,
pos: BlockPos,
state: BlockState,
) {
let pumpkin_block = self.get_pumpkin_block(&block);
if let Some(pumpkin_block) = pumpkin_block {
pumpkin_block
.on_entity_collision(world, entity, pos, block, state)
.await;
}
}
pub async fn on_use(
&self,
block: &Block,

View File

@ -262,7 +262,7 @@ impl LivingEntity {
.await;
}
fn tick_move(&self) {
async fn tick_move(&self) {
let velo = self.entity.velocity.load();
let pos = self.entity.pos.load();
self.entity
@ -272,6 +272,7 @@ impl LivingEntity {
self.entity
.velocity
.store(velo.multiply(multiplier, 1.0, multiplier));
self.entity.check_block_collision().await;
}
async fn tick_effects(&self) {
@ -297,7 +298,7 @@ impl LivingEntity {
impl EntityBase for LivingEntity {
async fn tick(&self, server: &Server) {
self.entity.tick(server).await;
self.tick_move();
self.tick_move().await;
self.tick_effects().await;
if self.time_until_regen.load(Relaxed) > 0 {
self.time_until_regen.fetch_sub(1, Relaxed);

View File

@ -484,6 +484,34 @@ impl Entity {
// } else {
// }
}
pub async fn check_block_collision(&self) {
let aabb = self.bounding_box.load();
let blockpos = BlockPos::new(
(aabb.min.x + 0.001).floor() as i32,
(aabb.min.y + 0.001).floor() as i32,
(aabb.min.z + 0.001).floor() as i32,
);
let blockpos1 = BlockPos::new(
(aabb.max.x - 0.001).floor() as i32,
(aabb.max.y - 0.001).floor() as i32,
(aabb.max.z - 0.001).floor() as i32,
);
let world = self.world.read().await;
for x in blockpos.0.x..=blockpos1.0.x {
for y in blockpos.0.y..=blockpos1.0.y {
for z in blockpos.0.z..=blockpos1.0.z {
let pos = BlockPos::new(x, y, z);
let (block, state) = world.get_block_and_block_state(&pos).await.unwrap();
world
.block_registry
.on_entity_collision(block, &world, self, pos, state)
.await;
}
}
}
}
}
#[async_trait]

View File

@ -64,7 +64,7 @@ use pumpkin_protocol::{
codec::var_int::VarInt,
};
use pumpkin_registry::DimensionType;
use pumpkin_util::math::{position::BlockPos, vector3::Vector3};
use pumpkin_util::math::{boundingbox::BoundingBox, position::BlockPos, vector3::Vector3};
use pumpkin_util::math::{position::chunk_section_from_pos, vector2::Vector2};
use pumpkin_util::text::{TextComponent, color::NamedColor};
use pumpkin_world::world::BlockFlags;
@ -1070,6 +1070,23 @@ impl World {
None
}
pub async fn get_entities_at_box(&self, aabb: &BoundingBox) -> Vec<Arc<dyn EntityBase>> {
let entities_guard = self.entities.read().await;
entities_guard
.values()
.filter(|entity| entity.get_entity().bounding_box.load().intersects(aabb))
.cloned()
.collect()
}
pub async fn get_players_at_box(&self, aabb: &BoundingBox) -> Vec<Arc<Player>> {
let players_guard = self.players.read().await;
players_guard
.values()
.filter(|player| player.get_entity().bounding_box.load().intersects(aabb))
.cloned()
.collect()
}
/// Retrieves a player by their unique UUID.
///
/// This function searches the world's active player list for a player with the specified UUID.