Jump to content

3 input furnace?


Eastonium

Recommended Posts

Hi, I'm trying to make a furnace, but with three inputs, and one output. Basically a shapeless crafting recipe for 1-3 items, but it has to be smeleted to get an output. I used to have a custom furnace of my own back in 1.8.1B, but i can't decompile it. :(

Does anyone know how I would start on something like this? (I've tried combining crafting bench and furnace code, but it doesn't work... at all...)

Link to comment
Share on other sites

  • Replies 59
  • Created
  • Last Reply

Top Posters In This Topic

If you google forge custom furnace you will find tutorials to create a 1 input furnace.

Also if you understand the TileEntity furnace you should get far :)

If you guys dont get it.. then well ya.. try harder...

Link to comment
Share on other sites

I recommend forgoing using furnace code entirely and coding it yourself. Use the ItemStack[] array to hold the ingredients. Compare that against possible recipes (if you want it shapeless, use either a list or simple check-for-item algorithms). If it has all the ingredients and all other conditions are met, call make() (or whatever you want to call it) and produce the product by setStackInSlot().

 

Sample code (do not copy and paste it, as it will not work for you):

public void make() {
	boolean consume = (this.par5Random.nextInt(16) == 0);
	if (consume) {
		for (int i = 0; i < ingredients.length; i++) {
			ReikaInventoryHelper.decrStack(i, this.inv);
		}
	}
	this.fuel++;
	//this.fuel += par5Random.nextInt(11)+5;
	if (this.fuel > this.CAPACITY)
		this.fuel = this.CAPACITY;
}

public boolean process() {
	if (this.fuel >= CAPACITY)
		return false;
	boolean allitems = this.getAllIngredients();
	if (this.inv[ingredients.length] == null)
		return false;
	if (this.inv[ingredients.length].itemID != Item.ghastTear.itemID) //need a ghast tear as fuel solvent
		return false;
	return allitems;
}

public boolean getAllIngredients() {
	for (int k = 0; k < ingredients.length; k++)
		if (this.inv[k] == null)
			return false;
	for (int i = 0; i < ingredients.length; i++) {
		if (!ReikaInventoryHelper.checkForItemStack(ingredients[i].itemID, ingredients[i].getItemDamage(), this.inv))
			return false;
	}
	return true;
}

Link to comment
Share on other sites

Thank you for your help,  but for the moment, I'm not at the experience level to be able to code my own furnace. I was hoping to just modify a furnace instead of writing it all myself. :P

Basically I don't really have a clue how to use what you have suggested.

Link to comment
Share on other sites

Thank you for your help,  but for the moment, I'm not at the experience level to be able to code my own furnace. I was hoping to just modify a furnace instead of writing it all myself. :P

Basically I don't really have a clue how to use what you have suggested.

I can walk you through it.

Here are the things you will need to understand:

  • Arrays
  • ItemStacks
  • IInventory and Slot Operations (set and get)

Which of those is currently causing you trouble?

Link to comment
Share on other sites

Thank you for your help,  but for the moment, I'm not at the experience level to be able to code my own furnace. I was hoping to just modify a furnace instead of writing it all myself. :P

Basically I don't really have a clue how to use what you have suggested.

I can walk you through it.

Here are the things you will need to understand:

  • Arrays
  • ItemStacks
  • IInventory and Slot Operations (set and get)

Which of those is currently causing you trouble?

 

Arrays, and IInventory and Slot Operations. I'm not good at those stuffs. ItemStacks I think I understand well enough.

Link to comment
Share on other sites

OK. We shall start with arrays since those are fundamental Java and will be useful to you pretty much forever.

An array is basically a collection of variables organized into an ordered structure. Imagine it as a row of "slots" (not unlike inventory slots) and each slot can hold one variable of the appropriate type. For example, an array of 21 double values (declared as double[] array = new double[21]; ) would store 21 doubles, accessed via their positions, called indices (singular: index).

Worth noting is that the indices start at zero and progress up to (size-1), for a total of (size) slots. So if you tried accessing slot 21 (or greater) out of the above example (with a line like double b = array[21]; ) you would crash with an "Array Index out of Bounds" exception. Similarly, negative numbers for indices will crash with the same error.

 

Inventories in a TileEntity are treated as an array of ItemStacks. Notice in the furnace code (near the top), something like ItemStack[] furnaceItemStacks = new ItemStack[3];?

That is declaring the ItemStack array of size 3 (indices 0-2), with each corresponding to a slot, in this case ingredient, fuel, output. Similarly, a chest has a size 27 array for its 27 slots.

[continued]

 

Link to comment
Share on other sites

[continued]

Now for IInventory. That is an interface you implement (notice how the TileEntityFurnace extends TileEntity implements IInventory), which will force you to include a few methods. The names of these are rather self-explanatory, names like getStackInSlot(int i), which gets the ItemStack in slot i of your inventory, and setStackInSlot(int i, ItemStack itemstack), which sets the stack in slot i to itemstack, overwriting its original contents. Others are similarly clear.

You can simply copy-and-paste these methods from the Furnace code if you like.

 

Now, notice how the furnace also has updateEntity(). This is called every tick (50 ms), and in the furnace is used to evaluate the contents of the inventory and act appropriately.

What you do next depends on what you plan to do with your furnace.

If you only plan on having a few recipes for this furnace, you can do is iterate through your inventory, and set a bunch of booleans depending on whether you come across various ingredient items (using getStackInSlot and loops). If all of them come out true (or if the right combination do), then you can call a smelt() method which uses setStackInSlot() as needed, adding to the output and subtracting from the input.

If you want a lot of different recipes, there is a shortcut, but it is a bit more complex. If you do intend to do this, I will explain it.

 

