// Path: src/main/java/net/darkhax/darkutils/DarkUtils.java
// @Mod(DarkUtils.MOD_ID)
// public class DarkUtils {
//     
//     public static final String MOD_ID = "darkutils";
//     
//     public static final String MOD_NAME = "Dark Utilities";
//     
//     public static final Logger LOG = LogManager.getLogger(MOD_NAME);
//     
//     public static final ItemGroup ITEM_GROUP = new ItemGroupBase(MOD_ID, () -> new ItemStack(DarkUtils.content.vectorPlate));
//     
//     public static final NetworkHelper NETWORK = new NetworkHelper(new ResourceLocation(MOD_ID, "main"), "2.0.X");
//     
//     public static RegistryHelper registry;
//     
//     public static Content content;
//     
//     public static AddonManager addons;
//     
//     public DarkUtils() {
//         
//         registry = DistExecutor.runForDist( () -> () -> new RegistryHelperClient(MOD_ID, LOG, ITEM_GROUP), () -> () -> new RegistryHelper(MOD_ID, LOG, ITEM_GROUP));
//         content = DistExecutor.runForDist( () -> () -> new ContentClient(registry), () -> () -> new Content(registry));
//         NETWORK.registerEnqueuedMessage(MessageSyncCrucibleType.class, NetworkHandlerServer::encodeStageMessage, t -> NetworkHandlerClient.decodeStageMessage(t), (t, u) -> NetworkHandlerClient.processSyncStagesMessage(t, u));
//         registry.initialize(FMLJavaModLoadingContext.get().getModEventBus());
//         
//         // Addons
//         addons = new AddonManager();
//     }
// }
// 
// Path: src/main/java/net/darkhax/darkutils/features/slimecrucible/SlimeCrucibleEvents.java
// public static class RecipeCraftedEvent extends Event {
//         
//     /**
//      * The recipe being crafted.
//      */
//     private final RecipeSlimeCrafting recipe;
//         
//     /**
//      * The player crafting the recipe.
//      */
//     private final PlayerEntity player;
//         
//     /**
//      * The world instance.
//      */
//     private final World world;
//         
//     /**
//      * The position of the slime crucible.
//      */
//     private final BlockPos pos;
//         
//     /**
//      * The original output item before modifications.
//      */
//     private final ItemStack originalOutput;
//         
//     /**
//      * The output item that will be used.
//      */
//     private ItemStack output;
//         
//     public RecipeCraftedEvent(RecipeSlimeCrafting recipe, PlayerEntity player, World world, BlockPos pos, ItemStack originalOutput) {
//             
//         this.recipe = recipe;
//         this.player = player;
//         this.world = world;
//         this.pos = pos;
//         this.originalOutput = originalOutput;
//         this.output = originalOutput.copy();
//     }
//         
//     /**
//      * Gets the stack instance that will actually be created.
//      * 
//      * @return The actual recipe output.
//      */
//     public ItemStack getOutput () {
//             
//         return this.output;
//     }
//         
//     /**
//      * Sets the output to a different item stack.
//      * 
//      * @param output The new output stack.
//      */
//     public void setOutput (ItemStack output) {
//             
//         this.output = output;
//     }
//         
//     /**
//      * Gets the recipe being crafted.
//      * 
//      * @return The recipe being crafted.
//      */
//     public RecipeSlimeCrafting getRecipe () {
//             
//         return this.recipe;
//     }
//         
//     /**
//      * Gets the player crafting the recipe.
//      * 
//      * @return The player crafting the recipe.
//      */
//     public PlayerEntity getPlayer () {
//             
//         return this.player;
//     }
//         
//     /**
//      * Gets the world instance.
//      * 
//      * @return The world instance.
//      */
//     public World getWorld () {
//             
//         return this.world;
//     }
//         
//     /**
//      * Gets the position of the crucible block.
//      * 
//      * @return The position of the crucible block.
//      */
//     public BlockPos getPos () {
//             
//         return this.pos;
//     }
//         
//     /**
//      * Gets the original and unmodified output stack.
//      * 
//      * @return The original and unmodified output stack.
//      */
//     public ItemStack getOriginalOutput () {
//             
//         return this.originalOutput;
//     }
// }


