Skip to content

ImperaZim/LibEnchantment

Repository files navigation

LibEnchantment

Description

A custom enchantment framework for PocketMine-MP that provides three behavior types (reactive, ticking, toggleable), automatic display rendering, and ready-to-use enchantment presets -- all with cooldown management, rarity-colored item tooltips, and automatic cleanup on disable.

Features

  • Custom Enchantment Base: Extensible base class with 8 usage types, 16 item types, cooldown management, chance-based activation, and priority ordering.
  • Reactive Enchantments: Event-driven enchantments that fire on damage, bow shoot, block break, sneak, interact, and other reagent events with directional filtering and stack tracking.
  • Ticking Enchantments: Interval-based enchantments that execute every N ticks while equipped, with configurable multi-item support.
  • Toggleable Enchantments: State-tracked enchantments that activate on equip and deactivate on unequip, maintaining per-player stack and armor stack counts.
  • Enchantment Factory: Static registration facade with auto-incrementing IDs starting at 100, reflection-based unregistration from PocketMine internals, and name/ID lookup.
  • Enchantment Manager: Central event hub handling 14 event types plus outbound/inbound packet interception for display injection and stripping.
  • Armor Effect Preset: Toggleable enchantment that applies potion effects while armor is equipped, with level-scaled amplifiers, Jump Boost fall damage immunity, and pre-existing effect preservation.
  • Attacker Debuff Preset: Reactive armor enchantment that applies debuff effects to attackers when the wearer is hit, with duration and amplifier scaling per level.
  • Damage Multiplier Preset: Reactive enchantment that conditionally multiplies outgoing damage based on a Closure condition, with per-level multiplier scaling.
  • Projectile Preset: Reactive enchantment that replaces default arrows with custom Projectile subclasses on bow shoot, maintaining ProjectileTracker state.
  • Weapon Effect Preset: Reactive weapon enchantment that applies effects to hit targets with duration and amplifier scaling per level.
  • Display Rendering: Automatic rarity-colored enchantment names with Roman numeral levels injected into item tooltips via outbound inventory packets, with clean reversal on inbound packets.
  • Automatic Cleanup: All custom enchantments are toggled OFF for all online players on plugin disable to prevent orphaned effects.

How to Use

Include or autoload LibEnchantment in your PocketMine-MP plugin. No external dependencies are required. LibEnchantment registers itself as a listener, schedules EnchantmentTickTask every 1 tick and DisplayCleanupTask every 200 ticks, and handles automatic cleanup on disable.

Requirements

  • PocketMine-MP API 5.0.0+
  • PHP 8.2+
  • No external dependencies
  • Loads at STARTUP

Installation

Register your custom enchantments in your plugin's onEnable:

use imperazim\enchantment\EnchantmentFactory;

public function onEnable(): void {
    $id = EnchantmentFactory::newId();
    EnchantmentFactory::register($id, new MyCustomEnchantment());
}

Components

imperazim\enchantment

Highlights

  • Added LibEnchantment class in imperazim\enchantment namespace.
  • Added EnchantmentFactory class in imperazim\enchantment namespace.
  • Added EnchantmentManager class in imperazim\enchantment namespace.
  • Added CustomEnchantment class in imperazim\enchantment namespace.

Usage

LibEnchantment -- Plugin entry point that bootstraps the entire framework:

use imperazim\enchantment\LibEnchantment;

// LibEnchantment is loaded as a plugin. No manual instantiation needed.
// It automatically:
// - Registers EnchantmentManager as a listener
// - Schedules EnchantmentTickTask every 1 tick
// - Schedules DisplayCleanupTask every 200 ticks
// - Toggles OFF all custom enchantments for all online players on disable

EnchantmentFactory -- Static facade for registering and looking up custom enchantments:

use imperazim\enchantment\EnchantmentFactory;
use imperazim\enchantment\CustomEnchantment;

// Get next available ID (starts at 100 to avoid vanilla 0-50)
$id = EnchantmentFactory::newId(); // 100, 101, 102, ...

