Add Flint and Steel

This commit is contained in:
Alexander Medvedev 2025-05-03 15:54:41 +02:00
parent 3e2ee98a93
commit c32b74f584
9 changed files with 361 additions and 28 deletions

View File

@ -0,0 +1,96 @@
use std::sync::Arc;
use pumpkin_world::chunk::TickPriority;
use rand::Rng;
use crate::block::pumpkin_block::PumpkinBlock;
use crate::entity::player::Player;
use crate::server::Server;
use crate::world::World;
use async_trait::async_trait;
use pumpkin_data::{Block, BlockState};
use pumpkin_macros::pumpkin_block;
use pumpkin_util::math::position::BlockPos;
use pumpkin_world::BlockStateId;
use pumpkin_world::block::BlockDirection;
use super::FireBlockBase;
#[pumpkin_block("minecraft:fire")]
pub struct FireBlock;
impl FireBlock {
pub fn get_fire_tick_delay() -> i32 {
30 + rand::thread_rng().gen_range(0..10)
}
}
#[async_trait]
impl PumpkinBlock for FireBlock {
async fn placed(
&self,
world: &Arc<World>,
block: &Block,
state_id: BlockStateId,
pos: &BlockPos,
old_state_id: BlockStateId,
notify: bool,
) {
FireBlockBase::placed(
&FireBlockBase,
world,
block,
state_id,
pos,
old_state_id,
notify,
)
.await;
world
.schedule_block_tick(
block,
*pos,
Self::get_fire_tick_delay() as u16,
TickPriority::Normal,
)
.await;
}
async fn get_state_for_neighbor_update(
&self,
world: &World,
_block: &Block,
state: BlockStateId,
block_pos: &BlockPos,
direction: BlockDirection,
_neighbor_pos: &BlockPos,
_neighbor_state: BlockStateId,
) -> BlockStateId {
// TODO: add can_place_at
if self.can_place_at(world, block_pos, direction).await {
return state;
}
Block::AIR.default_state_id
}
async fn broken(
&self,
block: &Block,
player: &Player,
position: BlockPos,
server: &Server,
world: Arc<World>,
state: BlockState,
) {
FireBlockBase::broken(
&FireBlockBase,
block,
player,
position,
server,
world,
state,
)
.await;
}
}

View File

@ -0,0 +1,80 @@
use std::sync::Arc;
use crate::block::pumpkin_block::PumpkinBlock;
use crate::entity::player::Player;
use crate::server::Server;
use crate::world::World;
use async_trait::async_trait;
use pumpkin_data::world::WorldEvent;
use pumpkin_data::{Block, BlockState};
use pumpkin_util::math::position::BlockPos;
use pumpkin_world::BlockStateId;
use pumpkin_world::block::BlockDirection;
use soul_fire::SoulFireBlock;
#[expect(clippy::module_inception)]
pub mod fire;
pub mod soul_fire;
pub struct FireBlockBase;
impl FireBlockBase {
pub async fn get_state(world: &World, pos: &BlockPos) -> Block {
let (block, _block_state) = world.get_block_and_block_state(&pos.down()).await.unwrap();
if SoulFireBlock::is_soul_base(&block) {
return Block::SOUL_FIRE;
}
// TODO
Block::FIRE
}
}
#[async_trait]
impl PumpkinBlock for FireBlockBase {
async fn placed(
&self,
_world: &Arc<World>,
_block: &Block,
state_id: BlockStateId,
_pos: &BlockPos,
old_state_id: BlockStateId,
_notify: bool,
) {
if old_state_id == state_id {
return;
}
}
async fn can_place_at(
&self,
world: &World,
block_pos: &BlockPos,
face: BlockDirection,
) -> bool {
let block_state = world.get_block_state(block_pos).await.unwrap();
if !block_state.is_air() {
return false;
}
let block = Self::get_state(world, block_pos).await;
if let Some(block) = world.block_registry.get_pumpkin_block(&block) {
return block.can_place_at(world, block_pos, face).await;
}
return false;
}
async fn broken(
&self,
_block: &Block,
_player: &Player,
position: BlockPos,
_server: &Server,
world: Arc<World>,
_state: BlockState,
) {
world
.sync_world_event(WorldEvent::FireExtinguished, position, 0)
.await;
}
}

