320 lines
10 KiB
Rust
320 lines
10 KiB
Rust
use std::sync::Arc;
|
|
|
|
use crate::block::BlockIsReplacing;
|
|
use crate::entity::EntityBase;
|
|
use crate::entity::player::Player;
|
|
use async_trait::async_trait;
|
|
use pumpkin_data::Block;
|
|
use pumpkin_data::BlockDirection;
|
|
use pumpkin_data::BlockState;
|
|
use pumpkin_data::FacingExt;
|
|
use pumpkin_data::HorizontalFacingExt;
|
|
use pumpkin_data::block_properties::BlockProperties;
|
|
use pumpkin_data::block_properties::Facing;
|
|
use pumpkin_protocol::server::play::SUseItemOn;
|
|
use pumpkin_util::math::position::BlockPos;
|
|
use pumpkin_world::BlockStateId;
|
|
use pumpkin_world::chunk::TickPriority;
|
|
use pumpkin_world::world::BlockAccessor;
|
|
use pumpkin_world::world::BlockFlags;
|
|
|
|
type RWallTorchProps = pumpkin_data::block_properties::FurnaceLikeProperties;
|
|
type RTorchProps = pumpkin_data::block_properties::RedstoneOreLikeProperties;
|
|
|
|
use crate::block::pumpkin_block::{BlockMetadata, PumpkinBlock};
|
|
use crate::server::Server;
|
|
use crate::world::World;
|
|
|
|
use super::get_redstone_power;
|
|
|
|
pub struct RedstoneTorchBlock;
|
|
|
|
impl BlockMetadata for RedstoneTorchBlock {
|
|
fn namespace(&self) -> &'static str {
|
|
"minecraft"
|
|
}
|
|
|
|
fn ids(&self) -> &'static [&'static str] {
|
|
&[Block::REDSTONE_TORCH.name, Block::REDSTONE_WALL_TORCH.name]
|
|
}
|
|
}
|
|
|
|
#[async_trait]
|
|
impl PumpkinBlock for RedstoneTorchBlock {
|
|
async fn on_place(
|
|
&self,
|
|
_server: &Server,
|
|
world: &World,
|
|
player: &Player,
|
|
block: &Block,
|
|
block_pos: &BlockPos,
|
|
face: BlockDirection,
|
|
replacing: BlockIsReplacing,
|
|
_use_item_on: &SUseItemOn,
|
|
) -> BlockStateId {
|
|
if face == BlockDirection::Down {
|
|
let support_block = world.get_block_state(&block_pos.down()).await;
|
|
if support_block.is_center_solid(BlockDirection::Up) {
|
|
return block.default_state.id;
|
|
}
|
|
}
|
|
let mut directions = player.get_entity().get_entity_facing_order();
|
|
|
|
if replacing == BlockIsReplacing::None {
|
|
let face = face.to_facing();
|
|
let mut i = 0;
|
|
while i < directions.len() && directions[i] != face {
|
|
i += 1;
|
|
}
|
|
|
|
if i > 0 {
|
|
directions.copy_within(0..i, 1);
|
|
directions[0] = face;
|
|
}
|
|
} else if directions[0] == Facing::Down {
|
|
let support_block = world.get_block_state(&block_pos.down()).await;
|
|
if support_block.is_center_solid(BlockDirection::Up) {
|
|
return block.default_state.id;
|
|
}
|
|
}
|
|
|
|
for dir in directions {
|
|
if dir != Facing::Up
|
|
&& dir != Facing::Down
|
|
&& can_place_at(world, block_pos, dir.to_block_direction()).await
|
|
{
|
|
let mut torch_props = RWallTorchProps::default(&Block::REDSTONE_WALL_TORCH);
|
|
torch_props.facing = dir
|
|
.opposite()
|
|
.to_block_direction()
|
|
.to_horizontal_facing()
|
|
.unwrap();
|
|
return torch_props.to_state_id(&Block::REDSTONE_WALL_TORCH);
|
|
}
|
|
}
|
|
|
|
let support_block = world.get_block_state(&block_pos.down()).await;
|
|
if support_block.is_center_solid(BlockDirection::Up) {
|
|
block.default_state.id
|
|
} else {
|
|
0
|
|
}
|
|
}
|
|
|
|
async fn can_place_at(
|
|
&self,
|
|
_server: Option<&Server>,
|
|
world: Option<&World>,
|
|
_block_accessor: &dyn BlockAccessor,
|
|
_player: Option<&Player>,
|
|
_block: &Block,
|
|
block_pos: &BlockPos,
|
|
_face: BlockDirection,
|
|
_use_item_on: Option<&SUseItemOn>,
|
|
) -> bool {
|
|
let world = world.unwrap();
|
|
let support_block = world.get_block_state(&block_pos.down()).await;
|
|
if support_block.is_center_solid(BlockDirection::Up) {
|
|
return true;
|
|
}
|
|
for dir in BlockDirection::horizontal() {
|
|
if can_place_at(world, block_pos, dir).await {
|
|
return true;
|
|
}
|
|
}
|
|
false
|
|
}
|
|
|
|
async fn get_state_for_neighbor_update(
|
|
&self,
|
|
world: &World,
|
|
block: &Block,
|
|
state: u16,
|
|
block_pos: &BlockPos,
|
|
direction: BlockDirection,
|
|
_neighbor_pos: &BlockPos,
|
|
_neighbor_state: u16,
|
|
) -> u16 {
|
|
if *block == Block::REDSTONE_WALL_TORCH {
|
|
let props = RWallTorchProps::from_state_id(state, block);
|
|
if props.facing.to_block_direction().opposite() == direction
|
|
&& !can_place_at(world, block_pos, props.facing.to_block_direction()).await
|
|
{
|
|
return 0;
|
|
}
|
|
} else if direction == BlockDirection::Down {
|
|
let support_block = world.get_block_state(&block_pos.down()).await;
|
|
if !support_block.is_center_solid(BlockDirection::Up) {
|
|
return 0;
|
|
}
|
|
}
|
|
state
|
|
}
|
|
|
|
async fn on_neighbor_update(
|
|
&self,
|
|
world: &Arc<World>,
|
|
block: &Block,
|
|
block_pos: &BlockPos,
|
|
_source_block: &Block,
|
|
_notify: bool,
|
|
) {
|
|
let state = world.get_block_state(block_pos).await;
|
|
|
|
if world.is_block_tick_scheduled(block_pos, block).await {
|
|
return;
|
|
}
|
|
|
|
if block == &Block::REDSTONE_WALL_TORCH {
|
|
let props = RWallTorchProps::from_state_id(state.id, block);
|
|
if props.lit
|
|
!= should_be_lit(
|
|
world,
|
|
block_pos,
|
|
props.facing.to_block_direction().opposite(),
|
|
)
|
|
.await
|
|
{
|
|
world
|
|
.schedule_block_tick(block, *block_pos, 2, TickPriority::Normal)
|
|
.await;
|
|
}
|
|
} else if block == &Block::REDSTONE_TORCH {
|
|
let props = RTorchProps::from_state_id(state.id, block);
|
|
if props.lit != should_be_lit(world, block_pos, BlockDirection::Down).await {
|
|
world
|
|
.schedule_block_tick(block, *block_pos, 2, TickPriority::Normal)
|
|
.await;
|
|
}
|
|
}
|
|
}
|
|
|
|
async fn emits_redstone_power(
|
|
&self,
|
|
_block: &Block,
|
|
_state: &BlockState,
|
|
_direction: BlockDirection,
|
|
) -> bool {
|
|
true
|
|
}
|
|
|
|
async fn get_weak_redstone_power(
|
|
&self,
|
|
block: &Block,
|
|
_world: &World,
|
|
_block_pos: &BlockPos,
|
|
state: &BlockState,
|
|
direction: BlockDirection,
|
|
) -> u8 {
|
|
if block == &Block::REDSTONE_WALL_TORCH {
|
|
let props = RWallTorchProps::from_state_id(state.id, block);
|
|
if props.lit && direction != props.facing.to_block_direction() {
|
|
return 15;
|
|
}
|
|
} else if block == &Block::REDSTONE_TORCH {
|
|
let props = RTorchProps::from_state_id(state.id, block);
|
|
if props.lit && direction != BlockDirection::Up {
|
|
return 15;
|
|
}
|
|
}
|
|
0
|
|
}
|
|
|
|
async fn get_strong_redstone_power(
|
|
&self,
|
|
block: &Block,
|
|
_world: &World,
|
|
_block_pos: &BlockPos,
|
|
state: &BlockState,
|
|
direction: BlockDirection,
|
|
) -> u8 {
|
|
if direction == BlockDirection::Down {
|
|
if block == &Block::REDSTONE_WALL_TORCH {
|
|
let props = RWallTorchProps::from_state_id(state.id, block);
|
|
if props.lit {
|
|
return 15;
|
|
}
|
|
} else if block == &Block::REDSTONE_TORCH {
|
|
let props = RTorchProps::from_state_id(state.id, block);
|
|
if props.lit {
|
|
return 15;
|
|
}
|
|
}
|
|
}
|
|
0
|
|
}
|
|
|
|
async fn on_scheduled_tick(&self, world: &Arc<World>, block: &Block, block_pos: &BlockPos) {
|
|
let state = world.get_block_state(block_pos).await;
|
|
if block == &Block::REDSTONE_WALL_TORCH {
|
|
let mut props = RWallTorchProps::from_state_id(state.id, block);
|
|
let should_be_lit_now = should_be_lit(
|
|
world,
|
|
block_pos,
|
|
props.facing.to_block_direction().opposite(),
|
|
)
|
|
.await;
|
|
if props.lit != should_be_lit_now {
|
|
props.lit = should_be_lit_now;
|
|
world
|
|
.set_block_state(block_pos, props.to_state_id(block), BlockFlags::NOTIFY_ALL)
|
|
.await;
|
|
update_neighbors(world, block_pos).await;
|
|
}
|
|
} else if block == &Block::REDSTONE_TORCH {
|
|
let mut props = RTorchProps::from_state_id(state.id, block);
|
|
let should_be_lit_now = should_be_lit(world, block_pos, BlockDirection::Down).await;
|
|
if props.lit != should_be_lit_now {
|
|
props.lit = should_be_lit_now;
|
|
world
|
|
.set_block_state(block_pos, props.to_state_id(block), BlockFlags::NOTIFY_ALL)
|
|
.await;
|
|
update_neighbors(world, block_pos).await;
|
|
}
|
|
}
|
|
}
|
|
|
|
async fn placed(
|
|
&self,
|
|
world: &Arc<World>,
|
|
_block: &Block,
|
|
_state_id: BlockStateId,
|
|
block_pos: &BlockPos,
|
|
_old_state_id: BlockStateId,
|
|
_notify: bool,
|
|
) {
|
|
update_neighbors(world, block_pos).await;
|
|
}
|
|
|
|
async fn on_state_replaced(
|
|
&self,
|
|
world: &Arc<World>,
|
|
_block: &Block,
|
|
location: BlockPos,
|
|
_old_state_id: BlockStateId,
|
|
_moved: bool,
|
|
) {
|
|
update_neighbors(world, &location).await;
|
|
}
|
|
}
|
|
|
|
pub async fn should_be_lit(world: &World, pos: &BlockPos, face: BlockDirection) -> bool {
|
|
let other_pos = pos.offset(face.to_offset());
|
|
let (block, state) = world.get_block_and_block_state(&other_pos).await;
|
|
get_redstone_power(&block, &state, world, &other_pos, face).await == 0
|
|
}
|
|
|
|
pub async fn update_neighbors(world: &Arc<World>, pos: &BlockPos) {
|
|
for dir in BlockDirection::all() {
|
|
let other_pos = pos.offset(dir.to_offset());
|
|
world.update_neighbors(&other_pos, None).await;
|
|
}
|
|
}
|
|
|
|
async fn can_place_at(world: &World, block_pos: &BlockPos, facing: BlockDirection) -> bool {
|
|
world
|
|
.get_block_state(&block_pos.offset(facing.to_offset()))
|
|
.await
|
|
.is_side_solid(facing.opposite())
|
|
}
|