Sorry that this is a bit brief (I can give a longer explanation later), but here are the parts that you need:
- A container class, which handles all of the server side stuff (like crafting)
- A gui class (extending GuiContainer), which handles the client side stuff (like displaying the gui)
- A gui handler, which ensures that the server and client get the right things, note that this needs to be registered using NetworkRegistry.INSTANCE.registerGuiHandler
- In the block's class, you need to override onBlockActivated and open the gui
What this does is when the block is right clicked, it opens the gui (as long as the world is not remote, I forgot what exactly this means). In openGui, it wants the instance of your mod class, and the id, which it will send to your gui handler to get the correct things.
As for the gui handler, you'll want to make a class like this:
public class GuiHandler implements IGuiHandler{
@Override
public Object getServerGuiElement(int ID, EntityPlayer player, World world, int x, int y, int z) {
IBlockState iblockstate = world.getBlockState(new BlockPos(x,y,z));
if(ID == GuiIDs.METAL_WORKBENCH && (iblockstate.getBlock() == RoHBlocks.METAL_WORKBENCH))
return new ContainerMetalWorkbench(player.inventory, world, new BlockPos(x, y, z));
}
@Override
public Object getClientGuiElement(int ID, EntityPlayer player, World world, int x, int y, int z) {
IBlockState iblockstate = world.getBlockState(new BlockPos(x,y,z));
if(ID == GuiIDs.METAL_WORKBENCH && (iblockstate.getBlock() == RoHBlocks.METAL_WORKBENCH))
return new GuiMetalWorkbench(player.inventory, world, new BlockPos(x, y, z));
}
}
What this does is the server one gets called on the server and has to return the correct Container, while the client one has to return the correct GuiContainer. I just set it up so it checks that the id and the block match.
The gui handler needs to be initialized and registered in the main mod class:
private static GuiHandler guihandler = new GuiHandler();
I don't remember why I registered it in the init phase.
public class ContainerMetalWorkbench extends Container{
/** The crafting matrix inventory (3x3). */
public InventoryCrafting craftMatrix = new InventoryCrafting(this, 3, 3);
public InventoryCraftResult craftResult = new InventoryCraftResult();
private final World world;
/** Position of the workbench */
private final BlockPos pos;
private final EntityPlayer player;
public ContainerMetalWorkbench(InventoryPlayer playerInventory, World worldIn, BlockPos blockPos) {
world = worldIn;
this.pos = blockPos;
this.player = playerInventory.player;
//This is the output slot
this.addSlotToContainer(new Workbench1SlotCrafting(playerInventory.player, this.craftMatrix, this.craftResult, 0, 124, 35));
//These are the input slots
for (int i = 0; i < 3; ++i)
{
for (int j = 0; j < 3; ++j)
{
this.addSlotToContainer(new Slot(this.craftMatrix, j + i * 3, 30 + j * 18, 17 + i * 18));
}
}
//these are the first 3 rows of inventory
for (int k = 0; k < 3; ++k)
{
for (int i1 = 0; i1 < 9; ++i1)
{
this.addSlotToContainer(new Slot(playerInventory, i1 + k * 9 + 9, 8 + i1 * 18, 84 + k * 18));
}
}
//this is the hotbar
for (int l = 0; l < 9; ++l)
{
this.addSlotToContainer(new Slot(playerInventory, l, 8 + l * 18, 142));
}
}
/**
* Callback for when the crafting matrix is changed.
*/
public void onCraftMatrixChanged(IInventory inventoryIn)
{
//craftResult.setInventorySlotContents(0, new ItemStack(Blocks.IRON_ORE));
//craftResult.setInventorySlotContents(0, MetalWorkbenchCraftingHandler.instance().getResult(this.craftMatrix, world));
if(!this.world.isRemote) {
EntityPlayerMP eplayer = (EntityPlayerMP) this.player;
//takes the items in the crafting matrix and finds a recipe with those things
IRecipe r = MetalWorkbenchCraftingHandler.instance().getResult((InventoryCrafting) inventoryIn, world);
ItemStack s;
if(r == null) s = ItemStack.EMPTY;
else{s = r.getCraftingResult(this.craftMatrix);}
this.craftResult.setInventorySlotContents(0, s);
eplayer.connection.sendPacket(new SPacketSetSlot(this.windowId, 0, s));
}
}
/**
* Called when the container is closed.
*/
public void onContainerClosed(EntityPlayer playerIn)
{
super.onContainerClosed(playerIn);
if (!this.world.isRemote)
{
this.clearContainer(playerIn, this.world, this.craftMatrix);
}
}
/**
* Determines whether supplied player can use this container
*/
public boolean canInteractWith(EntityPlayer playerIn)
{
if (this.world.getBlockState(this.pos).getBlock() != RoHBlocks.METAL_WORKBENCH)
{
return false;
}
else
{
return playerIn.getDistanceSq((double)this.pos.getX() + 0.5D, (double)this.pos.getY() + 0.5D, (double)this.pos.getZ() + 0.5D) <= 64.0D;
}
}
/**
* Handle when the stack in slot {@code index} is shift-clicked. Normally this moves the stack between the player
* inventory and the other inventory(s).
*/
public ItemStack transferStackInSlot(EntityPlayer playerIn, int index)
{
ItemStack itemstack = ItemStack.EMPTY;
Slot slot = this.inventorySlots.get(index);
if (slot != null && slot.getHasStack())
{
ItemStack itemstack1 = slot.getStack();
itemstack = itemstack1.copy();
if (index == 0)
{
itemstack1.getItem().onCreated(itemstack1, this.world, playerIn);
if (!this.mergeItemStack(itemstack1, 10, 46, true))
{
return ItemStack.EMPTY;
}
slot.onSlotChange(itemstack1, itemstack);
}
else if (index >= 10 && index < 37)
{
if (!this.mergeItemStack(itemstack1, 37, 46, false))
{
return ItemStack.EMPTY;
}
}
else if (index >= 37 && index < 46)
{
if (!this.mergeItemStack(itemstack1, 10, 37, false))
{
return ItemStack.EMPTY;
}
}
else if (!this.mergeItemStack(itemstack1, 10, 46, false))
{
return ItemStack.EMPTY;
}
if (itemstack1.isEmpty())
{
slot.putStack(ItemStack.EMPTY);
}
else
{
slot.onSlotChanged();
}
if (itemstack1.getCount() == itemstack.getCount())
{
return ItemStack.EMPTY;
}
ItemStack itemstack2 = slot.onTake(playerIn, itemstack1);
if (index == 0)
{
playerIn.dropItem(itemstack2, false);
}
}
return itemstack;
}
/**
* Called to determine if the current slot is valid for the stack merging (double-click) code. The stack passed in
* is null for the initial slot that was double-clicked.
*/
public boolean canMergeSlot(ItemStack stack, Slot slotIn)
{
return slotIn.inventory != this.craftResult && super.canMergeSlot(stack, slotIn);
}
}
So this class references a crafting handler, which I created to initialize the recipes and do some things like find the result of the recipes. I also used shaped recipes which technically doesn't need to be done, you just need a way to output the correct result
The gui class:
public class GuiMetalWorkbench extends GuiContainer{
private static final ResourceLocation METAL_WORKBENCH_GUI_TEXTURE = new ResourceLocation(Reference.MOD_ID + ":textures/gui/container/metal_workbench.png");
private final IInventory playerInventory;
public GuiMetalWorkbench(InventoryPlayer inventory, World world, BlockPos blockPos) {
super(new ContainerMetalWorkbench(inventory, world, blockPos));
playerInventory=inventory;
}
/**
* Draws the screen and all the components in it.
*/
public void drawScreen(int mouseX, int mouseY, float partialTicks)
{
this.drawDefaultBackground();
super.drawScreen(mouseX, mouseY, partialTicks);
this.renderHoveredToolTip(mouseX, mouseY);
}
protected void drawGuiContainerForegroundLayer(int mouseX, int mouseY)
{
this.fontRenderer.drawString("Metal Workbench", 28, 6, 4210752);
this.fontRenderer.drawString(I18n.format("container.inventory"), 8, this.ySize - 96 + 2, 4210752);
}
protected void drawGuiContainerBackgroundLayer(float partialTicks, int mouseX, int mouseY)
{
GlStateManager.color(1.0F, 1.0F, 1.0F, 1.0F);
this.mc.getTextureManager().bindTexture(METAL_WORKBENCH_GUI_TEXTURE);
int i = this.guiLeft;
int j = (this.height - this.ySize) / 2;
this.drawTexturedModalRect(i, j, 0, 0, this.xSize, this.ySize);
}
}
I mostly copied this from the crafting table gui I think.
For checking the source code, I know that if you use the standard "setupDecompWorkspace", then you should be able to check the source code under referenced libraries. It should be under something like forge-1.12.2, and then under net.minecraft.
I would recommend looking at the anvil code in 1.12.2 and then gutting it to turn it into your container rather than doing the smithing table in 1.16 because a lot of things change between versions.
Your container only has 39 slots (30-38 are the hotbar, 3-29 are the rest of the player inventory, 0 and 1 are input slots, and 2 is the output slot). I suspect that it crashes because it looks in slots that do not exist. It won't do that with a sword because the sword doesn't look for stacks to merge with. What you want to do is probably check if the item has a max stack size of one and then try to merge it in the first input slot, and otherwise try to merge it in the second input slot.
I would recommend looking at the ContainerRepair class, specifically the transferStackInSlot method.
I would also recommend overriding isItemValid for your output slot and returning false, so that you can't put items in the output slot, along with overriding onTake to remove the items in the input slots (a good example is also in the ContainerRepair class).
Nevermind I did it myself. I created a custom sound. I want to play the sound everytime I take something from the output slot, Just like the anvil after everytime we repair or enchant it plays a sound.
I'm new to modding I know only how to create a basic block or item.
But I want to make more like a custom crafting bench which actually does the same crafting but gives my mod items.
How to I exactly create it I need some sort of guide or tutorial.
Any help would be appreciated
Sorry that this is a bit brief (I can give a longer explanation later), but here are the parts that you need:
- A container class, which handles all of the server side stuff (like crafting)
- A gui class (extending GuiContainer), which handles the client side stuff (like displaying the gui)
- A gui handler, which ensures that the server and client get the right things, note that this needs to be registered using NetworkRegistry.INSTANCE.registerGuiHandler
- In the block's class, you need to override onBlockActivated and open the gui
Uncrafting Table (1.15.2): https://www.minecraftforum.net/forums/mapping-and-modding-java-edition/minecraft-mods/wip-mods/3032965-uncrafting-table
Ruins of Humanity (mod, no longer being developed): https://www.minecraftforum.net/forums/mapping-and-modding-java-edition/minecraft-mods/wip-mods/2898701-1-12-2-wip-early-alpha-ruins-of-humanity
Thanks for the help buddy. Can you give me a more detailed and comprehensive tutorial on this?
I made something exactly like this once, so the code I am posting has a lot of references to the thing I did.
So first, in your block class, you'll want to add this:
What this does is when the block is right clicked, it opens the gui (as long as the world is not remote, I forgot what exactly this means). In openGui, it wants the instance of your mod class, and the id, which it will send to your gui handler to get the correct things.
As for the gui handler, you'll want to make a class like this:
What this does is the server one gets called on the server and has to return the correct Container, while the client one has to return the correct GuiContainer. I just set it up so it checks that the id and the block match.
The gui handler needs to be initialized and registered in the main mod class:
I don't remember why I registered it in the init phase.
Where instance is the instance of the mod class
Here is the container class:
So this class references a crafting handler, which I created to initialize the recipes and do some things like find the result of the recipes. I also used shaped recipes which technically doesn't need to be done, you just need a way to output the correct result
The gui class:
I mostly copied this from the crafting table gui I think.
Uncrafting Table (1.15.2): https://www.minecraftforum.net/forums/mapping-and-modding-java-edition/minecraft-mods/wip-mods/3032965-uncrafting-table
Ruins of Humanity (mod, no longer being developed): https://www.minecraftforum.net/forums/mapping-and-modding-java-edition/minecraft-mods/wip-mods/2898701-1-12-2-wip-early-alpha-ruins-of-humanity
Thanks for the help. I'll try this later because now, I have a lot of work to do.
My final question: The code you have posted is for 1.12.2 right?
And how do I check the source code for vanilla blocks like you did for the crafting table?
Yes, the code I posted is for 1.12.2.
For checking the source code, I know that if you use the standard "setupDecompWorkspace", then you should be able to check the source code under referenced libraries. It should be under something like forge-1.12.2, and then under net.minecraft.
Uncrafting Table (1.15.2): https://www.minecraftforum.net/forums/mapping-and-modding-java-edition/minecraft-mods/wip-mods/3032965-uncrafting-table
Ruins of Humanity (mod, no longer being developed): https://www.minecraftforum.net/forums/mapping-and-modding-java-edition/minecraft-mods/wip-mods/2898701-1-12-2-wip-early-alpha-ruins-of-humanity
Hi, I'm back.
I did a lot of work and the smithing table in 1.16 suits my needs so, I tried back porting it.
I created the block, the tile entity, the container, GUI class and GUI Proxy (ie. GUIHandler).
I made it work when the player right clicks the block it opens the Smithing GUI.
All the slots are aligned properly.
However, I have some problems.
The item tool tips are not showing, when I close the inventory of the block the items that I put in don't come back to my inventory,
the items are being stored in the container, if I break the block Items don't drop.
And I'm able to put items into the output slot.
How can I disable that?
Are there any other methods I should add?
Would be nice if you could help me.
I would recommend looking at the anvil code in 1.12.2 and then gutting it to turn it into your container rather than doing the smithing table in 1.16 because a lot of things change between versions.
You don't need a tile entity if you do not need to store extra data in the block (such as the itemstacks of a chest).
1. Make sure you have
called in draw screen
2. Make sure you have
In your container class, this will cause it to drop all items when you close it.
3. The items are probably being stored in the tile entity, but there isn't the code to drop the stacks when the block is broken.
You need to override isItemValid and return false.
Uncrafting Table (1.15.2): https://www.minecraftforum.net/forums/mapping-and-modding-java-edition/minecraft-mods/wip-mods/3032965-uncrafting-table
Ruins of Humanity (mod, no longer being developed): https://www.minecraftforum.net/forums/mapping-and-modding-java-edition/minecraft-mods/wip-mods/2898701-1-12-2-wip-early-alpha-ruins-of-humanity
I removed the tile entity. As you said it was not necessary
The container is working but I exactly don't know how to create that slot class(For output slot) you mentioned.
Can you give me some more details on it?
And I have a github repository for you to reference:
https://github.com/Johan2403/JustMoreStuff-1.12.2/tree/master/JustMoreStuff
When I shift click a tool such as a sword it doesn't get into the slots but when I shift click an itemstack the game crashes.
I want the tool to enter the first slot and the itemstack to the second slot.
Please reply
Your container only has 39 slots (30-38 are the hotbar, 3-29 are the rest of the player inventory, 0 and 1 are input slots, and 2 is the output slot). I suspect that it crashes because it looks in slots that do not exist. It won't do that with a sword because the sword doesn't look for stacks to merge with. What you want to do is probably check if the item has a max stack size of one and then try to merge it in the first input slot, and otherwise try to merge it in the second input slot.
I would recommend looking at the ContainerRepair class, specifically the transferStackInSlot method.
I would also recommend overriding isItemValid for your output slot and returning false, so that you can't put items in the output slot, along with overriding onTake to remove the items in the input slots (a good example is also in the ContainerRepair class).
Uncrafting Table (1.15.2): https://www.minecraftforum.net/forums/mapping-and-modding-java-edition/minecraft-mods/wip-mods/3032965-uncrafting-table
Ruins of Humanity (mod, no longer being developed): https://www.minecraftforum.net/forums/mapping-and-modding-java-edition/minecraft-mods/wip-mods/2898701-1-12-2-wip-early-alpha-ruins-of-humanity
How do I create crafting recipes that is if I had a tool in the first slot and a item in the second slot it gives me a output?
You need to check if the items are in the correct slots and then if they are, get the output. In the ContainerRepair class, this is how it is done:
In the constructor:
onCraftMatrixChanged:
Where updateRepairOutput is the method that checks the input slots and then puts the output in the output slot.
Uncrafting Table (1.15.2): https://www.minecraftforum.net/forums/mapping-and-modding-java-edition/minecraft-mods/wip-mods/3032965-uncrafting-table
Ruins of Humanity (mod, no longer being developed): https://www.minecraftforum.net/forums/mapping-and-modding-java-edition/minecraft-mods/wip-mods/2898701-1-12-2-wip-early-alpha-ruins-of-humanity
I added a few recipes and they work fine how it works is it upgrades tools along with all existing enchantments.
But if the tools' durability is reduced the output item doesn't have the same durability it resets it to max that is a problem I have.
How do I solve it? You can check the GitHub I always commit and push whenever I make changes.
Please help.
Nevermind I did it myself. I created a custom sound. I want to play the sound everytime I take something from the output slot, Just like the anvil after everytime we repair or enchant it plays a sound.
Can you please tell me how to do it?
BUMP!
I made it play a custom sound after crafting myself.
Thanks V5_ for helping me complete this block it works fine and I'm able
to create recipes for it.
I need to learn more new stuff in modding and maybe even try to use the latest
versions of Forge.
Finally, Thanks for your help.