// Register an enchantment
EnchantmentFactory::register($id, new MyEnchantment());

// Look up by ID or name
$enchant = EnchantmentFactory::get($id);             // CustomEnchantment|null
$enchant = EnchantmentFactory::getByName("my_ench"); // CustomEnchantment|null

// Get all registered custom enchantments
$all = EnchantmentFactory::getAll(); // array<int, CustomEnchantment>

// Unregister (also removes from PocketMine's EnchantmentIdMap and StringToEnchantmentParser via reflection)
EnchantmentFactory::unregister($id);
EnchantmentFactory::unregister($enchant);

CustomEnchantment -- Base class extending PocketMine's Enchantment with usage types, item types, cooldowns, and chance-based activation:

use imperazim\enchantment\CustomEnchantment;
use pocketmine\item\enchantment\Rarity;

class MyEnchantment extends CustomEnchantment {

    public function __construct() {
        $this->name = "my_enchantment";
        $this->rarity = Rarity::RARE;
        $this->maxLevel = 3;
        $this->displayName = "My Enchantment";
        $this->description = "Does something cool";
        $this->cooldownDuration = 5;    // seconds
        $this->chance = 50;             // 0-100 percent
        $this->usageType = self::USAGE_HAND;
        $this->itemType = self::ITEM_SWORD;
    }
}

// Usage type constants:
// USAGE_HAND = 0, USAGE_ANY_INVENTORY = 1, USAGE_INVENTORY = 2, USAGE_ARMOR = 3,
// USAGE_HELMET = 4, USAGE_CHESTPLATE = 5, USAGE_LEGGINGS = 6, USAGE_BOOTS = 7

// Item type constants:
// ITEM_GLOBAL = 0, ITEM_SWORD = 1, ITEM_BOW = 2, ITEM_TOOL = 3, ITEM_SHEARS = 4,
// ITEM_FLINT_AND_STEEL = 5, ITEM_PICKAXE = 6, ITEM_AXE = 7, ITEM_HOE = 8,
// ITEM_SHOVEL = 9, ITEM_HELMET = 10, ITEM_CHESTPLATE = 11, ITEM_LEGGINGS = 12,
// ITEM_BOOTS = 13, ITEM_COMPASS = 14, ITEM_ELYTRA = 15

// Cooldown management
$enchant->setCooldown($player, 10);        // set 10-second cooldown
$remaining = $enchant->getCooldown($player); // get remaining cooldown
$enchant->clearCooldown($player);           // clear cooldown

// Query capabilities
$enchant->canReact();   // false (override via ReactiveTrait)
$enchant->canTick();    // false (override via TickingTrait)
$enchant->canToggle();  // false (override via ToggleableTrait)

EnchantmentManager -- Central event hub (registered automatically by LibEnchantment):

use imperazim\enchantment\EnchantmentManager;

// EnchantmentManager is registered as a Listener automatically.
// It handles the following events:
// - BlockBreakEvent
// - EntityDamageEvent (cancels fall damage if immunity active, triggers reactive for victim+attacker)
// - EntityEffectAddEvent
// - EntityShootBowEvent
// - PlayerDeathEvent
// - PlayerInteractEvent
// - PlayerItemHeldEvent (toggles OFF old item, ON new item)
// - PlayerJoinEvent (toggles ON all equipped + attaches CallbackInventoryListener)
// - PlayerMoveEvent (fall damage tracking, reactive on block boundary cross)
// - PlayerQuitEvent (toggles OFF all equipped)
// - PlayerToggleSneakEvent
// - ProjectileHitBlockEvent
// - ProjectileLaunchEvent (registers in ProjectileTracker)
//
// Packet interception:
// - Outbound InventorySlotPacket/InventoryContentPacket: injects display via EnchantmentDisplay
// - Inbound InventoryTransactionPacket/MobEquipmentPacket: strips display via filterDisplayedEnchants