import java.util.Collection;
import java.util.Comparator;
import java.util.List;
import java.util.function.Consumer;
import java.util.stream.Collectors;
import javax.annotation.Nullable;
import com.google.common.collect.Lists;
import net.darkhax.bookshelf.inventory.InventoryListenable;
import net.darkhax.bookshelf.inventory.SlotOutput;
import net.darkhax.bookshelf.util.WorldUtils;
import net.darkhax.darkutils.DarkUtils;
import net.darkhax.darkutils.features.slimecrucible.SlimeCrucibleEvents.RecipeCraftedEvent;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.entity.player.PlayerInventory;
import net.minecraft.entity.player.ServerPlayerEntity;
import net.minecraft.inventory.CraftResultInventory;
import net.minecraft.inventory.IInventory;
import net.minecraft.inventory.container.Container;
import net.minecraft.inventory.container.ContainerType;
import net.minecraft.inventory.container.Slot;
import net.minecraft.item.Item;
import net.minecraft.item.ItemStack;
import net.minecraft.tileentity.TileEntity;
import net.minecraft.util.IWorldPosCallable;
import net.minecraft.util.IntReferenceHolder;
import net.minecraft.util.SoundCategory;
import net.minecraft.util.math.BlockPos;
import net.minecraft.world.World;
import net.minecraftforge.common.MinecraftForge;
        this.detectAndSendChanges();
    }
    
    /**
     * Gets the recipe that has been selected by the user.
     * 
     * @return The current selected recipe.
     */
    public RecipeSlimeCrafting getSelectedRecipe () {
        
        return this.availableRecipes.get(this.getSelectedRecipeId());
    }
    
    private ItemStack tempCraftingStack = ItemStack.EMPTY;
    
    /**
     * Handles the output slot being changed by the player.
     * 
     * @param player The player who changed the slot.
     * @param stack The newly created ItemStack.
     */
    private ItemStack onOutputSlotChanged (PlayerEntity player, ItemStack stack) {
        
        final RecipeSlimeCrafting recipe = this.getSelectedRecipe();
        final ItemStack inputStack = this.slotInput.decrStackSize(recipe.getInputCount());
        final ItemStack outputCopy = stack;
        
        // Remove the consumed slime points from the tile and from the container.
        this.worldPosition.consume( (world, pos) -> {
            
// Path: src/main/java/net/darkhax/darkutils/DarkUtils.java
// @Mod(DarkUtils.MOD_ID)
// public class DarkUtils {
//     
//     public static final String MOD_ID = "darkutils";
//     
//     public static final String MOD_NAME = "Dark Utilities";
//     
//     public static final Logger LOG = LogManager.getLogger(MOD_NAME);
//     
//     public static final ItemGroup ITEM_GROUP = new ItemGroupBase(MOD_ID, () -> new ItemStack(DarkUtils.content.vectorPlate));
//     
//     public static final NetworkHelper NETWORK = new NetworkHelper(new ResourceLocation(MOD_ID, "main"), "2.0.X");
//     
//     public static RegistryHelper registry;
//     
//     public static Content content;
//     
//     public static AddonManager addons;
//     
//     public DarkUtils() {
//         
//         registry = DistExecutor.runForDist( () -> () -> new RegistryHelperClient(MOD_ID, LOG, ITEM_GROUP), () -> () -> new RegistryHelper(MOD_ID, LOG, ITEM_GROUP));
//         content = DistExecutor.runForDist( () -> () -> new ContentClient(registry), () -> () -> new Content(registry));
//         NETWORK.registerEnqueuedMessage(MessageSyncCrucibleType.class, NetworkHandlerServer::encodeStageMessage, t -> NetworkHandlerClient.decodeStageMessage(t), (t, u) -> NetworkHandlerClient.processSyncStagesMessage(t, u));
//         registry.initialize(FMLJavaModLoadingContext.get().getModEventBus());
//         
//         // Addons
//         addons = new AddonManager();
//     }
// }
// 
// Path: src/main/java/net/darkhax/darkutils/features/slimecrucible/SlimeCrucibleEvents.java
// public static class RecipeCraftedEvent extends Event {
//         
//     /**
//      * The recipe being crafted.
//      */
//     private final RecipeSlimeCrafting recipe;
//         
//     /**
//      * The player crafting the recipe.
//      */
//     private final PlayerEntity player;
//         
//     /**
//      * The world instance.
//      */
//     private final World world;
//         
//     /**
//      * The position of the slime crucible.
//      */
//     private final BlockPos pos;
//         
//     /**
//      * The original output item before modifications.
//      */
//     private final ItemStack originalOutput;
//         
//     /**
//      * The output item that will be used.
//      */
//     private ItemStack output;
//         
//     public RecipeCraftedEvent(RecipeSlimeCrafting recipe, PlayerEntity player, World world, BlockPos pos, ItemStack originalOutput) {
//             
//         this.recipe = recipe;
//         this.player = player;
//         this.world = world;
//         this.pos = pos;
//         this.originalOutput = originalOutput;
//         this.output = originalOutput.copy();
//     }
//         
//     /**
//      * Gets the stack instance that will actually be created.
//      * 
//      * @return The actual recipe output.
//      */
//     public ItemStack getOutput () {
//             
//         return this.output;
//     }
//         
//     /**
//      * Sets the output to a different item stack.
//      * 
//      * @param output The new output stack.
//      */
//     public void setOutput (ItemStack output) {
//             
//         this.output = output;
//     }
//         
//     /**
//      * Gets the recipe being crafted.
//      * 
//      * @return The recipe being crafted.
//      */
//     public RecipeSlimeCrafting getRecipe () {
//             
//         return this.recipe;
//     }
//         
//     /**
//      * Gets the player crafting the recipe.
//      * 
//      * @return The player crafting the recipe.
//      */
//     public PlayerEntity getPlayer () {
//             
//         return this.player;
//     }
//         
//     /**
//      * Gets the world instance.
//      * 
//      * @return The world instance.
//      */
//     public World getWorld () {
//             
//         return this.world;
//     }
//         
//     /**
//      * Gets the position of the crucible block.
//      * 
//      * @return The position of the crucible block.
//      */
//     public BlockPos getPos () {
//             
//         return this.pos;
//     }
//         
//     /**
//      * Gets the original and unmodified output stack.
//      * 
//      * @return The original and unmodified output stack.
//      */
//     public ItemStack getOriginalOutput () {
//             
//         return this.originalOutput;
//     }
// }


// Path: src/main/java/net/darkhax/darkutils/features/slimecrucible/ContainerSlimeCrucible.java
import java.util.Collection;
import java.util.Comparator;
import java.util.List;
import java.util.function.Consumer;
import java.util.stream.Collectors;
import javax.annotation.Nullable;
import com.google.common.collect.Lists;
import net.darkhax.bookshelf.inventory.InventoryListenable;
import net.darkhax.bookshelf.inventory.SlotOutput;
import net.darkhax.bookshelf.util.WorldUtils;
import net.darkhax.darkutils.DarkUtils;
import net.darkhax.darkutils.features.slimecrucible.SlimeCrucibleEvents.RecipeCraftedEvent;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.entity.player.PlayerInventory;
import net.minecraft.entity.player.ServerPlayerEntity;
import net.minecraft.inventory.CraftResultInventory;
import net.minecraft.inventory.IInventory;
import net.minecraft.inventory.container.Container;
import net.minecraft.inventory.container.ContainerType;
import net.minecraft.inventory.container.Slot;
import net.minecraft.item.Item;
import net.minecraft.item.ItemStack;
import net.minecraft.tileentity.TileEntity;
import net.minecraft.util.IWorldPosCallable;
import net.minecraft.util.IntReferenceHolder;
import net.minecraft.util.SoundCategory;
import net.minecraft.util.math.BlockPos;
import net.minecraft.world.World;
import net.minecraftforge.common.MinecraftForge;

        this.detectAndSendChanges();
    }
    
    /**
     * Gets the recipe that has been selected by the user.
     * 
     * @return The current selected recipe.
     */
    public RecipeSlimeCrafting getSelectedRecipe () {
        
        return this.availableRecipes.get(this.getSelectedRecipeId());
    }
    
    private ItemStack tempCraftingStack = ItemStack.EMPTY;
    
    /**
     * Handles the output slot being changed by the player.
     * 
     * @param player The player who changed the slot.
     * @param stack The newly created ItemStack.
     */
    private ItemStack onOutputSlotChanged (PlayerEntity player, ItemStack stack) {
        
        final RecipeSlimeCrafting recipe = this.getSelectedRecipe();
        final ItemStack inputStack = this.slotInput.decrStackSize(recipe.getInputCount());
        final ItemStack outputCopy = stack;
        
        // Remove the consumed slime points from the tile and from the container.
        this.worldPosition.consume( (world, pos) -> {
            
            final RecipeCraftedEvent event = new RecipeCraftedEvent(recipe, player, world, pos, outputCopy);