Jump to content

Recommended Posts

Posted

So i need to create a new itemstack or remove an item stack in a slot in a custom gui but it either doesn't do anything at all or it crashes. I have not dealt with packet handlers and i am almost certain that is it. I have tried following the tut on the wiki for packet handling and tried implementing it into my problem but it didn't change anything. I still think that might be the problem.

 

The whole setup includes a button on which clicked will either created a new item or delete the item.

 

I can post the code if needed but its almost identical to the one on the wiki.

 

Any suggestions or packet handling code for this type of problem will be appreciated

  • Replies 50
  • Created
  • Last Reply

Top Posters In This Topic

Posted

Have the button call the setInventorySlotContents method in your tile entity.

 

Sorry about the code not being formatted properly but the code button doesn't seem to be working for me atm :/

 

Ive tried that - I doesn't seem to work. Ive put the slot id which is 1 - i think and the new ItemStack(the id of the item, 1, 0) but it doesn't create the item in the slot - thats y i thought it could be a packet handler job. This is what i have so far - Gui class with button:

 

switch(b.id){

		case 2:{				

			tile.setInventorySlotContents(1, new ItemStack(Item.appleGold, 1, 0));

			break;
		}

 

And then my tile entity class as a whole:

 

package net.mod.gui;

import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.inventory.IInventory;
import net.minecraft.item.ItemStack;
import net.minecraft.nbt.NBTTagCompound;
import net.minecraft.nbt.NBTTagList;
import net.minecraft.tileentity.TileEntity;

public class TileEntityReplacer extends TileEntity implements IInventory {

private ItemStack[] inv;

public TileEntityReplacer() {
	inv = new ItemStack[9];
}

@Override
public void closeChest() {}

@Override
public ItemStack decrStackSize(int i, int j) {
	ItemStack stack = getStackInSlot(i);
	if (stack != null) {
		if (stack.stackSize <= j) {
			setInventorySlotContents(i, null);
		} else {
			stack = stack.splitStack(j);
			if (stack.stackSize == 0) {
				setInventorySlotContents(i,null);
			}
		}
	}
	return stack;
}

@Override
public String getInvName() {
	return "Replacer";
}

@Override
public int getInventoryStackLimit() {
	return 1;
}

@Override
public int getSizeInventory() {
	return inv.length;
}

@Override
public ItemStack getStackInSlot(int i) {
	return inv[i];
}

@Override
public ItemStack getStackInSlotOnClosing(int i) {
	ItemStack stack = getStackInSlot(i);
	if (stack != null) {
		setInventorySlotContents(i, null);
	}
	return stack;
}

@Override
public boolean isInvNameLocalized() {
	// TODO Auto-generated method stub
	return false;
}

@Override
public boolean isItemValidForSlot(int i, ItemStack itemstack) {
	// TODO Auto-generated method stub
	return false;
}

@Override
public boolean isUseableByPlayer(EntityPlayer entityplayer) {
	return worldObj.getBlockTileEntity(xCoord, yCoord, zCoord) == this && entityplayer.getDistanceSq(xCoord+0.5, yCoord+0.5, zCoord+0.5) < 64;
}

@Override
public void openChest() {}

@Override
public void setInventorySlotContents(int i, ItemStack itemstack) {
	inv[i] = itemstack;
	if (itemstack != null && itemstack.stackSize >= getInventoryStackLimit()) {
		itemstack.stackSize = getInventoryStackLimit();
	}
}

@Override public void readFromNBT(NBTTagCompound tagCompound) {
	super.readFromNBT(tagCompound);
	NBTTagList tagList = tagCompound.getTagList("Inventory");
	for (int i = 0; i < tagList.tagCount(); i++) {
		NBTTagCompound tag = (NBTTagCompound) tagList.tagAt(i);
		byte slot = tag.getByte("Slot");
		if (slot >= 0 && slot < inv.length) {
			inv[slot] = ItemStack.loadItemStackFromNBT(tag);
		}
	}
}

@Override public void writeToNBT(NBTTagCompound tagCompound) {
	super.writeToNBT(tagCompound);
	NBTTagList itemList = new NBTTagList();
	for (int i = 0; i < inv.length; i++) {
		ItemStack stack = inv[i];
		if (stack != null) {
			NBTTagCompound tag = new NBTTagCompound();
			tag.setByte("Slot", (byte)i);
			stack.writeToNBT(tag);
			itemList.appendTag(tag);
		}
	}
	tagCompound.setTag("Inventory", itemList);
}


}

 

and my container which in guihandler is called on server side rather than the gui:

 


package net.mod.Gui;

import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.entity.player.InventoryPlayer;
import net.minecraft.inventory.Container;
import net.minecraft.inventory.Slot;
import net.minecraft.item.ItemStack;

public class ContainerReplacer extends Container {

        protected TileEntityReplacer tileEntity;

        public ContainerReplacer (InventoryPlayer inventoryPlayer, TileEntityReplacer te){
                tileEntity = te;

                                addSlotToContainer(new Slot(tileEntity, 0, 44 , 35));
                                        //commonly used vanilla code that adds the player's inventory
                bindPlayerInventory(inventoryPlayer);
        }

        @Override
        public boolean canInteractWith(EntityPlayer player) {
                return tileEntity.isUseableByPlayer(player);
        }


        protected void bindPlayerInventory(InventoryPlayer inventoryPlayer) {
                for (int i = 0; i < 3; i++) {
                        for (int j = 0; j < 9; j++) {
                                addSlotToContainer(new Slot(inventoryPlayer, j + i * 9 + 9,
                                                8 + j * 18, 84 + i * 18));
                        }
                }

                for (int i = 0; i < 9; i++) {
                        addSlotToContainer(new Slot(inventoryPlayer, i, 8 + i * 18, 142));
                }
        }

        @Override
        public ItemStack transferStackInSlot(EntityPlayer player, int slot) {
                ItemStack stack = null;
                Slot slotObject = (Slot) inventorySlots.get(slot);

                //null checks and checks if the item can be stacked (maxStackSize > 1)
                if (slotObject != null && slotObject.getHasStack()) {
                        ItemStack stackInSlot = slotObject.getStack();
                        stack = stackInSlot.copy();

                        //merges the item into player inventory since its in the tileEntity
                        if (slot < 9) {
                                if (!this.mergeItemStack(stackInSlot, 0, 35, true)) {
                                        return null;
                                }
                        }
                        //places it into the tileEntity is possible since its in the player inventory
                        else if (!this.mergeItemStack(stackInSlot, 0, 9, false)) {
                                return null;
                        }

                        if (stackInSlot.stackSize == 0) {
                                slotObject.putStack(null);
                        } else {
                                slotObject.onSlotChanged();
                        }

                        if (stackInSlot.stackSize == stack.stackSize) {
                                return null;
                        }
                        slotObject.onPickupFromSlot(player, stackInSlot);
                }
                return stack;
        }
      

}

 

 

Posted

Your problem is exactly what you suspect: do NOT set data on the client side, you MUST send a packet to the TileEntity to correctly set its inventory contents on the server. The ItemStack and Packet classes already have some handy methods for doing so:

// out is a ByteArrayDataOutput, in is a ByteArrayDataInput
writeNBTTagCompound(stack.writeToNBT(new NBTTagCompound()), out);
stack = ItemStack.loadItemStackFromNBT(readNBTTagCompound(in));

You may have to copy the code for read and write NBTTagCompound from the Packet class to make it accessible, but it's there.

Posted

Your problem is exactly what you suspect: do NOT set data on the client side, you MUST send a packet to the TileEntity to correctly set its inventory contents on the server. The ItemStack and Packet classes already have some handy methods for doing so:

// out is a ByteArrayDataOutput, in is a ByteArrayDataInput
writeNBTTagCompound(stack.writeToNBT(new NBTTagCompound()), out);
stack = ItemStack.loadItemStackFromNBT(readNBTTagCompound(in));

You may have to copy the code for read and write NBTTagCompound from the Packet class to make it accessible, but it's there.

 

Ok - ive had a look through the packet.class but haven't come across anything, I thought i had the sufficient NBTtag methods - ill keep looking through the code but as of i can see theres not much that is helpful to me. - I could be just skipping over something obvious but I'm really fed up with this now - been trying to get this working for about a week now :(

Posted

Turns out they're public:

package net.minecraft.network.packet;

public static void writeItemStack(ItemStack par0ItemStack, DataOutput par1DataOutput) throws IOException

// so you can write your ItemStack to the output stream like this:
Packet.writeItemStack(itemstack, out);

 

So with this do i need to make my packet handler again or is all the needed things in mine craft already? - And you put this method in my Gui class when i click on the button or in the tile entity class in the setIventorySlotContents method?

 

I just don't seem to get PacketHandlers.

Posted

So I've made a new PacketHandler class as follows:

 


package net.mod.Common;

import java.io.ByteArrayInputStream;
import java.io.DataInputStream;
import java.io.IOException;

import net.minecraft.network.INetworkManager;
import net.minecraft.network.packet.Packet250CustomPayload;

import cpw.mods.fml.common.FMLCommonHandler;
import cpw.mods.fml.common.network.IPacketHandler;
import cpw.mods.fml.common.network.Player;

public class PacketHandler implements IPacketHandler {

        @Override
        public void onPacketData(INetworkManager manager,
                        Packet250CustomPayload par1, Player par2) {
                
                if (par1.channel.equals("UniqueModID")) {
                        handleRandom(par1);
                }
        }
        
        private void handleRandom(Packet250CustomPayload par1) {
                DataInputStream In = new DataInputStream(new ByteArrayInputStream(par1.data));
  
        }

}

 

and I've registered it in my common class using:

 


@NetworkMod(clientSideRequired = true, serverSideRequired = true, 
channels={"UniqueModID"}, packetHandler = PacketHandler.class)

 

Would i just send the id of the button - have a switch statement that creates a new item stack based on which button is pressed then use a  side(side.SERVER)and run that method in the tile entity? - I don't think it will work but should i try it ?

Posted

You make an ItemStack the exact same way regardless of what side you are on; it's just a matter of you sending the information you need to create the stack you want, which is going to be the same information you would have used to create the ItemStack on the client gui in the first place.

 

If you are creating different stacks based on the button pressed, for example, just send the button id to the server and handle it there instead.

 

Though you can send an entire ItemStack over the server quite easily by first writing it to NBT and then using CompressedStreamTools to send and receive the NBT tag compound.

Posted

You make an ItemStack the exact same way regardless of what side you are on; it's just a matter of you sending the information you need to create the stack you want, which is going to be the same information you would have used to create the ItemStack on the client gui in the first place.

 

If you are creating different stacks based on the button pressed, for example, just send the button id to the server and handle it there instead.

 

Though you can send an entire ItemStack over the server quite easily by first writing it to NBT and then using CompressedStreamTools to send and receive the NBT tag compound.

 

Ok so I'm finally getting somewhere - Ive got the buttons when pressed set an int to a certain value which is then sent and received by my packethandler class. In there i have it read the int and run a method where that int is a parameter. In there i have it setTileInventoryContents with a new ItemStack . When i create the item stack i have it create the item with an id of 4000 + the int that was sent through the packet.

 

Im not quite sure what I'm doing wrong but nothing shows up in the slot. - Ive added printOuts to check if the packet was being sent and the right int was being sent and that is all fine.

 

And suggestions?

 

Code in packet handler:

 

package net.mod.Common;

import java.io.ByteArrayInputStream;
import java.io.DataInputStream;
import java.io.IOException;

import net.minecraft.item.ItemStack;
import net.minecraft.network.INetworkManager;
import net.minecraft.network.packet.Packet250CustomPayload;
import cpw.mods.fml.common.network.IPacketHandler;
import cpw.mods.fml.common.network.Player;
import cpw.mods.fml.relauncher.Side;
import cpw.mods.fml.relauncher.SideOnly;

public class PacketHandler implements IPacketHandler {

public int number;
TileEntityReplacer te = new TileEntityReplacer();

        @Override
        public void onPacketData(INetworkManager manager,
                        Packet250CustomPayload par1, Player par2) {
                
                if (par1.channel.equals("UniqueModID")) {
                        handleRandom(par1);
                }
        }
        
        private void handleRandom(Packet250CustomPayload par1) {
                DataInputStream In = new DataInputStream(new ByteArrayInputStream(par1.data));
  
                try {
                    number = In.readInt();
            } catch (IOException e) {
                    e.printStackTrace();
                    return;
            }
                
                this.makeReplacement(number);
            
            System.out.println(number);
                
        }
        
        public void makeReplacement(int numberID){
        	
            te.setInventorySlotContents(1, new ItemStack(Common.ItemCustom.itemID + numberID, 1, 0));
        	
        }

}

 

Ive read that you can't just add a @SideOnly(Side.SERVER) in front as it stuffs things up and i have no idea what it does any way

 

Im hopping this time it IS something I've just missed.

 

Posted

Yeah no. You can't just set the inventory contents of a random tile inventory, you have to pass the coordinates of the TileEntity for which you want to set the contents, and get that exact TileEntity from the world.

 

And is there any particular reason you're adding 4000 to the integer you send? That doesn't make any sense to me... why not just send the id of the item you want? Are you sure that 4000+whatever random number you are sending is really an item?

Posted

Ok - So does this mean i have to send like 4 more ints - one for x, y, z and tile entity id? if so - how would i do that with packets - is it just the same thing but 4 times over? I have actually seen somewhere that you can use the Dimension Manager to send coords and stuff but i don't think it worked real well

 

And basically I've added like 10 items or so from 4000 to 4010(They are all the same Item class just different ids) and I've got ten buttons so i just thought if i use that itemCustom + whatever number button was pressed and sent through the packet it would select the right item.

 

Posted

You can write as much information as you need in a packet. See here for an example using a tile entity and some other data.

 

As for the items, no, don't rely on the ID that you think it's using, because those can be changed via config or other things. Just reference your item and get the ID that way, i.e. YourModItems.someItem.itemID, and sent that value in the packet. This way you will never be wrong.

Posted

So for that code you use that instead of the Packet250CustomPayLoad? Once i get all the data sending i can still use the te.setInventorySlotContents(0, new ItemStack(Common.ItemCustom.itemID + numberID, 1, 0)); to set the contents?

 

Also can all this be done using the NBTTagCompounds already in the tile entity class? - Forgot to ask that

Posted

You can use Packet250CustomPayload to do exactly the same thing, I was just providing an example of writing more than a single integer. Wherever you write the packet you will have to write all that data, and then read it all back in the same order when you receive the packet.

 

Yes, you can use the regular tile entity / IInventory methods when you receive the packet.

Posted

Ok so now I'm sending the x y z coords of the tile entity by using this in my Gui class:

public TileEntityReplacer tile = new TileEntityReplacer();
public int PosX = tile.xCoord;
public int PosY = tile.yCoord;
public int PosZ = tile.zCoord;

 

And when button clicked i use:


        ByteArrayOutputStream bos = new ByteArrayOutputStream(;
        DataOutputStream os = new DataOutputStream(bos);
        try {
        	os.writeInt(PosX);
        	os.writeInt(PosY);
        	os.writeInt(PosZ);
                os.writeInt(number);
        } catch (Exception ex) {
                ex.printStackTrace();
        }
        
        Packet250CustomPayload packet = new Packet250CustomPayload();
        packet.channel = "UniqueModID";
        packet.data = bos.toByteArray();
        packet.length = bos.size();
        
        
                Minecraft.getMinecraft().thePlayer.sendQueue.addToSendQueue(packet);

 

And finally i handle this by this:

package net.mod.Common;

import java.io.ByteArrayInputStream;
import java.io.DataInputStream;
import java.io.IOException;

import net.mod.GUI.TileEntityReplacer;
import net.minecraft.client.Minecraft;
import net.minecraft.item.ItemStack;
import net.minecraft.network.INetworkManager;
import net.minecraft.network.packet.Packet250CustomPayload;
import net.minecraft.tileentity.TileEntity;
import cpw.mods.fml.common.network.IPacketHandler;
import cpw.mods.fml.common.network.Player;
import cpw.mods.fml.relauncher.Side;
import cpw.mods.fml.relauncher.SideOnly;

public class PacketHandler implements IPacketHandler {

public int number;
public int PosX, PosY, PosZ;
TileEntityReplacer te = new TileEntityReplacer();

        @Override
        public void onPacketData(INetworkManager manager,
                        Packet250CustomPayload par1, Player par2) {
                
                if (par1.channel.equals("UniqueModID")) {
                        handleRandom(par1);
                }
        }
        
        private void handleRandom(Packet250CustomPayload par1) {
                DataInputStream In = new DataInputStream(new ByteArrayInputStream(par1.data));
  
                try {
                   
                    PosX = In.readInt();
                    PosY = In.readInt();
                    PosZ = In.readInt();
                    		
                    		number = In.readInt();
            } catch (IOException e) {
                    e.printStackTrace();
                    return;
            }
                
                this.makeReplacement(number);
            
            System.out.println(number);
                
        }
        
        public void makeRecord(int numberID){
        	
        	
    			TileEntity te = Minecraft.getMinecraft().thePlayer.worldObj.getBlockTileEntity(PosX, PosY, PosZ);
    			if (te instanceof TileEntityReplacer) {
    				((TileEntityReplacer) te).setInventorySlotContents(0, new ItemStack(Common.ItemCustom.itemID + number, 1, 0));;
    			
    		}
        	
        }

}

 

It still doesn't work - even if i say try to use an apple in the item stack it still won't set the slot contents

Posted

public TileEntityReplacer tile = new TileEntityReplacer();

Why are you creating a new tile entity in your gui??? A tile entity is supposed to be attached to a block in the actual world, you can't just create one in the middle of a gui or anywhere else really and expect it to do anything.

 

Where are you opening this gui from? Is it opened from a block? Does that block have a tile entity? You need to use that tile entity if so, by passing the tile entity in to your gui when you open it. If your gui is not opened from a block, then I don't know how you expect to be setting any tile entity's data from it, packet or not.

Posted

Yea - I've got it opening from a block but couldn't think of any other way of getting the x, y, z coords of the tile entity without using those variables. How else would i do this? - do i send the packets that contain the x y z from say the block instead when clicked on?

Posted

Well this is what i have in my guy handler:

 

package net.mod.GUI;

import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.tileentity.TileEntity;
import net.minecraft.world.World;
import cpw.mods.fml.common.network.IGuiHandler;

public class GuiHandler implements IGuiHandler {
        //returns an instance of the Container you made earlier
        @Override
        public Object getServerGuiElement(int id, EntityPlayer player, World world,
                        int x, int y, int z) {
        	
                
                TileEntity tileEntity = world.getBlockTileEntity(x, y, z);
                if(tileEntity instanceof TileEntityReplacer){
                        return new ContainerReplacer(player.inventory, (TileEntityReplacer) tileEntity);
                }
                return null;
                
                
        }

        //returns an instance of the Gui you made earlier
        @Override
        public Object getClientGuiElement(int id, EntityPlayer player, World world,
                        int x, int y, int z) {
        	
       
                TileEntity tileEntity = world.getBlockTileEntity(x, y, z);
                if(tileEntity instanceof TileEntityReplacer){
                        return new GuiReplacer(player.inventory, (TileEntityReplacer) tileEntity);
                }
                return null;

        }
}

 

If you can spot the problem with all my latest code that would be great - I had it all working back in 1.4.7 but i guess a fair few things have changed since then

Posted

Do you see in your Container class that you store the TileEntity in "protected TileEntityReplacer tileEntity;" ? Do the exact same thing in your Gui, then when you need to send the packet you have the tile entity you need, so you can use te.xCoord, te.zCoord, etc. as parameters in your packet constructor, or pass it directly:

public YourPacket(TileEntity te, int itemID) {
this.x = te.xCoord;
this.y = te.yCoord;
this.z = te.zCoord;
this.item = itemID;
}

Since you don't have a custom packet class though, you would just write those values directly to the output stream when creating your custom 250 payload packet.

Posted

Ok so I've made the appropriate changes and now it creates the item in slot but i think i have sync issues now as it just disappears when i try to pick it up.

 

hers my code in my packet handler that creates the item:

 

public void makeReplacement(int numberID){
        	
        	
    			TileEntity te = Minecraft.getMinecraft().thePlayer.worldObj.getBlockTileEntity(PosX, PosY, PosZ);
    			if (te instanceof TileEntityReplacer) {
    				((TileEntityReplacer) te).setInventorySlotContents(0, new ItemStack(Item.appleGold));;
    			
    		}
        	
        }

 

I will be using the int numberID in later usage of the Gui

Guest
This topic is now closed to further replies.

Announcements




×
×
  • Create New...

Important Information

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