imperazim\enchantment\trait

Highlights

  • Added ReactiveTrait trait in imperazim\enchantment\trait namespace.
  • Added TickingTrait trait in imperazim\enchantment\trait namespace.
  • Added ToggleableTrait trait in imperazim\enchantment\trait namespace.

Usage

ReactiveTrait -- Event-driven enchantment behavior with reagent matching, directional filtering, and stack tracking:

use imperazim\enchantment\CustomEnchantment;
use imperazim\enchantment\trait\ReactiveTrait;
use pocketmine\player\Player;
use pocketmine\item\Item;
use pocketmine\inventory\Inventory;
use pocketmine\event\Event;
use pocketmine\event\entity\EntityDamageByEntityEvent;

class PoisonStrikeEnchantment extends CustomEnchantment {
    use ReactiveTrait;

    public function __construct() {
        $this->name = "poison_strike";
        $this->rarity = Rarity::RARE;
        $this->maxLevel = 3;
        $this->displayName = "Poison Strike";
        $this->description = "Poisons enemies on hit";
        $this->cooldownDuration = 3;
        $this->chance = 75;
        $this->usageType = self::USAGE_HAND;
        $this->itemType = self::ITEM_SWORD;
    }

    // Override reagent to specify which events trigger this enchantment
    public function getReagent(): array {
        return [EntityDamageByEntityEvent::class];
    }

    // Override react() -- called when reagent event fires, cooldown passes, and chance succeeds
    public function react(
        Player $player,
        Item $item,
        Inventory $inventory,
        int $slot,
        Event $event,
        int $level,
        int $stack
    ): void {
        if ($event instanceof EntityDamageByEntityEvent) {
            $target = $event->getEntity();
            if ($target instanceof Player) {
                $effect = new EffectInstance(VanillaEffects::POISON(), 60 * $level, $level - 1);
                $target->getEffects()->add($effect);
            }
        }
    }

    // Optional: control directional filtering
    public function shouldReactToDamage(): bool {
        return true;  // react when player deals damage
    }

    public function shouldReactToDamaged(): bool {
        return false; // do not react when player takes damage
    }
}

TickingTrait -- Interval-based enchantment behavior that runs every N ticks while equipped:

use imperazim\enchantment\CustomEnchantment;
use imperazim\enchantment\trait\TickingTrait;
use pocketmine\player\Player;
use pocketmine\item\Item;
use pocketmine\inventory\Inventory;

class RegenerationAuraEnchantment extends CustomEnchantment {
    use TickingTrait;

    public function __construct() {
        $this->name = "regeneration_aura";
        $this->rarity = Rarity::UNCOMMON;
        $this->maxLevel = 2;
        $this->displayName = "Regeneration Aura";
        $this->description = "Slowly regenerates health while held";
        $this->usageType = self::USAGE_HAND;
        $this->itemType = self::ITEM_SWORD;
    }

    // Override interval (default is 1 tick; here we tick every 40 ticks = 2 seconds)
    public function getTickingInterval(): int {
        return 40;
    }

    // Override tick() -- called at each interval while item is equipped
    public function tick(
        Player $player,
        Item $item,
        Inventory $inventory,
        int $slot,
        int $level
    ): void {
        $health = $player->getHealth();
        $max = $player->getMaxHealth();
        if ($health < $max) {
            $player->setHealth(min($max, $health + (0.5 * $level)));
        }
    }

    // Optional: allow multiple items with this enchantment to tick simultaneously
    public function supportsMultipleItems(): bool {
        return false; // default: only the first matching item ticks
    }
}

ToggleableTrait -- State-tracked enchantment that activates on equip and deactivates on unequip:

use imperazim\enchantment\CustomEnchantment;
use imperazim\enchantment\trait\ToggleableTrait;
use pocketmine\player\Player;
use pocketmine\item\Item;
use pocketmine\inventory\Inventory;

class NightVisionEnchantment extends CustomEnchantment {
    use ToggleableTrait;