View File

@ -0,0 +1,97 @@
use std::sync::Arc;
use pumpkin_data::tag::Tagable;
use crate::block::pumpkin_block::PumpkinBlock;
use crate::entity::player::Player;
use crate::server::Server;
use crate::world::World;
use async_trait::async_trait;
use pumpkin_data::{Block, BlockState};
use pumpkin_macros::pumpkin_block;
use pumpkin_util::math::position::BlockPos;
use pumpkin_world::BlockStateId;
use pumpkin_world::block::BlockDirection;
use super::FireBlockBase;
#[pumpkin_block("minecraft:soul_fire")]
pub struct SoulFireBlock;
impl SoulFireBlock {
pub fn is_soul_base(block: &Block) -> bool {
block
.is_tagged_with("minecraft:soul_fire_base_blocks")
.unwrap()
}
}
#[async_trait]
impl PumpkinBlock for SoulFireBlock {
async fn placed(
&self,
world: &Arc<World>,
block: &Block,
state_id: BlockStateId,
pos: &BlockPos,
old_state_id: BlockStateId,
notify: bool,
) {
FireBlockBase::placed(
&FireBlockBase,
world,
block,
state_id,
pos,
old_state_id,
notify,
)
.await;
}
async fn get_state_for_neighbor_update(
&self,
world: &World,
_block: &Block,
state: BlockStateId,
block_pos: &BlockPos,
direction: BlockDirection,
_neighbor_pos: &BlockPos,
_neighbor_state: BlockStateId,
) -> BlockStateId {
if self.can_place_at(world, block_pos, direction).await {
return state;
}
Block::AIR.default_state_id
}
async fn can_place_at(
&self,
world: &World,
block_pos: &BlockPos,
_face: BlockDirection,
) -> bool {
Self::is_soul_base(&world.get_block(&block_pos.down()).await.unwrap())
}
async fn broken(
&self,
block: &Block,
player: &Player,
position: BlockPos,
server: &Server,
world: Arc<World>,
state: BlockState,
) {
FireBlockBase::broken(
&FireBlockBase,
block,
player,
position,
server,
world,
state,
)
.await;
}
}

View File

@ -7,6 +7,7 @@ use crate::server::Server;
use crate::world::{BlockFlags, World}; use crate::world::{BlockFlags, World};
use async_trait::async_trait; use async_trait::async_trait;
use pumpkin_data::item::Item; use pumpkin_data::item::Item;
use pumpkin_data::world::WorldEvent;
use pumpkin_data::{ use pumpkin_data::{
Block, BlockState, Block, BlockState,
block_properties::{BlockProperties, JukeboxLikeProperties}, block_properties::{BlockProperties, JukeboxLikeProperties},
@ -41,9 +42,11 @@ impl JukeboxBlock {
.await; .await;
} }
async fn stop_music(&self, block: &Block, location: BlockPos, world: &Arc<World>) { async fn stop_music(&self, block: &Block, position: BlockPos, world: &Arc<World>) {
self.set_record(false, block, location, world).await; self.set_record(false, block, position, world).await;
world.stop_record(location).await; world
.sync_world_event(WorldEvent::JukeboxStopsPlaying, position, 0)
.await;
} }
} }
@ -95,7 +98,13 @@ impl PumpkinBlock for JukeboxBlock {
//TODO: Update block nbt //TODO: Update block nbt
self.set_record(true, block, location, world).await; self.set_record(true, block, location, world).await;
world.play_record(jukebox_song as i32, location).await; world
.sync_world_event(
WorldEvent::JukeboxStartsPlaying,
location,
jukebox_song as i32,
)
.await;
BlockActionResult::Consume BlockActionResult::Consume
} }
@ -104,12 +113,14 @@ impl PumpkinBlock for JukeboxBlock {
&self, &self,
_block: &Block, _block: &Block,
_player: &Player, _player: &Player,
location: BlockPos, position: BlockPos,
_server: &Server, _server: &Server,
world: Arc<World>, world: Arc<World>,
_state: BlockState, _state: BlockState,
) { ) {
// For now just stop the music at this position // For now just stop the music at this position
world.stop_record(location).await; world
.sync_world_event(WorldEvent::JukeboxStopsPlaying, position, 0)
.await;
} }
} }

