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\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<string, array<string, PotionTypeRecipe>>
* @phpstan-var list<PotionTypeRecipe>
*/
protected array $potionTypeRecipes = [];
/**
* @var PotionContainerChangeRecipe[][]
* @phpstan-var array<int, array<string, PotionContainerChangeRecipe>>
* @var PotionContainerChangeRecipe[]
* @phpstan-var list<PotionContainerChangeRecipe>
*/
protected array $potionContainerChangeRecipes = [];
/**
* @var BrewingRecipe[][]
* @phpstan-var array<int, array<int, BrewingRecipe>>
*/
private array $brewingRecipeCache = [];
/** @phpstan-var ObjectSet<\Closure() : void> */
private ObjectSet $recipeRegisteredCallbacks;
@ -150,16 +157,16 @@ class CraftingManager{
}
/**
* @return PotionTypeRecipe[][]
* @phpstan-return array<string, array<string, PotionTypeRecipe>>
* @return PotionTypeRecipe[]
* @phpstan-return list<PotionTypeRecipe>
*/
public function getPotionTypeRecipes() : array{
return $this->potionTypeRecipes;
}
/**
* @return PotionContainerChangeRecipe[][]
* @phpstan-return array<int, array<string, PotionContainerChangeRecipe>>
* @return PotionContainerChangeRecipe[]
* @phpstan-return list<PotionContainerChangeRecipe>
*/
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;
}
}

View File

@ -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
));
}

View File

@ -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;
}
}

View File

@ -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();