    public function __construct() {
        $this->name = "night_vision";
        $this->rarity = Rarity::RARE;
        $this->maxLevel = 1;
        $this->displayName = "Night Vision";
        $this->description = "Grants night vision while wearing armor";
        $this->usageType = self::USAGE_HELMET;
        $this->itemType = self::ITEM_HELMET;
    }

    // Override toggle() -- called on equip ($toggle=true) and unequip ($toggle=false)
    public function toggle(
        Player $player,
        Item $item,
        Inventory $inventory,
        int $slot,
        int $level,
        bool $toggle
    ): void {
        if ($toggle) {
            $effect = new EffectInstance(VanillaEffects::NIGHT_VISION(), 2147483647, 0, false);
            $player->getEffects()->add($effect);
        } else {
            $player->getEffects()->remove(VanillaEffects::NIGHT_VISION());
        }
    }
}

// Query stack state (useful when multiple armor pieces have the same enchantment)
$stack = $enchant->getStack($player);       // int: sum of levels across equipped items
$armorStack = $enchant->getArmorStack($player); // int: count of equipped armor pieces with this enchant

imperazim\enchantment\type

Highlights

  • Added ReactiveEnchantment class in imperazim\enchantment\type namespace.
  • Added TickingEnchantment class in imperazim\enchantment\type namespace.
  • Added ToggleableEnchantment class in imperazim\enchantment\type namespace.

Usage

Convenience classes that combine CustomEnchantment with a single trait. Extend these instead of manually applying traits:

use imperazim\enchantment\type\ReactiveEnchantment;
use imperazim\enchantment\type\TickingEnchantment;
use imperazim\enchantment\type\ToggleableEnchantment;

// ReactiveEnchantment = CustomEnchantment + ReactiveTrait
class MyReactive extends ReactiveEnchantment {
    public function react(Player $player, Item $item, Inventory $inventory, int $slot, Event $event, int $level, int $stack): void {
        // handle reaction
    }
}

// TickingEnchantment = CustomEnchantment + TickingTrait
class MyTicking extends TickingEnchantment {
    public function tick(Player $player, Item $item, Inventory $inventory, int $slot, int $level): void {
        // handle tick
    }
}

// ToggleableEnchantment = CustomEnchantment + ToggleableTrait
class MyToggleable extends ToggleableEnchantment {
    public function toggle(Player $player, Item $item, Inventory $inventory, int $slot, int $level, bool $toggle): void {
        // handle equip/unequip
    }
}

imperazim\enchantment\preset

Highlights

  • Added ArmorEffectEnchantment class in imperazim\enchantment\preset namespace.
  • Added AttackerDebuffEnchantment class in imperazim\enchantment\preset namespace.
  • Added DamageMultiplierEnchantment class in imperazim\enchantment\preset namespace.
  • Added ProjectileEnchantment class in imperazim\enchantment\preset namespace.
  • Added WeaponEffectEnchantment class in imperazim\enchantment\preset namespace.

Usage

ArmorEffectEnchantment -- Toggleable enchantment that applies a potion effect while armor is equipped. Handles Jump Boost fall damage immunity, saves/restores pre-existing stronger effects, and removes the effect only when the last armor piece is unequipped:

use imperazim\enchantment\preset\ArmorEffectEnchantment;
use imperazim\enchantment\EnchantmentFactory;
use imperazim\enchantment\CustomEnchantment;
use pocketmine\entity\effect\VanillaEffects;

// Speed boost while wearing any armor piece
$speedArmor = new ArmorEffectEnchantment(
    name: "swiftness",
    effect: VanillaEffects::SPEED(),
    maxLevel: 3,
    usageType: CustomEnchantment::USAGE_ARMOR,
    rarity: Rarity::RARE,
    baseAmplifier: 0,        // amplifier at level 1 = base + multiplier * 1 = 1
    amplifierMultiplier: 1   // each level adds 1 to amplifier
);
EnchantmentFactory::register(EnchantmentFactory::newId(), $speedArmor);

