diff --git a/steel-core/build/build.rs b/steel-core/build/build.rs index 2387fb14c2f6..ac040db9dc24 100644 --- a/steel-core/build/build.rs +++ b/steel-core/build/build.rs @@ -25,6 +25,7 @@ struct Classes { } pub fn main() { + // let manifest_dir = env::var("CARGO_MANIFEST_DIR").expect("CARGO_MANIFEST_DIR not set"); let out_dir = format!("{manifest_dir}/src/behavior/generated"); diff --git a/steel-core/src/behavior/blocks/mod.rs b/steel-core/src/behavior/blocks/mod.rs index 93a35b573f0d..1341efd41142 100644 --- a/steel-core/src/behavior/blocks/mod.rs +++ b/steel-core/src/behavior/blocks/mod.rs @@ -24,7 +24,7 @@ pub use decoration::{ }; pub use fluid::LiquidBlock; pub use portal::{EndPortalFrameBlock, FireBlock, NetherPortalBlock, SoulFireBlock}; -pub use redstone::{ButtonBlock, RedstoneTorchBlock, RedstoneWallTorchBlock}; +pub use redstone::{BellBlock, ButtonBlock, RedstoneTorchBlock, RedstoneWallTorchBlock}; pub use vegetation::{ AzaleaBlock, BambooSaplingBlock, BambooStalkBlock, BeetrootBlock, CactusBlock, CactusFlowerBlock, CarrotBlock, CropBlock, DoublePlantBlock, FlowerBlock, NetherSproutsBlock, diff --git a/steel-core/src/behavior/blocks/redstone/bell_block.rs b/steel-core/src/behavior/blocks/redstone/bell_block.rs new file mode 100644 index 000000000000..be8fd40a74a5 --- /dev/null +++ b/steel-core/src/behavior/blocks/redstone/bell_block.rs @@ -0,0 +1,321 @@ +use std::sync::{Arc, Weak}; + +use steel_macros::block_behavior; +use steel_protocol::packets::game::SoundSource; +use steel_registry::blocks::BlockRef; +use steel_registry::blocks::block_state_ext::BlockStateExt; +use steel_registry::blocks::properties::{BellAttachType, BlockStateProperties}; +use steel_registry::blocks::shapes::SupportType; +use steel_registry::sound_events; +use steel_registry::vanilla_blocks; +use steel_registry::vanilla_game_events; +use steel_utils::locks::SyncMutex; +use steel_utils::types::UpdateFlags; +use steel_utils::{BlockPos, BlockStateId, Direction, axis::Axis}; + +use crate::behavior::BlockBehavior; +use crate::behavior::InventoryAccess; +use crate::behavior::context::{BlockHitResult, BlockPlaceContext, InteractionResult}; +use crate::block_entity::SharedBlockEntity; +use crate::block_entity::entities::BellBlockEntity; +use crate::entity::Entity; +use crate::player::Player; +use crate::world::game_event_context::GameEventContext; +use crate::world::{LevelReader, ScheduledTickAccess, World}; + +/// Bell +#[block_behavior] +pub struct BellBlock { + block: BlockRef, +} + +impl BellBlock { + /// Creates new bell block + #[must_use] + pub const fn new(block: BlockRef) -> Self { + Self { block } + } + + /// Returns the direction the bell is connected to (ceiling/floor/wall). + fn get_connected_direction(state: BlockStateId) -> Direction { + let attachment = state + .try_get_value(&BlockStateProperties::BELL_ATTACHMENT) + .unwrap_or(BellAttachType::Floor); + match attachment { + BellAttachType::Floor => Direction::Up, + BellAttachType::Ceiling => Direction::Down, + _ => state + .try_get_value(&BlockStateProperties::FACING) + .unwrap_or(Direction::North), + } + } + + /// Checks whether the hit was on the correct side of the bell. + fn is_proper_hit(state: BlockStateId, clicked_direction: Direction, click_y: f64) -> bool { + if clicked_direction.axis() == Axis::Y || click_y > 0.8124 { + return false; + } + + let facing = state + .try_get_value(&BlockStateProperties::FACING) + .unwrap_or(Direction::North); + let attachment = state + .try_get_value(&BlockStateProperties::BELL_ATTACHMENT) + .unwrap_or(BellAttachType::Floor); + + match attachment { + BellAttachType::Floor => facing.axis() == clicked_direction.axis(), + BellAttachType::SingleWall | BellAttachType::DoubleWall => { + facing.axis() != clicked_direction.axis() + } + BellAttachType::Ceiling => true, + } + } + + /// Central ring logic: notifies the block entity and plays a sound. + fn attempt_to_ring( + &self, + ringing_entity: Option<&dyn Entity>, + world: &Arc, + pos: BlockPos, + direction: Option, + ) -> bool { + let block_entity = world.get_block_entity(pos); + if let Some(be) = block_entity { + let mut bell_be = be.lock(); + if let Some(bell) = bell_be.as_any_mut().downcast_mut::() { + let dir = direction.unwrap_or_else(|| { + world + .get_block_state(pos) + .try_get_value(&BlockStateProperties::FACING) + .unwrap_or(Direction::North) + }); + bell.on_hit(dir); + world.play_sound( + &sound_events::BLOCK_BELL_USE, + SoundSource::Blocks, + pos, + 2.0, + 1.0, + None, + ); + world.block_event(pos, self.block, 1, dir as u8); + world.game_event( + &vanilla_game_events::BLOCK_CHANGE, + pos, + &GameEventContext::new(ringing_entity, None), + ); + return true; + } + } + false + } +} + +impl BlockBehavior for BellBlock { + fn get_state_for_placement(&self, context: &BlockPlaceContext<'_>) -> Option { + let clicked_face = context.clicked_face; + let pos = context.clicked_pos.relative(clicked_face); + println!( + "get_state_for_placement: clicked_pos={:?}, clicked_face={:?}, pos={:?}", + context.clicked_pos, context.clicked_face, pos + ); + let world = context.world; + let axis = clicked_face.axis(); + + if axis == Axis::Y { + let attachment = if clicked_face == Direction::Down { + BellAttachType::Ceiling + } else { + BellAttachType::Floor + }; + let state = self + .block + .default_state() + .set_value(&BlockStateProperties::BELL_ATTACHMENT, attachment) + .set_value(&BlockStateProperties::FACING, context.horizontal_direction); + if self.can_survive(state, world, pos) { + return Some(state); + } + } else { + let double_attached = { + let west = world.get_block_state(pos.west()); + let east = world.get_block_state(pos.east()); + let north = world.get_block_state(pos.north()); + let south = world.get_block_state(pos.south()); + (axis == Axis::X + && west.is_face_sturdy(Direction::East) + && east.is_face_sturdy(Direction::West)) + || (axis == Axis::Z + && north.is_face_sturdy(Direction::South) + && south.is_face_sturdy(Direction::North)) + }; + + let state = self + .block + .default_state() + .set_value(&BlockStateProperties::FACING, clicked_face.opposite()) + .set_value( + &BlockStateProperties::BELL_ATTACHMENT, + if double_attached { + BellAttachType::DoubleWall + } else { + BellAttachType::SingleWall + }, + ); + + if self.can_survive(state, world, pos) { + return Some(state); + } + + // Fallback: floor/ceiling + let below = world.get_block_state(pos.below()); + let can_attach_below = below.is_face_sturdy(Direction::Up); + let fallback = state.set_value( + &BlockStateProperties::BELL_ATTACHMENT, + if can_attach_below { + BellAttachType::Floor + } else { + BellAttachType::Ceiling + }, + ); + if self.can_survive(fallback, world, pos) { + return Some(fallback); + } + } + + None + } + + fn handle_neighbor_changed( + &self, + state: BlockStateId, + world: &Arc, + pos: BlockPos, + _source_block: BlockRef, + _moved_by_piston: bool, + ) { + // FIXME: Replace with proper neighbor signal check when World API is available. + let signal = false; // TODO: world.has_neighbor_signal(pos); + let powered = state + .try_get_value(&BlockStateProperties::POWERED) + .unwrap_or(false); + + if signal != powered { + if signal { + self.attempt_to_ring(None, world, pos, None); + } + let new_state = state.set_value(&BlockStateProperties::POWERED, signal); + world.set_block(pos, new_state, UpdateFlags::UPDATE_ALL); + } + } + + fn use_without_item( + &self, + state: BlockStateId, + world: &Arc, + pos: BlockPos, + player: &Player, + hit_result: &BlockHitResult, + _inv: &mut InventoryAccess, + ) -> InteractionResult { + let direction = hit_result.direction; + let click_y = hit_result.location.y - f64::from(pos.y()); + if Self::is_proper_hit(state, direction, click_y) + && self.attempt_to_ring(Some(player as &dyn Entity), world, pos, Some(direction)) + { + // TODO: award stat BELL_RING when stats are available + return InteractionResult::Success; + } + InteractionResult::Pass + } + + fn update_shape( + &self, + state: BlockStateId, + world: &dyn ScheduledTickAccess, + pos: BlockPos, + direction_to_neighbor: Direction, + _neighbor_pos: BlockPos, + neighbor_state: BlockStateId, + ) -> BlockStateId { + let attachment = state + .try_get_value(&BlockStateProperties::BELL_ATTACHMENT) + .unwrap_or(BellAttachType::Floor); + let connected_dir = Self::get_connected_direction(state).opposite(); + + // If the support block disappears, break unless double wall. + if connected_dir == direction_to_neighbor + && !self.can_survive(state, world, pos) + && attachment != BellAttachType::DoubleWall + { + return vanilla_blocks::AIR.default_state(); + } + + let facing = state + .try_get_value(&BlockStateProperties::FACING) + .unwrap_or(Direction::North); + + if direction_to_neighbor.axis() == facing.axis() { + if attachment == BellAttachType::DoubleWall + && !neighbor_state.is_face_sturdy(direction_to_neighbor) + { + // One side lost support -> become single wall. + return state + .set_value( + &BlockStateProperties::BELL_ATTACHMENT, + BellAttachType::SingleWall, + ) + .set_value( + &BlockStateProperties::FACING, + direction_to_neighbor.opposite(), + ); + } + + if attachment == BellAttachType::SingleWall + && connected_dir.opposite() == direction_to_neighbor + && neighbor_state.is_face_sturdy(facing) + { + // Gained support on the other side -> become double wall. + return state.set_value( + &BlockStateProperties::BELL_ATTACHMENT, + BellAttachType::DoubleWall, + ); + } + } + + state // No change needed + } + + fn can_survive(&self, state: BlockStateId, world: &dyn LevelReader, pos: BlockPos) -> bool { + let support_dir = Self::get_connected_direction(state); // уже направление к опоре + if support_dir == Direction::Down { + // колокол висит на потолке → опора сверху + world + .get_block_state(pos.above()) + .is_face_sturdy_for(Direction::Down, SupportType::Center) + } else { + world + .get_block_state(pos.relative(support_dir)) + .is_face_sturdy_for(support_dir.opposite(), SupportType::Full) + } + } + + // get_collision_shape не переопределён – используется стандартная реализация, + // которая берёт форму из Block (state.get_collision_shape()). + + fn has_block_entity(&self) -> bool { + true + } + + fn new_block_entity( + &self, + level: Weak, + pos: BlockPos, + state: BlockStateId, + ) -> Option { + Some(Arc::new(SyncMutex::new(BellBlockEntity::new( + level, pos, state, + )))) + } +} diff --git a/steel-core/src/behavior/blocks/redstone/mod.rs b/steel-core/src/behavior/blocks/redstone/mod.rs index ea3e711a193d..3e5a98f75705 100644 --- a/steel-core/src/behavior/blocks/redstone/mod.rs +++ b/steel-core/src/behavior/blocks/redstone/mod.rs @@ -1,5 +1,7 @@ +mod bell_block; mod button_block; mod redstone_torch_block; +pub use bell_block::BellBlock; pub use button_block::ButtonBlock; pub use redstone_torch_block::{RedstoneTorchBlock, RedstoneWallTorchBlock}; diff --git a/steel-core/src/block_entity/entities/bell.rs b/steel-core/src/block_entity/entities/bell.rs new file mode 100644 index 000000000000..4ac2d590a66b --- /dev/null +++ b/steel-core/src/block_entity/entities/bell.rs @@ -0,0 +1,240 @@ +use std::{ + any::Any, + sync::{Arc, Weak}, +}; + +use glam::DVec3; +use simdnbt::{borrow::BaseNbtCompound, owned::NbtCompound}; +use steel_protocol::packets::game::SoundSource; +use steel_registry::{ + TaggedRegistryExt, block_entity_type::BlockEntityTypeRef, sound_events, + vanilla_block_entity_types, vanilla_entity_type_tags::EntityTypeTag, +}; +use steel_utils::{BlockPos, BlockStateId, Direction, WorldAabb}; + +use crate::{block_entity::BlockEntity, entity::Entity, world::World}; + +/// Bell block entity +pub struct BellBlockEntity { + level: Weak, + position: BlockPos, + state: BlockStateId, + removed: bool, + + shaking: bool, + ticks: i32, + click_direction: Option, + last_ring_timestamp: i64, + resonating: bool, + resonation_ticks: i32, +} + +impl BellBlockEntity { + /// Creates new bell block entity + #[must_use] + pub const fn new(level: Weak, position: BlockPos, state: BlockStateId) -> Self { + Self { + level, + position, + state, + removed: false, + shaking: false, + ticks: 0, + click_direction: None, + last_ring_timestamp: 0, + resonating: false, + resonation_ticks: 0, + } + } + + /// Called when the bell is struck (player, projectile, redstone). + pub fn on_hit(&mut self, direction: Direction) { + self.click_direction = Some(direction); + if self.shaking { + self.ticks = 0; + } else { + self.shaking = true; + } + self.resonating = false; + self.resonation_ticks = 0; + self.update_nearby_entities(); + self.set_changed(); + } + + /// Refreshes the cached list of nearby entities and sets their `HEARD_BELL_TIME` memory. + fn update_nearby_entities(&mut self) { + let Some(world) = self.level.upgrade() else { + return; + }; + let now = world.level_data.read().game_time(); + if now <= self.last_ring_timestamp + 60 { + return; // already up‑to‑date + } + self.last_ring_timestamp = now; + + // Query living entities in a 48‑block cube. + let aabb = WorldAabb::new( + f64::from(self.position.x()) - 48.0, + f64::from(self.position.y()) - 48.0, + f64::from(self.position.z()) - 48.0, + f64::from(self.position.x()) + 48.0, + f64::from(self.position.y()) + 48.0, + f64::from(self.position.z()) + 48.0, + ); + let entities: Vec> = world + .get_entities_in_aabb(&aabb) + .into_iter() + .filter(|e| e.is_living_entity()) + .collect(); + + // Set memory for entities within 32 blocks (server side only). + for entity in &entities { + if entity.is_alive() && !entity.is_removed() { + let dist_sq = entity.position().distance_squared(DVec3::new( + f64::from(self.position.x()) + 0.5, + f64::from(self.position.y()) + 0.5, + f64::from(self.position.z()) + 0.5, + )); + if dist_sq <= 32.0 * 32.0 { + // TODO: set MemoryModuleType::HEARD_BELL_TIME when AI system is available. + // entity.brain().set_memory(MemoryModuleType::HEARD_BELL_TIME, now); + } + } + } + } + + /// Returns true if any raider is within 32 blocks. + fn are_raiders_nearby(world: &World, pos: BlockPos) -> bool { + let aabb = WorldAabb::new( + f64::from(pos.x()) - 32.0, + f64::from(pos.y()) - 32.0, + f64::from(pos.z()) - 32.0, + f64::from(pos.x()) + 32.0, + f64::from(pos.y()) + 32.0, + f64::from(pos.z()) + 32.0, + ); + for entity in world.get_entities_in_aabb(&aabb) { + if entity.is_alive() + && !entity.is_removed() + && steel_registry::REGISTRY + .entity_types + .is_in_tag(entity.entity_type(), &EntityTypeTag::RAIDERS) + { + return true; + } + } + false + } + + /// Applies the Glowing effect to all raiders within 48 blocks. + fn make_raiders_glow(world: &World, pos: BlockPos) { + let aabb = WorldAabb::new( + f64::from(pos.x()) - 48.0, + f64::from(pos.y()) - 48.0, + f64::from(pos.z()) - 48.0, + f64::from(pos.x()) + 48.0, + f64::from(pos.y()) + 48.0, + f64::from(pos.z()) + 48.0, + ); + for entity in world.get_entities_in_aabb(&aabb) { + if entity.is_alive() + && !entity.is_removed() + && steel_registry::REGISTRY + .entity_types + .is_in_tag(entity.entity_type(), &EntityTypeTag::RAIDERS) + { + // TODO: add effect via entity.add_effect(MobEffects::GLOWING, 60) when available. + } + } + } +} + +impl BlockEntity for BellBlockEntity { + fn as_any(&self) -> &dyn Any { + self + } + + fn as_any_mut(&mut self) -> &mut dyn Any { + self + } + + fn get_type(&self) -> BlockEntityTypeRef { + &vanilla_block_entity_types::BELL + } + + fn get_block_pos(&self) -> BlockPos { + self.position + } + + fn get_block_state(&self) -> BlockStateId { + self.state + } + + fn set_block_state(&mut self, state: BlockStateId) { + self.state = state; + } + + fn is_removed(&self) -> bool { + self.removed + } + + fn set_removed(&mut self) { + self.removed = true; + } + + fn clear_removed(&mut self) { + self.removed = false; + } + + fn get_level(&self) -> Option> { + self.level.upgrade() + } + + fn load_additional(&mut self, _nbt: &BaseNbtCompound<'_>) { + // Nothing to load beyond position/state. + } + + fn save_additional(&self, _nbt: &mut NbtCompound) { + // Nothing to save beyond position/state. + } + + fn is_ticking(&self) -> bool { + true + } + + fn tick(&mut self, world: &Arc) { + if self.shaking { + self.ticks += 1; + } + + if self.ticks >= 50 { + self.shaking = false; + self.ticks = 0; + self.set_changed(); + } + + if self.ticks >= 5 + && self.resonation_ticks == 0 + && Self::are_raiders_nearby(world, self.position) + { + self.resonating = true; + world.play_sound( + &sound_events::BLOCK_BELL_RESONATE, + SoundSource::Blocks, + self.position, + 2.0_f32, + 1.0_f32, + None, + ); + } + + if self.resonating { + self.resonation_ticks += 1; + if self.resonation_ticks >= 40 { + Self::make_raiders_glow(world, self.position); + self.resonating = false; + self.resonation_ticks = 0; + } + } + } +} diff --git a/steel-core/src/block_entity/entities/mod.rs b/steel-core/src/block_entity/entities/mod.rs index 296728dc40e9..ed459d767aa7 100644 --- a/steel-core/src/block_entity/entities/mod.rs +++ b/steel-core/src/block_entity/entities/mod.rs @@ -2,6 +2,7 @@ mod barrel; mod beehive; +mod bell; mod raw; mod sign; @@ -9,5 +10,6 @@ pub use barrel::{BARREL_SLOTS, BarrelBlockEntity}; pub use beehive::{ BEEHIVE_MAX_OCCUPANTS, BEEHIVE_MIN_OCCUPATION_TICKS_NECTARLESS, BeehiveBlockEntity, }; +pub use bell::BellBlockEntity; pub use raw::RawBlockEntity; pub use sign::{SIGN_LINES, SignBlockEntity, SignText}; diff --git a/steel-core/src/entity/mod.rs b/steel-core/src/entity/mod.rs index 6f93d495cb8c..9aef5b2e5e41 100644 --- a/steel-core/src/entity/mod.rs +++ b/steel-core/src/entity/mod.rs @@ -774,6 +774,14 @@ pub trait Entity: EntityEventSource + Send + Sync { false } + /// Returns the item stack that creative middle-click picks from this entity. + /// + /// Override in entity types that support picking (e.g., boats, minecarts, + /// item frames, paintings). Default returns `None`. + fn pick_item(&self) -> Option { + None + } + /// Returns whether this entity participates in vanilla push separation. /// /// Mirrors vanilla `Entity.isPushable`. Base entities are not pushable unless diff --git a/steel-core/src/player/game_mode.rs b/steel-core/src/player/game_mode.rs index 0d09725cfb96..ea4e27b65a02 100644 --- a/steel-core/src/player/game_mode.rs +++ b/steel-core/src/player/game_mode.rs @@ -8,7 +8,7 @@ use std::sync::Arc; use steel_protocol::packets::game::{ AnimateAction, CAnimate, CBlockChangedAck, CBlockUpdate, CChangeDifficulty, CGameEvent, COpenSignEditor, CPlayerInfoUpdate, CSetHeldSlot, GameEventType, PlayerAction, - SPickItemFromBlock, SPlayerAction, SSignUpdate, SUseItem, SUseItemOn, + SPickItemFromBlock, SPickItemFromEntity, SPlayerAction, SSignUpdate, SUseItem, SUseItemOn, }; use steel_registry::blocks::block_state_ext::BlockStateExt; use steel_registry::blocks::properties::Direction; @@ -603,6 +603,49 @@ impl Player { .broadcast_changes(&self.connection); } + /// Handles the creative pick item from entity packet (middle click on an entity). + pub fn handle_pick_item_from_entity(&self, packet: SPickItemFromEntity) { + if !self.has_infinite_materials() { + return; + } + + let world = self.get_world(); + let Some(entity) = world.get_entity_by_id(packet.entity_id) else { + return; + }; + + let Some(item_stack) = entity.pick_item() else { + return; + }; + + if item_stack.is_empty() { + return; + } + + // TODO: If include_data, add entity NBT data to the item stack + + let mut inventory = self.inventory.lock(); + let slot_with_item = inventory.find_slot_matching_item(&item_stack); + + if slot_with_item == -1 { + inventory.add_and_pick_item(item_stack); + } else if PlayerInventory::is_hotbar_slot(slot_with_item as usize) { + inventory.set_selected_slot(slot_with_item as u8); + } else { + inventory.pick_slot(slot_with_item); + } + + self.send_packet(CSetHeldSlot { + slot: i32::from(inventory.get_selected_slot()), + }); + + drop(inventory); + self.inventory_menu + .lock() + .behavior_mut() + .broadcast_changes(&self.connection); + } + /// Handles a sign update packet from the client. pub fn handle_sign_update(&self, packet: SSignUpdate) { if !self.is_within_block_interaction_range(packet.pos) { diff --git a/steel-core/src/player/networking.rs b/steel-core/src/player/networking.rs index ff72df60dc62..de62330098be 100644 --- a/steel-core/src/player/networking.rs +++ b/steel-core/src/player/networking.rs @@ -15,9 +15,9 @@ use steel_protocol::packets::game::{ SChatCommand, SChatSessionUpdate, SChunkBatchReceived, SClientCommand, SClientTickEnd, SCommandSuggestion, SContainerButtonClick, SContainerClick, SContainerClose, SContainerSlotStateChanged, SMovePlayerPos, SMovePlayerPosRot, SMovePlayerRot, - SMovePlayerStatusOnly, SMoveVehicle, SPickItemFromBlock, SPlayerAbilities, SPlayerAction, - SPlayerCommand, SPlayerInput, SPlayerLoad, SSetCarriedItem, SSetCreativeModeSlot, SSignUpdate, - SSwing, SUseItem, SUseItemOn, + SMovePlayerStatusOnly, SMoveVehicle, SPickItemFromBlock, SPickItemFromEntity, SPlayerAbilities, + SPlayerAction, SPlayerCommand, SPlayerInput, SPlayerLoad, SSetCarriedItem, + SSetCreativeModeSlot, SSignUpdate, SSwing, SUseItem, SUseItemOn, }; use steel_protocol::utils::{ConnectionProtocol, PacketError, RawPacket}; @@ -432,6 +432,10 @@ impl JavaConnection { let packet = SPickItemFromBlock::read_packet(data)?; player.handle_pick_item_from_block(packet); } + play::S_PICK_ITEM_FROM_ENTITY => { + let packet = SPickItemFromEntity::read_packet(data)?; + player.handle_pick_item_from_entity(packet); + } play::S_SIGN_UPDATE => { let packet = SSignUpdate::read_packet(data)?; player.handle_sign_update(packet); diff --git a/steel-core/src/worldgen/feature/features/lake.rs b/steel-core/src/worldgen/feature/features/lake.rs index 1cf98abd46e8..38d28558733a 100644 --- a/steel-core/src/worldgen/feature/features/lake.rs +++ b/steel-core/src/worldgen/feature/features/lake.rs @@ -176,7 +176,7 @@ impl FeatureDecorationRunner { } } - fn lake_is_boundary(grid: &[bool], x: i32, y: i32, z: i32) -> bool { + const fn lake_is_boundary(grid: &[bool], x: i32, y: i32, z: i32) -> bool { !grid[Self::lake_index(x, y, z)] && ((x < 15 && grid[Self::lake_index(x + 1, y, z)]) || (x > 0 && grid[Self::lake_index(x - 1, y, z)]) diff --git a/steel-protocol/src/packets/game/mod.rs b/steel-protocol/src/packets/game/mod.rs index 2bfce8f0d81e..cb77b8fff96f 100644 --- a/steel-protocol/src/packets/game/mod.rs +++ b/steel-protocol/src/packets/game/mod.rs @@ -77,6 +77,7 @@ mod s_container_slot_state_changed; mod s_move_player; mod s_move_vehicle; mod s_pick_item_from_block; +mod s_pick_item_from_entity; mod s_player_abilities; mod s_player_action; mod s_player_command; @@ -182,6 +183,7 @@ pub use s_move_player::{ }; pub use s_move_vehicle::SMoveVehicle; pub use s_pick_item_from_block::SPickItemFromBlock; +pub use s_pick_item_from_entity::SPickItemFromEntity; pub use s_player_abilities::SPlayerAbilities; pub use s_player_action::{PlayerAction, SPlayerAction}; pub use s_player_command::{PlayerCommandAction, SPlayerCommand}; diff --git a/steel-protocol/src/packets/game/s_pick_item_from_entity.rs b/steel-protocol/src/packets/game/s_pick_item_from_entity.rs new file mode 100644 index 000000000000..4451b180bc3e --- /dev/null +++ b/steel-protocol/src/packets/game/s_pick_item_from_entity.rs @@ -0,0 +1,9 @@ +use steel_macros::{ReadFrom, ServerPacket}; + +/// Serverbound packet sent when a player uses the pick block key (middle click) on a entity. +#[derive(ReadFrom, ServerPacket, Clone, Debug)] +pub struct SPickItemFromEntity { + #[read(as = VarInt)] + pub entity_id: i32, + pub include_data: bool, +} diff --git a/steel-registry/src/blocks/block_state_ext.rs b/steel-registry/src/blocks/block_state_ext.rs index 9e584730b6f9..ab266ab6b284 100644 --- a/steel-registry/src/blocks/block_state_ext.rs +++ b/steel-registry/src/blocks/block_state_ext.rs @@ -94,7 +94,12 @@ impl BlockStateExt for BlockStateId { } fn get_support_shape(&self) -> blocks::shapes::VoxelShape { - REGISTRY.blocks.get_support_shape(*self) + let shape = REGISTRY.blocks.get_support_shape(*self); + if shape.is_empty() { + self.get_collision_shape() + } else { + shape + } } fn get_outline_shape(&self) -> blocks::shapes::VoxelShape { diff --git a/steel-utils/src/direction.rs b/steel-utils/src/direction.rs index a42e9d18ade4..96582b7cd0da 100644 --- a/steel-utils/src/direction.rs +++ b/steel-utils/src/direction.rs @@ -9,6 +9,7 @@ use crate::{axis::Axis, codec::VarInt, serial::ReadFrom, types::BlockPos}; /// The six cardinal directions in Minecraft. #[derive(Clone, Copy, Debug)] #[derive_const(PartialEq)] +#[repr(u8)] pub enum Direction { /// Negative Y direction. Down, diff --git a/steel-worldgen/src/structure/ocean_monument.rs b/steel-worldgen/src/structure/ocean_monument.rs index fc8e910f52f3..945824328289 100644 --- a/steel-worldgen/src/structure/ocean_monument.rs +++ b/steel-worldgen/src/structure/ocean_monument.rs @@ -569,7 +569,7 @@ fn connected_room(rooms: &[RoomDefinition], room: usize, direction: Direction) - connection } -fn set_connection( +const fn set_connection( rooms: &mut [RoomDefinition], room: usize, direction: Direction, diff --git a/steel-worldgen/src/surface.rs b/steel-worldgen/src/surface.rs index 3bad8a0aca15..646153e0f9ca 100644 --- a/steel-worldgen/src/surface.rs +++ b/steel-worldgen/src/surface.rs @@ -96,7 +96,7 @@ impl<'a> SurfaceRuleContext<'a> { /// Returns a pre-resolved block state emitted by the generated surface rule. #[must_use] - pub fn block_state(&self, block_state_index: usize) -> BlockStateId { + pub const fn block_state(&self, block_state_index: usize) -> BlockStateId { self.block_states[block_state_index] }