A comprehensive custom content registration library for PocketMine-MP Bedrock Edition servers that provides a component-based system for registering custom items, blocks, and entities with full client-side rendering support via Bedrock's data-driven item/block system.
- Custom Items: Register fully data-driven custom items with automatic serializer/deserializer wiring, StringToItemParser integration, and optional Creative Inventory placement.
- Item Components: 37 built-in item components covering properties (damage, stack size, enchantability, animations, glint, etc.) and behaviors (food, durability, projectile, wearable, cooldown, digger, shooter, etc.).
- Auto-Detection: CustomItemTrait automatically detects the item base class and applies the correct default components (Armor, Food, Tool, Durable, ProjectileItem, Sword).
- Custom Blocks: Register custom blocks with full component NBT generation, permutation state support, block palette insertion sorted by fnv164 hash, and async worker thread synchronization.
- Block Components: 12 built-in block components for geometry, materials, collision, selection, light, friction, flammability, destructibility, and more.
- Block Permutations: Condition-based block state permutations with a Cartesian product helper, bitmask utilities, and a ready-made RotatableTrait for 4-direction rotation.
- Custom Entities: Register custom entities with automatic identifier injection into PocketMine's EntityFactory via reflection.
- Client-Side Packet Injection: Intercepts StartGamePacket to inject custom block palette and data_driven_items experiment, and ResourcePackStackPacket to inject experiments.
- Inventory Lock: Built-in item locking system that cancels drop, interact, and transaction events for locked items.
- Item Tick Task: Scheduled task scanning all online players' inventories every 20 ticks for items with ItemTaskComponent, executing callbacks when conditions match.
- Async Block Palette Sync: AsyncTask-based worker thread synchronization for custom block palettes using igbinary-serialized caches.
- Reflection Bridge: Static utilities for injecting into private PocketMine internals (item mappings, block palettes, entity identifiers).
Include or autoload LibCustom in your PocketMine-MP plugin. LibCustom requires
LibCommons for shared filesystem helpers used by the async block-palette
cache. LibCustom loads at STARTUP and automatically intercepts game packets to
inject custom content. Registration should be done during your plugin's
onEnable:
Requirements:
- PocketMine-MP API 5.0.0+
- PHP 8.2+
- LibCommons
Installation:
Place LibCustom in your server's plugins/ directory. It loads at STARTUP and initializes CustomManager automatically.
- Added
LibCustomclass inimperazim\customnamespace. - Added
CustomManagerclass inimperazim\customnamespace.
LibCustom -- Final PluginBase entry point. On enable it calls CustomManager::init(), schedules a delayed worker init hook for block palette sync, and schedules ItemTickTask every 20 ticks:
use imperazim\custom\LibCustom;
// LibCustom is loaded as a plugin. No manual instantiation needed.
// It automatically:
// - Initializes CustomManager on enable
// - Schedules block palette worker sync
// - Runs ItemTickTask every 20 ticksCustomManager -- Event listener that intercepts game packets and enforces inventory lock:
use imperazim\custom\CustomManager;
// CustomManager is initialized by LibCustom. It automatically:
// - Intercepts StartGamePacket to inject block palette + data_driven_items experiment
// - Intercepts ResourcePackStackPacket to inject experiments
// - Cancels drop/interact/transaction events for locked items- Added
CustomItemFactoryclass inimperazim\custom\itemnamespace. - Added
CustomItemTraittrait inimperazim\custom\itemnamespace. - Added
ItemComponentinterface inimperazim\custom\itemnamespace. - Added
ItemComponentsHolderinterface inimperazim\custom\itemnamespace. - Added
CreativeInventoryInfoclass inimperazim\custom\itemnamespace.
CustomItemFactory -- Singleton for registering and retrieving custom items:
use imperazim\custom\item\CustomItemFactory;
use imperazim\custom\item\CreativeInventoryInfo;
use imperazim\custom\item\component\CreativeCategory;
use imperazim\custom\item\component\CreativeGroup;
// Register a simple custom item
CustomItemFactory::getInstance()->register(
identifier: "myplugin:ruby",
factory: fn() => new Ruby(),
creative: new CreativeInventoryInfo(
category: CreativeCategory::ITEMS,
group: CreativeGroup::NONE
)
);
// Retrieve a custom item by identifier
$ruby = CustomItemFactory::getInstance()->get("myplugin:ruby", amount: 5);
// Get all registered item table entries (used internally for packet injection)
$entries = CustomItemFactory::getInstance()->getItemTableEntries();CustomItemTrait -- Trait for building component-based custom items. Auto-detects the base class and applies default components:
use pocketmine\item\Item;
use imperazim\custom\item\CustomItemTrait;
use imperazim\custom\item\ItemComponentsHolder;
use imperazim\custom\item\CreativeInventoryInfo;
use imperazim\custom\item\component\CreativeCategory;
class Ruby extends Item implements ItemComponentsHolder {
use CustomItemTrait;
public function __construct() {
parent::__construct(/* ... */);
// initComponents auto-detects base class:
// - Armor -> WearableComponent
// - Food -> FoodComponent + UseAnimation
// - Tool -> HandEquipped + Damage
// - Durable -> DurabilityComponent
// - ProjectileItem -> ProjectileComponent + Throwable
// - Sword -> CanDestroyInCreative(false)
$this->initComponents(
texture: "ruby",
creative: new CreativeInventoryInfo(
category: CreativeCategory::ITEMS
)
);
}
}Component management -- Add, check, and retrieve components on custom items:
use imperazim\custom\item\component\GlintComponent;
use imperazim\custom\item\component\MaxStackSizeComponent;
use imperazim\custom\item\component\LoreComponent;
// Add components after initialization
$item->addComponent(new GlintComponent(true));
$item->addComponent(new MaxStackSizeComponent(16));
$item->addComponent(new LoreComponent(["Rare gemstone", "Found in deep caves"]));
// Check and retrieve
if ($item->hasComponent("minecraft:glint")) {
$glint = $item->getComponent("minecraft:glint");
$value = $glint->getValue(); // true
}
// Get the full component NBT (CompoundTag)
$nbt = $item->getComponentNbt();Item utility methods -- Off-hand, cooldown, duration, and inventory lock:
// Allow item to be placed in off-hand slot
$item->allowOffHand();
// Set use cooldown (seconds)
$item->setUseCooldown(category: "ruby_ability", duration: 2.5);
// Set use duration (ticks)
$item->setUseDuration(30);
// Lock item in inventory (prevents drop/move)
$item->setLockOnInventory();
// Check lock state
if ($item->isLockedInInventory()) {
// Item cannot be dropped or moved
}- Added
AllowOffHandComponentclass inimperazim\custom\item\componentnamespace. - Added
CanDestroyInCreativeComponentclass inimperazim\custom\item\componentnamespace. - Added
DamageComponentclass inimperazim\custom\item\componentnamespace. - Added
EnchantableSlotComponentclass inimperazim\custom\item\componentnamespace. - Added
EnchantableValueComponentclass inimperazim\custom\item\componentnamespace. - Added
GlintComponentclass inimperazim\custom\item\componentnamespace. - Added
HandEquippedComponentclass inimperazim\custom\item\componentnamespace. - Added
HoverTextColorComponentclass inimperazim\custom\item\componentnamespace. - Added
LiquidClippedComponentclass inimperazim\custom\item\componentnamespace. - Added
LockInInventoryComponentclass inimperazim\custom\item\componentnamespace. - Added
MaxStackSizeComponentclass inimperazim\custom\item\componentnamespace. - Added
ShouldDespawnComponentclass inimperazim\custom\item\componentnamespace. - Added
StackedByDataComponentclass inimperazim\custom\item\componentnamespace. - Added
UseAnimationComponentclass inimperazim\custom\item\componentnamespace. - Added
UseDurationComponentclass inimperazim\custom\item\componentnamespace. - Added
BlockPlacerComponentclass inimperazim\custom\item\componentnamespace. - Added
BundleInteractionComponentclass inimperazim\custom\item\componentnamespace. - Added
CompostableComponentclass inimperazim\custom\item\componentnamespace. - Added
CooldownComponentclass inimperazim\custom\item\componentnamespace. - Added
DamageAbsorptionComponentclass inimperazim\custom\item\componentnamespace. - Added
DiggerComponentclass inimperazim\custom\item\componentnamespace. - Added
DisplayNameComponentclass inimperazim\custom\item\componentnamespace. - Added
DurabilityComponentclass inimperazim\custom\item\componentnamespace. - Added
DurabilitySensorComponentclass inimperazim\custom\item\componentnamespace. - Added
DyeableComponentclass inimperazim\custom\item\componentnamespace. - Added
EntityPlacerComponentclass inimperazim\custom\item\componentnamespace. - Added
FoodComponentclass inimperazim\custom\item\componentnamespace. - Added
FuelComponentclass inimperazim\custom\item\componentnamespace. - Added
IconComponentclass inimperazim\custom\item\componentnamespace. - Added
InteractButtonComponentclass inimperazim\custom\item\componentnamespace. - Added
ItemLockComponentclass inimperazim\custom\item\componentnamespace. - Added
ItemTaskComponentclass inimperazim\custom\item\componentnamespace. - Added
KeepOnDeathComponentclass inimperazim\custom\item\componentnamespace. - Added
LoreComponentclass inimperazim\custom\item\componentnamespace. - Added
OnHitEffectComponentclass inimperazim\custom\item\componentnamespace. - Added
ProjectileComponentclass inimperazim\custom\item\componentnamespace. - Added
RarityComponentclass inimperazim\custom\item\componentnamespace. - Added
RecordComponentclass inimperazim\custom\item\componentnamespace. - Added
RepairableComponentclass inimperazim\custom\item\componentnamespace. - Added
ShooterComponentclass inimperazim\custom\item\componentnamespace. - Added
TagsComponentclass inimperazim\custom\item\componentnamespace. - Added
ThrowableComponentclass inimperazim\custom\item\componentnamespace. - Added
UseModifiersComponentclass inimperazim\custom\item\componentnamespace. - Added
WearableComponentclass inimperazim\custom\item\componentnamespace.
Property components -- Components that map to item_properties in the item NBT (isProperty = true):
use imperazim\custom\item\component\AllowOffHandComponent;
use imperazim\custom\item\component\CanDestroyInCreativeComponent;
use imperazim\custom\item\component\DamageComponent;
use imperazim\custom\item\component\EnchantableSlotComponent;
use imperazim\custom\item\component\EnchantableValueComponent;
use imperazim\custom\item\component\GlintComponent;
use imperazim\custom\item\component\HandEquippedComponent;
use imperazim\custom\item\component\HoverTextColorComponent;
use imperazim\custom\item\component\LiquidClippedComponent;
use imperazim\custom\item\component\LockInInventoryComponent;
use imperazim\custom\item\component\MaxStackSizeComponent;
use imperazim\custom\item\component\ShouldDespawnComponent;
use imperazim\custom\item\component\StackedByDataComponent;
use imperazim\custom\item\component\UseAnimationComponent;
use imperazim\custom\item\component\UseDurationComponent;
// Boolean properties
$item->addComponent(new AllowOffHandComponent(true));
$item->addComponent(new CanDestroyInCreativeComponent(false));
$item->addComponent(new GlintComponent(true));
$item->addComponent(new HandEquippedComponent(true));
$item->addComponent(new LiquidClippedComponent(true));
$item->addComponent(new LockInInventoryComponent(true));
$item->addComponent(new ShouldDespawnComponent(false));
$item->addComponent(new StackedByDataComponent(true));
// Integer properties
$item->addComponent(new DamageComponent(8));
$item->addComponent(new EnchantableValueComponent(14));
$item->addComponent(new MaxStackSizeComponent(16));
$item->addComponent(new UseDurationComponent(20)); // ticks
// String properties
$item->addComponent(new EnchantableSlotComponent("sword"));
$item->addComponent(new HoverTextColorComponent("gold"));
// Use animation constants
$item->addComponent(new UseAnimationComponent(UseAnimationComponent::ANIMATION_NONE)); // 0
$item->addComponent(new UseAnimationComponent(UseAnimationComponent::ANIMATION_EAT)); // 1
$item->addComponent(new UseAnimationComponent(UseAnimationComponent::ANIMATION_DRINK)); // 2
$item->addComponent(new UseAnimationComponent(UseAnimationComponent::ANIMATION_CROSSBOW));// 4
$item->addComponent(new UseAnimationComponent(UseAnimationComponent::ANIMATION_BRUSH)); // 12Behavior components -- Components that map to components in the item NBT (isProperty = false):
use imperazim\custom\item\component\BlockPlacerComponent;
use imperazim\custom\item\component\BundleInteractionComponent;
use imperazim\custom\item\component\CompostableComponent;
use imperazim\custom\item\component\CooldownComponent;
use imperazim\custom\item\component\DamageAbsorptionComponent;
use imperazim\custom\item\component\DiggerComponent;
use imperazim\custom\item\component\DisplayNameComponent;
use imperazim\custom\item\component\DurabilityComponent;
use imperazim\custom\item\component\DurabilitySensorComponent;
use imperazim\custom\item\component\DyeableComponent;
use imperazim\custom\item\component\EntityPlacerComponent;
use imperazim\custom\item\component\FoodComponent;
use imperazim\custom\item\component\FuelComponent;
use imperazim\custom\item\component\IconComponent;
use imperazim\custom\item\component\InteractButtonComponent;
use imperazim\custom\item\component\ItemLockComponent;
use imperazim\custom\item\component\ItemTaskComponent;
use imperazim\custom\item\component\KeepOnDeathComponent;
use imperazim\custom\item\component\LoreComponent;
use imperazim\custom\item\component\OnHitEffectComponent;
use imperazim\custom\item\component\ProjectileComponent;
use imperazim\custom\item\component\RarityComponent;
use imperazim\custom\item\component\RecordComponent;
use imperazim\custom\item\component\RepairableComponent;
use imperazim\custom\item\component\ShooterComponent;
use imperazim\custom\item\component\TagsComponent;
use imperazim\custom\item\component\ThrowableComponent;
use imperazim\custom\item\component\UseModifiersComponent;
use imperazim\custom\item\component\WearableComponent;
// Block placer -- makes the item place a block when used
$item->addComponent(new BlockPlacerComponent(
blockId: "minecraft:stone",
useOn: ["minecraft:grass", "minecraft:dirt"]
));
// Bundle interaction -- bundle-like container behavior
$item->addComponent(new BundleInteractionComponent(slots: 27));
// Compostable -- can be placed in a composter
$item->addComponent(new CompostableComponent(chance: 0.65));
// Cooldown -- use cooldown category and duration
$item->addComponent(new CooldownComponent(category: "ender_pearl", duration: 1.0));
// Damage absorption -- absorbs damage from specified causes
$item->addComponent(new DamageAbsorptionComponent(causes: ["entity_attack", "projectile"]));
// Digger -- mining speed with fluent block/tag targeting
$digger = new DiggerComponent(useEfficiency: true);
$digger->withBlock("minecraft:stone", speed: 8.0);
$digger->withBlock("minecraft:cobblestone", speed: 8.0);
$digger->withTag("minecraft:is_pickaxe_mineable", speed: 6.0);
$item->addComponent($digger);
// Display name -- custom item display name
$item->addComponent(new DisplayNameComponent("Enchanted Ruby"));
// Durability -- item durability with damage chance range
$item->addComponent(new DurabilityComponent(
max: 250,
minChance: 60,
maxChance: 100
));
// Durability sensor -- callbacks at durability thresholds
$item->addComponent(new DurabilitySensorComponent(thresholds: [50, 25, 10]));
// Dyeable -- item supports color dyeing
$item->addComponent(new DyeableComponent(defaultColor: "#FF0000"));
// Entity placer -- spawns an entity on use
$item->addComponent(new EntityPlacerComponent(
entity: "minecraft:pig",
useOn: ["minecraft:grass"]
));
// Food -- edible item with nutrition and saturation
$item->addComponent(new FoodComponent(
canAlwaysEat: false,
nutrition: 6,
saturation: 9.6,
convertsTo: "myplugin:empty_bowl"
));
// Fuel -- furnace fuel with burn duration
$item->addComponent(new FuelComponent(duration: 200.0));
// Icon -- item texture with optional dyed and trim variants
$item->addComponent(new IconComponent(
texture: "ruby",
dyed: "ruby_dyed",
trim: "ruby_trim"
));
// Interact button -- shows interact button text on mobile
$item->addComponent(new InteractButtonComponent("Feed Animal"));
$item->addComponent(new InteractButtonComponent(true)); // default text
// Item lock -- lock mode for item
$item->addComponent(new ItemLockComponent(mode: "lock_in_inventory"));
// Item task -- server-side scheduled callback (runs via ItemTickTask)
$item->addComponent(new ItemTaskComponent(
callback: function($player, $item): void {
$player->sendMessage("Your item pulses with energy!");
},
filter: "myplugin:enchanted_ruby"
));
// Keep on death -- item persists through death
$item->addComponent(new KeepOnDeathComponent());
// Lore -- item description lines
$item->addComponent(new LoreComponent(lines: [
"A rare gemstone",
"Mined from the depths"
]));
// On-hit effect -- applies potion effect on hit (server-side only)
use pocketmine\entity\effect\EffectInstance;
use pocketmine\entity\effect\VanillaEffects;
$effect = new EffectInstance(VanillaEffects::POISON(), 100, 1);
$onHit = new OnHitEffectComponent($effect);
$item->addComponent($onHit);
// In your damage handler:
$onHit->applyEffect($targetLivingEntity);
// Projectile -- item acts as ammunition for a projectile
$item->addComponent(new ProjectileComponent(
minCritPower: 0.5,
entity: "minecraft:arrow"
));
// Rarity -- item rarity tier
$item->addComponent(new RarityComponent("epic"));
// Record -- music disc component
$item->addComponent(new RecordComponent(
duration: 180.0,
sound: "record.13",
comparator: 1
));
// Repairable -- items used to repair this item
$item->addComponent(new RepairableComponent(items: [
"myplugin:ruby",
"minecraft:diamond"
]));
// Shooter -- bow-like shooting behavior
$item->addComponent(new ShooterComponent(
ammo: "minecraft:arrow",
chargeOnDraw: true
));
// Tags -- Bedrock item tags
$item->addComponent(new TagsComponent(tags: [
"minecraft:is_sword",
"minecraft:is_tool"
]));
// Throwable -- item can be thrown
$item->addComponent(new ThrowableComponent(
swing: true,
scale: 1.0
));
// Use modifiers -- movement and use duration modifiers
$item->addComponent(new UseModifiersComponent(
moveMod: 0.5,
useDuration: 30.0
));
// Wearable -- item can be worn as armor
$item->addComponent(new WearableComponent(
slot: WearableComponent::SLOT_ARMOR_HEAD,
protection: 3,
dispensable: true
));
// Available slot constants:
// SLOT_ARMOR_HEAD, SLOT_ARMOR_CHEST, SLOT_ARMOR_LEGS, SLOT_ARMOR_FEET
// SLOT_WEAPON_OFFHAND, SLOT_SADDLE- Added
ComponentValidationExceptionclass inimperazim\custom\item\exceptionnamespace. - Added
ComponentConflictExceptionclass inimperazim\custom\item\exceptionnamespace. - Added
ComponentNotFoundExceptionclass inimperazim\custom\item\exceptionnamespace.
All exceptions extend RuntimeException and are thrown during component registration and retrieval:
use imperazim\custom\item\exception\ComponentValidationException;
use imperazim\custom\item\exception\ComponentConflictException;
use imperazim\custom\item\exception\ComponentNotFoundException;
try {
$item->addComponent(new DurabilityComponent(max: -1));
} catch (ComponentValidationException $e) {
// Invalid component parameters
$logger->error("Validation failed: " . $e->getMessage());
}
try {
$item->addComponent(new WearableComponent(slot: "head"));
$item->addComponent(new WearableComponent(slot: "chest")); // conflict
} catch (ComponentConflictException $e) {
// Duplicate or conflicting component
$logger->error("Conflict: " . $e->getMessage());
}
try {
$component = $item->getComponent("nonexistent:component");
} catch (ComponentNotFoundException $e) {
// Component not found on item
$logger->error("Not found: " . $e->getMessage());
}- Added
CustomBlockFactoryclass inimperazim\custom\blocknamespace. - Added
BlockComponentinterface inimperazim\custom\blocknamespace. - Added
BlockComponentsHolderinterface inimperazim\custom\blocknamespace. - Added
BlockPaletteManagerclass inimperazim\custom\blocknamespace. - Added
Materialclass inimperazim\custom\blocknamespace.
CustomBlockFactory -- Singleton for registering and retrieving custom blocks:
use imperazim\custom\block\CustomBlockFactory;
use imperazim\custom\item\CreativeInventoryInfo;
// Register a simple custom block
CustomBlockFactory::getInstance()->register(
blockFunc: fn() => new RubyBlock(),
identifier: "myplugin:ruby_block",
creative: new CreativeInventoryInfo(
category: CreativeCategory::CONSTRUCTION
)
);
// Register with custom serializer/deserializer
CustomBlockFactory::getInstance()->register(
blockFunc: fn() => new FancyBlock(),
identifier: "myplugin:fancy_block",
creative: null,
serializer: function(FancyBlock $block): Writer {
// Custom block state serialization
},
deserializer: function(Reader $in): FancyBlock {
// Custom block state deserialization
}
);
// Retrieve a custom block by identifier
$block = CustomBlockFactory::getInstance()->get("myplugin:ruby_block");
// Get all block palette entries (used internally for packet injection)
$entries = CustomBlockFactory::getInstance()->getBlockPaletteEntries();
// Add worker init hook for async block palette synchronization
CustomBlockFactory::getInstance()->addWorkerInitHook($cachePath);BlockComponentsHolder -- Interface for blocks that carry component data:
use pocketmine\block\Block;
use imperazim\custom\block\BlockComponentsHolder;
use imperazim\custom\block\BlockComponent;
use imperazim\custom\block\component\GeometryComponent;
use imperazim\custom\block\component\MaterialInstancesComponent;
use imperazim\custom\block\component\DestructibleByMiningComponent;
use imperazim\custom\block\component\LightEmissionComponent;
use imperazim\custom\block\Material;
class RubyBlock extends Block implements BlockComponentsHolder {
/** @return BlockComponent[] */
public function getBlockComponents(): array {
return [
new GeometryComponent("minecraft:geometry.full_block"),
new MaterialInstancesComponent([
new Material(
target: "*",
texture: "ruby_block",
renderMethod: "opaque",
faceDimming: true,
ambientOcclusion: true
)
]),
new DestructibleByMiningComponent(3.0),
new LightEmissionComponent(5),
];
}
}Material -- Value object for material instances used in block rendering:
use imperazim\custom\block\Material;
$material = new Material(
target: "*", // Face target: *, up, down, north, south, east, west
texture: "ruby_block", // Texture name from resource pack
renderMethod: "opaque", // opaque, alpha_test, blend, double_sided
faceDimming: true, // Apply face dimming
ambientOcclusion: true // Apply ambient occlusion
);
// Convert to NBT for block component registration
$nbt = $material->toNbt();BlockPaletteManager -- Singleton managing the sorted block state palette:
use imperazim\custom\block\BlockPaletteManager;
// Get all block states in the palette
$allStates = BlockPaletteManager::getInstance()->getStates();
// Get only custom block states
$customStates = BlockPaletteManager::getInstance()->getCustomStates();
// Insert a new state (re-sorts palette by fnv164 hash)
BlockPaletteManager::getInstance()->insertState($compoundTag, meta: 0);- Added
BreathabilityComponentclass inimperazim\custom\block\componentnamespace. - Added
CollisionBoxComponentclass inimperazim\custom\block\componentnamespace. - Added
BlockDisplayNameComponentclass inimperazim\custom\block\componentnamespace. - Added
DestructibleByExplosionComponentclass inimperazim\custom\block\componentnamespace. - Added
DestructibleByMiningComponentclass inimperazim\custom\block\componentnamespace. - Added
FlammableComponentclass inimperazim\custom\block\componentnamespace. - Added
FrictionComponentclass inimperazim\custom\block\componentnamespace. - Added
GeometryComponentclass inimperazim\custom\block\componentnamespace. - Added
LightDampeningComponentclass inimperazim\custom\block\componentnamespace. - Added
LightEmissionComponentclass inimperazim\custom\block\componentnamespace. - Added
MaterialInstancesComponentclass inimperazim\custom\block\componentnamespace. - Added
SelectionBoxComponentclass inimperazim\custom\block\componentnamespace.
All 12 block components implement the BlockComponent interface with getName(): string and toNbt(): CompoundTag:
use imperazim\custom\block\component\BreathabilityComponent;
use imperazim\custom\block\component\CollisionBoxComponent;
use imperazim\custom\block\component\BlockDisplayNameComponent;
use imperazim\custom\block\component\DestructibleByExplosionComponent;
use imperazim\custom\block\component\DestructibleByMiningComponent;
use imperazim\custom\block\component\FlammableComponent;
use imperazim\custom\block\component\FrictionComponent;
use imperazim\custom\block\component\GeometryComponent;
use imperazim\custom\block\component\LightDampeningComponent;
use imperazim\custom\block\component\LightEmissionComponent;
use imperazim\custom\block\component\MaterialInstancesComponent;
use imperazim\custom\block\component\SelectionBoxComponent;
use imperazim\custom\block\Material;
// Breathability -- solid or air-like
$components[] = new BreathabilityComponent(type: "solid");
// Collision box -- custom collision bounds
$components[] = new CollisionBoxComponent(
enabled: true,
origin: [-8.0, 0.0, -8.0],
size: [16.0, 16.0, 16.0]
);
// Display name
$components[] = new BlockDisplayNameComponent("Ruby Block");
// Destructible by explosion -- explosion resistance
$components[] = new DestructibleByExplosionComponent(resistance: 6.0);
// Destructible by mining -- mining time in seconds
$components[] = new DestructibleByMiningComponent(seconds: 3.0);
// Flammable -- fire catch and destroy chances
$components[] = new FlammableComponent(catch: 5, destroy: 20);
// Friction -- surface friction (0.0 to 1.0)
$components[] = new FrictionComponent(friction: 0.6);
// Geometry -- block model with optional bone visibility
$geometry = new GeometryComponent(id: "geometry.custom_chair");
$geometry->addBoneVisibility("seat", true);
$geometry->addBoneVisibility("back", "query.block_state('myplugin:has_back') == 1");
$components[] = $geometry;
// Light dampening -- light absorption (0-15)
$components[] = new LightDampeningComponent(level: 15);
// Light emission -- light output (0-15)
$components[] = new LightEmissionComponent(level: 5);
// Material instances -- textures and render methods per face
$components[] = new MaterialInstancesComponent([
new Material("*", "ruby_block", "opaque", true, true),
new Material("up", "ruby_block_top", "opaque", true, true),
]);
// Selection box -- custom selection/highlight bounds
$components[] = new SelectionBoxComponent(
enabled: true,
origin: [-8.0, 0.0, -8.0],
size: [16.0, 8.0, 16.0]
);- Added
Permutableinterface inimperazim\custom\block\permutationnamespace. - Added
BlockPropertyclass inimperazim\custom\block\permutationnamespace. - Added
Permutationclass inimperazim\custom\block\permutationnamespace. - Added
PermutationHelperclass inimperazim\custom\block\permutationnamespace. - Added
RotatableTraittrait inimperazim\custom\block\permutationnamespace.
Permutable -- Interface for blocks with state-based permutations:
use pocketmine\block\Block;
use imperazim\custom\block\BlockComponentsHolder;
use imperazim\custom\block\permutation\Permutable;
use imperazim\custom\block\permutation\BlockProperty;
use imperazim\custom\block\permutation\Permutation;
use pocketmine\data\bedrock\block\convert\BlockStateReader;
use pocketmine\data\bedrock\block\convert\BlockStateWriter;
use pocketmine\nbt\tag\CompoundTag;
class LampBlock extends Block implements BlockComponentsHolder, Permutable {
private bool $lit = false;
public function getBlockProperties(): array {
return [
new BlockProperty("myplugin:lit", [0, 1]),
];
}
public function getPermutations(): array {
return [
(new Permutation("query.block_state('myplugin:lit') == 1"))
->withComponent("minecraft:light_emission", 15),
(new Permutation("query.block_state('myplugin:lit') == 0"))
->withComponent("minecraft:light_emission", 0),
];
}
public function getCurrentBlockProperties(): CompoundTag {
return CompoundTag::create()
->setInt("myplugin:lit", $this->lit ? 1 : 0);
}
public function serializeState(BlockStateWriter $writer): void {
$writer->writeInt("myplugin:lit", $this->lit ? 1 : 0);
}
public function deserializeState(BlockStateReader $reader): void {
$this->lit = $reader->readBoundedInt("myplugin:lit", 0, 1) === 1;
}
}BlockProperty -- Value object defining a block state property and its valid values:
use imperazim\custom\block\permutation\BlockProperty;
// Boolean-like property (0 or 1)
$lit = new BlockProperty("myplugin:lit", [0, 1]);
// Multi-value property
$color = new BlockProperty("myplugin:color", [0, 1, 2, 3]); // 4 colors
// Convert to NBT for registration
$nbt = $lit->toNbt();Permutation -- Condition-based component overrides with fluent builder:
use imperazim\custom\block\permutation\Permutation;
$permutation = new Permutation("query.block_state('myplugin:color') == 2");
$permutation->withComponent("minecraft:material_instances", [
"*" => ["texture" => "lamp_blue", "render_method" => "opaque"]
]);
$permutation->withComponent("minecraft:light_emission", 10);
// Convert to NBT for registration
$nbt = $permutation->toNbt();PermutationHelper -- Static utilities for permutation state math:
use imperazim\custom\block\permutation\PermutationHelper;
use imperazim\custom\block\permutation\BlockProperty;
$lit = new BlockProperty("myplugin:lit", [0, 1]);
$color = new BlockProperty("myplugin:color", [0, 1, 2, 3]);
// Get all combinations of block property values
$product = PermutationHelper::getCartesianProduct($lit, $color);
// [[0,0], [0,1], [0,2], [0,3], [1,0], [1,1], [1,2], [1,3]]
// Convert property values to a single meta integer
$meta = PermutationHelper::toMeta($lit, $color); // e.g. 5
// Convert meta back to property values
$values = PermutationHelper::fromMeta($meta, $lit, $color);
// Get the total state bitmask for all properties
$bitmask = PermutationHelper::getStateBitmask($lit, $color);RotatableTrait -- Ready-made Permutable implementation for 4-direction block rotation:
use pocketmine\block\Block;
use imperazim\custom\block\BlockComponentsHolder;
use imperazim\custom\block\permutation\Permutable;
use imperazim\custom\block\permutation\RotatableTrait;
class CustomFurnace extends Block implements BlockComponentsHolder, Permutable {
use RotatableTrait;
// RotatableTrait automatically:
// - Adds "customies:rotation" property with values [0, 1, 2, 3]
// - Creates 4 permutations with minecraft:transformation for each direction
// - Sets rotation on place() based on player facing direction
// - Implements all Permutable interface methods
public function getBlockComponents(): array {
return [
// Your block components here
];
}
}
// The block automatically rotates to face the player when placed.
// No additional rotation logic needed.- Added
CustomEntityFactoryclass inimperazim\custom\entitynamespace.
CustomEntityFactory -- Singleton for registering custom entities:
use imperazim\custom\entity\CustomEntityFactory;
use pocketmine\entity\Entity;
use pocketmine\entity\EntitySizeInfo;
use pocketmine\entity\Location;
use pocketmine\nbt\tag\CompoundTag;
class CustomZombie extends Entity {
public static function getNetworkTypeId(): string {
return "myplugin:custom_zombie";
}
protected function getInitialSizeInfo(): EntitySizeInfo {
return new EntitySizeInfo(1.95, 0.6);
}
}
// Register the custom entity
CustomEntityFactory::getInstance()->register(
className: CustomZombie::class,
identifier: "myplugin:custom_zombie",
creationFunc: function(World $world, CompoundTag $nbt): CustomZombie {
return new CustomZombie(
Location::fromObject($world->getSpawnLocation(), $world),
$nbt
);
},
behaviourId: "minecraft:zombie"
);
// The entity identifier is automatically injected into PocketMine's
// EntityFactory via ReflectionBridge::injectEntityIdentifier.- Added
ReflectionBridgeclass inimperazim\custom\registrynamespace. - Added
RegistrationResultclass inimperazim\custom\registrynamespace.
ReflectionBridge -- Static utilities for injecting into private PocketMine internals:
use imperazim\custom\registry\ReflectionBridge;
// Inject a custom item mapping into ItemTypeDictionary
ReflectionBridge::injectItemMapping($stringId, $intId, $isComponentBased);
// Inject a block-item mapping
ReflectionBridge::injectBlockItemMapping($stringId, $intId);
// Get all registered block states
$states = ReflectionBridge::getBlockStates();
// Apply a modified block palette to the server
ReflectionBridge::applyBlockPalette($palette);
// Inject an entity identifier into PocketMine's EntityFactory
ReflectionBridge::injectEntityIdentifier($className, $identifier);RegistrationResult -- Value object for registration outcomes:
use imperazim\custom\registry\RegistrationResult;
// Create success result
$result = RegistrationResult::ok();
$result->success; // true
$result->error; // null
// Create failure result
$result = RegistrationResult::fail("Identifier already registered");
$result->success; // false
$result->error; // "Identifier already registered"- Added
AsyncBlockRegistrationTaskclass inimperazim\custom\tasknamespace. - Added
ItemTickTaskclass inimperazim\custom\tasknamespace.
AsyncBlockRegistrationTask -- Worker thread block palette synchronization:
use imperazim\custom\task\AsyncBlockRegistrationTask;
// Typically triggered automatically by CustomBlockFactory::addWorkerInitHook.
// The task reads an igbinary-serialized cache file, re-sorts the worker
// BlockStateDictionary, and registers blocks in the worker's
// RuntimeBlockStateRegistry and GlobalBlockStateHandlers.ItemTickTask -- Scheduled task that scans player inventories every 20 ticks:
use imperazim\custom\task\ItemTickTask;
use imperazim\custom\item\component\ItemTaskComponent;
// ItemTickTask is scheduled automatically by LibCustom on enable.
// It scans all online players' inventories for items with ItemTaskComponent.
// When an item's filter matches, the callback is executed:
$item->addComponent(new ItemTaskComponent(
callback: function($player, $item): void {
// Called every 20 ticks while the item is in the player's inventory
$player->getEffects()->add(
new EffectInstance(VanillaEffects::REGENERATION(), 25, 0)
);
},
filter: "myplugin:healing_charm"
));- Added
NbtHelperclass inimperazim\custom\utilnamespace.
NbtHelper -- Static NBT conversion utilities:
use imperazim\custom\util\NbtHelper;
// Convert a PHP value to the appropriate NBT Tag
$tag = NbtHelper::getTagType(42); // IntTag
$tag = NbtHelper::getTagType(3.14); // FloatTag
$tag = NbtHelper::getTagType("hello"); // StringTag
$tag = NbtHelper::getTagType(true); // ByteTag
// Convert a PHP array to ListTag or CompoundTag
$listTag = NbtHelper::getArrayTag([1, 2, 3]); // ListTag<IntTag>
$compoundTag = NbtHelper::getArrayTag(["key" => "value"]); // CompoundTag
$nestedTag = NbtHelper::getArrayTag(["a" => [1, 2], "b" => 3.0]); // CompoundTag with nested ListTagThis project is licensed under MIT. Please see the LICENSE file for details.
LibCustom now publishes an EasyLibrary 3.x internal package asset for the migration from standalone-only libraries to installable internal packages.
Release assets now include:
LibCustom-2.0.0.pharLibCustom-2.0.0.easylib.zippackage.ymlchecksums.txt
The standalone plugin remains supported. During the EasyLibrary 3.x migration, if the standalone plugin is installed on a server, EasyLibrary marks the internal package as shadowed and does not autoload it.