Also, you will need to interface with a container and a GUI if you want to use any nonstandard slot layout. Do you need assistance with this as well?

 

Also, to any admins: I apologize for making this two posts, but the forum system was not letting me post one long one.

Link to comment
Share on other sites

[continued]

Now for IInventory. That is an interface you implement (notice how the TileEntityFurnace extends TileEntity implements IInventory), which will force you to include a few methods. The names of these are rather self-explanatory, names like getStackInSlot(int i), which gets the ItemStack in slot i of your inventory, and setStackInSlot(int i, ItemStack itemstack), which sets the stack in slot i to itemstack, overwriting its original contents. Others are similarly clear.

You can simply copy-and-paste these methods from the Furnace code if you like.

 

Now, notice how the furnace also has updateEntity(). This is called every tick (50 ms), and in the furnace is used to evaluate the contents of the inventory and act appropriately.

What you do next depends on what you plan to do with your furnace.

If you only plan on having a few recipes for this furnace, you can do is iterate through your inventory, and set a bunch of booleans depending on whether you come across various ingredient items (using getStackInSlot and loops). If all of them come out true (or if the right combination do), then you can call a smelt() method which uses setStackInSlot() as needed, adding to the output and subtracting from the input.

If you want a lot of different recipes, there is a shortcut, but it is a bit more complex. If you do intend to do this, I will explain it.

 

Also, you will need to interface with a container and a GUI if you want to use any nonstandard slot layout. Do you need assistance with this as well?

 

Also, to any admins: I apologize for making this two posts, but the forum system was not letting me post one long one.

 

thanks so much for that. it really makes sense.  so do I put this all in my tile entity file? and yes. there will be lots of recipes.

Link to comment
Share on other sites

All of that goes in your TileEntity, yes.

As for allowing lots of recipes, it is easier to use a Recipes file, much like the default Furnace does.

In your TileEntity's smelt function, use the following line to determine the output ItemStack:

ItemStack itemstack = RecipesClass.smelting().getSmeltingResult(inventory[0].itemID, inventory[1].itemID);

With whatever parameters you see fit (as per number of input items and metadata sensitivity). Here I assume no metadata and two ingredients.

 

And to test whether you have the right recipe, you can largely copy and paste code from the furnace, but the critical thing is this bit:

ItemStack itemstack = RecipesGrinder.smelting().getSmeltingResult(inventory[0].itemID, inventory[1].itemID);

if (itemstack == null)

return false;

This checks the result of the ingredients against the recipe file. If the result is null (no matching entry) it returns out and stops the smelting process from occurring.

 

You can largely copy the recipes file itself, but change the constructor (private RecipesClass() {}) to include your own recipes. You may need to change the number or type of parameters on the addSmelting method; using an ItemStack list is the best as it is dynamically sized.

 

Also, did you need aid in Container interface?

Link to comment
Share on other sites

Sorry I haven't replied for a day. xD

I've been working on getting a basic furnace working, and I just got that done, so I'll finally get to incorporate this stuff you're telling me about.

I will need metadata for my recipes.. lots of it.

I'm guessing I won't be able to test the recipes with multiple stuff unless i have slots in the container to put them in so yes, i'll need help with that.

Thanks soo much. you've been a wonderful help.

Link to comment
Share on other sites

Sorry I haven't replied for a day. xD

I've been working on getting a basic furnace working, and I just got that done, so I'll finally get to incorporate this stuff you're telling me about.

I will need metadata for my recipes.. lots of it.

I'm guessing I won't be able to test the recipes with multiple stuff unless i have slots in the container to put them in so yes, i'll need help with that.

Thanks soo much. you've been a wonderful help.

If you use ItemStack arguments in the addSmelting, you can use the metadata property in them.

