From f68ffe662e578461fa49c8173322c53dcb017858 Mon Sep 17 00:00:00 2001 From: JeanTKG <102908437+jeantkg@users.noreply.github.com> Date: Thu, 25 Sep 2025 21:28:46 +0300 Subject: [PATCH 01/51] - fixed alot of item components - added new item components - remove the use src-namespace-prefix - added ItemManager (this turns json into a custom item) - added CustomiesItem class --- composer.json | 31 +++ plugin.yml | 5 +- resources/behavior/items/allowoffhand.json | 39 ++++ .../customies}/Customies.php | 9 + .../customies}/CustomiesListener.php | 0 .../customies}/block/BlockComponents.php | 0 .../customies}/block/BlockComponentsTrait.php | 0 .../customies}/block/BlockPalette.php | 0 .../block/CustomiesBlockFactory.php | 0 .../customies}/block/Material.php | 0 .../block/component/BlockComponent.php | 0 .../component/BreathabilityComponent.php | 0 .../block/component/CollisionBoxComponent.php | 0 .../DestructibleByExplosionComponent.php | 0 .../DestructibleByMiningComponent.php | 0 .../block/component/DisplayNameComponent.php | 0 .../block/component/FlammableComponent.php | 0 .../block/component/FrictionComponent.php | 0 .../block/component/GeometryComponent.php | 0 .../component/LightDampeningComponent.php | 0 .../component/LightEmissionComponent.php | 0 .../component/MaterialInstancesComponent.php | 0 .../block/component/SelectionBoxComponent.php | 0 .../block/permutations/BlockProperty.php | 0 .../block/permutations/Permutable.php | 0 .../block/permutations/Permutation.php | 0 .../block/permutations/Permutations.php | 0 .../block/permutations/RotatableTrait.php | 0 .../entity/CustomiesEntityFactory.php | 0 .../customies}/item/CreativeInventoryInfo.php | 0 .../customies/item/CustomiesItem.php | 198 ++++++++++++++++++ .../customies}/item/CustomiesItemFactory.php | 103 ++++++++- .../customies}/item/ItemComponents.php | 0 .../customies}/item/ItemComponentsTrait.php | 0 .../customies/item/ItemManager.php | 154 ++++++++++++++ .../item/component/AllowOffHandComponent.php | 12 +- .../item/component/BlockPlacerComponent.php | 13 +- .../component/BundleInteractionComponent.php | 10 +- .../CanDestroyInCreativeComponent.php | 12 +- .../item/component/CompostableComponent.php | 27 +++ .../item/component/CooldownComponent.php | 37 ++++ .../component/DamageAbsorptionComponent.php | 30 +++ .../item/component/DamageComponent.php | 28 +++ .../item/component/DiggerComponent.php | 7 +- .../item/component/DisplayNameComponent.php | 4 - .../item/component/DurabilityComponent.php | 4 - .../component/DurabilitySensorComponent.php | 39 ++++ .../item/component/DyeableComponent.php | 4 - .../item/component/EnchantableComponent.php | 68 ++++++ .../item/component/FireResistantComponent.php | 27 +++ .../item/component/FoodComponent.php | 4 - .../item/component/FuelComponent.php | 4 - .../item/component/GlintComponent.php | 12 +- .../item/component/HandEquippedComponent.php | 12 +- .../component/HoverTextColorComponent.php | 12 +- .../item/component/IconComponent.php | 51 +++++ .../component/InteractButtonComponent.php | 8 +- .../item/component/ItemComponent.php | 2 - .../item/component/LiquidClippedComponent.php | 12 +- .../item/component/MaxStackSizeComponent.php | 12 +- .../item/component/ProjectileComponent.php | 4 - .../item/component/RarityComponent.php | 4 - .../item/component/RecordComponent.php | 4 - .../item/component/ShooterComponent.php | 8 +- .../item/component/ShouldDespawnComponent.php | 12 +- .../item/component/StackedByDataComponent.php | 12 +- .../item/component/StorageItemComponent.php | 55 +++++ .../component/StorageWeightLimitComponent.php | 27 +++ .../StorageWeightModifierComponent.php | 27 +++ .../item/component/SwingDurationComponent.php | 27 +++ .../item/component/TagsComponent.php | 27 +++ .../item/component/ThrowableComponent.php | 25 ++- .../item/component/UseAnimationComponent.php | 14 +- .../item/component/UseModifiersComponent.php | 4 - .../item/component/WearableComponent.php | 18 +- .../task/AsyncRegisterBlocksTask.php | 0 .../customies}/util/NBT.php | 0 src/item/component/CooldownComponent.php | 42 ---- .../component/DamageAbsorptionComponent.php | 32 --- src/item/component/DamageComponent.php | 29 --- .../component/EnchantableSlotComponent.php | 48 ----- .../component/EnchantableValueComponent.php | 47 ----- src/item/component/IconComponent.php | 42 ---- src/item/component/UseDurationComponent.php | 29 --- test/server-output.txt | 73 +++++++ test/test.json | 30 +++ 86 files changed, 1189 insertions(+), 441 deletions(-) create mode 100644 composer.json create mode 100644 resources/behavior/items/allowoffhand.json rename src/{ => customiesdevs/customies}/Customies.php (78%) rename src/{ => customiesdevs/customies}/CustomiesListener.php (100%) rename src/{ => customiesdevs/customies}/block/BlockComponents.php (100%) rename src/{ => customiesdevs/customies}/block/BlockComponentsTrait.php (100%) rename src/{ => customiesdevs/customies}/block/BlockPalette.php (100%) rename src/{ => customiesdevs/customies}/block/CustomiesBlockFactory.php (100%) rename src/{ => customiesdevs/customies}/block/Material.php (100%) rename src/{ => customiesdevs/customies}/block/component/BlockComponent.php (100%) rename src/{ => customiesdevs/customies}/block/component/BreathabilityComponent.php (100%) rename src/{ => customiesdevs/customies}/block/component/CollisionBoxComponent.php (100%) rename src/{ => customiesdevs/customies}/block/component/DestructibleByExplosionComponent.php (100%) rename src/{ => customiesdevs/customies}/block/component/DestructibleByMiningComponent.php (100%) rename src/{ => customiesdevs/customies}/block/component/DisplayNameComponent.php (100%) rename src/{ => customiesdevs/customies}/block/component/FlammableComponent.php (100%) rename src/{ => customiesdevs/customies}/block/component/FrictionComponent.php (100%) rename src/{ => customiesdevs/customies}/block/component/GeometryComponent.php (100%) rename src/{ => customiesdevs/customies}/block/component/LightDampeningComponent.php (100%) rename src/{ => customiesdevs/customies}/block/component/LightEmissionComponent.php (100%) rename src/{ => customiesdevs/customies}/block/component/MaterialInstancesComponent.php (100%) rename src/{ => customiesdevs/customies}/block/component/SelectionBoxComponent.php (100%) rename src/{ => customiesdevs/customies}/block/permutations/BlockProperty.php (100%) rename src/{ => customiesdevs/customies}/block/permutations/Permutable.php (100%) rename src/{ => customiesdevs/customies}/block/permutations/Permutation.php (100%) rename src/{ => customiesdevs/customies}/block/permutations/Permutations.php (100%) rename src/{ => customiesdevs/customies}/block/permutations/RotatableTrait.php (100%) rename src/{ => customiesdevs/customies}/entity/CustomiesEntityFactory.php (100%) rename src/{ => customiesdevs/customies}/item/CreativeInventoryInfo.php (100%) create mode 100644 src/customiesdevs/customies/item/CustomiesItem.php rename src/{ => customiesdevs/customies}/item/CustomiesItemFactory.php (56%) rename src/{ => customiesdevs/customies}/item/ItemComponents.php (100%) rename src/{ => customiesdevs/customies}/item/ItemComponentsTrait.php (100%) create mode 100644 src/customiesdevs/customies/item/ItemManager.php rename src/{ => customiesdevs/customies}/item/component/AllowOffHandComponent.php (75%) rename src/{ => customiesdevs/customies}/item/component/BlockPlacerComponent.php (72%) rename src/{ => customiesdevs/customies}/item/component/BundleInteractionComponent.php (63%) rename src/{ => customiesdevs/customies}/item/component/CanDestroyInCreativeComponent.php (75%) create mode 100644 src/customiesdevs/customies/item/component/CompostableComponent.php create mode 100644 src/customiesdevs/customies/item/component/CooldownComponent.php create mode 100644 src/customiesdevs/customies/item/component/DamageAbsorptionComponent.php create mode 100644 src/customiesdevs/customies/item/component/DamageComponent.php rename src/{ => customiesdevs/customies}/item/component/DiggerComponent.php (93%) rename src/{ => customiesdevs/customies}/item/component/DisplayNameComponent.php (91%) rename src/{ => customiesdevs/customies}/item/component/DurabilityComponent.php (95%) create mode 100644 src/customiesdevs/customies/item/component/DurabilitySensorComponent.php rename src/{ => customiesdevs/customies}/item/component/DyeableComponent.php (91%) create mode 100644 src/customiesdevs/customies/item/component/EnchantableComponent.php create mode 100644 src/customiesdevs/customies/item/component/FireResistantComponent.php rename src/{ => customiesdevs/customies}/item/component/FoodComponent.php (95%) rename src/{ => customiesdevs/customies}/item/component/FuelComponent.php (90%) rename src/{ => customiesdevs/customies}/item/component/GlintComponent.php (75%) rename src/{ => customiesdevs/customies}/item/component/HandEquippedComponent.php (75%) rename src/{ => customiesdevs/customies}/item/component/HoverTextColorComponent.php (78%) create mode 100644 src/customiesdevs/customies/item/component/IconComponent.php rename src/{ => customiesdevs/customies}/item/component/InteractButtonComponent.php (84%) rename src/{ => customiesdevs/customies}/item/component/ItemComponent.php (82%) rename src/{ => customiesdevs/customies}/item/component/LiquidClippedComponent.php (76%) rename src/{ => customiesdevs/customies}/item/component/MaxStackSizeComponent.php (75%) rename src/{ => customiesdevs/customies}/item/component/ProjectileComponent.php (95%) rename src/{ => customiesdevs/customies}/item/component/RarityComponent.php (94%) rename src/{ => customiesdevs/customies}/item/component/RecordComponent.php (94%) rename src/{ => customiesdevs/customies}/item/component/ShooterComponent.php (95%) rename src/{ => customiesdevs/customies}/item/component/ShouldDespawnComponent.php (77%) rename src/{ => customiesdevs/customies}/item/component/StackedByDataComponent.php (79%) create mode 100644 src/customiesdevs/customies/item/component/StorageItemComponent.php create mode 100644 src/customiesdevs/customies/item/component/StorageWeightLimitComponent.php create mode 100644 src/customiesdevs/customies/item/component/StorageWeightModifierComponent.php create mode 100644 src/customiesdevs/customies/item/component/SwingDurationComponent.php create mode 100644 src/customiesdevs/customies/item/component/TagsComponent.php rename src/{ => customiesdevs/customies}/item/component/ThrowableComponent.php (73%) rename src/{ => customiesdevs/customies}/item/component/UseAnimationComponent.php (80%) rename src/{ => customiesdevs/customies}/item/component/UseModifiersComponent.php (93%) rename src/{ => customiesdevs/customies}/item/component/WearableComponent.php (71%) rename src/{ => customiesdevs/customies}/task/AsyncRegisterBlocksTask.php (100%) rename src/{ => customiesdevs/customies}/util/NBT.php (100%) delete mode 100644 src/item/component/CooldownComponent.php delete mode 100644 src/item/component/DamageAbsorptionComponent.php delete mode 100644 src/item/component/DamageComponent.php delete mode 100644 src/item/component/EnchantableSlotComponent.php delete mode 100644 src/item/component/EnchantableValueComponent.php delete mode 100644 src/item/component/IconComponent.php delete mode 100644 src/item/component/UseDurationComponent.php create mode 100644 test/server-output.txt create mode 100644 test/test.json diff --git a/composer.json b/composer.json new file mode 100644 index 00000000..3bd00f79 --- /dev/null +++ b/composer.json @@ -0,0 +1,31 @@ +{ + "name": "customiesdevs/customies", + "type": "pocketmine-plugin", + "minimum-stability": "beta", + "prefer-stable": true, + "autoload": { + "classmap": ["src"] + }, + "scripts": { + "analyze": "vendor/bin/phpstan analyze src --level=8", + "build": "php -dphar.readonly=0 vendor/bin/pharynx -i=. -c -p=PluginName.phar" + }, + "repositories": [ + + ], + "require": { + + }, + "require-dev": { + "pocketmine/pocketmine-mp": "^5.30", + "phpstan/phpstan": "^1.2", + "phpstan/phpstan-strict-rules": "^1.0", + "phpstan/extension-installer": "^1.0", + "friendsofphp/php-cs-fixer": "^3" + }, + "config": { + "allow-plugins": { + "phpstan/extension-installer": true + } + } +} diff --git a/plugin.yml b/plugin.yml index e1d59501..2ba9f40a 100644 --- a/plugin.yml +++ b/plugin.yml @@ -2,9 +2,8 @@ name: Customies description: A PocketMine-MP plugin that implements support for custom blocks, items and entities. main: customiesdevs\customies\Customies -src-namespace-prefix: customiesdevs\customies -version: 1.4.0 -api: 5.1.3 +version: 2.0.0 +api: 5.0.0 authors: - DenielWorld diff --git a/resources/behavior/items/allowoffhand.json b/resources/behavior/items/allowoffhand.json new file mode 100644 index 00000000..010af6ac --- /dev/null +++ b/resources/behavior/items/allowoffhand.json @@ -0,0 +1,39 @@ +{ + "format_version": "1.21.100", + "minecraft:item": { + "description": { + "identifier": "nk:allowoffhand", + "menu_category": { + "category": "items" + } + }, + "components": { + "minecraft:icon": { + "textures": { + "default": "apple" + } + }, + "minecraft:allow_off_hand": true, + "minecraft:can_destroy_in_creative": true, + "minecraft:dyeable": { + "default_color": "#175882" + }, + "minecraft:enchantable": { + "slot": "sword", + "value": 10 + }, + "minecraft:fuel": { + "duration": 3.0 + }, + "minecraft:glint": true, + "minecraft:hand_equipped": true, + "minecraft:hover_text_color": "minecoin_gold", + "minecraft:interact_button": "Use This Custom Item", + "minecraft:max_stack_size": 7, + "minecraft:fire_resistant": true, + "minecraft:rarity": "rare", + "minecraft:should_despawn": true, + "minecraft:stacked_by_data": true + } + } +} \ No newline at end of file diff --git a/src/Customies.php b/src/customiesdevs/customies/Customies.php similarity index 78% rename from src/Customies.php rename to src/customiesdevs/customies/Customies.php index 4ce46398..6ce82728 100644 --- a/src/Customies.php +++ b/src/customiesdevs/customies/Customies.php @@ -4,13 +4,22 @@ namespace customiesdevs\customies; use customiesdevs\customies\block\CustomiesBlockFactory; +use customiesdevs\customies\item\ItemManager; use pocketmine\plugin\PluginBase; use pocketmine\scheduler\ClosureTask; +use pocketmine\utils\SingletonTrait; final class Customies extends PluginBase { + use SingletonTrait; + + public function onLoad(): void{ + self::setInstance($this); + } protected function onEnable(): void { $this->getServer()->getPluginManager()->registerEvents(new CustomiesListener(), $this); + + ItemManager::getInstance()->registerItems(); $cachePath = $this->getDataFolder() . "idcache"; $this->getScheduler()->scheduleDelayedTask(new ClosureTask(static function () use ($cachePath): void { diff --git a/src/CustomiesListener.php b/src/customiesdevs/customies/CustomiesListener.php similarity index 100% rename from src/CustomiesListener.php rename to src/customiesdevs/customies/CustomiesListener.php diff --git a/src/block/BlockComponents.php b/src/customiesdevs/customies/block/BlockComponents.php similarity index 100% rename from src/block/BlockComponents.php rename to src/customiesdevs/customies/block/BlockComponents.php diff --git a/src/block/BlockComponentsTrait.php b/src/customiesdevs/customies/block/BlockComponentsTrait.php similarity index 100% rename from src/block/BlockComponentsTrait.php rename to src/customiesdevs/customies/block/BlockComponentsTrait.php diff --git a/src/block/BlockPalette.php b/src/customiesdevs/customies/block/BlockPalette.php similarity index 100% rename from src/block/BlockPalette.php rename to src/customiesdevs/customies/block/BlockPalette.php diff --git a/src/block/CustomiesBlockFactory.php b/src/customiesdevs/customies/block/CustomiesBlockFactory.php similarity index 100% rename from src/block/CustomiesBlockFactory.php rename to src/customiesdevs/customies/block/CustomiesBlockFactory.php diff --git a/src/block/Material.php b/src/customiesdevs/customies/block/Material.php similarity index 100% rename from src/block/Material.php rename to src/customiesdevs/customies/block/Material.php diff --git a/src/block/component/BlockComponent.php b/src/customiesdevs/customies/block/component/BlockComponent.php similarity index 100% rename from src/block/component/BlockComponent.php rename to src/customiesdevs/customies/block/component/BlockComponent.php diff --git a/src/block/component/BreathabilityComponent.php b/src/customiesdevs/customies/block/component/BreathabilityComponent.php similarity index 100% rename from src/block/component/BreathabilityComponent.php rename to src/customiesdevs/customies/block/component/BreathabilityComponent.php diff --git a/src/block/component/CollisionBoxComponent.php b/src/customiesdevs/customies/block/component/CollisionBoxComponent.php similarity index 100% rename from src/block/component/CollisionBoxComponent.php rename to src/customiesdevs/customies/block/component/CollisionBoxComponent.php diff --git a/src/block/component/DestructibleByExplosionComponent.php b/src/customiesdevs/customies/block/component/DestructibleByExplosionComponent.php similarity index 100% rename from src/block/component/DestructibleByExplosionComponent.php rename to src/customiesdevs/customies/block/component/DestructibleByExplosionComponent.php diff --git a/src/block/component/DestructibleByMiningComponent.php b/src/customiesdevs/customies/block/component/DestructibleByMiningComponent.php similarity index 100% rename from src/block/component/DestructibleByMiningComponent.php rename to src/customiesdevs/customies/block/component/DestructibleByMiningComponent.php diff --git a/src/block/component/DisplayNameComponent.php b/src/customiesdevs/customies/block/component/DisplayNameComponent.php similarity index 100% rename from src/block/component/DisplayNameComponent.php rename to src/customiesdevs/customies/block/component/DisplayNameComponent.php diff --git a/src/block/component/FlammableComponent.php b/src/customiesdevs/customies/block/component/FlammableComponent.php similarity index 100% rename from src/block/component/FlammableComponent.php rename to src/customiesdevs/customies/block/component/FlammableComponent.php diff --git a/src/block/component/FrictionComponent.php b/src/customiesdevs/customies/block/component/FrictionComponent.php similarity index 100% rename from src/block/component/FrictionComponent.php rename to src/customiesdevs/customies/block/component/FrictionComponent.php diff --git a/src/block/component/GeometryComponent.php b/src/customiesdevs/customies/block/component/GeometryComponent.php similarity index 100% rename from src/block/component/GeometryComponent.php rename to src/customiesdevs/customies/block/component/GeometryComponent.php diff --git a/src/block/component/LightDampeningComponent.php b/src/customiesdevs/customies/block/component/LightDampeningComponent.php similarity index 100% rename from src/block/component/LightDampeningComponent.php rename to src/customiesdevs/customies/block/component/LightDampeningComponent.php diff --git a/src/block/component/LightEmissionComponent.php b/src/customiesdevs/customies/block/component/LightEmissionComponent.php similarity index 100% rename from src/block/component/LightEmissionComponent.php rename to src/customiesdevs/customies/block/component/LightEmissionComponent.php diff --git a/src/block/component/MaterialInstancesComponent.php b/src/customiesdevs/customies/block/component/MaterialInstancesComponent.php similarity index 100% rename from src/block/component/MaterialInstancesComponent.php rename to src/customiesdevs/customies/block/component/MaterialInstancesComponent.php diff --git a/src/block/component/SelectionBoxComponent.php b/src/customiesdevs/customies/block/component/SelectionBoxComponent.php similarity index 100% rename from src/block/component/SelectionBoxComponent.php rename to src/customiesdevs/customies/block/component/SelectionBoxComponent.php diff --git a/src/block/permutations/BlockProperty.php b/src/customiesdevs/customies/block/permutations/BlockProperty.php similarity index 100% rename from src/block/permutations/BlockProperty.php rename to src/customiesdevs/customies/block/permutations/BlockProperty.php diff --git a/src/block/permutations/Permutable.php b/src/customiesdevs/customies/block/permutations/Permutable.php similarity index 100% rename from src/block/permutations/Permutable.php rename to src/customiesdevs/customies/block/permutations/Permutable.php diff --git a/src/block/permutations/Permutation.php b/src/customiesdevs/customies/block/permutations/Permutation.php similarity index 100% rename from src/block/permutations/Permutation.php rename to src/customiesdevs/customies/block/permutations/Permutation.php diff --git a/src/block/permutations/Permutations.php b/src/customiesdevs/customies/block/permutations/Permutations.php similarity index 100% rename from src/block/permutations/Permutations.php rename to src/customiesdevs/customies/block/permutations/Permutations.php diff --git a/src/block/permutations/RotatableTrait.php b/src/customiesdevs/customies/block/permutations/RotatableTrait.php similarity index 100% rename from src/block/permutations/RotatableTrait.php rename to src/customiesdevs/customies/block/permutations/RotatableTrait.php diff --git a/src/entity/CustomiesEntityFactory.php b/src/customiesdevs/customies/entity/CustomiesEntityFactory.php similarity index 100% rename from src/entity/CustomiesEntityFactory.php rename to src/customiesdevs/customies/entity/CustomiesEntityFactory.php diff --git a/src/item/CreativeInventoryInfo.php b/src/customiesdevs/customies/item/CreativeInventoryInfo.php similarity index 100% rename from src/item/CreativeInventoryInfo.php rename to src/customiesdevs/customies/item/CreativeInventoryInfo.php diff --git a/src/customiesdevs/customies/item/CustomiesItem.php b/src/customiesdevs/customies/item/CustomiesItem.php new file mode 100644 index 00000000..490ae2c4 --- /dev/null +++ b/src/customiesdevs/customies/item/CustomiesItem.php @@ -0,0 +1,198 @@ + $componentData) { + switch ($componentName) { + case "minecraft:allow_off_hand": + $this->addComponent(new AllowOffHandComponent($componentData)); + break; + case "minecraft:block_placer": + $this->addComponent(new BlockPlacerComponent(StringToItemParser::getInstance()->parse($componentData["block"])->getBlock())); + break; + case "minecraft:can_destroy_in_creative": + $this->addComponent(new CanDestroyInCreativeComponent($componentData)); + break; + case "minecraft:compostable": + $this->addComponent(new CompostableComponent($componentData["composting_chance"])); + break; + case "minecraft:cooldown": + $this->addComponent(new CooldownComponent( + $componentData["category"], + $componentData["duration"] + )); + break; + case "minecraft:damage": + $this->addComponent(new DamageComponent($componentData)); + break; + case "minecraft:damage_absorption": + $this->addComponent(new DamageAbsorptionComponent($componentData["absorbable_causes"])); + break; + case "minecraft:digger": + $this->addComponent(new DiggerComponent( + $componentData["use_efficiency"], + $componentData["destroy_speeds"] ?? [] + )); + break; + case "minecraft:display_name": + $this->addComponent(new DisplayNameComponent($componentData["value"])); + break; + case "minecraft:durability": + $this->addComponent(new DurabilityComponent( + $componentData["max_durability"], + $componentData["damage_chance"]["min"] ?? 0, + $componentData["damage_chance"]["max"] ?? 100 + )); + break; + case "minecraft:dyeable": + $this->addComponent(new DyeableComponent($componentData["default_color"])); + break; + case "minecraft:enchantable": + $this->addComponent(new EnchantableComponent($componentData["slot"], $componentData["value"])); + break; + case "minecraft:food": + $this->addComponent(new FoodComponent( + $componentData["can_always_eat"], + $componentData["nutrition"], + $componentData["saturation_modifier"], + $componentData["using_converts_to"] + )); + break; + case "minecraft:fuel": + $this->addComponent(new FuelComponent($componentData["duration"])); + break; + case "minecraft:glint": + $this->addComponent(new GlintComponent($componentData)); + break; + case "minecraft:hand_equipped": + $this->addComponent(new HandEquippedComponent($componentData)); + break; + case "minecraft:hover_text_color": + $this->addComponent(new HoverTextColorComponent($componentData)); + break; + case "minecraft:icon": + $this->addComponent(new IconComponent( + $componentData["textures"]["default"], + $componentData["textures"]["dyed"] ?? "", + $componentData["textures"]["icon_trim"] ?? "" + )); + break; + case "minecraft:interact_button": + $this->addComponent(new InteractButtonComponent($componentData)); + break; + case "minecraft:liquid_clipped": + $this->addComponent(new LiquidClippedComponent($componentData)); + break; + case "minecraft:max_stack_size": + $this->addComponent(new MaxStackSizeComponent($componentData)); + break; + case "minecraft:projectile": + $this->addComponent(new ProjectileComponent( + $componentData["minimum_critical_power"], + $componentData["projectile_entity"] + )); + break; + case "minecraft:rarity": + $this->addComponent(new RarityComponent($componentData)); + break; + case "minecraft:record": + $this->addComponent(new RecordComponent( + $componentData["comparator_signal"], + $componentData["duration"], + $componentData["sound_event"] + )); + break; + case "minecraft:repairable": + // not added yet + break; + case "minecraft:shooter": + $this->addComponent(new ShooterComponent( + $componentData["ammunition"][0]["item"], + $componentData["ammunition"][0]["use_offhand"] ?? false, + $componentData["ammunition"][0]["search_inventory"] ?? false, + $componentData["ammunition"][0]["use_in_creative"] ?? false, + $componentData["max_draw_duration"] ?? 0.0, + $componentData["scale_power_by_draw_duration"] ?? false, + $componentData["charge_on_draw"] ?? false + )); + break; + case "minecraft:should_despawn": + $this->addComponent(new ShouldDespawnComponent($componentData)); + break; + case "minecraft:stacked_by_data": + $this->addComponent(new StackedByDataComponent($componentData)); + break; + case "minecraft:throwable": + $this->addComponent(new ThrowableComponent( + $componentData["do_swing_animation"], + $componentData["launch_power_scale"], + $componentData["max_draw_duration"], + $componentData["max_launch_power"], + $componentData["min_draw_duration"], + $componentData["scale_power_by_draw_duration"] + )); + break; + case "minecraft:use_animation": + $this->addComponent(new UseAnimationComponent($componentData)); + break; + case "minecraft:use_modifiers": + $this->addComponent(new UseModifiersComponent( + $componentData["movement_modifier"], + $componentData["use_duration"] + )); + break; + case "minecraft:wearable": + $this->addComponent(new WearableComponent( + $componentData["slot"], + $componentData["protection"] ?? 0, + $componentData["hides_player_location"] ?? false, + )); + break; + } + } + } +} \ No newline at end of file diff --git a/src/item/CustomiesItemFactory.php b/src/customiesdevs/customies/item/CustomiesItemFactory.php similarity index 56% rename from src/item/CustomiesItemFactory.php rename to src/customiesdevs/customies/item/CustomiesItemFactory.php index 69275125..fd24393b 100644 --- a/src/item/CustomiesItemFactory.php +++ b/src/customiesdevs/customies/item/CustomiesItemFactory.php @@ -38,6 +38,10 @@ final class CustomiesItemFactory { /** * Get a custom item from its identifier. An exception will be thrown if the item is not registered. + * @param string $identifier The string identifier for the item, usually in the format "namespace:item_name" + * @param int $amount The amount of the item to be returned + * @return Item The item instance + * @throws InvalidArgumentException if the item is not registered */ public function get(string $identifier, int $amount = 1): Item { $item = StringToItemParser::getInstance()->parse($identifier); @@ -71,6 +75,10 @@ public function getItemTableEntries(): array { * Registers the item to the item factory and assigns it an ID. It also updates the required mappings and stores the * item components if present. * @phpstan-param class-string $className + * @param Closure $itemFunc A closure that returns an instance of the item to be registered + * @param string $identifier The string identifier for the item, usually in the format "namespace:item_name" + * @param CreativeInventoryInfo|null $creativeInfo The creative inventory info for the item, if any + * @throws InvalidArgumentException if the closure does not return an Item instance */ public function registerItem(Closure $itemFunc, string $identifier, ?CreativeInventoryInfo $creativeInfo = null): void { $item = $itemFunc(); @@ -119,25 +127,96 @@ public function registerItem(Closure $itemFunc, string $identifier, ?CreativeInv } /** - * Creates the NBT data for the item. + * Creates the NBT data for the item. This includes the components and their values. + * If the item does not have components, an empty CompoundTag is returned. + * @param Item $item The item for which to create the NBT data + * @param string $identifier The string identifier for the item, usually in the format "namespace:item_name" + * @param int $itemId The numerical ID to be assigned to the item + * @param CreativeInventoryInfo|null $creativeInfo The creative inventory info for the item, if any + * @return CompoundTag The NBT data for the item */ private function createItemNbt(Item $item, string $identifier, int $itemId, ?CreativeInventoryInfo $creativeInfo): CompoundTag { $components = CompoundTag::create(); $properties = CompoundTag::create(); - if ($item instanceof ItemComponents) { - foreach ($item->getComponents() as $component) { + if($item instanceof ItemComponents) { + // Set default values for all properties first + $properties->setTag("allow_off_hand", NBT::getTagType(false)); + $properties->setTag("can_destroy_in_creative", NBT::getTagType(true)); + $properties->setTag("damage", NBT::getTagType(0)); + $properties->setTag("enchantable_slot", NBT::getTagType("none")); + $properties->setTag("enchantable_value", NBT::getTagType(0)); + $properties->setTag("foil", NBT::getTagType(false)); + $properties->setTag("frame_count", NBT::getTagType(1)); + $properties->setTag("hand_equipped", NBT::getTagType(false)); + $properties->setTag("liquid_clipped", NBT::getTagType(false)); + $properties->setTag("max_stack_size", NBT::getTagType(64)); + $properties->setTag("mining_speed", NBT::getTagType(1)); + $properties->setTag("should_despawn", NBT::getTagType(true)); + $properties->setTag("stacked_by_data", NBT::getTagType(false)); + $properties->setTag("use_animation", NBT::getTagType(0)); + $properties->setTag("use_duration", NBT::getTagType(0)); + + foreach($item->getComponents() as $component) { $tag = NBT::getTagType($component->getValue()); - if ($tag === null) { + if($tag === null) { throw new RuntimeException("Failed to get tag type for component " . $component->getName()); } - if ($component->isProperty()) { - $properties->setTag($component->getName(), $tag); - continue; + + // Override defaults with component-specific values + switch($component->getName()) { + case "minecraft:allow_off_hand": + $properties->setTag("allow_off_hand", NBT::getTagType($component->getValue()["value"])); + break; + case "minecraft:can_destroy_in_creative": + $properties->setTag("can_destroy_in_creative", NBT::getTagType($component->getValue()["value"])); + break; + case "minecraft:damage": + $properties->setTag("damage", NBT::getTagType($component->getValue()["value"])); + break; + case "minecraft:enchantable": + $properties->setTag("enchantable_slot", NBT::getTagType($component->getValue()["slot"])); + $properties->setTag("enchantable_value", NBT::getTagType($component->getValue()["value"])); + break; + case "minecraft:glint": + $properties->setTag("foil", NBT::getTagType($component->getValue()["value"])); + break; + case "minecraft:hand_equipped": + $properties->setTag("hand_equipped", NBT::getTagType($component->getValue()["value"])); + break; + case "minecraft:hover_text_color": + $properties->setTag("hover_text_color", NBT::getTagType($component->getValue()["value"])); + break; + case "minecraft:liquid_clipped": + $properties->setTag("liquid_clipped", NBT::getTagType($component->getValue()["value"])); + break; + case "minecraft:max_stack_size": + $properties->setTag("max_stack_size", NBT::getTagType($component->getValue()["value"])); + break; + case "minecraft:icon": + $properties->setTag("minecraft:icon", $tag); + break; + case "minecraft:should_despawn": + $properties->setTag("should_despawn", NBT::getTagType($component->getValue()["value"])); + break; + case "minecraft:stacked_by_data": + $properties->setTag("stacked_by_data", NBT::getTagType($component->getValue()["value"])); + break; + case "minecraft:use_animation": + $properties->setTag("use_animation", NBT::getTagType($component->getValue()["value"])); + break; + case "minecraft:use_modifiers": + $properties->setTag("use_duration", NBT::getTagType($component->getValue()["use_duration"])); + break; + } + + // Setting the actual component + // The icon component is already set in item_properties, no need to set it again + if($component->getName() !== "minecraft:icon") { + $components->setTag($component->getName(), $tag); } - $components->setTag($component->getName(), $tag); } - if ($creativeInfo !== null) { + if($creativeInfo !== null) { $properties->setTag("creative_category", NBT::getTagType($creativeInfo->getNumericCategory())); $properties->setTag("creative_group", NBT::getTagType($creativeInfo->getGroup())); } @@ -152,6 +231,10 @@ private function createItemNbt(Item $item, string $identifier, int $itemId, ?Cre /** * Registers a custom item ID to the required mappings in the global ItemTypeDictionary instance. + * This allows the item to be recognized and used within the game. + * @param string $identifier The string identifier for the item, usually in the format "namespace:item_name" + * @param int $itemId The numerical ID to be assigned to the item + * @param ItemTypeEntry $entry The ItemTypeEntry instance representing the item */ private function registerCustomItemMapping(string $identifier, int $itemId, ItemTypeEntry $entry): void { $dictionary = TypeConverter::getInstance()->getItemTypeDictionary(); @@ -176,6 +259,8 @@ private function registerCustomItemMapping(string $identifier, int $itemId, Item /** * Registers the required mappings for the block to become an item that can be placed etc. It is assigned an ID that * correlates to its block ID. + * @param string $identifier The string identifier for the block item, usually in the format "namespace:block_name" + * @param Block $block The block instance to be registered as an item */ public function registerBlockItem(string $identifier, Block $block): void { $itemId = $block->getIdInfo()->getBlockTypeId(); diff --git a/src/item/ItemComponents.php b/src/customiesdevs/customies/item/ItemComponents.php similarity index 100% rename from src/item/ItemComponents.php rename to src/customiesdevs/customies/item/ItemComponents.php diff --git a/src/item/ItemComponentsTrait.php b/src/customiesdevs/customies/item/ItemComponentsTrait.php similarity index 100% rename from src/item/ItemComponentsTrait.php rename to src/customiesdevs/customies/item/ItemComponentsTrait.php diff --git a/src/customiesdevs/customies/item/ItemManager.php b/src/customiesdevs/customies/item/ItemManager.php new file mode 100644 index 00000000..3b95670d --- /dev/null +++ b/src/customiesdevs/customies/item/ItemManager.php @@ -0,0 +1,154 @@ +itemsDirectory = Customies::getInstance()->getDataFolder() . "behavior/items/"; + $this->ensureDirectoryExists(); + } + + /** + * Ensures the items directory exists + */ + private function ensureDirectoryExists(): void { + if(!is_dir($this->itemsDirectory)) { + mkdir($this->itemsDirectory, 0777, true); + } + } + + /** + * Gets all item file names in the items directory + */ + public function getItemFiles(): array { + $itemsFolder = scandir($this->itemsDirectory); + if($itemsFolder === false) { + return []; + } + return array_filter($itemsFolder, function($item) { + return $item !== '.' && + $item !== '..' && + is_file($this->itemsDirectory . $item) && + $this->isValidItemFile($item); + }); + } + + /** + * Validates if a file is a valid item configuration file + * @param string $filename + * @return bool True if the file is a valid item config (e.g., ends with .json) + */ + private function isValidItemFile(string $filename): bool { + return str_ends_with($filename, '.json'); + } + + /** + * Gets the configuration for a specific item + * @param string $itemName The name of the item configuration file + * @return Config The configuration object for the item + */ + public function getItemConfig(string $itemName): Config { + $configPath = $this->itemsDirectory . $itemName; + return new Config($configPath, Config::JSON); + } + + /** + * Gets all item configurations + * @return Config[] An associative array of item file names to their Config objects + */ + public function getItemConfigs(): array { + $configs = []; + foreach($this->getItemFiles() as $itemFile) { + $configs[$itemFile] = $this->getItemConfig($itemFile); + } + return $configs; + } + + /** + * Registers all items from configuration files + */ + public function registerItems(): void { + $registeredCount = 0; + $errorCount = 0; + foreach($this->getItemFiles() as $itemFile) { + try { + $this->registerItem($itemFile); + $registeredCount++; + } catch(\Exception $e) { + $errorCount++; + Customies::getInstance()->getLogger()->error( + "Failed to register item from '$itemFile': " . $e->getMessage() + ); + } + } + Customies::getInstance()->getLogger()->info( + "Registered $registeredCount custom items" . + ($errorCount > 0 ? " ($errorCount failed)" : "") + ); + } + + /** + * Registers a single item from its configuration file + * @param string $itemFile The name of the item configuration file + * @throws \InvalidArgumentException If the item configuration is invalid + */ + public function registerItem(string $itemFile): void { + $itemConfig = $this->getItemConfig($itemFile)->getAll(); + + if(!isset($itemConfig["minecraft:item"])) { + throw new \InvalidArgumentException("Invalid item config: missing minecraft:item"); + } + + $minecraftItem = $itemConfig["minecraft:item"]; + + if(!isset($minecraftItem["components"], $minecraftItem["description"]["identifier"])) { + throw new \InvalidArgumentException("Invalid item config: missing required fields"); + } + + $customItem = new CustomiesItem($minecraftItem["components"]); + $identifier = $minecraftItem["description"]["identifier"]; + + $creativeInfo = $this->getCreativeInventoryInfo($minecraftItem); + + CustomiesItemFactory::getInstance()->registerItem( + static fn() => $customItem, + $identifier, + $creativeInfo + ); + } + + /** + * Gets creative inventory information from config + * @param array $itemConfig The item configuration array + * @return CreativeInventoryInfo The creative inventory info object + */ + private function getCreativeInventoryInfo(array $itemConfig): CreativeInventoryInfo { + $category = CreativeInventoryInfo::CATEGORY_ITEMS; + $group = CreativeInventoryInfo::NONE; + + if(isset($itemConfig["description"]["menu_category"])) { + $creativeConfig = $itemConfig["description"]["menu_category"]; + $category = $creativeConfig["category"] ?? $category; + $group = $creativeConfig["group"] ?? $group; + } + + return new CreativeInventoryInfo($category, $group); + } +} \ No newline at end of file diff --git a/src/item/component/AllowOffHandComponent.php b/src/customiesdevs/customies/item/component/AllowOffHandComponent.php similarity index 75% rename from src/item/component/AllowOffHandComponent.php rename to src/customiesdevs/customies/item/component/AllowOffHandComponent.php index 337f8a02..016b8181 100644 --- a/src/item/component/AllowOffHandComponent.php +++ b/src/customiesdevs/customies/item/component/AllowOffHandComponent.php @@ -16,14 +16,12 @@ public function __construct(bool $offHand = true) { } public function getName(): string { - return "allow_off_hand"; + return "minecraft:allow_off_hand"; } - public function getValue(): bool { - return $this->offHand; - } - - public function isProperty(): bool { - return true; + public function getValue(): array { + return [ + "value" => $this->offHand + ]; } } \ No newline at end of file diff --git a/src/item/component/BlockPlacerComponent.php b/src/customiesdevs/customies/item/component/BlockPlacerComponent.php similarity index 72% rename from src/item/component/BlockPlacerComponent.php rename to src/customiesdevs/customies/item/component/BlockPlacerComponent.php index 2a0953ba..b7f01fa2 100644 --- a/src/item/component/BlockPlacerComponent.php +++ b/src/customiesdevs/customies/item/component/BlockPlacerComponent.php @@ -9,14 +9,17 @@ final class BlockPlacerComponent implements ItemComponent { private Block $block; + private bool $replaceBlockItem; private array $useOn = []; /** * Sets the item as a Planter item component for blocks. Items with this component will place a block when used. * @param Block $block + * @param bool $replaceBlockItem If true, the item will be registered as the item for this block. */ - public function __construct(Block $block) { + public function __construct(Block $block, bool $replaceBlockItem = false) { $this->block = $block; + $this->replaceBlockItem = $replaceBlockItem; } public function getName(): string { @@ -26,19 +29,17 @@ public function getName(): string { public function getValue(): array { return [ "block" => GlobalBlockStateHandlers::getSerializer()->serialize($this->block->getStateId())->getName(), + "replace_block_item" => $this->replaceBlockItem, "use_on" => $this->useOn ]; } - public function isProperty(): bool { - return false; - } - /** + * TODO: Update this * Add blocks to the `use_on` array in the required format. * @param Block ...$blocks */ - public function useOn(Block ...$blocks): self{ + public function useOn(Block ...$blocks): self { foreach($blocks as $block){ $this->useOn[] = [ "name" => GlobalBlockStateHandlers::getSerializer()->serialize($block->getStateId())->getName() diff --git a/src/item/component/BundleInteractionComponent.php b/src/customiesdevs/customies/item/component/BundleInteractionComponent.php similarity index 63% rename from src/item/component/BundleInteractionComponent.php rename to src/customiesdevs/customies/item/component/BundleInteractionComponent.php index 79e446fc..4716ec83 100644 --- a/src/item/component/BundleInteractionComponent.php +++ b/src/customiesdevs/customies/item/component/BundleInteractionComponent.php @@ -8,11 +8,11 @@ final class BundleInteractionComponent implements ItemComponent { private int $numViewableSlots; /** - * `minecraft:bundle_interaction` enables the bundle-specific interaction scheme and tooltip for an item. + * Enables the bundle-specific interaction scheme and tooltip for an item. * To use this component, the item must have a `minecraft:storage_item` item component defined. - * @param int $numViewableSlots The number of slots that are viewable in the bundle. + * @param int $numViewableSlots The maximum number of slots in the bundle viewable by the plater. Can be from 1 to 64. Default is 12. Value must be >= 1. Value must be <= 64. */ - public function __construct(int $numViewableSlots) { + public function __construct(int $numViewableSlots = 12) { $this->numViewableSlots = $numViewableSlots; } @@ -25,8 +25,4 @@ public function getValue(): array { "num_viewable_slots" => $this->numViewableSlots ]; } - - public function isProperty(): bool { - return false; - } } \ No newline at end of file diff --git a/src/item/component/CanDestroyInCreativeComponent.php b/src/customiesdevs/customies/item/component/CanDestroyInCreativeComponent.php similarity index 75% rename from src/item/component/CanDestroyInCreativeComponent.php rename to src/customiesdevs/customies/item/component/CanDestroyInCreativeComponent.php index 0c7c6d5f..04a3bb08 100644 --- a/src/item/component/CanDestroyInCreativeComponent.php +++ b/src/customiesdevs/customies/item/component/CanDestroyInCreativeComponent.php @@ -16,14 +16,12 @@ public function __construct(bool $canDestroyInCreative = true) { } public function getName(): string { - return "can_destroy_in_creative"; + return "minecraft:can_destroy_in_creative"; } - public function getValue(): bool { - return $this->canDestroyInCreative; - } - - public function isProperty(): bool { - return true; + public function getValue(): array { + return [ + "value" => $this->canDestroyInCreative + ]; } } diff --git a/src/customiesdevs/customies/item/component/CompostableComponent.php b/src/customiesdevs/customies/item/component/CompostableComponent.php new file mode 100644 index 00000000..694d1768 --- /dev/null +++ b/src/customiesdevs/customies/item/component/CompostableComponent.php @@ -0,0 +1,27 @@ += 1. Value must be <= 100. + */ + public function __construct(int $compostingChance) { + $this->compostingChance = $compostingChance; + } + + public function getName(): string { + return "minecraft:compostable"; + } + + public function getValue(): array { + return [ + "composting_chance" => $this->compostingChance + ]; + } +} \ No newline at end of file diff --git a/src/customiesdevs/customies/item/component/CooldownComponent.php b/src/customiesdevs/customies/item/component/CooldownComponent.php new file mode 100644 index 00000000..5870902f --- /dev/null +++ b/src/customiesdevs/customies/item/component/CooldownComponent.php @@ -0,0 +1,37 @@ +category = $category; + $this->duration = $duration; + } + + public function getName(): string { + return "minecraft:cooldown"; + } + + public function getValue(): array { + return [ + "category" => $this->category, + "duration" => $this->duration + ]; + } +} \ No newline at end of file diff --git a/src/customiesdevs/customies/item/component/DamageAbsorptionComponent.php b/src/customiesdevs/customies/item/component/DamageAbsorptionComponent.php new file mode 100644 index 00000000..6d0a7598 --- /dev/null +++ b/src/customiesdevs/customies/item/component/DamageAbsorptionComponent.php @@ -0,0 +1,30 @@ +absorbableCauses = $absorbableCauses; + } + + public function getName(): string { + return "minecraft:damage_absorption"; + } + + public function getValue(): array { + return [ + "absorbable_causes" => $this->absorbableCauses + ]; + } +} \ No newline at end of file diff --git a/src/customiesdevs/customies/item/component/DamageComponent.php b/src/customiesdevs/customies/item/component/DamageComponent.php new file mode 100644 index 00000000..d67f5c0c --- /dev/null +++ b/src/customiesdevs/customies/item/component/DamageComponent.php @@ -0,0 +1,28 @@ +damage = $damage; + } + + public function getName(): string { + return "minecraft:damage"; + } + + public function getValue(): array { + return [ + "value" => $this->damage + ]; + } +} \ No newline at end of file diff --git a/src/item/component/DiggerComponent.php b/src/customiesdevs/customies/item/component/DiggerComponent.php similarity index 93% rename from src/item/component/DiggerComponent.php rename to src/customiesdevs/customies/item/component/DiggerComponent.php index 2f2e761c..bf43edc9 100644 --- a/src/item/component/DiggerComponent.php +++ b/src/customiesdevs/customies/item/component/DiggerComponent.php @@ -17,8 +17,9 @@ final class DiggerComponent implements ItemComponent { * Allows a creator to determine how quickly an item can dig specific blocks. * @param bool $useEfficiency Determines whether the item should be impacted if the `efficiency` enchant is applied to it. */ - public function __construct(bool $useEfficiency) { + public function __construct(bool $useEfficiency, array $destroySpeeds = []) { $this->useEfficiency = $useEfficiency; + $this->destroySpeeds = $destroySpeeds; } public function getName(): string { @@ -32,10 +33,6 @@ public function getValue(): array { ]; } - public function isProperty(): bool { - return false; - } - /** * Add blocks to the `destroy_speeds` array in the required format. * @param int $speed Digging speed for the correlating block(s) diff --git a/src/item/component/DisplayNameComponent.php b/src/customiesdevs/customies/item/component/DisplayNameComponent.php similarity index 91% rename from src/item/component/DisplayNameComponent.php rename to src/customiesdevs/customies/item/component/DisplayNameComponent.php index 28ad340b..3d176cc8 100644 --- a/src/item/component/DisplayNameComponent.php +++ b/src/customiesdevs/customies/item/component/DisplayNameComponent.php @@ -25,8 +25,4 @@ public function getValue(): array { "value" => $this->name ]; } - - public function isProperty(): bool { - return false; - } } \ No newline at end of file diff --git a/src/item/component/DurabilityComponent.php b/src/customiesdevs/customies/item/component/DurabilityComponent.php similarity index 95% rename from src/item/component/DurabilityComponent.php rename to src/customiesdevs/customies/item/component/DurabilityComponent.php index 3cc9847e..e33d210f 100644 --- a/src/item/component/DurabilityComponent.php +++ b/src/customiesdevs/customies/item/component/DurabilityComponent.php @@ -34,8 +34,4 @@ public function getValue(): array { "max_durability" => $this->maxDurability ]; } - - public function isProperty(): bool { - return false; - } } \ No newline at end of file diff --git a/src/customiesdevs/customies/item/component/DurabilitySensorComponent.php b/src/customiesdevs/customies/item/component/DurabilitySensorComponent.php new file mode 100644 index 00000000..3e3e28ca --- /dev/null +++ b/src/customiesdevs/customies/item/component/DurabilitySensorComponent.php @@ -0,0 +1,39 @@ +durabilityThresholds = $durabilityThresholds; + } + + public function getName(): string { + return "minecraft:durability_sensor"; + } + + public function getValue(): array { + return [ + "durability_thresholds" => [ + $this->durabilityThresholds + ] + ]; + } + + // I dont know if this will work | TODO test it and see what it outputs + public function addDurabilityThreshold(int $durability, string $particleType = "", string $soundEvent = ""): self { + $this->durabilityThresholds[] = [ + "durability" => $durability, + "particle_type" => $particleType, + "sound_event" => $soundEvent + ]; + return $this; + } +} \ No newline at end of file diff --git a/src/item/component/DyeableComponent.php b/src/customiesdevs/customies/item/component/DyeableComponent.php similarity index 91% rename from src/item/component/DyeableComponent.php rename to src/customiesdevs/customies/item/component/DyeableComponent.php index 563c0ec8..d4ceca01 100644 --- a/src/item/component/DyeableComponent.php +++ b/src/customiesdevs/customies/item/component/DyeableComponent.php @@ -24,8 +24,4 @@ public function getValue(): array { "default_color" => $this->hex ]; } - - public function isProperty(): bool { - return false; - } } \ No newline at end of file diff --git a/src/customiesdevs/customies/item/component/EnchantableComponent.php b/src/customiesdevs/customies/item/component/EnchantableComponent.php new file mode 100644 index 00000000..d6029ba7 --- /dev/null +++ b/src/customiesdevs/customies/item/component/EnchantableComponent.php @@ -0,0 +1,68 @@ +slot = $slot; + $this->value = $value; + } + + public function getName(): string { + return "minecraft:enchantable"; + } + + public function getValue(): array { + return [ + "slot" => $this->slot, + "value" => $this->value + ]; + } +} \ No newline at end of file diff --git a/src/customiesdevs/customies/item/component/FireResistantComponent.php b/src/customiesdevs/customies/item/component/FireResistantComponent.php new file mode 100644 index 00000000..eaac5f3c --- /dev/null +++ b/src/customiesdevs/customies/item/component/FireResistantComponent.php @@ -0,0 +1,27 @@ +fireResistant = $fireResistant; + } + + public function getName(): string { + return "minecraft:fire_resistant"; + } + + public function getValue(): array { + return [ + "value" => $this->fireResistant + ]; + } +} \ No newline at end of file diff --git a/src/item/component/FoodComponent.php b/src/customiesdevs/customies/item/component/FoodComponent.php similarity index 95% rename from src/item/component/FoodComponent.php rename to src/customiesdevs/customies/item/component/FoodComponent.php index a06a855b..d2082b3d 100644 --- a/src/item/component/FoodComponent.php +++ b/src/customiesdevs/customies/item/component/FoodComponent.php @@ -38,8 +38,4 @@ public function getValue(): array { ] ]; } - - public function isProperty(): bool { - return false; - } } \ No newline at end of file diff --git a/src/item/component/FuelComponent.php b/src/customiesdevs/customies/item/component/FuelComponent.php similarity index 90% rename from src/item/component/FuelComponent.php rename to src/customiesdevs/customies/item/component/FuelComponent.php index f86e8554..804daa28 100644 --- a/src/item/component/FuelComponent.php +++ b/src/customiesdevs/customies/item/component/FuelComponent.php @@ -24,8 +24,4 @@ public function getValue(): array { "duration" => $this->duration ]; } - - public function isProperty(): bool { - return false; - } } \ No newline at end of file diff --git a/src/item/component/GlintComponent.php b/src/customiesdevs/customies/item/component/GlintComponent.php similarity index 75% rename from src/item/component/GlintComponent.php rename to src/customiesdevs/customies/item/component/GlintComponent.php index 5a9b2c19..30f5648c 100644 --- a/src/item/component/GlintComponent.php +++ b/src/customiesdevs/customies/item/component/GlintComponent.php @@ -16,14 +16,12 @@ public function __construct(bool $glint = true) { } public function getName(): string { - return "foil"; + return "minecraft:glint"; } - public function getValue(): bool { - return $this->glint; - } - - public function isProperty(): bool { - return true; + public function getValue(): array { + return [ + "value" => $this->glint + ]; } } \ No newline at end of file diff --git a/src/item/component/HandEquippedComponent.php b/src/customiesdevs/customies/item/component/HandEquippedComponent.php similarity index 75% rename from src/item/component/HandEquippedComponent.php rename to src/customiesdevs/customies/item/component/HandEquippedComponent.php index dad02413..af352248 100644 --- a/src/item/component/HandEquippedComponent.php +++ b/src/customiesdevs/customies/item/component/HandEquippedComponent.php @@ -16,14 +16,12 @@ public function __construct(bool $handEquipped = true) { } public function getName(): string { - return "hand_equipped"; + return "minecraft:hand_equipped"; } - public function getValue(): bool { - return $this->handEquipped; - } - - public function isProperty(): bool { - return true; + public function getValue(): array { + return [ + "value" => $this->handEquipped + ]; } } \ No newline at end of file diff --git a/src/item/component/HoverTextColorComponent.php b/src/customiesdevs/customies/item/component/HoverTextColorComponent.php similarity index 78% rename from src/item/component/HoverTextColorComponent.php rename to src/customiesdevs/customies/item/component/HoverTextColorComponent.php index 2947145f..db1d6504 100644 --- a/src/item/component/HoverTextColorComponent.php +++ b/src/customiesdevs/customies/item/component/HoverTextColorComponent.php @@ -17,14 +17,12 @@ public function __construct(string $hoverTextColor) { } public function getName(): string { - return "hover_text_color"; + return "minecraft:hover_text_color"; } - public function getValue(): string { - return $this->hoverTextColor; - } - - public function isProperty(): bool { - return true; + public function getValue(): array { + return [ + "value" => $this->hoverTextColor + ]; } } \ No newline at end of file diff --git a/src/customiesdevs/customies/item/component/IconComponent.php b/src/customiesdevs/customies/item/component/IconComponent.php new file mode 100644 index 00000000..4f3e79e0 --- /dev/null +++ b/src/customiesdevs/customies/item/component/IconComponent.php @@ -0,0 +1,51 @@ +default_texture = $default_texture; + $this->dyed_texture = $dyed_texture; + $this->trim_texture = $trim_texture; + $this->bundle_open_back_texture = $bundle_open_back_texture; + $this->bundle_open_front_tetxure = $bundle_open_front_texture; + } + + public function getName(): string { + return "minecraft:icon"; + } + + public function getValue(): array { + return [ + "textures" => [ + "default" => $this->default_texture, + "dyed" => $this->dyed_texture == "" ? $this->default_texture : $this->dyed_texture, + "icon_trim" => $this->trim_texture == "" ? $this->default_texture : $this->trim_texture, + "bundle_open_back" => $this->bundle_open_back_texture == "" ? $this->default_texture : $this->bundle_open_back_texture, + "bundle_open_front" => $this->bundle_open_front_tetxure == "" ? $this->default_texture : $this->bundle_open_front_tetxure + ] + ]; + } +} \ No newline at end of file diff --git a/src/item/component/InteractButtonComponent.php b/src/customiesdevs/customies/item/component/InteractButtonComponent.php similarity index 84% rename from src/item/component/InteractButtonComponent.php rename to src/customiesdevs/customies/item/component/InteractButtonComponent.php index fa24a019..1aa6163b 100644 --- a/src/item/component/InteractButtonComponent.php +++ b/src/customiesdevs/customies/item/component/InteractButtonComponent.php @@ -5,7 +5,7 @@ final class InteractButtonComponent implements ItemComponent { - private bool|string $interactButton; + private string $interactButton; /** * Ineract Button is a boolean or string that determines if the interact button is shown in touch controls, and what text is displayed on the button. When set to 'true', the default 'Use Item' text will be used. @@ -25,12 +25,8 @@ public function getName(): string { public function getValue(): array { return [ - "interact_text" => (string) $this->interactButton, + "interact_text" => $this->interactButton, "requires_interact" => 1 ]; } - - public function isProperty(): bool { - return false; - } } \ No newline at end of file diff --git a/src/item/component/ItemComponent.php b/src/customiesdevs/customies/item/component/ItemComponent.php similarity index 82% rename from src/item/component/ItemComponent.php rename to src/customiesdevs/customies/item/component/ItemComponent.php index 4f50fa56..f76a408c 100644 --- a/src/item/component/ItemComponent.php +++ b/src/customiesdevs/customies/item/component/ItemComponent.php @@ -8,6 +8,4 @@ interface ItemComponent { public function getName(): string; public function getValue(): mixed; - - public function isProperty(): bool; } \ No newline at end of file diff --git a/src/item/component/LiquidClippedComponent.php b/src/customiesdevs/customies/item/component/LiquidClippedComponent.php similarity index 76% rename from src/item/component/LiquidClippedComponent.php rename to src/customiesdevs/customies/item/component/LiquidClippedComponent.php index f80160d7..dcc4389d 100644 --- a/src/item/component/LiquidClippedComponent.php +++ b/src/customiesdevs/customies/item/component/LiquidClippedComponent.php @@ -16,14 +16,12 @@ public function __construct(bool $liquidClipped = true) { } public function getName(): string { - return "liquid_clipped"; + return "minecraft:liquid_clipped"; } - public function getValue(): bool { - return $this->liquidClipped; - } - - public function isProperty(): bool { - return true; + public function getValue(): array { + return [ + "value" => $this->liquidClipped + ]; } } \ No newline at end of file diff --git a/src/item/component/MaxStackSizeComponent.php b/src/customiesdevs/customies/item/component/MaxStackSizeComponent.php similarity index 75% rename from src/item/component/MaxStackSizeComponent.php rename to src/customiesdevs/customies/item/component/MaxStackSizeComponent.php index 4d2327a1..d5480f5b 100644 --- a/src/item/component/MaxStackSizeComponent.php +++ b/src/customiesdevs/customies/item/component/MaxStackSizeComponent.php @@ -16,14 +16,12 @@ public function __construct(int $maxStackSize = 64) { } public function getName(): string { - return "max_stack_size"; + return "minecraft:max_stack_size"; } - public function getValue(): int { - return $this->maxStackSize; - } - - public function isProperty(): bool { - return true; + public function getValue(): array { + return [ + "value" => $this->maxStackSize + ]; } } \ No newline at end of file diff --git a/src/item/component/ProjectileComponent.php b/src/customiesdevs/customies/item/component/ProjectileComponent.php similarity index 95% rename from src/item/component/ProjectileComponent.php rename to src/customiesdevs/customies/item/component/ProjectileComponent.php index 215ad273..742ce367 100644 --- a/src/item/component/ProjectileComponent.php +++ b/src/customiesdevs/customies/item/component/ProjectileComponent.php @@ -30,8 +30,4 @@ public function getValue(): array { "projectile_entity" => $this->projectileEntity ]; } - - public function isProperty(): bool { - return false; - } } \ No newline at end of file diff --git a/src/item/component/RarityComponent.php b/src/customiesdevs/customies/item/component/RarityComponent.php similarity index 94% rename from src/item/component/RarityComponent.php rename to src/customiesdevs/customies/item/component/RarityComponent.php index 7ee3a185..0f1eb19a 100644 --- a/src/item/component/RarityComponent.php +++ b/src/customiesdevs/customies/item/component/RarityComponent.php @@ -30,8 +30,4 @@ public function getValue(): array { "value" => $this->rarity ]; } - - public function isProperty(): bool { - return false; - } } \ No newline at end of file diff --git a/src/item/component/RecordComponent.php b/src/customiesdevs/customies/item/component/RecordComponent.php similarity index 94% rename from src/item/component/RecordComponent.php rename to src/customiesdevs/customies/item/component/RecordComponent.php index 7728d9d4..eca34eed 100644 --- a/src/item/component/RecordComponent.php +++ b/src/customiesdevs/customies/item/component/RecordComponent.php @@ -32,8 +32,4 @@ public function getValue(): array { "sound_event" => $this->soundEvent ]; } - - public function isProperty(): bool { - return false; - } } \ No newline at end of file diff --git a/src/item/component/ShooterComponent.php b/src/customiesdevs/customies/item/component/ShooterComponent.php similarity index 95% rename from src/item/component/ShooterComponent.php rename to src/customiesdevs/customies/item/component/ShooterComponent.php index 14f0e813..8043c7dd 100644 --- a/src/item/component/ShooterComponent.php +++ b/src/customiesdevs/customies/item/component/ShooterComponent.php @@ -43,7 +43,9 @@ public function getValue(): array { return [ "ammunition" => [ [ - "item" => $this->item, + "item" => [ + "name" => $this->item + ], "use_offhand" => $this->useOffhand, "search_inventory" => $this->searchInventory, "use_in_creative" => $this->useInCreative @@ -54,8 +56,4 @@ public function getValue(): array { "scale_power_by_draw_duration" => $this->scalePowerByDrawDuration ]; } - - public function isProperty(): bool { - return false; - } } \ No newline at end of file diff --git a/src/item/component/ShouldDespawnComponent.php b/src/customiesdevs/customies/item/component/ShouldDespawnComponent.php similarity index 77% rename from src/item/component/ShouldDespawnComponent.php rename to src/customiesdevs/customies/item/component/ShouldDespawnComponent.php index 126f8d54..9c06cc6f 100644 --- a/src/item/component/ShouldDespawnComponent.php +++ b/src/customiesdevs/customies/item/component/ShouldDespawnComponent.php @@ -16,14 +16,12 @@ public function __construct(bool $shouldDespawn = true) { } public function getName(): string { - return "should_despawn"; + return "minecraft:should_despawn"; } - public function getValue(): bool { - return $this->shouldDespawn; - } - - public function isProperty(): bool { - return true; + public function getValue(): array { + return [ + "value" => $this->shouldDespawn + ]; } } \ No newline at end of file diff --git a/src/item/component/StackedByDataComponent.php b/src/customiesdevs/customies/item/component/StackedByDataComponent.php similarity index 79% rename from src/item/component/StackedByDataComponent.php rename to src/customiesdevs/customies/item/component/StackedByDataComponent.php index 1eddb16f..f0b9997c 100644 --- a/src/item/component/StackedByDataComponent.php +++ b/src/customiesdevs/customies/item/component/StackedByDataComponent.php @@ -17,14 +17,12 @@ public function __construct(bool $stackedByData = true) { } public function getName(): string { - return "stacked_by_data"; + return "minecraft:stacked_by_data"; } - public function getValue(): bool { - return $this->stackedByData; - } - - public function isProperty(): bool { - return true; + public function getValue(): array { + return [ + "value" => $this->stackedByData + ]; } } \ No newline at end of file diff --git a/src/customiesdevs/customies/item/component/StorageItemComponent.php b/src/customiesdevs/customies/item/component/StorageItemComponent.php new file mode 100644 index 00000000..6a921d9e --- /dev/null +++ b/src/customiesdevs/customies/item/component/StorageItemComponent.php @@ -0,0 +1,55 @@ += 0. + * @param int $maxWeightLimit The maximum weight limit for the storage item. Maximum is 64. Default is 64. Value must be >= 0. + * @param int $weightInStorageItem The weight that the storage item itself contributes to the total weight of the items it contains. Value must be >= 0. + */ + public function __construct( + bool $allowNestedStorageItems = true, + array $allowedItems = [], + array $bannedItems = [], + int $maxSlots = 64, + int $maxWeightLimit = 64, + int $weightInStorageItem = 4 + ) { + $this->allowNestedStorageItems = $allowNestedStorageItems; + $this->allowedItems = $allowedItems; + $this->bannedItems = $bannedItems; + $this->maxSlots = $maxSlots; + $this->maxWeightLimit = $maxWeightLimit; + $this->weightInStorageItem = $weightInStorageItem; + } + + public function getName(): string { + return "minecraft:storage_item"; + } + + public function getValue(): array { + return [ + "allow_nested_storage_items" => $this->allowNestedStorageItems, + "allowed_items" => $this->allowedItems, + "banned_items" => $this->bannedItems, + "max_slots" => $this->maxSlots, + "max_weight_limit" => $this->maxWeightLimit, + "weight_in_storage_item" => $this->weightInStorageItem + ]; + } +} \ No newline at end of file diff --git a/src/customiesdevs/customies/item/component/StorageWeightLimitComponent.php b/src/customiesdevs/customies/item/component/StorageWeightLimitComponent.php new file mode 100644 index 00000000..5b0555bf --- /dev/null +++ b/src/customiesdevs/customies/item/component/StorageWeightLimitComponent.php @@ -0,0 +1,27 @@ += 0. + */ + public function __construct(int $maxWeightLimit = 64) { + $this->maxWeightLimit = $maxWeightLimit; + } + + public function getName(): string { + return "minecraft:storage_weight_limit"; + } + + public function getValue(): array { + return [ + "max_weight_limit" => $this->maxWeightLimit + ]; + } +} \ No newline at end of file diff --git a/src/customiesdevs/customies/item/component/StorageWeightModifierComponent.php b/src/customiesdevs/customies/item/component/StorageWeightModifierComponent.php new file mode 100644 index 00000000..48d0b4dd --- /dev/null +++ b/src/customiesdevs/customies/item/component/StorageWeightModifierComponent.php @@ -0,0 +1,27 @@ +weightInStorageItem = $weightInStorageItem; + } + + public function getName(): string { + return "minecraft:storage_weight_modifier"; + } + + public function getValue(): array { + return [ + "weight_in_storage_item" => $this->weightInStorageItem + ]; + } +} \ No newline at end of file diff --git a/src/customiesdevs/customies/item/component/SwingDurationComponent.php b/src/customiesdevs/customies/item/component/SwingDurationComponent.php new file mode 100644 index 00000000..7553ffe9 --- /dev/null +++ b/src/customiesdevs/customies/item/component/SwingDurationComponent.php @@ -0,0 +1,27 @@ +swingDuration = $swingDuration; + } + + public function getName(): string { + return "minecraft:swing_duration"; + } + + public function getValue(): array { + return [ + "value" => $this->swingDuration + ]; + } +} \ No newline at end of file diff --git a/src/customiesdevs/customies/item/component/TagsComponent.php b/src/customiesdevs/customies/item/component/TagsComponent.php new file mode 100644 index 00000000..aa3dc0cb --- /dev/null +++ b/src/customiesdevs/customies/item/component/TagsComponent.php @@ -0,0 +1,27 @@ +tags = $tags; + } + + public function getName(): string { + return "minecraft:tags"; + } + + public function getValue(): array { + return [ + "tags" => $this->tags + ]; + } +} \ No newline at end of file diff --git a/src/item/component/ThrowableComponent.php b/src/customiesdevs/customies/item/component/ThrowableComponent.php similarity index 73% rename from src/item/component/ThrowableComponent.php rename to src/customiesdevs/customies/item/component/ThrowableComponent.php index 8edcc94d..2524988a 100644 --- a/src/item/component/ThrowableComponent.php +++ b/src/customiesdevs/customies/item/component/ThrowableComponent.php @@ -14,14 +14,21 @@ final class ThrowableComponent implements ItemComponent { /** * Sets the throwable item component. - * @param bool $doSwingAnimation Determines whether the item should use the swing animation when thrown - * @param float $launchPowerScale The scale at which the power of the throw increases - * @param float $maxDrawDuration The maximum duration to draw a throwable item - * @param float $maxLaunchPower The maximum power to launch the throwable item - * @param float $minDrawDuration The minimum duration to draw a throwable item - * @param bool $scalePowerByDrawDuration Whether or not the power of the throw increases with duration charged + * @param bool $doSwingAnimation Determines whether the item should use the swing animation when thrown. Default is set to false. + * @param float $launchPowerScale The scale at which the power of the throw increases. Default is set to 1.0. + * @param float $maxDrawDuration The maximum duration to draw a throwable item. Default is set to 0.0. + * @param float $maxLaunchPower The maximum power to launch the throwable item. Default is set to 1.0. + * @param float $minDrawDuration The minimum duration to draw a throwable item. Default is set to 0.0. + * @param bool $scalePowerByDrawDuration Whether or not the power of the throw increases with duration charged. Default is set to false. */ - public function __construct(bool $doSwingAnimation = false, float $launchPowerScale = 1.0, float $maxDrawDuration = 0.0, float $maxLaunchPower = 1.0, float $minDrawDuration = 0.0, bool $scalePowerByDrawDuration = false) { + public function __construct( + bool $doSwingAnimation = false, + float $launchPowerScale = 1.0, + float $maxDrawDuration = 0.0, + float $maxLaunchPower = 1.0, + float $minDrawDuration = 0.0, + bool $scalePowerByDrawDuration = false + ) { $this->doSwingAnimation = $doSwingAnimation; $this->launchPowerScale = $launchPowerScale; $this->maxDrawDuration = $maxDrawDuration; @@ -44,8 +51,4 @@ public function getValue(): array { "scale_power_by_draw_duration" => $this->scalePowerByDrawDuration ]; } - - public function isProperty(): bool { - return false; - } } \ No newline at end of file diff --git a/src/item/component/UseAnimationComponent.php b/src/customiesdevs/customies/item/component/UseAnimationComponent.php similarity index 80% rename from src/item/component/UseAnimationComponent.php rename to src/customiesdevs/customies/item/component/UseAnimationComponent.php index deafdffc..6b94b733 100644 --- a/src/item/component/UseAnimationComponent.php +++ b/src/customiesdevs/customies/item/component/UseAnimationComponent.php @@ -20,21 +20,19 @@ final class UseAnimationComponent implements ItemComponent { /** * Determines which animation plays when using an item. - * @param int $animation Specifies which animation to play when the the item is used, Default is set to `0` + * @param int $animation Specifies which animation to play when the the item is used. */ public function __construct(int $animation) { $this->animation = $animation; } public function getName(): string { - return "use_animation"; + return "minecraft:use_animation"; } - public function getValue(): int { - return $this->animation; - } - - public function isProperty(): bool { - return true; + public function getValue(): array { + return [ + "value" => $this->animation + ]; } } \ No newline at end of file diff --git a/src/item/component/UseModifiersComponent.php b/src/customiesdevs/customies/item/component/UseModifiersComponent.php similarity index 93% rename from src/item/component/UseModifiersComponent.php rename to src/customiesdevs/customies/item/component/UseModifiersComponent.php index a4a6b11a..84c61fbd 100644 --- a/src/item/component/UseModifiersComponent.php +++ b/src/customiesdevs/customies/item/component/UseModifiersComponent.php @@ -28,8 +28,4 @@ public function getValue(): array { "use_duration" => $this->useDuration ]; } - - public function isProperty(): bool { - return false; - } } \ No newline at end of file diff --git a/src/item/component/WearableComponent.php b/src/customiesdevs/customies/item/component/WearableComponent.php similarity index 71% rename from src/item/component/WearableComponent.php rename to src/customiesdevs/customies/item/component/WearableComponent.php index 29a25ae8..b3848373 100644 --- a/src/item/component/WearableComponent.php +++ b/src/customiesdevs/customies/item/component/WearableComponent.php @@ -22,18 +22,18 @@ final class WearableComponent implements ItemComponent { private string $slot; private int $protection; - private bool $dispensable; + private bool $hidePlayerLocation; /** * Sets the wearable item component. - * @param string $slot Specifies where the item can be worn - * @param int $protection How much protection the wearable item provides - * @param bool $dispensable Whether the wearable item can be dispensed + * @param string $slot Specifies where the item can be worn. If any non-hand slot is chosen, the max stack size is set to 1. + * @param int $protection How much protection the wearable item provides. Default is set to 0. + * @param bool $hidePlayerLocation Determines whether the Player's location is hidden on Locator Maps and the Locator Bar when the wearable item is worn. Default is false. */ - public function __construct(string $slot, int $protection = 0, bool $dispensable = true) { + public function __construct(string $slot, int $protection = 0, bool $hidePlayerLocation = false) { $this->slot = $slot; $this->protection = $protection; - $this->dispensable = $dispensable; + $this->hidePlayerLocation = $hidePlayerLocation; } public function getName(): string { @@ -44,11 +44,7 @@ public function getValue(): array { return [ "slot" => $this->slot, "protection" => $this->protection, - "dispensable" => $this->dispensable + "hides_player_location" => $this->hidePlayerLocation ]; } - - public function isProperty(): bool { - return false; - } } \ No newline at end of file diff --git a/src/task/AsyncRegisterBlocksTask.php b/src/customiesdevs/customies/task/AsyncRegisterBlocksTask.php similarity index 100% rename from src/task/AsyncRegisterBlocksTask.php rename to src/customiesdevs/customies/task/AsyncRegisterBlocksTask.php diff --git a/src/util/NBT.php b/src/customiesdevs/customies/util/NBT.php similarity index 100% rename from src/util/NBT.php rename to src/customiesdevs/customies/util/NBT.php diff --git a/src/item/component/CooldownComponent.php b/src/item/component/CooldownComponent.php deleted file mode 100644 index 54f025a4..00000000 --- a/src/item/component/CooldownComponent.php +++ /dev/null @@ -1,42 +0,0 @@ -category = $category; - $this->duration = $duration; - } - - public function getName(): string { - return "minecraft:cooldown"; - } - - public function getValue(): array { - return [ - "category" => $this->category, - "duration" => $this->duration - ]; - } - - public function isProperty(): bool { - return false; - } -} \ No newline at end of file diff --git a/src/item/component/DamageAbsorptionComponent.php b/src/item/component/DamageAbsorptionComponent.php deleted file mode 100644 index 0acd6270..00000000 --- a/src/item/component/DamageAbsorptionComponent.php +++ /dev/null @@ -1,32 +0,0 @@ -absorbableCauses = $absorbableCauses; - } - - public function getName(): string { - return "minecraft:damage_absorption"; - } - - public function getValue(): array { - return [ - "absorbable_causes" => $this->absorbableCauses - ]; - } - - public function isProperty(): bool { - return false; - } -} \ No newline at end of file diff --git a/src/item/component/DamageComponent.php b/src/item/component/DamageComponent.php deleted file mode 100644 index 95a22a6e..00000000 --- a/src/item/component/DamageComponent.php +++ /dev/null @@ -1,29 +0,0 @@ -damage = $damage; - } - - public function getName(): string { - return "damage"; - } - - public function getValue(): int { - return $this->damage; - } - - public function isProperty(): bool { - return true; - } -} \ No newline at end of file diff --git a/src/item/component/EnchantableSlotComponent.php b/src/item/component/EnchantableSlotComponent.php deleted file mode 100644 index f97b520b..00000000 --- a/src/item/component/EnchantableSlotComponent.php +++ /dev/null @@ -1,48 +0,0 @@ -slot = $slot; - } - - public function getName(): string { - return "enchantable_slot"; - } - - public function getValue(): string { - return $this->slot; - } - - public function isProperty(): bool { - return true; - } -} \ No newline at end of file diff --git a/src/item/component/EnchantableValueComponent.php b/src/item/component/EnchantableValueComponent.php deleted file mode 100644 index 80963464..00000000 --- a/src/item/component/EnchantableValueComponent.php +++ /dev/null @@ -1,47 +0,0 @@ -value = $value; - } - - public function getName(): string { - return "enchantable_value"; - } - - public function getValue(): int { - return $this->value; - } - - public function isProperty(): bool { - return true; - } -} \ No newline at end of file diff --git a/src/item/component/IconComponent.php b/src/item/component/IconComponent.php deleted file mode 100644 index dd92a950..00000000 --- a/src/item/component/IconComponent.php +++ /dev/null @@ -1,42 +0,0 @@ -default_texture = $default_texture; - $this->dyed_texture = $dyed_texture; - $this->trim_texture = $trim_texture; - } - - public function getName(): string { - return "minecraft:icon"; - } - - public function getValue(): array { - return [ - "texture" => $this->default_texture, - "textures" => [ - "default" => $this->default_texture, - "dyed" => $this->dyed_texture, - "icon_trim" => $this->trim_texture - ] - ]; - } - - public function isProperty(): bool { - return true; - } -} \ No newline at end of file diff --git a/src/item/component/UseDurationComponent.php b/src/item/component/UseDurationComponent.php deleted file mode 100644 index 5c8b654e..00000000 --- a/src/item/component/UseDurationComponent.php +++ /dev/null @@ -1,29 +0,0 @@ -duration = $duration; - } - - public function getName(): string { - return "use_duration"; - } - - public function getValue(): int { - return $this->duration; - } - - public function isProperty(): bool { - return true; - } -} \ No newline at end of file diff --git a/test/server-output.txt b/test/server-output.txt new file mode 100644 index 00000000..6e108e4f --- /dev/null +++ b/test/server-output.txt @@ -0,0 +1,73 @@ +string(15) "nk:allowoffhand" +string(2132) "TAG_Compound={ + "minecraft:allow_off_hand" => TAG_Compound={ + "value" => TAG_Byte=1 + } + "minecraft:can_destroy_in_creative" => TAG_Compound={ + "value" => TAG_Byte=1 + } + "minecraft:dyeable" => TAG_Compound={ + "default_color" => TAG_String="#175882" + } + "minecraft:enchantable" => TAG_Compound={ + "slot" => TAG_String="sword" + "value" => TAG_Int=10 + } + "minecraft:fuel" => TAG_Compound={ + "duration" => TAG_Float=3 + } + "minecraft:glint" => TAG_Compound={ + "value" => TAG_Byte=1 + } + "minecraft:hand_equipped" => TAG_Compound={ + "value" => TAG_Byte=1 + } + "minecraft:hover_text_color" => TAG_Compound={ + "value" => TAG_String="minecoin_gold" + } + "minecraft:interact_button" => TAG_Compound={ + "interact_text" => TAG_String="Use This Custom Item" + "requires_interact" => TAG_Int=1 + } + "minecraft:max_stack_size" => TAG_Compound={ + "value" => TAG_Int=7 + } + "minecraft:rarity" => TAG_Compound={ + "value" => TAG_String="rare" + } + "minecraft:should_despawn" => TAG_Compound={ + "value" => TAG_Byte=1 + } + "minecraft:stacked_by_data" => TAG_Compound={ + "value" => TAG_Byte=1 + } + "item_properties" => TAG_Compound={ + "allow_off_hand" => TAG_Byte=1 + "can_destroy_in_creative" => TAG_Byte=1 + "damage" => TAG_Int=0 + "enchantable_slot" => TAG_String="sword" + "enchantable_value" => TAG_Int=10 + "foil" => TAG_Byte=1 + "frame_count" => TAG_Int=1 + "hand_equipped" => TAG_Byte=1 + "liquid_clipped" => TAG_Byte=0 + "max_stack_size" => TAG_Int=7 + "mining_speed" => TAG_Int=1 + "should_despawn" => TAG_Byte=1 + "stacked_by_data" => TAG_Byte=1 + "use_animation" => TAG_Int=0 + "use_duration" => TAG_Int=0 + "minecraft:icon" => TAG_Compound={ + "textures" => TAG_Compound={ + "default" => TAG_String="apple" + "dyed" => TAG_String="apple" + "icon_trim" => TAG_String="apple" + "bundle_open_back" => TAG_String="apple" + "bundle_open_front" => TAG_String="apple" + } + } + "hover_text_color" => TAG_String="minecoin_gold" + "creative_category" => TAG_Int=4 + "creative_group" => TAG_String="none" + } +}" \ No newline at end of file diff --git a/test/test.json b/test/test.json new file mode 100644 index 00000000..c323f552 --- /dev/null +++ b/test/test.json @@ -0,0 +1,30 @@ +{ + "components": { + "item_properties": { + "allow_off_hand": 0, // default value false + "can_destroy_in_creative": 1, // default value true + "creative_category": 4, + "creative_group": "", + "damage": 0, // default value int + "enchantable_slot": "none", // default value string + "enchantable_value": 0, // default value int + "foil": 0, // default value false + "frame_count": 1, // default value + "hand_equipped": 0, // default value false + "hover_text_color": "§g", // this is only added when the hover text color component is added + "liquid_clipped": 0, // default value false + "max_stack_size": 64, // default value + "minecraft:icon": { + "textures": { + "default": "nk:test" + } + }, + "mining_speed": 1, // default value + "should_despawn": 1, // default value true + "stacked_by_data": 0, // default value false + "use_animation": 0, // default value + "use_duration": 0 // default value + }, + "item_tags": [] + } +} \ No newline at end of file From 1d139bb3e1c07283b278221881125b860054971e Mon Sep 17 00:00:00 2001 From: JeanTKG <102908437+jeantkg@users.noreply.github.com> Date: Tue, 30 Sep 2025 20:47:56 +0300 Subject: [PATCH 02/51] blocks part 1 --- .../behavior/blocks/custom_slime_block.json | 32 ++ src/customiesdevs/customies/Customies.php | 2 + .../customies/block/BlockComponentsTrait.php | 3 +- .../customies/block/BlockManager.php | 153 +++++++ .../customies/block/CustomiesBlock.php | 21 + .../customies/block/CustomiesBlockFactory.php | 55 ++- .../customies/block/Material.php | 48 --- .../block/component/BlockComponent.php | 12 +- .../component/BreathabilityComponent.php | 30 -- .../block/component/CollisionBoxComponent.php | 30 +- .../component/CraftingTableComponent.php | 30 ++ .../DestructibleByExplosionComponent.php | 9 +- .../DestructibleByMiningComponent.php | 9 +- .../DestructionParticlesComponent.php | 36 ++ .../block/component/DisplayNameComponent.php | 9 +- .../block/component/FlammableComponent.php | 11 +- .../block/component/FrictionComponent.php | 9 +- .../block/component/GeometryComponent.php | 58 +-- .../block/component/ItemVisualComponent.php | 25 ++ .../component/LightDampeningComponent.php | 9 +- .../component/LightEmissionComponent.php | 9 +- .../component/LiquidDetectionComponent.php | 50 +++ .../block/component/LootComponent.php | 25 ++ .../block/component/MapColorComponent.php | 26 ++ .../component/MaterialInstancesComponent.php | 35 +- .../block/component/MovableComponent.php | 44 ++ .../component/PlacementFilterComponent.php | 32 ++ .../block/component/RandomOffsetComponent.php | 21 + .../RedstoneConductivityComponent.php | 30 ++ .../block/component/SelectionBoxComponent.php | 30 +- .../customies/block/properties/Material.php | 54 +++ .../block/properties/RenderMethod.php | 16 + .../customies/block/properties/TintMethod.php | 13 + .../item/component/RepairableComponent.php | 31 ++ .../customies/item/properties/RepairItems.php | 29 ++ test/blocks_data.json | 392 ++++++++++++++++++ 36 files changed, 1212 insertions(+), 216 deletions(-) create mode 100644 resources/behavior/blocks/custom_slime_block.json create mode 100644 src/customiesdevs/customies/block/BlockManager.php create mode 100644 src/customiesdevs/customies/block/CustomiesBlock.php delete mode 100644 src/customiesdevs/customies/block/Material.php delete mode 100644 src/customiesdevs/customies/block/component/BreathabilityComponent.php create mode 100644 src/customiesdevs/customies/block/component/CraftingTableComponent.php create mode 100644 src/customiesdevs/customies/block/component/DestructionParticlesComponent.php create mode 100644 src/customiesdevs/customies/block/component/ItemVisualComponent.php create mode 100644 src/customiesdevs/customies/block/component/LiquidDetectionComponent.php create mode 100644 src/customiesdevs/customies/block/component/LootComponent.php create mode 100644 src/customiesdevs/customies/block/component/MapColorComponent.php create mode 100644 src/customiesdevs/customies/block/component/MovableComponent.php create mode 100644 src/customiesdevs/customies/block/component/PlacementFilterComponent.php create mode 100644 src/customiesdevs/customies/block/component/RandomOffsetComponent.php create mode 100644 src/customiesdevs/customies/block/component/RedstoneConductivityComponent.php create mode 100644 src/customiesdevs/customies/block/properties/Material.php create mode 100644 src/customiesdevs/customies/block/properties/RenderMethod.php create mode 100644 src/customiesdevs/customies/block/properties/TintMethod.php create mode 100644 src/customiesdevs/customies/item/component/RepairableComponent.php create mode 100644 src/customiesdevs/customies/item/properties/RepairItems.php create mode 100644 test/blocks_data.json diff --git a/resources/behavior/blocks/custom_slime_block.json b/resources/behavior/blocks/custom_slime_block.json new file mode 100644 index 00000000..f2715b6a --- /dev/null +++ b/resources/behavior/blocks/custom_slime_block.json @@ -0,0 +1,32 @@ +{ + "format_version": "1.21.100", + "minecraft:block": { + "description": { + "identifier": "nk:custom_slime_block", + "menu_category": { + "category": "items" + } + }, + "components": { + "minecraft:geometry": "geometry.custom_slime_block", + "minecraft:material_instances": { + "*": { + "texture": "custom_slime_block", + "render_method": "blend" + } + }, + "minecraft:destructible_by_mining": { + "seconds_to_destroy": 0.033 + }, + "minecraft:destructible_by_explosion": { + "explosion_resistance": 0 + }, + "minecraft:light_dampening": 0, + "minecraft:movable": { + "movement_type": "push_pull", + "sticky": "same" + }, + "minecraft:map_color": "#7fb238" + } + } +} \ No newline at end of file diff --git a/src/customiesdevs/customies/Customies.php b/src/customiesdevs/customies/Customies.php index 6ce82728..6720824c 100644 --- a/src/customiesdevs/customies/Customies.php +++ b/src/customiesdevs/customies/Customies.php @@ -3,6 +3,7 @@ namespace customiesdevs\customies; +use customiesdevs\customies\block\BlockManager; use customiesdevs\customies\block\CustomiesBlockFactory; use customiesdevs\customies\item\ItemManager; use pocketmine\plugin\PluginBase; @@ -20,6 +21,7 @@ protected function onEnable(): void { $this->getServer()->getPluginManager()->registerEvents(new CustomiesListener(), $this); ItemManager::getInstance()->registerItems(); + BlockManager::getInstance()->registerBlocks(); $cachePath = $this->getDataFolder() . "idcache"; $this->getScheduler()->scheduleDelayedTask(new ClosureTask(static function () use ($cachePath): void { diff --git a/src/customiesdevs/customies/block/BlockComponentsTrait.php b/src/customiesdevs/customies/block/BlockComponentsTrait.php index fef4fa63..90c0b43f 100644 --- a/src/customiesdevs/customies/block/BlockComponentsTrait.php +++ b/src/customiesdevs/customies/block/BlockComponentsTrait.php @@ -3,7 +3,6 @@ namespace customiesdevs\customies\block; use customiesdevs\customies\block\component\BlockComponent; -use customiesdevs\customies\block\component\BreathabilityComponent; use customiesdevs\customies\block\component\CollisionBoxComponent; use customiesdevs\customies\block\component\DestructibleByExplosionComponent; use customiesdevs\customies\block\component\DestructibleByMiningComponent; @@ -15,6 +14,7 @@ use customiesdevs\customies\block\component\LightEmissionComponent; use customiesdevs\customies\block\component\MaterialInstancesComponent; use customiesdevs\customies\block\component\SelectionBoxComponent; +use customiesdevs\customies\block\properties\Material; trait BlockComponentsTrait { @@ -43,7 +43,6 @@ public function getComponents(): array { * @param bool $useGeometry Check if geometry component should be used, default is set to `true` */ protected function initComponent(string $texture, bool $useGeometry = true): void { - $this->addComponent(new BreathabilityComponent()); $this->addComponent(new DestructibleByExplosionComponent()); $this->addComponent(new DestructibleByMiningComponent($this->getBreakInfo()->getHardness())); $this->addComponent(new LightEmissionComponent($this->getLightLevel())); diff --git a/src/customiesdevs/customies/block/BlockManager.php b/src/customiesdevs/customies/block/BlockManager.php new file mode 100644 index 00000000..b97845bb --- /dev/null +++ b/src/customiesdevs/customies/block/BlockManager.php @@ -0,0 +1,153 @@ +blocksDirectory = Customies::getInstance()->getDataFolder() . "behavior/blocks/"; + $this->ensureDirectoryExists(); + } + + /** + * Ensures the blocks directory exists + */ + private function ensureDirectoryExists(): void { + if(!is_dir($this->blocksDirectory)) { + mkdir($this->blocksDirectory, 0777, true); + } + } + + /** + * Gets all block file names in the blocks directory + */ + public function getBlockFiles(): array { + $blocksFolder = scandir($this->blocksDirectory); + if($blocksFolder === false) { + return []; + } + return array_filter($blocksFolder, function($block) { + return $block !== '.' && + $block !== '..' && + is_file($this->blocksDirectory . $block) && + $this->isValidBlockFile($block); + }); + } + + /** + * Validates if a file is a valid block configuration file + * @param string $filename + * @return bool True if the file is a valid block config (e.g., ends with .json) + */ + private function isValidBlockFile(string $filename): bool { + return str_ends_with($filename, '.json'); + } + + /** + * Gets the configuration for a specific block + * @param string $blockName The name of the block configuration file + * @return Config The configuration object for the block + */ + public function getBlockConfig(string $blockName): Config { + $configPath = $this->blocksDirectory . $blockName; + return new Config($configPath, Config::JSON); + } + + /** + * Gets all block configurations + * @return Config[] An associative array of block file names to their Config objects + */ + public function getBlockConfigs(): array { + $configs = []; + foreach($this->getBlockFiles() as $blockFile) { + $configs[$blockFile] = $this->getBlockConfig($blockFile); + } + return $configs; + } + + /** + * Registers all blocks from configuration files + */ + public function registerBlocks(): void { + $registeredCount = 0; + $errorCount = 0; + foreach($this->getBlockFiles() as $blockFile) { + try { + $this->registerBlock($blockFile); + $registeredCount++; + } catch(\Exception $e) { + $errorCount++; + Customies::getInstance()->getLogger()->error( + "Failed to register block from '$blockFile': " . $e->getMessage() + ); + } + } + Customies::getInstance()->getLogger()->info( + "Registered $registeredCount custom blocks" . + ($errorCount > 0 ? " ($errorCount failed)" : "") + ); + } + + /** + * Registers a single block from its configuration file + * @param string $blockFile The name of the block configuration file + * @throws \InvalidArgumentException If the block configuration is invalid + */ + public function registerBlock(string $blockFile): void { + $blockConfig = $this->getBlockConfig($blockFile)->getAll(); + + if(!isset($blockConfig["minecraft:block"])) { + throw new \InvalidArgumentException("Invalid block config: missing minecraft:block"); + } + + $minecraftBlock = $blockConfig["minecraft:block"]; + + if(!isset($minecraftBlock["components"], $minecraftBlock["description"]["identifier"])) { + throw new \InvalidArgumentException("Invalid block config: missing required fields"); + } + + $customBlock = new CustomiesBlock($minecraftBlock["components"]); + $identifier = $minecraftBlock["description"]["identifier"]; + + $creativeInfo = $this->getCreativeInventoryInfo($minecraftBlock); + + CustomiesBlockFactory::getInstance()->registerBlock( + static fn() => $customBlock, + $identifier, + $creativeInfo + ); + } + + /** + * Gets creative inventory information from config + * @param array $blockConfig The block configuration array + * @return CreativeInventoryInfo The creative inventory info object + */ + private function getCreativeInventoryInfo(array $blockConfig): CreativeInventoryInfo { + $category = CreativeInventoryInfo::CATEGORY_ITEMS; + $group = CreativeInventoryInfo::NONE; + + if(isset($blockConfig["description"]["menu_category"])) { + $creativeConfig = $blockConfig["description"]["menu_category"]; + $category = $creativeConfig["category"] ?? $category; + $group = $creativeConfig["group"] ?? $group; + } + + return new CreativeInventoryInfo($category, $group); + } +} \ No newline at end of file diff --git a/src/customiesdevs/customies/block/CustomiesBlock.php b/src/customiesdevs/customies/block/CustomiesBlock.php new file mode 100644 index 00000000..ecc7718d --- /dev/null +++ b/src/customiesdevs/customies/block/CustomiesBlock.php @@ -0,0 +1,21 @@ + $componentData) { + switch ($componentName) { + + } + } + } +} \ No newline at end of file diff --git a/src/customiesdevs/customies/block/CustomiesBlockFactory.php b/src/customiesdevs/customies/block/CustomiesBlockFactory.php index ff4aa006..4324aa93 100644 --- a/src/customiesdevs/customies/block/CustomiesBlockFactory.php +++ b/src/customiesdevs/customies/block/CustomiesBlockFactory.php @@ -29,6 +29,8 @@ use pocketmine\utils\AssumptionFailedError; use pocketmine\utils\SingletonTrait; use pocketmine\world\format\io\GlobalBlockStateHandlers; +use RuntimeException; + use function array_map; use function array_reverse; use function hash; @@ -53,6 +55,7 @@ final class CustomiesBlockFactory { * Adds a worker initialize hook to the async pool to sync the BlockFactory for every thread worker that is created. * It is especially important for the workers that deal with chunk encoding, as using the wrong runtime ID mappings * can result in massive issues with almost every block showing as the wrong thing and causing lag to clients. + * @param string $cachePath The path where the block state cache file is located (usually server's root folder). */ public function addWorkerInitHook(string $cachePath): void { $server = Server::getInstance(); @@ -64,6 +67,9 @@ public function addWorkerInitHook(string $cachePath): void { /** * Get a custom block from its identifier. An exception will be thrown if the block is not registered. + * @param string $identifier + * @throws InvalidArgumentException + * @return Block A clone of the registered block. */ public function get(string $identifier): Block { return clone ( @@ -111,12 +117,8 @@ public function registerBlock(Closure $blockFunc, string $identifier, ?CreativeI $propertiesTag = CompoundTag::create(); $components = CompoundTag::create(); - if($block instanceof BlockComponents) { - foreach ($block->getComponents() as $component) { - $components->setTag($component->getName(), $component->getValue()); - } - } + // TODO if($block instanceof Permutable) { $blockPropertyNames = $blockPropertyValues = $blockProperties = []; foreach($block->getBlockProperties() as $blockProperty){ @@ -166,19 +168,11 @@ public function registerBlock(Closure $blockFunc, string $identifier, ?CreativeI $serializer ??= static fn() => new BlockStateWriter($identifier); $deserializer ??= static fn(BlockStateReader $in) => $block; } + GlobalBlockStateHandlers::getSerializer()->map($block, $serializer); GlobalBlockStateHandlers::getDeserializer()->map($identifier, $deserializer); - $creativeInfo ??= CreativeInventoryInfo::DEFAULT(); - $propertiesTag - ->setTag("components", - $components->setTag("minecraft:creative_category", CompoundTag::create() - ->setString("category", $creativeInfo->getCategory()) - ->setString("group", $creativeInfo->getGroup()))) - ->setTag("menu_category", CompoundTag::create() - ->setString("category", $creativeInfo->getCategory() ?? "") - ->setString("group", $creativeInfo->getGroup() ?? "")) - ->setInt("molangVersion", 1); + $nbt = $this->createBlockNBT($block, $creativeInfo); if($creativeInfo !== null){ $this->loadGroups(); @@ -206,7 +200,7 @@ public function registerBlock(Closure $blockFunc, string $identifier, ?CreativeI CreativeInventory::getInstance()->add($block->asItem(), $category, $group); } - $this->blockPaletteEntries[] = new BlockPaletteEntry($identifier, new CacheableNbt($propertiesTag)); + $this->blockPaletteEntries[] = new BlockPaletteEntry($identifier, new CacheableNbt($nbt)); $this->blockFuncs[$identifier] = [$blockFunc, $serializer, $deserializer]; // 1.20.60 added a new "block_id" field which depends on the order of the block palette entries. Every time we @@ -215,10 +209,33 @@ public function registerBlock(Closure $blockFunc, string $identifier, ?CreativeI return strcmp(hash("fnv164", $a->getName()), hash("fnv164", $b->getName())); }); foreach($this->blockPaletteEntries as $i => $entry) { - $root = $entry->getStates()->getRoot() - ->setTag("vanilla_block_data", CompoundTag::create() - ->setInt("block_id", 10000 + $i)); + /** @var CompoundTag $root */ + $root = $entry->getStates()->getRoot(); + $root->setTag("vanilla_block_data", CompoundTag::create()->setInt("block_id", 10000 + $i)); $this->blockPaletteEntries[$i] = new BlockPaletteEntry($entry->getName(), new CacheableNbt($root)); } } + + private function createBlockNBT(Block $block, ?CreativeInventoryInfo $creativeInfo): CompoundTag { + $propertiesTag = CompoundTag::create(); + $components = CompoundTag::create(); + + if($block instanceof BlockComponents) { + foreach ($block->getComponents() as $component) { + $tag = NBT::getTagType($component->getValue()); + if($tag === null) { + throw new RuntimeException("Failed to get tag type for component " . $component->getName()); + } + $components->setTag($component->getName(), $tag); + } + $propertiesTag + ->setTag("components", $components) + ->setTag("menu_category", CompoundTag::create() + ->setString("category", $creativeInfo->getCategory() ?? "") + ->setString("group", $creativeInfo->getGroup() ?? "")) + ->setInt("molangVersion", 12); + return $propertiesTag; + } + return CompoundTag::create(); + } } diff --git a/src/customiesdevs/customies/block/Material.php b/src/customiesdevs/customies/block/Material.php deleted file mode 100644 index 59db55d2..00000000 --- a/src/customiesdevs/customies/block/Material.php +++ /dev/null @@ -1,48 +0,0 @@ -target; - } - - /** - * Returns the material in the correct NBT format supported by the client. - */ - public function toNBT(): CompoundTag { - return CompoundTag::create() - ->setString("texture", $this->texture) - ->setString("render_method", $this->renderMethod) - ->setByte("face_dimming", $this->faceDimming ? 1 : 0) - ->setByte("ambient_occlusion", $this->ambientOcclusion ? 1 : 0); - } -} diff --git a/src/customiesdevs/customies/block/component/BlockComponent.php b/src/customiesdevs/customies/block/component/BlockComponent.php index 76fb4e1d..517f1f46 100644 --- a/src/customiesdevs/customies/block/component/BlockComponent.php +++ b/src/customiesdevs/customies/block/component/BlockComponent.php @@ -3,19 +3,9 @@ namespace customiesdevs\customies\block\component; -use pocketmine\nbt\tag\CompoundTag; - interface BlockComponent { - /** - * Returns the name of the component - * @return string - */ public function getName(): string; - /** - * Returns the value of the component - * @return CompoundTag - */ - public function getValue(): CompoundTag; + public function getValue(): mixed; } \ No newline at end of file diff --git a/src/customiesdevs/customies/block/component/BreathabilityComponent.php b/src/customiesdevs/customies/block/component/BreathabilityComponent.php deleted file mode 100644 index d8b8eb19..00000000 --- a/src/customiesdevs/customies/block/component/BreathabilityComponent.php +++ /dev/null @@ -1,30 +0,0 @@ -breathability = $breathability; - } - - public function getName(): string { - return "minecraft:breathability"; - } - - public function getValue(): CompoundTag { - return CompoundTag::create() - ->setString("value", $this->breathability); - } -} \ No newline at end of file diff --git a/src/customiesdevs/customies/block/component/CollisionBoxComponent.php b/src/customiesdevs/customies/block/component/CollisionBoxComponent.php index e0e641af..245ec377 100644 --- a/src/customiesdevs/customies/block/component/CollisionBoxComponent.php +++ b/src/customiesdevs/customies/block/component/CollisionBoxComponent.php @@ -3,9 +3,6 @@ namespace customiesdevs\customies\block\component; use pocketmine\math\Vector3; -use pocketmine\nbt\tag\CompoundTag; -use pocketmine\nbt\tag\FloatTag; -use pocketmine\nbt\tag\ListTag; class CollisionBoxComponent implements BlockComponent { @@ -29,18 +26,19 @@ public function getName(): string { return "minecraft:collision_box"; } - public function getValue(): CompoundTag { - return CompoundTag::create() - ->setByte("enabled", $this->useCollisionBox ? 1 : 0) - ->setTag("origin", new ListTag([ - new FloatTag($this->origin->getX()), - new FloatTag($this->origin->getY()), - new FloatTag($this->origin->getZ()) - ])) - ->setTag("size", new ListTag([ - new FloatTag($this->size->getX()), - new FloatTag($this->size->getY()), - new FloatTag($this->size->getZ()) - ])); + public function getValue(): array { + return [ + "enabled" => $this->useCollisionBox, + "origin" => [ + $this->origin->getX(), + $this->origin->getY(), + $this->origin->getZ() + ], + "size" => [ + $this->size->getX(), + $this->size->getY(), + $this->size->getZ() + ] + ]; } } \ No newline at end of file diff --git a/src/customiesdevs/customies/block/component/CraftingTableComponent.php b/src/customiesdevs/customies/block/component/CraftingTableComponent.php new file mode 100644 index 00000000..281a6e9d --- /dev/null +++ b/src/customiesdevs/customies/block/component/CraftingTableComponent.php @@ -0,0 +1,30 @@ +craftingTags = $craftingTags; + $this->tableName = $tableName; + } + + public function getName(): string { + return "minecraft:crafting_table"; + } + + public function getValue(): array { + return [ + "crafting_tags" => $this->craftingTags, + "table_name" => $this->tableName + ]; + } +} \ No newline at end of file diff --git a/src/customiesdevs/customies/block/component/DestructibleByExplosionComponent.php b/src/customiesdevs/customies/block/component/DestructibleByExplosionComponent.php index dd55d972..1066ce36 100644 --- a/src/customiesdevs/customies/block/component/DestructibleByExplosionComponent.php +++ b/src/customiesdevs/customies/block/component/DestructibleByExplosionComponent.php @@ -2,8 +2,6 @@ namespace customiesdevs\customies\block\component; -use pocketmine\nbt\tag\CompoundTag; - class DestructibleByExplosionComponent implements BlockComponent { private float $explosionResistance; @@ -20,8 +18,9 @@ public function getName(): string { return "minecraft:destructible_by_explosion"; } - public function getValue(): CompoundTag { - return CompoundTag::create() - ->setFloat("value", $this->explosionResistance); + public function getValue(): array { + return [ + "value" => $this->explosionResistance + ]; } } \ No newline at end of file diff --git a/src/customiesdevs/customies/block/component/DestructibleByMiningComponent.php b/src/customiesdevs/customies/block/component/DestructibleByMiningComponent.php index 458a44cb..3f80251f 100644 --- a/src/customiesdevs/customies/block/component/DestructibleByMiningComponent.php +++ b/src/customiesdevs/customies/block/component/DestructibleByMiningComponent.php @@ -2,8 +2,6 @@ namespace customiesdevs\customies\block\component; -use pocketmine\nbt\tag\CompoundTag; - class DestructibleByMiningComponent implements BlockComponent { private float $secondsToDestroy; @@ -20,8 +18,9 @@ public function getName(): string { return "minecraft:destructible_by_mining"; } - public function getValue(): CompoundTag { - return CompoundTag::create() - ->setFloat("value", $this->secondsToDestroy); + public function getValue(): array { + return [ + "value" => $this->secondsToDestroy + ]; } } \ No newline at end of file diff --git a/src/customiesdevs/customies/block/component/DestructionParticlesComponent.php b/src/customiesdevs/customies/block/component/DestructionParticlesComponent.php new file mode 100644 index 00000000..e30934dd --- /dev/null +++ b/src/customiesdevs/customies/block/component/DestructionParticlesComponent.php @@ -0,0 +1,36 @@ +particleCount = $particleCount; + $this->texture = $texture; + $this->tintMethod = $tintMethod; + } + + public function getName(): string { + return "minecraft:destruction_particles"; + } + + public function getValue(): array { + return [ + "particle_count" => $this->particleCount, + "texture" => $this->texture, + "tint_method" => $this->tintMethod + ]; + } +} \ No newline at end of file diff --git a/src/customiesdevs/customies/block/component/DisplayNameComponent.php b/src/customiesdevs/customies/block/component/DisplayNameComponent.php index b145b0a2..fcb6b6a2 100644 --- a/src/customiesdevs/customies/block/component/DisplayNameComponent.php +++ b/src/customiesdevs/customies/block/component/DisplayNameComponent.php @@ -2,8 +2,6 @@ namespace customiesdevs\customies\block\component; -use pocketmine\nbt\tag\CompoundTag; - class DisplayNameComponent implements BlockComponent { private string $displayName; @@ -23,8 +21,9 @@ public function getName(): string { return "minecraft:display_name"; } - public function getValue(): CompoundTag { - return CompoundTag::create() - ->setString("value", $this->displayName); + public function getValue(): array { + return [ + "value" => $this->displayName + ]; } } \ No newline at end of file diff --git a/src/customiesdevs/customies/block/component/FlammableComponent.php b/src/customiesdevs/customies/block/component/FlammableComponent.php index 537cb48a..1aa11529 100644 --- a/src/customiesdevs/customies/block/component/FlammableComponent.php +++ b/src/customiesdevs/customies/block/component/FlammableComponent.php @@ -2,8 +2,6 @@ namespace customiesdevs\customies\block\component; -use pocketmine\nbt\tag\CompoundTag; - class FlammableComponent implements BlockComponent { private int $catchChanceModifier; @@ -23,9 +21,10 @@ public function getName(): string { return "minecraft:flammable"; } - public function getValue(): CompoundTag { - return CompoundTag::create() - ->setInt("catch_chance_modifier", $this->catchChanceModifier) - ->setInt("destroy_chance_modifier", $this->destroyChanceModifier); + public function getValue(): array { + return [ + "catch_chance_modifier" => $this->catchChanceModifier, + "destroy_chance_modifier" => $this->destroyChanceModifier + ]; } } \ No newline at end of file diff --git a/src/customiesdevs/customies/block/component/FrictionComponent.php b/src/customiesdevs/customies/block/component/FrictionComponent.php index 4e9aa7bc..5ec200c6 100644 --- a/src/customiesdevs/customies/block/component/FrictionComponent.php +++ b/src/customiesdevs/customies/block/component/FrictionComponent.php @@ -2,8 +2,6 @@ namespace customiesdevs\customies\block\component; -use pocketmine\nbt\tag\CompoundTag; - class FrictionComponent implements BlockComponent { private float $friction; @@ -21,8 +19,9 @@ public function getName(): string { return "minecraft:friction"; } - public function getValue(): CompoundTag { - return CompoundTag::create() - ->setFloat("value", $this->friction); + public function getValue(): array { + return [ + "value" => $this->friction + ]; } } \ No newline at end of file diff --git a/src/customiesdevs/customies/block/component/GeometryComponent.php b/src/customiesdevs/customies/block/component/GeometryComponent.php index aee3999e..aad457d3 100644 --- a/src/customiesdevs/customies/block/component/GeometryComponent.php +++ b/src/customiesdevs/customies/block/component/GeometryComponent.php @@ -2,41 +2,51 @@ namespace customiesdevs\customies\block\component; -use pocketmine\nbt\tag\CompoundTag; - class GeometryComponent implements BlockComponent { - private string $geometry; - private CompoundTag $boneVisibility; + private string $identifier; + private array $boneVisibility; + private string $culling; + private string $cullingLayer; + private array|bool $uvLock; /** * The description identifier of the geometry to use to render this block. This identifier must either match an existing geometry identifier in any of the loaded resource packs or be one of the currently supported Vanilla identifiers: "minecraft:geometry.full_block" or "minecraft:geometry.cross". - * @param string $geometry + * @param string $identifier Specifies the geometry description identifier to use to render this block. This identifier must match an existing geometry identifier in any of the currently loaded resource packs. + * @param array $boneVisibility An optional list of true/false values that define the visibility of individual bones in the geometry file. In order to set up 'bone_visibility', the geometry file name must be entered as an identifier. After the identifier has been specified, bone_visibility can be defined based on the names of the bones in the specified geometry file on a true/false basis. Note that all bones default to 'true,' so bones should only be defined if they are being set to 'false.' Including bones set to 'true' will work the same as the default. + * @param string $culling An optional identifer of a culling definition. The culling definition is used to determine which faces of the block should be culled when rendering. The culling definition can be used to optimize rendering performance by reducing the number of faces that need to be rendered. This identifier must match an existing culling definition in any of the currently loaded resource packs. + * @param string $cullingLayer [Experimental] - A string that allows culling rule to group multiple blocks together when comparing them. When using the minecraft namespace, the only allowed culling layer identifiers are : "minecraft:culling_layer.undefined" or "minecraft:culling_layer.leaves". Additionally, the feature is currently only usable behind the "upcoming creator features" toggle. When using no namespaces or a custom one, the names must start and end with an alpha-numeric character. + * @param array|bool $uvLock A Boolean locking UV orientation of all bones in the geometry, or an array of strings locking UV orientation of specific bones in the geometry. For performance reasons it is recommended to use the Boolean. Note that for cubes using Box UVs, rather than Per-face UVs, 'uv_lock' is only supported if the cube faces are square. */ - public function __construct(string $geometry = "minecraft:geometry.full_block") { - $this->geometry = $geometry; - $this->boneVisibility = CompoundTag::create(); + public function __construct( + string $identifier = "minecraft:geometry.full_block", + array $boneVisibility = [], + string $culling = "", + string $cullingLayer = "minecraft:culling_layer.undefined", + array|bool $uvLock = false + ) { + $this->identifier = $identifier; + $this->culling = $culling; + $this->cullingLayer = $cullingLayer; + $this->uvLock = $uvLock; + $this->boneVisibility = $boneVisibility; } public function getName(): string { return "minecraft:geometry"; } - public function getValue(): CompoundTag { - return CompoundTag::create() - ->setTag("bone_visibility", $this->boneVisibility) - ->setString("culling", "") - ->setString("identifier", $this->geometry); - } - - public function addBoneVisibility(string $boneName, bool|string $visibility): self { - if(is_string($visibility) && !is_bool($visibility)){ - $this->boneVisibility->setTag($boneName, CompoundTag::create() - ->setString("expression", $visibility) - ->setShort("version", 12)); - } elseif(is_bool($visibility)){ - $this->boneVisibility->setFloat($boneName, $visibility ? 1 : 0); - } - return $this; + public function getValue(): array { + return [ + "bone_visibility" => $this->boneVisibility, + "culling" => $this->culling, + "culling_layer" => $this->cullingLayer, + "identifier" => $this->identifier, + "uv_lock" => $this->uvLock, + // no reason as to why these 3 exist, but its what minecraft is outputting + "ignoreGeometryForIsSolid" => false, + "needsLegacyTopRotation" => false, + "useBlockTypeLightAbsorption" => false + ]; } } \ No newline at end of file diff --git a/src/customiesdevs/customies/block/component/ItemVisualComponent.php b/src/customiesdevs/customies/block/component/ItemVisualComponent.php new file mode 100644 index 00000000..3de8b103 --- /dev/null +++ b/src/customiesdevs/customies/block/component/ItemVisualComponent.php @@ -0,0 +1,25 @@ +geometry = $geometry; + $this->materialInstances = $materialInstances; + } + + public function getName(): string { + return "minecraft:item_visual"; + } + + public function getValue(): array { + return [ + "geometry" => $this->geometry->getValue(), + "material_instances" => $this->materialInstances->getValue() + ]; + } +} \ No newline at end of file diff --git a/src/customiesdevs/customies/block/component/LightDampeningComponent.php b/src/customiesdevs/customies/block/component/LightDampeningComponent.php index 4cfc704d..8a96a6d1 100644 --- a/src/customiesdevs/customies/block/component/LightDampeningComponent.php +++ b/src/customiesdevs/customies/block/component/LightDampeningComponent.php @@ -2,8 +2,6 @@ namespace customiesdevs\customies\block\component; -use pocketmine\nbt\tag\CompoundTag; - class LightDampeningComponent implements BlockComponent { private int $dampening; @@ -20,8 +18,9 @@ public function getName(): string { return "minecraft:light_dampening"; } - public function getValue(): CompoundTag { - return CompoundTag::create() - ->setByte("lightLevel", $this->dampening); + public function getValue(): array { + return [ + "lightLevel" => $this->dampening + ]; } } \ No newline at end of file diff --git a/src/customiesdevs/customies/block/component/LightEmissionComponent.php b/src/customiesdevs/customies/block/component/LightEmissionComponent.php index 2ad043b7..f5e0d370 100644 --- a/src/customiesdevs/customies/block/component/LightEmissionComponent.php +++ b/src/customiesdevs/customies/block/component/LightEmissionComponent.php @@ -2,8 +2,6 @@ namespace customiesdevs\customies\block\component; -use pocketmine\nbt\tag\CompoundTag; - class LightEmissionComponent implements BlockComponent { private int $emission; @@ -20,8 +18,9 @@ public function getName(): string { return "minecraft:light_emission"; } - public function getValue(): CompoundTag { - return CompoundTag::create() - ->setByte("emission", $this->emission); + public function getValue(): array { + return [ + "lightLevel" => $this->emission + ]; } } \ No newline at end of file diff --git a/src/customiesdevs/customies/block/component/LiquidDetectionComponent.php b/src/customiesdevs/customies/block/component/LiquidDetectionComponent.php new file mode 100644 index 00000000..bc6df070 --- /dev/null +++ b/src/customiesdevs/customies/block/component/LiquidDetectionComponent.php @@ -0,0 +1,50 @@ +liquidType = $liquidType; + $this->canContainLiquid = $canContainLiquid; + $this->onLiquidTouches = $onLiquidTouches; + $this->stopsLiquidFlowingFromDirection = $stopsLiquidFlowingFromDirection; + } + + public function getName(): string { + return "minecraft:liquid_detection"; + } + + public function getValue(): array { + return [ + "detectionRules" => [ + [ + "liquidType" => $this->liquidType, + "canContainLiquid" => $this->canContainLiquid, + "onLiquidTouches" => $this->onLiquidTouches, + "stopsLiquidFromDirection" => $this->stopsLiquidFlowingFromDirection, + ] + ] + ]; + } +} \ No newline at end of file diff --git a/src/customiesdevs/customies/block/component/LootComponent.php b/src/customiesdevs/customies/block/component/LootComponent.php new file mode 100644 index 00000000..20a7e121 --- /dev/null +++ b/src/customiesdevs/customies/block/component/LootComponent.php @@ -0,0 +1,25 @@ +loot = $loot; + } + + public function getName(): string { + return "minecraft:loot"; + } + + public function getValue(): array { + return [ + "value" => $this->loot + ]; + } +} \ No newline at end of file diff --git a/src/customiesdevs/customies/block/component/MapColorComponent.php b/src/customiesdevs/customies/block/component/MapColorComponent.php new file mode 100644 index 00000000..1a63a806 --- /dev/null +++ b/src/customiesdevs/customies/block/component/MapColorComponent.php @@ -0,0 +1,26 @@ +color = $color; + } + + public function getName(): string { + return "minecraft:map_color"; + } + + public function getValue(): array { + return [ + "color" => $this->color + ]; + } +} \ No newline at end of file diff --git a/src/customiesdevs/customies/block/component/MaterialInstancesComponent.php b/src/customiesdevs/customies/block/component/MaterialInstancesComponent.php index 72dd6383..30581347 100644 --- a/src/customiesdevs/customies/block/component/MaterialInstancesComponent.php +++ b/src/customiesdevs/customies/block/component/MaterialInstancesComponent.php @@ -2,30 +2,37 @@ namespace customiesdevs\customies\block\component; -use customiesdevs\customies\block\Material; -use pocketmine\nbt\tag\CompoundTag; +use customiesdevs\customies\block\properties\Material; class MaterialInstancesComponent implements BlockComponent { - /** @var Material[] */ - private array $materials; - - public function __construct(array $materials) { - $this->materials = $materials; + /** + * The material instances for a block. Maps face or material_instance names in a geometry file to an actual material instance. You can assign a material instance object to any of these faces: "up", "down", "north", "south", "east", "west", or "*". You can also give an instance the name of your choosing such as "my_instance", and then assign it to a face by doing "north":"my_instance". + * @param Material[] $materials + */ + public function __construct(private readonly array $materials) { + if(count($materials) === 0){ + throw new \InvalidArgumentException("At least one material must be defined"); + } + foreach($materials as $material){ + if(!$material instanceof Material){ + throw new \InvalidArgumentException("All materials must be instances of ".Material::class); + } + } } public function getName(): string { return "minecraft:material_instances"; } - public function getValue(): CompoundTag { - $materials = CompoundTag::create(); + public function getValue(): array { + $materials = []; foreach($this->materials as $material){ - $materials->setTag($material->getTarget(), $material->toNBT()); + $materials[$material->getTarget()] = $material->toArray(); } - - return CompoundTag::create() - ->setTag("mappings", CompoundTag::create()) - ->setTag("materials", $materials); + return [ + "mappings" => [], + "materials" => $materials + ]; } } \ No newline at end of file diff --git a/src/customiesdevs/customies/block/component/MovableComponent.php b/src/customiesdevs/customies/block/component/MovableComponent.php new file mode 100644 index 00000000..70565f8b --- /dev/null +++ b/src/customiesdevs/customies/block/component/MovableComponent.php @@ -0,0 +1,44 @@ +movementType = $movementType; + $this->sticky = $sticky; + } + + public function getName(): string { + return "minecraft:movable"; + } + + public function getValue(): array { + return [ + "movement_type" => $this->movementType, + "sticky" => $this->sticky + ]; + } +} \ No newline at end of file diff --git a/src/customiesdevs/customies/block/component/PlacementFilterComponent.php b/src/customiesdevs/customies/block/component/PlacementFilterComponent.php new file mode 100644 index 00000000..527ae0f2 --- /dev/null +++ b/src/customiesdevs/customies/block/component/PlacementFilterComponent.php @@ -0,0 +1,32 @@ +allowedFaces = $allowedFaces; + $this->blockFilter = $blockFilter; + } + + public function getName(): string { + return "minecraft:placement_filter"; + } + + public function getValue(): array { + return [ + "conditions" => [ + [ + "allowed_faces" => $this->allowedFaces, + "block_filter" => $this->blockFilter + ] + ] + ]; + } +} \ No newline at end of file diff --git a/src/customiesdevs/customies/block/component/RandomOffsetComponent.php b/src/customiesdevs/customies/block/component/RandomOffsetComponent.php new file mode 100644 index 00000000..3981a8f8 --- /dev/null +++ b/src/customiesdevs/customies/block/component/RandomOffsetComponent.php @@ -0,0 +1,21 @@ +allowsWireToStepDown = $allowsWireToStepDown; + $this->redstoneConductor = $redstoneConductor; + } + + public function getName(): string { + return "minecraft:redstone_conductivity"; + } + + public function getValue(): array { + return [ + "allows_wire_to_step_down" => $this->allowsWireToStepDown, + "redstone_conductor" => $this->redstoneConductor + ]; + } +} \ No newline at end of file diff --git a/src/customiesdevs/customies/block/component/SelectionBoxComponent.php b/src/customiesdevs/customies/block/component/SelectionBoxComponent.php index 2f0ebce6..60432ef5 100644 --- a/src/customiesdevs/customies/block/component/SelectionBoxComponent.php +++ b/src/customiesdevs/customies/block/component/SelectionBoxComponent.php @@ -3,9 +3,6 @@ namespace customiesdevs\customies\block\component; use pocketmine\math\Vector3; -use pocketmine\nbt\tag\CompoundTag; -use pocketmine\nbt\tag\FloatTag; -use pocketmine\nbt\tag\ListTag; class SelectionBoxComponent implements BlockComponent { @@ -29,18 +26,19 @@ public function getName(): string { return "minecraft:selection_box"; } - public function getValue(): CompoundTag { - return CompoundTag::create() - ->setByte("enabled", $this->useSelectionBox ? 1 : 0) - ->setTag("origin", new ListTag([ - new FloatTag($this->origin->getX()), - new FloatTag($this->origin->getY()), - new FloatTag($this->origin->getZ()) - ])) - ->setTag("size", new ListTag([ - new FloatTag($this->size->getX()), - new FloatTag($this->size->getY()), - new FloatTag($this->size->getZ()) - ])); + public function getValue(): array { + return [ + "enabled" => $this->useSelectionBox, + "origin" => [ + $this->origin->getX(), + $this->origin->getY(), + $this->origin->getZ() + ], + "size" => [ + $this->size->getX(), + $this->size->getY(), + $this->size->getZ() + ] + ]; } } \ No newline at end of file diff --git a/src/customiesdevs/customies/block/properties/Material.php b/src/customiesdevs/customies/block/properties/Material.php new file mode 100644 index 00000000..4c956c93 --- /dev/null +++ b/src/customiesdevs/customies/block/properties/Material.php @@ -0,0 +1,54 @@ +target; + } + + public function toArray(): array { + return [ + "texture" => $this->texture, + "render_method" => $this->renderMethod, + "tint_method" => $this->tintMethod, + "ambient_occlusion" => $this->ambientOcclusion, + "face_dimming" => $this->faceDimming, + "isotropic" => $this->isotropic + ]; + } +} diff --git a/src/customiesdevs/customies/block/properties/RenderMethod.php b/src/customiesdevs/customies/block/properties/RenderMethod.php new file mode 100644 index 00000000..5efe4989 --- /dev/null +++ b/src/customiesdevs/customies/block/properties/RenderMethod.php @@ -0,0 +1,16 @@ +repairItems as $repairItem) { + $repairItems[] = $repairItem->toArray(); + } + return [ + "repair_items" => $repairItems + ]; + } +} \ No newline at end of file diff --git a/src/customiesdevs/customies/item/properties/RepairItems.php b/src/customiesdevs/customies/item/properties/RepairItems.php new file mode 100644 index 00000000..233ee494 --- /dev/null +++ b/src/customiesdevs/customies/item/properties/RepairItems.php @@ -0,0 +1,29 @@ +items as $item) { + $items[] = [ + "name" => $item + ]; + } + return [ + "items" => $items, + "repair_amount" => $this->repairAmount + ]; + } + +} \ No newline at end of file diff --git a/test/blocks_data.json b/test/blocks_data.json new file mode 100644 index 00000000..9b877547 --- /dev/null +++ b/test/blocks_data.json @@ -0,0 +1,392 @@ +[ + { + "Name": "nk:custom_slime_block", + "Properties": { + "components": { + "minecraft:destructible_by_mining": { + "value": 0.033 + }, + "minecraft:geometry": { + "bone_visibility": {}, + "culling": "", + "culling_layer": "minecraft:culling_layer.undefined", + "identifier": "geometry.custom_slime_block", + "ignoreGeometryForIsSolid": 0, + "needsLegacyTopRotation": 0, + "useBlockTypeLightAbsorption": 0, + "uv_lock": 0 + }, + "minecraft:light_dampening": { + "lightLevel": 0 + }, + "minecraft:material_instances": { + "mappings": {}, + "materials": { + "*": { + "ambient_occlusion": 1, + "face_dimming": 1, + "isotropic": 0, + "render_method": "blend", + "texture": "custom_slime_block", + "tint_method": "none" + } + } + } + }, + "menu_category": { + "category": "items", + "group": "", + "is_hidden_in_commands": 0 + }, + "molangVersion": 12, + "vanilla_block_data": { + "block_id": 10001, + "material": "dirt" + } + } + }, + { + "Name": "nk:custom_glass", + "Properties": { + "components": { + "minecraft:destructible_by_mining": { + "value": 0.3 + }, + "minecraft:geometry": { + "bone_visibility": {}, + "culling": "nk:culling.custom_glass", + "culling_layer": "minecraft:culling_layer.undefined", + "identifier": "geometry.custom_glass", + "ignoreGeometryForIsSolid": 0, + "needsLegacyTopRotation": 0, + "useBlockTypeLightAbsorption": 0, + "uv_lock": 0 + }, + "minecraft:light_dampening": { + "lightLevel": 0 + }, + "minecraft:material_instances": { + "mappings": {}, + "materials": { + "*": { + "ambient_occlusion": 1, + "face_dimming": 1, + "isotropic": 0, + "render_method": "blend", + "texture": "custom_glass", + "tint_method": "none" + } + } + } + }, + "menu_category": { + "category": "items", + "group": "", + "is_hidden_in_commands": 0 + }, + "molangVersion": 12, + "vanilla_block_data": { + "block_id": 10002, + "material": "dirt" + } + } + }, + { + "Name": "nk:custom_crop", + "Properties": { + "components": { + "minecraft:collision_box": { + "enabled": 0, + "origin": [ + -8, + 0, + -8 + ], + "size": [ + 16, + 16, + 16 + ] + }, + "minecraft:geometry": { + "bone_visibility": {}, + "culling": "", + "culling_layer": "minecraft:culling_layer.undefined", + "identifier": "geometry.crop", + "ignoreGeometryForIsSolid": 0, + "needsLegacyTopRotation": 0, + "useBlockTypeLightAbsorption": 0, + "uv_lock": 0 + }, + "minecraft:light_dampening": { + "lightLevel": 0 + }, + "minecraft:liquid_detection": { + "detectionRules": [ + { + "canContainLiquid": 0, + "liquidType": "water", + "onLiquidTouches": "broken", + "stopsLiquidFromDirection": 0 + } + ] + }, + "minecraft:material_instances": { + "mappings": {}, + "materials": { + "*": { + "ambient_occlusion": 0, + "face_dimming": 0, + "isotropic": 0, + "render_method": "alpha_test_single_sided", + "texture": "nk:custom_crop_0", + "tint_method": "none" + } + } + }, + "minecraft:placement_filter": { + "conditions": [ + { + "allowed_faces": 2, + "block_filter": [ + { + "name": "minecraft:farmland" + } + ] + } + ] + } + }, + "menu_category": { + "category": "nature", + "group": "", + "is_hidden_in_commands": 0 + }, + "molangVersion": 12, + "permutations": [ + { + "components": { + "minecraft:custom_components": { + "hasPlayerInteract": 1, + "hasPlayerPlacing": 0, + "isV1Component": 0 + } + }, + "condition": "q.block_state('nk:growth') \u003c 7" + }, + { + "components": { + "minecraft:selection_box": { + "enabled": 1, + "origin": [ + -8, + 0, + -8 + ], + "size": [ + 16, + 1.6, + 16 + ] + } + }, + "condition": "q.block_state('nk:growth') == 0" + }, + { + "components": { + "minecraft:selection_box": { + "enabled": 1, + "origin": [ + -8, + 0, + -8 + ], + "size": [ + 16, + 3.2, + 16 + ] + } + }, + "condition": "q.block_state('nk:growth') == 1" + }, + { + "components": { + "minecraft:material_instances": { + "mappings": {}, + "materials": { + "*": { + "ambient_occlusion": 0, + "face_dimming": 0, + "isotropic": 0, + "render_method": "alpha_test_single_sided", + "texture": "nk:custom_crop_1", + "tint_method": "none" + } + } + } + }, + "condition": "q.block_state('nk:growth') \u003e= 2" + }, + { + "components": { + "minecraft:selection_box": { + "enabled": 1, + "origin": [ + -8, + 0, + -8 + ], + "size": [ + 16, + 4.8, + 16 + ] + } + }, + "condition": "q.block_state('nk:growth') == 2" + }, + { + "components": { + "minecraft:selection_box": { + "enabled": 1, + "origin": [ + -8, + 0, + -8 + ], + "size": [ + 16, + 6.4, + 16 + ] + } + }, + "condition": "q.block_state('nk:growth') == 3" + }, + { + "components": { + "minecraft:material_instances": { + "mappings": {}, + "materials": { + "*": { + "ambient_occlusion": 0, + "face_dimming": 0, + "isotropic": 0, + "render_method": "alpha_test_single_sided", + "texture": "nk:custom_crop_2", + "tint_method": "none" + } + } + } + }, + "condition": "q.block_state('nk:growth') \u003e= 4" + }, + { + "components": { + "minecraft:selection_box": { + "enabled": 1, + "origin": [ + -8, + 0, + -8 + ], + "size": [ + 16, + 8, + 16 + ] + } + }, + "condition": "q.block_state('nk:growth') == 4" + }, + { + "components": { + "minecraft:selection_box": { + "enabled": 1, + "origin": [ + -8, + 0, + -8 + ], + "size": [ + 16, + 9.6, + 16 + ] + } + }, + "condition": "q.block_state('nk:growth') == 5" + }, + { + "components": { + "minecraft:selection_box": { + "enabled": 1, + "origin": [ + -8, + 0, + -8 + ], + "size": [ + 16, + 11.2, + 16 + ] + } + }, + "condition": "q.block_state('nk:growth') == 6" + }, + { + "components": { + "minecraft:material_instances": { + "mappings": {}, + "materials": { + "*": { + "ambient_occlusion": 0, + "face_dimming": 0, + "isotropic": 0, + "render_method": "alpha_test_single_sided", + "texture": "nk:custom_crop_3", + "tint_method": "none" + } + } + }, + "minecraft:selection_box": { + "enabled": 1, + "origin": [ + -8, + 0, + -8 + ], + "size": [ + 16, + 12.8, + 16 + ] + } + }, + "condition": "q.block_state('nk:growth') == 7" + } + ], + "properties": [ + { + "enum": [ + 0, + 1, + 2, + 3, + 4, + 5, + 6, + 7 + ], + "name": "nk:growth" + } + ], + "vanilla_block_data": { + "block_id": 10003, + "material": "dirt" + } + } + } +] \ No newline at end of file From 156ec2a8868fdbf7571dab3b8700dd9f3592aade Mon Sep 17 00:00:00 2001 From: JeanTKG <102908437+jeantkg@users.noreply.github.com> Date: Fri, 3 Oct 2025 19:47:13 +0300 Subject: [PATCH 03/51] Refactor block components to use VanillaBlockComponents registry - Updated custom slime block JSON to use full_block geometry and slime_block texture. - Refactored BlockComponentsTrait to utilize RenderMethod enum for material instances. - Modified BlockManager to register blocks using a closure that captures components. - Enhanced CustomiesBlock to support dynamic component registration from JSON. - Introduced VanillaBlockComponents class to centralize component identifiers and their mappings. - Updated individual block component classes to use the new VanillaBlockComponents constants. - Implemented fromJson methods for various components to facilitate JSON parsing. - Added support for new render methods and tint methods in Material class. - Improved documentation and type safety across block component implementations. --- .../behavior/blocks/custom_slime_block.json | 4 +- .../customies/block/BlockComponentsTrait.php | 3 +- .../customies/block/BlockManager.php | 9 +- .../customies/block/CustomiesBlock.php | 17 ++- .../customies/block/CustomiesBlockFactory.php | 39 +++-- .../block/component/BlockComponent.php | 16 ++ .../block/component/CollisionBoxComponent.php | 27 +++- .../component/CraftingTableComponent.php | 9 +- .../DestructibleByExplosionComponent.php | 6 +- .../DestructibleByMiningComponent.php | 6 +- .../DestructionParticlesComponent.php | 18 ++- .../block/component/DisplayNameComponent.php | 6 +- .../block/component/FlammableComponent.php | 9 +- .../block/component/FrictionComponent.php | 6 +- .../block/component/GeometryComponent.php | 12 +- .../block/component/ItemVisualComponent.php | 9 +- .../component/LightDampeningComponent.php | 6 +- .../component/LightEmissionComponent.php | 6 +- .../component/LiquidDetectionComponent.php | 12 +- .../block/component/LootComponent.php | 6 +- .../block/component/MapColorComponent.php | 6 +- .../component/MaterialInstancesComponent.php | 10 +- .../block/component/MovableComponent.php | 9 +- .../component/PlacementFilterComponent.php | 10 +- .../block/component/RandomOffsetComponent.php | 7 +- .../RedstoneConductivityComponent.php | 9 +- .../block/component/SelectionBoxComponent.php | 21 ++- .../component/VanillaBlockComponents.php | 137 ++++++++++++++++++ .../customies/block/properties/Material.php | 24 ++- .../block/properties/RenderMethod.php | 18 +-- .../customies/item/CustomiesItemFactory.php | 1 - 31 files changed, 415 insertions(+), 63 deletions(-) create mode 100644 src/customiesdevs/customies/block/component/VanillaBlockComponents.php diff --git a/resources/behavior/blocks/custom_slime_block.json b/resources/behavior/blocks/custom_slime_block.json index f2715b6a..e249618f 100644 --- a/resources/behavior/blocks/custom_slime_block.json +++ b/resources/behavior/blocks/custom_slime_block.json @@ -8,10 +8,10 @@ } }, "components": { - "minecraft:geometry": "geometry.custom_slime_block", + "minecraft:geometry": "minecraft:geometry.full_block", "minecraft:material_instances": { "*": { - "texture": "custom_slime_block", + "texture": "slime_block", "render_method": "blend" } }, diff --git a/src/customiesdevs/customies/block/BlockComponentsTrait.php b/src/customiesdevs/customies/block/BlockComponentsTrait.php index 90c0b43f..4228d388 100644 --- a/src/customiesdevs/customies/block/BlockComponentsTrait.php +++ b/src/customiesdevs/customies/block/BlockComponentsTrait.php @@ -15,6 +15,7 @@ use customiesdevs\customies\block\component\MaterialInstancesComponent; use customiesdevs\customies\block\component\SelectionBoxComponent; use customiesdevs\customies\block\properties\Material; +use customiesdevs\customies\block\properties\RenderMethod; trait BlockComponentsTrait { @@ -61,6 +62,6 @@ protected function initComponent(string $texture, bool $useGeometry = true): voi if($this->getName() !== "Unknown") { $this->addComponent(new DisplayNameComponent($this->getName())); } - $this->addComponent(new MaterialInstancesComponent([new Material(Material::TARGET_ALL, $texture, Material::RENDER_METHOD_OPAQUE)])); + $this->addComponent(new MaterialInstancesComponent([new Material(Material::TARGET_ALL, $texture, RenderMethod::OPAQUE)])); } } \ No newline at end of file diff --git a/src/customiesdevs/customies/block/BlockManager.php b/src/customiesdevs/customies/block/BlockManager.php index b97845bb..37eb556e 100644 --- a/src/customiesdevs/customies/block/BlockManager.php +++ b/src/customiesdevs/customies/block/BlockManager.php @@ -6,6 +6,7 @@ use customiesdevs\customies\Customies; use customiesdevs\customies\item\CreativeInventoryInfo; +use pocketmine\block\Block; use pocketmine\utils\Config; use pocketmine\utils\SingletonTrait; use function array_filter; @@ -121,13 +122,15 @@ public function registerBlock(string $blockFile): void { throw new \InvalidArgumentException("Invalid block config: missing required fields"); } - $customBlock = new CustomiesBlock($minecraftBlock["components"]); $identifier = $minecraftBlock["description"]["identifier"]; + $components = $minecraftBlock["components"]; $creativeInfo = $this->getCreativeInventoryInfo($minecraftBlock); - + CustomiesBlockFactory::getInstance()->registerBlock( - static fn() => $customBlock, + static function() use ($components): Block { + return new CustomiesBlock($components); + }, $identifier, $creativeInfo ); diff --git a/src/customiesdevs/customies/block/CustomiesBlock.php b/src/customiesdevs/customies/block/CustomiesBlock.php index ecc7718d..aa2f14bb 100644 --- a/src/customiesdevs/customies/block/CustomiesBlock.php +++ b/src/customiesdevs/customies/block/CustomiesBlock.php @@ -2,19 +2,26 @@ namespace customiesdevs\customies\block; +use customiesdevs\customies\block\component\VanillaBlockComponents; use pocketmine\block\Block; +use pocketmine\block\BlockBreakInfo; use pocketmine\block\BlockIdentifier; use pocketmine\block\BlockTypeIds; +use pocketmine\block\BlockTypeInfo; class CustomiesBlock extends Block implements BlockComponents { - - use BlockComponentsTrait; + use BlockComponentsTrait; public function __construct(array $components) { - parent::__construct(new BlockIdentifier(BlockTypeIds::newId())); + parent::__construct( + new BlockIdentifier(BlockTypeIds::newId()), + "Custom Block", + new BlockTypeInfo(new BlockBreakInfo(1)) + ); foreach ($components as $componentName => $componentData) { - switch ($componentName) { - + $componentClass = VanillaBlockComponents::classFor($componentName) ?? null; + if ($componentClass !== null && method_exists($componentClass, 'fromJson')) { + $this->addComponent($componentClass::fromJson($componentData)); } } } diff --git a/src/customiesdevs/customies/block/CustomiesBlockFactory.php b/src/customiesdevs/customies/block/CustomiesBlockFactory.php index 4324aa93..bb0923c0 100644 --- a/src/customiesdevs/customies/block/CustomiesBlockFactory.php +++ b/src/customiesdevs/customies/block/CustomiesBlockFactory.php @@ -78,6 +78,10 @@ public function get(string $identifier): Block { ); } + /** + * Loads all the creative groups from the CreativeInventory entries. This is used to ensure that all groups are + * available when registering new blocks with creative inventory info. + */ private function loadGroups() : void { if($this->groups !== []){ return; @@ -101,9 +105,12 @@ public function getBlockPaletteEntries(): array { /** * Register a block to the BlockFactory and all the required mappings. A custom stateReader and stateWriter can be * provided to allow for custom block state serialization. - * @phpstan-param (Closure(): Block) $blockFunc - * @phpstan-param null|(Closure(BlockStateWriter): Block) $serializer - * @phpstan-param null|(Closure(Block): BlockStateReader) $deserializer + * @param Closure $blockFunc A closure that returns a new instance of the block to register. + * @param string $identifier The unique identifier for the block (e.g. "namespace:block_name"). + * @param CreativeInventoryInfo|null $creativeInfo Optional creative inventory information for the block. + * @param Closure|null $serializer Optional closure that takes a BlockStateWriter and returns it after writing the block state. + * @param Closure|null $deserializer Optional closure that takes a BlockStateReader and returns a new instance of the block after reading the state. + * @throws InvalidArgumentException If the blockFunc does not return a Block instance. */ public function registerBlock(Closure $blockFunc, string $identifier, ?CreativeInventoryInfo $creativeInfo = null, ?Closure $serializer = null, ?Closure $deserializer = null): void { $block = $blockFunc(); @@ -118,6 +125,8 @@ public function registerBlock(Closure $blockFunc, string $identifier, ?CreativeI $propertiesTag = CompoundTag::create(); $components = CompoundTag::create(); + $nbt = $this->createBlockNBT($block, $creativeInfo); + // TODO if($block instanceof Permutable) { $blockPropertyNames = $blockPropertyValues = $blockProperties = []; @@ -172,8 +181,6 @@ public function registerBlock(Closure $blockFunc, string $identifier, ?CreativeI GlobalBlockStateHandlers::getSerializer()->map($block, $serializer); GlobalBlockStateHandlers::getDeserializer()->map($identifier, $deserializer); - $nbt = $this->createBlockNBT($block, $creativeInfo); - if($creativeInfo !== null){ $this->loadGroups(); if($creativeInfo->getCategory() === CreativeInventoryInfo::CATEGORY_ALL || $creativeInfo->getCategory() === CreativeInventoryInfo::CATEGORY_COMMANDS){ @@ -211,11 +218,20 @@ public function registerBlock(Closure $blockFunc, string $identifier, ?CreativeI foreach($this->blockPaletteEntries as $i => $entry) { /** @var CompoundTag $root */ $root = $entry->getStates()->getRoot(); - $root->setTag("vanilla_block_data", CompoundTag::create()->setInt("block_id", 10000 + $i)); + $root->setTag("vanilla_block_data", CompoundTag::create() + ->setInt("block_id", 10000 + $i) + ->setString("material", "dirt")); $this->blockPaletteEntries[$i] = new BlockPaletteEntry($entry->getName(), new CacheableNbt($root)); } } + /** + * Creates the NBT data for the block. This includes the components and their values. + * If the block does not have components, an empty CompoundTag is returned. + * @param Block $block The block to create the NBT data for. + * @param CreativeInventoryInfo|null $creativeInfo Optional creative inventory information for the block. + * @return CompoundTag The NBT data for the block. + */ private function createBlockNBT(Block $block, ?CreativeInventoryInfo $creativeInfo): CompoundTag { $propertiesTag = CompoundTag::create(); $components = CompoundTag::create(); @@ -228,13 +244,16 @@ private function createBlockNBT(Block $block, ?CreativeInventoryInfo $creativeIn } $components->setTag($component->getName(), $tag); } + if($creativeInfo !== null) { + $propertiesTag->setTag("menu_category", CompoundTag::create() + ->setString("category", $creativeInfo->getCategory() ?? "") + ->setString("group", $creativeInfo->getGroup() ?? "")); + } $propertiesTag ->setTag("components", $components) - ->setTag("menu_category", CompoundTag::create() - ->setString("category", $creativeInfo->getCategory() ?? "") - ->setString("group", $creativeInfo->getGroup() ?? "")) ->setInt("molangVersion", 12); - return $propertiesTag; + \var_dump($propertiesTag->__toString()); + return $propertiesTag; } return CompoundTag::create(); } diff --git a/src/customiesdevs/customies/block/component/BlockComponent.php b/src/customiesdevs/customies/block/component/BlockComponent.php index 517f1f46..53a9017c 100644 --- a/src/customiesdevs/customies/block/component/BlockComponent.php +++ b/src/customiesdevs/customies/block/component/BlockComponent.php @@ -5,7 +5,23 @@ interface BlockComponent { + /** + * The component identifier, e.g. "minecraft:collision_box" + * @return string + */ public function getName(): string; + /** + * The value of this component, as it would appear in a block JSON. + * @return mixed + */ public function getValue(): mixed; + + /** + * Create a component instance from decoded JSON (block definition) data. + * Implementations should be tolerant of missing keys and apply sensible defaults. + * @param mixed $data The raw value found under the component identifier in a block JSON. + * @return static + */ + public static function fromJson(mixed $data): static; } \ No newline at end of file diff --git a/src/customiesdevs/customies/block/component/CollisionBoxComponent.php b/src/customiesdevs/customies/block/component/CollisionBoxComponent.php index 245ec377..e3d06e1a 100644 --- a/src/customiesdevs/customies/block/component/CollisionBoxComponent.php +++ b/src/customiesdevs/customies/block/component/CollisionBoxComponent.php @@ -16,14 +16,18 @@ class CollisionBoxComponent implements BlockComponent { * @param Vector3 $size Size of each side of the collision box. Size is specified as [x, y, z]. "origin" + "size" must be in the range (-8, 0, -8) to (8, 16, 8), inclusive. * @param bool $useCollisionBox If collision should be enabled, default is set to `true`. */ - public function __construct(bool $useCollisionBox = true, Vector3 $origin = new Vector3(-8.0, 0.0, -8.0), Vector3 $size = new Vector3(16.0, 16.0, 16.0)) { + public function __construct( + bool $useCollisionBox = true, + Vector3 $origin = new Vector3(-8, 0, -8), + Vector3 $size = new Vector3(16, 16, 16) + ) { $this->useCollisionBox = $useCollisionBox; $this->origin = $origin; $this->size = $size; } public function getName(): string { - return "minecraft:collision_box"; + return VanillaBlockComponents::COLLISION_BOX; } public function getValue(): array { @@ -41,4 +45,23 @@ public function getValue(): array { ] ]; } + + public static function fromJson(mixed $data): static { + if (is_bool($data)) { + return new self($data); + } + return new self( + true, + new Vector3( + $data["origin"][0] ?? -8, + $data["origin"][1] ?? 0, + $data["origin"][2] ?? -8 + ), + new Vector3( + $data["size"][0] ?? 16, + $data["size"][1] ?? 16, + $data["size"][2] ?? 16 + ) + ); + } } \ No newline at end of file diff --git a/src/customiesdevs/customies/block/component/CraftingTableComponent.php b/src/customiesdevs/customies/block/component/CraftingTableComponent.php index 281a6e9d..b3783fbd 100644 --- a/src/customiesdevs/customies/block/component/CraftingTableComponent.php +++ b/src/customiesdevs/customies/block/component/CraftingTableComponent.php @@ -18,7 +18,7 @@ public function __construct(string $tableName = "Crafting Table", array $craftin } public function getName(): string { - return "minecraft:crafting_table"; + return VanillaBlockComponents::CRAFTING_TABLE; } public function getValue(): array { @@ -27,4 +27,11 @@ public function getValue(): array { "table_name" => $this->tableName ]; } + + public static function fromJson(mixed $data): static { + return new self( + $data["table_name"] ?? "Crafting Table", + $data["crafting_tags"] ?? ["crafting_table"] + ); + } } \ No newline at end of file diff --git a/src/customiesdevs/customies/block/component/DestructibleByExplosionComponent.php b/src/customiesdevs/customies/block/component/DestructibleByExplosionComponent.php index 1066ce36..d1df8679 100644 --- a/src/customiesdevs/customies/block/component/DestructibleByExplosionComponent.php +++ b/src/customiesdevs/customies/block/component/DestructibleByExplosionComponent.php @@ -15,7 +15,7 @@ public function __construct(float $explosionResistance = 0.0) { } public function getName(): string { - return "minecraft:destructible_by_explosion"; + return VanillaBlockComponents::DESTRUCTIBLE_BY_EXPLOSION; } public function getValue(): array { @@ -23,4 +23,8 @@ public function getValue(): array { "value" => $this->explosionResistance ]; } + + public static function fromJson(mixed $data): static { + return new self($data["explosion_resistance"] ?? 0.0); + } } \ No newline at end of file diff --git a/src/customiesdevs/customies/block/component/DestructibleByMiningComponent.php b/src/customiesdevs/customies/block/component/DestructibleByMiningComponent.php index 3f80251f..05c247d0 100644 --- a/src/customiesdevs/customies/block/component/DestructibleByMiningComponent.php +++ b/src/customiesdevs/customies/block/component/DestructibleByMiningComponent.php @@ -15,7 +15,7 @@ public function __construct(float $secondsToDestroy = 0.0) { } public function getName(): string { - return "minecraft:destructible_by_mining"; + return VanillaBlockComponents::DESTRUCTIBLE_BY_MINING; } public function getValue(): array { @@ -23,4 +23,8 @@ public function getValue(): array { "value" => $this->secondsToDestroy ]; } + + public static function fromJson(mixed $data): static { + return new self($data["seconds_to_destroy"] ?? 0.0); + } } \ No newline at end of file diff --git a/src/customiesdevs/customies/block/component/DestructionParticlesComponent.php b/src/customiesdevs/customies/block/component/DestructionParticlesComponent.php index e30934dd..d2518fb7 100644 --- a/src/customiesdevs/customies/block/component/DestructionParticlesComponent.php +++ b/src/customiesdevs/customies/block/component/DestructionParticlesComponent.php @@ -8,29 +8,37 @@ class DestructionParticlesComponent implements BlockComponent { private int $particleCount; private string $texture; - private string $tintMethod; + private TintMethod $tintMethod; /** * Sets the particles that will be used when block is destroyed. * @param int $particleCount Optional, number of particles to spawn of destruction. Default is 100, maximum is 255 inclusively * @param string $texture The texture name used for the particle. - * @param string $tintMethod Tint multiplied to the color. Tint method logic varies, but often refers to the "rain" and "temperature" of the biome the block is placed in to compute the tint. + * @param TintMethod $tintMethod Tint multiplied to the color. Tint method logic varies, but often refers to the "rain" and "temperature" of the biome the block is placed in to compute the tint. */ - public function __construct(int $particleCount = 100, string $texture = "", string $tintMethod = TintMethod::NONE) { + public function __construct(int $particleCount = 100, string $texture = "", TintMethod $tintMethod = TintMethod::NONE) { $this->particleCount = $particleCount; $this->texture = $texture; $this->tintMethod = $tintMethod; } public function getName(): string { - return "minecraft:destruction_particles"; + return VanillaBlockComponents::DESTRUCTION_PARTICLES; } public function getValue(): array { return [ "particle_count" => $this->particleCount, "texture" => $this->texture, - "tint_method" => $this->tintMethod + "tint_method" => $this->tintMethod->value ]; } + + public static function fromJson(mixed $data): static { + return new self( + $data["particle_count"] ?? 100, + $data["texture"] ?? "", + TintMethod::tryFrom($data["tint_method"] ?? "") ?? TintMethod::NONE + ); + } } \ No newline at end of file diff --git a/src/customiesdevs/customies/block/component/DisplayNameComponent.php b/src/customiesdevs/customies/block/component/DisplayNameComponent.php index fcb6b6a2..73ee8c0e 100644 --- a/src/customiesdevs/customies/block/component/DisplayNameComponent.php +++ b/src/customiesdevs/customies/block/component/DisplayNameComponent.php @@ -18,7 +18,7 @@ public function __construct(string $displayName) { } public function getName(): string { - return "minecraft:display_name"; + return VanillaBlockComponents::DISPLAY_NAME; } public function getValue(): array { @@ -26,4 +26,8 @@ public function getValue(): array { "value" => $this->displayName ]; } + + public static function fromJson(mixed $data): static { + return new self($data); + } } \ No newline at end of file diff --git a/src/customiesdevs/customies/block/component/FlammableComponent.php b/src/customiesdevs/customies/block/component/FlammableComponent.php index 1aa11529..950b6087 100644 --- a/src/customiesdevs/customies/block/component/FlammableComponent.php +++ b/src/customiesdevs/customies/block/component/FlammableComponent.php @@ -18,7 +18,7 @@ public function __construct(int $catchChanceModifier = 5, int $destroyChanceModi } public function getName(): string { - return "minecraft:flammable"; + return VanillaBlockComponents::FLAMMABLE; } public function getValue(): array { @@ -27,4 +27,11 @@ public function getValue(): array { "destroy_chance_modifier" => $this->destroyChanceModifier ]; } + + public static function fromJson(mixed $data): static { + return new self( + $data["catch_chance_modifier"] ?? 5, + $data["destroy_chance_modifier"] ?? 20 + ); + } } \ No newline at end of file diff --git a/src/customiesdevs/customies/block/component/FrictionComponent.php b/src/customiesdevs/customies/block/component/FrictionComponent.php index 5ec200c6..540d8bcb 100644 --- a/src/customiesdevs/customies/block/component/FrictionComponent.php +++ b/src/customiesdevs/customies/block/component/FrictionComponent.php @@ -16,7 +16,7 @@ public function __construct(float $friction) { } public function getName(): string { - return "minecraft:friction"; + return VanillaBlockComponents::FRICTION; } public function getValue(): array { @@ -24,4 +24,8 @@ public function getValue(): array { "value" => $this->friction ]; } + + public static function fromJson(mixed $data): static { + return new self($data); + } } \ No newline at end of file diff --git a/src/customiesdevs/customies/block/component/GeometryComponent.php b/src/customiesdevs/customies/block/component/GeometryComponent.php index aad457d3..82542d3e 100644 --- a/src/customiesdevs/customies/block/component/GeometryComponent.php +++ b/src/customiesdevs/customies/block/component/GeometryComponent.php @@ -33,7 +33,7 @@ public function __construct( } public function getName(): string { - return "minecraft:geometry"; + return VanillaBlockComponents::GEOMETRY; } public function getValue(): array { @@ -49,4 +49,14 @@ public function getValue(): array { "useBlockTypeLightAbsorption" => false ]; } + + public static function fromJson(mixed $data): static { + return new self( + $data["identifier"] ?? "minecraft:geometry.full_block", + $data["bone_visibility"] ?? [], + $data["culling"] ?? "", + $data["culling_layer"] ?? "minecraft:culling_layer.undefined", + $data["uv_lock"] ?? false + ); + } } \ No newline at end of file diff --git a/src/customiesdevs/customies/block/component/ItemVisualComponent.php b/src/customiesdevs/customies/block/component/ItemVisualComponent.php index 3de8b103..22852dac 100644 --- a/src/customiesdevs/customies/block/component/ItemVisualComponent.php +++ b/src/customiesdevs/customies/block/component/ItemVisualComponent.php @@ -13,7 +13,7 @@ public function __construct(GeometryComponent $geometry, MaterialInstancesCompon } public function getName(): string { - return "minecraft:item_visual"; + return VanillaBlockComponents::ITEM_VISUAL; } public function getValue(): array { @@ -22,4 +22,11 @@ public function getValue(): array { "material_instances" => $this->materialInstances->getValue() ]; } + + public static function fromJson(mixed $data): static { + return new self( + GeometryComponent::fromJson($data["geometry"] ?? []), + MaterialInstancesComponent::fromJson($data["material_instances"] ?? []) + ); + } } \ No newline at end of file diff --git a/src/customiesdevs/customies/block/component/LightDampeningComponent.php b/src/customiesdevs/customies/block/component/LightDampeningComponent.php index 8a96a6d1..669c9159 100644 --- a/src/customiesdevs/customies/block/component/LightDampeningComponent.php +++ b/src/customiesdevs/customies/block/component/LightDampeningComponent.php @@ -15,7 +15,7 @@ public function __construct(int $dampening = 15) { } public function getName(): string { - return "minecraft:light_dampening"; + return VanillaBlockComponents::LIGHT_DAMPENING; } public function getValue(): array { @@ -23,4 +23,8 @@ public function getValue(): array { "lightLevel" => $this->dampening ]; } + + public static function fromJson(mixed $data): static { + return new self($data ?? 15); + } } \ No newline at end of file diff --git a/src/customiesdevs/customies/block/component/LightEmissionComponent.php b/src/customiesdevs/customies/block/component/LightEmissionComponent.php index f5e0d370..7e3d45f5 100644 --- a/src/customiesdevs/customies/block/component/LightEmissionComponent.php +++ b/src/customiesdevs/customies/block/component/LightEmissionComponent.php @@ -15,7 +15,7 @@ public function __construct(int $emission = 0) { } public function getName(): string { - return "minecraft:light_emission"; + return VanillaBlockComponents::LIGHT_EMISSION; } public function getValue(): array { @@ -23,4 +23,8 @@ public function getValue(): array { "lightLevel" => $this->emission ]; } + + public static function fromJson(mixed $data): static { + return new self($data ?? 0); + } } \ No newline at end of file diff --git a/src/customiesdevs/customies/block/component/LiquidDetectionComponent.php b/src/customiesdevs/customies/block/component/LiquidDetectionComponent.php index bc6df070..71db5bdf 100644 --- a/src/customiesdevs/customies/block/component/LiquidDetectionComponent.php +++ b/src/customiesdevs/customies/block/component/LiquidDetectionComponent.php @@ -32,7 +32,7 @@ public function __construct(string $liquidType = "water", bool $canContainLiquid } public function getName(): string { - return "minecraft:liquid_detection"; + return VanillaBlockComponents::LIQUID_DETECTION; } public function getValue(): array { @@ -47,4 +47,14 @@ public function getValue(): array { ] ]; } + + public static function fromJson(mixed $data): static { + $rule = $data["detectionRules"][0] ?? []; + return new self( + $rule["liquid_type"] ?? "water", + $rule["can_contain_liquid"] ?? false, + $rule["on_liquid_touches"] ?? self::BLOCKING, + $rule["stops_liquid_flowing_from_direction"] ?? [] + ); + } } \ No newline at end of file diff --git a/src/customiesdevs/customies/block/component/LootComponent.php b/src/customiesdevs/customies/block/component/LootComponent.php index 20a7e121..82acff38 100644 --- a/src/customiesdevs/customies/block/component/LootComponent.php +++ b/src/customiesdevs/customies/block/component/LootComponent.php @@ -14,7 +14,7 @@ public function __construct(string $loot) { } public function getName(): string { - return "minecraft:loot"; + return VanillaBlockComponents::LOOT; } public function getValue(): array { @@ -22,4 +22,8 @@ public function getValue(): array { "value" => $this->loot ]; } + + public static function fromJson(mixed $data): static { + return new self($data); + } } \ No newline at end of file diff --git a/src/customiesdevs/customies/block/component/MapColorComponent.php b/src/customiesdevs/customies/block/component/MapColorComponent.php index 1a63a806..d568afc0 100644 --- a/src/customiesdevs/customies/block/component/MapColorComponent.php +++ b/src/customiesdevs/customies/block/component/MapColorComponent.php @@ -15,7 +15,7 @@ public function __construct(string|array $color) { } public function getName(): string { - return "minecraft:map_color"; + return VanillaBlockComponents::MAP_COLOR; } public function getValue(): array { @@ -23,4 +23,8 @@ public function getValue(): array { "color" => $this->color ]; } + + public static function fromJson(mixed $data): static { + return new self($data); + } } \ No newline at end of file diff --git a/src/customiesdevs/customies/block/component/MaterialInstancesComponent.php b/src/customiesdevs/customies/block/component/MaterialInstancesComponent.php index 30581347..88ab21c3 100644 --- a/src/customiesdevs/customies/block/component/MaterialInstancesComponent.php +++ b/src/customiesdevs/customies/block/component/MaterialInstancesComponent.php @@ -22,7 +22,7 @@ public function __construct(private readonly array $materials) { } public function getName(): string { - return "minecraft:material_instances"; + return VanillaBlockComponents::MATERIAL_INSTANCES; } public function getValue(): array { @@ -35,4 +35,12 @@ public function getValue(): array { "materials" => $materials ]; } + + public static function fromJson(mixed $data): static { + $materials = []; + foreach($data as $target => $materialData){ + $materials[] = Material::fromArray($target, $materialData); + } + return new self($materials); + } } \ No newline at end of file diff --git a/src/customiesdevs/customies/block/component/MovableComponent.php b/src/customiesdevs/customies/block/component/MovableComponent.php index 70565f8b..3cbdd5a7 100644 --- a/src/customiesdevs/customies/block/component/MovableComponent.php +++ b/src/customiesdevs/customies/block/component/MovableComponent.php @@ -32,7 +32,7 @@ public function __construct(string $movementType = self::MOVEMENT_TYPE_PUSH_PULL } public function getName(): string { - return "minecraft:movable"; + return VanillaBlockComponents::MOVABLE; } public function getValue(): array { @@ -41,4 +41,11 @@ public function getValue(): array { "sticky" => $this->sticky ]; } + + public static function fromJson(mixed $data): static { + return new self( + $data["movement_type"] ?? self::MOVEMENT_TYPE_PUSH_PULL, + $data["sticky"] ?? self::STICKY_NONE + ); + } } \ No newline at end of file diff --git a/src/customiesdevs/customies/block/component/PlacementFilterComponent.php b/src/customiesdevs/customies/block/component/PlacementFilterComponent.php index 527ae0f2..f375ad63 100644 --- a/src/customiesdevs/customies/block/component/PlacementFilterComponent.php +++ b/src/customiesdevs/customies/block/component/PlacementFilterComponent.php @@ -16,7 +16,7 @@ public function __construct(array $allowedFaces = [], array $blockFilter = []) { } public function getName(): string { - return "minecraft:placement_filter"; + return VanillaBlockComponents::PLACEMENT_FILTER; } public function getValue(): array { @@ -29,4 +29,12 @@ public function getValue(): array { ] ]; } + + // TODO Needs more data on this + public static function fromJson(mixed $data): static { + return new self( + $data["conditions"][0]["allowed_faces"] ?? [], + $data["conditions"][0]["block_filter"] ?? [] + ); + } } \ No newline at end of file diff --git a/src/customiesdevs/customies/block/component/RandomOffsetComponent.php b/src/customiesdevs/customies/block/component/RandomOffsetComponent.php index 3981a8f8..93b23fd4 100644 --- a/src/customiesdevs/customies/block/component/RandomOffsetComponent.php +++ b/src/customiesdevs/customies/block/component/RandomOffsetComponent.php @@ -11,11 +11,16 @@ public function __construct() { } public function getName(): string { - return "minecraft:random_offset"; + return VanillaBlockComponents::RANDOM_OFFSET; } public function getValue(): array { return [ ]; } + + // TODO Needs more data on this + public static function fromJson(mixed $data): static { + return new self(); + } } \ No newline at end of file diff --git a/src/customiesdevs/customies/block/component/RedstoneConductivityComponent.php b/src/customiesdevs/customies/block/component/RedstoneConductivityComponent.php index 7832058a..b6aa37c3 100644 --- a/src/customiesdevs/customies/block/component/RedstoneConductivityComponent.php +++ b/src/customiesdevs/customies/block/component/RedstoneConductivityComponent.php @@ -18,7 +18,7 @@ public function __construct(bool $allowsWireToStepDown = true, bool $redstoneCon } public function getName(): string { - return "minecraft:redstone_conductivity"; + return VanillaBlockComponents::REDSTONE_CONDUCTIVITY; } public function getValue(): array { @@ -27,4 +27,11 @@ public function getValue(): array { "redstone_conductor" => $this->redstoneConductor ]; } + + public static function fromJson(mixed $data): static { + return new self( + $data["allows_wire_to_step_down"] ?? true, + $data["redstone_conductor"] ?? false + ); + } } \ No newline at end of file diff --git a/src/customiesdevs/customies/block/component/SelectionBoxComponent.php b/src/customiesdevs/customies/block/component/SelectionBoxComponent.php index 60432ef5..44cf2ff4 100644 --- a/src/customiesdevs/customies/block/component/SelectionBoxComponent.php +++ b/src/customiesdevs/customies/block/component/SelectionBoxComponent.php @@ -23,7 +23,7 @@ public function __construct(bool $useSelectionBox = true, ?Vector3 $origin = new } public function getName(): string { - return "minecraft:selection_box"; + return VanillaBlockComponents::SELECTION_BOX; } public function getValue(): array { @@ -41,4 +41,23 @@ public function getValue(): array { ] ]; } + + public static function fromJson(mixed $data): static { + if (is_bool($data)) { + return new self($data); + } + return new self( + true, + new Vector3( + $data["origin"][0] ?? -8, + $data["origin"][1] ?? 0, + $data["origin"][2] ?? -8 + ), + new Vector3( + $data["size"][0] ?? 16, + $data["size"][1] ?? 16, + $data["size"][2] ?? 16 + ) + ); + } } \ No newline at end of file diff --git a/src/customiesdevs/customies/block/component/VanillaBlockComponents.php b/src/customiesdevs/customies/block/component/VanillaBlockComponents.php new file mode 100644 index 00000000..ee0071c5 --- /dev/null +++ b/src/customiesdevs/customies/block/component/VanillaBlockComponents.php @@ -0,0 +1,137 @@ + FQCN implementing BlockComponent. + * @var array> + */ + private static array $classMap = [ + self::COLLISION_BOX => CollisionBoxComponent::class, + self::CRAFTING_TABLE => CraftingTableComponent::class, + self::DESTRUCTIBLE_BY_EXPLOSION => DestructibleByExplosionComponent::class, + self::DESTRUCTIBLE_BY_MINING => DestructibleByMiningComponent::class, + self::DESTRUCTION_PARTICLES => DestructionParticlesComponent::class, + self::DISPLAY_NAME => DisplayNameComponent::class, + self::FLAMMABLE => FlammableComponent::class, + self::FRICTION => FrictionComponent::class, + self::GEOMETRY => GeometryComponent::class, + self::ITEM_VISUAL => ItemVisualComponent::class, + self::LIGHT_DAMPENING => LightDampeningComponent::class, + self::LIGHT_EMISSION => LightEmissionComponent::class, + self::LIQUID_DETECTION => LiquidDetectionComponent::class, + self::LOOT => LootComponent::class, + self::MAP_COLOR => MapColorComponent::class, + self::MATERIAL_INSTANCES => MaterialInstancesComponent::class, + self::MOVABLE => MovableComponent::class, + self::PLACEMENT_FILTER => PlacementFilterComponent::class, + self::RANDOM_OFFSET => RandomOffsetComponent::class, + self::REDSTONE_CONDUCTIVITY => RedstoneConductivityComponent::class, + self::SELECTION_BOX => SelectionBoxComponent::class, + ]; + + private function __construct() {} + + /** + * Get all component identifiers. + * @return string[] + */ + public static function all(): array { + return self::ALL; + } + + /** + * Check if the given identifier is a known component. + * @param string $identifier Component identifier + * @return bool True if the identifier is known, false otherwise. + */ + public static function isValid(string $identifier): bool { + return in_array($identifier, self::ALL, true); + } + + /** + * Returns the component class for the identifier if registered, null otherwise. + * @param string $identifier Component identifier + * @return class-string|null + */ + public static function classFor(string $identifier): ?string { + return self::$classMap[$identifier] ?? null; + } + + /** + * Determine if a component class exists for the identifier. + * @param string $identifier Component identifier + * @return bool True if a class is registered for the identifier, false otherwise. + */ + public static function hasClass(string $identifier): bool { + return isset(self::$classMap[$identifier]); + } + + /** + * Register or override a component class at runtime. + * @param string $identifier Component identifier (must be one of the known constants) + * @param class-string $fqcn Fully qualified class name implementing BlockComponent + */ + public static function registerClass(string $identifier, string $fqcn): void { + if (!self::isValid($identifier)) { + throw new \InvalidArgumentException("Unknown component id: $identifier"); + } + if (!is_subclass_of($fqcn, BlockComponent::class)) { + throw new \InvalidArgumentException("Class $fqcn must implement " . BlockComponent::class); + } + self::$classMap[$identifier] = $fqcn; + } +} \ No newline at end of file diff --git a/src/customiesdevs/customies/block/properties/Material.php b/src/customiesdevs/customies/block/properties/Material.php index 4c956c93..7fc8f870 100644 --- a/src/customiesdevs/customies/block/properties/Material.php +++ b/src/customiesdevs/customies/block/properties/Material.php @@ -17,8 +17,8 @@ final class Material { /** * @param string $target The targeted face for the material. Possible values are: "*", "sides", "up", "down", "north", "east", "south", "west". * @param string $texture Texture name for the material. - * @param string $renderMethod The render method to use. - * @param string $tintMethod Tint multiplied to the color. Tint method logic varies, but often refers to the "rain" and "temperature" of the biome the block is placed in to compute the tint. + * @param RenderMethod $renderMethod The render method to use. + * @param TintMethod $tintMethod Tint multiplied to the color. Tint method logic varies, but often refers to the "rain" and "temperature" of the biome the block is placed in to compute the tint. * @param float $ambientOcclusion If this material has ambient occlusion applied when lighting, shadows will be created around and underneath the block. Decimal value controls exponent applied to a value after lighting. * @param boolean $faceDimming This material should be dimmed by the direction it's facing. * @param boolean $isotropic Should the faces that this material is applied to randomize their UVs? @@ -26,8 +26,8 @@ final class Material { public function __construct( private readonly string $target, private readonly string $texture, - private readonly string $renderMethod = RenderMethod::OPAQUE, - private readonly string $tintMethod = TintMethod::NONE, + private readonly RenderMethod $renderMethod = RenderMethod::OPAQUE, + private readonly TintMethod $tintMethod = TintMethod::NONE, private readonly float $ambientOcclusion = 1.0, private readonly bool $faceDimming = false, private readonly bool $isotropic = false @@ -40,12 +40,24 @@ public function __construct( public function getTarget(): string { return $this->target; } + + public static function fromArray(string $target, array $data): self { + return new self( + $target, + $data["texture"], + RenderMethod::tryFrom($data["render_method"] ?? "") ?? RenderMethod::OPAQUE, + TintMethod::tryFrom($data["tint_method"] ?? "") ?? TintMethod::NONE, + (float)($data["ambient_occlusion"] ?? 1.0), + (bool)($data["face_dimming"] ?? false), + (bool)($data["isotropic"] ?? false) + ); + } public function toArray(): array { return [ "texture" => $this->texture, - "render_method" => $this->renderMethod, - "tint_method" => $this->tintMethod, + "render_method" => $this->renderMethod->value, + "tint_method" => $this->tintMethod->value, "ambient_occlusion" => $this->ambientOcclusion, "face_dimming" => $this->faceDimming, "isotropic" => $this->isotropic diff --git a/src/customiesdevs/customies/block/properties/RenderMethod.php b/src/customiesdevs/customies/block/properties/RenderMethod.php index 5efe4989..89695a57 100644 --- a/src/customiesdevs/customies/block/properties/RenderMethod.php +++ b/src/customiesdevs/customies/block/properties/RenderMethod.php @@ -3,14 +3,14 @@ namespace customiesdevs\customies\block\properties; enum RenderMethod: string { - case ALPHA_TEST = "alpha_test"; - case ALPHA_TEST_SINGLE_SIDED = "alpha_test_single_sided"; - case BLEND = "blend"; - case DOUBLE_SIDED = "double_sided"; - case OPAQUE = "opaque"; + case ALPHA_TEST = "alpha_test"; + case ALPHA_TEST_SINGLE_SIDED = "alpha_test_single_sided"; + case BLEND = "blend"; + case DOUBLE_SIDED = "double_sided"; + case OPAQUE = "opaque"; - // Distance-Based Render Methods - case ALPHA_TEST_TO_OPAQUE = "alpha_test_to_opaque"; - case ALPHA_TEST_SINGLE_SIDED_TO_OPAQUE = "alpha_test_single_sided_to_opaque"; - case BLEND_TO_OPAQUE = "blend_to_opaque"; + // Distance-Based Render Methods + case ALPHA_TEST_TO_OPAQUE = "alpha_test_to_opaque"; + case ALPHA_TEST_SINGLE_SIDED_TO_OPAQUE = "alpha_test_single_sided_to_opaque"; + case BLEND_TO_OPAQUE = "blend_to_opaque"; } \ No newline at end of file diff --git a/src/customiesdevs/customies/item/CustomiesItemFactory.php b/src/customiesdevs/customies/item/CustomiesItemFactory.php index fd24393b..7e86b344 100644 --- a/src/customiesdevs/customies/item/CustomiesItemFactory.php +++ b/src/customiesdevs/customies/item/CustomiesItemFactory.php @@ -74,7 +74,6 @@ public function getItemTableEntries(): array { /** * Registers the item to the item factory and assigns it an ID. It also updates the required mappings and stores the * item components if present. - * @phpstan-param class-string $className * @param Closure $itemFunc A closure that returns an instance of the item to be registered * @param string $identifier The string identifier for the item, usually in the format "namespace:item_name" * @param CreativeInventoryInfo|null $creativeInfo The creative inventory info for the item, if any From 699b95aaaba82b3c1990b3a3dbdb2a0cfec40f36 Mon Sep 17 00:00:00 2001 From: JeanTKG <102908437+jeantkg@users.noreply.github.com> Date: Sat, 4 Oct 2025 22:08:13 +0300 Subject: [PATCH 04/51] Implement fromJson method for item components and add VanillaItemComponents class - Added fromJson method to various item component classes to facilitate the creation of component instances from JSON data. - Introduced VanillaItemComponents class to define constants for all item component identifiers and manage their associated classes. - Enhanced item component interfaces to include fromJson method for better JSON handling. --- .../customies/block/CustomiesBlock.php | 2 +- .../component/VanillaBlockComponents.php | 4 +- .../customies/item/CustomiesItem.php | 182 +---------------- .../item/component/AllowOffHandComponent.php | 4 + .../item/component/BlockPlacerComponent.php | 17 ++ .../component/BundleInteractionComponent.php | 5 + .../CanDestroyInCreativeComponent.php | 4 + .../item/component/CompostableComponent.php | 4 + .../item/component/CooldownComponent.php | 4 + .../component/DamageAbsorptionComponent.php | 4 + .../item/component/DamageComponent.php | 4 + .../item/component/DiggerComponent.php | 5 + .../item/component/DisplayNameComponent.php | 4 + .../item/component/DurabilityComponent.php | 4 + .../component/DurabilitySensorComponent.php | 4 + .../item/component/DyeableComponent.php | 4 + .../item/component/EnchantableComponent.php | 4 + .../item/component/FireResistantComponent.php | 4 + .../item/component/FoodComponent.php | 9 + .../item/component/FuelComponent.php | 4 + .../item/component/GlintComponent.php | 4 + .../item/component/HandEquippedComponent.php | 4 + .../component/HoverTextColorComponent.php | 4 + .../item/component/IconComponent.php | 10 + .../component/InteractButtonComponent.php | 4 + .../item/component/ItemComponent.php | 16 ++ .../item/component/LiquidClippedComponent.php | 4 + .../item/component/MaxStackSizeComponent.php | 4 + .../item/component/ProjectileComponent.php | 4 + .../item/component/RarityComponent.php | 4 + .../item/component/RecordComponent.php | 4 + .../item/component/RepairableComponent.php | 10 + .../item/component/ShooterComponent.php | 12 ++ .../item/component/ShouldDespawnComponent.php | 4 + .../item/component/StackedByDataComponent.php | 4 + .../item/component/StorageItemComponent.php | 11 + .../component/StorageWeightLimitComponent.php | 4 + .../StorageWeightModifierComponent.php | 4 + .../item/component/SwingDurationComponent.php | 4 + .../item/component/TagsComponent.php | 4 + .../item/component/ThrowableComponent.php | 11 + .../item/component/UseAnimationComponent.php | 4 + .../item/component/UseModifiersComponent.php | 4 + .../item/component/VanillaItemComponents.php | 189 ++++++++++++++++++ .../item/component/WearableComponent.php | 8 + .../customies/item/properties/RepairItems.php | 10 + 46 files changed, 440 insertions(+), 181 deletions(-) create mode 100644 src/customiesdevs/customies/item/component/VanillaItemComponents.php diff --git a/src/customiesdevs/customies/block/CustomiesBlock.php b/src/customiesdevs/customies/block/CustomiesBlock.php index aa2f14bb..520f9bec 100644 --- a/src/customiesdevs/customies/block/CustomiesBlock.php +++ b/src/customiesdevs/customies/block/CustomiesBlock.php @@ -19,7 +19,7 @@ public function __construct(array $components) { new BlockTypeInfo(new BlockBreakInfo(1)) ); foreach ($components as $componentName => $componentData) { - $componentClass = VanillaBlockComponents::classFor($componentName) ?? null; + $componentClass = VanillaBlockComponents::getClass($componentName) ?? null; if ($componentClass !== null && method_exists($componentClass, 'fromJson')) { $this->addComponent($componentClass::fromJson($componentData)); } diff --git a/src/customiesdevs/customies/block/component/VanillaBlockComponents.php b/src/customiesdevs/customies/block/component/VanillaBlockComponents.php index ee0071c5..1c631502 100644 --- a/src/customiesdevs/customies/block/component/VanillaBlockComponents.php +++ b/src/customiesdevs/customies/block/component/VanillaBlockComponents.php @@ -89,7 +89,7 @@ private function __construct() {} * Get all component identifiers. * @return string[] */ - public static function all(): array { + public static function getAll(): array { return self::ALL; } @@ -107,7 +107,7 @@ public static function isValid(string $identifier): bool { * @param string $identifier Component identifier * @return class-string|null */ - public static function classFor(string $identifier): ?string { + public static function getClass(string $identifier): ?string { return self::$classMap[$identifier] ?? null; } diff --git a/src/customiesdevs/customies/item/CustomiesItem.php b/src/customiesdevs/customies/item/CustomiesItem.php index 490ae2c4..903c2404 100644 --- a/src/customiesdevs/customies/item/CustomiesItem.php +++ b/src/customiesdevs/customies/item/CustomiesItem.php @@ -2,196 +2,22 @@ namespace customiesdevs\customies\item; -use customiesdevs\customies\item\component\AllowOffHandComponent; -use customiesdevs\customies\item\component\BlockPlacerComponent; -use customiesdevs\customies\item\component\CanDestroyInCreativeComponent; -use customiesdevs\customies\item\component\CompostableComponent; -use customiesdevs\customies\item\component\CooldownComponent; -use customiesdevs\customies\item\component\DamageAbsorptionComponent; -use customiesdevs\customies\item\component\DamageComponent; -use customiesdevs\customies\item\component\DiggerComponent; -use customiesdevs\customies\item\component\DisplayNameComponent; -use customiesdevs\customies\item\component\DurabilityComponent; -use customiesdevs\customies\item\component\DyeableComponent; -use customiesdevs\customies\item\component\EnchantableComponent; -use customiesdevs\customies\item\component\FoodComponent; -use customiesdevs\customies\item\component\FuelComponent; -use customiesdevs\customies\item\component\GlintComponent; -use customiesdevs\customies\item\component\HandEquippedComponent; -use customiesdevs\customies\item\component\HoverTextColorComponent; -use customiesdevs\customies\item\component\IconComponent; -use customiesdevs\customies\item\component\InteractButtonComponent; -use customiesdevs\customies\item\component\LiquidClippedComponent; -use customiesdevs\customies\item\component\MaxStackSizeComponent; -use customiesdevs\customies\item\component\ProjectileComponent; -use customiesdevs\customies\item\component\RarityComponent; -use customiesdevs\customies\item\component\RecordComponent; -use customiesdevs\customies\item\component\ShooterComponent; -use customiesdevs\customies\item\component\ShouldDespawnComponent; -use customiesdevs\customies\item\component\StackedByDataComponent; -use customiesdevs\customies\item\component\ThrowableComponent; -use customiesdevs\customies\item\component\UseAnimationComponent; -use customiesdevs\customies\item\component\UseModifiersComponent; -use customiesdevs\customies\item\component\WearableComponent; +use customiesdevs\customies\item\component\VanillaItemComponents; use customiesdevs\customies\item\ItemComponents; use customiesdevs\customies\item\ItemComponentsTrait; use pocketmine\item\Item; use pocketmine\item\ItemIdentifier; use pocketmine\item\ItemTypeIds; -use pocketmine\item\StringToItemParser; class CustomiesItem extends Item implements ItemComponents { - use ItemComponentsTrait; public function __construct(array $components) { parent::__construct(new ItemIdentifier(ItemTypeIds::newId())); foreach ($components as $componentName => $componentData) { - switch ($componentName) { - case "minecraft:allow_off_hand": - $this->addComponent(new AllowOffHandComponent($componentData)); - break; - case "minecraft:block_placer": - $this->addComponent(new BlockPlacerComponent(StringToItemParser::getInstance()->parse($componentData["block"])->getBlock())); - break; - case "minecraft:can_destroy_in_creative": - $this->addComponent(new CanDestroyInCreativeComponent($componentData)); - break; - case "minecraft:compostable": - $this->addComponent(new CompostableComponent($componentData["composting_chance"])); - break; - case "minecraft:cooldown": - $this->addComponent(new CooldownComponent( - $componentData["category"], - $componentData["duration"] - )); - break; - case "minecraft:damage": - $this->addComponent(new DamageComponent($componentData)); - break; - case "minecraft:damage_absorption": - $this->addComponent(new DamageAbsorptionComponent($componentData["absorbable_causes"])); - break; - case "minecraft:digger": - $this->addComponent(new DiggerComponent( - $componentData["use_efficiency"], - $componentData["destroy_speeds"] ?? [] - )); - break; - case "minecraft:display_name": - $this->addComponent(new DisplayNameComponent($componentData["value"])); - break; - case "minecraft:durability": - $this->addComponent(new DurabilityComponent( - $componentData["max_durability"], - $componentData["damage_chance"]["min"] ?? 0, - $componentData["damage_chance"]["max"] ?? 100 - )); - break; - case "minecraft:dyeable": - $this->addComponent(new DyeableComponent($componentData["default_color"])); - break; - case "minecraft:enchantable": - $this->addComponent(new EnchantableComponent($componentData["slot"], $componentData["value"])); - break; - case "minecraft:food": - $this->addComponent(new FoodComponent( - $componentData["can_always_eat"], - $componentData["nutrition"], - $componentData["saturation_modifier"], - $componentData["using_converts_to"] - )); - break; - case "minecraft:fuel": - $this->addComponent(new FuelComponent($componentData["duration"])); - break; - case "minecraft:glint": - $this->addComponent(new GlintComponent($componentData)); - break; - case "minecraft:hand_equipped": - $this->addComponent(new HandEquippedComponent($componentData)); - break; - case "minecraft:hover_text_color": - $this->addComponent(new HoverTextColorComponent($componentData)); - break; - case "minecraft:icon": - $this->addComponent(new IconComponent( - $componentData["textures"]["default"], - $componentData["textures"]["dyed"] ?? "", - $componentData["textures"]["icon_trim"] ?? "" - )); - break; - case "minecraft:interact_button": - $this->addComponent(new InteractButtonComponent($componentData)); - break; - case "minecraft:liquid_clipped": - $this->addComponent(new LiquidClippedComponent($componentData)); - break; - case "minecraft:max_stack_size": - $this->addComponent(new MaxStackSizeComponent($componentData)); - break; - case "minecraft:projectile": - $this->addComponent(new ProjectileComponent( - $componentData["minimum_critical_power"], - $componentData["projectile_entity"] - )); - break; - case "minecraft:rarity": - $this->addComponent(new RarityComponent($componentData)); - break; - case "minecraft:record": - $this->addComponent(new RecordComponent( - $componentData["comparator_signal"], - $componentData["duration"], - $componentData["sound_event"] - )); - break; - case "minecraft:repairable": - // not added yet - break; - case "minecraft:shooter": - $this->addComponent(new ShooterComponent( - $componentData["ammunition"][0]["item"], - $componentData["ammunition"][0]["use_offhand"] ?? false, - $componentData["ammunition"][0]["search_inventory"] ?? false, - $componentData["ammunition"][0]["use_in_creative"] ?? false, - $componentData["max_draw_duration"] ?? 0.0, - $componentData["scale_power_by_draw_duration"] ?? false, - $componentData["charge_on_draw"] ?? false - )); - break; - case "minecraft:should_despawn": - $this->addComponent(new ShouldDespawnComponent($componentData)); - break; - case "minecraft:stacked_by_data": - $this->addComponent(new StackedByDataComponent($componentData)); - break; - case "minecraft:throwable": - $this->addComponent(new ThrowableComponent( - $componentData["do_swing_animation"], - $componentData["launch_power_scale"], - $componentData["max_draw_duration"], - $componentData["max_launch_power"], - $componentData["min_draw_duration"], - $componentData["scale_power_by_draw_duration"] - )); - break; - case "minecraft:use_animation": - $this->addComponent(new UseAnimationComponent($componentData)); - break; - case "minecraft:use_modifiers": - $this->addComponent(new UseModifiersComponent( - $componentData["movement_modifier"], - $componentData["use_duration"] - )); - break; - case "minecraft:wearable": - $this->addComponent(new WearableComponent( - $componentData["slot"], - $componentData["protection"] ?? 0, - $componentData["hides_player_location"] ?? false, - )); - break; + $componentClass = VanillaItemComponents::getClass($componentName) ?? null; + if ($componentClass !== null && method_exists($componentClass, 'fromJson')) { + $this->addComponent($componentClass::fromJson($componentData)); } } } diff --git a/src/customiesdevs/customies/item/component/AllowOffHandComponent.php b/src/customiesdevs/customies/item/component/AllowOffHandComponent.php index 016b8181..d6c27467 100644 --- a/src/customiesdevs/customies/item/component/AllowOffHandComponent.php +++ b/src/customiesdevs/customies/item/component/AllowOffHandComponent.php @@ -24,4 +24,8 @@ public function getValue(): array { "value" => $this->offHand ]; } + + public static function fromJson(mixed $data): static { + return new self($data ?? true); + } } \ No newline at end of file diff --git a/src/customiesdevs/customies/item/component/BlockPlacerComponent.php b/src/customiesdevs/customies/item/component/BlockPlacerComponent.php index b7f01fa2..65c13f35 100644 --- a/src/customiesdevs/customies/item/component/BlockPlacerComponent.php +++ b/src/customiesdevs/customies/item/component/BlockPlacerComponent.php @@ -4,6 +4,10 @@ namespace customiesdevs\customies\item\component; use pocketmine\block\Block; +use pocketmine\block\VanillaBlocks; +use pocketmine\data\bedrock\block\upgrade\LegacyBlockIdToStringIdMap; +use pocketmine\item\LegacyStringToItemParser; +use pocketmine\item\StringToItemParser; use pocketmine\world\format\io\GlobalBlockStateHandlers; final class BlockPlacerComponent implements ItemComponent { @@ -47,4 +51,17 @@ public function useOn(Block ...$blocks): self { } return $this; } + + public static function fromJson(mixed $data): static { + $block = StringToItemParser::getInstance()->parse($data["block"] ?? "")?->getBlock(); + $blocks = $data["use_on"] ?? []; + $useOn = []; + foreach($blocks as $blockData){ + $blockId = StringToItemParser::getInstance()->parse($blockData)->getBlock(); + if($blockId !== null){ + $useOn[] = $blockId; + } + } + return new self($block ?? VanillaBlocks::AIR(), $data["replace_block_item"] ?? false)->useOn(...$useOn); + } } \ No newline at end of file diff --git a/src/customiesdevs/customies/item/component/BundleInteractionComponent.php b/src/customiesdevs/customies/item/component/BundleInteractionComponent.php index 4716ec83..51ffeabc 100644 --- a/src/customiesdevs/customies/item/component/BundleInteractionComponent.php +++ b/src/customiesdevs/customies/item/component/BundleInteractionComponent.php @@ -25,4 +25,9 @@ public function getValue(): array { "num_viewable_slots" => $this->numViewableSlots ]; } + + public static function fromJson(mixed $data): static { + return new self($data["num_viewable_slots"] ?? 12); + } + } \ No newline at end of file diff --git a/src/customiesdevs/customies/item/component/CanDestroyInCreativeComponent.php b/src/customiesdevs/customies/item/component/CanDestroyInCreativeComponent.php index 04a3bb08..95e2f954 100644 --- a/src/customiesdevs/customies/item/component/CanDestroyInCreativeComponent.php +++ b/src/customiesdevs/customies/item/component/CanDestroyInCreativeComponent.php @@ -24,4 +24,8 @@ public function getValue(): array { "value" => $this->canDestroyInCreative ]; } + + public static function fromJson(mixed $data): static { + return new self($data ?? true); + } } diff --git a/src/customiesdevs/customies/item/component/CompostableComponent.php b/src/customiesdevs/customies/item/component/CompostableComponent.php index 694d1768..0123fb96 100644 --- a/src/customiesdevs/customies/item/component/CompostableComponent.php +++ b/src/customiesdevs/customies/item/component/CompostableComponent.php @@ -24,4 +24,8 @@ public function getValue(): array { "composting_chance" => $this->compostingChance ]; } + + public static function fromJson(mixed $data): static { + return new self($data["composting_chance"] ?? 0); + } } \ No newline at end of file diff --git a/src/customiesdevs/customies/item/component/CooldownComponent.php b/src/customiesdevs/customies/item/component/CooldownComponent.php index 5870902f..d804e1b9 100644 --- a/src/customiesdevs/customies/item/component/CooldownComponent.php +++ b/src/customiesdevs/customies/item/component/CooldownComponent.php @@ -34,4 +34,8 @@ public function getValue(): array { "duration" => $this->duration ]; } + + public static function fromJson(mixed $data): static { + return new self($data["category"] ?? self::CATEGORY_SHIELD, $data["duration"] ?? 0.0); + } } \ No newline at end of file diff --git a/src/customiesdevs/customies/item/component/DamageAbsorptionComponent.php b/src/customiesdevs/customies/item/component/DamageAbsorptionComponent.php index 6d0a7598..4d77d749 100644 --- a/src/customiesdevs/customies/item/component/DamageAbsorptionComponent.php +++ b/src/customiesdevs/customies/item/component/DamageAbsorptionComponent.php @@ -27,4 +27,8 @@ public function getValue(): array { "absorbable_causes" => $this->absorbableCauses ]; } + + public static function fromJson(mixed $data): static { + return new self($data["absorbable_causes"] ?? []); + } } \ No newline at end of file diff --git a/src/customiesdevs/customies/item/component/DamageComponent.php b/src/customiesdevs/customies/item/component/DamageComponent.php index d67f5c0c..c1941c26 100644 --- a/src/customiesdevs/customies/item/component/DamageComponent.php +++ b/src/customiesdevs/customies/item/component/DamageComponent.php @@ -25,4 +25,8 @@ public function getValue(): array { "value" => $this->damage ]; } + + public static function fromJson(mixed $data): static { + return new self($data ?? 0); + } } \ No newline at end of file diff --git a/src/customiesdevs/customies/item/component/DiggerComponent.php b/src/customiesdevs/customies/item/component/DiggerComponent.php index bf43edc9..f44e7cec 100644 --- a/src/customiesdevs/customies/item/component/DiggerComponent.php +++ b/src/customiesdevs/customies/item/component/DiggerComponent.php @@ -65,4 +65,9 @@ public function withTags(int $speed, string ...$tags): DiggerComponent { ]; return $this; } + + public static function fromJson(mixed $data): static { + $component = new self($data["use_efficiency"] ?? false, $data["destroy_speeds"] ?? []); + return $component; + } } \ No newline at end of file diff --git a/src/customiesdevs/customies/item/component/DisplayNameComponent.php b/src/customiesdevs/customies/item/component/DisplayNameComponent.php index 3d176cc8..7ea540a2 100644 --- a/src/customiesdevs/customies/item/component/DisplayNameComponent.php +++ b/src/customiesdevs/customies/item/component/DisplayNameComponent.php @@ -25,4 +25,8 @@ public function getValue(): array { "value" => $this->name ]; } + + public static function fromJson(mixed $data): static { + return new self($data ?? ""); + } } \ No newline at end of file diff --git a/src/customiesdevs/customies/item/component/DurabilityComponent.php b/src/customiesdevs/customies/item/component/DurabilityComponent.php index e33d210f..a10db9ed 100644 --- a/src/customiesdevs/customies/item/component/DurabilityComponent.php +++ b/src/customiesdevs/customies/item/component/DurabilityComponent.php @@ -34,4 +34,8 @@ public function getValue(): array { "max_durability" => $this->maxDurability ]; } + + public static function fromJson(mixed $data): static { + return new self($data["max_durability"] ?? 0, $data["damage_chance"]["min"] ?? 100, $data["damage_chance"]["max"] ?? 100); + } } \ No newline at end of file diff --git a/src/customiesdevs/customies/item/component/DurabilitySensorComponent.php b/src/customiesdevs/customies/item/component/DurabilitySensorComponent.php index 3e3e28ca..f6a24165 100644 --- a/src/customiesdevs/customies/item/component/DurabilitySensorComponent.php +++ b/src/customiesdevs/customies/item/component/DurabilitySensorComponent.php @@ -36,4 +36,8 @@ public function addDurabilityThreshold(int $durability, string $particleType = " ]; return $this; } + + public static function fromJson(mixed $data): static { + return new self($data["durability_thresholds"] ?? []); + } } \ No newline at end of file diff --git a/src/customiesdevs/customies/item/component/DyeableComponent.php b/src/customiesdevs/customies/item/component/DyeableComponent.php index d4ceca01..f4819d81 100644 --- a/src/customiesdevs/customies/item/component/DyeableComponent.php +++ b/src/customiesdevs/customies/item/component/DyeableComponent.php @@ -24,4 +24,8 @@ public function getValue(): array { "default_color" => $this->hex ]; } + + public static function fromJson(mixed $data): static { + return new self($data["default_color"] ?? "#ffffff"); + } } \ No newline at end of file diff --git a/src/customiesdevs/customies/item/component/EnchantableComponent.php b/src/customiesdevs/customies/item/component/EnchantableComponent.php index d6029ba7..124139d3 100644 --- a/src/customiesdevs/customies/item/component/EnchantableComponent.php +++ b/src/customiesdevs/customies/item/component/EnchantableComponent.php @@ -65,4 +65,8 @@ public function getValue(): array { "value" => $this->value ]; } + + public static function fromJson(mixed $data): static { + return new self($data["slot"] ?? self::SLOT_ALL, $data["value"] ?? 1); + } } \ No newline at end of file diff --git a/src/customiesdevs/customies/item/component/FireResistantComponent.php b/src/customiesdevs/customies/item/component/FireResistantComponent.php index eaac5f3c..cc719db0 100644 --- a/src/customiesdevs/customies/item/component/FireResistantComponent.php +++ b/src/customiesdevs/customies/item/component/FireResistantComponent.php @@ -24,4 +24,8 @@ public function getValue(): array { "value" => $this->fireResistant ]; } + + public static function fromJson(mixed $data): static { + return new self($data ?? true); + } } \ No newline at end of file diff --git a/src/customiesdevs/customies/item/component/FoodComponent.php b/src/customiesdevs/customies/item/component/FoodComponent.php index d2082b3d..11fc11ce 100644 --- a/src/customiesdevs/customies/item/component/FoodComponent.php +++ b/src/customiesdevs/customies/item/component/FoodComponent.php @@ -38,4 +38,13 @@ public function getValue(): array { ] ]; } + + public static function fromJson(mixed $data): static { + return new self( + $data["can_always_eat"] ?? false, + $data["nutrition"] ?? 0, + $data["saturation_modifier"] ?? 0.6, + $data["using_converts_to"] ?? "" + ); + } } \ No newline at end of file diff --git a/src/customiesdevs/customies/item/component/FuelComponent.php b/src/customiesdevs/customies/item/component/FuelComponent.php index 804daa28..6eea2372 100644 --- a/src/customiesdevs/customies/item/component/FuelComponent.php +++ b/src/customiesdevs/customies/item/component/FuelComponent.php @@ -24,4 +24,8 @@ public function getValue(): array { "duration" => $this->duration ]; } + + public static function fromJson(mixed $data): static { + return new self($data["duration"] ?? 0.0); + } } \ No newline at end of file diff --git a/src/customiesdevs/customies/item/component/GlintComponent.php b/src/customiesdevs/customies/item/component/GlintComponent.php index 30f5648c..df95c465 100644 --- a/src/customiesdevs/customies/item/component/GlintComponent.php +++ b/src/customiesdevs/customies/item/component/GlintComponent.php @@ -24,4 +24,8 @@ public function getValue(): array { "value" => $this->glint ]; } + + public static function fromJson(mixed $data): static { + return new self($data ?? true); + } } \ No newline at end of file diff --git a/src/customiesdevs/customies/item/component/HandEquippedComponent.php b/src/customiesdevs/customies/item/component/HandEquippedComponent.php index af352248..d2a4e2d4 100644 --- a/src/customiesdevs/customies/item/component/HandEquippedComponent.php +++ b/src/customiesdevs/customies/item/component/HandEquippedComponent.php @@ -24,4 +24,8 @@ public function getValue(): array { "value" => $this->handEquipped ]; } + + public static function fromJson(mixed $data): static { + return new self($data ?? true); + } } \ No newline at end of file diff --git a/src/customiesdevs/customies/item/component/HoverTextColorComponent.php b/src/customiesdevs/customies/item/component/HoverTextColorComponent.php index db1d6504..48fdeef2 100644 --- a/src/customiesdevs/customies/item/component/HoverTextColorComponent.php +++ b/src/customiesdevs/customies/item/component/HoverTextColorComponent.php @@ -25,4 +25,8 @@ public function getValue(): array { "value" => $this->hoverTextColor ]; } + + public static function fromJson(mixed $data): static { + return new self($data ?? "white"); + } } \ No newline at end of file diff --git a/src/customiesdevs/customies/item/component/IconComponent.php b/src/customiesdevs/customies/item/component/IconComponent.php index 4f3e79e0..4050e1c7 100644 --- a/src/customiesdevs/customies/item/component/IconComponent.php +++ b/src/customiesdevs/customies/item/component/IconComponent.php @@ -48,4 +48,14 @@ public function getValue(): array { ] ]; } + + public static function fromJson(mixed $data): static { + return new self( + $data["textures"]["default"] ?? "customies:missing_texture", + $data["textures"]["dyed"] ?? "", + $data["textures"]["icon_trim"] ?? "", + $data["textures"]["bundle_open_back"] ?? "", + $data["textures"]["bundle_open_front"] ?? "" + ); + } } \ No newline at end of file diff --git a/src/customiesdevs/customies/item/component/InteractButtonComponent.php b/src/customiesdevs/customies/item/component/InteractButtonComponent.php index 1aa6163b..a835577c 100644 --- a/src/customiesdevs/customies/item/component/InteractButtonComponent.php +++ b/src/customiesdevs/customies/item/component/InteractButtonComponent.php @@ -29,4 +29,8 @@ public function getValue(): array { "requires_interact" => 1 ]; } + + public static function fromJson(mixed $data): static { + return new self($data ?? "action.interact.use"); + } } \ No newline at end of file diff --git a/src/customiesdevs/customies/item/component/ItemComponent.php b/src/customiesdevs/customies/item/component/ItemComponent.php index f76a408c..d12cac5f 100644 --- a/src/customiesdevs/customies/item/component/ItemComponent.php +++ b/src/customiesdevs/customies/item/component/ItemComponent.php @@ -5,7 +5,23 @@ interface ItemComponent { + /** + * The component identifier, e.g. "minecraft:display_name" + * @return string + */ public function getName(): string; + /** + * The value of this component, as it would appear in an item JSON. + * @return mixed + */ public function getValue(): mixed; + + /** + * Create a component instance from decoded JSON (item definition) data. + * Implementations should be tolerant of missing keys and apply sensible defaults. + * @param mixed $data The raw value found under the component identifier in an item JSON. + * @return static + */ + public static function fromJson(mixed $data): static; } \ No newline at end of file diff --git a/src/customiesdevs/customies/item/component/LiquidClippedComponent.php b/src/customiesdevs/customies/item/component/LiquidClippedComponent.php index dcc4389d..b5e62d88 100644 --- a/src/customiesdevs/customies/item/component/LiquidClippedComponent.php +++ b/src/customiesdevs/customies/item/component/LiquidClippedComponent.php @@ -24,4 +24,8 @@ public function getValue(): array { "value" => $this->liquidClipped ]; } + + public static function fromJson(mixed $data): static { + return new self($data ?? true); + } } \ No newline at end of file diff --git a/src/customiesdevs/customies/item/component/MaxStackSizeComponent.php b/src/customiesdevs/customies/item/component/MaxStackSizeComponent.php index d5480f5b..f5affde6 100644 --- a/src/customiesdevs/customies/item/component/MaxStackSizeComponent.php +++ b/src/customiesdevs/customies/item/component/MaxStackSizeComponent.php @@ -24,4 +24,8 @@ public function getValue(): array { "value" => $this->maxStackSize ]; } + + public static function fromJson(mixed $data): static { + return new self($data ?? 64); + } } \ No newline at end of file diff --git a/src/customiesdevs/customies/item/component/ProjectileComponent.php b/src/customiesdevs/customies/item/component/ProjectileComponent.php index 742ce367..140cc41f 100644 --- a/src/customiesdevs/customies/item/component/ProjectileComponent.php +++ b/src/customiesdevs/customies/item/component/ProjectileComponent.php @@ -30,4 +30,8 @@ public function getValue(): array { "projectile_entity" => $this->projectileEntity ]; } + + public static function fromJson(mixed $data): static { + return new self($data["minimum_critical_power"] ?? 0.0, $data["projectile_entity"] ?? ""); + } } \ No newline at end of file diff --git a/src/customiesdevs/customies/item/component/RarityComponent.php b/src/customiesdevs/customies/item/component/RarityComponent.php index 0f1eb19a..b6aa7132 100644 --- a/src/customiesdevs/customies/item/component/RarityComponent.php +++ b/src/customiesdevs/customies/item/component/RarityComponent.php @@ -30,4 +30,8 @@ public function getValue(): array { "value" => $this->rarity ]; } + + public static function fromJson(mixed $data): static { + return new self($data ?? self::COMMON); + } } \ No newline at end of file diff --git a/src/customiesdevs/customies/item/component/RecordComponent.php b/src/customiesdevs/customies/item/component/RecordComponent.php index eca34eed..ad101c4d 100644 --- a/src/customiesdevs/customies/item/component/RecordComponent.php +++ b/src/customiesdevs/customies/item/component/RecordComponent.php @@ -32,4 +32,8 @@ public function getValue(): array { "sound_event" => $this->soundEvent ]; } + + public static function fromJson(mixed $data): static { + return new self($data["comparator_signal"] ?? 1, $data["duration"] ?? 0.0, $data["sound_event"] ?? ""); + } } \ No newline at end of file diff --git a/src/customiesdevs/customies/item/component/RepairableComponent.php b/src/customiesdevs/customies/item/component/RepairableComponent.php index d0e44e6a..06e987bf 100644 --- a/src/customiesdevs/customies/item/component/RepairableComponent.php +++ b/src/customiesdevs/customies/item/component/RepairableComponent.php @@ -28,4 +28,14 @@ public function getValue(): array { "repair_items" => $repairItems ]; } + + public static function fromJson(mixed $data): static { + $repairItems = []; + if(is_array($data["repair_items"] ?? null)) { + foreach($data["repair_items"] as $repairItem) { + $repairItems[] = RepairItems::fromArray($repairItem); + } + } + return new self($repairItems); + } } \ No newline at end of file diff --git a/src/customiesdevs/customies/item/component/ShooterComponent.php b/src/customiesdevs/customies/item/component/ShooterComponent.php index 8043c7dd..cc0f9a57 100644 --- a/src/customiesdevs/customies/item/component/ShooterComponent.php +++ b/src/customiesdevs/customies/item/component/ShooterComponent.php @@ -56,4 +56,16 @@ public function getValue(): array { "scale_power_by_draw_duration" => $this->scalePowerByDrawDuration ]; } + + public static function fromJson(mixed $data): static { + return new self( + $data["ammunition"][0]["item"] ?? "", + $data["ammunition"][0]["use_offhand"] ?? false, + $data["ammunition"][0]["search_inventory"] ?? false, + $data["ammunition"][0]["use_in_creative"] ?? false, + $data["charge_on_draw"] ?? false, + $data["max_draw_duration"] ?? 0.0, + $data["scale_power_by_draw_duration"] ?? false + ); + } } \ No newline at end of file diff --git a/src/customiesdevs/customies/item/component/ShouldDespawnComponent.php b/src/customiesdevs/customies/item/component/ShouldDespawnComponent.php index 9c06cc6f..31c71e3e 100644 --- a/src/customiesdevs/customies/item/component/ShouldDespawnComponent.php +++ b/src/customiesdevs/customies/item/component/ShouldDespawnComponent.php @@ -24,4 +24,8 @@ public function getValue(): array { "value" => $this->shouldDespawn ]; } + + public static function fromJson(mixed $data): static { + return new self($data ?? true); + } } \ No newline at end of file diff --git a/src/customiesdevs/customies/item/component/StackedByDataComponent.php b/src/customiesdevs/customies/item/component/StackedByDataComponent.php index f0b9997c..cf3cb8eb 100644 --- a/src/customiesdevs/customies/item/component/StackedByDataComponent.php +++ b/src/customiesdevs/customies/item/component/StackedByDataComponent.php @@ -25,4 +25,8 @@ public function getValue(): array { "value" => $this->stackedByData ]; } + + public static function fromJson(mixed $data): static { + return new self($data ?? true); + } } \ No newline at end of file diff --git a/src/customiesdevs/customies/item/component/StorageItemComponent.php b/src/customiesdevs/customies/item/component/StorageItemComponent.php index 6a921d9e..281e3e3d 100644 --- a/src/customiesdevs/customies/item/component/StorageItemComponent.php +++ b/src/customiesdevs/customies/item/component/StorageItemComponent.php @@ -52,4 +52,15 @@ public function getValue(): array { "weight_in_storage_item" => $this->weightInStorageItem ]; } + + public static function fromJson(mixed $data): static { + return new self( + $data["allow_nested_storage_items"] ?? true, + $data["allowed_items"] ?? [], + $data["banned_items"] ?? [], + $data["max_slots"] ?? 64, + $data["max_weight_limit"] ?? 64, + $data["weight_in_storage_item"] ?? 4 + ); + } } \ No newline at end of file diff --git a/src/customiesdevs/customies/item/component/StorageWeightLimitComponent.php b/src/customiesdevs/customies/item/component/StorageWeightLimitComponent.php index 5b0555bf..13a151a7 100644 --- a/src/customiesdevs/customies/item/component/StorageWeightLimitComponent.php +++ b/src/customiesdevs/customies/item/component/StorageWeightLimitComponent.php @@ -24,4 +24,8 @@ public function getValue(): array { "max_weight_limit" => $this->maxWeightLimit ]; } + + public static function fromJson(mixed $data): static { + return new self($data["max_weight_limit"] ?? 64); + } } \ No newline at end of file diff --git a/src/customiesdevs/customies/item/component/StorageWeightModifierComponent.php b/src/customiesdevs/customies/item/component/StorageWeightModifierComponent.php index 48d0b4dd..501d0a85 100644 --- a/src/customiesdevs/customies/item/component/StorageWeightModifierComponent.php +++ b/src/customiesdevs/customies/item/component/StorageWeightModifierComponent.php @@ -24,4 +24,8 @@ public function getValue(): array { "weight_in_storage_item" => $this->weightInStorageItem ]; } + + public static function fromJson(mixed $data): static { + return new self($data["weight_in_storage_item"] ?? 4); + } } \ No newline at end of file diff --git a/src/customiesdevs/customies/item/component/SwingDurationComponent.php b/src/customiesdevs/customies/item/component/SwingDurationComponent.php index 7553ffe9..1d0b8700 100644 --- a/src/customiesdevs/customies/item/component/SwingDurationComponent.php +++ b/src/customiesdevs/customies/item/component/SwingDurationComponent.php @@ -24,4 +24,8 @@ public function getValue(): array { "value" => $this->swingDuration ]; } + + public static function fromJson(mixed $data): static { + return new self($data ?? 0.3); + } } \ No newline at end of file diff --git a/src/customiesdevs/customies/item/component/TagsComponent.php b/src/customiesdevs/customies/item/component/TagsComponent.php index aa3dc0cb..520a5c6a 100644 --- a/src/customiesdevs/customies/item/component/TagsComponent.php +++ b/src/customiesdevs/customies/item/component/TagsComponent.php @@ -24,4 +24,8 @@ public function getValue(): array { "tags" => $this->tags ]; } + + public static function fromJson(mixed $data): static { + return new self(is_array($data["tags"] ?? null) ? $data["tags"] : []); + } } \ No newline at end of file diff --git a/src/customiesdevs/customies/item/component/ThrowableComponent.php b/src/customiesdevs/customies/item/component/ThrowableComponent.php index 2524988a..bb668395 100644 --- a/src/customiesdevs/customies/item/component/ThrowableComponent.php +++ b/src/customiesdevs/customies/item/component/ThrowableComponent.php @@ -51,4 +51,15 @@ public function getValue(): array { "scale_power_by_draw_duration" => $this->scalePowerByDrawDuration ]; } + + public static function fromJson(mixed $data): static { + return new self( + $data["do_swing_animation"] ?? false, + $data["launch_power_scale"] ?? 1.0, + $data["max_draw_duration"] ?? 0.0, + $data["max_launch_power"] ?? 1.0, + $data["min_draw_duration"] ?? 0.0, + $data["scale_power_by_draw_duration"] ?? false + ); + } } \ No newline at end of file diff --git a/src/customiesdevs/customies/item/component/UseAnimationComponent.php b/src/customiesdevs/customies/item/component/UseAnimationComponent.php index 6b94b733..b3670e2a 100644 --- a/src/customiesdevs/customies/item/component/UseAnimationComponent.php +++ b/src/customiesdevs/customies/item/component/UseAnimationComponent.php @@ -35,4 +35,8 @@ public function getValue(): array { "value" => $this->animation ]; } + + public static function fromJson(mixed $data): static { + return new self($data ?? self::ANIMATION_NONE); + } } \ No newline at end of file diff --git a/src/customiesdevs/customies/item/component/UseModifiersComponent.php b/src/customiesdevs/customies/item/component/UseModifiersComponent.php index 84c61fbd..039b3b85 100644 --- a/src/customiesdevs/customies/item/component/UseModifiersComponent.php +++ b/src/customiesdevs/customies/item/component/UseModifiersComponent.php @@ -28,4 +28,8 @@ public function getValue(): array { "use_duration" => $this->useDuration ]; } + + public static function fromJson(mixed $data): static { + return new self($data["movement_modifier"] ?? 1.0, $data["use_duration"] ?? 0); + } } \ No newline at end of file diff --git a/src/customiesdevs/customies/item/component/VanillaItemComponents.php b/src/customiesdevs/customies/item/component/VanillaItemComponents.php new file mode 100644 index 00000000..92d10c0e --- /dev/null +++ b/src/customiesdevs/customies/item/component/VanillaItemComponents.php @@ -0,0 +1,189 @@ + FQCN implementing ItemComponent. + * @var array> + */ + private static array $classMap = [ + self::ALLOW_OFF_HAND => AllowOffHandComponent::class, + self::BLOCK_PLACER => BlockPlacerComponent::class, + self::BUNDLE_INTERACTION => BundleInteractionComponent::class, + self::CAN_DESTROY_IN_CREATIVE => CanDestroyInCreativeComponent::class, + self::COMPOSTABLE => CompostableComponent::class, + self::COOLDOWN => CooldownComponent::class, + self::DAMAGE_ABSORPTION => DamageAbsorptionComponent::class, + self::DAMAGE => DamageComponent::class, + self::DIGGER => DiggerComponent::class, + self::DISPLAY_NAME => DisplayNameComponent::class, + self::DURABILITY => DurabilityComponent::class, + self::DURABILITY_SENSOR => DurabilitySensorComponent::class, + self::DYEABLE => DyeableComponent::class, + self::ENCHANTABLE => EnchantableComponent::class, + self::FIRE_RESISTANT => FireResistantComponent::class, + self::FOOD => FoodComponent::class, + self::FUEL => FuelComponent::class, + self::GLINT => GlintComponent::class, + self::HAND_EQUIPPED => HandEquippedComponent::class, + self::HOVER_TEXT_COLOR => HoverTextColorComponent::class, + self::ICON => IconComponent::class, + self::INTERACT_BUTTON => InteractButtonComponent::class, + self::LIQUID_CLIPPED => LiquidClippedComponent::class, + self::MAX_STACK_SIZE => MaxStackSizeComponent::class, + self::PROJECTILE => ProjectileComponent::class, + self::RARITY => RarityComponent::class, + self::RECORD => RecordComponent::class, + self::REPAIRABLE => RepairableComponent::class, + self::SHOOTER => ShooterComponent::class, + self::SHOULD_DESPAWN => ShouldDespawnComponent::class, + self::STACKED_BY_DATA => StackedByDataComponent::class, + self::STORAGE_ITEM => StorageItemComponent::class, + self::STORAGE_WEIGHT_LIMIT => StorageWeightLimitComponent::class, + self::STORAGE_WEIGHT_MODIFIER => StorageWeightModifierComponent::class, + self::SWING_DURATION => SwingDurationComponent::class, + self::TAGS => TagsComponent::class, + self::THROWABLE => ThrowableComponent::class, + self::USE_ANIMATION => UseAnimationComponent::class, + self::USE_MODIFIERS => UseModifiersComponent::class, + self::WEARABLE => WearableComponent::class, + ]; + + private function __construct() {} + + /** + * Get all component identifiers. + * @return string[] + */ + public static function getAll(): array { + return self::ALL; + } + + /** + * Check if the given identifier is a known component. + * @param string $identifier Component identifier + * @return bool True if the identifier is known, false otherwise. + */ + public static function isValid(string $identifier): bool { + return in_array($identifier, self::ALL, true); + } + + /** + * Returns the component class for the identifier if registered, null otherwise. + * @param string $identifier Component identifier + * @return class-string|null + */ + public static function getClass(string $identifier): ?string { + return self::$classMap[$identifier] ?? null; + } + + /** + * Determine if a component class exists for the identifier. + * @param string $identifier Component identifier + * @return bool True if a class is registered for the identifier, false otherwise. + */ + public static function hasClass(string $identifier): bool { + return isset(self::$classMap[$identifier]); + } + + /** + * Register or override a component class at runtime. + * @param string $identifier Component identifier (must be one of the known constants) + * @param class-string $fqcn Fully qualified class name implementing ItemComponent + */ + public static function registerClass(string $identifier, string $fqcn): void { + if (!self::isValid($identifier)) { + throw new \InvalidArgumentException("Unknown component id: $identifier"); + } + if (!is_subclass_of($fqcn, ItemComponent::class)) { + throw new \InvalidArgumentException("Class $fqcn must implement " . ItemComponent::class); + } + self::$classMap[$identifier] = $fqcn; + } +} \ No newline at end of file diff --git a/src/customiesdevs/customies/item/component/WearableComponent.php b/src/customiesdevs/customies/item/component/WearableComponent.php index b3848373..33c203c1 100644 --- a/src/customiesdevs/customies/item/component/WearableComponent.php +++ b/src/customiesdevs/customies/item/component/WearableComponent.php @@ -47,4 +47,12 @@ public function getValue(): array { "hides_player_location" => $this->hidePlayerLocation ]; } + + public static function fromJson(mixed $data): static { + return new self( + $data["slot"] ?? self::SLOT_NONE, + $data["protection"] ?? 0, + $data["hides_player_location"] ?? false + ); + } } \ No newline at end of file diff --git a/src/customiesdevs/customies/item/properties/RepairItems.php b/src/customiesdevs/customies/item/properties/RepairItems.php index 233ee494..cba8fbc0 100644 --- a/src/customiesdevs/customies/item/properties/RepairItems.php +++ b/src/customiesdevs/customies/item/properties/RepairItems.php @@ -26,4 +26,14 @@ public function toArray(): array { ]; } + public static function fromArray(array $data): self { + $items = []; + if(is_array($data["items"] ?? null)) { + foreach($data["items"] as $item) { + $items[] = $item["name"] ?? ""; + } + } + return new self($items, $data["repair_amount"] ?? 0); + } + } \ No newline at end of file From 18ea86c0efbfa92f2cf587e485207daaa291aef2 Mon Sep 17 00:00:00 2001 From: JeanTKG <102908437+jeantkg@users.noreply.github.com> Date: Sun, 5 Oct 2025 01:59:33 +0300 Subject: [PATCH 05/51] Remove unused custom slime block and allowoffhand item JSON files; add custom example block and item JSON files --- ...ime_block.json => custom_example_block.json} | 17 ++++++++--------- ...lowoffhand.json => custom_example_item.json} | 3 +-- .../item/component/BlockPlacerComponent.php | 4 +++- 3 files changed, 12 insertions(+), 12 deletions(-) rename resources/behavior/blocks/{custom_slime_block.json => custom_example_block.json} (61%) rename resources/behavior/items/{allowoffhand.json => custom_example_item.json} (93%) diff --git a/resources/behavior/blocks/custom_slime_block.json b/resources/behavior/blocks/custom_example_block.json similarity index 61% rename from resources/behavior/blocks/custom_slime_block.json rename to resources/behavior/blocks/custom_example_block.json index e249618f..a9219d56 100644 --- a/resources/behavior/blocks/custom_slime_block.json +++ b/resources/behavior/blocks/custom_example_block.json @@ -2,7 +2,7 @@ "format_version": "1.21.100", "minecraft:block": { "description": { - "identifier": "nk:custom_slime_block", + "identifier": "customies:custom_example_block", "menu_category": { "category": "items" } @@ -11,22 +11,21 @@ "minecraft:geometry": "minecraft:geometry.full_block", "minecraft:material_instances": { "*": { - "texture": "slime_block", - "render_method": "blend" + "texture": "stone", + "render_method": "blend", + "isotropic": true } }, + "minecraft:display_name": "Custom Example Block", "minecraft:destructible_by_mining": { "seconds_to_destroy": 0.033 }, + "minecraft:friction": 0.1, "minecraft:destructible_by_explosion": { "explosion_resistance": 0 }, - "minecraft:light_dampening": 0, - "minecraft:movable": { - "movement_type": "push_pull", - "sticky": "same" - }, - "minecraft:map_color": "#7fb238" + "minecraft:light_dampening": 15, + "minecraft:light_emission": 10 } } } \ No newline at end of file diff --git a/resources/behavior/items/allowoffhand.json b/resources/behavior/items/custom_example_item.json similarity index 93% rename from resources/behavior/items/allowoffhand.json rename to resources/behavior/items/custom_example_item.json index 010af6ac..28651856 100644 --- a/resources/behavior/items/allowoffhand.json +++ b/resources/behavior/items/custom_example_item.json @@ -2,7 +2,7 @@ "format_version": "1.21.100", "minecraft:item": { "description": { - "identifier": "nk:allowoffhand", + "identifier": "customies:custom_example_item", "menu_category": { "category": "items" } @@ -25,7 +25,6 @@ "minecraft:fuel": { "duration": 3.0 }, - "minecraft:glint": true, "minecraft:hand_equipped": true, "minecraft:hover_text_color": "minecoin_gold", "minecraft:interact_button": "Use This Custom Item", diff --git a/src/customiesdevs/customies/item/component/BlockPlacerComponent.php b/src/customiesdevs/customies/item/component/BlockPlacerComponent.php index 65c13f35..93e6a1a4 100644 --- a/src/customiesdevs/customies/item/component/BlockPlacerComponent.php +++ b/src/customiesdevs/customies/item/component/BlockPlacerComponent.php @@ -62,6 +62,8 @@ public static function fromJson(mixed $data): static { $useOn[] = $blockId; } } - return new self($block ?? VanillaBlocks::AIR(), $data["replace_block_item"] ?? false)->useOn(...$useOn); + $blockPlacer = new self($block ?? VanillaBlocks::AIR(), $data["replace_block_item"] ?? false); + $blockPlacer->useOn(...$useOn); + return $blockPlacer; } } \ No newline at end of file From 517b53d9b15092dadf8b1760d283d2efc5c2ece8 Mon Sep 17 00:00:00 2001 From: JeanTKG <102908437+jeantkg@users.noreply.github.com> Date: Mon, 6 Oct 2025 20:58:14 +0300 Subject: [PATCH 06/51] Refactor item and block component traits; remove unused ItemComponentsTrait and BlockComponentsTrait files; implement new ItemProperties class for better component management --- .../customies/block/BlockComponentsTrait.php | 67 --------- .../customies/block/CustomiesBlock.php | 1 + .../customies/block/CustomiesBlockFactory.php | 6 +- .../component/CraftingTableComponent.php | 3 +- .../component/LightEmissionComponent.php | 2 +- .../block/traits/BlockComponentsTrait.php | 26 ++++ .../customies/block/traits/DefaultTrait.php | 11 ++ .../customies/item/CustomiesItem.php | 2 +- .../customies/item/CustomiesItemFactory.php | 106 ++------------ .../customies/item/ItemComponentsTrait.php | 104 ------------- .../item/properties/ItemProperties.php | 137 ++++++++++++++++++ .../customies/item/traits/DefaultTrait.php | 12 ++ .../item/traits/ItemComponentsTrait.php | 27 ++++ 13 files changed, 234 insertions(+), 270 deletions(-) delete mode 100644 src/customiesdevs/customies/block/BlockComponentsTrait.php create mode 100644 src/customiesdevs/customies/block/traits/BlockComponentsTrait.php create mode 100644 src/customiesdevs/customies/block/traits/DefaultTrait.php delete mode 100644 src/customiesdevs/customies/item/ItemComponentsTrait.php create mode 100644 src/customiesdevs/customies/item/properties/ItemProperties.php create mode 100644 src/customiesdevs/customies/item/traits/DefaultTrait.php create mode 100644 src/customiesdevs/customies/item/traits/ItemComponentsTrait.php diff --git a/src/customiesdevs/customies/block/BlockComponentsTrait.php b/src/customiesdevs/customies/block/BlockComponentsTrait.php deleted file mode 100644 index 4228d388..00000000 --- a/src/customiesdevs/customies/block/BlockComponentsTrait.php +++ /dev/null @@ -1,67 +0,0 @@ -components[$component->getName()] = $component; - } - - public function hasComponent(string $name): bool { - return isset($this->components[$name]); - } - - /** - * @return BlockComponent[] - */ - public function getComponents(): array { - return $this->components; - } - - /** - * Initialises a block's components with default values inferred from existing properties. - * @todo Work on more default values depending on different pm classes similar to items - * @param string $texture Texture name for the material. - * @param bool $useGeometry Check if geometry component should be used, default is set to `true` - */ - protected function initComponent(string $texture, bool $useGeometry = true): void { - $this->addComponent(new DestructibleByExplosionComponent()); - $this->addComponent(new DestructibleByMiningComponent($this->getBreakInfo()->getHardness())); - $this->addComponent(new LightEmissionComponent($this->getLightLevel())); - $this->addComponent(new LightDampeningComponent($this->getLightFilter())); - $this->addComponent(new FrictionComponent($this->getFrictionFactor())); - if ($useGeometry){ - $this->addComponent(new GeometryComponent()); - } - $this->addComponent(new SelectionBoxComponent()); - if($this->hasEntityCollision()){ - $this->addComponent(new CollisionBoxComponent()); - } - if($this->getFlammability() > 0){ - $this->addComponent(new FlammableComponent($this->getFlameEncouragement())); - } - if($this->getName() !== "Unknown") { - $this->addComponent(new DisplayNameComponent($this->getName())); - } - $this->addComponent(new MaterialInstancesComponent([new Material(Material::TARGET_ALL, $texture, RenderMethod::OPAQUE)])); - } -} \ No newline at end of file diff --git a/src/customiesdevs/customies/block/CustomiesBlock.php b/src/customiesdevs/customies/block/CustomiesBlock.php index 520f9bec..55b37fe8 100644 --- a/src/customiesdevs/customies/block/CustomiesBlock.php +++ b/src/customiesdevs/customies/block/CustomiesBlock.php @@ -3,6 +3,7 @@ namespace customiesdevs\customies\block; use customiesdevs\customies\block\component\VanillaBlockComponents; +use customiesdevs\customies\block\traits\BlockComponentsTrait; use pocketmine\block\Block; use pocketmine\block\BlockBreakInfo; use pocketmine\block\BlockIdentifier; diff --git a/src/customiesdevs/customies/block/CustomiesBlockFactory.php b/src/customiesdevs/customies/block/CustomiesBlockFactory.php index bb0923c0..d67356c7 100644 --- a/src/customiesdevs/customies/block/CustomiesBlockFactory.php +++ b/src/customiesdevs/customies/block/CustomiesBlockFactory.php @@ -219,8 +219,7 @@ public function registerBlock(Closure $blockFunc, string $identifier, ?CreativeI /** @var CompoundTag $root */ $root = $entry->getStates()->getRoot(); $root->setTag("vanilla_block_data", CompoundTag::create() - ->setInt("block_id", 10000 + $i) - ->setString("material", "dirt")); + ->setInt("block_id", 10000 + $i)); $this->blockPaletteEntries[$i] = new BlockPaletteEntry($entry->getName(), new CacheableNbt($root)); } } @@ -251,8 +250,7 @@ private function createBlockNBT(Block $block, ?CreativeInventoryInfo $creativeIn } $propertiesTag ->setTag("components", $components) - ->setInt("molangVersion", 12); - \var_dump($propertiesTag->__toString()); + ->setInt("molangVersion", 13); return $propertiesTag; } return CompoundTag::create(); diff --git a/src/customiesdevs/customies/block/component/CraftingTableComponent.php b/src/customiesdevs/customies/block/component/CraftingTableComponent.php index b3783fbd..d9d5218f 100644 --- a/src/customiesdevs/customies/block/component/CraftingTableComponent.php +++ b/src/customiesdevs/customies/block/component/CraftingTableComponent.php @@ -24,7 +24,8 @@ public function getName(): string { public function getValue(): array { return [ "crafting_tags" => $this->craftingTags, - "table_name" => $this->tableName + "table_name" => $this->tableName, + "grid_size" => 3 ]; } diff --git a/src/customiesdevs/customies/block/component/LightEmissionComponent.php b/src/customiesdevs/customies/block/component/LightEmissionComponent.php index 7e3d45f5..022dd9cf 100644 --- a/src/customiesdevs/customies/block/component/LightEmissionComponent.php +++ b/src/customiesdevs/customies/block/component/LightEmissionComponent.php @@ -20,7 +20,7 @@ public function getName(): string { public function getValue(): array { return [ - "lightLevel" => $this->emission + "emission" => $this->emission ]; } diff --git a/src/customiesdevs/customies/block/traits/BlockComponentsTrait.php b/src/customiesdevs/customies/block/traits/BlockComponentsTrait.php new file mode 100644 index 00000000..3099d754 --- /dev/null +++ b/src/customiesdevs/customies/block/traits/BlockComponentsTrait.php @@ -0,0 +1,26 @@ +components[$component->getName()] = $component; + } + + public function hasComponent(string $name): bool { + return isset($this->components[$name]); + } + + /** + * @return BlockComponent[] + */ + public function getComponents(): array { + return $this->components; + } +} \ No newline at end of file diff --git a/src/customiesdevs/customies/block/traits/DefaultTrait.php b/src/customiesdevs/customies/block/traits/DefaultTrait.php new file mode 100644 index 00000000..e9818690 --- /dev/null +++ b/src/customiesdevs/customies/block/traits/DefaultTrait.php @@ -0,0 +1,11 @@ +setTag("allow_off_hand", NBT::getTagType(false)); - $properties->setTag("can_destroy_in_creative", NBT::getTagType(true)); - $properties->setTag("damage", NBT::getTagType(0)); - $properties->setTag("enchantable_slot", NBT::getTagType("none")); - $properties->setTag("enchantable_value", NBT::getTagType(0)); - $properties->setTag("foil", NBT::getTagType(false)); - $properties->setTag("frame_count", NBT::getTagType(1)); - $properties->setTag("hand_equipped", NBT::getTagType(false)); - $properties->setTag("liquid_clipped", NBT::getTagType(false)); - $properties->setTag("max_stack_size", NBT::getTagType(64)); - $properties->setTag("mining_speed", NBT::getTagType(1)); - $properties->setTag("should_despawn", NBT::getTagType(true)); - $properties->setTag("stacked_by_data", NBT::getTagType(false)); - $properties->setTag("use_animation", NBT::getTagType(0)); - $properties->setTag("use_duration", NBT::getTagType(0)); - - foreach($item->getComponents() as $component) { - $tag = NBT::getTagType($component->getValue()); - if($tag === null) { - throw new RuntimeException("Failed to get tag type for component " . $component->getName()); - } - - // Override defaults with component-specific values - switch($component->getName()) { - case "minecraft:allow_off_hand": - $properties->setTag("allow_off_hand", NBT::getTagType($component->getValue()["value"])); - break; - case "minecraft:can_destroy_in_creative": - $properties->setTag("can_destroy_in_creative", NBT::getTagType($component->getValue()["value"])); - break; - case "minecraft:damage": - $properties->setTag("damage", NBT::getTagType($component->getValue()["value"])); - break; - case "minecraft:enchantable": - $properties->setTag("enchantable_slot", NBT::getTagType($component->getValue()["slot"])); - $properties->setTag("enchantable_value", NBT::getTagType($component->getValue()["value"])); - break; - case "minecraft:glint": - $properties->setTag("foil", NBT::getTagType($component->getValue()["value"])); - break; - case "minecraft:hand_equipped": - $properties->setTag("hand_equipped", NBT::getTagType($component->getValue()["value"])); - break; - case "minecraft:hover_text_color": - $properties->setTag("hover_text_color", NBT::getTagType($component->getValue()["value"])); - break; - case "minecraft:liquid_clipped": - $properties->setTag("liquid_clipped", NBT::getTagType($component->getValue()["value"])); - break; - case "minecraft:max_stack_size": - $properties->setTag("max_stack_size", NBT::getTagType($component->getValue()["value"])); - break; - case "minecraft:icon": - $properties->setTag("minecraft:icon", $tag); - break; - case "minecraft:should_despawn": - $properties->setTag("should_despawn", NBT::getTagType($component->getValue()["value"])); - break; - case "minecraft:stacked_by_data": - $properties->setTag("stacked_by_data", NBT::getTagType($component->getValue()["value"])); - break; - case "minecraft:use_animation": - $properties->setTag("use_animation", NBT::getTagType($component->getValue()["value"])); - break; - case "minecraft:use_modifiers": - $properties->setTag("use_duration", NBT::getTagType($component->getValue()["use_duration"])); - break; - } - - // Setting the actual component - // The icon component is already set in item_properties, no need to set it again - if($component->getName() !== "minecraft:icon") { - $components->setTag($component->getName(), $tag); - } - } - if($creativeInfo !== null) { - $properties->setTag("creative_category", NBT::getTagType($creativeInfo->getNumericCategory())); - $properties->setTag("creative_group", NBT::getTagType($creativeInfo->getGroup())); - } - $components->setTag("item_properties", $properties); - return CompoundTag::create() - ->setTag("components", $components) - ->setInt("id", $itemId) - ->setString("name", $identifier); + if (!($item instanceof ItemComponents)) { + return CompoundTag::create(); } - return CompoundTag::create(); + + $builder = new ItemProperties(); + $builder->applyComponents($item->getComponents()); + $builder->setCreativeInfo($creativeInfo); + + $components = $builder->buildComponentsTag(); + + return CompoundTag::create() + ->setTag('components', $components) + ->setInt('id', $itemId) + ->setString('name', $identifier); } /** diff --git a/src/customiesdevs/customies/item/ItemComponentsTrait.php b/src/customiesdevs/customies/item/ItemComponentsTrait.php deleted file mode 100644 index 2dbacfe0..00000000 --- a/src/customiesdevs/customies/item/ItemComponentsTrait.php +++ /dev/null @@ -1,104 +0,0 @@ -components[$component->getName()] = $component; - } - - public function hasComponent(string $name): bool { - return isset($this->components[$name]); - } - - /** - * @return ItemComponent[] - */ - public function getComponents(): array { - return $this->components; - } - - /** - * Initializes the item with default components that are required for the item to function correctly. - */ - protected function initComponent(string $texture): void { - $this->addComponent(new IconComponent($texture)); - $this->addComponent(new CanDestroyInCreativeComponent()); - $this->addComponent(new MaxStackSizeComponent($this->getMaxStackSize())); - - if($this instanceof Armor) { - $slot = match ($this->getArmorSlot()) { - ArmorInventory::SLOT_HEAD => WearableComponent::SLOT_ARMOR_HEAD, - ArmorInventory::SLOT_CHEST => WearableComponent::SLOT_ARMOR_CHEST, - ArmorInventory::SLOT_LEGS => WearableComponent::SLOT_ARMOR_LEGS, - ArmorInventory::SLOT_FEET => WearableComponent::SLOT_ARMOR_FEET, - default => WearableComponent::SLOT_ARMOR - }; - $this->addComponent(new WearableComponent($slot, $this->getDefensePoints())); - } - - if($this instanceof Consumable) { - if(($food = $this instanceof Food)) { - $this->addComponent(new FoodComponent(!$this->requiresHunger())); - } - $this->addComponent(new UseAnimationComponent($food ? UseAnimationComponent::ANIMATION_EAT : UseAnimationComponent::ANIMATION_DRINK)); - $this->setUseDuration(20); - } - - if($this instanceof Durable) { - $this->addComponent(new DurabilityComponent($this->getMaxDurability())); - } - - if($this instanceof ProjectileItem) { - $this->addComponent(new ProjectileComponent(1.25, "projectile")); - $this->addComponent(new ThrowableComponent(true)); - } - - if($this->getName() !== "Unknown") { - $this->addComponent(new DisplayNameComponent($this->getName())); - } - - if($this->getFuelTime() > 0) { - $this->addComponent(new FuelComponent($this->getFuelTime())); - } - - if($this->getAttackPoints() > 0) { - $this->addComponent(new DamageComponent($this->getAttackPoints())); - } - - if($this instanceof Tool) { - $this->addComponent(new HandEquippedComponent()); - if ($this instanceof Sword) { - $this->addComponent(new CanDestroyInCreativeComponent(false)); - } - } - } -} diff --git a/src/customiesdevs/customies/item/properties/ItemProperties.php b/src/customiesdevs/customies/item/properties/ItemProperties.php new file mode 100644 index 00000000..3e6e5126 --- /dev/null +++ b/src/customiesdevs/customies/item/properties/ItemProperties.php @@ -0,0 +1,137 @@ + false, + 'can_destroy_in_creative' => true, + 'damage' => 0, + 'enchantable_slot' => 'none', + 'enchantable_value' => 0, + 'foil' => false, + 'frame_count' => 1, + 'hand_equipped' => false, + 'liquid_clipped' => false, + 'max_stack_size' => 64, + 'mining_speed' => 1, + 'should_despawn' => true, + 'stacked_by_data' => false, + 'use_animation' => 0, + 'use_duration' => 0, + ]; + + /** + * Declarative mapping for components that override item_properties + * componentName => [ propertyName => keyInsideComponentValue ] + */ + private const MAPPINGS = [ + 'minecraft:allow_off_hand' => ['allow_off_hand' => 'value'], + 'minecraft:can_destroy_in_creative' => ['can_destroy_in_creative' => 'value'], + 'minecraft:damage' => ['damage' => 'value'], + 'minecraft:enchantable' => ['enchantable_slot' => 'slot', 'enchantable_value' => 'value'], + 'minecraft:glint' => ['foil' => 'value'], + 'minecraft:hand_equipped' => ['hand_equipped' => 'value'], + 'minecraft:hover_text_color' => ['hover_text_color' => 'value'], + 'minecraft:liquid_clipped' => ['liquid_clipped' => 'value'], + 'minecraft:max_stack_size' => ['max_stack_size' => 'value'], + 'minecraft:should_despawn' => ['should_despawn' => 'value'], + 'minecraft:stacked_by_data' => ['stacked_by_data' => 'value'], + 'minecraft:use_animation' => ['use_animation' => 'value'], + 'minecraft:use_modifiers' => ['use_duration' => 'use_duration'], + ]; + + private CompoundTag $properties; + private array $tags; + private CompoundTag $components; + + public function __construct() { + $this->properties = CompoundTag::create(); + $this->tags = []; + $this->components = CompoundTag::create(); + + // Initialize defaults + foreach(self::DEFAULTS as $name => $default) { + $this->properties->setTag($name, NBT::getTagType($default)); + } + } + + /** + * Applies a list of components, updating both item_properties and the components tag. + * The raw component is always added to components (except for minecraft:icon which belongs under item_properties). + * @param ItemComponent[] $components + */ + public function applyComponents(array $components): void { + foreach($components as $component) { + $name = $component->getName(); + $value = $component->getValue(); + + $tag = NBT::getTagType($value); + if($tag === null) { + throw new RuntimeException("Failed to get tag type for component {$name}"); + } + + if($name === 'minecraft:icon') { + // icon is stored under item_properties + $this->properties->setTag('minecraft:icon', $tag); + continue; + } + + if($name === 'minecraft:tags') { + $this->tags = $value["tags"] ?? []; + } + + if(isset(self::MAPPINGS[$name])) { + foreach(self::MAPPINGS[$name] as $prop => $key) { + if(!is_array($value) || !array_key_exists($key, $value)) { + throw new RuntimeException("Missing required key '{$key}' in component {$name}"); + } + $this->properties->setTag($prop, NBT::getTagType($value[$key])); + } + } + + // Always record the raw component (except icon, handled above) + $this->components->setTag($name, $tag); + } + } + + /** + * Sets the creative category and group tags based on the provided CreativeInventoryInfo. + * @param CreativeInventoryInfo|null $creativeInfo The creative inventory info, or null to skip setting + */ + public function setCreativeInfo(?CreativeInventoryInfo $creativeInfo): void { + if($creativeInfo !== null) { + $this->properties->setTag('creative_category', NBT::getTagType($creativeInfo->getNumericCategory())); + $this->properties->setTag('creative_group', NBT::getTagType($creativeInfo->getGroup())); + } + } + + /** + * Build a CompoundTag containing both item_properties and the accumulated component tags. + * @return CompoundTag The combined components tag + */ + public function buildComponentsTag(): CompoundTag { + return CompoundTag::create() + ->setTag('item_properties', $this->properties) + ->setTag('item_tags', NBT::getTagType($this->tags)) + ->merge($this->components); + } +} diff --git a/src/customiesdevs/customies/item/traits/DefaultTrait.php b/src/customiesdevs/customies/item/traits/DefaultTrait.php new file mode 100644 index 00000000..7082c26e --- /dev/null +++ b/src/customiesdevs/customies/item/traits/DefaultTrait.php @@ -0,0 +1,12 @@ +components[$component->getName()] = $component; + } + + public function hasComponent(string $name): bool { + return isset($this->components[$name]); + } + + /** + * @return ItemComponent[] + */ + public function getComponents(): array { + return $this->components; + } +} From 0595dc1c22b59c8f92db2c4f42cef838197b29f4 Mon Sep 17 00:00:00 2001 From: JeanTKG <102908437+jeantkg@users.noreply.github.com> Date: Thu, 16 Oct 2025 20:19:04 +0300 Subject: [PATCH 07/51] feat: Introduce BlockBreakInfoComponent and enhance block component system - Added BlockBreakInfoComponent to manage block breaking properties such as hardness, tool type, tool harvest level, and blast resistance. - Updated CustomiesBlock to utilize BlockBreakInfoComponent for dynamic block properties. - Enhanced BlockComponents interface with getComponent method for better component retrieval. - Modified VanillaBlockComponents to support custom component registration. - Refactored item components to use VanillaItemComponents for consistency. - Improved ItemProperties to handle component mappings and apply them correctly. - Updated various item component classes to return their names from VanillaItemComponents. --- .../behavior/blocks/custom_example_block.json | 7 +- src/customiesdevs/customies/Customies.php | 3 + .../customies/block/BlockComponents.php | 7 ++ .../customies/block/CustomiesBlock.php | 27 ++++-- .../component/LightDampeningComponent.php | 9 +- .../component/LightEmissionComponent.php | 9 +- .../component/VanillaBlockComponents.php | 32 +++++++- .../custom/BlockBreakInfoComponent.php | 82 +++++++++++++++++++ .../block/traits/BlockComponentsTrait.php | 4 + .../customies/item/ItemComponents.php | 7 ++ .../item/component/AllowOffHandComponent.php | 2 +- .../item/component/BlockPlacerComponent.php | 2 +- .../component/BundleInteractionComponent.php | 2 +- .../CanDestroyInCreativeComponent.php | 2 +- .../item/component/CompostableComponent.php | 2 +- .../item/component/CooldownComponent.php | 2 +- .../component/DamageAbsorptionComponent.php | 2 +- .../item/component/DamageComponent.php | 2 +- .../item/component/DiggerComponent.php | 2 +- .../item/component/DisplayNameComponent.php | 4 +- .../item/component/DurabilityComponent.php | 2 +- .../component/DurabilitySensorComponent.php | 2 +- .../item/component/DyeableComponent.php | 2 +- .../item/component/EnchantableComponent.php | 2 +- .../item/component/FireResistantComponent.php | 2 +- .../item/component/FoodComponent.php | 2 +- .../item/component/FuelComponent.php | 2 +- .../item/component/GlintComponent.php | 2 +- .../item/component/HandEquippedComponent.php | 2 +- .../component/HoverTextColorComponent.php | 2 +- .../item/component/IconComponent.php | 2 +- .../component/InteractButtonComponent.php | 2 +- .../item/component/LiquidClippedComponent.php | 2 +- .../item/component/MaxStackSizeComponent.php | 2 +- .../item/component/ProjectileComponent.php | 2 +- .../item/component/RarityComponent.php | 2 +- .../item/component/RecordComponent.php | 2 +- .../item/component/RepairableComponent.php | 2 +- .../item/component/ShooterComponent.php | 2 +- .../item/component/ShouldDespawnComponent.php | 2 +- .../item/component/StackedByDataComponent.php | 2 +- .../item/component/StorageItemComponent.php | 2 +- .../component/StorageWeightLimitComponent.php | 2 +- .../StorageWeightModifierComponent.php | 2 +- .../item/component/SwingDurationComponent.php | 2 +- .../item/component/TagsComponent.php | 2 +- .../item/component/ThrowableComponent.php | 2 +- .../item/component/UseAnimationComponent.php | 2 +- .../item/component/UseModifiersComponent.php | 2 +- .../item/component/VanillaItemComponents.php | 30 ++++++- .../item/component/WearableComponent.php | 2 +- .../item/properties/ItemProperties.php | 32 ++++---- .../item/traits/ItemComponentsTrait.php | 4 + 53 files changed, 262 insertions(+), 73 deletions(-) create mode 100644 src/customiesdevs/customies/block/component/custom/BlockBreakInfoComponent.php diff --git a/resources/behavior/blocks/custom_example_block.json b/resources/behavior/blocks/custom_example_block.json index a9219d56..442aab7a 100644 --- a/resources/behavior/blocks/custom_example_block.json +++ b/resources/behavior/blocks/custom_example_block.json @@ -17,8 +17,13 @@ } }, "minecraft:display_name": "Custom Example Block", + "customies:block_break_info": { + "hardness": 3, + "tool_type": "pickaxe", + "tool_harvest_level": 4 + }, "minecraft:destructible_by_mining": { - "seconds_to_destroy": 0.033 + "seconds_to_destroy": 0.6 }, "minecraft:friction": 0.1, "minecraft:destructible_by_explosion": { diff --git a/src/customiesdevs/customies/Customies.php b/src/customiesdevs/customies/Customies.php index 6720824c..46b7423b 100644 --- a/src/customiesdevs/customies/Customies.php +++ b/src/customiesdevs/customies/Customies.php @@ -4,6 +4,8 @@ namespace customiesdevs\customies; use customiesdevs\customies\block\BlockManager; +use customiesdevs\customies\block\component\custom\BlockBreakInfoComponent; +use customiesdevs\customies\block\component\VanillaBlockComponents; use customiesdevs\customies\block\CustomiesBlockFactory; use customiesdevs\customies\item\ItemManager; use pocketmine\plugin\PluginBase; @@ -15,6 +17,7 @@ final class Customies extends PluginBase { public function onLoad(): void{ self::setInstance($this); + VanillaBlockComponents::registerCustomComponent("customies:block_break_info", BlockBreakInfoComponent::class); } protected function onEnable(): void { diff --git a/src/customiesdevs/customies/block/BlockComponents.php b/src/customiesdevs/customies/block/BlockComponents.php index 8994af03..7bd8beab 100644 --- a/src/customiesdevs/customies/block/BlockComponents.php +++ b/src/customiesdevs/customies/block/BlockComponents.php @@ -21,6 +21,13 @@ public function addComponent(BlockComponent $component): void; */ public function hasComponent(string $name): bool; + /** + * Returns the component with the provided name, or null if it does not exist. + * @param string $name + * @return BlockComponent|null + */ + public function getComponent(string $name): ?BlockComponent; + /** * @return BlockComponent[] */ diff --git a/src/customiesdevs/customies/block/CustomiesBlock.php b/src/customiesdevs/customies/block/CustomiesBlock.php index 55b37fe8..5ff2e485 100644 --- a/src/customiesdevs/customies/block/CustomiesBlock.php +++ b/src/customiesdevs/customies/block/CustomiesBlock.php @@ -2,11 +2,13 @@ namespace customiesdevs\customies\block; +use customiesdevs\customies\block\component\custom\BlockBreakInfoComponent; use customiesdevs\customies\block\component\VanillaBlockComponents; use customiesdevs\customies\block\traits\BlockComponentsTrait; use pocketmine\block\Block; use pocketmine\block\BlockBreakInfo; use pocketmine\block\BlockIdentifier; +use pocketmine\block\BlockToolType; use pocketmine\block\BlockTypeIds; use pocketmine\block\BlockTypeInfo; @@ -14,16 +16,31 @@ class CustomiesBlock extends Block implements BlockComponents { use BlockComponentsTrait; public function __construct(array $components) { - parent::__construct( - new BlockIdentifier(BlockTypeIds::newId()), - "Custom Block", - new BlockTypeInfo(new BlockBreakInfo(1)) - ); foreach ($components as $componentName => $componentData) { $componentClass = VanillaBlockComponents::getClass($componentName) ?? null; if ($componentClass !== null && method_exists($componentClass, 'fromJson')) { $this->addComponent($componentClass::fromJson($componentData)); } + + if($this->hasComponent("customies:block_break_info")) { + $breakInfo = $this->getComponent("customies:block_break_info"); + if($breakInfo instanceof BlockBreakInfoComponent) { + $hardness = $breakInfo->getValue()["hardness"]; + $toolType = $breakInfo->getValue()["tool_type"]; + $toolHarvestLevel = $breakInfo->getValue()["tool_harvest_level"]; + $blastResistance = $breakInfo->getValue()["blast_resistance"]; + } + } } + parent::__construct( + new BlockIdentifier(BlockTypeIds::newId()), + "Custom Block", + new BlockTypeInfo(new BlockBreakInfo( + $hardness ?? 1.0, + $toolType ?? BlockToolType::NONE, + $toolHarvestLevel ?? 0, + $blastResistance ?? null + )) + ); } } \ No newline at end of file diff --git a/src/customiesdevs/customies/block/component/LightDampeningComponent.php b/src/customiesdevs/customies/block/component/LightDampeningComponent.php index 669c9159..e463e461 100644 --- a/src/customiesdevs/customies/block/component/LightDampeningComponent.php +++ b/src/customiesdevs/customies/block/component/LightDampeningComponent.php @@ -2,6 +2,8 @@ namespace customiesdevs\customies\block\component; +use pocketmine\nbt\tag\CompoundTag; + class LightDampeningComponent implements BlockComponent { private int $dampening; @@ -18,10 +20,9 @@ public function getName(): string { return VanillaBlockComponents::LIGHT_DAMPENING; } - public function getValue(): array { - return [ - "lightLevel" => $this->dampening - ]; + public function getValue(): CompoundTag { + return CompoundTag::create() + ->setByte("lightLevel", $this->dampening); } public static function fromJson(mixed $data): static { diff --git a/src/customiesdevs/customies/block/component/LightEmissionComponent.php b/src/customiesdevs/customies/block/component/LightEmissionComponent.php index 022dd9cf..57d99cfe 100644 --- a/src/customiesdevs/customies/block/component/LightEmissionComponent.php +++ b/src/customiesdevs/customies/block/component/LightEmissionComponent.php @@ -2,6 +2,8 @@ namespace customiesdevs\customies\block\component; +use pocketmine\nbt\tag\CompoundTag; + class LightEmissionComponent implements BlockComponent { private int $emission; @@ -18,10 +20,9 @@ public function getName(): string { return VanillaBlockComponents::LIGHT_EMISSION; } - public function getValue(): array { - return [ - "emission" => $this->emission - ]; + public function getValue(): CompoundTag { + return CompoundTag::create() + ->setByte("emission", $this->emission); } public static function fromJson(mixed $data): static { diff --git a/src/customiesdevs/customies/block/component/VanillaBlockComponents.php b/src/customiesdevs/customies/block/component/VanillaBlockComponents.php index 1c631502..d1dd6808 100644 --- a/src/customiesdevs/customies/block/component/VanillaBlockComponents.php +++ b/src/customiesdevs/customies/block/component/VanillaBlockComponents.php @@ -99,7 +99,7 @@ public static function getAll(): array { * @return bool True if the identifier is known, false otherwise. */ public static function isValid(string $identifier): bool { - return in_array($identifier, self::ALL, true); + return in_array($identifier, self::ALL, true) || isset(self::$classMap[$identifier]); } /** @@ -125,7 +125,7 @@ public static function hasClass(string $identifier): bool { * @param string $identifier Component identifier (must be one of the known constants) * @param class-string $fqcn Fully qualified class name implementing BlockComponent */ - public static function registerClass(string $identifier, string $fqcn): void { + public static function registerComponent(string $identifier, string $fqcn): void { if (!self::isValid($identifier)) { throw new \InvalidArgumentException("Unknown component id: $identifier"); } @@ -134,4 +134,32 @@ public static function registerClass(string $identifier, string $fqcn): void { } self::$classMap[$identifier] = $fqcn; } + + /** + * Register a custom (non-vanilla) component identifier to a class at runtime. + * This allows plugins to introduce new components beyond the built-in list. + * + * Rules: + * - Identifier must follow a simple namespaced pattern (e.g. "myplugin:my_component") + * - Identifier must not be one of the known vanilla identifiers (use registerClass() to override those) + * - The class must implement BlockComponent + * + * @param string $identifier Custom component identifier + * @param class-string $fqcn Fully qualified class name implementing BlockComponent + */ + public static function registerCustomComponent(string $identifier, string $fqcn): void { + // Basic identifier format check: namespace:name (lowercase, digits, underscore, dash, dot, slash) + if(!preg_match('/^[a-z0-9_\-\.]+:[a-z0-9_\-\.\/]+$/', $identifier)) { + throw new \InvalidArgumentException("Invalid component identifier format: $identifier"); + } + // Reserve minecraft namespace for vanilla identifiers + if(str_starts_with($identifier, 'minecraft:')) { + throw new \InvalidArgumentException("The namespace 'minecraft' is reserved for vanilla component identifiers. Use registerClass() to override existing ones."); + } + if (!is_subclass_of($fqcn, BlockComponent::class)) { + throw new \InvalidArgumentException("Class $fqcn must implement " . BlockComponent::class); + } + // Register or override existing custom mapping + self::$classMap[$identifier] = $fqcn; + } } \ No newline at end of file diff --git a/src/customiesdevs/customies/block/component/custom/BlockBreakInfoComponent.php b/src/customiesdevs/customies/block/component/custom/BlockBreakInfoComponent.php new file mode 100644 index 00000000..d0304af8 --- /dev/null +++ b/src/customiesdevs/customies/block/component/custom/BlockBreakInfoComponent.php @@ -0,0 +1,82 @@ +hardness = $hardness; + $this->toolType = $toolType; + $this->toolHarvestLevel = $toolHarvestLevel; + $this->blastResistance = $blastResistance ?? $hardness * 5; + } + + public function getName(): string { + return "customies:block_break_info"; + } + + public function getValue(): array { + switch($this->toolType) { + case self::NONE: + $toolType = BlockToolType::NONE; + break; + case self::SWORD: + $toolType = BlockToolType::SWORD; + break; + case self::SHOVEL: + $toolType = BlockToolType::SHOVEL; + break; + case self::PICKAXE: + $toolType = BlockToolType::PICKAXE; + break; + case self::AXE: + $toolType = BlockToolType::AXE; + break; + case self::SHEARS: + $toolType = BlockToolType::SHEARS; + break; + case self::HOE: + $toolType = BlockToolType::HOE; + break; + default: + throw new \InvalidArgumentException("Invalid tool type: " . $this->toolType); + } + + return [ + "hardness" => $this->hardness, + "tool_type" => $toolType, + "tool_harvest_level" => $this->toolHarvestLevel, + "blast_resistance" => $this->blastResistance, + ]; + } + + public static function fromJson(mixed $data): static { + return new self( + $data["hardness"] ?? 1.0, + $data["tool_type"] ?? self::NONE, + $data["tool_harvest_level"] ?? 0, + $data["blast_resistance"] ?? null + ); + } +} \ No newline at end of file diff --git a/src/customiesdevs/customies/block/traits/BlockComponentsTrait.php b/src/customiesdevs/customies/block/traits/BlockComponentsTrait.php index 3099d754..6216842f 100644 --- a/src/customiesdevs/customies/block/traits/BlockComponentsTrait.php +++ b/src/customiesdevs/customies/block/traits/BlockComponentsTrait.php @@ -17,6 +17,10 @@ public function hasComponent(string $name): bool { return isset($this->components[$name]); } + public function getComponent(string $name): ?BlockComponent { + return $this->components[$name] ?? null; + } + /** * @return BlockComponent[] */ diff --git a/src/customiesdevs/customies/item/ItemComponents.php b/src/customiesdevs/customies/item/ItemComponents.php index cb3c60d7..68360c5a 100644 --- a/src/customiesdevs/customies/item/ItemComponents.php +++ b/src/customiesdevs/customies/item/ItemComponents.php @@ -22,6 +22,13 @@ public function addComponent(ItemComponent $component): void; */ public function hasComponent(string $name): bool; + /** + * Returns the component with the provided name, or null if it does not exist. + * @param string $name + * @return ItemComponent|null + */ + public function getComponent(string $name): ?ItemComponent; + /** * Returns the fully-structured CompoundTag that can be sent to a client in the ItemComponentsPacket. * @return ItemComponent[] diff --git a/src/customiesdevs/customies/item/component/AllowOffHandComponent.php b/src/customiesdevs/customies/item/component/AllowOffHandComponent.php index d6c27467..1f08c4a7 100644 --- a/src/customiesdevs/customies/item/component/AllowOffHandComponent.php +++ b/src/customiesdevs/customies/item/component/AllowOffHandComponent.php @@ -16,7 +16,7 @@ public function __construct(bool $offHand = true) { } public function getName(): string { - return "minecraft:allow_off_hand"; + return VanillaItemComponents::ALLOW_OFF_HAND; } public function getValue(): array { diff --git a/src/customiesdevs/customies/item/component/BlockPlacerComponent.php b/src/customiesdevs/customies/item/component/BlockPlacerComponent.php index 93e6a1a4..948135c9 100644 --- a/src/customiesdevs/customies/item/component/BlockPlacerComponent.php +++ b/src/customiesdevs/customies/item/component/BlockPlacerComponent.php @@ -27,7 +27,7 @@ public function __construct(Block $block, bool $replaceBlockItem = false) { } public function getName(): string { - return "minecraft:block_placer"; + return VanillaItemComponents::BLOCK_PLACER; } public function getValue(): array { diff --git a/src/customiesdevs/customies/item/component/BundleInteractionComponent.php b/src/customiesdevs/customies/item/component/BundleInteractionComponent.php index 51ffeabc..59cdb802 100644 --- a/src/customiesdevs/customies/item/component/BundleInteractionComponent.php +++ b/src/customiesdevs/customies/item/component/BundleInteractionComponent.php @@ -17,7 +17,7 @@ public function __construct(int $numViewableSlots = 12) { } public function getName(): string { - return "minecraft:bundle_interaction"; + return VanillaItemComponents::BUNDLE_INTERACTION; } public function getValue(): array { diff --git a/src/customiesdevs/customies/item/component/CanDestroyInCreativeComponent.php b/src/customiesdevs/customies/item/component/CanDestroyInCreativeComponent.php index 95e2f954..da7bc968 100644 --- a/src/customiesdevs/customies/item/component/CanDestroyInCreativeComponent.php +++ b/src/customiesdevs/customies/item/component/CanDestroyInCreativeComponent.php @@ -16,7 +16,7 @@ public function __construct(bool $canDestroyInCreative = true) { } public function getName(): string { - return "minecraft:can_destroy_in_creative"; + return VanillaItemComponents::CAN_DESTROY_IN_CREATIVE; } public function getValue(): array { diff --git a/src/customiesdevs/customies/item/component/CompostableComponent.php b/src/customiesdevs/customies/item/component/CompostableComponent.php index 0123fb96..87de6f07 100644 --- a/src/customiesdevs/customies/item/component/CompostableComponent.php +++ b/src/customiesdevs/customies/item/component/CompostableComponent.php @@ -16,7 +16,7 @@ public function __construct(int $compostingChance) { } public function getName(): string { - return "minecraft:compostable"; + return VanillaItemComponents::COMPOSTABLE; } public function getValue(): array { diff --git a/src/customiesdevs/customies/item/component/CooldownComponent.php b/src/customiesdevs/customies/item/component/CooldownComponent.php index d804e1b9..4d416cd2 100644 --- a/src/customiesdevs/customies/item/component/CooldownComponent.php +++ b/src/customiesdevs/customies/item/component/CooldownComponent.php @@ -25,7 +25,7 @@ public function __construct(string $category, float $duration) { } public function getName(): string { - return "minecraft:cooldown"; + return VanillaItemComponents::COOLDOWN; } public function getValue(): array { diff --git a/src/customiesdevs/customies/item/component/DamageAbsorptionComponent.php b/src/customiesdevs/customies/item/component/DamageAbsorptionComponent.php index 4d77d749..26df5782 100644 --- a/src/customiesdevs/customies/item/component/DamageAbsorptionComponent.php +++ b/src/customiesdevs/customies/item/component/DamageAbsorptionComponent.php @@ -19,7 +19,7 @@ public function __construct(array $absorbableCauses) { } public function getName(): string { - return "minecraft:damage_absorption"; + return VanillaItemComponents::DAMAGE_ABSORPTION; } public function getValue(): array { diff --git a/src/customiesdevs/customies/item/component/DamageComponent.php b/src/customiesdevs/customies/item/component/DamageComponent.php index c1941c26..f07099d1 100644 --- a/src/customiesdevs/customies/item/component/DamageComponent.php +++ b/src/customiesdevs/customies/item/component/DamageComponent.php @@ -17,7 +17,7 @@ public function __construct(int $damage) { } public function getName(): string { - return "minecraft:damage"; + return VanillaItemComponents::DAMAGE; } public function getValue(): array { diff --git a/src/customiesdevs/customies/item/component/DiggerComponent.php b/src/customiesdevs/customies/item/component/DiggerComponent.php index f44e7cec..6063a0fc 100644 --- a/src/customiesdevs/customies/item/component/DiggerComponent.php +++ b/src/customiesdevs/customies/item/component/DiggerComponent.php @@ -23,7 +23,7 @@ public function __construct(bool $useEfficiency, array $destroySpeeds = []) { } public function getName(): string { - return "minecraft:digger"; + return VanillaItemComponents::DIGGER; } public function getValue(): array { diff --git a/src/customiesdevs/customies/item/component/DisplayNameComponent.php b/src/customiesdevs/customies/item/component/DisplayNameComponent.php index 7ea540a2..ab802c64 100644 --- a/src/customiesdevs/customies/item/component/DisplayNameComponent.php +++ b/src/customiesdevs/customies/item/component/DisplayNameComponent.php @@ -17,7 +17,7 @@ public function __construct(string $name) { } public function getName(): string { - return "minecraft:display_name"; + return VanillaItemComponents::DISPLAY_NAME; } public function getValue(): array { @@ -27,6 +27,6 @@ public function getValue(): array { } public static function fromJson(mixed $data): static { - return new self($data ?? ""); + return new self($data["value"] ?? ""); } } \ No newline at end of file diff --git a/src/customiesdevs/customies/item/component/DurabilityComponent.php b/src/customiesdevs/customies/item/component/DurabilityComponent.php index a10db9ed..629de59e 100644 --- a/src/customiesdevs/customies/item/component/DurabilityComponent.php +++ b/src/customiesdevs/customies/item/component/DurabilityComponent.php @@ -22,7 +22,7 @@ public function __construct(int $maxDurability, int $minDamageChance = 100, int } public function getName(): string { - return "minecraft:durability"; + return VanillaItemComponents::DURABILITY; } public function getValue(): array { diff --git a/src/customiesdevs/customies/item/component/DurabilitySensorComponent.php b/src/customiesdevs/customies/item/component/DurabilitySensorComponent.php index f6a24165..4380db05 100644 --- a/src/customiesdevs/customies/item/component/DurabilitySensorComponent.php +++ b/src/customiesdevs/customies/item/component/DurabilitySensorComponent.php @@ -16,7 +16,7 @@ public function __construct(array $durabilityThresholds = []) { } public function getName(): string { - return "minecraft:durability_sensor"; + return VanillaItemComponents::DURABILITY_SENSOR; } public function getValue(): array { diff --git a/src/customiesdevs/customies/item/component/DyeableComponent.php b/src/customiesdevs/customies/item/component/DyeableComponent.php index f4819d81..c6d49b1e 100644 --- a/src/customiesdevs/customies/item/component/DyeableComponent.php +++ b/src/customiesdevs/customies/item/component/DyeableComponent.php @@ -16,7 +16,7 @@ public function __construct(string $hex) { } public function getName(): string { - return "minecraft:dyeable"; + return VanillaItemComponents::DYEABLE; } public function getValue(): array { diff --git a/src/customiesdevs/customies/item/component/EnchantableComponent.php b/src/customiesdevs/customies/item/component/EnchantableComponent.php index 124139d3..7877d5bb 100644 --- a/src/customiesdevs/customies/item/component/EnchantableComponent.php +++ b/src/customiesdevs/customies/item/component/EnchantableComponent.php @@ -56,7 +56,7 @@ public function __construct(string $slot = self::SLOT_ALL, int $value = 1) { } public function getName(): string { - return "minecraft:enchantable"; + return VanillaItemComponents::ENCHANTABLE; } public function getValue(): array { diff --git a/src/customiesdevs/customies/item/component/FireResistantComponent.php b/src/customiesdevs/customies/item/component/FireResistantComponent.php index cc719db0..c6d46042 100644 --- a/src/customiesdevs/customies/item/component/FireResistantComponent.php +++ b/src/customiesdevs/customies/item/component/FireResistantComponent.php @@ -16,7 +16,7 @@ public function __construct(bool $fireResistant = true) { } public function getName(): string { - return "minecraft:fire_resistant"; + return VanillaItemComponents::FIRE_RESISTANT; } public function getValue(): array { diff --git a/src/customiesdevs/customies/item/component/FoodComponent.php b/src/customiesdevs/customies/item/component/FoodComponent.php index 11fc11ce..2a9d614b 100644 --- a/src/customiesdevs/customies/item/component/FoodComponent.php +++ b/src/customiesdevs/customies/item/component/FoodComponent.php @@ -25,7 +25,7 @@ public function __construct(bool $canAlwaysEat = false, int $nutrition = 0, floa } public function getName(): string { - return "minecraft:food"; + return VanillaItemComponents::FOOD; } public function getValue(): array { diff --git a/src/customiesdevs/customies/item/component/FuelComponent.php b/src/customiesdevs/customies/item/component/FuelComponent.php index 6eea2372..5da2781c 100644 --- a/src/customiesdevs/customies/item/component/FuelComponent.php +++ b/src/customiesdevs/customies/item/component/FuelComponent.php @@ -16,7 +16,7 @@ public function __construct(float $duration) { } public function getName(): string { - return "minecraft:fuel"; + return VanillaItemComponents::FUEL; } public function getValue(): array { diff --git a/src/customiesdevs/customies/item/component/GlintComponent.php b/src/customiesdevs/customies/item/component/GlintComponent.php index df95c465..dce501ab 100644 --- a/src/customiesdevs/customies/item/component/GlintComponent.php +++ b/src/customiesdevs/customies/item/component/GlintComponent.php @@ -16,7 +16,7 @@ public function __construct(bool $glint = true) { } public function getName(): string { - return "minecraft:glint"; + return VanillaItemComponents::GLINT; } public function getValue(): array { diff --git a/src/customiesdevs/customies/item/component/HandEquippedComponent.php b/src/customiesdevs/customies/item/component/HandEquippedComponent.php index d2a4e2d4..65b6e09e 100644 --- a/src/customiesdevs/customies/item/component/HandEquippedComponent.php +++ b/src/customiesdevs/customies/item/component/HandEquippedComponent.php @@ -16,7 +16,7 @@ public function __construct(bool $handEquipped = true) { } public function getName(): string { - return "minecraft:hand_equipped"; + return VanillaItemComponents::HAND_EQUIPPED; } public function getValue(): array { diff --git a/src/customiesdevs/customies/item/component/HoverTextColorComponent.php b/src/customiesdevs/customies/item/component/HoverTextColorComponent.php index 48fdeef2..22a947eb 100644 --- a/src/customiesdevs/customies/item/component/HoverTextColorComponent.php +++ b/src/customiesdevs/customies/item/component/HoverTextColorComponent.php @@ -17,7 +17,7 @@ public function __construct(string $hoverTextColor) { } public function getName(): string { - return "minecraft:hover_text_color"; + return VanillaItemComponents::HOVER_TEXT_COLOR; } public function getValue(): array { diff --git a/src/customiesdevs/customies/item/component/IconComponent.php b/src/customiesdevs/customies/item/component/IconComponent.php index 4050e1c7..e5b5efac 100644 --- a/src/customiesdevs/customies/item/component/IconComponent.php +++ b/src/customiesdevs/customies/item/component/IconComponent.php @@ -34,7 +34,7 @@ public function __construct( } public function getName(): string { - return "minecraft:icon"; + return VanillaItemComponents::ICON; } public function getValue(): array { diff --git a/src/customiesdevs/customies/item/component/InteractButtonComponent.php b/src/customiesdevs/customies/item/component/InteractButtonComponent.php index a835577c..2a5b1620 100644 --- a/src/customiesdevs/customies/item/component/InteractButtonComponent.php +++ b/src/customiesdevs/customies/item/component/InteractButtonComponent.php @@ -20,7 +20,7 @@ public function __construct(bool|string $interactButton) { } public function getName(): string { - return "minecraft:interact_button"; + return VanillaItemComponents::INTERACT_BUTTON; } public function getValue(): array { diff --git a/src/customiesdevs/customies/item/component/LiquidClippedComponent.php b/src/customiesdevs/customies/item/component/LiquidClippedComponent.php index b5e62d88..e599fe35 100644 --- a/src/customiesdevs/customies/item/component/LiquidClippedComponent.php +++ b/src/customiesdevs/customies/item/component/LiquidClippedComponent.php @@ -16,7 +16,7 @@ public function __construct(bool $liquidClipped = true) { } public function getName(): string { - return "minecraft:liquid_clipped"; + return VanillaItemComponents::LIQUID_CLIPPED; } public function getValue(): array { diff --git a/src/customiesdevs/customies/item/component/MaxStackSizeComponent.php b/src/customiesdevs/customies/item/component/MaxStackSizeComponent.php index f5affde6..a012f452 100644 --- a/src/customiesdevs/customies/item/component/MaxStackSizeComponent.php +++ b/src/customiesdevs/customies/item/component/MaxStackSizeComponent.php @@ -16,7 +16,7 @@ public function __construct(int $maxStackSize = 64) { } public function getName(): string { - return "minecraft:max_stack_size"; + return VanillaItemComponents::MAX_STACK_SIZE; } public function getValue(): array { diff --git a/src/customiesdevs/customies/item/component/ProjectileComponent.php b/src/customiesdevs/customies/item/component/ProjectileComponent.php index 140cc41f..f217e8f8 100644 --- a/src/customiesdevs/customies/item/component/ProjectileComponent.php +++ b/src/customiesdevs/customies/item/component/ProjectileComponent.php @@ -21,7 +21,7 @@ public function __construct(float $minimumCriticalPower, string $projectileEntit } public function getName(): string { - return "minecraft:projectile"; + return VanillaItemComponents::PROJECTILE; } public function getValue(): array { diff --git a/src/customiesdevs/customies/item/component/RarityComponent.php b/src/customiesdevs/customies/item/component/RarityComponent.php index b6aa7132..fd434978 100644 --- a/src/customiesdevs/customies/item/component/RarityComponent.php +++ b/src/customiesdevs/customies/item/component/RarityComponent.php @@ -22,7 +22,7 @@ public function __construct(string $rarity = self::COMMON) { } public function getName(): string { - return "minecraft:rarity"; + return VanillaItemComponents::RARITY; } public function getValue(): array { diff --git a/src/customiesdevs/customies/item/component/RecordComponent.php b/src/customiesdevs/customies/item/component/RecordComponent.php index ad101c4d..d8d00c4a 100644 --- a/src/customiesdevs/customies/item/component/RecordComponent.php +++ b/src/customiesdevs/customies/item/component/RecordComponent.php @@ -22,7 +22,7 @@ public function __construct(int $comparatorSignal, float $duration, string $soun } public function getName(): string { - return "minecraft:record"; + return VanillaItemComponents::RECORD; } public function getValue(): array { diff --git a/src/customiesdevs/customies/item/component/RepairableComponent.php b/src/customiesdevs/customies/item/component/RepairableComponent.php index 06e987bf..44581901 100644 --- a/src/customiesdevs/customies/item/component/RepairableComponent.php +++ b/src/customiesdevs/customies/item/component/RepairableComponent.php @@ -16,7 +16,7 @@ public function __construct( ) {} public function getName(): string { - return "minecraft:repairable"; + return VanillaItemComponents::REPAIRABLE; } public function getValue(): array { diff --git a/src/customiesdevs/customies/item/component/ShooterComponent.php b/src/customiesdevs/customies/item/component/ShooterComponent.php index cc0f9a57..5ed9f672 100644 --- a/src/customiesdevs/customies/item/component/ShooterComponent.php +++ b/src/customiesdevs/customies/item/component/ShooterComponent.php @@ -36,7 +36,7 @@ public function __construct(string $item, bool $useOffhand = false, bool $search } public function getName(): string { - return "minecraft:shooter"; + return VanillaItemComponents::SHOOTER; } public function getValue(): array { diff --git a/src/customiesdevs/customies/item/component/ShouldDespawnComponent.php b/src/customiesdevs/customies/item/component/ShouldDespawnComponent.php index 31c71e3e..82c7ed6a 100644 --- a/src/customiesdevs/customies/item/component/ShouldDespawnComponent.php +++ b/src/customiesdevs/customies/item/component/ShouldDespawnComponent.php @@ -16,7 +16,7 @@ public function __construct(bool $shouldDespawn = true) { } public function getName(): string { - return "minecraft:should_despawn"; + return VanillaItemComponents::SHOULD_DESPAWN; } public function getValue(): array { diff --git a/src/customiesdevs/customies/item/component/StackedByDataComponent.php b/src/customiesdevs/customies/item/component/StackedByDataComponent.php index cf3cb8eb..ee4d9b9d 100644 --- a/src/customiesdevs/customies/item/component/StackedByDataComponent.php +++ b/src/customiesdevs/customies/item/component/StackedByDataComponent.php @@ -17,7 +17,7 @@ public function __construct(bool $stackedByData = true) { } public function getName(): string { - return "minecraft:stacked_by_data"; + return VanillaItemComponents::STACKED_BY_DATA; } public function getValue(): array { diff --git a/src/customiesdevs/customies/item/component/StorageItemComponent.php b/src/customiesdevs/customies/item/component/StorageItemComponent.php index 281e3e3d..903ad8d8 100644 --- a/src/customiesdevs/customies/item/component/StorageItemComponent.php +++ b/src/customiesdevs/customies/item/component/StorageItemComponent.php @@ -39,7 +39,7 @@ public function __construct( } public function getName(): string { - return "minecraft:storage_item"; + return VanillaItemComponents::STORAGE_ITEM; } public function getValue(): array { diff --git a/src/customiesdevs/customies/item/component/StorageWeightLimitComponent.php b/src/customiesdevs/customies/item/component/StorageWeightLimitComponent.php index 13a151a7..c15efbec 100644 --- a/src/customiesdevs/customies/item/component/StorageWeightLimitComponent.php +++ b/src/customiesdevs/customies/item/component/StorageWeightLimitComponent.php @@ -16,7 +16,7 @@ public function __construct(int $maxWeightLimit = 64) { } public function getName(): string { - return "minecraft:storage_weight_limit"; + return VanillaItemComponents::STORAGE_WEIGHT_LIMIT; } public function getValue(): array { diff --git a/src/customiesdevs/customies/item/component/StorageWeightModifierComponent.php b/src/customiesdevs/customies/item/component/StorageWeightModifierComponent.php index 501d0a85..75ebf9d0 100644 --- a/src/customiesdevs/customies/item/component/StorageWeightModifierComponent.php +++ b/src/customiesdevs/customies/item/component/StorageWeightModifierComponent.php @@ -16,7 +16,7 @@ public function __construct(int $weightInStorageItem = 4) { } public function getName(): string { - return "minecraft:storage_weight_modifier"; + return VanillaItemComponents::STORAGE_WEIGHT_MODIFIER; } public function getValue(): array { diff --git a/src/customiesdevs/customies/item/component/SwingDurationComponent.php b/src/customiesdevs/customies/item/component/SwingDurationComponent.php index 1d0b8700..0dec2564 100644 --- a/src/customiesdevs/customies/item/component/SwingDurationComponent.php +++ b/src/customiesdevs/customies/item/component/SwingDurationComponent.php @@ -16,7 +16,7 @@ public function __construct(float $swingDuration = 0.3) { } public function getName(): string { - return "minecraft:swing_duration"; + return VanillaItemComponents::SWING_DURATION; } public function getValue(): array { diff --git a/src/customiesdevs/customies/item/component/TagsComponent.php b/src/customiesdevs/customies/item/component/TagsComponent.php index 520a5c6a..54eb6af4 100644 --- a/src/customiesdevs/customies/item/component/TagsComponent.php +++ b/src/customiesdevs/customies/item/component/TagsComponent.php @@ -16,7 +16,7 @@ public function __construct(array $tags = []) { } public function getName(): string { - return "minecraft:tags"; + return VanillaItemComponents::TAGS; } public function getValue(): array { diff --git a/src/customiesdevs/customies/item/component/ThrowableComponent.php b/src/customiesdevs/customies/item/component/ThrowableComponent.php index bb668395..0d6d9904 100644 --- a/src/customiesdevs/customies/item/component/ThrowableComponent.php +++ b/src/customiesdevs/customies/item/component/ThrowableComponent.php @@ -38,7 +38,7 @@ public function __construct( } public function getName(): string { - return "minecraft:throwable"; + return VanillaItemComponents::THROWABLE; } public function getValue(): array { diff --git a/src/customiesdevs/customies/item/component/UseAnimationComponent.php b/src/customiesdevs/customies/item/component/UseAnimationComponent.php index b3670e2a..3f58d10c 100644 --- a/src/customiesdevs/customies/item/component/UseAnimationComponent.php +++ b/src/customiesdevs/customies/item/component/UseAnimationComponent.php @@ -27,7 +27,7 @@ public function __construct(int $animation) { } public function getName(): string { - return "minecraft:use_animation"; + return VanillaItemComponents::USE_ANIMATION; } public function getValue(): array { diff --git a/src/customiesdevs/customies/item/component/UseModifiersComponent.php b/src/customiesdevs/customies/item/component/UseModifiersComponent.php index 039b3b85..76685a82 100644 --- a/src/customiesdevs/customies/item/component/UseModifiersComponent.php +++ b/src/customiesdevs/customies/item/component/UseModifiersComponent.php @@ -19,7 +19,7 @@ public function __construct(float $movementModifier, float $useDuration = 0) { } public function getName(): string { - return "minecraft:use_modifiers"; + return VanillaItemComponents::USE_MODIFIERS; } public function getValue(): array { diff --git a/src/customiesdevs/customies/item/component/VanillaItemComponents.php b/src/customiesdevs/customies/item/component/VanillaItemComponents.php index 92d10c0e..2cf8760f 100644 --- a/src/customiesdevs/customies/item/component/VanillaItemComponents.php +++ b/src/customiesdevs/customies/item/component/VanillaItemComponents.php @@ -151,7 +151,7 @@ public static function getAll(): array { * @return bool True if the identifier is known, false otherwise. */ public static function isValid(string $identifier): bool { - return in_array($identifier, self::ALL, true); + return in_array($identifier, self::ALL, true) || isset(self::$classMap[$identifier]); } /** @@ -186,4 +186,32 @@ public static function registerClass(string $identifier, string $fqcn): void { } self::$classMap[$identifier] = $fqcn; } + + /** + * Register a custom (non-vanilla) component identifier to a class at runtime. + * This allows plugins to introduce new components beyond the built-in list. + * + * Rules: + * - Identifier must follow a simple namespaced pattern (e.g. "myplugin:my_component") + * - Identifier must not be one of the known vanilla identifiers (use registerClass() to override those) + * - Class must implement ItemComponent + * + * @param string $identifier Custom component identifier + * @param class-string $fqcn Fully qualified class name implementing ItemComponent + */ + public static function registerCustomComponent(string $identifier, string $fqcn): void { + // Basic identifier format check: namespace:name (lowercase, digits, underscore, dash, dot, slash) + if(!preg_match('/^[a-z0-9_\-\.]+:[a-z0-9_\-\.\/]+$/', $identifier)) { + throw new \InvalidArgumentException("Invalid component identifier format: $identifier"); + } + // Reserve minecraft namespace for vanilla identifiers + if(str_starts_with($identifier, 'minecraft:')) { + throw new \InvalidArgumentException("The namespace 'minecraft' is reserved for vanilla component identifiers. Use registerClass() to override existing ones."); + } + if (!is_subclass_of($fqcn, ItemComponent::class)) { + throw new \InvalidArgumentException("Class $fqcn must implement " . ItemComponent::class); + } + // Register or override existing custom mapping + self::$classMap[$identifier] = $fqcn; + } } \ No newline at end of file diff --git a/src/customiesdevs/customies/item/component/WearableComponent.php b/src/customiesdevs/customies/item/component/WearableComponent.php index 33c203c1..d66b224b 100644 --- a/src/customiesdevs/customies/item/component/WearableComponent.php +++ b/src/customiesdevs/customies/item/component/WearableComponent.php @@ -37,7 +37,7 @@ public function __construct(string $slot, int $protection = 0, bool $hidePlayerL } public function getName(): string { - return "minecraft:wearable"; + return VanillaItemComponents::WEARABLE; } public function getValue(): array { diff --git a/src/customiesdevs/customies/item/properties/ItemProperties.php b/src/customiesdevs/customies/item/properties/ItemProperties.php index 3e6e5126..59a8afa9 100644 --- a/src/customiesdevs/customies/item/properties/ItemProperties.php +++ b/src/customiesdevs/customies/item/properties/ItemProperties.php @@ -18,6 +18,7 @@ * - tags handling */ final class ItemProperties { + /** * Default property values placed under item_properties */ @@ -44,19 +45,19 @@ final class ItemProperties { * componentName => [ propertyName => keyInsideComponentValue ] */ private const MAPPINGS = [ - 'minecraft:allow_off_hand' => ['allow_off_hand' => 'value'], + 'minecraft:allow_off_hand' => ['allow_off_hand' => 'value'], 'minecraft:can_destroy_in_creative' => ['can_destroy_in_creative' => 'value'], - 'minecraft:damage' => ['damage' => 'value'], - 'minecraft:enchantable' => ['enchantable_slot' => 'slot', 'enchantable_value' => 'value'], - 'minecraft:glint' => ['foil' => 'value'], - 'minecraft:hand_equipped' => ['hand_equipped' => 'value'], - 'minecraft:hover_text_color' => ['hover_text_color' => 'value'], - 'minecraft:liquid_clipped' => ['liquid_clipped' => 'value'], - 'minecraft:max_stack_size' => ['max_stack_size' => 'value'], - 'minecraft:should_despawn' => ['should_despawn' => 'value'], - 'minecraft:stacked_by_data' => ['stacked_by_data' => 'value'], - 'minecraft:use_animation' => ['use_animation' => 'value'], - 'minecraft:use_modifiers' => ['use_duration' => 'use_duration'], + 'minecraft:damage' => ['damage' => 'value'], + 'minecraft:enchantable' => ['enchantable_slot' => 'slot', 'enchantable_value' => 'value'], + 'minecraft:glint' => ['foil' => 'value'], + 'minecraft:hand_equipped' => ['hand_equipped' => 'value'], + 'minecraft:hover_text_color' => ['hover_text_color' => 'value'], + 'minecraft:liquid_clipped' => ['liquid_clipped' => 'value'], + 'minecraft:max_stack_size' => ['max_stack_size' => 'value'], + 'minecraft:should_despawn' => ['should_despawn' => 'value'], + 'minecraft:stacked_by_data' => ['stacked_by_data' => 'value'], + 'minecraft:use_animation' => ['use_animation' => 'value'], + 'minecraft:use_modifiers' => ['use_duration' => 'use_duration'], ]; private CompoundTag $properties; @@ -75,8 +76,8 @@ public function __construct() { } /** - * Applies a list of components, updating both item_properties and the components tag. - * The raw component is always added to components (except for minecraft:icon which belongs under item_properties). + * Applies the given item components to the properties builder. + * This updates both item_properties and the components tag. * @param ItemComponent[] $components */ public function applyComponents(array $components): void { @@ -106,9 +107,10 @@ public function applyComponents(array $components): void { } $this->properties->setTag($prop, NBT::getTagType($value[$key])); } + continue; } - // Always record the raw component (except icon, handled above) + // Always record the raw component (except icon and item properties, handled above) $this->components->setTag($name, $tag); } } diff --git a/src/customiesdevs/customies/item/traits/ItemComponentsTrait.php b/src/customiesdevs/customies/item/traits/ItemComponentsTrait.php index 625b8632..47037ccc 100644 --- a/src/customiesdevs/customies/item/traits/ItemComponentsTrait.php +++ b/src/customiesdevs/customies/item/traits/ItemComponentsTrait.php @@ -18,6 +18,10 @@ public function hasComponent(string $name): bool { return isset($this->components[$name]); } + public function getComponent(string $name): ?ItemComponent { + return $this->components[$name] ?? null; + } + /** * @return ItemComponent[] */ From 7b2d3f0a0f013d5a0475d34c6d549331d55073c3 Mon Sep 17 00:00:00 2001 From: JeanTKG <102908437+jeantkg@users.noreply.github.com> Date: Fri, 14 Nov 2025 13:21:20 +0300 Subject: [PATCH 08/51] readded src-namespace-prefix --- composer.json | 31 --- plugin.yml | 1 + .../behavior/blocks/custom_example_block.json | 5 - .../customies => }/Customies.php | 3 - .../customies => }/CustomiesListener.php | 0 .../customies => }/block/BlockComponents.php | 0 .../customies => }/block/BlockManager.php | 0 .../customies => }/block/BlockPalette.php | 0 src/block/CustomiesBlock.php | 83 +++++++ .../block/CustomiesBlockFactory.php | 0 .../block/component/BlockComponent.php | 0 .../block/component/CollisionBoxComponent.php | 2 +- .../component/CraftingTableComponent.php | 2 +- .../DestructibleByExplosionComponent.php | 2 +- .../DestructibleByMiningComponent.php | 2 +- .../DestructionParticlesComponent.php | 2 +- .../block/component/DisplayNameComponent.php | 2 +- .../block/component/FlammableComponent.php | 2 +- .../block/component/FrictionComponent.php | 2 +- .../block/component/GeometryComponent.php | 2 +- .../block/component/ItemVisualComponent.php | 2 +- .../component/LightDampeningComponent.php | 2 +- .../component/LightEmissionComponent.php | 2 +- .../component/LiquidDetectionComponent.php | 2 +- .../block/component/LootComponent.php | 2 +- .../block/component/MapColorComponent.php | 2 +- .../component/MaterialInstancesComponent.php | 2 +- .../block/component/MovableComponent.php | 2 +- .../component/PlacementFilterComponent.php | 2 +- .../block/component/RandomOffsetComponent.php | 2 +- .../RedstoneConductivityComponent.php | 2 +- .../block/component/SelectionBoxComponent.php | 2 +- .../block/permutations/BlockProperty.php | 0 .../block/permutations/Permutable.php | 0 .../block/permutations/Permutation.php | 0 .../block/permutations/Permutations.php | 0 .../block/permutations/RotatableTrait.php | 0 .../block/properties/Material.php | 0 .../block/properties/RenderMethod.php | 0 .../block/properties/TintMethod.php | 0 .../block/traits/BlockComponentsTrait.php | 0 .../block/traits/DefaultTrait.php | 0 .../customies/block/CustomiesBlock.php | 46 ---- .../component/VanillaBlockComponents.php | 165 ------------- .../custom/BlockBreakInfoComponent.php | 82 ------- .../customies/item/CustomiesItem.php | 24 -- .../item/component/VanillaItemComponents.php | 217 ------------------ .../entity/CustomiesEntityFactory.php | 0 .../item/CreativeInventoryInfo.php | 0 src/item/CustomiesItem.php | 110 +++++++++ .../item/CustomiesItemFactory.php | 0 .../customies => }/item/ItemComponents.php | 0 .../customies => }/item/ItemManager.php | 0 .../item/component/AllowOffHandComponent.php | 2 +- .../item/component/BlockPlacerComponent.php | 2 +- .../component/BundleInteractionComponent.php | 4 +- .../CanDestroyInCreativeComponent.php | 2 +- .../item/component/CompostableComponent.php | 2 +- .../item/component/CooldownComponent.php | 2 +- .../component/DamageAbsorptionComponent.php | 2 +- .../item/component/DamageComponent.php | 2 +- .../item/component/DiggerComponent.php | 2 +- .../item/component/DisplayNameComponent.php | 2 +- .../item/component/DurabilityComponent.php | 2 +- .../component/DurabilitySensorComponent.php | 2 +- .../item/component/DyeableComponent.php | 2 +- .../item/component/EnchantableComponent.php | 2 +- .../item/component/FireResistantComponent.php | 2 +- .../item/component/FoodComponent.php | 6 +- .../item/component/FuelComponent.php | 2 +- .../item/component/GlintComponent.php | 2 +- .../item/component/HandEquippedComponent.php | 2 +- .../component/HoverTextColorComponent.php | 2 +- .../item/component/IconComponent.php | 8 +- .../component/InteractButtonComponent.php | 2 +- .../item/component/ItemComponent.php | 0 .../item/component/LiquidClippedComponent.php | 2 +- .../item/component/MaxStackSizeComponent.php | 2 +- .../item/component/ProjectileComponent.php | 2 +- .../item/component/RarityComponent.php | 2 +- .../item/component/RecordComponent.php | 2 +- .../item/component/RepairableComponent.php | 2 +- .../item/component/ShooterComponent.php | 2 +- .../item/component/ShouldDespawnComponent.php | 2 +- .../item/component/StackedByDataComponent.php | 2 +- .../item/component/StorageItemComponent.php | 2 +- .../component/StorageWeightLimitComponent.php | 2 +- .../StorageWeightModifierComponent.php | 2 +- .../item/component/SwingDurationComponent.php | 2 +- .../item/component/TagsComponent.php | 2 +- .../item/component/ThrowableComponent.php | 2 +- .../item/component/UseAnimationComponent.php | 2 +- .../item/component/UseModifiersComponent.php | 2 +- .../item/component/WearableComponent.php | 2 +- .../item/properties/ItemProperties.php | 7 +- .../item/properties/RepairItems.php | 0 .../item/traits/DefaultTrait.php | 0 .../item/traits/ItemComponentsTrait.php | 0 .../task/AsyncRegisterBlocksTask.php | 0 .../customies => }/util/NBT.php | 0 100 files changed, 266 insertions(+), 642 deletions(-) delete mode 100644 composer.json rename src/{customiesdevs/customies => }/Customies.php (81%) rename src/{customiesdevs/customies => }/CustomiesListener.php (100%) rename src/{customiesdevs/customies => }/block/BlockComponents.php (100%) rename src/{customiesdevs/customies => }/block/BlockManager.php (100%) rename src/{customiesdevs/customies => }/block/BlockPalette.php (100%) create mode 100644 src/block/CustomiesBlock.php rename src/{customiesdevs/customies => }/block/CustomiesBlockFactory.php (100%) rename src/{customiesdevs/customies => }/block/component/BlockComponent.php (100%) rename src/{customiesdevs/customies => }/block/component/CollisionBoxComponent.php (97%) rename src/{customiesdevs/customies => }/block/component/CraftingTableComponent.php (97%) rename src/{customiesdevs/customies => }/block/component/DestructibleByExplosionComponent.php (95%) rename src/{customiesdevs/customies => }/block/component/DestructibleByMiningComponent.php (94%) rename src/{customiesdevs/customies => }/block/component/DestructionParticlesComponent.php (96%) rename src/{customiesdevs/customies => }/block/component/DisplayNameComponent.php (95%) rename src/{customiesdevs/customies => }/block/component/FlammableComponent.php (98%) rename src/{customiesdevs/customies => }/block/component/FrictionComponent.php (94%) rename src/{customiesdevs/customies => }/block/component/GeometryComponent.php (98%) rename src/{customiesdevs/customies => }/block/component/ItemVisualComponent.php (94%) rename src/{customiesdevs/customies => }/block/component/LightDampeningComponent.php (93%) rename src/{customiesdevs/customies => }/block/component/LightEmissionComponent.php (93%) rename src/{customiesdevs/customies => }/block/component/LiquidDetectionComponent.php (98%) rename src/{customiesdevs/customies => }/block/component/LootComponent.php (93%) rename src/{customiesdevs/customies => }/block/component/MapColorComponent.php (94%) rename src/{customiesdevs/customies => }/block/component/MaterialInstancesComponent.php (96%) rename src/{customiesdevs/customies => }/block/component/MovableComponent.php (97%) rename src/{customiesdevs/customies => }/block/component/PlacementFilterComponent.php (94%) rename src/{customiesdevs/customies => }/block/component/RandomOffsetComponent.php (89%) rename src/{customiesdevs/customies => }/block/component/RedstoneConductivityComponent.php (95%) rename src/{customiesdevs/customies => }/block/component/SelectionBoxComponent.php (97%) rename src/{customiesdevs/customies => }/block/permutations/BlockProperty.php (100%) rename src/{customiesdevs/customies => }/block/permutations/Permutable.php (100%) rename src/{customiesdevs/customies => }/block/permutations/Permutation.php (100%) rename src/{customiesdevs/customies => }/block/permutations/Permutations.php (100%) rename src/{customiesdevs/customies => }/block/permutations/RotatableTrait.php (100%) rename src/{customiesdevs/customies => }/block/properties/Material.php (100%) rename src/{customiesdevs/customies => }/block/properties/RenderMethod.php (100%) rename src/{customiesdevs/customies => }/block/properties/TintMethod.php (100%) rename src/{customiesdevs/customies => }/block/traits/BlockComponentsTrait.php (100%) rename src/{customiesdevs/customies => }/block/traits/DefaultTrait.php (100%) delete mode 100644 src/customiesdevs/customies/block/CustomiesBlock.php delete mode 100644 src/customiesdevs/customies/block/component/VanillaBlockComponents.php delete mode 100644 src/customiesdevs/customies/block/component/custom/BlockBreakInfoComponent.php delete mode 100644 src/customiesdevs/customies/item/CustomiesItem.php delete mode 100644 src/customiesdevs/customies/item/component/VanillaItemComponents.php rename src/{customiesdevs/customies => }/entity/CustomiesEntityFactory.php (100%) rename src/{customiesdevs/customies => }/item/CreativeInventoryInfo.php (100%) create mode 100644 src/item/CustomiesItem.php rename src/{customiesdevs/customies => }/item/CustomiesItemFactory.php (100%) rename src/{customiesdevs/customies => }/item/ItemComponents.php (100%) rename src/{customiesdevs/customies => }/item/ItemManager.php (100%) rename src/{customiesdevs/customies => }/item/component/AllowOffHandComponent.php (92%) rename src/{customiesdevs/customies => }/item/component/BlockPlacerComponent.php (97%) rename src/{customiesdevs/customies => }/item/component/BundleInteractionComponent.php (88%) rename src/{customiesdevs/customies => }/item/component/CanDestroyInCreativeComponent.php (92%) rename src/{customiesdevs/customies => }/item/component/CompostableComponent.php (95%) rename src/{customiesdevs/customies => }/item/component/CooldownComponent.php (96%) rename src/{customiesdevs/customies => }/item/component/DamageAbsorptionComponent.php (95%) rename src/{customiesdevs/customies => }/item/component/DamageComponent.php (94%) rename src/{customiesdevs/customies => }/item/component/DiggerComponent.php (98%) rename src/{customiesdevs/customies => }/item/component/DisplayNameComponent.php (93%) rename src/{customiesdevs/customies => }/item/component/DurabilityComponent.php (97%) rename src/{customiesdevs/customies => }/item/component/DurabilitySensorComponent.php (95%) rename src/{customiesdevs/customies => }/item/component/DyeableComponent.php (94%) rename src/{customiesdevs/customies => }/item/component/EnchantableComponent.php (98%) rename src/{customiesdevs/customies => }/item/component/FireResistantComponent.php (93%) rename src/{customiesdevs/customies => }/item/component/FoodComponent.php (93%) rename src/{customiesdevs/customies => }/item/component/FuelComponent.php (94%) rename src/{customiesdevs/customies => }/item/component/GlintComponent.php (93%) rename src/{customiesdevs/customies => }/item/component/HandEquippedComponent.php (93%) rename src/{customiesdevs/customies => }/item/component/HoverTextColorComponent.php (93%) rename src/{customiesdevs/customies => }/item/component/IconComponent.php (87%) rename src/{customiesdevs/customies => }/item/component/InteractButtonComponent.php (95%) rename src/{customiesdevs/customies => }/item/component/ItemComponent.php (100%) rename src/{customiesdevs/customies => }/item/component/LiquidClippedComponent.php (93%) rename src/{customiesdevs/customies => }/item/component/MaxStackSizeComponent.php (92%) rename src/{customiesdevs/customies => }/item/component/ProjectileComponent.php (96%) rename src/{customiesdevs/customies => }/item/component/RarityComponent.php (96%) rename src/{customiesdevs/customies => }/item/component/RecordComponent.php (96%) rename src/{customiesdevs/customies => }/item/component/RepairableComponent.php (96%) rename src/{customiesdevs/customies => }/item/component/ShooterComponent.php (98%) rename src/{customiesdevs/customies => }/item/component/ShouldDespawnComponent.php (93%) rename src/{customiesdevs/customies => }/item/component/StackedByDataComponent.php (93%) rename src/{customiesdevs/customies => }/item/component/StorageItemComponent.php (98%) rename src/{customiesdevs/customies => }/item/component/StorageWeightLimitComponent.php (93%) rename src/{customiesdevs/customies => }/item/component/StorageWeightModifierComponent.php (93%) rename src/{customiesdevs/customies => }/item/component/SwingDurationComponent.php (95%) rename src/{customiesdevs/customies => }/item/component/TagsComponent.php (94%) rename src/{customiesdevs/customies => }/item/component/ThrowableComponent.php (98%) rename src/{customiesdevs/customies => }/item/component/UseAnimationComponent.php (95%) rename src/{customiesdevs/customies => }/item/component/UseModifiersComponent.php (95%) rename src/{customiesdevs/customies => }/item/component/WearableComponent.php (97%) rename src/{customiesdevs/customies => }/item/properties/ItemProperties.php (96%) rename src/{customiesdevs/customies => }/item/properties/RepairItems.php (100%) rename src/{customiesdevs/customies => }/item/traits/DefaultTrait.php (100%) rename src/{customiesdevs/customies => }/item/traits/ItemComponentsTrait.php (100%) rename src/{customiesdevs/customies => }/task/AsyncRegisterBlocksTask.php (100%) rename src/{customiesdevs/customies => }/util/NBT.php (100%) diff --git a/composer.json b/composer.json deleted file mode 100644 index 3bd00f79..00000000 --- a/composer.json +++ /dev/null @@ -1,31 +0,0 @@ -{ - "name": "customiesdevs/customies", - "type": "pocketmine-plugin", - "minimum-stability": "beta", - "prefer-stable": true, - "autoload": { - "classmap": ["src"] - }, - "scripts": { - "analyze": "vendor/bin/phpstan analyze src --level=8", - "build": "php -dphar.readonly=0 vendor/bin/pharynx -i=. -c -p=PluginName.phar" - }, - "repositories": [ - - ], - "require": { - - }, - "require-dev": { - "pocketmine/pocketmine-mp": "^5.30", - "phpstan/phpstan": "^1.2", - "phpstan/phpstan-strict-rules": "^1.0", - "phpstan/extension-installer": "^1.0", - "friendsofphp/php-cs-fixer": "^3" - }, - "config": { - "allow-plugins": { - "phpstan/extension-installer": true - } - } -} diff --git a/plugin.yml b/plugin.yml index 2ba9f40a..c966693f 100644 --- a/plugin.yml +++ b/plugin.yml @@ -2,6 +2,7 @@ name: Customies description: A PocketMine-MP plugin that implements support for custom blocks, items and entities. main: customiesdevs\customies\Customies +src-namespace-prefix: customiesdevs\customies version: 2.0.0 api: 5.0.0 diff --git a/resources/behavior/blocks/custom_example_block.json b/resources/behavior/blocks/custom_example_block.json index 442aab7a..64c5238f 100644 --- a/resources/behavior/blocks/custom_example_block.json +++ b/resources/behavior/blocks/custom_example_block.json @@ -17,11 +17,6 @@ } }, "minecraft:display_name": "Custom Example Block", - "customies:block_break_info": { - "hardness": 3, - "tool_type": "pickaxe", - "tool_harvest_level": 4 - }, "minecraft:destructible_by_mining": { "seconds_to_destroy": 0.6 }, diff --git a/src/customiesdevs/customies/Customies.php b/src/Customies.php similarity index 81% rename from src/customiesdevs/customies/Customies.php rename to src/Customies.php index 46b7423b..6720824c 100644 --- a/src/customiesdevs/customies/Customies.php +++ b/src/Customies.php @@ -4,8 +4,6 @@ namespace customiesdevs\customies; use customiesdevs\customies\block\BlockManager; -use customiesdevs\customies\block\component\custom\BlockBreakInfoComponent; -use customiesdevs\customies\block\component\VanillaBlockComponents; use customiesdevs\customies\block\CustomiesBlockFactory; use customiesdevs\customies\item\ItemManager; use pocketmine\plugin\PluginBase; @@ -17,7 +15,6 @@ final class Customies extends PluginBase { public function onLoad(): void{ self::setInstance($this); - VanillaBlockComponents::registerCustomComponent("customies:block_break_info", BlockBreakInfoComponent::class); } protected function onEnable(): void { diff --git a/src/customiesdevs/customies/CustomiesListener.php b/src/CustomiesListener.php similarity index 100% rename from src/customiesdevs/customies/CustomiesListener.php rename to src/CustomiesListener.php diff --git a/src/customiesdevs/customies/block/BlockComponents.php b/src/block/BlockComponents.php similarity index 100% rename from src/customiesdevs/customies/block/BlockComponents.php rename to src/block/BlockComponents.php diff --git a/src/customiesdevs/customies/block/BlockManager.php b/src/block/BlockManager.php similarity index 100% rename from src/customiesdevs/customies/block/BlockManager.php rename to src/block/BlockManager.php diff --git a/src/customiesdevs/customies/block/BlockPalette.php b/src/block/BlockPalette.php similarity index 100% rename from src/customiesdevs/customies/block/BlockPalette.php rename to src/block/BlockPalette.php diff --git a/src/block/CustomiesBlock.php b/src/block/CustomiesBlock.php new file mode 100644 index 00000000..a1ded7ba --- /dev/null +++ b/src/block/CustomiesBlock.php @@ -0,0 +1,83 @@ +> + */ + private static array $componentRegistry = [ + 'minecraft:collision_box' => CollisionBoxComponent::class, + 'minecraft:crafting_table' => CraftingTableComponent::class, + 'minecraft:destructible_by_explosion' => DestructibleByExplosionComponent::class, + 'minecraft:destructible_by_mining' => DestructibleByMiningComponent::class, + 'minecraft:destruction_particles' => DestructionParticlesComponent::class, + 'minecraft:display_name' => DisplayNameComponent::class, + 'minecraft:flammable' => FlammableComponent::class, + 'minecraft:friction' => FrictionComponent::class, + 'minecraft:geometry' => GeometryComponent::class, + 'minecraft:item_visual' => ItemVisualComponent::class, + 'minecraft:light_dampening' => LightDampeningComponent::class, + 'minecraft:light_emission' => LightEmissionComponent::class, + 'minecraft:liquid_detection' => LiquidDetectionComponent::class, + 'minecraft:loot' => LootComponent::class, + 'minecraft:map_color' => MapColorComponent::class, + 'minecraft:material_instances' => MaterialInstancesComponent::class, + 'minecraft:movable' => MovableComponent::class, + 'minecraft:placement_filter' => PlacementFilterComponent::class, + 'minecraft:random_offset' => RandomOffsetComponent::class, + 'minecraft:redstone_conductivity' => RedstoneConductivityComponent::class, + 'minecraft:selection_box' => SelectionBoxComponent::class, + ]; + + public function __construct(array $components) { + foreach ($components as $componentName => $componentData) { + $componentClass = self::$componentRegistry[$componentName] ?? null; + if ($componentClass !== null && method_exists($componentClass, 'fromJson')) { + $this->addComponent($componentClass::fromJson($componentData)); + } + } + parent::__construct( + new BlockIdentifier(BlockTypeIds::newId()), + "Custom Block", + new BlockTypeInfo(new BlockBreakInfo( + $hardness ?? 1.0, + $toolType ?? BlockToolType::NONE, + $toolHarvestLevel ?? 0, + $blastResistance ?? null + )) + ); + } +} \ No newline at end of file diff --git a/src/customiesdevs/customies/block/CustomiesBlockFactory.php b/src/block/CustomiesBlockFactory.php similarity index 100% rename from src/customiesdevs/customies/block/CustomiesBlockFactory.php rename to src/block/CustomiesBlockFactory.php diff --git a/src/customiesdevs/customies/block/component/BlockComponent.php b/src/block/component/BlockComponent.php similarity index 100% rename from src/customiesdevs/customies/block/component/BlockComponent.php rename to src/block/component/BlockComponent.php diff --git a/src/customiesdevs/customies/block/component/CollisionBoxComponent.php b/src/block/component/CollisionBoxComponent.php similarity index 97% rename from src/customiesdevs/customies/block/component/CollisionBoxComponent.php rename to src/block/component/CollisionBoxComponent.php index e3d06e1a..b7e93825 100644 --- a/src/customiesdevs/customies/block/component/CollisionBoxComponent.php +++ b/src/block/component/CollisionBoxComponent.php @@ -27,7 +27,7 @@ public function __construct( } public function getName(): string { - return VanillaBlockComponents::COLLISION_BOX; + return 'minecraft:collision_box'; } public function getValue(): array { diff --git a/src/customiesdevs/customies/block/component/CraftingTableComponent.php b/src/block/component/CraftingTableComponent.php similarity index 97% rename from src/customiesdevs/customies/block/component/CraftingTableComponent.php rename to src/block/component/CraftingTableComponent.php index d9d5218f..cfe32bb9 100644 --- a/src/customiesdevs/customies/block/component/CraftingTableComponent.php +++ b/src/block/component/CraftingTableComponent.php @@ -18,7 +18,7 @@ public function __construct(string $tableName = "Crafting Table", array $craftin } public function getName(): string { - return VanillaBlockComponents::CRAFTING_TABLE; + return 'minecraft:crafting_table'; } public function getValue(): array { diff --git a/src/customiesdevs/customies/block/component/DestructibleByExplosionComponent.php b/src/block/component/DestructibleByExplosionComponent.php similarity index 95% rename from src/customiesdevs/customies/block/component/DestructibleByExplosionComponent.php rename to src/block/component/DestructibleByExplosionComponent.php index d1df8679..eb3417b8 100644 --- a/src/customiesdevs/customies/block/component/DestructibleByExplosionComponent.php +++ b/src/block/component/DestructibleByExplosionComponent.php @@ -15,7 +15,7 @@ public function __construct(float $explosionResistance = 0.0) { } public function getName(): string { - return VanillaBlockComponents::DESTRUCTIBLE_BY_EXPLOSION; + return 'minecraft:destructible_by_explosion'; } public function getValue(): array { diff --git a/src/customiesdevs/customies/block/component/DestructibleByMiningComponent.php b/src/block/component/DestructibleByMiningComponent.php similarity index 94% rename from src/customiesdevs/customies/block/component/DestructibleByMiningComponent.php rename to src/block/component/DestructibleByMiningComponent.php index 05c247d0..27f4ff79 100644 --- a/src/customiesdevs/customies/block/component/DestructibleByMiningComponent.php +++ b/src/block/component/DestructibleByMiningComponent.php @@ -15,7 +15,7 @@ public function __construct(float $secondsToDestroy = 0.0) { } public function getName(): string { - return VanillaBlockComponents::DESTRUCTIBLE_BY_MINING; + return 'minecraft:destructible_by_mining'; } public function getValue(): array { diff --git a/src/customiesdevs/customies/block/component/DestructionParticlesComponent.php b/src/block/component/DestructionParticlesComponent.php similarity index 96% rename from src/customiesdevs/customies/block/component/DestructionParticlesComponent.php rename to src/block/component/DestructionParticlesComponent.php index d2518fb7..f320d5e8 100644 --- a/src/customiesdevs/customies/block/component/DestructionParticlesComponent.php +++ b/src/block/component/DestructionParticlesComponent.php @@ -23,7 +23,7 @@ public function __construct(int $particleCount = 100, string $texture = "", Tint } public function getName(): string { - return VanillaBlockComponents::DESTRUCTION_PARTICLES; + return 'minecraft:destruction_particles'; } public function getValue(): array { diff --git a/src/customiesdevs/customies/block/component/DisplayNameComponent.php b/src/block/component/DisplayNameComponent.php similarity index 95% rename from src/customiesdevs/customies/block/component/DisplayNameComponent.php rename to src/block/component/DisplayNameComponent.php index 73ee8c0e..325e0d36 100644 --- a/src/customiesdevs/customies/block/component/DisplayNameComponent.php +++ b/src/block/component/DisplayNameComponent.php @@ -18,7 +18,7 @@ public function __construct(string $displayName) { } public function getName(): string { - return VanillaBlockComponents::DISPLAY_NAME; + return 'minecraft:display_name'; } public function getValue(): array { diff --git a/src/customiesdevs/customies/block/component/FlammableComponent.php b/src/block/component/FlammableComponent.php similarity index 98% rename from src/customiesdevs/customies/block/component/FlammableComponent.php rename to src/block/component/FlammableComponent.php index 950b6087..3db58149 100644 --- a/src/customiesdevs/customies/block/component/FlammableComponent.php +++ b/src/block/component/FlammableComponent.php @@ -18,7 +18,7 @@ public function __construct(int $catchChanceModifier = 5, int $destroyChanceModi } public function getName(): string { - return VanillaBlockComponents::FLAMMABLE; + return 'minecraft:flammable'; } public function getValue(): array { diff --git a/src/customiesdevs/customies/block/component/FrictionComponent.php b/src/block/component/FrictionComponent.php similarity index 94% rename from src/customiesdevs/customies/block/component/FrictionComponent.php rename to src/block/component/FrictionComponent.php index 540d8bcb..8fb28d73 100644 --- a/src/customiesdevs/customies/block/component/FrictionComponent.php +++ b/src/block/component/FrictionComponent.php @@ -16,7 +16,7 @@ public function __construct(float $friction) { } public function getName(): string { - return VanillaBlockComponents::FRICTION; + return 'minecraft:friction'; } public function getValue(): array { diff --git a/src/customiesdevs/customies/block/component/GeometryComponent.php b/src/block/component/GeometryComponent.php similarity index 98% rename from src/customiesdevs/customies/block/component/GeometryComponent.php rename to src/block/component/GeometryComponent.php index 82542d3e..2efd395d 100644 --- a/src/customiesdevs/customies/block/component/GeometryComponent.php +++ b/src/block/component/GeometryComponent.php @@ -33,7 +33,7 @@ public function __construct( } public function getName(): string { - return VanillaBlockComponents::GEOMETRY; + return 'minecraft:geometry'; } public function getValue(): array { diff --git a/src/customiesdevs/customies/block/component/ItemVisualComponent.php b/src/block/component/ItemVisualComponent.php similarity index 94% rename from src/customiesdevs/customies/block/component/ItemVisualComponent.php rename to src/block/component/ItemVisualComponent.php index 22852dac..e260fea7 100644 --- a/src/customiesdevs/customies/block/component/ItemVisualComponent.php +++ b/src/block/component/ItemVisualComponent.php @@ -13,7 +13,7 @@ public function __construct(GeometryComponent $geometry, MaterialInstancesCompon } public function getName(): string { - return VanillaBlockComponents::ITEM_VISUAL; + return 'minecraft:item_visual'; } public function getValue(): array { diff --git a/src/customiesdevs/customies/block/component/LightDampeningComponent.php b/src/block/component/LightDampeningComponent.php similarity index 93% rename from src/customiesdevs/customies/block/component/LightDampeningComponent.php rename to src/block/component/LightDampeningComponent.php index e463e461..ceb83227 100644 --- a/src/customiesdevs/customies/block/component/LightDampeningComponent.php +++ b/src/block/component/LightDampeningComponent.php @@ -17,7 +17,7 @@ public function __construct(int $dampening = 15) { } public function getName(): string { - return VanillaBlockComponents::LIGHT_DAMPENING; + return 'minecraft:light_dampening'; } public function getValue(): CompoundTag { diff --git a/src/customiesdevs/customies/block/component/LightEmissionComponent.php b/src/block/component/LightEmissionComponent.php similarity index 93% rename from src/customiesdevs/customies/block/component/LightEmissionComponent.php rename to src/block/component/LightEmissionComponent.php index 57d99cfe..0099e80d 100644 --- a/src/customiesdevs/customies/block/component/LightEmissionComponent.php +++ b/src/block/component/LightEmissionComponent.php @@ -17,7 +17,7 @@ public function __construct(int $emission = 0) { } public function getName(): string { - return VanillaBlockComponents::LIGHT_EMISSION; + return 'minecraft:light_emission'; } public function getValue(): CompoundTag { diff --git a/src/customiesdevs/customies/block/component/LiquidDetectionComponent.php b/src/block/component/LiquidDetectionComponent.php similarity index 98% rename from src/customiesdevs/customies/block/component/LiquidDetectionComponent.php rename to src/block/component/LiquidDetectionComponent.php index 71db5bdf..6d2f9b93 100644 --- a/src/customiesdevs/customies/block/component/LiquidDetectionComponent.php +++ b/src/block/component/LiquidDetectionComponent.php @@ -32,7 +32,7 @@ public function __construct(string $liquidType = "water", bool $canContainLiquid } public function getName(): string { - return VanillaBlockComponents::LIQUID_DETECTION; + return 'minecraft:liquid_detection'; } public function getValue(): array { diff --git a/src/customiesdevs/customies/block/component/LootComponent.php b/src/block/component/LootComponent.php similarity index 93% rename from src/customiesdevs/customies/block/component/LootComponent.php rename to src/block/component/LootComponent.php index 82acff38..89da9689 100644 --- a/src/customiesdevs/customies/block/component/LootComponent.php +++ b/src/block/component/LootComponent.php @@ -14,7 +14,7 @@ public function __construct(string $loot) { } public function getName(): string { - return VanillaBlockComponents::LOOT; + return 'minecraft:loot'; } public function getValue(): array { diff --git a/src/customiesdevs/customies/block/component/MapColorComponent.php b/src/block/component/MapColorComponent.php similarity index 94% rename from src/customiesdevs/customies/block/component/MapColorComponent.php rename to src/block/component/MapColorComponent.php index d568afc0..c191e0e7 100644 --- a/src/customiesdevs/customies/block/component/MapColorComponent.php +++ b/src/block/component/MapColorComponent.php @@ -15,7 +15,7 @@ public function __construct(string|array $color) { } public function getName(): string { - return VanillaBlockComponents::MAP_COLOR; + return 'minecraft:map_color'; } public function getValue(): array { diff --git a/src/customiesdevs/customies/block/component/MaterialInstancesComponent.php b/src/block/component/MaterialInstancesComponent.php similarity index 96% rename from src/customiesdevs/customies/block/component/MaterialInstancesComponent.php rename to src/block/component/MaterialInstancesComponent.php index 88ab21c3..afc9c6fe 100644 --- a/src/customiesdevs/customies/block/component/MaterialInstancesComponent.php +++ b/src/block/component/MaterialInstancesComponent.php @@ -22,7 +22,7 @@ public function __construct(private readonly array $materials) { } public function getName(): string { - return VanillaBlockComponents::MATERIAL_INSTANCES; + return 'minecraft:material_instances'; } public function getValue(): array { diff --git a/src/customiesdevs/customies/block/component/MovableComponent.php b/src/block/component/MovableComponent.php similarity index 97% rename from src/customiesdevs/customies/block/component/MovableComponent.php rename to src/block/component/MovableComponent.php index 3cbdd5a7..2d59593a 100644 --- a/src/customiesdevs/customies/block/component/MovableComponent.php +++ b/src/block/component/MovableComponent.php @@ -32,7 +32,7 @@ public function __construct(string $movementType = self::MOVEMENT_TYPE_PUSH_PULL } public function getName(): string { - return VanillaBlockComponents::MOVABLE; + return 'minecraft:movable'; } public function getValue(): array { diff --git a/src/customiesdevs/customies/block/component/PlacementFilterComponent.php b/src/block/component/PlacementFilterComponent.php similarity index 94% rename from src/customiesdevs/customies/block/component/PlacementFilterComponent.php rename to src/block/component/PlacementFilterComponent.php index f375ad63..bb17b061 100644 --- a/src/customiesdevs/customies/block/component/PlacementFilterComponent.php +++ b/src/block/component/PlacementFilterComponent.php @@ -16,7 +16,7 @@ public function __construct(array $allowedFaces = [], array $blockFilter = []) { } public function getName(): string { - return VanillaBlockComponents::PLACEMENT_FILTER; + return 'minecraft:placement_filter'; } public function getValue(): array { diff --git a/src/customiesdevs/customies/block/component/RandomOffsetComponent.php b/src/block/component/RandomOffsetComponent.php similarity index 89% rename from src/customiesdevs/customies/block/component/RandomOffsetComponent.php rename to src/block/component/RandomOffsetComponent.php index 93b23fd4..109a7dce 100644 --- a/src/customiesdevs/customies/block/component/RandomOffsetComponent.php +++ b/src/block/component/RandomOffsetComponent.php @@ -11,7 +11,7 @@ public function __construct() { } public function getName(): string { - return VanillaBlockComponents::RANDOM_OFFSET; + return 'minecraft:random_offset'; } public function getValue(): array { diff --git a/src/customiesdevs/customies/block/component/RedstoneConductivityComponent.php b/src/block/component/RedstoneConductivityComponent.php similarity index 95% rename from src/customiesdevs/customies/block/component/RedstoneConductivityComponent.php rename to src/block/component/RedstoneConductivityComponent.php index b6aa37c3..d592c1d1 100644 --- a/src/customiesdevs/customies/block/component/RedstoneConductivityComponent.php +++ b/src/block/component/RedstoneConductivityComponent.php @@ -18,7 +18,7 @@ public function __construct(bool $allowsWireToStepDown = true, bool $redstoneCon } public function getName(): string { - return VanillaBlockComponents::REDSTONE_CONDUCTIVITY; + return 'minecraft:redstone_conductivity'; } public function getValue(): array { diff --git a/src/customiesdevs/customies/block/component/SelectionBoxComponent.php b/src/block/component/SelectionBoxComponent.php similarity index 97% rename from src/customiesdevs/customies/block/component/SelectionBoxComponent.php rename to src/block/component/SelectionBoxComponent.php index 44cf2ff4..0cc983e1 100644 --- a/src/customiesdevs/customies/block/component/SelectionBoxComponent.php +++ b/src/block/component/SelectionBoxComponent.php @@ -23,7 +23,7 @@ public function __construct(bool $useSelectionBox = true, ?Vector3 $origin = new } public function getName(): string { - return VanillaBlockComponents::SELECTION_BOX; + return 'minecraft:selection_box'; } public function getValue(): array { diff --git a/src/customiesdevs/customies/block/permutations/BlockProperty.php b/src/block/permutations/BlockProperty.php similarity index 100% rename from src/customiesdevs/customies/block/permutations/BlockProperty.php rename to src/block/permutations/BlockProperty.php diff --git a/src/customiesdevs/customies/block/permutations/Permutable.php b/src/block/permutations/Permutable.php similarity index 100% rename from src/customiesdevs/customies/block/permutations/Permutable.php rename to src/block/permutations/Permutable.php diff --git a/src/customiesdevs/customies/block/permutations/Permutation.php b/src/block/permutations/Permutation.php similarity index 100% rename from src/customiesdevs/customies/block/permutations/Permutation.php rename to src/block/permutations/Permutation.php diff --git a/src/customiesdevs/customies/block/permutations/Permutations.php b/src/block/permutations/Permutations.php similarity index 100% rename from src/customiesdevs/customies/block/permutations/Permutations.php rename to src/block/permutations/Permutations.php diff --git a/src/customiesdevs/customies/block/permutations/RotatableTrait.php b/src/block/permutations/RotatableTrait.php similarity index 100% rename from src/customiesdevs/customies/block/permutations/RotatableTrait.php rename to src/block/permutations/RotatableTrait.php diff --git a/src/customiesdevs/customies/block/properties/Material.php b/src/block/properties/Material.php similarity index 100% rename from src/customiesdevs/customies/block/properties/Material.php rename to src/block/properties/Material.php diff --git a/src/customiesdevs/customies/block/properties/RenderMethod.php b/src/block/properties/RenderMethod.php similarity index 100% rename from src/customiesdevs/customies/block/properties/RenderMethod.php rename to src/block/properties/RenderMethod.php diff --git a/src/customiesdevs/customies/block/properties/TintMethod.php b/src/block/properties/TintMethod.php similarity index 100% rename from src/customiesdevs/customies/block/properties/TintMethod.php rename to src/block/properties/TintMethod.php diff --git a/src/customiesdevs/customies/block/traits/BlockComponentsTrait.php b/src/block/traits/BlockComponentsTrait.php similarity index 100% rename from src/customiesdevs/customies/block/traits/BlockComponentsTrait.php rename to src/block/traits/BlockComponentsTrait.php diff --git a/src/customiesdevs/customies/block/traits/DefaultTrait.php b/src/block/traits/DefaultTrait.php similarity index 100% rename from src/customiesdevs/customies/block/traits/DefaultTrait.php rename to src/block/traits/DefaultTrait.php diff --git a/src/customiesdevs/customies/block/CustomiesBlock.php b/src/customiesdevs/customies/block/CustomiesBlock.php deleted file mode 100644 index 5ff2e485..00000000 --- a/src/customiesdevs/customies/block/CustomiesBlock.php +++ /dev/null @@ -1,46 +0,0 @@ - $componentData) { - $componentClass = VanillaBlockComponents::getClass($componentName) ?? null; - if ($componentClass !== null && method_exists($componentClass, 'fromJson')) { - $this->addComponent($componentClass::fromJson($componentData)); - } - - if($this->hasComponent("customies:block_break_info")) { - $breakInfo = $this->getComponent("customies:block_break_info"); - if($breakInfo instanceof BlockBreakInfoComponent) { - $hardness = $breakInfo->getValue()["hardness"]; - $toolType = $breakInfo->getValue()["tool_type"]; - $toolHarvestLevel = $breakInfo->getValue()["tool_harvest_level"]; - $blastResistance = $breakInfo->getValue()["blast_resistance"]; - } - } - } - parent::__construct( - new BlockIdentifier(BlockTypeIds::newId()), - "Custom Block", - new BlockTypeInfo(new BlockBreakInfo( - $hardness ?? 1.0, - $toolType ?? BlockToolType::NONE, - $toolHarvestLevel ?? 0, - $blastResistance ?? null - )) - ); - } -} \ No newline at end of file diff --git a/src/customiesdevs/customies/block/component/VanillaBlockComponents.php b/src/customiesdevs/customies/block/component/VanillaBlockComponents.php deleted file mode 100644 index d1dd6808..00000000 --- a/src/customiesdevs/customies/block/component/VanillaBlockComponents.php +++ /dev/null @@ -1,165 +0,0 @@ - FQCN implementing BlockComponent. - * @var array> - */ - private static array $classMap = [ - self::COLLISION_BOX => CollisionBoxComponent::class, - self::CRAFTING_TABLE => CraftingTableComponent::class, - self::DESTRUCTIBLE_BY_EXPLOSION => DestructibleByExplosionComponent::class, - self::DESTRUCTIBLE_BY_MINING => DestructibleByMiningComponent::class, - self::DESTRUCTION_PARTICLES => DestructionParticlesComponent::class, - self::DISPLAY_NAME => DisplayNameComponent::class, - self::FLAMMABLE => FlammableComponent::class, - self::FRICTION => FrictionComponent::class, - self::GEOMETRY => GeometryComponent::class, - self::ITEM_VISUAL => ItemVisualComponent::class, - self::LIGHT_DAMPENING => LightDampeningComponent::class, - self::LIGHT_EMISSION => LightEmissionComponent::class, - self::LIQUID_DETECTION => LiquidDetectionComponent::class, - self::LOOT => LootComponent::class, - self::MAP_COLOR => MapColorComponent::class, - self::MATERIAL_INSTANCES => MaterialInstancesComponent::class, - self::MOVABLE => MovableComponent::class, - self::PLACEMENT_FILTER => PlacementFilterComponent::class, - self::RANDOM_OFFSET => RandomOffsetComponent::class, - self::REDSTONE_CONDUCTIVITY => RedstoneConductivityComponent::class, - self::SELECTION_BOX => SelectionBoxComponent::class, - ]; - - private function __construct() {} - - /** - * Get all component identifiers. - * @return string[] - */ - public static function getAll(): array { - return self::ALL; - } - - /** - * Check if the given identifier is a known component. - * @param string $identifier Component identifier - * @return bool True if the identifier is known, false otherwise. - */ - public static function isValid(string $identifier): bool { - return in_array($identifier, self::ALL, true) || isset(self::$classMap[$identifier]); - } - - /** - * Returns the component class for the identifier if registered, null otherwise. - * @param string $identifier Component identifier - * @return class-string|null - */ - public static function getClass(string $identifier): ?string { - return self::$classMap[$identifier] ?? null; - } - - /** - * Determine if a component class exists for the identifier. - * @param string $identifier Component identifier - * @return bool True if a class is registered for the identifier, false otherwise. - */ - public static function hasClass(string $identifier): bool { - return isset(self::$classMap[$identifier]); - } - - /** - * Register or override a component class at runtime. - * @param string $identifier Component identifier (must be one of the known constants) - * @param class-string $fqcn Fully qualified class name implementing BlockComponent - */ - public static function registerComponent(string $identifier, string $fqcn): void { - if (!self::isValid($identifier)) { - throw new \InvalidArgumentException("Unknown component id: $identifier"); - } - if (!is_subclass_of($fqcn, BlockComponent::class)) { - throw new \InvalidArgumentException("Class $fqcn must implement " . BlockComponent::class); - } - self::$classMap[$identifier] = $fqcn; - } - - /** - * Register a custom (non-vanilla) component identifier to a class at runtime. - * This allows plugins to introduce new components beyond the built-in list. - * - * Rules: - * - Identifier must follow a simple namespaced pattern (e.g. "myplugin:my_component") - * - Identifier must not be one of the known vanilla identifiers (use registerClass() to override those) - * - The class must implement BlockComponent - * - * @param string $identifier Custom component identifier - * @param class-string $fqcn Fully qualified class name implementing BlockComponent - */ - public static function registerCustomComponent(string $identifier, string $fqcn): void { - // Basic identifier format check: namespace:name (lowercase, digits, underscore, dash, dot, slash) - if(!preg_match('/^[a-z0-9_\-\.]+:[a-z0-9_\-\.\/]+$/', $identifier)) { - throw new \InvalidArgumentException("Invalid component identifier format: $identifier"); - } - // Reserve minecraft namespace for vanilla identifiers - if(str_starts_with($identifier, 'minecraft:')) { - throw new \InvalidArgumentException("The namespace 'minecraft' is reserved for vanilla component identifiers. Use registerClass() to override existing ones."); - } - if (!is_subclass_of($fqcn, BlockComponent::class)) { - throw new \InvalidArgumentException("Class $fqcn must implement " . BlockComponent::class); - } - // Register or override existing custom mapping - self::$classMap[$identifier] = $fqcn; - } -} \ No newline at end of file diff --git a/src/customiesdevs/customies/block/component/custom/BlockBreakInfoComponent.php b/src/customiesdevs/customies/block/component/custom/BlockBreakInfoComponent.php deleted file mode 100644 index d0304af8..00000000 --- a/src/customiesdevs/customies/block/component/custom/BlockBreakInfoComponent.php +++ /dev/null @@ -1,82 +0,0 @@ -hardness = $hardness; - $this->toolType = $toolType; - $this->toolHarvestLevel = $toolHarvestLevel; - $this->blastResistance = $blastResistance ?? $hardness * 5; - } - - public function getName(): string { - return "customies:block_break_info"; - } - - public function getValue(): array { - switch($this->toolType) { - case self::NONE: - $toolType = BlockToolType::NONE; - break; - case self::SWORD: - $toolType = BlockToolType::SWORD; - break; - case self::SHOVEL: - $toolType = BlockToolType::SHOVEL; - break; - case self::PICKAXE: - $toolType = BlockToolType::PICKAXE; - break; - case self::AXE: - $toolType = BlockToolType::AXE; - break; - case self::SHEARS: - $toolType = BlockToolType::SHEARS; - break; - case self::HOE: - $toolType = BlockToolType::HOE; - break; - default: - throw new \InvalidArgumentException("Invalid tool type: " . $this->toolType); - } - - return [ - "hardness" => $this->hardness, - "tool_type" => $toolType, - "tool_harvest_level" => $this->toolHarvestLevel, - "blast_resistance" => $this->blastResistance, - ]; - } - - public static function fromJson(mixed $data): static { - return new self( - $data["hardness"] ?? 1.0, - $data["tool_type"] ?? self::NONE, - $data["tool_harvest_level"] ?? 0, - $data["blast_resistance"] ?? null - ); - } -} \ No newline at end of file diff --git a/src/customiesdevs/customies/item/CustomiesItem.php b/src/customiesdevs/customies/item/CustomiesItem.php deleted file mode 100644 index ea692e47..00000000 --- a/src/customiesdevs/customies/item/CustomiesItem.php +++ /dev/null @@ -1,24 +0,0 @@ - $componentData) { - $componentClass = VanillaItemComponents::getClass($componentName) ?? null; - if ($componentClass !== null && method_exists($componentClass, 'fromJson')) { - $this->addComponent($componentClass::fromJson($componentData)); - } - } - } -} \ No newline at end of file diff --git a/src/customiesdevs/customies/item/component/VanillaItemComponents.php b/src/customiesdevs/customies/item/component/VanillaItemComponents.php deleted file mode 100644 index 2cf8760f..00000000 --- a/src/customiesdevs/customies/item/component/VanillaItemComponents.php +++ /dev/null @@ -1,217 +0,0 @@ - FQCN implementing ItemComponent. - * @var array> - */ - private static array $classMap = [ - self::ALLOW_OFF_HAND => AllowOffHandComponent::class, - self::BLOCK_PLACER => BlockPlacerComponent::class, - self::BUNDLE_INTERACTION => BundleInteractionComponent::class, - self::CAN_DESTROY_IN_CREATIVE => CanDestroyInCreativeComponent::class, - self::COMPOSTABLE => CompostableComponent::class, - self::COOLDOWN => CooldownComponent::class, - self::DAMAGE_ABSORPTION => DamageAbsorptionComponent::class, - self::DAMAGE => DamageComponent::class, - self::DIGGER => DiggerComponent::class, - self::DISPLAY_NAME => DisplayNameComponent::class, - self::DURABILITY => DurabilityComponent::class, - self::DURABILITY_SENSOR => DurabilitySensorComponent::class, - self::DYEABLE => DyeableComponent::class, - self::ENCHANTABLE => EnchantableComponent::class, - self::FIRE_RESISTANT => FireResistantComponent::class, - self::FOOD => FoodComponent::class, - self::FUEL => FuelComponent::class, - self::GLINT => GlintComponent::class, - self::HAND_EQUIPPED => HandEquippedComponent::class, - self::HOVER_TEXT_COLOR => HoverTextColorComponent::class, - self::ICON => IconComponent::class, - self::INTERACT_BUTTON => InteractButtonComponent::class, - self::LIQUID_CLIPPED => LiquidClippedComponent::class, - self::MAX_STACK_SIZE => MaxStackSizeComponent::class, - self::PROJECTILE => ProjectileComponent::class, - self::RARITY => RarityComponent::class, - self::RECORD => RecordComponent::class, - self::REPAIRABLE => RepairableComponent::class, - self::SHOOTER => ShooterComponent::class, - self::SHOULD_DESPAWN => ShouldDespawnComponent::class, - self::STACKED_BY_DATA => StackedByDataComponent::class, - self::STORAGE_ITEM => StorageItemComponent::class, - self::STORAGE_WEIGHT_LIMIT => StorageWeightLimitComponent::class, - self::STORAGE_WEIGHT_MODIFIER => StorageWeightModifierComponent::class, - self::SWING_DURATION => SwingDurationComponent::class, - self::TAGS => TagsComponent::class, - self::THROWABLE => ThrowableComponent::class, - self::USE_ANIMATION => UseAnimationComponent::class, - self::USE_MODIFIERS => UseModifiersComponent::class, - self::WEARABLE => WearableComponent::class, - ]; - - private function __construct() {} - - /** - * Get all component identifiers. - * @return string[] - */ - public static function getAll(): array { - return self::ALL; - } - - /** - * Check if the given identifier is a known component. - * @param string $identifier Component identifier - * @return bool True if the identifier is known, false otherwise. - */ - public static function isValid(string $identifier): bool { - return in_array($identifier, self::ALL, true) || isset(self::$classMap[$identifier]); - } - - /** - * Returns the component class for the identifier if registered, null otherwise. - * @param string $identifier Component identifier - * @return class-string|null - */ - public static function getClass(string $identifier): ?string { - return self::$classMap[$identifier] ?? null; - } - - /** - * Determine if a component class exists for the identifier. - * @param string $identifier Component identifier - * @return bool True if a class is registered for the identifier, false otherwise. - */ - public static function hasClass(string $identifier): bool { - return isset(self::$classMap[$identifier]); - } - - /** - * Register or override a component class at runtime. - * @param string $identifier Component identifier (must be one of the known constants) - * @param class-string $fqcn Fully qualified class name implementing ItemComponent - */ - public static function registerClass(string $identifier, string $fqcn): void { - if (!self::isValid($identifier)) { - throw new \InvalidArgumentException("Unknown component id: $identifier"); - } - if (!is_subclass_of($fqcn, ItemComponent::class)) { - throw new \InvalidArgumentException("Class $fqcn must implement " . ItemComponent::class); - } - self::$classMap[$identifier] = $fqcn; - } - - /** - * Register a custom (non-vanilla) component identifier to a class at runtime. - * This allows plugins to introduce new components beyond the built-in list. - * - * Rules: - * - Identifier must follow a simple namespaced pattern (e.g. "myplugin:my_component") - * - Identifier must not be one of the known vanilla identifiers (use registerClass() to override those) - * - Class must implement ItemComponent - * - * @param string $identifier Custom component identifier - * @param class-string $fqcn Fully qualified class name implementing ItemComponent - */ - public static function registerCustomComponent(string $identifier, string $fqcn): void { - // Basic identifier format check: namespace:name (lowercase, digits, underscore, dash, dot, slash) - if(!preg_match('/^[a-z0-9_\-\.]+:[a-z0-9_\-\.\/]+$/', $identifier)) { - throw new \InvalidArgumentException("Invalid component identifier format: $identifier"); - } - // Reserve minecraft namespace for vanilla identifiers - if(str_starts_with($identifier, 'minecraft:')) { - throw new \InvalidArgumentException("The namespace 'minecraft' is reserved for vanilla component identifiers. Use registerClass() to override existing ones."); - } - if (!is_subclass_of($fqcn, ItemComponent::class)) { - throw new \InvalidArgumentException("Class $fqcn must implement " . ItemComponent::class); - } - // Register or override existing custom mapping - self::$classMap[$identifier] = $fqcn; - } -} \ No newline at end of file diff --git a/src/customiesdevs/customies/entity/CustomiesEntityFactory.php b/src/entity/CustomiesEntityFactory.php similarity index 100% rename from src/customiesdevs/customies/entity/CustomiesEntityFactory.php rename to src/entity/CustomiesEntityFactory.php diff --git a/src/customiesdevs/customies/item/CreativeInventoryInfo.php b/src/item/CreativeInventoryInfo.php similarity index 100% rename from src/customiesdevs/customies/item/CreativeInventoryInfo.php rename to src/item/CreativeInventoryInfo.php diff --git a/src/item/CustomiesItem.php b/src/item/CustomiesItem.php new file mode 100644 index 00000000..f8ee57ae --- /dev/null +++ b/src/item/CustomiesItem.php @@ -0,0 +1,110 @@ +> + */ + private static array $componentRegistry = [ + 'minecraft:allow_off_hand' => AllowOffHandComponent::class, + 'minecraft:block_placer' => BlockPlacerComponent::class, + 'minecraft:bundle_interaction' => BundleInteractionComponent::class, + 'minecraft:can_destroy_in_creative' => CanDestroyInCreativeComponent::class, + 'minecraft:compostable' => CompostableComponent::class, + 'minecraft:cooldown' => CooldownComponent::class, + 'minecraft:damage_absorption' => DamageAbsorptionComponent::class, + 'minecraft:damage' => DamageComponent::class, + 'minecraft:digger' => DiggerComponent::class, + 'minecraft:display_name' => DisplayNameComponent::class, + 'minecraft:durability' => DurabilityComponent::class, + 'minecraft:durability_sensor' => DurabilitySensorComponent::class, + 'minecraft:dyeable' => DyeableComponent::class, + 'minecraft:enchantable' => EnchantableComponent::class, + 'minecraft:fire_resistant' => FireResistantComponent::class, + 'minecraft:food' => FoodComponent::class, + 'minecraft:fuel' => FuelComponent::class, + 'minecraft:glint' => GlintComponent::class, + 'minecraft:hand_equipped' => HandEquippedComponent::class, + 'minecraft:hover_text_color' => HoverTextColorComponent::class, + 'minecraft:icon' => IconComponent::class, + 'minecraft:interact_button' => InteractButtonComponent::class, + 'minecraft:liquid_clipped' => LiquidClippedComponent::class, + 'minecraft:max_stack_size' => MaxStackSizeComponent::class, + 'minecraft:projectile' => ProjectileComponent::class, + 'minecraft:rarity' => RarityComponent::class, + 'minecraft:record' => RecordComponent::class, + 'minecraft:repairable' => RepairableComponent::class, + 'minecraft:shooter' => ShooterComponent::class, + 'minecraft:should_despawn' => ShouldDespawnComponent::class, + 'minecraft:stacked_by_data' => StackedByDataComponent::class, + 'minecraft:storage_item' => StorageItemComponent::class, + 'minecraft:storage_weight_limit' => StorageWeightLimitComponent::class, + 'minecraft:storage_weight_modifier' => StorageWeightModifierComponent::class, + 'minecraft:swing_duration' => SwingDurationComponent::class, + 'minecraft:tags' => TagsComponent::class, + 'minecraft:throwable' => ThrowableComponent::class, + 'minecraft:use_animation' => UseAnimationComponent::class, + 'minecraft:use_modifiers' => UseModifiersComponent::class, + 'minecraft:wearable' => WearableComponent::class, + ]; + + public function __construct(array $components) { + parent::__construct(new ItemIdentifier(ItemTypeIds::newId())); + foreach ($components as $componentName => $componentData) { + $componentClass = self::$componentRegistry[$componentName] ?? null; + if ($componentClass !== null && method_exists($componentClass, 'fromJson')) { + $this->addComponent($componentClass::fromJson($componentData)); + } + } + } +} \ No newline at end of file diff --git a/src/customiesdevs/customies/item/CustomiesItemFactory.php b/src/item/CustomiesItemFactory.php similarity index 100% rename from src/customiesdevs/customies/item/CustomiesItemFactory.php rename to src/item/CustomiesItemFactory.php diff --git a/src/customiesdevs/customies/item/ItemComponents.php b/src/item/ItemComponents.php similarity index 100% rename from src/customiesdevs/customies/item/ItemComponents.php rename to src/item/ItemComponents.php diff --git a/src/customiesdevs/customies/item/ItemManager.php b/src/item/ItemManager.php similarity index 100% rename from src/customiesdevs/customies/item/ItemManager.php rename to src/item/ItemManager.php diff --git a/src/customiesdevs/customies/item/component/AllowOffHandComponent.php b/src/item/component/AllowOffHandComponent.php similarity index 92% rename from src/customiesdevs/customies/item/component/AllowOffHandComponent.php rename to src/item/component/AllowOffHandComponent.php index 1f08c4a7..4736873b 100644 --- a/src/customiesdevs/customies/item/component/AllowOffHandComponent.php +++ b/src/item/component/AllowOffHandComponent.php @@ -16,7 +16,7 @@ public function __construct(bool $offHand = true) { } public function getName(): string { - return VanillaItemComponents::ALLOW_OFF_HAND; + return 'minecraft:allow_off_hand'; } public function getValue(): array { diff --git a/src/customiesdevs/customies/item/component/BlockPlacerComponent.php b/src/item/component/BlockPlacerComponent.php similarity index 97% rename from src/customiesdevs/customies/item/component/BlockPlacerComponent.php rename to src/item/component/BlockPlacerComponent.php index 948135c9..f0b81f43 100644 --- a/src/customiesdevs/customies/item/component/BlockPlacerComponent.php +++ b/src/item/component/BlockPlacerComponent.php @@ -27,7 +27,7 @@ public function __construct(Block $block, bool $replaceBlockItem = false) { } public function getName(): string { - return VanillaItemComponents::BLOCK_PLACER; + return 'minecraft:block_placer'; } public function getValue(): array { diff --git a/src/customiesdevs/customies/item/component/BundleInteractionComponent.php b/src/item/component/BundleInteractionComponent.php similarity index 88% rename from src/customiesdevs/customies/item/component/BundleInteractionComponent.php rename to src/item/component/BundleInteractionComponent.php index 59cdb802..63a6b80a 100644 --- a/src/customiesdevs/customies/item/component/BundleInteractionComponent.php +++ b/src/item/component/BundleInteractionComponent.php @@ -10,14 +10,14 @@ final class BundleInteractionComponent implements ItemComponent { /** * Enables the bundle-specific interaction scheme and tooltip for an item. * To use this component, the item must have a `minecraft:storage_item` item component defined. - * @param int $numViewableSlots The maximum number of slots in the bundle viewable by the plater. Can be from 1 to 64. Default is 12. Value must be >= 1. Value must be <= 64. + * @param int $numViewableSlots The maximum number of slots in the bundle viewable by the player. Can be from 1 to 64. Default is 12. Value must be >= 1. Value must be <= 64. */ public function __construct(int $numViewableSlots = 12) { $this->numViewableSlots = $numViewableSlots; } public function getName(): string { - return VanillaItemComponents::BUNDLE_INTERACTION; + return 'minecraft:bundle_interaction'; } public function getValue(): array { diff --git a/src/customiesdevs/customies/item/component/CanDestroyInCreativeComponent.php b/src/item/component/CanDestroyInCreativeComponent.php similarity index 92% rename from src/customiesdevs/customies/item/component/CanDestroyInCreativeComponent.php rename to src/item/component/CanDestroyInCreativeComponent.php index da7bc968..9207203b 100644 --- a/src/customiesdevs/customies/item/component/CanDestroyInCreativeComponent.php +++ b/src/item/component/CanDestroyInCreativeComponent.php @@ -16,7 +16,7 @@ public function __construct(bool $canDestroyInCreative = true) { } public function getName(): string { - return VanillaItemComponents::CAN_DESTROY_IN_CREATIVE; + return 'minecraft:can_destroy_in_creative'; } public function getValue(): array { diff --git a/src/customiesdevs/customies/item/component/CompostableComponent.php b/src/item/component/CompostableComponent.php similarity index 95% rename from src/customiesdevs/customies/item/component/CompostableComponent.php rename to src/item/component/CompostableComponent.php index 87de6f07..2315aca8 100644 --- a/src/customiesdevs/customies/item/component/CompostableComponent.php +++ b/src/item/component/CompostableComponent.php @@ -16,7 +16,7 @@ public function __construct(int $compostingChance) { } public function getName(): string { - return VanillaItemComponents::COMPOSTABLE; + return 'minecraft:compostable'; } public function getValue(): array { diff --git a/src/customiesdevs/customies/item/component/CooldownComponent.php b/src/item/component/CooldownComponent.php similarity index 96% rename from src/customiesdevs/customies/item/component/CooldownComponent.php rename to src/item/component/CooldownComponent.php index 4d416cd2..06baa327 100644 --- a/src/customiesdevs/customies/item/component/CooldownComponent.php +++ b/src/item/component/CooldownComponent.php @@ -25,7 +25,7 @@ public function __construct(string $category, float $duration) { } public function getName(): string { - return VanillaItemComponents::COOLDOWN; + return 'minecraft:cooldown'; } public function getValue(): array { diff --git a/src/customiesdevs/customies/item/component/DamageAbsorptionComponent.php b/src/item/component/DamageAbsorptionComponent.php similarity index 95% rename from src/customiesdevs/customies/item/component/DamageAbsorptionComponent.php rename to src/item/component/DamageAbsorptionComponent.php index 26df5782..966d5a51 100644 --- a/src/customiesdevs/customies/item/component/DamageAbsorptionComponent.php +++ b/src/item/component/DamageAbsorptionComponent.php @@ -19,7 +19,7 @@ public function __construct(array $absorbableCauses) { } public function getName(): string { - return VanillaItemComponents::DAMAGE_ABSORPTION; + return 'minecraft:damage_absorption'; } public function getValue(): array { diff --git a/src/customiesdevs/customies/item/component/DamageComponent.php b/src/item/component/DamageComponent.php similarity index 94% rename from src/customiesdevs/customies/item/component/DamageComponent.php rename to src/item/component/DamageComponent.php index f07099d1..5d8fa99e 100644 --- a/src/customiesdevs/customies/item/component/DamageComponent.php +++ b/src/item/component/DamageComponent.php @@ -17,7 +17,7 @@ public function __construct(int $damage) { } public function getName(): string { - return VanillaItemComponents::DAMAGE; + return 'minecraft:damage'; } public function getValue(): array { diff --git a/src/customiesdevs/customies/item/component/DiggerComponent.php b/src/item/component/DiggerComponent.php similarity index 98% rename from src/customiesdevs/customies/item/component/DiggerComponent.php rename to src/item/component/DiggerComponent.php index 6063a0fc..7441bb00 100644 --- a/src/customiesdevs/customies/item/component/DiggerComponent.php +++ b/src/item/component/DiggerComponent.php @@ -23,7 +23,7 @@ public function __construct(bool $useEfficiency, array $destroySpeeds = []) { } public function getName(): string { - return VanillaItemComponents::DIGGER; + return 'minecraft:digger'; } public function getValue(): array { diff --git a/src/customiesdevs/customies/item/component/DisplayNameComponent.php b/src/item/component/DisplayNameComponent.php similarity index 93% rename from src/customiesdevs/customies/item/component/DisplayNameComponent.php rename to src/item/component/DisplayNameComponent.php index ab802c64..5824e2d5 100644 --- a/src/customiesdevs/customies/item/component/DisplayNameComponent.php +++ b/src/item/component/DisplayNameComponent.php @@ -17,7 +17,7 @@ public function __construct(string $name) { } public function getName(): string { - return VanillaItemComponents::DISPLAY_NAME; + return 'minecraft:display_name'; } public function getValue(): array { diff --git a/src/customiesdevs/customies/item/component/DurabilityComponent.php b/src/item/component/DurabilityComponent.php similarity index 97% rename from src/customiesdevs/customies/item/component/DurabilityComponent.php rename to src/item/component/DurabilityComponent.php index 629de59e..b5cce005 100644 --- a/src/customiesdevs/customies/item/component/DurabilityComponent.php +++ b/src/item/component/DurabilityComponent.php @@ -22,7 +22,7 @@ public function __construct(int $maxDurability, int $minDamageChance = 100, int } public function getName(): string { - return VanillaItemComponents::DURABILITY; + return 'minecraft:durability'; } public function getValue(): array { diff --git a/src/customiesdevs/customies/item/component/DurabilitySensorComponent.php b/src/item/component/DurabilitySensorComponent.php similarity index 95% rename from src/customiesdevs/customies/item/component/DurabilitySensorComponent.php rename to src/item/component/DurabilitySensorComponent.php index 4380db05..05d72c1f 100644 --- a/src/customiesdevs/customies/item/component/DurabilitySensorComponent.php +++ b/src/item/component/DurabilitySensorComponent.php @@ -16,7 +16,7 @@ public function __construct(array $durabilityThresholds = []) { } public function getName(): string { - return VanillaItemComponents::DURABILITY_SENSOR; + return 'minecraft:durability_sensor'; } public function getValue(): array { diff --git a/src/customiesdevs/customies/item/component/DyeableComponent.php b/src/item/component/DyeableComponent.php similarity index 94% rename from src/customiesdevs/customies/item/component/DyeableComponent.php rename to src/item/component/DyeableComponent.php index c6d49b1e..9aa6733f 100644 --- a/src/customiesdevs/customies/item/component/DyeableComponent.php +++ b/src/item/component/DyeableComponent.php @@ -16,7 +16,7 @@ public function __construct(string $hex) { } public function getName(): string { - return VanillaItemComponents::DYEABLE; + return 'minecraft:dyeable'; } public function getValue(): array { diff --git a/src/customiesdevs/customies/item/component/EnchantableComponent.php b/src/item/component/EnchantableComponent.php similarity index 98% rename from src/customiesdevs/customies/item/component/EnchantableComponent.php rename to src/item/component/EnchantableComponent.php index 7877d5bb..8824805d 100644 --- a/src/customiesdevs/customies/item/component/EnchantableComponent.php +++ b/src/item/component/EnchantableComponent.php @@ -56,7 +56,7 @@ public function __construct(string $slot = self::SLOT_ALL, int $value = 1) { } public function getName(): string { - return VanillaItemComponents::ENCHANTABLE; + return 'minecraft:enchantable'; } public function getValue(): array { diff --git a/src/customiesdevs/customies/item/component/FireResistantComponent.php b/src/item/component/FireResistantComponent.php similarity index 93% rename from src/customiesdevs/customies/item/component/FireResistantComponent.php rename to src/item/component/FireResistantComponent.php index c6d46042..b35a5290 100644 --- a/src/customiesdevs/customies/item/component/FireResistantComponent.php +++ b/src/item/component/FireResistantComponent.php @@ -16,7 +16,7 @@ public function __construct(bool $fireResistant = true) { } public function getName(): string { - return VanillaItemComponents::FIRE_RESISTANT; + return 'minecraft:fire_resistant'; } public function getValue(): array { diff --git a/src/customiesdevs/customies/item/component/FoodComponent.php b/src/item/component/FoodComponent.php similarity index 93% rename from src/customiesdevs/customies/item/component/FoodComponent.php rename to src/item/component/FoodComponent.php index 2a9d614b..78bc180e 100644 --- a/src/customiesdevs/customies/item/component/FoodComponent.php +++ b/src/item/component/FoodComponent.php @@ -25,7 +25,7 @@ public function __construct(bool $canAlwaysEat = false, int $nutrition = 0, floa } public function getName(): string { - return VanillaItemComponents::FOOD; + return 'minecraft:food'; } public function getValue(): array { @@ -33,9 +33,7 @@ public function getValue(): array { "can_always_eat" => $this->canAlwaysEat, "nutrition" => $this->nutrition, "saturation_modifier" => $this->saturationModifier, - "using_converts_to" => [ - "name" => $this->usingConvertsTo - ] + "using_converts_to" => $this->usingConvertsTo ]; } diff --git a/src/customiesdevs/customies/item/component/FuelComponent.php b/src/item/component/FuelComponent.php similarity index 94% rename from src/customiesdevs/customies/item/component/FuelComponent.php rename to src/item/component/FuelComponent.php index 5da2781c..db78bdc7 100644 --- a/src/customiesdevs/customies/item/component/FuelComponent.php +++ b/src/item/component/FuelComponent.php @@ -16,7 +16,7 @@ public function __construct(float $duration) { } public function getName(): string { - return VanillaItemComponents::FUEL; + return 'minecraft:fuel'; } public function getValue(): array { diff --git a/src/customiesdevs/customies/item/component/GlintComponent.php b/src/item/component/GlintComponent.php similarity index 93% rename from src/customiesdevs/customies/item/component/GlintComponent.php rename to src/item/component/GlintComponent.php index dce501ab..af597d8a 100644 --- a/src/customiesdevs/customies/item/component/GlintComponent.php +++ b/src/item/component/GlintComponent.php @@ -16,7 +16,7 @@ public function __construct(bool $glint = true) { } public function getName(): string { - return VanillaItemComponents::GLINT; + return 'minecraft:glint'; } public function getValue(): array { diff --git a/src/customiesdevs/customies/item/component/HandEquippedComponent.php b/src/item/component/HandEquippedComponent.php similarity index 93% rename from src/customiesdevs/customies/item/component/HandEquippedComponent.php rename to src/item/component/HandEquippedComponent.php index 65b6e09e..5ce70c61 100644 --- a/src/customiesdevs/customies/item/component/HandEquippedComponent.php +++ b/src/item/component/HandEquippedComponent.php @@ -16,7 +16,7 @@ public function __construct(bool $handEquipped = true) { } public function getName(): string { - return VanillaItemComponents::HAND_EQUIPPED; + return 'minecraft:hand_equipped'; } public function getValue(): array { diff --git a/src/customiesdevs/customies/item/component/HoverTextColorComponent.php b/src/item/component/HoverTextColorComponent.php similarity index 93% rename from src/customiesdevs/customies/item/component/HoverTextColorComponent.php rename to src/item/component/HoverTextColorComponent.php index 22a947eb..3ea51336 100644 --- a/src/customiesdevs/customies/item/component/HoverTextColorComponent.php +++ b/src/item/component/HoverTextColorComponent.php @@ -17,7 +17,7 @@ public function __construct(string $hoverTextColor) { } public function getName(): string { - return VanillaItemComponents::HOVER_TEXT_COLOR; + return 'minecraft:hover_text_color'; } public function getValue(): array { diff --git a/src/customiesdevs/customies/item/component/IconComponent.php b/src/item/component/IconComponent.php similarity index 87% rename from src/customiesdevs/customies/item/component/IconComponent.php rename to src/item/component/IconComponent.php index e5b5efac..86c5adbe 100644 --- a/src/customiesdevs/customies/item/component/IconComponent.php +++ b/src/item/component/IconComponent.php @@ -9,7 +9,7 @@ final class IconComponent implements ItemComponent { private string $dyed_texture; private string $trim_texture; private string $bundle_open_back_texture; - private string $bundle_open_front_tetxure; + private string $bundle_open_front_texture; /** * Determines the icon to represent the item in the UI and elsewhere. @@ -30,11 +30,11 @@ public function __construct( $this->dyed_texture = $dyed_texture; $this->trim_texture = $trim_texture; $this->bundle_open_back_texture = $bundle_open_back_texture; - $this->bundle_open_front_tetxure = $bundle_open_front_texture; + $this->bundle_open_front_texture = $bundle_open_front_texture; } public function getName(): string { - return VanillaItemComponents::ICON; + return 'minecraft:icon'; } public function getValue(): array { @@ -44,7 +44,7 @@ public function getValue(): array { "dyed" => $this->dyed_texture == "" ? $this->default_texture : $this->dyed_texture, "icon_trim" => $this->trim_texture == "" ? $this->default_texture : $this->trim_texture, "bundle_open_back" => $this->bundle_open_back_texture == "" ? $this->default_texture : $this->bundle_open_back_texture, - "bundle_open_front" => $this->bundle_open_front_tetxure == "" ? $this->default_texture : $this->bundle_open_front_tetxure + "bundle_open_front" => $this->bundle_open_front_texture == "" ? $this->default_texture : $this->bundle_open_front_texture ] ]; } diff --git a/src/customiesdevs/customies/item/component/InteractButtonComponent.php b/src/item/component/InteractButtonComponent.php similarity index 95% rename from src/customiesdevs/customies/item/component/InteractButtonComponent.php rename to src/item/component/InteractButtonComponent.php index 2a5b1620..38e32ce7 100644 --- a/src/customiesdevs/customies/item/component/InteractButtonComponent.php +++ b/src/item/component/InteractButtonComponent.php @@ -20,7 +20,7 @@ public function __construct(bool|string $interactButton) { } public function getName(): string { - return VanillaItemComponents::INTERACT_BUTTON; + return 'minecraft:interact_button'; } public function getValue(): array { diff --git a/src/customiesdevs/customies/item/component/ItemComponent.php b/src/item/component/ItemComponent.php similarity index 100% rename from src/customiesdevs/customies/item/component/ItemComponent.php rename to src/item/component/ItemComponent.php diff --git a/src/customiesdevs/customies/item/component/LiquidClippedComponent.php b/src/item/component/LiquidClippedComponent.php similarity index 93% rename from src/customiesdevs/customies/item/component/LiquidClippedComponent.php rename to src/item/component/LiquidClippedComponent.php index e599fe35..ec0dca53 100644 --- a/src/customiesdevs/customies/item/component/LiquidClippedComponent.php +++ b/src/item/component/LiquidClippedComponent.php @@ -16,7 +16,7 @@ public function __construct(bool $liquidClipped = true) { } public function getName(): string { - return VanillaItemComponents::LIQUID_CLIPPED; + return 'minecraft:liquid_clipped'; } public function getValue(): array { diff --git a/src/customiesdevs/customies/item/component/MaxStackSizeComponent.php b/src/item/component/MaxStackSizeComponent.php similarity index 92% rename from src/customiesdevs/customies/item/component/MaxStackSizeComponent.php rename to src/item/component/MaxStackSizeComponent.php index a012f452..1d90b8c3 100644 --- a/src/customiesdevs/customies/item/component/MaxStackSizeComponent.php +++ b/src/item/component/MaxStackSizeComponent.php @@ -16,7 +16,7 @@ public function __construct(int $maxStackSize = 64) { } public function getName(): string { - return VanillaItemComponents::MAX_STACK_SIZE; + return 'minecraft:max_stack_size'; } public function getValue(): array { diff --git a/src/customiesdevs/customies/item/component/ProjectileComponent.php b/src/item/component/ProjectileComponent.php similarity index 96% rename from src/customiesdevs/customies/item/component/ProjectileComponent.php rename to src/item/component/ProjectileComponent.php index f217e8f8..6c624d8b 100644 --- a/src/customiesdevs/customies/item/component/ProjectileComponent.php +++ b/src/item/component/ProjectileComponent.php @@ -21,7 +21,7 @@ public function __construct(float $minimumCriticalPower, string $projectileEntit } public function getName(): string { - return VanillaItemComponents::PROJECTILE; + return 'minecraft:projectile'; } public function getValue(): array { diff --git a/src/customiesdevs/customies/item/component/RarityComponent.php b/src/item/component/RarityComponent.php similarity index 96% rename from src/customiesdevs/customies/item/component/RarityComponent.php rename to src/item/component/RarityComponent.php index fd434978..7094d199 100644 --- a/src/customiesdevs/customies/item/component/RarityComponent.php +++ b/src/item/component/RarityComponent.php @@ -22,7 +22,7 @@ public function __construct(string $rarity = self::COMMON) { } public function getName(): string { - return VanillaItemComponents::RARITY; + return 'minecraft:rarity'; } public function getValue(): array { diff --git a/src/customiesdevs/customies/item/component/RecordComponent.php b/src/item/component/RecordComponent.php similarity index 96% rename from src/customiesdevs/customies/item/component/RecordComponent.php rename to src/item/component/RecordComponent.php index d8d00c4a..bb1870a7 100644 --- a/src/customiesdevs/customies/item/component/RecordComponent.php +++ b/src/item/component/RecordComponent.php @@ -22,7 +22,7 @@ public function __construct(int $comparatorSignal, float $duration, string $soun } public function getName(): string { - return VanillaItemComponents::RECORD; + return 'minecraft:record'; } public function getValue(): array { diff --git a/src/customiesdevs/customies/item/component/RepairableComponent.php b/src/item/component/RepairableComponent.php similarity index 96% rename from src/customiesdevs/customies/item/component/RepairableComponent.php rename to src/item/component/RepairableComponent.php index 44581901..10bc6f8f 100644 --- a/src/customiesdevs/customies/item/component/RepairableComponent.php +++ b/src/item/component/RepairableComponent.php @@ -16,7 +16,7 @@ public function __construct( ) {} public function getName(): string { - return VanillaItemComponents::REPAIRABLE; + return 'minecraft:repairable'; } public function getValue(): array { diff --git a/src/customiesdevs/customies/item/component/ShooterComponent.php b/src/item/component/ShooterComponent.php similarity index 98% rename from src/customiesdevs/customies/item/component/ShooterComponent.php rename to src/item/component/ShooterComponent.php index 5ed9f672..e57aca08 100644 --- a/src/customiesdevs/customies/item/component/ShooterComponent.php +++ b/src/item/component/ShooterComponent.php @@ -36,7 +36,7 @@ public function __construct(string $item, bool $useOffhand = false, bool $search } public function getName(): string { - return VanillaItemComponents::SHOOTER; + return 'minecraft:shooter'; } public function getValue(): array { diff --git a/src/customiesdevs/customies/item/component/ShouldDespawnComponent.php b/src/item/component/ShouldDespawnComponent.php similarity index 93% rename from src/customiesdevs/customies/item/component/ShouldDespawnComponent.php rename to src/item/component/ShouldDespawnComponent.php index 82c7ed6a..3e10697f 100644 --- a/src/customiesdevs/customies/item/component/ShouldDespawnComponent.php +++ b/src/item/component/ShouldDespawnComponent.php @@ -16,7 +16,7 @@ public function __construct(bool $shouldDespawn = true) { } public function getName(): string { - return VanillaItemComponents::SHOULD_DESPAWN; + return 'minecraft:should_despawn'; } public function getValue(): array { diff --git a/src/customiesdevs/customies/item/component/StackedByDataComponent.php b/src/item/component/StackedByDataComponent.php similarity index 93% rename from src/customiesdevs/customies/item/component/StackedByDataComponent.php rename to src/item/component/StackedByDataComponent.php index ee4d9b9d..7c743ecc 100644 --- a/src/customiesdevs/customies/item/component/StackedByDataComponent.php +++ b/src/item/component/StackedByDataComponent.php @@ -17,7 +17,7 @@ public function __construct(bool $stackedByData = true) { } public function getName(): string { - return VanillaItemComponents::STACKED_BY_DATA; + return 'minecraft:stacked_by_data'; } public function getValue(): array { diff --git a/src/customiesdevs/customies/item/component/StorageItemComponent.php b/src/item/component/StorageItemComponent.php similarity index 98% rename from src/customiesdevs/customies/item/component/StorageItemComponent.php rename to src/item/component/StorageItemComponent.php index 903ad8d8..ccff4e0f 100644 --- a/src/customiesdevs/customies/item/component/StorageItemComponent.php +++ b/src/item/component/StorageItemComponent.php @@ -39,7 +39,7 @@ public function __construct( } public function getName(): string { - return VanillaItemComponents::STORAGE_ITEM; + return 'minecraft:storage_item'; } public function getValue(): array { diff --git a/src/customiesdevs/customies/item/component/StorageWeightLimitComponent.php b/src/item/component/StorageWeightLimitComponent.php similarity index 93% rename from src/customiesdevs/customies/item/component/StorageWeightLimitComponent.php rename to src/item/component/StorageWeightLimitComponent.php index c15efbec..1951a1c7 100644 --- a/src/customiesdevs/customies/item/component/StorageWeightLimitComponent.php +++ b/src/item/component/StorageWeightLimitComponent.php @@ -16,7 +16,7 @@ public function __construct(int $maxWeightLimit = 64) { } public function getName(): string { - return VanillaItemComponents::STORAGE_WEIGHT_LIMIT; + return 'minecraft:storage_weight_limit'; } public function getValue(): array { diff --git a/src/customiesdevs/customies/item/component/StorageWeightModifierComponent.php b/src/item/component/StorageWeightModifierComponent.php similarity index 93% rename from src/customiesdevs/customies/item/component/StorageWeightModifierComponent.php rename to src/item/component/StorageWeightModifierComponent.php index 75ebf9d0..57af341f 100644 --- a/src/customiesdevs/customies/item/component/StorageWeightModifierComponent.php +++ b/src/item/component/StorageWeightModifierComponent.php @@ -16,7 +16,7 @@ public function __construct(int $weightInStorageItem = 4) { } public function getName(): string { - return VanillaItemComponents::STORAGE_WEIGHT_MODIFIER; + return 'minecraft:storage_weight_modifier'; } public function getValue(): array { diff --git a/src/customiesdevs/customies/item/component/SwingDurationComponent.php b/src/item/component/SwingDurationComponent.php similarity index 95% rename from src/customiesdevs/customies/item/component/SwingDurationComponent.php rename to src/item/component/SwingDurationComponent.php index 0dec2564..fabf7794 100644 --- a/src/customiesdevs/customies/item/component/SwingDurationComponent.php +++ b/src/item/component/SwingDurationComponent.php @@ -16,7 +16,7 @@ public function __construct(float $swingDuration = 0.3) { } public function getName(): string { - return VanillaItemComponents::SWING_DURATION; + return 'minecraft:swing_duration'; } public function getValue(): array { diff --git a/src/customiesdevs/customies/item/component/TagsComponent.php b/src/item/component/TagsComponent.php similarity index 94% rename from src/customiesdevs/customies/item/component/TagsComponent.php rename to src/item/component/TagsComponent.php index 54eb6af4..8d15fb1e 100644 --- a/src/customiesdevs/customies/item/component/TagsComponent.php +++ b/src/item/component/TagsComponent.php @@ -16,7 +16,7 @@ public function __construct(array $tags = []) { } public function getName(): string { - return VanillaItemComponents::TAGS; + return 'minecraft:tags'; } public function getValue(): array { diff --git a/src/customiesdevs/customies/item/component/ThrowableComponent.php b/src/item/component/ThrowableComponent.php similarity index 98% rename from src/customiesdevs/customies/item/component/ThrowableComponent.php rename to src/item/component/ThrowableComponent.php index 0d6d9904..36ce6884 100644 --- a/src/customiesdevs/customies/item/component/ThrowableComponent.php +++ b/src/item/component/ThrowableComponent.php @@ -38,7 +38,7 @@ public function __construct( } public function getName(): string { - return VanillaItemComponents::THROWABLE; + return 'minecraft:throwable'; } public function getValue(): array { diff --git a/src/customiesdevs/customies/item/component/UseAnimationComponent.php b/src/item/component/UseAnimationComponent.php similarity index 95% rename from src/customiesdevs/customies/item/component/UseAnimationComponent.php rename to src/item/component/UseAnimationComponent.php index 3f58d10c..9c285999 100644 --- a/src/customiesdevs/customies/item/component/UseAnimationComponent.php +++ b/src/item/component/UseAnimationComponent.php @@ -27,7 +27,7 @@ public function __construct(int $animation) { } public function getName(): string { - return VanillaItemComponents::USE_ANIMATION; + return 'minecraft:use_animation'; } public function getValue(): array { diff --git a/src/customiesdevs/customies/item/component/UseModifiersComponent.php b/src/item/component/UseModifiersComponent.php similarity index 95% rename from src/customiesdevs/customies/item/component/UseModifiersComponent.php rename to src/item/component/UseModifiersComponent.php index 76685a82..bbee55b8 100644 --- a/src/customiesdevs/customies/item/component/UseModifiersComponent.php +++ b/src/item/component/UseModifiersComponent.php @@ -19,7 +19,7 @@ public function __construct(float $movementModifier, float $useDuration = 0) { } public function getName(): string { - return VanillaItemComponents::USE_MODIFIERS; + return 'minecraft:use_modifiers'; } public function getValue(): array { diff --git a/src/customiesdevs/customies/item/component/WearableComponent.php b/src/item/component/WearableComponent.php similarity index 97% rename from src/customiesdevs/customies/item/component/WearableComponent.php rename to src/item/component/WearableComponent.php index d66b224b..08fb5507 100644 --- a/src/customiesdevs/customies/item/component/WearableComponent.php +++ b/src/item/component/WearableComponent.php @@ -37,7 +37,7 @@ public function __construct(string $slot, int $protection = 0, bool $hidePlayerL } public function getName(): string { - return VanillaItemComponents::WEARABLE; + return 'minecraft:wearable'; } public function getValue(): array { diff --git a/src/customiesdevs/customies/item/properties/ItemProperties.php b/src/item/properties/ItemProperties.php similarity index 96% rename from src/customiesdevs/customies/item/properties/ItemProperties.php rename to src/item/properties/ItemProperties.php index 59a8afa9..3b218415 100644 --- a/src/customiesdevs/customies/item/properties/ItemProperties.php +++ b/src/item/properties/ItemProperties.php @@ -105,9 +105,14 @@ public function applyComponents(array $components): void { if(!is_array($value) || !array_key_exists($key, $value)) { throw new RuntimeException("Missing required key '{$key}' in component {$name}"); } + if($key === 'use_duration') { + $value[$key] = (int)($value[$key] * 20); + } $this->properties->setTag($prop, NBT::getTagType($value[$key])); } - continue; + if(!($name === 'minecraft:use_modifiers')) { + continue; + } } // Always record the raw component (except icon and item properties, handled above) diff --git a/src/customiesdevs/customies/item/properties/RepairItems.php b/src/item/properties/RepairItems.php similarity index 100% rename from src/customiesdevs/customies/item/properties/RepairItems.php rename to src/item/properties/RepairItems.php diff --git a/src/customiesdevs/customies/item/traits/DefaultTrait.php b/src/item/traits/DefaultTrait.php similarity index 100% rename from src/customiesdevs/customies/item/traits/DefaultTrait.php rename to src/item/traits/DefaultTrait.php diff --git a/src/customiesdevs/customies/item/traits/ItemComponentsTrait.php b/src/item/traits/ItemComponentsTrait.php similarity index 100% rename from src/customiesdevs/customies/item/traits/ItemComponentsTrait.php rename to src/item/traits/ItemComponentsTrait.php diff --git a/src/customiesdevs/customies/task/AsyncRegisterBlocksTask.php b/src/task/AsyncRegisterBlocksTask.php similarity index 100% rename from src/customiesdevs/customies/task/AsyncRegisterBlocksTask.php rename to src/task/AsyncRegisterBlocksTask.php diff --git a/src/customiesdevs/customies/util/NBT.php b/src/util/NBT.php similarity index 100% rename from src/customiesdevs/customies/util/NBT.php rename to src/util/NBT.php From 3b4c910561f83b270e2cbd99fb0de75372b8f0a9 Mon Sep 17 00:00:00 2001 From: JeanTKG <102908437+jeantkg@users.noreply.github.com> Date: Sun, 14 Dec 2025 13:45:13 +0300 Subject: [PATCH 09/51] clean up --- plugin.yml | 2 +- src/Customies.php | 4 +- .../component/EmbeddedVisualComponent.php | 32 ++ .../component/FlowerPottableComponent.php | 20 + src/block/component/ItemVisualComponent.php | 4 +- src/item/CustomiesItemFactory.php | 86 +++- src/item/component/AllowOffHandComponent.php | 4 + src/item/component/BlockPlacerComponent.php | 4 + .../component/BundleInteractionComponent.php | 4 + .../CanDestroyInCreativeComponent.php | 4 + src/item/component/CompostableComponent.php | 4 + src/item/component/CooldownComponent.php | 4 + .../component/DamageAbsorptionComponent.php | 4 + src/item/component/DamageComponent.php | 4 + src/item/component/DiggerComponent.php | 4 + src/item/component/DisplayNameComponent.php | 4 + src/item/component/DurabilityComponent.php | 4 + .../component/DurabilitySensorComponent.php | 4 + src/item/component/DyeableComponent.php | 4 + src/item/component/EnchantableComponent.php | 4 + src/item/component/FireResistantComponent.php | 4 + src/item/component/FoodComponent.php | 4 + src/item/component/FuelComponent.php | 4 + src/item/component/GlintComponent.php | 4 + src/item/component/HandEquippedComponent.php | 4 + .../component/HoverTextColorComponent.php | 4 + src/item/component/IconComponent.php | 4 + .../component/InteractButtonComponent.php | 4 + src/item/component/ItemComponent.php | 6 + src/item/component/LiquidClippedComponent.php | 4 + src/item/component/MaxStackSizeComponent.php | 4 + src/item/component/ProjectileComponent.php | 4 + src/item/component/RarityComponent.php | 4 + src/item/component/RecordComponent.php | 4 + src/item/component/RepairableComponent.php | 4 + src/item/component/ShooterComponent.php | 4 + src/item/component/ShouldDespawnComponent.php | 4 + src/item/component/StackedByDataComponent.php | 4 + src/item/component/StorageItemComponent.php | 4 + .../component/StorageWeightLimitComponent.php | 4 + .../StorageWeightModifierComponent.php | 4 + src/item/component/SwingDurationComponent.php | 4 + src/item/component/TagsComponent.php | 4 + src/item/component/ThrowableComponent.php | 4 + src/item/component/UseAnimationComponent.php | 4 + src/item/component/UseModifiersComponent.php | 4 + src/item/component/WearableComponent.php | 4 + src/item/properties/ItemProperties.php | 144 ------- src/{block => json}/BlockManager.php | 3 +- src/{block => json}/CustomiesBlock.php | 3 +- src/{item => json}/CustomiesItem.php | 2 +- src/{item => json}/ItemManager.php | 2 +- test/blocks_data.json | 392 ------------------ test/server-output.txt | 73 ---- test/test.json | 30 -- 55 files changed, 303 insertions(+), 660 deletions(-) create mode 100644 src/block/component/EmbeddedVisualComponent.php create mode 100644 src/block/component/FlowerPottableComponent.php delete mode 100644 src/item/properties/ItemProperties.php rename src/{block => json}/BlockManager.php (97%) rename src/{block => json}/CustomiesBlock.php (97%) rename src/{item => json}/CustomiesItem.php (99%) rename src/{item => json}/ItemManager.php (99%) delete mode 100644 test/blocks_data.json delete mode 100644 test/server-output.txt delete mode 100644 test/test.json diff --git a/plugin.yml b/plugin.yml index c966693f..c122b5d8 100644 --- a/plugin.yml +++ b/plugin.yml @@ -4,7 +4,7 @@ description: A PocketMine-MP plugin that implements support for custom blocks, i main: customiesdevs\customies\Customies src-namespace-prefix: customiesdevs\customies version: 2.0.0 -api: 5.0.0 +api: 5.1.3 authors: - DenielWorld diff --git a/src/Customies.php b/src/Customies.php index 6720824c..72d4ae51 100644 --- a/src/Customies.php +++ b/src/Customies.php @@ -3,9 +3,9 @@ namespace customiesdevs\customies; -use customiesdevs\customies\block\BlockManager; use customiesdevs\customies\block\CustomiesBlockFactory; -use customiesdevs\customies\item\ItemManager; +use customiesdevs\customies\json\BlockManager; +use customiesdevs\customies\json\ItemManager; use pocketmine\plugin\PluginBase; use pocketmine\scheduler\ClosureTask; use pocketmine\utils\SingletonTrait; diff --git a/src/block/component/EmbeddedVisualComponent.php b/src/block/component/EmbeddedVisualComponent.php new file mode 100644 index 00000000..07e4784f --- /dev/null +++ b/src/block/component/EmbeddedVisualComponent.php @@ -0,0 +1,32 @@ +geometry = $geometry; + $this->materialInstances = $materialInstances; + } + + public function getName(): string { + return 'minecraft:embedded_visual'; + } + + public function getValue(): array { + return [ + "geometry" => $this->geometry->getValue(), + "material_instances" => $this->materialInstances->getValue() + ]; + } + + public static function fromJson(mixed $data): static { + return new self( + GeometryComponent::fromJson($data["geometry"] ?? []), + MaterialInstancesComponent::fromJson($data["material_instances"] ?? []) + ); + } +} \ No newline at end of file diff --git a/src/block/component/FlowerPottableComponent.php b/src/block/component/FlowerPottableComponent.php new file mode 100644 index 00000000..54eda779 --- /dev/null +++ b/src/block/component/FlowerPottableComponent.php @@ -0,0 +1,20 @@ + $this->geometry->getValue(), - "material_instances" => $this->materialInstances->getValue() + "geometryDescription" => $this->geometry->getValue(), + "materialInstancesDescription" => $this->materialInstances->getValue() ]; } diff --git a/src/item/CustomiesItemFactory.php b/src/item/CustomiesItemFactory.php index 1734f082..496978f0 100644 --- a/src/item/CustomiesItemFactory.php +++ b/src/item/CustomiesItemFactory.php @@ -21,7 +21,7 @@ use pocketmine\utils\AssumptionFailedError; use pocketmine\utils\SingletonTrait; use pocketmine\world\format\io\GlobalItemDataHandlers; -use customiesdevs\customies\item\properties\ItemProperties; +use customiesdevs\customies\util\NBT; use ReflectionClass; use function array_values; @@ -29,6 +29,27 @@ final class CustomiesItemFactory { use SingletonTrait; + /** + * Default property values for item_properties + */ + private const PROPERTY_DEFAULTS = [ + 'allow_off_hand' => false, + 'can_destroy_in_creative' => true, + 'damage' => 0, + 'enchantable_slot' => 'none', + 'enchantable_value' => 0, + 'foil' => false, + 'frame_count' => 1, + 'hand_equipped' => false, + 'liquid_clipped' => false, + 'max_stack_size' => 64, + 'mining_speed' => 1, + 'should_despawn' => true, + 'stacked_by_data' => false, + 'use_animation' => 0, + 'use_duration' => 0, + ]; + /** * @var ItemTypeEntry[] */ @@ -127,23 +148,64 @@ public function registerItem(Closure $itemFunc, string $identifier, ?CreativeInv /** * Creates the NBT data for the item. This includes the components and their values. * If the item does not have components, an empty CompoundTag is returned. - * @param Item $item The item for which to create the NBT data - * @param string $identifier The string identifier for the item, usually in the format "namespace:item_name" - * @param int $itemId The numerical ID to be assigned to the item - * @param CreativeInventoryInfo|null $creativeInfo The creative inventory info for the item, if any - * @return CompoundTag The NBT data for the item */ private function createItemNbt(Item $item, string $identifier, int $itemId, ?CreativeInventoryInfo $creativeInfo): CompoundTag { - if (!($item instanceof ItemComponents)) { + if(!($item instanceof ItemComponents)) { return CompoundTag::create(); } - $builder = new ItemProperties(); - $builder->applyComponents($item->getComponents()); - $builder->setCreativeInfo($creativeInfo); + // Initialize item_properties with defaults + $properties = CompoundTag::create(); + foreach(self::PROPERTY_DEFAULTS as $name => $default) { + $properties->setTag($name, NBT::getTagType($default)); + } + + // Set creative info + if($creativeInfo !== null) { + $properties->setTag('creative_category', NBT::getTagType($creativeInfo->getNumericCategory())); + $properties->setTag('creative_group', NBT::getTagType($creativeInfo->getGroup())); + } + + $tags = []; + $componentsTag = CompoundTag::create(); + + // Process each component + foreach($item->getComponents() as $component) { + $name = $component->getName(); + $value = $component->getValue(); + $tag = NBT::getTagType($value); + + // Icon goes to item_properties + if($name === 'minecraft:icon') { + $properties->setTag('minecraft:icon', $tag); + continue; + } + + // Tags go to item_tags + if($name === 'minecraft:tags') { + $tags = $value['tags'] ?? []; + $componentsTag->setTag($name, $tag); + continue; + } + + // Components with property mappings override item_properties + $mapping = $component->getPropertyMapping(); + if($mapping !== null) { + foreach($mapping as $prop => $key) { + $properties->setTag($prop, NBT::getTagType($value[$key])); + } + continue; + } + + // Everything else goes to components + $componentsTag->setTag($name, $tag); + } + + $components = CompoundTag::create() + ->setTag('item_properties', $properties) + ->setTag('item_tags', NBT::getTagType($tags)) + ->merge($componentsTag); - $components = $builder->buildComponentsTag(); - return CompoundTag::create() ->setTag('components', $components) ->setInt('id', $itemId) diff --git a/src/item/component/AllowOffHandComponent.php b/src/item/component/AllowOffHandComponent.php index 4736873b..20a2a8a4 100644 --- a/src/item/component/AllowOffHandComponent.php +++ b/src/item/component/AllowOffHandComponent.php @@ -25,6 +25,10 @@ public function getValue(): array { ]; } + public function getPropertyMapping(): ?array { + return ['allow_off_hand' => 'value']; + } + public static function fromJson(mixed $data): static { return new self($data ?? true); } diff --git a/src/item/component/BlockPlacerComponent.php b/src/item/component/BlockPlacerComponent.php index f0b81f43..fb87a80a 100644 --- a/src/item/component/BlockPlacerComponent.php +++ b/src/item/component/BlockPlacerComponent.php @@ -38,6 +38,10 @@ public function getValue(): array { ]; } + public function getPropertyMapping(): ?array { + return null; + } + /** * TODO: Update this * Add blocks to the `use_on` array in the required format. diff --git a/src/item/component/BundleInteractionComponent.php b/src/item/component/BundleInteractionComponent.php index 63a6b80a..994c0076 100644 --- a/src/item/component/BundleInteractionComponent.php +++ b/src/item/component/BundleInteractionComponent.php @@ -26,6 +26,10 @@ public function getValue(): array { ]; } + public function getPropertyMapping(): ?array { + return null; + } + public static function fromJson(mixed $data): static { return new self($data["num_viewable_slots"] ?? 12); } diff --git a/src/item/component/CanDestroyInCreativeComponent.php b/src/item/component/CanDestroyInCreativeComponent.php index 9207203b..187c30f5 100644 --- a/src/item/component/CanDestroyInCreativeComponent.php +++ b/src/item/component/CanDestroyInCreativeComponent.php @@ -25,6 +25,10 @@ public function getValue(): array { ]; } + public function getPropertyMapping(): ?array { + return ['can_destroy_in_creative' => 'value']; + } + public static function fromJson(mixed $data): static { return new self($data ?? true); } diff --git a/src/item/component/CompostableComponent.php b/src/item/component/CompostableComponent.php index 2315aca8..9ffa5a4d 100644 --- a/src/item/component/CompostableComponent.php +++ b/src/item/component/CompostableComponent.php @@ -25,6 +25,10 @@ public function getValue(): array { ]; } + public function getPropertyMapping(): ?array { + return null; + } + public static function fromJson(mixed $data): static { return new self($data["composting_chance"] ?? 0); } diff --git a/src/item/component/CooldownComponent.php b/src/item/component/CooldownComponent.php index 06baa327..31ec6de4 100644 --- a/src/item/component/CooldownComponent.php +++ b/src/item/component/CooldownComponent.php @@ -35,6 +35,10 @@ public function getValue(): array { ]; } + public function getPropertyMapping(): ?array { + return null; + } + public static function fromJson(mixed $data): static { return new self($data["category"] ?? self::CATEGORY_SHIELD, $data["duration"] ?? 0.0); } diff --git a/src/item/component/DamageAbsorptionComponent.php b/src/item/component/DamageAbsorptionComponent.php index 966d5a51..d27e3949 100644 --- a/src/item/component/DamageAbsorptionComponent.php +++ b/src/item/component/DamageAbsorptionComponent.php @@ -28,6 +28,10 @@ public function getValue(): array { ]; } + public function getPropertyMapping(): ?array { + return null; + } + public static function fromJson(mixed $data): static { return new self($data["absorbable_causes"] ?? []); } diff --git a/src/item/component/DamageComponent.php b/src/item/component/DamageComponent.php index 5d8fa99e..a88a0f04 100644 --- a/src/item/component/DamageComponent.php +++ b/src/item/component/DamageComponent.php @@ -26,6 +26,10 @@ public function getValue(): array { ]; } + public function getPropertyMapping(): ?array { + return ['damage' => 'value']; + } + public static function fromJson(mixed $data): static { return new self($data ?? 0); } diff --git a/src/item/component/DiggerComponent.php b/src/item/component/DiggerComponent.php index 7441bb00..a15a1166 100644 --- a/src/item/component/DiggerComponent.php +++ b/src/item/component/DiggerComponent.php @@ -33,6 +33,10 @@ public function getValue(): array { ]; } + public function getPropertyMapping(): ?array { + return null; + } + /** * Add blocks to the `destroy_speeds` array in the required format. * @param int $speed Digging speed for the correlating block(s) diff --git a/src/item/component/DisplayNameComponent.php b/src/item/component/DisplayNameComponent.php index 5824e2d5..9fba8127 100644 --- a/src/item/component/DisplayNameComponent.php +++ b/src/item/component/DisplayNameComponent.php @@ -26,6 +26,10 @@ public function getValue(): array { ]; } + public function getPropertyMapping(): ?array { + return null; + } + public static function fromJson(mixed $data): static { return new self($data["value"] ?? ""); } diff --git a/src/item/component/DurabilityComponent.php b/src/item/component/DurabilityComponent.php index b5cce005..7841b32a 100644 --- a/src/item/component/DurabilityComponent.php +++ b/src/item/component/DurabilityComponent.php @@ -35,6 +35,10 @@ public function getValue(): array { ]; } + public function getPropertyMapping(): ?array { + return null; + } + public static function fromJson(mixed $data): static { return new self($data["max_durability"] ?? 0, $data["damage_chance"]["min"] ?? 100, $data["damage_chance"]["max"] ?? 100); } diff --git a/src/item/component/DurabilitySensorComponent.php b/src/item/component/DurabilitySensorComponent.php index 05d72c1f..8b08976f 100644 --- a/src/item/component/DurabilitySensorComponent.php +++ b/src/item/component/DurabilitySensorComponent.php @@ -27,6 +27,10 @@ public function getValue(): array { ]; } + public function getPropertyMapping(): ?array { + return null; + } + // I dont know if this will work | TODO test it and see what it outputs public function addDurabilityThreshold(int $durability, string $particleType = "", string $soundEvent = ""): self { $this->durabilityThresholds[] = [ diff --git a/src/item/component/DyeableComponent.php b/src/item/component/DyeableComponent.php index 9aa6733f..7f3312cb 100644 --- a/src/item/component/DyeableComponent.php +++ b/src/item/component/DyeableComponent.php @@ -25,6 +25,10 @@ public function getValue(): array { ]; } + public function getPropertyMapping(): ?array { + return null; + } + public static function fromJson(mixed $data): static { return new self($data["default_color"] ?? "#ffffff"); } diff --git a/src/item/component/EnchantableComponent.php b/src/item/component/EnchantableComponent.php index 8824805d..cd9ae9a1 100644 --- a/src/item/component/EnchantableComponent.php +++ b/src/item/component/EnchantableComponent.php @@ -66,6 +66,10 @@ public function getValue(): array { ]; } + public function getPropertyMapping(): ?array { + return ['enchantable_slot' => 'slot', 'enchantable_value' => 'value']; + } + public static function fromJson(mixed $data): static { return new self($data["slot"] ?? self::SLOT_ALL, $data["value"] ?? 1); } diff --git a/src/item/component/FireResistantComponent.php b/src/item/component/FireResistantComponent.php index b35a5290..a686d01c 100644 --- a/src/item/component/FireResistantComponent.php +++ b/src/item/component/FireResistantComponent.php @@ -25,6 +25,10 @@ public function getValue(): array { ]; } + public function getPropertyMapping(): ?array { + return null; + } + public static function fromJson(mixed $data): static { return new self($data ?? true); } diff --git a/src/item/component/FoodComponent.php b/src/item/component/FoodComponent.php index 78bc180e..5b51e538 100644 --- a/src/item/component/FoodComponent.php +++ b/src/item/component/FoodComponent.php @@ -37,6 +37,10 @@ public function getValue(): array { ]; } + public function getPropertyMapping(): ?array { + return null; + } + public static function fromJson(mixed $data): static { return new self( $data["can_always_eat"] ?? false, diff --git a/src/item/component/FuelComponent.php b/src/item/component/FuelComponent.php index db78bdc7..ec95af6e 100644 --- a/src/item/component/FuelComponent.php +++ b/src/item/component/FuelComponent.php @@ -25,6 +25,10 @@ public function getValue(): array { ]; } + public function getPropertyMapping(): ?array { + return null; + } + public static function fromJson(mixed $data): static { return new self($data["duration"] ?? 0.0); } diff --git a/src/item/component/GlintComponent.php b/src/item/component/GlintComponent.php index af597d8a..7e116ade 100644 --- a/src/item/component/GlintComponent.php +++ b/src/item/component/GlintComponent.php @@ -25,6 +25,10 @@ public function getValue(): array { ]; } + public function getPropertyMapping(): ?array { + return ['foil' => 'value']; + } + public static function fromJson(mixed $data): static { return new self($data ?? true); } diff --git a/src/item/component/HandEquippedComponent.php b/src/item/component/HandEquippedComponent.php index 5ce70c61..370d41e4 100644 --- a/src/item/component/HandEquippedComponent.php +++ b/src/item/component/HandEquippedComponent.php @@ -25,6 +25,10 @@ public function getValue(): array { ]; } + public function getPropertyMapping(): ?array { + return ['hand_equipped' => 'value']; + } + public static function fromJson(mixed $data): static { return new self($data ?? true); } diff --git a/src/item/component/HoverTextColorComponent.php b/src/item/component/HoverTextColorComponent.php index 3ea51336..f9699c1d 100644 --- a/src/item/component/HoverTextColorComponent.php +++ b/src/item/component/HoverTextColorComponent.php @@ -26,6 +26,10 @@ public function getValue(): array { ]; } + public function getPropertyMapping(): ?array { + return ['hover_text_color' => 'value']; + } + public static function fromJson(mixed $data): static { return new self($data ?? "white"); } diff --git a/src/item/component/IconComponent.php b/src/item/component/IconComponent.php index 86c5adbe..00904f1b 100644 --- a/src/item/component/IconComponent.php +++ b/src/item/component/IconComponent.php @@ -49,6 +49,10 @@ public function getValue(): array { ]; } + public function getPropertyMapping(): ?array { + return null; + } + public static function fromJson(mixed $data): static { return new self( $data["textures"]["default"] ?? "customies:missing_texture", diff --git a/src/item/component/InteractButtonComponent.php b/src/item/component/InteractButtonComponent.php index 38e32ce7..655f8e3e 100644 --- a/src/item/component/InteractButtonComponent.php +++ b/src/item/component/InteractButtonComponent.php @@ -30,6 +30,10 @@ public function getValue(): array { ]; } + public function getPropertyMapping(): ?array { + return null; + } + public static function fromJson(mixed $data): static { return new self($data ?? "action.interact.use"); } diff --git a/src/item/component/ItemComponent.php b/src/item/component/ItemComponent.php index d12cac5f..2778b21b 100644 --- a/src/item/component/ItemComponent.php +++ b/src/item/component/ItemComponent.php @@ -17,6 +17,12 @@ public function getName(): string; */ public function getValue(): mixed; + /** + * Returns property mappings if this component maps to item_properties. + * @return array|null [propertyName => keyInValue] or null if not a property + */ + public function getPropertyMapping(): ?array; + /** * Create a component instance from decoded JSON (item definition) data. * Implementations should be tolerant of missing keys and apply sensible defaults. diff --git a/src/item/component/LiquidClippedComponent.php b/src/item/component/LiquidClippedComponent.php index ec0dca53..cb3a12e3 100644 --- a/src/item/component/LiquidClippedComponent.php +++ b/src/item/component/LiquidClippedComponent.php @@ -25,6 +25,10 @@ public function getValue(): array { ]; } + public function getPropertyMapping(): ?array { + return ['liquid_clipped' => 'value']; + } + public static function fromJson(mixed $data): static { return new self($data ?? true); } diff --git a/src/item/component/MaxStackSizeComponent.php b/src/item/component/MaxStackSizeComponent.php index 1d90b8c3..defc3577 100644 --- a/src/item/component/MaxStackSizeComponent.php +++ b/src/item/component/MaxStackSizeComponent.php @@ -25,6 +25,10 @@ public function getValue(): array { ]; } + public function getPropertyMapping(): ?array { + return ['max_stack_size' => 'value']; + } + public static function fromJson(mixed $data): static { return new self($data ?? 64); } diff --git a/src/item/component/ProjectileComponent.php b/src/item/component/ProjectileComponent.php index 6c624d8b..000a3620 100644 --- a/src/item/component/ProjectileComponent.php +++ b/src/item/component/ProjectileComponent.php @@ -31,6 +31,10 @@ public function getValue(): array { ]; } + public function getPropertyMapping(): ?array { + return null; + } + public static function fromJson(mixed $data): static { return new self($data["minimum_critical_power"] ?? 0.0, $data["projectile_entity"] ?? ""); } diff --git a/src/item/component/RarityComponent.php b/src/item/component/RarityComponent.php index 7094d199..319de6ad 100644 --- a/src/item/component/RarityComponent.php +++ b/src/item/component/RarityComponent.php @@ -31,6 +31,10 @@ public function getValue(): array { ]; } + public function getPropertyMapping(): ?array { + return null; + } + public static function fromJson(mixed $data): static { return new self($data ?? self::COMMON); } diff --git a/src/item/component/RecordComponent.php b/src/item/component/RecordComponent.php index bb1870a7..e992869b 100644 --- a/src/item/component/RecordComponent.php +++ b/src/item/component/RecordComponent.php @@ -33,6 +33,10 @@ public function getValue(): array { ]; } + public function getPropertyMapping(): ?array { + return null; + } + public static function fromJson(mixed $data): static { return new self($data["comparator_signal"] ?? 1, $data["duration"] ?? 0.0, $data["sound_event"] ?? ""); } diff --git a/src/item/component/RepairableComponent.php b/src/item/component/RepairableComponent.php index 10bc6f8f..842ae5f4 100644 --- a/src/item/component/RepairableComponent.php +++ b/src/item/component/RepairableComponent.php @@ -29,6 +29,10 @@ public function getValue(): array { ]; } + public function getPropertyMapping(): ?array { + return null; + } + public static function fromJson(mixed $data): static { $repairItems = []; if(is_array($data["repair_items"] ?? null)) { diff --git a/src/item/component/ShooterComponent.php b/src/item/component/ShooterComponent.php index e57aca08..62fc7b0c 100644 --- a/src/item/component/ShooterComponent.php +++ b/src/item/component/ShooterComponent.php @@ -57,6 +57,10 @@ public function getValue(): array { ]; } + public function getPropertyMapping(): ?array { + return null; + } + public static function fromJson(mixed $data): static { return new self( $data["ammunition"][0]["item"] ?? "", diff --git a/src/item/component/ShouldDespawnComponent.php b/src/item/component/ShouldDespawnComponent.php index 3e10697f..cb226053 100644 --- a/src/item/component/ShouldDespawnComponent.php +++ b/src/item/component/ShouldDespawnComponent.php @@ -25,6 +25,10 @@ public function getValue(): array { ]; } + public function getPropertyMapping(): ?array { + return ['should_despawn' => 'value']; + } + public static function fromJson(mixed $data): static { return new self($data ?? true); } diff --git a/src/item/component/StackedByDataComponent.php b/src/item/component/StackedByDataComponent.php index 7c743ecc..35046e53 100644 --- a/src/item/component/StackedByDataComponent.php +++ b/src/item/component/StackedByDataComponent.php @@ -26,6 +26,10 @@ public function getValue(): array { ]; } + public function getPropertyMapping(): ?array { + return ['stacked_by_data' => 'value']; + } + public static function fromJson(mixed $data): static { return new self($data ?? true); } diff --git a/src/item/component/StorageItemComponent.php b/src/item/component/StorageItemComponent.php index ccff4e0f..f26d9033 100644 --- a/src/item/component/StorageItemComponent.php +++ b/src/item/component/StorageItemComponent.php @@ -53,6 +53,10 @@ public function getValue(): array { ]; } + public function getPropertyMapping(): ?array { + return null; + } + public static function fromJson(mixed $data): static { return new self( $data["allow_nested_storage_items"] ?? true, diff --git a/src/item/component/StorageWeightLimitComponent.php b/src/item/component/StorageWeightLimitComponent.php index 1951a1c7..1c2fe1a1 100644 --- a/src/item/component/StorageWeightLimitComponent.php +++ b/src/item/component/StorageWeightLimitComponent.php @@ -25,6 +25,10 @@ public function getValue(): array { ]; } + public function getPropertyMapping(): ?array { + return null; + } + public static function fromJson(mixed $data): static { return new self($data["max_weight_limit"] ?? 64); } diff --git a/src/item/component/StorageWeightModifierComponent.php b/src/item/component/StorageWeightModifierComponent.php index 57af341f..fe75be83 100644 --- a/src/item/component/StorageWeightModifierComponent.php +++ b/src/item/component/StorageWeightModifierComponent.php @@ -25,6 +25,10 @@ public function getValue(): array { ]; } + public function getPropertyMapping(): ?array { + return null; + } + public static function fromJson(mixed $data): static { return new self($data["weight_in_storage_item"] ?? 4); } diff --git a/src/item/component/SwingDurationComponent.php b/src/item/component/SwingDurationComponent.php index fabf7794..992c9127 100644 --- a/src/item/component/SwingDurationComponent.php +++ b/src/item/component/SwingDurationComponent.php @@ -25,6 +25,10 @@ public function getValue(): array { ]; } + public function getPropertyMapping(): ?array { + return null; + } + public static function fromJson(mixed $data): static { return new self($data ?? 0.3); } diff --git a/src/item/component/TagsComponent.php b/src/item/component/TagsComponent.php index 8d15fb1e..46eb042a 100644 --- a/src/item/component/TagsComponent.php +++ b/src/item/component/TagsComponent.php @@ -25,6 +25,10 @@ public function getValue(): array { ]; } + public function getPropertyMapping(): ?array { + return null; + } + public static function fromJson(mixed $data): static { return new self(is_array($data["tags"] ?? null) ? $data["tags"] : []); } diff --git a/src/item/component/ThrowableComponent.php b/src/item/component/ThrowableComponent.php index 36ce6884..51318757 100644 --- a/src/item/component/ThrowableComponent.php +++ b/src/item/component/ThrowableComponent.php @@ -52,6 +52,10 @@ public function getValue(): array { ]; } + public function getPropertyMapping(): ?array { + return null; + } + public static function fromJson(mixed $data): static { return new self( $data["do_swing_animation"] ?? false, diff --git a/src/item/component/UseAnimationComponent.php b/src/item/component/UseAnimationComponent.php index 9c285999..b4900492 100644 --- a/src/item/component/UseAnimationComponent.php +++ b/src/item/component/UseAnimationComponent.php @@ -36,6 +36,10 @@ public function getValue(): array { ]; } + public function getPropertyMapping(): ?array { + return ['use_animation' => 'value']; + } + public static function fromJson(mixed $data): static { return new self($data ?? self::ANIMATION_NONE); } diff --git a/src/item/component/UseModifiersComponent.php b/src/item/component/UseModifiersComponent.php index bbee55b8..2220c096 100644 --- a/src/item/component/UseModifiersComponent.php +++ b/src/item/component/UseModifiersComponent.php @@ -29,6 +29,10 @@ public function getValue(): array { ]; } + public function getPropertyMapping(): ?array { + return ['use_duration' => 'use_duration']; + } + public static function fromJson(mixed $data): static { return new self($data["movement_modifier"] ?? 1.0, $data["use_duration"] ?? 0); } diff --git a/src/item/component/WearableComponent.php b/src/item/component/WearableComponent.php index 08fb5507..7006aed2 100644 --- a/src/item/component/WearableComponent.php +++ b/src/item/component/WearableComponent.php @@ -48,6 +48,10 @@ public function getValue(): array { ]; } + public function getPropertyMapping(): ?array { + return null; + } + public static function fromJson(mixed $data): static { return new self( $data["slot"] ?? self::SLOT_NONE, diff --git a/src/item/properties/ItemProperties.php b/src/item/properties/ItemProperties.php deleted file mode 100644 index 3b218415..00000000 --- a/src/item/properties/ItemProperties.php +++ /dev/null @@ -1,144 +0,0 @@ - false, - 'can_destroy_in_creative' => true, - 'damage' => 0, - 'enchantable_slot' => 'none', - 'enchantable_value' => 0, - 'foil' => false, - 'frame_count' => 1, - 'hand_equipped' => false, - 'liquid_clipped' => false, - 'max_stack_size' => 64, - 'mining_speed' => 1, - 'should_despawn' => true, - 'stacked_by_data' => false, - 'use_animation' => 0, - 'use_duration' => 0, - ]; - - /** - * Declarative mapping for components that override item_properties - * componentName => [ propertyName => keyInsideComponentValue ] - */ - private const MAPPINGS = [ - 'minecraft:allow_off_hand' => ['allow_off_hand' => 'value'], - 'minecraft:can_destroy_in_creative' => ['can_destroy_in_creative' => 'value'], - 'minecraft:damage' => ['damage' => 'value'], - 'minecraft:enchantable' => ['enchantable_slot' => 'slot', 'enchantable_value' => 'value'], - 'minecraft:glint' => ['foil' => 'value'], - 'minecraft:hand_equipped' => ['hand_equipped' => 'value'], - 'minecraft:hover_text_color' => ['hover_text_color' => 'value'], - 'minecraft:liquid_clipped' => ['liquid_clipped' => 'value'], - 'minecraft:max_stack_size' => ['max_stack_size' => 'value'], - 'minecraft:should_despawn' => ['should_despawn' => 'value'], - 'minecraft:stacked_by_data' => ['stacked_by_data' => 'value'], - 'minecraft:use_animation' => ['use_animation' => 'value'], - 'minecraft:use_modifiers' => ['use_duration' => 'use_duration'], - ]; - - private CompoundTag $properties; - private array $tags; - private CompoundTag $components; - - public function __construct() { - $this->properties = CompoundTag::create(); - $this->tags = []; - $this->components = CompoundTag::create(); - - // Initialize defaults - foreach(self::DEFAULTS as $name => $default) { - $this->properties->setTag($name, NBT::getTagType($default)); - } - } - - /** - * Applies the given item components to the properties builder. - * This updates both item_properties and the components tag. - * @param ItemComponent[] $components - */ - public function applyComponents(array $components): void { - foreach($components as $component) { - $name = $component->getName(); - $value = $component->getValue(); - - $tag = NBT::getTagType($value); - if($tag === null) { - throw new RuntimeException("Failed to get tag type for component {$name}"); - } - - if($name === 'minecraft:icon') { - // icon is stored under item_properties - $this->properties->setTag('minecraft:icon', $tag); - continue; - } - - if($name === 'minecraft:tags') { - $this->tags = $value["tags"] ?? []; - } - - if(isset(self::MAPPINGS[$name])) { - foreach(self::MAPPINGS[$name] as $prop => $key) { - if(!is_array($value) || !array_key_exists($key, $value)) { - throw new RuntimeException("Missing required key '{$key}' in component {$name}"); - } - if($key === 'use_duration') { - $value[$key] = (int)($value[$key] * 20); - } - $this->properties->setTag($prop, NBT::getTagType($value[$key])); - } - if(!($name === 'minecraft:use_modifiers')) { - continue; - } - } - - // Always record the raw component (except icon and item properties, handled above) - $this->components->setTag($name, $tag); - } - } - - /** - * Sets the creative category and group tags based on the provided CreativeInventoryInfo. - * @param CreativeInventoryInfo|null $creativeInfo The creative inventory info, or null to skip setting - */ - public function setCreativeInfo(?CreativeInventoryInfo $creativeInfo): void { - if($creativeInfo !== null) { - $this->properties->setTag('creative_category', NBT::getTagType($creativeInfo->getNumericCategory())); - $this->properties->setTag('creative_group', NBT::getTagType($creativeInfo->getGroup())); - } - } - - /** - * Build a CompoundTag containing both item_properties and the accumulated component tags. - * @return CompoundTag The combined components tag - */ - public function buildComponentsTag(): CompoundTag { - return CompoundTag::create() - ->setTag('item_properties', $this->properties) - ->setTag('item_tags', NBT::getTagType($this->tags)) - ->merge($this->components); - } -} diff --git a/src/block/BlockManager.php b/src/json/BlockManager.php similarity index 97% rename from src/block/BlockManager.php rename to src/json/BlockManager.php index 37eb556e..253bdf23 100644 --- a/src/block/BlockManager.php +++ b/src/json/BlockManager.php @@ -2,8 +2,9 @@ declare(strict_types=1); -namespace customiesdevs\customies\block; +namespace customiesdevs\customies\json; +use customiesdevs\customies\block\CustomiesBlockFactory; use customiesdevs\customies\Customies; use customiesdevs\customies\item\CreativeInventoryInfo; use pocketmine\block\Block; diff --git a/src/block/CustomiesBlock.php b/src/json/CustomiesBlock.php similarity index 97% rename from src/block/CustomiesBlock.php rename to src/json/CustomiesBlock.php index a1ded7ba..d67edbf5 100644 --- a/src/block/CustomiesBlock.php +++ b/src/json/CustomiesBlock.php @@ -1,7 +1,8 @@ TAG_Compound={ - "value" => TAG_Byte=1 - } - "minecraft:can_destroy_in_creative" => TAG_Compound={ - "value" => TAG_Byte=1 - } - "minecraft:dyeable" => TAG_Compound={ - "default_color" => TAG_String="#175882" - } - "minecraft:enchantable" => TAG_Compound={ - "slot" => TAG_String="sword" - "value" => TAG_Int=10 - } - "minecraft:fuel" => TAG_Compound={ - "duration" => TAG_Float=3 - } - "minecraft:glint" => TAG_Compound={ - "value" => TAG_Byte=1 - } - "minecraft:hand_equipped" => TAG_Compound={ - "value" => TAG_Byte=1 - } - "minecraft:hover_text_color" => TAG_Compound={ - "value" => TAG_String="minecoin_gold" - } - "minecraft:interact_button" => TAG_Compound={ - "interact_text" => TAG_String="Use This Custom Item" - "requires_interact" => TAG_Int=1 - } - "minecraft:max_stack_size" => TAG_Compound={ - "value" => TAG_Int=7 - } - "minecraft:rarity" => TAG_Compound={ - "value" => TAG_String="rare" - } - "minecraft:should_despawn" => TAG_Compound={ - "value" => TAG_Byte=1 - } - "minecraft:stacked_by_data" => TAG_Compound={ - "value" => TAG_Byte=1 - } - "item_properties" => TAG_Compound={ - "allow_off_hand" => TAG_Byte=1 - "can_destroy_in_creative" => TAG_Byte=1 - "damage" => TAG_Int=0 - "enchantable_slot" => TAG_String="sword" - "enchantable_value" => TAG_Int=10 - "foil" => TAG_Byte=1 - "frame_count" => TAG_Int=1 - "hand_equipped" => TAG_Byte=1 - "liquid_clipped" => TAG_Byte=0 - "max_stack_size" => TAG_Int=7 - "mining_speed" => TAG_Int=1 - "should_despawn" => TAG_Byte=1 - "stacked_by_data" => TAG_Byte=1 - "use_animation" => TAG_Int=0 - "use_duration" => TAG_Int=0 - "minecraft:icon" => TAG_Compound={ - "textures" => TAG_Compound={ - "default" => TAG_String="apple" - "dyed" => TAG_String="apple" - "icon_trim" => TAG_String="apple" - "bundle_open_back" => TAG_String="apple" - "bundle_open_front" => TAG_String="apple" - } - } - "hover_text_color" => TAG_String="minecoin_gold" - "creative_category" => TAG_Int=4 - "creative_group" => TAG_String="none" - } -}" \ No newline at end of file diff --git a/test/test.json b/test/test.json deleted file mode 100644 index c323f552..00000000 --- a/test/test.json +++ /dev/null @@ -1,30 +0,0 @@ -{ - "components": { - "item_properties": { - "allow_off_hand": 0, // default value false - "can_destroy_in_creative": 1, // default value true - "creative_category": 4, - "creative_group": "", - "damage": 0, // default value int - "enchantable_slot": "none", // default value string - "enchantable_value": 0, // default value int - "foil": 0, // default value false - "frame_count": 1, // default value - "hand_equipped": 0, // default value false - "hover_text_color": "§g", // this is only added when the hover text color component is added - "liquid_clipped": 0, // default value false - "max_stack_size": 64, // default value - "minecraft:icon": { - "textures": { - "default": "nk:test" - } - }, - "mining_speed": 1, // default value - "should_despawn": 1, // default value true - "stacked_by_data": 0, // default value false - "use_animation": 0, // default value - "use_duration": 0 // default value - }, - "item_tags": [] - } -} \ No newline at end of file From 1516329a6095ef98004b42e16ce2e9195646e31f Mon Sep 17 00:00:00 2001 From: JeanTKG <102908437+jeantkg@users.noreply.github.com> Date: Sun, 14 Dec 2025 14:51:37 +0300 Subject: [PATCH 10/51] Refactor item and block component handling - Updated `ItemComponent` interface to clarify property mapping return values. - Modified `LiquidClippedComponent`, `MaxStackSizeComponent`, `ShouldDespawnComponent`, and `StackedByDataComponent` to return actual property values instead of a placeholder string. - Enhanced `UseAnimationComponent` to use string constants for animations and added a mapping for converting them to integers. - Updated `UseModifiersComponent` to return the actual use duration value. - Removed `BlockManager` and `ItemManager` classes, consolidating their functionality into a new `BehaviorManager` class for better organization and management of custom items and blocks from JSON files. - Introduced `BlockComponentRegistry` and `ItemComponentRegistry` for centralized management of component mappings, allowing for easier registration and retrieval of components. - Updated `CustomiesBlock` and `CustomiesItem` constructors to utilize the new registries for component instantiation from JSON data. --- src/Customies.php | 8 +- src/item/CustomiesItemFactory.php | 10 +- src/item/component/AllowOffHandComponent.php | 2 +- .../CanDestroyInCreativeComponent.php | 2 +- src/item/component/DamageComponent.php | 2 +- src/item/component/EnchantableComponent.php | 2 +- src/item/component/GlintComponent.php | 2 +- src/item/component/HandEquippedComponent.php | 2 +- .../component/HoverTextColorComponent.php | 2 +- src/item/component/IconComponent.php | 15 +- src/item/component/ItemComponent.php | 2 +- src/item/component/LiquidClippedComponent.php | 2 +- src/item/component/MaxStackSizeComponent.php | 2 +- src/item/component/ShouldDespawnComponent.php | 2 +- src/item/component/StackedByDataComponent.php | 2 +- src/item/component/UseAnimationComponent.php | 43 +++-- src/item/component/UseModifiersComponent.php | 2 +- src/json/BehaviorManager.php | 182 ++++++++++++++++++ src/json/BlockComponentRegistry.php | 103 ++++++++++ src/json/BlockManager.php | 157 --------------- src/json/CustomiesBlock.php | 66 +------ src/json/CustomiesItem.php | 98 +--------- src/json/ItemComponentRegistry.php | 141 ++++++++++++++ src/json/ItemManager.php | 154 --------------- 24 files changed, 496 insertions(+), 507 deletions(-) create mode 100644 src/json/BehaviorManager.php create mode 100644 src/json/BlockComponentRegistry.php delete mode 100644 src/json/BlockManager.php create mode 100644 src/json/ItemComponentRegistry.php delete mode 100644 src/json/ItemManager.php diff --git a/src/Customies.php b/src/Customies.php index 72d4ae51..880f3e32 100644 --- a/src/Customies.php +++ b/src/Customies.php @@ -4,8 +4,7 @@ namespace customiesdevs\customies; use customiesdevs\customies\block\CustomiesBlockFactory; -use customiesdevs\customies\json\BlockManager; -use customiesdevs\customies\json\ItemManager; +use customiesdevs\customies\json\BehaviorManager; use pocketmine\plugin\PluginBase; use pocketmine\scheduler\ClosureTask; use pocketmine\utils\SingletonTrait; @@ -19,9 +18,8 @@ public function onLoad(): void{ protected function onEnable(): void { $this->getServer()->getPluginManager()->registerEvents(new CustomiesListener(), $this); - - ItemManager::getInstance()->registerItems(); - BlockManager::getInstance()->registerBlocks(); + + BehaviorManager::getInstance()->registerAll(); $cachePath = $this->getDataFolder() . "idcache"; $this->getScheduler()->scheduleDelayedTask(new ClosureTask(static function () use ($cachePath): void { diff --git a/src/item/CustomiesItemFactory.php b/src/item/CustomiesItemFactory.php index 496978f0..7fbb5c43 100644 --- a/src/item/CustomiesItemFactory.php +++ b/src/item/CustomiesItemFactory.php @@ -188,16 +188,15 @@ private function createItemNbt(Item $item, string $identifier, int $itemId, ?Cre continue; } - // Components with property mappings override item_properties + // Components with property mappings also update item_properties $mapping = $component->getPropertyMapping(); if($mapping !== null) { - foreach($mapping as $prop => $key) { - $properties->setTag($prop, NBT::getTagType($value[$key])); + foreach($mapping as $prop => $propValue) { + $properties->setTag($prop, NBT::getTagType($propValue)); } - continue; } - // Everything else goes to components + // All components go to components tag $componentsTag->setTag($name, $tag); } @@ -206,6 +205,7 @@ private function createItemNbt(Item $item, string $identifier, int $itemId, ?Cre ->setTag('item_tags', NBT::getTagType($tags)) ->merge($componentsTag); + \var_dump($components->__toString()); return CompoundTag::create() ->setTag('components', $components) ->setInt('id', $itemId) diff --git a/src/item/component/AllowOffHandComponent.php b/src/item/component/AllowOffHandComponent.php index 20a2a8a4..60f2a1a5 100644 --- a/src/item/component/AllowOffHandComponent.php +++ b/src/item/component/AllowOffHandComponent.php @@ -26,7 +26,7 @@ public function getValue(): array { } public function getPropertyMapping(): ?array { - return ['allow_off_hand' => 'value']; + return ['allow_off_hand' => $this->offHand]; } public static function fromJson(mixed $data): static { diff --git a/src/item/component/CanDestroyInCreativeComponent.php b/src/item/component/CanDestroyInCreativeComponent.php index 187c30f5..1f307fdb 100644 --- a/src/item/component/CanDestroyInCreativeComponent.php +++ b/src/item/component/CanDestroyInCreativeComponent.php @@ -26,7 +26,7 @@ public function getValue(): array { } public function getPropertyMapping(): ?array { - return ['can_destroy_in_creative' => 'value']; + return ['can_destroy_in_creative' => $this->canDestroyInCreative]; } public static function fromJson(mixed $data): static { diff --git a/src/item/component/DamageComponent.php b/src/item/component/DamageComponent.php index a88a0f04..ea6d5ef2 100644 --- a/src/item/component/DamageComponent.php +++ b/src/item/component/DamageComponent.php @@ -27,7 +27,7 @@ public function getValue(): array { } public function getPropertyMapping(): ?array { - return ['damage' => 'value']; + return ['damage' => $this->damage]; } public static function fromJson(mixed $data): static { diff --git a/src/item/component/EnchantableComponent.php b/src/item/component/EnchantableComponent.php index cd9ae9a1..5a12996a 100644 --- a/src/item/component/EnchantableComponent.php +++ b/src/item/component/EnchantableComponent.php @@ -67,7 +67,7 @@ public function getValue(): array { } public function getPropertyMapping(): ?array { - return ['enchantable_slot' => 'slot', 'enchantable_value' => 'value']; + return ['enchantable_slot' => $this->slot, 'enchantable_value' => $this->value]; } public static function fromJson(mixed $data): static { diff --git a/src/item/component/GlintComponent.php b/src/item/component/GlintComponent.php index 7e116ade..9d6f5f8d 100644 --- a/src/item/component/GlintComponent.php +++ b/src/item/component/GlintComponent.php @@ -26,7 +26,7 @@ public function getValue(): array { } public function getPropertyMapping(): ?array { - return ['foil' => 'value']; + return ['foil' => $this->glint]; } public static function fromJson(mixed $data): static { diff --git a/src/item/component/HandEquippedComponent.php b/src/item/component/HandEquippedComponent.php index 370d41e4..09b159c5 100644 --- a/src/item/component/HandEquippedComponent.php +++ b/src/item/component/HandEquippedComponent.php @@ -26,7 +26,7 @@ public function getValue(): array { } public function getPropertyMapping(): ?array { - return ['hand_equipped' => 'value']; + return ['hand_equipped' => $this->handEquipped]; } public static function fromJson(mixed $data): static { diff --git a/src/item/component/HoverTextColorComponent.php b/src/item/component/HoverTextColorComponent.php index f9699c1d..13836ef6 100644 --- a/src/item/component/HoverTextColorComponent.php +++ b/src/item/component/HoverTextColorComponent.php @@ -27,7 +27,7 @@ public function getValue(): array { } public function getPropertyMapping(): ?array { - return ['hover_text_color' => 'value']; + return ['hover_text_color' => $this->hoverTextColor]; } public static function fromJson(mixed $data): static { diff --git a/src/item/component/IconComponent.php b/src/item/component/IconComponent.php index 00904f1b..1ec71d90 100644 --- a/src/item/component/IconComponent.php +++ b/src/item/component/IconComponent.php @@ -38,15 +38,12 @@ public function getName(): string { } public function getValue(): array { - return [ - "textures" => [ - "default" => $this->default_texture, - "dyed" => $this->dyed_texture == "" ? $this->default_texture : $this->dyed_texture, - "icon_trim" => $this->trim_texture == "" ? $this->default_texture : $this->trim_texture, - "bundle_open_back" => $this->bundle_open_back_texture == "" ? $this->default_texture : $this->bundle_open_back_texture, - "bundle_open_front" => $this->bundle_open_front_texture == "" ? $this->default_texture : $this->bundle_open_front_texture - ] - ]; + $textures = ["default" => $this->default_texture]; + if($this->dyed_texture !== "") $textures["dyed"] = $this->dyed_texture; + if($this->trim_texture !== "") $textures["icon_trim"] = $this->trim_texture; + if($this->bundle_open_back_texture !== "") $textures["bundle_open_back"] = $this->bundle_open_back_texture; + if($this->bundle_open_front_texture !== "") $textures["bundle_open_front"] = $this->bundle_open_front_texture; + return ["textures" => $textures]; } public function getPropertyMapping(): ?array { diff --git a/src/item/component/ItemComponent.php b/src/item/component/ItemComponent.php index 2778b21b..a7d994ef 100644 --- a/src/item/component/ItemComponent.php +++ b/src/item/component/ItemComponent.php @@ -19,7 +19,7 @@ public function getValue(): mixed; /** * Returns property mappings if this component maps to item_properties. - * @return array|null [propertyName => keyInValue] or null if not a property + * @return array|null [propertyName => propertyValue] or null if not a property */ public function getPropertyMapping(): ?array; diff --git a/src/item/component/LiquidClippedComponent.php b/src/item/component/LiquidClippedComponent.php index cb3a12e3..ed7f72eb 100644 --- a/src/item/component/LiquidClippedComponent.php +++ b/src/item/component/LiquidClippedComponent.php @@ -26,7 +26,7 @@ public function getValue(): array { } public function getPropertyMapping(): ?array { - return ['liquid_clipped' => 'value']; + return ['liquid_clipped' => $this->liquidClipped]; } public static function fromJson(mixed $data): static { diff --git a/src/item/component/MaxStackSizeComponent.php b/src/item/component/MaxStackSizeComponent.php index defc3577..0b608b1e 100644 --- a/src/item/component/MaxStackSizeComponent.php +++ b/src/item/component/MaxStackSizeComponent.php @@ -26,7 +26,7 @@ public function getValue(): array { } public function getPropertyMapping(): ?array { - return ['max_stack_size' => 'value']; + return ['max_stack_size' => $this->maxStackSize]; } public static function fromJson(mixed $data): static { diff --git a/src/item/component/ShouldDespawnComponent.php b/src/item/component/ShouldDespawnComponent.php index cb226053..76ec7e71 100644 --- a/src/item/component/ShouldDespawnComponent.php +++ b/src/item/component/ShouldDespawnComponent.php @@ -26,7 +26,7 @@ public function getValue(): array { } public function getPropertyMapping(): ?array { - return ['should_despawn' => 'value']; + return ['should_despawn' => $this->shouldDespawn]; } public static function fromJson(mixed $data): static { diff --git a/src/item/component/StackedByDataComponent.php b/src/item/component/StackedByDataComponent.php index 35046e53..5b006c7e 100644 --- a/src/item/component/StackedByDataComponent.php +++ b/src/item/component/StackedByDataComponent.php @@ -27,7 +27,7 @@ public function getValue(): array { } public function getPropertyMapping(): ?array { - return ['stacked_by_data' => 'value']; + return ['stacked_by_data' => $this->stackedByData]; } public static function fromJson(mixed $data): static { diff --git a/src/item/component/UseAnimationComponent.php b/src/item/component/UseAnimationComponent.php index b4900492..65cf3dc4 100644 --- a/src/item/component/UseAnimationComponent.php +++ b/src/item/component/UseAnimationComponent.php @@ -5,24 +5,37 @@ final class UseAnimationComponent implements ItemComponent { - public const ANIMATION_NONE = 0; - public const ANIMATION_EAT = 1; - public const ANIMATION_DRINK = 2; - public const ANIMATION_BLOCK = 3; - public const ANIMATION_BOW = 4; - public const ANIMATION_CAMERA = 5; - public const ANIMATION_SPEAR = 6; - public const ANIMATION_CROSSBOW = 9; - public const ANIMATION_SPYGLASS = 10; - public const ANIMATION_BRUSH = 12; - - private int $animation; + public const ANIMATION_NONE = 'none'; + public const ANIMATION_EAT = 'eat'; + public const ANIMATION_DRINK = 'drink'; + public const ANIMATION_BLOCK = 'block'; + public const ANIMATION_BOW = 'bow'; + public const ANIMATION_CAMERA = 'camera'; + public const ANIMATION_SPEAR = 'spear'; + public const ANIMATION_CROSSBOW = 'crossbow'; + public const ANIMATION_SPYGLASS = 'spyglass'; + public const ANIMATION_BRUSH = 'brush'; + + private const STRING_TO_INT = [ + self::ANIMATION_NONE => 0, + self::ANIMATION_EAT => 1, + self::ANIMATION_DRINK => 2, + self::ANIMATION_BLOCK => 3, + self::ANIMATION_BOW => 4, + self::ANIMATION_CAMERA => 5, + self::ANIMATION_SPEAR => 6, + self::ANIMATION_CROSSBOW => 9, + self::ANIMATION_SPYGLASS => 10, + self::ANIMATION_BRUSH => 12, + ]; + + private string $animation; /** * Determines which animation plays when using an item. - * @param int $animation Specifies which animation to play when the the item is used. + * @param string $animation Specifies which animation to play when the the item is used. */ - public function __construct(int $animation) { + public function __construct(string $animation) { $this->animation = $animation; } @@ -37,7 +50,7 @@ public function getValue(): array { } public function getPropertyMapping(): ?array { - return ['use_animation' => 'value']; + return ['use_animation' => self::STRING_TO_INT[$this->animation] ?? 0]; } public static function fromJson(mixed $data): static { diff --git a/src/item/component/UseModifiersComponent.php b/src/item/component/UseModifiersComponent.php index 2220c096..ffcda696 100644 --- a/src/item/component/UseModifiersComponent.php +++ b/src/item/component/UseModifiersComponent.php @@ -30,7 +30,7 @@ public function getValue(): array { } public function getPropertyMapping(): ?array { - return ['use_duration' => 'use_duration']; + return ['use_duration' => $this->useDuration]; } public static function fromJson(mixed $data): static { diff --git a/src/json/BehaviorManager.php b/src/json/BehaviorManager.php new file mode 100644 index 00000000..1ac156dd --- /dev/null +++ b/src/json/BehaviorManager.php @@ -0,0 +1,182 @@ +behaviorDirectory = Customies::getInstance()->getDataFolder() . "behavior/"; + $this->ensureDirectoriesExist(); + } + + /** + * Ensures the behavior directories exist + */ + private function ensureDirectoriesExist(): void { + foreach(['items', 'blocks'] as $subdir) { + $path = $this->behaviorDirectory . $subdir . "/"; + if(!is_dir($path)) { + mkdir($path, 0777, true); + } + } + } + + /** + * Gets all JSON files in a subdirectory + * @param string $subdir The subdirectory name ('items' or 'blocks') + * @return string[] + */ + private function getJsonFiles(string $subdir): array { + $path = $this->behaviorDirectory . $subdir . "/"; + $files = scandir($path); + if($files === false) { + return []; + } + return array_filter($files, function($file) use ($path) { + return $file !== '.' && + $file !== '..' && + is_file($path . $file) && + str_ends_with($file, '.json'); + }); + } + + /** + * Gets a configuration from a JSON file + */ + private function getConfig(string $subdir, string $filename): Config { + return new Config($this->behaviorDirectory . $subdir . "/" . $filename, Config::JSON); + } + + /** + * Registers all items and blocks from behavior files + */ + public function registerAll(): void { + $this->registerItems(); + $this->registerBlocks(); + } + + /** + * Registers all items from JSON files + */ + public function registerItems(): void { + $this->registerFromDirectory('items', 'minecraft:item', function(array $config): void { + $this->registerItem($config); + }); + } + + /** + * Registers all blocks from JSON files + */ + public function registerBlocks(): void { + $this->registerFromDirectory('blocks', 'minecraft:block', function(array $config): void { + $this->registerBlock($config); + }); + } + + /** + * Generic registration loop + */ + private function registerFromDirectory(string $subdir, string $rootKey, callable $register): void { + $registeredCount = 0; + $errorCount = 0; + $type = rtrim($subdir, 's'); // 'items' -> 'item' + + foreach($this->getJsonFiles($subdir) as $file) { + try { + $config = $this->getConfig($subdir, $file)->getAll(); + if(!isset($config[$rootKey])) { + throw new \InvalidArgumentException("Missing $rootKey"); + } + $register($config[$rootKey]); + $registeredCount++; + } catch(\Exception $e) { + $errorCount++; + Customies::getInstance()->getLogger()->error( + "Failed to register $type from '$file': " . $e->getMessage() + ); + } + } + + if($registeredCount > 0 || $errorCount > 0) { + Customies::getInstance()->getLogger()->info( + "Registered $registeredCount custom {$subdir}" . + ($errorCount > 0 ? " ($errorCount failed)" : "") + ); + } + } + + /** + * Registers a single item from config + */ + private function registerItem(array $config): void { + if(!isset($config["components"], $config["description"]["identifier"])) { + throw new \InvalidArgumentException("Missing required fields"); + } + + $identifier = $config["description"]["identifier"]; + $components = $config["components"]; + $creativeInfo = $this->getCreativeInfo($config); + + CustomiesItemFactory::getInstance()->registerItem( + static fn() => new CustomiesItem($components), + $identifier, + $creativeInfo + ); + } + + /** + * Registers a single block from config + */ + private function registerBlock(array $config): void { + if(!isset($config["components"], $config["description"]["identifier"])) { + throw new \InvalidArgumentException("Missing required fields"); + } + + $identifier = $config["description"]["identifier"]; + $components = $config["components"]; + $creativeInfo = $this->getCreativeInfo($config); + + CustomiesBlockFactory::getInstance()->registerBlock( + static fn(): Block => new CustomiesBlock($components), + $identifier, + $creativeInfo + ); + } + + /** + * Gets creative inventory info from config + */ + private function getCreativeInfo(array $config): CreativeInventoryInfo { + $category = CreativeInventoryInfo::CATEGORY_ITEMS; + $group = CreativeInventoryInfo::NONE; + + if(isset($config["description"]["menu_category"])) { + $menuCategory = $config["description"]["menu_category"]; + $category = $menuCategory["category"] ?? $category; + $group = $menuCategory["group"] ?? $group; + } + + return new CreativeInventoryInfo($category, $group); + } +} diff --git a/src/json/BlockComponentRegistry.php b/src/json/BlockComponentRegistry.php new file mode 100644 index 00000000..2627ff2c --- /dev/null +++ b/src/json/BlockComponentRegistry.php @@ -0,0 +1,103 @@ +> */ + private static array $components = [ + 'minecraft:collision_box' => CollisionBoxComponent::class, + 'minecraft:crafting_table' => CraftingTableComponent::class, + 'minecraft:destructible_by_explosion' => DestructibleByExplosionComponent::class, + 'minecraft:destructible_by_mining' => DestructibleByMiningComponent::class, + 'minecraft:destruction_particles' => DestructionParticlesComponent::class, + 'minecraft:display_name' => DisplayNameComponent::class, + 'minecraft:flammable' => FlammableComponent::class, + 'minecraft:friction' => FrictionComponent::class, + 'minecraft:geometry' => GeometryComponent::class, + 'minecraft:item_visual' => ItemVisualComponent::class, + 'minecraft:light_dampening' => LightDampeningComponent::class, + 'minecraft:light_emission' => LightEmissionComponent::class, + 'minecraft:liquid_detection' => LiquidDetectionComponent::class, + 'minecraft:loot' => LootComponent::class, + 'minecraft:map_color' => MapColorComponent::class, + 'minecraft:material_instances' => MaterialInstancesComponent::class, + 'minecraft:movable' => MovableComponent::class, + 'minecraft:placement_filter' => PlacementFilterComponent::class, + 'minecraft:random_offset' => RandomOffsetComponent::class, + 'minecraft:redstone_conductivity' => RedstoneConductivityComponent::class, + 'minecraft:selection_box' => SelectionBoxComponent::class, + ]; + + /** + * Register a custom component class. + * @param string $name The component identifier (e.g., 'yourplugin:custom_component') + * @param class-string $class The component class + */ + public static function register(string $name, string $class): void { + self::$components[$name] = $class; + } + + /** + * Get a component class by its identifier. + * @return class-string|null + */ + public static function get(string $name): ?string { + return self::$components[$name] ?? null; + } + + /** + * Check if a component is registered. + */ + public static function has(string $name): bool { + return isset(self::$components[$name]); + } + + /** + * Create a component instance from JSON data. + * @return BlockComponent|null Returns null if component is not registered + */ + public static function fromJson(string $name, mixed $data): ?BlockComponent { + $class = self::get($name); + if($class === null) { + return null; + } + return $class::fromJson($data); + } + + /** + * Get all registered component identifiers. + * @return string[] + */ + public static function getAll(): array { + return array_keys(self::$components); + } +} diff --git a/src/json/BlockManager.php b/src/json/BlockManager.php deleted file mode 100644 index 253bdf23..00000000 --- a/src/json/BlockManager.php +++ /dev/null @@ -1,157 +0,0 @@ -blocksDirectory = Customies::getInstance()->getDataFolder() . "behavior/blocks/"; - $this->ensureDirectoryExists(); - } - - /** - * Ensures the blocks directory exists - */ - private function ensureDirectoryExists(): void { - if(!is_dir($this->blocksDirectory)) { - mkdir($this->blocksDirectory, 0777, true); - } - } - - /** - * Gets all block file names in the blocks directory - */ - public function getBlockFiles(): array { - $blocksFolder = scandir($this->blocksDirectory); - if($blocksFolder === false) { - return []; - } - return array_filter($blocksFolder, function($block) { - return $block !== '.' && - $block !== '..' && - is_file($this->blocksDirectory . $block) && - $this->isValidBlockFile($block); - }); - } - - /** - * Validates if a file is a valid block configuration file - * @param string $filename - * @return bool True if the file is a valid block config (e.g., ends with .json) - */ - private function isValidBlockFile(string $filename): bool { - return str_ends_with($filename, '.json'); - } - - /** - * Gets the configuration for a specific block - * @param string $blockName The name of the block configuration file - * @return Config The configuration object for the block - */ - public function getBlockConfig(string $blockName): Config { - $configPath = $this->blocksDirectory . $blockName; - return new Config($configPath, Config::JSON); - } - - /** - * Gets all block configurations - * @return Config[] An associative array of block file names to their Config objects - */ - public function getBlockConfigs(): array { - $configs = []; - foreach($this->getBlockFiles() as $blockFile) { - $configs[$blockFile] = $this->getBlockConfig($blockFile); - } - return $configs; - } - - /** - * Registers all blocks from configuration files - */ - public function registerBlocks(): void { - $registeredCount = 0; - $errorCount = 0; - foreach($this->getBlockFiles() as $blockFile) { - try { - $this->registerBlock($blockFile); - $registeredCount++; - } catch(\Exception $e) { - $errorCount++; - Customies::getInstance()->getLogger()->error( - "Failed to register block from '$blockFile': " . $e->getMessage() - ); - } - } - Customies::getInstance()->getLogger()->info( - "Registered $registeredCount custom blocks" . - ($errorCount > 0 ? " ($errorCount failed)" : "") - ); - } - - /** - * Registers a single block from its configuration file - * @param string $blockFile The name of the block configuration file - * @throws \InvalidArgumentException If the block configuration is invalid - */ - public function registerBlock(string $blockFile): void { - $blockConfig = $this->getBlockConfig($blockFile)->getAll(); - - if(!isset($blockConfig["minecraft:block"])) { - throw new \InvalidArgumentException("Invalid block config: missing minecraft:block"); - } - - $minecraftBlock = $blockConfig["minecraft:block"]; - - if(!isset($minecraftBlock["components"], $minecraftBlock["description"]["identifier"])) { - throw new \InvalidArgumentException("Invalid block config: missing required fields"); - } - - $identifier = $minecraftBlock["description"]["identifier"]; - $components = $minecraftBlock["components"]; - - $creativeInfo = $this->getCreativeInventoryInfo($minecraftBlock); - - CustomiesBlockFactory::getInstance()->registerBlock( - static function() use ($components): Block { - return new CustomiesBlock($components); - }, - $identifier, - $creativeInfo - ); - } - - /** - * Gets creative inventory information from config - * @param array $blockConfig The block configuration array - * @return CreativeInventoryInfo The creative inventory info object - */ - private function getCreativeInventoryInfo(array $blockConfig): CreativeInventoryInfo { - $category = CreativeInventoryInfo::CATEGORY_ITEMS; - $group = CreativeInventoryInfo::NONE; - - if(isset($blockConfig["description"]["menu_category"])) { - $creativeConfig = $blockConfig["description"]["menu_category"]; - $category = $creativeConfig["category"] ?? $category; - $group = $creativeConfig["group"] ?? $group; - } - - return new CreativeInventoryInfo($category, $group); - } -} \ No newline at end of file diff --git a/src/json/CustomiesBlock.php b/src/json/CustomiesBlock.php index d67edbf5..54b16d03 100644 --- a/src/json/CustomiesBlock.php +++ b/src/json/CustomiesBlock.php @@ -1,30 +1,9 @@ > - */ - private static array $componentRegistry = [ - 'minecraft:collision_box' => CollisionBoxComponent::class, - 'minecraft:crafting_table' => CraftingTableComponent::class, - 'minecraft:destructible_by_explosion' => DestructibleByExplosionComponent::class, - 'minecraft:destructible_by_mining' => DestructibleByMiningComponent::class, - 'minecraft:destruction_particles' => DestructionParticlesComponent::class, - 'minecraft:display_name' => DisplayNameComponent::class, - 'minecraft:flammable' => FlammableComponent::class, - 'minecraft:friction' => FrictionComponent::class, - 'minecraft:geometry' => GeometryComponent::class, - 'minecraft:item_visual' => ItemVisualComponent::class, - 'minecraft:light_dampening' => LightDampeningComponent::class, - 'minecraft:light_emission' => LightEmissionComponent::class, - 'minecraft:liquid_detection' => LiquidDetectionComponent::class, - 'minecraft:loot' => LootComponent::class, - 'minecraft:map_color' => MapColorComponent::class, - 'minecraft:material_instances' => MaterialInstancesComponent::class, - 'minecraft:movable' => MovableComponent::class, - 'minecraft:placement_filter' => PlacementFilterComponent::class, - 'minecraft:random_offset' => RandomOffsetComponent::class, - 'minecraft:redstone_conductivity' => RedstoneConductivityComponent::class, - 'minecraft:selection_box' => SelectionBoxComponent::class, - ]; - + public function __construct(array $components) { - foreach ($components as $componentName => $componentData) { - $componentClass = self::$componentRegistry[$componentName] ?? null; - if ($componentClass !== null && method_exists($componentClass, 'fromJson')) { - $this->addComponent($componentClass::fromJson($componentData)); + foreach($components as $name => $data) { + $component = BlockComponentRegistry::fromJson($name, $data); + if($component !== null) { + $this->addComponent($component); } } parent::__construct( new BlockIdentifier(BlockTypeIds::newId()), "Custom Block", new BlockTypeInfo(new BlockBreakInfo( - $hardness ?? 1.0, - $toolType ?? BlockToolType::NONE, - $toolHarvestLevel ?? 0, + $hardness ?? 1.0, + $toolType ?? BlockToolType::NONE, + $toolHarvestLevel ?? 0, $blastResistance ?? null )) ); diff --git a/src/json/CustomiesItem.php b/src/json/CustomiesItem.php index 89f50c1f..e79f0dbc 100644 --- a/src/json/CustomiesItem.php +++ b/src/json/CustomiesItem.php @@ -1,48 +1,8 @@ > - */ - private static array $componentRegistry = [ - 'minecraft:allow_off_hand' => AllowOffHandComponent::class, - 'minecraft:block_placer' => BlockPlacerComponent::class, - 'minecraft:bundle_interaction' => BundleInteractionComponent::class, - 'minecraft:can_destroy_in_creative' => CanDestroyInCreativeComponent::class, - 'minecraft:compostable' => CompostableComponent::class, - 'minecraft:cooldown' => CooldownComponent::class, - 'minecraft:damage_absorption' => DamageAbsorptionComponent::class, - 'minecraft:damage' => DamageComponent::class, - 'minecraft:digger' => DiggerComponent::class, - 'minecraft:display_name' => DisplayNameComponent::class, - 'minecraft:durability' => DurabilityComponent::class, - 'minecraft:durability_sensor' => DurabilitySensorComponent::class, - 'minecraft:dyeable' => DyeableComponent::class, - 'minecraft:enchantable' => EnchantableComponent::class, - 'minecraft:fire_resistant' => FireResistantComponent::class, - 'minecraft:food' => FoodComponent::class, - 'minecraft:fuel' => FuelComponent::class, - 'minecraft:glint' => GlintComponent::class, - 'minecraft:hand_equipped' => HandEquippedComponent::class, - 'minecraft:hover_text_color' => HoverTextColorComponent::class, - 'minecraft:icon' => IconComponent::class, - 'minecraft:interact_button' => InteractButtonComponent::class, - 'minecraft:liquid_clipped' => LiquidClippedComponent::class, - 'minecraft:max_stack_size' => MaxStackSizeComponent::class, - 'minecraft:projectile' => ProjectileComponent::class, - 'minecraft:rarity' => RarityComponent::class, - 'minecraft:record' => RecordComponent::class, - 'minecraft:repairable' => RepairableComponent::class, - 'minecraft:shooter' => ShooterComponent::class, - 'minecraft:should_despawn' => ShouldDespawnComponent::class, - 'minecraft:stacked_by_data' => StackedByDataComponent::class, - 'minecraft:storage_item' => StorageItemComponent::class, - 'minecraft:storage_weight_limit' => StorageWeightLimitComponent::class, - 'minecraft:storage_weight_modifier' => StorageWeightModifierComponent::class, - 'minecraft:swing_duration' => SwingDurationComponent::class, - 'minecraft:tags' => TagsComponent::class, - 'minecraft:throwable' => ThrowableComponent::class, - 'minecraft:use_animation' => UseAnimationComponent::class, - 'minecraft:use_modifiers' => UseModifiersComponent::class, - 'minecraft:wearable' => WearableComponent::class, - ]; - + public function __construct(array $components) { parent::__construct(new ItemIdentifier(ItemTypeIds::newId())); - foreach ($components as $componentName => $componentData) { - $componentClass = self::$componentRegistry[$componentName] ?? null; - if ($componentClass !== null && method_exists($componentClass, 'fromJson')) { - $this->addComponent($componentClass::fromJson($componentData)); + foreach($components as $name => $data) { + $component = ItemComponentRegistry::fromJson($name, $data); + if($component !== null) { + $this->addComponent($component); } } } diff --git a/src/json/ItemComponentRegistry.php b/src/json/ItemComponentRegistry.php new file mode 100644 index 00000000..56f19ade --- /dev/null +++ b/src/json/ItemComponentRegistry.php @@ -0,0 +1,141 @@ +> */ + private static array $components = [ + 'minecraft:allow_off_hand' => AllowOffHandComponent::class, + 'minecraft:block_placer' => BlockPlacerComponent::class, + 'minecraft:bundle_interaction' => BundleInteractionComponent::class, + 'minecraft:can_destroy_in_creative' => CanDestroyInCreativeComponent::class, + 'minecraft:compostable' => CompostableComponent::class, + 'minecraft:cooldown' => CooldownComponent::class, + 'minecraft:damage_absorption' => DamageAbsorptionComponent::class, + 'minecraft:damage' => DamageComponent::class, + 'minecraft:digger' => DiggerComponent::class, + 'minecraft:display_name' => DisplayNameComponent::class, + 'minecraft:durability' => DurabilityComponent::class, + 'minecraft:durability_sensor' => DurabilitySensorComponent::class, + 'minecraft:dyeable' => DyeableComponent::class, + 'minecraft:enchantable' => EnchantableComponent::class, + 'minecraft:fire_resistant' => FireResistantComponent::class, + 'minecraft:food' => FoodComponent::class, + 'minecraft:fuel' => FuelComponent::class, + 'minecraft:glint' => GlintComponent::class, + 'minecraft:hand_equipped' => HandEquippedComponent::class, + 'minecraft:hover_text_color' => HoverTextColorComponent::class, + 'minecraft:icon' => IconComponent::class, + 'minecraft:interact_button' => InteractButtonComponent::class, + 'minecraft:liquid_clipped' => LiquidClippedComponent::class, + 'minecraft:max_stack_size' => MaxStackSizeComponent::class, + 'minecraft:projectile' => ProjectileComponent::class, + 'minecraft:rarity' => RarityComponent::class, + 'minecraft:record' => RecordComponent::class, + 'minecraft:repairable' => RepairableComponent::class, + 'minecraft:shooter' => ShooterComponent::class, + 'minecraft:should_despawn' => ShouldDespawnComponent::class, + 'minecraft:stacked_by_data' => StackedByDataComponent::class, + 'minecraft:storage_item' => StorageItemComponent::class, + 'minecraft:storage_weight_limit' => StorageWeightLimitComponent::class, + 'minecraft:storage_weight_modifier' => StorageWeightModifierComponent::class, + 'minecraft:swing_duration' => SwingDurationComponent::class, + 'minecraft:tags' => TagsComponent::class, + 'minecraft:throwable' => ThrowableComponent::class, + 'minecraft:use_animation' => UseAnimationComponent::class, + 'minecraft:use_modifiers' => UseModifiersComponent::class, + 'minecraft:wearable' => WearableComponent::class, + ]; + + /** + * Register a custom component class. + * @param string $name The component identifier (e.g., 'yourplugin:custom_effect') + * @param class-string $class The component class + */ + public static function register(string $name, string $class): void { + self::$components[$name] = $class; + } + + /** + * Get a component class by its identifier. + * @return class-string|null + */ + public static function get(string $name): ?string { + return self::$components[$name] ?? null; + } + + /** + * Check if a component is registered. + */ + public static function has(string $name): bool { + return isset(self::$components[$name]); + } + + /** + * Create a component instance from JSON data. + * @return ItemComponent|null Returns null if component is not registered + */ + public static function fromJson(string $name, mixed $data): ?ItemComponent { + $class = self::get($name); + if($class === null) { + return null; + } + return $class::fromJson($data); + } + + /** + * Get all registered component identifiers. + * @return string[] + */ + public static function getAll(): array { + return array_keys(self::$components); + } +} diff --git a/src/json/ItemManager.php b/src/json/ItemManager.php deleted file mode 100644 index 633eb382..00000000 --- a/src/json/ItemManager.php +++ /dev/null @@ -1,154 +0,0 @@ -itemsDirectory = Customies::getInstance()->getDataFolder() . "behavior/items/"; - $this->ensureDirectoryExists(); - } - - /** - * Ensures the items directory exists - */ - private function ensureDirectoryExists(): void { - if(!is_dir($this->itemsDirectory)) { - mkdir($this->itemsDirectory, 0777, true); - } - } - - /** - * Gets all item file names in the items directory - */ - public function getItemFiles(): array { - $itemsFolder = scandir($this->itemsDirectory); - if($itemsFolder === false) { - return []; - } - return array_filter($itemsFolder, function($item) { - return $item !== '.' && - $item !== '..' && - is_file($this->itemsDirectory . $item) && - $this->isValidItemFile($item); - }); - } - - /** - * Validates if a file is a valid item configuration file - * @param string $filename - * @return bool True if the file is a valid item config (e.g., ends with .json) - */ - private function isValidItemFile(string $filename): bool { - return str_ends_with($filename, '.json'); - } - - /** - * Gets the configuration for a specific item - * @param string $itemName The name of the item configuration file - * @return Config The configuration object for the item - */ - public function getItemConfig(string $itemName): Config { - $configPath = $this->itemsDirectory . $itemName; - return new Config($configPath, Config::JSON); - } - - /** - * Gets all item configurations - * @return Config[] An associative array of item file names to their Config objects - */ - public function getItemConfigs(): array { - $configs = []; - foreach($this->getItemFiles() as $itemFile) { - $configs[$itemFile] = $this->getItemConfig($itemFile); - } - return $configs; - } - - /** - * Registers all items from configuration files - */ - public function registerItems(): void { - $registeredCount = 0; - $errorCount = 0; - foreach($this->getItemFiles() as $itemFile) { - try { - $this->registerItem($itemFile); - $registeredCount++; - } catch(\Exception $e) { - $errorCount++; - Customies::getInstance()->getLogger()->error( - "Failed to register item from '$itemFile': " . $e->getMessage() - ); - } - } - Customies::getInstance()->getLogger()->info( - "Registered $registeredCount custom items" . - ($errorCount > 0 ? " ($errorCount failed)" : "") - ); - } - - /** - * Registers a single item from its configuration file - * @param string $itemFile The name of the item configuration file - * @throws \InvalidArgumentException If the item configuration is invalid - */ - public function registerItem(string $itemFile): void { - $itemConfig = $this->getItemConfig($itemFile)->getAll(); - - if(!isset($itemConfig["minecraft:item"])) { - throw new \InvalidArgumentException("Invalid item config: missing minecraft:item"); - } - - $minecraftItem = $itemConfig["minecraft:item"]; - - if(!isset($minecraftItem["components"], $minecraftItem["description"]["identifier"])) { - throw new \InvalidArgumentException("Invalid item config: missing required fields"); - } - - $customItem = new CustomiesItem($minecraftItem["components"]); - $identifier = $minecraftItem["description"]["identifier"]; - - $creativeInfo = $this->getCreativeInventoryInfo($minecraftItem); - - CustomiesItemFactory::getInstance()->registerItem( - static fn() => $customItem, - $identifier, - $creativeInfo - ); - } - - /** - * Gets creative inventory information from config - * @param array $itemConfig The item configuration array - * @return CreativeInventoryInfo The creative inventory info object - */ - private function getCreativeInventoryInfo(array $itemConfig): CreativeInventoryInfo { - $category = CreativeInventoryInfo::CATEGORY_ITEMS; - $group = CreativeInventoryInfo::NONE; - - if(isset($itemConfig["description"]["menu_category"])) { - $creativeConfig = $itemConfig["description"]["menu_category"]; - $category = $creativeConfig["category"] ?? $category; - $group = $creativeConfig["group"] ?? $group; - } - - return new CreativeInventoryInfo($category, $group); - } -} \ No newline at end of file From a3fb86b75a2098bd2924a1518184c67a7ca7b67b Mon Sep 17 00:00:00 2001 From: JeanTKG <102908437+jeantkg@users.noreply.github.com> Date: Sun, 14 Dec 2025 17:38:22 +0300 Subject: [PATCH 11/51] feat: Implement CollisionBoxComponent and Box class for enhanced collision handling --- src/block/CustomiesBlockFactory.php | 1 + src/block/component/CollisionBoxComponent.php | 107 +++++++++++------- src/block/properties/Box.php | 73 ++++++++++++ 3 files changed, 141 insertions(+), 40 deletions(-) create mode 100644 src/block/properties/Box.php diff --git a/src/block/CustomiesBlockFactory.php b/src/block/CustomiesBlockFactory.php index d67356c7..6e0aec3c 100644 --- a/src/block/CustomiesBlockFactory.php +++ b/src/block/CustomiesBlockFactory.php @@ -251,6 +251,7 @@ private function createBlockNBT(Block $block, ?CreativeInventoryInfo $creativeIn $propertiesTag ->setTag("components", $components) ->setInt("molangVersion", 13); + \var_dump($propertiesTag->__toString()); return $propertiesTag; } return CompoundTag::create(); diff --git a/src/block/component/CollisionBoxComponent.php b/src/block/component/CollisionBoxComponent.php index b7e93825..76e65b22 100644 --- a/src/block/component/CollisionBoxComponent.php +++ b/src/block/component/CollisionBoxComponent.php @@ -1,29 +1,45 @@ useCollisionBox = $useCollisionBox; - $this->origin = $origin; - $this->size = $size; + public function __construct(bool $enabled = true) { + $this->enabled = $enabled; + } + + /** + * Add a collision box. + * @param Box $box The box to add + * @return $this + */ + public function addBox(Box $box): self { + $this->boxes[] = $box; + return $this; + } + + /** + * Add collision boxes. + * @param Box[] $boxes The boxes to add + * @return $this + */ + public function addBoxes(array $boxes): self { + foreach($boxes as $box) { + $this->boxes[] = $box; + } + return $this; } public function getName(): string { @@ -31,37 +47,48 @@ public function getName(): string { } public function getValue(): array { + $convertedBoxes = []; + foreach($this->boxes as $box) { + $convertedBoxes[] = $box->toNbtArray(); + } return [ - "enabled" => $this->useCollisionBox, - "origin" => [ - $this->origin->getX(), - $this->origin->getY(), - $this->origin->getZ() - ], - "size" => [ - $this->size->getX(), - $this->size->getY(), - $this->size->getZ() - ] + "enabled" => $this->enabled ? 1 : 0, + "boxes" => $convertedBoxes ]; } public static function fromJson(mixed $data): static { - if (is_bool($data)) { + // false or true + if(is_bool($data)) { return new self($data); } - return new self( - true, - new Vector3( - $data["origin"][0] ?? -8, - $data["origin"][1] ?? 0, - $data["origin"][2] ?? -8 - ), - new Vector3( - $data["size"][0] ?? 16, - $data["size"][1] ?? 16, - $data["size"][2] ?? 16 - ) - ); + + $component = new self(true); + $boxes = []; + + // Array of boxes + if(is_array($data) && isset($data[0])) { + foreach($data as $box) { + $origin = $box['origin'] ?? [-8, 0, -8]; + $size = $box['size'] ?? [16, 24, 16]; + $boxes[] = new Box( + new Vector3($origin[0], $origin[1], $origin[2]), + new Vector3($size[0], $size[1], $size[2]) + ); + } + return $component->addBoxes($boxes); + } + + // Single box object + if(is_array($data) && isset($data['origin'])) { + $origin = $data['origin']; + $size = $data['size'] ?? [16, 24, 16]; + return $component->addBox(new Box( + new Vector3($origin[0], $origin[1], $origin[2]), + new Vector3($size[0], $size[1], $size[2]) + )); + } + + return $component; } } \ No newline at end of file diff --git a/src/block/properties/Box.php b/src/block/properties/Box.php new file mode 100644 index 00000000..ae8bc77d --- /dev/null +++ b/src/block/properties/Box.php @@ -0,0 +1,73 @@ +x)); + $originY = max(0, min(23, $origin->y)); + $originZ = max(-8, min(7, $origin->z)); + + // Clamp size + $sizeX = max(1, min(16, $size->x)); + $sizeY = max(1, min(24, $size->y)); + $sizeZ = max(1, min(16, $size->z)); + + // Clamp to ensure origin + size is valid + $sizeX = min($sizeX, 8 - $originX); + $sizeY = min($sizeY, 24 - $originY); + $sizeZ = min($sizeZ, 8 - $originZ); + + $this->origin = new Vector3($originX, $originY, $originZ); + $this->size = new Vector3($sizeX, $sizeY, $sizeZ); + } + + public static function fromAxisAlignedBB(AxisAlignedBB $bb): self { + return new self( + new Vector3($bb->minX, $bb->minY, $bb->minZ), + new Vector3($bb->maxX - $bb->minX, $bb->maxY - $bb->minY, $bb->maxZ - $bb->minZ) + ); + } + + public function getOrigin(): Vector3 { return $this->origin; } + public function getSize(): Vector3 { return $this->size; } + public function getMax(): Vector3 { return $this->origin->addVector($this->size); } + + /** + * Convert to NBT output format. + */ + public function toNbtArray(): array { + $max = $this->getMax(); + return [ + "minX" => $this->origin->x + 8, + "minY" => $this->origin->y, + "minZ" => $this->origin->z + 8, + "maxX" => $max->x + 8, + "maxY" => $max->y, + "maxZ" => $max->z + 8, + ]; + } + + public function toAxisAlignedBB(): AxisAlignedBB { + $max = $this->getMax(); + return new AxisAlignedBB( + $this->origin->x, $this->origin->y, $this->origin->z, + $max->x, $max->y, $max->z + ); + } +} From 7566fc95f7f4c3a1a13e52a2731be2d72474d77a Mon Sep 17 00:00:00 2001 From: HydroGames-dev Date: Sun, 14 Dec 2025 23:16:00 +0530 Subject: [PATCH 12/51] Update Components --- plugin.yml | 5 +- src/block/CustomiesBlockFactory.php | 3 +- .../component/CraftingTableComponent.php | 38 --- src/block/component/GeometryComponent.php | 2 +- .../component/LiquidDetectionComponent.php | 2 +- .../RedstoneConductivityComponent.php | 8 +- src/block/component/SupportComponent.php | 32 +++ src/block/permutations/BlockTrait.php | 53 +++++ src/block/permutations/RotatableTrait.php | 45 +++- src/block/properties/Material.php | 4 +- src/item/CustomiesItemFactory.php | 6 +- src/item/component/BlockPlacerComponent.php | 73 ------ src/item/component/DyeableComponent.php | 29 ++- src/item/component/EnchantableComponent.php | 1 + src/item/component/KineticWeaponComponent.php | 101 ++++++++ .../component/PiercingWeaponComponent.php | 51 ++++ src/item/component/StorageItemComponent.php | 88 ++++--- src/item/component/SwingSoundsComponent.php | 45 ++++ src/item/component/UseAnimationComponent.php | 2 +- src/item/component/UseModifiersComponent.php | 37 ++- src/item/component/WearableComponent.php | 21 +- src/item/properties/DamageCause.php | 43 ++++ src/item/properties/ParticleType.php | 98 ++++++++ src/item/properties/SoundEvent.php | 224 ++++++++++++++++++ 24 files changed, 828 insertions(+), 183 deletions(-) delete mode 100644 src/block/component/CraftingTableComponent.php create mode 100644 src/block/component/SupportComponent.php create mode 100644 src/block/permutations/BlockTrait.php delete mode 100644 src/item/component/BlockPlacerComponent.php create mode 100644 src/item/component/KineticWeaponComponent.php create mode 100644 src/item/component/PiercingWeaponComponent.php create mode 100644 src/item/component/SwingSoundsComponent.php create mode 100644 src/item/properties/DamageCause.php create mode 100644 src/item/properties/ParticleType.php create mode 100644 src/item/properties/SoundEvent.php diff --git a/plugin.yml b/plugin.yml index c122b5d8..239a3439 100644 --- a/plugin.yml +++ b/plugin.yml @@ -4,12 +4,13 @@ description: A PocketMine-MP plugin that implements support for custom blocks, i main: customiesdevs\customies\Customies src-namespace-prefix: customiesdevs\customies version: 2.0.0 -api: 5.1.3 +api: 5.37.0 authors: - DenielWorld - TwistedAsylumMC + contributors: - JackNoordhuis - Unickorn - - abimek + - abimek \ No newline at end of file diff --git a/src/block/CustomiesBlockFactory.php b/src/block/CustomiesBlockFactory.php index 6e0aec3c..34e033e6 100644 --- a/src/block/CustomiesBlockFactory.php +++ b/src/block/CustomiesBlockFactory.php @@ -246,7 +246,8 @@ private function createBlockNBT(Block $block, ?CreativeInventoryInfo $creativeIn if($creativeInfo !== null) { $propertiesTag->setTag("menu_category", CompoundTag::create() ->setString("category", $creativeInfo->getCategory() ?? "") - ->setString("group", $creativeInfo->getGroup() ?? "")); + ->setString("group", $creativeInfo->getGroup() ?? "")) + ->setByte("is_hidden_in_commands", 0); } $propertiesTag ->setTag("components", $components) diff --git a/src/block/component/CraftingTableComponent.php b/src/block/component/CraftingTableComponent.php deleted file mode 100644 index cfe32bb9..00000000 --- a/src/block/component/CraftingTableComponent.php +++ /dev/null @@ -1,38 +0,0 @@ -craftingTags = $craftingTags; - $this->tableName = $tableName; - } - - public function getName(): string { - return 'minecraft:crafting_table'; - } - - public function getValue(): array { - return [ - "crafting_tags" => $this->craftingTags, - "table_name" => $this->tableName, - "grid_size" => 3 - ]; - } - - public static function fromJson(mixed $data): static { - return new self( - $data["table_name"] ?? "Crafting Table", - $data["crafting_tags"] ?? ["crafting_table"] - ); - } -} \ No newline at end of file diff --git a/src/block/component/GeometryComponent.php b/src/block/component/GeometryComponent.php index 2efd395d..8836ca78 100644 --- a/src/block/component/GeometryComponent.php +++ b/src/block/component/GeometryComponent.php @@ -22,7 +22,7 @@ public function __construct( string $identifier = "minecraft:geometry.full_block", array $boneVisibility = [], string $culling = "", - string $cullingLayer = "minecraft:culling_layer.undefined", + string $cullingLayer = "minecraft:culling_layer.undefined", array|bool $uvLock = false ) { $this->identifier = $identifier; diff --git a/src/block/component/LiquidDetectionComponent.php b/src/block/component/LiquidDetectionComponent.php index 6d2f9b93..3d302b0a 100644 --- a/src/block/component/LiquidDetectionComponent.php +++ b/src/block/component/LiquidDetectionComponent.php @@ -43,7 +43,7 @@ public function getValue(): array { "canContainLiquid" => $this->canContainLiquid, "onLiquidTouches" => $this->onLiquidTouches, "stopsLiquidFromDirection" => $this->stopsLiquidFlowingFromDirection, - ] + ] ] ]; } diff --git a/src/block/component/RedstoneConductivityComponent.php b/src/block/component/RedstoneConductivityComponent.php index d592c1d1..2a5f23d5 100644 --- a/src/block/component/RedstoneConductivityComponent.php +++ b/src/block/component/RedstoneConductivityComponent.php @@ -23,15 +23,15 @@ public function getName(): string { public function getValue(): array { return [ - "allows_wire_to_step_down" => $this->allowsWireToStepDown, - "redstone_conductor" => $this->redstoneConductor + "allowsWireToStepDown" => $this->allowsWireToStepDown, + "redstoneConductor" => $this->redstoneConductor ]; } public static function fromJson(mixed $data): static { return new self( - $data["allows_wire_to_step_down"] ?? true, - $data["redstone_conductor"] ?? false + $data["allowsWireToStepDown"] ?? true, + $data["allowsWireToStepDown"] ?? false ); } } \ No newline at end of file diff --git a/src/block/component/SupportComponent.php b/src/block/component/SupportComponent.php new file mode 100644 index 00000000..416d8bc0 --- /dev/null +++ b/src/block/component/SupportComponent.php @@ -0,0 +1,32 @@ +shape = $shape; + } + + public function getName(): string { + return 'minecraft:support'; + } + + public function getValue(): array { + return [ + "shape" => $this->shape + ]; + } + + public static function fromJson(mixed $data): static { + return new self(is_array($data) ? ($data["shape"] ?? self::STAIRS) : self::STAIRS); + } +} \ No newline at end of file diff --git a/src/block/permutations/BlockTrait.php b/src/block/permutations/BlockTrait.php new file mode 100644 index 00000000..a6fb23f5 --- /dev/null +++ b/src/block/permutations/BlockTrait.php @@ -0,0 +1,53 @@ +setTag("name", new StringTag("minecraft:placement_direction")) + ->setTag("enabled_states", CompoundTag::create() + ->setTag("cardinal_direction", new ByteTag($cardinalDirection ? 1 : 0)) + ->setTag("corner_and_cardinal_direction", new ByteTag($cornerAndCardinalDirection ? 1 : 0)) + ->setTag("facing_direction", new ByteTag($facingDirection ? 1 : 0)) + ) + ->setTag("blocks_to_corner_with", new ListTag([], NBT::TAG_String)) + ->setTag("y_rotation_offset", new FloatTag((float) $yRotationOffset)); + + return CompoundTag::create() + ->setTag("traits", new ListTag([$trait], NBT::TAG_Compound)); + } + + public function addPlacementPosition( + bool $blockFace = false, + bool $verticalHalf = false + ): CompoundTag { + $trait = CompoundTag::create() + ->setTag("name", new StringTag("minecraft:placement_position")) + ->setTag("enabled_states", CompoundTag::create() + ->setTag("block_face", new ByteTag($blockFace ? 1 : 0)) + ->setTag("vertical_half", new ByteTag($verticalHalf ? 1 : 0)) + ); + + return CompoundTag::create() + ->setTag("traits", new ListTag([$trait], NBT::TAG_Compound)); + } +} \ No newline at end of file diff --git a/src/block/permutations/RotatableTrait.php b/src/block/permutations/RotatableTrait.php index 5e16d741..9018b3df 100644 --- a/src/block/permutations/RotatableTrait.php +++ b/src/block/permutations/RotatableTrait.php @@ -31,50 +31,79 @@ public function getBlockProperties(): array { */ public function getPermutations(): array { return [ - (new Permutation("q.block_property('customies:rotation') == 2")) + (new Permutation("q.block_state('customies:rotation') == 2")) ->withComponent("minecraft:transformation", CompoundTag::create() ->setInt("RX", 0) + ->setFloat("RXP", 0.0) ->setInt("RY", 0) + ->setFloat("RYP", 0.0) ->setInt("RZ", 0) + ->setFloat("RZP", 0.0) ->setFloat("SX", 1.0) + ->setFloat("SXP", 0.0) ->setFloat("SY", 1.0) + ->setFloat("SYP", 0.0) ->setFloat("SZ", 1.0) + ->setFloat("SZP", 0.0) ->setFloat("TX", 0.0) ->setFloat("TY", 0.0) - ->setFloat("TZ", 0.0)), - (new Permutation("q.block_property('customies:rotation') == 3")) + ->setFloat("TZ", 0.0) + ->setByte("hasJsonVersionBeforeValidation", 0) + ), + (new Permutation("q.block_state('customies:rotation') == 3")) ->withComponent("minecraft:transformation", CompoundTag::create() ->setInt("RX", 0) + ->setFloat("RXP", 0.0) ->setInt("RY", 2) + ->setFloat("RYP", 0.0) ->setInt("RZ", 0) + ->setFloat("RZP", 0.0) ->setFloat("SX", 1.0) + ->setFloat("SXP", 0.0) ->setFloat("SY", 1.0) + ->setFloat("SYP", 0.0) ->setFloat("SZ", 1.0) + ->setFloat("SZP", 0.0) ->setFloat("TX", 0.0) ->setFloat("TY", 0.0) - ->setFloat("TZ", 0.0)), - (new Permutation("q.block_property('customies:rotation') == 4")) + ->setFloat("TZ", 0.0)->setByte("hasJsonVersionBeforeValidation", 0) + ), + (new Permutation("q.block_state('customies:rotation') == 4")) ->withComponent("minecraft:transformation", CompoundTag::create() ->setInt("RX", 0) + ->setFloat("RXP", 0.0) ->setInt("RY", 1) + ->setFloat("RYP", 0.0) ->setInt("RZ", 0) + ->setFloat("RZP", 0.0) ->setFloat("SX", 1.0) + ->setFloat("SXP", 0.0) ->setFloat("SY", 1.0) + ->setFloat("SYP", 0.0) ->setFloat("SZ", 1.0) + ->setFloat("SZP", 0.0) ->setFloat("TX", 0.0) ->setFloat("TY", 0.0) - ->setFloat("TZ", 0.0)), - (new Permutation("q.block_property('customies:rotation') == 5")) + ->setFloat("TZ", 0.0)->setByte("hasJsonVersionBeforeValidation", 0) + ), + (new Permutation("q.block_state('customies:rotation') == 5")) ->withComponent("minecraft:transformation", CompoundTag::create() ->setInt("RX", 0) + ->setFloat("RXP", 0.0) ->setInt("RY", 3) + ->setFloat("RYP", 0.0) ->setInt("RZ", 0) + ->setFloat("RZP", 0.0) ->setFloat("SX", 1.0) + ->setFloat("SXP", 0.0) ->setFloat("SY", 1.0) + ->setFloat("SYP", 0.0) ->setFloat("SZ", 1.0) + ->setFloat("SZP", 0.0) ->setFloat("TX", 0.0) ->setFloat("TY", 0.0) - ->setFloat("TZ", 0.0)), + ->setFloat("TZ", 0.0)->setByte("hasJsonVersionBeforeValidation", 0) + ), ]; } diff --git a/src/block/properties/Material.php b/src/block/properties/Material.php index 7fc8f870..2ec56db0 100644 --- a/src/block/properties/Material.php +++ b/src/block/properties/Material.php @@ -48,7 +48,7 @@ public static function fromArray(string $target, array $data): self { RenderMethod::tryFrom($data["render_method"] ?? "") ?? RenderMethod::OPAQUE, TintMethod::tryFrom($data["tint_method"] ?? "") ?? TintMethod::NONE, (float)($data["ambient_occlusion"] ?? 1.0), - (bool)($data["face_dimming"] ?? false), + (bool)($data["packed_bools"] ?? 0x01), (bool)($data["isotropic"] ?? false) ); } @@ -59,7 +59,7 @@ public function toArray(): array { "render_method" => $this->renderMethod->value, "tint_method" => $this->tintMethod->value, "ambient_occlusion" => $this->ambientOcclusion, - "face_dimming" => $this->faceDimming, + "packed_bools" => $this->faceDimming, "isotropic" => $this->isotropic ]; } diff --git a/src/item/CustomiesItemFactory.php b/src/item/CustomiesItemFactory.php index 7fbb5c43..bafb9989 100644 --- a/src/item/CustomiesItemFactory.php +++ b/src/item/CustomiesItemFactory.php @@ -43,7 +43,7 @@ final class CustomiesItemFactory { 'hand_equipped' => false, 'liquid_clipped' => false, 'max_stack_size' => 64, - 'mining_speed' => 1, + 'mining_speed' => 1.0, 'should_despawn' => true, 'stacked_by_data' => false, 'use_animation' => 0, @@ -157,7 +157,9 @@ private function createItemNbt(Item $item, string $identifier, int $itemId, ?Cre // Initialize item_properties with defaults $properties = CompoundTag::create(); foreach(self::PROPERTY_DEFAULTS as $name => $default) { - $properties->setTag($name, NBT::getTagType($default)); + $properties + ->setTag($name, NBT::getTagType($default)) + ->setByte("hidden_in_commands", 2); } // Set creative info diff --git a/src/item/component/BlockPlacerComponent.php b/src/item/component/BlockPlacerComponent.php deleted file mode 100644 index fb87a80a..00000000 --- a/src/item/component/BlockPlacerComponent.php +++ /dev/null @@ -1,73 +0,0 @@ -block = $block; - $this->replaceBlockItem = $replaceBlockItem; - } - - public function getName(): string { - return 'minecraft:block_placer'; - } - - public function getValue(): array { - return [ - "block" => GlobalBlockStateHandlers::getSerializer()->serialize($this->block->getStateId())->getName(), - "replace_block_item" => $this->replaceBlockItem, - "use_on" => $this->useOn - ]; - } - - public function getPropertyMapping(): ?array { - return null; - } - - /** - * TODO: Update this - * Add blocks to the `use_on` array in the required format. - * @param Block ...$blocks - */ - public function useOn(Block ...$blocks): self { - foreach($blocks as $block){ - $this->useOn[] = [ - "name" => GlobalBlockStateHandlers::getSerializer()->serialize($block->getStateId())->getName() - ]; - } - return $this; - } - - public static function fromJson(mixed $data): static { - $block = StringToItemParser::getInstance()->parse($data["block"] ?? "")?->getBlock(); - $blocks = $data["use_on"] ?? []; - $useOn = []; - foreach($blocks as $blockData){ - $blockId = StringToItemParser::getInstance()->parse($blockData)->getBlock(); - if($blockId !== null){ - $useOn[] = $blockId; - } - } - $blockPlacer = new self($block ?? VanillaBlocks::AIR(), $data["replace_block_item"] ?? false); - $blockPlacer->useOn(...$useOn); - return $blockPlacer; - } -} \ No newline at end of file diff --git a/src/item/component/DyeableComponent.php b/src/item/component/DyeableComponent.php index 7f3312cb..206305fa 100644 --- a/src/item/component/DyeableComponent.php +++ b/src/item/component/DyeableComponent.php @@ -5,14 +5,15 @@ final class DyeableComponent implements ItemComponent { - private string $hex; + /** @var int[] */ + private array $rgb; /** * Allows the item to be dyed by cauldron water. Once dyed, the item will display the `dyed` texture defined in the `minecraft:icon` component rather than `default`. - * @param string $hex The hex color code (e.g "#47ff5a") + * @param string $hex Hex color code (e.g. "#175882") */ public function __construct(string $hex) { - $this->hex = $hex; + $this->rgb = self::hexToRgb($hex); } public function getName(): string { @@ -21,7 +22,7 @@ public function getName(): string { public function getValue(): array { return [ - "default_color" => $this->hex + "default_color" => $this->rgb ]; } @@ -29,7 +30,25 @@ public function getPropertyMapping(): ?array { return null; } + private static function hexToRgb(string $hex): array { + $hex = ltrim($hex, '#'); + if(strlen($hex) !== 6) throw new \InvalidArgumentException("Invalid hex color: {$hex}"); + return [ + hexdec(substr($hex, 0, 2)), + hexdec(substr($hex, 2, 2)), + hexdec(substr($hex, 4, 2)) + ]; + } + + private static function rgbToHex(array $rgb): string { + [$r, $g, $b] = $rgb; + return sprintf("#%02x%02x%02x", $r, $g, $b); + } + public static function fromJson(mixed $data): static { - return new self($data["default_color"] ?? "#ffffff"); + if(isset($data["default_color"]) && is_array($data["default_color"])){ + return new self(self::rgbToHex($data["default_color"])); + } + return new self("#ffffff"); } } \ No newline at end of file diff --git a/src/item/component/EnchantableComponent.php b/src/item/component/EnchantableComponent.php index 5a12996a..9be46e62 100644 --- a/src/item/component/EnchantableComponent.php +++ b/src/item/component/EnchantableComponent.php @@ -22,6 +22,7 @@ final class EnchantableComponent implements ItemComponent { public const SLOT_SHEARS = "shears"; public const SLOT_SHIELD = "shield"; public const SLOT_SHOVEL = "shovel"; + public const SLOT_SPEAR = "melee_spear"; public const SLOT_SWORD = "sword"; // Armor Enchantability diff --git a/src/item/component/KineticWeaponComponent.php b/src/item/component/KineticWeaponComponent.php new file mode 100644 index 00000000..4345e65d --- /dev/null +++ b/src/item/component/KineticWeaponComponent.php @@ -0,0 +1,101 @@ + 2.0, 'max' => 7.5], + float $damageModifier = 0.0, + float $damageMultiplier = 0.7, + int $delay = 15, + array $dismountConditions = [ + 'min_speed' => 14.0, + 'min_relative_speed' => 0.0, + 'max_duration' => 100 + ], + float $hitboxMargin = 0.25, + array $knockbackConditions = [ + 'min_speed' => 5.1, + 'min_relative_speed' => 0.0, + 'max_duration' => 120 + ], + array $reach = ['min' => 2.0, 'max' => 4.5] + ) { + $this->creativeReach = $creativeReach; + $this->damageModifier = $damageModifier; + $this->damageMultiplier = $damageMultiplier; + $this->delay = $delay; + $this->dismountConditions = $dismountConditions; + $this->hitboxMargin = $hitboxMargin; + $this->knockbackConditions = $knockbackConditions; + $this->reach = $reach; + } + + public function getName(): string { + return 'minecraft:kinetic_weapon'; + } + + public function getValue(): array { + return [ + "creative_reach" => [ + "min" => (float) $this->creativeReach['min'], + "max" => (float) $this->creativeReach['max'] + ], + "damage_modifier" => $this->damageModifier, + "damage_multiplier" => $this->damageMultiplier, + "delay" => $this->delay, + "dismount_conditions" => [ + "min_speed" => (float) $this->dismountConditions['min_speed'], + "min_relative_speed" => (float) $this->dismountConditions['min_relative_speed'], + "max_duration" => (int) $this->dismountConditions['max_duration'] + ], + "hitbox_margin" => $this->hitboxMargin, + "knockback_conditions" => [ + "min_speed" => (float) $this->knockbackConditions['min_speed'], + "min_relative_speed" => (float) $this->knockbackConditions['min_relative_speed'], + "max_duration" => (int) $this->knockbackConditions['max_duration'] + ], + "reach" => [ + "min" => (float) $this->reach['min'], + "max" => (float) $this->reach['max'] + ], + ]; + } + + public function getPropertyMapping(): ?array { + return null; + } + + public static function fromJson(mixed $data): static { + return new self( + $data['creative_reach'] ?? ['min' => 2.0, 'max' => 7.5], + (float) ($data['damage_modifier'] ?? 0.0), + (float) ($data['damage_multiplier'] ?? 0.7), + (int) ($data['delay'] ?? 15), + $data['dismount_conditions'] ?? [ + 'min_speed' => 14.0, + 'min_relative_speed' => 0.0, + 'max_duration' => 100 + ], + (float) ($data['hitbox_margin'] ?? 0.25), + $data['knockback_conditions'] ?? [ + 'min_speed' => 14.0, + 'min_relative_speed' => 0.0, + 'max_duration' => 120 + ], + $data['reach'] ?? ['min' => 2.0, 'max' => 4.5] + ); + } +} \ No newline at end of file diff --git a/src/item/component/PiercingWeaponComponent.php b/src/item/component/PiercingWeaponComponent.php new file mode 100644 index 00000000..4f0db69f --- /dev/null +++ b/src/item/component/PiercingWeaponComponent.php @@ -0,0 +1,51 @@ + 2.0, 'max' => 7.5], + float $hitboxMargin = 0.25, + array $reach = ['min' => 2.0, 'max' => 4.5] + ) { + $this->creativeReach = $creativeReach; + $this->hitboxMargin = $hitboxMargin; + $this->reach = $reach; + } + + public function getName(): string { + return 'minecraft:piercing_weapon'; + } + + public function getValue(): array { + return [ + "creative_reach" => [ + "min" => (float) $this->creativeReach['min'], + "max" => (float) $this->creativeReach['max'] + ], + "hitbox_margin" => $this->hitboxMargin, + "reach" => [ + "min" => (float) $this->reach['min'], + "max" => (float) $this->reach['max'] + ] + ]; + } + + public function getPropertyMapping(): ?array { + return null; + } + + public static function fromJson(mixed $data): static { + return new self( + $data['creative_reach'] ?? ['min' => 2.0, 'max' => 7.5], + (float) ($data['hitbox_margin'] ?? 0.25), + $data['reach'] ?? ['min' => 2.0, 'max' => 4.5] + ); + } +} \ No newline at end of file diff --git a/src/item/component/StorageItemComponent.php b/src/item/component/StorageItemComponent.php index f26d9033..4c6c8779 100644 --- a/src/item/component/StorageItemComponent.php +++ b/src/item/component/StorageItemComponent.php @@ -3,39 +3,27 @@ namespace customiesdevs\customies\item\component; +use pocketmine\item\Item; + final class StorageItemComponent implements ItemComponent { private bool $allowNestedStorageItems; - private array $allowedItems; - private array $bannedItems; + private array $allowedItems = []; + private array $bannedItems = []; private int $maxSlots; - private int $maxWeightLimit; - private int $weightInStorageItem; /** * Enables an item to store data of the dynamic container associated with it. * A dynamic container is a container for storing items that is linked to an item instead of a block or an entity. * @param bool $allowNestedStorageItems Determines whether another Storage Item is allowed inside of this item. Default is true. - * @param array $allowedItems List of items that are exclusively allowed in this Storage Item. If empty all items are allowed. - * @param array $bannedItems List of items that are not allowed in this Storage Item. * @param int $maxSlots The maximum allowed weight of the sum of all contained items. Maximum is 64. Default is 64. Value must be >= 0. - * @param int $maxWeightLimit The maximum weight limit for the storage item. Maximum is 64. Default is 64. Value must be >= 0. - * @param int $weightInStorageItem The weight that the storage item itself contributes to the total weight of the items it contains. Value must be >= 0. */ public function __construct( - bool $allowNestedStorageItems = true, - array $allowedItems = [], - array $bannedItems = [], - int $maxSlots = 64, - int $maxWeightLimit = 64, - int $weightInStorageItem = 4 + bool $allowNestedStorageItems = true, + int $maxSlots = 64 ) { $this->allowNestedStorageItems = $allowNestedStorageItems; - $this->allowedItems = $allowedItems; - $this->bannedItems = $bannedItems; $this->maxSlots = $maxSlots; - $this->maxWeightLimit = $maxWeightLimit; - $this->weightInStorageItem = $weightInStorageItem; } public function getName(): string { @@ -47,9 +35,7 @@ public function getValue(): array { "allow_nested_storage_items" => $this->allowNestedStorageItems, "allowed_items" => $this->allowedItems, "banned_items" => $this->bannedItems, - "max_slots" => $this->maxSlots, - "max_weight_limit" => $this->maxWeightLimit, - "weight_in_storage_item" => $this->weightInStorageItem + "max_slots" => $this->maxSlots ]; } @@ -57,14 +43,62 @@ public function getPropertyMapping(): ?array { return null; } + /** + * List of items that are exclusively allowed in this Storage Item. + * If empty, all items are allowed. + * @param Item|Item[] $items + */ + public function allowItem(Item|array $items): self { + foreach($this->normalizeItems($items) as $name){ + if(!$this->containsItem($this->allowedItems, $name)){ + $this->allowedItems[] = ["name" => $name]; + } + } + return $this; + } + + /** + * List of items that are NOT allowed in this Storage Item. + * @param Item|Item[] $items + */ + public function banItem(Item|array $items): self { + foreach($this->normalizeItems($items) as $name){ + if(!$this->containsItem($this->bannedItems, $name)){ + $this->bannedItems[] = ["name" => $name]; + } + } + return $this; + } + + /** + * @return string[] + */ + private function normalizeItems(Item|array $items): array { + $items = is_array($items) ? $items : [$items]; + $names = []; + foreach($items as $item){ + if(!$item instanceof Item) continue; + $names[] = $item->nbtSerialize()->getString("Name", "unknown"); + } + return array_unique($names); + } + + private function containsItem(array $list, string $name): bool { + foreach($list as $entry){ + if(($entry["name"] ?? null) === $name){ + return true; + } + } + return false; + } + public static function fromJson(mixed $data): static { - return new self( + $self = new self( $data["allow_nested_storage_items"] ?? true, - $data["allowed_items"] ?? [], - $data["banned_items"] ?? [], - $data["max_slots"] ?? 64, - $data["max_weight_limit"] ?? 64, - $data["weight_in_storage_item"] ?? 4 + $data["max_slots"] ?? 64 ); + $self->allowedItems = $data["allowed_items"] ?? []; + $self->bannedItems = $data["banned_items"] ?? []; + return $self; } } \ No newline at end of file diff --git a/src/item/component/SwingSoundsComponent.php b/src/item/component/SwingSoundsComponent.php new file mode 100644 index 00000000..37bd8b40 --- /dev/null +++ b/src/item/component/SwingSoundsComponent.php @@ -0,0 +1,45 @@ +critical = $critical; + $this->hit = $hit; + $this->miss = $miss; + } + + public function getName(): string { + return "minecraft:swing_sounds"; + } + + public function getValue(): array { + return [ + "attack_critical_hit" => $this->critical, + "attack_hit" => $this->hit, + "attack_miss" => $this->miss, + ]; + } + + public function getPropertyMapping(): ?array { + return null; + } + + public static function fromJson(mixed $data): static { + return new self( + $data["attack_critical_hit"] ?? "attack.critical", + $data["attack_hit"] ?? "attack.strong", + $data["attack_miss"] ?? "attack.nodamage" + ); + } +} \ No newline at end of file diff --git a/src/item/component/UseAnimationComponent.php b/src/item/component/UseAnimationComponent.php index 65cf3dc4..7596f729 100644 --- a/src/item/component/UseAnimationComponent.php +++ b/src/item/component/UseAnimationComponent.php @@ -35,7 +35,7 @@ final class UseAnimationComponent implements ItemComponent { * Determines which animation plays when using an item. * @param string $animation Specifies which animation to play when the the item is used. */ - public function __construct(string $animation) { + public function __construct(string $animation = self::ANIMATION_NONE) { $this->animation = $animation; } diff --git a/src/item/component/UseModifiersComponent.php b/src/item/component/UseModifiersComponent.php index ffcda696..796a47a2 100644 --- a/src/item/component/UseModifiersComponent.php +++ b/src/item/component/UseModifiersComponent.php @@ -7,15 +7,26 @@ final class UseModifiersComponent implements ItemComponent { private float $useDuration; private float $movementModifier; + private bool $emitVibrations; + private ?string $startSound; /** - * Determines how long an item takes to use in combination with components such as Shooter, Throwable, or Food. - * @param float $useDuration How long the item takes to use in seconds - * @param float $movementModifier Modifier value to scale the players movement speed when item is in use + * Determines how an item behaves while being used. + * @param float $movementModifier Modifier applied to player movement speed + * @param float $useDuration How long the item takes to use (seconds) + * @param bool $emitVibrations Whether the item emits vibration events + * @param string|null $startSound Sound played when use starts */ - public function __construct(float $movementModifier, float $useDuration = 0) { - $this->useDuration = $useDuration; + public function __construct( + float $movementModifier = 1.0, + float $useDuration = 0.0, + bool $emitVibrations = false, + ?string $startSound = null + ) { $this->movementModifier = $movementModifier; + $this->useDuration = $useDuration; + $this->emitVibrations = $emitVibrations; + $this->startSound = $startSound; } public function getName(): string { @@ -23,10 +34,15 @@ public function getName(): string { } public function getValue(): array { - return [ + $value = [ "movement_modifier" => $this->movementModifier, - "use_duration" => $this->useDuration + "use_duration" => $this->useDuration, + "emit_vibrations" => $this->emitVibrations ]; + if($this->startSound !== null){ + $value["start_sound"] = $this->startSound; + } + return $value; } public function getPropertyMapping(): ?array { @@ -34,6 +50,11 @@ public function getPropertyMapping(): ?array { } public static function fromJson(mixed $data): static { - return new self($data["movement_modifier"] ?? 1.0, $data["use_duration"] ?? 0); + return new self( + (float) ($data["movement_modifier"] ?? 1.0), + (float) ($data["use_duration"] ?? 0.0), + (bool) ($data["emit_vibrations"] ?? false), + $data["start_sound"] ?? null + ); } } \ No newline at end of file diff --git a/src/item/component/WearableComponent.php b/src/item/component/WearableComponent.php index 7006aed2..76ec7810 100644 --- a/src/item/component/WearableComponent.php +++ b/src/item/component/WearableComponent.php @@ -5,20 +5,21 @@ final class WearableComponent implements ItemComponent { - public const SLOT_ARMOR = "slot.armor"; - public const SLOT_ARMOR_CHEST = "slot.armor.chest"; - public const SLOT_ARMOR_FEET = "slot.armor.feet"; public const SLOT_ARMOR_HEAD = "slot.armor.head"; + public const SLOT_ARMOR_CHEST = "slot.armor.chest"; public const SLOT_ARMOR_LEGS = "slot.armor.legs"; - public const SLOT_CHEST = "slot.chest"; - public const SLOT_ENDERCHEST = "slot.enderchest"; - public const SLOT_EQUIPPABLE = "slot.equippable"; + public const SLOT_ARMOR_FEET = "slot.armor.feet"; + public const SLOT_BODY = "slot.armor.body"; + public const SLOT_WEAPON_MAIN_HAND = "slot.weapon.mainhand"; + public const SLOT_WEAPON_OFF_HAND = "slot.weapon.offhand"; + public const SLOT_HOTBAR = "slot.hotbar"; public const SLOT_INVENTORY = "slot.inventory"; - public const SLOT_NONE = "none"; + public const SLOT_ENDERCHEST = "slot.enderchest"; public const SLOT_SADDLE = "slot.saddle"; - public const SLOT_WEAPON_MAIN_HAND = "slot.weapon.mainhand"; - public const SLOT_WEAPON_OFF_HAND = "slot.weapon.offhand"; + public const SLOT_ARMOR = "slot.armor"; + public const SLOT_CHEST = "slot.chest"; + public const SLOT_EQUIPPABLE = "slot.equippable"; private string $slot; private int $protection; @@ -54,7 +55,7 @@ public function getPropertyMapping(): ?array { public static function fromJson(mixed $data): static { return new self( - $data["slot"] ?? self::SLOT_NONE, + $data["slot"] ?? self::SLOT_WEAPON_MAIN_HAND, $data["protection"] ?? 0, $data["hides_player_location"] ?? false ); diff --git a/src/item/properties/DamageCause.php b/src/item/properties/DamageCause.php new file mode 100644 index 00000000..f8782999 --- /dev/null +++ b/src/item/properties/DamageCause.php @@ -0,0 +1,43 @@ + Date: Sun, 14 Dec 2025 23:18:01 +0530 Subject: [PATCH 13/51] Update RotatableTrait.php --- src/block/permutations/RotatableTrait.php | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/block/permutations/RotatableTrait.php b/src/block/permutations/RotatableTrait.php index 9018b3df..05317778 100644 --- a/src/block/permutations/RotatableTrait.php +++ b/src/block/permutations/RotatableTrait.php @@ -66,7 +66,8 @@ public function getPermutations(): array { ->setFloat("SZP", 0.0) ->setFloat("TX", 0.0) ->setFloat("TY", 0.0) - ->setFloat("TZ", 0.0)->setByte("hasJsonVersionBeforeValidation", 0) + ->setFloat("TZ", 0.0) + ->setByte("hasJsonVersionBeforeValidation", 0) ), (new Permutation("q.block_state('customies:rotation') == 4")) ->withComponent("minecraft:transformation", CompoundTag::create() @@ -84,7 +85,8 @@ public function getPermutations(): array { ->setFloat("SZP", 0.0) ->setFloat("TX", 0.0) ->setFloat("TY", 0.0) - ->setFloat("TZ", 0.0)->setByte("hasJsonVersionBeforeValidation", 0) + ->setFloat("TZ", 0.0) + ->setByte("hasJsonVersionBeforeValidation", 0) ), (new Permutation("q.block_state('customies:rotation') == 5")) ->withComponent("minecraft:transformation", CompoundTag::create() @@ -102,7 +104,8 @@ public function getPermutations(): array { ->setFloat("SZP", 0.0) ->setFloat("TX", 0.0) ->setFloat("TY", 0.0) - ->setFloat("TZ", 0.0)->setByte("hasJsonVersionBeforeValidation", 0) + ->setFloat("TZ", 0.0) + ->setByte("hasJsonVersionBeforeValidation", 0) ), ]; } From 65bd1c6b3b45766e27de204877681e62ace48293 Mon Sep 17 00:00:00 2001 From: HydroGames-dev Date: Sun, 14 Dec 2025 23:19:30 +0530 Subject: [PATCH 14/51] Update StorageItemComponent.php --- src/item/component/StorageItemComponent.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/item/component/StorageItemComponent.php b/src/item/component/StorageItemComponent.php index 4c6c8779..2fb5124e 100644 --- a/src/item/component/StorageItemComponent.php +++ b/src/item/component/StorageItemComponent.php @@ -8,8 +8,8 @@ final class StorageItemComponent implements ItemComponent { private bool $allowNestedStorageItems; - private array $allowedItems = []; - private array $bannedItems = []; + private array $allowedItems; + private array $bannedItems; private int $maxSlots; /** From 0b0d374a992486969062b92a1e3fee96231c0766 Mon Sep 17 00:00:00 2001 From: HydroGames-dev Date: Sun, 14 Dec 2025 23:33:24 +0530 Subject: [PATCH 15/51] remove these --- src/block/component/LootComponent.php | 29 --------------- .../RedstoneConductivityComponent.php | 37 ------------------- 2 files changed, 66 deletions(-) delete mode 100644 src/block/component/LootComponent.php delete mode 100644 src/block/component/RedstoneConductivityComponent.php diff --git a/src/block/component/LootComponent.php b/src/block/component/LootComponent.php deleted file mode 100644 index 89da9689..00000000 --- a/src/block/component/LootComponent.php +++ /dev/null @@ -1,29 +0,0 @@ -loot = $loot; - } - - public function getName(): string { - return 'minecraft:loot'; - } - - public function getValue(): array { - return [ - "value" => $this->loot - ]; - } - - public static function fromJson(mixed $data): static { - return new self($data); - } -} \ No newline at end of file diff --git a/src/block/component/RedstoneConductivityComponent.php b/src/block/component/RedstoneConductivityComponent.php deleted file mode 100644 index 2a5f23d5..00000000 --- a/src/block/component/RedstoneConductivityComponent.php +++ /dev/null @@ -1,37 +0,0 @@ -allowsWireToStepDown = $allowsWireToStepDown; - $this->redstoneConductor = $redstoneConductor; - } - - public function getName(): string { - return 'minecraft:redstone_conductivity'; - } - - public function getValue(): array { - return [ - "allowsWireToStepDown" => $this->allowsWireToStepDown, - "redstoneConductor" => $this->redstoneConductor - ]; - } - - public static function fromJson(mixed $data): static { - return new self( - $data["allowsWireToStepDown"] ?? true, - $data["allowsWireToStepDown"] ?? false - ); - } -} \ No newline at end of file From c1b8dcebe2d386fa3ab8c710566a51a0069264e3 Mon Sep 17 00:00:00 2001 From: HydroGames-dev Date: Mon, 15 Dec 2025 14:25:00 +0530 Subject: [PATCH 16/51] PHPDocs (AI), Implemented some data driven stuff --- README.md | 6 +- src/Customies.php | 27 +++-- src/CustomiesListener.php | 9 +- src/block/BlockPalette.php | 2 +- src/block/CustomiesBlockFactory.php | 39 ++++--- src/block/component/CollisionBoxComponent.php | 10 +- .../component/LiquidDetectionComponent.php | 42 ++++++-- src/block/component/MovableComponent.php | 39 +++++-- .../component/PlacementFilterComponent.php | 45 +++++--- src/block/permutations/Permutable.php | 2 +- src/block/properties/AllowedFace.php | 18 ++++ src/block/properties/BlockDescriptor.php | 67 ++++++++++++ src/block/properties/Box.php | 64 +++++++++-- src/block/properties/Material.php | 102 ++++++++++++++---- src/block/properties/PlacementCondition.php | 56 ++++++++++ src/block/traits/BlockComponentsTrait.php | 26 ++++- src/entity/CustomiesEntityFactory.php | 7 +- src/item/CreativeInventoryInfo.php | 45 ++++++-- src/item/CustomiesItemFactory.php | 55 +++++----- .../component/BundleInteractionComponent.php | 1 - .../CanDestroyInCreativeComponent.php | 2 +- src/item/component/CooldownComponent.php | 17 ++- .../component/DamageAbsorptionComponent.php | 37 ++++++- src/item/component/DiggerComponent.php | 34 ++++-- .../component/DurabilitySensorComponent.php | 40 +++++-- src/item/component/DyeableComponent.php | 19 +++- src/item/component/EnchantableComponent.php | 31 ++++-- .../component/InteractButtonComponent.php | 2 +- src/item/component/ItemComponent.php | 3 + src/item/component/RepairableComponent.php | 18 ++-- src/item/component/TagsComponent.php | 4 +- src/item/properties/DamageCause.php | 5 +- src/item/properties/ParticleType.php | 5 +- src/item/properties/RepairItems.php | 49 ++++++++- src/item/properties/SoundEvent.php | 5 +- src/item/traits/ItemComponentsTrait.php | 29 ++++- src/json/BehaviorManager.php | 73 ++++++++----- src/json/BlockComponentRegistry.php | 43 ++++---- src/json/CustomiesBlock.php | 45 ++++++-- src/json/CustomiesItem.php | 13 +++ src/json/ItemComponentRegistry.php | 49 ++++++--- src/task/AsyncRegisterBlocksTask.php | 27 +++-- src/util/NBT.php | 19 +++- 43 files changed, 949 insertions(+), 282 deletions(-) create mode 100644 src/block/properties/AllowedFace.php create mode 100644 src/block/properties/BlockDescriptor.php create mode 100644 src/block/properties/PlacementCondition.php diff --git a/README.md b/README.md index 621c5969..b850ded8 100644 --- a/README.md +++ b/README.md @@ -8,13 +8,15 @@ A PocketMine-MP plugin that implements support for custom blocks, items and enti Discord -Official Discord community chat for socializing, receiving help with the plugin, and sharing creations. Join in on the -fun! +Official Discord community chat for socializing, receiving help with the plugin, and sharing creations. Join in on the fun! ## Usage The usage guides have been moved to the [Customies Wiki](https://github.com/CustomiesDevs/Customies/wiki)! +[![Mojang Item Docs](https://img.shields.io/badge/📖-Microsoft_Docs-blue)](https://learn.microsoft.com/en-us/minecraft/creator/reference/content/itemreference/examples/itemcomponentlist?view=minecraft-bedrock-stable) +[![Mojang Block Docs](https://img.shields.io/badge/📖-Microsoft_Docs-blue)](https://learn.microsoft.com/en-us/minecraft/creator/reference/content/blockreference/examples/blockcomponents/blockcomponentslist?view=minecraft-bedrock-stable) + ## Important Contributors | Name | Contribution | diff --git a/src/Customies.php b/src/Customies.php index 880f3e32..494241c4 100644 --- a/src/Customies.php +++ b/src/Customies.php @@ -10,22 +10,31 @@ use pocketmine\utils\SingletonTrait; final class Customies extends PluginBase { - use SingletonTrait; + use SingletonTrait { + setInstance as private; + reset as private; + } public function onLoad(): void{ self::setInstance($this); } + /** + * Called when the plugin is enabled. + * + * Registers event listeners, loads and registers all behavior definitions, + * and schedules initialization hooks for custom blocks after the server + * has fully started. + */ protected function onEnable(): void { $this->getServer()->getPluginManager()->registerEvents(new CustomiesListener(), $this); - + // Register all custom behavior JSON definitions BehaviorManager::getInstance()->registerAll(); - - $cachePath = $this->getDataFolder() . "idcache"; - $this->getScheduler()->scheduleDelayedTask(new ClosureTask(static function () use ($cachePath): void { - // This task is scheduled with a 0-tick delay so it runs as soon as the server has started. Plugins should - // register their custom blocks and entities in onEnable() before this is executed. - CustomiesBlockFactory::getInstance()->addWorkerInitHook($cachePath); + $this->getScheduler()->scheduleDelayedTask(new ClosureTask(static function (): void { + // This task is scheduled with a 0-tick delay so it runs as soon as the server has started. + // Plugins should register their custom blocks and entities in onEnable() + // before this is executed. + CustomiesBlockFactory::getInstance()->addWorkerInitHook(); }), 0); } -} +} \ No newline at end of file diff --git a/src/CustomiesListener.php b/src/CustomiesListener.php index 4ade1464..4c801fac 100644 --- a/src/CustomiesListener.php +++ b/src/CustomiesListener.php @@ -13,14 +13,15 @@ use function count; final class CustomiesListener implements Listener { + /** @var BlockPaletteEntry[] */ private array $cachedBlockPalette = []; private Experiments $experiments; public function __construct() { $this->experiments = new Experiments([ - // "data_driven_items" is required for custom blocks to render in-game. With this disabled, they will be - // shown as the UPDATE texture block. + // "data_driven_items" is required for custom blocks to render in-game. + // With this disabled, custom blocks will appear as the UPDATE texture block. "data_driven_items" => true, ], true); } @@ -35,9 +36,9 @@ public function onDataPacketSend(DataPacketSendEvent $event): void { } $packet->levelSettings->experiments = $this->experiments; $packet->blockPalette = $this->cachedBlockPalette; - } elseif($packet instanceof ResourcePackStackPacket) { + }elseif($packet instanceof ResourcePackStackPacket) { $packet->experiments = $this->experiments; } } } -} +} \ No newline at end of file diff --git a/src/block/BlockPalette.php b/src/block/BlockPalette.php index 0e037484..18083f29 100644 --- a/src/block/BlockPalette.php +++ b/src/block/BlockPalette.php @@ -112,4 +112,4 @@ private function sortWith(BlockStateDictionaryEntry $newState): void { throw new AssumptionFailedError(BlockTypeNames::INFO_UPDATE . " should always exist") ); } -} +} \ No newline at end of file diff --git a/src/block/CustomiesBlockFactory.php b/src/block/CustomiesBlockFactory.php index 34e033e6..784800aa 100644 --- a/src/block/CustomiesBlockFactory.php +++ b/src/block/CustomiesBlockFactory.php @@ -38,7 +38,10 @@ use function usort; final class CustomiesBlockFactory { - use SingletonTrait; + use SingletonTrait { + setInstance as private; + reset as private; + } /** * @var Closure[] @@ -47,28 +50,28 @@ final class CustomiesBlockFactory { private array $blockFuncs = []; /** @var BlockPaletteEntry[] */ private array $blockPaletteEntries = []; - /** @var array */ + /** @var array Map of block identifiers to block instances */ private array $customBlocks = []; + /** @var array Map of group names to creative groups */ private array $groups = []; /** * Adds a worker initialize hook to the async pool to sync the BlockFactory for every thread worker that is created. * It is especially important for the workers that deal with chunk encoding, as using the wrong runtime ID mappings * can result in massive issues with almost every block showing as the wrong thing and causing lag to clients. - * @param string $cachePath The path where the block state cache file is located (usually server's root folder). */ - public function addWorkerInitHook(string $cachePath): void { + public function addWorkerInitHook(): void { $server = Server::getInstance(); $blocks = $this->blockFuncs; - $server->getAsyncPool()->addWorkerStartHook(static function (int $worker) use ($cachePath, $server, $blocks): void { - $server->getAsyncPool()->submitTaskToWorker(new AsyncRegisterBlocksTask($cachePath, $blocks), $worker); + $server->getAsyncPool()->addWorkerStartHook(static function (int $worker) use ($server, $blocks): void { + $server->getAsyncPool()->submitTaskToWorker(new AsyncRegisterBlocksTask($blocks), $worker); }); } /** * Get a custom block from its identifier. An exception will be thrown if the block is not registered. - * @param string $identifier - * @throws InvalidArgumentException + * @param string $identifier Unique block identifier (e.g. "namespace:block_name") + * @throws InvalidArgumentException If the block is not registered * @return Block A clone of the registered block. */ public function get(string $identifier): Block { @@ -112,7 +115,13 @@ public function getBlockPaletteEntries(): array { * @param Closure|null $deserializer Optional closure that takes a BlockStateReader and returns a new instance of the block after reading the state. * @throws InvalidArgumentException If the blockFunc does not return a Block instance. */ - public function registerBlock(Closure $blockFunc, string $identifier, ?CreativeInventoryInfo $creativeInfo = null, ?Closure $serializer = null, ?Closure $deserializer = null): void { + public function registerBlock( + Closure $blockFunc, + string $identifier, + ?CreativeInventoryInfo $creativeInfo = null, + ?Closure $serializer = null, + ?Closure $deserializer = null + ): void { $block = $blockFunc(); if(!$block instanceof Block) { throw new InvalidArgumentException("Class returned from closure is not a Block"); @@ -136,7 +145,6 @@ public function registerBlock(Closure $blockFunc, string $identifier, ?CreativeI $blockProperties[] = $blockProperty->toNBT(); } $permutations = array_map(static fn(Permutation $permutation) => $permutation->toNBT(), $block->getPermutations()); - // The 'minecraft:on_player_placing' component is required for the client to predict block placement, making // it a smoother experience for the end-user. $components->setTag("minecraft:on_player_placing", CompoundTag::create()); @@ -157,12 +165,12 @@ public function registerBlock(Closure $blockFunc, string $identifier, ?CreativeI BlockPalette::getInstance()->insertState($blockState, $meta); } - $serializer ??= static function (Permutable $block) use ($identifier, $blockPropertyNames) : BlockStateWriter { + $serializer ??= static function (Permutable $block) use ($identifier) : BlockStateWriter { $b = BlockStateWriter::create($identifier); $block->serializeState($b); return $b; }; - $deserializer ??= static function (BlockStateReader $in) use ($block, $identifier, $blockPropertyNames) : Permutable { + $deserializer ??= static function (BlockStateReader $in) use ($identifier) : Permutable { $b = CustomiesBlockFactory::getInstance()->get($identifier); assert($b instanceof Permutable); $b->deserializeState($in); @@ -201,7 +209,7 @@ public function registerBlock(Closure $blockFunc, string $identifier, ?CreativeI CreativeInventoryInfo::CATEGORY_ITEMS => CreativeCategory::ITEMS, CreativeInventoryInfo::CATEGORY_NATURE => CreativeCategory::NATURE, CreativeInventoryInfo::CATEGORY_EQUIPMENT => CreativeCategory::EQUIPMENT, - default => throw new AssumptionFailedError("Unknown category") + default => throw new AssumptionFailedError("Unknown Creative Category") }; CreativeInventory::getInstance()->add($block->asItem(), $category, $group); @@ -218,8 +226,7 @@ public function registerBlock(Closure $blockFunc, string $identifier, ?CreativeI foreach($this->blockPaletteEntries as $i => $entry) { /** @var CompoundTag $root */ $root = $entry->getStates()->getRoot(); - $root->setTag("vanilla_block_data", CompoundTag::create() - ->setInt("block_id", 10000 + $i)); + $root->setTag("vanilla_block_data", CompoundTag::create()->setInt("block_id", 10000 + $i)); $this->blockPaletteEntries[$i] = new BlockPaletteEntry($entry->getName(), new CacheableNbt($root)); } } @@ -257,4 +264,4 @@ private function createBlockNBT(Block $block, ?CreativeInventoryInfo $creativeIn } return CompoundTag::create(); } -} +} \ No newline at end of file diff --git a/src/block/component/CollisionBoxComponent.php b/src/block/component/CollisionBoxComponent.php index 76e65b22..15bd10be 100644 --- a/src/block/component/CollisionBoxComponent.php +++ b/src/block/component/CollisionBoxComponent.php @@ -21,8 +21,9 @@ public function __construct(bool $enabled = true) { } /** - * Add a collision box. - * @param Box $box The box to add + * Adds a single collision box. + * @param Box $box + * The collision box to add. * @return $this */ public function addBox(Box $box): self { @@ -31,8 +32,9 @@ public function addBox(Box $box): self { } /** - * Add collision boxes. - * @param Box[] $boxes The boxes to add + * Adds multiple collision boxes. + * @param Box[] $boxes + * An array of collision boxes to add. * @return $this */ public function addBoxes(array $boxes): self { diff --git a/src/block/component/LiquidDetectionComponent.php b/src/block/component/LiquidDetectionComponent.php index 3d302b0a..073a0398 100644 --- a/src/block/component/LiquidDetectionComponent.php +++ b/src/block/component/LiquidDetectionComponent.php @@ -4,27 +4,51 @@ class LiquidDetectionComponent implements BlockComponent { + /** The block stops liquid flow (default behavior). */ public const BLOCKING = "blocking"; + /** The block is destroyed completely when touched by liquid. */ public const BROKEN = "broken"; + /** The block is destroyed and drops its item form. */ public const POPPED = "popped"; + /** The block does not react; liquid visually flows through it. */ public const NO_REACTION = "no_reaction"; + /** @var string The liquid type this rule applies to (currently only "water"). */ private string $liquidType; + /** @var bool Whether the block can contain the liquid (e.g. waterlogged). */ private bool $canContainLiquid; + /** @var string Reaction when liquid touches the block. */ private string $onLiquidTouches; + /** + * @var string[] + * Directions from which liquid flow is blocked. + * Valid values: "up", "down", "north", "south", "east", "west" + */ private array $stopsLiquidFlowingFromDirection; /** - * @param string $liquidType The type of liquid this detection rule is for. Currently, water is the only supported liquid type. If this field is omitted, water will be the liquid type by default. - * @param bool $canContainLiquid Whether this block can contain the liquid. For example, if the liquid type is water, this means the block can be waterlogged. - * @param string $onLiquidTouches How the block reacts to flowing water. Must be one of the following options: - - "blocking" - The default value for this field. The block stops the liquid from flowing. - - "broken" - The block is destroyed completely. - - "popped" - The block is destroyed and its item is spawned. - - "no_reaction" - The block is unaffected; visually, the liquid will flow through the block. - * @param array $stopsLiquidFlowingFromDirection When a block contains a liquid, controls the directions in which the liquid can't flow out from the block. Also controls the directions in which a block can stop liquid flowing into it if no_reaction is set for the on_liquid_touches field. Can be a list of the following directions: "up", "down", "north", "south", "east", "west". The default is an empty list; this means that liquid can flow out of all directions by default. + * Creates a new liquid detection rule. + * @param string $liquidType + * The liquid type this rule applies to. Defaults to `"water"`. + * @param bool $canContainLiquid + * Whether the block can contain the liquid (e.g. waterlogging). + * @param string $onLiquidTouches + * How the block reacts when liquid touches it. + * Must be one of: + * - {@see self::BLOCKING} + * - {@see self::BROKEN} + * - {@see self::POPPED} + * - {@see self::NO_REACTION} + * @param string[] $stopsLiquidFlowingFromDirection + * Directions in which liquid is prevented from flowing. + * If empty, liquid can flow freely in all directions. */ - public function __construct(string $liquidType = "water", bool $canContainLiquid = false, string $onLiquidTouches = self::BLOCKING, array $stopsLiquidFlowingFromDirection = []) { + public function __construct( + string $liquidType = "water", + bool $canContainLiquid = false, + string $onLiquidTouches = self::BLOCKING, + array $stopsLiquidFlowingFromDirection = [] + ) { $this->liquidType = $liquidType; $this->canContainLiquid = $canContainLiquid; $this->onLiquidTouches = $onLiquidTouches; diff --git a/src/block/component/MovableComponent.php b/src/block/component/MovableComponent.php index 2d59593a..567e730a 100644 --- a/src/block/component/MovableComponent.php +++ b/src/block/component/MovableComponent.php @@ -4,29 +4,46 @@ class MovableComponent implements BlockComponent { + /** Block can be pushed and pulled by pistons (default). */ public const MOVEMENT_TYPE_PUSH_PULL = "push_pull"; + /** Block can be pushed but not pulled by sticky pistons. */ public const MOVEMENT_TYPE_PUSH = "push"; + /** Block is destroyed when moved by a piston. */ public const MOVEMENT_TYPE_POPPED = "popped"; + /** Block cannot be moved by pistons at all. */ public const MOVEMENT_TYPE_IMMOVABLE = "immovable"; - + /** + * Adjacent blocks with compatible rules will be moved together. + * Only works with {@see MOVEMENT_TYPE_PUSH_PULL}. + */ public const STICKY_SAME = "same"; + /** Default behavior; does not move adjacent blocks. */ public const STICKY_NONE = "none"; + /** @var string How the block reacts to piston movement. */ private string $movementType; + /** @var string How the block interacts with adjacent blocks when moved. */ private string $sticky; /** - * Determines how a block can be moved by pistons. - * @param string $movementType [Required] How the block reacts to being pushed by another block like a piston. Must be one of the following options: - - "push_pull" - The default value for this field. The block will be pushed and pulled by a piston. - - "push" - The block will only be pulled by a piston and will ignore a sticky piston. - - "popped" - The block is destroyed when moved by a piston. - - "immovable" - The block is unaffected by a piston. - * @param string $sticky [Optional] How the block should handle adjacent blocks around it when being pushed by another block like a piston. Must be one of the following options: - - "same" - Adjacent blocks to this block will be moved when moved. This excludes other blocks with the "same" property. This will only work with the movement_type: "push_pull". - - "none" - The default and will not move adjacent blocks. + * Creates a new movable component definition. + * @param string $movementType + * Determines how the block reacts to piston movement. + * Must be one of: + * - {@see MOVEMENT_TYPE_PUSH_PULL} + * - {@see MOVEMENT_TYPE_PUSH} + * - {@see MOVEMENT_TYPE_POPPED} + * - {@see MOVEMENT_TYPE_IMMOVABLE} + * @param string $sticky + * Determines whether adjacent blocks are moved together. + * Must be one of: + * - {@see STICKY_NONE} + * - {@see STICKY_SAME} */ - public function __construct(string $movementType = self::MOVEMENT_TYPE_PUSH_PULL, string $sticky = self::STICKY_NONE) { + public function __construct( + string $movementType = self::MOVEMENT_TYPE_PUSH_PULL, + string $sticky = self::STICKY_NONE + ) { $this->movementType = $movementType; $this->sticky = $sticky; } diff --git a/src/block/component/PlacementFilterComponent.php b/src/block/component/PlacementFilterComponent.php index bb17b061..6eb39f87 100644 --- a/src/block/component/PlacementFilterComponent.php +++ b/src/block/component/PlacementFilterComponent.php @@ -2,17 +2,30 @@ namespace customiesdevs\customies\block\component; +use customiesdevs\customies\block\properties\PlacementCondition; +use InvalidArgumentException; + class PlacementFilterComponent implements BlockComponent { - private array $allowedFaces; - private array $blockFilter; + /** @var PlacementCondition[] */ + private array $conditions = []; /** - * TODO Needs more data on this + * @param PlacementCondition[] $conditions (max 64) */ - public function __construct(array $allowedFaces = [], array $blockFilter = []) { - $this->allowedFaces = $allowedFaces; - $this->blockFilter = $blockFilter; + public function __construct(array $conditions = []) { + if(count($conditions) > 64){ + throw new InvalidArgumentException("Placement filter may not exceed 64 conditions"); + } + $this->conditions = $conditions; + } + + public function addCondition(PlacementCondition $condition): self { + if(count($this->conditions) >= 64){ + throw new InvalidArgumentException("Placement filter may not exceed 64 conditions"); + } + $this->conditions[] = $condition; + return $this; } public function getName(): string { @@ -21,20 +34,18 @@ public function getName(): string { public function getValue(): array { return [ - "conditions" => [ - [ - "allowed_faces" => $this->allowedFaces, - "block_filter" => $this->blockFilter - ] - ] + "conditions" => array_map( + static fn(PlacementCondition $c) => $c->toArray(), + $this->conditions + ) ]; } - // TODO Needs more data on this public static function fromJson(mixed $data): static { - return new self( - $data["conditions"][0]["allowed_faces"] ?? [], - $data["conditions"][0]["block_filter"] ?? [] - ); + $conditions = []; + foreach($data["conditions"] ?? [] as $condition){ + $conditions[] = PlacementCondition::fromArray($condition); + } + return new self($conditions); } } \ No newline at end of file diff --git a/src/block/permutations/Permutable.php b/src/block/permutations/Permutable.php index 60637fc1..de921535 100644 --- a/src/block/permutations/Permutable.php +++ b/src/block/permutations/Permutable.php @@ -38,4 +38,4 @@ public function serializeState(BlockStateWriter $blockStateOut): void; * Deserializes the block state from the given BlockStateReader. */ public function deserializeState(BlockStateReader $blockStateIn): void; -} +} \ No newline at end of file diff --git a/src/block/properties/AllowedFace.php b/src/block/properties/AllowedFace.php new file mode 100644 index 00000000..fa872a62 --- /dev/null +++ b/src/block/properties/AllowedFace.php @@ -0,0 +1,18 @@ + $states + * @param string|null $tags Molang tag query + * @param int|null $tagsVersion Molang version + */ + public function __construct( + ?string $name = null, + array $states = [], + ?string $tags = null, + ?int $tagsVersion = null + ) { + $this->name = $name; + $this->states = $states; + $this->tags = $tags; + $this->tagsVersion = $tagsVersion; + } + + /** + * Converts descriptor to Bedrock format. + */ + public function toArray(): array { + $data = []; + if($this->name !== null){ + $data["name"] = $this->name; + } + if(!empty($this->states)){ + $data["states"] = $this->states; + } + if($this->tags !== null){ + $data["tags"] = $this->tags; + if($this->tagsVersion !== null) { + $data["tags_version"] = $this->tagsVersion; + } + } + return $data; + } + + public static function fromArray(array $data): self { + return new self( + $data["name"] ?? null, + $data["states"] ?? [], + $data["tags"] ?? null, + $data["tags_version"] ?? null + ); + } +} \ No newline at end of file diff --git a/src/block/properties/Box.php b/src/block/properties/Box.php index ae8bc77d..50b6f66f 100644 --- a/src/block/properties/Box.php +++ b/src/block/properties/Box.php @@ -7,16 +7,32 @@ use pocketmine\math\Vector3; /** - * Represents a box with origin and size for teh CollisionBox component. - * Origin must be in range (-8, 0, -8) to (7, 23, 7). - * Size must be in range (1, 1, 1) to (16, 24, 16). - * Origin + size must be in range (-8, 0, -8) to (8, 24, 8). + * Represents a collision box definition used by the `minecraft:collision_box` + * (and similar) block components. + * + * Coordinates are defined in **block-relative space**, where the block center + * is `(0, 0, 0)` and the full block spans from `(-8, 0, -8)` to `(8, 24, 8)`. + * + * ### Constraints + * - **Origin** must be in range `(-8, 0, -8)` to `(7, 23, 7)` + * - **Size** must be in range `(1, 1, 1)` to `(16, 24, 16)` + * - **Origin + Size** must not exceed `(8, 24, 8)` + * + * All values are automatically clamped to valid ranges. */ class Box { + /** @var Vector3 Origin (minimum corner) of the box in block-relative coordinates. */ private Vector3 $origin; + /** @var Vector3 Size (width, height, depth) of the box. */ private Vector3 $size; + /** + * Creates a new collision box definition. + * Values are clamped to Minecraft's valid block collision bounds. + * @param Vector3 $origin Minimum corner of the box. + * @param Vector3 $size Dimensions of the box. + */ public function __construct(Vector3 $origin, Vector3 $size) { // Clamp origin $originX = max(-8, min(7, $origin->x)); @@ -37,19 +53,49 @@ public function __construct(Vector3 $origin, Vector3 $size) { $this->size = new Vector3($sizeX, $sizeY, $sizeZ); } - public static function fromAxisAlignedBB(AxisAlignedBB $bb): self { + /** + * Creates a box from an {@see AxisAlignedBB}. + * @param AxisAlignedBB $bb The bounding box to convert. + * @return self + */ + public static function fromAABB(AxisAlignedBB $bb): self { return new self( new Vector3($bb->minX, $bb->minY, $bb->minZ), new Vector3($bb->maxX - $bb->minX, $bb->maxY - $bb->minY, $bb->maxZ - $bb->minZ) ); } + /** + * Returns the origin (minimum corner) of the box. + * @return Vector3 + */ public function getOrigin(): Vector3 { return $this->origin; } + + /** + * Returns the size of the box. + * @return Vector3 + */ public function getSize(): Vector3 { return $this->size; } + + /** + * Returns the maximum corner of the box (origin + size). + * @return Vector3 + */ public function getMax(): Vector3 { return $this->origin->addVector($this->size); } /** - * Convert to NBT output format. + * Converts the box into the Bedrock NBT array format. + * + * Coordinates are converted from block-relative space into + * client-expected values (X and Z shifted by +8). + * @return array{ + * minX: float, + * minY: float, + * minZ: float, + * maxX: float, + * maxY: float, + * maxZ: float + * } */ public function toNbtArray(): array { $max = $this->getMax(); @@ -63,6 +109,10 @@ public function toNbtArray(): array { ]; } + /** + * Converts this box into an {@see AxisAlignedBB}. + * @return AxisAlignedBB + */ public function toAxisAlignedBB(): AxisAlignedBB { $max = $this->getMax(); return new AxisAlignedBB( @@ -70,4 +120,4 @@ public function toAxisAlignedBB(): AxisAlignedBB { $max->x, $max->y, $max->z ); } -} +} \ No newline at end of file diff --git a/src/block/properties/Material.php b/src/block/properties/Material.php index 2ec56db0..8f8eb23c 100644 --- a/src/block/properties/Material.php +++ b/src/block/properties/Material.php @@ -14,14 +14,43 @@ final class Material { public const TARGET_SOUTH = "south"; public const TARGET_WEST = "west"; + /* packed_bools bit flags (byte) */ + /** Enables directional face shading */ + public const FLAG_FACE_DIMMING = 0x01; + /** Enables randomized UV rotation per face */ + public const FLAG_RANDOM_UV_ROTATION = 0x02; + /** Enables texture variation support */ + public const FLAG_TEXTURE_VARIATION = 0x04; + /** - * @param string $target The targeted face for the material. Possible values are: "*", "sides", "up", "down", "north", "east", "south", "west". - * @param string $texture Texture name for the material. - * @param RenderMethod $renderMethod The render method to use. - * @param TintMethod $tintMethod Tint multiplied to the color. Tint method logic varies, but often refers to the "rain" and "temperature" of the biome the block is placed in to compute the tint. - * @param float $ambientOcclusion If this material has ambient occlusion applied when lighting, shadows will be created around and underneath the block. Decimal value controls exponent applied to a value after lighting. - * @param boolean $faceDimming This material should be dimmed by the direction it's facing. - * @param boolean $isotropic Should the faces that this material is applied to randomize their UVs? + * @param string $target + * Targeted face for the material. + * Valid values: + * - "*" + * - "sides" + * - "up" + * - "down" + * - "north" + * - "east" + * - "south" + * - "west" + * @param string $texture + * Texture identifier used by this material instance. + * @param RenderMethod $renderMethod + * Render method controlling how the block is drawn + * (opaque, blend, alpha_test, etc). + * @param TintMethod $tintMethod + * Tint logic applied to the texture (biome-based, none, etc). + * @param float $ambientOcclusion + * Ambient occlusion strength. Typically `1.0`. + * @param int $packedBools + * Bitmask controlling material behavior. + * Supported flags: + * - {@see self::FLAG_FACE_DIMMING} + * - {@see self::FLAG_RANDOM_UV_ROTATION} + * - {@see self::FLAG_TEXTURE_VARIATION} + * @param bool $alphaMaskedTint + * Whether the tint should only apply to alpha-masked pixels. */ public function __construct( private readonly string $target, @@ -29,8 +58,8 @@ public function __construct( private readonly RenderMethod $renderMethod = RenderMethod::OPAQUE, private readonly TintMethod $tintMethod = TintMethod::NONE, private readonly float $ambientOcclusion = 1.0, - private readonly bool $faceDimming = false, - private readonly bool $isotropic = false + private readonly int $packedBools = self::FLAG_FACE_DIMMING, + private readonly bool $alphaMaskedTint = false, ) {} /** @@ -41,26 +70,63 @@ public function getTarget(): string { return $this->target; } + /** + * Creates a Material instance from a decoded material definition. + * @param string $target + * @param array{ + * texture: string, + * render_method?: string, + * tint_method?: string, + * ambient_occlusion?: float|int, + * packed_bools?: int, + * face_dimming?: bool|int, + * isotropic?: bool|int, + * alpha_masked_tint?: bool|int + * } $data + */ public static function fromArray(string $target, array $data): self { + $packedBools = 0; + if(isset($data["packed_bools"])){ + $packedBools = (int) $data["packed_bools"]; + }else{ + if(!empty($data["face_dimming"])){ + $packedBools |= self::FLAG_FACE_DIMMING; + } + if(!empty($data["isotropic"])){ + $packedBools |= self::FLAG_RANDOM_UV_ROTATION; + } + } return new self( $target, $data["texture"], RenderMethod::tryFrom($data["render_method"] ?? "") ?? RenderMethod::OPAQUE, TintMethod::tryFrom($data["tint_method"] ?? "") ?? TintMethod::NONE, - (float)($data["ambient_occlusion"] ?? 1.0), - (bool)($data["packed_bools"] ?? 0x01), - (bool)($data["isotropic"] ?? false) + (float) ($data["ambient_occlusion"] ?? 1.0), + $packedBools, + (bool) ($data["alpha_masked_tint"] ?? false) ); - } - + } + + /** + * Converts the material into Bedrock-compatible NBT/JSON format. + * + * @return array{ + * texture: string, + * render_method: string, + * tint_method: string, + * ambient_occlusion: float, + * packed_bools: int, + * alpha_masked_tint: bool + * } + */ public function toArray(): array { return [ "texture" => $this->texture, "render_method" => $this->renderMethod->value, "tint_method" => $this->tintMethod->value, - "ambient_occlusion" => $this->ambientOcclusion, - "packed_bools" => $this->faceDimming, - "isotropic" => $this->isotropic + "ambient_occlusion" => $this->ambientOcclusion, + "packed_bools" => $this->packedBools, + "alpha_masked_tint" => $this->alphaMaskedTint ]; } -} +} \ No newline at end of file diff --git a/src/block/properties/PlacementCondition.php b/src/block/properties/PlacementCondition.php new file mode 100644 index 00000000..9f90627c --- /dev/null +++ b/src/block/properties/PlacementCondition.php @@ -0,0 +1,56 @@ + 6){ + throw new InvalidArgumentException("Placement condition may not exceed 6 allowed faces"); + } + if(count($blockFilters) > 64){ + throw new InvalidArgumentException("Placement condition may not exceed 64 block filters"); + } + $this->allowedFaces = $allowedFaces; + $this->blockFilters = $blockFilters; + } + + public function toArray(): array { + return [ + "allowed_faces" => array_map( + static fn(AllowedFace $f) => $f->value, + $this->allowedFaces + ), + "block_filter" => array_map( + static fn(BlockDescriptor $b) => $b->toArray(), + $this->blockFilters + ) + ]; + } + + public static function fromArray(array $data): self { + return new self( + array_map( + static fn(string $f) => AllowedFace::from($f), + $data["allowed_faces"] ?? [] + ), + array_map( + static fn(array $b) => BlockDescriptor::fromArray($b), + $data["block_filter"] ?? [] + ) + ); + } +} \ No newline at end of file diff --git a/src/block/traits/BlockComponentsTrait.php b/src/block/traits/BlockComponentsTrait.php index 6216842f..9797486d 100644 --- a/src/block/traits/BlockComponentsTrait.php +++ b/src/block/traits/BlockComponentsTrait.php @@ -6,23 +6,45 @@ trait BlockComponentsTrait { - /** @var BlockComponent[] */ + /** + * Registered block components indexed by component name. + * @var array + */ private array $components; + /** + * Adds or replaces a block component. + * If a component with the same name already exists, it will be overwritten. + * @param BlockComponent $component The component to add. + */ public function addComponent(BlockComponent $component): void { $this->components[$component->getName()] = $component; } + /** + * Checks whether the block has a component with the given name. + * + * @param string $name Component identifier (e.g. "minecraft:flammable") + * @return bool True if the component exists, false otherwise. + */ public function hasComponent(string $name): bool { return isset($this->components[$name]); } + /** + * Retrieves a component by its name. + * + * @param string $name Component identifier. + * @return BlockComponent|null The component if present, otherwise null. + */ public function getComponent(string $name): ?BlockComponent { return $this->components[$name] ?? null; } /** - * @return BlockComponent[] + * Returns all registered block components. + * + * @return array */ public function getComponents(): array { return $this->components; diff --git a/src/entity/CustomiesEntityFactory.php b/src/entity/CustomiesEntityFactory.php index e6da13ac..09bfbb8c 100644 --- a/src/entity/CustomiesEntityFactory.php +++ b/src/entity/CustomiesEntityFactory.php @@ -17,7 +17,10 @@ use ReflectionClass; class CustomiesEntityFactory { - use SingletonTrait; + use SingletonTrait { + setInstance as private; + reset as private; + } /** * Register an entity to the EntityFactory and all the required mappings. An optional behaviour identifier can be @@ -44,4 +47,4 @@ private function updateStaticPacketCache(string $identifier, string $behaviourId ->setString("bid", $behaviourId)); $packet->identifiers = new CacheableNbt($root); } -} +} \ No newline at end of file diff --git a/src/item/CreativeInventoryInfo.php b/src/item/CreativeInventoryInfo.php index 9b1eb5da..a667a18d 100644 --- a/src/item/CreativeInventoryInfo.php +++ b/src/item/CreativeInventoryInfo.php @@ -4,6 +4,8 @@ final class CreativeInventoryInfo { + const NONE = "none"; + const CATEGORY_ALL = "all"; const CATEGORY_COMMANDS = "commands"; const CATEGORY_CONSTRUCTION = "construction"; @@ -11,18 +13,22 @@ final class CreativeInventoryInfo { const CATEGORY_ITEMS = "items"; const CATEGORY_NATURE = "nature"; - const NONE = "none"; const GROUP_ANVIL = "itemGroup.name.anvil"; const GROUP_ARROW = "itemGroup.name.arrow"; const GROUP_AXE = "itemGroup.name.axe"; const GROUP_BANNER = "itemGroup.name.banner"; const GROUP_BANNER_PATTERN = "itemGroup.name.banner_pattern"; + const GROUP_BAR = "itemGroup.name.bars"; const GROUP_BED = "itemGroup.name.bed"; const GROUP_BOAT = "itemGroup.name.boat"; const GROUP_BOOTS = "itemGroup.name.boots"; + const GROUP_BUNDLES = "itemGroup.name.bundles"; const GROUP_BUTTONS = "itemGroup.name.buttons"; + const GROUP_CANDLES = "itemGroup.name.candles"; + const GROUP_CHAINS = "itemGroup.name.chains"; const GROUP_CHALKBOARD = "itemGroup.name.chalkboard"; const GROUP_CHEST = "itemGroup.name.chest"; + const GROUP_CHEST_BOAT = "itemGroup.name.chestboat"; const GROUP_CHESTPLATE = "itemGroup.name.chestplate"; const GROUP_CONCRETE = "itemGroup.name.concrete"; const GROUP_CONCRETE_POWDER = "itemGroup.name.concretePowder"; @@ -42,12 +48,18 @@ final class CreativeInventoryInfo { const GROUP_GLASS = "itemGroup.name.glass"; const GROUP_GLASS_PANE = "itemGroup.name.glassPane"; const GROUP_GLAZED_TERRACOTTA = "itemGroup.name.glazedTerracotta"; + const GROUP_GOAT_HORN = "itemGroup.name.goatHorn"; + const GROUP_GOLEM_STATUE = "itemGroup.name.copper_golem_statue"; const GROUP_GRASS = "itemGroup.name.grass"; + const GROUP_HANGING_SIGN = "itemGroup.name.hanging_sign"; + const GROUP_HARNESSES = "itemGroup.name.harnesses"; const GROUP_HELMET = "itemGroup.name.helmet"; const GROUP_HOE = "itemGroup.name.hoe"; const GROUP_HORSE_ARMOR = "itemGroup.name.horseArmor"; + const GROUP_LANTERNS = "itemGroup.name.lanterns"; const GROUP_LEAVES = "itemGroup.name.leaves"; const GROUP_LEGGINGS = "itemGroup.name.leggings"; + const GROUP_LIGHTNING_ROD = "itemGroup.name.lightning_rod"; const GROUP_LINGERING_POTION = "itemGroup.name.lingeringPotion"; const GROUP_LOG = "itemGroup.name.log"; const GROUP_MINECRAFT = "itemGroup.name.minecart"; @@ -55,25 +67,32 @@ final class CreativeInventoryInfo { const GROUP_MOB_EGGS = "itemGroup.name.mobEgg"; const GROUP_MONSTER_STONE_EGG = "itemGroup.name.monsterStoneEgg"; const GROUP_MUSHROOM = "itemGroup.name.mushroom"; + const GROUP_NAUTILUS_ARMOR = "itemGroup.name.nautilus_armor"; const GROUP_NETHERWART_BLOCK = "itemGroup.name.netherWartBlock"; + const GROUP_OMINOUS_BOTTLE = "itemGroup.name.ominousBottle"; const GROUP_ORE = "itemGroup.name.ore"; const GROUP_PERMISSION = "itemGroup.name.permission"; const GROUP_PICKAXE = "itemGroup.name.pickaxe"; const GROUP_PLANKS = "itemGroup.name.planks"; const GROUP_POTION = "itemGroup.name.potion"; + const GROUP_POTTERY_SHERDS = "itemGroup.name.potterySherds"; const GROUP_PRESSURE_PLATE = "itemGroup.name.pressurePlate"; const GROUP_RAIL = "itemGroup.name.rail"; const GROUP_RAW_FOOD = "itemGroup.name.rawFood"; const GROUP_RECORD = "itemGroup.name.record"; const GROUP_SANDSTONE = "itemGroup.name.sandstone"; const GROUP_SAPLING = "itemGroup.name.sapling"; + const GROUP_SCULK = "itemGroup.name.sculk"; const GROUP_SEED = "itemGroup.name.seed"; + const GROUP_SHELF = "itemGroup.name.shelf"; const GROUP_SHOVEL = "itemGroup.name.shovel"; const GROUP_SHULKER_BOX = "itemGroup.name.shulkerBox"; const GROUP_SIGN = "itemGroup.name.sign"; const GROUP_SKULL = "itemGroup.name.skull"; const GROUP_SLAB = "itemGroup.name.slab"; const GROUP_SLASH_POTION = "itemGroup.name.splashPotion"; + const GROUP_SMITHING_TEMPLATES = "itemGroup.name.smithing_templates"; + const GROUP_SPEAR = "itemGroup.name.spear"; const GROUP_STAINED_CLAY = "itemGroup.name.stainedClay"; const GROUP_STAIRS = "itemGroup.name.stairs"; const GROUP_STONE = "itemGroup.name.stone"; @@ -84,40 +103,46 @@ final class CreativeInventoryInfo { const GROUP_WOOD = "itemGroup.name.wood"; const GROUP_WOOL = "itemGroup.name.wool"; const GROUP_WOOL_CARPET = "itemGroup.name.woolCarpet"; - const GROUP_CANDLES = "itemGroup.name.candles"; - const GROUP_GOAT_HORN = "itemGroup.name.goatHorn"; /** - * Returns a default type which puts the item in to the all category and no sub group. + * Returns a default CreativeInventoryInfo instance (all category, no group) */ public static function DEFAULT(): self { return new self(self::CATEGORY_ALL, self::NONE); } - public function __construct(private readonly string $category = self::NONE, private readonly string $group = self::NONE) { } + /** + * @param string $category The category this item belongs to + * @param string $group The group this item belongs to (optional) + */ + public function __construct( + private readonly string $category = self::NONE, + private readonly string $group = self::NONE + ) {} /** - * Returns the category the item is part of. + * Returns the creative inventory category. */ public function getCategory(): string { return $this->category; } /** - * Returns the numeric representation of the category the item is part of. + * Returns the numeric representation of the category. + * 0 = all, 1 = construction, 2 = nature, 3 = equipment, 4 = items */ public function getNumericCategory(): int { - return match ($this->getCategory()) { + return match ($this->category) { self::CATEGORY_CONSTRUCTION => 1, self::CATEGORY_NATURE => 2, self::CATEGORY_EQUIPMENT => 3, self::CATEGORY_ITEMS => 4, - default => 0 + default => 0, }; } /** - * Returns the group the item is part of, if any. + * Returns the creative inventory group. */ public function getGroup(): string { return $this->group; diff --git a/src/item/CustomiesItemFactory.php b/src/item/CustomiesItemFactory.php index bafb9989..a70e4b85 100644 --- a/src/item/CustomiesItemFactory.php +++ b/src/item/CustomiesItemFactory.php @@ -27,11 +27,12 @@ use function array_values; final class CustomiesItemFactory { - use SingletonTrait; + use SingletonTrait { + setInstance as private; + reset as private; + } - /** - * Default property values for item_properties - */ + /** Default values for item_properties */ private const PROPERTY_DEFAULTS = [ 'allow_off_hand' => false, 'can_destroy_in_creative' => true, @@ -50,10 +51,9 @@ final class CustomiesItemFactory { 'use_duration' => 0, ]; - /** - * @var ItemTypeEntry[] - */ + /** @var ItemTypeEntry[] */ private array $itemTableEntries = []; + /** @var CreativeGroup[] */ private array $groups = []; /** @@ -84,7 +84,7 @@ private function loadGroups() : void { } /** - * Returns custom item entries + * Returns all registered item table entries. * @return ItemTypeEntry[] */ public function getItemTableEntries(): array { @@ -108,46 +108,46 @@ public function registerItem(Closure $itemFunc, string $identifier, ?CreativeInv GlobalItemDataHandlers::getDeserializer()->map($identifier, fn() => clone $item); GlobalItemDataHandlers::getSerializer()->map($item, fn() => new SavedItemData($identifier)); - StringToItemParser::getInstance()->register($identifier, fn() => clone $item); // This is where the components are added to the item $componentBased = $item instanceof ItemComponents; - $nbt = $this->createItemNbt($item, $identifier, $itemId, $creativeInfo); - if($creativeInfo !== null){ $this->loadGroups(); if($creativeInfo->getCategory() === CreativeInventoryInfo::CATEGORY_ALL || $creativeInfo->getCategory() === CreativeInventoryInfo::CATEGORY_COMMANDS){ return; } - - $group = $this->groups[$creativeInfo->getGroup()] ?? ($creativeInfo->getGroup() !== "" && $creativeInfo->getGroup() !== CreativeInventoryInfo::NONE ? new CreativeGroup( - new Translatable($creativeInfo->getGroup()), - $item - ) : null); - - if($group !== null){ + $group = $this->groups[$creativeInfo->getGroup()] ?? null; + if( + $group === null && $creativeInfo->getGroup() !== "" && + $creativeInfo->getGroup() !== CreativeInventoryInfo::NONE + ){ + $group = new CreativeGroup(new Translatable($creativeInfo->getGroup()), $item); $this->groups[$group->getName()->getText()] = $group; } - $category = match ($creativeInfo->getCategory()) { CreativeInventoryInfo::CATEGORY_CONSTRUCTION => CreativeCategory::CONSTRUCTION, CreativeInventoryInfo::CATEGORY_ITEMS => CreativeCategory::ITEMS, CreativeInventoryInfo::CATEGORY_NATURE => CreativeCategory::NATURE, CreativeInventoryInfo::CATEGORY_EQUIPMENT => CreativeCategory::EQUIPMENT, - default => throw new AssumptionFailedError("Unknown category") + default => throw new AssumptionFailedError("Unknown Creative Category") }; - CreativeInventory::getInstance()->add($item, $category, $group); - } - - $this->itemTableEntries[$identifier] = $entry = new ItemTypeEntry($identifier, $itemId, $componentBased, $componentBased ? 1 : 0, new CacheableNbt($nbt)); + } + $nbt = $this->createItemNbt($item, $identifier, $itemId, $creativeInfo); + $entry = new ItemTypeEntry( + $identifier, + $itemId, + $componentBased, + $componentBased ? 1 : 0, + new CacheableNbt($nbt) + ); + $this->itemTableEntries[$identifier] = $entry; $this->registerCustomItemMapping($identifier, $itemId, $entry); } /** - * Creates the NBT data for the item. This includes the components and their values. - * If the item does not have components, an empty CompoundTag is returned. + * Creates the CompoundTag for an item, including components and default properties. */ private function createItemNbt(Item $item, string $identifier, int $itemId, ?CreativeInventoryInfo $creativeInfo): CompoundTag { if(!($item instanceof ItemComponents)) { @@ -250,7 +250,8 @@ private function registerCustomItemMapping(string $identifier, int $itemId, Item public function registerBlockItem(string $identifier, Block $block): void { $itemId = $block->getIdInfo()->getBlockTypeId(); StringToItemParser::getInstance()->registerBlock($identifier, fn() => clone $block); - $this->itemTableEntries[] = $entry = new ItemTypeEntry($identifier, $itemId, false, 2, new CacheableNbt(CompoundTag::create())); + $entry = new ItemTypeEntry($identifier, $itemId, false, 2, new CacheableNbt(CompoundTag::create())); + $this->itemTableEntries[] = $entry; $this->registerCustomItemMapping($identifier, $itemId, $entry); $blockItemIdMap = BlockItemIdMap::getInstance(); diff --git a/src/item/component/BundleInteractionComponent.php b/src/item/component/BundleInteractionComponent.php index 994c0076..67ef945f 100644 --- a/src/item/component/BundleInteractionComponent.php +++ b/src/item/component/BundleInteractionComponent.php @@ -33,5 +33,4 @@ public function getPropertyMapping(): ?array { public static function fromJson(mixed $data): static { return new self($data["num_viewable_slots"] ?? 12); } - } \ No newline at end of file diff --git a/src/item/component/CanDestroyInCreativeComponent.php b/src/item/component/CanDestroyInCreativeComponent.php index 1f307fdb..8f1b797a 100644 --- a/src/item/component/CanDestroyInCreativeComponent.php +++ b/src/item/component/CanDestroyInCreativeComponent.php @@ -32,4 +32,4 @@ public function getPropertyMapping(): ?array { public static function fromJson(mixed $data): static { return new self($data ?? true); } -} +} \ No newline at end of file diff --git a/src/item/component/CooldownComponent.php b/src/item/component/CooldownComponent.php index 31ec6de4..d8a1bae3 100644 --- a/src/item/component/CooldownComponent.php +++ b/src/item/component/CooldownComponent.php @@ -11,17 +11,23 @@ final class CooldownComponent implements ItemComponent { public const CATEGORY_WINDCHARGE = "wind_charge"; public const CATEGORY_CHORUS = "chorusfruit"; + public const TYPE_ATTACK = "attack"; + public const TYPE_USE = "use"; + private string $category; private float $duration; + private string $type; /** * The duration of time (in seconds) items with a matching category will spend cooling down before becoming usable again. * @param string $category All items with the same "category" are put on cooldown when one is used. * @param float $duration How long the item is on cooldown before being able to be used again. + * @param string $type The type of cooldown (e.g., "use", "attack"). Default is "use". */ - public function __construct(string $category, float $duration) { + public function __construct(string $category, float $duration, string $type = self::TYPE_USE) { $this->category = $category; $this->duration = $duration; + $this->type = $type; } public function getName(): string { @@ -31,7 +37,8 @@ public function getName(): string { public function getValue(): array { return [ "category" => $this->category, - "duration" => $this->duration + "duration" => $this->duration, + "type" => $this->type, ]; } @@ -40,6 +47,10 @@ public function getPropertyMapping(): ?array { } public static function fromJson(mixed $data): static { - return new self($data["category"] ?? self::CATEGORY_SHIELD, $data["duration"] ?? 0.0); + return new self( + $data["category"] ?? self::CATEGORY_SHIELD, + $data["duration"] ?? 0.0, + $data["type"] ?? "use" + ); } } \ No newline at end of file diff --git a/src/item/component/DamageAbsorptionComponent.php b/src/item/component/DamageAbsorptionComponent.php index d27e3949..a4cb4422 100644 --- a/src/item/component/DamageAbsorptionComponent.php +++ b/src/item/component/DamageAbsorptionComponent.php @@ -3,8 +3,14 @@ namespace customiesdevs\customies\item\component; +use customiesdevs\customies\item\properties\DamageCause; + final class DamageAbsorptionComponent implements ItemComponent { + /** + * List of damage causes that can be absorbed by the item. + * @var DamageCause[] Must contain at least 1 item for meaningful effect. + */ private array $absorbableCauses; /** @@ -14,7 +20,7 @@ final class DamageAbsorptionComponent implements ItemComponent { * Because of this, the item also needs a `minecraft:durability` component. * @param array $absorbableCauses List of damage causes that can be absorbed by the item. By default, no damage cause is absorbed. Value must have at least 1 items. */ - public function __construct(array $absorbableCauses) { + public function __construct(array $absorbableCauses = []) { $this->absorbableCauses = $absorbableCauses; } @@ -24,7 +30,10 @@ public function getName(): string { public function getValue(): array { return [ - "absorbable_causes" => $this->absorbableCauses + "absorbable_causes" => array_map( + fn(DamageCause $cause) => $cause->value, + $this->absorbableCauses + ) ]; } @@ -32,7 +41,29 @@ public function getPropertyMapping(): ?array { return null; } + /** + * Adds a single damage cause to the absorbable list. + * @param DamageCause $cause + */ + public function addCause(DamageCause $cause): self { + if(!in_array($cause, $this->absorbableCauses, true)){ + $this->absorbableCauses[] = $cause; + } + return $this; + } + public static function fromJson(mixed $data): static { - return new self($data["absorbable_causes"] ?? []); + $causes = []; + if(is_array($data["absorbable_causes"] ?? null)){ + foreach($data["absorbable_causes"] as $cause){ + if(is_string($cause)){ + $enumCause = DamageCause::tryFrom($cause); + if($enumCause !== null){ + $causes[] = $enumCause; + } + } + } + } + return new self($causes); } } \ No newline at end of file diff --git a/src/item/component/DiggerComponent.php b/src/item/component/DiggerComponent.php index a15a1166..e9a76a3b 100644 --- a/src/item/component/DiggerComponent.php +++ b/src/item/component/DiggerComponent.php @@ -10,12 +10,14 @@ final class DiggerComponent implements ItemComponent { + /** @var array, speed: int}> */ private array $destroySpeeds; private bool $useEfficiency; /** * Allows a creator to determine how quickly an item can dig specific blocks. - * @param bool $useEfficiency Determines whether the item should be impacted if the `efficiency` enchant is applied to it. + * @param bool $useEfficiency Determines whether the item should be impacted by the Efficiency enchantment. + * @param array, speed: int}> $destroySpeeds Optional array of destroy speeds. */ public function __construct(bool $useEfficiency, array $destroySpeeds = []) { $this->useEfficiency = $useEfficiency; @@ -37,12 +39,12 @@ public function getPropertyMapping(): ?array { return null; } - /** - * Add blocks to the `destroy_speeds` array in the required format. + /** + * Adds blocks to the `destroy_speeds` array with a specified speed. * @param int $speed Digging speed for the correlating block(s) - * @param Block ...$blocks A list of blocks to dig with correlating speeds of digging + * @param Block ...$blocks A list of blocks to dig with the given speed */ - public function withBlocks(int $speed, Block ...$blocks): DiggerComponent { + public function withBlocks(int $speed, Block ...$blocks): self { foreach($blocks as $block){ $this->destroySpeeds[] = [ "block" => [ @@ -55,11 +57,11 @@ public function withBlocks(int $speed, Block ...$blocks): DiggerComponent { } /** - * Add blocks to the `destroy_speeds` array in the required format. - * @param int $speed Digging speed for the correlating block(s) - * @param string ...$tags A list of blocks to dig with correlating speeds of digging + * Adds block tags to the `destroy_speeds` array with a specified speed. + * @param int $speed Digging speed for the correlating blocks + * @param string ...$tags A list of block tags */ - public function withTags(int $speed, string ...$tags): DiggerComponent { + public function withTags(int $speed, string ...$tags): self { $query = implode(",", array_map(fn($tag) => "'" . $tag . "'", $tags)); $this->destroySpeeds[] = [ "block" => [ @@ -70,8 +72,18 @@ public function withTags(int $speed, string ...$tags): DiggerComponent { return $this; } + /** + * Returns the array of destroy speeds. + * @return array, speed: int}> + */ + public function getDestroySpeeds(): array { + return $this->destroySpeeds; + } + public static function fromJson(mixed $data): static { - $component = new self($data["use_efficiency"] ?? false, $data["destroy_speeds"] ?? []); - return $component; + return new self( + $data["use_efficiency"] ?? false, + $data["destroy_speeds"] ?? [] + ); } } \ No newline at end of file diff --git a/src/item/component/DurabilitySensorComponent.php b/src/item/component/DurabilitySensorComponent.php index 8b08976f..87593acf 100644 --- a/src/item/component/DurabilitySensorComponent.php +++ b/src/item/component/DurabilitySensorComponent.php @@ -3,13 +3,16 @@ namespace customiesdevs\customies\item\component; +use customiesdevs\customies\item\properties\ParticleType; +use customiesdevs\customies\item\properties\SoundEvent; + final class DurabilitySensorComponent implements ItemComponent { private array $durabilityThresholds; /** - * TODO needs rework * Enables an item to emit effects when it receives damage. Because of this, the item also needs a `minecraft:durability` component. + * @param array $durabilityThresholds */ public function __construct(array $durabilityThresholds = []) { $this->durabilityThresholds = $durabilityThresholds; @@ -21,9 +24,14 @@ public function getName(): string { public function getValue(): array { return [ - "durability_thresholds" => [ + "durability_thresholds" => array_map( + fn(array $threshold) => [ + "durability" => $threshold["durability"], + "particle_type" => $threshold["particle_type"]?->value, + "sound_event" => $threshold["sound_event"]?->value + ], $this->durabilityThresholds - ] + ) ]; } @@ -31,9 +39,19 @@ public function getPropertyMapping(): ?array { return null; } - // I dont know if this will work | TODO test it and see what it outputs - public function addDurabilityThreshold(int $durability, string $particleType = "", string $soundEvent = ""): self { - $this->durabilityThresholds[] = [ + /** + * Adds a new durability threshold. + * + * @param int $durability The durability at which this threshold triggers + * @param ParticleType|null $particleType Optional particle effect + * @param SoundEvent|null $soundEvent Optional sound effect + */ + public function addDurabilityThreshold( + int $durability, + ?ParticleType $particleType = null, + ?SoundEvent $soundEvent = null + ): self { + $this->durabilityThresholds[] = [ "durability" => $durability, "particle_type" => $particleType, "sound_event" => $soundEvent @@ -42,6 +60,14 @@ public function addDurabilityThreshold(int $durability, string $particleType = " } public static function fromJson(mixed $data): static { - return new self($data["durability_thresholds"] ?? []); + $thresholds = []; + foreach($data["durability_thresholds"] ?? [] as $threshold) { + $thresholds[] = [ + "durability" => $threshold["durability"] ?? 0, + "particle_type" => isset($threshold["particle_type"]) ? ParticleType::tryFrom($threshold["particle_type"]) : null, + "sound_event" => isset($threshold["sound_event"]) ? SoundEvent::tryFrom($threshold["sound_event"]) : null + ]; + } + return new self($thresholds); } } \ No newline at end of file diff --git a/src/item/component/DyeableComponent.php b/src/item/component/DyeableComponent.php index 206305fa..fb6ab377 100644 --- a/src/item/component/DyeableComponent.php +++ b/src/item/component/DyeableComponent.php @@ -5,12 +5,16 @@ final class DyeableComponent implements ItemComponent { - /** @var int[] */ + /** + * RGB color values as an array of three integers [R, G, B]. + * @var int[] + */ private array $rgb; /** * Allows the item to be dyed by cauldron water. Once dyed, the item will display the `dyed` texture defined in the `minecraft:icon` component rather than `default`. * @param string $hex Hex color code (e.g. "#175882") + * @throws \InvalidArgumentException If the hex code is invalid */ public function __construct(string $hex) { $this->rgb = self::hexToRgb($hex); @@ -30,6 +34,13 @@ public function getPropertyMapping(): ?array { return null; } + /** + * Converts a hex color string to an RGB array. + * + * @param string $hex Hex color string (e.g. "#175882") + * @return int[] Array of RGB values [R, G, B] + * @throws \InvalidArgumentException If the hex code is invalid + */ private static function hexToRgb(string $hex): array { $hex = ltrim($hex, '#'); if(strlen($hex) !== 6) throw new \InvalidArgumentException("Invalid hex color: {$hex}"); @@ -40,6 +51,12 @@ private static function hexToRgb(string $hex): array { ]; } + /** + * Converts an RGB array to a hex color string. + * + * @param int[] $rgb Array of RGB values [R, G, B] + * @return string Hex color string (e.g. "#175882") + */ private static function rgbToHex(array $rgb): string { [$r, $g, $b] = $rgb; return sprintf("#%02x%02x%02x", $r, $g, $b); diff --git a/src/item/component/EnchantableComponent.php b/src/item/component/EnchantableComponent.php index 9be46e62..c4044164 100644 --- a/src/item/component/EnchantableComponent.php +++ b/src/item/component/EnchantableComponent.php @@ -5,42 +5,51 @@ final class EnchantableComponent implements ItemComponent { + public const SLOT_NONE = "none"; public const SLOT_ALL = "all"; - public const SLOT_BOOTS = "armor_feet"; - public const SLOT_CHESTPLATE = "armor_torso"; public const SLOT_HELMET = "armor_head"; + public const SLOT_CHESTPLATE = "armor_torso"; public const SLOT_LEGGINGS = "armor_legs"; - public const SLOT_AXE = "axe"; + public const SLOT_BOOTS = "armor_feet"; + public const SLOT_SWORD = "sword"; public const SLOT_BOW = "bow"; - public const SLOT_COSMETIC_HEAD = "cosmetic_head"; + public const SLOT_SPEAR = "spear"; + public const SLOT_MELEE_SPEAR = "melee_spear"; public const SLOT_CROSSBOW = "crossbow"; - public const SLOT_ELYTRA = "elytra"; - public const SLOT_FISHING_ROD = "fishing_rod"; - public const SLOT_FLINT = "flintsteel"; + public const SLOT_TOOL_GENERIC = "g_tool"; public const SLOT_HOE = "hoe"; - public const SLOT_PICKAXE = "pickaxe"; public const SLOT_SHEARS = "shears"; + public const SLOT_FLINTSTEEL = "flintsteel"; public const SLOT_SHIELD = "shield"; + public const SLOT_DIGGING = "g_digging"; + public const SLOT_AXE = "axe"; + public const SLOT_PICKAXE = "pickaxe"; public const SLOT_SHOVEL = "shovel"; - public const SLOT_SPEAR = "melee_spear"; - public const SLOT_SWORD = "sword"; + public const SLOT_FISHING_ROD = "fishing_rod"; + public const SLOT_CARROT_STICK = "carrot_stick"; + public const SLOT_ELYTRA = "elytra"; + public const SLOT_COSMETIC_HEAD = "cosmetic_head"; // Armor Enchantability public const ARMOR_LEATHER = 15; public const ARMOR_CHAIN = 12; + public const ARMOR_COPPER = 8; public const ARMOR_IRON = 9; public const ARMOR_GOLD = 25; public const ARMOR_DIAMOND = 10; public const ARMOR_TURTLE = 9; public const ARMOR_NETHERITE = 15; public const ARMOR_OTHER = 1; + // Tool Enchantability public const TOOL_WOOD = 15; public const TOOL_STONE = 5; + public const TOOL_COOPER = 13; public const TOOL_IRON = 14; public const TOOL_GOLD = 22; public const TOOL_DIAMOND = 10; public const TOOL_NETHERITE = 15; + public const TOOL_MACE = 15; public const TOOL_OTHER = 1; private string $slot; @@ -51,7 +60,7 @@ final class EnchantableComponent implements ItemComponent { * @param string $slot Specifies which types of enchantments can be applied. For example, `bow` would allow this item to be enchanted as if it were a bow * @param int $value Specifies the value of the enchantment, Default is set to `1` */ - public function __construct(string $slot = self::SLOT_ALL, int $value = 1) { + public function __construct(string $slot = self::SLOT_NONE, int $value = 1) { $this->slot = $slot; $this->value = $value; } diff --git a/src/item/component/InteractButtonComponent.php b/src/item/component/InteractButtonComponent.php index 655f8e3e..eacf2d75 100644 --- a/src/item/component/InteractButtonComponent.php +++ b/src/item/component/InteractButtonComponent.php @@ -26,7 +26,7 @@ public function getName(): string { public function getValue(): array { return [ "interact_text" => $this->interactButton, - "requires_interact" => 1 + "requires_interact" => true ]; } diff --git a/src/item/component/ItemComponent.php b/src/item/component/ItemComponent.php index a7d994ef..b3257766 100644 --- a/src/item/component/ItemComponent.php +++ b/src/item/component/ItemComponent.php @@ -3,6 +3,9 @@ namespace customiesdevs\customies\item\component; +/** + * @template T + */ interface ItemComponent { /** diff --git a/src/item/component/RepairableComponent.php b/src/item/component/RepairableComponent.php index 842ae5f4..45ecdb00 100644 --- a/src/item/component/RepairableComponent.php +++ b/src/item/component/RepairableComponent.php @@ -8,24 +8,24 @@ final class RepairableComponent implements ItemComponent { /** - * Defines the items that can be used to repair a defined item, and the amount of durability each item restores upon repair. Each entry needs to define a list of strings for 'items' that can be used for the repair and an optional 'repair_amount' for how much durability is repaired. + * Defines the items that can be used to repair a defined item, and the amount of durability each item restores upon repair. Each entry needs to define a list of strings for 'items' that can be used for the repair and an optional 'repair_amount' for how much durability is repaired. * @param RepairItems[] $repairItems List of repair item entries. Each entry needs to define a list of strings for items that can be used for the repair and an optional repair_amount for how much durability is gained. - */ + */ public function __construct( - private readonly array $repairItems, - ) {} + private readonly array $repairItems, + ) {} public function getName(): string { return 'minecraft:repairable'; } public function getValue(): array { - $repairItems = []; - foreach($this->repairItems as $repairItem) { - $repairItems[] = $repairItem->toArray(); - } + $repairItems = []; + foreach($this->repairItems as $repairItem) { + $repairItems[] = $repairItem->toArray(); + } return [ - "repair_items" => $repairItems + "repair_items" => $repairItems ]; } diff --git a/src/item/component/TagsComponent.php b/src/item/component/TagsComponent.php index 46eb042a..0c6119d7 100644 --- a/src/item/component/TagsComponent.php +++ b/src/item/component/TagsComponent.php @@ -21,8 +21,8 @@ public function getName(): string { public function getValue(): array { return [ - "tags" => $this->tags - ]; + "tags" => $this->tags + ]; } public function getPropertyMapping(): ?array { diff --git a/src/item/properties/DamageCause.php b/src/item/properties/DamageCause.php index f8782999..0bdb3631 100644 --- a/src/item/properties/DamageCause.php +++ b/src/item/properties/DamageCause.php @@ -3,7 +3,10 @@ namespace customiesdevs\customies\item\properties; -final class DamageCause { +/** + * Represents all possible causes of damage in the game. + */ +enum DamageCause: string { const NONE = "none"; const ALL = "all"; const ANVIL = "anvil"; diff --git a/src/item/properties/ParticleType.php b/src/item/properties/ParticleType.php index cd19721f..7a7eb0c2 100644 --- a/src/item/properties/ParticleType.php +++ b/src/item/properties/ParticleType.php @@ -3,7 +3,10 @@ namespace customiesdevs\customies\item\properties; -final class ParticleType { +/** + * Represents all available particle types in the game. + */ +enum ParticleType: string { const NONE = "none"; const BUBBLE = "bubble"; const BUBBLE_MANUAL = "bubblemanual"; diff --git a/src/item/properties/RepairItems.php b/src/item/properties/RepairItems.php index cba8fbc0..e5420c92 100644 --- a/src/item/properties/RepairItems.php +++ b/src/item/properties/RepairItems.php @@ -5,14 +5,32 @@ class RepairItems { /** - * @param string[] $items Items that may be used to repair an item. - * @param int $repairAmount How much the item is repaired. + * @param string[] $items List of item names that may be used for repairing. + * @param int $repairAmount Amount by which the item is repaired. */ public function __construct( private readonly array $items, private readonly int $repairAmount, ) {} + /** + * Returns an array representation of the repair items. + * + * The returned array has the following structure: + * [ + * "items" => [ + * ["name" => "item1"], + * ["name" => "item2"], + * ... + * ], + * "repair_amount" => int + * ] + * + * @return array{ + * items: array, + * repair_amount: int + * } + */ public function toArray(): array { $items = []; foreach($this->items as $item) { @@ -26,6 +44,18 @@ public function toArray(): array { ]; } + /** + * Creates a RepairItems instance from an array representation. + * + * Expects an array in the format returned by `toArray()`. + * Missing or invalid values are replaced with defaults (empty array or 0). + * + * @param array{ + * items?: array, + * repair_amount?: int + * } $data + * @return self + */ public static function fromArray(array $data): self { $items = []; if(is_array($data["items"] ?? null)) { @@ -36,4 +66,19 @@ public static function fromArray(array $data): self { return new self($items, $data["repair_amount"] ?? 0); } + /** + * Returns the list of item names that can be used for repair. + * + * @return string[] + */ + public function getItems(): array { + return $this->items; + } + + /** + * Returns the repair amount provided by these items. + */ + public function getRepairAmount(): int { + return $this->repairAmount; + } } \ No newline at end of file diff --git a/src/item/properties/SoundEvent.php b/src/item/properties/SoundEvent.php index 5297b48f..a42f41e2 100644 --- a/src/item/properties/SoundEvent.php +++ b/src/item/properties/SoundEvent.php @@ -3,7 +3,10 @@ namespace customiesdevs\customies\item\properties; -final class SoundEvent { +/** + * Represents all possible sound events in the game. + */ +enum SoundEvent: string { const ITEM_USE_ON = "item.use.on"; const HIT = "hit"; const STEP = "step"; diff --git a/src/item/traits/ItemComponentsTrait.php b/src/item/traits/ItemComponentsTrait.php index 47037ccc..bd25c5d3 100644 --- a/src/item/traits/ItemComponentsTrait.php +++ b/src/item/traits/ItemComponentsTrait.php @@ -7,25 +7,48 @@ trait ItemComponentsTrait { - /** @var ItemComponent[] */ + /** + * Registered item components indexed by component name. + * + * @var array + */ private array $components; + /** + * Adds a component to the item. + * + * @param ItemComponent $component The component to add + */ public function addComponent(ItemComponent $component): void { $this->components[$component->getName()] = $component; } + /** + * Checks if the item has a component by its name. + * + * @param string $name The name of the component + * @return bool True if the component exists, false otherwise + */ public function hasComponent(string $name): bool { return isset($this->components[$name]); } + /** + * Retrieves a component by its name. + * + * @param string $name The name of the component + * @return ItemComponent|null The component if found, null otherwise + */ public function getComponent(string $name): ?ItemComponent { return $this->components[$name] ?? null; } /** - * @return ItemComponent[] + * Returns all components of the item. + * + * @return ItemComponent[] Array of all components */ public function getComponents(): array { return $this->components; } -} +} \ No newline at end of file diff --git a/src/json/BehaviorManager.php b/src/json/BehaviorManager.php index 1ac156dd..0b281d4c 100644 --- a/src/json/BehaviorManager.php +++ b/src/json/BehaviorManager.php @@ -19,10 +19,17 @@ /** * Manages loading and registration of custom items and blocks from JSON behavior files. + * + * Provides methods to scan the behavior folder, read JSON definitions, + * and register items and blocks with their components. */ final class BehaviorManager { - use SingletonTrait; + use SingletonTrait { + setInstance as private; + reset as private; + } + /** @var string Path to the behavior folder */ private string $behaviorDirectory; public function __construct() { @@ -31,7 +38,7 @@ public function __construct() { } /** - * Ensures the behavior directories exist + * Ensures that the 'items' and 'blocks' subdirectories exist. */ private function ensureDirectoriesExist(): void { foreach(['items', 'blocks'] as $subdir) { @@ -43,9 +50,10 @@ private function ensureDirectoriesExist(): void { } /** - * Gets all JSON files in a subdirectory - * @param string $subdir The subdirectory name ('items' or 'blocks') - * @return string[] + * Gets all JSON files in a given subdirectory. + * + * @param string $subdir Subdirectory name ('items' or 'blocks') + * @return string[] List of JSON filenames */ private function getJsonFiles(string $subdir): array { $path = $this->behaviorDirectory . $subdir . "/"; @@ -53,7 +61,7 @@ private function getJsonFiles(string $subdir): array { if($files === false) { return []; } - return array_filter($files, function($file) use ($path) { + return array_filter($files, static function(string $file) use ($path): bool { return $file !== '.' && $file !== '..' && is_file($path . $file) && @@ -62,14 +70,18 @@ private function getJsonFiles(string $subdir): array { } /** - * Gets a configuration from a JSON file + * Reads a JSON configuration file. + * + * @param string $subdir Subdirectory name + * @param string $filename JSON filename + * @return Config */ private function getConfig(string $subdir, string $filename): Config { return new Config($this->behaviorDirectory . $subdir . "/" . $filename, Config::JSON); } /** - * Registers all items and blocks from behavior files + * Registers all items and blocks from behavior files. */ public function registerAll(): void { $this->registerItems(); @@ -77,7 +89,7 @@ public function registerAll(): void { } /** - * Registers all items from JSON files + * Registers all items from JSON files. */ public function registerItems(): void { $this->registerFromDirectory('items', 'minecraft:item', function(array $config): void { @@ -86,7 +98,7 @@ public function registerItems(): void { } /** - * Registers all blocks from JSON files + * Registers all blocks from JSON files. */ public function registerBlocks(): void { $this->registerFromDirectory('blocks', 'minecraft:block', function(array $config): void { @@ -95,49 +107,51 @@ public function registerBlocks(): void { } /** - * Generic registration loop + * Generic method for registering items or blocks from a directory. + * + * @param string $subdir Subdirectory name ('items' or 'blocks') + * @param string $rootKey Root key in JSON ('minecraft:item' or 'minecraft:block') + * @param callable(array): void $register Callback to register each entry */ private function registerFromDirectory(string $subdir, string $rootKey, callable $register): void { $registeredCount = 0; $errorCount = 0; - $type = rtrim($subdir, 's'); // 'items' -> 'item' - + $type = rtrim($subdir, 's'); // 'items' -> 'item', 'blocks' -> 'block' foreach($this->getJsonFiles($subdir) as $file) { try { $config = $this->getConfig($subdir, $file)->getAll(); if(!isset($config[$rootKey])) { - throw new \InvalidArgumentException("Missing $rootKey"); + throw new \InvalidArgumentException("Missing '$rootKey' in JSON file"); } $register($config[$rootKey]); $registeredCount++; - } catch(\Exception $e) { + }catch(\Exception $e) { $errorCount++; Customies::getInstance()->getLogger()->error( "Failed to register $type from '$file': " . $e->getMessage() ); } } - if($registeredCount > 0 || $errorCount > 0) { Customies::getInstance()->getLogger()->info( - "Registered $registeredCount custom {$subdir}" . + "Registered $registeredCount custom {$subdir}" . ($errorCount > 0 ? " ($errorCount failed)" : "") ); } } /** - * Registers a single item from config + * Registers a single item from configuration. + * + * @param array $config JSON-decoded item configuration */ private function registerItem(array $config): void { if(!isset($config["components"], $config["description"]["identifier"])) { - throw new \InvalidArgumentException("Missing required fields"); + throw new \InvalidArgumentException("Missing required fields 'components' or 'description.identifier'"); } - $identifier = $config["description"]["identifier"]; $components = $config["components"]; $creativeInfo = $this->getCreativeInfo($config); - CustomiesItemFactory::getInstance()->registerItem( static fn() => new CustomiesItem($components), $identifier, @@ -146,17 +160,17 @@ private function registerItem(array $config): void { } /** - * Registers a single block from config + * Registers a single block from configuration. + * + * @param array $config JSON-decoded block configuration */ private function registerBlock(array $config): void { if(!isset($config["components"], $config["description"]["identifier"])) { - throw new \InvalidArgumentException("Missing required fields"); + throw new \InvalidArgumentException("Missing required fields 'components' or 'description.identifier'"); } - $identifier = $config["description"]["identifier"]; $components = $config["components"]; $creativeInfo = $this->getCreativeInfo($config); - CustomiesBlockFactory::getInstance()->registerBlock( static fn(): Block => new CustomiesBlock($components), $identifier, @@ -165,18 +179,19 @@ private function registerBlock(array $config): void { } /** - * Gets creative inventory info from config + * Extracts CreativeInventoryInfo from configuration. + * + * @param array $config JSON-decoded configuration + * @return CreativeInventoryInfo */ private function getCreativeInfo(array $config): CreativeInventoryInfo { $category = CreativeInventoryInfo::CATEGORY_ITEMS; $group = CreativeInventoryInfo::NONE; - if(isset($config["description"]["menu_category"])) { $menuCategory = $config["description"]["menu_category"]; $category = $menuCategory["category"] ?? $category; $group = $menuCategory["group"] ?? $group; } - return new CreativeInventoryInfo($category, $group); } -} +} \ No newline at end of file diff --git a/src/json/BlockComponentRegistry.php b/src/json/BlockComponentRegistry.php index 2627ff2c..259db274 100644 --- a/src/json/BlockComponentRegistry.php +++ b/src/json/BlockComponentRegistry.php @@ -5,7 +5,6 @@ use customiesdevs\customies\block\component\BlockComponent; use customiesdevs\customies\block\component\CollisionBoxComponent; -use customiesdevs\customies\block\component\CraftingTableComponent; use customiesdevs\customies\block\component\DestructibleByExplosionComponent; use customiesdevs\customies\block\component\DestructibleByMiningComponent; use customiesdevs\customies\block\component\DestructionParticlesComponent; @@ -17,14 +16,13 @@ use customiesdevs\customies\block\component\LightDampeningComponent; use customiesdevs\customies\block\component\LightEmissionComponent; use customiesdevs\customies\block\component\LiquidDetectionComponent; -use customiesdevs\customies\block\component\LootComponent; use customiesdevs\customies\block\component\MapColorComponent; use customiesdevs\customies\block\component\MaterialInstancesComponent; use customiesdevs\customies\block\component\MovableComponent; use customiesdevs\customies\block\component\PlacementFilterComponent; use customiesdevs\customies\block\component\RandomOffsetComponent; -use customiesdevs\customies\block\component\RedstoneConductivityComponent; use customiesdevs\customies\block\component\SelectionBoxComponent; +use customiesdevs\customies\block\component\SupportComponent; /** * Central registry for block component mappings. @@ -32,10 +30,12 @@ */ final class BlockComponentRegistry { - /** @var array> */ + /** + * Maps component identifiers to their implementing class names. + * @var array> + */ private static array $components = [ 'minecraft:collision_box' => CollisionBoxComponent::class, - 'minecraft:crafting_table' => CraftingTableComponent::class, 'minecraft:destructible_by_explosion' => DestructibleByExplosionComponent::class, 'minecraft:destructible_by_mining' => DestructibleByMiningComponent::class, 'minecraft:destruction_particles' => DestructionParticlesComponent::class, @@ -47,28 +47,30 @@ final class BlockComponentRegistry { 'minecraft:light_dampening' => LightDampeningComponent::class, 'minecraft:light_emission' => LightEmissionComponent::class, 'minecraft:liquid_detection' => LiquidDetectionComponent::class, - 'minecraft:loot' => LootComponent::class, 'minecraft:map_color' => MapColorComponent::class, 'minecraft:material_instances' => MaterialInstancesComponent::class, 'minecraft:movable' => MovableComponent::class, 'minecraft:placement_filter' => PlacementFilterComponent::class, 'minecraft:random_offset' => RandomOffsetComponent::class, - 'minecraft:redstone_conductivity' => RedstoneConductivityComponent::class, 'minecraft:selection_box' => SelectionBoxComponent::class, + 'minecraft:support' => SupportComponent::class, ]; /** - * Register a custom component class. - * @param string $name The component identifier (e.g., 'yourplugin:custom_component') - * @param class-string $class The component class + * Register a custom block component. + * + * @param string $name Component identifier (e.g., 'yourplugin:custom_component') + * @param class-string $class Fully qualified class name implementing BlockComponent */ public static function register(string $name, string $class): void { self::$components[$name] = $class; } /** - * Get a component class by its identifier. - * @return class-string|null + * Get the class name of a component by its identifier. + * + * @param string $name Component identifier + * @return class-string|null Returns the class name, or null if not registered */ public static function get(string $name): ?string { return self::$components[$name] ?? null; @@ -76,26 +78,31 @@ public static function get(string $name): ?string { /** * Check if a component is registered. + * + * @param string $name Component identifier + * @return bool True if registered, false otherwise */ public static function has(string $name): bool { return isset(self::$components[$name]); } /** - * Create a component instance from JSON data. - * @return BlockComponent|null Returns null if component is not registered + * Instantiate a block component from JSON data. + * + * @param string $name Component identifier + * @param mixed $data JSON-decoded data for the component + * @return BlockComponent|null Returns the component instance or null if not registered */ public static function fromJson(string $name, mixed $data): ?BlockComponent { $class = self::get($name); - if($class === null) { - return null; - } + if($class === null) return null; return $class::fromJson($data); } /** * Get all registered component identifiers. - * @return string[] + * + * @return string[] List of component IDs */ public static function getAll(): array { return array_keys(self::$components); diff --git a/src/json/CustomiesBlock.php b/src/json/CustomiesBlock.php index 54b16d03..13799bfd 100644 --- a/src/json/CustomiesBlock.php +++ b/src/json/CustomiesBlock.php @@ -15,22 +15,45 @@ class CustomiesBlock extends Block implements BlockComponents { use BlockComponentsTrait; - public function __construct(array $components) { - foreach($components as $name => $data) { - $component = BlockComponentRegistry::fromJson($name, $data); - if($component !== null) { - $this->addComponent($component); - } - } + /** + * Constructs a new CustomiesBlock with the given components. + * + * Each component is provided as an associative array where the key is the + * component identifier and the value is JSON-decoded data. + * + * Components are automatically instantiated via BlockComponentRegistry and added + * to this block if they are registered. + * + * @param array $components Associative array of component identifiers to data + * @param float|null $hardness The hardness of the block (default 1.0) + * @param int|null $toolType The required tool type to break the block (default BlockToolType::NONE) + * @param int|null $toolHarvestLevel The required tool harvest level (default 0) + * @param float|null $blastResistance Optional blast resistance + */ + public function __construct( + array $components, + ?float $hardness = 1.0, + ?int $toolType = BlockToolType::NONE, + ?int $toolHarvestLevel = 0, + ?float $blastResistance = null + ) { + // Construct the base Block with identifier and block info parent::__construct( new BlockIdentifier(BlockTypeIds::newId()), "Custom Block", new BlockTypeInfo(new BlockBreakInfo( - $hardness ?? 1.0, - $toolType ?? BlockToolType::NONE, - $toolHarvestLevel ?? 0, - $blastResistance ?? null + $hardness, + $toolType, + $toolHarvestLevel, + $blastResistance )) ); + // Add all registered components + foreach($components as $name => $data) { + $component = BlockComponentRegistry::fromJson($name, $data); + if($component !== null) { + $this->addComponent($component); + } + } } } \ No newline at end of file diff --git a/src/json/CustomiesItem.php b/src/json/CustomiesItem.php index e79f0dbc..0d07590f 100644 --- a/src/json/CustomiesItem.php +++ b/src/json/CustomiesItem.php @@ -12,8 +12,21 @@ class CustomiesItem extends Item implements ItemComponents { use ItemComponentsTrait; + /** + * Constructs a new CustomiesItem with the given components. + * + * Each component is provided as an associative array where the key is the + * component identifier and the value is JSON-decoded data. + * + * Components are automatically instantiated via ItemComponentRegistry and added + * to this item if they are registered. + * + * @param array $components Associative array of component identifiers to data + */ public function __construct(array $components) { + // Create a new item with a unique identifier parent::__construct(new ItemIdentifier(ItemTypeIds::newId())); + // Add all registered components foreach($components as $name => $data) { $component = ItemComponentRegistry::fromJson($name, $data); if($component !== null) { diff --git a/src/json/ItemComponentRegistry.php b/src/json/ItemComponentRegistry.php index 56f19ade..86b5aecc 100644 --- a/src/json/ItemComponentRegistry.php +++ b/src/json/ItemComponentRegistry.php @@ -4,7 +4,6 @@ namespace customiesdevs\customies\json; use customiesdevs\customies\item\component\AllowOffHandComponent; -use customiesdevs\customies\item\component\BlockPlacerComponent; use customiesdevs\customies\item\component\BundleInteractionComponent; use customiesdevs\customies\item\component\CanDestroyInCreativeComponent; use customiesdevs\customies\item\component\CompostableComponent; @@ -26,8 +25,10 @@ use customiesdevs\customies\item\component\IconComponent; use customiesdevs\customies\item\component\InteractButtonComponent; use customiesdevs\customies\item\component\ItemComponent; +use customiesdevs\customies\item\component\KineticWeaponComponent; use customiesdevs\customies\item\component\LiquidClippedComponent; use customiesdevs\customies\item\component\MaxStackSizeComponent; +use customiesdevs\customies\item\component\PiercingWeaponComponent; use customiesdevs\customies\item\component\ProjectileComponent; use customiesdevs\customies\item\component\RarityComponent; use customiesdevs\customies\item\component\RecordComponent; @@ -39,6 +40,7 @@ use customiesdevs\customies\item\component\StorageWeightLimitComponent; use customiesdevs\customies\item\component\StorageWeightModifierComponent; use customiesdevs\customies\item\component\SwingDurationComponent; +use customiesdevs\customies\item\component\SwingSoundsComponent; use customiesdevs\customies\item\component\TagsComponent; use customiesdevs\customies\item\component\ThrowableComponent; use customiesdevs\customies\item\component\UseAnimationComponent; @@ -51,10 +53,12 @@ */ final class ItemComponentRegistry { - /** @var array> */ + /** + * Maps component identifiers to their implementing class names. + * @var array> + */ private static array $components = [ 'minecraft:allow_off_hand' => AllowOffHandComponent::class, - 'minecraft:block_placer' => BlockPlacerComponent::class, 'minecraft:bundle_interaction' => BundleInteractionComponent::class, 'minecraft:can_destroy_in_creative' => CanDestroyInCreativeComponent::class, 'minecraft:compostable' => CompostableComponent::class, @@ -63,8 +67,8 @@ final class ItemComponentRegistry { 'minecraft:damage' => DamageComponent::class, 'minecraft:digger' => DiggerComponent::class, 'minecraft:display_name' => DisplayNameComponent::class, - 'minecraft:durability' => DurabilityComponent::class, 'minecraft:durability_sensor' => DurabilitySensorComponent::class, + 'minecraft:durability' => DurabilityComponent::class, 'minecraft:dyeable' => DyeableComponent::class, 'minecraft:enchantable' => EnchantableComponent::class, 'minecraft:fire_resistant' => FireResistantComponent::class, @@ -75,8 +79,10 @@ final class ItemComponentRegistry { 'minecraft:hover_text_color' => HoverTextColorComponent::class, 'minecraft:icon' => IconComponent::class, 'minecraft:interact_button' => InteractButtonComponent::class, + 'minecraft:kinetic_weapon' => KineticWeaponComponent::class, 'minecraft:liquid_clipped' => LiquidClippedComponent::class, 'minecraft:max_stack_size' => MaxStackSizeComponent::class, + 'minecraft:piercing_weapon' => PiercingWeaponComponent::class, 'minecraft:projectile' => ProjectileComponent::class, 'minecraft:rarity' => RarityComponent::class, 'minecraft:record' => RecordComponent::class, @@ -88,6 +94,7 @@ final class ItemComponentRegistry { 'minecraft:storage_weight_limit' => StorageWeightLimitComponent::class, 'minecraft:storage_weight_modifier' => StorageWeightModifierComponent::class, 'minecraft:swing_duration' => SwingDurationComponent::class, + 'minecraft:swing_sounds' => SwingSoundsComponent::class, 'minecraft:tags' => TagsComponent::class, 'minecraft:throwable' => ThrowableComponent::class, 'minecraft:use_animation' => UseAnimationComponent::class, @@ -96,24 +103,30 @@ final class ItemComponentRegistry { ]; /** - * Register a custom component class. - * @param string $name The component identifier (e.g., 'yourplugin:custom_effect') - * @param class-string $class The component class + * Register a new custom item component. + * + * @param string $name Component identifier (e.g., 'yourplugin:custom_effect') + * @param class-string $class Fully qualified class name implementing ItemComponent */ public static function register(string $name, string $class): void { self::$components[$name] = $class; } /** - * Get a component class by its identifier. - * @return class-string|null + * Retrieve a component class by its identifier. + * + * @param string $name Component identifier + * @return class-string|null Returns the class name or null if not registered */ public static function get(string $name): ?string { return self::$components[$name] ?? null; } /** - * Check if a component is registered. + * Check whether a component is registered. + * + * @param string $name Component identifier + * @return bool True if the component is registered, false otherwise */ public static function has(string $name): bool { return isset(self::$components[$name]); @@ -121,21 +134,23 @@ public static function has(string $name): bool { /** * Create a component instance from JSON data. - * @return ItemComponent|null Returns null if component is not registered + * + * @param string $name Component identifier + * @param mixed $data JSON-decoded data for the component + * @return ItemComponent|null Returns the component instance or null if the component is not registered */ public static function fromJson(string $name, mixed $data): ?ItemComponent { $class = self::get($name); - if($class === null) { - return null; - } + if($class === null) return null; return $class::fromJson($data); } /** - * Get all registered component identifiers. - * @return string[] + * Get a list of all registered component identifiers. + * + * @return string[] List of component IDs */ public static function getAll(): array { return array_keys(self::$components); } -} +} \ No newline at end of file diff --git a/src/task/AsyncRegisterBlocksTask.php b/src/task/AsyncRegisterBlocksTask.php index 1600d837..5b37d822 100644 --- a/src/task/AsyncRegisterBlocksTask.php +++ b/src/task/AsyncRegisterBlocksTask.php @@ -9,6 +9,7 @@ use pocketmine\data\bedrock\block\convert\BlockStateReader; use pocketmine\data\bedrock\block\convert\BlockStateWriter; use pocketmine\scheduler\AsyncTask; +use Closure; final class AsyncRegisterBlocksTask extends AsyncTask { @@ -17,14 +18,21 @@ final class AsyncRegisterBlocksTask extends AsyncTask { private ThreadSafeArray $deserializer; /** - * @param Closure[] $blockFuncs - * @phpstan-param array $blockFuncs + * Constructor. + * @param Closure[] $blockFuncs Array of callbacks used for block registration + * @phpstan-param array $blockFuncs Associative array where the key is the block identifier and the value is a triple of: + * - block creation function + * - serializer function + * - deserializer function */ - public function __construct(private string $cachePath, array $blockFuncs) { + public function __construct(array $blockFuncs) { $this->blockFuncs = new ThreadSafeArray(); $this->serializer = new ThreadSafeArray(); $this->deserializer = new ThreadSafeArray(); - foreach($blockFuncs as $identifier => [$blockFunc, $serializer, $deserializer]){ $this->blockFuncs[$identifier] = $blockFunc; $this->serializer[$identifier] = $serializer; @@ -34,9 +42,14 @@ public function __construct(private string $cachePath, array $blockFuncs) { public function onRun(): void { foreach($this->blockFuncs as $identifier => $blockFunc){ - // We do not care about the model or creative inventory data in other threads since it is unused outside of + // We do not care about the creative inventory data in other threads since it is unused outside of // the main thread. - CustomiesBlockFactory::getInstance()->registerBlock($blockFunc, $identifier, serializer: $this->serializer[$identifier], deserializer: $this->deserializer[$identifier]); + CustomiesBlockFactory::getInstance()->registerBlock( + $blockFunc, + (string) $identifier, + serializer: $this->serializer[$identifier], + deserializer: $this->deserializer[$identifier] + ); } } -} +} \ No newline at end of file diff --git a/src/util/NBT.php b/src/util/NBT.php index 0ef9cadc..9a04bd59 100644 --- a/src/util/NBT.php +++ b/src/util/NBT.php @@ -23,7 +23,18 @@ class NBT { /** - * Attempts to return the correct Tag for the provided type. + * Attempts to return the correct NBT Tag for the provided PHP value. + * Supported conversions: + * - array → ListTag or CompoundTag + * - bool → ByteTag + * - float → FloatTag + * - int → IntTag + * - string → StringTag + * - CompoundTag → Returned as-is + * + * @param mixed $type The value to convert into an NBT Tag + * @return Tag|null Returns the corresponding Tag instance, or null if the + * type cannot be converted. */ public static function getTagType($type): ?Tag { return match (true) { @@ -38,7 +49,11 @@ public static function getTagType($type): ?Tag { } /** - * Creates a Tag that is either a ListTag or CompoundTag based on the data types of the keys in the provided array. + * Creates an NBT Tag from an array. + * - If the array uses sequential numeric keys (0..n), a ListTag is created. + * - Otherwise, a CompoundTag is created with each key mapped to a Tag. + * @param array $array The array to convert into an NBT Tag + * @return Tag Returns either a ListTag or CompoundTag depending on the array structure */ private static function getArrayTag(array $array): Tag { if(array_keys($array) === range(0, count($array) - 1)) { From 56b5d97e7fbb47c01f8f853845c9d7271630c8d9 Mon Sep 17 00:00:00 2001 From: HydroGames-dev Date: Mon, 15 Dec 2025 14:47:31 +0530 Subject: [PATCH 17/51] phpstan fix --- src/block/CustomiesBlockFactory.php | 4 ++-- src/item/component/DurabilitySensorComponent.php | 4 ++-- src/json/CustomiesBlock.php | 12 ++++++------ 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/src/block/CustomiesBlockFactory.php b/src/block/CustomiesBlockFactory.php index 784800aa..cfb15a0a 100644 --- a/src/block/CustomiesBlockFactory.php +++ b/src/block/CustomiesBlockFactory.php @@ -252,8 +252,8 @@ private function createBlockNBT(Block $block, ?CreativeInventoryInfo $creativeIn } if($creativeInfo !== null) { $propertiesTag->setTag("menu_category", CompoundTag::create() - ->setString("category", $creativeInfo->getCategory() ?? "") - ->setString("group", $creativeInfo->getGroup() ?? "")) + ->setString("category", $creativeInfo->getCategory()) + ->setString("group", $creativeInfo->getGroup())) ->setByte("is_hidden_in_commands", 0); } $propertiesTag diff --git a/src/item/component/DurabilitySensorComponent.php b/src/item/component/DurabilitySensorComponent.php index 87593acf..ff866328 100644 --- a/src/item/component/DurabilitySensorComponent.php +++ b/src/item/component/DurabilitySensorComponent.php @@ -12,8 +12,8 @@ final class DurabilitySensorComponent implements ItemComponent { /** * Enables an item to emit effects when it receives damage. Because of this, the item also needs a `minecraft:durability` component. - * @param array $durabilityThresholds - */ + * @param array $durabilityThresholds + */ public function __construct(array $durabilityThresholds = []) { $this->durabilityThresholds = $durabilityThresholds; } diff --git a/src/json/CustomiesBlock.php b/src/json/CustomiesBlock.php index 13799bfd..16861b63 100644 --- a/src/json/CustomiesBlock.php +++ b/src/json/CustomiesBlock.php @@ -25,16 +25,16 @@ class CustomiesBlock extends Block implements BlockComponents { * to this block if they are registered. * * @param array $components Associative array of component identifiers to data - * @param float|null $hardness The hardness of the block (default 1.0) - * @param int|null $toolType The required tool type to break the block (default BlockToolType::NONE) - * @param int|null $toolHarvestLevel The required tool harvest level (default 0) + * @param float $hardness The hardness of the block (default 1.0) + * @param int $toolType The required tool type to break the block (default BlockToolType::NONE) + * @param int $toolHarvestLevel The required tool harvest level (default 0) * @param float|null $blastResistance Optional blast resistance */ public function __construct( array $components, - ?float $hardness = 1.0, - ?int $toolType = BlockToolType::NONE, - ?int $toolHarvestLevel = 0, + float $hardness = 1.0, + int $toolType = BlockToolType::NONE, + int $toolHarvestLevel = 0, ?float $blastResistance = null ) { // Construct the base Block with identifier and block info From f94fa9aa101207b476736e7872040b830ad2a8c4 Mon Sep 17 00:00:00 2001 From: JeanTKG <102908437+jeantkg@users.noreply.github.com> Date: Mon, 15 Dec 2025 21:46:46 +0300 Subject: [PATCH 18/51] Refactor: Clean up code by removing unused components and simplifying class usage --- plugin.yml | 1 - src/Customies.php | 17 +---- src/CustomiesListener.php | 1 - src/block/CustomiesBlockFactory.php | 8 +-- src/block/component/ItemVisualComponent.php | 4 +- .../component/MaterialInstancesComponent.php | 4 +- src/block/component/MovableComponent.php | 68 ------------------- src/block/properties/Material.php | 46 +++---------- src/json/BlockComponentRegistry.php | 1 - 9 files changed, 20 insertions(+), 130 deletions(-) delete mode 100644 src/block/component/MovableComponent.php diff --git a/plugin.yml b/plugin.yml index 239a3439..16b72fbf 100644 --- a/plugin.yml +++ b/plugin.yml @@ -9,7 +9,6 @@ api: 5.37.0 authors: - DenielWorld - TwistedAsylumMC - contributors: - JackNoordhuis - Unickorn diff --git a/src/Customies.php b/src/Customies.php index 494241c4..c7707613 100644 --- a/src/Customies.php +++ b/src/Customies.php @@ -10,30 +10,19 @@ use pocketmine\utils\SingletonTrait; final class Customies extends PluginBase { - use SingletonTrait { - setInstance as private; - reset as private; - } + use SingletonTrait; public function onLoad(): void{ self::setInstance($this); } - /** - * Called when the plugin is enabled. - * - * Registers event listeners, loads and registers all behavior definitions, - * and schedules initialization hooks for custom blocks after the server - * has fully started. - */ protected function onEnable(): void { $this->getServer()->getPluginManager()->registerEvents(new CustomiesListener(), $this); // Register all custom behavior JSON definitions BehaviorManager::getInstance()->registerAll(); $this->getScheduler()->scheduleDelayedTask(new ClosureTask(static function (): void { - // This task is scheduled with a 0-tick delay so it runs as soon as the server has started. - // Plugins should register their custom blocks and entities in onEnable() - // before this is executed. + // This task is scheduled with a 0-tick delay so it runs as soon as the server has started. Plugins should + // register their custom blocks and entities in onEnable() before this is executed CustomiesBlockFactory::getInstance()->addWorkerInitHook(); }), 0); } diff --git a/src/CustomiesListener.php b/src/CustomiesListener.php index 4c801fac..44c4ba92 100644 --- a/src/CustomiesListener.php +++ b/src/CustomiesListener.php @@ -13,7 +13,6 @@ use function count; final class CustomiesListener implements Listener { - /** @var BlockPaletteEntry[] */ private array $cachedBlockPalette = []; private Experiments $experiments; diff --git a/src/block/CustomiesBlockFactory.php b/src/block/CustomiesBlockFactory.php index cfb15a0a..64bb731e 100644 --- a/src/block/CustomiesBlockFactory.php +++ b/src/block/CustomiesBlockFactory.php @@ -38,10 +38,7 @@ use function usort; final class CustomiesBlockFactory { - use SingletonTrait { - setInstance as private; - reset as private; - } + use SingletonTrait; /** * @var Closure[] @@ -185,7 +182,7 @@ public function registerBlock( $serializer ??= static fn() => new BlockStateWriter($identifier); $deserializer ??= static fn(BlockStateReader $in) => $block; } - + GlobalBlockStateHandlers::getSerializer()->map($block, $serializer); GlobalBlockStateHandlers::getDeserializer()->map($identifier, $deserializer); @@ -259,7 +256,6 @@ private function createBlockNBT(Block $block, ?CreativeInventoryInfo $creativeIn $propertiesTag ->setTag("components", $components) ->setInt("molangVersion", 13); - \var_dump($propertiesTag->__toString()); return $propertiesTag; } return CompoundTag::create(); diff --git a/src/block/component/ItemVisualComponent.php b/src/block/component/ItemVisualComponent.php index 43940ae2..f2e7f00b 100644 --- a/src/block/component/ItemVisualComponent.php +++ b/src/block/component/ItemVisualComponent.php @@ -2,6 +2,8 @@ namespace customiesdevs\customies\block\component; +use customiesdevs\customies\block\properties\Material; + class ItemVisualComponent implements BlockComponent { private GeometryComponent $geometry; @@ -19,7 +21,7 @@ public function getName(): string { public function getValue(): array { return [ "geometryDescription" => $this->geometry->getValue(), - "materialInstancesDescription" => $this->materialInstances->getValue() + "materialInstancesDescription" => $this->materialInstances->getValue(Material::FLAG_FACE_DIMMING) ]; } diff --git a/src/block/component/MaterialInstancesComponent.php b/src/block/component/MaterialInstancesComponent.php index afc9c6fe..b6aa0767 100644 --- a/src/block/component/MaterialInstancesComponent.php +++ b/src/block/component/MaterialInstancesComponent.php @@ -25,10 +25,10 @@ public function getName(): string { return 'minecraft:material_instances'; } - public function getValue(): array { + public function getValue(int $packedBools = Material::FLAG_TEXTURE_VARIATION): array { $materials = []; foreach($this->materials as $material){ - $materials[$material->getTarget()] = $material->toArray(); + $materials[$material->getTarget()] = $material->toArray($packedBools); } return [ "mappings" => [], diff --git a/src/block/component/MovableComponent.php b/src/block/component/MovableComponent.php deleted file mode 100644 index 567e730a..00000000 --- a/src/block/component/MovableComponent.php +++ /dev/null @@ -1,68 +0,0 @@ -movementType = $movementType; - $this->sticky = $sticky; - } - - public function getName(): string { - return 'minecraft:movable'; - } - - public function getValue(): array { - return [ - "movement_type" => $this->movementType, - "sticky" => $this->sticky - ]; - } - - public static function fromJson(mixed $data): static { - return new self( - $data["movement_type"] ?? self::MOVEMENT_TYPE_PUSH_PULL, - $data["sticky"] ?? self::STICKY_NONE - ); - } -} \ No newline at end of file diff --git a/src/block/properties/Material.php b/src/block/properties/Material.php index 8f8eb23c..8833355a 100644 --- a/src/block/properties/Material.php +++ b/src/block/properties/Material.php @@ -16,7 +16,7 @@ final class Material { /* packed_bools bit flags (byte) */ /** Enables directional face shading */ - public const FLAG_FACE_DIMMING = 0x01; + public const FLAG_FACE_DIMMING = 0x01; /** Enables randomized UV rotation per face */ public const FLAG_RANDOM_UV_ROTATION = 0x02; /** Enables texture variation support */ @@ -43,23 +43,13 @@ final class Material { * Tint logic applied to the texture (biome-based, none, etc). * @param float $ambientOcclusion * Ambient occlusion strength. Typically `1.0`. - * @param int $packedBools - * Bitmask controlling material behavior. - * Supported flags: - * - {@see self::FLAG_FACE_DIMMING} - * - {@see self::FLAG_RANDOM_UV_ROTATION} - * - {@see self::FLAG_TEXTURE_VARIATION} - * @param bool $alphaMaskedTint - * Whether the tint should only apply to alpha-masked pixels. */ public function __construct( private readonly string $target, private readonly string $texture, private readonly RenderMethod $renderMethod = RenderMethod::OPAQUE, private readonly TintMethod $tintMethod = TintMethod::NONE, - private readonly float $ambientOcclusion = 1.0, - private readonly int $packedBools = self::FLAG_FACE_DIMMING, - private readonly bool $alphaMaskedTint = false, + private readonly float $ambientOcclusion = 1.0 ) {} /** @@ -78,55 +68,39 @@ public function getTarget(): string { * render_method?: string, * tint_method?: string, * ambient_occlusion?: float|int, - * packed_bools?: int, - * face_dimming?: bool|int, - * isotropic?: bool|int, - * alpha_masked_tint?: bool|int * } $data */ public static function fromArray(string $target, array $data): self { - $packedBools = 0; - if(isset($data["packed_bools"])){ - $packedBools = (int) $data["packed_bools"]; - }else{ - if(!empty($data["face_dimming"])){ - $packedBools |= self::FLAG_FACE_DIMMING; - } - if(!empty($data["isotropic"])){ - $packedBools |= self::FLAG_RANDOM_UV_ROTATION; - } - } return new self( $target, $data["texture"], RenderMethod::tryFrom($data["render_method"] ?? "") ?? RenderMethod::OPAQUE, TintMethod::tryFrom($data["tint_method"] ?? "") ?? TintMethod::NONE, - (float) ($data["ambient_occlusion"] ?? 1.0), - $packedBools, - (bool) ($data["alpha_masked_tint"] ?? false) + (float) ($data["ambient_occlusion"] ?? 1.0) ); } /** * Converts the material into Bedrock-compatible NBT/JSON format. - * + * @param int $packedBools Context-specific packed_bools value (1=item_visual, 4=permutations, 5=material_instances) * @return array{ * texture: string, * render_method: string, * tint_method: string, * ambient_occlusion: float, - * packed_bools: int, - * alpha_masked_tint: bool * } */ - public function toArray(): array { + public function toArray(int $packedBools = self::FLAG_TEXTURE_VARIATION): array { return [ "texture" => $this->texture, "render_method" => $this->renderMethod->value, "tint_method" => $this->tintMethod->value, "ambient_occlusion" => $this->ambientOcclusion, - "packed_bools" => $this->packedBools, - "alpha_masked_tint" => $this->alphaMaskedTint + "packed_bools" => $packedBools, + // Fixed values + "alpha_masked_tint" => false, + "face_dimming" => true, + "isotropic" => false, ]; } } \ No newline at end of file diff --git a/src/json/BlockComponentRegistry.php b/src/json/BlockComponentRegistry.php index 259db274..edc3b72d 100644 --- a/src/json/BlockComponentRegistry.php +++ b/src/json/BlockComponentRegistry.php @@ -49,7 +49,6 @@ final class BlockComponentRegistry { 'minecraft:liquid_detection' => LiquidDetectionComponent::class, 'minecraft:map_color' => MapColorComponent::class, 'minecraft:material_instances' => MaterialInstancesComponent::class, - 'minecraft:movable' => MovableComponent::class, 'minecraft:placement_filter' => PlacementFilterComponent::class, 'minecraft:random_offset' => RandomOffsetComponent::class, 'minecraft:selection_box' => SelectionBoxComponent::class, From 57c86365a4ba0b4145bdf75029afae08237b7720 Mon Sep 17 00:00:00 2001 From: JeanTKG <102908437+jeantkg@users.noreply.github.com> Date: Tue, 16 Dec 2025 19:38:26 +0300 Subject: [PATCH 19/51] a better solution --- .../component/EmbeddedVisualComponent.php | 37 +++++++++++++++---- src/block/component/ItemVisualComponent.php | 36 ++++++++++++++---- .../component/MaterialInstancesComponent.php | 7 +++- src/block/properties/Material.php | 8 +--- 4 files changed, 63 insertions(+), 25 deletions(-) diff --git a/src/block/component/EmbeddedVisualComponent.php b/src/block/component/EmbeddedVisualComponent.php index 07e4784f..f4aad8eb 100644 --- a/src/block/component/EmbeddedVisualComponent.php +++ b/src/block/component/EmbeddedVisualComponent.php @@ -2,14 +2,22 @@ namespace customiesdevs\customies\block\component; -class EmbeddedVisualComponent implements BlockComponent { +use customiesdevs\customies\block\properties\Material; - private GeometryComponent $geometry; - private MaterialInstancesComponent $materialInstances; +class EmbeddedVisualComponent implements BlockComponent { - public function __construct(GeometryComponent $geometry, MaterialInstancesComponent $materialInstances) { - $this->geometry = $geometry; - $this->materialInstances = $materialInstances; + public function __construct( + private readonly GeometryComponent $geometry, + private readonly array $materials + ) { + if(count($materials) === 0){ + throw new \InvalidArgumentException("At least one material must be defined"); + } + foreach($materials as $material){ + if(!$material instanceof Material){ + throw new \InvalidArgumentException("All materials must be instances of ".Material::class); + } + } } public function getName(): string { @@ -17,16 +25,29 @@ public function getName(): string { } public function getValue(): array { + $materials = []; + foreach($this->materials as $material){ + $materials[$material->getTarget()] = [ + "alpha_masked_tint" => false, + "face_dimming" => true, + "isotropic" => false, + ...$material->toArray() + ]; + } return [ "geometry" => $this->geometry->getValue(), - "material_instances" => $this->materialInstances->getValue() + "material_instances" => $materials ]; } public static function fromJson(mixed $data): static { + $materials = []; + foreach($data as $target => $materialData){ + $materials[] = Material::fromArray($target, $materialData); + } return new self( GeometryComponent::fromJson($data["geometry"] ?? []), - MaterialInstancesComponent::fromJson($data["material_instances"] ?? []) + $materials ); } } \ No newline at end of file diff --git a/src/block/component/ItemVisualComponent.php b/src/block/component/ItemVisualComponent.php index f2e7f00b..cf20daab 100644 --- a/src/block/component/ItemVisualComponent.php +++ b/src/block/component/ItemVisualComponent.php @@ -6,12 +6,18 @@ class ItemVisualComponent implements BlockComponent { - private GeometryComponent $geometry; - private MaterialInstancesComponent $materialInstances; - - public function __construct(GeometryComponent $geometry, MaterialInstancesComponent $materialInstances) { - $this->geometry = $geometry; - $this->materialInstances = $materialInstances; + public function __construct( + private readonly GeometryComponent $geometry, + private readonly array $materials + ) { + if(count($materials) === 0){ + throw new \InvalidArgumentException("At least one material must be defined"); + } + foreach($materials as $material){ + if(!$material instanceof Material){ + throw new \InvalidArgumentException("All materials must be instances of ".Material::class); + } + } } public function getName(): string { @@ -19,16 +25,30 @@ public function getName(): string { } public function getValue(): array { + $materials = []; + foreach($this->materials as $material){ + $materials[$material->getTarget()] = [ + "packed_bools" => Material::FLAG_FACE_DIMMING, + ...$material->toArray() + ]; + } return [ "geometryDescription" => $this->geometry->getValue(), - "materialInstancesDescription" => $this->materialInstances->getValue(Material::FLAG_FACE_DIMMING) + "materialInstancesDescription" => [ + "mappings" => [], + "materials" => $materials + ] ]; } public static function fromJson(mixed $data): static { + $materials = []; + foreach($data as $target => $materialData){ + $materials[] = Material::fromArray($target, $materialData); + } return new self( GeometryComponent::fromJson($data["geometry"] ?? []), - MaterialInstancesComponent::fromJson($data["material_instances"] ?? []) + $materials ); } } \ No newline at end of file diff --git a/src/block/component/MaterialInstancesComponent.php b/src/block/component/MaterialInstancesComponent.php index b6aa0767..bad4dc89 100644 --- a/src/block/component/MaterialInstancesComponent.php +++ b/src/block/component/MaterialInstancesComponent.php @@ -25,10 +25,13 @@ public function getName(): string { return 'minecraft:material_instances'; } - public function getValue(int $packedBools = Material::FLAG_TEXTURE_VARIATION): array { + public function getValue(): array { $materials = []; foreach($this->materials as $material){ - $materials[$material->getTarget()] = $material->toArray($packedBools); + $materials[$material->getTarget()] = [ + "packed_bools" => Material::FLAG_TEXTURE_VARIATION, + ...$material->toArray() + ]; } return [ "mappings" => [], diff --git a/src/block/properties/Material.php b/src/block/properties/Material.php index 8833355a..ce2db19b 100644 --- a/src/block/properties/Material.php +++ b/src/block/properties/Material.php @@ -82,7 +82,6 @@ public static function fromArray(string $target, array $data): self { /** * Converts the material into Bedrock-compatible NBT/JSON format. - * @param int $packedBools Context-specific packed_bools value (1=item_visual, 4=permutations, 5=material_instances) * @return array{ * texture: string, * render_method: string, @@ -90,17 +89,12 @@ public static function fromArray(string $target, array $data): self { * ambient_occlusion: float, * } */ - public function toArray(int $packedBools = self::FLAG_TEXTURE_VARIATION): array { + public function toArray(): array { return [ "texture" => $this->texture, "render_method" => $this->renderMethod->value, "tint_method" => $this->tintMethod->value, "ambient_occlusion" => $this->ambientOcclusion, - "packed_bools" => $packedBools, - // Fixed values - "alpha_masked_tint" => false, - "face_dimming" => true, - "isotropic" => false, ]; } } \ No newline at end of file From c032ab2dec61243102f4d3abc7a346f10df08efd Mon Sep 17 00:00:00 2001 From: JeanTKG <102908437+jeantkg@users.noreply.github.com> Date: Tue, 16 Dec 2025 20:04:38 +0300 Subject: [PATCH 20/51] Fix: Update FlowerPottableComponent name and simplify SingletonTrait usage in multiple classes --- src/block/component/FlowerPottableComponent.php | 2 +- src/entity/CustomiesEntityFactory.php | 5 +---- src/item/CustomiesItemFactory.php | 5 +---- src/json/BehaviorManager.php | 5 +---- src/json/BlockComponentRegistry.php | 1 - 5 files changed, 4 insertions(+), 14 deletions(-) diff --git a/src/block/component/FlowerPottableComponent.php b/src/block/component/FlowerPottableComponent.php index 54eda779..792fadbe 100644 --- a/src/block/component/FlowerPottableComponent.php +++ b/src/block/component/FlowerPottableComponent.php @@ -7,7 +7,7 @@ class FlowerPottableComponent implements BlockComponent { public function __construct() {} public function getName(): string { - return 'minecraft:embedded_visual'; + return 'minecraft:flower_pottable'; } public function getValue(): array { diff --git a/src/entity/CustomiesEntityFactory.php b/src/entity/CustomiesEntityFactory.php index 09bfbb8c..4a8015c5 100644 --- a/src/entity/CustomiesEntityFactory.php +++ b/src/entity/CustomiesEntityFactory.php @@ -17,10 +17,7 @@ use ReflectionClass; class CustomiesEntityFactory { - use SingletonTrait { - setInstance as private; - reset as private; - } + use SingletonTrait; /** * Register an entity to the EntityFactory and all the required mappings. An optional behaviour identifier can be diff --git a/src/item/CustomiesItemFactory.php b/src/item/CustomiesItemFactory.php index a70e4b85..19d7729b 100644 --- a/src/item/CustomiesItemFactory.php +++ b/src/item/CustomiesItemFactory.php @@ -27,10 +27,7 @@ use function array_values; final class CustomiesItemFactory { - use SingletonTrait { - setInstance as private; - reset as private; - } + use SingletonTrait; /** Default values for item_properties */ private const PROPERTY_DEFAULTS = [ diff --git a/src/json/BehaviorManager.php b/src/json/BehaviorManager.php index 0b281d4c..af6bd619 100644 --- a/src/json/BehaviorManager.php +++ b/src/json/BehaviorManager.php @@ -24,10 +24,7 @@ * and register items and blocks with their components. */ final class BehaviorManager { - use SingletonTrait { - setInstance as private; - reset as private; - } + use SingletonTrait; /** @var string Path to the behavior folder */ private string $behaviorDirectory; diff --git a/src/json/BlockComponentRegistry.php b/src/json/BlockComponentRegistry.php index edc3b72d..34658fc9 100644 --- a/src/json/BlockComponentRegistry.php +++ b/src/json/BlockComponentRegistry.php @@ -18,7 +18,6 @@ use customiesdevs\customies\block\component\LiquidDetectionComponent; use customiesdevs\customies\block\component\MapColorComponent; use customiesdevs\customies\block\component\MaterialInstancesComponent; -use customiesdevs\customies\block\component\MovableComponent; use customiesdevs\customies\block\component\PlacementFilterComponent; use customiesdevs\customies\block\component\RandomOffsetComponent; use customiesdevs\customies\block\component\SelectionBoxComponent; From ad9e6b72e3e4c51bffac0bb82ba110bc19abb4df Mon Sep 17 00:00:00 2001 From: JeanTKG <102908437+jeantkg@users.noreply.github.com> Date: Tue, 16 Dec 2025 20:25:35 +0300 Subject: [PATCH 21/51] Refactor: Enhance DefaultTrait to initialize components with texture and name parameters --- src/block/traits/DefaultTrait.php | 21 +++++++++++++++++---- src/item/traits/DefaultTrait.php | 12 ++++++++---- 2 files changed, 25 insertions(+), 8 deletions(-) diff --git a/src/block/traits/DefaultTrait.php b/src/block/traits/DefaultTrait.php index e9818690..e5f181f4 100644 --- a/src/block/traits/DefaultTrait.php +++ b/src/block/traits/DefaultTrait.php @@ -2,10 +2,23 @@ namespace customiesdevs\customies\block\traits; +use customiesdevs\customies\block\component\DisplayNameComponent; +use customiesdevs\customies\block\component\GeometryComponent; +use customiesdevs\customies\block\component\MaterialInstancesComponent; +use customiesdevs\customies\block\properties\Material; + trait DefaultTrait { - use BlockComponentsTrait; + use BlockComponentsTrait; - protected function initComponent(): void { - // Default items do not have any components for now. - } + /** + * Initializes the default components for a block with the given texture and name. + * @param string $texture + * @param string $name + * @return void + */ + protected function initComponent(string $texture, string $name): void { + $this->addComponent(new GeometryComponent()); + $this->addComponent(new MaterialInstancesComponent([new Material("*", $texture)])); + $this->addComponent(new DisplayNameComponent($name)); + } } \ No newline at end of file diff --git a/src/item/traits/DefaultTrait.php b/src/item/traits/DefaultTrait.php index 7082c26e..c0625e03 100644 --- a/src/item/traits/DefaultTrait.php +++ b/src/item/traits/DefaultTrait.php @@ -2,11 +2,15 @@ namespace customiesdevs\customies\item\traits; +use customiesdevs\customies\item\component\DisplayNameComponent; +use customiesdevs\customies\item\component\IconComponent; + trait DefaultTrait { - use ItemComponentsTrait; + use ItemComponentsTrait; - protected function initComponent(): void { - // Default items do not have any components for now. - } + protected function initComponent(string $texture, string $name): void { + $this->addComponent(new IconComponent($texture)); + $this->addComponent(new DisplayNameComponent($name)); + } } \ No newline at end of file From 00acd28389604ea2110e433217d752a009be0b91 Mon Sep 17 00:00:00 2001 From: HydroGames-dev Date: Wed, 17 Dec 2025 11:08:06 +0530 Subject: [PATCH 22/51] temp fix untill jean checks this --- src/block/CustomiesBlockFactory.php | 42 +++++++++-------------- src/block/component/GeometryComponent.php | 4 +-- src/item/CustomiesItemFactory.php | 12 +++++-- 3 files changed, 29 insertions(+), 29 deletions(-) diff --git a/src/block/CustomiesBlockFactory.php b/src/block/CustomiesBlockFactory.php index 64bb731e..770fa649 100644 --- a/src/block/CustomiesBlockFactory.php +++ b/src/block/CustomiesBlockFactory.php @@ -128,11 +128,7 @@ public function registerBlock( CustomiesItemFactory::getInstance()->registerBlockItem($identifier, $block); $this->customBlocks[$identifier] = $block; - $propertiesTag = CompoundTag::create(); - $components = CompoundTag::create(); - $nbt = $this->createBlockNBT($block, $creativeInfo); - // TODO if($block instanceof Permutable) { $blockPropertyNames = $blockPropertyValues = $blockProperties = []; @@ -142,10 +138,15 @@ public function registerBlock( $blockProperties[] = $blockProperty->toNBT(); } $permutations = array_map(static fn(Permutation $permutation) => $permutation->toNBT(), $block->getPermutations()); + $componentsTag = $nbt->getTag("components"); + if(!$componentsTag instanceof CompoundTag){ + $componentsTag = CompoundTag::create(); + $nbt->setTag("components", $componentsTag); + } // The 'minecraft:on_player_placing' component is required for the client to predict block placement, making // it a smoother experience for the end-user. - $components->setTag("minecraft:on_player_placing", CompoundTag::create()); - $propertiesTag + $componentsTag->setTag("minecraft:on_player_placing", CompoundTag::create()); + $nbt ->setTag("permutations", new ListTag($permutations)) ->setTag("properties", new ListTag(array_reverse($blockProperties))); // fix client-side order @@ -228,17 +229,9 @@ public function registerBlock( } } - /** - * Creates the NBT data for the block. This includes the components and their values. - * If the block does not have components, an empty CompoundTag is returned. - * @param Block $block The block to create the NBT data for. - * @param CreativeInventoryInfo|null $creativeInfo Optional creative inventory information for the block. - * @return CompoundTag The NBT data for the block. - */ private function createBlockNBT(Block $block, ?CreativeInventoryInfo $creativeInfo): CompoundTag { $propertiesTag = CompoundTag::create(); $components = CompoundTag::create(); - if($block instanceof BlockComponents) { foreach ($block->getComponents() as $component) { $tag = NBT::getTagType($component->getValue()); @@ -247,17 +240,16 @@ private function createBlockNBT(Block $block, ?CreativeInventoryInfo $creativeIn } $components->setTag($component->getName(), $tag); } - if($creativeInfo !== null) { - $propertiesTag->setTag("menu_category", CompoundTag::create() - ->setString("category", $creativeInfo->getCategory()) - ->setString("group", $creativeInfo->getGroup())) - ->setByte("is_hidden_in_commands", 0); - } - $propertiesTag - ->setTag("components", $components) - ->setInt("molangVersion", 13); - return $propertiesTag; } - return CompoundTag::create(); + if($creativeInfo !== null) { + $propertiesTag->setTag("menu_category", CompoundTag::create() + ->setString("category", $creativeInfo->getCategory()) + ->setString("group", $creativeInfo->getGroup())) + ->setByte("is_hidden_in_commands", 0); + } + $propertiesTag + ->setTag("components", $components) + ->setInt("molangVersion", 13); + return $propertiesTag; } } \ No newline at end of file diff --git a/src/block/component/GeometryComponent.php b/src/block/component/GeometryComponent.php index 8836ca78..6cd8866d 100644 --- a/src/block/component/GeometryComponent.php +++ b/src/block/component/GeometryComponent.php @@ -22,7 +22,7 @@ public function __construct( string $identifier = "minecraft:geometry.full_block", array $boneVisibility = [], string $culling = "", - string $cullingLayer = "minecraft:culling_layer.undefined", + string $cullingLayer = "", array|bool $uvLock = false ) { $this->identifier = $identifier; @@ -55,7 +55,7 @@ public static function fromJson(mixed $data): static { $data["identifier"] ?? "minecraft:geometry.full_block", $data["bone_visibility"] ?? [], $data["culling"] ?? "", - $data["culling_layer"] ?? "minecraft:culling_layer.undefined", + $data["culling_layer"] ?? "", $data["uv_lock"] ?? false ); } diff --git a/src/item/CustomiesItemFactory.php b/src/item/CustomiesItemFactory.php index 19d7729b..82a04beb 100644 --- a/src/item/CustomiesItemFactory.php +++ b/src/item/CustomiesItemFactory.php @@ -4,6 +4,7 @@ namespace customiesdevs\customies\item; use Closure; +use customiesdevs\customies\Customies; use InvalidArgumentException; use pocketmine\block\Block; use pocketmine\data\bedrock\item\BlockItemIdMap; @@ -204,7 +205,14 @@ private function createItemNbt(Item $item, string $identifier, int $itemId, ?Cre ->setTag('item_tags', NBT::getTagType($tags)) ->merge($componentsTag); - \var_dump($components->__toString()); + + $debugFile = Customies::getInstance()->getDataFolder() . "debug_nbt.txt"; + file_put_contents( + $debugFile, + "==== ITEM DEBUG ====" . PHP_EOL . + (string) $components . PHP_EOL . PHP_EOL, + FILE_APPEND + ); return CompoundTag::create() ->setTag('components', $components) ->setInt('id', $itemId) @@ -259,4 +267,4 @@ public function registerBlockItem(string $identifier, Block $block): void { $value = $itemToBlockId->getValue($blockItemIdMap); $itemToBlockId->setValue($blockItemIdMap, $value + [$identifier => $identifier]); } -} +} \ No newline at end of file From 239d54e360c20a417937e53ab6c282172150ac68 Mon Sep 17 00:00:00 2001 From: HydroGames-dev Date: Wed, 17 Dec 2025 12:07:16 +0530 Subject: [PATCH 23/51] update CustomiesBlockFactory.php --- src/block/CustomiesBlockFactory.php | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/src/block/CustomiesBlockFactory.php b/src/block/CustomiesBlockFactory.php index 770fa649..2575b1df 100644 --- a/src/block/CustomiesBlockFactory.php +++ b/src/block/CustomiesBlockFactory.php @@ -139,15 +139,10 @@ public function registerBlock( } $permutations = array_map(static fn(Permutation $permutation) => $permutation->toNBT(), $block->getPermutations()); $componentsTag = $nbt->getTag("components"); - if(!$componentsTag instanceof CompoundTag){ - $componentsTag = CompoundTag::create(); - $nbt->setTag("components", $componentsTag); - } // The 'minecraft:on_player_placing' component is required for the client to predict block placement, making // it a smoother experience for the end-user. $componentsTag->setTag("minecraft:on_player_placing", CompoundTag::create()); - $nbt - ->setTag("permutations", new ListTag($permutations)) + $nbt->setTag("permutations", new ListTag($permutations)) ->setTag("properties", new ListTag(array_reverse($blockProperties))); // fix client-side order foreach(Permutations::getCartesianProduct($blockPropertyValues) as $meta => $permutations){ @@ -247,9 +242,8 @@ private function createBlockNBT(Block $block, ?CreativeInventoryInfo $creativeIn ->setString("group", $creativeInfo->getGroup())) ->setByte("is_hidden_in_commands", 0); } - $propertiesTag - ->setTag("components", $components) - ->setInt("molangVersion", 13); + $propertiesTag->setTag("components", $components)->setInt("molangVersion", 13); + return $propertiesTag; } } \ No newline at end of file From abf72e813dea56cf858813d80c67430f7e867608 Mon Sep 17 00:00:00 2001 From: HydroGames-dev Date: Wed, 17 Dec 2025 12:12:26 +0530 Subject: [PATCH 24/51] update CustomiesBlockFactory.php --- src/block/CustomiesBlockFactory.php | 55 ++++++++++++++--------------- 1 file changed, 26 insertions(+), 29 deletions(-) diff --git a/src/block/CustomiesBlockFactory.php b/src/block/CustomiesBlockFactory.php index 2575b1df..aa40e13f 100644 --- a/src/block/CustomiesBlockFactory.php +++ b/src/block/CustomiesBlockFactory.php @@ -128,7 +128,27 @@ public function registerBlock( CustomiesItemFactory::getInstance()->registerBlockItem($identifier, $block); $this->customBlocks[$identifier] = $block; - $nbt = $this->createBlockNBT($block, $creativeInfo); + $propertiesTag = CompoundTag::create(); + $components = CompoundTag::create(); + + if($block instanceof BlockComponents) { + foreach ($block->getComponents() as $component) { + $tag = NBT::getTagType($component->getValue()); + if($tag === null) { + throw new RuntimeException("Failed to get tag type for component " . $component->getName()); + } + $components->setTag($component->getName(), $tag); + } + } + if($creativeInfo !== null) { + $propertiesTag->setTag("menu_category", CompoundTag::create() + ->setString("category", $creativeInfo->getCategory()) + ->setString("group", $creativeInfo->getGroup())) + ->setByte("is_hidden_in_commands", 0); + } + $propertiesTag->setTag("components", $components) + ->setInt("molangVersion", 13); + // TODO if($block instanceof Permutable) { $blockPropertyNames = $blockPropertyValues = $blockProperties = []; @@ -138,12 +158,12 @@ public function registerBlock( $blockProperties[] = $blockProperty->toNBT(); } $permutations = array_map(static fn(Permutation $permutation) => $permutation->toNBT(), $block->getPermutations()); - $componentsTag = $nbt->getTag("components"); + // The 'minecraft:on_player_placing' component is required for the client to predict block placement, making // it a smoother experience for the end-user. - $componentsTag->setTag("minecraft:on_player_placing", CompoundTag::create()); - $nbt->setTag("permutations", new ListTag($permutations)) - ->setTag("properties", new ListTag(array_reverse($blockProperties))); // fix client-side order + $components->setTag("minecraft:on_player_placing", CompoundTag::create()); + $propertiesTag->setTag("permutations", new ListTag($permutations)); + $propertiesTag->setTag("properties", new ListTag(array_reverse($blockProperties))); // fix client-side order foreach(Permutations::getCartesianProduct($blockPropertyValues) as $meta => $permutations){ // We need to insert states for every possible permutation to allow for all blocks to be used and to @@ -208,7 +228,7 @@ public function registerBlock( CreativeInventory::getInstance()->add($block->asItem(), $category, $group); } - $this->blockPaletteEntries[] = new BlockPaletteEntry($identifier, new CacheableNbt($nbt)); + $this->blockPaletteEntries[] = new BlockPaletteEntry($identifier, new CacheableNbt($propertiesTag)); $this->blockFuncs[$identifier] = [$blockFunc, $serializer, $deserializer]; // 1.20.60 added a new "block_id" field which depends on the order of the block palette entries. Every time we @@ -223,27 +243,4 @@ public function registerBlock( $this->blockPaletteEntries[$i] = new BlockPaletteEntry($entry->getName(), new CacheableNbt($root)); } } - - private function createBlockNBT(Block $block, ?CreativeInventoryInfo $creativeInfo): CompoundTag { - $propertiesTag = CompoundTag::create(); - $components = CompoundTag::create(); - if($block instanceof BlockComponents) { - foreach ($block->getComponents() as $component) { - $tag = NBT::getTagType($component->getValue()); - if($tag === null) { - throw new RuntimeException("Failed to get tag type for component " . $component->getName()); - } - $components->setTag($component->getName(), $tag); - } - } - if($creativeInfo !== null) { - $propertiesTag->setTag("menu_category", CompoundTag::create() - ->setString("category", $creativeInfo->getCategory()) - ->setString("group", $creativeInfo->getGroup())) - ->setByte("is_hidden_in_commands", 0); - } - $propertiesTag->setTag("components", $components)->setInt("molangVersion", 13); - - return $propertiesTag; - } } \ No newline at end of file From 6044cb3ec3088c3f314e32319d273e666a5a4adb Mon Sep 17 00:00:00 2001 From: HydroGames-dev Date: Wed, 17 Dec 2025 13:45:50 +0530 Subject: [PATCH 25/51] Remove Debugging is_hidden_in_commands went in the wrong list --- src/block/CustomiesBlockFactory.php | 4 ++-- src/item/CustomiesItemFactory.php | 9 --------- 2 files changed, 2 insertions(+), 11 deletions(-) diff --git a/src/block/CustomiesBlockFactory.php b/src/block/CustomiesBlockFactory.php index aa40e13f..c72fefc0 100644 --- a/src/block/CustomiesBlockFactory.php +++ b/src/block/CustomiesBlockFactory.php @@ -143,8 +143,8 @@ public function registerBlock( if($creativeInfo !== null) { $propertiesTag->setTag("menu_category", CompoundTag::create() ->setString("category", $creativeInfo->getCategory()) - ->setString("group", $creativeInfo->getGroup())) - ->setByte("is_hidden_in_commands", 0); + ->setString("group", $creativeInfo->getGroup()) + ->setByte("is_hidden_in_commands", 0)); } $propertiesTag->setTag("components", $components) ->setInt("molangVersion", 13); diff --git a/src/item/CustomiesItemFactory.php b/src/item/CustomiesItemFactory.php index 82a04beb..21addb01 100644 --- a/src/item/CustomiesItemFactory.php +++ b/src/item/CustomiesItemFactory.php @@ -4,7 +4,6 @@ namespace customiesdevs\customies\item; use Closure; -use customiesdevs\customies\Customies; use InvalidArgumentException; use pocketmine\block\Block; use pocketmine\data\bedrock\item\BlockItemIdMap; @@ -205,14 +204,6 @@ private function createItemNbt(Item $item, string $identifier, int $itemId, ?Cre ->setTag('item_tags', NBT::getTagType($tags)) ->merge($componentsTag); - - $debugFile = Customies::getInstance()->getDataFolder() . "debug_nbt.txt"; - file_put_contents( - $debugFile, - "==== ITEM DEBUG ====" . PHP_EOL . - (string) $components . PHP_EOL . PHP_EOL, - FILE_APPEND - ); return CompoundTag::create() ->setTag('components', $components) ->setInt('id', $itemId) From d2826b0006c9657f3f3c436ec0cd52284d7f357c Mon Sep 17 00:00:00 2001 From: HydroGames-dev Date: Wed, 17 Dec 2025 14:01:33 +0530 Subject: [PATCH 26/51] Works somehow --- src/block/CustomiesBlockFactory.php | 11 +++++------ src/block/component/GeometryComponent.php | 4 ++-- src/block/component/MaterialInstancesComponent.php | 2 +- src/block/properties/Material.php | 4 +++- 4 files changed, 11 insertions(+), 10 deletions(-) diff --git a/src/block/CustomiesBlockFactory.php b/src/block/CustomiesBlockFactory.php index c72fefc0..8671fdf7 100644 --- a/src/block/CustomiesBlockFactory.php +++ b/src/block/CustomiesBlockFactory.php @@ -146,10 +146,12 @@ public function registerBlock( ->setString("group", $creativeInfo->getGroup()) ->setByte("is_hidden_in_commands", 0)); } - $propertiesTag->setTag("components", $components) - ->setInt("molangVersion", 13); + // The 'minecraft:on_player_placing' component is required for the client to predict block placement, making + // it a smoother experience for the end-user. + $components->setTag("minecraft:on_player_placing", CompoundTag::create()); + $propertiesTag->setTag("components", $components); + $propertiesTag->setInt("molangVersion", 13); - // TODO if($block instanceof Permutable) { $blockPropertyNames = $blockPropertyValues = $blockProperties = []; foreach($block->getBlockProperties() as $blockProperty){ @@ -159,9 +161,6 @@ public function registerBlock( } $permutations = array_map(static fn(Permutation $permutation) => $permutation->toNBT(), $block->getPermutations()); - // The 'minecraft:on_player_placing' component is required for the client to predict block placement, making - // it a smoother experience for the end-user. - $components->setTag("minecraft:on_player_placing", CompoundTag::create()); $propertiesTag->setTag("permutations", new ListTag($permutations)); $propertiesTag->setTag("properties", new ListTag(array_reverse($blockProperties))); // fix client-side order diff --git a/src/block/component/GeometryComponent.php b/src/block/component/GeometryComponent.php index 6cd8866d..81e91dcc 100644 --- a/src/block/component/GeometryComponent.php +++ b/src/block/component/GeometryComponent.php @@ -22,7 +22,7 @@ public function __construct( string $identifier = "minecraft:geometry.full_block", array $boneVisibility = [], string $culling = "", - string $cullingLayer = "", + string $cullingLayer = "minecraft:culling_layer.undefined", array|bool $uvLock = false ) { $this->identifier = $identifier; @@ -54,7 +54,7 @@ public static function fromJson(mixed $data): static { return new self( $data["identifier"] ?? "minecraft:geometry.full_block", $data["bone_visibility"] ?? [], - $data["culling"] ?? "", + $data["culling"] ?? "minecraft:culling_layer.undefined", $data["culling_layer"] ?? "", $data["uv_lock"] ?? false ); diff --git a/src/block/component/MaterialInstancesComponent.php b/src/block/component/MaterialInstancesComponent.php index bad4dc89..fe4e446e 100644 --- a/src/block/component/MaterialInstancesComponent.php +++ b/src/block/component/MaterialInstancesComponent.php @@ -29,7 +29,7 @@ public function getValue(): array { $materials = []; foreach($this->materials as $material){ $materials[$material->getTarget()] = [ - "packed_bools" => Material::FLAG_TEXTURE_VARIATION, + "packed_bools" => Material::FLAG_UNKNOWN, ...$material->toArray() ]; } diff --git a/src/block/properties/Material.php b/src/block/properties/Material.php index ce2db19b..3c6e9f6a 100644 --- a/src/block/properties/Material.php +++ b/src/block/properties/Material.php @@ -20,7 +20,9 @@ final class Material { /** Enables randomized UV rotation per face */ public const FLAG_RANDOM_UV_ROTATION = 0x02; /** Enables texture variation support */ - public const FLAG_TEXTURE_VARIATION = 0x04; + public const FLAG_TEXTURE_VARIATION = 0x04; + /* Unknown to us */ + public const FLAG_UNKNOWN = 0x05; /** * @param string $target From f1094a74e07b0453117ee669e3967b5973f4dca3 Mon Sep 17 00:00:00 2001 From: JeanTKG <102908437+jeantkg@users.noreply.github.com> Date: Wed, 17 Dec 2025 13:58:54 +0300 Subject: [PATCH 27/51] Refactor block components to use CompoundTag for packed_bools and improve permutation handling --- src/block/CustomiesBlockFactory.php | 3 +++ src/block/component/ItemVisualComponent.php | 3 ++- src/block/component/MaterialInstancesComponent.php | 5 +++-- src/block/permutations/Permutation.php | 7 +++++++ src/block/properties/Material.php | 10 ---------- 5 files changed, 15 insertions(+), 13 deletions(-) diff --git a/src/block/CustomiesBlockFactory.php b/src/block/CustomiesBlockFactory.php index 8671fdf7..bc78cbbb 100644 --- a/src/block/CustomiesBlockFactory.php +++ b/src/block/CustomiesBlockFactory.php @@ -146,12 +146,15 @@ public function registerBlock( ->setString("group", $creativeInfo->getGroup()) ->setByte("is_hidden_in_commands", 0)); } + // The 'minecraft:on_player_placing' component is required for the client to predict block placement, making // it a smoother experience for the end-user. + // Is this even used anymore?????? $components->setTag("minecraft:on_player_placing", CompoundTag::create()); $propertiesTag->setTag("components", $components); $propertiesTag->setInt("molangVersion", 13); + // TODO refactor this mess if($block instanceof Permutable) { $blockPropertyNames = $blockPropertyValues = $blockProperties = []; foreach($block->getBlockProperties() as $blockProperty){ diff --git a/src/block/component/ItemVisualComponent.php b/src/block/component/ItemVisualComponent.php index cf20daab..b8a48484 100644 --- a/src/block/component/ItemVisualComponent.php +++ b/src/block/component/ItemVisualComponent.php @@ -3,6 +3,7 @@ namespace customiesdevs\customies\block\component; use customiesdevs\customies\block\properties\Material; +use pocketmine\nbt\tag\CompoundTag; class ItemVisualComponent implements BlockComponent { @@ -28,7 +29,7 @@ public function getValue(): array { $materials = []; foreach($this->materials as $material){ $materials[$material->getTarget()] = [ - "packed_bools" => Material::FLAG_FACE_DIMMING, + CompoundTag::create()->setByte("packed_bools", 1), ...$material->toArray() ]; } diff --git a/src/block/component/MaterialInstancesComponent.php b/src/block/component/MaterialInstancesComponent.php index fe4e446e..9ca66f87 100644 --- a/src/block/component/MaterialInstancesComponent.php +++ b/src/block/component/MaterialInstancesComponent.php @@ -3,6 +3,7 @@ namespace customiesdevs\customies\block\component; use customiesdevs\customies\block\properties\Material; +use pocketmine\nbt\tag\CompoundTag; class MaterialInstancesComponent implements BlockComponent { @@ -25,11 +26,11 @@ public function getName(): string { return 'minecraft:material_instances'; } - public function getValue(): array { + public function getValue(int $packedBools = 5): array { $materials = []; foreach($this->materials as $material){ $materials[$material->getTarget()] = [ - "packed_bools" => Material::FLAG_UNKNOWN, + CompoundTag::create()->setByte("packed_bools", $packedBools), ...$material->toArray() ]; } diff --git a/src/block/permutations/Permutation.php b/src/block/permutations/Permutation.php index f9ff12a3..7f31ee2e 100644 --- a/src/block/permutations/Permutation.php +++ b/src/block/permutations/Permutation.php @@ -3,6 +3,8 @@ namespace customiesdevs\customies\block\permutations; +use customiesdevs\customies\block\component\BlockComponent; +use customiesdevs\customies\block\component\MaterialInstancesComponent; use customiesdevs\customies\util\NBT; use pocketmine\nbt\tag\CompoundTag; @@ -18,6 +20,11 @@ public function __construct(private readonly string $condition) { * Returns the permutation with the provided component added to the current list of components. */ public function withComponent(string $component, mixed $value) : self { + if ($value instanceof MaterialInstancesComponent) { + $value = $value->getValue(4); // Use packed_bools = 4 for permutations + } elseif ($value instanceof BlockComponent) { + $value = $value->getValue(); + } $this->components->setTag($component, NBT::getTagType($value)); return $this; } diff --git a/src/block/properties/Material.php b/src/block/properties/Material.php index 3c6e9f6a..45f57612 100644 --- a/src/block/properties/Material.php +++ b/src/block/properties/Material.php @@ -14,16 +14,6 @@ final class Material { public const TARGET_SOUTH = "south"; public const TARGET_WEST = "west"; - /* packed_bools bit flags (byte) */ - /** Enables directional face shading */ - public const FLAG_FACE_DIMMING = 0x01; - /** Enables randomized UV rotation per face */ - public const FLAG_RANDOM_UV_ROTATION = 0x02; - /** Enables texture variation support */ - public const FLAG_TEXTURE_VARIATION = 0x04; - /* Unknown to us */ - public const FLAG_UNKNOWN = 0x05; - /** * @param string $target * Targeted face for the material. From 05b67f8d02d83ea95a039e1d9a254d5bf50e395c Mon Sep 17 00:00:00 2001 From: JeanTKG <102908437+jeantkg@users.noreply.github.com> Date: Wed, 17 Dec 2025 23:21:10 +0300 Subject: [PATCH 28/51] Implement TransformationComponent and CardinalDirectionRotationTrait; refactor withComponent method in Permutation --- .../component/TransformationComponent.php | 80 ++++++++++++++++++ .../CardinalDirectionRotationTrait.php | 83 +++++++++++++++++++ src/block/permutations/Permutation.php | 12 ++- src/block/permutations/RotatableTrait.php | 1 + 4 files changed, 169 insertions(+), 7 deletions(-) create mode 100644 src/block/component/TransformationComponent.php create mode 100644 src/block/permutations/CardinalDirectionRotationTrait.php diff --git a/src/block/component/TransformationComponent.php b/src/block/component/TransformationComponent.php new file mode 100644 index 00000000..40fdc641 --- /dev/null +++ b/src/block/component/TransformationComponent.php @@ -0,0 +1,80 @@ +rotation->x) { + 0 => 0, + 90 => 1, + 180 => 2, + 270, -90 => 3, + default => 0 + }; + $ry = match ((int) $this->rotation->y) { + 0 => 0, + 90 => 1, + 180 => 2, + 270, -90 => 3, + default => 0 + }; + $rz = match ((int) $this->rotation->z) { + 0 => 0, + 90 => 1, + 180 => 2, + 270, -90 => 3, + default => 0 + }; + return [ + "RX" => $rx, + "RY" => $ry, + "RZ" => $rz, + "RXP" => $this->rotationPivot->x, + "RYP" => $this->rotationPivot->y, + "RZP" => $this->rotationPivot->z, + "SX" => $this->scale->x, + "SY" => $this->scale->y, + "SZ" => $this->scale->z, + "SXP" => $this->scalePivot->x, + "SYP" => $this->scalePivot->y, + "SZP" => $this->scalePivot->z, + "TX" => $this->translation->x, + "TY" => $this->translation->y, + "TZ" => $this->translation->z, + "hasJsonVersionBeforeValidation" => false + ]; + } + + public static function fromJson(mixed $data): static { + return new self( + $data['rotation'] ?? new Vector3(0, 0, 0), + $data['rotation_pivot'] ?? new Vector3(0, 0, 0), + $data['scale'] ?? new Vector3(1, 1, 1), + $data['scale_pivot'] ?? new Vector3(0, 0, 0), + $data['translation'] ?? new Vector3(0, 0, 0) + ); + } +} \ No newline at end of file diff --git a/src/block/permutations/CardinalDirectionRotationTrait.php b/src/block/permutations/CardinalDirectionRotationTrait.php new file mode 100644 index 00000000..0b0dff2c --- /dev/null +++ b/src/block/permutations/CardinalDirectionRotationTrait.php @@ -0,0 +1,83 @@ +withComponent(new TransformationComponent()), + (new Permutation("q.block_state('minecraft:cardinal_direction') == 'west'")) + ->withComponent(new TransformationComponent( + new Vector3(0, 90, 0) + )), + (new Permutation("q.block_state('minecraft:cardinal_direction') == 'south'")) + ->withComponent(new TransformationComponent( + new Vector3(0, 180, 0) + )), + (new Permutation("q.block_state('minecraft:cardinal_direction') == 'east'")) + ->withComponent(new TransformationComponent( + new Vector3(0, -90, 0) + )), + ]; + } + + public function getCurrentBlockProperties(): array { + return [$this->facing]; + } + + protected function writeStateToMeta(): int { + return Permutations::toMeta($this); + } + + public function readStateFromData(int $id, int $stateMeta): void { + $blockProperties = Permutations::fromMeta($this, $stateMeta); + $this->facing = $blockProperties[0] ?? Facing::NORTH; + } + + public function getStateBitmask(): int { + return Permutations::getStateBitmask($this); + } + + public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, ?Player $player = null): bool { + if($player !== null) { + $this->facing = $player->getHorizontalFacing(); + } + return parent::place($tx, $item, $blockReplace, $blockClicked, $face, $clickVector, $player); + } + + public function serializeState(BlockStateWriter $out): void { + $out->writeInt("customies:rotation", $this->facing); + } + + public function deserializeState(BlockStateReader $in): void { + $this->facing = $in->readInt("customies:rotation"); + } +} \ No newline at end of file diff --git a/src/block/permutations/Permutation.php b/src/block/permutations/Permutation.php index 7f31ee2e..e3cd2f9c 100644 --- a/src/block/permutations/Permutation.php +++ b/src/block/permutations/Permutation.php @@ -19,13 +19,11 @@ public function __construct(private readonly string $condition) { /** * Returns the permutation with the provided component added to the current list of components. */ - public function withComponent(string $component, mixed $value) : self { - if ($value instanceof MaterialInstancesComponent) { - $value = $value->getValue(4); // Use packed_bools = 4 for permutations - } elseif ($value instanceof BlockComponent) { - $value = $value->getValue(); - } - $this->components->setTag($component, NBT::getTagType($value)); + public function withComponent(BlockComponent $component) : self { + $value = ($component instanceof MaterialInstancesComponent) + ? $component->getValue(4) // Use packed_bools = 4 for permutations + : $component->getValue(); + $this->components->setTag($component->getName(), NBT::getTagType($value)); return $this; } diff --git a/src/block/permutations/RotatableTrait.php b/src/block/permutations/RotatableTrait.php index 05317778..e05bf4d4 100644 --- a/src/block/permutations/RotatableTrait.php +++ b/src/block/permutations/RotatableTrait.php @@ -26,6 +26,7 @@ public function getBlockProperties(): array { ]; } + // This will be removed soon /** * @return Permutation[] */ From cd1c89e7cfef046673d74ced6d9fff8264219e8a Mon Sep 17 00:00:00 2001 From: JeanTKG <102908437+jeantkg@users.noreply.github.com> Date: Fri, 19 Dec 2025 22:28:07 +0300 Subject: [PATCH 29/51] Refactor NBT handling to replace CompoundTag with ByteTag for packed_bools and improve error handling in getArrayTag method --- src/block/component/ItemVisualComponent.php | 4 ++-- .../component/MaterialInstancesComponent.php | 4 ++-- src/util/NBT.php | 19 +++++++++++++++---- 3 files changed, 19 insertions(+), 8 deletions(-) diff --git a/src/block/component/ItemVisualComponent.php b/src/block/component/ItemVisualComponent.php index b8a48484..bae1c7bb 100644 --- a/src/block/component/ItemVisualComponent.php +++ b/src/block/component/ItemVisualComponent.php @@ -3,7 +3,7 @@ namespace customiesdevs\customies\block\component; use customiesdevs\customies\block\properties\Material; -use pocketmine\nbt\tag\CompoundTag; +use pocketmine\nbt\tag\ByteTag; class ItemVisualComponent implements BlockComponent { @@ -29,7 +29,7 @@ public function getValue(): array { $materials = []; foreach($this->materials as $material){ $materials[$material->getTarget()] = [ - CompoundTag::create()->setByte("packed_bools", 1), + "packed_bools" => new ByteTag(1), ...$material->toArray() ]; } diff --git a/src/block/component/MaterialInstancesComponent.php b/src/block/component/MaterialInstancesComponent.php index 9ca66f87..29dbee07 100644 --- a/src/block/component/MaterialInstancesComponent.php +++ b/src/block/component/MaterialInstancesComponent.php @@ -3,7 +3,7 @@ namespace customiesdevs\customies\block\component; use customiesdevs\customies\block\properties\Material; -use pocketmine\nbt\tag\CompoundTag; +use pocketmine\nbt\tag\ByteTag; class MaterialInstancesComponent implements BlockComponent { @@ -30,7 +30,7 @@ public function getValue(int $packedBools = 5): array { $materials = []; foreach($this->materials as $material){ $materials[$material->getTarget()] = [ - CompoundTag::create()->setByte("packed_bools", $packedBools), + "packed_bools" => new ByteTag($packedBools), ...$material->toArray() ]; } diff --git a/src/util/NBT.php b/src/util/NBT.php index 9a04bd59..c2c0fd66 100644 --- a/src/util/NBT.php +++ b/src/util/NBT.php @@ -30,7 +30,7 @@ class NBT { * - float → FloatTag * - int → IntTag * - string → StringTag - * - CompoundTag → Returned as-is + * - Tag → Returned as-is * * @param mixed $type The value to convert into an NBT Tag * @return Tag|null Returns the corresponding Tag instance, or null if the @@ -38,12 +38,12 @@ class NBT { */ public static function getTagType($type): ?Tag { return match (true) { + $type instanceof Tag => $type, is_array($type) => self::getArrayTag($type), is_bool($type) => new ByteTag($type ? 1 : 0), is_float($type) => new FloatTag($type), is_int($type) => new IntTag($type), is_string($type) => new StringTag($type), - $type instanceof CompoundTag => $type, default => null, }; } @@ -54,14 +54,25 @@ public static function getTagType($type): ?Tag { * - Otherwise, a CompoundTag is created with each key mapped to a Tag. * @param array $array The array to convert into an NBT Tag * @return Tag Returns either a ListTag or CompoundTag depending on the array structure + * @throws \InvalidArgumentException If any value cannot be converted to a Tag */ private static function getArrayTag(array $array): Tag { if(array_keys($array) === range(0, count($array) - 1)) { - return new ListTag(array_map(fn($value) => self::getTagType($value), $array)); + return new ListTag(array_map(function($value) { + $tag = self::getTagType($value); + if($tag === null) { + throw new \InvalidArgumentException("Cannot convert value of type " . get_debug_type($value) . " to NBT Tag"); + } + return $tag; + }, $array)); } $tag = CompoundTag::create(); foreach($array as $key => $value){ - $tag->setTag($key, self::getTagType($value)); + $valueTag = self::getTagType($value); + if($valueTag === null) { + throw new \InvalidArgumentException("Cannot convert value of type " . get_debug_type($value) . " for key '$key' to NBT Tag"); + } + $tag->setTag((string) $key, $valueTag); } return $tag; } From 4ce3c746cf4645295b006188ce9a6ae38e408509 Mon Sep 17 00:00:00 2001 From: JeanTKG <102908437+jeantkg@users.noreply.github.com> Date: Fri, 19 Dec 2025 22:32:40 +0300 Subject: [PATCH 30/51] Refactor RotatableTrait to use TransformationComponent instead of CompoundTag for permutations --- .../CardinalDirectionRotationTrait.php | 83 ------------------- src/block/permutations/RotatableTrait.php | 78 ++--------------- 2 files changed, 5 insertions(+), 156 deletions(-) delete mode 100644 src/block/permutations/CardinalDirectionRotationTrait.php diff --git a/src/block/permutations/CardinalDirectionRotationTrait.php b/src/block/permutations/CardinalDirectionRotationTrait.php deleted file mode 100644 index 0b0dff2c..00000000 --- a/src/block/permutations/CardinalDirectionRotationTrait.php +++ /dev/null @@ -1,83 +0,0 @@ -withComponent(new TransformationComponent()), - (new Permutation("q.block_state('minecraft:cardinal_direction') == 'west'")) - ->withComponent(new TransformationComponent( - new Vector3(0, 90, 0) - )), - (new Permutation("q.block_state('minecraft:cardinal_direction') == 'south'")) - ->withComponent(new TransformationComponent( - new Vector3(0, 180, 0) - )), - (new Permutation("q.block_state('minecraft:cardinal_direction') == 'east'")) - ->withComponent(new TransformationComponent( - new Vector3(0, -90, 0) - )), - ]; - } - - public function getCurrentBlockProperties(): array { - return [$this->facing]; - } - - protected function writeStateToMeta(): int { - return Permutations::toMeta($this); - } - - public function readStateFromData(int $id, int $stateMeta): void { - $blockProperties = Permutations::fromMeta($this, $stateMeta); - $this->facing = $blockProperties[0] ?? Facing::NORTH; - } - - public function getStateBitmask(): int { - return Permutations::getStateBitmask($this); - } - - public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, ?Player $player = null): bool { - if($player !== null) { - $this->facing = $player->getHorizontalFacing(); - } - return parent::place($tx, $item, $blockReplace, $blockClicked, $face, $clickVector, $player); - } - - public function serializeState(BlockStateWriter $out): void { - $out->writeInt("customies:rotation", $this->facing); - } - - public function deserializeState(BlockStateReader $in): void { - $this->facing = $in->readInt("customies:rotation"); - } -} \ No newline at end of file diff --git a/src/block/permutations/RotatableTrait.php b/src/block/permutations/RotatableTrait.php index e05bf4d4..656809ba 100644 --- a/src/block/permutations/RotatableTrait.php +++ b/src/block/permutations/RotatableTrait.php @@ -3,6 +3,7 @@ namespace customiesdevs\customies\block\permutations; +use customiesdevs\customies\block\component\TransformationComponent; use pocketmine\block\Block; use pocketmine\block\utils\HorizontalFacingTrait; use pocketmine\data\bedrock\block\convert\BlockStateReader; @@ -10,7 +11,6 @@ use pocketmine\item\Item; use pocketmine\math\Facing; use pocketmine\math\Vector3; -use pocketmine\nbt\tag\CompoundTag; use pocketmine\player\Player; use pocketmine\world\BlockTransaction; @@ -33,81 +33,13 @@ public function getBlockProperties(): array { public function getPermutations(): array { return [ (new Permutation("q.block_state('customies:rotation') == 2")) - ->withComponent("minecraft:transformation", CompoundTag::create() - ->setInt("RX", 0) - ->setFloat("RXP", 0.0) - ->setInt("RY", 0) - ->setFloat("RYP", 0.0) - ->setInt("RZ", 0) - ->setFloat("RZP", 0.0) - ->setFloat("SX", 1.0) - ->setFloat("SXP", 0.0) - ->setFloat("SY", 1.0) - ->setFloat("SYP", 0.0) - ->setFloat("SZ", 1.0) - ->setFloat("SZP", 0.0) - ->setFloat("TX", 0.0) - ->setFloat("TY", 0.0) - ->setFloat("TZ", 0.0) - ->setByte("hasJsonVersionBeforeValidation", 0) - ), + ->withComponent(new TransformationComponent()), (new Permutation("q.block_state('customies:rotation') == 3")) - ->withComponent("minecraft:transformation", CompoundTag::create() - ->setInt("RX", 0) - ->setFloat("RXP", 0.0) - ->setInt("RY", 2) - ->setFloat("RYP", 0.0) - ->setInt("RZ", 0) - ->setFloat("RZP", 0.0) - ->setFloat("SX", 1.0) - ->setFloat("SXP", 0.0) - ->setFloat("SY", 1.0) - ->setFloat("SYP", 0.0) - ->setFloat("SZ", 1.0) - ->setFloat("SZP", 0.0) - ->setFloat("TX", 0.0) - ->setFloat("TY", 0.0) - ->setFloat("TZ", 0.0) - ->setByte("hasJsonVersionBeforeValidation", 0) - ), + ->withComponent(new TransformationComponent(new Vector3(0, 180, 0))), (new Permutation("q.block_state('customies:rotation') == 4")) - ->withComponent("minecraft:transformation", CompoundTag::create() - ->setInt("RX", 0) - ->setFloat("RXP", 0.0) - ->setInt("RY", 1) - ->setFloat("RYP", 0.0) - ->setInt("RZ", 0) - ->setFloat("RZP", 0.0) - ->setFloat("SX", 1.0) - ->setFloat("SXP", 0.0) - ->setFloat("SY", 1.0) - ->setFloat("SYP", 0.0) - ->setFloat("SZ", 1.0) - ->setFloat("SZP", 0.0) - ->setFloat("TX", 0.0) - ->setFloat("TY", 0.0) - ->setFloat("TZ", 0.0) - ->setByte("hasJsonVersionBeforeValidation", 0) - ), + ->withComponent(new TransformationComponent(new Vector3(0, 90, 0))), (new Permutation("q.block_state('customies:rotation') == 5")) - ->withComponent("minecraft:transformation", CompoundTag::create() - ->setInt("RX", 0) - ->setFloat("RXP", 0.0) - ->setInt("RY", 3) - ->setFloat("RYP", 0.0) - ->setInt("RZ", 0) - ->setFloat("RZP", 0.0) - ->setFloat("SX", 1.0) - ->setFloat("SXP", 0.0) - ->setFloat("SY", 1.0) - ->setFloat("SYP", 0.0) - ->setFloat("SZ", 1.0) - ->setFloat("SZP", 0.0) - ->setFloat("TX", 0.0) - ->setFloat("TY", 0.0) - ->setFloat("TZ", 0.0) - ->setByte("hasJsonVersionBeforeValidation", 0) - ), + ->withComponent(new TransformationComponent(new Vector3(0, -90, 0))), ]; } From 464143b11b97c23c5163212d3f7ac0a0fdaf1775 Mon Sep 17 00:00:00 2001 From: JeanTKG <102908437+jeantkg@users.noreply.github.com> Date: Sat, 20 Dec 2025 16:35:41 +0300 Subject: [PATCH 31/51] right way to do materials --- src/block/component/ItemVisualComponent.php | 4 +-- .../component/MaterialInstancesComponent.php | 8 ++---- src/block/properties/Material.php | 27 ++++++++++++++++--- 3 files changed, 28 insertions(+), 11 deletions(-) diff --git a/src/block/component/ItemVisualComponent.php b/src/block/component/ItemVisualComponent.php index bae1c7bb..b531d0aa 100644 --- a/src/block/component/ItemVisualComponent.php +++ b/src/block/component/ItemVisualComponent.php @@ -29,8 +29,8 @@ public function getValue(): array { $materials = []; foreach($this->materials as $material){ $materials[$material->getTarget()] = [ - "packed_bools" => new ByteTag(1), - ...$material->toArray() + ...$material->toArray(), + "packed_bools" => new ByteTag(Material::FACE_DIMMING) ]; } return [ diff --git a/src/block/component/MaterialInstancesComponent.php b/src/block/component/MaterialInstancesComponent.php index 29dbee07..afc9c6fe 100644 --- a/src/block/component/MaterialInstancesComponent.php +++ b/src/block/component/MaterialInstancesComponent.php @@ -3,7 +3,6 @@ namespace customiesdevs\customies\block\component; use customiesdevs\customies\block\properties\Material; -use pocketmine\nbt\tag\ByteTag; class MaterialInstancesComponent implements BlockComponent { @@ -26,13 +25,10 @@ public function getName(): string { return 'minecraft:material_instances'; } - public function getValue(int $packedBools = 5): array { + public function getValue(): array { $materials = []; foreach($this->materials as $material){ - $materials[$material->getTarget()] = [ - "packed_bools" => new ByteTag($packedBools), - ...$material->toArray() - ]; + $materials[$material->getTarget()] = $material->toArray(); } return [ "mappings" => [], diff --git a/src/block/properties/Material.php b/src/block/properties/Material.php index 45f57612..57639e79 100644 --- a/src/block/properties/Material.php +++ b/src/block/properties/Material.php @@ -3,6 +3,8 @@ namespace customiesdevs\customies\block\properties; +use pocketmine\nbt\tag\ByteTag; + final class Material { public const TARGET_ALL = "*"; @@ -14,6 +16,12 @@ final class Material { public const TARGET_SOUTH = "south"; public const TARGET_WEST = "west"; + public const FACE_DIMMING = 1; + public const RANDOMIZE_UV_ROTATION = 2; + public const SUPPORTS_TEXTURE_VARIATION = 4; + + private int $packed_bools; + /** * @param string $target * Targeted face for the material. @@ -35,14 +43,24 @@ final class Material { * Tint logic applied to the texture (biome-based, none, etc). * @param float $ambientOcclusion * Ambient occlusion strength. Typically `1.0`. + * @param bool $face_dimming + * Whether face dimming/shading is enabled for the block. + * @param bool $isotropic + * Whether randomized UV rotation is enabled for the block. */ public function __construct( private readonly string $target, private readonly string $texture, private readonly RenderMethod $renderMethod = RenderMethod::OPAQUE, private readonly TintMethod $tintMethod = TintMethod::NONE, - private readonly float $ambientOcclusion = 1.0 - ) {} + private readonly float $ambientOcclusion = 1.0, + private readonly bool $face_dimming = true, + private readonly bool $isotropic = false, + ) { + $this->packed_bools = self::SUPPORTS_TEXTURE_VARIATION + | ($face_dimming ? self::FACE_DIMMING : 0) + | ($isotropic ? self::RANDOMIZE_UV_ROTATION : 0); + } /** * Returns the targeted face for the material. @@ -68,7 +86,9 @@ public static function fromArray(string $target, array $data): self { $data["texture"], RenderMethod::tryFrom($data["render_method"] ?? "") ?? RenderMethod::OPAQUE, TintMethod::tryFrom($data["tint_method"] ?? "") ?? TintMethod::NONE, - (float) ($data["ambient_occlusion"] ?? 1.0) + (float) ($data["ambient_occlusion"] ?? 1.0), + $data["face_dimming"] ?? true, + $data["isotropic"] ?? false ); } @@ -87,6 +107,7 @@ public function toArray(): array { "render_method" => $this->renderMethod->value, "tint_method" => $this->tintMethod->value, "ambient_occlusion" => $this->ambientOcclusion, + "packed_bools" => new ByteTag($this->packed_bools) ]; } } \ No newline at end of file From 06e569f8ace9af1b937690e59b1828c5d7b8ad3a Mon Sep 17 00:00:00 2001 From: JeanTKG <102908437+jeantkg@users.noreply.github.com> Date: Thu, 25 Dec 2025 16:13:09 +0300 Subject: [PATCH 32/51] Implement new block traits and states system; refactor block component handling and NBT serialization --- src/block/CustomiesBlockFactory.php | 81 ++++++++++++++++--- src/block/component/CollisionBoxComponent.php | 2 +- src/block/permutations/Permutation.php | 2 +- src/test/permutation/BlockPermutation.php | 31 +++++++ src/test/permutation/BlockPermutations.php | 29 +++++++ .../permutation/BlockPermutationsTrait.php | 43 ++++++++++ src/test/state/BlockState.php | 41 ++++++++++ src/test/state/BlockStates.php | 15 ++++ src/test/state/BlockStatesTrait.php | 25 ++++++ src/test/state/BooleanState.php | 42 ++++++++++ src/test/state/IntRangeState.php | 47 +++++++++++ src/test/state/IntState.php | 46 +++++++++++ src/test/state/StringState.php | 46 +++++++++++ src/test/trait/BlockTrait.php | 10 +++ src/test/trait/BlockTraits.php | 15 ++++ src/test/trait/BlockTraitsTrait.php | 47 +++++++++++ src/test/trait/PlacementDirectionTrait.php | 28 +++++++ src/test/trait/PlacementPositionTrait.php | 24 ++++++ src/util/ByteArray.php | 35 ++++++++ src/util/NBT.php | 12 +++ 20 files changed, 610 insertions(+), 11 deletions(-) create mode 100644 src/test/permutation/BlockPermutation.php create mode 100644 src/test/permutation/BlockPermutations.php create mode 100644 src/test/permutation/BlockPermutationsTrait.php create mode 100644 src/test/state/BlockState.php create mode 100644 src/test/state/BlockStates.php create mode 100644 src/test/state/BlockStatesTrait.php create mode 100644 src/test/state/BooleanState.php create mode 100644 src/test/state/IntRangeState.php create mode 100644 src/test/state/IntState.php create mode 100644 src/test/state/StringState.php create mode 100644 src/test/trait/BlockTrait.php create mode 100644 src/test/trait/BlockTraits.php create mode 100644 src/test/trait/BlockTraitsTrait.php create mode 100644 src/test/trait/PlacementDirectionTrait.php create mode 100644 src/test/trait/PlacementPositionTrait.php create mode 100644 src/util/ByteArray.php diff --git a/src/block/CustomiesBlockFactory.php b/src/block/CustomiesBlockFactory.php index bc78cbbb..8e01320b 100644 --- a/src/block/CustomiesBlockFactory.php +++ b/src/block/CustomiesBlockFactory.php @@ -7,6 +7,10 @@ use customiesdevs\customies\block\permutations\Permutable; use customiesdevs\customies\block\permutations\Permutation; use customiesdevs\customies\block\permutations\Permutations; +use customiesdevs\customies\test\permutation\BlockPermutation as NewBlockPermutation; +use customiesdevs\customies\test\permutation\BlockPermutations; +use customiesdevs\customies\test\state\BlockStates; +use customiesdevs\customies\test\trait\BlockTraits; use customiesdevs\customies\item\CreativeInventoryInfo; use customiesdevs\customies\item\CustomiesItemFactory; use customiesdevs\customies\task\AsyncRegisterBlocksTask; @@ -128,7 +132,7 @@ public function registerBlock( CustomiesItemFactory::getInstance()->registerBlockItem($identifier, $block); $this->customBlocks[$identifier] = $block; - $propertiesTag = CompoundTag::create(); + $nbt = CompoundTag::create(); $components = CompoundTag::create(); if($block instanceof BlockComponents) { @@ -141,7 +145,7 @@ public function registerBlock( } } if($creativeInfo !== null) { - $propertiesTag->setTag("menu_category", CompoundTag::create() + $nbt->setTag("menu_category", CompoundTag::create() ->setString("category", $creativeInfo->getCategory()) ->setString("group", $creativeInfo->getGroup()) ->setByte("is_hidden_in_commands", 0)); @@ -151,11 +155,70 @@ public function registerBlock( // it a smoother experience for the end-user. // Is this even used anymore?????? $components->setTag("minecraft:on_player_placing", CompoundTag::create()); - $propertiesTag->setTag("components", $components); - $propertiesTag->setInt("molangVersion", 13); - // TODO refactor this mess - if($block instanceof Permutable) { + // New API: Block Traits + if($block instanceof BlockTraits) { + $traits = []; + foreach($block->getTraits() as $trait) { + $traits[] = $trait->getValue(); + } + $nbt->setTag("traits", NBT::getTagType($traits)); + } + + $nbt->setTag("components", $components); + $nbt->setInt("molangVersion", 13); + + // New API: BlockPermutations (independent of BlockStates - works with BlockTraits too) + if($block instanceof BlockPermutations) { + $permutations = array_map( + static fn(NewBlockPermutation $p) => NBT::getTagType($p->toArray()), + $block->getPermutations() + ); + $nbt->setTag("permutations", new ListTag($permutations)); + } + + // New API: BlockStates (custom state properties) + if($block instanceof BlockStates) { + $stateNames = $stateValues = $stateNBT = []; + foreach($block->getStates() as $state) { + $stateNames[] = $state->getName(); + $value = $state->getValue(); + // Extract enum values for Cartesian product + $stateValues[] = $value["enum"] ?? []; + $stateNBT[] = NBT::getTagType($value); + } + $nbt->setTag("properties", new ListTag(array_reverse($stateNBT))); // fix client-side order + + // Generate block palette entries for all state combinations + foreach(Permutations::getCartesianProduct($stateValues) as $meta => $combination) { + $states = CompoundTag::create(); + foreach($combination as $i => $value) { + $states->setTag($stateNames[$i], NBT::getTagType($value)); + } + $blockState = CompoundTag::create() + ->setString(BlockStateData::TAG_NAME, $identifier) + ->setTag(BlockStateData::TAG_STATES, $states); + BlockPalette::getInstance()->insertState($blockState, $meta); + } + + $serializer ??= static function (BlockStates $block) use ($identifier) : BlockStateWriter { + $writer = BlockStateWriter::create($identifier); + foreach($block->getStates() as $state) { + $state->serialize($writer); + } + return $writer; + }; + $deserializer ??= static function (BlockStateReader $in) use ($identifier) : BlockStates { + $b = CustomiesBlockFactory::getInstance()->get($identifier); + assert($b instanceof BlockStates); + foreach($b->getStates() as $state) { + $state->deserialize($in); + } + return $b; + }; + } + // Legacy API: Permutable (deprecated) + elseif($block instanceof Permutable) { $blockPropertyNames = $blockPropertyValues = $blockProperties = []; foreach($block->getBlockProperties() as $blockProperty){ $blockPropertyNames[] = $blockProperty->getName(); @@ -164,8 +227,8 @@ public function registerBlock( } $permutations = array_map(static fn(Permutation $permutation) => $permutation->toNBT(), $block->getPermutations()); - $propertiesTag->setTag("permutations", new ListTag($permutations)); - $propertiesTag->setTag("properties", new ListTag(array_reverse($blockProperties))); // fix client-side order + $nbt->setTag("permutations", new ListTag($permutations)); + $nbt->setTag("properties", new ListTag(array_reverse($blockProperties))); // fix client-side order foreach(Permutations::getCartesianProduct($blockPropertyValues) as $meta => $permutations){ // We need to insert states for every possible permutation to allow for all blocks to be used and to @@ -230,7 +293,7 @@ public function registerBlock( CreativeInventory::getInstance()->add($block->asItem(), $category, $group); } - $this->blockPaletteEntries[] = new BlockPaletteEntry($identifier, new CacheableNbt($propertiesTag)); + $this->blockPaletteEntries[] = new BlockPaletteEntry($identifier, new CacheableNbt($nbt)); $this->blockFuncs[$identifier] = [$blockFunc, $serializer, $deserializer]; // 1.20.60 added a new "block_id" field which depends on the order of the block palette entries. Every time we diff --git a/src/block/component/CollisionBoxComponent.php b/src/block/component/CollisionBoxComponent.php index 15bd10be..efcbf98f 100644 --- a/src/block/component/CollisionBoxComponent.php +++ b/src/block/component/CollisionBoxComponent.php @@ -54,7 +54,7 @@ public function getValue(): array { $convertedBoxes[] = $box->toNbtArray(); } return [ - "enabled" => $this->enabled ? 1 : 0, + "enabled" => $this->enabled, "boxes" => $convertedBoxes ]; } diff --git a/src/block/permutations/Permutation.php b/src/block/permutations/Permutation.php index e3cd2f9c..4df9a4aa 100644 --- a/src/block/permutations/Permutation.php +++ b/src/block/permutations/Permutation.php @@ -19,7 +19,7 @@ public function __construct(private readonly string $condition) { /** * Returns the permutation with the provided component added to the current list of components. */ - public function withComponent(BlockComponent $component) : self { + public function withComponent(BlockComponent $component): self { $value = ($component instanceof MaterialInstancesComponent) ? $component->getValue(4) // Use packed_bools = 4 for permutations : $component->getValue(); diff --git a/src/test/permutation/BlockPermutation.php b/src/test/permutation/BlockPermutation.php new file mode 100644 index 00000000..6f066a43 --- /dev/null +++ b/src/test/permutation/BlockPermutation.php @@ -0,0 +1,31 @@ +condition; + } + + public function getComponents(): BlockComponent { + return $this->components; + } + + public function toArray(): array { + return [ + "condition" => $this->condition, + "components" => [ + $this->components->getName() => $this->components->getValue() + ] + ]; + } +} diff --git a/src/test/permutation/BlockPermutations.php b/src/test/permutation/BlockPermutations.php new file mode 100644 index 00000000..cc5ddfc8 --- /dev/null +++ b/src/test/permutation/BlockPermutations.php @@ -0,0 +1,29 @@ +permutations[] = $permutation; + } + + /** + * Adds multiple permutations at once. + * @param BlockPermutation[] $permutations + */ + public function addPermutations(array $permutations): void { + foreach($permutations as $permutation) { + $this->addPermutation($permutation); + } + } + + /** + * Returns all registered block permutations. + * @return BlockPermutation[] + */ + public function getPermutations(): array { + return $this->permutations; + } +} diff --git a/src/test/state/BlockState.php b/src/test/state/BlockState.php new file mode 100644 index 00000000..1cd26eb7 --- /dev/null +++ b/src/test/state/BlockState.php @@ -0,0 +1,41 @@ +states[$state->getName()] = $state; + } + + public function hasState(string $name): bool { + return isset($this->states[$name]); + } + + public function getState(string $name): ?BlockState { + return $this->states[$name] ?? null; + } + + public function getStates(): array { + return $this->states; + } +} diff --git a/src/test/state/BooleanState.php b/src/test/state/BooleanState.php new file mode 100644 index 00000000..bfa133e9 --- /dev/null +++ b/src/test/state/BooleanState.php @@ -0,0 +1,42 @@ +name; + } + + public function getValue(): array { + return [ + "enum" => new ByteArray([false, true]), + "name" => $this->name + ]; + } + + public function getCurrentValue(): bool { + return $this->currentValue; + } + + public function setCurrentValue(mixed $value): void { + $this->currentValue = (bool) $value; + } + + public function serialize(BlockStateWriter $writer): void { + $writer->writeBool($this->name, $this->currentValue); + } + + public function deserialize(BlockStateReader $reader): void { + $this->currentValue = $reader->readBool($this->name); + } +} \ No newline at end of file diff --git a/src/test/state/IntRangeState.php b/src/test/state/IntRangeState.php new file mode 100644 index 00000000..e8ae4238 --- /dev/null +++ b/src/test/state/IntRangeState.php @@ -0,0 +1,47 @@ +currentValue = $min; + } + + public function getName(): string { + return $this->name; + } + + public function getValue(): array { + return [ + "enum" => range($this->min, $this->max), + "name" => $this->name + ]; + } + + public function getCurrentValue(): int { + return $this->currentValue; + } + + public function setCurrentValue(mixed $value): void { + $this->currentValue = max($this->min, min($this->max, (int) $value)); + } + + public function serialize(BlockStateWriter $writer): void { + $writer->writeInt($this->name, $this->currentValue); + } + + public function deserialize(BlockStateReader $reader): void { + $this->currentValue = $reader->readInt($this->name); + } +} \ No newline at end of file diff --git a/src/test/state/IntState.php b/src/test/state/IntState.php new file mode 100644 index 00000000..d0656720 --- /dev/null +++ b/src/test/state/IntState.php @@ -0,0 +1,46 @@ +currentValue = $values[0] ?? 0; + } + + public function getName(): string { + return $this->name; + } + + public function getValue(): array { + return [ + "enum" => $this->values, + "name" => $this->name + ]; + } + + public function getCurrentValue(): int { + return $this->currentValue; + } + + public function setCurrentValue(mixed $value): void { + $this->currentValue = (int) $value; + } + + public function serialize(BlockStateWriter $writer): void { + $writer->writeInt($this->name, $this->currentValue); + } + + public function deserialize(BlockStateReader $reader): void { + $this->currentValue = $reader->readInt($this->name); + } +} \ No newline at end of file diff --git a/src/test/state/StringState.php b/src/test/state/StringState.php new file mode 100644 index 00000000..9b08f6d2 --- /dev/null +++ b/src/test/state/StringState.php @@ -0,0 +1,46 @@ +currentValue = $values[0] ?? ''; + } + + public function getName(): string { + return $this->name; + } + + public function getValue(): array { + return [ + "enum" => $this->values, + "name" => $this->name + ]; + } + + public function getCurrentValue(): string { + return $this->currentValue; + } + + public function setCurrentValue(mixed $value): void { + $this->currentValue = (string) $value; + } + + public function serialize(BlockStateWriter $writer): void { + $writer->writeString($this->name, $this->currentValue); + } + + public function deserialize(BlockStateReader $reader): void { + $this->currentValue = $reader->readString($this->name); + } +} \ No newline at end of file diff --git a/src/test/trait/BlockTrait.php b/src/test/trait/BlockTrait.php new file mode 100644 index 00000000..c30e55e6 --- /dev/null +++ b/src/test/trait/BlockTrait.php @@ -0,0 +1,10 @@ + + */ + private array $traits = []; + + /** + * Adds or replaces a block trait. + */ + public function addTrait(BlockTrait $trait): void { + $this->traits[$trait->getName()] = $trait; + } + + /** + * Checks whether the block has a trait with the given name. + */ + public function hasTrait(string $name): bool { + return isset($this->traits[$name]); + } + + /** + * Retrieves a trait by its name. + */ + public function getTrait(string $name): ?BlockTrait { + return $this->traits[$name] ?? null; + } + + /** + * Returns all registered block traits. + * @return array + */ + public function getTraits(): array { + return $this->traits; + } +} diff --git a/src/test/trait/PlacementDirectionTrait.php b/src/test/trait/PlacementDirectionTrait.php new file mode 100644 index 00000000..b662c688 --- /dev/null +++ b/src/test/trait/PlacementDirectionTrait.php @@ -0,0 +1,28 @@ + [], + "enabled_states" => [ + "cardinal_direction" => in_array("minecraft:cardinal_direction", $this->state, true), + "corner_and_cardinal_direction" => in_array("minecraft:corner_and_cardinal_direction", $this->state, true), + "facing_direction" => in_array("minecraft:facing_direction", $this->state, true), + ], + "name" => $this->getName(), + "y_rotation_offset" => $this->yRotationOffset + ]; + } +} \ No newline at end of file diff --git a/src/test/trait/PlacementPositionTrait.php b/src/test/trait/PlacementPositionTrait.php new file mode 100644 index 00000000..428f00a2 --- /dev/null +++ b/src/test/trait/PlacementPositionTrait.php @@ -0,0 +1,24 @@ + [ + "block_face" => in_array("minecraft:block_face", $this->state, true), + "vertical_half" => in_array("minecraft:vertical_half", $this->state, true), + ], + "name" => $this->getName() + ]; + } +} \ No newline at end of file diff --git a/src/util/ByteArray.php b/src/util/ByteArray.php new file mode 100644 index 00000000..a3486579 --- /dev/null +++ b/src/util/ByteArray.php @@ -0,0 +1,35 @@ + new ByteArray([false, true]) // Outputs: [B; 0b, 1b] + * ``` + */ +final class ByteArray { + + /** @var int[] */ + private readonly array $values; + + /** + * @param array $values Array of booleans or integers (0-255) + */ + public function __construct(array $values) { + $this->values = array_map( + fn(bool|int $v) => is_bool($v) ? ($v ? 1 : 0) : $v, + $values + ); + } + + /** + * @return int[] + */ + public function getValues(): array { + return $this->values; + } +} diff --git a/src/util/NBT.php b/src/util/NBT.php index c2c0fd66..a23c0698 100644 --- a/src/util/NBT.php +++ b/src/util/NBT.php @@ -3,6 +3,7 @@ namespace customiesdevs\customies\util; +use pocketmine\nbt\tag\ByteArrayTag; use pocketmine\nbt\tag\ByteTag; use pocketmine\nbt\tag\CompoundTag; use pocketmine\nbt\tag\FloatTag; @@ -31,6 +32,7 @@ class NBT { * - int → IntTag * - string → StringTag * - Tag → Returned as-is + * - ByteArray → ListTag of ByteTag * * @param mixed $type The value to convert into an NBT Tag * @return Tag|null Returns the corresponding Tag instance, or null if the @@ -39,6 +41,7 @@ class NBT { public static function getTagType($type): ?Tag { return match (true) { $type instanceof Tag => $type, + $type instanceof ByteArray => self::getByteArrayTag($type), is_array($type) => self::getArrayTag($type), is_bool($type) => new ByteTag($type ? 1 : 0), is_float($type) => new FloatTag($type), @@ -48,6 +51,15 @@ public static function getTagType($type): ?Tag { }; } + /** + * Creates a ByteArrayTag from a ByteArray. + * @param ByteArray $byteArray The byte array to convert + * @return ByteArrayTag Returns a ByteArrayTag + */ + private static function getByteArrayTag(ByteArray $byteArray): ByteArrayTag { + return new ByteArrayTag(pack("C*", ...$byteArray->getValues())); + } + /** * Creates an NBT Tag from an array. * - If the array uses sequential numeric keys (0..n), a ListTag is created. From 9acd7c082aa983aebc49c1d6563c9a613adb050c Mon Sep 17 00:00:00 2001 From: JeanTKG <102908437+jeantkg@users.noreply.github.com> Date: Fri, 26 Dec 2025 15:14:09 +0300 Subject: [PATCH 33/51] Refactor block state and trait systems - Removed obsolete BlockPermutations, BlockPermutationsTrait, BlockState, BlockStates, BlockStatesTrait, BooleanState, IntRangeState, IntState, StringState, BlockTrait, BlockTraits, BlockTraitsTrait, PlacementDirectionTrait, and PlacementPositionTrait interfaces and traits. - Introduced new BlockPermutation and BlockPermutations classes to handle block permutations. - Implemented BlockComponents and BlockComponentsTrait for managing block components. - Established BlockState and BlockStates interfaces for state management, along with their respective implementations. - Created a comprehensive registry system for block permutations and states from JSON data. - Enhanced item component management with ItemComponentsTrait for better item handling. --- src/block/CustomiesBlockFactory.php | 17 +++-- .../blockpermutations}/BlockPermutation.php | 2 +- .../blockpermutations}/BlockPermutations.php | 2 +- .../BlockPermutationsTrait.php | 2 +- src/block/{ => component}/BlockComponents.php | 5 +- .../BlockComponentsTrait.php | 22 +++++- .../state => block/states}/BlockState.php | 2 +- .../state => block/states}/BlockStates.php | 2 +- .../states}/BlockStatesTrait.php | 2 +- .../state => block/states}/BooleanState.php | 2 +- .../state => block/states}/IntRangeState.php | 2 +- src/{test/state => block/states}/IntState.php | 2 +- .../state => block/states}/StringState.php | 2 +- .../trait => block/traits}/BlockTrait.php | 2 +- .../trait => block/traits}/BlockTraits.php | 2 +- .../traits}/BlockTraitsTrait.php | 2 +- src/block/traits/DefaultTrait.php | 24 ------ .../traits}/PlacementDirectionTrait.php | 2 +- .../traits}/PlacementPositionTrait.php | 2 +- src/item/{traits => }/ItemComponentsTrait.php | 9 ++- src/item/traits/DefaultTrait.php | 16 ---- src/json/BehaviorManager.php | 13 +++- src/json/BlockPermutationRegistry.php | 54 ++++++++++++++ src/json/BlockStateRegistry.php | 63 ++++++++++++++++ src/json/BlockTraitRegistry.php | 74 +++++++++++++++++++ src/json/CustomiesBlock.php | 49 +++++++++--- src/json/CustomiesItem.php | 2 +- 27 files changed, 293 insertions(+), 85 deletions(-) rename src/{test/permutation => block/blockpermutations}/BlockPermutation.php (90%) rename src/{test/permutation => block/blockpermutations}/BlockPermutations.php (90%) rename src/{test/permutation => block/blockpermutations}/BlockPermutationsTrait.php (93%) rename src/block/{ => component}/BlockComponents.php (88%) rename src/block/{traits => component}/BlockComponentsTrait.php (63%) rename src/{test/state => block/states}/BlockState.php (95%) rename src/{test/state => block/states}/BlockStates.php (85%) rename src/{test/state => block/states}/BlockStatesTrait.php (90%) rename src/{test/state => block/states}/BooleanState.php (95%) rename src/{test/state => block/states}/IntRangeState.php (95%) rename src/{test/state => block/states}/IntState.php (95%) rename src/{test/state => block/states}/StringState.php (95%) rename src/{test/trait => block/traits}/BlockTrait.php (73%) rename src/{test/trait => block/traits}/BlockTraits.php (85%) rename src/{test/trait => block/traits}/BlockTraitsTrait.php (94%) delete mode 100644 src/block/traits/DefaultTrait.php rename src/{test/trait => block/traits}/PlacementDirectionTrait.php (93%) rename src/{test/trait => block/traits}/PlacementPositionTrait.php (91%) rename src/item/{traits => }/ItemComponentsTrait.php (77%) delete mode 100644 src/item/traits/DefaultTrait.php create mode 100644 src/json/BlockPermutationRegistry.php create mode 100644 src/json/BlockStateRegistry.php create mode 100644 src/json/BlockTraitRegistry.php diff --git a/src/block/CustomiesBlockFactory.php b/src/block/CustomiesBlockFactory.php index 8e01320b..2a4d09f4 100644 --- a/src/block/CustomiesBlockFactory.php +++ b/src/block/CustomiesBlockFactory.php @@ -7,13 +7,15 @@ use customiesdevs\customies\block\permutations\Permutable; use customiesdevs\customies\block\permutations\Permutation; use customiesdevs\customies\block\permutations\Permutations; -use customiesdevs\customies\test\permutation\BlockPermutation as NewBlockPermutation; -use customiesdevs\customies\test\permutation\BlockPermutations; -use customiesdevs\customies\test\state\BlockStates; -use customiesdevs\customies\test\trait\BlockTraits; +use customiesdevs\customies\block\blockpermutations\BlockPermutation; +use customiesdevs\customies\block\blockpermutations\BlockPermutations; +use customiesdevs\customies\block\component\BlockComponents; +use customiesdevs\customies\block\states\BlockStates; +use customiesdevs\customies\block\traits\BlockTraits; use customiesdevs\customies\item\CreativeInventoryInfo; use customiesdevs\customies\item\CustomiesItemFactory; use customiesdevs\customies\task\AsyncRegisterBlocksTask; +use customiesdevs\customies\util\ByteArray; use customiesdevs\customies\util\NBT; use InvalidArgumentException; use pocketmine\block\Block; @@ -171,7 +173,7 @@ public function registerBlock( // New API: BlockPermutations (independent of BlockStates - works with BlockTraits too) if($block instanceof BlockPermutations) { $permutations = array_map( - static fn(NewBlockPermutation $p) => NBT::getTagType($p->toArray()), + static fn(BlockPermutation $p) => NBT::getTagType($p->toArray()), $block->getPermutations() ); $nbt->setTag("permutations", new ListTag($permutations)); @@ -183,8 +185,9 @@ public function registerBlock( foreach($block->getStates() as $state) { $stateNames[] = $state->getName(); $value = $state->getValue(); - // Extract enum values for Cartesian product - $stateValues[] = $value["enum"] ?? []; + // Extract enum values for Cartesian product (handle ByteArray for boolean states) + $enum = $value["enum"] ?? []; + $stateValues[] = $enum instanceof ByteArray ? $enum->getValues() : $enum; $stateNBT[] = NBT::getTagType($value); } $nbt->setTag("properties", new ListTag(array_reverse($stateNBT))); // fix client-side order diff --git a/src/test/permutation/BlockPermutation.php b/src/block/blockpermutations/BlockPermutation.php similarity index 90% rename from src/test/permutation/BlockPermutation.php rename to src/block/blockpermutations/BlockPermutation.php index 6f066a43..bef2c28a 100644 --- a/src/test/permutation/BlockPermutation.php +++ b/src/block/blockpermutations/BlockPermutation.php @@ -1,7 +1,7 @@ */ - private array $components; + private array $components = []; /** * Adds or replaces a block component. @@ -49,4 +50,17 @@ public function getComponent(string $name): ?BlockComponent { public function getComponents(): array { return $this->components; } -} \ No newline at end of file + + /** + * Initializes the default components for a block with the given texture and name. + * Adds geometry, material instances, and display name components. + * + * @param string $texture The texture identifier + * @param string $name The display name of the block + */ + protected function initDefaultComponents(string $texture, string $name): void { + $this->addComponent(new GeometryComponent()); + $this->addComponent(new MaterialInstancesComponent([new Material("*", $texture)])); + $this->addComponent(new DisplayNameComponent($name)); + } +} diff --git a/src/test/state/BlockState.php b/src/block/states/BlockState.php similarity index 95% rename from src/test/state/BlockState.php rename to src/block/states/BlockState.php index 1cd26eb7..98ecf5f4 100644 --- a/src/test/state/BlockState.php +++ b/src/block/states/BlockState.php @@ -1,7 +1,7 @@ addComponent(new GeometryComponent()); - $this->addComponent(new MaterialInstancesComponent([new Material("*", $texture)])); - $this->addComponent(new DisplayNameComponent($name)); - } -} \ No newline at end of file diff --git a/src/test/trait/PlacementDirectionTrait.php b/src/block/traits/PlacementDirectionTrait.php similarity index 93% rename from src/test/trait/PlacementDirectionTrait.php rename to src/block/traits/PlacementDirectionTrait.php index b662c688..ee10a692 100644 --- a/src/test/trait/PlacementDirectionTrait.php +++ b/src/block/traits/PlacementDirectionTrait.php @@ -1,6 +1,6 @@ components; } + + protected function initDefaultComponent(string $texture, string $name): void { + $this->addComponent(new IconComponent($texture)); + $this->addComponent(new DisplayNameComponent($name)); + } } \ No newline at end of file diff --git a/src/item/traits/DefaultTrait.php b/src/item/traits/DefaultTrait.php deleted file mode 100644 index c0625e03..00000000 --- a/src/item/traits/DefaultTrait.php +++ /dev/null @@ -1,16 +0,0 @@ -addComponent(new IconComponent($texture)); - $this->addComponent(new DisplayNameComponent($name)); - } - -} \ No newline at end of file diff --git a/src/json/BehaviorManager.php b/src/json/BehaviorManager.php index af6bd619..51dcb067 100644 --- a/src/json/BehaviorManager.php +++ b/src/json/BehaviorManager.php @@ -162,14 +162,19 @@ private function registerItem(array $config): void { * @param array $config JSON-decoded block configuration */ private function registerBlock(array $config): void { - if(!isset($config["components"], $config["description"]["identifier"])) { - throw new \InvalidArgumentException("Missing required fields 'components' or 'description.identifier'"); + if(!isset($config["description"]["identifier"])) { + throw new \InvalidArgumentException("Missing required field 'description.identifier'"); } + $identifier = $config["description"]["identifier"]; - $components = $config["components"]; + $components = $config["components"] ?? []; + $traits = $config["description"]["traits"] ?? []; + $states = $config["description"]["states"] ?? []; + $permutations = $config["permutations"] ?? []; $creativeInfo = $this->getCreativeInfo($config); + CustomiesBlockFactory::getInstance()->registerBlock( - static fn(): Block => new CustomiesBlock($components), + static fn(): Block => new CustomiesBlock($components, $traits, $states, $permutations), $identifier, $creativeInfo ); diff --git a/src/json/BlockPermutationRegistry.php b/src/json/BlockPermutationRegistry.php new file mode 100644 index 00000000..2084f768 --- /dev/null +++ b/src/json/BlockPermutationRegistry.php @@ -0,0 +1,54 @@ + $data + * @return BlockPermutation[] + */ + public static function fromJson(array $data): array { + $permutations = []; + + foreach($data as $permutation) { + if(!isset($permutation['condition'], $permutation['components'])) { + continue; + } + + $condition = $permutation['condition']; + $components = $permutation['components']; + + // Each permutation should have exactly one component (typically transformation) + foreach($components as $componentName => $componentData) { + $component = BlockComponentRegistry::fromJson($componentName, $componentData); + if($component !== null) { + $permutations[] = new BlockPermutation($condition, $component); + break; // Only take the first component per permutation + } + } + } + + return $permutations; + } +} diff --git a/src/json/BlockStateRegistry.php b/src/json/BlockStateRegistry.php new file mode 100644 index 00000000..74fe9fde --- /dev/null +++ b/src/json/BlockStateRegistry.php @@ -0,0 +1,63 @@ +> + */ + private static array $traits = [ + 'minecraft:placement_direction' => PlacementDirectionTrait::class, + 'minecraft:placement_position' => PlacementPositionTrait::class, + ]; + + /** + * Register a custom block trait. + * + * @param string $name Trait identifier (e.g., 'minecraft:placement_direction') + * @param class-string $class Fully qualified class name implementing BlockTrait + */ + public static function register(string $name, string $class): void { + self::$traits[$name] = $class; + } + + /** + * Get the class name of a trait by its identifier. + * + * @param string $name Trait identifier + * @return class-string|null Returns the class name, or null if not registered + */ + public static function get(string $name): ?string { + return self::$traits[$name] ?? null; + } + + /** + * Check if a trait is registered. + * + * @param string $name Trait identifier + * @return bool True if registered, false otherwise + */ + public static function has(string $name): bool { + return isset(self::$traits[$name]); + } + + /** + * Instantiate a block trait from JSON data. + * + * @param string $name Trait identifier + * @param mixed $data JSON-decoded data for the trait + * @return BlockTrait|null Returns the trait instance or null if not registered + */ + public static function fromJson(string $name, mixed $data): ?BlockTrait { + return match($name) { + 'minecraft:placement_direction' => new PlacementDirectionTrait( + state: $data['enabled_states'] ?? [], + yRotationOffset: (float) ($data['y_rotation_offset'] ?? 0.0) + ), + 'minecraft:placement_position' => new PlacementPositionTrait( + state: $data['enabled_states'] ?? [] + ), + default => null + }; + } +} diff --git a/src/json/CustomiesBlock.php b/src/json/CustomiesBlock.php index 16861b63..e811351e 100644 --- a/src/json/CustomiesBlock.php +++ b/src/json/CustomiesBlock.php @@ -3,8 +3,14 @@ namespace customiesdevs\customies\json; -use customiesdevs\customies\block\BlockComponents; -use customiesdevs\customies\block\traits\BlockComponentsTrait; +use customiesdevs\customies\block\component\BlockComponents; +use customiesdevs\customies\block\component\BlockComponentsTrait; +use customiesdevs\customies\block\blockpermutations\BlockPermutations; +use customiesdevs\customies\block\blockpermutations\BlockPermutationsTrait; +use customiesdevs\customies\block\states\BlockStates; +use customiesdevs\customies\block\states\BlockStatesTrait; +use customiesdevs\customies\block\traits\BlockTraits; +use customiesdevs\customies\block\traits\BlockTraitsTrait; use pocketmine\block\Block; use pocketmine\block\BlockBreakInfo; use pocketmine\block\BlockIdentifier; @@ -12,19 +18,19 @@ use pocketmine\block\BlockTypeIds; use pocketmine\block\BlockTypeInfo; -class CustomiesBlock extends Block implements BlockComponents { +class CustomiesBlock extends Block implements BlockComponents, BlockTraits, BlockStates, BlockPermutations { use BlockComponentsTrait; + use BlockTraitsTrait; + use BlockStatesTrait; + use BlockPermutationsTrait; /** - * Constructs a new CustomiesBlock with the given components. - * - * Each component is provided as an associative array where the key is the - * component identifier and the value is JSON-decoded data. - * - * Components are automatically instantiated via BlockComponentRegistry and added - * to this block if they are registered. + * Constructs a new CustomiesBlock with the given configuration. * * @param array $components Associative array of component identifiers to data + * @param array $traits Associative array of trait identifiers to data + * @param array $states Associative array of state identifiers to values + * @param array $permutations Array of permutation definitions * @param float $hardness The hardness of the block (default 1.0) * @param int $toolType The required tool type to break the block (default BlockToolType::NONE) * @param int $toolHarvestLevel The required tool harvest level (default 0) @@ -32,6 +38,9 @@ class CustomiesBlock extends Block implements BlockComponents { */ public function __construct( array $components, + array $traits = [], + array $states = [], + array $permutations = [], float $hardness = 1.0, int $toolType = BlockToolType::NONE, int $toolHarvestLevel = 0, @@ -48,6 +57,7 @@ public function __construct( $blastResistance )) ); + // Add all registered components foreach($components as $name => $data) { $component = BlockComponentRegistry::fromJson($name, $data); @@ -55,5 +65,24 @@ public function __construct( $this->addComponent($component); } } + + // Add all registered traits + foreach($traits as $name => $data) { + $trait = BlockTraitRegistry::fromJson($name, $data); + if($trait !== null) { + $this->addTrait($trait); + } + } + + // Add all registered states + foreach($states as $name => $data) { + $state = BlockStateRegistry::fromJson($name, $data); + if($state !== null) { + $this->addState($state); + } + } + + // Add all permutations + $this->addPermutations(BlockPermutationRegistry::fromJson($permutations)); } } \ No newline at end of file diff --git a/src/json/CustomiesItem.php b/src/json/CustomiesItem.php index 0d07590f..cdc5373c 100644 --- a/src/json/CustomiesItem.php +++ b/src/json/CustomiesItem.php @@ -4,7 +4,7 @@ namespace customiesdevs\customies\json; use customiesdevs\customies\item\ItemComponents; -use customiesdevs\customies\item\traits\ItemComponentsTrait; +use customiesdevs\customies\item\ItemComponentsTrait; use pocketmine\item\Item; use pocketmine\item\ItemIdentifier; use pocketmine\item\ItemTypeIds; From 11e74d46029bb14d75b21f7f03f154480dd545f0 Mon Sep 17 00:00:00 2001 From: JeanTKG <102908437+jeantkg@users.noreply.github.com> Date: Fri, 26 Dec 2025 19:04:53 +0300 Subject: [PATCH 34/51] Enhance block and item registration systems; add new state and trait methods, improve NBT handling, and ensure compatibility with upcoming features. --- src/CustomiesListener.php | 1 + src/block/CustomiesBlockFactory.php | 20 +++---- .../blockpermutations/BlockPermutations.php | 6 +- .../BlockPermutationsTrait.php | 6 +- src/block/component/CollisionBoxComponent.php | 8 ++- src/block/properties/Box.php | 12 ++-- src/block/properties/Material.php | 13 ++-- src/block/states/BlockState.php | 60 ++++++++++--------- src/block/states/BlockStates.php | 18 ++++++ src/block/states/BlockStatesTrait.php | 21 +++++++ src/block/states/BooleanState.php | 3 +- src/block/traits/BlockTrait.php | 7 +++ src/block/traits/BlockTraits.php | 18 ++++++ src/block/traits/BlockTraitsTrait.php | 12 ++-- src/item/CustomiesItemFactory.php | 6 +- 15 files changed, 137 insertions(+), 74 deletions(-) diff --git a/src/CustomiesListener.php b/src/CustomiesListener.php index 44c4ba92..c56abf68 100644 --- a/src/CustomiesListener.php +++ b/src/CustomiesListener.php @@ -22,6 +22,7 @@ public function __construct() { // "data_driven_items" is required for custom blocks to render in-game. // With this disabled, custom blocks will appear as the UPDATE texture block. "data_driven_items" => true, + "upcoming_creator_features" => true ], true); } diff --git a/src/block/CustomiesBlockFactory.php b/src/block/CustomiesBlockFactory.php index 2a4d09f4..583cbe06 100644 --- a/src/block/CustomiesBlockFactory.php +++ b/src/block/CustomiesBlockFactory.php @@ -15,7 +15,6 @@ use customiesdevs\customies\item\CreativeInventoryInfo; use customiesdevs\customies\item\CustomiesItemFactory; use customiesdevs\customies\task\AsyncRegisterBlocksTask; -use customiesdevs\customies\util\ByteArray; use customiesdevs\customies\util\NBT; use InvalidArgumentException; use pocketmine\block\Block; @@ -121,7 +120,7 @@ public function getBlockPaletteEntries(): array { public function registerBlock( Closure $blockFunc, string $identifier, - ?CreativeInventoryInfo $creativeInfo = null, + ?CreativeInventoryInfo $creativeInfo = new CreativeInventoryInfo(CreativeInventoryInfo::CATEGORY_ITEMS), ?Closure $serializer = null, ?Closure $deserializer = null ): void { @@ -156,10 +155,10 @@ public function registerBlock( // The 'minecraft:on_player_placing' component is required for the client to predict block placement, making // it a smoother experience for the end-user. // Is this even used anymore?????? - $components->setTag("minecraft:on_player_placing", CompoundTag::create()); + // $components->setTag("minecraft:on_player_placing", CompoundTag::create()); // New API: Block Traits - if($block instanceof BlockTraits) { + if($block instanceof BlockTraits && count($block->getTraits()) > 0) { $traits = []; foreach($block->getTraits() as $trait) { $traits[] = $trait->getValue(); @@ -170,8 +169,8 @@ public function registerBlock( $nbt->setTag("components", $components); $nbt->setInt("molangVersion", 13); - // New API: BlockPermutations (independent of BlockStates - works with BlockTraits too) - if($block instanceof BlockPermutations) { + // New API: BlockPermutations + if($block instanceof BlockPermutations && count($block->getPermutations()) > 0) { $permutations = array_map( static fn(BlockPermutation $p) => NBT::getTagType($p->toArray()), $block->getPermutations() @@ -179,15 +178,13 @@ public function registerBlock( $nbt->setTag("permutations", new ListTag($permutations)); } - // New API: BlockStates (custom state properties) - if($block instanceof BlockStates) { + // New API: BlockStates + if($block instanceof BlockStates && count($block->getStates()) > 0) { $stateNames = $stateValues = $stateNBT = []; foreach($block->getStates() as $state) { $stateNames[] = $state->getName(); $value = $state->getValue(); - // Extract enum values for Cartesian product (handle ByteArray for boolean states) - $enum = $value["enum"] ?? []; - $stateValues[] = $enum instanceof ByteArray ? $enum->getValues() : $enum; + $stateValues[] = $value["enum"] ?? []; $stateNBT[] = NBT::getTagType($value); } $nbt->setTag("properties", new ListTag(array_reverse($stateNBT))); // fix client-side order @@ -305,7 +302,6 @@ public function registerBlock( return strcmp(hash("fnv164", $a->getName()), hash("fnv164", $b->getName())); }); foreach($this->blockPaletteEntries as $i => $entry) { - /** @var CompoundTag $root */ $root = $entry->getStates()->getRoot(); $root->setTag("vanilla_block_data", CompoundTag::create()->setInt("block_id", 10000 + $i)); $this->blockPaletteEntries[$i] = new BlockPaletteEntry($entry->getName(), new CacheableNbt($root)); diff --git a/src/block/blockpermutations/BlockPermutations.php b/src/block/blockpermutations/BlockPermutations.php index edc671be..287dac89 100644 --- a/src/block/blockpermutations/BlockPermutations.php +++ b/src/block/blockpermutations/BlockPermutations.php @@ -3,15 +3,11 @@ namespace customiesdevs\customies\block\blockpermutations; -/** - * Interface for blocks that have permutations. - * - * Similar to BlockComponents but for permutations. - */ interface BlockPermutations { /** * Adds a permutation to the block. + * @param BlockPermutation $permutation */ public function addPermutation(BlockPermutation $permutation): void; diff --git a/src/block/blockpermutations/BlockPermutationsTrait.php b/src/block/blockpermutations/BlockPermutationsTrait.php index afbde785..ed502eff 100644 --- a/src/block/blockpermutations/BlockPermutationsTrait.php +++ b/src/block/blockpermutations/BlockPermutationsTrait.php @@ -3,11 +3,6 @@ namespace customiesdevs\customies\block\blockpermutations; -/** - * Trait that implements BlockPermutations interface. - * - * Similar to BlockComponentsTrait but for permutations. - */ trait BlockPermutationsTrait { /** @@ -18,6 +13,7 @@ trait BlockPermutationsTrait { /** * Adds a permutation to the block. + * @param BlockPermutation $permutation */ public function addPermutation(BlockPermutation $permutation): void { $this->permutations[] = $permutation; diff --git a/src/block/component/CollisionBoxComponent.php b/src/block/component/CollisionBoxComponent.php index efcbf98f..6d6ae942 100644 --- a/src/block/component/CollisionBoxComponent.php +++ b/src/block/component/CollisionBoxComponent.php @@ -53,9 +53,13 @@ public function getValue(): array { foreach($this->boxes as $box) { $convertedBoxes[] = $box->toNbtArray(); } + //if no boxes are defined we add a default full block box + if(empty($convertedBoxes)) { + $convertedBoxes[] = (new Box(new Vector3(-8, 0, -8), new Vector3(16, 16, 16)))->toNbtArray(); + } return [ - "enabled" => $this->enabled, - "boxes" => $convertedBoxes + "boxes" => $convertedBoxes, + "enabled" => $this->enabled ]; } diff --git a/src/block/properties/Box.php b/src/block/properties/Box.php index 50b6f66f..d79206d7 100644 --- a/src/block/properties/Box.php +++ b/src/block/properties/Box.php @@ -100,12 +100,12 @@ public function getMax(): Vector3 { return $this->origin->addVector($this->size) public function toNbtArray(): array { $max = $this->getMax(); return [ - "minX" => $this->origin->x + 8, - "minY" => $this->origin->y, - "minZ" => $this->origin->z + 8, - "maxX" => $max->x + 8, - "maxY" => $max->y, - "maxZ" => $max->z + 8, + "minX" => (float) ($this->origin->x + 8), + "minY" => (float) $this->origin->y, + "minZ" => (float) ($this->origin->z + 8), + "maxX" => (float) ($max->x + 8), + "maxY" => (float) $max->y, + "maxZ" => (float) ($max->z + 8), ]; } diff --git a/src/block/properties/Material.php b/src/block/properties/Material.php index 57639e79..20d247a9 100644 --- a/src/block/properties/Material.php +++ b/src/block/properties/Material.php @@ -93,21 +93,22 @@ public static function fromArray(string $target, array $data): self { } /** - * Converts the material into Bedrock-compatible NBT/JSON format. + * Converts the material into Bedrock-compatible NBT format. * @return array{ - * texture: string, + * ambient_occlusion: float, + * packed_bools: ByteTag, * render_method: string, + * texture: string, * tint_method: string, - * ambient_occlusion: float, * } */ public function toArray(): array { return [ - "texture" => $this->texture, + "ambient_occlusion" => $this->ambientOcclusion, + "packed_bools" => new ByteTag($this->packed_bools), "render_method" => $this->renderMethod->value, + "texture" => $this->texture, "tint_method" => $this->tintMethod->value, - "ambient_occlusion" => $this->ambientOcclusion, - "packed_bools" => new ByteTag($this->packed_bools) ]; } } \ No newline at end of file diff --git a/src/block/states/BlockState.php b/src/block/states/BlockState.php index 98ecf5f4..541c8f4e 100644 --- a/src/block/states/BlockState.php +++ b/src/block/states/BlockState.php @@ -8,34 +8,36 @@ interface BlockState { - /** - * Returns the state property name (e.g., "minecraft:cardinal_direction"). - */ - public function getName(): string; - - /** - * Returns the NBT value definition for client (enum + name). - */ - public function getValue(): array; - - /** - * Gets the current state value. - */ - public function getCurrentValue(): mixed; - - /** - * Sets the current state value. - */ - public function setCurrentValue(mixed $value): void; - - /** - * Writes the current state value to the BlockStateWriter. - */ - public function serialize(BlockStateWriter $writer): void; - - /** - * Reads the state value from the BlockStateReader and sets it. - */ - public function deserialize(BlockStateReader $reader): void; + /** + * Returns the state property name (e.g., "minecraft:cardinal_direction"). + */ + public function getName(): string; + + /** + * Returns the NBT value definition for client (enum + name). + * The "enum" values must be native PHP types (bool, int, string) that + * NBT::getTagType() can convert to the correct NBT tag type. + */ + public function getValue(): array; + + /** + * Gets the current state value. + */ + public function getCurrentValue(): mixed; + + /** + * Sets the current state value. + */ + public function setCurrentValue(mixed $value): void; + + /** + * Writes the current state value to the BlockStateWriter. + */ + public function serialize(BlockStateWriter $writer): void; + + /** + * Reads the state value from the BlockStateReader and sets it. + */ + public function deserialize(BlockStateReader $reader): void; } diff --git a/src/block/states/BlockStates.php b/src/block/states/BlockStates.php index ba930bed..82c34a48 100644 --- a/src/block/states/BlockStates.php +++ b/src/block/states/BlockStates.php @@ -5,10 +5,28 @@ interface BlockStates { + /** + * Adds a state to the block. + * @param BlockState $trait + */ public function addState(BlockState $trait): void; + + /** + * Checks if the block has a state by name. + * @param string $name + * @return bool + */ public function hasState(string $name): bool; + + /** + * Retrieves a state by its name. + * @param string $name + * @return BlockState|null + */ public function getState(string $name): ?BlockState; + /** + * Returns all registered block states. * @return BlockState[] */ public function getStates(): array; diff --git a/src/block/states/BlockStatesTrait.php b/src/block/states/BlockStatesTrait.php index 4aafe3fe..da4d3f0d 100644 --- a/src/block/states/BlockStatesTrait.php +++ b/src/block/states/BlockStatesTrait.php @@ -5,20 +5,41 @@ trait BlockStatesTrait { + /** + * @var BlockState[] + */ private array $states = []; + /** + * Adds a state to the block. + * @param BlockState $state + */ public function addState(BlockState $state): void { $this->states[$state->getName()] = $state; } + /** + * Checks whether the block has a state with the given name. + * @param string $name + * @return bool + */ public function hasState(string $name): bool { return isset($this->states[$name]); } + /** + * Retrieves a state by its name. + * @param string $name + * @return BlockState|null + */ public function getState(string $name): ?BlockState { return $this->states[$name] ?? null; } + /** + * Returns all registered block states. + * @return BlockState[] + */ public function getStates(): array { return $this->states; } diff --git a/src/block/states/BooleanState.php b/src/block/states/BooleanState.php index e4dab6a6..34121db4 100644 --- a/src/block/states/BooleanState.php +++ b/src/block/states/BooleanState.php @@ -3,7 +3,6 @@ namespace customiesdevs\customies\block\states; -use customiesdevs\customies\util\ByteArray; use pocketmine\data\bedrock\block\convert\BlockStateReader; use pocketmine\data\bedrock\block\convert\BlockStateWriter; @@ -19,7 +18,7 @@ public function getName(): string { public function getValue(): array { return [ - "enum" => new ByteArray([false, true]), + "enum" => [false, true], // Native bools -> NBT::getTagType() converts to ByteTag "name" => $this->name ]; } diff --git a/src/block/traits/BlockTrait.php b/src/block/traits/BlockTrait.php index 127acfe2..cbe15200 100644 --- a/src/block/traits/BlockTrait.php +++ b/src/block/traits/BlockTrait.php @@ -4,7 +4,14 @@ namespace customiesdevs\customies\block\traits; interface BlockTrait { + + /** + * Returns the name of the trait. + */ public function getName(): string; + /** + * Returns the value of the trait. + */ public function getValue(): mixed; } diff --git a/src/block/traits/BlockTraits.php b/src/block/traits/BlockTraits.php index 3e2a4a8f..b9597550 100644 --- a/src/block/traits/BlockTraits.php +++ b/src/block/traits/BlockTraits.php @@ -5,10 +5,28 @@ interface BlockTraits { + /** + * Adds a trait to the block. + * @param BlockTrait $trait + */ public function addTrait(BlockTrait $trait): void; + + /** + * Checks if the block has a trait by name. + * @param string $name + * @return bool + */ public function hasTrait(string $name): bool; + + /** + * Retrieves a trait by its name. + * @param string $name + * @return BlockTrait|null + */ public function getTrait(string $name): ?BlockTrait; + /** + * Returns all traits of the block. * @return BlockTrait[] */ public function getTraits(): array; diff --git a/src/block/traits/BlockTraitsTrait.php b/src/block/traits/BlockTraitsTrait.php index 6ae96e6a..50e8f618 100644 --- a/src/block/traits/BlockTraitsTrait.php +++ b/src/block/traits/BlockTraitsTrait.php @@ -3,11 +3,6 @@ namespace customiesdevs\customies\block\traits; -/** - * Trait that implements BlockTraits interface. - * - * Similar to BlockComponentsTrait but for block traits. - */ trait BlockTraitsTrait { /** @@ -17,7 +12,8 @@ trait BlockTraitsTrait { private array $traits = []; /** - * Adds or replaces a block trait. + * Adds a block trait. + * @param BlockTrait $trait */ public function addTrait(BlockTrait $trait): void { $this->traits[$trait->getName()] = $trait; @@ -25,6 +21,8 @@ public function addTrait(BlockTrait $trait): void { /** * Checks whether the block has a trait with the given name. + * @param string $name + * @return bool */ public function hasTrait(string $name): bool { return isset($this->traits[$name]); @@ -32,6 +30,8 @@ public function hasTrait(string $name): bool { /** * Retrieves a trait by its name. + * @param string $name + * @return BlockTrait|null */ public function getTrait(string $name): ?BlockTrait { return $this->traits[$name] ?? null; diff --git a/src/item/CustomiesItemFactory.php b/src/item/CustomiesItemFactory.php index 21addb01..b7088eda 100644 --- a/src/item/CustomiesItemFactory.php +++ b/src/item/CustomiesItemFactory.php @@ -96,7 +96,11 @@ public function getItemTableEntries(): array { * @param CreativeInventoryInfo|null $creativeInfo The creative inventory info for the item, if any * @throws InvalidArgumentException if the closure does not return an Item instance */ - public function registerItem(Closure $itemFunc, string $identifier, ?CreativeInventoryInfo $creativeInfo = null): void { + public function registerItem( + Closure $itemFunc, + string $identifier, + ?CreativeInventoryInfo $creativeInfo = new CreativeInventoryInfo(CreativeInventoryInfo::CATEGORY_ITEMS) + ): void { $item = $itemFunc(); if(!$item instanceof Item) { throw new InvalidArgumentException("Class returned from closure is not a Item"); From aba178e595598ed32088d7d8e7b7858e4c91d89e Mon Sep 17 00:00:00 2001 From: JeanTKG <102908437+jeantkg@users.noreply.github.com> Date: Fri, 26 Dec 2025 19:06:21 +0300 Subject: [PATCH 35/51] Revert version number to 1.5.0 in plugin.yml --- plugin.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugin.yml b/plugin.yml index 16b72fbf..c5abfcfa 100644 --- a/plugin.yml +++ b/plugin.yml @@ -3,7 +3,7 @@ description: A PocketMine-MP plugin that implements support for custom blocks, i main: customiesdevs\customies\Customies src-namespace-prefix: customiesdevs\customies -version: 2.0.0 +version: 1.5.0 api: 5.37.0 authors: From 7671f0624b51700a14f02578865c9765ed09ad17 Mon Sep 17 00:00:00 2001 From: JeanTKG <102908437+jeantkg@users.noreply.github.com> Date: Sat, 27 Dec 2025 19:20:31 +0300 Subject: [PATCH 36/51] Refactor block permutations and states - Introduced BlockPermutation and BlockPermutations interface for managing block permutations. - Added BlockPermutationsTrait to handle adding and retrieving permutations. - Removed obsolete BlockProperty, Permutable, and related classes. - Simplified BlockState management by consolidating state handling into BlockState class. - Updated CustomiesBlock to utilize new permutation and state structures. - Removed unused traits and streamlined component initialization in ItemComponentsTrait. - Adjusted BehaviorManager and Block registries to align with new structure. --- src/block/CustomiesBlockFactory.php | 98 ++++--------------- src/block/component/BlockComponentsTrait.php | 2 +- src/block/component/GeometryComponent.php | 4 +- .../component/TransformationComponent.php | 47 +++++---- .../BlockPermutation.php | 2 +- .../BlockPermutations.php | 6 +- .../BlockPermutationsTrait.php | 11 ++- src/block/permutations/BlockProperty.php | 38 ------- src/block/permutations/BlockTrait.php | 53 ---------- src/block/permutations/Permutable.php | 41 -------- src/block/permutations/Permutation.php | 38 ------- src/block/permutations/Permutations.php | 15 +-- src/block/permutations/RotatableTrait.php | 77 --------------- src/block/states/BlockState.php | 53 +++++----- src/block/states/BlockStates.php | 20 ++++ src/block/states/BooleanState.php | 41 -------- src/block/states/IntRangeState.php | 47 --------- src/block/states/IntState.php | 46 --------- src/block/states/StringState.php | 46 --------- src/block/traits/BlockTrait.php | 17 ---- src/block/traits/BlockTraits.php | 33 ------- src/block/traits/BlockTraitsTrait.php | 47 --------- src/block/traits/PlacementDirectionTrait.php | 28 ------ src/block/traits/PlacementPositionTrait.php | 24 ----- src/item/ItemComponentsTrait.php | 2 +- src/json/BehaviorManager.php | 5 +- src/json/BlockComponentRegistry.php | 2 + src/json/BlockPermutationRegistry.php | 3 +- src/json/BlockStateRegistry.php | 33 ++----- src/json/BlockTraitRegistry.php | 74 -------------- src/json/CustomiesBlock.php | 74 ++++++++------ 31 files changed, 174 insertions(+), 853 deletions(-) rename src/block/{blockpermutations => permutations}/BlockPermutation.php (90%) rename src/block/{blockpermutations => permutations}/BlockPermutations.php (75%) rename src/block/{blockpermutations => permutations}/BlockPermutationsTrait.php (72%) delete mode 100644 src/block/permutations/BlockProperty.php delete mode 100644 src/block/permutations/BlockTrait.php delete mode 100644 src/block/permutations/Permutable.php delete mode 100644 src/block/permutations/Permutation.php delete mode 100644 src/block/permutations/RotatableTrait.php delete mode 100644 src/block/states/BooleanState.php delete mode 100644 src/block/states/IntRangeState.php delete mode 100644 src/block/states/IntState.php delete mode 100644 src/block/states/StringState.php delete mode 100644 src/block/traits/BlockTrait.php delete mode 100644 src/block/traits/BlockTraits.php delete mode 100644 src/block/traits/BlockTraitsTrait.php delete mode 100644 src/block/traits/PlacementDirectionTrait.php delete mode 100644 src/block/traits/PlacementPositionTrait.php delete mode 100644 src/json/BlockTraitRegistry.php diff --git a/src/block/CustomiesBlockFactory.php b/src/block/CustomiesBlockFactory.php index 583cbe06..8bdfa3d1 100644 --- a/src/block/CustomiesBlockFactory.php +++ b/src/block/CustomiesBlockFactory.php @@ -4,14 +4,11 @@ namespace customiesdevs\customies\block; use Closure; -use customiesdevs\customies\block\permutations\Permutable; -use customiesdevs\customies\block\permutations\Permutation; +use customiesdevs\customies\block\permutations\BlockPermutation; +use customiesdevs\customies\block\permutations\BlockPermutations; use customiesdevs\customies\block\permutations\Permutations; -use customiesdevs\customies\block\blockpermutations\BlockPermutation; -use customiesdevs\customies\block\blockpermutations\BlockPermutations; use customiesdevs\customies\block\component\BlockComponents; use customiesdevs\customies\block\states\BlockStates; -use customiesdevs\customies\block\traits\BlockTraits; use customiesdevs\customies\item\CreativeInventoryInfo; use customiesdevs\customies\item\CustomiesItemFactory; use customiesdevs\customies\task\AsyncRegisterBlocksTask; @@ -152,83 +149,20 @@ public function registerBlock( ->setByte("is_hidden_in_commands", 0)); } - // The 'minecraft:on_player_placing' component is required for the client to predict block placement, making - // it a smoother experience for the end-user. - // Is this even used anymore?????? - // $components->setTag("minecraft:on_player_placing", CompoundTag::create()); - - // New API: Block Traits - if($block instanceof BlockTraits && count($block->getTraits()) > 0) { - $traits = []; - foreach($block->getTraits() as $trait) { - $traits[] = $trait->getValue(); - } - $nbt->setTag("traits", NBT::getTagType($traits)); - } - - $nbt->setTag("components", $components); - $nbt->setInt("molangVersion", 13); - - // New API: BlockPermutations - if($block instanceof BlockPermutations && count($block->getPermutations()) > 0) { - $permutations = array_map( - static fn(BlockPermutation $p) => NBT::getTagType($p->toArray()), - $block->getPermutations() - ); - $nbt->setTag("permutations", new ListTag($permutations)); - } - - // New API: BlockStates - if($block instanceof BlockStates && count($block->getStates()) > 0) { - $stateNames = $stateValues = $stateNBT = []; - foreach($block->getStates() as $state) { - $stateNames[] = $state->getName(); - $value = $state->getValue(); - $stateValues[] = $value["enum"] ?? []; - $stateNBT[] = NBT::getTagType($value); - } - $nbt->setTag("properties", new ListTag(array_reverse($stateNBT))); // fix client-side order - - // Generate block palette entries for all state combinations - foreach(Permutations::getCartesianProduct($stateValues) as $meta => $combination) { - $states = CompoundTag::create(); - foreach($combination as $i => $value) { - $states->setTag($stateNames[$i], NBT::getTagType($value)); - } - $blockState = CompoundTag::create() - ->setString(BlockStateData::TAG_NAME, $identifier) - ->setTag(BlockStateData::TAG_STATES, $states); - BlockPalette::getInstance()->insertState($blockState, $meta); - } - - $serializer ??= static function (BlockStates $block) use ($identifier) : BlockStateWriter { - $writer = BlockStateWriter::create($identifier); - foreach($block->getStates() as $state) { - $state->serialize($writer); - } - return $writer; - }; - $deserializer ??= static function (BlockStateReader $in) use ($identifier) : BlockStates { - $b = CustomiesBlockFactory::getInstance()->get($identifier); - assert($b instanceof BlockStates); - foreach($b->getStates() as $state) { - $state->deserialize($in); - } - return $b; - }; - } - // Legacy API: Permutable (deprecated) - elseif($block instanceof Permutable) { + if($block instanceof BlockPermutations) { $blockPropertyNames = $blockPropertyValues = $blockProperties = []; - foreach($block->getBlockProperties() as $blockProperty){ + foreach($block->getStates() as $blockProperty){ $blockPropertyNames[] = $blockProperty->getName(); $blockPropertyValues[] = $blockProperty->getValues(); - $blockProperties[] = $blockProperty->toNBT(); + $blockProperties[] = NBT::getTagType($blockProperty->getValue()); } - $permutations = array_map(static fn(Permutation $permutation) => $permutation->toNBT(), $block->getPermutations()); + $permutations = array_map(static fn(BlockPermutation $permutation) => NBT::getTagType($permutation->toArray()), $block->getPermutations()); - $nbt->setTag("permutations", new ListTag($permutations)); - $nbt->setTag("properties", new ListTag(array_reverse($blockProperties))); // fix client-side order + // The 'minecraft:on_player_placing' component is required for the client to predict block placement, making + // it a smoother experience for the end-user. + $components->setTag("minecraft:on_player_placing", CompoundTag::create()); + $nbt->setTag("permutations", new ListTag($permutations)) + ->setTag("properties", new ListTag(array_reverse($blockProperties))); // fix client-side order foreach(Permutations::getCartesianProduct($blockPropertyValues) as $meta => $permutations){ // We need to insert states for every possible permutation to allow for all blocks to be used and to @@ -243,14 +177,14 @@ public function registerBlock( BlockPalette::getInstance()->insertState($blockState, $meta); } - $serializer ??= static function (Permutable $block) use ($identifier) : BlockStateWriter { + $serializer ??= static function (BlockPermutations $block) use ($identifier, $blockPropertyNames) : BlockStateWriter { $b = BlockStateWriter::create($identifier); $block->serializeState($b); return $b; }; - $deserializer ??= static function (BlockStateReader $in) use ($identifier) : Permutable { + $deserializer ??= static function (BlockStateReader $in) use ($block, $identifier, $blockPropertyNames) : BlockPermutations { $b = CustomiesBlockFactory::getInstance()->get($identifier); - assert($b instanceof Permutable); + assert($b instanceof BlockPermutations); $b->deserializeState($in); return $b; }; @@ -263,10 +197,12 @@ public function registerBlock( $serializer ??= static fn() => new BlockStateWriter($identifier); $deserializer ??= static fn(BlockStateReader $in) => $block; } - GlobalBlockStateHandlers::getSerializer()->map($block, $serializer); GlobalBlockStateHandlers::getDeserializer()->map($identifier, $deserializer); + $nbt->setTag("components", $components); + $nbt->setInt("molangVersion", 13); + if($creativeInfo !== null){ $this->loadGroups(); if($creativeInfo->getCategory() === CreativeInventoryInfo::CATEGORY_ALL || $creativeInfo->getCategory() === CreativeInventoryInfo::CATEGORY_COMMANDS){ diff --git a/src/block/component/BlockComponentsTrait.php b/src/block/component/BlockComponentsTrait.php index b5d5ecc9..6b59306a 100644 --- a/src/block/component/BlockComponentsTrait.php +++ b/src/block/component/BlockComponentsTrait.php @@ -58,7 +58,7 @@ public function getComponents(): array { * @param string $texture The texture identifier * @param string $name The display name of the block */ - protected function initDefaultComponents(string $texture, string $name): void { + protected function initComponents(string $texture, string $name): void { $this->addComponent(new GeometryComponent()); $this->addComponent(new MaterialInstancesComponent([new Material("*", $texture)])); $this->addComponent(new DisplayNameComponent($name)); diff --git a/src/block/component/GeometryComponent.php b/src/block/component/GeometryComponent.php index 81e91dcc..8836ca78 100644 --- a/src/block/component/GeometryComponent.php +++ b/src/block/component/GeometryComponent.php @@ -54,8 +54,8 @@ public static function fromJson(mixed $data): static { return new self( $data["identifier"] ?? "minecraft:geometry.full_block", $data["bone_visibility"] ?? [], - $data["culling"] ?? "minecraft:culling_layer.undefined", - $data["culling_layer"] ?? "", + $data["culling"] ?? "", + $data["culling_layer"] ?? "minecraft:culling_layer.undefined", $data["uv_lock"] ?? false ); } diff --git a/src/block/component/TransformationComponent.php b/src/block/component/TransformationComponent.php index 40fdc641..62fb3c01 100644 --- a/src/block/component/TransformationComponent.php +++ b/src/block/component/TransformationComponent.php @@ -52,29 +52,38 @@ public function getValue(): array { "RX" => $rx, "RY" => $ry, "RZ" => $rz, - "RXP" => $this->rotationPivot->x, - "RYP" => $this->rotationPivot->y, - "RZP" => $this->rotationPivot->z, - "SX" => $this->scale->x, - "SY" => $this->scale->y, - "SZ" => $this->scale->z, - "SXP" => $this->scalePivot->x, - "SYP" => $this->scalePivot->y, - "SZP" => $this->scalePivot->z, - "TX" => $this->translation->x, - "TY" => $this->translation->y, - "TZ" => $this->translation->z, + "RXP" => (float) $this->rotationPivot->x, + "RYP" => (float) $this->rotationPivot->y, + "RZP" => (float) $this->rotationPivot->z, + "SX" => (float) $this->scale->x, + "SY" => (float) $this->scale->y, + "SZ" => (float) $this->scale->z, + "SXP" => (float) $this->scalePivot->x, + "SYP" => (float) $this->scalePivot->y, + "SZP" => (float) $this->scalePivot->z, + "TX" => (float) $this->translation->x, + "TY" => (float) $this->translation->y, + "TZ" => (float) $this->translation->z, "hasJsonVersionBeforeValidation" => false ]; } public static function fromJson(mixed $data): static { - return new self( - $data['rotation'] ?? new Vector3(0, 0, 0), - $data['rotation_pivot'] ?? new Vector3(0, 0, 0), - $data['scale'] ?? new Vector3(1, 1, 1), - $data['scale_pivot'] ?? new Vector3(0, 0, 0), - $data['translation'] ?? new Vector3(0, 0, 0) - ); + $rotation = isset($data['rotation']) + ? new Vector3($data['rotation'][0] ?? 0, $data['rotation'][1] ?? 0, $data['rotation'][2] ?? 0) + : new Vector3(0, 0, 0); + $rotationPivot = isset($data['rotation_pivot']) + ? new Vector3($data['rotation_pivot'][0] ?? 0, $data['rotation_pivot'][1] ?? 0, $data['rotation_pivot'][2] ?? 0) + : new Vector3(0, 0, 0); + $scale = isset($data['scale']) + ? new Vector3($data['scale'][0] ?? 1, $data['scale'][1] ?? 1, $data['scale'][2] ?? 1) + : new Vector3(1, 1, 1); + $scalePivot = isset($data['scale_pivot']) + ? new Vector3($data['scale_pivot'][0] ?? 0, $data['scale_pivot'][1] ?? 0, $data['scale_pivot'][2] ?? 0) + : new Vector3(0, 0, 0); + $translation = isset($data['translation']) + ? new Vector3($data['translation'][0] ?? 0, $data['translation'][1] ?? 0, $data['translation'][2] ?? 0) + : new Vector3(0, 0, 0); + return new self($rotation, $rotationPivot, $scale, $scalePivot, $translation); } } \ No newline at end of file diff --git a/src/block/blockpermutations/BlockPermutation.php b/src/block/permutations/BlockPermutation.php similarity index 90% rename from src/block/blockpermutations/BlockPermutation.php rename to src/block/permutations/BlockPermutation.php index bef2c28a..ebf38e61 100644 --- a/src/block/blockpermutations/BlockPermutation.php +++ b/src/block/permutations/BlockPermutation.php @@ -1,7 +1,7 @@ permutations[] = $permutation; + $this->blockPermutations[] = $permutation; } /** @@ -34,6 +37,6 @@ public function addPermutations(array $permutations): void { * @return BlockPermutation[] */ public function getPermutations(): array { - return $this->permutations; + return $this->blockPermutations; } } diff --git a/src/block/permutations/BlockProperty.php b/src/block/permutations/BlockProperty.php deleted file mode 100644 index 6550c432..00000000 --- a/src/block/permutations/BlockProperty.php +++ /dev/null @@ -1,38 +0,0 @@ -name; - } - - /** - * Returns the array of possible values of the block property provided in the constructor. - */ - public function getValues(): array { - return $this->values; - } - - /** - * Returns the block property in the correct NBT format supported by the client. - */ - public function toNBT(): CompoundTag { - $values = array_map(static fn($value) => NBT::getTagType($value), $this->values); - return CompoundTag::create() - ->setString("name", $this->name) - ->setTag("enum", new ListTag($values)); - } -} \ No newline at end of file diff --git a/src/block/permutations/BlockTrait.php b/src/block/permutations/BlockTrait.php deleted file mode 100644 index a6fb23f5..00000000 --- a/src/block/permutations/BlockTrait.php +++ /dev/null @@ -1,53 +0,0 @@ -setTag("name", new StringTag("minecraft:placement_direction")) - ->setTag("enabled_states", CompoundTag::create() - ->setTag("cardinal_direction", new ByteTag($cardinalDirection ? 1 : 0)) - ->setTag("corner_and_cardinal_direction", new ByteTag($cornerAndCardinalDirection ? 1 : 0)) - ->setTag("facing_direction", new ByteTag($facingDirection ? 1 : 0)) - ) - ->setTag("blocks_to_corner_with", new ListTag([], NBT::TAG_String)) - ->setTag("y_rotation_offset", new FloatTag((float) $yRotationOffset)); - - return CompoundTag::create() - ->setTag("traits", new ListTag([$trait], NBT::TAG_Compound)); - } - - public function addPlacementPosition( - bool $blockFace = false, - bool $verticalHalf = false - ): CompoundTag { - $trait = CompoundTag::create() - ->setTag("name", new StringTag("minecraft:placement_position")) - ->setTag("enabled_states", CompoundTag::create() - ->setTag("block_face", new ByteTag($blockFace ? 1 : 0)) - ->setTag("vertical_half", new ByteTag($verticalHalf ? 1 : 0)) - ); - - return CompoundTag::create() - ->setTag("traits", new ListTag([$trait], NBT::TAG_Compound)); - } -} \ No newline at end of file diff --git a/src/block/permutations/Permutable.php b/src/block/permutations/Permutable.php deleted file mode 100644 index de921535..00000000 --- a/src/block/permutations/Permutable.php +++ /dev/null @@ -1,41 +0,0 @@ -components = CompoundTag::create(); - } - - /** - * Returns the permutation with the provided component added to the current list of components. - */ - public function withComponent(BlockComponent $component): self { - $value = ($component instanceof MaterialInstancesComponent) - ? $component->getValue(4) // Use packed_bools = 4 for permutations - : $component->getValue(); - $this->components->setTag($component->getName(), NBT::getTagType($value)); - return $this; - } - - /** - * Returns the permutation in the correct NBT format supported by the client. - */ - public function toNBT(): CompoundTag { - return CompoundTag::create() - ->setString("condition", $this->condition) - ->setTag("components", $this->components); - } -} \ No newline at end of file diff --git a/src/block/permutations/Permutations.php b/src/block/permutations/Permutations.php index 96d7b2f0..04d109ad 100644 --- a/src/block/permutations/Permutations.php +++ b/src/block/permutations/Permutations.php @@ -3,6 +3,7 @@ namespace customiesdevs\customies\block\permutations; +use customiesdevs\customies\block\states\BlockState; use Exception; use function array_map; use function count; @@ -17,9 +18,9 @@ class Permutations { * of the block. An exception is thrown if the meta value does not match any combinations of all the block * properties. */ - public static function fromMeta(Permutable $block, int $meta): array { + public static function fromMeta(BlockPermutations $block, int $meta): array { $properties = self::getCartesianProduct( - array_map(static fn(BlockProperty $blockProperty) => $blockProperty->getValues(), $block->getBlockProperties()) + array_map(static fn(BlockState $blockProperty) => $blockProperty->getValues(), $block->getStates()) )[$meta] ?? null; if($properties === null) { throw new Exception("Unable to calculate permutations from block meta: " . $meta); @@ -31,12 +32,12 @@ public static function fromMeta(Permutable $block, int $meta): array { * Attempts to convert the block in to a meta value based on the possible permutations of the block. An exception is * thrown if the state of the block is not a possible combination of all the block properties. */ - public static function toMeta(Permutable $block): int { + public static function toMeta(BlockPermutations $block): int { $properties = self::getCartesianProduct( - array_map(static fn(BlockProperty $blockProperty) => $blockProperty->getValues(), $block->getBlockProperties()) + array_map(static fn(BlockState $blockProperty) => $blockProperty->getValues(), $block->getStates()) ); foreach($properties as $meta => $permutations){ - if($permutations === $block->getCurrentBlockProperties()) { + if($permutations === $block->getCurrentStates()) { return $meta; } } @@ -46,8 +47,8 @@ public static function toMeta(Permutable $block): int { /** * Returns the number of bits required to represent all the possible permutations of the block. */ - public static function getStateBitmask(Permutable $block): int { - $possibleValues = array_map(static fn(BlockProperty $blockProperty) => $blockProperty->getValues(), $block->getBlockProperties()); + public static function getStateBitmask(BlockPermutations $block): int { + $possibleValues = array_map(static fn(BlockState $blockProperty) => $blockProperty->getValues(), $block->getStates()); return count(self::getCartesianProduct($possibleValues)) - 1; } diff --git a/src/block/permutations/RotatableTrait.php b/src/block/permutations/RotatableTrait.php deleted file mode 100644 index 656809ba..00000000 --- a/src/block/permutations/RotatableTrait.php +++ /dev/null @@ -1,77 +0,0 @@ -withComponent(new TransformationComponent()), - (new Permutation("q.block_state('customies:rotation') == 3")) - ->withComponent(new TransformationComponent(new Vector3(0, 180, 0))), - (new Permutation("q.block_state('customies:rotation') == 4")) - ->withComponent(new TransformationComponent(new Vector3(0, 90, 0))), - (new Permutation("q.block_state('customies:rotation') == 5")) - ->withComponent(new TransformationComponent(new Vector3(0, -90, 0))), - ]; - } - - public function getCurrentBlockProperties(): array { - return [$this->facing]; - } - - protected function writeStateToMeta(): int { - return Permutations::toMeta($this); - } - - public function readStateFromData(int $id, int $stateMeta): void { - $blockProperties = Permutations::fromMeta($this, $stateMeta); - $this->facing = $blockProperties[0] ?? Facing::NORTH; - } - - public function getStateBitmask(): int { - return Permutations::getStateBitmask($this); - } - - public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, ?Player $player = null): bool { - if($player !== null) { - $this->facing = $player->getHorizontalFacing(); - } - return parent::place($tx, $item, $blockReplace, $blockClicked, $face, $clickVector, $player); - } - - public function serializeState(BlockStateWriter $out): void { - $out->writeInt("customies:rotation", $this->facing); - } - - public function deserializeState(BlockStateReader $in): void { - $this->facing = $in->readInt("customies:rotation"); - } -} \ No newline at end of file diff --git a/src/block/states/BlockState.php b/src/block/states/BlockState.php index 541c8f4e..fa055c04 100644 --- a/src/block/states/BlockState.php +++ b/src/block/states/BlockState.php @@ -3,41 +3,46 @@ namespace customiesdevs\customies\block\states; -use pocketmine\data\bedrock\block\convert\BlockStateReader; -use pocketmine\data\bedrock\block\convert\BlockStateWriter; +/** + * Represents a block state property with a name and array of possible values. + * Automatically detects the value type (bool, int, string) for serialization. + */ +class BlockState { -interface BlockState { + protected mixed $currentValue; /** - * Returns the state property name (e.g., "minecraft:cardinal_direction"). + * @param string $name The state property name (e.g., "customies:rotation") + * @param array $values Array of possible values (bool[], int[], or string[]) */ - public function getName(): string; + public function __construct( + protected readonly string $name, + protected readonly array $values + ) { + $this->currentValue = $values[0] ?? null; + } /** - * Returns the NBT value definition for client (enum + name). - * The "enum" values must be native PHP types (bool, int, string) that - * NBT::getTagType() can convert to the correct NBT tag type. + * Returns the state property name. */ - public function getValue(): array; + public function getName(): string { + return $this->name; + } /** - * Gets the current state value. + * Returns the possible values array. */ - public function getCurrentValue(): mixed; + public function getValues(): array { + return $this->values; + } /** - * Sets the current state value. - */ - public function setCurrentValue(mixed $value): void; - - /** - * Writes the current state value to the BlockStateWriter. - */ - public function serialize(BlockStateWriter $writer): void; - - /** - * Reads the state value from the BlockStateReader and sets it. + * Returns the NBT value definition for client (enum + name). */ - public function deserialize(BlockStateReader $reader): void; - + public function getValue(): array { + return [ + "enum" => $this->values, + "name" => $this->name + ]; + } } diff --git a/src/block/states/BlockStates.php b/src/block/states/BlockStates.php index 82c34a48..8d08f696 100644 --- a/src/block/states/BlockStates.php +++ b/src/block/states/BlockStates.php @@ -3,6 +3,9 @@ namespace customiesdevs\customies\block\states; +use pocketmine\data\bedrock\block\convert\BlockStateReader; +use pocketmine\data\bedrock\block\convert\BlockStateWriter; + interface BlockStates { /** @@ -30,4 +33,21 @@ public function getState(string $name): ?BlockState; * @return BlockState[] */ public function getStates(): array; + + /** + * Returns an array of the current block property values in the same order as those in getBlockProperties(). It is + * used to convert the current properties in to a meta value that can be stored on disk in the world. + * @return mixed[] + */ + public function getCurrentStates(): array; + + /** + * Serializes the block state to the given BlockStateWriter. + */ + public function serializeState(BlockStateWriter $blockStateOut): void; + + /** + * Deserializes the block state from the given BlockStateReader. + */ + public function deserializeState(BlockStateReader $blockStateIn): void; } diff --git a/src/block/states/BooleanState.php b/src/block/states/BooleanState.php deleted file mode 100644 index 34121db4..00000000 --- a/src/block/states/BooleanState.php +++ /dev/null @@ -1,41 +0,0 @@ -name; - } - - public function getValue(): array { - return [ - "enum" => [false, true], // Native bools -> NBT::getTagType() converts to ByteTag - "name" => $this->name - ]; - } - - public function getCurrentValue(): bool { - return $this->currentValue; - } - - public function setCurrentValue(mixed $value): void { - $this->currentValue = (bool) $value; - } - - public function serialize(BlockStateWriter $writer): void { - $writer->writeBool($this->name, $this->currentValue); - } - - public function deserialize(BlockStateReader $reader): void { - $this->currentValue = $reader->readBool($this->name); - } -} \ No newline at end of file diff --git a/src/block/states/IntRangeState.php b/src/block/states/IntRangeState.php deleted file mode 100644 index b47f4103..00000000 --- a/src/block/states/IntRangeState.php +++ /dev/null @@ -1,47 +0,0 @@ -currentValue = $min; - } - - public function getName(): string { - return $this->name; - } - - public function getValue(): array { - return [ - "enum" => range($this->min, $this->max), - "name" => $this->name - ]; - } - - public function getCurrentValue(): int { - return $this->currentValue; - } - - public function setCurrentValue(mixed $value): void { - $this->currentValue = max($this->min, min($this->max, (int) $value)); - } - - public function serialize(BlockStateWriter $writer): void { - $writer->writeInt($this->name, $this->currentValue); - } - - public function deserialize(BlockStateReader $reader): void { - $this->currentValue = $reader->readInt($this->name); - } -} \ No newline at end of file diff --git a/src/block/states/IntState.php b/src/block/states/IntState.php deleted file mode 100644 index 987169a3..00000000 --- a/src/block/states/IntState.php +++ /dev/null @@ -1,46 +0,0 @@ -currentValue = $values[0] ?? 0; - } - - public function getName(): string { - return $this->name; - } - - public function getValue(): array { - return [ - "enum" => $this->values, - "name" => $this->name - ]; - } - - public function getCurrentValue(): int { - return $this->currentValue; - } - - public function setCurrentValue(mixed $value): void { - $this->currentValue = (int) $value; - } - - public function serialize(BlockStateWriter $writer): void { - $writer->writeInt($this->name, $this->currentValue); - } - - public function deserialize(BlockStateReader $reader): void { - $this->currentValue = $reader->readInt($this->name); - } -} \ No newline at end of file diff --git a/src/block/states/StringState.php b/src/block/states/StringState.php deleted file mode 100644 index 729c6fdb..00000000 --- a/src/block/states/StringState.php +++ /dev/null @@ -1,46 +0,0 @@ -currentValue = $values[0] ?? ''; - } - - public function getName(): string { - return $this->name; - } - - public function getValue(): array { - return [ - "enum" => $this->values, - "name" => $this->name - ]; - } - - public function getCurrentValue(): string { - return $this->currentValue; - } - - public function setCurrentValue(mixed $value): void { - $this->currentValue = (string) $value; - } - - public function serialize(BlockStateWriter $writer): void { - $writer->writeString($this->name, $this->currentValue); - } - - public function deserialize(BlockStateReader $reader): void { - $this->currentValue = $reader->readString($this->name); - } -} \ No newline at end of file diff --git a/src/block/traits/BlockTrait.php b/src/block/traits/BlockTrait.php deleted file mode 100644 index cbe15200..00000000 --- a/src/block/traits/BlockTrait.php +++ /dev/null @@ -1,17 +0,0 @@ - - */ - private array $traits = []; - - /** - * Adds a block trait. - * @param BlockTrait $trait - */ - public function addTrait(BlockTrait $trait): void { - $this->traits[$trait->getName()] = $trait; - } - - /** - * Checks whether the block has a trait with the given name. - * @param string $name - * @return bool - */ - public function hasTrait(string $name): bool { - return isset($this->traits[$name]); - } - - /** - * Retrieves a trait by its name. - * @param string $name - * @return BlockTrait|null - */ - public function getTrait(string $name): ?BlockTrait { - return $this->traits[$name] ?? null; - } - - /** - * Returns all registered block traits. - * @return array - */ - public function getTraits(): array { - return $this->traits; - } -} diff --git a/src/block/traits/PlacementDirectionTrait.php b/src/block/traits/PlacementDirectionTrait.php deleted file mode 100644 index ee10a692..00000000 --- a/src/block/traits/PlacementDirectionTrait.php +++ /dev/null @@ -1,28 +0,0 @@ - [], - "enabled_states" => [ - "cardinal_direction" => in_array("minecraft:cardinal_direction", $this->state, true), - "corner_and_cardinal_direction" => in_array("minecraft:corner_and_cardinal_direction", $this->state, true), - "facing_direction" => in_array("minecraft:facing_direction", $this->state, true), - ], - "name" => $this->getName(), - "y_rotation_offset" => $this->yRotationOffset - ]; - } -} \ No newline at end of file diff --git a/src/block/traits/PlacementPositionTrait.php b/src/block/traits/PlacementPositionTrait.php deleted file mode 100644 index f5a1d7df..00000000 --- a/src/block/traits/PlacementPositionTrait.php +++ /dev/null @@ -1,24 +0,0 @@ - [ - "block_face" => in_array("minecraft:block_face", $this->state, true), - "vertical_half" => in_array("minecraft:vertical_half", $this->state, true), - ], - "name" => $this->getName() - ]; - } -} \ No newline at end of file diff --git a/src/item/ItemComponentsTrait.php b/src/item/ItemComponentsTrait.php index bd68591a..0fc6f24e 100644 --- a/src/item/ItemComponentsTrait.php +++ b/src/item/ItemComponentsTrait.php @@ -54,7 +54,7 @@ public function getComponents(): array { return $this->components; } - protected function initDefaultComponent(string $texture, string $name): void { + protected function initComponent(string $texture, string $name): void { $this->addComponent(new IconComponent($texture)); $this->addComponent(new DisplayNameComponent($name)); } diff --git a/src/json/BehaviorManager.php b/src/json/BehaviorManager.php index 51dcb067..e230fdcc 100644 --- a/src/json/BehaviorManager.php +++ b/src/json/BehaviorManager.php @@ -168,13 +168,10 @@ private function registerBlock(array $config): void { $identifier = $config["description"]["identifier"]; $components = $config["components"] ?? []; - $traits = $config["description"]["traits"] ?? []; - $states = $config["description"]["states"] ?? []; - $permutations = $config["permutations"] ?? []; $creativeInfo = $this->getCreativeInfo($config); CustomiesBlockFactory::getInstance()->registerBlock( - static fn(): Block => new CustomiesBlock($components, $traits, $states, $permutations), + static fn(): Block => new CustomiesBlock($components), $identifier, $creativeInfo ); diff --git a/src/json/BlockComponentRegistry.php b/src/json/BlockComponentRegistry.php index 34658fc9..c67aa03b 100644 --- a/src/json/BlockComponentRegistry.php +++ b/src/json/BlockComponentRegistry.php @@ -22,6 +22,7 @@ use customiesdevs\customies\block\component\RandomOffsetComponent; use customiesdevs\customies\block\component\SelectionBoxComponent; use customiesdevs\customies\block\component\SupportComponent; +use customiesdevs\customies\block\component\TransformationComponent; /** * Central registry for block component mappings. @@ -52,6 +53,7 @@ final class BlockComponentRegistry { 'minecraft:random_offset' => RandomOffsetComponent::class, 'minecraft:selection_box' => SelectionBoxComponent::class, 'minecraft:support' => SupportComponent::class, + 'minecraft:transformation' => TransformationComponent::class, ]; /** diff --git a/src/json/BlockPermutationRegistry.php b/src/json/BlockPermutationRegistry.php index 2084f768..d961b89b 100644 --- a/src/json/BlockPermutationRegistry.php +++ b/src/json/BlockPermutationRegistry.php @@ -3,8 +3,7 @@ namespace customiesdevs\customies\json; -use customiesdevs\customies\block\component\BlockComponent; -use customiesdevs\customies\block\blockpermutations\BlockPermutation; +use customiesdevs\customies\block\permutations\BlockPermutation; /** * Registry for block permutation mappings. diff --git a/src/json/BlockStateRegistry.php b/src/json/BlockStateRegistry.php index 74fe9fde..53fba333 100644 --- a/src/json/BlockStateRegistry.php +++ b/src/json/BlockStateRegistry.php @@ -4,10 +4,6 @@ namespace customiesdevs\customies\json; use customiesdevs\customies\block\states\BlockState; -use customiesdevs\customies\block\states\BooleanState; -use customiesdevs\customies\block\states\IntRangeState; -use customiesdevs\customies\block\states\IntState; -use customiesdevs\customies\block\states\StringState; /** * Registry for block state mappings. @@ -23,39 +19,24 @@ final class BlockStateRegistry { * - Integer: "customies:level": [0, 1, 2, 3] * - Integer range: "customies:age": { "values": { "min": 0, "max": 15 } } * - String: "customies:color": ["red", "green", "blue"] + * - Bedrock built-in: "minecraft:block_face": true (uses predefined state class) * * @param string $name State identifier * @param mixed $data JSON-decoded data for the state * @return BlockState|null Returns the state instance or null if invalid */ public static function fromJson(string $name, mixed $data): ?BlockState { + // Handle range format: { "values": { "min": 0, "max": 15 } } if(is_array($data) && isset($data['values']['min'], $data['values']['max'])) { - return new IntRangeState( - $name, - (int) $data['values']['min'], - (int) $data['values']['max'] - ); + $min = (int) $data['values']['min']; + $max = (int) $data['values']['max']; + return new BlockState($name, range($min, $max)); } - // Handle array format + // Handle array format - BlockState auto-detects type from values if(is_array($data) && $data !== []) { - $firstValue = $data[0] ?? null; - - // Boolean state: [false, true] - if(is_bool($firstValue)) { - return new BooleanState($name); - } - - // Integer state: [0, 1, 2, 3] - if(is_int($firstValue)) { - return new IntState($name, array_map('intval', $data)); - } - - // String state: ["north", "south", "east", "west"] - if(is_string($firstValue)) { - return new StringState($name, array_map('strval', $data)); - } + return new BlockState($name, $data); } return null; diff --git a/src/json/BlockTraitRegistry.php b/src/json/BlockTraitRegistry.php deleted file mode 100644 index 9a05f87f..00000000 --- a/src/json/BlockTraitRegistry.php +++ /dev/null @@ -1,74 +0,0 @@ -> - */ - private static array $traits = [ - 'minecraft:placement_direction' => PlacementDirectionTrait::class, - 'minecraft:placement_position' => PlacementPositionTrait::class, - ]; - - /** - * Register a custom block trait. - * - * @param string $name Trait identifier (e.g., 'minecraft:placement_direction') - * @param class-string $class Fully qualified class name implementing BlockTrait - */ - public static function register(string $name, string $class): void { - self::$traits[$name] = $class; - } - - /** - * Get the class name of a trait by its identifier. - * - * @param string $name Trait identifier - * @return class-string|null Returns the class name, or null if not registered - */ - public static function get(string $name): ?string { - return self::$traits[$name] ?? null; - } - - /** - * Check if a trait is registered. - * - * @param string $name Trait identifier - * @return bool True if registered, false otherwise - */ - public static function has(string $name): bool { - return isset(self::$traits[$name]); - } - - /** - * Instantiate a block trait from JSON data. - * - * @param string $name Trait identifier - * @param mixed $data JSON-decoded data for the trait - * @return BlockTrait|null Returns the trait instance or null if not registered - */ - public static function fromJson(string $name, mixed $data): ?BlockTrait { - return match($name) { - 'minecraft:placement_direction' => new PlacementDirectionTrait( - state: $data['enabled_states'] ?? [], - yRotationOffset: (float) ($data['y_rotation_offset'] ?? 0.0) - ), - 'minecraft:placement_position' => new PlacementPositionTrait( - state: $data['enabled_states'] ?? [] - ), - default => null - }; - } -} diff --git a/src/json/CustomiesBlock.php b/src/json/CustomiesBlock.php index e811351e..344cea6a 100644 --- a/src/json/CustomiesBlock.php +++ b/src/json/CustomiesBlock.php @@ -3,32 +3,37 @@ namespace customiesdevs\customies\json; -use customiesdevs\customies\block\component\BlockComponents; -use customiesdevs\customies\block\component\BlockComponentsTrait; use customiesdevs\customies\block\blockpermutations\BlockPermutations; use customiesdevs\customies\block\blockpermutations\BlockPermutationsTrait; -use customiesdevs\customies\block\states\BlockStates; -use customiesdevs\customies\block\states\BlockStatesTrait; -use customiesdevs\customies\block\traits\BlockTraits; -use customiesdevs\customies\block\traits\BlockTraitsTrait; +use customiesdevs\customies\block\component\BlockComponents; +use customiesdevs\customies\block\component\BlockComponentsTrait; use pocketmine\block\Block; use pocketmine\block\BlockBreakInfo; use pocketmine\block\BlockIdentifier; use pocketmine\block\BlockToolType; use pocketmine\block\BlockTypeIds; use pocketmine\block\BlockTypeInfo; +use pocketmine\block\utils\HorizontalFacingTrait; +use pocketmine\block\utils\PillarRotationTrait; +use pocketmine\data\bedrock\block\convert\BlockStateReader; +use pocketmine\data\bedrock\block\convert\BlockStateWriter; +use pocketmine\item\Item; +use pocketmine\math\Axis; +use pocketmine\math\Vector3; +use pocketmine\player\Player; +use pocketmine\world\BlockTransaction; -class CustomiesBlock extends Block implements BlockComponents, BlockTraits, BlockStates, BlockPermutations { +class CustomiesBlock extends Block implements BlockComponents, BlockPermutations { use BlockComponentsTrait; - use BlockTraitsTrait; - use BlockStatesTrait; use BlockPermutationsTrait; + use HorizontalFacingTrait; + use PillarRotationTrait; + /** * Constructs a new CustomiesBlock with the given configuration. * * @param array $components Associative array of component identifiers to data - * @param array $traits Associative array of trait identifiers to data * @param array $states Associative array of state identifiers to values * @param array $permutations Array of permutation definitions * @param float $hardness The hardness of the block (default 1.0) @@ -38,9 +43,6 @@ class CustomiesBlock extends Block implements BlockComponents, BlockTraits, Bloc */ public function __construct( array $components, - array $traits = [], - array $states = [], - array $permutations = [], float $hardness = 1.0, int $toolType = BlockToolType::NONE, int $toolHarvestLevel = 0, @@ -65,24 +67,38 @@ public function __construct( $this->addComponent($component); } } + } - // Add all registered traits - foreach($traits as $name => $data) { - $trait = BlockTraitRegistry::fromJson($name, $data); - if($trait !== null) { - $this->addTrait($trait); - } - } + public function getCurrentStates(): array { + return [$this->axis]; + } - // Add all registered states - foreach($states as $name => $data) { - $state = BlockStateRegistry::fromJson($name, $data); - if($state !== null) { - $this->addState($state); - } - } + public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, ?Player $player = null): bool { + $this->axis = match($face) { + 0, 1 => Axis::Y, // down, up + 2, 3 => Axis::Z, // north, south + 4, 5 => Axis::X, // west, east + default => Axis::Y + }; + return parent::place($tx, $item, $blockReplace, $blockClicked, $face, $clickVector, $player); + } + + public function serializeState(BlockStateWriter $out): void { + $rotation = match($this->axis) { + Axis::X => "east", + Axis::Y => "up", + Axis::Z => "north", + default => "down" + }; + $out->writeString("minecraft:block_face", $rotation); + } - // Add all permutations - $this->addPermutations(BlockPermutationRegistry::fromJson($permutations)); + public function deserializeState(BlockStateReader $in): void { + $this->axis = match($in->readString("minecraft:block_face")) { + "east", "west" => Axis::X, + "up", "down" => Axis::Y, + "north", "south" => Axis::Z, + default => Axis::Y + }; } } \ No newline at end of file From 63d04486ae855e9045f552af6ca69cdc2e3a75dc Mon Sep 17 00:00:00 2001 From: JeanTKG <102908437+jeantkg@users.noreply.github.com> Date: Sat, 27 Dec 2025 20:34:48 +0300 Subject: [PATCH 37/51] remove JSON loader --- src/Customies.php | 12 +- src/block/CustomiesBlockFactory.php | 1 - src/block/component/BlockComponent.php | 8 - src/block/component/CollisionBoxComponent.php | 35 ---- .../DestructibleByExplosionComponent.php | 4 - .../DestructibleByMiningComponent.php | 4 - .../DestructionParticlesComponent.php | 8 - src/block/component/DisplayNameComponent.php | 4 - .../component/EmbeddedVisualComponent.php | 11 - src/block/component/FlammableComponent.php | 7 - .../component/FlowerPottableComponent.php | 4 - src/block/component/FrictionComponent.php | 4 - src/block/component/GeometryComponent.php | 10 - src/block/component/ItemVisualComponent.php | 11 - .../component/LightDampeningComponent.php | 4 - .../component/LightEmissionComponent.php | 4 - .../component/LiquidDetectionComponent.php | 10 - src/block/component/MapColorComponent.php | 4 - .../component/MaterialInstancesComponent.php | 8 - .../component/PlacementFilterComponent.php | 8 - src/block/component/RandomOffsetComponent.php | 5 - src/block/component/SelectionBoxComponent.php | 19 -- src/block/component/SupportComponent.php | 4 - .../component/TransformationComponent.php | 19 -- src/block/example/ExampleBlock.php | 99 +++++++++ src/item/component/AllowOffHandComponent.php | 4 - .../component/BundleInteractionComponent.php | 4 - .../CanDestroyInCreativeComponent.php | 4 - src/item/component/CompostableComponent.php | 4 - src/item/component/CooldownComponent.php | 8 - .../component/DamageAbsorptionComponent.php | 15 -- src/item/component/DamageComponent.php | 4 - src/item/component/DiggerComponent.php | 7 - src/item/component/DisplayNameComponent.php | 4 - src/item/component/DurabilityComponent.php | 4 - .../component/DurabilitySensorComponent.php | 12 -- src/item/component/DyeableComponent.php | 7 - src/item/component/EnchantableComponent.php | 4 - src/item/component/FireResistantComponent.php | 4 - src/item/component/FoodComponent.php | 9 - src/item/component/FuelComponent.php | 4 - src/item/component/GlintComponent.php | 4 - src/item/component/HandEquippedComponent.php | 4 - .../component/HoverTextColorComponent.php | 4 - src/item/component/IconComponent.php | 10 - .../component/InteractButtonComponent.php | 4 - src/item/component/ItemComponent.php | 8 - src/item/component/KineticWeaponComponent.php | 21 -- src/item/component/LiquidClippedComponent.php | 4 - src/item/component/MaxStackSizeComponent.php | 4 - .../component/PiercingWeaponComponent.php | 8 - src/item/component/ProjectileComponent.php | 4 - src/item/component/RarityComponent.php | 4 - src/item/component/RecordComponent.php | 4 - src/item/component/RepairableComponent.php | 10 - src/item/component/ShooterComponent.php | 12 -- src/item/component/ShouldDespawnComponent.php | 4 - src/item/component/StackedByDataComponent.php | 4 - src/item/component/StorageItemComponent.php | 10 - .../component/StorageWeightLimitComponent.php | 4 - .../StorageWeightModifierComponent.php | 4 - src/item/component/SwingDurationComponent.php | 4 - src/item/component/SwingSoundsComponent.php | 8 - src/item/component/TagsComponent.php | 4 - src/item/component/ThrowableComponent.php | 11 - src/item/component/UseAnimationComponent.php | 4 - src/item/component/UseModifiersComponent.php | 9 - src/item/component/WearableComponent.php | 8 - src/json/BehaviorManager.php | 196 ------------------ src/json/BlockComponentRegistry.php | 110 ---------- src/json/BlockPermutationRegistry.php | 53 ----- src/json/BlockStateRegistry.php | 44 ---- src/json/CustomiesBlock.php | 104 ---------- src/json/CustomiesItem.php | 37 ---- src/json/ItemComponentRegistry.php | 156 -------------- src/util/ByteArray.php | 35 ---- src/util/NBT.php | 12 -- 77 files changed, 108 insertions(+), 1223 deletions(-) create mode 100644 src/block/example/ExampleBlock.php delete mode 100644 src/json/BehaviorManager.php delete mode 100644 src/json/BlockComponentRegistry.php delete mode 100644 src/json/BlockPermutationRegistry.php delete mode 100644 src/json/BlockStateRegistry.php delete mode 100644 src/json/CustomiesBlock.php delete mode 100644 src/json/CustomiesItem.php delete mode 100644 src/json/ItemComponentRegistry.php delete mode 100644 src/util/ByteArray.php diff --git a/src/Customies.php b/src/Customies.php index c7707613..9f19fda7 100644 --- a/src/Customies.php +++ b/src/Customies.php @@ -4,7 +4,8 @@ namespace customiesdevs\customies; use customiesdevs\customies\block\CustomiesBlockFactory; -use customiesdevs\customies\json\BehaviorManager; +use customiesdevs\customies\block\example\ExampleBlock; +use customiesdevs\customies\item\CreativeInventoryInfo; use pocketmine\plugin\PluginBase; use pocketmine\scheduler\ClosureTask; use pocketmine\utils\SingletonTrait; @@ -18,8 +19,13 @@ public function onLoad(): void{ protected function onEnable(): void { $this->getServer()->getPluginManager()->registerEvents(new CustomiesListener(), $this); - // Register all custom behavior JSON definitions - BehaviorManager::getInstance()->registerAll(); + + CustomiesBlockFactory::getInstance()->registerBlock( + static fn() => new ExampleBlock(), + "customies:example_block", + new CreativeInventoryInfo(CreativeInventoryInfo::CATEGORY_ITEMS) + ); + $this->getScheduler()->scheduleDelayedTask(new ClosureTask(static function (): void { // This task is scheduled with a 0-tick delay so it runs as soon as the server has started. Plugins should // register their custom blocks and entities in onEnable() before this is executed diff --git a/src/block/CustomiesBlockFactory.php b/src/block/CustomiesBlockFactory.php index 8bdfa3d1..da928153 100644 --- a/src/block/CustomiesBlockFactory.php +++ b/src/block/CustomiesBlockFactory.php @@ -8,7 +8,6 @@ use customiesdevs\customies\block\permutations\BlockPermutations; use customiesdevs\customies\block\permutations\Permutations; use customiesdevs\customies\block\component\BlockComponents; -use customiesdevs\customies\block\states\BlockStates; use customiesdevs\customies\item\CreativeInventoryInfo; use customiesdevs\customies\item\CustomiesItemFactory; use customiesdevs\customies\task\AsyncRegisterBlocksTask; diff --git a/src/block/component/BlockComponent.php b/src/block/component/BlockComponent.php index 53a9017c..c9790da2 100644 --- a/src/block/component/BlockComponent.php +++ b/src/block/component/BlockComponent.php @@ -16,12 +16,4 @@ public function getName(): string; * @return mixed */ public function getValue(): mixed; - - /** - * Create a component instance from decoded JSON (block definition) data. - * Implementations should be tolerant of missing keys and apply sensible defaults. - * @param mixed $data The raw value found under the component identifier in a block JSON. - * @return static - */ - public static function fromJson(mixed $data): static; } \ No newline at end of file diff --git a/src/block/component/CollisionBoxComponent.php b/src/block/component/CollisionBoxComponent.php index 6d6ae942..85c0198b 100644 --- a/src/block/component/CollisionBoxComponent.php +++ b/src/block/component/CollisionBoxComponent.php @@ -62,39 +62,4 @@ public function getValue(): array { "enabled" => $this->enabled ]; } - - public static function fromJson(mixed $data): static { - // false or true - if(is_bool($data)) { - return new self($data); - } - - $component = new self(true); - $boxes = []; - - // Array of boxes - if(is_array($data) && isset($data[0])) { - foreach($data as $box) { - $origin = $box['origin'] ?? [-8, 0, -8]; - $size = $box['size'] ?? [16, 24, 16]; - $boxes[] = new Box( - new Vector3($origin[0], $origin[1], $origin[2]), - new Vector3($size[0], $size[1], $size[2]) - ); - } - return $component->addBoxes($boxes); - } - - // Single box object - if(is_array($data) && isset($data['origin'])) { - $origin = $data['origin']; - $size = $data['size'] ?? [16, 24, 16]; - return $component->addBox(new Box( - new Vector3($origin[0], $origin[1], $origin[2]), - new Vector3($size[0], $size[1], $size[2]) - )); - } - - return $component; - } } \ No newline at end of file diff --git a/src/block/component/DestructibleByExplosionComponent.php b/src/block/component/DestructibleByExplosionComponent.php index eb3417b8..948f0cc4 100644 --- a/src/block/component/DestructibleByExplosionComponent.php +++ b/src/block/component/DestructibleByExplosionComponent.php @@ -23,8 +23,4 @@ public function getValue(): array { "value" => $this->explosionResistance ]; } - - public static function fromJson(mixed $data): static { - return new self($data["explosion_resistance"] ?? 0.0); - } } \ No newline at end of file diff --git a/src/block/component/DestructibleByMiningComponent.php b/src/block/component/DestructibleByMiningComponent.php index 27f4ff79..a02a4d48 100644 --- a/src/block/component/DestructibleByMiningComponent.php +++ b/src/block/component/DestructibleByMiningComponent.php @@ -23,8 +23,4 @@ public function getValue(): array { "value" => $this->secondsToDestroy ]; } - - public static function fromJson(mixed $data): static { - return new self($data["seconds_to_destroy"] ?? 0.0); - } } \ No newline at end of file diff --git a/src/block/component/DestructionParticlesComponent.php b/src/block/component/DestructionParticlesComponent.php index f320d5e8..23ac4dac 100644 --- a/src/block/component/DestructionParticlesComponent.php +++ b/src/block/component/DestructionParticlesComponent.php @@ -33,12 +33,4 @@ public function getValue(): array { "tint_method" => $this->tintMethod->value ]; } - - public static function fromJson(mixed $data): static { - return new self( - $data["particle_count"] ?? 100, - $data["texture"] ?? "", - TintMethod::tryFrom($data["tint_method"] ?? "") ?? TintMethod::NONE - ); - } } \ No newline at end of file diff --git a/src/block/component/DisplayNameComponent.php b/src/block/component/DisplayNameComponent.php index 325e0d36..2b6ca3b6 100644 --- a/src/block/component/DisplayNameComponent.php +++ b/src/block/component/DisplayNameComponent.php @@ -26,8 +26,4 @@ public function getValue(): array { "value" => $this->displayName ]; } - - public static function fromJson(mixed $data): static { - return new self($data); - } } \ No newline at end of file diff --git a/src/block/component/EmbeddedVisualComponent.php b/src/block/component/EmbeddedVisualComponent.php index f4aad8eb..bfc8fc8f 100644 --- a/src/block/component/EmbeddedVisualComponent.php +++ b/src/block/component/EmbeddedVisualComponent.php @@ -39,15 +39,4 @@ public function getValue(): array { "material_instances" => $materials ]; } - - public static function fromJson(mixed $data): static { - $materials = []; - foreach($data as $target => $materialData){ - $materials[] = Material::fromArray($target, $materialData); - } - return new self( - GeometryComponent::fromJson($data["geometry"] ?? []), - $materials - ); - } } \ No newline at end of file diff --git a/src/block/component/FlammableComponent.php b/src/block/component/FlammableComponent.php index 3db58149..eee995c7 100644 --- a/src/block/component/FlammableComponent.php +++ b/src/block/component/FlammableComponent.php @@ -27,11 +27,4 @@ public function getValue(): array { "destroy_chance_modifier" => $this->destroyChanceModifier ]; } - - public static function fromJson(mixed $data): static { - return new self( - $data["catch_chance_modifier"] ?? 5, - $data["destroy_chance_modifier"] ?? 20 - ); - } } \ No newline at end of file diff --git a/src/block/component/FlowerPottableComponent.php b/src/block/component/FlowerPottableComponent.php index 792fadbe..190cff9c 100644 --- a/src/block/component/FlowerPottableComponent.php +++ b/src/block/component/FlowerPottableComponent.php @@ -13,8 +13,4 @@ public function getName(): string { public function getValue(): array { return []; } - - public static function fromJson(mixed $data): static { - return new self(); - } } \ No newline at end of file diff --git a/src/block/component/FrictionComponent.php b/src/block/component/FrictionComponent.php index 8fb28d73..6433d36b 100644 --- a/src/block/component/FrictionComponent.php +++ b/src/block/component/FrictionComponent.php @@ -24,8 +24,4 @@ public function getValue(): array { "value" => $this->friction ]; } - - public static function fromJson(mixed $data): static { - return new self($data); - } } \ No newline at end of file diff --git a/src/block/component/GeometryComponent.php b/src/block/component/GeometryComponent.php index 8836ca78..2b8bcc2f 100644 --- a/src/block/component/GeometryComponent.php +++ b/src/block/component/GeometryComponent.php @@ -49,14 +49,4 @@ public function getValue(): array { "useBlockTypeLightAbsorption" => false ]; } - - public static function fromJson(mixed $data): static { - return new self( - $data["identifier"] ?? "minecraft:geometry.full_block", - $data["bone_visibility"] ?? [], - $data["culling"] ?? "", - $data["culling_layer"] ?? "minecraft:culling_layer.undefined", - $data["uv_lock"] ?? false - ); - } } \ No newline at end of file diff --git a/src/block/component/ItemVisualComponent.php b/src/block/component/ItemVisualComponent.php index b531d0aa..be00c932 100644 --- a/src/block/component/ItemVisualComponent.php +++ b/src/block/component/ItemVisualComponent.php @@ -41,15 +41,4 @@ public function getValue(): array { ] ]; } - - public static function fromJson(mixed $data): static { - $materials = []; - foreach($data as $target => $materialData){ - $materials[] = Material::fromArray($target, $materialData); - } - return new self( - GeometryComponent::fromJson($data["geometry"] ?? []), - $materials - ); - } } \ No newline at end of file diff --git a/src/block/component/LightDampeningComponent.php b/src/block/component/LightDampeningComponent.php index ceb83227..4798b3dc 100644 --- a/src/block/component/LightDampeningComponent.php +++ b/src/block/component/LightDampeningComponent.php @@ -24,8 +24,4 @@ public function getValue(): CompoundTag { return CompoundTag::create() ->setByte("lightLevel", $this->dampening); } - - public static function fromJson(mixed $data): static { - return new self($data ?? 15); - } } \ No newline at end of file diff --git a/src/block/component/LightEmissionComponent.php b/src/block/component/LightEmissionComponent.php index 0099e80d..fcd78996 100644 --- a/src/block/component/LightEmissionComponent.php +++ b/src/block/component/LightEmissionComponent.php @@ -24,8 +24,4 @@ public function getValue(): CompoundTag { return CompoundTag::create() ->setByte("emission", $this->emission); } - - public static function fromJson(mixed $data): static { - return new self($data ?? 0); - } } \ No newline at end of file diff --git a/src/block/component/LiquidDetectionComponent.php b/src/block/component/LiquidDetectionComponent.php index 073a0398..b4de75ca 100644 --- a/src/block/component/LiquidDetectionComponent.php +++ b/src/block/component/LiquidDetectionComponent.php @@ -71,14 +71,4 @@ public function getValue(): array { ] ]; } - - public static function fromJson(mixed $data): static { - $rule = $data["detectionRules"][0] ?? []; - return new self( - $rule["liquid_type"] ?? "water", - $rule["can_contain_liquid"] ?? false, - $rule["on_liquid_touches"] ?? self::BLOCKING, - $rule["stops_liquid_flowing_from_direction"] ?? [] - ); - } } \ No newline at end of file diff --git a/src/block/component/MapColorComponent.php b/src/block/component/MapColorComponent.php index c191e0e7..34656c66 100644 --- a/src/block/component/MapColorComponent.php +++ b/src/block/component/MapColorComponent.php @@ -23,8 +23,4 @@ public function getValue(): array { "color" => $this->color ]; } - - public static function fromJson(mixed $data): static { - return new self($data); - } } \ No newline at end of file diff --git a/src/block/component/MaterialInstancesComponent.php b/src/block/component/MaterialInstancesComponent.php index afc9c6fe..1648c6f2 100644 --- a/src/block/component/MaterialInstancesComponent.php +++ b/src/block/component/MaterialInstancesComponent.php @@ -35,12 +35,4 @@ public function getValue(): array { "materials" => $materials ]; } - - public static function fromJson(mixed $data): static { - $materials = []; - foreach($data as $target => $materialData){ - $materials[] = Material::fromArray($target, $materialData); - } - return new self($materials); - } } \ No newline at end of file diff --git a/src/block/component/PlacementFilterComponent.php b/src/block/component/PlacementFilterComponent.php index 6eb39f87..31866eb2 100644 --- a/src/block/component/PlacementFilterComponent.php +++ b/src/block/component/PlacementFilterComponent.php @@ -40,12 +40,4 @@ public function getValue(): array { ) ]; } - - public static function fromJson(mixed $data): static { - $conditions = []; - foreach($data["conditions"] ?? [] as $condition){ - $conditions[] = PlacementCondition::fromArray($condition); - } - return new self($conditions); - } } \ No newline at end of file diff --git a/src/block/component/RandomOffsetComponent.php b/src/block/component/RandomOffsetComponent.php index 109a7dce..56a4a527 100644 --- a/src/block/component/RandomOffsetComponent.php +++ b/src/block/component/RandomOffsetComponent.php @@ -18,9 +18,4 @@ public function getValue(): array { return [ ]; } - - // TODO Needs more data on this - public static function fromJson(mixed $data): static { - return new self(); - } } \ No newline at end of file diff --git a/src/block/component/SelectionBoxComponent.php b/src/block/component/SelectionBoxComponent.php index 0cc983e1..466139de 100644 --- a/src/block/component/SelectionBoxComponent.php +++ b/src/block/component/SelectionBoxComponent.php @@ -41,23 +41,4 @@ public function getValue(): array { ] ]; } - - public static function fromJson(mixed $data): static { - if (is_bool($data)) { - return new self($data); - } - return new self( - true, - new Vector3( - $data["origin"][0] ?? -8, - $data["origin"][1] ?? 0, - $data["origin"][2] ?? -8 - ), - new Vector3( - $data["size"][0] ?? 16, - $data["size"][1] ?? 16, - $data["size"][2] ?? 16 - ) - ); - } } \ No newline at end of file diff --git a/src/block/component/SupportComponent.php b/src/block/component/SupportComponent.php index 416d8bc0..53842dbe 100644 --- a/src/block/component/SupportComponent.php +++ b/src/block/component/SupportComponent.php @@ -25,8 +25,4 @@ public function getValue(): array { "shape" => $this->shape ]; } - - public static function fromJson(mixed $data): static { - return new self(is_array($data) ? ($data["shape"] ?? self::STAIRS) : self::STAIRS); - } } \ No newline at end of file diff --git a/src/block/component/TransformationComponent.php b/src/block/component/TransformationComponent.php index 62fb3c01..6e5af663 100644 --- a/src/block/component/TransformationComponent.php +++ b/src/block/component/TransformationComponent.php @@ -67,23 +67,4 @@ public function getValue(): array { "hasJsonVersionBeforeValidation" => false ]; } - - public static function fromJson(mixed $data): static { - $rotation = isset($data['rotation']) - ? new Vector3($data['rotation'][0] ?? 0, $data['rotation'][1] ?? 0, $data['rotation'][2] ?? 0) - : new Vector3(0, 0, 0); - $rotationPivot = isset($data['rotation_pivot']) - ? new Vector3($data['rotation_pivot'][0] ?? 0, $data['rotation_pivot'][1] ?? 0, $data['rotation_pivot'][2] ?? 0) - : new Vector3(0, 0, 0); - $scale = isset($data['scale']) - ? new Vector3($data['scale'][0] ?? 1, $data['scale'][1] ?? 1, $data['scale'][2] ?? 1) - : new Vector3(1, 1, 1); - $scalePivot = isset($data['scale_pivot']) - ? new Vector3($data['scale_pivot'][0] ?? 0, $data['scale_pivot'][1] ?? 0, $data['scale_pivot'][2] ?? 0) - : new Vector3(0, 0, 0); - $translation = isset($data['translation']) - ? new Vector3($data['translation'][0] ?? 0, $data['translation'][1] ?? 0, $data['translation'][2] ?? 0) - : new Vector3(0, 0, 0); - return new self($rotation, $rotationPivot, $scale, $scalePivot, $translation); - } } \ No newline at end of file diff --git a/src/block/example/ExampleBlock.php b/src/block/example/ExampleBlock.php new file mode 100644 index 00000000..a027dea7 --- /dev/null +++ b/src/block/example/ExampleBlock.php @@ -0,0 +1,99 @@ +addComponent(new GeometryComponent("minecraft:geometry.full_block")); + + // Material instances - bark on sides, tops on up/down + $this->addComponent(new MaterialInstancesComponent([ + new Material(Material::TARGET_ALL, "bum_template_bark"), + new Material(Material::TARGET_UP, "bum_template_tops"), + new Material(Material::TARGET_DOWN, "bum_template_tops"), + ])); + $this->addState(new BlockState("minecraft:block_face", ["north", "south", "east", "west", "up", "down"])); + $this->addPermutations([ + new BlockPermutation( + "q.block_state('minecraft:block_face') == 'west' || q.block_state('minecraft:block_face') == 'east'", + new TransformationComponent(new Vector3(0, 0, 90)) + ), + new BlockPermutation( + "q.block_state('minecraft:block_face') == 'down' || q.block_state('minecraft:block_face') == 'up'", + new TransformationComponent(new Vector3(0, 0, 0)) + ), + new BlockPermutation( + "q.block_state('minecraft:block_face') == 'north' || q.block_state('minecraft:block_face') == 'south'", + new TransformationComponent(new Vector3(90, 0, 0)) + ) + ]); + } + + public function getCurrentStates(): array { + return [$this->axis]; + } + + public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, ?Player $player = null): bool { + $this->axis = match($face) { + 0, 1 => Axis::Y, // down, up + 2, 3 => Axis::Z, // north, south + 4, 5 => Axis::X, // west, east + default => Axis::Y + }; + return parent::place($tx, $item, $blockReplace, $blockClicked, $face, $clickVector, $player); + } + + public function serializeState(BlockStateWriter $out): void { + $rotation = match($this->axis) { + Axis::X => "east", + Axis::Y => "up", + Axis::Z => "north", + default => "down" + }; + $out->writeString("minecraft:block_face", $rotation); + } + + public function deserializeState(BlockStateReader $in): void { + $this->axis = match($in->readString("minecraft:block_face")) { + "east", "west" => Axis::X, + "up", "down" => Axis::Y, + "north", "south" => Axis::Z, + default => Axis::Y + }; + } +} diff --git a/src/item/component/AllowOffHandComponent.php b/src/item/component/AllowOffHandComponent.php index 60f2a1a5..effb379c 100644 --- a/src/item/component/AllowOffHandComponent.php +++ b/src/item/component/AllowOffHandComponent.php @@ -28,8 +28,4 @@ public function getValue(): array { public function getPropertyMapping(): ?array { return ['allow_off_hand' => $this->offHand]; } - - public static function fromJson(mixed $data): static { - return new self($data ?? true); - } } \ No newline at end of file diff --git a/src/item/component/BundleInteractionComponent.php b/src/item/component/BundleInteractionComponent.php index 67ef945f..a9835017 100644 --- a/src/item/component/BundleInteractionComponent.php +++ b/src/item/component/BundleInteractionComponent.php @@ -29,8 +29,4 @@ public function getValue(): array { public function getPropertyMapping(): ?array { return null; } - - public static function fromJson(mixed $data): static { - return new self($data["num_viewable_slots"] ?? 12); - } } \ No newline at end of file diff --git a/src/item/component/CanDestroyInCreativeComponent.php b/src/item/component/CanDestroyInCreativeComponent.php index 8f1b797a..bb904355 100644 --- a/src/item/component/CanDestroyInCreativeComponent.php +++ b/src/item/component/CanDestroyInCreativeComponent.php @@ -28,8 +28,4 @@ public function getValue(): array { public function getPropertyMapping(): ?array { return ['can_destroy_in_creative' => $this->canDestroyInCreative]; } - - public static function fromJson(mixed $data): static { - return new self($data ?? true); - } } \ No newline at end of file diff --git a/src/item/component/CompostableComponent.php b/src/item/component/CompostableComponent.php index 9ffa5a4d..0e12fe7e 100644 --- a/src/item/component/CompostableComponent.php +++ b/src/item/component/CompostableComponent.php @@ -28,8 +28,4 @@ public function getValue(): array { public function getPropertyMapping(): ?array { return null; } - - public static function fromJson(mixed $data): static { - return new self($data["composting_chance"] ?? 0); - } } \ No newline at end of file diff --git a/src/item/component/CooldownComponent.php b/src/item/component/CooldownComponent.php index d8a1bae3..cee6c83d 100644 --- a/src/item/component/CooldownComponent.php +++ b/src/item/component/CooldownComponent.php @@ -45,12 +45,4 @@ public function getValue(): array { public function getPropertyMapping(): ?array { return null; } - - public static function fromJson(mixed $data): static { - return new self( - $data["category"] ?? self::CATEGORY_SHIELD, - $data["duration"] ?? 0.0, - $data["type"] ?? "use" - ); - } } \ No newline at end of file diff --git a/src/item/component/DamageAbsorptionComponent.php b/src/item/component/DamageAbsorptionComponent.php index a4cb4422..0e6029b6 100644 --- a/src/item/component/DamageAbsorptionComponent.php +++ b/src/item/component/DamageAbsorptionComponent.php @@ -51,19 +51,4 @@ public function addCause(DamageCause $cause): self { } return $this; } - - public static function fromJson(mixed $data): static { - $causes = []; - if(is_array($data["absorbable_causes"] ?? null)){ - foreach($data["absorbable_causes"] as $cause){ - if(is_string($cause)){ - $enumCause = DamageCause::tryFrom($cause); - if($enumCause !== null){ - $causes[] = $enumCause; - } - } - } - } - return new self($causes); - } } \ No newline at end of file diff --git a/src/item/component/DamageComponent.php b/src/item/component/DamageComponent.php index ea6d5ef2..073c75d4 100644 --- a/src/item/component/DamageComponent.php +++ b/src/item/component/DamageComponent.php @@ -29,8 +29,4 @@ public function getValue(): array { public function getPropertyMapping(): ?array { return ['damage' => $this->damage]; } - - public static function fromJson(mixed $data): static { - return new self($data ?? 0); - } } \ No newline at end of file diff --git a/src/item/component/DiggerComponent.php b/src/item/component/DiggerComponent.php index e9a76a3b..54a03ddd 100644 --- a/src/item/component/DiggerComponent.php +++ b/src/item/component/DiggerComponent.php @@ -79,11 +79,4 @@ public function withTags(int $speed, string ...$tags): self { public function getDestroySpeeds(): array { return $this->destroySpeeds; } - - public static function fromJson(mixed $data): static { - return new self( - $data["use_efficiency"] ?? false, - $data["destroy_speeds"] ?? [] - ); - } } \ No newline at end of file diff --git a/src/item/component/DisplayNameComponent.php b/src/item/component/DisplayNameComponent.php index 9fba8127..4236190c 100644 --- a/src/item/component/DisplayNameComponent.php +++ b/src/item/component/DisplayNameComponent.php @@ -29,8 +29,4 @@ public function getValue(): array { public function getPropertyMapping(): ?array { return null; } - - public static function fromJson(mixed $data): static { - return new self($data["value"] ?? ""); - } } \ No newline at end of file diff --git a/src/item/component/DurabilityComponent.php b/src/item/component/DurabilityComponent.php index 7841b32a..5109cbac 100644 --- a/src/item/component/DurabilityComponent.php +++ b/src/item/component/DurabilityComponent.php @@ -38,8 +38,4 @@ public function getValue(): array { public function getPropertyMapping(): ?array { return null; } - - public static function fromJson(mixed $data): static { - return new self($data["max_durability"] ?? 0, $data["damage_chance"]["min"] ?? 100, $data["damage_chance"]["max"] ?? 100); - } } \ No newline at end of file diff --git a/src/item/component/DurabilitySensorComponent.php b/src/item/component/DurabilitySensorComponent.php index ff866328..bdfe830c 100644 --- a/src/item/component/DurabilitySensorComponent.php +++ b/src/item/component/DurabilitySensorComponent.php @@ -58,16 +58,4 @@ public function addDurabilityThreshold( ]; return $this; } - - public static function fromJson(mixed $data): static { - $thresholds = []; - foreach($data["durability_thresholds"] ?? [] as $threshold) { - $thresholds[] = [ - "durability" => $threshold["durability"] ?? 0, - "particle_type" => isset($threshold["particle_type"]) ? ParticleType::tryFrom($threshold["particle_type"]) : null, - "sound_event" => isset($threshold["sound_event"]) ? SoundEvent::tryFrom($threshold["sound_event"]) : null - ]; - } - return new self($thresholds); - } } \ No newline at end of file diff --git a/src/item/component/DyeableComponent.php b/src/item/component/DyeableComponent.php index fb6ab377..75df6c12 100644 --- a/src/item/component/DyeableComponent.php +++ b/src/item/component/DyeableComponent.php @@ -61,11 +61,4 @@ private static function rgbToHex(array $rgb): string { [$r, $g, $b] = $rgb; return sprintf("#%02x%02x%02x", $r, $g, $b); } - - public static function fromJson(mixed $data): static { - if(isset($data["default_color"]) && is_array($data["default_color"])){ - return new self(self::rgbToHex($data["default_color"])); - } - return new self("#ffffff"); - } } \ No newline at end of file diff --git a/src/item/component/EnchantableComponent.php b/src/item/component/EnchantableComponent.php index c4044164..037f21c5 100644 --- a/src/item/component/EnchantableComponent.php +++ b/src/item/component/EnchantableComponent.php @@ -79,8 +79,4 @@ public function getValue(): array { public function getPropertyMapping(): ?array { return ['enchantable_slot' => $this->slot, 'enchantable_value' => $this->value]; } - - public static function fromJson(mixed $data): static { - return new self($data["slot"] ?? self::SLOT_ALL, $data["value"] ?? 1); - } } \ No newline at end of file diff --git a/src/item/component/FireResistantComponent.php b/src/item/component/FireResistantComponent.php index a686d01c..031e32d9 100644 --- a/src/item/component/FireResistantComponent.php +++ b/src/item/component/FireResistantComponent.php @@ -28,8 +28,4 @@ public function getValue(): array { public function getPropertyMapping(): ?array { return null; } - - public static function fromJson(mixed $data): static { - return new self($data ?? true); - } } \ No newline at end of file diff --git a/src/item/component/FoodComponent.php b/src/item/component/FoodComponent.php index 5b51e538..ee32b628 100644 --- a/src/item/component/FoodComponent.php +++ b/src/item/component/FoodComponent.php @@ -40,13 +40,4 @@ public function getValue(): array { public function getPropertyMapping(): ?array { return null; } - - public static function fromJson(mixed $data): static { - return new self( - $data["can_always_eat"] ?? false, - $data["nutrition"] ?? 0, - $data["saturation_modifier"] ?? 0.6, - $data["using_converts_to"] ?? "" - ); - } } \ No newline at end of file diff --git a/src/item/component/FuelComponent.php b/src/item/component/FuelComponent.php index ec95af6e..811ca5e1 100644 --- a/src/item/component/FuelComponent.php +++ b/src/item/component/FuelComponent.php @@ -28,8 +28,4 @@ public function getValue(): array { public function getPropertyMapping(): ?array { return null; } - - public static function fromJson(mixed $data): static { - return new self($data["duration"] ?? 0.0); - } } \ No newline at end of file diff --git a/src/item/component/GlintComponent.php b/src/item/component/GlintComponent.php index 9d6f5f8d..e766a5c0 100644 --- a/src/item/component/GlintComponent.php +++ b/src/item/component/GlintComponent.php @@ -28,8 +28,4 @@ public function getValue(): array { public function getPropertyMapping(): ?array { return ['foil' => $this->glint]; } - - public static function fromJson(mixed $data): static { - return new self($data ?? true); - } } \ No newline at end of file diff --git a/src/item/component/HandEquippedComponent.php b/src/item/component/HandEquippedComponent.php index 09b159c5..b67574b5 100644 --- a/src/item/component/HandEquippedComponent.php +++ b/src/item/component/HandEquippedComponent.php @@ -28,8 +28,4 @@ public function getValue(): array { public function getPropertyMapping(): ?array { return ['hand_equipped' => $this->handEquipped]; } - - public static function fromJson(mixed $data): static { - return new self($data ?? true); - } } \ No newline at end of file diff --git a/src/item/component/HoverTextColorComponent.php b/src/item/component/HoverTextColorComponent.php index 13836ef6..0761ed2f 100644 --- a/src/item/component/HoverTextColorComponent.php +++ b/src/item/component/HoverTextColorComponent.php @@ -29,8 +29,4 @@ public function getValue(): array { public function getPropertyMapping(): ?array { return ['hover_text_color' => $this->hoverTextColor]; } - - public static function fromJson(mixed $data): static { - return new self($data ?? "white"); - } } \ No newline at end of file diff --git a/src/item/component/IconComponent.php b/src/item/component/IconComponent.php index 1ec71d90..a28f5bb3 100644 --- a/src/item/component/IconComponent.php +++ b/src/item/component/IconComponent.php @@ -49,14 +49,4 @@ public function getValue(): array { public function getPropertyMapping(): ?array { return null; } - - public static function fromJson(mixed $data): static { - return new self( - $data["textures"]["default"] ?? "customies:missing_texture", - $data["textures"]["dyed"] ?? "", - $data["textures"]["icon_trim"] ?? "", - $data["textures"]["bundle_open_back"] ?? "", - $data["textures"]["bundle_open_front"] ?? "" - ); - } } \ No newline at end of file diff --git a/src/item/component/InteractButtonComponent.php b/src/item/component/InteractButtonComponent.php index eacf2d75..3e335acf 100644 --- a/src/item/component/InteractButtonComponent.php +++ b/src/item/component/InteractButtonComponent.php @@ -33,8 +33,4 @@ public function getValue(): array { public function getPropertyMapping(): ?array { return null; } - - public static function fromJson(mixed $data): static { - return new self($data ?? "action.interact.use"); - } } \ No newline at end of file diff --git a/src/item/component/ItemComponent.php b/src/item/component/ItemComponent.php index b3257766..800af220 100644 --- a/src/item/component/ItemComponent.php +++ b/src/item/component/ItemComponent.php @@ -25,12 +25,4 @@ public function getValue(): mixed; * @return array|null [propertyName => propertyValue] or null if not a property */ public function getPropertyMapping(): ?array; - - /** - * Create a component instance from decoded JSON (item definition) data. - * Implementations should be tolerant of missing keys and apply sensible defaults. - * @param mixed $data The raw value found under the component identifier in an item JSON. - * @return static - */ - public static function fromJson(mixed $data): static; } \ No newline at end of file diff --git a/src/item/component/KineticWeaponComponent.php b/src/item/component/KineticWeaponComponent.php index 4345e65d..5d62d504 100644 --- a/src/item/component/KineticWeaponComponent.php +++ b/src/item/component/KineticWeaponComponent.php @@ -77,25 +77,4 @@ public function getValue(): array { public function getPropertyMapping(): ?array { return null; } - - public static function fromJson(mixed $data): static { - return new self( - $data['creative_reach'] ?? ['min' => 2.0, 'max' => 7.5], - (float) ($data['damage_modifier'] ?? 0.0), - (float) ($data['damage_multiplier'] ?? 0.7), - (int) ($data['delay'] ?? 15), - $data['dismount_conditions'] ?? [ - 'min_speed' => 14.0, - 'min_relative_speed' => 0.0, - 'max_duration' => 100 - ], - (float) ($data['hitbox_margin'] ?? 0.25), - $data['knockback_conditions'] ?? [ - 'min_speed' => 14.0, - 'min_relative_speed' => 0.0, - 'max_duration' => 120 - ], - $data['reach'] ?? ['min' => 2.0, 'max' => 4.5] - ); - } } \ No newline at end of file diff --git a/src/item/component/LiquidClippedComponent.php b/src/item/component/LiquidClippedComponent.php index ed7f72eb..914be38d 100644 --- a/src/item/component/LiquidClippedComponent.php +++ b/src/item/component/LiquidClippedComponent.php @@ -28,8 +28,4 @@ public function getValue(): array { public function getPropertyMapping(): ?array { return ['liquid_clipped' => $this->liquidClipped]; } - - public static function fromJson(mixed $data): static { - return new self($data ?? true); - } } \ No newline at end of file diff --git a/src/item/component/MaxStackSizeComponent.php b/src/item/component/MaxStackSizeComponent.php index 0b608b1e..57f1fc5d 100644 --- a/src/item/component/MaxStackSizeComponent.php +++ b/src/item/component/MaxStackSizeComponent.php @@ -28,8 +28,4 @@ public function getValue(): array { public function getPropertyMapping(): ?array { return ['max_stack_size' => $this->maxStackSize]; } - - public static function fromJson(mixed $data): static { - return new self($data ?? 64); - } } \ No newline at end of file diff --git a/src/item/component/PiercingWeaponComponent.php b/src/item/component/PiercingWeaponComponent.php index 4f0db69f..e104d41b 100644 --- a/src/item/component/PiercingWeaponComponent.php +++ b/src/item/component/PiercingWeaponComponent.php @@ -40,12 +40,4 @@ public function getValue(): array { public function getPropertyMapping(): ?array { return null; } - - public static function fromJson(mixed $data): static { - return new self( - $data['creative_reach'] ?? ['min' => 2.0, 'max' => 7.5], - (float) ($data['hitbox_margin'] ?? 0.25), - $data['reach'] ?? ['min' => 2.0, 'max' => 4.5] - ); - } } \ No newline at end of file diff --git a/src/item/component/ProjectileComponent.php b/src/item/component/ProjectileComponent.php index 000a3620..0994c0fa 100644 --- a/src/item/component/ProjectileComponent.php +++ b/src/item/component/ProjectileComponent.php @@ -34,8 +34,4 @@ public function getValue(): array { public function getPropertyMapping(): ?array { return null; } - - public static function fromJson(mixed $data): static { - return new self($data["minimum_critical_power"] ?? 0.0, $data["projectile_entity"] ?? ""); - } } \ No newline at end of file diff --git a/src/item/component/RarityComponent.php b/src/item/component/RarityComponent.php index 319de6ad..f5bfb27f 100644 --- a/src/item/component/RarityComponent.php +++ b/src/item/component/RarityComponent.php @@ -34,8 +34,4 @@ public function getValue(): array { public function getPropertyMapping(): ?array { return null; } - - public static function fromJson(mixed $data): static { - return new self($data ?? self::COMMON); - } } \ No newline at end of file diff --git a/src/item/component/RecordComponent.php b/src/item/component/RecordComponent.php index e992869b..3795f427 100644 --- a/src/item/component/RecordComponent.php +++ b/src/item/component/RecordComponent.php @@ -36,8 +36,4 @@ public function getValue(): array { public function getPropertyMapping(): ?array { return null; } - - public static function fromJson(mixed $data): static { - return new self($data["comparator_signal"] ?? 1, $data["duration"] ?? 0.0, $data["sound_event"] ?? ""); - } } \ No newline at end of file diff --git a/src/item/component/RepairableComponent.php b/src/item/component/RepairableComponent.php index 45ecdb00..83e7f3a2 100644 --- a/src/item/component/RepairableComponent.php +++ b/src/item/component/RepairableComponent.php @@ -32,14 +32,4 @@ public function getValue(): array { public function getPropertyMapping(): ?array { return null; } - - public static function fromJson(mixed $data): static { - $repairItems = []; - if(is_array($data["repair_items"] ?? null)) { - foreach($data["repair_items"] as $repairItem) { - $repairItems[] = RepairItems::fromArray($repairItem); - } - } - return new self($repairItems); - } } \ No newline at end of file diff --git a/src/item/component/ShooterComponent.php b/src/item/component/ShooterComponent.php index 62fc7b0c..608808a6 100644 --- a/src/item/component/ShooterComponent.php +++ b/src/item/component/ShooterComponent.php @@ -60,16 +60,4 @@ public function getValue(): array { public function getPropertyMapping(): ?array { return null; } - - public static function fromJson(mixed $data): static { - return new self( - $data["ammunition"][0]["item"] ?? "", - $data["ammunition"][0]["use_offhand"] ?? false, - $data["ammunition"][0]["search_inventory"] ?? false, - $data["ammunition"][0]["use_in_creative"] ?? false, - $data["charge_on_draw"] ?? false, - $data["max_draw_duration"] ?? 0.0, - $data["scale_power_by_draw_duration"] ?? false - ); - } } \ No newline at end of file diff --git a/src/item/component/ShouldDespawnComponent.php b/src/item/component/ShouldDespawnComponent.php index 76ec7e71..1ff046c0 100644 --- a/src/item/component/ShouldDespawnComponent.php +++ b/src/item/component/ShouldDespawnComponent.php @@ -28,8 +28,4 @@ public function getValue(): array { public function getPropertyMapping(): ?array { return ['should_despawn' => $this->shouldDespawn]; } - - public static function fromJson(mixed $data): static { - return new self($data ?? true); - } } \ No newline at end of file diff --git a/src/item/component/StackedByDataComponent.php b/src/item/component/StackedByDataComponent.php index 5b006c7e..f5888204 100644 --- a/src/item/component/StackedByDataComponent.php +++ b/src/item/component/StackedByDataComponent.php @@ -29,8 +29,4 @@ public function getValue(): array { public function getPropertyMapping(): ?array { return ['stacked_by_data' => $this->stackedByData]; } - - public static function fromJson(mixed $data): static { - return new self($data ?? true); - } } \ No newline at end of file diff --git a/src/item/component/StorageItemComponent.php b/src/item/component/StorageItemComponent.php index 2fb5124e..4372934b 100644 --- a/src/item/component/StorageItemComponent.php +++ b/src/item/component/StorageItemComponent.php @@ -91,14 +91,4 @@ private function containsItem(array $list, string $name): bool { } return false; } - - public static function fromJson(mixed $data): static { - $self = new self( - $data["allow_nested_storage_items"] ?? true, - $data["max_slots"] ?? 64 - ); - $self->allowedItems = $data["allowed_items"] ?? []; - $self->bannedItems = $data["banned_items"] ?? []; - return $self; - } } \ No newline at end of file diff --git a/src/item/component/StorageWeightLimitComponent.php b/src/item/component/StorageWeightLimitComponent.php index 1c2fe1a1..a3540369 100644 --- a/src/item/component/StorageWeightLimitComponent.php +++ b/src/item/component/StorageWeightLimitComponent.php @@ -28,8 +28,4 @@ public function getValue(): array { public function getPropertyMapping(): ?array { return null; } - - public static function fromJson(mixed $data): static { - return new self($data["max_weight_limit"] ?? 64); - } } \ No newline at end of file diff --git a/src/item/component/StorageWeightModifierComponent.php b/src/item/component/StorageWeightModifierComponent.php index fe75be83..26d20170 100644 --- a/src/item/component/StorageWeightModifierComponent.php +++ b/src/item/component/StorageWeightModifierComponent.php @@ -28,8 +28,4 @@ public function getValue(): array { public function getPropertyMapping(): ?array { return null; } - - public static function fromJson(mixed $data): static { - return new self($data["weight_in_storage_item"] ?? 4); - } } \ No newline at end of file diff --git a/src/item/component/SwingDurationComponent.php b/src/item/component/SwingDurationComponent.php index 992c9127..bc00698f 100644 --- a/src/item/component/SwingDurationComponent.php +++ b/src/item/component/SwingDurationComponent.php @@ -28,8 +28,4 @@ public function getValue(): array { public function getPropertyMapping(): ?array { return null; } - - public static function fromJson(mixed $data): static { - return new self($data ?? 0.3); - } } \ No newline at end of file diff --git a/src/item/component/SwingSoundsComponent.php b/src/item/component/SwingSoundsComponent.php index 37bd8b40..128fcba7 100644 --- a/src/item/component/SwingSoundsComponent.php +++ b/src/item/component/SwingSoundsComponent.php @@ -34,12 +34,4 @@ public function getValue(): array { public function getPropertyMapping(): ?array { return null; } - - public static function fromJson(mixed $data): static { - return new self( - $data["attack_critical_hit"] ?? "attack.critical", - $data["attack_hit"] ?? "attack.strong", - $data["attack_miss"] ?? "attack.nodamage" - ); - } } \ No newline at end of file diff --git a/src/item/component/TagsComponent.php b/src/item/component/TagsComponent.php index 0c6119d7..0c755c34 100644 --- a/src/item/component/TagsComponent.php +++ b/src/item/component/TagsComponent.php @@ -28,8 +28,4 @@ public function getValue(): array { public function getPropertyMapping(): ?array { return null; } - - public static function fromJson(mixed $data): static { - return new self(is_array($data["tags"] ?? null) ? $data["tags"] : []); - } } \ No newline at end of file diff --git a/src/item/component/ThrowableComponent.php b/src/item/component/ThrowableComponent.php index 51318757..ed082924 100644 --- a/src/item/component/ThrowableComponent.php +++ b/src/item/component/ThrowableComponent.php @@ -55,15 +55,4 @@ public function getValue(): array { public function getPropertyMapping(): ?array { return null; } - - public static function fromJson(mixed $data): static { - return new self( - $data["do_swing_animation"] ?? false, - $data["launch_power_scale"] ?? 1.0, - $data["max_draw_duration"] ?? 0.0, - $data["max_launch_power"] ?? 1.0, - $data["min_draw_duration"] ?? 0.0, - $data["scale_power_by_draw_duration"] ?? false - ); - } } \ No newline at end of file diff --git a/src/item/component/UseAnimationComponent.php b/src/item/component/UseAnimationComponent.php index 7596f729..06b43c66 100644 --- a/src/item/component/UseAnimationComponent.php +++ b/src/item/component/UseAnimationComponent.php @@ -52,8 +52,4 @@ public function getValue(): array { public function getPropertyMapping(): ?array { return ['use_animation' => self::STRING_TO_INT[$this->animation] ?? 0]; } - - public static function fromJson(mixed $data): static { - return new self($data ?? self::ANIMATION_NONE); - } } \ No newline at end of file diff --git a/src/item/component/UseModifiersComponent.php b/src/item/component/UseModifiersComponent.php index 796a47a2..ee3a8a90 100644 --- a/src/item/component/UseModifiersComponent.php +++ b/src/item/component/UseModifiersComponent.php @@ -48,13 +48,4 @@ public function getValue(): array { public function getPropertyMapping(): ?array { return ['use_duration' => $this->useDuration]; } - - public static function fromJson(mixed $data): static { - return new self( - (float) ($data["movement_modifier"] ?? 1.0), - (float) ($data["use_duration"] ?? 0.0), - (bool) ($data["emit_vibrations"] ?? false), - $data["start_sound"] ?? null - ); - } } \ No newline at end of file diff --git a/src/item/component/WearableComponent.php b/src/item/component/WearableComponent.php index 76ec7810..f9069e0f 100644 --- a/src/item/component/WearableComponent.php +++ b/src/item/component/WearableComponent.php @@ -52,12 +52,4 @@ public function getValue(): array { public function getPropertyMapping(): ?array { return null; } - - public static function fromJson(mixed $data): static { - return new self( - $data["slot"] ?? self::SLOT_WEAPON_MAIN_HAND, - $data["protection"] ?? 0, - $data["hides_player_location"] ?? false - ); - } } \ No newline at end of file diff --git a/src/json/BehaviorManager.php b/src/json/BehaviorManager.php deleted file mode 100644 index e230fdcc..00000000 --- a/src/json/BehaviorManager.php +++ /dev/null @@ -1,196 +0,0 @@ -behaviorDirectory = Customies::getInstance()->getDataFolder() . "behavior/"; - $this->ensureDirectoriesExist(); - } - - /** - * Ensures that the 'items' and 'blocks' subdirectories exist. - */ - private function ensureDirectoriesExist(): void { - foreach(['items', 'blocks'] as $subdir) { - $path = $this->behaviorDirectory . $subdir . "/"; - if(!is_dir($path)) { - mkdir($path, 0777, true); - } - } - } - - /** - * Gets all JSON files in a given subdirectory. - * - * @param string $subdir Subdirectory name ('items' or 'blocks') - * @return string[] List of JSON filenames - */ - private function getJsonFiles(string $subdir): array { - $path = $this->behaviorDirectory . $subdir . "/"; - $files = scandir($path); - if($files === false) { - return []; - } - return array_filter($files, static function(string $file) use ($path): bool { - return $file !== '.' && - $file !== '..' && - is_file($path . $file) && - str_ends_with($file, '.json'); - }); - } - - /** - * Reads a JSON configuration file. - * - * @param string $subdir Subdirectory name - * @param string $filename JSON filename - * @return Config - */ - private function getConfig(string $subdir, string $filename): Config { - return new Config($this->behaviorDirectory . $subdir . "/" . $filename, Config::JSON); - } - - /** - * Registers all items and blocks from behavior files. - */ - public function registerAll(): void { - $this->registerItems(); - $this->registerBlocks(); - } - - /** - * Registers all items from JSON files. - */ - public function registerItems(): void { - $this->registerFromDirectory('items', 'minecraft:item', function(array $config): void { - $this->registerItem($config); - }); - } - - /** - * Registers all blocks from JSON files. - */ - public function registerBlocks(): void { - $this->registerFromDirectory('blocks', 'minecraft:block', function(array $config): void { - $this->registerBlock($config); - }); - } - - /** - * Generic method for registering items or blocks from a directory. - * - * @param string $subdir Subdirectory name ('items' or 'blocks') - * @param string $rootKey Root key in JSON ('minecraft:item' or 'minecraft:block') - * @param callable(array): void $register Callback to register each entry - */ - private function registerFromDirectory(string $subdir, string $rootKey, callable $register): void { - $registeredCount = 0; - $errorCount = 0; - $type = rtrim($subdir, 's'); // 'items' -> 'item', 'blocks' -> 'block' - foreach($this->getJsonFiles($subdir) as $file) { - try { - $config = $this->getConfig($subdir, $file)->getAll(); - if(!isset($config[$rootKey])) { - throw new \InvalidArgumentException("Missing '$rootKey' in JSON file"); - } - $register($config[$rootKey]); - $registeredCount++; - }catch(\Exception $e) { - $errorCount++; - Customies::getInstance()->getLogger()->error( - "Failed to register $type from '$file': " . $e->getMessage() - ); - } - } - if($registeredCount > 0 || $errorCount > 0) { - Customies::getInstance()->getLogger()->info( - "Registered $registeredCount custom {$subdir}" . - ($errorCount > 0 ? " ($errorCount failed)" : "") - ); - } - } - - /** - * Registers a single item from configuration. - * - * @param array $config JSON-decoded item configuration - */ - private function registerItem(array $config): void { - if(!isset($config["components"], $config["description"]["identifier"])) { - throw new \InvalidArgumentException("Missing required fields 'components' or 'description.identifier'"); - } - $identifier = $config["description"]["identifier"]; - $components = $config["components"]; - $creativeInfo = $this->getCreativeInfo($config); - CustomiesItemFactory::getInstance()->registerItem( - static fn() => new CustomiesItem($components), - $identifier, - $creativeInfo - ); - } - - /** - * Registers a single block from configuration. - * - * @param array $config JSON-decoded block configuration - */ - private function registerBlock(array $config): void { - if(!isset($config["description"]["identifier"])) { - throw new \InvalidArgumentException("Missing required field 'description.identifier'"); - } - - $identifier = $config["description"]["identifier"]; - $components = $config["components"] ?? []; - $creativeInfo = $this->getCreativeInfo($config); - - CustomiesBlockFactory::getInstance()->registerBlock( - static fn(): Block => new CustomiesBlock($components), - $identifier, - $creativeInfo - ); - } - - /** - * Extracts CreativeInventoryInfo from configuration. - * - * @param array $config JSON-decoded configuration - * @return CreativeInventoryInfo - */ - private function getCreativeInfo(array $config): CreativeInventoryInfo { - $category = CreativeInventoryInfo::CATEGORY_ITEMS; - $group = CreativeInventoryInfo::NONE; - if(isset($config["description"]["menu_category"])) { - $menuCategory = $config["description"]["menu_category"]; - $category = $menuCategory["category"] ?? $category; - $group = $menuCategory["group"] ?? $group; - } - return new CreativeInventoryInfo($category, $group); - } -} \ No newline at end of file diff --git a/src/json/BlockComponentRegistry.php b/src/json/BlockComponentRegistry.php deleted file mode 100644 index c67aa03b..00000000 --- a/src/json/BlockComponentRegistry.php +++ /dev/null @@ -1,110 +0,0 @@ -> - */ - private static array $components = [ - 'minecraft:collision_box' => CollisionBoxComponent::class, - 'minecraft:destructible_by_explosion' => DestructibleByExplosionComponent::class, - 'minecraft:destructible_by_mining' => DestructibleByMiningComponent::class, - 'minecraft:destruction_particles' => DestructionParticlesComponent::class, - 'minecraft:display_name' => DisplayNameComponent::class, - 'minecraft:flammable' => FlammableComponent::class, - 'minecraft:friction' => FrictionComponent::class, - 'minecraft:geometry' => GeometryComponent::class, - 'minecraft:item_visual' => ItemVisualComponent::class, - 'minecraft:light_dampening' => LightDampeningComponent::class, - 'minecraft:light_emission' => LightEmissionComponent::class, - 'minecraft:liquid_detection' => LiquidDetectionComponent::class, - 'minecraft:map_color' => MapColorComponent::class, - 'minecraft:material_instances' => MaterialInstancesComponent::class, - 'minecraft:placement_filter' => PlacementFilterComponent::class, - 'minecraft:random_offset' => RandomOffsetComponent::class, - 'minecraft:selection_box' => SelectionBoxComponent::class, - 'minecraft:support' => SupportComponent::class, - 'minecraft:transformation' => TransformationComponent::class, - ]; - - /** - * Register a custom block component. - * - * @param string $name Component identifier (e.g., 'yourplugin:custom_component') - * @param class-string $class Fully qualified class name implementing BlockComponent - */ - public static function register(string $name, string $class): void { - self::$components[$name] = $class; - } - - /** - * Get the class name of a component by its identifier. - * - * @param string $name Component identifier - * @return class-string|null Returns the class name, or null if not registered - */ - public static function get(string $name): ?string { - return self::$components[$name] ?? null; - } - - /** - * Check if a component is registered. - * - * @param string $name Component identifier - * @return bool True if registered, false otherwise - */ - public static function has(string $name): bool { - return isset(self::$components[$name]); - } - - /** - * Instantiate a block component from JSON data. - * - * @param string $name Component identifier - * @param mixed $data JSON-decoded data for the component - * @return BlockComponent|null Returns the component instance or null if not registered - */ - public static function fromJson(string $name, mixed $data): ?BlockComponent { - $class = self::get($name); - if($class === null) return null; - return $class::fromJson($data); - } - - /** - * Get all registered component identifiers. - * - * @return string[] List of component IDs - */ - public static function getAll(): array { - return array_keys(self::$components); - } -} diff --git a/src/json/BlockPermutationRegistry.php b/src/json/BlockPermutationRegistry.php deleted file mode 100644 index d961b89b..00000000 --- a/src/json/BlockPermutationRegistry.php +++ /dev/null @@ -1,53 +0,0 @@ - $data - * @return BlockPermutation[] - */ - public static function fromJson(array $data): array { - $permutations = []; - - foreach($data as $permutation) { - if(!isset($permutation['condition'], $permutation['components'])) { - continue; - } - - $condition = $permutation['condition']; - $components = $permutation['components']; - - // Each permutation should have exactly one component (typically transformation) - foreach($components as $componentName => $componentData) { - $component = BlockComponentRegistry::fromJson($componentName, $componentData); - if($component !== null) { - $permutations[] = new BlockPermutation($condition, $component); - break; // Only take the first component per permutation - } - } - } - - return $permutations; - } -} diff --git a/src/json/BlockStateRegistry.php b/src/json/BlockStateRegistry.php deleted file mode 100644 index 53fba333..00000000 --- a/src/json/BlockStateRegistry.php +++ /dev/null @@ -1,44 +0,0 @@ - $components Associative array of component identifiers to data - * @param array $states Associative array of state identifiers to values - * @param array $permutations Array of permutation definitions - * @param float $hardness The hardness of the block (default 1.0) - * @param int $toolType The required tool type to break the block (default BlockToolType::NONE) - * @param int $toolHarvestLevel The required tool harvest level (default 0) - * @param float|null $blastResistance Optional blast resistance - */ - public function __construct( - array $components, - float $hardness = 1.0, - int $toolType = BlockToolType::NONE, - int $toolHarvestLevel = 0, - ?float $blastResistance = null - ) { - // Construct the base Block with identifier and block info - parent::__construct( - new BlockIdentifier(BlockTypeIds::newId()), - "Custom Block", - new BlockTypeInfo(new BlockBreakInfo( - $hardness, - $toolType, - $toolHarvestLevel, - $blastResistance - )) - ); - - // Add all registered components - foreach($components as $name => $data) { - $component = BlockComponentRegistry::fromJson($name, $data); - if($component !== null) { - $this->addComponent($component); - } - } - } - - public function getCurrentStates(): array { - return [$this->axis]; - } - - public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, ?Player $player = null): bool { - $this->axis = match($face) { - 0, 1 => Axis::Y, // down, up - 2, 3 => Axis::Z, // north, south - 4, 5 => Axis::X, // west, east - default => Axis::Y - }; - return parent::place($tx, $item, $blockReplace, $blockClicked, $face, $clickVector, $player); - } - - public function serializeState(BlockStateWriter $out): void { - $rotation = match($this->axis) { - Axis::X => "east", - Axis::Y => "up", - Axis::Z => "north", - default => "down" - }; - $out->writeString("minecraft:block_face", $rotation); - } - - public function deserializeState(BlockStateReader $in): void { - $this->axis = match($in->readString("minecraft:block_face")) { - "east", "west" => Axis::X, - "up", "down" => Axis::Y, - "north", "south" => Axis::Z, - default => Axis::Y - }; - } -} \ No newline at end of file diff --git a/src/json/CustomiesItem.php b/src/json/CustomiesItem.php deleted file mode 100644 index cdc5373c..00000000 --- a/src/json/CustomiesItem.php +++ /dev/null @@ -1,37 +0,0 @@ - $components Associative array of component identifiers to data - */ - public function __construct(array $components) { - // Create a new item with a unique identifier - parent::__construct(new ItemIdentifier(ItemTypeIds::newId())); - // Add all registered components - foreach($components as $name => $data) { - $component = ItemComponentRegistry::fromJson($name, $data); - if($component !== null) { - $this->addComponent($component); - } - } - } -} \ No newline at end of file diff --git a/src/json/ItemComponentRegistry.php b/src/json/ItemComponentRegistry.php deleted file mode 100644 index 86b5aecc..00000000 --- a/src/json/ItemComponentRegistry.php +++ /dev/null @@ -1,156 +0,0 @@ -> - */ - private static array $components = [ - 'minecraft:allow_off_hand' => AllowOffHandComponent::class, - 'minecraft:bundle_interaction' => BundleInteractionComponent::class, - 'minecraft:can_destroy_in_creative' => CanDestroyInCreativeComponent::class, - 'minecraft:compostable' => CompostableComponent::class, - 'minecraft:cooldown' => CooldownComponent::class, - 'minecraft:damage_absorption' => DamageAbsorptionComponent::class, - 'minecraft:damage' => DamageComponent::class, - 'minecraft:digger' => DiggerComponent::class, - 'minecraft:display_name' => DisplayNameComponent::class, - 'minecraft:durability_sensor' => DurabilitySensorComponent::class, - 'minecraft:durability' => DurabilityComponent::class, - 'minecraft:dyeable' => DyeableComponent::class, - 'minecraft:enchantable' => EnchantableComponent::class, - 'minecraft:fire_resistant' => FireResistantComponent::class, - 'minecraft:food' => FoodComponent::class, - 'minecraft:fuel' => FuelComponent::class, - 'minecraft:glint' => GlintComponent::class, - 'minecraft:hand_equipped' => HandEquippedComponent::class, - 'minecraft:hover_text_color' => HoverTextColorComponent::class, - 'minecraft:icon' => IconComponent::class, - 'minecraft:interact_button' => InteractButtonComponent::class, - 'minecraft:kinetic_weapon' => KineticWeaponComponent::class, - 'minecraft:liquid_clipped' => LiquidClippedComponent::class, - 'minecraft:max_stack_size' => MaxStackSizeComponent::class, - 'minecraft:piercing_weapon' => PiercingWeaponComponent::class, - 'minecraft:projectile' => ProjectileComponent::class, - 'minecraft:rarity' => RarityComponent::class, - 'minecraft:record' => RecordComponent::class, - 'minecraft:repairable' => RepairableComponent::class, - 'minecraft:shooter' => ShooterComponent::class, - 'minecraft:should_despawn' => ShouldDespawnComponent::class, - 'minecraft:stacked_by_data' => StackedByDataComponent::class, - 'minecraft:storage_item' => StorageItemComponent::class, - 'minecraft:storage_weight_limit' => StorageWeightLimitComponent::class, - 'minecraft:storage_weight_modifier' => StorageWeightModifierComponent::class, - 'minecraft:swing_duration' => SwingDurationComponent::class, - 'minecraft:swing_sounds' => SwingSoundsComponent::class, - 'minecraft:tags' => TagsComponent::class, - 'minecraft:throwable' => ThrowableComponent::class, - 'minecraft:use_animation' => UseAnimationComponent::class, - 'minecraft:use_modifiers' => UseModifiersComponent::class, - 'minecraft:wearable' => WearableComponent::class, - ]; - - /** - * Register a new custom item component. - * - * @param string $name Component identifier (e.g., 'yourplugin:custom_effect') - * @param class-string $class Fully qualified class name implementing ItemComponent - */ - public static function register(string $name, string $class): void { - self::$components[$name] = $class; - } - - /** - * Retrieve a component class by its identifier. - * - * @param string $name Component identifier - * @return class-string|null Returns the class name or null if not registered - */ - public static function get(string $name): ?string { - return self::$components[$name] ?? null; - } - - /** - * Check whether a component is registered. - * - * @param string $name Component identifier - * @return bool True if the component is registered, false otherwise - */ - public static function has(string $name): bool { - return isset(self::$components[$name]); - } - - /** - * Create a component instance from JSON data. - * - * @param string $name Component identifier - * @param mixed $data JSON-decoded data for the component - * @return ItemComponent|null Returns the component instance or null if the component is not registered - */ - public static function fromJson(string $name, mixed $data): ?ItemComponent { - $class = self::get($name); - if($class === null) return null; - return $class::fromJson($data); - } - - /** - * Get a list of all registered component identifiers. - * - * @return string[] List of component IDs - */ - public static function getAll(): array { - return array_keys(self::$components); - } -} \ No newline at end of file diff --git a/src/util/ByteArray.php b/src/util/ByteArray.php deleted file mode 100644 index a3486579..00000000 --- a/src/util/ByteArray.php +++ /dev/null @@ -1,35 +0,0 @@ - new ByteArray([false, true]) // Outputs: [B; 0b, 1b] - * ``` - */ -final class ByteArray { - - /** @var int[] */ - private readonly array $values; - - /** - * @param array $values Array of booleans or integers (0-255) - */ - public function __construct(array $values) { - $this->values = array_map( - fn(bool|int $v) => is_bool($v) ? ($v ? 1 : 0) : $v, - $values - ); - } - - /** - * @return int[] - */ - public function getValues(): array { - return $this->values; - } -} diff --git a/src/util/NBT.php b/src/util/NBT.php index a23c0698..c2c0fd66 100644 --- a/src/util/NBT.php +++ b/src/util/NBT.php @@ -3,7 +3,6 @@ namespace customiesdevs\customies\util; -use pocketmine\nbt\tag\ByteArrayTag; use pocketmine\nbt\tag\ByteTag; use pocketmine\nbt\tag\CompoundTag; use pocketmine\nbt\tag\FloatTag; @@ -32,7 +31,6 @@ class NBT { * - int → IntTag * - string → StringTag * - Tag → Returned as-is - * - ByteArray → ListTag of ByteTag * * @param mixed $type The value to convert into an NBT Tag * @return Tag|null Returns the corresponding Tag instance, or null if the @@ -41,7 +39,6 @@ class NBT { public static function getTagType($type): ?Tag { return match (true) { $type instanceof Tag => $type, - $type instanceof ByteArray => self::getByteArrayTag($type), is_array($type) => self::getArrayTag($type), is_bool($type) => new ByteTag($type ? 1 : 0), is_float($type) => new FloatTag($type), @@ -51,15 +48,6 @@ public static function getTagType($type): ?Tag { }; } - /** - * Creates a ByteArrayTag from a ByteArray. - * @param ByteArray $byteArray The byte array to convert - * @return ByteArrayTag Returns a ByteArrayTag - */ - private static function getByteArrayTag(ByteArray $byteArray): ByteArrayTag { - return new ByteArrayTag(pack("C*", ...$byteArray->getValues())); - } - /** * Creates an NBT Tag from an array. * - If the array uses sequential numeric keys (0..n), a ListTag is created. From 68145a77084d2ae265d7c8eeea363255f5e7237d Mon Sep 17 00:00:00 2001 From: JeanTKG <102908437+jeantkg@users.noreply.github.com> Date: Sat, 27 Dec 2025 20:38:06 +0300 Subject: [PATCH 38/51] removed the resource folder --- .../behavior/blocks/custom_example_block.json | 31 --------------- .../behavior/items/custom_example_item.json | 38 ------------------- 2 files changed, 69 deletions(-) delete mode 100644 resources/behavior/blocks/custom_example_block.json delete mode 100644 resources/behavior/items/custom_example_item.json diff --git a/resources/behavior/blocks/custom_example_block.json b/resources/behavior/blocks/custom_example_block.json deleted file mode 100644 index 64c5238f..00000000 --- a/resources/behavior/blocks/custom_example_block.json +++ /dev/null @@ -1,31 +0,0 @@ -{ - "format_version": "1.21.100", - "minecraft:block": { - "description": { - "identifier": "customies:custom_example_block", - "menu_category": { - "category": "items" - } - }, - "components": { - "minecraft:geometry": "minecraft:geometry.full_block", - "minecraft:material_instances": { - "*": { - "texture": "stone", - "render_method": "blend", - "isotropic": true - } - }, - "minecraft:display_name": "Custom Example Block", - "minecraft:destructible_by_mining": { - "seconds_to_destroy": 0.6 - }, - "minecraft:friction": 0.1, - "minecraft:destructible_by_explosion": { - "explosion_resistance": 0 - }, - "minecraft:light_dampening": 15, - "minecraft:light_emission": 10 - } - } -} \ No newline at end of file diff --git a/resources/behavior/items/custom_example_item.json b/resources/behavior/items/custom_example_item.json deleted file mode 100644 index 28651856..00000000 --- a/resources/behavior/items/custom_example_item.json +++ /dev/null @@ -1,38 +0,0 @@ -{ - "format_version": "1.21.100", - "minecraft:item": { - "description": { - "identifier": "customies:custom_example_item", - "menu_category": { - "category": "items" - } - }, - "components": { - "minecraft:icon": { - "textures": { - "default": "apple" - } - }, - "minecraft:allow_off_hand": true, - "minecraft:can_destroy_in_creative": true, - "minecraft:dyeable": { - "default_color": "#175882" - }, - "minecraft:enchantable": { - "slot": "sword", - "value": 10 - }, - "minecraft:fuel": { - "duration": 3.0 - }, - "minecraft:hand_equipped": true, - "minecraft:hover_text_color": "minecoin_gold", - "minecraft:interact_button": "Use This Custom Item", - "minecraft:max_stack_size": 7, - "minecraft:fire_resistant": true, - "minecraft:rarity": "rare", - "minecraft:should_despawn": true, - "minecraft:stacked_by_data": true - } - } -} \ No newline at end of file From 08b55830764ab90d3135b99a5b9fdd660364c112 Mon Sep 17 00:00:00 2001 From: HydroGames-dev Date: Mon, 29 Dec 2025 00:50:13 +0530 Subject: [PATCH 39/51] Refactor block components and improve block factory Moved BlockComponents and BlockComponentsTrait to a new namespace, refactored CustomiesBlockFactory for better creative inventory and permutation handling, and made most block components final. Improved validation for materials, enhanced TransformationComponent and RandomOffsetComponent, and added block state templates. Also included .editorconfig and .gitattributes for consistent code style and line endings. --- .editorconfig | 22 ++ .gitattributes | 4 + src/Customies.php | 13 +- src/CustomiesListener.php | 4 +- src/block/{component => }/BlockComponents.php | 4 +- .../{component => }/BlockComponentsTrait.php | 6 +- src/block/CustomiesBlockFactory.php | 217 ++++++++++-------- src/block/component/CollisionBoxComponent.php | 48 ++-- .../DestructibleByExplosionComponent.php | 2 +- .../DestructibleByMiningComponent.php | 2 +- .../DestructionParticlesComponent.php | 10 +- src/block/component/DisplayNameComponent.php | 2 +- .../component/EmbeddedVisualComponent.php | 16 +- src/block/component/FlammableComponent.php | 2 +- .../component/FlowerPottableComponent.php | 2 +- src/block/component/FrictionComponent.php | 2 +- src/block/component/GeometryComponent.php | 4 +- src/block/component/ItemVisualComponent.php | 16 +- .../component/LightDampeningComponent.php | 2 +- .../component/LightEmissionComponent.php | 2 +- .../component/LiquidDetectionComponent.php | 4 +- src/block/component/MapColorComponent.php | 2 +- .../component/MaterialInstancesComponent.php | 13 +- .../component/PlacementFilterComponent.php | 18 +- src/block/component/RandomOffsetComponent.php | 69 +++++- src/block/component/SelectionBoxComponent.php | 2 +- src/block/component/SupportComponent.php | 2 +- .../component/TransformationComponent.php | 53 ++--- src/block/example/ExampleBlock.php | 10 +- src/block/permutations/BlockPermutation.php | 2 +- src/block/permutations/BlockPermutations.php | 2 +- .../permutations/BlockPermutationsTrait.php | 2 +- src/block/permutations/Permutations.php | 11 +- src/block/properties/BlockDescriptor.php | 2 +- src/block/properties/Box.php | 2 +- src/block/properties/Material.php | 48 +++- src/block/states/BlockState.php | 2 +- src/block/states/BlockStates.php | 2 +- src/block/states/BlockStatesTrait.php | 2 +- src/block/states/templates/AnyFacingState.php | 98 ++++++++ src/block/states/templates/BlockFaceState.php | 98 ++++++++ .../templates/HorizontalFacingState.php | 82 +++++++ .../states/templates/PillarRotationState.php | 86 +++++++ src/entity/CustomiesEntityFactory.php | 2 +- src/item/CustomiesItemFactory.php | 2 +- .../component/DamageAbsorptionComponent.php | 2 +- src/item/component/DiggerComponent.php | 2 +- .../component/DurabilitySensorComponent.php | 2 +- src/item/component/DyeableComponent.php | 2 +- src/item/component/EnchantableComponent.php | 1 + src/item/component/KineticWeaponComponent.php | 8 +- .../component/PiercingWeaponComponent.php | 4 +- src/item/component/RepairableComponent.php | 2 +- src/item/component/StorageItemComponent.php | 4 +- src/item/component/TagsComponent.php | 2 +- src/item/properties/RepairItems.php | 4 +- src/task/AsyncRegisterBlocksTask.php | 2 - src/util/NBT.php | 8 +- 58 files changed, 773 insertions(+), 266 deletions(-) create mode 100644 .editorconfig create mode 100644 .gitattributes rename src/block/{component => }/BlockComponents.php (88%) rename src/block/{component => }/BlockComponentsTrait.php (86%) create mode 100644 src/block/states/templates/AnyFacingState.php create mode 100644 src/block/states/templates/BlockFaceState.php create mode 100644 src/block/states/templates/HorizontalFacingState.php create mode 100644 src/block/states/templates/PillarRotationState.php diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 00000000..4ab347ca --- /dev/null +++ b/.editorconfig @@ -0,0 +1,22 @@ +# http://editorconfig.org/ +root = yes + +[*] +indent_size = 4 +indent_style = tab +charset = utf-8 +end_of_line = LF +insert_final_newline = true +trim_trailing_whitespace = true + +[composer.json] +indent_size = 4 + +[{*.json,*.yml,*.yaml}] +indent_style = space +indent_size = 2 + +[*.md] +trim_trailing_whitespace = false +indent_style = space +indent_size = 2 \ No newline at end of file diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 00000000..ce1c9324 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,4 @@ +# Auto detect text files and perform LF normalization +* text=auto +*.php text eol=lf +*.txt text eol=lf \ No newline at end of file diff --git a/src/Customies.php b/src/Customies.php index 9f19fda7..eb72f6dd 100644 --- a/src/Customies.php +++ b/src/Customies.php @@ -19,13 +19,12 @@ public function onLoad(): void{ protected function onEnable(): void { $this->getServer()->getPluginManager()->registerEvents(new CustomiesListener(), $this); - - CustomiesBlockFactory::getInstance()->registerBlock( - static fn() => new ExampleBlock(), - "customies:example_block", - new CreativeInventoryInfo(CreativeInventoryInfo::CATEGORY_ITEMS) - ); - + // why? + // CustomiesBlockFactory::getInstance()->registerBlock( + // static fn() => new ExampleBlock(), + // "customies:example_block", + // new CreativeInventoryInfo(CreativeInventoryInfo::CATEGORY_ITEMS) + // ); $this->getScheduler()->scheduleDelayedTask(new ClosureTask(static function (): void { // This task is scheduled with a 0-tick delay so it runs as soon as the server has started. Plugins should // register their custom blocks and entities in onEnable() before this is executed diff --git a/src/CustomiesListener.php b/src/CustomiesListener.php index c56abf68..8604429b 100644 --- a/src/CustomiesListener.php +++ b/src/CustomiesListener.php @@ -28,8 +28,8 @@ public function __construct() { public function onDataPacketSend(DataPacketSendEvent $event): void { foreach($event->getPackets() as $packet){ - if($packet instanceof StartGamePacket) { - if(count($this->cachedBlockPalette) === 0) { + if($packet instanceof StartGamePacket){ + if(count($this->cachedBlockPalette) === 0){ // Wait for the data to be needed before it is actually cached. Allows for all blocks and items to be // registered before they are cached for the rest of the runtime. $this->cachedBlockPalette = CustomiesBlockFactory::getInstance()->getBlockPaletteEntries(); diff --git a/src/block/component/BlockComponents.php b/src/block/BlockComponents.php similarity index 88% rename from src/block/component/BlockComponents.php rename to src/block/BlockComponents.php index 30a4dcf3..93ce7f8a 100644 --- a/src/block/component/BlockComponents.php +++ b/src/block/BlockComponents.php @@ -1,7 +1,9 @@ customBlocks[$identifier] ?? - throw new InvalidArgumentException("Custom block $identifier is not registered") - ); + if(!isset($this->customBlocks[$identifier])){ + throw new InvalidArgumentException("Custom block $identifier is not registered"); + } + return clone $this->customBlocks[$identifier]; } /** * Loads all the creative groups from the CreativeInventory entries. This is used to ensure that all groups are * available when registering new blocks with creative inventory info. */ - private function loadGroups() : void { + private function loadCreativeGroups(): void { if($this->groups !== []){ return; } @@ -108,7 +108,7 @@ public function getBlockPaletteEntries(): array { * provided to allow for custom block state serialization. * @param Closure $blockFunc A closure that returns a new instance of the block to register. * @param string $identifier The unique identifier for the block (e.g. "namespace:block_name"). - * @param CreativeInventoryInfo|null $creativeInfo Optional creative inventory information for the block. + * @param CreativeInventoryInfo $creativeInfo Creative inventory information for the block. Default set to `Equipment` Category. * @param Closure|null $serializer Optional closure that takes a BlockStateWriter and returns it after writing the block state. * @param Closure|null $deserializer Optional closure that takes a BlockStateReader and returns a new instance of the block after reading the state. * @throws InvalidArgumentException If the blockFunc does not return a Block instance. @@ -116,12 +116,12 @@ public function getBlockPaletteEntries(): array { public function registerBlock( Closure $blockFunc, string $identifier, - ?CreativeInventoryInfo $creativeInfo = new CreativeInventoryInfo(CreativeInventoryInfo::CATEGORY_ITEMS), + CreativeInventoryInfo $creativeInfo = new CreativeInventoryInfo(CreativeInventoryInfo::CATEGORY_EQUIPMENT), ?Closure $serializer = null, ?Closure $deserializer = null ): void { $block = $blockFunc(); - if(!$block instanceof Block) { + if(!$block instanceof Block){ throw new InvalidArgumentException("Class returned from closure is not a Block"); } @@ -129,117 +129,130 @@ public function registerBlock( CustomiesItemFactory::getInstance()->registerBlockItem($identifier, $block); $this->customBlocks[$identifier] = $block; - $nbt = CompoundTag::create(); - $components = CompoundTag::create(); - - if($block instanceof BlockComponents) { - foreach ($block->getComponents() as $component) { - $tag = NBT::getTagType($component->getValue()); - if($tag === null) { - throw new RuntimeException("Failed to get tag type for component " . $component->getName()); - } - $components->setTag($component->getName(), $tag); + $nbtTag = CompoundTag::create(); + $componentsTag = CompoundTag::create(); + // Adds Components to Block + if($block instanceof BlockComponents){ + foreach($block->getComponents() as $component){ + $tag = NBT::getTagType($component->getValue()) ?? throw new RuntimeException("Failed to get tag type for component: " . $component->getName()); + $componentsTag->setTag($component->getName(), $tag); } } - if($creativeInfo !== null) { - $nbt->setTag("menu_category", CompoundTag::create() + // Creative NBT + $nbtTag->setTag("menu_category", + CompoundTag::create() ->setString("category", $creativeInfo->getCategory()) ->setString("group", $creativeInfo->getGroup()) - ->setByte("is_hidden_in_commands", 0)); - } - - if($block instanceof BlockPermutations) { - $blockPropertyNames = $blockPropertyValues = $blockProperties = []; - foreach($block->getStates() as $blockProperty){ - $blockPropertyNames[] = $blockProperty->getName(); - $blockPropertyValues[] = $blockProperty->getValues(); - $blockProperties[] = NBT::getTagType($blockProperty->getValue()); - } - $permutations = array_map(static fn(BlockPermutation $permutation) => NBT::getTagType($permutation->toArray()), $block->getPermutations()); - - // The 'minecraft:on_player_placing' component is required for the client to predict block placement, making - // it a smoother experience for the end-user. - $components->setTag("minecraft:on_player_placing", CompoundTag::create()); - $nbt->setTag("permutations", new ListTag($permutations)) - ->setTag("properties", new ListTag(array_reverse($blockProperties))); // fix client-side order - - foreach(Permutations::getCartesianProduct($blockPropertyValues) as $meta => $permutations){ - // We need to insert states for every possible permutation to allow for all blocks to be used and to - // keep in sync with the client's block palette. - $states = CompoundTag::create(); - foreach($permutations as $i => $value){ - $states->setTag($blockPropertyNames[$i], NBT::getTagType($value)); - } - $blockState = CompoundTag::create() - ->setString(BlockStateData::TAG_NAME, $identifier) - ->setTag(BlockStateData::TAG_STATES, $states); - BlockPalette::getInstance()->insertState($blockState, $meta); - } - - $serializer ??= static function (BlockPermutations $block) use ($identifier, $blockPropertyNames) : BlockStateWriter { - $b = BlockStateWriter::create($identifier); - $block->serializeState($b); - return $b; - }; - $deserializer ??= static function (BlockStateReader $in) use ($block, $identifier, $blockPropertyNames) : BlockPermutations { - $b = CustomiesBlockFactory::getInstance()->get($identifier); - assert($b instanceof BlockPermutations); - $b->deserializeState($in); - return $b; - }; - } else { + ->setByte("is_hidden_in_commands", 0) + ); + // Adds States/Permutation to Block + if($block instanceof BlockPermutations){ + // Register the States/Permutation to the block + $this->registerPermutations($block, $identifier, $nbtTag, $serializer, $deserializer); + }else{ // If a block does not contain any permutations we can just insert the one state. - $blockState = CompoundTag::create() - ->setString(BlockStateData::TAG_NAME, $identifier) - ->setTag(BlockStateData::TAG_STATES, CompoundTag::create()); - BlockPalette::getInstance()->insertState($blockState); - $serializer ??= static fn() => new BlockStateWriter($identifier); + BlockPalette::getInstance()->insertState( + CompoundTag::create() + ->setString(BlockStateData::TAG_NAME, $identifier) + ->setTag(BlockStateData::TAG_STATES, CompoundTag::create()) + ); + $serializer ??= BlockStateWriter::create($identifier); $deserializer ??= static fn(BlockStateReader $in) => $block; } GlobalBlockStateHandlers::getSerializer()->map($block, $serializer); GlobalBlockStateHandlers::getDeserializer()->map($identifier, $deserializer); - - $nbt->setTag("components", $components); - $nbt->setInt("molangVersion", 13); - - if($creativeInfo !== null){ - $this->loadGroups(); - if($creativeInfo->getCategory() === CreativeInventoryInfo::CATEGORY_ALL || $creativeInfo->getCategory() === CreativeInventoryInfo::CATEGORY_COMMANDS){ - return; - } - - $group = $this->groups[$creativeInfo->getGroup()] ?? ($creativeInfo->getGroup() !== "" && $creativeInfo->getGroup() !== CreativeInventoryInfo::NONE ? new CreativeGroup( - new Translatable($creativeInfo->getGroup()), - $block->asItem() - ) : null); - - if($group !== null){ - $this->groups[$group->getName()->getText()] = $group; - } - - $category = match ($creativeInfo->getCategory()) { - CreativeInventoryInfo::CATEGORY_CONSTRUCTION => CreativeCategory::CONSTRUCTION, - CreativeInventoryInfo::CATEGORY_ITEMS => CreativeCategory::ITEMS, - CreativeInventoryInfo::CATEGORY_NATURE => CreativeCategory::NATURE, - CreativeInventoryInfo::CATEGORY_EQUIPMENT => CreativeCategory::EQUIPMENT, - default => throw new AssumptionFailedError("Unknown Creative Category") - }; - - CreativeInventory::getInstance()->add($block->asItem(), $category, $group); - } - - $this->blockPaletteEntries[] = new BlockPaletteEntry($identifier, new CacheableNbt($nbt)); + // The 'minecraft:on_player_placing' component is required for the client to predict block placement, making + // it a smoother experience for the end-user. + $componentsTag->setTag("minecraft:on_player_placing", CompoundTag::create()); + $nbtTag->setTag("blockTags", new ListTag()); + $nbtTag->setTag("components", $componentsTag); + $nbtTag->setInt("molangVersion", 13); + // Registers the block to creative inventory + $this->loadCreativeGroups(); + $this->registerCreativeInfo($block, $creativeInfo); + $this->blockPaletteEntries[] = new BlockPaletteEntry($identifier, new CacheableNbt($nbtTag)); $this->blockFuncs[$identifier] = [$blockFunc, $serializer, $deserializer]; - // 1.20.60 added a new "block_id" field which depends on the order of the block palette entries. Every time we // insert a new block, we need to re-sort the block palette entries to keep in sync with the client. usort($this->blockPaletteEntries, static function(BlockPaletteEntry $a, BlockPaletteEntry $b): int { return strcmp(hash("fnv164", $a->getName()), hash("fnv164", $b->getName())); }); - foreach($this->blockPaletteEntries as $i => $entry) { + foreach($this->blockPaletteEntries as $i => $entry){ $root = $entry->getStates()->getRoot(); $root->setTag("vanilla_block_data", CompoundTag::create()->setInt("block_id", 10000 + $i)); $this->blockPaletteEntries[$i] = new BlockPaletteEntry($entry->getName(), new CacheableNbt($root)); } } + + private function registerPermutations( + BlockPermutations $block, + string $identifier, + CompoundTag $nbt, + ?Closure &$serializer, + ?Closure &$deserializer + ): void { + $blockNames = $blockValues = $blockProperties = []; + foreach($block->getStates() as $state){ + $blockNames[] = $state->getName(); + $blockValues[] = $state->getValues(); + $blockProperties[] = NBT::getTagType($state->getValue()); + } + $nbt->setTag("permutations", new ListTag(array_map( + static fn(BlockPermutation $p) => NBT::getTagType($p->toArray()), + $block->getPermutations() + ))); + $nbt->setTag("properties", new ListTag(array_reverse($blockProperties))); + foreach(Permutations::getCartesianProduct($blockValues) as $meta => $stateValues){ + $stateTag = CompoundTag::create(); + // We need to insert states for every possible permutation to allow for all blocks to be used and to + // keep in sync with the client's block palette. + foreach($stateValues as $i => $value){ + $stateTag->setTag($blockNames[$i], NBT::getTagType($value)); + } + BlockPalette::getInstance()->insertState( + CompoundTag::create() + ->setString(BlockStateData::TAG_NAME, $identifier) + ->setTag(BlockStateData::TAG_STATES, $stateTag), + $meta + ); + } + $serializer ??= static function (BlockPermutations $b) use ($identifier): BlockStateWriter { + $writer = BlockStateWriter::create($identifier); + $b->serializeState($writer); + return $writer; + }; + $deserializer ??= static function (BlockStateReader $in) use ($identifier): BlockPermutations { + $b = CustomiesBlockFactory::getInstance()->get($identifier); + assert($b instanceof BlockPermutations); + $b->deserializeState($in); + return $b; + }; + } + + private function registerCreativeInfo( + Block $block, + CreativeInventoryInfo $creativeInfo + ): void { + if( + $creativeInfo->getCategory() === CreativeInventoryInfo::CATEGORY_ALL || + $creativeInfo->getCategory() === CreativeInventoryInfo::CATEGORY_COMMANDS + ){ + return; + } + $group = null; + if( + $creativeInfo->getGroup() !== "" && + $creativeInfo->getGroup() !== CreativeInventoryInfo::NONE + ){ + $group = $this->groups[$creativeInfo->getGroup()] ??= new CreativeGroup(new Translatable($creativeInfo->getGroup()), $block->asItem()); + } + $category = match($creativeInfo->getCategory()){ + CreativeInventoryInfo::CATEGORY_CONSTRUCTION => CreativeCategory::CONSTRUCTION, + CreativeInventoryInfo::CATEGORY_ITEMS => CreativeCategory::ITEMS, + CreativeInventoryInfo::CATEGORY_NATURE => CreativeCategory::NATURE, + CreativeInventoryInfo::CATEGORY_EQUIPMENT => CreativeCategory::EQUIPMENT, + default => throw new AssumptionFailedError("Unknown Creative Category"), + }; + CreativeInventory::getInstance()->add($block->asItem(), $category, $group); + } } \ No newline at end of file diff --git a/src/block/component/CollisionBoxComponent.php b/src/block/component/CollisionBoxComponent.php index 85c0198b..a3f256f2 100644 --- a/src/block/component/CollisionBoxComponent.php +++ b/src/block/component/CollisionBoxComponent.php @@ -6,7 +6,7 @@ use customiesdevs\customies\block\properties\Box; use pocketmine\math\Vector3; -class CollisionBoxComponent implements BlockComponent { +final class CollisionBoxComponent implements BlockComponent { private bool $enabled; /** @var Box[] */ @@ -20,6 +20,33 @@ public function __construct(bool $enabled = true) { $this->enabled = $enabled; } + public function getName(): string { + return 'minecraft:collision_box'; + } + + public function getValue(): array { + $boxes = []; + foreach($this->boxes as $box) { + $boxes[] = $box->toNbtArray(); + } + //if no boxes are defined we add a default full block box + if(empty($boxes)){ + $boxes[] = $this->enabled ? self::defaultCollisionBox()->toNbtArray() : self::noCollisionBox()->toNbtArray(); + } + return [ + "boxes" => $boxes, + "enabled" => $this->enabled + ]; + } + + public static function defaultCollisionBox(): Box { + return new Box(new Vector3(-8, 0, -8), new Vector3(16, 8, 16)); + } + + public static function noCollisionBox(): Box { + return new Box(new Vector3(-8, 0, -8), new Vector3(0.0001, 0.0001, 0.0001)); + } + /** * Adds a single collision box. * @param Box $box @@ -43,23 +70,4 @@ public function addBoxes(array $boxes): self { } return $this; } - - public function getName(): string { - return 'minecraft:collision_box'; - } - - public function getValue(): array { - $convertedBoxes = []; - foreach($this->boxes as $box) { - $convertedBoxes[] = $box->toNbtArray(); - } - //if no boxes are defined we add a default full block box - if(empty($convertedBoxes)) { - $convertedBoxes[] = (new Box(new Vector3(-8, 0, -8), new Vector3(16, 16, 16)))->toNbtArray(); - } - return [ - "boxes" => $convertedBoxes, - "enabled" => $this->enabled - ]; - } } \ No newline at end of file diff --git a/src/block/component/DestructibleByExplosionComponent.php b/src/block/component/DestructibleByExplosionComponent.php index 948f0cc4..52737e94 100644 --- a/src/block/component/DestructibleByExplosionComponent.php +++ b/src/block/component/DestructibleByExplosionComponent.php @@ -2,7 +2,7 @@ namespace customiesdevs\customies\block\component; -class DestructibleByExplosionComponent implements BlockComponent { +final class DestructibleByExplosionComponent implements BlockComponent { private float $explosionResistance; diff --git a/src/block/component/DestructibleByMiningComponent.php b/src/block/component/DestructibleByMiningComponent.php index a02a4d48..b4d019dc 100644 --- a/src/block/component/DestructibleByMiningComponent.php +++ b/src/block/component/DestructibleByMiningComponent.php @@ -2,7 +2,7 @@ namespace customiesdevs\customies\block\component; -class DestructibleByMiningComponent implements BlockComponent { +final class DestructibleByMiningComponent implements BlockComponent { private float $secondsToDestroy; diff --git a/src/block/component/DestructionParticlesComponent.php b/src/block/component/DestructionParticlesComponent.php index 23ac4dac..fdf959a1 100644 --- a/src/block/component/DestructionParticlesComponent.php +++ b/src/block/component/DestructionParticlesComponent.php @@ -4,7 +4,7 @@ use customiesdevs\customies\block\properties\TintMethod; -class DestructionParticlesComponent implements BlockComponent { +final class DestructionParticlesComponent implements BlockComponent { private int $particleCount; private string $texture; @@ -16,8 +16,12 @@ class DestructionParticlesComponent implements BlockComponent { * @param string $texture The texture name used for the particle. * @param TintMethod $tintMethod Tint multiplied to the color. Tint method logic varies, but often refers to the "rain" and "temperature" of the biome the block is placed in to compute the tint. */ - public function __construct(int $particleCount = 100, string $texture = "", TintMethod $tintMethod = TintMethod::NONE) { - $this->particleCount = $particleCount; + public function __construct( + int $particleCount = 100, + string $texture = "", + TintMethod $tintMethod = TintMethod::NONE + ) { + $this->particleCount = max(0, min(255, $particleCount)); $this->texture = $texture; $this->tintMethod = $tintMethod; } diff --git a/src/block/component/DisplayNameComponent.php b/src/block/component/DisplayNameComponent.php index 2b6ca3b6..0637a037 100644 --- a/src/block/component/DisplayNameComponent.php +++ b/src/block/component/DisplayNameComponent.php @@ -2,7 +2,7 @@ namespace customiesdevs\customies\block\component; -class DisplayNameComponent implements BlockComponent { +final class DisplayNameComponent implements BlockComponent { private string $displayName; diff --git a/src/block/component/EmbeddedVisualComponent.php b/src/block/component/EmbeddedVisualComponent.php index bfc8fc8f..dc50a0e3 100644 --- a/src/block/component/EmbeddedVisualComponent.php +++ b/src/block/component/EmbeddedVisualComponent.php @@ -4,20 +4,16 @@ use customiesdevs\customies\block\properties\Material; -class EmbeddedVisualComponent implements BlockComponent { +final class EmbeddedVisualComponent implements BlockComponent { + /** + * @param Material[] $materials + */ public function __construct( private readonly GeometryComponent $geometry, - private readonly array $materials + private readonly array $materials = [] ) { - if(count($materials) === 0){ - throw new \InvalidArgumentException("At least one material must be defined"); - } - foreach($materials as $material){ - if(!$material instanceof Material){ - throw new \InvalidArgumentException("All materials must be instances of ".Material::class); - } - } + Material::validMaterials($materials); } public function getName(): string { diff --git a/src/block/component/FlammableComponent.php b/src/block/component/FlammableComponent.php index eee995c7..d707f729 100644 --- a/src/block/component/FlammableComponent.php +++ b/src/block/component/FlammableComponent.php @@ -2,7 +2,7 @@ namespace customiesdevs\customies\block\component; -class FlammableComponent implements BlockComponent { +final class FlammableComponent implements BlockComponent { private int $catchChanceModifier; private int $destroyChanceModifier; diff --git a/src/block/component/FlowerPottableComponent.php b/src/block/component/FlowerPottableComponent.php index 190cff9c..35e40450 100644 --- a/src/block/component/FlowerPottableComponent.php +++ b/src/block/component/FlowerPottableComponent.php @@ -2,7 +2,7 @@ namespace customiesdevs\customies\block\component; -class FlowerPottableComponent implements BlockComponent { +final class FlowerPottableComponent implements BlockComponent { public function __construct() {} diff --git a/src/block/component/FrictionComponent.php b/src/block/component/FrictionComponent.php index 6433d36b..fe0aff9c 100644 --- a/src/block/component/FrictionComponent.php +++ b/src/block/component/FrictionComponent.php @@ -2,7 +2,7 @@ namespace customiesdevs\customies\block\component; -class FrictionComponent implements BlockComponent { +final class FrictionComponent implements BlockComponent { private float $friction; diff --git a/src/block/component/GeometryComponent.php b/src/block/component/GeometryComponent.php index 2b8bcc2f..bc03630c 100644 --- a/src/block/component/GeometryComponent.php +++ b/src/block/component/GeometryComponent.php @@ -2,10 +2,10 @@ namespace customiesdevs\customies\block\component; -class GeometryComponent implements BlockComponent { +final class GeometryComponent implements BlockComponent { private string $identifier; - private array $boneVisibility; + private array $boneVisibility = []; private string $culling; private string $cullingLayer; private array|bool $uvLock; diff --git a/src/block/component/ItemVisualComponent.php b/src/block/component/ItemVisualComponent.php index be00c932..2ade002a 100644 --- a/src/block/component/ItemVisualComponent.php +++ b/src/block/component/ItemVisualComponent.php @@ -5,20 +5,16 @@ use customiesdevs\customies\block\properties\Material; use pocketmine\nbt\tag\ByteTag; -class ItemVisualComponent implements BlockComponent { +final class ItemVisualComponent implements BlockComponent { + /** + * @param Material[] $materials + */ public function __construct( private readonly GeometryComponent $geometry, - private readonly array $materials + private readonly array $materials = [] ) { - if(count($materials) === 0){ - throw new \InvalidArgumentException("At least one material must be defined"); - } - foreach($materials as $material){ - if(!$material instanceof Material){ - throw new \InvalidArgumentException("All materials must be instances of ".Material::class); - } - } + Material::validMaterials($materials); } public function getName(): string { diff --git a/src/block/component/LightDampeningComponent.php b/src/block/component/LightDampeningComponent.php index 4798b3dc..cbc35c30 100644 --- a/src/block/component/LightDampeningComponent.php +++ b/src/block/component/LightDampeningComponent.php @@ -4,7 +4,7 @@ use pocketmine\nbt\tag\CompoundTag; -class LightDampeningComponent implements BlockComponent { +final class LightDampeningComponent implements BlockComponent { private int $dampening; diff --git a/src/block/component/LightEmissionComponent.php b/src/block/component/LightEmissionComponent.php index fcd78996..b84bae4f 100644 --- a/src/block/component/LightEmissionComponent.php +++ b/src/block/component/LightEmissionComponent.php @@ -4,7 +4,7 @@ use pocketmine\nbt\tag\CompoundTag; -class LightEmissionComponent implements BlockComponent { +final class LightEmissionComponent implements BlockComponent { private int $emission; diff --git a/src/block/component/LiquidDetectionComponent.php b/src/block/component/LiquidDetectionComponent.php index b4de75ca..cba8edb5 100644 --- a/src/block/component/LiquidDetectionComponent.php +++ b/src/block/component/LiquidDetectionComponent.php @@ -2,7 +2,7 @@ namespace customiesdevs\customies\block\component; -class LiquidDetectionComponent implements BlockComponent { +final class LiquidDetectionComponent implements BlockComponent { /** The block stops liquid flow (default behavior). */ public const BLOCKING = "blocking"; @@ -24,7 +24,7 @@ class LiquidDetectionComponent implements BlockComponent { * Directions from which liquid flow is blocked. * Valid values: "up", "down", "north", "south", "east", "west" */ - private array $stopsLiquidFlowingFromDirection; + private array $stopsLiquidFlowingFromDirection = []; /** * Creates a new liquid detection rule. diff --git a/src/block/component/MapColorComponent.php b/src/block/component/MapColorComponent.php index 34656c66..fd095553 100644 --- a/src/block/component/MapColorComponent.php +++ b/src/block/component/MapColorComponent.php @@ -2,7 +2,7 @@ namespace customiesdevs\customies\block\component; -class MapColorComponent implements BlockComponent { +final class MapColorComponent implements BlockComponent { private string|array $color; diff --git a/src/block/component/MaterialInstancesComponent.php b/src/block/component/MaterialInstancesComponent.php index 1648c6f2..8239526c 100644 --- a/src/block/component/MaterialInstancesComponent.php +++ b/src/block/component/MaterialInstancesComponent.php @@ -4,21 +4,14 @@ use customiesdevs\customies\block\properties\Material; -class MaterialInstancesComponent implements BlockComponent { +final class MaterialInstancesComponent implements BlockComponent { /** * The material instances for a block. Maps face or material_instance names in a geometry file to an actual material instance. You can assign a material instance object to any of these faces: "up", "down", "north", "south", "east", "west", or "*". You can also give an instance the name of your choosing such as "my_instance", and then assign it to a face by doing "north":"my_instance". * @param Material[] $materials */ - public function __construct(private readonly array $materials) { - if(count($materials) === 0){ - throw new \InvalidArgumentException("At least one material must be defined"); - } - foreach($materials as $material){ - if(!$material instanceof Material){ - throw new \InvalidArgumentException("All materials must be instances of ".Material::class); - } - } + public function __construct(private readonly array $materials = []) { + Material::validMaterials($materials); } public function getName(): string { diff --git a/src/block/component/PlacementFilterComponent.php b/src/block/component/PlacementFilterComponent.php index 31866eb2..2b8a0684 100644 --- a/src/block/component/PlacementFilterComponent.php +++ b/src/block/component/PlacementFilterComponent.php @@ -5,7 +5,7 @@ use customiesdevs\customies\block\properties\PlacementCondition; use InvalidArgumentException; -class PlacementFilterComponent implements BlockComponent { +final class PlacementFilterComponent implements BlockComponent { /** @var PlacementCondition[] */ private array $conditions = []; @@ -20,14 +20,6 @@ public function __construct(array $conditions = []) { $this->conditions = $conditions; } - public function addCondition(PlacementCondition $condition): self { - if(count($this->conditions) >= 64){ - throw new InvalidArgumentException("Placement filter may not exceed 64 conditions"); - } - $this->conditions[] = $condition; - return $this; - } - public function getName(): string { return 'minecraft:placement_filter'; } @@ -40,4 +32,12 @@ public function getValue(): array { ) ]; } + + public function addCondition(PlacementCondition $condition): self { + if(count($this->conditions) >= 64){ + throw new InvalidArgumentException("Placement filter may not exceed 64 conditions"); + } + $this->conditions[] = $condition; + return $this; + } } \ No newline at end of file diff --git a/src/block/component/RandomOffsetComponent.php b/src/block/component/RandomOffsetComponent.php index 56a4a527..d0631947 100644 --- a/src/block/component/RandomOffsetComponent.php +++ b/src/block/component/RandomOffsetComponent.php @@ -2,12 +2,27 @@ namespace customiesdevs\customies\block\component; -class RandomOffsetComponent implements BlockComponent { +use pocketmine\math\Vector3; + +final class RandomOffsetComponent implements BlockComponent { + + private Vector3 $min; + private Vector3 $max; + private Vector3 $steps; /** - * TODO Needs more data on this + * @param Vector3 $min Minimum offset per axis (x, y, z) + * @param Vector3 $max Maximum offset per axis (x, y, z) + * @param Vector3 $steps Steps per axis (x, y, z) */ - public function __construct() { + public function __construct( + Vector3 $min = new Vector3(0.0, 0.0, 0.0), + Vector3 $max = new Vector3(0.0, 0.0, 0.0), + ?Vector3 $steps = new Vector3(0, 0, 0) + ) { + $this->min = $min; + $this->max = $max; + $this->steps = $steps; } public function getName(): string { @@ -16,6 +31,54 @@ public function getName(): string { public function getValue(): array { return [ + "x" => [ + "steps" => (int) $this->steps->x, + "range" => ["min" => $this->min->x, "max" => $this->max->x] + ], + "y" => [ + "steps" => (int) $this->steps->y, + "range" => ["min" => $this->min->y, "max" => $this->max->y] + ], + "z" => [ + "steps" => (int) $this->steps->z, + "range" => ["min" => $this->min->z, "max" => $this->max->z] + ], ]; } + + public function setMin(Vector3 $min): self { + $this->min = $min; + return $this; + } + + public function setMax(Vector3 $max): self { + $this->max = $max; + return $this; + } + + public function setSteps(Vector3 $steps): self { + $this->steps = $steps; + return $this; + } + + public function setX(float $min, float $max, int $steps = 0): self { + $this->min->x = $min; + $this->max->x = $max; + $this->steps->x = $steps; + return $this; + } + + public function setY(float $min, float $max, int $steps = 0): self { + $this->min->y = $min; + $this->max->y = $max; + $this->steps->y = $steps; + return $this; + } + + public function setZ(float $min, float $max, int $steps = 0): self { + $this->min->z = $min; + $this->max->z = $max; + $this->steps->z = $steps; + return $this; + } } \ No newline at end of file diff --git a/src/block/component/SelectionBoxComponent.php b/src/block/component/SelectionBoxComponent.php index 466139de..4d822e21 100644 --- a/src/block/component/SelectionBoxComponent.php +++ b/src/block/component/SelectionBoxComponent.php @@ -4,7 +4,7 @@ use pocketmine\math\Vector3; -class SelectionBoxComponent implements BlockComponent { +final class SelectionBoxComponent implements BlockComponent { private bool $useSelectionBox; private Vector3 $origin; diff --git a/src/block/component/SupportComponent.php b/src/block/component/SupportComponent.php index 53842dbe..88682ac6 100644 --- a/src/block/component/SupportComponent.php +++ b/src/block/component/SupportComponent.php @@ -2,7 +2,7 @@ namespace customiesdevs\customies\block\component; -class SupportComponent implements BlockComponent { +final class SupportComponent implements BlockComponent { public const FENCE = "fence"; public const STAIRS = "stair"; diff --git a/src/block/component/TransformationComponent.php b/src/block/component/TransformationComponent.php index 6e5af663..b064d237 100644 --- a/src/block/component/TransformationComponent.php +++ b/src/block/component/TransformationComponent.php @@ -4,7 +4,7 @@ use pocketmine\math\Vector3; -class TransformationComponent implements BlockComponent { +final class TransformationComponent implements BlockComponent { /** * The block's translation, rotation and scale with respect to the center of its world position. @@ -15,11 +15,11 @@ class TransformationComponent implements BlockComponent { * @param Vector3 $translation The block's translation */ public function __construct( - private readonly Vector3 $rotation = new Vector3(0, 0, 0), - private readonly Vector3 $rotationPivot = new Vector3(0, 0, 0), - private readonly Vector3 $scale = new Vector3(1, 1, 1), - private readonly Vector3 $scalePivot = new Vector3(0, 0, 0), - private readonly Vector3 $translation = new Vector3(0, 0, 0) + private readonly Vector3 $rotation = new Vector3(0.0, 0.0, 0.0), + private readonly Vector3 $rotationPivot = new Vector3(0.0, 0.0, 0.0), + private readonly Vector3 $scale = new Vector3(1.0, 1.0, 1.0), + private readonly Vector3 $scalePivot = new Vector3(0.0, 0.0, 0.0), + private readonly Vector3 $translation = new Vector3(0.0, 0.0, 0.0) ) {} public function getName(): string { @@ -27,31 +27,10 @@ public function getName(): string { } public function getValue(): array { - $rx = match ((int) $this->rotation->x) { - 0 => 0, - 90 => 1, - 180 => 2, - 270, -90 => 3, - default => 0 - }; - $ry = match ((int) $this->rotation->y) { - 0 => 0, - 90 => 1, - 180 => 2, - 270, -90 => 3, - default => 0 - }; - $rz = match ((int) $this->rotation->z) { - 0 => 0, - 90 => 1, - 180 => 2, - 270, -90 => 3, - default => 0 - }; return [ - "RX" => $rx, - "RY" => $ry, - "RZ" => $rz, + "RX" => (int) self::rotationToIndex($this->rotation->x), + "RY" => (int) self::rotationToIndex($this->rotation->y), + "RZ" => (int) self::rotationToIndex($this->rotation->z), "RXP" => (float) $this->rotationPivot->x, "RYP" => (float) $this->rotationPivot->y, "RZP" => (float) $this->rotationPivot->z, @@ -67,4 +46,18 @@ public function getValue(): array { "hasJsonVersionBeforeValidation" => false ]; } + + private static function rotationToIndex(float $d): int { + $d = ((int) $d) % 360; + if($d < 0){ + $d += 360; + } + return match($d){ + 0 => 0, // North + 90 => 1, // West + 180 => 2, // South + 270, -90 => 3, // East + default => 0 // North By Default + }; + } } \ No newline at end of file diff --git a/src/block/example/ExampleBlock.php b/src/block/example/ExampleBlock.php index a027dea7..5eed794d 100644 --- a/src/block/example/ExampleBlock.php +++ b/src/block/example/ExampleBlock.php @@ -3,8 +3,8 @@ namespace customiesdevs\customies\block\example; -use customiesdevs\customies\block\component\BlockComponents; -use customiesdevs\customies\block\component\BlockComponentsTrait; +use customiesdevs\customies\block\BlockComponents; +use customiesdevs\customies\block\BlockComponentsTrait; use customiesdevs\customies\block\component\GeometryComponent; use customiesdevs\customies\block\component\MaterialInstancesComponent; use customiesdevs\customies\block\component\TransformationComponent; @@ -47,8 +47,8 @@ public function __construct() { new Material(Material::TARGET_UP, "bum_template_tops"), new Material(Material::TARGET_DOWN, "bum_template_tops"), ])); - $this->addState(new BlockState("minecraft:block_face", ["north", "south", "east", "west", "up", "down"])); - $this->addPermutations([ + $this->addState(new BlockState("minecraft:block_face", ["north", "south", "east", "west", "up", "down"])); + $this->addPermutations([ new BlockPermutation( "q.block_state('minecraft:block_face') == 'west' || q.block_state('minecraft:block_face') == 'east'", new TransformationComponent(new Vector3(0, 0, 90)) @@ -96,4 +96,4 @@ public function deserializeState(BlockStateReader $in): void { default => Axis::Y }; } -} +} \ No newline at end of file diff --git a/src/block/permutations/BlockPermutation.php b/src/block/permutations/BlockPermutation.php index ebf38e61..87682cc1 100644 --- a/src/block/permutations/BlockPermutation.php +++ b/src/block/permutations/BlockPermutation.php @@ -28,4 +28,4 @@ public function toArray(): array { ] ]; } -} +} \ No newline at end of file diff --git a/src/block/permutations/BlockPermutations.php b/src/block/permutations/BlockPermutations.php index 96006c12..c396de87 100644 --- a/src/block/permutations/BlockPermutations.php +++ b/src/block/permutations/BlockPermutations.php @@ -24,4 +24,4 @@ public function addPermutations(array $permutations): void; * @return BlockPermutation[] */ public function getPermutations(): array; -} +} \ No newline at end of file diff --git a/src/block/permutations/BlockPermutationsTrait.php b/src/block/permutations/BlockPermutationsTrait.php index 53750a7d..a2ee893a 100644 --- a/src/block/permutations/BlockPermutationsTrait.php +++ b/src/block/permutations/BlockPermutationsTrait.php @@ -39,4 +39,4 @@ public function addPermutations(array $permutations): void { public function getPermutations(): array { return $this->blockPermutations; } -} +} \ No newline at end of file diff --git a/src/block/permutations/Permutations.php b/src/block/permutations/Permutations.php index 04d109ad..ef736357 100644 --- a/src/block/permutations/Permutations.php +++ b/src/block/permutations/Permutations.php @@ -57,13 +57,20 @@ public static function getStateBitmask(BlockPermutations $block): int { * product (https://en.wikipedia.org/wiki/Cartesian_product). */ public static function getCartesianProduct(array $arrays): array { + if($arrays === []){ + return [[]]; + } $result = []; $count = count($arrays) - 1; $combinations = array_product(array_map(static fn(array $array) => count($array), $arrays)); for($i = 0; $i < $combinations; $i++){ - $result[] = array_map(static fn(array $array) => current($array), $arrays); + $row = []; + foreach($arrays as $index => $_){ + $row[] = current($arrays[$index]); + } + $result[] = $row; for($j = $count; $j >= 0; $j--){ - if(next($arrays[$j])) { + if(next($arrays[$j]) !== false){ break; } reset($arrays[$j]); diff --git a/src/block/properties/BlockDescriptor.php b/src/block/properties/BlockDescriptor.php index 296878c1..40f9730c 100644 --- a/src/block/properties/BlockDescriptor.php +++ b/src/block/properties/BlockDescriptor.php @@ -14,7 +14,7 @@ final class BlockDescriptor { private ?string $name; - private array $states; + private array $states = []; private ?string $tags; private ?int $tagsVersion; diff --git a/src/block/properties/Box.php b/src/block/properties/Box.php index d79206d7..ebf04ccc 100644 --- a/src/block/properties/Box.php +++ b/src/block/properties/Box.php @@ -20,7 +20,7 @@ * * All values are automatically clamped to valid ranges. */ -class Box { +final class Box { /** @var Vector3 Origin (minimum corner) of the box in block-relative coordinates. */ private Vector3 $origin; diff --git a/src/block/properties/Material.php b/src/block/properties/Material.php index 20d247a9..794d760d 100644 --- a/src/block/properties/Material.php +++ b/src/block/properties/Material.php @@ -16,10 +16,22 @@ final class Material { public const TARGET_SOUTH = "south"; public const TARGET_WEST = "west"; + /** Enables face shading/dimming based on lighting direction */ public const FACE_DIMMING = 1; + /** Enables randomized UV rotation */ public const RANDOMIZE_UV_ROTATION = 2; + /** Indicates support for texture variation */ public const SUPPORTS_TEXTURE_VARIATION = 4; + /** + * Packed material flags stored as a bitmask. + * Example: + * `self::FACE_DIMMING` | `self::RANDOMIZE_UV_ROTATION` = `0x03` + * @var int + * @see self::FACE_DIMMING + * @see self::RANDOMIZE_UV_ROTATION + * @see self::SUPPORTS_TEXTURE_VARIATION + */ private int $packed_bools; /** @@ -70,17 +82,31 @@ public function getTarget(): string { return $this->target; } + /** + * Returns the material flag bitmask. + * @return int Bitmask composed of FLAG_* constants. + */ + public function getBitSet(): int { + return $this->packed_bools; + } + /** * Creates a Material instance from a decoded material definition. - * @param string $target + * @param string $target Targeted face for the material. * @param array{ * texture: string, * render_method?: string, * tint_method?: string, * ambient_occlusion?: float|int, + * face_dimming?: bool, + * isotropic?: bool * } $data + * @return self */ public static function fromArray(string $target, array $data): self { + if(!isset($data['texture'])){ + throw new \InvalidArgumentException('Material texture is required'); + } return new self( $target, $data["texture"], @@ -104,11 +130,25 @@ public static function fromArray(string $target, array $data): self { */ public function toArray(): array { return [ - "ambient_occlusion" => $this->ambientOcclusion, - "packed_bools" => new ByteTag($this->packed_bools), - "render_method" => $this->renderMethod->value, "texture" => $this->texture, + "render_method" => $this->renderMethod->value, "tint_method" => $this->tintMethod->value, + "ambient_occlusion" => $this->ambientOcclusion, + "packed_bools" => new ByteTag($this->packed_bools) ]; } + + /** + * @param Material[] $materials + */ + public static function validMaterials(array $materials): void{ + if($materials === []){ + throw new \InvalidArgumentException('At least one material must be defined'); + } + foreach($materials as $material){ + if(!$material instanceof Material){ + throw new \InvalidArgumentException('All materials must be instances of ' . Material::class); + } + } + } } \ No newline at end of file diff --git a/src/block/states/BlockState.php b/src/block/states/BlockState.php index fa055c04..ab720571 100644 --- a/src/block/states/BlockState.php +++ b/src/block/states/BlockState.php @@ -45,4 +45,4 @@ public function getValue(): array { "name" => $this->name ]; } -} +} \ No newline at end of file diff --git a/src/block/states/BlockStates.php b/src/block/states/BlockStates.php index 8d08f696..71da35ff 100644 --- a/src/block/states/BlockStates.php +++ b/src/block/states/BlockStates.php @@ -50,4 +50,4 @@ public function serializeState(BlockStateWriter $blockStateOut): void; * Deserializes the block state from the given BlockStateReader. */ public function deserializeState(BlockStateReader $blockStateIn): void; -} +} \ No newline at end of file diff --git a/src/block/states/BlockStatesTrait.php b/src/block/states/BlockStatesTrait.php index da4d3f0d..da62361f 100644 --- a/src/block/states/BlockStatesTrait.php +++ b/src/block/states/BlockStatesTrait.php @@ -43,4 +43,4 @@ public function getState(string $name): ?BlockState { public function getStates(): array { return $this->states; } -} +} \ No newline at end of file diff --git a/src/block/states/templates/AnyFacingState.php b/src/block/states/templates/AnyFacingState.php new file mode 100644 index 00000000..44d5ab45 --- /dev/null +++ b/src/block/states/templates/AnyFacingState.php @@ -0,0 +1,98 @@ +addState(new BlockState("minecraft:facing_direction", + ["down", "up","north", "south", "east", "west"] + )); + } + + protected function initPermutations(): void { + $this->addPermutations([ + new BlockPermutation( + "q.block_state('minecraft:facing_direction') == 'down'", + new TransformationComponent(new Vector3(90, 0, 0)) + ), + new BlockPermutation( + "q.block_state('minecraft:facing_direction') == 'up'", + new TransformationComponent(new Vector3(-90, 0, 0)) + ), + new BlockPermutation( + "q.block_state('minecraft:facing_direction') == 'north'", + new TransformationComponent(new Vector3(0, 0, 0)) + ), + new BlockPermutation( + "q.block_state('minecraft:facing_direction') == 'south'", + new TransformationComponent(new Vector3(0, 180, 0)) + ), + new BlockPermutation( + "q.block_state('minecraft:facing_direction') == 'west'", + new TransformationComponent(new Vector3(0, 90, 0)) + ), + new BlockPermutation( + "q.block_state('minecraft:facing_direction') == 'east'", + new TransformationComponent(new Vector3(0, -90, 0)) + ), + ]); + } + + public function getCurrentStates(): array { + return [$this->facing]; + } + + public function serializeState(BlockStateWriter $out): void { + $out->writeString( + "minecraft:facing_direction", + match($this->facing){ + Facing::DOWN => "down", + Facing::UP => "up", + Facing::NORTH => "north", + Facing::SOUTH => "south", + Facing::WEST => "west", + Facing::EAST => "east", + } + ); + } + + public function deserializeState(BlockStateReader $in): void { + $this->facing = match($in->readString("minecraft:facing_direction")){ + "down" => Facing::UP, + "up" => Facing::DOWN, + "north" => Facing::NORTH, + "south" => Facing::SOUTH, + "west" => Facing::WEST, + "east" => Facing::EAST, + }; + } + + public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, ?Player $player = null): bool { + $this->facing = Facing::opposite($face); + return parent::place($tx, $item, $blockReplace, $blockClicked, $face, $clickVector, $player); + } +} \ No newline at end of file diff --git a/src/block/states/templates/BlockFaceState.php b/src/block/states/templates/BlockFaceState.php new file mode 100644 index 00000000..f35654de --- /dev/null +++ b/src/block/states/templates/BlockFaceState.php @@ -0,0 +1,98 @@ +addState(new BlockState("minecraft:block_face", + ["down", "up","north", "south", "east", "west"] + )); + } + + protected function initPermutations(): void { + $this->addPermutations([ + new BlockPermutation( + "q.block_state('minecraft:block_face') == 'down'", + new TransformationComponent(new Vector3(90, 0, 0)) + ), + new BlockPermutation( + "q.block_state('minecraft:block_face') == 'up'", + new TransformationComponent(new Vector3(-90, 0, 0)) + ), + new BlockPermutation( + "q.block_state('minecraft:block_face') == 'north'", + new TransformationComponent(new Vector3(0, 0, 0)) + ), + new BlockPermutation( + "q.block_state('minecraft:block_face') == 'south'", + new TransformationComponent(new Vector3(0, 180, 0)) + ), + new BlockPermutation( + "q.block_state('minecraft:block_face') == 'west'", + new TransformationComponent(new Vector3(0, 90, 0)) + ), + new BlockPermutation( + "q.block_state('minecraft:block_face') == 'east'", + new TransformationComponent(new Vector3(0, -90, 0)) + ), + ]); + } + + public function getCurrentStates(): array { + return [$this->facing]; + } + + public function serializeState(BlockStateWriter $out): void { + $out->writeString( + "minecraft:block_face", + match($this->facing){ + Facing::DOWN => "down", + Facing::UP => "up", + Facing::NORTH => "north", + Facing::SOUTH => "south", + Facing::WEST => "west", + Facing::EAST => "east", + } + ); + } + + public function deserializeState(BlockStateReader $in): void { + $this->facing = match($in->readString("minecraft:block_face")){ + "down" => Facing::UP, + "up" => Facing::DOWN, + "north" => Facing::NORTH, + "south" => Facing::SOUTH, + "west" => Facing::WEST, + "east" => Facing::EAST, + }; + } + + public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, ?Player $player = null): bool { + $this->facing = $face; + return parent::place($tx, $item, $blockReplace, $blockClicked, $face, $clickVector, $player); + } +} \ No newline at end of file diff --git a/src/block/states/templates/HorizontalFacingState.php b/src/block/states/templates/HorizontalFacingState.php new file mode 100644 index 00000000..5aa0c2af --- /dev/null +++ b/src/block/states/templates/HorizontalFacingState.php @@ -0,0 +1,82 @@ +addState(new BlockState("minecraft:cardinal_direction", + ["north", "south", "west", "east"] + )); + } + + protected function initPermutations(): void { + $this->addPermutations([ + new BlockPermutation( + "q.block_state('minecraft:cardinal_direction') == 'north'", + new TransformationComponent(new Vector3(0, 0, 0)) + ), + new BlockPermutation( + "q.block_state('minecraft:cardinal_direction') == 'south'", + new TransformationComponent(new Vector3(0, 180, 0)) + ), + new BlockPermutation( + "q.block_state('minecraft:cardinal_direction') == 'west'", + new TransformationComponent(new Vector3(0, 90, 0)) + ), + new BlockPermutation( + "q.block_state('minecraft:cardinal_direction') == 'east'", + new TransformationComponent(new Vector3(0, -90, 0)) + ), + ]); + } + + public function getCurrentStates(): array { + return [$this->facing]; + } + + public function serializeState(BlockStateWriter $out): void { + $out->writeString( + "minecraft:cardinal_direction", + match($this->facing){ + Facing::DOWN => "down", + Facing::UP => "up", + Facing::NORTH => "north", + Facing::SOUTH => "south", + Facing::WEST => "west", + Facing::EAST => "east", + } + ); + } + + public function deserializeState(BlockStateReader $in): void { + $this->facing = match($in->readString("minecraft:cardinal_direction")){ + "north" => Facing::NORTH, + "south" => Facing::SOUTH, + "west" => Facing::WEST, + "east" => Facing::EAST, + }; + } +} \ No newline at end of file diff --git a/src/block/states/templates/PillarRotationState.php b/src/block/states/templates/PillarRotationState.php new file mode 100644 index 00000000..ccca0a16 --- /dev/null +++ b/src/block/states/templates/PillarRotationState.php @@ -0,0 +1,86 @@ +addState(new BlockState("minecraft:block_face", + ["down", "up","north", "south", "east", "west"] + )); + } + + protected function initPermutations(): void { + $this->addPermutations([ + new BlockPermutation( + "q.block_state('minecraft:block_face') == 'west' || q.block_state('minecraft:block_face') == 'east'", + new TransformationComponent(new Vector3(0, 0, 90)) + ), + new BlockPermutation( + "q.block_state('minecraft:block_face') == 'down' || q.block_state('minecraft:block_face') == 'up'", + new TransformationComponent(new Vector3(0, 0, 0)) + ), + new BlockPermutation( + "q.block_state('minecraft:block_face') == 'north' || q.block_state('minecraft:block_face') == 'south'", + new TransformationComponent(new Vector3(90, 0, 0)) + ) + ]); + } + + public function getCurrentStates(): array { + return [$this->axis]; + } + + public function serializeState(BlockStateWriter $out): void { + $rotation = match($this->axis) { + Axis::X => "east", + Axis::Y => "up", + Axis::Z => "north", + default => "down" + }; + $out->writeString("minecraft:block_face", $rotation); + } + + public function deserializeState(BlockStateReader $in): void { + $this->axis = match($in->readString("minecraft:block_face")) { + "east", "west" => Axis::X, + "up", "down" => Axis::Y, + "north", "south" => Axis::Z, + default => Axis::Y + }; + } + + public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, ?Player $player = null): bool { + $this->axis = match($face) { + 0, 1 => Axis::Y, // down, up + 2, 3 => Axis::Z, // north, south + 4, 5 => Axis::X, // west, east + default => Axis::Y + }; + return parent::place($tx, $item, $blockReplace, $blockClicked, $face, $clickVector, $player); + } +} \ No newline at end of file diff --git a/src/entity/CustomiesEntityFactory.php b/src/entity/CustomiesEntityFactory.php index 4a8015c5..b25b1261 100644 --- a/src/entity/CustomiesEntityFactory.php +++ b/src/entity/CustomiesEntityFactory.php @@ -16,7 +16,7 @@ use pocketmine\world\World; use ReflectionClass; -class CustomiesEntityFactory { +final class CustomiesEntityFactory { use SingletonTrait; /** diff --git a/src/item/CustomiesItemFactory.php b/src/item/CustomiesItemFactory.php index b7088eda..7ee21c24 100644 --- a/src/item/CustomiesItemFactory.php +++ b/src/item/CustomiesItemFactory.php @@ -4,6 +4,7 @@ namespace customiesdevs\customies\item; use Closure; +use customiesdevs\customies\util\NBT; use InvalidArgumentException; use pocketmine\block\Block; use pocketmine\data\bedrock\item\BlockItemIdMap; @@ -21,7 +22,6 @@ use pocketmine\utils\AssumptionFailedError; use pocketmine\utils\SingletonTrait; use pocketmine\world\format\io\GlobalItemDataHandlers; -use customiesdevs\customies\util\NBT; use ReflectionClass; use function array_values; diff --git a/src/item/component/DamageAbsorptionComponent.php b/src/item/component/DamageAbsorptionComponent.php index 0e6029b6..c371f565 100644 --- a/src/item/component/DamageAbsorptionComponent.php +++ b/src/item/component/DamageAbsorptionComponent.php @@ -11,7 +11,7 @@ final class DamageAbsorptionComponent implements ItemComponent { * List of damage causes that can be absorbed by the item. * @var DamageCause[] Must contain at least 1 item for meaningful effect. */ - private array $absorbableCauses; + private array $absorbableCauses = []; /** * It allows an item to absorb damage that would otherwise be dealt to its wearer. diff --git a/src/item/component/DiggerComponent.php b/src/item/component/DiggerComponent.php index 54a03ddd..a388ee21 100644 --- a/src/item/component/DiggerComponent.php +++ b/src/item/component/DiggerComponent.php @@ -11,7 +11,7 @@ final class DiggerComponent implements ItemComponent { /** @var array, speed: int}> */ - private array $destroySpeeds; + private array $destroySpeeds = []; private bool $useEfficiency; /** diff --git a/src/item/component/DurabilitySensorComponent.php b/src/item/component/DurabilitySensorComponent.php index bdfe830c..66239bb3 100644 --- a/src/item/component/DurabilitySensorComponent.php +++ b/src/item/component/DurabilitySensorComponent.php @@ -8,7 +8,7 @@ final class DurabilitySensorComponent implements ItemComponent { - private array $durabilityThresholds; + private array $durabilityThresholds = []; /** * Enables an item to emit effects when it receives damage. Because of this, the item also needs a `minecraft:durability` component. diff --git a/src/item/component/DyeableComponent.php b/src/item/component/DyeableComponent.php index 75df6c12..4ade1b1f 100644 --- a/src/item/component/DyeableComponent.php +++ b/src/item/component/DyeableComponent.php @@ -9,7 +9,7 @@ final class DyeableComponent implements ItemComponent { * RGB color values as an array of three integers [R, G, B]. * @var int[] */ - private array $rgb; + private array $rgb = []; /** * Allows the item to be dyed by cauldron water. Once dyed, the item will display the `dyed` texture defined in the `minecraft:icon` component rather than `default`. diff --git a/src/item/component/EnchantableComponent.php b/src/item/component/EnchantableComponent.php index 037f21c5..bd40291a 100644 --- a/src/item/component/EnchantableComponent.php +++ b/src/item/component/EnchantableComponent.php @@ -5,6 +5,7 @@ final class EnchantableComponent implements ItemComponent { + // Item Type public const SLOT_NONE = "none"; public const SLOT_ALL = "all"; public const SLOT_HELMET = "armor_head"; diff --git a/src/item/component/KineticWeaponComponent.php b/src/item/component/KineticWeaponComponent.php index 5d62d504..349d11ce 100644 --- a/src/item/component/KineticWeaponComponent.php +++ b/src/item/component/KineticWeaponComponent.php @@ -5,15 +5,15 @@ final class KineticWeaponComponent implements ItemComponent { - private array $creativeReach; + private array $creativeReach = []; private float $damageModifier; private float $damageMultiplier; private int $delay; - private array $dismountConditions; + private array $dismountConditions = []; private float $hitboxMargin; - private array $knockbackConditions; - private array $reach; + private array $knockbackConditions = []; + private array $reach = []; public function __construct( array $creativeReach = ['min' => 2.0, 'max' => 7.5], diff --git a/src/item/component/PiercingWeaponComponent.php b/src/item/component/PiercingWeaponComponent.php index e104d41b..f65d0806 100644 --- a/src/item/component/PiercingWeaponComponent.php +++ b/src/item/component/PiercingWeaponComponent.php @@ -5,9 +5,9 @@ final class PiercingWeaponComponent implements ItemComponent { - private array $creativeReach; + private array $creativeReach = []; private float $hitboxMargin; - private array $reach; + private array $reach = []; public function __construct( array $creativeReach = ['min' => 2.0, 'max' => 7.5], diff --git a/src/item/component/RepairableComponent.php b/src/item/component/RepairableComponent.php index 83e7f3a2..89bf1282 100644 --- a/src/item/component/RepairableComponent.php +++ b/src/item/component/RepairableComponent.php @@ -12,7 +12,7 @@ final class RepairableComponent implements ItemComponent { * @param RepairItems[] $repairItems List of repair item entries. Each entry needs to define a list of strings for items that can be used for the repair and an optional repair_amount for how much durability is gained. */ public function __construct( - private readonly array $repairItems, + private readonly array $repairItems = [], ) {} public function getName(): string { diff --git a/src/item/component/StorageItemComponent.php b/src/item/component/StorageItemComponent.php index 4372934b..9d2c3cdc 100644 --- a/src/item/component/StorageItemComponent.php +++ b/src/item/component/StorageItemComponent.php @@ -8,8 +8,8 @@ final class StorageItemComponent implements ItemComponent { private bool $allowNestedStorageItems; - private array $allowedItems; - private array $bannedItems; + private array $allowedItems = []; + private array $bannedItems = []; private int $maxSlots; /** diff --git a/src/item/component/TagsComponent.php b/src/item/component/TagsComponent.php index 0c755c34..1bd8e00f 100644 --- a/src/item/component/TagsComponent.php +++ b/src/item/component/TagsComponent.php @@ -5,7 +5,7 @@ final class TagsComponent implements ItemComponent { - private array $tags; + private array $tags = []; /** * Determines which tags are included on a given item. diff --git a/src/item/properties/RepairItems.php b/src/item/properties/RepairItems.php index e5420c92..d65e028a 100644 --- a/src/item/properties/RepairItems.php +++ b/src/item/properties/RepairItems.php @@ -2,14 +2,14 @@ namespace customiesdevs\customies\item\properties; -class RepairItems { +final class RepairItems { /** * @param string[] $items List of item names that may be used for repairing. * @param int $repairAmount Amount by which the item is repaired. */ public function __construct( - private readonly array $items, + private readonly array $items = [], private readonly int $repairAmount, ) {} diff --git a/src/task/AsyncRegisterBlocksTask.php b/src/task/AsyncRegisterBlocksTask.php index 5b37d822..dbf2df36 100644 --- a/src/task/AsyncRegisterBlocksTask.php +++ b/src/task/AsyncRegisterBlocksTask.php @@ -42,8 +42,6 @@ public function __construct(array $blockFuncs) { public function onRun(): void { foreach($this->blockFuncs as $identifier => $blockFunc){ - // We do not care about the creative inventory data in other threads since it is unused outside of - // the main thread. CustomiesBlockFactory::getInstance()->registerBlock( $blockFunc, (string) $identifier, diff --git a/src/util/NBT.php b/src/util/NBT.php index c2c0fd66..b40f7c78 100644 --- a/src/util/NBT.php +++ b/src/util/NBT.php @@ -20,7 +20,7 @@ use function is_string; use function range; -class NBT { +final class NBT { /** * Attempts to return the correct NBT Tag for the provided PHP value. @@ -37,7 +37,7 @@ class NBT { * type cannot be converted. */ public static function getTagType($type): ?Tag { - return match (true) { + return match (true){ $type instanceof Tag => $type, is_array($type) => self::getArrayTag($type), is_bool($type) => new ByteTag($type ? 1 : 0), @@ -57,8 +57,8 @@ public static function getTagType($type): ?Tag { * @throws \InvalidArgumentException If any value cannot be converted to a Tag */ private static function getArrayTag(array $array): Tag { - if(array_keys($array) === range(0, count($array) - 1)) { - return new ListTag(array_map(function($value) { + if(array_keys($array) === range(0, count($array) - 1)){ + return new ListTag(array_map(function($value): Tag { $tag = self::getTagType($value); if($tag === null) { throw new \InvalidArgumentException("Cannot convert value of type " . get_debug_type($value) . " to NBT Tag"); From a8cf8d2ed720b977f18e739cc018901c42ebf73b Mon Sep 17 00:00:00 2001 From: HydroGames-dev Date: Mon, 29 Dec 2025 00:59:32 +0530 Subject: [PATCH 40/51] Update Customies.php --- src/Customies.php | 6 ------ 1 file changed, 6 deletions(-) diff --git a/src/Customies.php b/src/Customies.php index eb72f6dd..c040c3ce 100644 --- a/src/Customies.php +++ b/src/Customies.php @@ -19,12 +19,6 @@ public function onLoad(): void{ protected function onEnable(): void { $this->getServer()->getPluginManager()->registerEvents(new CustomiesListener(), $this); - // why? - // CustomiesBlockFactory::getInstance()->registerBlock( - // static fn() => new ExampleBlock(), - // "customies:example_block", - // new CreativeInventoryInfo(CreativeInventoryInfo::CATEGORY_ITEMS) - // ); $this->getScheduler()->scheduleDelayedTask(new ClosureTask(static function (): void { // This task is scheduled with a 0-tick delay so it runs as soon as the server has started. Plugins should // register their custom blocks and entities in onEnable() before this is executed From 9f00215e0970540fdff5bbbf1e1d2933a6ca846b Mon Sep 17 00:00:00 2001 From: HydroGames-dev Date: Mon, 29 Dec 2025 01:00:31 +0530 Subject: [PATCH 41/51] Move ExampleBlock to examples directory Renamed ExampleBlock.php from src/block/example to src/examples and updated its namespace accordingly for better organization. --- src/{block/example => examples}/ExampleBlock.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) rename src/{block/example => examples}/ExampleBlock.php (98%) diff --git a/src/block/example/ExampleBlock.php b/src/examples/ExampleBlock.php similarity index 98% rename from src/block/example/ExampleBlock.php rename to src/examples/ExampleBlock.php index 5eed794d..633157d1 100644 --- a/src/block/example/ExampleBlock.php +++ b/src/examples/ExampleBlock.php @@ -1,7 +1,7 @@ Date: Tue, 30 Dec 2025 20:22:21 +0530 Subject: [PATCH 42/51] Refactor creative group and permutation handling Moved creative group management to CreativeInventoryInfo and refactored block/item registration to use it. Removed Permutations.php and moved Cartesian product logic to BlockPermutation. Improved docblocks and type hints across block, item, and entity classes for clarity and maintainability. --- src/Customies.php | 3 +- src/CustomiesListener.php | 1 + src/block/BlockPalette.php | 7 ++ src/block/CustomiesBlockFactory.php | 52 ++++------- src/block/permutations/BlockPermutation.php | 44 +++++++++ src/block/permutations/Permutations.php | 81 ----------------- src/block/properties/BlockDescriptor.php | 7 ++ src/block/properties/Material.php | 3 + src/block/properties/PlacementCondition.php | 6 ++ src/block/states/BlockState.php | 4 + src/block/states/BlockStates.php | 5 ++ src/block/states/BlockStatesTrait.php | 3 +- src/entity/CustomiesEntityFactory.php | 6 ++ src/item/CreativeInventoryInfo.php | 57 ++++++++++++ src/item/CustomiesItemFactory.php | 98 +++++++++------------ src/item/ItemComponents.php | 11 ++- src/item/ItemComponentsTrait.php | 14 ++- src/item/component/ItemComponent.php | 3 - src/item/properties/RepairItems.php | 1 + src/task/AsyncRegisterBlocksTask.php | 2 +- 20 files changed, 222 insertions(+), 186 deletions(-) delete mode 100644 src/block/permutations/Permutations.php diff --git a/src/Customies.php b/src/Customies.php index c040c3ce..aba7d870 100644 --- a/src/Customies.php +++ b/src/Customies.php @@ -4,8 +4,6 @@ namespace customiesdevs\customies; use customiesdevs\customies\block\CustomiesBlockFactory; -use customiesdevs\customies\block\example\ExampleBlock; -use customiesdevs\customies\item\CreativeInventoryInfo; use pocketmine\plugin\PluginBase; use pocketmine\scheduler\ClosureTask; use pocketmine\utils\SingletonTrait; @@ -19,6 +17,7 @@ public function onLoad(): void{ protected function onEnable(): void { $this->getServer()->getPluginManager()->registerEvents(new CustomiesListener(), $this); + $this->getScheduler()->scheduleDelayedTask(new ClosureTask(static function (): void { // This task is scheduled with a 0-tick delay so it runs as soon as the server has started. Plugins should // register their custom blocks and entities in onEnable() before this is executed diff --git a/src/CustomiesListener.php b/src/CustomiesListener.php index 8604429b..fbdfcebd 100644 --- a/src/CustomiesListener.php +++ b/src/CustomiesListener.php @@ -13,6 +13,7 @@ use function count; final class CustomiesListener implements Listener { + /** @var BlockPaletteEntry[] */ private array $cachedBlockPalette = []; private Experiments $experiments; diff --git a/src/block/BlockPalette.php b/src/block/BlockPalette.php index 18083f29..3df7c192 100644 --- a/src/block/BlockPalette.php +++ b/src/block/BlockPalette.php @@ -45,6 +45,7 @@ public function __construct() { } /** + * Returns all block states in the palette. * @return BlockStateDictionaryEntry[] */ public function getStates(): array { @@ -52,6 +53,7 @@ public function getStates(): array { } /** + * Returns all custom block states. * @return BlockStateDictionaryEntry[] */ public function getCustomStates(): array { @@ -60,6 +62,9 @@ public function getCustomStates(): array { /** * Inserts the provided state in to the correct position of the palette. + * @param CompoundTag $state + * @param int $meta + * @return void */ public function insertState(CompoundTag $state, int $meta = 0): void { if(($name = $state->getString(BlockStateData::TAG_NAME, "")) === "") { @@ -74,6 +79,8 @@ public function insertState(CompoundTag $state, int $meta = 0): void { /** * Sorts the palette's block states in the correct order, also adding the provided state to the array. + * @param BlockStateDictionaryEntry $newState + * @return void */ private function sortWith(BlockStateDictionaryEntry $newState): void { // To sort the block palette we first have to split the palette up in to groups of states. We only want to sort diff --git a/src/block/CustomiesBlockFactory.php b/src/block/CustomiesBlockFactory.php index 3ce8229a..3db63b8d 100644 --- a/src/block/CustomiesBlockFactory.php +++ b/src/block/CustomiesBlockFactory.php @@ -7,7 +7,6 @@ use customiesdevs\customies\block\BlockComponents; use customiesdevs\customies\block\permutations\BlockPermutation; use customiesdevs\customies\block\permutations\BlockPermutations; -use customiesdevs\customies\block\permutations\Permutations; use customiesdevs\customies\item\CreativeInventoryInfo; use customiesdevs\customies\item\CustomiesItemFactory; use customiesdevs\customies\task\AsyncRegisterBlocksTask; @@ -31,7 +30,6 @@ use pocketmine\utils\SingletonTrait; use pocketmine\world\format\io\GlobalBlockStateHandlers; use RuntimeException; - use function array_map; use function array_reverse; use function hash; @@ -69,8 +67,8 @@ public function addWorkerInitHook(): void { /** * Get a custom block from its identifier. An exception will be thrown if the block is not registered. * @param string $identifier Unique block identifier (e.g. "namespace:block_name") - * @throws InvalidArgumentException If the block is not registered * @return Block A clone of the registered block. + * @throws InvalidArgumentException If the block is not registered */ public function get(string $identifier): Block { if(!isset($this->customBlocks[$identifier])){ @@ -79,22 +77,6 @@ public function get(string $identifier): Block { return clone $this->customBlocks[$identifier]; } - /** - * Loads all the creative groups from the CreativeInventory entries. This is used to ensure that all groups are - * available when registering new blocks with creative inventory info. - */ - private function loadCreativeGroups(): void { - if($this->groups !== []){ - return; - } - foreach(CreativeInventory::getInstance()->getAllEntries() as $entry){ - $group = $entry->getGroup(); - if($group !== null){ - $this->groups[$group->getName()->getText()] = $group; - } - } - } - /** * Returns all the block palette entries that need to be sent to the client. * @return BlockPaletteEntry[] @@ -168,7 +150,6 @@ public function registerBlock( $nbtTag->setTag("components", $componentsTag); $nbtTag->setInt("molangVersion", 13); // Registers the block to creative inventory - $this->loadCreativeGroups(); $this->registerCreativeInfo($block, $creativeInfo); $this->blockPaletteEntries[] = new BlockPaletteEntry($identifier, new CacheableNbt($nbtTag)); $this->blockFuncs[$identifier] = [$blockFunc, $serializer, $deserializer]; @@ -184,6 +165,14 @@ public function registerBlock( } } + /** + * Registers permutations and states for a BlockPermutations instance. + * @param BlockPermutations $block The block instance + * @param string $identifier The unique block identifier + * @param CompoundTag $nbt The NBT tag to populate with permutation data + * @param Closure|null &$serializer Reference to the serializer closure to set + * @param Closure|null &$deserializer Reference to the deserializer closure to set + */ private function registerPermutations( BlockPermutations $block, string $identifier, @@ -202,7 +191,7 @@ private function registerPermutations( $block->getPermutations() ))); $nbt->setTag("properties", new ListTag(array_reverse($blockProperties))); - foreach(Permutations::getCartesianProduct($blockValues) as $meta => $stateValues){ + foreach(BlockPermutation::getCartesianProduct($blockValues) as $meta => $stateValues){ $stateTag = CompoundTag::create(); // We need to insert states for every possible permutation to allow for all blocks to be used and to // keep in sync with the client's block palette. @@ -228,23 +217,20 @@ private function registerPermutations( return $b; }; } - + + /** + * Registers the block in the creative inventory based on the provided CreativeInventoryInfo. + * @param Block $block The block to register + * @param CreativeInventoryInfo $creativeInfo The creative inventory information + */ private function registerCreativeInfo( Block $block, CreativeInventoryInfo $creativeInfo ): void { - if( - $creativeInfo->getCategory() === CreativeInventoryInfo::CATEGORY_ALL || - $creativeInfo->getCategory() === CreativeInventoryInfo::CATEGORY_COMMANDS - ){ - return; - } $group = null; - if( - $creativeInfo->getGroup() !== "" && - $creativeInfo->getGroup() !== CreativeInventoryInfo::NONE - ){ - $group = $this->groups[$creativeInfo->getGroup()] ??= new CreativeGroup(new Translatable($creativeInfo->getGroup()), $block->asItem()); + if($creativeInfo->getGroup() !== CreativeInventoryInfo::NONE){ + $group = CreativeInventoryInfo::get($creativeInfo->getGroup()) ?? new CreativeGroup(new Translatable($creativeInfo->getGroup()), $block->asItem()); + CreativeInventoryInfo::set($group); } $category = match($creativeInfo->getCategory()){ CreativeInventoryInfo::CATEGORY_CONSTRUCTION => CreativeCategory::CONSTRUCTION, diff --git a/src/block/permutations/BlockPermutation.php b/src/block/permutations/BlockPermutation.php index 87682cc1..6b17e762 100644 --- a/src/block/permutations/BlockPermutation.php +++ b/src/block/permutations/BlockPermutation.php @@ -7,19 +7,32 @@ class BlockPermutation { + /** + * @param string $condition The condition to evaluate for this permutation + * @param BlockComponent $components The components to apply if the condition is met + */ public function __construct( private readonly string $condition, private readonly BlockComponent $components ) {} + /** + * Gets the condition string for this permutation. + */ public function getCondition(): string { return $this->condition; } + /** + * Gets the components associated with this permutation. + */ public function getComponents(): BlockComponent { return $this->components; } + /** + * Converts the BlockPermutation to an array format. + */ public function toArray(): array { return [ "condition" => $this->condition, @@ -28,4 +41,35 @@ public function toArray(): array { ] ]; } + + /** + * Computes the Cartesian product of the provided arrays. + * Each array in the input represents a set of possible values for a block property. + * The result is an array of all possible combinations of these values. + * + * @param array[] $arrays An array of arrays, each containing possible values for a block property + * @return array An array of arrays, each representing a unique combination of property values + */ + public static function getCartesianProduct(array $arrays): array { + if($arrays === []){ + return [[]]; + } + $result = []; + $count = count($arrays) - 1; + $combinations = array_product(array_map(static fn(array $array) => count($array), $arrays)); + for($i = 0; $i < $combinations; $i++){ + $row = []; + foreach($arrays as $index => $_){ + $row[] = current($arrays[$index]); + } + $result[] = $row; + for($j = $count; $j >= 0; $j--){ + if(next($arrays[$j]) !== false){ + break; + } + reset($arrays[$j]); + } + } + return $result; + } } \ No newline at end of file diff --git a/src/block/permutations/Permutations.php b/src/block/permutations/Permutations.php deleted file mode 100644 index ef736357..00000000 --- a/src/block/permutations/Permutations.php +++ /dev/null @@ -1,81 +0,0 @@ - $blockProperty->getValues(), $block->getStates()) - )[$meta] ?? null; - if($properties === null) { - throw new Exception("Unable to calculate permutations from block meta: " . $meta); - } - return $properties; - } - - /** - * Attempts to convert the block in to a meta value based on the possible permutations of the block. An exception is - * thrown if the state of the block is not a possible combination of all the block properties. - */ - public static function toMeta(BlockPermutations $block): int { - $properties = self::getCartesianProduct( - array_map(static fn(BlockState $blockProperty) => $blockProperty->getValues(), $block->getStates()) - ); - foreach($properties as $meta => $permutations){ - if($permutations === $block->getCurrentStates()) { - return $meta; - } - } - throw new Exception("Unable to calculate block meta from current permutations"); - } - - /** - * Returns the number of bits required to represent all the possible permutations of the block. - */ - public static function getStateBitmask(BlockPermutations $block): int { - $possibleValues = array_map(static fn(BlockState $blockProperty) => $blockProperty->getValues(), $block->getStates()); - return count(self::getCartesianProduct($possibleValues)) - 1; - } - - /** - * Returns an 2-dimensional array containing all possible combinations of the provided arrays using the cartesian - * product (https://en.wikipedia.org/wiki/Cartesian_product). - */ - public static function getCartesianProduct(array $arrays): array { - if($arrays === []){ - return [[]]; - } - $result = []; - $count = count($arrays) - 1; - $combinations = array_product(array_map(static fn(array $array) => count($array), $arrays)); - for($i = 0; $i < $combinations; $i++){ - $row = []; - foreach($arrays as $index => $_){ - $row[] = current($arrays[$index]); - } - $result[] = $row; - for($j = $count; $j >= 0; $j--){ - if(next($arrays[$j]) !== false){ - break; - } - reset($arrays[$j]); - } - } - return $result; - } -} \ No newline at end of file diff --git a/src/block/properties/BlockDescriptor.php b/src/block/properties/BlockDescriptor.php index 40f9730c..a141b77c 100644 --- a/src/block/properties/BlockDescriptor.php +++ b/src/block/properties/BlockDescriptor.php @@ -13,9 +13,13 @@ */ final class BlockDescriptor { + /** @var string|null */ private ?string $name; + /** @var array */ private array $states = []; + /** @var string|null */ private ?string $tags; + /** @var int|null */ private ?int $tagsVersion; /** @@ -56,6 +60,9 @@ public function toArray(): array { return $data; } + /** + * Creates a BlockDescriptor from an array. + */ public static function fromArray(array $data): self { return new self( $data["name"] ?? null, diff --git a/src/block/properties/Material.php b/src/block/properties/Material.php index 794d760d..cb195370 100644 --- a/src/block/properties/Material.php +++ b/src/block/properties/Material.php @@ -102,6 +102,7 @@ public function getBitSet(): int { * isotropic?: bool * } $data * @return self + * @throws \InvalidArgumentException if required fields are missing or invalid. */ public static function fromArray(string $target, array $data): self { if(!isset($data['texture'])){ @@ -139,7 +140,9 @@ public function toArray(): array { } /** + * Validates an array of materials. * @param Material[] $materials + * @throws \InvalidArgumentException if the array is empty or contains invalid entries. */ public static function validMaterials(array $materials): void{ if($materials === []){ diff --git a/src/block/properties/PlacementCondition.php b/src/block/properties/PlacementCondition.php index 9f90627c..6b1f53b3 100644 --- a/src/block/properties/PlacementCondition.php +++ b/src/block/properties/PlacementCondition.php @@ -28,6 +28,9 @@ public function __construct(array $allowedFaces, array $blockFilters) { $this->blockFilters = $blockFilters; } + /** + * Converts the placement condition to Bedrock format. + */ public function toArray(): array { return [ "allowed_faces" => array_map( @@ -41,6 +44,9 @@ public function toArray(): array { ]; } + /** + * Creates a PlacementCondition from an array. + */ public static function fromArray(array $data): self { return new self( array_map( diff --git a/src/block/states/BlockState.php b/src/block/states/BlockState.php index ab720571..7084d0c8 100644 --- a/src/block/states/BlockState.php +++ b/src/block/states/BlockState.php @@ -9,6 +9,7 @@ */ class BlockState { + /** @var mixed The current value of the state property */ protected mixed $currentValue; /** @@ -24,6 +25,7 @@ public function __construct( /** * Returns the state property name. + * @return string */ public function getName(): string { return $this->name; @@ -31,6 +33,7 @@ public function getName(): string { /** * Returns the possible values array. + * @return array */ public function getValues(): array { return $this->values; @@ -38,6 +41,7 @@ public function getValues(): array { /** * Returns the NBT value definition for client (enum + name). + * @return array */ public function getValue(): array { return [ diff --git a/src/block/states/BlockStates.php b/src/block/states/BlockStates.php index 71da35ff..31a80c53 100644 --- a/src/block/states/BlockStates.php +++ b/src/block/states/BlockStates.php @@ -11,6 +11,7 @@ interface BlockStates { /** * Adds a state to the block. * @param BlockState $trait + * @return void */ public function addState(BlockState $trait): void; @@ -43,11 +44,15 @@ public function getCurrentStates(): array; /** * Serializes the block state to the given BlockStateWriter. + * @param BlockStateWriter $blockStateOut + * @return void */ public function serializeState(BlockStateWriter $blockStateOut): void; /** * Deserializes the block state from the given BlockStateReader. + * @param BlockStateReader $blockStateIn + * @return void */ public function deserializeState(BlockStateReader $blockStateIn): void; } \ No newline at end of file diff --git a/src/block/states/BlockStatesTrait.php b/src/block/states/BlockStatesTrait.php index da62361f..a1281463 100644 --- a/src/block/states/BlockStatesTrait.php +++ b/src/block/states/BlockStatesTrait.php @@ -6,13 +6,14 @@ trait BlockStatesTrait { /** - * @var BlockState[] + * @var BlockState[] Array of registered block states */ private array $states = []; /** * Adds a state to the block. * @param BlockState $state + * @return void */ public function addState(BlockState $state): void { $this->states[$state->getName()] = $state; diff --git a/src/entity/CustomiesEntityFactory.php b/src/entity/CustomiesEntityFactory.php index b25b1261..5c0a9ea3 100644 --- a/src/entity/CustomiesEntityFactory.php +++ b/src/entity/CustomiesEntityFactory.php @@ -32,6 +32,12 @@ public function registerEntity(string $className, string $identifier, ?Closure $ $this->updateStaticPacketCache($identifier, $behaviourId); } + /** + * Updates the AvailableActorIdentifiersPacket to include the new entity. + * @param string $identifier example: "customies:my_entity" + * @param string $behaviourId + * @return void + */ private function updateStaticPacketCache(string $identifier, string $behaviourId): void { $instance = StaticPacketCache::getInstance(); $property = (new ReflectionClass($instance))->getProperty("availableActorIdentifiers"); diff --git a/src/item/CreativeInventoryInfo.php b/src/item/CreativeInventoryInfo.php index a667a18d..e6bdbc96 100644 --- a/src/item/CreativeInventoryInfo.php +++ b/src/item/CreativeInventoryInfo.php @@ -2,8 +2,14 @@ namespace customiesdevs\customies\item; +use pocketmine\inventory\CreativeInventory; +use pocketmine\inventory\CreativeGroup; + final class CreativeInventoryInfo { + /** @var array|null */ + private static ?array $groups = null; + const NONE = "none"; const CATEGORY_ALL = "all"; @@ -106,6 +112,7 @@ final class CreativeInventoryInfo { /** * Returns a default CreativeInventoryInfo instance (all category, no group) + * @return self */ public static function DEFAULT(): self { return new self(self::CATEGORY_ALL, self::NONE); @@ -122,6 +129,7 @@ public function __construct( /** * Returns the creative inventory category. + * @return string */ public function getCategory(): string { return $this->category; @@ -130,6 +138,7 @@ public function getCategory(): string { /** * Returns the numeric representation of the category. * 0 = all, 1 = construction, 2 = nature, 3 = equipment, 4 = items + * @return int */ public function getNumericCategory(): int { return match ($this->category) { @@ -143,8 +152,56 @@ public function getNumericCategory(): int { /** * Returns the creative inventory group. + * @return string */ public function getGroup(): string { return $this->group; } + + /** + * Loads all existing creative groups from the Creative Inventory. + * @return void + */ + public static function load(): void { + if(self::$groups !== null){ + return; + } + $groups = []; + foreach(CreativeInventory::getInstance()->getAllEntries() as $entry){ + $group = $entry->getGroup(); + if($group !== null){ + $groups[$group->getName()->getText()] = $group; + } + } + self::$groups = $groups; + } + + /** + * Returns the CreativeGroup instance for the given name, or null if it does not exist. + * @param string $name + * @return CreativeGroup|null + */ + public static function get(string $name): ?CreativeGroup { + self::load(); + return self::$groups[$name] ?? null; + } + + /** + * Sets a CreativeGroup instance in the internal list. + * @param CreativeGroup $group + * @return void + */ + public static function set(CreativeGroup $group): void { + self::load(); + self::$groups[$group->getName()->getText()] = $group; + } + + /** + * Returns all loaded CreativeGroup instances. + * @return CreativeGroup[] + */ + public static function all(): array { + self::load(); + return self::$groups; + } } \ No newline at end of file diff --git a/src/item/CustomiesItemFactory.php b/src/item/CustomiesItemFactory.php index 7ee21c24..e284a9a2 100644 --- a/src/item/CustomiesItemFactory.php +++ b/src/item/CustomiesItemFactory.php @@ -23,7 +23,6 @@ use pocketmine\utils\SingletonTrait; use pocketmine\world\format\io\GlobalItemDataHandlers; use ReflectionClass; - use function array_values; final class CustomiesItemFactory { @@ -55,6 +54,7 @@ final class CustomiesItemFactory { /** * Get a custom item from its identifier. An exception will be thrown if the item is not registered. + * * @param string $identifier The string identifier for the item, usually in the format "namespace:item_name" * @param int $amount The amount of the item to be returned * @return Item The item instance @@ -68,20 +68,9 @@ public function get(string $identifier, int $amount = 1): Item { return $item->setCount($amount); } - private function loadGroups() : void { - if($this->groups !== []){ - return; - } - foreach(CreativeInventory::getInstance()->getAllEntries() as $entry){ - $group = $entry->getGroup(); - if($group !== null){ - $this->groups[$group->getName()->getText()] = $group; - } - } - } - /** * Returns all registered item table entries. + * * @return ItemTypeEntry[] */ public function getItemTableEntries(): array { @@ -91,15 +80,16 @@ public function getItemTableEntries(): array { /** * Registers the item to the item factory and assigns it an ID. It also updates the required mappings and stores the * item components if present. + * * @param Closure $itemFunc A closure that returns an instance of the item to be registered * @param string $identifier The string identifier for the item, usually in the format "namespace:item_name" - * @param CreativeInventoryInfo|null $creativeInfo The creative inventory info for the item, if any + * @param CreativeInventoryInfo $creativeInfo The creative inventory info for the item, if any * @throws InvalidArgumentException if the closure does not return an Item instance */ public function registerItem( Closure $itemFunc, string $identifier, - ?CreativeInventoryInfo $creativeInfo = new CreativeInventoryInfo(CreativeInventoryInfo::CATEGORY_ITEMS) + CreativeInventoryInfo $creativeInfo = new CreativeInventoryInfo(CreativeInventoryInfo::CATEGORY_EQUIPMENT) ): void { $item = $itemFunc(); if(!$item instanceof Item) { @@ -111,30 +101,11 @@ public function registerItem( GlobalItemDataHandlers::getSerializer()->map($item, fn() => new SavedItemData($identifier)); StringToItemParser::getInstance()->register($identifier, fn() => clone $item); - // This is where the components are added to the item + // Adding item components $componentBased = $item instanceof ItemComponents; - if($creativeInfo !== null){ - $this->loadGroups(); - if($creativeInfo->getCategory() === CreativeInventoryInfo::CATEGORY_ALL || $creativeInfo->getCategory() === CreativeInventoryInfo::CATEGORY_COMMANDS){ - return; - } - $group = $this->groups[$creativeInfo->getGroup()] ?? null; - if( - $group === null && $creativeInfo->getGroup() !== "" && - $creativeInfo->getGroup() !== CreativeInventoryInfo::NONE - ){ - $group = new CreativeGroup(new Translatable($creativeInfo->getGroup()), $item); - $this->groups[$group->getName()->getText()] = $group; - } - $category = match ($creativeInfo->getCategory()) { - CreativeInventoryInfo::CATEGORY_CONSTRUCTION => CreativeCategory::CONSTRUCTION, - CreativeInventoryInfo::CATEGORY_ITEMS => CreativeCategory::ITEMS, - CreativeInventoryInfo::CATEGORY_NATURE => CreativeCategory::NATURE, - CreativeInventoryInfo::CATEGORY_EQUIPMENT => CreativeCategory::EQUIPMENT, - default => throw new AssumptionFailedError("Unknown Creative Category") - }; - CreativeInventory::getInstance()->add($item, $category, $group); - } + // Registers the item to creative inventory + $this->registerCreativeInfo($item, $creativeInfo); + // Create the NBT data for the item $nbt = $this->createItemNbt($item, $identifier, $itemId, $creativeInfo); $entry = new ItemTypeEntry( $identifier, @@ -150,61 +121,50 @@ public function registerItem( /** * Creates the CompoundTag for an item, including components and default properties. */ - private function createItemNbt(Item $item, string $identifier, int $itemId, ?CreativeInventoryInfo $creativeInfo): CompoundTag { + private function createItemNbt(Item $item, string $identifier, int $itemId, CreativeInventoryInfo $creativeInfo): CompoundTag { if(!($item instanceof ItemComponents)) { return CompoundTag::create(); } - // Initialize item_properties with defaults - $properties = CompoundTag::create(); + $propertiesTag = CompoundTag::create(); foreach(self::PROPERTY_DEFAULTS as $name => $default) { - $properties + $propertiesTag ->setTag($name, NBT::getTagType($default)) ->setByte("hidden_in_commands", 2); } - // Set creative info - if($creativeInfo !== null) { - $properties->setTag('creative_category', NBT::getTagType($creativeInfo->getNumericCategory())); - $properties->setTag('creative_group', NBT::getTagType($creativeInfo->getGroup())); - } - + $propertiesTag->setTag('creative_category', NBT::getTagType($creativeInfo->getNumericCategory())); + $propertiesTag->setTag('creative_group', NBT::getTagType($creativeInfo->getGroup())); $tags = []; $componentsTag = CompoundTag::create(); - // Process each component foreach($item->getComponents() as $component) { $name = $component->getName(); $value = $component->getValue(); $tag = NBT::getTagType($value); - // Icon goes to item_properties if($name === 'minecraft:icon') { - $properties->setTag('minecraft:icon', $tag); + $propertiesTag->setTag('minecraft:icon', $tag); continue; } - // Tags go to item_tags if($name === 'minecraft:tags') { $tags = $value['tags'] ?? []; $componentsTag->setTag($name, $tag); continue; } - // Components with property mappings also update item_properties $mapping = $component->getPropertyMapping(); if($mapping !== null) { foreach($mapping as $prop => $propValue) { - $properties->setTag($prop, NBT::getTagType($propValue)); + $propertiesTag->setTag($prop, NBT::getTagType($propValue)); } } - // All components go to components tag $componentsTag->setTag($name, $tag); } - $components = CompoundTag::create() - ->setTag('item_properties', $properties) + ->setTag('item_properties', $propertiesTag) ->setTag('item_tags', NBT::getTagType($tags)) ->merge($componentsTag); @@ -262,4 +222,28 @@ public function registerBlockItem(string $identifier, Block $block): void { $value = $itemToBlockId->getValue($blockItemIdMap); $itemToBlockId->setValue($blockItemIdMap, $value + [$identifier => $identifier]); } + + /** + * Registers the Item in the creative inventory based on the provided CreativeInventoryInfo. + * @param Item $item The item to register + * @param CreativeInventoryInfo $creativeInfo The creative inventory information + */ + private function registerCreativeInfo( + Item $item, + CreativeInventoryInfo $creativeInfo + ): void { + $group = null; + if($creativeInfo->getGroup() !== CreativeInventoryInfo::NONE){ + $group = CreativeInventoryInfo::get($creativeInfo->getGroup()) ?? new CreativeGroup(new Translatable($creativeInfo->getGroup()), $item); + CreativeInventoryInfo::set($group); + } + $category = match($creativeInfo->getCategory()){ + CreativeInventoryInfo::CATEGORY_CONSTRUCTION => CreativeCategory::CONSTRUCTION, + CreativeInventoryInfo::CATEGORY_ITEMS => CreativeCategory::ITEMS, + CreativeInventoryInfo::CATEGORY_NATURE => CreativeCategory::NATURE, + CreativeInventoryInfo::CATEGORY_EQUIPMENT => CreativeCategory::EQUIPMENT, + default => throw new AssumptionFailedError("Unknown Creative Category"), + }; + CreativeInventory::getInstance()->add($item, $category, $group); + } } \ No newline at end of file diff --git a/src/item/ItemComponents.php b/src/item/ItemComponents.php index 68360c5a..e9696640 100644 --- a/src/item/ItemComponents.php +++ b/src/item/ItemComponents.php @@ -8,8 +8,8 @@ interface ItemComponents { /** - * Add component adds a component to the item that can be returned in the getComponents() method to be sent over - * the network. + * Adds a component to the item + * * @param ItemComponent $component * @return void */ @@ -17,6 +17,7 @@ public function addComponent(ItemComponent $component): void; /** * Returns if the item has the component with the provided name. + * * @param string $name * @return bool */ @@ -24,14 +25,16 @@ public function hasComponent(string $name): bool; /** * Returns the component with the provided name, or null if it does not exist. + * * @param string $name * @return ItemComponent|null */ public function getComponent(string $name): ?ItemComponent; /** - * Returns the fully-structured CompoundTag that can be sent to a client in the ItemComponentsPacket. - * @return ItemComponent[] + * Returns all components of the item. + * + * @return ItemComponent[] Array of all components */ public function getComponents(): array; } diff --git a/src/item/ItemComponentsTrait.php b/src/item/ItemComponentsTrait.php index 0fc6f24e..465cdf17 100644 --- a/src/item/ItemComponentsTrait.php +++ b/src/item/ItemComponentsTrait.php @@ -11,14 +11,14 @@ trait ItemComponentsTrait { /** * Registered item components indexed by component name. - * + * * @var array */ private array $components; /** * Adds a component to the item. - * + * * @param ItemComponent $component The component to add */ public function addComponent(ItemComponent $component): void { @@ -27,7 +27,7 @@ public function addComponent(ItemComponent $component): void { /** * Checks if the item has a component by its name. - * + * * @param string $name The name of the component * @return bool True if the component exists, false otherwise */ @@ -37,7 +37,7 @@ public function hasComponent(string $name): bool { /** * Retrieves a component by its name. - * + * * @param string $name The name of the component * @return ItemComponent|null The component if found, null otherwise */ @@ -54,6 +54,12 @@ public function getComponents(): array { return $this->components; } + /** + * Initializes common item components. + * + * @param string $texture The texture identifier for the icon + * @param string $name The display name of the item + */ protected function initComponent(string $texture, string $name): void { $this->addComponent(new IconComponent($texture)); $this->addComponent(new DisplayNameComponent($name)); diff --git a/src/item/component/ItemComponent.php b/src/item/component/ItemComponent.php index 800af220..3b2512cc 100644 --- a/src/item/component/ItemComponent.php +++ b/src/item/component/ItemComponent.php @@ -3,9 +3,6 @@ namespace customiesdevs\customies\item\component; -/** - * @template T - */ interface ItemComponent { /** diff --git a/src/item/properties/RepairItems.php b/src/item/properties/RepairItems.php index d65e028a..6a782a6d 100644 --- a/src/item/properties/RepairItems.php +++ b/src/item/properties/RepairItems.php @@ -77,6 +77,7 @@ public function getItems(): array { /** * Returns the repair amount provided by these items. + * @return int */ public function getRepairAmount(): int { return $this->repairAmount; diff --git a/src/task/AsyncRegisterBlocksTask.php b/src/task/AsyncRegisterBlocksTask.php index dbf2df36..6a85a9b8 100644 --- a/src/task/AsyncRegisterBlocksTask.php +++ b/src/task/AsyncRegisterBlocksTask.php @@ -3,13 +3,13 @@ namespace customiesdevs\customies\task; +use Closure; use customiesdevs\customies\block\CustomiesBlockFactory; use pmmp\thread\ThreadSafeArray; use pocketmine\block\Block; use pocketmine\data\bedrock\block\convert\BlockStateReader; use pocketmine\data\bedrock\block\convert\BlockStateWriter; use pocketmine\scheduler\AsyncTask; -use Closure; final class AsyncRegisterBlocksTask extends AsyncTask { From a9bc84d4a57ee1f61a9f7e4c365fe8602155afb6 Mon Sep 17 00:00:00 2001 From: HydroGames-dev Date: Tue, 30 Dec 2025 20:44:34 +0530 Subject: [PATCH 43/51] Move properties classes to utils namespaces Refactored block and item property classes by moving them from the properties namespace to a new utils namespace. Updated all relevant imports and usages to reflect the new locations. This improves code organization and clarifies the purpose of these utility classes. --- src/block/BlockComponentsTrait.php | 2 +- src/block/component/CollisionBoxComponent.php | 2 +- src/block/component/DestructionParticlesComponent.php | 2 +- src/block/component/EmbeddedVisualComponent.php | 2 +- src/block/component/ItemVisualComponent.php | 2 +- src/block/component/MaterialInstancesComponent.php | 2 +- src/block/component/PlacementFilterComponent.php | 2 +- src/block/{properties => utils}/AllowedFace.php | 2 +- src/block/{properties => utils}/BlockDescriptor.php | 2 +- src/block/{properties => utils}/Box.php | 2 +- src/block/{properties => utils}/Material.php | 2 +- src/block/{properties => utils}/PlacementCondition.php | 4 ++-- src/block/{properties => utils}/RenderMethod.php | 2 +- src/block/{properties => utils}/TintMethod.php | 2 +- src/examples/ExampleBlock.php | 2 +- src/item/component/DamageAbsorptionComponent.php | 2 +- src/item/component/DurabilitySensorComponent.php | 4 ++-- src/item/component/RepairableComponent.php | 2 +- src/item/{properties => utils}/DamageCause.php | 2 +- src/item/{properties => utils}/ParticleType.php | 2 +- src/item/{properties => utils}/RepairItems.php | 2 +- src/item/{properties => utils}/SoundEvent.php | 2 +- 22 files changed, 24 insertions(+), 24 deletions(-) rename src/block/{properties => utils}/AllowedFace.php (84%) rename src/block/{properties => utils}/BlockDescriptor.php (96%) rename src/block/{properties => utils}/Box.php (98%) rename src/block/{properties => utils}/Material.php (98%) rename src/block/{properties => utils}/PlacementCondition.php (92%) rename src/block/{properties => utils}/RenderMethod.php (89%) rename src/block/{properties => utils}/TintMethod.php (83%) rename src/item/{properties => utils}/DamageCause.php (96%) rename src/item/{properties => utils}/ParticleType.php (98%) rename src/item/{properties => utils}/RepairItems.php (97%) rename src/item/{properties => utils}/SoundEvent.php (99%) diff --git a/src/block/BlockComponentsTrait.php b/src/block/BlockComponentsTrait.php index a886ba1f..dd9604d8 100644 --- a/src/block/BlockComponentsTrait.php +++ b/src/block/BlockComponentsTrait.php @@ -7,7 +7,7 @@ use customiesdevs\customies\block\component\DisplayNameComponent; use customiesdevs\customies\block\component\GeometryComponent; use customiesdevs\customies\block\component\MaterialInstancesComponent; -use customiesdevs\customies\block\properties\Material; +use customiesdevs\customies\block\utils\Material; trait BlockComponentsTrait { diff --git a/src/block/component/CollisionBoxComponent.php b/src/block/component/CollisionBoxComponent.php index a3f256f2..c088dfb4 100644 --- a/src/block/component/CollisionBoxComponent.php +++ b/src/block/component/CollisionBoxComponent.php @@ -3,7 +3,7 @@ namespace customiesdevs\customies\block\component; -use customiesdevs\customies\block\properties\Box; +use customiesdevs\customies\block\utils\Box; use pocketmine\math\Vector3; final class CollisionBoxComponent implements BlockComponent { diff --git a/src/block/component/DestructionParticlesComponent.php b/src/block/component/DestructionParticlesComponent.php index fdf959a1..b90bde1c 100644 --- a/src/block/component/DestructionParticlesComponent.php +++ b/src/block/component/DestructionParticlesComponent.php @@ -2,7 +2,7 @@ namespace customiesdevs\customies\block\component; -use customiesdevs\customies\block\properties\TintMethod; +use customiesdevs\customies\block\utils\TintMethod; final class DestructionParticlesComponent implements BlockComponent { diff --git a/src/block/component/EmbeddedVisualComponent.php b/src/block/component/EmbeddedVisualComponent.php index dc50a0e3..1af25c75 100644 --- a/src/block/component/EmbeddedVisualComponent.php +++ b/src/block/component/EmbeddedVisualComponent.php @@ -2,7 +2,7 @@ namespace customiesdevs\customies\block\component; -use customiesdevs\customies\block\properties\Material; +use customiesdevs\customies\block\utils\Material; final class EmbeddedVisualComponent implements BlockComponent { diff --git a/src/block/component/ItemVisualComponent.php b/src/block/component/ItemVisualComponent.php index 2ade002a..7cf7f91f 100644 --- a/src/block/component/ItemVisualComponent.php +++ b/src/block/component/ItemVisualComponent.php @@ -2,7 +2,7 @@ namespace customiesdevs\customies\block\component; -use customiesdevs\customies\block\properties\Material; +use customiesdevs\customies\block\utils\Material; use pocketmine\nbt\tag\ByteTag; final class ItemVisualComponent implements BlockComponent { diff --git a/src/block/component/MaterialInstancesComponent.php b/src/block/component/MaterialInstancesComponent.php index 8239526c..6f7c3b7a 100644 --- a/src/block/component/MaterialInstancesComponent.php +++ b/src/block/component/MaterialInstancesComponent.php @@ -2,7 +2,7 @@ namespace customiesdevs\customies\block\component; -use customiesdevs\customies\block\properties\Material; +use customiesdevs\customies\block\utils\Material; final class MaterialInstancesComponent implements BlockComponent { diff --git a/src/block/component/PlacementFilterComponent.php b/src/block/component/PlacementFilterComponent.php index 2b8a0684..cc10d5fe 100644 --- a/src/block/component/PlacementFilterComponent.php +++ b/src/block/component/PlacementFilterComponent.php @@ -2,7 +2,7 @@ namespace customiesdevs\customies\block\component; -use customiesdevs\customies\block\properties\PlacementCondition; +use customiesdevs\customies\block\utils\PlacementCondition; use InvalidArgumentException; final class PlacementFilterComponent implements BlockComponent { diff --git a/src/block/properties/AllowedFace.php b/src/block/utils/AllowedFace.php similarity index 84% rename from src/block/properties/AllowedFace.php rename to src/block/utils/AllowedFace.php index fa872a62..4d68ecbe 100644 --- a/src/block/properties/AllowedFace.php +++ b/src/block/utils/AllowedFace.php @@ -1,7 +1,7 @@ Date: Tue, 30 Dec 2025 20:47:28 +0530 Subject: [PATCH 44/51] Prevent duplicate component initialization in traits Added checks in BlockComponentsTrait and ItemComponentsTrait to ensure components are only initialized if none are set yet. This avoids redundant component addition and potential side effects. --- src/block/BlockComponentsTrait.php | 7 ++++++- src/item/ItemComponentsTrait.php | 5 +++++ 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/src/block/BlockComponentsTrait.php b/src/block/BlockComponentsTrait.php index dd9604d8..1089dfb6 100644 --- a/src/block/BlockComponentsTrait.php +++ b/src/block/BlockComponentsTrait.php @@ -56,6 +56,7 @@ public function getComponents(): array { } /** + * @todo * Initializes the default components for a block with the given texture and name. * Adds geometry, material instances, and display name components. * @@ -63,8 +64,12 @@ public function getComponents(): array { * @param string $name The display name of the block */ protected function initComponents(string $texture, string $name): void { + // Only initialize if no components are set yet + if($this->getComponents() !== []){ + return; + } $this->addComponent(new GeometryComponent()); $this->addComponent(new MaterialInstancesComponent([new Material("*", $texture)])); $this->addComponent(new DisplayNameComponent($name)); } -} +} \ No newline at end of file diff --git a/src/item/ItemComponentsTrait.php b/src/item/ItemComponentsTrait.php index 465cdf17..b9470059 100644 --- a/src/item/ItemComponentsTrait.php +++ b/src/item/ItemComponentsTrait.php @@ -55,12 +55,17 @@ public function getComponents(): array { } /** + * @todo * Initializes common item components. * * @param string $texture The texture identifier for the icon * @param string $name The display name of the item */ protected function initComponent(string $texture, string $name): void { + // Only initialize if no components are set yet + if($this->getComponents() !== []){ + return; + } $this->addComponent(new IconComponent($texture)); $this->addComponent(new DisplayNameComponent($name)); } From cbd22cd7bad442e323474c2548634bc87073b5b3 Mon Sep 17 00:00:00 2001 From: HydroGames-dev Date: Wed, 31 Dec 2025 09:22:42 +0530 Subject: [PATCH 45/51] Refactor block/item utils to properties, improve components Moved utility classes from 'utils' to 'properties' namespaces for both blocks and items, updating all references accordingly. Extracted cartesian product logic from BlockPermutation to a new Permutations class. Improved CollisionBoxComponent to use default/no collision boxes from Box, and added validation for box types. Enhanced item property handling in CustomiesItemFactory, including property ordering and type casting, and added a method to sort CompoundTags. Removed the ExampleBlock from examples. --- src/block/CustomiesBlockFactory.php | 5 +- src/block/{ => component}/BlockComponents.php | 2 +- .../{ => component}/BlockComponentsTrait.php | 4 +- src/block/component/CollisionBoxComponent.php | 37 ++++--- .../DestructionParticlesComponent.php | 2 +- .../component/EmbeddedVisualComponent.php | 2 +- src/block/component/ItemVisualComponent.php | 2 +- .../component/MaterialInstancesComponent.php | 2 +- .../component/PlacementFilterComponent.php | 2 +- src/block/permutations/BlockPermutation.php | 31 ------ src/block/permutations/Permutations.php | 81 +++++++++++++++ .../{utils => properties}/AllowedFace.php | 2 +- .../{utils => properties}/BlockDescriptor.php | 2 +- src/block/{utils => properties}/Box.php | 18 +++- src/block/{utils => properties}/Material.php | 2 +- .../PlacementCondition.php | 4 +- .../{utils => properties}/RenderMethod.php | 2 +- .../{utils => properties}/TintMethod.php | 2 +- src/block/states/BlockStates.php | 4 - src/examples/ExampleBlock.php | 99 ------------------- src/item/CustomiesItemFactory.php | 69 +++++++++---- .../component/DamageAbsorptionComponent.php | 2 +- .../component/DurabilitySensorComponent.php | 4 +- src/item/component/MaxStackSizeComponent.php | 6 +- src/item/component/RepairableComponent.php | 2 +- src/item/component/UseAnimationComponent.php | 2 +- .../{utils => properties}/DamageCause.php | 2 +- .../{utils => properties}/ParticleType.php | 2 +- .../{utils => properties}/RepairItems.php | 2 +- src/item/{utils => properties}/SoundEvent.php | 2 +- src/util/NBT.php | 18 +++- 31 files changed, 217 insertions(+), 199 deletions(-) rename src/block/{ => component}/BlockComponents.php (94%) rename src/block/{ => component}/BlockComponentsTrait.php (95%) create mode 100644 src/block/permutations/Permutations.php rename src/block/{utils => properties}/AllowedFace.php (84%) rename src/block/{utils => properties}/BlockDescriptor.php (96%) rename src/block/{utils => properties}/Box.php (87%) rename src/block/{utils => properties}/Material.php (98%) rename src/block/{utils => properties}/PlacementCondition.php (92%) rename src/block/{utils => properties}/RenderMethod.php (89%) rename src/block/{utils => properties}/TintMethod.php (83%) delete mode 100644 src/examples/ExampleBlock.php rename src/item/{utils => properties}/DamageCause.php (96%) rename src/item/{utils => properties}/ParticleType.php (98%) rename src/item/{utils => properties}/RepairItems.php (97%) rename src/item/{utils => properties}/SoundEvent.php (99%) diff --git a/src/block/CustomiesBlockFactory.php b/src/block/CustomiesBlockFactory.php index 3db63b8d..dade14a9 100644 --- a/src/block/CustomiesBlockFactory.php +++ b/src/block/CustomiesBlockFactory.php @@ -4,9 +4,10 @@ namespace customiesdevs\customies\block; use Closure; -use customiesdevs\customies\block\BlockComponents; +use customiesdevs\customies\block\component\BlockComponents; use customiesdevs\customies\block\permutations\BlockPermutation; use customiesdevs\customies\block\permutations\BlockPermutations; +use customiesdevs\customies\block\permutations\Permutations; use customiesdevs\customies\item\CreativeInventoryInfo; use customiesdevs\customies\item\CustomiesItemFactory; use customiesdevs\customies\task\AsyncRegisterBlocksTask; @@ -191,7 +192,7 @@ private function registerPermutations( $block->getPermutations() ))); $nbt->setTag("properties", new ListTag(array_reverse($blockProperties))); - foreach(BlockPermutation::getCartesianProduct($blockValues) as $meta => $stateValues){ + foreach(Permutations::getCartesianProduct($blockValues) as $meta => $stateValues){ $stateTag = CompoundTag::create(); // We need to insert states for every possible permutation to allow for all blocks to be used and to // keep in sync with the client's block palette. diff --git a/src/block/BlockComponents.php b/src/block/component/BlockComponents.php similarity index 94% rename from src/block/BlockComponents.php rename to src/block/component/BlockComponents.php index 93ce7f8a..befac4bc 100644 --- a/src/block/BlockComponents.php +++ b/src/block/component/BlockComponents.php @@ -1,7 +1,7 @@ enabled = $enabled; + //if no boxes are defined we add a default full block box + if($this->enabled){ + $this->boxes[] = Box::defaultCollisionBox(); + }else{ + // We send a small box here or it will crash client + // or just dont use the component at all + $this->boxes[] = Box::noCollisionBox(); + } } public function getName(): string { @@ -26,27 +33,15 @@ public function getName(): string { public function getValue(): array { $boxes = []; - foreach($this->boxes as $box) { + foreach($this->boxes as $box){ $boxes[] = $box->toNbtArray(); } - //if no boxes are defined we add a default full block box - if(empty($boxes)){ - $boxes[] = $this->enabled ? self::defaultCollisionBox()->toNbtArray() : self::noCollisionBox()->toNbtArray(); - } return [ "boxes" => $boxes, "enabled" => $this->enabled ]; } - public static function defaultCollisionBox(): Box { - return new Box(new Vector3(-8, 0, -8), new Vector3(16, 8, 16)); - } - - public static function noCollisionBox(): Box { - return new Box(new Vector3(-8, 0, -8), new Vector3(0.0001, 0.0001, 0.0001)); - } - /** * Adds a single collision box. * @param Box $box @@ -54,6 +49,9 @@ public static function noCollisionBox(): Box { * @return $this */ public function addBox(Box $box): self { + if(count($this->boxes) === 1){ + $this->boxes = []; + } $this->boxes[] = $box; return $this; } @@ -63,9 +61,16 @@ public function addBox(Box $box): self { * @param Box[] $boxes * An array of collision boxes to add. * @return $this + * @throws \InvalidArgumentException If any element in the array is not an instance of Box. */ public function addBoxes(array $boxes): self { - foreach($boxes as $box) { + if(count($this->boxes) === 1){ + $this->boxes = []; + } + foreach($boxes as $box){ + if(!$box instanceof Box){ + throw new \InvalidArgumentException("All boxes must be instances of " . Box::class); + } $this->boxes[] = $box; } return $this; diff --git a/src/block/component/DestructionParticlesComponent.php b/src/block/component/DestructionParticlesComponent.php index b90bde1c..fdf959a1 100644 --- a/src/block/component/DestructionParticlesComponent.php +++ b/src/block/component/DestructionParticlesComponent.php @@ -2,7 +2,7 @@ namespace customiesdevs\customies\block\component; -use customiesdevs\customies\block\utils\TintMethod; +use customiesdevs\customies\block\properties\TintMethod; final class DestructionParticlesComponent implements BlockComponent { diff --git a/src/block/component/EmbeddedVisualComponent.php b/src/block/component/EmbeddedVisualComponent.php index 1af25c75..dc50a0e3 100644 --- a/src/block/component/EmbeddedVisualComponent.php +++ b/src/block/component/EmbeddedVisualComponent.php @@ -2,7 +2,7 @@ namespace customiesdevs\customies\block\component; -use customiesdevs\customies\block\utils\Material; +use customiesdevs\customies\block\properties\Material; final class EmbeddedVisualComponent implements BlockComponent { diff --git a/src/block/component/ItemVisualComponent.php b/src/block/component/ItemVisualComponent.php index 7cf7f91f..2ade002a 100644 --- a/src/block/component/ItemVisualComponent.php +++ b/src/block/component/ItemVisualComponent.php @@ -2,7 +2,7 @@ namespace customiesdevs\customies\block\component; -use customiesdevs\customies\block\utils\Material; +use customiesdevs\customies\block\properties\Material; use pocketmine\nbt\tag\ByteTag; final class ItemVisualComponent implements BlockComponent { diff --git a/src/block/component/MaterialInstancesComponent.php b/src/block/component/MaterialInstancesComponent.php index 6f7c3b7a..8239526c 100644 --- a/src/block/component/MaterialInstancesComponent.php +++ b/src/block/component/MaterialInstancesComponent.php @@ -2,7 +2,7 @@ namespace customiesdevs\customies\block\component; -use customiesdevs\customies\block\utils\Material; +use customiesdevs\customies\block\properties\Material; final class MaterialInstancesComponent implements BlockComponent { diff --git a/src/block/component/PlacementFilterComponent.php b/src/block/component/PlacementFilterComponent.php index cc10d5fe..2b8a0684 100644 --- a/src/block/component/PlacementFilterComponent.php +++ b/src/block/component/PlacementFilterComponent.php @@ -2,7 +2,7 @@ namespace customiesdevs\customies\block\component; -use customiesdevs\customies\block\utils\PlacementCondition; +use customiesdevs\customies\block\properties\PlacementCondition; use InvalidArgumentException; final class PlacementFilterComponent implements BlockComponent { diff --git a/src/block/permutations/BlockPermutation.php b/src/block/permutations/BlockPermutation.php index 6b17e762..652d00c3 100644 --- a/src/block/permutations/BlockPermutation.php +++ b/src/block/permutations/BlockPermutation.php @@ -41,35 +41,4 @@ public function toArray(): array { ] ]; } - - /** - * Computes the Cartesian product of the provided arrays. - * Each array in the input represents a set of possible values for a block property. - * The result is an array of all possible combinations of these values. - * - * @param array[] $arrays An array of arrays, each containing possible values for a block property - * @return array An array of arrays, each representing a unique combination of property values - */ - public static function getCartesianProduct(array $arrays): array { - if($arrays === []){ - return [[]]; - } - $result = []; - $count = count($arrays) - 1; - $combinations = array_product(array_map(static fn(array $array) => count($array), $arrays)); - for($i = 0; $i < $combinations; $i++){ - $row = []; - foreach($arrays as $index => $_){ - $row[] = current($arrays[$index]); - } - $result[] = $row; - for($j = $count; $j >= 0; $j--){ - if(next($arrays[$j]) !== false){ - break; - } - reset($arrays[$j]); - } - } - return $result; - } } \ No newline at end of file diff --git a/src/block/permutations/Permutations.php b/src/block/permutations/Permutations.php new file mode 100644 index 00000000..ef736357 --- /dev/null +++ b/src/block/permutations/Permutations.php @@ -0,0 +1,81 @@ + $blockProperty->getValues(), $block->getStates()) + )[$meta] ?? null; + if($properties === null) { + throw new Exception("Unable to calculate permutations from block meta: " . $meta); + } + return $properties; + } + + /** + * Attempts to convert the block in to a meta value based on the possible permutations of the block. An exception is + * thrown if the state of the block is not a possible combination of all the block properties. + */ + public static function toMeta(BlockPermutations $block): int { + $properties = self::getCartesianProduct( + array_map(static fn(BlockState $blockProperty) => $blockProperty->getValues(), $block->getStates()) + ); + foreach($properties as $meta => $permutations){ + if($permutations === $block->getCurrentStates()) { + return $meta; + } + } + throw new Exception("Unable to calculate block meta from current permutations"); + } + + /** + * Returns the number of bits required to represent all the possible permutations of the block. + */ + public static function getStateBitmask(BlockPermutations $block): int { + $possibleValues = array_map(static fn(BlockState $blockProperty) => $blockProperty->getValues(), $block->getStates()); + return count(self::getCartesianProduct($possibleValues)) - 1; + } + + /** + * Returns an 2-dimensional array containing all possible combinations of the provided arrays using the cartesian + * product (https://en.wikipedia.org/wiki/Cartesian_product). + */ + public static function getCartesianProduct(array $arrays): array { + if($arrays === []){ + return [[]]; + } + $result = []; + $count = count($arrays) - 1; + $combinations = array_product(array_map(static fn(array $array) => count($array), $arrays)); + for($i = 0; $i < $combinations; $i++){ + $row = []; + foreach($arrays as $index => $_){ + $row[] = current($arrays[$index]); + } + $result[] = $row; + for($j = $count; $j >= 0; $j--){ + if(next($arrays[$j]) !== false){ + break; + } + reset($arrays[$j]); + } + } + return $result; + } +} \ No newline at end of file diff --git a/src/block/utils/AllowedFace.php b/src/block/properties/AllowedFace.php similarity index 84% rename from src/block/utils/AllowedFace.php rename to src/block/properties/AllowedFace.php index 4d68ecbe..fa872a62 100644 --- a/src/block/utils/AllowedFace.php +++ b/src/block/properties/AllowedFace.php @@ -1,7 +1,7 @@ x, $max->y, $max->z ); } + + /** + * Returns a default full block collision box. + * @return Box + */ + public static function defaultCollisionBox(): Box { + return new self(new Vector3(-8.0, 0.0, -8.0), new Vector3(16.0, 16.0, 16.0)); + } + + /** + * Returns a box that effectively has no collision. + * @return Box + */ + public static function noCollisionBox(): Box { + return new self(new Vector3(-8.0, 0.0, -8.0), new Vector3(0.0001, 0.00001, 0.0001)); + } } \ No newline at end of file diff --git a/src/block/utils/Material.php b/src/block/properties/Material.php similarity index 98% rename from src/block/utils/Material.php rename to src/block/properties/Material.php index ee2f2329..cb195370 100644 --- a/src/block/utils/Material.php +++ b/src/block/properties/Material.php @@ -1,7 +1,7 @@ addComponent(new GeometryComponent("minecraft:geometry.full_block")); - - // Material instances - bark on sides, tops on up/down - $this->addComponent(new MaterialInstancesComponent([ - new Material(Material::TARGET_ALL, "bum_template_bark"), - new Material(Material::TARGET_UP, "bum_template_tops"), - new Material(Material::TARGET_DOWN, "bum_template_tops"), - ])); - $this->addState(new BlockState("minecraft:block_face", ["north", "south", "east", "west", "up", "down"])); - $this->addPermutations([ - new BlockPermutation( - "q.block_state('minecraft:block_face') == 'west' || q.block_state('minecraft:block_face') == 'east'", - new TransformationComponent(new Vector3(0, 0, 90)) - ), - new BlockPermutation( - "q.block_state('minecraft:block_face') == 'down' || q.block_state('minecraft:block_face') == 'up'", - new TransformationComponent(new Vector3(0, 0, 0)) - ), - new BlockPermutation( - "q.block_state('minecraft:block_face') == 'north' || q.block_state('minecraft:block_face') == 'south'", - new TransformationComponent(new Vector3(90, 0, 0)) - ) - ]); - } - - public function getCurrentStates(): array { - return [$this->axis]; - } - - public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, ?Player $player = null): bool { - $this->axis = match($face) { - 0, 1 => Axis::Y, // down, up - 2, 3 => Axis::Z, // north, south - 4, 5 => Axis::X, // west, east - default => Axis::Y - }; - return parent::place($tx, $item, $blockReplace, $blockClicked, $face, $clickVector, $player); - } - - public function serializeState(BlockStateWriter $out): void { - $rotation = match($this->axis) { - Axis::X => "east", - Axis::Y => "up", - Axis::Z => "north", - default => "down" - }; - $out->writeString("minecraft:block_face", $rotation); - } - - public function deserializeState(BlockStateReader $in): void { - $this->axis = match($in->readString("minecraft:block_face")) { - "east", "west" => Axis::X, - "up", "down" => Axis::Y, - "north", "south" => Axis::Z, - default => Axis::Y - }; - } -} \ No newline at end of file diff --git a/src/item/CustomiesItemFactory.php b/src/item/CustomiesItemFactory.php index e284a9a2..43d7cc72 100644 --- a/src/item/CustomiesItemFactory.php +++ b/src/item/CustomiesItemFactory.php @@ -30,21 +30,43 @@ final class CustomiesItemFactory { /** Default values for item_properties */ private const PROPERTY_DEFAULTS = [ - 'allow_off_hand' => false, - 'can_destroy_in_creative' => true, - 'damage' => 0, - 'enchantable_slot' => 'none', - 'enchantable_value' => 0, - 'foil' => false, - 'frame_count' => 1, - 'hand_equipped' => false, - 'liquid_clipped' => false, - 'max_stack_size' => 64, - 'mining_speed' => 1.0, - 'should_despawn' => true, - 'stacked_by_data' => false, - 'use_animation' => 0, - 'use_duration' => 0, + 'allow_off_hand' => false, // Byte + 'can_destroy_in_creative' => true, // Byte + 'damage' => 0, // Int + 'enchantable_slot' => 'none', // String + 'enchantable_value' => 0, // Int + 'foil' => false, // Byte + 'frame_count' => 1, // Int + 'hand_equipped' => false, // Byte + 'liquid_clipped' => false, // Byte + 'max_stack_size' => 64, // Int + 'mining_speed' => 1.0, // Float + 'should_despawn' => true, // Byte + 'stacked_by_data' => false, // Byte + 'use_animation' => 0, // Int + 'use_duration' => 0, // Int + ]; + + private const PROPERTY_ORDER = [ + 'allow_off_hand', + 'can_destroy_in_creative', + 'creative_category', + 'creative_group', + 'damage', + 'enchantable_slot', + 'enchantable_value', + 'foil', + 'frame_count', + 'hand_equipped', + 'hidden_in_commands', + 'liquid_clipped', + 'max_stack_size', + 'minecraft:icon', + 'mining_speed', + 'should_despawn', + 'stacked_by_data', + 'use_animation', + 'use_duration', ]; /** @var ItemTypeEntry[] */ @@ -129,12 +151,12 @@ private function createItemNbt(Item $item, string $identifier, int $itemId, Crea $propertiesTag = CompoundTag::create(); foreach(self::PROPERTY_DEFAULTS as $name => $default) { $propertiesTag - ->setTag($name, NBT::getTagType($default)) - ->setByte("hidden_in_commands", 2); + ->setTag($name, NBT::getTagType($default)); } // Set creative info - $propertiesTag->setTag('creative_category', NBT::getTagType($creativeInfo->getNumericCategory())); - $propertiesTag->setTag('creative_group', NBT::getTagType($creativeInfo->getGroup())); + $propertiesTag->setTag('creative_category', NBT::getTagType((int) $creativeInfo->getNumericCategory())); + $propertiesTag->setTag('creative_group', NBT::getTagType((string) $creativeInfo->getGroup())); + $propertiesTag->setByte("hidden_in_commands", 2); $tags = []; $componentsTag = CompoundTag::create(); // Process each component @@ -157,17 +179,26 @@ private function createItemNbt(Item $item, string $identifier, int $itemId, Crea $mapping = $component->getPropertyMapping(); if($mapping !== null) { foreach($mapping as $prop => $propValue) { + if($prop === "use_duration"){ + $propertiesTag->setTag("use_duration", NBT::getTagType((int) round($propValue * 20))); + continue; + } $propertiesTag->setTag($prop, NBT::getTagType($propValue)); } } // All components go to components tag $componentsTag->setTag($name, $tag); } + $propertiesTag = NBT::sortCompoundTag($propertiesTag, self::PROPERTY_ORDER); $components = CompoundTag::create() ->setTag('item_properties', $propertiesTag) ->setTag('item_tags', NBT::getTagType($tags)) ->merge($componentsTag); + \file_put_contents( + "{$itemId}.json", + $components, JSON_PRETTY_PRINT + ); return CompoundTag::create() ->setTag('components', $components) ->setInt('id', $itemId) diff --git a/src/item/component/DamageAbsorptionComponent.php b/src/item/component/DamageAbsorptionComponent.php index 26edc440..c371f565 100644 --- a/src/item/component/DamageAbsorptionComponent.php +++ b/src/item/component/DamageAbsorptionComponent.php @@ -3,7 +3,7 @@ namespace customiesdevs\customies\item\component; -use customiesdevs\customies\item\utils\DamageCause; +use customiesdevs\customies\item\properties\DamageCause; final class DamageAbsorptionComponent implements ItemComponent { diff --git a/src/item/component/DurabilitySensorComponent.php b/src/item/component/DurabilitySensorComponent.php index abd8f2f4..66239bb3 100644 --- a/src/item/component/DurabilitySensorComponent.php +++ b/src/item/component/DurabilitySensorComponent.php @@ -3,8 +3,8 @@ namespace customiesdevs\customies\item\component; -use customiesdevs\customies\item\utils\ParticleType; -use customiesdevs\customies\item\utils\SoundEvent; +use customiesdevs\customies\item\properties\ParticleType; +use customiesdevs\customies\item\properties\SoundEvent; final class DurabilitySensorComponent implements ItemComponent { diff --git a/src/item/component/MaxStackSizeComponent.php b/src/item/component/MaxStackSizeComponent.php index 57f1fc5d..c11b7b85 100644 --- a/src/item/component/MaxStackSizeComponent.php +++ b/src/item/component/MaxStackSizeComponent.php @@ -3,6 +3,8 @@ namespace customiesdevs\customies\item\component; +use pocketmine\nbt\tag\ByteTag; + final class MaxStackSizeComponent implements ItemComponent { private int $maxStackSize; @@ -21,11 +23,11 @@ public function getName(): string { public function getValue(): array { return [ - "value" => $this->maxStackSize + "value" => new ByteTag($this->maxStackSize) ]; } public function getPropertyMapping(): ?array { - return ['max_stack_size' => $this->maxStackSize]; + return ['max_stack_size' => (int) $this->maxStackSize]; } } \ No newline at end of file diff --git a/src/item/component/RepairableComponent.php b/src/item/component/RepairableComponent.php index a568f2a8..89bf1282 100644 --- a/src/item/component/RepairableComponent.php +++ b/src/item/component/RepairableComponent.php @@ -3,7 +3,7 @@ namespace customiesdevs\customies\item\component; -use customiesdevs\customies\item\utils\RepairItems; +use customiesdevs\customies\item\properties\RepairItems; final class RepairableComponent implements ItemComponent { diff --git a/src/item/component/UseAnimationComponent.php b/src/item/component/UseAnimationComponent.php index 06b43c66..cd7530b3 100644 --- a/src/item/component/UseAnimationComponent.php +++ b/src/item/component/UseAnimationComponent.php @@ -50,6 +50,6 @@ public function getValue(): array { } public function getPropertyMapping(): ?array { - return ['use_animation' => self::STRING_TO_INT[$this->animation] ?? 0]; + return ['use_animation' => (int) self::STRING_TO_INT[$this->animation] ?? 0]; } } \ No newline at end of file diff --git a/src/item/utils/DamageCause.php b/src/item/properties/DamageCause.php similarity index 96% rename from src/item/utils/DamageCause.php rename to src/item/properties/DamageCause.php index 62115a4b..0bdb3631 100644 --- a/src/item/utils/DamageCause.php +++ b/src/item/properties/DamageCause.php @@ -1,7 +1,7 @@ getTag($key); + if($existing !== null){ + $sorted->setTag($key, $existing); + } + } + foreach($tag->getValue() as $key => $value){ + if($sorted->getTag((string) $key) === null){ + $sorted->setTag((string) $key, $value); + } + } + return $sorted; + } } \ No newline at end of file From ec178ee3389c127234178b1365ec559b681711a1 Mon Sep 17 00:00:00 2001 From: HydroGames-dev Date: Wed, 31 Dec 2025 09:23:52 +0530 Subject: [PATCH 46/51] Update CustomiesItemFactory.php --- src/item/CustomiesItemFactory.php | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/item/CustomiesItemFactory.php b/src/item/CustomiesItemFactory.php index 43d7cc72..c5fd0e98 100644 --- a/src/item/CustomiesItemFactory.php +++ b/src/item/CustomiesItemFactory.php @@ -195,10 +195,6 @@ private function createItemNbt(Item $item, string $identifier, int $itemId, Crea ->setTag('item_tags', NBT::getTagType($tags)) ->merge($componentsTag); - \file_put_contents( - "{$itemId}.json", - $components, JSON_PRETTY_PRINT - ); return CompoundTag::create() ->setTag('components', $components) ->setInt('id', $itemId) From caaa960ad628fbf7e994732c66ecbea00c883515 Mon Sep 17 00:00:00 2001 From: HydroGames-dev Date: Wed, 31 Dec 2025 10:18:13 +0530 Subject: [PATCH 47/51] Refactor creative inventory registration logic Moved creative inventory registration for items and blocks into CreativeInventoryInfo::registerCreativeInfo, consolidating duplicate logic from CustomiesBlockFactory and CustomiesItemFactory. This improves maintainability and centralizes creative inventory handling. --- src/block/CustomiesBlockFactory.php | 125 +++++++++------------------- src/item/CreativeInventoryInfo.php | 34 +++++++- src/item/CustomiesItemFactory.php | 30 +------ 3 files changed, 73 insertions(+), 116 deletions(-) diff --git a/src/block/CustomiesBlockFactory.php b/src/block/CustomiesBlockFactory.php index dade14a9..705ec39b 100644 --- a/src/block/CustomiesBlockFactory.php +++ b/src/block/CustomiesBlockFactory.php @@ -18,16 +18,12 @@ use pocketmine\data\bedrock\block\BlockStateData; use pocketmine\data\bedrock\block\convert\BlockStateReader; use pocketmine\data\bedrock\block\convert\BlockStateWriter; -use pocketmine\inventory\CreativeCategory; use pocketmine\inventory\CreativeGroup; -use pocketmine\inventory\CreativeInventory; -use pocketmine\lang\Translatable; use pocketmine\nbt\tag\CompoundTag; use pocketmine\nbt\tag\ListTag; use pocketmine\network\mcpe\protocol\types\BlockPaletteEntry; use pocketmine\network\mcpe\protocol\types\CacheableNbt; use pocketmine\Server; -use pocketmine\utils\AssumptionFailedError; use pocketmine\utils\SingletonTrait; use pocketmine\world\format\io\GlobalBlockStateHandlers; use RuntimeException; @@ -92,8 +88,8 @@ public function getBlockPaletteEntries(): array { * @param Closure $blockFunc A closure that returns a new instance of the block to register. * @param string $identifier The unique identifier for the block (e.g. "namespace:block_name"). * @param CreativeInventoryInfo $creativeInfo Creative inventory information for the block. Default set to `Equipment` Category. - * @param Closure|null $serializer Optional closure that takes a BlockStateWriter and returns it after writing the block state. - * @param Closure|null $deserializer Optional closure that takes a BlockStateReader and returns a new instance of the block after reading the state. + * @param (Closure(BlockStateWriter): Block)|null $serializer Optional closure that takes a BlockStateWriter and returns it after writing the block state. + * @param (Closure(Block): BlockStateReader)|null $deserializer Optional closure that takes a BlockStateReader and returns a new instance of the block after reading the state. * @throws InvalidArgumentException If the blockFunc does not return a Block instance. */ public function registerBlock( @@ -130,8 +126,42 @@ public function registerBlock( ); // Adds States/Permutation to Block if($block instanceof BlockPermutations){ - // Register the States/Permutation to the block - $this->registerPermutations($block, $identifier, $nbtTag, $serializer, $deserializer); + $blockNames = $blockValues = $blockProperties = []; + foreach($block->getStates() as $state){ + $blockNames[] = $state->getName(); + $blockValues[] = $state->getValues(); + $blockProperties[] = NBT::getTagType($state->getValue()); + } + $nbtTag->setTag("permutations", new ListTag(array_map( + static fn(BlockPermutation $p) => NBT::getTagType($p->toArray()), + $block->getPermutations() + ))); + $nbtTag->setTag("properties", new ListTag(array_reverse($blockProperties))); + foreach(Permutations::getCartesianProduct($blockValues) as $meta => $stateValues){ + $stateTag = CompoundTag::create(); + // We need to insert states for every possible permutation to allow for all blocks to be used and to + // keep in sync with the client's block palette. + foreach($stateValues as $i => $value){ + $stateTag->setTag($blockNames[$i], NBT::getTagType($value)); + } + BlockPalette::getInstance()->insertState( + CompoundTag::create() + ->setString(BlockStateData::TAG_NAME, $identifier) + ->setTag(BlockStateData::TAG_STATES, $stateTag), + $meta + ); + } + $serializer ??= static function (BlockPermutations $b) use ($identifier): BlockStateWriter { + $writer = BlockStateWriter::create($identifier); + $b->serializeState($writer); + return $writer; + }; + $deserializer ??= static function (BlockStateReader $in) use ($identifier): BlockPermutations { + $b = CustomiesBlockFactory::getInstance()->get($identifier); + assert($b instanceof BlockPermutations); + $b->deserializeState($in); + return $b; + }; }else{ // If a block does not contain any permutations we can just insert the one state. BlockPalette::getInstance()->insertState( @@ -151,7 +181,7 @@ public function registerBlock( $nbtTag->setTag("components", $componentsTag); $nbtTag->setInt("molangVersion", 13); // Registers the block to creative inventory - $this->registerCreativeInfo($block, $creativeInfo); + CreativeInventoryInfo::registerCreativeInfo($block, $creativeInfo); $this->blockPaletteEntries[] = new BlockPaletteEntry($identifier, new CacheableNbt($nbtTag)); $this->blockFuncs[$identifier] = [$blockFunc, $serializer, $deserializer]; // 1.20.60 added a new "block_id" field which depends on the order of the block palette entries. Every time we @@ -165,81 +195,4 @@ public function registerBlock( $this->blockPaletteEntries[$i] = new BlockPaletteEntry($entry->getName(), new CacheableNbt($root)); } } - - /** - * Registers permutations and states for a BlockPermutations instance. - * @param BlockPermutations $block The block instance - * @param string $identifier The unique block identifier - * @param CompoundTag $nbt The NBT tag to populate with permutation data - * @param Closure|null &$serializer Reference to the serializer closure to set - * @param Closure|null &$deserializer Reference to the deserializer closure to set - */ - private function registerPermutations( - BlockPermutations $block, - string $identifier, - CompoundTag $nbt, - ?Closure &$serializer, - ?Closure &$deserializer - ): void { - $blockNames = $blockValues = $blockProperties = []; - foreach($block->getStates() as $state){ - $blockNames[] = $state->getName(); - $blockValues[] = $state->getValues(); - $blockProperties[] = NBT::getTagType($state->getValue()); - } - $nbt->setTag("permutations", new ListTag(array_map( - static fn(BlockPermutation $p) => NBT::getTagType($p->toArray()), - $block->getPermutations() - ))); - $nbt->setTag("properties", new ListTag(array_reverse($blockProperties))); - foreach(Permutations::getCartesianProduct($blockValues) as $meta => $stateValues){ - $stateTag = CompoundTag::create(); - // We need to insert states for every possible permutation to allow for all blocks to be used and to - // keep in sync with the client's block palette. - foreach($stateValues as $i => $value){ - $stateTag->setTag($blockNames[$i], NBT::getTagType($value)); - } - BlockPalette::getInstance()->insertState( - CompoundTag::create() - ->setString(BlockStateData::TAG_NAME, $identifier) - ->setTag(BlockStateData::TAG_STATES, $stateTag), - $meta - ); - } - $serializer ??= static function (BlockPermutations $b) use ($identifier): BlockStateWriter { - $writer = BlockStateWriter::create($identifier); - $b->serializeState($writer); - return $writer; - }; - $deserializer ??= static function (BlockStateReader $in) use ($identifier): BlockPermutations { - $b = CustomiesBlockFactory::getInstance()->get($identifier); - assert($b instanceof BlockPermutations); - $b->deserializeState($in); - return $b; - }; - } - - /** - * Registers the block in the creative inventory based on the provided CreativeInventoryInfo. - * @param Block $block The block to register - * @param CreativeInventoryInfo $creativeInfo The creative inventory information - */ - private function registerCreativeInfo( - Block $block, - CreativeInventoryInfo $creativeInfo - ): void { - $group = null; - if($creativeInfo->getGroup() !== CreativeInventoryInfo::NONE){ - $group = CreativeInventoryInfo::get($creativeInfo->getGroup()) ?? new CreativeGroup(new Translatable($creativeInfo->getGroup()), $block->asItem()); - CreativeInventoryInfo::set($group); - } - $category = match($creativeInfo->getCategory()){ - CreativeInventoryInfo::CATEGORY_CONSTRUCTION => CreativeCategory::CONSTRUCTION, - CreativeInventoryInfo::CATEGORY_ITEMS => CreativeCategory::ITEMS, - CreativeInventoryInfo::CATEGORY_NATURE => CreativeCategory::NATURE, - CreativeInventoryInfo::CATEGORY_EQUIPMENT => CreativeCategory::EQUIPMENT, - default => throw new AssumptionFailedError("Unknown Creative Category"), - }; - CreativeInventory::getInstance()->add($block->asItem(), $category, $group); - } } \ No newline at end of file diff --git a/src/item/CreativeInventoryInfo.php b/src/item/CreativeInventoryInfo.php index e6bdbc96..c5efea07 100644 --- a/src/item/CreativeInventoryInfo.php +++ b/src/item/CreativeInventoryInfo.php @@ -2,8 +2,13 @@ namespace customiesdevs\customies\item; -use pocketmine\inventory\CreativeInventory; +use pocketmine\block\Block; +use pocketmine\inventory\CreativeCategory; use pocketmine\inventory\CreativeGroup; +use pocketmine\inventory\CreativeInventory; +use pocketmine\item\Item; +use pocketmine\lang\Translatable; +use pocketmine\utils\AssumptionFailedError; final class CreativeInventoryInfo { @@ -204,4 +209,31 @@ public static function all(): array { self::load(); return self::$groups; } + + /** + * Registers the Item/Bloxk in the creative inventory based on the provided CreativeInventoryInfo. + * @param Item|Block $type The item/block to register + * @param CreativeInventoryInfo $creativeInfo The creative inventory information + */ + public static function registerCreativeInfo( + Item|Block $type, + CreativeInventoryInfo $creativeInfo + ): void { + $group = null; + if($creativeInfo->getGroup() !== CreativeInventoryInfo::NONE){ + $group = CreativeInventoryInfo::get($creativeInfo->getGroup()) + ?? new CreativeGroup( + new Translatable($creativeInfo->getGroup()), + $type instanceof Block ? $type->asItem() : $type + ); + } + $category = match($creativeInfo->getCategory()){ + CreativeInventoryInfo::CATEGORY_CONSTRUCTION => CreativeCategory::CONSTRUCTION, + CreativeInventoryInfo::CATEGORY_ITEMS => CreativeCategory::ITEMS, + CreativeInventoryInfo::CATEGORY_NATURE => CreativeCategory::NATURE, + CreativeInventoryInfo::CATEGORY_EQUIPMENT => CreativeCategory::EQUIPMENT, + default => throw new AssumptionFailedError("Unknown Creative Category: " . $creativeInfo->getCategory()), + }; + CreativeInventory::getInstance()->add($type instanceof Block ? $type->asItem() : $type, $category, $group); + } } \ No newline at end of file diff --git a/src/item/CustomiesItemFactory.php b/src/item/CustomiesItemFactory.php index c5fd0e98..2ec77470 100644 --- a/src/item/CustomiesItemFactory.php +++ b/src/item/CustomiesItemFactory.php @@ -9,17 +9,13 @@ use pocketmine\block\Block; use pocketmine\data\bedrock\item\BlockItemIdMap; use pocketmine\data\bedrock\item\SavedItemData; -use pocketmine\inventory\CreativeCategory; use pocketmine\inventory\CreativeGroup; -use pocketmine\inventory\CreativeInventory; use pocketmine\item\Item; use pocketmine\item\StringToItemParser; -use pocketmine\lang\Translatable; use pocketmine\nbt\tag\CompoundTag; use pocketmine\network\mcpe\convert\TypeConverter; use pocketmine\network\mcpe\protocol\types\CacheableNbt; use pocketmine\network\mcpe\protocol\types\ItemTypeEntry; -use pocketmine\utils\AssumptionFailedError; use pocketmine\utils\SingletonTrait; use pocketmine\world\format\io\GlobalItemDataHandlers; use ReflectionClass; @@ -126,7 +122,7 @@ public function registerItem( // Adding item components $componentBased = $item instanceof ItemComponents; // Registers the item to creative inventory - $this->registerCreativeInfo($item, $creativeInfo); + CreativeInventoryInfo::registerCreativeInfo($item, $creativeInfo); // Create the NBT data for the item $nbt = $this->createItemNbt($item, $identifier, $itemId, $creativeInfo); $entry = new ItemTypeEntry( @@ -249,28 +245,4 @@ public function registerBlockItem(string $identifier, Block $block): void { $value = $itemToBlockId->getValue($blockItemIdMap); $itemToBlockId->setValue($blockItemIdMap, $value + [$identifier => $identifier]); } - - /** - * Registers the Item in the creative inventory based on the provided CreativeInventoryInfo. - * @param Item $item The item to register - * @param CreativeInventoryInfo $creativeInfo The creative inventory information - */ - private function registerCreativeInfo( - Item $item, - CreativeInventoryInfo $creativeInfo - ): void { - $group = null; - if($creativeInfo->getGroup() !== CreativeInventoryInfo::NONE){ - $group = CreativeInventoryInfo::get($creativeInfo->getGroup()) ?? new CreativeGroup(new Translatable($creativeInfo->getGroup()), $item); - CreativeInventoryInfo::set($group); - } - $category = match($creativeInfo->getCategory()){ - CreativeInventoryInfo::CATEGORY_CONSTRUCTION => CreativeCategory::CONSTRUCTION, - CreativeInventoryInfo::CATEGORY_ITEMS => CreativeCategory::ITEMS, - CreativeInventoryInfo::CATEGORY_NATURE => CreativeCategory::NATURE, - CreativeInventoryInfo::CATEGORY_EQUIPMENT => CreativeCategory::EQUIPMENT, - default => throw new AssumptionFailedError("Unknown Creative Category"), - }; - CreativeInventory::getInstance()->add($item, $category, $group); - } } \ No newline at end of file From bfa86f7b83e78e01f2364c91e664716a55ff95f8 Mon Sep 17 00:00:00 2001 From: HydroGames-dev Date: Wed, 31 Dec 2025 22:14:03 +0530 Subject: [PATCH 48/51] improve collision handling improved collision box logic to handle enabled/disabled states --- src/block/CustomiesBlockFactory.php | 2 -- src/block/component/BlockComponentsTrait.php | 2 +- src/block/component/CollisionBoxComponent.php | 16 ++++++++-------- .../permutations/BlockPermutationsTrait.php | 2 +- src/block/properties/Box.php | 10 +--------- src/item/CustomiesItemFactory.php | 3 +-- src/item/component/TagsComponent.php | 8 +++++++- 7 files changed, 19 insertions(+), 24 deletions(-) diff --git a/src/block/CustomiesBlockFactory.php b/src/block/CustomiesBlockFactory.php index 705ec39b..86417819 100644 --- a/src/block/CustomiesBlockFactory.php +++ b/src/block/CustomiesBlockFactory.php @@ -45,8 +45,6 @@ final class CustomiesBlockFactory { private array $blockPaletteEntries = []; /** @var array Map of block identifiers to block instances */ private array $customBlocks = []; - /** @var array Map of group names to creative groups */ - private array $groups = []; /** * Adds a worker initialize hook to the async pool to sync the BlockFactory for every thread worker that is created. diff --git a/src/block/component/BlockComponentsTrait.php b/src/block/component/BlockComponentsTrait.php index 1cbb51d9..57ae339f 100644 --- a/src/block/component/BlockComponentsTrait.php +++ b/src/block/component/BlockComponentsTrait.php @@ -15,7 +15,7 @@ trait BlockComponentsTrait { * Registered block components indexed by component name. * @var array */ - private array $components = []; + private array $components; /** * Adds or replaces a block component. diff --git a/src/block/component/CollisionBoxComponent.php b/src/block/component/CollisionBoxComponent.php index 0b78e35b..d0556415 100644 --- a/src/block/component/CollisionBoxComponent.php +++ b/src/block/component/CollisionBoxComponent.php @@ -17,14 +17,6 @@ final class CollisionBoxComponent implements BlockComponent { */ public function __construct(bool $enabled = true) { $this->enabled = $enabled; - //if no boxes are defined we add a default full block box - if($this->enabled){ - $this->boxes[] = Box::defaultCollisionBox(); - }else{ - // We send a small box here or it will crash client - // or just dont use the component at all - $this->boxes[] = Box::noCollisionBox(); - } } public function getName(): string { @@ -36,6 +28,14 @@ public function getValue(): array { foreach($this->boxes as $box){ $boxes[] = $box->toNbtArray(); } + // if no boxes are defined we add a default full block box + if($this->enabled && empty($boxes)){ + $boxes[] = Box::defaultBox()->toNbtArray(); + } + // no collision + if(!$this->enabled){ + $boxes[] = []; + } return [ "boxes" => $boxes, "enabled" => $this->enabled diff --git a/src/block/permutations/BlockPermutationsTrait.php b/src/block/permutations/BlockPermutationsTrait.php index a2ee893a..18097870 100644 --- a/src/block/permutations/BlockPermutationsTrait.php +++ b/src/block/permutations/BlockPermutationsTrait.php @@ -12,7 +12,7 @@ trait BlockPermutationsTrait { * Registered block permutations. * @var BlockPermutation[] */ - private array $blockPermutations = []; + private array $blockPermutations; /** * Adds a permutation to the block. diff --git a/src/block/properties/Box.php b/src/block/properties/Box.php index 93a4e0f7..9a762735 100644 --- a/src/block/properties/Box.php +++ b/src/block/properties/Box.php @@ -125,15 +125,7 @@ public function toAxisAlignedBB(): AxisAlignedBB { * Returns a default full block collision box. * @return Box */ - public static function defaultCollisionBox(): Box { + public static function defaultBox(): Box { return new self(new Vector3(-8.0, 0.0, -8.0), new Vector3(16.0, 16.0, 16.0)); } - - /** - * Returns a box that effectively has no collision. - * @return Box - */ - public static function noCollisionBox(): Box { - return new self(new Vector3(-8.0, 0.0, -8.0), new Vector3(0.0001, 0.00001, 0.0001)); - } } \ No newline at end of file diff --git a/src/item/CustomiesItemFactory.php b/src/item/CustomiesItemFactory.php index 2ec77470..97ae82d6 100644 --- a/src/item/CustomiesItemFactory.php +++ b/src/item/CustomiesItemFactory.php @@ -43,6 +43,7 @@ final class CustomiesItemFactory { 'use_duration' => 0, // Int ]; + /** Order in which properties should appear in item_properties */ private const PROPERTY_ORDER = [ 'allow_off_hand', 'can_destroy_in_creative', @@ -67,8 +68,6 @@ final class CustomiesItemFactory { /** @var ItemTypeEntry[] */ private array $itemTableEntries = []; - /** @var CreativeGroup[] */ - private array $groups = []; /** * Get a custom item from its identifier. An exception will be thrown if the item is not registered. diff --git a/src/item/component/TagsComponent.php b/src/item/component/TagsComponent.php index 1bd8e00f..5c588196 100644 --- a/src/item/component/TagsComponent.php +++ b/src/item/component/TagsComponent.php @@ -5,13 +5,19 @@ final class TagsComponent implements ItemComponent { + /** @var string[] */ private array $tags = []; /** * Determines which tags are included on a given item. - * @param array $tags An array that can contain multiple item tags. + * @param string[] $tags An array that can contain multiple item tags. */ public function __construct(array $tags = []) { + foreach($tags as $tag){ + if(!is_string($tag)){ + throw new \InvalidArgumentException('All tags must be strings'); + } + } $this->tags = $tags; } From 0e3f86e7b98a9a47215296ac47322f90e872f20f Mon Sep 17 00:00:00 2001 From: HydroGames-dev Date: Mon, 5 Jan 2026 00:51:17 +0530 Subject: [PATCH 49/51] Add validation and enhancements to item/block components Introduces stricter input validation and error handling for various item and block components, such as destructible, durability, compostable, food, and enchantable components. Adds new helper methods, improves type safety, and enhances extensibility (e.g., new EffectType enum, support for potion effects in FoodComponent, and improved handling of reach/conditions in weapon components). Also fixes enum usage in DamageCause and updates constructors and docblocks for clarity and correctness. --- .../DestructibleByMiningComponent.php | 64 ++- src/block/properties/BlockDescriptor.php | 14 +- src/item/CreativeInventoryInfo.php | 6 + .../component/BundleInteractionComponent.php | 6 +- src/item/component/CompostableComponent.php | 10 +- src/item/component/CooldownComponent.php | 8 + .../component/DamageAbsorptionComponent.php | 24 +- src/item/component/DamageComponent.php | 8 +- src/item/component/DiggerComponent.php | 43 +- src/item/component/DurabilityComponent.php | 15 + .../component/DurabilitySensorComponent.php | 55 ++- src/item/component/DyeableComponent.php | 11 - src/item/component/EnchantableComponent.php | 12 +- src/item/component/FoodComponent.php | 157 ++++++- src/item/component/FuelComponent.php | 6 +- src/item/component/KineticWeaponComponent.php | 110 +++-- src/item/component/MaxStackSizeComponent.php | 3 + .../component/PiercingWeaponComponent.php | 35 +- src/item/component/ProjectileComponent.php | 5 +- src/item/component/ShooterComponent.php | 10 +- src/item/component/SwingSoundsComponent.php | 24 +- src/item/component/UseModifiersComponent.php | 10 +- src/item/properties/DamageCause.php | 72 +-- src/item/properties/EffectType.php | 80 ++++ src/item/properties/ParticleType.php | 182 ++++---- src/item/properties/RepairAmount.php | 51 ++ src/item/properties/RepairItems.php | 78 +--- src/item/properties/SoundEvent.php | 435 +++++++++--------- 28 files changed, 1002 insertions(+), 532 deletions(-) create mode 100644 src/item/properties/EffectType.php create mode 100644 src/item/properties/RepairAmount.php diff --git a/src/block/component/DestructibleByMiningComponent.php b/src/block/component/DestructibleByMiningComponent.php index b4d019dc..db86b695 100644 --- a/src/block/component/DestructibleByMiningComponent.php +++ b/src/block/component/DestructibleByMiningComponent.php @@ -2,15 +2,31 @@ namespace customiesdevs\customies\block\component; +use pocketmine\nbt\tag\ShortTag; + final class DestructibleByMiningComponent implements BlockComponent { + /** Seconds to destroy with base equipment */ private float $secondsToDestroy; + /** + * @var array + */ + private array $itemSpecificSpeeds = []; /** * Describes the destructible by mining properties for this block. If set to true, the block will take the default number of seconds to destroy. If set to false, this block is indestructible by mining. If the component is omitted, the block will take the default number of seconds to destroy. * @param float $secondsToDestroy Sets the number of seconds it takes to destroy the block with base equipment. Greater numbers result in greater mining times. */ public function __construct(float $secondsToDestroy = 0.0) { + if($secondsToDestroy < 0){ + throw new \InvalidArgumentException("secondsToDestroy must be >= 0"); + } $this->secondsToDestroy = $secondsToDestroy; } @@ -19,8 +35,54 @@ public function getName(): string { } public function getValue(): array { - return [ + $data = [ "value" => $this->secondsToDestroy ]; + if($this->itemSpecificSpeeds !== []){ + $data["item_specific_speeds"] = $this->itemSpecificSpeeds; + } + return $data; + } + + /** + * Adds an item-specific destroy speed using item tags (Molang). + * @param float $destroySpeed + * @param string $tags Molang tag expression + * @param int $molangVersion + */ + public function addItemSpeedByTags( + float $destroySpeed, + string $tags, + ): self { + if($destroySpeed <= 0){ + throw new \InvalidArgumentException("destroy_speed must be > 0"); + } + $this->itemSpecificSpeeds[] = [ + "destroy_speed" => $destroySpeed, + "item" => [ + "MolangVersion" => new ShortTag(13), + "Tags" => $tags + ] + ]; + return $this; + } + + /** + * Adds an item-specific destroy speed using an item identifier. + * @param float $destroySpeed + * @param string $itemId + */ + public function addItemSpeedByItem( + float $destroySpeed, + string $itemId + ): self { + if($destroySpeed <= 0){ + throw new \InvalidArgumentException("destroy_speed must be > 0"); + } + $this->itemSpecificSpeeds[] = [ + "destroy_speed" => $destroySpeed, + "item" => $itemId + ]; + return $this; } } \ No newline at end of file diff --git a/src/block/properties/BlockDescriptor.php b/src/block/properties/BlockDescriptor.php index a141b77c..711f5e72 100644 --- a/src/block/properties/BlockDescriptor.php +++ b/src/block/properties/BlockDescriptor.php @@ -19,25 +19,20 @@ final class BlockDescriptor { private array $states = []; /** @var string|null */ private ?string $tags; - /** @var int|null */ - private ?int $tagsVersion; /** * @param string|null $name Block identifier (e.g. minecraft:dirt) * @param array $states * @param string|null $tags Molang tag query - * @param int|null $tagsVersion Molang version */ public function __construct( ?string $name = null, array $states = [], - ?string $tags = null, - ?int $tagsVersion = null + ?string $tags = null ) { $this->name = $name; $this->states = $states; $this->tags = $tags; - $this->tagsVersion = $tagsVersion; } /** @@ -53,9 +48,7 @@ public function toArray(): array { } if($this->tags !== null){ $data["tags"] = $this->tags; - if($this->tagsVersion !== null) { - $data["tags_version"] = $this->tagsVersion; - } + $data["tags_version"] = (int) 13; } return $data; } @@ -67,8 +60,7 @@ public static function fromArray(array $data): self { return new self( $data["name"] ?? null, $data["states"] ?? [], - $data["tags"] ?? null, - $data["tags_version"] ?? null + $data["tags"] ?? null ); } } \ No newline at end of file diff --git a/src/item/CreativeInventoryInfo.php b/src/item/CreativeInventoryInfo.php index c5efea07..487508a8 100644 --- a/src/item/CreativeInventoryInfo.php +++ b/src/item/CreativeInventoryInfo.php @@ -219,6 +219,12 @@ public static function registerCreativeInfo( Item|Block $type, CreativeInventoryInfo $creativeInfo ): void { + if( + $creativeInfo->getCategory() === self::CATEGORY_ALL || + $creativeInfo->getCategory() === self::CATEGORY_COMMANDS + ){ + return; + } $group = null; if($creativeInfo->getGroup() !== CreativeInventoryInfo::NONE){ $group = CreativeInventoryInfo::get($creativeInfo->getGroup()) diff --git a/src/item/component/BundleInteractionComponent.php b/src/item/component/BundleInteractionComponent.php index a9835017..1b7b0df7 100644 --- a/src/item/component/BundleInteractionComponent.php +++ b/src/item/component/BundleInteractionComponent.php @@ -10,9 +10,13 @@ final class BundleInteractionComponent implements ItemComponent { /** * Enables the bundle-specific interaction scheme and tooltip for an item. * To use this component, the item must have a `minecraft:storage_item` item component defined. - * @param int $numViewableSlots The maximum number of slots in the bundle viewable by the player. Can be from 1 to 64. Default is 12. Value must be >= 1. Value must be <= 64. + * @param int $numViewableSlots The maximum number of slots in the bundle viewable by the player. Can be from 1 to 64. Default is 12. + * @throws \InvalidArgumentException if the number of viewable slots is not between 1 and 64. */ public function __construct(int $numViewableSlots = 12) { + if($numViewableSlots < 1 || $numViewableSlots > 64) { + throw new \InvalidArgumentException("Number of viewable-slots must be between 1 and 64, $numViewableSlots given"); + } $this->numViewableSlots = $numViewableSlots; } diff --git a/src/item/component/CompostableComponent.php b/src/item/component/CompostableComponent.php index 0e12fe7e..3b42d248 100644 --- a/src/item/component/CompostableComponent.php +++ b/src/item/component/CompostableComponent.php @@ -3,15 +3,21 @@ namespace customiesdevs\customies\item\component; +use pocketmine\nbt\tag\ByteTag; + final class CompostableComponent implements ItemComponent { private int $compostingChance; /** * Specifies that an item is compostable and provides the chance of creating a composting layer in the composter. - * @param int $compostingChance The chance of this item to create a layer upon composting with the composter. Valid value range is 1 - 100 inclusive Value must be >= 1. Value must be <= 100. + * @param int $compostingChance The chance of this item to create a layer upon composting with the composter. + * @throws \InvalidArgumentException if the composting chance is not between 1 and 100. */ public function __construct(int $compostingChance) { + if($compostingChance < 1 || $compostingChance > 100) { + throw new \InvalidArgumentException("Composting chance must be between 1 and 100, $compostingChance given"); + } $this->compostingChance = $compostingChance; } @@ -21,7 +27,7 @@ public function getName(): string { public function getValue(): array { return [ - "composting_chance" => $this->compostingChance + "composting_chance" => new ByteTag($this->compostingChance) ]; } diff --git a/src/item/component/CooldownComponent.php b/src/item/component/CooldownComponent.php index cee6c83d..99341f21 100644 --- a/src/item/component/CooldownComponent.php +++ b/src/item/component/CooldownComponent.php @@ -11,7 +11,15 @@ final class CooldownComponent implements ItemComponent { public const CATEGORY_WINDCHARGE = "wind_charge"; public const CATEGORY_CHORUS = "chorusfruit"; + /** + * Causes the cooldown to start when the player attacks while holding the item and + * prevents the item from being used to attack while the cooldown is active. + */ public const TYPE_ATTACK = "attack"; + /** + * Causes the cooldown to start when the item is used and + * prevents the item from being used while the cooldown is active. + */ public const TYPE_USE = "use"; private string $category; diff --git a/src/item/component/DamageAbsorptionComponent.php b/src/item/component/DamageAbsorptionComponent.php index c371f565..41e68715 100644 --- a/src/item/component/DamageAbsorptionComponent.php +++ b/src/item/component/DamageAbsorptionComponent.php @@ -9,7 +9,7 @@ final class DamageAbsorptionComponent implements ItemComponent { /** * List of damage causes that can be absorbed by the item. - * @var DamageCause[] Must contain at least 1 item for meaningful effect. + * @var DamageCause[] */ private array $absorbableCauses = []; @@ -18,10 +18,12 @@ final class DamageAbsorptionComponent implements ItemComponent { * For this to happen, the item needs to be equipped in an armor slot. * The absorbed damage reduces the item's durability, with any excess damage being ignored. * Because of this, the item also needs a `minecraft:durability` component. - * @param array $absorbableCauses List of damage causes that can be absorbed by the item. By default, no damage cause is absorbed. Value must have at least 1 items. + * @param array $absorbableCauses List of damage causes that can be absorbed by the item. By default, no damage cause is absorbed. */ public function __construct(array $absorbableCauses = []) { - $this->absorbableCauses = $absorbableCauses; + foreach($absorbableCauses as $cause){ + $this->addCause($cause); + } } public function getName(): string { @@ -30,8 +32,8 @@ public function getName(): string { public function getValue(): array { return [ - "absorbable_causes" => array_map( - fn(DamageCause $cause) => $cause->value, + 'absorbable_causes' => array_map( + static fn (DamageCause $cause) => $cause->value, $this->absorbableCauses ) ]; @@ -42,10 +44,16 @@ public function getPropertyMapping(): ?array { } /** - * Adds a single damage cause to the absorbable list. - * @param DamageCause $cause + * Adds a damage cause to the absorbable list. + * @param DamageCause|string $cause */ - public function addCause(DamageCause $cause): self { + public function addCause(DamageCause|string $cause): self { + if(is_string($cause)){ + $cause = DamageCause::tryFrom($cause); + if($cause === null){ + throw new \InvalidArgumentException("Invalid damage cause: {$cause}"); + } + } if(!in_array($cause, $this->absorbableCauses, true)){ $this->absorbableCauses[] = $cause; } diff --git a/src/item/component/DamageComponent.php b/src/item/component/DamageComponent.php index 073c75d4..1038f7d8 100644 --- a/src/item/component/DamageComponent.php +++ b/src/item/component/DamageComponent.php @@ -3,6 +3,8 @@ namespace customiesdevs\customies\item\component; +use pocketmine\nbt\tag\ByteTag; + final class DamageComponent implements ItemComponent { private int $damage; @@ -11,8 +13,12 @@ final class DamageComponent implements ItemComponent { * Determines how much extra damage the item does on attack. * Note that this must be a positive value. * @param int $damage Specifies how much extra damage the item does, must be a positive number. + * @throws \InvalidArgumentException if the damage value is negative. */ public function __construct(int $damage) { + if($damage < 0) { + throw new \InvalidArgumentException("Damage value must be a positive number, $damage given"); + } $this->damage = $damage; } @@ -22,7 +28,7 @@ public function getName(): string { public function getValue(): array { return [ - "value" => $this->damage + "value" => new ByteTag($this->damage) ]; } diff --git a/src/item/component/DiggerComponent.php b/src/item/component/DiggerComponent.php index a388ee21..f59ba488 100644 --- a/src/item/component/DiggerComponent.php +++ b/src/item/component/DiggerComponent.php @@ -10,16 +10,30 @@ final class DiggerComponent implements ItemComponent { - /** @var array, speed: int}> */ + /** + * @var array + */ private array $destroySpeeds = []; private bool $useEfficiency; /** * Allows a creator to determine how quickly an item can dig specific blocks. * @param bool $useEfficiency Determines whether the item should be impacted by the Efficiency enchantment. - * @param array, speed: int}> $destroySpeeds Optional array of destroy speeds. + * @param array $destroySpeeds An array of blocks/tags and their corresponding digging speeds. */ - public function __construct(bool $useEfficiency, array $destroySpeeds = []) { + public function __construct(bool $useEfficiency = false, array $destroySpeeds = []) { $this->useEfficiency = $useEfficiency; $this->destroySpeeds = $destroySpeeds; } @@ -62,19 +76,36 @@ public function withBlocks(int $speed, Block ...$blocks): self { * @param string ...$tags A list of block tags */ public function withTags(int $speed, string ...$tags): self { - $query = implode(",", array_map(fn($tag) => "'" . $tag . "'", $tags)); + $query = implode(", ", array_map(fn(string $tag) => "'" . $tag . "'", $tags)); $this->destroySpeeds[] = [ "block" => [ - "tags" => "query.any_tag(" . $query . ")" + "tags" => "query.any_tag($query)" ], "speed" => $speed ]; return $this; } + /** @todo */ + private function withStates(int $speed, array ...$states): self { + foreach($states as $state){ + $stateArray = []; + foreach($state as $key => $value){ + $stateArray[$key] = $value; + } + $this->destroySpeeds[] = [ + "block" => [ + "states" => $stateArray, + ], + "speed" => $speed + ]; + } + return $this; + } + /** * Returns the array of destroy speeds. - * @return array, speed: int}> + * @return array The destroy speeds array. */ public function getDestroySpeeds(): array { return $this->destroySpeeds; diff --git a/src/item/component/DurabilityComponent.php b/src/item/component/DurabilityComponent.php index 5109cbac..a71e28f8 100644 --- a/src/item/component/DurabilityComponent.php +++ b/src/item/component/DurabilityComponent.php @@ -14,8 +14,17 @@ final class DurabilityComponent implements ItemComponent { * @param int $maxDurability Max durability is the amount of damage that this item can take before breaking * @param int $minDamageChance Specifies the percentage minimum chance for durability to take damage. Range: [0, 100]. Default is set to `100` * @param int $maxDamageChance Specifies the percentage maximum chance for durability to take damage. Range: [0, 100]. Default is set to `100` + * @throws \InvalidArgumentException if the min or max damage chance is not between 0 and 100, or if minDamageChance is greater than maxDamageChance, or if maxDurability is not between 0 and 2147483647. */ public function __construct(int $maxDurability, int $minDamageChance = 100, int $maxDamageChance = 100) { + self::validate($minDamageChance, 'Minimum'); + self::validate($maxDamageChance, 'Maximum'); + if($minDamageChance > $maxDamageChance){ + throw new \InvalidArgumentException("Minimum damage chance ($minDamageChance) cannot be greater than maximum damage chance ($maxDamageChance)"); + } + if($maxDurability < 0 || $maxDurability > 2147483647){ + throw new \InvalidArgumentException("Max durability must be between 0 and 2147483647, $maxDurability given"); + } $this->maxDurability = $maxDurability; $this->minDamageChance = $minDamageChance; $this->maxDamageChance = $maxDamageChance; @@ -38,4 +47,10 @@ public function getValue(): array { public function getPropertyMapping(): ?array { return null; } + + private static function validate(int $value, string $type): void { + if($value < 0 || $value > 100){ + throw new \InvalidArgumentException("$type damage chance must be between 0 and 100, $value given"); + } + } } \ No newline at end of file diff --git a/src/item/component/DurabilitySensorComponent.php b/src/item/component/DurabilitySensorComponent.php index 66239bb3..ae26f30d 100644 --- a/src/item/component/DurabilitySensorComponent.php +++ b/src/item/component/DurabilitySensorComponent.php @@ -8,14 +8,37 @@ final class DurabilitySensorComponent implements ItemComponent { + /** + * @var array + */ private array $durabilityThresholds = []; /** * Enables an item to emit effects when it receives damage. Because of this, the item also needs a `minecraft:durability` component. - * @param array $durabilityThresholds - */ + * @param array $durabilityThresholds + */ public function __construct(array $durabilityThresholds = []) { - $this->durabilityThresholds = $durabilityThresholds; + if(isset($durabilityThresholds['durability'])){ + $durabilityThresholds = [$durabilityThresholds]; + } + foreach($durabilityThresholds as $threshold){ + if(!is_array($threshold)){ + throw new \InvalidArgumentException("Durability threshold must be an array"); + } + $this->addDurabilityThreshold( + $threshold['durability'], + $threshold['particle_type'] ?? null, + $threshold['sound_event'] ?? null + ); + } } public function getName(): string { @@ -27,8 +50,8 @@ public function getValue(): array { "durability_thresholds" => array_map( fn(array $threshold) => [ "durability" => $threshold["durability"], - "particle_type" => $threshold["particle_type"]?->value, - "sound_event" => $threshold["sound_event"]?->value + "particle_type" => $threshold["particle_type"], + "sound_event" => $threshold["sound_event"] ], $this->durabilityThresholds ) @@ -40,21 +63,27 @@ public function getPropertyMapping(): ?array { } /** - * Adds a new durability threshold. - * - * @param int $durability The durability at which this threshold triggers - * @param ParticleType|null $particleType Optional particle effect - * @param SoundEvent|null $soundEvent Optional sound effect + * Adds a durability threshold that triggers effects when the item's durability reaches the specified value. + * @param int $durability The durability threshold at which the effects are triggered. Must be >= 0. + * @param ParticleType|null $particleType The type of particle effect to emit when the threshold is reached. If null, no particle effect is emitted. + * @param SoundEvent|null $soundEvent The sound event to play when the threshold is reached. If null, no sound is played. + * @return $this */ public function addDurabilityThreshold( int $durability, ?ParticleType $particleType = null, ?SoundEvent $soundEvent = null ): self { - $this->durabilityThresholds[] = [ + if($durability < 0){ + throw new \InvalidArgumentException("Durability threshold must be >= 0, $durability given"); + } + if($particleType === null && $soundEvent === null){ + throw new \InvalidArgumentException("At least one of particle_type or sound_event must be specified"); + } + $this->durabilityThresholds[] = [ "durability" => $durability, - "particle_type" => $particleType, - "sound_event" => $soundEvent + "particle_type" => $particleType->value, + "sound_event" => $soundEvent->value ]; return $this; } diff --git a/src/item/component/DyeableComponent.php b/src/item/component/DyeableComponent.php index 4ade1b1f..9217b622 100644 --- a/src/item/component/DyeableComponent.php +++ b/src/item/component/DyeableComponent.php @@ -50,15 +50,4 @@ private static function hexToRgb(string $hex): array { hexdec(substr($hex, 4, 2)) ]; } - - /** - * Converts an RGB array to a hex color string. - * - * @param int[] $rgb Array of RGB values [R, G, B] - * @return string Hex color string (e.g. "#175882") - */ - private static function rgbToHex(array $rgb): string { - [$r, $g, $b] = $rgb; - return sprintf("#%02x%02x%02x", $r, $g, $b); - } } \ No newline at end of file diff --git a/src/item/component/EnchantableComponent.php b/src/item/component/EnchantableComponent.php index bd40291a..f3433404 100644 --- a/src/item/component/EnchantableComponent.php +++ b/src/item/component/EnchantableComponent.php @@ -3,6 +3,8 @@ namespace customiesdevs\customies\item\component; +use pocketmine\nbt\tag\ByteTag; + final class EnchantableComponent implements ItemComponent { // Item Type @@ -59,9 +61,13 @@ final class EnchantableComponent implements ItemComponent { /** * Determines what enchantments can be applied to the item. Not all enchantments will have an effect on all item components. * @param string $slot Specifies which types of enchantments can be applied. For example, `bow` would allow this item to be enchanted as if it were a bow - * @param int $value Specifies the value of the enchantment, Default is set to `1` + * @param int $value Specifies the value of the enchantment. + * @throws \InvalidArgumentException if the value is not between 0 and 32767. */ - public function __construct(string $slot = self::SLOT_NONE, int $value = 1) { + public function __construct(string $slot, int $value) { + if($value < 0 || $value > 32767){ + throw new \InvalidArgumentException("Enchantable value must be between 0 and 32767, $value given"); + } $this->slot = $slot; $this->value = $value; } @@ -73,7 +79,7 @@ public function getName(): string { public function getValue(): array { return [ "slot" => $this->slot, - "value" => $this->value + "value" => new ByteTag($this->value) ]; } diff --git a/src/item/component/FoodComponent.php b/src/item/component/FoodComponent.php index ee32b628..b3decc11 100644 --- a/src/item/component/FoodComponent.php +++ b/src/item/component/FoodComponent.php @@ -3,21 +3,69 @@ namespace customiesdevs\customies\item\component; +use customiesdevs\customies\item\properties\EffectType; + final class FoodComponent implements ItemComponent { + /** Default eating behavior */ + public const USE_ACTION_NORMAL = -1; + /** Chorus fruit teleport */ + public const USE_ACTION_CHORUS_TELEPORT = 0; + /** Suspicious stew effect handler */ + public const USE_ACTION_SUSPICIOUS_STEW_EFFECT = 1; + + public const MODIFIER_POOR = 0.1; + public const MODIFIER_LOW = 0.3; + public const MODIFIER_NORMAL = 0.6; + public const MODIFIER_GOOD = 0.8; + public const MODIFIER_SUPERNATURAL = 1.2; + + /** No cooldown type */ + public const COOLDOWN_DEFAULT = ""; + /** Chorus fruit cooldown type (forces 20 ticks) */ + public const COOLDOWN_CHORUSFRUIT = "chorusfruit"; + private bool $canAlwaysEat; private int $nutrition; private float $saturationModifier; private string $usingConvertsTo; + private ?int $cooldownTime = null; + private ?string $cooldownType = null; + private ?int $onUseAction = null; + /** @var array{0: float, 1: float, 2: float}|null */ + private ?array $onUseRange = null; + /** + * Potion effects applied when the food is eaten. + * + * @var array + */ + private array $effects = []; + /** + * List of potion effect IDs to remove when the food is consumed. + * @var int[] + */ + private array $removeEffects = []; /** * Sets the item as a food component, allowing it to be edible to the player. - * @param bool $canAlwaysEat If true you can always eat this item (even when not hungry) - * @param int $nutrition Value that is added to the entity's nutrition when the item is used - * @param float $saturationModifier - * @param string $usingConvertsTo When used, converts to the item specified by the string in this field. Default does not convert item + * @param bool $canAlwaysEat Whether the player can always eat this food, even when not hungry. Default is false. + * @param int $nutrition The amount of hunger points this food item restores when eaten. Default is 0. + * @param float $saturationModifier The saturation modifier for this food item. Default is `MODIFIER_NORMAL` (0.6). + * @param string $usingConvertsTo The item this food converts to after being consumed. Default is an empty string. */ - public function __construct(bool $canAlwaysEat = false, int $nutrition = 0, float $saturationModifier = 0.6, string $usingConvertsTo = "") { + public function __construct( + bool $canAlwaysEat = false, + int $nutrition = 0, + float $saturationModifier = self::MODIFIER_NORMAL, + string $usingConvertsTo = "" + ) { $this->canAlwaysEat = $canAlwaysEat; $this->nutrition = $nutrition; $this->saturationModifier = $saturationModifier; @@ -29,15 +77,112 @@ public function getName(): string { } public function getValue(): array { - return [ + $data = [ "can_always_eat" => $this->canAlwaysEat, "nutrition" => $this->nutrition, "saturation_modifier" => $this->saturationModifier, "using_converts_to" => $this->usingConvertsTo ]; + if($this->cooldownTime !== null){ + $data['cooldown_time'] = $this->cooldownTime; + $data['cooldown_type'] = $this->cooldownType ?? self::COOLDOWN_DEFAULT; + } + if($this->onUseAction !== null){ + $data["on_use_action"] = $this->onUseAction; + $data["on_use_range"] = $this->onUseRange ?? [8.0, 8.0, 8.0]; + } + if($this->effects !== []){ + $data["effects"] = $this->effects; + } + if($this->removeEffects !== []){ + $data['remove_effects'] = $this->removeEffects; + } + return $data; } public function getPropertyMapping(): ?array { return null; } + + /** + * Sets a cooldown after eating. + * If `COOLDOWN_CHORUSFRUIT` is used, cooldown time is forced to 20 ticks. + * @param int $time Cooldown duration in ticks + * @param string $type Cooldown type + */ + public function addCooldown(int $time = 0, string $type = self::COOLDOWN_DEFAULT): self { + if($time < 0){ + throw new \InvalidArgumentException("Cooldown time must be >= 0"); + } + $this->cooldownType = $type; + $this->cooldownTime = ($type === self::COOLDOWN_CHORUSFRUIT) ? 20 : $time; + return $this; + } + + /** + * Defines an on-use event action. + * @param int $action One of the USE_ACTION_* constants + * @param float $x Effect range X + * @param float $y Effect range Y + * @param float $z Effect range Z + */ + public function onUseEvent( + int $action = self::USE_ACTION_NORMAL, + float $x = 8.0, + float $y = 8.0, + float $z = 8.0 + ): self { + if(!in_array($action, [ + self::USE_ACTION_NORMAL, + self::USE_ACTION_CHORUS_TELEPORT, + self::USE_ACTION_SUSPICIOUS_STEW_EFFECT + ], true)){ + throw new \InvalidArgumentException("Invalid on_use_action: $action"); + } + $this->onUseAction = $action; + $this->onUseRange = [$x, $y, $z]; + return $this; + } + + /** + * Adds a potion effect applied when the food is consumed. + * @param EffectType $effect Potion effect type + * @param int $duration Effect duration in seconds + * @param int $amplifier Effect strength (0 = level I) + * @param float $chance Chance to apply the effect) + */ + public function addEffect( + EffectType $effect, + int $duration, + int $amplifier = 0, + float $chance = 1.0 + ): self { + if($duration <= 0){ + throw new \InvalidArgumentException("Effect duration must be > 0"); + } + if($amplifier < 0){ + throw new \InvalidArgumentException("Effect amplifier must be >= 0"); + } + $this->effects[] = [ + "name" => $effect->getName(), + "id" => $effect->getId(), + "descriptionId" => $effect->getDescriptionId(), + "duration" => $duration, + "amplifier" => $amplifier, + "chance" => $chance + ]; + return $this; + } + + /** + * Removes one or more potion effects when the food is consumed. + * @param EffectType ...$effects Effects to remove + */ + public function removeEffects(EffectType ...$effects): self { + foreach($effects as $effect){ + $this->removeEffects[] = $effect->getId(); + } + $this->removeEffects = array_values(array_unique($this->removeEffects)); + return $this; + } } \ No newline at end of file diff --git a/src/item/component/FuelComponent.php b/src/item/component/FuelComponent.php index 811ca5e1..8b40cd5b 100644 --- a/src/item/component/FuelComponent.php +++ b/src/item/component/FuelComponent.php @@ -9,9 +9,13 @@ final class FuelComponent implements ItemComponent { /** * Allows this item to be used as fuel in a furnace to 'cook' other items. - * @param float $duration Amount of time, in seconds, this fuel will cook items + * @param float $duration Amount of time, in seconds, this fuel will cook items. + * @throws \InvalidArgumentException if the fuel duration is less than 0.05 */ public function __construct(float $duration) { + if($duration < 0.05){ + throw new \InvalidArgumentException("Fuel duration must be at least 0.05 seconds, $duration given"); + } $this->duration = $duration; } diff --git a/src/item/component/KineticWeaponComponent.php b/src/item/component/KineticWeaponComponent.php index 349d11ce..d9ddfb1c 100644 --- a/src/item/component/KineticWeaponComponent.php +++ b/src/item/component/KineticWeaponComponent.php @@ -3,23 +3,42 @@ namespace customiesdevs\customies\item\component; +use pocketmine\nbt\tag\ShortTag; + final class KineticWeaponComponent implements ItemComponent { - private array $creativeReach = []; + private array $creativeReach; private float $damageModifier; private float $damageMultiplier; private int $delay; - - private array $dismountConditions = []; + private array $damageConditions; + private array $dismountConditions; private float $hitboxMargin; - private array $knockbackConditions = []; - private array $reach = []; + private array $knockbackConditions; + private array $reach; + /** + * The kinetic weapon component defines the behavior of kinetic weapons, which deal damage based on their speed and other conditions. + * @param array $creativeReach The reach of the weapon in creative mode + * @param float $damageModifier A flat modifier added to the damage dealt by the weapon + * @param float $damageMultiplier A multiplier applied to the damage dealt by the weapon + * @param int $delay The delay between uses of the weapon, in ticks + * @param array $damageConditions Conditions that must be met for the weapon to deal damage + * @param array $dismountConditions Conditions that must be met for the weapon to dismount entities + * @param float $hitboxMargin The margin added to the hitbox of the weapon + * @param array $knockbackConditions Conditions that must be met for the weapon to apply knockback + * @param array $reach The reach of the weapon in survival mode + */ public function __construct( array $creativeReach = ['min' => 2.0, 'max' => 7.5], float $damageModifier = 0.0, float $damageMultiplier = 0.7, int $delay = 15, + array $damageConditions = [ + 'min_speed' => 0.0, + 'min_relative_speed' => 4.6, + 'max_duration' => 200 + ], array $dismountConditions = [ 'min_speed' => 14.0, 'min_relative_speed' => 0.0, @@ -33,48 +52,75 @@ public function __construct( ], array $reach = ['min' => 2.0, 'max' => 4.5] ) { - $this->creativeReach = $creativeReach; + $this->creativeReach = self::validateRange($creativeReach, 'creative_reach'); + $this->reach = self::validateRange($reach, 'reach'); $this->damageModifier = $damageModifier; $this->damageMultiplier = $damageMultiplier; $this->delay = $delay; - $this->dismountConditions = $dismountConditions; + $this->damageConditions = self::validateConditions($damageConditions, 'damage_conditions'); + $this->dismountConditions = self::validateConditions($dismountConditions, 'dismount_conditions'); + $this->knockbackConditions = self::validateConditions($knockbackConditions, 'knockback_conditions'); $this->hitboxMargin = $hitboxMargin; - $this->knockbackConditions = $knockbackConditions; - $this->reach = $reach; } - public function getName(): string { return 'minecraft:kinetic_weapon'; } public function getValue(): array { return [ - "creative_reach" => [ - "min" => (float) $this->creativeReach['min'], - "max" => (float) $this->creativeReach['max'] - ], - "damage_modifier" => $this->damageModifier, - "damage_multiplier" => $this->damageMultiplier, - "delay" => $this->delay, - "dismount_conditions" => [ - "min_speed" => (float) $this->dismountConditions['min_speed'], - "min_relative_speed" => (float) $this->dismountConditions['min_relative_speed'], - "max_duration" => (int) $this->dismountConditions['max_duration'] - ], - "hitbox_margin" => $this->hitboxMargin, - "knockback_conditions" => [ - "min_speed" => (float) $this->knockbackConditions['min_speed'], - "min_relative_speed" => (float) $this->knockbackConditions['min_relative_speed'], - "max_duration" => (int) $this->knockbackConditions['max_duration'] - ], - "reach" => [ - "min" => (float) $this->reach['min'], - "max" => (float) $this->reach['max'] - ], + "minecraft:kinetic_weapon" => [ + "creative_reach" => self::rangeToArray($this->creativeReach), + "damage_conditions" => self::conditionsToArray($this->damageConditions), + "damage_modifier" => $this->damageModifier, + "damage_multiplier" => $this->damageMultiplier, + "delay" => new ShortTag($this->delay), + "dismount_conditions" => self::conditionsToArray($this->dismountConditions), + "hitbox_margin" => $this->hitboxMargin, + "knockback_conditions" => self::conditionsToArray($this->knockbackConditions), + "reach" => self::rangeToArray($this->reach), + ] ]; } public function getPropertyMapping(): ?array { return null; } + + private static function validateRange(array $range, string $name): array { + if(!isset($range['min'], $range['max'])){ + throw new \InvalidArgumentException("$name must contain min and max"); + } + return [ + 'min' => (float) $range['min'], + 'max' => (float) $range['max'] + ]; + } + + private static function validateConditions(array $conditions, string $name): array { + foreach(['min_speed', 'min_relative_speed', 'max_duration'] as $key){ + if(!array_key_exists($key, $conditions)){ + throw new \InvalidArgumentException("$name missing $key"); + } + } + return [ + 'min_speed' => (float) $conditions['min_speed'], + 'min_relative_speed' => (float) $conditions['min_relative_speed'], + 'max_duration' => (int) $conditions['max_duration'] + ]; + } + + private static function rangeToArray(array $range): array { + return [ + "min" => $range['min'], + "max" => $range['max'] + ]; + } + + private static function conditionsToArray(array $conditions): array { + return [ + "min_speed" => $conditions['min_speed'], + "min_relative_speed" => $conditions['min_relative_speed'], + "max_duration" => new ShortTag($conditions['max_duration']) + ]; + } } \ No newline at end of file diff --git a/src/item/component/MaxStackSizeComponent.php b/src/item/component/MaxStackSizeComponent.php index c11b7b85..335fd485 100644 --- a/src/item/component/MaxStackSizeComponent.php +++ b/src/item/component/MaxStackSizeComponent.php @@ -14,6 +14,9 @@ final class MaxStackSizeComponent implements ItemComponent { * @param int $maxStackSize Max Size, Default is set to `64` */ public function __construct(int $maxStackSize = 64) { + if($maxStackSize < 1 || $maxStackSize > 64) { + throw new \InvalidArgumentException("Max stack size must be between 1 and 64, $maxStackSize given."); + } $this->maxStackSize = $maxStackSize; } diff --git a/src/item/component/PiercingWeaponComponent.php b/src/item/component/PiercingWeaponComponent.php index f65d0806..6e109141 100644 --- a/src/item/component/PiercingWeaponComponent.php +++ b/src/item/component/PiercingWeaponComponent.php @@ -5,18 +5,18 @@ final class PiercingWeaponComponent implements ItemComponent { - private array $creativeReach = []; + private array $creativeReach; private float $hitboxMargin; - private array $reach = []; + private array $reach; public function __construct( array $creativeReach = ['min' => 2.0, 'max' => 7.5], float $hitboxMargin = 0.25, array $reach = ['min' => 2.0, 'max' => 4.5] ) { - $this->creativeReach = $creativeReach; + $this->creativeReach = self::validateRange($creativeReach, 'creative_reach'); + $this->reach = self::validateRange($reach, 'reach'); $this->hitboxMargin = $hitboxMargin; - $this->reach = $reach; } public function getName(): string { @@ -25,19 +25,30 @@ public function getName(): string { public function getValue(): array { return [ - "creative_reach" => [ - "min" => (float) $this->creativeReach['min'], - "max" => (float) $this->creativeReach['max'] - ], + "creative_reach" => self::rangeToArray($this->creativeReach), "hitbox_margin" => $this->hitboxMargin, - "reach" => [ - "min" => (float) $this->reach['min'], - "max" => (float) $this->reach['max'] - ] + "reach" => self::rangeToArray($this->reach) ]; } public function getPropertyMapping(): ?array { return null; } + + private static function validateRange(array $range, string $name): array { + if(!isset($range['min'], $range['max'])){ + throw new \InvalidArgumentException("$name must contain min and max values"); + } + return [ + 'min' => (float) $range['min'], + 'max' => (float) $range['max'] + ]; + } + + private static function rangeToArray(array $range): array { + return [ + "min" => $range['min'], + "max" => $range['max'] + ]; + } } \ No newline at end of file diff --git a/src/item/component/ProjectileComponent.php b/src/item/component/ProjectileComponent.php index 0994c0fa..2e371bb0 100644 --- a/src/item/component/ProjectileComponent.php +++ b/src/item/component/ProjectileComponent.php @@ -5,6 +5,9 @@ final class ProjectileComponent implements ItemComponent { + public const ENTITY_ARROW = "minecraft:arrow<>"; + public const ENTITY_WINDCHARGE = "minecraft:wind_charge_projectile<>"; + private float $minimumCriticalPower; private string $projectileEntity; @@ -15,7 +18,7 @@ final class ProjectileComponent implements ItemComponent { * @param float $minimumCriticalPower Specifies how long a player must charge a projectile for it to critically hit * @param string $projectileEntity Which entity is to be fired as a projectile */ - public function __construct(float $minimumCriticalPower, string $projectileEntity) { + public function __construct(float $minimumCriticalPower = 0, string $projectileEntity) { $this->minimumCriticalPower = $minimumCriticalPower; $this->projectileEntity = $projectileEntity; } diff --git a/src/item/component/ShooterComponent.php b/src/item/component/ShooterComponent.php index 608808a6..3fd0bf42 100644 --- a/src/item/component/ShooterComponent.php +++ b/src/item/component/ShooterComponent.php @@ -25,7 +25,15 @@ final class ShooterComponent implements ItemComponent { * @param float $maxDrawDuration Draw Duration. Default is set to 0 * @param bool $scalePowerByDrawDuration Scale power by draw duration? Default is set to false */ - public function __construct(string $item, bool $useOffhand = false, bool $searchInventory = false, bool $useInCreative = false, bool $chargeOnDraw = false, float $maxDrawDuration = 0.0, bool $scalePowerByDrawDuration = false) { + public function __construct( + string $item, + bool $useOffhand = false, + bool $searchInventory = false, + bool $useInCreative = false, + bool $chargeOnDraw = false, + float $maxDrawDuration = 0.0, + bool $scalePowerByDrawDuration = false + ) { $this->item = $item; $this->useOffhand = $useOffhand; $this->searchInventory = $searchInventory; diff --git a/src/item/component/SwingSoundsComponent.php b/src/item/component/SwingSoundsComponent.php index 128fcba7..4b69e11c 100644 --- a/src/item/component/SwingSoundsComponent.php +++ b/src/item/component/SwingSoundsComponent.php @@ -3,21 +3,15 @@ namespace customiesdevs\customies\item\component; -final class SwingSoundsComponent implements ItemComponent { +use customiesdevs\customies\item\properties\SoundEvent; - private string $critical; - private string $hit; - private string $miss; +final class SwingSoundsComponent implements ItemComponent { public function __construct( - string $critical = "attack.critical", - string $hit = "attack.strong", - string $miss = "attack.nodamage" - ) { - $this->critical = $critical; - $this->hit = $hit; - $this->miss = $miss; - } + private SoundEvent $critical = SoundEvent::ATTACK_CRITICAL, + private SoundEvent $hit = SoundEvent::ATTACK_STRONG, + private SoundEvent $miss = SoundEvent::ATTACK_NODAMAGE, + ) {} public function getName(): string { return "minecraft:swing_sounds"; @@ -25,9 +19,9 @@ public function getName(): string { public function getValue(): array { return [ - "attack_critical_hit" => $this->critical, - "attack_hit" => $this->hit, - "attack_miss" => $this->miss, + "attack_critical_hit" => $this->critical->value, + "attack_hit" => $this->hit->value, + "attack_miss" => $this->miss->value, ]; } diff --git a/src/item/component/UseModifiersComponent.php b/src/item/component/UseModifiersComponent.php index ee3a8a90..8011e412 100644 --- a/src/item/component/UseModifiersComponent.php +++ b/src/item/component/UseModifiersComponent.php @@ -3,6 +3,8 @@ namespace customiesdevs\customies\item\component; +use customiesdevs\customies\item\properties\SoundEvent; + final class UseModifiersComponent implements ItemComponent { private float $useDuration; @@ -15,18 +17,18 @@ final class UseModifiersComponent implements ItemComponent { * @param float $movementModifier Modifier applied to player movement speed * @param float $useDuration How long the item takes to use (seconds) * @param bool $emitVibrations Whether the item emits vibration events - * @param string|null $startSound Sound played when use starts + * @param SoundEvent|string|null $startSound Sound played when use starts */ public function __construct( float $movementModifier = 1.0, float $useDuration = 0.0, bool $emitVibrations = false, - ?string $startSound = null + SoundEvent|string|null $startSound = null ) { $this->movementModifier = $movementModifier; $this->useDuration = $useDuration; $this->emitVibrations = $emitVibrations; - $this->startSound = $startSound; + $this->startSound = $startSound instanceof SoundEvent ? $startSound->value : $startSound; } public function getName(): string { @@ -40,7 +42,7 @@ public function getValue(): array { "emit_vibrations" => $this->emitVibrations ]; if($this->startSound !== null){ - $value["start_sound"] = $this->startSound; + $value['start_sound'] = $this->startSound; } return $value; } diff --git a/src/item/properties/DamageCause.php b/src/item/properties/DamageCause.php index 0bdb3631..7b41e867 100644 --- a/src/item/properties/DamageCause.php +++ b/src/item/properties/DamageCause.php @@ -7,40 +7,40 @@ * Represents all possible causes of damage in the game. */ enum DamageCause: string { - const NONE = "none"; - const ALL = "all"; - const ANVIL = "anvil"; - const BLOCK_EXPLOSION = "block_explosion"; - const CAMPFIRE = "campfire"; - const CHARGING = "charging"; - const CONTACT = "contact"; - const DROWNING = "drowning"; - const ENTITY_ATTACK = "entity_attack"; - const ENTITY_EXPLOSION = "entity_explosion"; - const FALL = "fall"; - const FALLING_BLOCK = "falling_block"; - const FIRE = "fire"; - const FIRE_TICK = "fire_tick"; - const FIREWORKS = "fireworks"; - const FLY_INTO_WALL = "fly_into_wall"; - const FREEZING = "freezing"; - const LAVA = "lava"; - const LIGHTNING = "lightning"; - const MAGIC = "magic"; - const MAGMA = "magma"; - const OVERRIDE = "override"; - const PISTON = "piston"; - const PROJECTILE = "projectile"; - const RAM_ATTACK = "ram_attack"; - const SELF_DESTRUCT = "self_destruct"; - const SONIC_BOOM = "sonic_boom"; - const SOUL_CAMPFIRE = "soul_campfire"; - const STALACTITE = "stalactite"; - const STALAGMITE = "stalagmite"; - const STARVE = "starve"; - const SUFFOCATION = "suffocation"; - const TEMPERATURE = "temperature"; - const THORNS = "thorns"; - const VOID = "void"; - const WITHER = "wither"; + case NONE = "none"; + case ALL = "all"; + case ANVIL = "anvil"; + case BLOCK_EXPLOSION = "block_explosion"; + case CAMPFIRE = "campfire"; + case CHARGING = "charging"; + case CONTACT = "contact"; + case DROWNING = "drowning"; + case ENTITY_ATTACK = "entity_attack"; + case ENTITY_EXPLOSION = "entity_explosion"; + case FALL = "fall"; + case FALLING_BLOCK = "falling_block"; + case FIRE = "fire"; + case FIRE_TICK = "fire_tick"; + case FIREWORKS = "fireworks"; + case FLY_INTO_WALL = "fly_into_wall"; + case FREEZING = "freezing"; + case LAVA = "lava"; + case LIGHTNING = "lightning"; + case MAGIC = "magic"; + case MAGMA = "magma"; + case OVERRIDE = "override"; + case PISTON = "piston"; + case PROJECTILE = "projectile"; + case RAM_ATTACK = "ram_attack"; + case SELF_DESTRUCT = "self_destruct"; + case SONIC_BOOM = "sonic_boom"; + case SOUL_CAMPFIRE = "soul_campfire"; + case STALACTITE = "stalactite"; + case STALAGMITE = "stalagmite"; + case STARVE = "starve"; + case SUFFOCATION = "suffocation"; + case TEMPERATURE = "temperature"; + case THORNS = "thorns"; + case VOID = "void"; + case WITHER = "wither"; } \ No newline at end of file diff --git a/src/item/properties/EffectType.php b/src/item/properties/EffectType.php new file mode 100644 index 00000000..58cfda1e --- /dev/null +++ b/src/item/properties/EffectType.php @@ -0,0 +1,80 @@ +name); + } + + public function getDescriptionId(): string { + return match($this){ + self::REGENERATION => KnownTranslationKeys::POTION_REGENERATION, + self::ABSORPTION => KnownTranslationKeys::POTION_ABSORPTION, + self::RESISTANCE => KnownTranslationKeys::POTION_RESISTANCE, + self::FIRE_RESISTANCE => KnownTranslationKeys::POTION_FIRERESISTANCE, + self::POISON, self::FATAL_POISON => KnownTranslationKeys::POTION_POISON, + self::WITHER => KnownTranslationKeys::POTION_WITHER, + self::SPEED => KnownTranslationKeys::POTION_MOVESPEED, + self::SLOWNESS => KnownTranslationKeys::POTION_MOVESLOWDOWN, + self::HASTE => KnownTranslationKeys::POTION_DIGSPEED, + self::MINING_FATIGUE => KnownTranslationKeys::POTION_DIGSLOWDOWN, + self::STRENGTH => KnownTranslationKeys::POTION_DAMAGEBOOST, + self::INSTANT_HEALTH => KnownTranslationKeys::POTION_HEAL, + self::INSTANT_DAMAGE => KnownTranslationKeys::POTION_HARM, + self::JUMP_BOOST => KnownTranslationKeys::POTION_JUMP, + self::NAUSEA => KnownTranslationKeys::POTION_CONFUSION, + self::BLINDNESS => KnownTranslationKeys::POTION_BLINDNESS, + self::NIGHT_VISION => KnownTranslationKeys::POTION_NIGHTVISION, + self::HUNGER => KnownTranslationKeys::POTION_HUNGER, + self::WEAKNESS => KnownTranslationKeys::POTION_WEAKNESS, + self::HEALTH_BOOST => KnownTranslationKeys::POTION_HEALTHBOOST, + self::SATURATION => KnownTranslationKeys::POTION_SATURATION, + self::LEVITATION => KnownTranslationKeys::POTION_LEVITATION, + self::CONDUIT_POWER => KnownTranslationKeys::POTION_CONDUITPOWER, + self::SLOW_FALLING => KnownTranslationKeys::POTION_SLOWFALLING, + self::BAD_OMEN => "effect.badOmen", + self::VILLAGE_HERO => "effect.villageHero", + self::DARKNESS => KnownTranslationKeys::EFFECT_DARKNESS, + self::WATER_BREATHING => KnownTranslationKeys::POTION_WATERBREATHING, + }; + } + + public function getId(): int { + return $this->value; + } +} \ No newline at end of file diff --git a/src/item/properties/ParticleType.php b/src/item/properties/ParticleType.php index 7a7eb0c2..c38546e5 100644 --- a/src/item/properties/ParticleType.php +++ b/src/item/properties/ParticleType.php @@ -7,95 +7,95 @@ * Represents all available particle types in the game. */ enum ParticleType: string { - const NONE = "none"; - const BUBBLE = "bubble"; - const BUBBLE_MANUAL = "bubblemanual"; - const CRIT = "crit"; - const BLOCK_FORCE_FIELD = "blockforcefield"; - const SMOKE = "smoke"; - const EXPLODE = "explode"; - const EVAPORATION = "evaporation"; - const FLAME = "flame"; - const CANDLE_FLAME = "candleflame"; - const LAVA = "lava"; - const LARGE_SMOKE = "largesmoke"; - const RED_DUST = "reddust"; - const RISING_BORDER_DUST = "risingborderdust"; - const ICON_CRACK = "iconcrack"; - const SNOWBALL_POOF = "snowballpoof"; - const LARGE_EXPLODE = "largeexplode"; - const HUGE_EXPLOSION = "hugeexplosion"; - const BREEZE_WIND_EXPLOSION = "breezewindexplosion"; - const MOB_FLAME = "mobflame"; - const HEART = "heart"; - const TERRAIN = "terrain"; - const TOWN_AURA = "townaura"; - const PORTAL = "portal"; - const WATER_SPLASH = "watersplash"; - const WATER_SPLASH_MANUAL = "watersplashmanual"; - const WATER_WAKE = "waterwake"; - const DRIP_WATER = "dripwater"; - const DRIP_LAVA = "driplava"; - const DRIP_HONEY = "driphoney"; - const STALACTITE_DRIP_WATER = "stalactitedripwater"; - const STALACTITE_DRIP_LAVA = "stalactitedriplava"; - const FALLING_DUST = "fallingdust"; - const MOB_SPELL = "mobspell"; - const MOB_SPELL_AMBIENT = "mobspellambient"; - const MOB_SPELL_INSTANTANEOUS = "mobspellinstantaneous"; - const INK = "ink"; - const SLIME = "slime"; - const RAIN_SPLASH = "rainsplash"; - const VILLAGER_ANGRY = "villagerangry"; - const VILLAGER_HAPPY = "villagerhappy"; - const ENCHANTING_TABLE = "enchantingtable"; - const TRACKING_EMITTER = "trackingemitter"; - const NOTE = "note"; - const WITCH_SPELL = "witchspell"; - const CARROT_BOOST = "carrotboost"; - const MOB_APPEARANCE = "mobappearance"; - const END_ROD = "endrod"; - const DRAGON_BREATH = "dragonbreath"; - const SPIT = "spit"; - const TOTEM = "totem"; - const FOOD = "food"; - const FIREWORKS_STARTER = "fireworksstarter"; - const FIREWORKS = "fireworks"; - const FIREWORKS_OVERLAY = "fireworksoverlay"; - const BALLOON_GAS = "balloongas"; - const COLORED_FLAME = "coloredflame"; - const SPARKLER = "sparkler"; - const CONDUIT = "conduit"; - const BUBBLE_COLUMN_UP = "bubblecolumnup"; - const BUBBLE_COLUMN_DOWN = "bubblecolumndown"; - const SNEEZE = "sneeze"; - const SHULKER_BULLET = "shulkerbullet"; - const BLEACH = "bleach"; - const DRAGON_DESTROY_BLOCK = "dragondestroyblock"; - const MYCELIUM_DUST = "myceliumdust"; - const FALLING_BORDER_DUST = "fallingborderdust"; - const CAMPFIRE_SMOKE = "campfiresmoke"; - const CAMPFIRE_SMOKE_TALL = "campfiresmoketall"; - const DRAGON_BREATH_FIRE = "dragonbreathfire"; - const DRAGON_BREATH_TRAIL = "dragonbreathtrail"; - const SOUL = "soul"; - const OBSIDIAN_TEAR = "obsidiantear"; - const PORTAL_REVERSE = "portalreverse"; - const SNOWFLAKE = "snowflake"; - const WAX = "wax"; - const ELECTRIC_SPARK = "electricspark"; - const SHRIEK = "shriek"; - const SCULK_SOUL = "sculksoul"; - const SONIC_EXPLOSION = "sonicexplosion"; - const DUST_PLUME = "dustplume"; - const WHITE_SMOKE = "whitesmoke"; - const VAULT_CONNECTION = "vaultconnection"; - const WIND_EXPLOSION = "windexplosion"; - const WOLF_ARMOR_CRACK = "wolfarmorcrack"; - const CREAKING_CRUMBLE = "creakingcrumble"; - const PALE_OAK_LEAVES = "paleoakleaves"; - const EYE_BLOSSOM_OPEN = "eyeblossomopen"; - const EYE_BLOSSOM_CLOSE = "eyeblossomclose"; - const BLUE_FLAME = "blueflame"; - const GREEN_FLAME = "greenflame"; + case NONE = "none"; + case BUBBLE = "bubble"; + case BUBBLE_MANUAL = "bubblemanual"; + case CRIT = "crit"; + case BLOCK_FORCE_FIELD = "blockforcefield"; + case SMOKE = "smoke"; + case EXPLODE = "explode"; + case EVAPORATION = "evaporation"; + case FLAME = "flame"; + case CANDLE_FLAME = "candleflame"; + case LAVA = "lava"; + case LARGE_SMOKE = "largesmoke"; + case RED_DUST = "reddust"; + case RISING_BORDER_DUST = "risingborderdust"; + case ICON_CRACK = "iconcrack"; + case SNOWBALL_POOF = "snowballpoof"; + case LARGE_EXPLODE = "largeexplode"; + case HUGE_EXPLOSION = "hugeexplosion"; + case BREEZE_WIND_EXPLOSION = "breezewindexplosion"; + case MOB_FLAME = "mobflame"; + case HEART = "heart"; + case TERRAIN = "terrain"; + case TOWN_AURA = "townaura"; + case PORTAL = "portal"; + case WATER_SPLASH = "watersplash"; + case WATER_SPLASH_MANUAL = "watersplashmanual"; + case WATER_WAKE = "waterwake"; + case DRIP_WATER = "dripwater"; + case DRIP_LAVA = "driplava"; + case DRIP_HONEY = "driphoney"; + case STALACTITE_DRIP_WATER = "stalactitedripwater"; + case STALACTITE_DRIP_LAVA = "stalactitedriplava"; + case FALLING_DUST = "fallingdust"; + case MOB_SPELL = "mobspell"; + case MOB_SPELL_AMBIENT = "mobspellambient"; + case MOB_SPELL_INSTANTANEOUS = "mobspellinstantaneous"; + case INK = "ink"; + case SLIME = "slime"; + case RAIN_SPLASH = "rainsplash"; + case VILLAGER_ANGRY = "villagerangry"; + case VILLAGER_HAPPY = "villagerhappy"; + case ENCHANTING_TABLE = "enchantingtable"; + case TRACKING_EMITTER = "trackingemitter"; + case NOTE = "note"; + case WITCH_SPELL = "witchspell"; + case CARROT_BOOST = "carrotboost"; + case MOB_APPEARANCE = "mobappearance"; + case END_ROD = "endrod"; + case DRAGON_BREATH = "dragonbreath"; + case SPIT = "spit"; + case TOTEM = "totem"; + case FOOD = "food"; + case FIREWORKS_STARTER = "fireworksstarter"; + case FIREWORKS = "fireworks"; + case FIREWORKS_OVERLAY = "fireworksoverlay"; + case BALLOON_GAS = "balloongas"; + case COLORED_FLAME = "coloredflame"; + case SPARKLER = "sparkler"; + case CONDUIT = "conduit"; + case BUBBLE_COLUMN_UP = "bubblecolumnup"; + case BUBBLE_COLUMN_DOWN = "bubblecolumndown"; + case SNEEZE = "sneeze"; + case SHULKER_BULLET = "shulkerbullet"; + case BLEACH = "bleach"; + case DRAGON_DESTROY_BLOCK = "dragondestroyblock"; + case MYCELIUM_DUST = "myceliumdust"; + case FALLING_BORDER_DUST = "fallingborderdust"; + case CAMPFIRE_SMOKE = "campfiresmoke"; + case CAMPFIRE_SMOKE_TALL = "campfiresmoketall"; + case DRAGON_BREATH_FIRE = "dragonbreathfire"; + case DRAGON_BREATH_TRAIL = "dragonbreathtrail"; + case SOUL = "soul"; + case OBSIDIAN_TEAR = "obsidiantear"; + case PORTAL_REVERSE = "portalreverse"; + case SNOWFLAKE = "snowflake"; + case WAX = "wax"; + case ELECTRIC_SPARK = "electricspark"; + case SHRIEK = "shriek"; + case SCULK_SOUL = "sculksoul"; + case SONIC_EXPLOSION = "sonicexplosion"; + case DUST_PLUME = "dustplume"; + case WHITE_SMOKE = "whitesmoke"; + case VAULT_CONNECTION = "vaultconnection"; + case WIND_EXPLOSION = "windexplosion"; + case WOLF_ARMOR_CRACK = "wolfarmorcrack"; + case CREAKING_CRUMBLE = "creakingcrumble"; + case PALE_OAK_LEAVES = "paleoakleaves"; + case EYE_BLOSSOM_OPEN = "eyeblossomopen"; + case EYE_BLOSSOM_CLOSE = "eyeblossomclose"; + case BLUE_FLAME = "blueflame"; + case GREEN_FLAME = "greenflame"; } \ No newline at end of file diff --git a/src/item/properties/RepairAmount.php b/src/item/properties/RepairAmount.php new file mode 100644 index 00000000..1d8e1159 --- /dev/null +++ b/src/item/properties/RepairAmount.php @@ -0,0 +1,51 @@ +numeric !== null){ + return $this->numeric; + } + return [ + "expression" => $this->expression, + "version" => new ShortTag(13) + ]; + } +} diff --git a/src/item/properties/RepairItems.php b/src/item/properties/RepairItems.php index 6a782a6d..dad2301c 100644 --- a/src/item/properties/RepairItems.php +++ b/src/item/properties/RepairItems.php @@ -1,85 +1,45 @@ [ - * ["name" => "item1"], - * ["name" => "item2"], - * ... - * ], - * "repair_amount" => int - * ] - * * @return array{ - * items: array, - * repair_amount: int + * items: array, + * repair_amount: int|float|array * } */ public function toArray(): array { - $items = []; - foreach($this->items as $item) { - $items[] = [ - "name" => $item - ]; - } return [ - "items" => $items, - "repair_amount" => $this->repairAmount + "items" => array_map( + static fn(string $item) => ["name" => $item], + $this->items + ), + "repair_amount" => $this->repairAmount->toArray() ]; } - /** - * Creates a RepairItems instance from an array representation. - * - * Expects an array in the format returned by `toArray()`. - * Missing or invalid values are replaced with defaults (empty array or 0). - * - * @param array{ - * items?: array, - * repair_amount?: int - * } $data - * @return self - */ - public static function fromArray(array $data): self { - $items = []; - if(is_array($data["items"] ?? null)) { - foreach($data["items"] as $item) { - $items[] = $item["name"] ?? ""; - } - } - return new self($items, $data["repair_amount"] ?? 0); - } - - /** - * Returns the list of item names that can be used for repair. - * - * @return string[] - */ + /** @return string[] */ public function getItems(): array { return $this->items; } - /** - * Returns the repair amount provided by these items. - * @return int - */ - public function getRepairAmount(): int { + public function getRepairAmount(): RepairAmount { return $this->repairAmount; } } \ No newline at end of file diff --git a/src/item/properties/SoundEvent.php b/src/item/properties/SoundEvent.php index a42f41e2..5c2ba6df 100644 --- a/src/item/properties/SoundEvent.php +++ b/src/item/properties/SoundEvent.php @@ -7,221 +7,222 @@ * Represents all possible sound events in the game. */ enum SoundEvent: string { - const ITEM_USE_ON = "item.use.on"; - const HIT = "hit"; - const STEP = "step"; - const STEP_BABY = "step.baby"; - const FLY = "fly"; - const JUMP = "jump"; - const JUMP_PREVENT = "jump.prevent"; - const BREAK = "break"; - const PLACE = "place"; - const HEAVY_STEP = "heavy.step"; - const GALLOP = "gallop"; - const FALL = "fall"; - const HURT = "hurt"; - const HURT_BABY = "hurt.baby"; - const HURT_IN_WATER = "hurt.in.water"; - const DEATH = "death"; - const DEATH_BABY = "death.baby"; - const DEATH_IN_WATER = "death.in.water"; - const DEATH_BY_ZOMBIE = "death.to.zombie"; - const AMBIENT = "ambient"; - const AMBIENT_BABY = "ambient.baby"; - const AMBIENT_IN_WATER = "ambient.in.water"; - const AMBIENT_IN_AIR = "ambient.in.air"; - const AMBIENT_TAME = "ambient.tame"; - const AMBIENT_POLLINATE = "ambient.pollinate"; - const BREATHE = "breathe"; - const MAD = "mad"; - const BOOST = "boost"; - const BOW = "bow"; - const SQUISH_BIG = "squish.big"; - const SQUISH_SMALL = "squish.small"; - const FALL_BIG = "fall.big"; - const FALL_SMALL = "fall.small"; - const SPLASH = "splash"; - const FIZZ = "fizz"; - const FLAP = "flap"; - const SWIM = "swim"; - const DRINK = "drink"; - const DRINK_HONEY = "drink.honey"; - const DRINK_MILK = "drink.milk"; - const EAT = "eat"; - const TAKEOFF = "takeoff"; - const SHAKE = "shake"; - const PLOP = "plop"; - const LAND = "land"; - const SADDLE = "saddle"; - const ARMOR = "armor"; - const ARMOR_STAND_PLACE = "mob.armor_stand.place"; - const PLACE_CHEST = "add.chest"; - const THROW = "throw"; - const ATTACK = "attack"; - const ATTACK_NODAMAGE = "attack.nodamage"; - const ATTACK_STRONG = "attack.strong"; - const WARN = "warn"; - const SHEAR = "shear"; - const MILK = "milk"; - const THUNDER = "thunder"; - const EXPLODE = "explode"; - const FIRE = "fire"; - const IGNITE = "ignite"; - const FUSE = "fuse"; - const STARE = "stare"; - const SPAWN = "spawn"; - const BORN = "born"; - const SHOOT = "shoot"; - const BREAK_BLOCK = "break.block"; - const LAUNCH = "launch"; - const BLAST = "blast"; - const BLAST_LARGE = "large.blast"; - const TWINKLE = "twinkle"; - const REMEDY = "remedy"; - const UNFECT = "unfect"; - const CONVERT_DROWNED = "convert_to_drowned"; - const LEVELUP = "levelup"; - const BOW_HIT = "bow.hit"; - const BULLET_HIT = "bullet.hit"; - const EXTINGUISH_FISH = "extinguish.fire"; - const ITEM_FIZZ = "item.fizz"; - const CHEST_OPEN = "chest.open"; - const CHEST_CLOSED = "chest.closed"; - const SHULKERBOX_OPEN = "shulkerbox.open"; - const SHULKERBOX_CLOSED = "shulkerbox.closed"; - const ENDERCHEST_OPEN = "enderchest.open"; - const ENDERCHEST_CLOSED = "enderchest.closed"; - const POWER_ON = "power.on"; - const POWER_OFF = "power.off"; - const ATTACH = "attach"; - const DETACH = "detach"; - const DENY = "deny"; - const TRIPOD = "tripod"; - const POP = "pop"; - const DROP_SLOT = "drop.slot"; - const NOTE = "note"; - const THORNS = "thorns"; - const PISTON_IN = "piston.in"; - const PISTON_OUT = "piston.out"; - const PORTAL = "portal"; - const WATER = "water"; - const LAVA_POP = "lava.pop"; - const LAVA = "lava"; - const BEACON_ACTIVATE = "beacon.activate"; - const BEACON_AMBIENT = "beacon.ambient"; - const BEACON_DEACTIVATE = "beacon.deactivate"; - const BEACON_POWER = "beacon.power"; - const CONDUIT_ACTIVATE = "conduit.activate"; - const CONDUIT_AMBIENT = "conduit.ambient"; - const CONDUIT_ATTACK = "conduit.attack"; - const CONDUIT_DEACTIVATE = "conduit.deactivate"; - const CONDUIT_SHORT = "conduit.short"; - const BUBBLE_POP = "bubble.pop"; - const BUBBLE_UP = "bubble.up"; - const BUBBLE_UPINSIDE = "bubble.upinside"; - const BUBBLE_DOWN = "bubble.down"; - const BUBBLE_DOWNINSIDE = "bubble.downinside"; - const BURP = "burp"; - const BUCKET_FILL_WATER = "bucket.fill.water"; - const BUCKET_EMPTY_WATER = "bucket.empty.water"; - const BUCKET_FILL_LAVA = "bucket.fill.lava"; - const BUCKET_EMPTY_LAVA = "bucket.empty.lava"; - const BUCKET_FILL_FISH = "bucket.fill.fish"; - const BUCKET_EMPTY_FISH = "bucket.empty.fish"; - const ARMOR_EQUIP_CHAIN = "armor.equip_chain"; - const ARMOR_EQUIP_DIAMOND = "armor.equip_diamond"; - const ARMOR_EQUIP_ELYTRA = "armor.equip_elytra"; - const ARMOR_EQUIP_GENERIC = "armor.equip_generic"; - const ARMOR_EQUIP_GOLD = "armor.equip_gold"; - const ARMOR_EQUIP_IRON = "armor.equip_iron"; - const ARMOR_EQUIP_LEATHER = "armor.equip_leather"; - const ARMOR_EQUIP_NETHERITE = "armor.equip_netherite"; - const RECORD_13 = "record.13"; - const RECORD_CAT = "record.cat"; - const RECORD_BLOCKS = "record.blocks"; - const RECORD_CHIRP = "record.chirp"; - const RECORD_CREATOR = "record.creator"; - const RECORD_CREATOR_MUSICBOX = "record.creator_music_box"; - const RECORD_FAR = "record.far"; - const RECORD_MALL = "record.mall"; - const RECORD_MELLOHI = "record.mellohi"; - const RECORD_STAL = "record.stal"; - const RECORD_STRAD = "record.strad"; - const RECORD_WARD = "record.ward"; - const RECORD_11 = "record.11"; - const RECORD_WAIT = "record.wait"; - const RECORD_PIGSTEP = "record.pigstep"; - const RECORD_PRECIPICE = "record.precipice"; - const RECORD_RELIC = "record.relic"; - const RECORD_OTHERSIDE = "record.otherside"; - const RECORD_5 = "record.5"; - const RECORD_TEARS = "record.tears"; - const RECORD_LAVA_CHICKEN = "record.lava_chicken"; - const FLOP = "flop"; - const ELDERGUARDIAN_CURSE = "elderguardian.curse"; - const TELEPORT = "teleport"; - const SHULKER_OPEN = "shulker.open"; - const SHULKER_CLOSE = "shulker.close"; - const MOB_WARNING = "mob.warning"; - const MOB_WARNING_BABY = "mob.warning.baby"; - const HAGGLE = "haggle"; - const HAGGLE_YES = "haggle.yes"; - const HAGGLE_NO = "haggle.no"; - const HAGGLE_IDLE = "haggle.idle"; - const DISAPPEARED = "disappeared"; - const REAPPEARED = "reappeared"; - const CHORUS_GROW = "chorusgrow"; - const CHORUS_DEATH = "chorusdeath"; - const GLASS = "glass"; - const POTION_BREWED = "potion.brewed"; - const CAST_SPELL = "cast.spell"; - const PREPARE_ATTACK = "prepare.attack"; - const PREPARE_SUMMON = "prepare.summon"; - const PREPARE_WOLOLO = "prepare.wololo"; - const FANG = "fang"; - const CHARGE = "charge"; - const CAMERA_TAKEPIC = "camera.take_picture"; - const LEASH_BREAK = "leashknot.break"; - const LEASH_PLACE = "leashknot.place"; - const GROWL = "growl"; - const WHINE = "whine"; - const PANT = "pant"; - const PURR = "purr"; - const PURREOW = "purreow"; - const DEATH_MIN = "death.min.volume"; - const DEATH_MID = "death.mid.volume"; - const IMITATE_BLAZE = "imitate.blaze"; - const IMITATE_CAVE_SPIDER = "imitate.cave_spider"; - const IMITATE_CREEPER = "imitate.creeper"; - const IMITATE_ELDERGUARDIAN = "imitate.elder_guardian"; - const IMITATE_ENDERDRAGON = "imitate.ender_dragon"; - const IMITATE_ENDERMAN = "imitate.enderman"; - const IMITATE_ENDERMITE = "imitate.endermite"; - const END_PORTAL_FRAME_FILL = "block.end_portal_frame.fill"; - const END_PORTAL_SPAWN = "block.end_portal.spawn"; - const ANVIL_USE = "random.anvil_use"; - const BOTTLE_DRAGONBREATH = "bottle.dragonbreath"; - const BALLONPOP = "balloonpop"; - const SPARKLER_ACTIVE = "sparkler.active"; - const CREAKING_HEART_SPAWN = "creaking_heart_spawn"; - const ACTIVATE = "activate"; - const DEACTIVATE = "deactivate"; - const FREEZE = "freeze"; - const UNFREEZE = "unfreeze"; - const OPEN = "open"; - const OPEN_LONG = "open_long"; - const CLOSE = "close"; - const CLOSE_LONG = "close_long"; - const PLACE_IN_WATER = "place_in_water"; - const STATE_CHANGE = "state_change"; - const UNEQUIP_GENERIC = "armor.unequip_generic"; - const LEAD_LEASH = "lead.leash"; - const LEAD_UNLEASH = "lead.unleash"; - const LEAD_BREAK = "lead.break"; - const UNSADDLE = "unsaddle"; - const EQUIP_COPPER = "armor.equip_copper"; - const PLACE_ITEM = "place_item"; - const SINGLE_SWAP = "single_swap"; - const MULTI_SWAP = "multi_swap"; - const UNDEFINED = "undefined"; + case ITEM_USE_ON = "item.use.on"; + case HIT = "hit"; + case STEP = "step"; + case STEP_BABY = "step.baby"; + case FLY = "fly"; + case JUMP = "jump"; + case JUMP_PREVENT = "jump.prevent"; + case BREAK = "break"; + case PLACE = "place"; + case HEAVY_STEP = "heavy.step"; + case GALLOP = "gallop"; + case FALL = "fall"; + case HURT = "hurt"; + case HURT_BABY = "hurt.baby"; + case HURT_IN_WATER = "hurt.in.water"; + case DEATH = "death"; + case DEATH_BABY = "death.baby"; + case DEATH_IN_WATER = "death.in.water"; + case DEATH_BY_ZOMBIE = "death.to.zombie"; + case AMBIENT = "ambient"; + case AMBIENT_BABY = "ambient.baby"; + case AMBIENT_IN_WATER = "ambient.in.water"; + case AMBIENT_IN_AIR = "ambient.in.air"; + case AMBIENT_TAME = "ambient.tame"; + case AMBIENT_POLLINATE = "ambient.pollinate"; + case BREATHE = "breathe"; + case MAD = "mad"; + case BOOST = "boost"; + case BOW = "bow"; + case SQUISH_BIG = "squish.big"; + case SQUISH_SMALL = "squish.small"; + case FALL_BIG = "fall.big"; + case FALL_SMALL = "fall.small"; + case SPLASH = "splash"; + case FIZZ = "fizz"; + case FLAP = "flap"; + case SWIM = "swim"; + case DRINK = "drink"; + case DRINK_HONEY = "drink.honey"; + case DRINK_MILK = "drink.milk"; + case EAT = "eat"; + case TAKEOFF = "takeoff"; + case SHAKE = "shake"; + case PLOP = "plop"; + case LAND = "land"; + case SADDLE = "saddle"; + case ARMOR = "armor"; + case ARMOR_STAND_PLACE = "mob.armor_stand.place"; + case PLACE_CHEST = "add.chest"; + case THROW = "throw"; + case ATTACK = "attack"; + case ATTACK_CRITICAL = "attack.critical"; + case ATTACK_NODAMAGE = "attack.nodamage"; + case ATTACK_STRONG = "attack.strong"; + case WARN = "warn"; + case SHEAR = "shear"; + case MILK = "milk"; + case THUNDER = "thunder"; + case EXPLODE = "explode"; + case FIRE = "fire"; + case IGNITE = "ignite"; + case FUSE = "fuse"; + case STARE = "stare"; + case SPAWN = "spawn"; + case BORN = "born"; + case SHOOT = "shoot"; + case BREAK_BLOCK = "break.block"; + case LAUNCH = "launch"; + case BLAST = "blast"; + case BLAST_LARGE = "large.blast"; + case TWINKLE = "twinkle"; + case REMEDY = "remedy"; + case UNFECT = "unfect"; + case CONVERT_DROWNED = "convert_to_drowned"; + case LEVELUP = "levelup"; + case BOW_HIT = "bow.hit"; + case BULLET_HIT = "bullet.hit"; + case EXTINGUISH_FISH = "extinguish.fire"; + case ITEM_FIZZ = "item.fizz"; + case CHEST_OPEN = "chest.open"; + case CHEST_CLOSED = "chest.closed"; + case SHULKERBOX_OPEN = "shulkerbox.open"; + case SHULKERBOX_CLOSED = "shulkerbox.closed"; + case ENDERCHEST_OPEN = "enderchest.open"; + case ENDERCHEST_CLOSED = "enderchest.closed"; + case POWER_ON = "power.on"; + case POWER_OFF = "power.off"; + case ATTACH = "attach"; + case DETACH = "detach"; + case DENY = "deny"; + case TRIPOD = "tripod"; + case POP = "pop"; + case DROP_SLOT = "drop.slot"; + case NOTE = "note"; + case THORNS = "thorns"; + case PISTON_IN = "piston.in"; + case PISTON_OUT = "piston.out"; + case PORTAL = "portal"; + case WATER = "water"; + case LAVA_POP = "lava.pop"; + case LAVA = "lava"; + case BEACON_ACTIVATE = "beacon.activate"; + case BEACON_AMBIENT = "beacon.ambient"; + case BEACON_DEACTIVATE = "beacon.deactivate"; + case BEACON_POWER = "beacon.power"; + case CONDUIT_ACTIVATE = "conduit.activate"; + case CONDUIT_AMBIENT = "conduit.ambient"; + case CONDUIT_ATTACK = "conduit.attack"; + case CONDUIT_DEACTIVATE = "conduit.deactivate"; + case CONDUIT_SHORT = "conduit.short"; + case BUBBLE_POP = "bubble.pop"; + case BUBBLE_UP = "bubble.up"; + case BUBBLE_UPINSIDE = "bubble.upinside"; + case BUBBLE_DOWN = "bubble.down"; + case BUBBLE_DOWNINSIDE = "bubble.downinside"; + case BURP = "burp"; + case BUCKET_FILL_WATER = "bucket.fill.water"; + case BUCKET_EMPTY_WATER = "bucket.empty.water"; + case BUCKET_FILL_LAVA = "bucket.fill.lava"; + case BUCKET_EMPTY_LAVA = "bucket.empty.lava"; + case BUCKET_FILL_FISH = "bucket.fill.fish"; + case BUCKET_EMPTY_FISH = "bucket.empty.fish"; + case ARMOR_EQUIP_CHAIN = "armor.equip_chain"; + case ARMOR_EQUIP_DIAMOND = "armor.equip_diamond"; + case ARMOR_EQUIP_ELYTRA = "armor.equip_elytra"; + case ARMOR_EQUIP_GENERIC = "armor.equip_generic"; + case ARMOR_EQUIP_GOLD = "armor.equip_gold"; + case ARMOR_EQUIP_IRON = "armor.equip_iron"; + case ARMOR_EQUIP_LEATHER = "armor.equip_leather"; + case ARMOR_EQUIP_NETHERITE = "armor.equip_netherite"; + case RECORD_13 = "record.13"; + case RECORD_CAT = "record.cat"; + case RECORD_BLOCKS = "record.blocks"; + case RECORD_CHIRP = "record.chirp"; + case RECORD_CREATOR = "record.creator"; + case RECORD_CREATOR_MUSICBOX = "record.creator_music_box"; + case RECORD_FAR = "record.far"; + case RECORD_MALL = "record.mall"; + case RECORD_MELLOHI = "record.mellohi"; + case RECORD_STAL = "record.stal"; + case RECORD_STRAD = "record.strad"; + case RECORD_WARD = "record.ward"; + case RECORD_11 = "record.11"; + case RECORD_WAIT = "record.wait"; + case RECORD_PIGSTEP = "record.pigstep"; + case RECORD_PRECIPICE = "record.precipice"; + case RECORD_RELIC = "record.relic"; + case RECORD_OTHERSIDE = "record.otherside"; + case RECORD_5 = "record.5"; + case RECORD_TEARS = "record.tears"; + case RECORD_LAVA_CHICKEN = "record.lava_chicken"; + case FLOP = "flop"; + case ELDERGUARDIAN_CURSE = "elderguardian.curse"; + case TELEPORT = "teleport"; + case SHULKER_OPEN = "shulker.open"; + case SHULKER_CLOSE = "shulker.close"; + case MOB_WARNING = "mob.warning"; + case MOB_WARNING_BABY = "mob.warning.baby"; + case HAGGLE = "haggle"; + case HAGGLE_YES = "haggle.yes"; + case HAGGLE_NO = "haggle.no"; + case HAGGLE_IDLE = "haggle.idle"; + case DISAPPEARED = "disappeared"; + case REAPPEARED = "reappeared"; + case CHORUS_GROW = "chorusgrow"; + case CHORUS_DEATH = "chorusdeath"; + case GLASS = "glass"; + case POTION_BREWED = "potion.brewed"; + case CAST_SPELL = "cast.spell"; + case PREPARE_ATTACK = "prepare.attack"; + case PREPARE_SUMMON = "prepare.summon"; + case PREPARE_WOLOLO = "prepare.wololo"; + case FANG = "fang"; + case CHARGE = "charge"; + case CAMERA_TAKEPIC = "camera.take_picture"; + case LEASH_BREAK = "leashknot.break"; + case LEASH_PLACE = "leashknot.place"; + case GROWL = "growl"; + case WHINE = "whine"; + case PANT = "pant"; + case PURR = "purr"; + case PURREOW = "purreow"; + case DEATH_MIN = "death.min.volume"; + case DEATH_MID = "death.mid.volume"; + case IMITATE_BLAZE = "imitate.blaze"; + case IMITATE_CAVE_SPIDER = "imitate.cave_spider"; + case IMITATE_CREEPER = "imitate.creeper"; + case IMITATE_ELDERGUARDIAN = "imitate.elder_guardian"; + case IMITATE_ENDERDRAGON = "imitate.ender_dragon"; + case IMITATE_ENDERMAN = "imitate.enderman"; + case IMITATE_ENDERMITE = "imitate.endermite"; + case END_PORTAL_FRAME_FILL = "block.end_portal_frame.fill"; + case END_PORTAL_SPAWN = "block.end_portal.spawn"; + case ANVIL_USE = "random.anvil_use"; + case BOTTLE_DRAGONBREATH = "bottle.dragonbreath"; + case BALLONPOP = "balloonpop"; + case SPARKLER_ACTIVE = "sparkler.active"; + case CREAKING_HEART_SPAWN = "creaking_heart_spawn"; + case ACTIVATE = "activate"; + case DEACTIVATE = "deactivate"; + case FREEZE = "freeze"; + case UNFREEZE = "unfreeze"; + case OPEN = "open"; + case OPEN_LONG = "open_long"; + case CLOSE = "close"; + case CLOSE_LONG = "close_long"; + case PLACE_IN_WATER = "place_in_water"; + case STATE_CHANGE = "state_change"; + case UNEQUIP_GENERIC = "armor.unequip_generic"; + case LEAD_LEASH = "lead.leash"; + case LEAD_UNLEASH = "lead.unleash"; + case LEAD_BREAK = "lead.break"; + case UNSADDLE = "unsaddle"; + case EQUIP_COPPER = "armor.equip_copper"; + case PLACE_ITEM = "place_item"; + case SINGLE_SWAP = "single_swap"; + case MULTI_SWAP = "multi_swap"; + case UNDEFINED = "undefined"; } \ No newline at end of file From 226ae46ac64634ebaa84da920256af3c62cb5b5b Mon Sep 17 00:00:00 2001 From: HydroGames-dev Date: Fri, 9 Jan 2026 13:11:07 +0530 Subject: [PATCH 50/51] Remove unused CreativeGroup imports Eliminated unnecessary imports of CreativeGroup from CustomiesBlockFactory, CustomiesItemFactory. --- src/block/CustomiesBlockFactory.php | 3 +-- src/item/CustomiesItemFactory.php | 1 - src/task/AsyncRegisterBlocksTask.php | 1 - 3 files changed, 1 insertion(+), 4 deletions(-) diff --git a/src/block/CustomiesBlockFactory.php b/src/block/CustomiesBlockFactory.php index 86417819..7a2ff9b5 100644 --- a/src/block/CustomiesBlockFactory.php +++ b/src/block/CustomiesBlockFactory.php @@ -18,7 +18,6 @@ use pocketmine\data\bedrock\block\BlockStateData; use pocketmine\data\bedrock\block\convert\BlockStateReader; use pocketmine\data\bedrock\block\convert\BlockStateWriter; -use pocketmine\inventory\CreativeGroup; use pocketmine\nbt\tag\CompoundTag; use pocketmine\nbt\tag\ListTag; use pocketmine\network\mcpe\protocol\types\BlockPaletteEntry; @@ -167,7 +166,7 @@ public function registerBlock( ->setString(BlockStateData::TAG_NAME, $identifier) ->setTag(BlockStateData::TAG_STATES, CompoundTag::create()) ); - $serializer ??= BlockStateWriter::create($identifier); + $serializer ??= static fn() => new BlockStateWriter($identifier); $deserializer ??= static fn(BlockStateReader $in) => $block; } GlobalBlockStateHandlers::getSerializer()->map($block, $serializer); diff --git a/src/item/CustomiesItemFactory.php b/src/item/CustomiesItemFactory.php index 97ae82d6..cd381f0e 100644 --- a/src/item/CustomiesItemFactory.php +++ b/src/item/CustomiesItemFactory.php @@ -9,7 +9,6 @@ use pocketmine\block\Block; use pocketmine\data\bedrock\item\BlockItemIdMap; use pocketmine\data\bedrock\item\SavedItemData; -use pocketmine\inventory\CreativeGroup; use pocketmine\item\Item; use pocketmine\item\StringToItemParser; use pocketmine\nbt\tag\CompoundTag; diff --git a/src/task/AsyncRegisterBlocksTask.php b/src/task/AsyncRegisterBlocksTask.php index 6a85a9b8..aea5e95e 100644 --- a/src/task/AsyncRegisterBlocksTask.php +++ b/src/task/AsyncRegisterBlocksTask.php @@ -18,7 +18,6 @@ final class AsyncRegisterBlocksTask extends AsyncTask { private ThreadSafeArray $deserializer; /** - * Constructor. * @param Closure[] $blockFuncs Array of callbacks used for block registration * @phpstan-param array Date: Wed, 14 Jan 2026 15:57:25 +0300 Subject: [PATCH 51/51] Remove deprecated block state classes and add new rotation traits for improved block handling --- .../traits/BlockFaceRotationTrait.php} | 25 +++++++------ .../CardinalDirectionRotationTrait.php} | 35 ++++++++++++------- .../traits/FacingDirectionRotationTrait.php} | 25 +++++++------ .../traits/LogRotationTrait.php} | 28 +++++++-------- 4 files changed, 59 insertions(+), 54 deletions(-) rename src/block/{states/templates/BlockFaceState.php => permutations/traits/BlockFaceRotationTrait.php} (87%) rename src/block/{states/templates/HorizontalFacingState.php => permutations/traits/CardinalDirectionRotationTrait.php} (69%) rename src/block/{states/templates/AnyFacingState.php => permutations/traits/FacingDirectionRotationTrait.php} (87%) rename src/block/{states/templates/PillarRotationState.php => permutations/traits/LogRotationTrait.php} (90%) diff --git a/src/block/states/templates/BlockFaceState.php b/src/block/permutations/traits/BlockFaceRotationTrait.php similarity index 87% rename from src/block/states/templates/BlockFaceState.php rename to src/block/permutations/traits/BlockFaceRotationTrait.php index f35654de..d354e5cb 100644 --- a/src/block/states/templates/BlockFaceState.php +++ b/src/block/permutations/traits/BlockFaceRotationTrait.php @@ -1,15 +1,13 @@ addState(new BlockState("minecraft:block_face", - ["down", "up","north", "south", "east", "west"] + ["down", "up", "north", "south", "east", "west"] )); } @@ -66,10 +65,15 @@ public function getCurrentStates(): array { return [$this->facing]; } + public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, ?Player $player = null): bool { + $this->facing = $face; + return parent::place($tx, $item, $blockReplace, $blockClicked, $face, $clickVector, $player); + } + public function serializeState(BlockStateWriter $out): void { $out->writeString( "minecraft:block_face", - match($this->facing){ + match ($this->facing) { Facing::DOWN => "down", Facing::UP => "up", Facing::NORTH => "north", @@ -81,7 +85,7 @@ public function serializeState(BlockStateWriter $out): void { } public function deserializeState(BlockStateReader $in): void { - $this->facing = match($in->readString("minecraft:block_face")){ + $this->facing = match ($in->readString("minecraft:block_face")) { "down" => Facing::UP, "up" => Facing::DOWN, "north" => Facing::NORTH, @@ -90,9 +94,4 @@ public function deserializeState(BlockStateReader $in): void { "east" => Facing::EAST, }; } - - public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, ?Player $player = null): bool { - $this->facing = $face; - return parent::place($tx, $item, $blockReplace, $blockClicked, $face, $clickVector, $player); - } -} \ No newline at end of file +} diff --git a/src/block/states/templates/HorizontalFacingState.php b/src/block/permutations/traits/CardinalDirectionRotationTrait.php similarity index 69% rename from src/block/states/templates/HorizontalFacingState.php rename to src/block/permutations/traits/CardinalDirectionRotationTrait.php index 5aa0c2af..3f06e541 100644 --- a/src/block/states/templates/HorizontalFacingState.php +++ b/src/block/permutations/traits/CardinalDirectionRotationTrait.php @@ -1,29 +1,29 @@ facing]; } + public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, ?Player $player = null): bool { + $this->facing = match($face) { + Facing::NORTH => Facing::SOUTH, + Facing::SOUTH => Facing::NORTH, + Facing::WEST => Facing::EAST, + Facing::EAST => Facing::WEST, + default => Facing::opposite($player?->getHorizontalFacing() ?? Facing::NORTH) + }; + return parent::place($tx, $item, $blockReplace, $blockClicked, $face, $clickVector, $player); + } + public function serializeState(BlockStateWriter $out): void { $out->writeString( "minecraft:cardinal_direction", - match($this->facing){ - Facing::DOWN => "down", - Facing::UP => "up", + match($this->facing) { Facing::NORTH => "north", Facing::SOUTH => "south", Facing::WEST => "west", @@ -72,11 +81,11 @@ public function serializeState(BlockStateWriter $out): void { } public function deserializeState(BlockStateReader $in): void { - $this->facing = match($in->readString("minecraft:cardinal_direction")){ + $this->facing = match($in->readString("minecraft:cardinal_direction")) { "north" => Facing::NORTH, "south" => Facing::SOUTH, "west" => Facing::WEST, "east" => Facing::EAST, }; } -} \ No newline at end of file +} diff --git a/src/block/states/templates/AnyFacingState.php b/src/block/permutations/traits/FacingDirectionRotationTrait.php similarity index 87% rename from src/block/states/templates/AnyFacingState.php rename to src/block/permutations/traits/FacingDirectionRotationTrait.php index 44d5ab45..c605ee50 100644 --- a/src/block/states/templates/AnyFacingState.php +++ b/src/block/permutations/traits/FacingDirectionRotationTrait.php @@ -1,15 +1,13 @@ addState(new BlockState("minecraft:facing_direction", - ["down", "up","north", "south", "east", "west"] + ["down", "up", "north", "south", "east", "west"] )); } @@ -66,10 +65,15 @@ public function getCurrentStates(): array { return [$this->facing]; } + public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, ?Player $player = null): bool { + $this->facing = Facing::opposite($face); + return parent::place($tx, $item, $blockReplace, $blockClicked, $face, $clickVector, $player); + } + public function serializeState(BlockStateWriter $out): void { $out->writeString( "minecraft:facing_direction", - match($this->facing){ + match ($this->facing) { Facing::DOWN => "down", Facing::UP => "up", Facing::NORTH => "north", @@ -81,7 +85,7 @@ public function serializeState(BlockStateWriter $out): void { } public function deserializeState(BlockStateReader $in): void { - $this->facing = match($in->readString("minecraft:facing_direction")){ + $this->facing = match ($in->readString("minecraft:facing_direction")) { "down" => Facing::UP, "up" => Facing::DOWN, "north" => Facing::NORTH, @@ -90,9 +94,4 @@ public function deserializeState(BlockStateReader $in): void { "east" => Facing::EAST, }; } - - public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, ?Player $player = null): bool { - $this->facing = Facing::opposite($face); - return parent::place($tx, $item, $blockReplace, $blockClicked, $face, $clickVector, $player); - } -} \ No newline at end of file +} diff --git a/src/block/states/templates/PillarRotationState.php b/src/block/permutations/traits/LogRotationTrait.php similarity index 90% rename from src/block/states/templates/PillarRotationState.php rename to src/block/permutations/traits/LogRotationTrait.php index ccca0a16..33ec3bd8 100644 --- a/src/block/states/templates/PillarRotationState.php +++ b/src/block/permutations/traits/LogRotationTrait.php @@ -1,15 +1,13 @@ axis]; } + public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, ?Player $player = null): bool { + $this->axis = match($face) { + 0, 1 => Axis::Y, // down, up + 2, 3 => Axis::Z, // north, south + 4, 5 => Axis::X, // west, east + default => Axis::Y + }; + return parent::place($tx, $item, $blockReplace, $blockClicked, $face, $clickVector, $player); + } + public function serializeState(BlockStateWriter $out): void { $rotation = match($this->axis) { Axis::X => "east", @@ -73,14 +81,4 @@ public function deserializeState(BlockStateReader $in): void { default => Axis::Y }; } - - public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, ?Player $player = null): bool { - $this->axis = match($face) { - 0, 1 => Axis::Y, // down, up - 2, 3 => Axis::Z, // north, south - 4, 5 => Axis::X, // west, east - default => Axis::Y - }; - return parent::place($tx, $item, $blockReplace, $blockClicked, $face, $clickVector, $player); - } -} \ No newline at end of file +}