// Jump Boost on boots (automatically grants fall damage immunity)
$jumpBoots = new ArmorEffectEnchantment(
    name: "spring_boots",
    effect: VanillaEffects::JUMP_BOOST(),
    maxLevel: 2,
    usageType: CustomEnchantment::USAGE_BOOTS,
    rarity: Rarity::UNCOMMON,
    baseAmplifier: 1,
    amplifierMultiplier: 1
);
EnchantmentFactory::register(EnchantmentFactory::newId(), $jumpBoots);

// Effect is applied with infinite duration (2147483647 ticks), visible=false
// Pre-existing stronger effects are saved and restored when armor is removed

AttackerDebuffEnchantment -- Reactive armor enchantment that applies debuff effects to the attacker when the wearer is hit. Duration and amplifier scale with enchantment level:

use imperazim\enchantment\preset\AttackerDebuffEnchantment;
use imperazim\enchantment\EnchantmentFactory;
use pocketmine\entity\effect\EffectInstance;
use pocketmine\entity\effect\VanillaEffects;

// Thorns that slow and weaken attackers
$thornsCurse = new AttackerDebuffEnchantment(
    name: "thorns_curse",
    effects: [
        new EffectInstance(VanillaEffects::SLOWNESS(), 60, 0),  // 3s base, amplifier 0
        new EffectInstance(VanillaEffects::WEAKNESS(), 40, 0),  // 2s base, amplifier 0
    ],
    rarity: Rarity::RARE
);
EnchantmentFactory::register(EnchantmentFactory::newId(), $thornsCurse);

// At level 2: duration becomes 60*2=120 ticks (6s) and amplifier becomes 0*2=0
// At level 3: duration becomes 60*3=180 ticks (9s) and amplifier becomes 0*3=0

DamageMultiplierEnchantment -- Reactive enchantment that conditionally multiplies outgoing damage based on a Closure condition. Multiplier scales per level:

use imperazim\enchantment\preset\DamageMultiplierEnchantment;
use imperazim\enchantment\EnchantmentFactory;
use pocketmine\event\entity\EntityDamageByEntityEvent;

// Smite: extra damage vs undead mobs
$smite = new DamageMultiplierEnchantment(
    name: "holy_smite",
    condition: function (EntityDamageByEntityEvent $event): bool {
        $entity = $event->getEntity();
        return $entity instanceof Zombie || $entity instanceof Skeleton;
    },
    multiplierPerLevel: 0.15, // +15% base damage per level
    rarity: Rarity::UNCOMMON
);
EnchantmentFactory::register(EnchantmentFactory::newId(), $smite);

// At level 3: baseDamage * (1 + 0.15 * 3) = baseDamage * 1.45

// Bane of Players: extra damage vs players
$bane = new DamageMultiplierEnchantment(
    name: "bane_of_players",
    condition: function (EntityDamageByEntityEvent $event): bool {
        return $event->getEntity() instanceof Player;
    },
    multiplierPerLevel: 0.1,
    rarity: Rarity::RARE
);
EnchantmentFactory::register(EnchantmentFactory::newId(), $bane);

ProjectileEnchantment -- Reactive enchantment that replaces the default arrow with a custom Projectile subclass on EntityShootBowEvent. Runs at priority 2 (before other bow enchantments) and maintains ProjectileTracker state:

use imperazim\enchantment\preset\ProjectileEnchantment;
use imperazim\enchantment\EnchantmentFactory;

// Replace arrows with a custom explosive arrow projectile
$explosive = new ProjectileEnchantment(
    name: "explosive_arrows",
    projectileClass: ExplosiveArrow::class, // must extend pocketmine Projectile
    rarity: Rarity::MYTHIC
);
EnchantmentFactory::register(EnchantmentFactory::newId(), $explosive);

// When a player shoots with a bow that has this enchantment:
// 1. The default arrow is replaced with an ExplosiveArrow instance
// 2. The source item is registered in ProjectileTracker
// 3. Other enchantments on the bow can still process the new projectile

