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.
This commit is contained in:
Dylan K. Taylor 2022-06-29 16:11:15 +01:00
parent 8831dff61b
commit 5e4e5147d9
No known key found for this signature in database
GPG Key ID: 8927471A91CAFD3D
4 changed files with 86 additions and 53 deletions

View File

@ -29,6 +29,7 @@ use pocketmine\nbt\TreeRoot;
use pocketmine\utils\BinaryStream; use pocketmine\utils\BinaryStream;
use pocketmine\utils\DestructorCallbackTrait; use pocketmine\utils\DestructorCallbackTrait;
use pocketmine\utils\ObjectSet; use pocketmine\utils\ObjectSet;
use function morton2d_encode;
use function usort; use function usort;
class CraftingManager{ class CraftingManager{
@ -47,16 +48,22 @@ class CraftingManager{
/** /**
* @var PotionTypeRecipe[][] * @var PotionTypeRecipe[][]
* @phpstan-var array<string, array<string, PotionTypeRecipe>> * @phpstan-var list<PotionTypeRecipe>
*/ */
protected array $potionTypeRecipes = []; protected array $potionTypeRecipes = [];
/** /**
* @var PotionContainerChangeRecipe[][] * @var PotionContainerChangeRecipe[]
* @phpstan-var array<int, array<string, PotionContainerChangeRecipe>> * @phpstan-var list<PotionContainerChangeRecipe>
*/ */
protected array $potionContainerChangeRecipes = []; protected array $potionContainerChangeRecipes = [];
/**
* @var BrewingRecipe[][]
* @phpstan-var array<int, array<int, BrewingRecipe>>
*/
private array $brewingRecipeCache = [];
/** @phpstan-var ObjectSet<\Closure() : void> */ /** @phpstan-var ObjectSet<\Closure() : void> */
private ObjectSet $recipeRegisteredCallbacks; private ObjectSet $recipeRegisteredCallbacks;
@ -150,16 +157,16 @@ class CraftingManager{
} }
/** /**
* @return PotionTypeRecipe[][] * @return PotionTypeRecipe[]
* @phpstan-return array<string, array<string, PotionTypeRecipe>> * @phpstan-return list<PotionTypeRecipe>
*/ */
public function getPotionTypeRecipes() : array{ public function getPotionTypeRecipes() : array{
return $this->potionTypeRecipes; return $this->potionTypeRecipes;
} }
/** /**
* @return PotionContainerChangeRecipe[][] * @return PotionContainerChangeRecipe[]
* @phpstan-return array<int, array<string, PotionContainerChangeRecipe>> * @phpstan-return list<PotionContainerChangeRecipe>
*/ */
public function getPotionContainerChangeRecipes() : array{ public function getPotionContainerChangeRecipes() : array{
return $this->potionContainerChangeRecipes; return $this->potionContainerChangeRecipes;
@ -182,9 +189,7 @@ class CraftingManager{
} }
public function registerPotionTypeRecipe(PotionTypeRecipe $recipe) : void{ public function registerPotionTypeRecipe(PotionTypeRecipe $recipe) : void{
$input = $recipe->getInput(); $this->potionTypeRecipes[] = $recipe;
$ingredient = $recipe->getIngredient();
$this->potionTypeRecipes[$input->getId() . ":" . $input->getMeta()][$ingredient->getId() . ":" . $ingredient->getMeta()] = $recipe;
foreach($this->recipeRegisteredCallbacks as $callback){ foreach($this->recipeRegisteredCallbacks as $callback){
$callback(); $callback();
@ -192,8 +197,7 @@ class CraftingManager{
} }
public function registerPotionContainerChangeRecipe(PotionContainerChangeRecipe $recipe) : void{ public function registerPotionContainerChangeRecipe(PotionContainerChangeRecipe $recipe) : void{
$ingredient = $recipe->getIngredient(); $this->potionContainerChangeRecipes[] = $recipe;
$this->potionContainerChangeRecipes[$recipe->getInputItemId()][$ingredient->getId() . ":" . $ingredient->getMeta()] = $recipe;
foreach($this->recipeRegisteredCallbacks as $callback){ foreach($this->recipeRegisteredCallbacks as $callback){
$callback(); $callback();
@ -252,7 +256,25 @@ class CraftingManager{
} }
public function matchBrewingRecipe(Item $input, Item $ingredient) : ?BrewingRecipe{ public function matchBrewingRecipe(Item $input, Item $ingredient) : ?BrewingRecipe{
return $this->potionTypeRecipes[$input->getId() . ":" . $input->getMeta()][$ingredient->getId() . ":" . $ingredient->getMeta()] ?? $inputHash = morton2d_encode($input->getId(), $input->getMeta());
$this->potionContainerChangeRecipes[$input->getId()][$ingredient->getId() . ":" . $ingredient->getMeta()] ?? null; $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;
} }
} }

View File

@ -24,9 +24,9 @@ declare(strict_types=1);
namespace pocketmine\crafting; namespace pocketmine\crafting;
use pocketmine\data\bedrock\item\ItemTypeDeserializeException; use pocketmine\data\bedrock\item\ItemTypeDeserializeException;
use pocketmine\data\bedrock\item\upgrade\LegacyItemIdToStringIdMap;
use pocketmine\data\SavedDataLoadingException; use pocketmine\data\SavedDataLoadingException;
use pocketmine\item\Item; use pocketmine\item\Item;
use pocketmine\item\ItemFactory;
use pocketmine\utils\AssumptionFailedError; use pocketmine\utils\AssumptionFailedError;
use pocketmine\utils\Utils; use pocketmine\utils\Utils;
use pocketmine\world\format\io\GlobalItemDataHandlers; use pocketmine\world\format\io\GlobalItemDataHandlers;
@ -172,17 +172,23 @@ final class CraftingManagerFromDataHelper{
} }
foreach($recipes["potion_container_change"] as $recipe){ foreach($recipes["potion_container_change"] as $recipe){
try{ try{
$input = ItemFactory::getInstance()->get($recipe["input_item_id"]);
$ingredient = Item::jsonDeserialize($recipe["ingredient"]); $ingredient = Item::jsonDeserialize($recipe["ingredient"]);
$output = ItemFactory::getInstance()->get($recipe["output_item_id"]);
}catch(SavedDataLoadingException){ }catch(SavedDataLoadingException){
//unknown item //unknown item
continue; 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( $result->registerPotionContainerChangeRecipe(new PotionContainerChangeRecipe(
$input->getId(), $inputId,
$ingredient, $ingredient,
$output->getId() $outputId
)); ));
} }

View File

@ -23,20 +23,21 @@ declare(strict_types=1);
namespace pocketmine\crafting; namespace pocketmine\crafting;
use pocketmine\data\bedrock\item\SavedItemData;
use pocketmine\item\Item; use pocketmine\item\Item;
use pocketmine\item\ItemFactory; use pocketmine\world\format\io\GlobalItemDataHandlers;
class PotionContainerChangeRecipe implements BrewingRecipe{ class PotionContainerChangeRecipe implements BrewingRecipe{
public function __construct( public function __construct(
private int $inputItemId, private string $inputItemId,
private Item $ingredient, private Item $ingredient,
private int $outputItemId private string $outputItemId
){ ){
$this->ingredient = clone $ingredient; $this->ingredient = clone $ingredient;
} }
public function getInputItemId() : int{ public function getInputItemId() : string{
return $this->inputItemId; return $this->inputItemId;
} }
@ -44,11 +45,20 @@ class PotionContainerChangeRecipe implements BrewingRecipe{
return clone $this->ingredient; return clone $this->ingredient;
} }
public function getOutputItemId() : int{ public function getOutputItemId() : string{
return $this->outputItemId; return $this->outputItemId;
} }
public function getResultFor(Item $input) : ?Item{ 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;
} }
} }

View File

@ -28,8 +28,7 @@ use pocketmine\crafting\FurnaceType;
use pocketmine\crafting\RecipeIngredient; use pocketmine\crafting\RecipeIngredient;
use pocketmine\crafting\ShapelessRecipeType; use pocketmine\crafting\ShapelessRecipeType;
use pocketmine\item\Item; use pocketmine\item\Item;
use pocketmine\item\ItemFactory; use pocketmine\network\mcpe\convert\GlobalItemTypeDictionary;
use pocketmine\network\mcpe\convert\ItemTranslator;
use pocketmine\network\mcpe\convert\TypeConverter; use pocketmine\network\mcpe\convert\TypeConverter;
use pocketmine\network\mcpe\protocol\CraftingDataPacket; use pocketmine\network\mcpe\protocol\CraftingDataPacket;
use pocketmine\network\mcpe\protocol\types\inventory\ItemStack; use pocketmine\network\mcpe\protocol\types\inventory\ItemStack;
@ -149,35 +148,31 @@ final class CraftingDataCache{
} }
$potionTypeRecipes = []; $potionTypeRecipes = [];
foreach($manager->getPotionTypeRecipes() as $recipes){ foreach($manager->getPotionTypeRecipes() as $recipe){
foreach($recipes as $recipe){ $input = $converter->coreItemStackToNet($recipe->getInput());
$input = $converter->coreItemStackToNet($recipe->getInput()); $ingredient = $converter->coreItemStackToNet($recipe->getIngredient());
$ingredient = $converter->coreItemStackToNet($recipe->getIngredient()); $output = $converter->coreItemStackToNet($recipe->getOutput());
$output = $converter->coreItemStackToNet($recipe->getOutput()); $potionTypeRecipes[] = new ProtocolPotionTypeRecipe(
$potionTypeRecipes[] = new ProtocolPotionTypeRecipe( $input->getId(),
$input->getId(), $input->getMeta(),
$input->getMeta(), $ingredient->getId(),
$ingredient->getId(), $ingredient->getMeta(),
$ingredient->getMeta(), $output->getId(),
$output->getId(), $output->getMeta()
$output->getMeta() );
);
}
} }
$potionContainerChangeRecipes = []; $potionContainerChangeRecipes = [];
$itemTranslator = ItemTranslator::getInstance(); $itemTypeDictionary = GlobalItemTypeDictionary::getInstance()->getDictionary();
foreach($manager->getPotionContainerChangeRecipes() as $recipes){ foreach($manager->getPotionContainerChangeRecipes() as $recipe){
foreach($recipes as $recipe){ $input = $itemTypeDictionary->fromStringId($recipe->getInputItemId());
$input = $itemTranslator->toNetworkId(ItemFactory::getInstance()->get($recipe->getInputItemId(), 0)); $ingredient = $converter->coreItemStackToNet($recipe->getIngredient());
$ingredient = $itemTranslator->toNetworkId($recipe->getIngredient()); $output = $itemTypeDictionary->fromStringId($recipe->getOutputItemId());
$output = $itemTranslator->toNetworkId(ItemFactory::getInstance()->get($recipe->getOutputItemId(), 0)); $potionContainerChangeRecipes[] = new ProtocolPotionContainerChangeRecipe(
$potionContainerChangeRecipes[] = new ProtocolPotionContainerChangeRecipe( $input,
$input[0], $ingredient->getId(),
$ingredient[0], $output
$output[0] );
);
}
} }
Timings::$craftingDataCacheRebuild->stopTiming(); Timings::$craftingDataCacheRebuild->stopTiming();