no, just changing the instance string to lowercase and public static RM instance; to public static RM instance = new RM(); worked for me. Thank you
No problem. Still, you should use your Reference.MOD_ID for the instance string, since that's what you're using elsewhere, and then if you ever change it, it changes everywhere
Put out some notes on how to update your custom player inventory / extended properties to 1.7.2, including how to update your KeyHandler and Network code. Hope you find it useful.
I tried creating an extended inventory myself and I got the GUI opening up via a keybind.
I got the containers working too but when I tried moving items around I was not able to pick them up and I couldn't figure out why.
Thats were your tutorial came in.
I literally copy-pasted all your classes and I'm getting the same strange occurrence.
Below is my code (I removed everything I could that I thought was unnecessary like imports and methods that arent used in opening and maintaining the GUI since I'm not crashing.):
Main Class
//Mod Information
@Mod(modid = Reference.MODID, name = Reference.MODNAME, version = Reference.MODVERSION)
@NetworkMod(clientSideRequired=true, serverSideRequired=false,
channels={"GenericClass", "GenericSpec"}, packetHandler = PacketHandler.class)
public class FantasyCraft {
@Instance(Reference.MODID)
public static FantasyCraft instance;
//Proxy Instance
@SidedProxy( clientSide = "unrelentless.fantasycraft.proxy.ClientProxy", serverSide = "unrelentless.fantasycraft.proxy.CommonProxy")
public static CommonProxy proxy;
@EventHandler
public void preInit(FMLPreInitializationEvent event) throws IOException{
}
@EventHandler
public void init(FMLInitializationEvent event){
/* *//**
* Called from the main game loop to update the screen.
*//*
@Override
public void updateScreen()
{
if (this.mc.playerController.isInCreativeMode())
{
this.mc.displayGuiScreen(new GuiContainerCreative(this.mc.thePlayer));
}
}*/
/* *//**
* Adds the buttons (and other controls) to the screen in question.
*//*
@Override
public void initGui()
{
this.buttonList.clear();
/**
* Draw the foreground layer for the GuiContainer (everything in front of the items)
*/
@Override
protected void drawGuiContainerForegroundLayer(int par1, int par2)
{
this.fontRenderer.drawString(""+props.getJob(), 101, 5, 0x0033FF);
this.fontRenderer.drawString("("+props.getSpec()+")", 101, 15, 0x0080FF);
/**
* Draws the screen and all the components in it.
*/
@Override
public void drawScreen(int par1, int par2, float par3)
{
super.drawScreen(par1, par2, par3);
this.xSize_lo = (float)par1;
this.ySize_lo = (float)par2;
}
/**
* Draw the background layer for the GuiContainer (everything behind the items)
*/
@Override
protected void drawGuiContainerBackgroundLayer(float par1, int par2, int par3)
{
GL11.glColor4f(1.0F, 1.0F, 1.0F, 1.0F);
this.mc.getTextureManager().bindTexture(textureLoc);
int k = this.guiLeft;
int l = this.guiTop;
this.drawTexturedModalRect(k, l, 0, 0, this.xSize, this.ySize);
drawGraphic(k + 51, l + 75, 30, (float)(k + 51) - this.xSize_lo, (float)(l + 75 - 50) - this.ySize_lo, this.mc.thePlayer);
}
public class InventoryStats implements IInventory
{
/** The name for your custom inventory, possibly just "Inventory" */
private final String name = "Custom Inventory";
/** In case your inventory name is too generic, define a name to store the NBT tag in as well */
private final String tagName = "CustomInvTag";
/** Define the inventory size here for easy reference */
// This is also the place to define which slot is which if you have different types,
// for example SLOT_SHIELD = 0, SLOT_AMULET = 1;
public static final int INV_SIZE = 2;
/** Inventory's size must be same as number of slots you add to the Container class */
ItemStack[] inventory = new ItemStack[INV_SIZE];
public InventoryStats()
{
// don't need anything here!
}
@Override
public int getSizeInventory()
{
return inventory.length;
}
@Override
public ItemStack getStackInSlot(int slot)
{
return inventory[slot];
}
@Override
public ItemStack decrStackSize(int slot, int amount)
{
ItemStack stack = getStackInSlot(slot);
if (stack != null)
{
if (stack.stackSize > amount)
{
stack = stack.splitStack(amount);
if (stack.stackSize == 0)
{
setInventorySlotContents(slot, null);
}
}
else
{
setInventorySlotContents(slot, null);
}
@Override
public String getInvName()
{
return name;
}
@Override
public boolean isInvNameLocalized()
{
return name.length() > 0;
}
/**
* Our custom slots are similar to armor - only one item per slot
*/
@Override
public int getInventoryStackLimit()
{
return 1;
}
@Override
public void onInventoryChanged()
{
for (int i = 0; i < this.getSizeInventory(); ++i)
{
if (this.getStackInSlot(i) != null && this.getStackInSlot(i).stackSize == 0)
this.setInventorySlotContents(i, null);
}
}
@Override
public boolean isUseableByPlayer(EntityPlayer entityplayer)
{
return true;
}
@Override
public void openChest() {}
@Override
public void closeChest() {}
/**
* This method doesn't seem to do what it claims to do, as
* items can still be left-clicked and placed in the inventory
* even when this returns false
*/
@Override
public boolean isItemValidForSlot(int slot, ItemStack itemstack)
{
// If you have different kinds of slots, then check them here:
// if (slot == SLOT_SHIELD && itemstack.getItem() instanceof ItemShield) return true;
// For now, only ItemUseMana items can be stored in these slots
return true;
}
public void writeToNBT(NBTTagCompound tagcompound)
{
NBTTagList nbttaglist = new NBTTagList();
for (int i = 0; i < this.getSizeInventory(); ++i)
{
if (this.getStackInSlot(i) != null)
{
NBTTagCompound nbttagcompound1 = new NBTTagCompound();
nbttagcompound1.setByte("Slot", (byte) i);
this.getStackInSlot(i).writeToNBT(nbttagcompound1);
nbttaglist.appendTag(nbttagcompound1);
}
}
// We're storing our items in a custom tag list using our 'tagName' from above
// to prevent potential conflicts
tagcompound.setTag(tagName, nbttaglist);
}
public void readFromNBT(NBTTagCompound tagcompound)
{
NBTTagList nbttaglist = tagcompound.getTagList(tagName);
for (int i = 0; i < nbttaglist.tagCount(); ++i)
{
NBTTagCompound nbttagcompound1 = (NBTTagCompound)nbttaglist.tagAt(i);
byte b0 = nbttagcompound1.getByte("Slot");
public class ContainerStats extends Container
{
/** Avoid magic numbers! This will greatly reduce the chance of you making errors in 'transferStackInSlot' method */
private static final int ARMOR_START = InventoryStats.INV_SIZE, ARMOR_END = ARMOR_START+3,
INV_START = ARMOR_END+1, INV_END = INV_START+26, HOTBAR_START = INV_END+1,
HOTBAR_END = HOTBAR_START+8;
public ContainerStats(EntityPlayer player, InventoryPlayer inventoryPlayer, InventoryStats inventoryCustom)
{
int i;
// Add CUSTOM slots - we'll just add two for now, both of the same type.
// Make a new Slot class for each different item type you want to add
this.addSlotToContainer(new SlotFCraftArmor(inventoryCustom, 0, 80, 8));
this.addSlotToContainer(new SlotFCraftArmor(inventoryCustom, 1, 80, 26));
// Add ARMOR slots; note you need to make a public version of SlotArmor
// just copy and paste the vanilla code into a new class and change what you need
for (i = 0; i < 4; ++i)
{
this.addSlotToContainer(new SlotArmorCopy(player.inventoryContainer, inventoryPlayer, inventoryPlayer.getSizeInventory() - 1 - i, 8, 8 + i * 18, i));
}
// Add vanilla PLAYER INVENTORY - just copied/pasted from vanilla classes
for (i = 0; i < 3; ++i)
{
for (int j = 0; j < 9; ++j)
{
this.addSlotToContainer(new Slot(inventoryPlayer, j + i * 9 + 9, 8 + j * 18, 84 + i * 18));
}
}
// Add ACTION BAR - just copied/pasted from vanilla classes
for (i = 0; i < 9; ++i)
{
this.addSlotToContainer(new Slot(inventoryPlayer, i, 8 + i * 18, 142));
}
}
/**
* This should always return true, since custom inventory can be accessed from anywhere
*/
@Override
public boolean canInteractWith(EntityPlayer player)
{
return true;
}
/**
* Called when a player shift-clicks on a slot. You must override this or you will crash when someone does that.
* Basically the same as every other container I make, since I define the same constant indices for all of them
*/
public ItemStack transferStackInSlot(EntityPlayer player, int par2)
{
ItemStack itemstack = null;
Slot slot = (Slot) this.inventorySlots.get(par2);
// Either armor slot or custom item slot was clicked
if (par2 < INV_START)
{
// try to place in player inventory / action bar
if (!this.mergeItemStack(itemstack1, INV_START, HOTBAR_END + 1, true))
{
return null;
}
slot.onSlotChange(itemstack1, itemstack);
}
// Item is in inventory / hotbar, try to place either in custom or armor slots
else
{
// if item is our custom item
if (itemstack1.getItem() instanceof Item)
{
if (!this.mergeItemStack(itemstack1, 0, InventoryStats.INV_SIZE, false))
{
return null;
}
}
// if item is armor
else if (itemstack1.getItem() instanceof ItemArmor)
{
int type = ((ItemArmor) itemstack1.getItem()).armorType;
if (!this.mergeItemStack(itemstack1, ARMOR_START + type, ARMOR_START + type + 1, false))
{
return null;
}
}
// item in player's inventory, but not in action bar
else if (par2 >= INV_START && par2 < HOTBAR_START)
{
// place in action bar
if (!this.mergeItemStack(itemstack1, HOTBAR_START, HOTBAR_START + 1, false))
{
return null;
}
}
// item in action bar - place in player inventory
else if (par2 >= HOTBAR_START && par2 < HOTBAR_END + 1)
{
if (!this.mergeItemStack(itemstack1, INV_START, INV_END + 1, false))
{
return null;
}
}
}
if (itemstack1.stackSize == itemstack.stackSize)
{
return null;
}
slot.onPickupFromSlot(player, itemstack1);
}
return itemstack;
}
}
Slots
public class SlotArmorCopy extends Slot
{
/**
* The armor type that can be placed on that slot, it uses the same values of armorType field on ItemArmor.
*/
final int armorType;
/**
* The parent class of this clot, ContainerPlayer, SlotArmor is a Anon inner class.
*/
final Container parent;
public SlotArmorCopy(Container container, IInventory par2IInventory, int par3, int par4, int par5, int par6)
{
super(par2IInventory, par3, par4, par5);
this.parent = container;
this.armorType = par6;
}
/**
* Returns the maximum stack size for a given slot (usually the same as getInventoryStackLimit(), but 1 in the case
* of armor slots)
*/
public int getSlotStackLimit ()
{
return 1;
}
/**
* Check if the stack is a valid item for this slot. Always true beside for the armor slots.
*/
public boolean isItemValid (ItemStack par1ItemStack)
{
Item item = (par1ItemStack == null ? null : par1ItemStack.getItem());
boolean isValidArmor = false;
if (item instanceof ItemArmor) {
isValidArmor = (((ItemArmor)item).armorType == armorType);
}
return item != null && (isValidArmor || (item instanceof ItemBlock && armorType == 0));
}
@SideOnly(Side.CLIENT)
/**
* Returns the icon index on items.png that is used as background image of the slot.
*/
public Icon getBackgroundIconIndex ()
{
return ItemArmor.func_94602_b(this.armorType);
}
}
-------------------------------------------------------------------------------------
public class SlotFCraftArmor extends Slot {
public SlotFCraftArmor(IInventory inventoryStats, int slotIndex, int xDisp, int yDisp) {
super(inventoryStats, slotIndex, xDisp, yDisp);
}
/**
* Returns the maximum stack size for a given slot (usually the same as getInventoryStackLimit(), but 1 in the case
* of armor slots)
*/
public int getSlotStackLimit ()
{
return 1;
}
/**
* Check if the stack is a valid item for this slot. Always true beside for the armor slots.
*/
public boolean isItemValid (ItemStack par1ItemStack)
{
Item item = (par1ItemStack == null ? null : par1ItemStack.getItem());
return item != null && (item instanceof FCraftArmor);
}
}
Extended Properties
public class FCraftJobCore implements IExtendedEntityProperties{
/*each new instance of extended player requires a uniqiue name*/
public static final String EXT_PROP_NAME = "playerClassExtention";
/*include the entity to which the properties belong for easy access
final because we won't be changing which player it is*/
private final EntityPlayer player;
public final InventoryStats inventory = new InventoryStats();
public FCraftJobCore(EntityPlayer player){
this.player = player;
}
/**
* Used to register these extended properties for the player during EntityConstructing event
* This method is for convenience only; it will make your code look nicer
*/
public static final void register(EntityPlayer player){
player.registerExtendedProperties(FCraftJobCore.EXT_PROP_NAME, new FCraftJobCore(player));
}
/**
* Returns ExtendedPlayer properties for player
* This method is for convenience only; it will make your code look nicer
*/
public static final FCraftJobCore get(EntityPlayer player){
return (FCraftJobCore) player.getExtendedProperties(EXT_PROP_NAME);
}
// Save any custom data that needs saving here
@Override
public void saveNBTData(NBTTagCompound compound) {
// We need to create a new tag compound that will save everything for our Extended Properties
NBTTagCompound properties = new NBTTagCompound();
this.inventory.writeToNBT(properties);
/*
Now add our custom tag to the player's tag with a unique name (our property's name).
This will allow you to save multiple types of properties and distinguish between them.
If you only have one type, it isn't as important, but it will still avoid conflicts
between your tag names and vanilla tag names. For instance, if you add some "Items" tag,
that will conflict with vanilla. Not good. So just use a unique tag name.
*/
compound.setTag(EXT_PROP_NAME, properties);
}
// Load whatever data you saved
@Override
public void loadNBTData(NBTTagCompound compound) {
// Here we fetch the unique tag compound we set for this class of Extended Properties
NBTTagCompound properties = (NBTTagCompound) compound.getTag(EXT_PROP_NAME);
this.inventory.readFromNBT(properties);
}
@Override
public void init(Entity entity, World world) {
}
public final void sync()
{
ByteArrayOutputStream bos = new ByteArrayOutputStream(8);
DataOutputStream outputStream = new DataOutputStream(bos);
// We'll write max mana first so when we set current mana client
// side, it doesn't get set to 0 (see methods below)
try {
outputStream.writeByte(PacketHandler.EXTENDED_PROPERTIES);
outputStream.writeInt(this.currentJob);
outputStream.writeInt(this.currentSpecialization);
} catch (Exception ex) {
ex.printStackTrace();
}
Packet250CustomPayload packet = new Packet250CustomPayload("GenericClass", bos.toByteArray());
// We only want to send from the server to the client
if (FMLCommonHandler.instance().getEffectiveSide().isServer()) {
EntityPlayerMP player1 = (EntityPlayerMP) player;
PacketDispatcher.sendPacketToPlayer(packet, (Player) player1);
}
}
}
}
Keybinding (Your code)
(I got rid of the if statement because I couldnt find tutkeymap anywhere in your code)
public class KeybindHelper {
/** Key index for easy handling */
public static final int CUSTOM_INV = 0;
/** This stores all of our key bindings and is always updated with the in-game settings */
public static final KeyBinding[] keys = new KeyBinding[desc.length];
/** This initializes and registers all the key bindings */
public static void init()
{
boolean[] repeat = new boolean[desc.length];
// just use a for loop to run through all the values
for (int i = 0; i < desc.length; ++i) {
keys[i] = new KeyBinding(desc[i], keyValues[i]);
repeat[i] = false;
}
@SideOnly(Side.CLIENT)
public class KeybindHandler extends KeyHandler
{
/** Not really important. I use it to store/find keys in the config file */
public static final String label = "Tutorial Key";
public KeybindHandler(KeyBinding[] keyBindings, boolean[] repeatings) {
super(keyBindings, repeatings);
}
@Override
public String getLabel() {
return this.label;
}
@Override
public void keyDown(EnumSet<TickType> types, KeyBinding kb, boolean tickEnd, boolean isRepeat)
{
if (tickEnd && FMLCommonHandler.instance().getEffectiveSide() == Side.CLIENT)
{
EntityPlayer player = FMLClientHandler.instance().getClient().thePlayer;
// If the custom inventory screen is open, close it
if (player.openContainer != null && player.openContainer instanceof ContainerStats)
player.closeScreen();
// Otherwise, open the screen. Here you will need to send a packet to the server telling it
// to open the corresponding server gui element, or your inventory won't function
else if (FMLClientHandler.instance().getClient().inGameHasFocus)
{
// Send a packet to the server; here I use a custom packet class to build the packet for me
// Don't worry, we'll cover this in the next step
((EntityClientPlayerMP) player).sendQueue.addToSendQueue(PacketOpenServerGui.getPacket(3));
// The client-side Gui element should open automatically when the server opens the Gui
// If for some reason it does not, you can open it manually on the client side here:
player.openGui(FantasyCraft.instance, 3, player.worldObj, (int) player.posX, (int) player.posY, (int) player.posZ);
}
}
}
@Override
public void keyUp(EnumSet<TickType> types, KeyBinding kb, boolean tickEnd) {
// Don't need to do anything here!
}
@Override
public EnumSet<TickType> ticks() {
// We're only interested in client ticks, as that's when the keyboard will fire
return EnumSet.of(TickType.CLIENT);
}
}
PacketHandler
public class PacketHandler implements IPacketHandler{
public static final byte EXTENDED_PROPERTIES = 1, OPEN_SERVER_GUI = 2;
@Override
public void onPacketData(INetworkManager manager,
Packet250CustomPayload packet, Player player) {
DataInputStream inputStream = new DataInputStream(new ByteArrayInputStream(packet.data));
byte packetType;
try {
// Read the packet type
packetType = inputStream.readByte();
} catch (IOException e) {
e.printStackTrace();
return;
}
private void handleOpenServerGui(Packet250CustomPayload packet, EntityPlayer player, DataInputStream inputStream)
{
int guiID;
// inputStream is already open, so we don't need to do anything other than continue reading from it:
try {
guiID = inputStream.readInt();
} catch (IOException e) {
e.printStackTrace();
return;
}
// Now we can open the server gui element:
player.openGui(FantasyCraft.instance, guiID, player.worldObj, (int) player.posX, (int) player.posY, (int) player.posZ);
}
}
Proxy
public class ClientProxy extends CommonProxy {
@Override
public void registerRenderThings()
{
KeybindHelper.init();
}
@Override
public Object getClientGuiElement(int ID, EntityPlayer player, World world, int x, int y, int z)
{
TileEntity tileEntity = world.getBlockTileEntity(x, y, z);
InventoryStats invStats = FCraftJobCore.get(player).inventory;
switch(ID){
case 3: return new GuiStats(player, player.inventory, invStats);
default: return null;
}
}
}
--------------------------------------------------------------------------------------------------
public class CommonProxy implements IGuiHandler {
/** Used to store IExtendedEntityProperties data temporarily between player death and respawn */
private static final Map<String, NBTTagCompound> extendedEntityData = new HashMap<String, NBTTagCompound>();
public void registerRenderThings() {
}
public void registerSound() {
}
@Override
public Object getClientGuiElement(int ID, EntityPlayer player, World world, int x, int y, int z)
{
return null;
}
@Override
public Object getServerGuiElement(int ID, EntityPlayer player, World world, int x, int y, int z)
{
TileEntity tileEntity = world.getBlockTileEntity(x, y, z);
InventoryStats invStats = FCraftJobCore.get(player).inventory;
if(tileEntity != null)
{
switch(ID)
{
case 1: return new ContainerDopingStation(player.inventory, (TileEntityDopingStation)tileEntity);
//case 3: return new ContainerStats(player, player.inventory, invStats);
}
}
return tileEntity;
}
/**
* Adds an entity's custom data to the map for temporary storage
* @param compound An NBT Tag Compound that stores the IExtendedEntityProperties data only
*/
public static void storeEntityData(String name, NBTTagCompound compound)
{
extendedEntityData.put(name, compound);
}
/**
* Removes the compound from the map and returns the NBT tag stored for name or null if none exists
*/
public static NBTTagCompound getEntityData(String name)
{
return extendedEntityData.remove(name);
}
}
EDIT: Missed one.
PacketOpenServerGUI (Your code)
public class PacketOpenServerGui
{
public static Packet250CustomPayload getPacket(int guiID)
{
ByteArrayOutputStream bos = new ByteArrayOutputStream(5);
DataOutputStream outputStream = new DataOutputStream(bos);
try {
// Notice this first line writes the packet id - do this in your ExtendedPlayer.sync() method as well and add 1 to the size.
outputStream.writeByte(PacketHandler.OPEN_SERVER_GUI);
outputStream.writeInt(guiID);
} catch (Exception ex) {
ex.printStackTrace();
}
return new Packet250CustomPayload("GenericClass", bos.toByteArray());
}
}
I think thats all. As you can see I've used a couple of your tutorials already and they've worked great but I'm just not sure what I'm doing wrong with this one. Very strange IMO but probably a stupid mistake somewhere.
Thanks for your help thus far, and I'm sure we can get this sorted.
EDIT:
I commented out
player.openGui(FantasyCraft.instance, 3, player.worldObj, (int) player.posX, (int) player.posY, (int) player.posZ);
in keybindHandler to see if the GUI would still open. It doesn't, although it does reach the
player.openGui(FantasyCraft.instance, guiID, player.worldObj, (int) player.posX, (int) player.posY, (int) player.posZ);
in PacketHandler. guiID is infact 3, which is correct. I'm stumped.
I tried creating an extended inventory myself and I got the GUI opening up via a keybind.
I got the containers working too but when I tried moving items around I was not able to pick them up and I couldn't figure out why.
Thats were your tutorial came in.
I literally copy-pasted all your classes and I'm getting the same strange occurrence.
Below is my code (I removed everything I could that I thought was unnecessary like imports and methods that arent used in opening and maintaining the GUI since I'm not crashing.):
I think thats all. As you can see I've used a couple of your tutorials already and they've worked great but I'm just not sure what I'm doing wrong with this one. Very strange IMO but probably a stupid mistake somewhere.
Thanks for your help thus far, and I'm sure we can get this sorted.
Sure, let me just point out a couple things that are different from what I do or just strike me as odd:
Main Class
@Instance(Reference.MODID)
// I usually initialize my instance like this, rather than assigning it during one of the loading methods:
public static FantasyCraft instance = new FantasyCraft();
Proxy
@Override
public Object getClientGuiElement(int ID, EntityPlayer player, World world, int x, int y, int z)
{
// why are you getting a tile entity here? you don't use it for anything...
TileEntity tileEntity = world.getBlockTileEntity(x, y, z);
// here you get your custom inventory correctly, which is why you can see the client side gui
InventoryStats invStats = FCraftJobCore.get(player).inventory;
switch(ID){
case 3: return new GuiStats(player, player.inventory, invStats);
default: return null;
}
}
}
--------------------------------------------------------------------------------------------------
@Override
public Object getServerGuiElement(int ID, EntityPlayer player, World world, int x, int y, int z)
{
TileEntity tileEntity = world.getBlockTileEntity(x, y, z);
// here you are getting the player's stored inventory, good
InventoryStats invStats = FCraftJobCore.get(player).inventory;
// but here you will only open the server gui if there is a tile entity at your position...
// which means your player inventory will likely never work correctly
// and also you commented out your ContainerStats part
if(tileEntity != null)
{
switch(ID)
{
case 1: return new ContainerDopingStation(player.inventory, (TileEntityDopingStation)tileEntity);
//case 3: return new ContainerStats(player, player.inventory, invStats);
}
}
return tileEntity;
}
I think you should take another look at the GuiHandler code.
Thank you.
I knew its a stupid error somewhere and it was.
It was indeed the if(tileEntity != null) that was causing me trouble.
Sometimes you just overlook the simplest things.
Thanks for your help.
Who knew you can place a dirt block in your helmet slot and your head becomes a dirt block...works with any block. I might need to look at the slot permission more closely....
Who knew you can place a dirt block in your helmet slot and your head becomes a dirt block...works with any block. I might need to look at the slot permission more closely....
Lol, yeah it happens to me a lot, too. Sometimes it just takes someone else looking at your code to spot the obvious. Pretty awesome-looking interface you've got there; reminds me of Shining Force / Final Fantasy Tactics-style games xD
Re: dirt helmet - did you look at the armor slot code I posted? You also have to make sure to check if the stack is valid for each slot when you shift-click, or it will allow you to put anything in there
Hi! I keep all the variables in DataWatcher. How do I store inventory too DataWatcher?? And also how to keep things new slots after death? Maximum in DataWatcher ID = 31 and the number of slots is much greater. = (
Hi! I keep all the variables in DataWatcher. How do I store inventory too DataWatcher?? And also how to keep things new slots after death? Maximum in DataWatcher ID = 31 and the number of slots is much greater. = (
Why do you want to store an inventory in DataWatcher? Not only is that not possible once you exceed the 32 slot limit of DataWatcher, it's also a massive waste of such a limited resource and will surely make your mod incompatible with any mod that tries to use even one slot of DataWatcher.
What exactly are you trying to do here? If you just want your items to stay with you, look at how the ender chest does it. All you need to do is find somewhere to store the inventory between death and respawn (hint: you can find the solution in my IExtendedEntityProperties tutorial).
Not unless you write your own DataWatcher class or use ASM to modify the bytecode of the Minecraft class, no. Just learn about packets (1.7.2) or packets (1.6.4), it's not that hard and is a much better solution for what you want to do than DataWatcher.
Not unless you write your own DataWatcher class or use ASM to modify the bytecode of the Minecraft class, no. Just learn about packets (1.7.2) or packets (1.6.4), it's not that hard and is a much better solution for what you want to do than DataWatcher.
No, I'm not going to tell you how to write a DataWatcher class. Learn how to use packets. Forget about DataWatcher as a method of storing an inventory, that's not what it's designed for.
I've already told you, please read my tutorial on IExtendedEntityProperties. There is a section about keeping your stored data (like your inventory) persistent through death. I can't do any more than point you in the right direction - you're still going to have to code it yourself after spending the time to understand the information I have already provided.
I'm having a Problem with Items disappearing when I reopen my Inventory.
Please use [ spoiler ] tags in addition to code tags when you post huge blocks of text.
As for your problem, make sure you are returning BOTH a server AND a client gui element (a NEW one in each, not some static reference).
If that doesn't fix it, then what kind of inventory is it - player, item, block? If it's an item, you need to follow the Item inventory tutorial carefully to make sure the Item is saving and loading the inventory properly.
I'm having the same trouble as CiDsPlay did with the items disappearing when the GUI is reopened with my item. Can you take a look please because I really want to get this working? Thanks!
Hmm, it all looks mostly alright...
One thing: do NOT include an inventory in your Item class -> DELETE: "private InventoryItem inventory; "
Items are all static, meaning there is only ever one single instance of the Item; the way Item inventories work is to create a new inventory every single time from the data stored in the ItemStack NBT, and then store that data back as NBT when the inventory changes / closes.
At least it doesn't look like you're trying to access that anywhere.
Also, I would rename your variable InventoryItem#itemstack to something that reflects that it is the itemstack containing the inventory, so as to prevent accidental confusion with other itemstacks in the IInventory methods.
Just to make sure it still works in 1.7.2, I updated the code and added the working example to my 1.7.2 GitHub Tutorial section. I didn't have any trouble with items disappearing, though I don't see anything that really strikes me as wrong with your current code.
However, I did just notice something; in InventoryItem#readFromNBT, calling setStackInSlot(int, ItemStack) calls markDirty, which writes to NBT... I was doing the same thing and it worked for me, but that's a bad idea and bound to cause trouble, so you should change it to:
NBTTagList items = compound.getTagList("ItemInventory", Constants.NBT.TAG_COMPOUND);
for (int i = 0; i < items.tagCount(); ++i) {
NBTTagCompound item = items.getCompoundTagAt(i);
byte slot = item.getByte("Slot");
if (slot >= 0 && slot < getSizeInventory()) {
// EVIL!!! DELETE:
// setInventorySlotContents(slot, ItemStack.loadItemStackFromNBT(item));
// YES, USE THIS ONE:
inventory[slot] = ItemStack.loadItemStackFromNBT(item);
}
}
A relic from when I knew less...
Anyway, like I said, it was working both ways for me, so something else is probably the cause.
No problem. Still, you should use your Reference.MOD_ID for the instance string, since that's what you're using elsewhere, and then if you ever change it, it changes everywhere
I tried creating an extended inventory myself and I got the GUI opening up via a keybind.
I got the containers working too but when I tried moving items around I was not able to pick them up and I couldn't figure out why.
Thats were your tutorial came in.
I literally copy-pasted all your classes and I'm getting the same strange occurrence.
Below is my code (I removed everything I could that I thought was unnecessary like imports and methods that arent used in opening and maintaining the GUI since I'm not crashing.):
Main Class
//Mod Information
@Mod(modid = Reference.MODID, name = Reference.MODNAME, version = Reference.MODVERSION)
@NetworkMod(clientSideRequired=true, serverSideRequired=false,
channels={"GenericClass", "GenericSpec"}, packetHandler = PacketHandler.class)
public class FantasyCraft {
@Instance(Reference.MODID)
public static FantasyCraft instance;
//Proxy Instance
@SidedProxy( clientSide = "unrelentless.fantasycraft.proxy.ClientProxy", serverSide = "unrelentless.fantasycraft.proxy.CommonProxy")
public static CommonProxy proxy;
@EventHandler
public void preInit(FMLPreInitializationEvent event) throws IOException{
}
@EventHandler
public void init(FMLInitializationEvent event){
proxy.registerRenderThings();
MinecraftForge.EVENT_BUS.register(new JobEventHandler());
NetworkRegistry.instance().registerGuiHandler(this, this.proxy);
instance = this;
}
@EventHandler
public void postInit(FMLPostInitializationEvent event){
}
}
GUI Class
public class GuiStats extends InventoryEffectRenderer
{
public InventoryPlayer inv;
public InventoryStats inventoryStats;
ResourceLocation textureLoc = new ResourceLocation(Reference.MODID + ":" + "textures/gui/inventoryStats"+".png");
FCraftJobCore props;
EntityPlayer player;
float[] stats;
private final InventoryStats inventory;
private float xSize_lo;
private float ySize_lo;
public GuiStats(EntityPlayer player, InventoryPlayer playerInv, InventoryStats inventoryStats)
{
super(new ContainerStats(playerInv.player, playerInv, inventoryStats));
this.inventory = inventoryStats;
props = FCraftJobCore.get(player);
this.player = player;
stats = props.getStats();
}
/* *//**
* Called from the main game loop to update the screen.
*//*
@Override
public void updateScreen()
{
if (this.mc.playerController.isInCreativeMode())
{
this.mc.displayGuiScreen(new GuiContainerCreative(this.mc.thePlayer));
}
}*/
/* *//**
* Adds the buttons (and other controls) to the screen in question.
*//*
@Override
public void initGui()
{
this.buttonList.clear();
if (this.mc.playerController.isInCreativeMode())
{
this.mc.displayGuiScreen(new GuiContainerCreative(this.mc.thePlayer));
}
else
{
super.initGui();
}
}*/
/**
* Draw the foreground layer for the GuiContainer (everything in front of the items)
*/
@Override
protected void drawGuiContainerForegroundLayer(int par1, int par2)
{
this.fontRenderer.drawString(""+props.getJob(), 101, 5, 0x0033FF);
this.fontRenderer.drawString("("+props.getSpec()+")", 101, 15, 0x0080FF);
this.fontRenderer.drawString("HP", 112, 27, 0xFFFFFF);
this.fontRenderer.drawString("MP", 112, 36, 0xFFFFFF);
this.fontRenderer.drawString("STR", 110, 45, 0xFFFFFF);
this.fontRenderer.drawString("INT", 110, 54, 0xFFFFFF);
this.fontRenderer.drawString("DEX", 110, 63, 0xFFFFFF);
this.fontRenderer.drawString("LUK", 110, 72, 0xFFFFFF);
this.fontRenderer.drawString(""+(int)player.getMaxHealth(), 136, 27, 0xFFFFFF);
this.fontRenderer.drawString(""+(int)props.getMaxMana(), 136, 36, 0xFFFFFF);
this.fontRenderer.drawString(""+(int)stats[0], 136, 45, 0xFFFFFF);
this.fontRenderer.drawString(""+(int)stats[1], 136, 54, 0xFFFFFF);
this.fontRenderer.drawString(""+(int)stats[2], 136, 63, 0xFFFFFF);
this.fontRenderer.drawString(""+(int)stats[3], 136, 72, 0xFFFFFF);
}
/**
* Draws the screen and all the components in it.
*/
@Override
public void drawScreen(int par1, int par2, float par3)
{
super.drawScreen(par1, par2, par3);
this.xSize_lo = (float)par1;
this.ySize_lo = (float)par2;
}
/**
* Draw the background layer for the GuiContainer (everything behind the items)
*/
@Override
protected void drawGuiContainerBackgroundLayer(float par1, int par2, int par3)
{
GL11.glColor4f(1.0F, 1.0F, 1.0F, 1.0F);
this.mc.getTextureManager().bindTexture(textureLoc);
int k = this.guiLeft;
int l = this.guiTop;
this.drawTexturedModalRect(k, l, 0, 0, this.xSize, this.ySize);
drawGraphic(k + 51, l + 75, 30, (float)(k + 51) - this.xSize_lo, (float)(l + 75 - 50) - this.ySize_lo, this.mc.thePlayer);
}
public static void drawGraphic(int par0, int par1, int par2, float par3, float par4, EntityLivingBase par5EntityLivingBase)
{
GL11.glEnable(GL11.GL_COLOR_MATERIAL);
GL11.glPushMatrix();
GL11.glTranslatef((float)par0, (float)par1, 50.0F);
GL11.glScalef((float)(-par2), (float)par2, (float)par2);
GL11.glRotatef(180.0F, 0.0F, 0.0F, 1.0F);
float f2 = par5EntityLivingBase.renderYawOffset;
float f3 = par5EntityLivingBase.rotationYaw;
float f4 = par5EntityLivingBase.rotationPitch;
float f5 = par5EntityLivingBase.prevRotationYawHead;
float f6 = par5EntityLivingBase.rotationYawHead;
GL11.glRotatef(135.0F, 0.0F, 1.0F, 0.0F);
RenderHelper.enableStandardItemLighting();
GL11.glRotatef(-135.0F, 0.0F, 1.0F, 0.0F);
GL11.glRotatef(-((float)Math.atan((double)(par4 / 40.0F))) * 20.0F, 1.0F, 0.0F, 0.0F);
par5EntityLivingBase.renderYawOffset = (float)Math.atan((double)(par3 / 40.0F)) * 20.0F;
par5EntityLivingBase.rotationYaw = (float)Math.atan((double)(par3 / 40.0F)) * 40.0F;
par5EntityLivingBase.rotationPitch = -((float)Math.atan((double)(par4 / 40.0F))) * 20.0F;
par5EntityLivingBase.rotationYawHead = par5EntityLivingBase.rotationYaw;
par5EntityLivingBase.prevRotationYawHead = par5EntityLivingBase.rotationYaw;
GL11.glTranslatef(0.0F, par5EntityLivingBase.yOffset, 0.0F);
RenderManager.instance.playerViewY = 180.0F;
RenderManager.instance.renderEntityWithPosYaw(par5EntityLivingBase, 0.0D, 0.0D, 0.0D, 0.0F, 1.0F);
par5EntityLivingBase.renderYawOffset = f2;
par5EntityLivingBase.rotationYaw = f3;
par5EntityLivingBase.rotationPitch = f4;
par5EntityLivingBase.prevRotationYawHead = f5;
par5EntityLivingBase.rotationYawHead = f6;
GL11.glPopMatrix();
RenderHelper.disableStandardItemLighting();
GL11.glDisable(GL12.GL_RESCALE_NORMAL);
OpenGlHelper.setActiveTexture(OpenGlHelper.lightmapTexUnit);
GL11.glDisable(GL11.GL_TEXTURE_2D);
OpenGlHelper.setActiveTexture(OpenGlHelper.defaultTexUnit);
}
}
Inventory Class (Your code)
public class InventoryStats implements IInventory
{
/** The name for your custom inventory, possibly just "Inventory" */
private final String name = "Custom Inventory";
/** In case your inventory name is too generic, define a name to store the NBT tag in as well */
private final String tagName = "CustomInvTag";
/** Define the inventory size here for easy reference */
// This is also the place to define which slot is which if you have different types,
// for example SLOT_SHIELD = 0, SLOT_AMULET = 1;
public static final int INV_SIZE = 2;
/** Inventory's size must be same as number of slots you add to the Container class */
ItemStack[] inventory = new ItemStack[INV_SIZE];
public InventoryStats()
{
// don't need anything here!
}
@Override
public int getSizeInventory()
{
return inventory.length;
}
@Override
public ItemStack getStackInSlot(int slot)
{
return inventory[slot];
}
@Override
public ItemStack decrStackSize(int slot, int amount)
{
ItemStack stack = getStackInSlot(slot);
if (stack != null)
{
if (stack.stackSize > amount)
{
stack = stack.splitStack(amount);
if (stack.stackSize == 0)
{
setInventorySlotContents(slot, null);
}
}
else
{
setInventorySlotContents(slot, null);
}
this.onInventoryChanged();
}
return stack;
}
@Override
public ItemStack getStackInSlotOnClosing(int slot)
{
ItemStack stack = getStackInSlot(slot);
if (stack != null)
{
setInventorySlotContents(slot, null);
}
return stack;
}
@Override
public void setInventorySlotContents(int slot, ItemStack itemstack)
{
this.inventory[slot] = itemstack;
if (itemstack != null && itemstack.stackSize > this.getInventoryStackLimit())
{
itemstack.stackSize = this.getInventoryStackLimit();
}
this.onInventoryChanged();
}
@Override
public String getInvName()
{
return name;
}
@Override
public boolean isInvNameLocalized()
{
return name.length() > 0;
}
/**
* Our custom slots are similar to armor - only one item per slot
*/
@Override
public int getInventoryStackLimit()
{
return 1;
}
@Override
public void onInventoryChanged()
{
for (int i = 0; i < this.getSizeInventory(); ++i)
{
if (this.getStackInSlot(i) != null && this.getStackInSlot(i).stackSize == 0)
this.setInventorySlotContents(i, null);
}
}
@Override
public boolean isUseableByPlayer(EntityPlayer entityplayer)
{
return true;
}
@Override
public void openChest() {}
@Override
public void closeChest() {}
/**
* This method doesn't seem to do what it claims to do, as
* items can still be left-clicked and placed in the inventory
* even when this returns false
*/
@Override
public boolean isItemValidForSlot(int slot, ItemStack itemstack)
{
// If you have different kinds of slots, then check them here:
// if (slot == SLOT_SHIELD && itemstack.getItem() instanceof ItemShield) return true;
// For now, only ItemUseMana items can be stored in these slots
return true;
}
public void writeToNBT(NBTTagCompound tagcompound)
{
NBTTagList nbttaglist = new NBTTagList();
for (int i = 0; i < this.getSizeInventory(); ++i)
{
if (this.getStackInSlot(i) != null)
{
NBTTagCompound nbttagcompound1 = new NBTTagCompound();
nbttagcompound1.setByte("Slot", (byte) i);
this.getStackInSlot(i).writeToNBT(nbttagcompound1);
nbttaglist.appendTag(nbttagcompound1);
}
}
// We're storing our items in a custom tag list using our 'tagName' from above
// to prevent potential conflicts
tagcompound.setTag(tagName, nbttaglist);
}
public void readFromNBT(NBTTagCompound tagcompound)
{
NBTTagList nbttaglist = tagcompound.getTagList(tagName);
for (int i = 0; i < nbttaglist.tagCount(); ++i)
{
NBTTagCompound nbttagcompound1 = (NBTTagCompound)nbttaglist.tagAt(i);
byte b0 = nbttagcompound1.getByte("Slot");
if (b0 >= 0 && b0 < this.getSizeInventory())
{
this.setInventorySlotContents(b0, ItemStack.loadItemStackFromNBT(nbttagcompound1));
}
}
}
}
Container Class (Your Code)
public class ContainerStats extends Container
{
/** Avoid magic numbers! This will greatly reduce the chance of you making errors in 'transferStackInSlot' method */
private static final int ARMOR_START = InventoryStats.INV_SIZE, ARMOR_END = ARMOR_START+3,
INV_START = ARMOR_END+1, INV_END = INV_START+26, HOTBAR_START = INV_END+1,
HOTBAR_END = HOTBAR_START+8;
public ContainerStats(EntityPlayer player, InventoryPlayer inventoryPlayer, InventoryStats inventoryCustom)
{
int i;
// Add CUSTOM slots - we'll just add two for now, both of the same type.
// Make a new Slot class for each different item type you want to add
this.addSlotToContainer(new SlotFCraftArmor(inventoryCustom, 0, 80, 8));
this.addSlotToContainer(new SlotFCraftArmor(inventoryCustom, 1, 80, 26));
// Add ARMOR slots; note you need to make a public version of SlotArmor
// just copy and paste the vanilla code into a new class and change what you need
for (i = 0; i < 4; ++i)
{
this.addSlotToContainer(new SlotArmorCopy(player.inventoryContainer, inventoryPlayer, inventoryPlayer.getSizeInventory() - 1 - i, 8, 8 + i * 18, i));
}
// Add vanilla PLAYER INVENTORY - just copied/pasted from vanilla classes
for (i = 0; i < 3; ++i)
{
for (int j = 0; j < 9; ++j)
{
this.addSlotToContainer(new Slot(inventoryPlayer, j + i * 9 + 9, 8 + j * 18, 84 + i * 18));
}
}
// Add ACTION BAR - just copied/pasted from vanilla classes
for (i = 0; i < 9; ++i)
{
this.addSlotToContainer(new Slot(inventoryPlayer, i, 8 + i * 18, 142));
}
}
/**
* This should always return true, since custom inventory can be accessed from anywhere
*/
@Override
public boolean canInteractWith(EntityPlayer player)
{
return true;
}
/**
* Called when a player shift-clicks on a slot. You must override this or you will crash when someone does that.
* Basically the same as every other container I make, since I define the same constant indices for all of them
*/
public ItemStack transferStackInSlot(EntityPlayer player, int par2)
{
ItemStack itemstack = null;
Slot slot = (Slot) this.inventorySlots.get(par2);
if (slot != null && slot.getHasStack())
{
ItemStack itemstack1 = slot.getStack();
itemstack = itemstack1.copy();
// Either armor slot or custom item slot was clicked
if (par2 < INV_START)
{
// try to place in player inventory / action bar
if (!this.mergeItemStack(itemstack1, INV_START, HOTBAR_END + 1, true))
{
return null;
}
slot.onSlotChange(itemstack1, itemstack);
}
// Item is in inventory / hotbar, try to place either in custom or armor slots
else
{
// if item is our custom item
if (itemstack1.getItem() instanceof Item)
{
if (!this.mergeItemStack(itemstack1, 0, InventoryStats.INV_SIZE, false))
{
return null;
}
}
// if item is armor
else if (itemstack1.getItem() instanceof ItemArmor)
{
int type = ((ItemArmor) itemstack1.getItem()).armorType;
if (!this.mergeItemStack(itemstack1, ARMOR_START + type, ARMOR_START + type + 1, false))
{
return null;
}
}
// item in player's inventory, but not in action bar
else if (par2 >= INV_START && par2 < HOTBAR_START)
{
// place in action bar
if (!this.mergeItemStack(itemstack1, HOTBAR_START, HOTBAR_START + 1, false))
{
return null;
}
}
// item in action bar - place in player inventory
else if (par2 >= HOTBAR_START && par2 < HOTBAR_END + 1)
{
if (!this.mergeItemStack(itemstack1, INV_START, INV_END + 1, false))
{
return null;
}
}
}
if (itemstack1.stackSize == 0)
{
slot.putStack((ItemStack) null);
}
else
{
slot.onSlotChanged();
}
if (itemstack1.stackSize == itemstack.stackSize)
{
return null;
}
slot.onPickupFromSlot(player, itemstack1);
}
return itemstack;
}
}
Slots
public class SlotArmorCopy extends Slot
{
/**
* The armor type that can be placed on that slot, it uses the same values of armorType field on ItemArmor.
*/
final int armorType;
/**
* The parent class of this clot, ContainerPlayer, SlotArmor is a Anon inner class.
*/
final Container parent;
public SlotArmorCopy(Container container, IInventory par2IInventory, int par3, int par4, int par5, int par6)
{
super(par2IInventory, par3, par4, par5);
this.parent = container;
this.armorType = par6;
}
/**
* Returns the maximum stack size for a given slot (usually the same as getInventoryStackLimit(), but 1 in the case
* of armor slots)
*/
public int getSlotStackLimit ()
{
return 1;
}
/**
* Check if the stack is a valid item for this slot. Always true beside for the armor slots.
*/
public boolean isItemValid (ItemStack par1ItemStack)
{
Item item = (par1ItemStack == null ? null : par1ItemStack.getItem());
boolean isValidArmor = false;
if (item instanceof ItemArmor) {
isValidArmor = (((ItemArmor)item).armorType == armorType);
}
return item != null && (isValidArmor || (item instanceof ItemBlock && armorType == 0));
}
@SideOnly(Side.CLIENT)
/**
* Returns the icon index on items.png that is used as background image of the slot.
*/
public Icon getBackgroundIconIndex ()
{
return ItemArmor.func_94602_b(this.armorType);
}
}
-------------------------------------------------------------------------------------
public class SlotFCraftArmor extends Slot {
public SlotFCraftArmor(IInventory inventoryStats, int slotIndex, int xDisp, int yDisp) {
super(inventoryStats, slotIndex, xDisp, yDisp);
}
/**
* Returns the maximum stack size for a given slot (usually the same as getInventoryStackLimit(), but 1 in the case
* of armor slots)
*/
public int getSlotStackLimit ()
{
return 1;
}
/**
* Check if the stack is a valid item for this slot. Always true beside for the armor slots.
*/
public boolean isItemValid (ItemStack par1ItemStack)
{
Item item = (par1ItemStack == null ? null : par1ItemStack.getItem());
return item != null && (item instanceof FCraftArmor);
}
}
Extended Properties
public class FCraftJobCore implements IExtendedEntityProperties{
/*each new instance of extended player requires a uniqiue name*/
public static final String EXT_PROP_NAME = "playerClassExtention";
/*include the entity to which the properties belong for easy access
final because we won't be changing which player it is*/
private final EntityPlayer player;
public final InventoryStats inventory = new InventoryStats();
public FCraftJobCore(EntityPlayer player){
this.player = player;
}
/**
* Used to register these extended properties for the player during EntityConstructing event
* This method is for convenience only; it will make your code look nicer
*/
public static final void register(EntityPlayer player){
player.registerExtendedProperties(FCraftJobCore.EXT_PROP_NAME, new FCraftJobCore(player));
}
/**
* Returns ExtendedPlayer properties for player
* This method is for convenience only; it will make your code look nicer
*/
public static final FCraftJobCore get(EntityPlayer player){
return (FCraftJobCore) player.getExtendedProperties(EXT_PROP_NAME);
}
// Save any custom data that needs saving here
@Override
public void saveNBTData(NBTTagCompound compound) {
// We need to create a new tag compound that will save everything for our Extended Properties
NBTTagCompound properties = new NBTTagCompound();
this.inventory.writeToNBT(properties);
/*
Now add our custom tag to the player's tag with a unique name (our property's name).
This will allow you to save multiple types of properties and distinguish between them.
If you only have one type, it isn't as important, but it will still avoid conflicts
between your tag names and vanilla tag names. For instance, if you add some "Items" tag,
that will conflict with vanilla. Not good. So just use a unique tag name.
*/
compound.setTag(EXT_PROP_NAME, properties);
}
// Load whatever data you saved
@Override
public void loadNBTData(NBTTagCompound compound) {
// Here we fetch the unique tag compound we set for this class of Extended Properties
NBTTagCompound properties = (NBTTagCompound) compound.getTag(EXT_PROP_NAME);
this.inventory.readFromNBT(properties);
}
@Override
public void init(Entity entity, World world) {
}
public final void sync()
{
ByteArrayOutputStream bos = new ByteArrayOutputStream(8);
DataOutputStream outputStream = new DataOutputStream(bos);
// We'll write max mana first so when we set current mana client
// side, it doesn't get set to 0 (see methods below)
try {
outputStream.writeByte(PacketHandler.EXTENDED_PROPERTIES);
outputStream.writeInt(this.currentJob);
outputStream.writeInt(this.currentSpecialization);
} catch (Exception ex) {
ex.printStackTrace();
}
Packet250CustomPayload packet = new Packet250CustomPayload("GenericClass", bos.toByteArray());
// We only want to send from the server to the client
if (FMLCommonHandler.instance().getEffectiveSide().isServer()) {
EntityPlayerMP player1 = (EntityPlayerMP) player;
PacketDispatcher.sendPacketToPlayer(packet, (Player) player1);
}
}
}
}
Keybinding (Your code)
(I got rid of the if statement because I couldnt find tutkeymap anywhere in your code)
public class KeybindHelper {
/** Key index for easy handling */
public static final int CUSTOM_INV = 0;
/** Key descriptions */
private static final String[] desc = {"Custom Inventory"};
/** Default key values */
private static final int[] keyValues = {Keyboard.KEY_O};
/** This stores all of our key bindings and is always updated with the in-game settings */
public static final KeyBinding[] keys = new KeyBinding[desc.length];
/** This initializes and registers all the key bindings */
public static void init()
{
boolean[] repeat = new boolean[desc.length];
// just use a for loop to run through all the values
for (int i = 0; i < desc.length; ++i) {
keys[i] = new KeyBinding(desc[i], keyValues[i]);
repeat[i] = false;
}
KeyBindingRegistry.registerKeyBinding(new KeybindHandler(keys, repeat));
}
}
--------------------------------------------------------------------------------------------------
@SideOnly(Side.CLIENT)
public class KeybindHandler extends KeyHandler
{
/** Not really important. I use it to store/find keys in the config file */
public static final String label = "Tutorial Key";
public KeybindHandler(KeyBinding[] keyBindings, boolean[] repeatings) {
super(keyBindings, repeatings);
}
@Override
public String getLabel() {
return this.label;
}
@Override
public void keyDown(EnumSet<TickType> types, KeyBinding kb, boolean tickEnd, boolean isRepeat)
{
if (tickEnd && FMLCommonHandler.instance().getEffectiveSide() == Side.CLIENT)
{
EntityPlayer player = FMLClientHandler.instance().getClient().thePlayer;
// If the custom inventory screen is open, close it
if (player.openContainer != null && player.openContainer instanceof ContainerStats)
player.closeScreen();
// Otherwise, open the screen. Here you will need to send a packet to the server telling it
// to open the corresponding server gui element, or your inventory won't function
else if (FMLClientHandler.instance().getClient().inGameHasFocus)
{
// Send a packet to the server; here I use a custom packet class to build the packet for me
// Don't worry, we'll cover this in the next step
((EntityClientPlayerMP) player).sendQueue.addToSendQueue(PacketOpenServerGui.getPacket(3));
// The client-side Gui element should open automatically when the server opens the Gui
// If for some reason it does not, you can open it manually on the client side here:
player.openGui(FantasyCraft.instance, 3, player.worldObj, (int) player.posX, (int) player.posY, (int) player.posZ);
}
}
}
@Override
public void keyUp(EnumSet<TickType> types, KeyBinding kb, boolean tickEnd) {
// Don't need to do anything here!
}
@Override
public EnumSet<TickType> ticks() {
// We're only interested in client ticks, as that's when the keyboard will fire
return EnumSet.of(TickType.CLIENT);
}
}
PacketHandler
public class PacketHandler implements IPacketHandler{
public static final byte EXTENDED_PROPERTIES = 1, OPEN_SERVER_GUI = 2;
@Override
public void onPacketData(INetworkManager manager,
Packet250CustomPayload packet, Player player) {
DataInputStream inputStream = new DataInputStream(new ByteArrayInputStream(packet.data));
byte packetType;
try {
// Read the packet type
packetType = inputStream.readByte();
} catch (IOException e) {
e.printStackTrace();
return;
}
if(packet.channel.equals("GenericClass")){
switch(packetType) {
case EXTENDED_PROPERTIES: handleClass(packet, player, inputStream); break;
case OPEN_SERVER_GUI: handleOpenServerGui(packet, (EntityPlayer) player, inputStream); break;
default: System.out.println("[PACKET][WARNING] Unknown packet type " + packetType);
}
}
//handleClass(packet, player);
/* }else if(packet.channel.equals("GenericSpec")){
handleSpec(packet, player, inputStream);
}*/
}
private void handleOpenServerGui(Packet250CustomPayload packet, EntityPlayer player, DataInputStream inputStream)
{
int guiID;
// inputStream is already open, so we don't need to do anything other than continue reading from it:
try {
guiID = inputStream.readInt();
} catch (IOException e) {
e.printStackTrace();
return;
}
// Now we can open the server gui element:
player.openGui(FantasyCraft.instance, guiID, player.worldObj, (int) player.posX, (int) player.posY, (int) player.posZ);
}
}
Proxy
public class ClientProxy extends CommonProxy {
@Override
public void registerRenderThings()
{
KeybindHelper.init();
}
@Override
public Object getClientGuiElement(int ID, EntityPlayer player, World world, int x, int y, int z)
{
TileEntity tileEntity = world.getBlockTileEntity(x, y, z);
InventoryStats invStats = FCraftJobCore.get(player).inventory;
switch(ID){
case 3: return new GuiStats(player, player.inventory, invStats);
default: return null;
}
}
}
--------------------------------------------------------------------------------------------------
public class CommonProxy implements IGuiHandler {
/** Used to store IExtendedEntityProperties data temporarily between player death and respawn */
private static final Map<String, NBTTagCompound> extendedEntityData = new HashMap<String, NBTTagCompound>();
public void registerRenderThings() {
}
public void registerSound() {
}
@Override
public Object getClientGuiElement(int ID, EntityPlayer player, World world, int x, int y, int z)
{
return null;
}
@Override
public Object getServerGuiElement(int ID, EntityPlayer player, World world, int x, int y, int z)
{
TileEntity tileEntity = world.getBlockTileEntity(x, y, z);
InventoryStats invStats = FCraftJobCore.get(player).inventory;
if(tileEntity != null)
{
switch(ID)
{
case 1: return new ContainerDopingStation(player.inventory, (TileEntityDopingStation)tileEntity);
//case 3: return new ContainerStats(player, player.inventory, invStats);
}
}
return tileEntity;
}
/**
* Adds an entity's custom data to the map for temporary storage
* @param compound An NBT Tag Compound that stores the IExtendedEntityProperties data only
*/
public static void storeEntityData(String name, NBTTagCompound compound)
{
extendedEntityData.put(name, compound);
}
/**
* Removes the compound from the map and returns the NBT tag stored for name or null if none exists
*/
public static NBTTagCompound getEntityData(String name)
{
return extendedEntityData.remove(name);
}
}
EDIT: Missed one.
PacketOpenServerGUI (Your code)
public class PacketOpenServerGui
{
public static Packet250CustomPayload getPacket(int guiID)
{
ByteArrayOutputStream bos = new ByteArrayOutputStream(5);
DataOutputStream outputStream = new DataOutputStream(bos);
try {
// Notice this first line writes the packet id - do this in your ExtendedPlayer.sync() method as well and add 1 to the size.
outputStream.writeByte(PacketHandler.OPEN_SERVER_GUI);
outputStream.writeInt(guiID);
} catch (Exception ex) {
ex.printStackTrace();
}
return new Packet250CustomPayload("GenericClass", bos.toByteArray());
}
}
Thanks for your help thus far, and I'm sure we can get this sorted.
EDIT:
I commented out
player.openGui(FantasyCraft.instance, 3, player.worldObj, (int) player.posX, (int) player.posY, (int) player.posZ);
in keybindHandler to see if the GUI would still open. It doesn't, although it does reach the
player.openGui(FantasyCraft.instance, guiID, player.worldObj, (int) player.posX, (int) player.posY, (int) player.posZ);
in PacketHandler. guiID is infact 3, which is correct. I'm stumped.
Sure, let me just point out a couple things that are different from what I do or just strike me as odd:
Main Class
Proxy
I think you should take another look at the GuiHandler code.
Thank you.
I knew its a stupid error somewhere and it was.
It was indeed the if(tileEntity != null) that was causing me trouble.
Sometimes you just overlook the simplest things.
Thanks for your help.
Who knew you can place a dirt block in your helmet slot and your head becomes a dirt block...works with any block. I might need to look at the slot permission more closely....
Lol, yeah it happens to me a lot, too. Sometimes it just takes someone else looking at your code to spot the obvious. Pretty awesome-looking interface you've got there; reminds me of Shining Force / Final Fantasy Tactics-style games xD
Re: dirt helmet - did you look at the armor slot code I posted? You also have to make sure to check if the stack is valid for each slot when you shift-click, or it will allow you to put anything in there
Why do you want to store an inventory in DataWatcher? Not only is that not possible once you exceed the 32 slot limit of DataWatcher, it's also a massive waste of such a limited resource and will surely make your mod incompatible with any mod that tries to use even one slot of DataWatcher.
What exactly are you trying to do here? If you just want your items to stay with you, look at how the ender chest does it. All you need to do is find somewhere to store the inventory between death and respawn (hint: you can find the solution in my IExtendedEntityProperties tutorial).
Not unless you write your own DataWatcher class or use ASM to modify the bytecode of the Minecraft class, no. Just learn about packets (1.7.2) or packets (1.6.4), it's not that hard and is a much better solution for what you want to do than DataWatcher.
You can tell how to make your DataWatcher?
No, I'm not going to tell you how to write a DataWatcher class. Learn how to use packets. Forget about DataWatcher as a method of storing an inventory, that's not what it's designed for.
I've already told you, please read my tutorial on IExtendedEntityProperties. There is a section about keeping your stored data (like your inventory) persistent through death. I can't do any more than point you in the right direction - you're still going to have to code it yourself after spending the time to understand the information I have already provided.
Please use [ spoiler ] tags in addition to code tags when you post huge blocks of text.
As for your problem, make sure you are returning BOTH a server AND a client gui element (a NEW one in each, not some static reference).
If that doesn't fix it, then what kind of inventory is it - player, item, block? If it's an item, you need to follow the Item inventory tutorial carefully to make sure the Item is saving and loading the inventory properly.
That's what all the "isItemValidForSlot" methods are for; there is one in IInventory and one in Slot.
Hmm, it all looks mostly alright...
One thing: do NOT include an inventory in your Item class -> DELETE: "private InventoryItem inventory; "
Items are all static, meaning there is only ever one single instance of the Item; the way Item inventories work is to create a new inventory every single time from the data stored in the ItemStack NBT, and then store that data back as NBT when the inventory changes / closes.
At least it doesn't look like you're trying to access that anywhere.
Also, I would rename your variable InventoryItem#itemstack to something that reflects that it is the itemstack containing the inventory, so as to prevent accidental confusion with other itemstacks in the IInventory methods.
Just to make sure it still works in 1.7.2, I updated the code and added the working example to my 1.7.2 GitHub Tutorial section. I didn't have any trouble with items disappearing, though I don't see anything that really strikes me as wrong with your current code.
However, I did just notice something; in InventoryItem#readFromNBT, calling setStackInSlot(int, ItemStack) calls markDirty, which writes to NBT... I was doing the same thing and it worked for me, but that's a bad idea and bound to cause trouble, so you should change it to:
Anyway, like I said, it was working both ways for me, so something else is probably the cause.