View File

@ -13,6 +13,7 @@ pub mod doors;
pub mod farmland; pub mod farmland;
pub mod fence_gates; pub mod fence_gates;
pub mod fences; pub mod fences;
pub mod fire;
pub mod furnace; pub mod furnace;
pub mod glass_panes; pub mod glass_panes;
pub mod iron_bars; pub mod iron_bars;

View File

@ -4,6 +4,8 @@ use blocks::doors::DoorBlock;
use blocks::farmland::FarmLandBlock; use blocks::farmland::FarmLandBlock;
use blocks::fence_gates::FenceGateBlock; use blocks::fence_gates::FenceGateBlock;
use blocks::fences::FenceBlock; use blocks::fences::FenceBlock;
use blocks::fire::fire::FireBlock;
use blocks::fire::soul_fire::SoulFireBlock;
use blocks::glass_panes::GlassPaneBlock; use blocks::glass_panes::GlassPaneBlock;
use blocks::iron_bars::IronBarsBlock; use blocks::iron_bars::IronBarsBlock;
use blocks::logs::LogBlock; use blocks::logs::LogBlock;
@ -52,7 +54,7 @@ use crate::{block::blocks::crafting_table::CraftingTableBlock, entity::player::P
use crate::{block::blocks::jukebox::JukeboxBlock, entity::experience_orb::ExperienceOrbEntity}; use crate::{block::blocks::jukebox::JukeboxBlock, entity::experience_orb::ExperienceOrbEntity};
use std::sync::Arc; use std::sync::Arc;
mod blocks; pub(crate) mod blocks;
mod fluids; mod fluids;
pub mod pumpkin_block; pub mod pumpkin_block;
pub mod pumpkin_fluid; pub mod pumpkin_fluid;
@ -84,6 +86,10 @@ pub fn default_registry() -> Arc<BlockRegistry> {
manager.register(TorchBlock); manager.register(TorchBlock);
manager.register(WallBlock); manager.register(WallBlock);
// Fire
manager.register(SoulFireBlock);
manager.register(FireBlock);
// Redstone // Redstone
manager.register(ButtonBlock); manager.register(ButtonBlock);
manager.register(LeverBlock); manager.register(LeverBlock);

View File

@ -0,0 +1,54 @@
use crate::block::blocks::fire::FireBlockBase;
use crate::block::pumpkin_block::PumpkinBlock;
use crate::entity::player::Player;
use crate::item::pumpkin_item::{ItemMetadata, PumpkinItem};
use crate::server::Server;
use crate::world::BlockFlags;
use async_trait::async_trait;
use pumpkin_data::Block;
use pumpkin_data::item::Item;
use pumpkin_util::math::position::BlockPos;
use pumpkin_world::block::{BlockDirection, HorizontalFacingExt};
pub struct FlintAndSteelItem;
impl ItemMetadata for FlintAndSteelItem {
fn ids() -> Box<[u16]> {
[Item::FLINT_AND_STEEL.id].into()
}
}
#[async_trait]
impl PumpkinItem for FlintAndSteelItem {
async fn use_on_block(
&self,
_item: &Item,
player: &Player,
location: BlockPos,
face: BlockDirection,
_block: &Block,
_server: &Server,
) {
// TODO: check CampfireBlock, CandleBlock and CandleCakeBlock
let world = player.world().await;
let pos = location.offset(face.to_offset());
if FireBlockBase::can_place_at(
&FireBlockBase,
&world,
&pos,
player
.living_entity
.entity
.get_horizontal_facing()
.to_block_direction(),
)
.await
{
let state = FireBlockBase::get_state(&world, &pos).await;
world
.set_block_state(&pos, state.default_state_id, BlockFlags::NOTIFY_ALL)
.await;
// TODO
}
}
}

View File

@ -1,5 +1,6 @@
mod axe; mod axe;
mod egg; mod egg;
mod flint_and_steel;
mod hoe; mod hoe;
mod honeycomb; mod honeycomb;
mod shovel; mod shovel;
@ -9,6 +10,7 @@ mod trident;
use axe::AxeItem; use axe::AxeItem;
use egg::EggItem; use egg::EggItem;
use flint_and_steel::FlintAndSteelItem;
use hoe::HoeItem; use hoe::HoeItem;
use honeycomb::HoneyCombItem; use honeycomb::HoneyCombItem;
use shovel::ShovelItem; use shovel::ShovelItem;
@ -25,6 +27,7 @@ pub fn default_registry() -> Arc<ItemRegistry> {
manager.register(SnowBallItem); manager.register(SnowBallItem);
manager.register(HoeItem); manager.register(HoeItem);
manager.register(EggItem); manager.register(EggItem);
manager.register(FlintAndSteelItem);
manager.register(SwordItem); manager.register(SwordItem);
manager.register(TridentItem); manager.register(TridentItem);
manager.register(ShovelItem); manager.register(ShovelItem);

View File

@ -44,6 +44,7 @@ use pumpkin_nbt::to_bytes_unnamed;
use pumpkin_protocol::client::play::{ use pumpkin_protocol::client::play::{
CRemoveMobEffect, CSetEntityMetadata, MetaDataType, Metadata, CRemoveMobEffect, CSetEntityMetadata, MetaDataType, Metadata,
}; };
use pumpkin_protocol::codec::identifier::Identifier;
use pumpkin_protocol::ser::serializer::Serializer; use pumpkin_protocol::ser::serializer::Serializer;
use pumpkin_protocol::{ use pumpkin_protocol::{
ClientPacket, IdOr, SoundEvent, ClientPacket, IdOr, SoundEvent,
@ -54,7 +55,6 @@ use pumpkin_protocol::{
}, },
server::play::SChatMessage, server::play::SChatMessage,
}; };
use pumpkin_protocol::{client::play::CLevelEvent, codec::identifier::Identifier};
use pumpkin_protocol::{ use pumpkin_protocol::{
client::play::{ client::play::{
CBlockUpdate, CDisguisedChatMessage, CExplosion, CRespawn, CSetBlockDestroyStage, CBlockUpdate, CDisguisedChatMessage, CExplosion, CRespawn, CSetBlockDestroyStage,
@ -361,26 +361,6 @@ impl World {
self.play_sound(sound, category, &new_vec).await; self.play_sound(sound, category, &new_vec).await;
} }
pub async fn play_record(&self, record_id: i32, position: BlockPos) {
self.broadcast_packet_all(&CLevelEvent::new(
WorldEvent::JukeboxStartsPlaying as i32,
position,
record_id,
false,
))
.await;
}
pub async fn stop_record(&self, position: BlockPos) {
self.broadcast_packet_all(&CLevelEvent::new(
WorldEvent::JukeboxStopsPlaying as i32,
position,
0,
false,
))
.await;
}
pub async fn tick(self: &Arc<Self>, server: &Server) { pub async fn tick(self: &Arc<Self>, server: &Server) {
self.flush_block_updates().await; self.flush_block_updates().await;
@ -1536,6 +1516,11 @@ impl World {
} }
} }
pub async fn sync_world_event(&self, world_event: WorldEvent, position: BlockPos, data: i32) {
self.broadcast_packet_all(&CWorldEvent::new(world_event as i32, position, data, false))
.await;
}
pub async fn get_chunk(&self, position: &BlockPos) -> Arc<RwLock<ChunkData>> { pub async fn get_chunk(&self, position: &BlockPos) -> Arc<RwLock<ChunkData>> {
let (chunk_coordinate, _) = position.chunk_and_chunk_relative_position(); let (chunk_coordinate, _) = position.chunk_and_chunk_relative_position();