As for containers, they are rather simple. Have a look in the container for the vanilla furnace. Most of that you can ignore for now - just copy and paste it. Now, see where, in the constructor, it has entries addSlotToContainer? That is where it adds the actual slots (the part of the GUI that makes the items "snap" in place). The arguments are the relevant inventory (player or furnace), id (corresponding to the index in the inventory of the TileEntity (eg inventory[6] -> addSlot(--, 6, --, --)), and x and y 2D coordinates. Simply add as many of these as you like wherever you like.

Link to comment
Share on other sites

Hi there,

 

Reika you helped a lot in explaining this, thanks for that! gave you a couple of +

 

So i'm also working on a 2 input furnace, but i still didn't get it to work yet, especially the part with addRecipe and such.

The container and GUI work fine.

 

These are the tileentity and recipe files:

 

TileEntityGemcutter

 

 

package Mod_Ores.Blocks.Gemcutter;

 

import Mod_Ores.mod_Ores;

import cpw.mods.fml.common.registry.GameRegistry;

import cpw.mods.fml.relauncher.Side;

import cpw.mods.fml.relauncher.SideOnly;

import net.minecraft.block.Block;

import net.minecraft.block.BlockFurnace;

import net.minecraft.block.material.Material;

import net.minecraft.entity.player.EntityPlayer;

import net.minecraft.inventory.ISidedInventory;

import net.minecraft.item.Item;

import net.minecraft.item.ItemBlock;

import net.minecraft.item.ItemHoe;

import net.minecraft.item.ItemStack;

import net.minecraft.item.ItemSword;

import net.minecraft.item.ItemTool;

import net.minecraft.item.crafting.FurnaceRecipes;

import net.minecraft.nbt.NBTTagCompound;

import net.minecraft.nbt.NBTTagList;

import net.minecraft.tileentity.TileEntity;

import net.minecraftforge.common.ForgeDirection;

import net.minecraftforge.common.ForgeDummyContainer;

 

public class TileEntityGemcutter extends TileEntity implements net.minecraftforge.common.ISidedInventory

{

    private static final int[] field_102010_d = new int[] {0};

    private static final int[] field_102011_e = new int[] {2, 1};

    private static final int[] field_102009_f = new int[] {1};

 

    /**

    * The ItemStacks that hold the items currently being used in the furnace

    */

    private ItemStack[] gemcutterItemStacks = new ItemStack[4];

 

    /** The number of ticks that the furnace will keep burning */

    public int gemcutterBurnTime = 0;

    /**

    * The number of ticks that a fresh copy of the currently-burning item would keep the furnace burning for

    */

    public int currentGemItemBurnTime = 0;

 

    /** The number of ticks that the current item has been cooking for */

    public int gemcutterCookTime = 0;

    public int gemcutterCookTime2 = 0;

    private String field_94130_e;

 

    /**

    * Returns the number of slots in the inventory.

    */

    @Override

    public int getSizeInventory()

    {

        return this.gemcutterItemStacks.length;

    }

 

    /**

    * Returns the stack in slot i

    */

    @Override

    public ItemStack getStackInSlot(int par1)

    {

        return this.gemcutterItemStacks[par1];

    }

 

    /**

    * Removes from an inventory slot (first arg) up to a specified number (second arg) of items and returns them in a

    * new stack.

    */

    @Override

    public ItemStack decrStackSize(int par1, int par2)

    {

        if (this.gemcutterItemStacks[par1] != null)

        {

            ItemStack itemstack;

 

            if (this.gemcutterItemStacks[par1].stackSize <= par2)

            {

                itemstack = this.gemcutterItemStacks[par1];

                this.gemcutterItemStacks[par1] = null;

                return itemstack;

            }

            else

            {

                itemstack = this.gemcutterItemStacks[par1].splitStack(par2);

 

                if (this.gemcutterItemStacks[par1].stackSize == 0)

                {

                    this.gemcutterItemStacks[par1] = null;

                }

 

                return itemstack;

            }

        }

        else

        {

            return null;

        }

    }

 

    /**

    * When some containers are closed they call this on each slot, then drop whatever it returns as an EntityItem -

    * like when you close a workbench GUI.

    */

    @Override

    public ItemStack getStackInSlotOnClosing(int par1)

    {

        if (this.gemcutterItemStacks[par1] != null)

        {

            ItemStack itemstack = this.gemcutterItemStacks[par1];

            this.gemcutterItemStacks[par1] = null;

            return itemstack;

        }

        else

        {

            return null;

        }

    }

 

    /**

    * Sets the given item stack to the specified slot in the inventory (can be crafting or armor sections).

    */

    @Override

    public void setInventorySlotContents(int par1, ItemStack par2ItemStack)

    {

        this.gemcutterItemStacks[par1] = par2ItemStack;

 

        if (par2ItemStack != null && par2ItemStack.stackSize > this.getInventoryStackLimit())

        {

            par2ItemStack.stackSize = this.getInventoryStackLimit();

        }

    }

 

    /**

    * Returns the name of the inventory.

    */

    public String getInvName()

    {

        return this.isInvNameLocalized() ? this.field_94130_e : "container.gemcutter";

    }

 

    /**

    * If this returns false, the inventory name will be used as an unlocalized name, and translated into the player's

    * language. Otherwise it will be used directly.

    */

    public boolean isInvNameLocalized()

    {

        return this.field_94130_e != null && this.field_94130_e.length() > 0;

    }

 

    public void func_94129_a(String par1Str)

    {

        this.field_94130_e = par1Str;

    }

 

    /**

    * Reads a tile entity from NBT.

    */

    @Override

    public void readFromNBT(NBTTagCompound par1NBTTagCompound)

    {

        super.readFromNBT(par1NBTTagCompound);

        NBTTagList nbttaglist = par1NBTTagCompound.getTagList("Items");

        this.gemcutterItemStacks = new ItemStack[this.getSizeInventory()];

 

        for (int i = 0; i < nbttaglist.tagCount(); ++i)

        {

            NBTTagCompound nbttagcompound1 = (NBTTagCompound)nbttaglist.tagAt(i);

            byte b0 = nbttagcompound1.getByte("Slot");

 

            if (b0 >= 0 && b0 < this.gemcutterItemStacks.length)

            {

                this.gemcutterItemStacks[b0] = ItemStack.loadItemStackFromNBT(nbttagcompound1);

            }

        }

 

        this.gemcutterBurnTime = par1NBTTagCompound.getShort("BurnTime");

        this.gemcutterCookTime = par1NBTTagCompound.getShort("CookTime");

        this.currentGemItemBurnTime = getItemBurnTime(this.gemcutterItemStacks[1]);

 

        if (par1NBTTagCompound.hasKey("CustomName"))

        {

            this.field_94130_e = par1NBTTagCompound.getString("CustomName");

        }

    }

 

    /**

    * Writes a tile entity to NBT.

    */

    @Override

    public void writeToNBT(NBTTagCompound par1NBTTagCompound)

    {

        super.writeToNBT(par1NBTTagCompound);

        par1NBTTagCompound.setShort("BurnTime", (short)this.gemcutterBurnTime);

        par1NBTTagCompound.setShort("CookTime", (short)this.gemcutterCookTime);

        NBTTagList nbttaglist = new NBTTagList();

 

        for (int i = 0; i < this.gemcutterItemStacks.length; ++i)

        {

            if (this.gemcutterItemStacks != null)

            {

                NBTTagCompound nbttagcompound1 = new NBTTagCompound();

                nbttagcompound1.setByte("Slot", (byte)i);

                this.gemcutterItemStacks.writeToNBT(nbttagcompound1);

                nbttaglist.appendTag(nbttagcompound1);

            }

        }

 

        par1NBTTagCompound.setTag("Items", nbttaglist);

 

        if (this.isInvNameLocalized())

        {

            par1NBTTagCompound.setString("CustomName", this.field_94130_e);

        }

    }

 

    /**

    * Returns the maximum stack size for a inventory slot. Seems to always be 64, possibly will be extended. *Isn't

    * this more of a set than a get?*

    */

    public int getInventoryStackLimit()

    {

        return 64;

    }

 

    @SideOnly(Side.CLIENT)

 

    /**

    * Returns an integer between 0 and the passed value representing how close the current item is to being completely

    * cooked

    */

    public int getCookProgressScaled(int par1)

    {

        return this.gemcutterCookTime * par1 / 200;

    }

 

    @SideOnly(Side.CLIENT)

 

    /**

    * Returns an integer between 0 and the passed value representing how much burn time is left on the current fuel

    * item, where 0 means that the item is exhausted and the passed value means that the item is fresh

    */

    public int getBurnTimeRemainingScaled(int par1)

    {

        if (this.currentGemItemBurnTime == 0)

        {

            this.currentGemItemBurnTime = 200;

        }

 

        return this.gemcutterBurnTime * par1 / this.currentGemItemBurnTime;

    }

 

    /**

    * Returns true if the furnace is currently burning

    */

    public boolean isBurning()

    {

        return this.gemcutterBurnTime > 0;

    }

 

    /**

    * Allows the entity to update its state. Overridden in most subclasses, e.g. the mob spawner uses this to count

    * ticks and creates a new spawn inside its implementation.

    */

    public void updateEntity()

    {

        boolean flag = this.gemcutterBurnTime > 0;

        boolean flag1 = false;

 

        if (this.gemcutterBurnTime > 0)

        {

            --this.gemcutterBurnTime;

        }

 

        if (!this.worldObj.isRemote)

        {

            if (this.gemcutterBurnTime == 0 && this.canSmelt())

            {

                this.currentGemItemBurnTime = this.gemcutterBurnTime = getItemBurnTime(this.gemcutterItemStacks[1]);

 

                if (this.gemcutterBurnTime > 0)

                {

                    flag1 = true;

 

                    if (this.gemcutterItemStacks[1] != null)

                    {

                        --this.gemcutterItemStacks[1].stackSize;

 

                        if (this.gemcutterItemStacks[1].stackSize == 0)

                        {

                            this.gemcutterItemStacks[1] = this.gemcutterItemStacks[1].getItem().getContainerItemStack(gemcutterItemStacks[1]);

                        }

                    }

                }

            }

 

            if (this.isBurning() && this.canSmelt())

            {

                ++this.gemcutterCookTime;

 

                if (this.gemcutterCookTime == 200)

                {

                    this.gemcutterCookTime = 0;

                    this.smeltItem();

                    flag1 = true;

                }

            }

            else

            {

                this.gemcutterCookTime = 0;

            }

 

            if (flag != this.gemcutterBurnTime > 0)

            {

                flag1 = true;

                BlockGemcutterBench.updateGemcutterBlockState(this.gemcutterBurnTime > 0, this.worldObj, this.xCoord, this.yCoord, this.zCoord);

            }

        }

 

        if (flag1)

        {

            this.onInventoryChanged();

        }

    }

 

    /**

    * Returns true if the furnace can smelt an item, i.e. has a source item, destination stack isn't full, etc.

    */

    private boolean canSmelt()

    {

        if (this.gemcutterItemStacks[0] == null)

        {

            return false;

        }

        if (this.gemcutterItemStacks[3] == null)

        {

            return false;

        }

        else

        {

            ItemStack itemstack = GemcutterRecipes.smelting().getSmeltingResult(this.gemcutterItemStacks[0], this.gemcutterItemStacks[3]);

            if (itemstack == null) return false;

            if (this.gemcutterItemStacks[2] == null) return true;

            if (!this.gemcutterItemStacks[2].isItemEqual(itemstack)) return false;

            int result = gemcutterItemStacks[2].stackSize + itemstack.stackSize;

            return (result <= getInventoryStackLimit() && result <= itemstack.getMaxStackSize());

        }

    }

 

    /**

    * Turn one item from the furnace source stack into the appropriate smelted item in the furnace result stack

    */

    public void smeltItem()

    {

        if (this.canSmelt())

        {

            ItemStack itemstack = GemcutterRecipes.smelting().getSmeltingResult(this.gemcutterItemStacks[0], this.gemcutterItemStacks[3]);

           

            if (this.gemcutterItemStacks[2] == null)

            {

                this.gemcutterItemStacks[2] = itemstack.copy();

            }

            else if (this.gemcutterItemStacks[2].isItemEqual(itemstack))

            {

                gemcutterItemStacks[2].stackSize += itemstack.stackSize;

            }

 

            --this.gemcutterItemStacks[0].stackSize;

 

            if (this.gemcutterItemStacks[0].stackSize <= 0)

            {

                this.gemcutterItemStacks[0] = null;

            }

           

            --this.gemcutterItemStacks[3].stackSize;

 

            if (this.gemcutterItemStacks[3].stackSize <= 0)

            {

                this.gemcutterItemStacks[3] = null;

            }

        }

    }

 

    /**

    * Returns the number of ticks that the supplied fuel item will keep the furnace burning, or 0 if the item isn't

    * fuel

    */

    public static int getItemBurnTime(ItemStack par0ItemStack)

    {

        if (par0ItemStack == null)

        {

            return 0;

        }

        else

        {

            int i = par0ItemStack.getItem().itemID;

            Item item = par0ItemStack.getItem();

 

            if (par0ItemStack.getItem() instanceof ItemBlock && Block.blocksList != null)

            {

                Block block = Block.blocksList;

 

                /*if (block == Block.woodSingleSlab)

                {

                    return 150;

                }

 

                if (block.blockMaterial == Material.wood)

                {

                    return 300;

                }*/

            }

 

            //if (i == mod_Ores.PolisherTowel.itemID) return 800;

            if (i == mod_Ores.Polisher.itemID) return 1600;

            //if (i == mod_Ores.UraniumLiquid.itemID) return 1600;

            //if (i == Item.coal.itemID) return 1600;

            return GameRegistry.getFuelValue(par0ItemStack);

        }

    }

 

    /**

    * Return true if item is a fuel source (getItemBurnTime() > 0).

    */

    public static boolean isItemFuel(ItemStack par0ItemStack)

    {

        return getItemBurnTime(par0ItemStack) > 0;

    }

 

    /**

    * Do not make give this method the name canInteractWith because it clashes with Container

    */

    @Override

    public boolean isUseableByPlayer(EntityPlayer par1EntityPlayer)

    {

        return this.worldObj.getBlockTileEntity(this.xCoord, this.yCoord, this.zCoord) != this ? false : par1EntityPlayer.getDistanceSq((double)this.xCoord + 0.5D, (double)this.yCoord + 0.5D, (double)this.zCoord + 0.5D) <= 64.0D;

    }

 

    public void openChest() {}

 

    public void closeChest() {}

 

    /**

    * Returns true if automation is allowed to insert the given stack (ignoring stack size) into the given slot.

    */

    public boolean isStackValidForSlot(int par1, ItemStack par2ItemStack)

    {

        return par1 == 2 ? false : (par1 == 1 ? isItemFuel(par2ItemStack) : true);

    }

 

    /**

    * Get the size of the side inventory.

    */

    public int[] getSizeInventorySide(int par1)

    {

        return par1 == 0 ? field_102011_e : (par1 == 1 ? field_102010_d : field_102009_f);

    }

 

    public boolean func_102007_a(int par1, ItemStack par2ItemStack, int par3)

    {

        return this.isStackValidForSlot(par1, par2ItemStack);

    }

 

    public boolean func_102008_b(int par1, ItemStack par2ItemStack, int par3)

    {

        return par3 != 0 || par1 != 1 || par2ItemStack.itemID == Item.bucketEmpty.itemID;

    }

 

    /***********************************************************************************

    * This function is here for compatibilities sake, Modders should Check for

    * Sided before ContainerWorldly, Vanilla Minecraft does not follow the sided standard

    * that Modding has for a while.

    *

    * In vanilla:

    *

    *  Top: Ores

    *  Sides: Fuel

    *  Bottom: Output

    *

    * Standard Modding:

    *  Top: Ores

    *  Sides: Output

    *  Bottom: Fuel

    *

    * The Modding one is designed after the GUI, the vanilla one is designed because its

    * intended use is for the hopper, which logically would take things in from the top.

    *

    * This will possibly be removed in future updates, and make vanilla the definitive

    * standard.

    */

 

    @Override

    public int getStartInventorySide(ForgeDirection side)

    {

        if (ForgeDummyContainer.legacyFurnaceSides)

        {

            if (side == ForgeDirection.DOWN) return 1;

            if (side == ForgeDirection.UP) return 0;

            return 2;

        }

        else

        {

            if (side == ForgeDirection.DOWN) return 2;

            if (side == ForgeDirection.UP) return 0;

            return 1;

        }

    }

 

    @Override

    public int getSizeInventorySide(ForgeDirection side)

    {

        return 1;

    }

}

 

 

 

GemcutterRecipes

 

 

package Mod_Ores.Blocks.Gemcutter;

 

import java.util.Arrays;

import java.util.HashMap;

import java.util.List;

import java.util.Map;

 

import Mod_Ores.mod_Ores;

import net.minecraft.block.Block;

import net.minecraft.item.Item;

import net.minecraft.item.ItemStack;

 

public class GemcutterRecipes

{

    private static final GemcutterRecipes gemcutterBase = new GemcutterRecipes();

 

    /** The list of smelting results. */

    private Map gemcutterList = new HashMap();

    private Map experienceList = new HashMap();

    private HashMap<List<Integer>, ItemStack> gemcutterSmeltingList = new HashMap<List<Integer>, ItemStack>();

    private HashMap<List<Integer>, Float> metaExperience = new HashMap<List<Integer>, Float>();

 

    /**

    * Used to call methods addSmelting and getSmeltingResult.

    */

    public static final GemcutterRecipes smelting()

    {

        return gemcutterBase;

    }

 

    private GemcutterRecipes()

    {

        this.addGemcutterSmelting(mod_Ores.AmazoniteUncut.itemID, mod_Ores.Polisher.itemID, new ItemStack(mod_Ores.AmazoniteGem), 1.0F);

    }

 

    /**

    * Adds a smelting recipe.

    */

    public void addGemcutterSmelting(int par1, int par2, ItemStack par2ItemStack, float par3)

    {

    StringBuffer sb = new StringBuffer(32);

    sb.append(Math.min(par1, par2)).append("_").append(Math.max(par1, par2));

        this.gemcutterList.put(Integer.valueOf(sb.toString()), par2ItemStack);

        this.experienceList.put(Integer.valueOf(par2ItemStack.itemID), Float.valueOf(par3));

    }

 

    /**

    * Returns the smelting result of an item.

    * Deprecated in favor of a metadata sensitive version

    */

    @Deprecated

    public ItemStack getSmeltingResult(int itemstack, int itemstack1)

    {

    StringBuffer sb = new StringBuffer(32);

    sb.append(Math.min(itemstack, itemstack1)).append("_").append(Math.max(itemstack, itemstack1));

        return (ItemStack)this.gemcutterList.get(Integer.valueOf(sb.toString()));

    }

 

    public Map getSmeltingList()

    {

        return this.gemcutterList;

    }

 

    @Deprecated //In favor of ItemStack sensitive version

    public float getExperience(int par1)

    {

        return this.experienceList.containsKey(Integer.valueOf(par1)) ? ((Float)this.experienceList.get(Integer.valueOf(par1))).floatValue() : 0.0F;

    }

 

    /**

    * A metadata sensitive version of adding a furnace recipe.

    */

    public void addSmelting(int itemID, int metadata, ItemStack itemstack, float experience)

    {

    gemcutterSmeltingList.put(Arrays.asList(itemID, metadata), itemstack);

        metaExperience.put(Arrays.asList(itemID, metadata), experience);

    }

 

    /**

    * Used to get the resulting ItemStack form a source ItemStack

    * @param item The Source ItemStack

    * @return The result ItemStack

    */

    public ItemStack getSmeltingResult(ItemStack item, ItemStack item2)

    {

        if (item == null)

        {

            return null;

        }

        ItemStack ret = (ItemStack)gemcutterSmeltingList.get(Arrays.asList(item.itemID, item.getItemDamage()));

        if (ret != null)

        {

            return ret;

        }

        return (ItemStack)gemcutterList.get(Integer.valueOf(item.itemID));

    }

 

    /**

    * Grabs the amount of base experience for this item to give when pulled from the furnace slot.

    */

    public float getExperience(ItemStack item)

    {

        if (item == null || item.getItem() == null)

        {

            return 0;

        }

        float ret = item.getItem().getSmeltingExperience(item);

        if (ret < 0 && metaExperience.containsKey(Arrays.asList(item.itemID, item.getItemDamage())))

        {

            ret = metaExperience.get(Arrays.asList(item.itemID, item.getItemDamage()));

        }

        if (ret < 0 && experienceList.containsKey(item.itemID))

        {

            ret = ((Float)experienceList.get(item.itemID)).floatValue();

        }

        return (ret < 0 ? 0 : ret);

    }

 

    public Map<List<Integer>, ItemStack> getMetaSmeltingList()

    {

        return gemcutterSmeltingList;

    }

}

 

 

 

I am probably doing somethings completely wrong. Appreciate your help!

Link to comment
Share on other sites

Sorry I haven't replied for a day. xD

I've been working on getting a basic furnace working, and I just got that done, so I'll finally get to incorporate this stuff you're telling me about.

I will need metadata for my recipes.. lots of it.

I'm guessing I won't be able to test the recipes with multiple stuff unless i have slots in the container to put them in so yes, i'll need help with that.

Thanks soo much. you've been a wonderful help.

If you use ItemStack arguments in the addSmelting, you can use the metadata property in them.

As for containers, they are rather simple. Have a look in the container for the vanilla furnace. Most of that you can ignore for now - just copy and paste it. Now, see where, in the constructor, it has entries addSlotToContainer? That is where it adds the actual slots (the part of the GUI that makes the items "snap" in place). The arguments are the relevant inventory (player or furnace), id (corresponding to the index in the inventory of the TileEntity (eg inventory[6] -> addSlot(--, 6, --, --)), and x and y 2D coordinates. Simply add as many of these as you like wherever you like.

 

Alright, I have 3 input slots now, and they work for holding items.

I've been looking at the TileEntityClass and Recipe Class, and I'm seeing a lot of methods that involve the recipe stuff and making the recipes, and checking if it's valid and stuff, and I know I'm going to have to change that stuff, but for now... um... let me put it a different way.

In the TileEntityClass file, I think All I would need to edit is the canSmelt and smeltItem Functions, and in the RecipeClass File, the addSmelting (the one with metadata) and getSmeltingResult Functions.

How would I go about making these work for my furnace? And will I need to add anything extra? And how do I call on the multiple slots to use them ass the array for the possible ingredients?

Link to comment
Share on other sites

Alright, I have 3 input slots now, and they work for holding items.

I've been looking at the TileEntityClass and Recipe Class, and I'm seeing a lot of methods that involve the recipe stuff and making the recipes, and checking if it's valid and stuff, and I know I'm going to have to change that stuff, but for now... um... let me put it a different way.

In the TileEntityClass file, I think All I would need to edit is the canSmelt and smeltItem Functions, and in the RecipeClass File, the addSmelting (the one with metadata) and getSmeltingResult Functions.

How would I go about making these work for my furnace? And will I need to add anything extra? And how do I call on the multiple slots to use them ass the array for the possible ingredients?

You hardcode your recipes directly into the Recipes class, in the constructor. Basically call addSmelting a bunch of times, with arguments you choose and design. If you use ItemStacks, like addSmelting(is1, is2...), you can fetch IDs, metadata, and NBT as needed.

You see where the canSmelt checks if recipe(inputs) != null? That is how it knows if it is a valid recipe.

Link to comment
Share on other sites

Could any of you help me?

 

Create your own thread mate, just take the post u made above and make a new thread :)

That way we can focus on you alone there and not mix up problems :)

 

