From 0804a07626c8e565e969d603f0ef30faaaf987a5 Mon Sep 17 00:00:00 2001 From: ht06 Date: Tue, 1 Jul 2025 08:50:52 +0200 Subject: [PATCH] Implement Flower pot and change the state of eye blossom flower (#930) * feat: place flowers, saplings, ... inside the pot * feat: remove flowers, saplings, ... inside the pot and check when placing block next to the pot * feat: add changement for eye blossom flower state (open and closed) * fix: adapt to the BlockState commit (8ebc9ee) * fix: delete unused import * fix: refactor * cargo clippy + fmt * feat: use extractor to place the flower inside the pot * All done * fix: replace blocking_lock() by lock().await --------- Co-authored-by: Alexander Medvedev --- assets/flower_pot_transformations.json | 39 ++++++ pumpkin-data/build/build.rs | 10 +- .../build/flower_pot_transformations.rs | 27 ++++ pumpkin-data/src/lib.rs | 4 + pumpkin/src/block/blocks/flower_pots.rs | 118 ++++++++++++++++++ pumpkin/src/block/blocks/mod.rs | 1 + pumpkin/src/block/blocks/plant/flower.rs | 35 +++++- pumpkin/src/block/mod.rs | 2 + 8 files changed, 232 insertions(+), 4 deletions(-) create mode 100644 assets/flower_pot_transformations.json create mode 100644 pumpkin-data/build/flower_pot_transformations.rs create mode 100644 pumpkin/src/block/blocks/flower_pots.rs diff --git a/assets/flower_pot_transformations.json b/assets/flower_pot_transformations.json new file mode 100644 index 000000000..6c65cf234 --- /dev/null +++ b/assets/flower_pot_transformations.json @@ -0,0 +1,39 @@ +{ + "50": 383, + "237": 398, + "242": 403, + "247": 406, + "269": 761, + "51": 384, + "239": 400, + "230": 1102, + "55": 388, + "241": 402, + "238": 399, + "231": 1103, + "203": 391, + "244": 381, + "56": 389, + "229": 392, + "207": 407, + "205": 1085, + "240": 401, + "234": 395, + "243": 404, + "54": 387, + "251": 881, + "249": 879, + "235": 396, + "52": 385, + "53": 386, + "206": 1086, + "250": 880, + "248": 405, + "49": 382, + "236": 397, + "233": 394, + "328": 408, + "252": 882, + "57": 390, + "232": 393 +} \ No newline at end of file diff --git a/pumpkin-data/build/build.rs b/pumpkin-data/build/build.rs index f50ca6081..85907115a 100644 --- a/pumpkin-data/build/build.rs +++ b/pumpkin-data/build/build.rs @@ -1,8 +1,7 @@ -use quote::{format_ident, quote}; -use std::{fs, io::Write, path::Path, process::Command}; - use heck::ToPascalCase; use proc_macro2::TokenStream; +use quote::{format_ident, quote}; +use std::{fs, io::Write, path::Path, process::Command}; mod biome; mod block; @@ -12,6 +11,7 @@ mod damage_type; mod entity_pose; mod entity_status; mod entity_type; +mod flower_pot_transformations; mod fluid; mod game_event; mod game_rules; @@ -63,6 +63,10 @@ pub fn main() { write_generated_file(block::build(), "block.rs"); write_generated_file(tag::build(), "tag.rs"); write_generated_file(noise_router::build(), "noise_router.rs"); + write_generated_file( + flower_pot_transformations::build(), + "flower_pot_transformations.rs", + ); write_generated_file( composter_increase_chance::build(), "composter_increase_chance.rs", diff --git a/pumpkin-data/build/flower_pot_transformations.rs b/pumpkin-data/build/flower_pot_transformations.rs new file mode 100644 index 000000000..bb6ad1c0a --- /dev/null +++ b/pumpkin-data/build/flower_pot_transformations.rs @@ -0,0 +1,27 @@ +use proc_macro2::TokenStream; +use quote::quote; +use std::{collections::HashMap, fs}; +pub(crate) fn build() -> TokenStream { + println!("cargo:rerun-if-changed=../assets/flower_pot_transformations.json"); + + let flower_pot_transformation: HashMap = serde_json::from_str( + &fs::read_to_string("../assets/flower_pot_transformations.json").unwrap(), + ) + .expect("Failed to parse flower_pot_transformations.json"); + let mut variants = TokenStream::new(); + + for (item_id, potted_block_id) in flower_pot_transformation { + variants.extend(quote! { + #item_id => Some(#potted_block_id), + }); + } + quote! { + #[must_use] + pub const fn get_potted_item(item_id: u16) -> Option { + match item_id { + #variants + _ => None, + } + } + } +} diff --git a/pumpkin-data/src/lib.rs b/pumpkin-data/src/lib.rs index 9c7e387c7..a340e928e 100644 --- a/pumpkin-data/src/lib.rs +++ b/pumpkin-data/src/lib.rs @@ -128,6 +128,10 @@ pub mod noise_router; #[path = "generated/composter_increase_chance.rs"] pub mod composter_increase_chance; +#[rustfmt::skip] +#[path = "generated/flower_pot_transformations.rs"] +pub mod flower_pot_transformations; + mod block_direction; pub mod block_state; mod blocks; diff --git a/pumpkin/src/block/blocks/flower_pots.rs b/pumpkin/src/block/blocks/flower_pots.rs new file mode 100644 index 000000000..3864c7230 --- /dev/null +++ b/pumpkin/src/block/blocks/flower_pots.rs @@ -0,0 +1,118 @@ +use crate::block::pumpkin_block::{BlockMetadata, PumpkinBlock}; +use crate::block::registry::BlockActionResult; +use crate::entity::player::Player; +use crate::server::Server; +use crate::world::World; +use async_trait::async_trait; +use pumpkin_data::Block; +use pumpkin_data::flower_pot_transformations::get_potted_item; +use pumpkin_data::item::Item; +use pumpkin_data::tag::{RegistryKey, get_tag_values}; +use pumpkin_registry::VanillaDimensionType; +use pumpkin_util::math::position::BlockPos; +use pumpkin_world::world::BlockFlags; +use std::sync::Arc; + +pub struct FlowerPotBlock; + +impl BlockMetadata for FlowerPotBlock { + fn namespace(&self) -> &'static str { + "minecraft" + } + + fn ids(&self) -> &'static [&'static str] { + get_tag_values(RegistryKey::Block, "minecraft:flower_pots").unwrap() + } +} + +#[async_trait] +impl PumpkinBlock for FlowerPotBlock { + async fn normal_use( + &self, + block: &Block, + _player: &Player, + location: BlockPos, + _server: &Server, + world: &Arc, + ) { + if !block.eq(&Block::FLOWER_POT) { + world + .set_block_state( + &location, + Block::FLOWER_POT.default_state.id, + BlockFlags::NOTIFY_ALL, + ) + .await; + } + } + + #[allow(clippy::too_many_arguments)] + async fn use_with_item( + &self, + block: &Block, + _player: &Player, + location: BlockPos, + item: &Item, + _server: &Server, + world: &Arc, + ) -> BlockActionResult { + //Place the flower inside the pot + if block.eq(&Block::FLOWER_POT) { + if let Some(potted_block_id) = get_potted_item(item.id) { + world + .set_block_state( + &location, + Block::from_id(potted_block_id).unwrap().default_state.id, + BlockFlags::NOTIFY_ALL, + ) + .await; + } + return BlockActionResult::Consume; + } + + //if the player have an item that can be potted in his hand, nothing happens + if let Some(_potted_block_id) = get_potted_item(item.id) { + return BlockActionResult::Consume; + } + + //get the flower + empty the pot + world + .set_block_state( + &location, + Block::FLOWER_POT.default_state.id, + BlockFlags::NOTIFY_ALL, + ) + .await; + BlockActionResult::Consume + } + + async fn random_tick(&self, block: &Block, world: &Arc, pos: &BlockPos) { + if world.dimension_type.eq(&VanillaDimensionType::Overworld) + || world + .dimension_type + .eq(&VanillaDimensionType::OverworldCaves) + { + if block.eq(&Block::POTTED_CLOSED_EYEBLOSSOM) + && world.level_time.lock().await.time_of_day > 14500 + { + world + .set_block_state( + pos, + Block::POTTED_OPEN_EYEBLOSSOM.default_state.id, + BlockFlags::NOTIFY_ALL, + ) + .await; + } + } else if block.eq(&Block::POTTED_OPEN_EYEBLOSSOM) + && world.level_time.lock().await.time_of_day <= 14500 + { + world + .set_block_state( + pos, + Block::POTTED_CLOSED_EYEBLOSSOM.default_state.id, + BlockFlags::NOTIFY_ALL, + ) + .await; + } + } +} diff --git a/pumpkin/src/block/blocks/mod.rs b/pumpkin/src/block/blocks/mod.rs index 39e56965d..ae2cd925e 100644 --- a/pumpkin/src/block/blocks/mod.rs +++ b/pumpkin/src/block/blocks/mod.rs @@ -16,6 +16,7 @@ pub mod farmland; pub mod fence_gates; pub mod fences; pub mod fire; +pub mod flower_pots; pub mod furnace; pub mod glass_panes; pub mod grindstone; diff --git a/pumpkin/src/block/blocks/plant/flower.rs b/pumpkin/src/block/blocks/plant/flower.rs index 33fd572fc..9e8db541e 100644 --- a/pumpkin/src/block/blocks/plant/flower.rs +++ b/pumpkin/src/block/blocks/plant/flower.rs @@ -2,8 +2,10 @@ use async_trait::async_trait; use pumpkin_data::tag::{RegistryKey, Tagable, get_tag_values}; use pumpkin_data::{Block, BlockDirection}; use pumpkin_protocol::java::server::play::SUseItemOn; +use pumpkin_registry::VanillaDimensionType; use pumpkin_util::math::position::BlockPos; -use pumpkin_world::world::BlockAccessor; +use pumpkin_world::world::{BlockAccessor, BlockFlags}; +use std::sync::Arc; use crate::block::pumpkin_block::{BlockMetadata, PumpkinBlock}; use crate::entity::player::Player; @@ -38,4 +40,35 @@ impl PumpkinBlock for FlowerBlock { let block_below = block_accessor.get_block(&block_pos.down()).await; block_below.is_tagged_with("minecraft:dirt").unwrap() || block_below == Block::FARMLAND } + + async fn random_tick(&self, block: &Block, world: &Arc, pos: &BlockPos) { + //TODO add trail particule + if world.dimension_type.eq(&VanillaDimensionType::Overworld) + || world + .dimension_type + .eq(&VanillaDimensionType::OverworldCaves) + { + if block.eq(&Block::CLOSED_EYEBLOSSOM) + && world.level_time.lock().await.time_of_day > 14500 + { + world + .set_block_state( + pos, + Block::OPEN_EYEBLOSSOM.default_state.id, + BlockFlags::NOTIFY_ALL, + ) + .await; + } else if block.eq(&Block::OPEN_EYEBLOSSOM) + && world.level_time.lock().await.time_of_day <= 14500 + { + world + .set_block_state( + pos, + Block::CLOSED_EYEBLOSSOM.default_state.id, + BlockFlags::NOTIFY_ALL, + ) + .await; + } + } + } } diff --git a/pumpkin/src/block/mod.rs b/pumpkin/src/block/mod.rs index 704129258..809787cef 100644 --- a/pumpkin/src/block/mod.rs +++ b/pumpkin/src/block/mod.rs @@ -75,6 +75,7 @@ use pumpkin_util::random::{RandomGenerator, get_seed, xoroshiro128::Xoroshiro}; use pumpkin_world::BlockStateId; use crate::block::blocks::campfire::CampfireBlock; +use crate::block::blocks::flower_pots::FlowerPotBlock; use crate::block::blocks::plant::roots::RootsBlock; use crate::block::blocks::skull_block::SkullBlock; use crate::block::loot::LootContextParameters; @@ -110,6 +111,7 @@ pub fn default_registry() -> Arc { manager.register(FarmLandBlock); manager.register(FenceGateBlock); manager.register(FenceBlock); + manager.register(FlowerPotBlock); manager.register(FurnaceBlock); manager.register(GlassPaneBlock); manager.register(GrindstoneBlock);