HalestormXV

AstralForge

Oct 31st, 2020 (edited)
963
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. package com.halestormxv.mysterium.astralforge;
  2.  
  3. import com.halestormxv.mysterium.init.ItemInitDeferred;
  4. import com.halestormxv.mysterium.init.ModTileEntityTypes;
  5. import com.halestormxv.mysterium.init.RecipeSerializerInit;
  6. import com.halestormxv.mysterium.objects.items.MysteriumFuel;
  7. import com.halestormxv.mysterium.recipes.LunarForgeRecipe;
  8. import com.halestormxv.mysterium.util.SetBlockStateFlag;
  9. import net.minecraft.block.BlockState;
  10. import net.minecraft.entity.player.PlayerEntity;
  11. import net.minecraft.entity.player.PlayerInventory;
  12. import net.minecraft.inventory.*;
  13. import net.minecraft.inventory.container.Container;
  14. import net.minecraft.inventory.container.INamedContainerProvider;
  15. import net.minecraft.item.ItemStack;
  16. import net.minecraft.item.crafting.*;
  17. import net.minecraft.nbt.CompoundNBT;
  18. import net.minecraft.network.NetworkManager;
  19. import net.minecraft.network.play.server.SUpdateTileEntityPacket;
  20. import net.minecraft.tileentity.ITickableTileEntity;
  21. import net.minecraft.tileentity.TileEntity;
  22. import net.minecraft.tileentity.TileEntityType;
  23. import net.minecraft.util.math.BlockPos;
  24. import net.minecraft.util.text.ITextComponent;
  25. import net.minecraft.util.text.TranslationTextComponent;
  26. import net.minecraft.world.World;
  27. import net.minecraftforge.items.IItemHandlerModifiable;
  28. import net.minecraftforge.items.wrapper.InvWrapper;
  29. import net.minecraftforge.items.wrapper.RecipeWrapper;
  30.  
  31. import javax.annotation.Nullable;
  32. import java.util.Collections;
  33. import java.util.Optional;
  34. import java.util.Set;
  35. import java.util.stream.Collectors;
  36.  
  37. public class TileEntityAstralForge extends TileEntity implements ITickableTileEntity, INamedContainerProvider
  38. {
  39.  
  40.     public static final int FUEL_SLOTS_COUNT = 4;
  41.     public static final int INPUT_SLOTS_COUNT = 5;
  42.     public static final int OUTPUT_SLOTS_COUNT = 5;
  43.     public static final int TOTAL_SLOTS_COUNT = FUEL_SLOTS_COUNT + INPUT_SLOTS_COUNT + OUTPUT_SLOTS_COUNT;
  44.  
  45.     private AstralForgeZoneContents fuelZoneContents;
  46.     private AstralForgeZoneContents inputZoneContents;
  47.     private AstralForgeZoneContents outputZoneContents;
  48.  
  49.     private final AstralForgeStateData astralForgeStateData = new AstralForgeStateData();
  50.  
  51.     public TileEntityAstralForge(TileEntityType<?> tileEntityTypeIn){
  52.         super(tileEntityTypeIn);
  53.         fuelZoneContents = AstralForgeZoneContents.createForTileEntity(FUEL_SLOTS_COUNT, this::canPlayerAccessInventory, this::markDirty);
  54.         inputZoneContents = AstralForgeZoneContents.createForTileEntity(INPUT_SLOTS_COUNT, this::canPlayerAccessInventory, this::markDirty);
  55.         outputZoneContents = AstralForgeZoneContents.createForTileEntity(OUTPUT_SLOTS_COUNT, this::canPlayerAccessInventory, this::markDirty);
  56.     }
  57.  
  58.     public TileEntityAstralForge(){
  59.         this(ModTileEntityTypes.ASTRAL_FORGE.get());
  60.     }
  61.  
  62.     // Return true if the given player is able to use this block. In this case it checks that
  63.     // 1) the world tileentity hasn't been replaced in the meantime, and
  64.     // 2) the player isn't too far away from the centre of the block
  65.     public boolean canPlayerAccessInventory(PlayerEntity player) {
  66.         if (this.world.getTileEntity(this.pos) != this) return false;
  67.         final double X_CENTRE_OFFSET = 0.5;
  68.         final double Y_CENTRE_OFFSET = 0.5;
  69.         final double Z_CENTRE_OFFSET = 0.5;
  70.         final double MAXIMUM_DISTANCE_SQ = 8.0 * 8.0;
  71.         return player.getDistanceSq(pos.getX() + X_CENTRE_OFFSET, pos.getY() + Y_CENTRE_OFFSET, pos.getZ() + Z_CENTRE_OFFSET) < MAXIMUM_DISTANCE_SQ;
  72.     }
  73.  
  74.     /**
  75.      * Get the number of slots which have fuel burning in them.
  76.      * @return number of slots with burning fuel, 0 - FUEL_SLOTS_COUNT
  77.      */
  78.     public int numberOfBurningFuelSlots()   {
  79.         int burningCount = 0;
  80.         for (int burnTime : astralForgeStateData.burnTimeRemainings) {
  81.             if (burnTime > 0) ++burningCount;
  82.         }
  83.         return burningCount;
  84.     }
  85.  
  86.     // This method is called every tick to update the tile entity, i.e.
  87.     // - see if the fuel has run out, and if so turn the furnace "off" and slowly uncook the current item (if any)
  88.     // - see if the current smelting input item has finished smelting; if so, convert it to output
  89.     // - burn fuel slots
  90.     // It runs both on the server and the client but we only need to do updates on the server side.
  91.     @Override
  92.     public void tick() {
  93.         if (world.isRemote) return; // do nothing on client.
  94.         ItemStack currentlySmeltingItem = getCurrentlySmeltingInputItem();
  95.  
  96.         // if user has changed the input slots, reset the smelting time
  97.         if (!ItemStack.areItemsEqual(currentlySmeltingItem, currentlySmeltingItemLastTick)) {  // == and != don't work!
  98.             astralForgeStateData.cookTimeElapsed = 0;
  99.         }
  100.         currentlySmeltingItemLastTick = currentlySmeltingItem.copy();
  101.  
  102.         if (!currentlySmeltingItem.isEmpty()) {
  103.             int numberOfFuelBurning = burnFuel();
  104.  
  105.             // If fuel is available, keep cooking the item, otherwise start "uncooking" it at double speed
  106.             if (numberOfFuelBurning > 0) {
  107.                 astralForgeStateData.cookTimeElapsed += numberOfFuelBurning;
  108.             }   else {
  109.                 astralForgeStateData.cookTimeElapsed -= 2;
  110.             }
  111.             if (astralForgeStateData.cookTimeElapsed < 0) astralForgeStateData.cookTimeElapsed = 0;
  112.  
  113.             int cookTimeForCurrentItem = getCookTime(this.world, currentlySmeltingItem);
  114.             astralForgeStateData.cookTimeForCompletion = cookTimeForCurrentItem;
  115.             // If cookTime has reached maxCookTime smelt the item and reset cookTime
  116.             if (astralForgeStateData.cookTimeElapsed >= cookTimeForCurrentItem) {
  117.                 smeltFirstSuitableInputItem();
  118.                 astralForgeStateData.cookTimeElapsed = 0;
  119.             }
  120.         }   else {
  121.             astralForgeStateData.cookTimeElapsed = 0;
  122.         }
  123.  
  124.         // when the number of burning slots changes, we need to force the block to re-render, otherwise the change in
  125.         //   state will not be visible.  Likewise, we need to force a lighting recalculation.
  126.         // The block update (for renderer) is only required on client side, but the lighting is required on both, since
  127.         //    the client needs it for rendering and the server needs it for crop growth etc
  128.         int numberBurning = numberOfBurningFuelSlots();
  129.         BlockState currentBlockState = world.getBlockState(this.pos);
  130.         BlockState newBlockState = currentBlockState.with(BlockAstralForge.BURNING_SIDES_COUNT, numberBurning);
  131.         if (!newBlockState.equals(currentBlockState)) {
  132.             final int FLAGS = SetBlockStateFlag.get(SetBlockStateFlag.BLOCK_UPDATE, SetBlockStateFlag.SEND_TO_CLIENTS);
  133.             world.setBlockState(this.pos, newBlockState, FLAGS);
  134.             markDirty();
  135.         }
  136.     }
  137.  
  138.     /**
  139.      *  for each fuel slot: decreases the burn time, checks if burnTimeRemainings = 0 and tries to consume a new piece of fuel if one is available
  140.      * @return the number of fuel slots which are burning
  141.      */
  142.     private int burnFuel() {
  143.         int burningCount = 0;
  144.         boolean inventoryChanged = false;
  145.  
  146.         for (int fuelIndex = 0; fuelIndex < FUEL_SLOTS_COUNT; fuelIndex++) {
  147.             if (astralForgeStateData.burnTimeRemainings[fuelIndex] > 0) {
  148.                 --astralForgeStateData.burnTimeRemainings[fuelIndex];
  149.                 ++burningCount;
  150.             }
  151.  
  152.             if (astralForgeStateData.burnTimeRemainings[fuelIndex] == 0) {
  153.                 ItemStack fuelItemStack = fuelZoneContents.getStackInSlot(fuelIndex);
  154.                 if (!fuelItemStack.isEmpty() && getItemBurnTime(this.world, fuelItemStack) > 0) {
  155.                     // If the stack in this slot isn't empty and is fuel, set burnTimeRemainings & burnTimeInitialValues to the
  156.                     // item's burn time and decrease the stack size
  157.                     int burnTimeForItem = getItemBurnTime(this.world, fuelItemStack);
  158.                     astralForgeStateData.burnTimeRemainings[fuelIndex] = burnTimeForItem;
  159.                     astralForgeStateData.burnTimeInitialValues[fuelIndex] = burnTimeForItem;
  160.                     fuelZoneContents.decrStackSize(fuelIndex, 1);
  161.                     ++burningCount;
  162.                     inventoryChanged = true;
  163.  
  164.                     // If the stack size now equals 0 set the slot contents to the item container item. This is for fuel
  165.                     // item such as lava buckets so that the bucket is not consumed. If the item dose not have
  166.                     // a container item, getContainerItem returns ItemStack.EMPTY which sets the slot contents to empty
  167.                     if (fuelItemStack.isEmpty()) {
  168.                         ItemStack containerItem = fuelItemStack.getContainerItem();
  169.                         fuelZoneContents.setInventorySlotContents(fuelIndex, containerItem);
  170.                     }
  171.                 }
  172.             }
  173.         }
  174.         if (inventoryChanged) markDirty();
  175.         return burningCount;
  176.     }
  177.  
  178.     /**
  179.      * Check if any of the input item are smeltable and there is sufficient space in the output slots
  180.      * @return the ItemStack of the first input item that can be smelted; ItemStack.EMPTY if none
  181.      */
  182.     private ItemStack getCurrentlySmeltingInputItem() {return smeltFirstSuitableInputItem(false);}
  183.  
  184.     /**
  185.      * Smelt an input item into an output slot, if possible
  186.      */
  187.     private void smeltFirstSuitableInputItem() {
  188.         smeltFirstSuitableInputItem(true);
  189.     }
  190.  
  191.     /**
  192.      * checks that there is an item to be smelted in one of the input slots and that there is room for the result in the output slots
  193.      * If desired, performs the smelt
  194.      * @param performSmelt if true, perform the smelt.  if false, check whether smelting is possible, but don't change the inventory
  195.      * @return a copy of the ItemStack of the input item smelted or to-be-smelted
  196.      */
  197.     private ItemStack smeltFirstSuitableInputItem(boolean performSmelt)
  198.     {
  199.         Integer firstSuitableInputSlot = null;
  200.         Integer firstSuitableOutputSlot = null;
  201.         ItemStack result = ItemStack.EMPTY;
  202.         ItemStack modResult = ItemStack.EMPTY;
  203.  
  204.         // finds the first input slot which is smeltable and whose result fits into an output slot (stacking if possible)
  205.         for (int inputIndex = 0; inputIndex < INPUT_SLOTS_COUNT; inputIndex++)  {
  206.             ItemStack itemStackToSmelt = inputZoneContents.getStackInSlot(inputIndex);
  207.             if (!itemStackToSmelt.isEmpty()) {
  208.                 result = getSmeltingResultForItem(this.world, itemStackToSmelt);
  209.                 if (!result.isEmpty()) {
  210.                     // find the first suitable output slot- either empty, or with identical item that has enough space
  211.                     for (int outputIndex = 0; outputIndex < OUTPUT_SLOTS_COUNT; outputIndex++) {
  212.                         if (willItemStackFit(outputZoneContents, outputIndex, result)) {
  213.                             firstSuitableInputSlot = inputIndex;
  214.                             firstSuitableOutputSlot = outputIndex;
  215.                             break;
  216.                         }
  217.                     }
  218.                     if (firstSuitableInputSlot != null) break;
  219.                 }
  220.                 modResult = getModSmeltingResultForItem(this.world, itemStackToSmelt).getRecipeOutput().copy();
  221.                 if (!modResult.isEmpty()){
  222.                     for (int outputIndex = 0; outputIndex < OUTPUT_SLOTS_COUNT; outputIndex++) {
  223.                         if (willItemStackFit(outputZoneContents, outputIndex, modResult)) {
  224.                             firstSuitableInputSlot = inputIndex;
  225.                             firstSuitableOutputSlot = outputIndex;
  226.                             break;
  227.                         }
  228.                     }
  229.                     if (firstSuitableInputSlot != null) break;
  230.                 }
  231.             }
  232.         }
  233.  
  234.         if (firstSuitableInputSlot == null) return ItemStack.EMPTY;
  235.  
  236.         ItemStack returnvalue = inputZoneContents.getStackInSlot(firstSuitableInputSlot).copy();
  237.         if (!performSmelt) return returnvalue;
  238.  
  239.         // alter input and output
  240.         inputZoneContents.decrStackSize(firstSuitableInputSlot, 1);
  241.         outputZoneContents.increaseStackSize(firstSuitableOutputSlot, result);
  242.         outputZoneContents.increaseStackSize(firstSuitableOutputSlot, modResult);
  243.  
  244.         markDirty();
  245.         return returnvalue;
  246.     }
  247.  
  248.     /**
  249.      * Will the given ItemStack fully fit into the target slot?
  250.      * @param astralForgeZoneContents
  251.      * @param slotIndex
  252.      * @param itemStackOrigin
  253.      * @return true if the given ItemStack will fit completely; false otherwise
  254.      */
  255.     public boolean willItemStackFit(AstralForgeZoneContents astralForgeZoneContents, int slotIndex, ItemStack itemStackOrigin) {
  256.         ItemStack itemStackDestination = astralForgeZoneContents.getStackInSlot(slotIndex);
  257.  
  258.         if (itemStackDestination.isEmpty() || itemStackOrigin.isEmpty()) {
  259.             return true;
  260.         }
  261.  
  262.         if (!itemStackOrigin.isItemEqual(itemStackDestination)) {
  263.             return false;
  264.         }
  265.  
  266.         int sizeAfterMerge = itemStackDestination.getCount() + itemStackOrigin.getCount();
  267.         if (sizeAfterMerge <= astralForgeZoneContents.getInventoryStackLimit() && sizeAfterMerge <= itemStackDestination.getMaxStackSize()) {
  268.             return true;
  269.         }
  270.         return false;
  271.     }
  272.  
  273.     // returns the number of ticks the given item will burn. Returns 0 if the given item is not a valid fuel
  274.     public static int getItemBurnTime(World world, ItemStack stack)
  275.     {
  276.         int burntime = net.minecraftforge.common.ForgeHooks.getBurnTime(stack);
  277.         return burntime;
  278.     }
  279.  
  280.     // returns the smelting result for the given stack. Returns ItemStack.EMPTY if the given stack can not be smelted
  281.     public static ItemStack getSmeltingResultForItem(World world, ItemStack itemStack) {
  282.         Optional<FurnaceRecipe> matchingRecipe = getMatchingRecipeForInput(world, itemStack);
  283.         if (!matchingRecipe.isPresent()) return ItemStack.EMPTY;
  284.         return matchingRecipe.get().getRecipeOutput().copy();  // beware! You must deep copy otherwise you will alter the recipe itself
  285.     }
  286.  
  287.     // gets the recipe which matches the given input, or Missing if none.
  288.     public static Optional<FurnaceRecipe> getMatchingRecipeForInput(World world, ItemStack itemStack) {
  289.         RecipeManager recipeManager = world.getRecipeManager();
  290.         Inventory singleItemInventory = new Inventory(itemStack);
  291.         Optional<FurnaceRecipe> matchingRecipe = recipeManager.getRecipe(IRecipeType.SMELTING, singleItemInventory, world);
  292.         return matchingRecipe;
  293.     }
  294.  
  295.     @Nullable
  296.     private static LunarForgeRecipe getModSmeltingResultForItem(World world, ItemStack stack){
  297.         if(stack == null){ return null; }
  298.         IItemHandlerModifiable modItemInventory = new InvWrapper(new Inventory(stack));
  299.         Set<IRecipe<?>> recipes = findRecipesByType(RecipeSerializerInit.LUNAR_FORGE_TYPE, world);
  300.         for (IRecipe<?> iRecipe : recipes){
  301.             LunarForgeRecipe recipe = (LunarForgeRecipe) iRecipe;
  302.             if (recipe.matches(new RecipeWrapper(modItemInventory), world)){
  303.                 return recipe;
  304.             }
  305.         }
  306.         return null;
  307.     }
  308.  
  309.     private static Set<IRecipe<?>> findRecipesByType(IRecipeType<?> typeIn, World world) {
  310.         return world != null ? world.getRecipeManager().getRecipes().stream().filter(recipe -> recipe.getType() == typeIn).collect(Collectors.toSet()) : Collections.emptySet();
  311.     }
  312.  
  313.     /**
  314.      * Gets the cooking time for this recipe input
  315.      * @param world
  316.      * @param itemStack the input item to be smelted
  317.      * @return cooking time (ticks) or 0 if no matching recipe
  318.      */
  319.     public static int getCookTime(World world, ItemStack itemStack) {
  320.         Optional<FurnaceRecipe> matchingRecipe = getMatchingRecipeForInput(world, itemStack);
  321.         if(itemStack == null) {
  322.             return 0;
  323.         }
  324.         IItemHandlerModifiable modItemInventory = new InvWrapper(new Inventory(itemStack));
  325.         Set<IRecipe<?>> recipes = findRecipesByType(RecipeSerializerInit.LUNAR_FORGE_TYPE, world);
  326.         for (IRecipe<?> iRecipe : recipes) {
  327.             LunarForgeRecipe recipe = (LunarForgeRecipe) iRecipe;
  328.             if (recipe.matches(new RecipeWrapper(modItemInventory), world)) {
  329.                 return recipe.getCookTime();
  330.             }
  331.         }
  332.         return matchingRecipe.map(AbstractCookingRecipe::getCookTime).orElse(0);
  333.     }
  334.  
  335.     // Return true if the given stack is allowed to be inserted in the given slot
  336.     // Unlike the vanilla furnace, we allow anything to be placed in the fuel slots
  337.     static public boolean isItemValidForFuelSlot(ItemStack itemStack)
  338.     {
  339.         int burntime = net.minecraftforge.common.ForgeHooks.getBurnTime(itemStack);
  340.         return burntime > 0;
  341.     }
  342.  
  343.     // Return true if the given stack is allowed to be inserted in the given slot
  344.     // Unlike the vanilla furnace, we allow anything to be placed in the input slots
  345.     static public boolean isItemValidForInputSlot(ItemStack itemStack)
  346.     {
  347.         return true;
  348.     }
  349.  
  350.     // Return true if the given stack is allowed to be inserted in the given slot
  351.     static public boolean isItemValidForOutputSlot(ItemStack itemStack)
  352.     {
  353.         return false;
  354.     }
  355.  
  356.     //------------------------------
  357.     private final String FUEL_SLOTS_NBT = "fuelSlots";
  358.     private final String INPUT_SLOTS_NBT = "inputSlots";
  359.     private final String OUTPUT_SLOTS_NBT = "outputSlots";
  360.  
  361.     // This is where you save any data that you don't want to lose when the tile entity unloads
  362.     // In this case, it saves the state of the furnace (burn time etc) and the itemstacks stored in the fuel, input, and output slots
  363.     @Override
  364.     public CompoundNBT write(CompoundNBT parentNBTTagCompound)
  365.     {
  366.         super.write(parentNBTTagCompound); // The super call is required to save and load the tile's location
  367.  
  368.         astralForgeStateData.putIntoNBT(parentNBTTagCompound);
  369.         parentNBTTagCompound.put(FUEL_SLOTS_NBT, fuelZoneContents.serializeNBT());
  370.         parentNBTTagCompound.put(INPUT_SLOTS_NBT, inputZoneContents.serializeNBT());
  371.         parentNBTTagCompound.put(OUTPUT_SLOTS_NBT, outputZoneContents.serializeNBT());
  372.         return parentNBTTagCompound;
  373.     }
  374.  
  375.     // This is where you load the data that you saved in writeToNBT
  376.     @Override
  377.     public void read(CompoundNBT nbtTagCompound)
  378.     {
  379.         super.read(nbtTagCompound); // The super call is required to save and load the tile's location
  380.  
  381.         astralForgeStateData.readFromNBT(nbtTagCompound);
  382.  
  383.         CompoundNBT inventoryNBT = nbtTagCompound.getCompound(FUEL_SLOTS_NBT);
  384.         fuelZoneContents.deserializeNBT(inventoryNBT);
  385.  
  386.         inventoryNBT = nbtTagCompound.getCompound(INPUT_SLOTS_NBT);
  387.         inputZoneContents.deserializeNBT(inventoryNBT);
  388.  
  389.         inventoryNBT = nbtTagCompound.getCompound(OUTPUT_SLOTS_NBT);
  390.         outputZoneContents.deserializeNBT(inventoryNBT);
  391.  
  392.         if (fuelZoneContents.getSizeInventory() != FUEL_SLOTS_COUNT
  393.                 || inputZoneContents.getSizeInventory() != INPUT_SLOTS_COUNT
  394.                 || outputZoneContents.getSizeInventory() != OUTPUT_SLOTS_COUNT
  395.         )
  396.             throw new IllegalArgumentException("Corrupted NBT: Number of inventory slots did not match expected.");
  397.     }
  398.  
  399.     //  // When the world loads from disk, the server needs to send the TileEntity information to the client
  400. //  //  it uses getUpdatePacket(), getUpdateTag(), onDataPacket(), and handleUpdateTag() to do this
  401.     @Override
  402.     @Nullable
  403.     public SUpdateTileEntityPacket getUpdatePacket()
  404.     {
  405.         CompoundNBT updateTagDescribingTileEntityState = getUpdateTag();
  406.         final int METADATA = 42; // arbitrary.
  407.         return new SUpdateTileEntityPacket(this.pos, METADATA, updateTagDescribingTileEntityState);
  408.     }
  409.  
  410.     @Override
  411.     public void onDataPacket(NetworkManager net, SUpdateTileEntityPacket pkt) {
  412.         CompoundNBT updateTagDescribingTileEntityState = pkt.getNbtCompound();
  413.         handleUpdateTag(updateTagDescribingTileEntityState);
  414.     }
  415.  
  416.     /* Creates a tag containing the TileEntity information, used by vanilla to transmit from server to client
  417.        Warning - although our getUpdatePacket() uses this method, vanilla also calls it directly, so don't remove it.
  418.      */
  419.     @Override
  420.     public CompoundNBT getUpdateTag()
  421.     {
  422.         CompoundNBT nbtTagCompound = new CompoundNBT();
  423.         write(nbtTagCompound);
  424.         return nbtTagCompound;
  425.     }
  426.  
  427.     /* Populates this TileEntity with information from the tag, used by vanilla to transmit from server to client
  428.      *  The vanilla default is suitable for this example but I've included an explicit definition anyway.
  429.      */
  430.     @Override
  431.     public void handleUpdateTag(CompoundNBT tag) { read(tag); }
  432.  
  433.     /**
  434.      * When this tile entity is destroyed, drop all of its contents into the world
  435.      * @param world
  436.      * @param blockPos
  437.      */
  438.     public void dropAllContents(World world, BlockPos blockPos) {
  439.         InventoryHelper.dropInventoryItems(world, blockPos, fuelZoneContents);
  440.         InventoryHelper.dropInventoryItems(world, blockPos, inputZoneContents);
  441.         InventoryHelper.dropInventoryItems(world, blockPos, outputZoneContents);
  442.     }
  443.  
  444.     // -------------  The following two methods are used to make the TileEntity perform as a NamedContainerProvider, i.e.
  445.     //  1) Provide a name used when displaying the container, and
  446.     //  2) Creating an instance of container on the server, and linking it to the inventory items stored within the TileEntity
  447.  
  448.     /**
  449.      *  standard code to look up what the human-readable name is.
  450.      *  Can be useful when the tileentity has a customised name (eg "David's footlocker")
  451.      */
  452.     @Override
  453.     public ITextComponent getDisplayName() {
  454.         return new TranslationTextComponent("container.mysterium.astral_forge");
  455.     }
  456.  
  457.     /**
  458.      * The name is misleading; createMenu has nothing to do with creating a Screen, it is used to create the Container on the server only
  459.      * @param windowID
  460.      * @param playerInventory
  461.      * @param playerEntity
  462.      * @return
  463.      */
  464.     @Nullable
  465.     @Override
  466.     public Container createMenu(int windowID, PlayerInventory playerInventory, PlayerEntity playerEntity) {
  467.         return ContainerAstralForge.createContainerServerSide(windowID, playerInventory,
  468.                 inputZoneContents, outputZoneContents, fuelZoneContents, astralForgeStateData);
  469.     }
  470.  
  471.     private ItemStack currentlySmeltingItemLastTick = ItemStack.EMPTY;
  472.  
  473. }
  474.  
RAW Paste Data