When you create your new thread remember to post code at www.pastebin.com

And set syntax highligthning to Java - this way reading youre code will be super easy = faster help <3

If you guys dont get it.. then well ya.. try harder...

Link to comment
Share on other sites

You hardcode your recipes directly into the Recipes class, in the constructor. Basically call addSmelting a bunch of times, with arguments you choose and design. If you use ItemStacks, like addSmelting(is1, is2...), you can fetch IDs, metadata, and NBT as needed.

You see where the canSmelt checks if recipe(inputs) != null? That is how it knows if it is a valid recipe.

 

Ok, So I modified my TileEntityClass, and I don't think I have to do anything more with it, Hopefully... Here is the part I modified:

http://pastebin.com/V70LJ1K5

And in the Recipes File, My brain likes to twist into a pretzel.

There's something like "private HashMap<List<Integer>, Float> metaExperience = new HashMap<List<Integer>, Float>();" that woks with the metadata recipes and Experience, and I have no Idea how to use those. I know I probably need one, but how to implement it is the question...

Along with that, I made this:

public void addSmelting(ItemStack ingredient1, ItemStack ingredient2, ItemStack ingredient3, ItemStack Result, float experience)

    {

   

    }

But since I don't know what that other HashMap thing is I have no Idea what to put in here... :P

