From 5e4e5147d90a249317be0355b5bd09c9efddc6bb Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Wed, 29 Jun 2022 16:11:15 +0100 Subject: [PATCH] Remove ItemFactory involvement from brewing this involves a very nasty hack for potion container change, but for the time being it can't be helped. --- src/crafting/CraftingManager.php | 50 +++++++++++++----- .../CraftingManagerFromDataHelper.php | 16 ++++-- src/crafting/PotionContainerChangeRecipe.php | 22 +++++--- src/network/mcpe/cache/CraftingDataCache.php | 51 +++++++++---------- 4 files changed, 86 insertions(+), 53 deletions(-) diff --git a/src/crafting/CraftingManager.php b/src/crafting/CraftingManager.php index f23ba53ac..516cf385b 100644 --- a/src/crafting/CraftingManager.php +++ b/src/crafting/CraftingManager.php @@ -29,6 +29,7 @@ use pocketmine\nbt\TreeRoot; use pocketmine\utils\BinaryStream; use pocketmine\utils\DestructorCallbackTrait; use pocketmine\utils\ObjectSet; +use function morton2d_encode; use function usort; class CraftingManager{ @@ -47,16 +48,22 @@ class CraftingManager{ /** * @var PotionTypeRecipe[][] - * @phpstan-var array> + * @phpstan-var list */ protected array $potionTypeRecipes = []; /** - * @var PotionContainerChangeRecipe[][] - * @phpstan-var array> + * @var PotionContainerChangeRecipe[] + * @phpstan-var list */ protected array $potionContainerChangeRecipes = []; + /** + * @var BrewingRecipe[][] + * @phpstan-var array> + */ + private array $brewingRecipeCache = []; + /** @phpstan-var ObjectSet<\Closure() : void> */ private ObjectSet $recipeRegisteredCallbacks; @@ -150,16 +157,16 @@ class CraftingManager{ } /** - * @return PotionTypeRecipe[][] - * @phpstan-return array> + * @return PotionTypeRecipe[] + * @phpstan-return list */ public function getPotionTypeRecipes() : array{ return $this->potionTypeRecipes; } /** - * @return PotionContainerChangeRecipe[][] - * @phpstan-return array> + * @return PotionContainerChangeRecipe[] + * @phpstan-return list */ public function getPotionContainerChangeRecipes() : array{ return $this->potionContainerChangeRecipes; @@ -182,9 +189,7 @@ class CraftingManager{ } public function registerPotionTypeRecipe(PotionTypeRecipe $recipe) : void{ - $input = $recipe->getInput(); - $ingredient = $recipe->getIngredient(); - $this->potionTypeRecipes[$input->getId() . ":" . $input->getMeta()][$ingredient->getId() . ":" . $ingredient->getMeta()] = $recipe; + $this->potionTypeRecipes[] = $recipe; foreach($this->recipeRegisteredCallbacks as $callback){ $callback(); @@ -192,8 +197,7 @@ class CraftingManager{ } public function registerPotionContainerChangeRecipe(PotionContainerChangeRecipe $recipe) : void{ - $ingredient = $recipe->getIngredient(); - $this->potionContainerChangeRecipes[$recipe->getInputItemId()][$ingredient->getId() . ":" . $ingredient->getMeta()] = $recipe; + $this->potionContainerChangeRecipes[] = $recipe; foreach($this->recipeRegisteredCallbacks as $callback){ $callback(); @@ -252,7 +256,25 @@ class CraftingManager{ } public function matchBrewingRecipe(Item $input, Item $ingredient) : ?BrewingRecipe{ - return $this->potionTypeRecipes[$input->getId() . ":" . $input->getMeta()][$ingredient->getId() . ":" . $ingredient->getMeta()] ?? - $this->potionContainerChangeRecipes[$input->getId()][$ingredient->getId() . ":" . $ingredient->getMeta()] ?? null; + $inputHash = morton2d_encode($input->getId(), $input->getMeta()); + $ingredientHash = morton2d_encode($ingredient->getId(), $ingredient->getMeta()); + $recipe = $this->brewingRecipeCache[$inputHash][$ingredientHash] ?? null; + if($recipe !== null){ + return $recipe; + } + + foreach($this->potionContainerChangeRecipes as $recipe){ + if($recipe->getIngredient()->equals($ingredient) && $recipe->getResultFor($input) !== null){ + return $this->brewingRecipeCache[$inputHash][$ingredientHash] = $recipe; + } + } + + foreach($this->potionTypeRecipes as $recipe){ + if($recipe->getIngredient()->equals($ingredient) && $recipe->getResultFor($input) !== null){ + return $this->brewingRecipeCache[$inputHash][$ingredientHash] = $recipe; + } + } + + return null; } } diff --git a/src/crafting/CraftingManagerFromDataHelper.php b/src/crafting/CraftingManagerFromDataHelper.php index d4bb14b3d..455e0315e 100644 --- a/src/crafting/CraftingManagerFromDataHelper.php +++ b/src/crafting/CraftingManagerFromDataHelper.php @@ -24,9 +24,9 @@ declare(strict_types=1); namespace pocketmine\crafting; use pocketmine\data\bedrock\item\ItemTypeDeserializeException; +use pocketmine\data\bedrock\item\upgrade\LegacyItemIdToStringIdMap; use pocketmine\data\SavedDataLoadingException; use pocketmine\item\Item; -use pocketmine\item\ItemFactory; use pocketmine\utils\AssumptionFailedError; use pocketmine\utils\Utils; use pocketmine\world\format\io\GlobalItemDataHandlers; @@ -172,17 +172,23 @@ final class CraftingManagerFromDataHelper{ } foreach($recipes["potion_container_change"] as $recipe){ try{ - $input = ItemFactory::getInstance()->get($recipe["input_item_id"]); $ingredient = Item::jsonDeserialize($recipe["ingredient"]); - $output = ItemFactory::getInstance()->get($recipe["output_item_id"]); }catch(SavedDataLoadingException){ //unknown item continue; } + + //TODO: we'll be able to get rid of these conversions once the crafting data is updated + $inputId = LegacyItemIdToStringIdMap::getInstance()->legacyToString($recipe["input_item_id"]); + $outputId = LegacyItemIdToStringIdMap::getInstance()->legacyToString($recipe["output_item_id"]); + if($inputId === null || $outputId === null){ + //unknown item + continue; + } $result->registerPotionContainerChangeRecipe(new PotionContainerChangeRecipe( - $input->getId(), + $inputId, $ingredient, - $output->getId() + $outputId )); } diff --git a/src/crafting/PotionContainerChangeRecipe.php b/src/crafting/PotionContainerChangeRecipe.php index da24050cb..d358c28e2 100644 --- a/src/crafting/PotionContainerChangeRecipe.php +++ b/src/crafting/PotionContainerChangeRecipe.php @@ -23,20 +23,21 @@ declare(strict_types=1); namespace pocketmine\crafting; +use pocketmine\data\bedrock\item\SavedItemData; use pocketmine\item\Item; -use pocketmine\item\ItemFactory; +use pocketmine\world\format\io\GlobalItemDataHandlers; class PotionContainerChangeRecipe implements BrewingRecipe{ public function __construct( - private int $inputItemId, + private string $inputItemId, private Item $ingredient, - private int $outputItemId + private string $outputItemId ){ $this->ingredient = clone $ingredient; } - public function getInputItemId() : int{ + public function getInputItemId() : string{ return $this->inputItemId; } @@ -44,11 +45,20 @@ class PotionContainerChangeRecipe implements BrewingRecipe{ return clone $this->ingredient; } - public function getOutputItemId() : int{ + public function getOutputItemId() : string{ return $this->outputItemId; } public function getResultFor(Item $input) : ?Item{ - return $input->getId() === $this->getInputItemId() ? ItemFactory::getInstance()->get($this->getOutputItemId(), $input->getMeta()) : null; + //TODO: this is a really awful hack, but there isn't another way for now + //this relies on transforming the serialized item's ID, relying on the target item type's data being the same as the input. + //This is the same assumption previously made using ItemFactory::get(), except it was less obvious how bad it was. + //The other way is to bake the actual Potion class types into here, which isn't great for data-driving stuff. + //We need a better solution for this. + + $data = GlobalItemDataHandlers::getSerializer()->serializeType($input); + return $data->getName() === $this->getInputItemId() ? + GlobalItemDataHandlers::getDeserializer()->deserializeType(new SavedItemData($this->getOutputItemId(), $data->getMeta(), $data->getBlock(), $data->getTag())) : + null; } } diff --git a/src/network/mcpe/cache/CraftingDataCache.php b/src/network/mcpe/cache/CraftingDataCache.php index 8c1cd532d..6d86b8555 100644 --- a/src/network/mcpe/cache/CraftingDataCache.php +++ b/src/network/mcpe/cache/CraftingDataCache.php @@ -28,8 +28,7 @@ use pocketmine\crafting\FurnaceType; use pocketmine\crafting\RecipeIngredient; use pocketmine\crafting\ShapelessRecipeType; use pocketmine\item\Item; -use pocketmine\item\ItemFactory; -use pocketmine\network\mcpe\convert\ItemTranslator; +use pocketmine\network\mcpe\convert\GlobalItemTypeDictionary; use pocketmine\network\mcpe\convert\TypeConverter; use pocketmine\network\mcpe\protocol\CraftingDataPacket; use pocketmine\network\mcpe\protocol\types\inventory\ItemStack; @@ -149,35 +148,31 @@ final class CraftingDataCache{ } $potionTypeRecipes = []; - foreach($manager->getPotionTypeRecipes() as $recipes){ - foreach($recipes as $recipe){ - $input = $converter->coreItemStackToNet($recipe->getInput()); - $ingredient = $converter->coreItemStackToNet($recipe->getIngredient()); - $output = $converter->coreItemStackToNet($recipe->getOutput()); - $potionTypeRecipes[] = new ProtocolPotionTypeRecipe( - $input->getId(), - $input->getMeta(), - $ingredient->getId(), - $ingredient->getMeta(), - $output->getId(), - $output->getMeta() - ); - } + foreach($manager->getPotionTypeRecipes() as $recipe){ + $input = $converter->coreItemStackToNet($recipe->getInput()); + $ingredient = $converter->coreItemStackToNet($recipe->getIngredient()); + $output = $converter->coreItemStackToNet($recipe->getOutput()); + $potionTypeRecipes[] = new ProtocolPotionTypeRecipe( + $input->getId(), + $input->getMeta(), + $ingredient->getId(), + $ingredient->getMeta(), + $output->getId(), + $output->getMeta() + ); } $potionContainerChangeRecipes = []; - $itemTranslator = ItemTranslator::getInstance(); - foreach($manager->getPotionContainerChangeRecipes() as $recipes){ - foreach($recipes as $recipe){ - $input = $itemTranslator->toNetworkId(ItemFactory::getInstance()->get($recipe->getInputItemId(), 0)); - $ingredient = $itemTranslator->toNetworkId($recipe->getIngredient()); - $output = $itemTranslator->toNetworkId(ItemFactory::getInstance()->get($recipe->getOutputItemId(), 0)); - $potionContainerChangeRecipes[] = new ProtocolPotionContainerChangeRecipe( - $input[0], - $ingredient[0], - $output[0] - ); - } + $itemTypeDictionary = GlobalItemTypeDictionary::getInstance()->getDictionary(); + foreach($manager->getPotionContainerChangeRecipes() as $recipe){ + $input = $itemTypeDictionary->fromStringId($recipe->getInputItemId()); + $ingredient = $converter->coreItemStackToNet($recipe->getIngredient()); + $output = $itemTypeDictionary->fromStringId($recipe->getOutputItemId()); + $potionContainerChangeRecipes[] = new ProtocolPotionContainerChangeRecipe( + $input, + $ingredient->getId(), + $output + ); } Timings::$craftingDataCacheRebuild->stopTiming();