41    use DestructorCallbackTrait;
 
   47    protected array $shapedRecipes = [];
 
   52    protected array $shapelessRecipes = [];
 
   58    private array $craftingRecipeIndex = [];
 
   64    protected array $furnaceRecipeManagers = [];
 
   70    protected array $potionTypeRecipes = [];
 
   76    protected array $potionContainerChangeRecipes = [];
 
   82    private array $brewingRecipeCache = [];
 
   85    private ObjectSet $recipeRegisteredCallbacks;
 
   87    public function __construct(){
 
   88        $this->recipeRegisteredCallbacks = 
new ObjectSet();
 
   89        foreach(FurnaceType::cases() as $furnaceType){
 
   93        $recipeRegisteredCallbacks = $this->recipeRegisteredCallbacks;
 
   94        foreach($this->furnaceRecipeManagers as $furnaceRecipeManager){
 
   95            $furnaceRecipeManager->getRecipeRegisteredCallbacks()->add(
static function(
FurnaceRecipe $recipe) use ($recipeRegisteredCallbacks) : 
void{
 
   96                foreach($recipeRegisteredCallbacks as $callback){
 
  112        ($retval = $i1->getStateId() <=> $i2->getStateId()) === 0 && ($retval = $i1->getCount() <=> $i2->getCount()) === 0;
 
 
  117    private static function hashOutput(
Item $output) : string{
 
  118        $write = new ByteBufferWriter();
 
  119        VarInt::writeSignedInt($write, $output->getStateId());
 
  124        return $write->getData();
 
  130    private static function hashOutputs(array $outputs) : string{
 
  131        if(count($outputs) === 1){
 
  132            return self::hashOutput(array_shift($outputs));
 
  135        foreach($outputs as $o){
 
  138            $hash = self::hashOutput($o);
 
  139            $unique[$hash] = $hash;
 
  141        ksort($unique, SORT_STRING);
 
  142        return implode(
"", $unique);
 
  150        return $this->shapelessRecipes;
 
 
  158        return $this->shapedRecipes;
 
 
  166        return $this->craftingRecipeIndex;
 
 
  169    public function getCraftingRecipeFromIndex(
int $index) : ?
CraftingRecipe{
 
  170        return $this->craftingRecipeIndex[$index] ?? null;
 
  173    public function getFurnaceRecipeManager(FurnaceType $furnaceType) : FurnaceRecipeManager{
 
  174        return $this->furnaceRecipeManagers[spl_object_id($furnaceType)];
 
  182        return $this->potionTypeRecipes;
 
 
  190        return $this->potionContainerChangeRecipes;
 
 
  193    public function registerShapedRecipe(
ShapedRecipe $recipe) : void{
 
  194        $this->shapedRecipes[self::hashOutputs($recipe->getResults())][] = $recipe;
 
  195        $this->craftingRecipeIndex[] = $recipe;
 
  197        foreach($this->recipeRegisteredCallbacks as $callback){
 
  202    public function registerShapelessRecipe(ShapelessRecipe $recipe) : void{
 
  203        $this->shapelessRecipes[self::hashOutputs($recipe->getResults())][] = $recipe;
 
  204        $this->craftingRecipeIndex[] = $recipe;
 
  206        foreach($this->recipeRegisteredCallbacks as $callback){
 
  211    public function registerPotionTypeRecipe(PotionTypeRecipe $recipe) : void{
 
  212        $this->potionTypeRecipes[] = $recipe;
 
  214        foreach($this->recipeRegisteredCallbacks as $callback){
 
  219    public function registerPotionContainerChangeRecipe(PotionContainerChangeRecipe $recipe) : void{
 
  220        $this->potionContainerChangeRecipes[] = $recipe;
 
  222        foreach($this->recipeRegisteredCallbacks as $callback){
 
  233        $outputHash = self::hashOutputs($outputs);
 
  235        if(isset($this->shapedRecipes[$outputHash])){
 
  236            foreach($this->shapedRecipes[$outputHash] as $recipe){
 
  237                if($recipe->matchesCraftingGrid($grid)){
 
  243        if(isset($this->shapelessRecipes[$outputHash])){
 
  244            foreach($this->shapelessRecipes[$outputHash] as $recipe){
 
  245                if($recipe->matchesCraftingGrid($grid)){
 
 
  263        $outputHash = self::hashOutputs($outputs);
 
  265        if(isset($this->shapedRecipes[$outputHash])){
 
  266            foreach($this->shapedRecipes[$outputHash] as $recipe){
 
  271        if(isset($this->shapelessRecipes[$outputHash])){
 
  272            foreach($this->shapelessRecipes[$outputHash] as $recipe){
 
 
  278    public function matchBrewingRecipe(Item $input, Item $ingredient) : ?BrewingRecipe{
 
  279        $inputHash = $input->getStateId();
 
  280        $ingredientHash = $ingredient->getStateId();
 
  281        $cached = $this->brewingRecipeCache[$inputHash][$ingredientHash] ?? 
null;
 
  282        if($cached !== 
null){
 
  286        foreach($this->potionContainerChangeRecipes as $recipe){
 
  287            if($recipe->getIngredient()->accepts($ingredient) && $recipe->getResultFor($input) !== 
null){
 
  288                return $this->brewingRecipeCache[$inputHash][$ingredientHash] = $recipe;
 
  292        foreach($this->potionTypeRecipes as $recipe){
 
  293            if($recipe->getIngredient()->accepts($ingredient) && $recipe->getResultFor($input) !== 
null){
 
  294                return $this->brewingRecipeCache[$inputHash][$ingredientHash] = $recipe;