Link to comment
Share on other sites

Ok, So I modified my TileEntityClass, and I don't think I have to do anything more with it, Hopefully... Here is the part I modified:

http://pastebin.com/V70LJ1K5

And in the Recipes File, My brain likes to twist into a pretzel.

There's something like "private HashMap<List<Integer>, Float> metaExperience = new HashMap<List<Integer>, Float>();" that woks with the metadata recipes and Experience, and I have no Idea how to use those. I know I probably need one, but how to implement it is the question...

Along with that, I made this:

public void addSmelting(ItemStack ingredient1, ItemStack ingredient2, ItemStack ingredient3, ItemStack Result, float experience)

    {

   

    }

But since I don't know what that other HashMap thing is I have no Idea what to put in here... :P

Worth noting before I start is that I have never seen the class MaskForgeRecipes before and if it uses a different structural layout than I am familiar with, my advice may be somewhat inaccurate.

 

Anyways, a hashmap is just a list of objects with a given "key", that is an address. For example, if you declared HashMap map = new HashMap() then added map.put(new ItemStack(Item.diamond.itemID, 1, 0), Block.dirt.blockID), then there would be an object (in this case the ID of a dirt block) stored with the key of a diamond item ID. So if you were to call map.get(Item.diamond.itemID), you would get Block.dirt.blockID. What the hashmap in your recipes file is doing is storing a more complicated structure of this, using a list (are you familiar with Java lists?) of Items as the key, and the output as the object (also a pair of objects, one float experience value and one itemstack).