WeaponEffectEnchantment -- Reactive weapon enchantment that applies effects to the hit target. Duration and amplifier scale with enchantment level:

use imperazim\enchantment\preset\WeaponEffectEnchantment;
use imperazim\enchantment\EnchantmentFactory;
use pocketmine\entity\effect\EffectInstance;
use pocketmine\entity\effect\VanillaEffects;

// Frost sword: slows and blinds targets
$frostBlade = new WeaponEffectEnchantment(
    name: "frost_blade",
    effects: [
        new EffectInstance(VanillaEffects::SLOWNESS(), 80, 1),  // 4s base, amplifier 1
        new EffectInstance(VanillaEffects::BLINDNESS(), 40, 0), // 2s base, amplifier 0
    ],
    rarity: Rarity::RARE
);
EnchantmentFactory::register(EnchantmentFactory::newId(), $frostBlade);

// At level 2: Slowness becomes 80*2=160 ticks (8s) at amplifier 1*2=2
//             Blindness becomes 40*2=80 ticks (4s) at amplifier 0*2=0

imperazim\enchantment\display

Highlights

  • Added EnchantmentDisplay class in imperazim\enchantment\display namespace.
  • Added RomanNumeral class in imperazim\enchantment\display namespace.

Usage

EnchantmentDisplay -- Automatic rarity-colored enchantment rendering on item tooltips via packet interception. Rarity color mapping: COMMON=YELLOW, UNCOMMON=BLUE, RARE=GOLD, MYTHIC=LIGHT_PURPLE:

use imperazim\enchantment\display\EnchantmentDisplay;
use pocketmine\network\mcpe\protocol\types\inventory\ItemStack;

// Display injection is handled automatically by EnchantmentManager's packet interception.
// Outbound InventorySlotPacket / InventoryContentPacket:
//   EnchantmentDisplay::displayEnchants(ItemStack) → ItemStack
//   - Builds multi-line custom name with rarity-colored enchantment names + Roman numeral levels
//   - Saves original display NBT under 'OriginalDisplayTag'

// Inbound InventoryTransactionPacket / MobEquipmentPacket:
//   EnchantmentDisplay::filterDisplayedEnchants(ItemStack) → ItemStack
//   - Reverses displayEnchants, restores original display from 'OriginalDisplayTag'

// Manual usage (if needed):
$displayedStack = EnchantmentDisplay::displayEnchants($itemStack);
$originalStack = EnchantmentDisplay::filterDisplayedEnchants($displayedStack);

RomanNumeral -- Integer to Roman numeral string converter:

use imperazim\enchantment\display\RomanNumeral;

$numeral = RomanNumeral::convert(1);  // "I"
$numeral = RomanNumeral::convert(4);  // "IV"
$numeral = RomanNumeral::convert(5);  // "V"
$numeral = RomanNumeral::convert(10); // "X"

imperazim\enchantment\task

Highlights

  • Added EnchantmentTickTask class in imperazim\enchantment\task namespace.
  • Added DisplayCleanupTask class in imperazim\enchantment\task namespace.

Usage

EnchantmentTickTask -- Runs every 1 tick. Scans all online players' inventory and armor for enchantments with canTick(). Validates usage type, checks tick interval, prevents duplicates (unless supportsMultipleItems() returns true), and calls onTick():

use imperazim\enchantment\task\EnchantmentTickTask;

// Scheduled automatically by LibEnchantment::onEnable() every 1 tick.
// No manual setup required.
// For each online player:
//   For each item in inventory + armor:
//     For each CustomEnchantment on the item where canTick() is true:
//       If usageType matches slot and interval has elapsed:
//         enchantment->onTick(player, item, inventory, slot, level)

DisplayCleanupTask -- Runs every 200 ticks. Safety net that removes orphaned OriginalDisplayTag NBT from items in all players' inventories:

