From 47976bac34bb03453ea3258d35b1bce808ee3a87 Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Sat, 24 Oct 2020 17:59:46 +0100 Subject: [PATCH] Moved enchantment ID handling to pocketmine/data/bedrock package this permits plugins to register their own enchantments mapped to MCPE IDs again. --- src/data/bedrock/EnchantmentIdMap.php | 95 +++++++++++++++++++ src/item/Item.php | 6 +- src/item/ItemEnchantmentHandlingTrait.php | 8 +- src/item/enchantment/Enchantment.php | 13 +-- src/item/enchantment/EnchantmentInstance.php | 7 +- .../enchantment/ProtectionEnchantment.php | 4 +- src/item/enchantment/VanillaEnchantments.php | 17 ---- .../data/bedrock/EnchantmentIdMapTest.php | 38 ++++++++ 8 files changed, 153 insertions(+), 35 deletions(-) create mode 100644 src/data/bedrock/EnchantmentIdMap.php create mode 100644 tests/phpunit/data/bedrock/EnchantmentIdMapTest.php diff --git a/src/data/bedrock/EnchantmentIdMap.php b/src/data/bedrock/EnchantmentIdMap.php new file mode 100644 index 000000000..6b7c8bd19 --- /dev/null +++ b/src/data/bedrock/EnchantmentIdMap.php @@ -0,0 +1,95 @@ + + */ + private $idToEnch = []; + /** + * @var int[] + * @phpstan-var array + */ + private $enchToId = []; + + private function __construct(){ + $this->register(EnchantmentIds::PROTECTION, VanillaEnchantments::PROTECTION()); + $this->register(EnchantmentIds::FIRE_PROTECTION, VanillaEnchantments::FIRE_PROTECTION()); + $this->register(EnchantmentIds::FEATHER_FALLING, VanillaEnchantments::FEATHER_FALLING()); + $this->register(EnchantmentIds::BLAST_PROTECTION, VanillaEnchantments::BLAST_PROTECTION()); + $this->register(EnchantmentIds::PROJECTILE_PROTECTION, VanillaEnchantments::PROJECTILE_PROTECTION()); + $this->register(EnchantmentIds::THORNS, VanillaEnchantments::THORNS()); + $this->register(EnchantmentIds::RESPIRATION, VanillaEnchantments::RESPIRATION()); + + $this->register(EnchantmentIds::SHARPNESS, VanillaEnchantments::SHARPNESS()); + //TODO: smite, bane of arthropods (these don't make sense now because their applicable mobs don't exist yet) + + $this->register(EnchantmentIds::KNOCKBACK, VanillaEnchantments::KNOCKBACK()); + $this->register(EnchantmentIds::FIRE_ASPECT, VanillaEnchantments::FIRE_ASPECT()); + + $this->register(EnchantmentIds::EFFICIENCY, VanillaEnchantments::EFFICIENCY()); + $this->register(EnchantmentIds::SILK_TOUCH, VanillaEnchantments::SILK_TOUCH()); + $this->register(EnchantmentIds::UNBREAKING, VanillaEnchantments::UNBREAKING()); + + $this->register(EnchantmentIds::POWER, VanillaEnchantments::POWER()); + $this->register(EnchantmentIds::PUNCH, VanillaEnchantments::PUNCH()); + $this->register(EnchantmentIds::FLAME, VanillaEnchantments::FLAME()); + $this->register(EnchantmentIds::INFINITY, VanillaEnchantments::INFINITY()); + + $this->register(EnchantmentIds::MENDING, VanillaEnchantments::MENDING()); + + $this->register(EnchantmentIds::VANISHING, VanillaEnchantments::VANISHING()); + } + + public function register(int $mcpeId, Enchantment $enchantment) : void{ + $this->idToEnch[$mcpeId] = $enchantment; + $this->enchToId[$enchantment->getRuntimeId()] = $mcpeId; + } + + public function fromId(int $id) : ?Enchantment{ + //we might not have all the enchantment IDs registered + return $this->idToEnch[$id] ?? null; + } + + public function toId(Enchantment $enchantment) : int{ + if(!array_key_exists($enchantment->getRuntimeId(), $this->enchToId)){ + //this should never happen, so we treat it as an exceptional condition + throw new \InvalidArgumentException("Enchantment does not have a mapped ID"); + } + return $this->enchToId[$enchantment->getRuntimeId()]; + } +} diff --git a/src/item/Item.php b/src/item/Item.php index 7228c15cd..10f270cce 100644 --- a/src/item/Item.php +++ b/src/item/Item.php @@ -31,9 +31,9 @@ use pocketmine\block\Block; use pocketmine\block\BlockBreakInfo; use pocketmine\block\BlockToolType; use pocketmine\block\VanillaBlocks; +use pocketmine\data\bedrock\EnchantmentIdMap; use pocketmine\entity\Entity; use pocketmine\item\enchantment\EnchantmentInstance; -use pocketmine\item\enchantment\VanillaEnchantments; use pocketmine\math\Vector3; use pocketmine\nbt\LittleEndianNbtSerializer; use pocketmine\nbt\NBT; @@ -285,7 +285,7 @@ class Item implements \JsonSerializable{ if($level <= 0){ continue; } - $type = VanillaEnchantments::byMcpeId($magicNumber); + $type = EnchantmentIdMap::getInstance()->fromId($magicNumber); if($type !== null){ $this->addEnchantment(new EnchantmentInstance($type, $level)); } @@ -336,7 +336,7 @@ class Item implements \JsonSerializable{ $ench = new ListTag(); foreach($this->getEnchantments() as $enchantmentInstance){ $ench->push(CompoundTag::create() - ->setShort("id", $enchantmentInstance->getType()->getId()) + ->setShort("id", EnchantmentIdMap::getInstance()->toId($enchantmentInstance->getType())) ->setShort("lvl", $enchantmentInstance->getLevel()) ); } diff --git a/src/item/ItemEnchantmentHandlingTrait.php b/src/item/ItemEnchantmentHandlingTrait.php index 9ef8c6cee..8d44f0a07 100644 --- a/src/item/ItemEnchantmentHandlingTrait.php +++ b/src/item/ItemEnchantmentHandlingTrait.php @@ -40,12 +40,12 @@ trait ItemEnchantmentHandlingTrait{ } public function hasEnchantment(Enchantment $enchantment, int $level = -1) : bool{ - $id = $enchantment->getId(); + $id = $enchantment->getRuntimeId(); return isset($this->enchantments[$id]) and ($level === -1 or $this->enchantments[$id]->getLevel() === $level); } public function getEnchantment(Enchantment $enchantment) : ?EnchantmentInstance{ - return $this->enchantments[$enchantment->getId()] ?? null; + return $this->enchantments[$enchantment->getRuntimeId()] ?? null; } /** @@ -54,7 +54,7 @@ trait ItemEnchantmentHandlingTrait{ public function removeEnchantment(Enchantment $enchantment, int $level = -1) : self{ $instance = $this->getEnchantment($enchantment); if($instance !== null and ($level === -1 or $instance->getLevel() === $level)){ - unset($this->enchantments[$enchantment->getId()]); + unset($this->enchantments[$enchantment->getRuntimeId()]); } return $this; @@ -72,7 +72,7 @@ trait ItemEnchantmentHandlingTrait{ * @return $this */ public function addEnchantment(EnchantmentInstance $enchantment) : self{ - $this->enchantments[$enchantment->getId()] = $enchantment; + $this->enchantments[$enchantment->getRuntimeId()] = $enchantment; return $this; } diff --git a/src/item/enchantment/Enchantment.php b/src/item/enchantment/Enchantment.php index f2a9101da..c4081af96 100644 --- a/src/item/enchantment/Enchantment.php +++ b/src/item/enchantment/Enchantment.php @@ -31,7 +31,7 @@ use function constant; class Enchantment{ /** @var int */ - private $id; + private $internalRuntimeId; /** @var string */ private $name; /** @var int */ @@ -43,8 +43,8 @@ class Enchantment{ /** @var int */ private $maxLevel; - public function __construct(int $id, string $name, int $rarity, int $primaryItemFlags, int $secondaryItemFlags, int $maxLevel){ - $this->id = $id; + public function __construct(int $internalRuntimeId, string $name, int $rarity, int $primaryItemFlags, int $secondaryItemFlags, int $maxLevel){ + $this->internalRuntimeId = $internalRuntimeId; $this->name = $name; $this->rarity = $rarity; $this->primaryItemFlags = $primaryItemFlags; @@ -53,10 +53,11 @@ class Enchantment{ } /** - * Returns the ID of this enchantment as per Minecraft PE + * Returns the internal runtime ID of this enchantment. + * WARNING: DO NOT STORE THIS IDENTIFIER - IT MAY CHANGE AFTER RESTART */ - public function getId() : int{ - return $this->id; + public function getRuntimeId() : int{ + return $this->internalRuntimeId; } /** diff --git a/src/item/enchantment/EnchantmentInstance.php b/src/item/enchantment/EnchantmentInstance.php index f25c05d75..443c33913 100644 --- a/src/item/enchantment/EnchantmentInstance.php +++ b/src/item/enchantment/EnchantmentInstance.php @@ -53,10 +53,11 @@ final class EnchantmentInstance{ } /** - * Returns the type identifier of this enchantment instance. + * Returns the runtime type identifier of this enchantment instance. + * WARNING: DO NOT STORE THIS IDENTIFIER - IT MAY CHANGE AFTER SERVER RESTART */ - public function getId() : int{ - return $this->enchantment->getId(); + public function getRuntimeId() : int{ + return $this->enchantment->getRuntimeId(); } /** diff --git a/src/item/enchantment/ProtectionEnchantment.php b/src/item/enchantment/ProtectionEnchantment.php index 8fc603aa9..141678b07 100644 --- a/src/item/enchantment/ProtectionEnchantment.php +++ b/src/item/enchantment/ProtectionEnchantment.php @@ -38,8 +38,8 @@ class ProtectionEnchantment extends Enchantment{ * * @param int[]|null $applicableDamageTypes EntityDamageEvent::CAUSE_* constants which this enchantment type applies to, or null if it applies to all types of damage. */ - public function __construct(int $id, string $name, int $rarity, int $primaryItemFlags, int $secondaryItemFlags, int $maxLevel, float $typeModifier, ?array $applicableDamageTypes){ - parent::__construct($id, $name, $rarity, $primaryItemFlags, $secondaryItemFlags, $maxLevel); + public function __construct(int $internalRuntimeId, string $name, int $rarity, int $primaryItemFlags, int $secondaryItemFlags, int $maxLevel, float $typeModifier, ?array $applicableDamageTypes){ + parent::__construct($internalRuntimeId, $name, $rarity, $primaryItemFlags, $secondaryItemFlags, $maxLevel); $this->typeModifier = $typeModifier; if($applicableDamageTypes !== null){ diff --git a/src/item/enchantment/VanillaEnchantments.php b/src/item/enchantment/VanillaEnchantments.php index eca5f7abb..965b7316c 100644 --- a/src/item/enchantment/VanillaEnchantments.php +++ b/src/item/enchantment/VanillaEnchantments.php @@ -25,7 +25,6 @@ namespace pocketmine\item\enchantment; use pocketmine\event\entity\EntityDamageEvent; use pocketmine\utils\RegistryTrait; -use function array_key_exists; /** * This doc-block is generated automatically, do not modify it manually. @@ -55,12 +54,6 @@ use function array_key_exists; final class VanillaEnchantments{ use RegistryTrait; - /** - * @var Enchantment[] - * @phpstan-var array - */ - private static $mcpeIdMap = []; - protected static function setup() : void{ self::register("PROTECTION", new ProtectionEnchantment(EnchantmentIds::PROTECTION, "%enchantment.protect.all", Rarity::COMMON, ItemFlags::ARMOR, ItemFlags::NONE, 4, 0.75, null)); self::register("FIRE_PROTECTION", new ProtectionEnchantment(EnchantmentIds::FIRE_PROTECTION, "%enchantment.protect.fire", Rarity::UNCOMMON, ItemFlags::ARMOR, ItemFlags::NONE, 4, 1.25, [ @@ -103,17 +96,7 @@ final class VanillaEnchantments{ } protected static function register(string $name, Enchantment $member) : void{ - if(array_key_exists($member->getId(), self::$mcpeIdMap)){ - throw new \InvalidArgumentException("MCPE enchantment ID " . $member->getId() . " is already assigned"); - } self::_registryRegister($name, $member); - self::$mcpeIdMap[$member->getId()] = $member; - } - - public static function byMcpeId(int $id) : ?Enchantment{ - //TODO: this shouldn't be in here, it's unnecessarily limiting - self::checkInit(); - return self::$mcpeIdMap[$id] ?? null; } /** diff --git a/tests/phpunit/data/bedrock/EnchantmentIdMapTest.php b/tests/phpunit/data/bedrock/EnchantmentIdMapTest.php new file mode 100644 index 000000000..eb1f35381 --- /dev/null +++ b/tests/phpunit/data/bedrock/EnchantmentIdMapTest.php @@ -0,0 +1,38 @@ +toId($enchantment); + $enchantment2 = EnchantmentIdMap::getInstance()->fromId($id); + self::assertTrue($enchantment === $enchantment2); + } + } +}