Link to comment
Share on other sites

Worth noting before I start is that I have never seen the class MaskForgeRecipes before and if it uses a different structural layout than I am familiar with, my advice may be somewhat inaccurate.

 

Anyways, a hashmap is just a list of objects with a given "key", that is an address. For example, if you declared HashMap map = new HashMap() then added map.put(new ItemStack(Item.diamond.itemID, 1, 0), Block.dirt.blockID), then there would be an object (in this case the ID of a dirt block) stored with the key of a diamond item ID. So if you were to call map.get(Item.diamond.itemID), you would get Block.dirt.blockID. What the hashmap in your recipes file is doing is storing a more complicated structure of this, using a list (are you familiar with Java lists?) of Items as the key, and the output as the object (also a pair of objects, one float experience value and one itemstack).

My furnace's name is "MaskForge" so that's why it has that name. It's basically the same as the FurnaceRecipes file.

So should I use a HashMap for my addSmeling method? or use what I attemped to make? or an array? If i'm correct, it can be done any of those ways. Thanks again for all the help.

Link to comment
Share on other sites

My furnace's name is "MaskForge" so that's why it has that name. It's basically the same as the FurnaceRecipes file.

So should I use a HashMap for my addSmeling method? or use what I attemped to make? or an array? If i'm correct, it can be done any of those ways. Thanks again for all the help.