use imperazim\enchantment\task\DisplayCleanupTask;

// Scheduled automatically by LibEnchantment::onEnable() every 200 ticks.
// No manual setup required.
// Cleans up leftover display NBT tags that may remain from edge cases.

imperazim\enchantment\util

Highlights

  • Added EnchantmentUtils class in imperazim\enchantment\util namespace.
  • Added ProjectileTracker class in imperazim\enchantment\util namespace.

Usage

EnchantmentUtils -- Item type checks, enchantment sorting, and fall damage prevention tracking:

use imperazim\enchantment\util\EnchantmentUtils;
use imperazim\enchantment\CustomEnchantment;

// Armor slot checks
EnchantmentUtils::isHelmet($item);      // bool
EnchantmentUtils::isChestplate($item);  // bool
EnchantmentUtils::isLeggings($item);    // bool
EnchantmentUtils::isBoots($item);       // bool

// Check if an item matches any of the 16 ITEM_* type constants
EnchantmentUtils::itemMatchesItemType($item, CustomEnchantment::ITEM_SWORD);   // bool
EnchantmentUtils::itemMatchesItemType($item, CustomEnchantment::ITEM_BOW);     // bool
EnchantmentUtils::itemMatchesItemType($item, CustomEnchantment::ITEM_PICKAXE); // bool
EnchantmentUtils::itemMatchesItemType($item, CustomEnchantment::ITEM_ELYTRA);  // bool
EnchantmentUtils::itemMatchesItemType($item, CustomEnchantment::ITEM_GLOBAL);  // always true

// Sort enchantment instances by priority
$sorted = EnchantmentUtils::sortEnchantmentsByPriority($enchantmentInstances);

// Fall damage prevention (used internally by ArmorEffectEnchantment for Jump Boost)
EnchantmentUtils::setShouldTakeFallDamage($player, false, duration: 1);
$shouldTake = EnchantmentUtils::shouldTakeFallDamage($player);      // bool
$duration = EnchantmentUtils::getNoFallDamageDuration($player);     // int
EnchantmentUtils::increaseNoFallDamageDuration($player, 1);

ProjectileTracker -- Static tracker that maps projectile entity IDs to their source items. Solves the weapon-switch-after-shoot problem by preserving the original bow's enchantments:

use imperazim\enchantment\util\ProjectileTracker;

// Register a projectile (called automatically by EnchantmentManager on ProjectileLaunchEvent)
ProjectileTracker::addProjectile($entity->getId(), $bowItem);

// Check if a projectile is tracked
ProjectileTracker::isTracked($entityId); // bool

// Retrieve source item and its enchantments
$item = ProjectileTracker::getItem($entityId);              // Item|null
$enchants = ProjectileTracker::getEnchantments($entityId);  // EnchantmentInstance[]

// Remove tracking (called when projectile hits or despawns)
ProjectileTracker::removeProjectile($entityId);

imperazim\enchantment\exception

Highlights

  • Added EnchantmentException class in imperazim\enchantment\exception namespace.

Usage

Runtime exception for enchantment-related errors, extending RuntimeException:

use imperazim\enchantment\exception\EnchantmentException;

try {
    EnchantmentFactory::register($id, $enchantment);
} catch (EnchantmentException $e) {
    $logger->warning("Enchantment error: " . $e->getMessage());
}

Licensing information

This project is licensed under MIT. Please see the LICENSE file for details.

EasyLibrary internal package

LibEnchantment now publishes an EasyLibrary 3.x internal package asset for the migration from standalone-only libraries to installable internal packages.

Release assets now include:

  • LibEnchantment-2.0.0.phar
  • LibEnchantment-2.0.0.easylib.zip
  • package.yml
  • checksums.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.

About

Custom enchantment registration and runtime utilities for PocketMine-MP plugins.

Topics

Resources

License

Code of conduct

Contributing

Security policy

Stars

1 star

Watchers

0 watching

Forks

Packages

 
 
 

Contributors

Languages