Use the HashMap. The current key format is likely a pair of numbers (ID and metadata). Change that to use a few (dependent on ingredient count) ItemStacks instead, and then use Map.put((ItemStack arguments), ItemStack output).

Link to comment
Share on other sites

Use the HashMap. The current key format is likely a pair of numbers (ID and metadata). Change that to use a few (dependent on ingredient count) ItemStacks instead, and then use Map.put((ItemStack arguments), ItemStack output).

 

So, I use something like ((returnItemStack)ingredient1ItemStack, ingredient2ItemStack, ingredient3ItemStack) with the return items stack thing thing i'm using to find the recipe? And the part i'm not getting is how to connect the ingredients for the recipe to the slots in the furnace.

Link to comment
Share on other sites

You connect the ingredients with array indices. Look again at the canSmelt() function. See the references to inventory[a], where a is an integer? Those are checking the specific slots in the inventory (which, as per your container file, relate to specific item "positions" in the GUI).

 

Sorry I haven't replied. So I am not getting this... :P I don't know if I'm trying to make it more complicated than it needs to be, but I thought for a shapeless recipe I have to use an array or something, so I'm utterly confused.

Link to comment
Share on other sites

Guest
This topic is now closed to further replies.



  • Recently Browsing

    • No registered users viewing this page.
  • Posts

    • I'm developing a dimension, but it's kinda resource intensive so some times during player teleporting it lags behind making the player phase down into the void, so im trying to implement some kind of pregeneration to force the game loading a small set of chunks in the are the player will teleport to. Some of the things i've tried like using ServerLevel and ServerChunkCache methods like getChunk() dont actually trigger chunk generation if the chunk isn't already on persistent storage (already generated) or placing tickets, but that doesn't work either. Ideally i should be able to check when the task has ended too. I've peeked around some pregen engines, but they're too complex for my current understanding of the system of which I have just a basic understanding (how ServerLevel ,ServerChunkCache  and ChunkMap work) of. Any tips or other classes I should be looking into to understand how to do this correctly?
    • https://mclo.gs/4UC49Ao
    • Way back in the Forge 1.17 days, work started for adding JPMS (Java Platform Module Support) to ModLauncher and ForgeModLoader. This has been used internally by Forge and some libraries for a while now, but mods (those with mods.toml specifically) have not been able to take advantage of it. As of Forge 1.21.1 and 1.21.3, this is now possible!   What is JPMS and what does it mean for modders? JPMS is the Java Platform Module System, introduced in Java 9. It allows you to define modules, which are collections of packages and resources that can be exported or hidden from other modules. This allows for much more fine-tuned control over visibility, cleaner syntax for service declarations and support for sealed types across packages. For example, you might have a mod with a module called `com.example.mod` that exports `com.example.mod.api` and `com.example.mod.impl` to other mods, but hides `com.example.mod.internal` from them. This would allow you to have a clean API for other mods to use, while keeping your internal implementation details hidden from IDE hints, helping prevent accidental usage of internals that might break without prior notice. This is particularly useful if you'd like to use public records with module-private constructors or partially module-private record components, as you can create a sealed interface that only your record implements, having the interface be exported and the record hidden. It's also nice for declaring and using services, as you'll get compile-time errors from the Java compiler for typos and the like, rather than deferring to runtime errors. In more advanced cases, you can also have public methods that are only accessible to specific other modules -- handy if you want internal interactions between multiple of your own mods.   How do I bypass it? We understand there may be drama in implementing a system that prevents mods from accessing each other's internals when necessary (like when a mod is abandoned or you need to fix a compat issue) -- after all, we are already modding a game that doesn't have explicit support for Java mods yet. We have already thought of this and are offering APIs from day one to selectively bypass module restrictions. Let me be clear: Forge mods are not required to use JPMS. If you don't want to use it, you don't have to. The default behaviour is to have fully open, fully exported automatic modules. In Java, you can use the `Add-Opens` and `Add-Exports` manifest attributes to selectively bypass module restrictions of other mods at launch time, and we've added explicit support for these when loading your Forge mods. At compile-time, you can use existing solutions such as the extra-java-module-info Gradle plugin to deal with non-modular dependencies and add extra opens and exports to other modules. Here's an example on how to make the internal package `com.example.examplemod.internal` open to your mod in your build.gradle: tasks.named('jar', Jar) { manifest { attributes([ 'Add-Opens' : 'com.example.examplemod/com.example.examplemod.internal' 'Specification-Title' : mod_id, 'Specification-Vendor' : mod_authors // (...) ]) } } With the above in your mod's jar manifest, you can now reflectively access the classes inside that internal package. Multiple entries are separated with a space, as per Java's official spec. You can also use Add-Exports to directly call without reflection, however you'd need to use the Gradle plugin mentioned earlier to be able to compile. The syntax for Add-Exports is the same as Add-Opens, and instructions for the compile-time step with the Gradle plugin are detailed later in this post. Remember to prefer the opens and exports keywords inside module-info.java for sources you control. The Add-Opens/Add-Exports attributes are only intended for forcing open other mods.   What else is new with module support? Previously, the runtime module name was always forced to the first mod ID in your `mods.toml` file and all packages were forced fully open and exported. Module names are now distinguished from mod IDs, meaning the module name in your module-info.java can be different from the mod ID in your `mods.toml`. This allows you to have a more descriptive module name that doesn't have to be the same as your mod ID, however we strongly recommend including your mod ID as part of your module name to aid troubleshooting. The `Automatic-Module-Name` manifest attribute is now also honoured, allowing you to specify a module name for your mod without needing to create a `module-info.java` file. This is particularly useful for mods that don't care about JPMS features but want to have a more descriptive module name and easier integration with other mods that do use JPMS.   How do I use it? The first step is to create a `module-info.java` file in your mod's source directory. This file should be in the same package as your main mod class, and should look something like this: open module com.example.examplemod { requires net.minecraftforge.eventbus; requires net.minecraftforge.fmlcore; requires net.minecraftforge.forge; requires net.minecraftforge.javafmlmod; requires net.minecraftforge.mergetool.api; requires org.slf4j; requires logging; } For now, we're leaving the whole module open to reflection, which is a good starting point. When we know we want to close something off, we can remove the open modifier from the module and open or export individual packages instead. Remember that you need to be open to Forge (module name net.minecraftforge.forge), otherwise it can't call your mod's constructor. Next is fixing modules in Gradle. While Forge and Java support modules properly, Gradle does not put automatic modules on the module path by default, meaning that the logging module (from com.mojang:logging) is not found. To fix this, add the Gradle plugin and add a compile-time module definition for that Mojang library: plugins { // (...) id 'org.gradlex.extra-java-module-info' version "1.9" } // (...) extraJavaModuleInfo { failOnMissingModuleInfo = false automaticModule("com.mojang:logging", "logging") } The automatic module override specified in your build.gradle should match the runtime one to avoid errors. You can do the same for any library or mod dependency that is missing either a module-info or explicit Automatic-Module-Name, however be aware that you may need to update your mod once said library adds one. That's all you need to get started with module support in your mods. You can learn more about modules and how to use them at dev.java.
    • Faire la mise à jour grâce à ce lien m'a aider personnellement, merci à @Paint_Ninja. https://www.amd.com/en/support 
    • When I came across the 'Exit Code: I got a 1 error in my Minecraft mods, so I decided to figure out what was wrong. First, I took a look at the logs. In the mods folder (usually where you'd find logs or crash reports), I found the latest.log file or the corresponding crash report. I read it through carefully, looking for any lines with errors or warnings. Then I checked the Minecraft Forge support site, where you can often find info on what causes errors and how to fix them. I then disabled half of my mods and tried running the game. If the error disappeared, it meant that the problem was with the disabled mod. I repeated this several times to find the problem mod.
  • Topics

  • Who's Online (See full list)

×
×
  • Create New...

Important Information

By using this site, you agree to our Terms of Use.