What is NBTTagCompound?
NBTTagCompound is a custom data structure used by Mojang for storing primitive data (byte, short, int, float, double, long, String [not primitive, but exceptionally considered such in Java]).
What is stackTagCompound
The ItemStack class has a public member (class-level variable) of the NBTTagCompound type called stackTagCompound. Basically, every ItemStack in the game has the potential to store unlimited amounts of arbitrary primitive data.
Mojang uses this for things such as storing the custom name of an ItemStack renamed on the Anvil. (Yes, even your solitary Diamond Sword is an ItemStack. It's just a stack of size 1.)
An ItemStack's stackTagCompound is serialized to the save data so anything you throw in here is saved forever and ever.
Quote from Important Notice »
Forge users will need to import NBT-stuffs, thanks to CPW and Lex refactoring MC:
import net.minecraft.nbt.*;
(You can import only the stuff you're using, but this is easier :P)
Storing data to stackTagCompound
In any scope where you have access to an ItemStack, you can fiddle with its stackTagCompound. For the sake of this example, we'll work with the Item class's onCreated method, which gets called whenever you take a stack of it out of the result slot of the crafting table.
public void onCreated(ItemStack par1ItemStack, World par2World, EntityPlayer par3EntityPlayer) {}
The first thing you want to do is an instantiation check:
public void onCreated(ItemStack par1ItemStack, World par2World, EntityPlayer par3EntityPlayer) {
if( par1ItemStack.stackTagCompound == null )
par1ItemStack.setTagCompound( new NBTTagCompound( ) );
}
This is absolutely important. For one, the stackTagCompound is only declared by default since not every Item in the game makes use of it. Basically, Java knows an ItemStack can have a stackTagCompound but not everyone actually has one. For two, it's just good practice to make sure the object you're manipulating actually exists. Java will throw a fit if you try to manipulate a null object.
Now comes the meat and potatoes.
NBTTagCompound has a few handy methods:
setByte( String s, byte b )
setShort( String s, short sh )
setInteger( String s, int i )
setLong( String s, long l )
setFloat( String s, float f )
setDouble( String s, double d )
setString( String s, String st )
setByteArray( String s, byte[] ba )
setIntArray( String s, int[] ia )
setBoolean( String s, boolean bl )
There are some other set* methods, but those are a bit complicated and allow for literally embedding complicated groups of things into your complicated group of things.
The first String parameter is the unique key for your data. Think of it as the name you're filing it under. It's important that this key doesn't conflict with any other key (they are case-sensitive, of course)! Here's some usage examples:
public void onCreated(ItemStack par1ItemStack, World par2World, EntityPlayer par3EntityPlayer) {
if( par1ItemStack.stackTagCompound == null )
par1ItemStack.setTagCompound( new NBTTagCompound( ) );
int i = 45;
int[] ia = { 1, 2, 3, 7 };
String s = "Created by Player " . par3EntityPlayer.username;
par1ItemStack.stackTagCompound.setInteger( "AnInteger", i );
par1ItemStack.stackTagCompound.setIntArray( "AnIntegerArray", ia );
par1ItemStack.stackTagCompound.setString( "WhoCreated", s );
}
Getting data from a stackTagcompound
Obviously, if you're storing to it, you want to read from it at some point as well. For this example, we'll work with Item's addInformation method which lets you add custom information to the ItemStack's tooltip (like how an enchanted thing's enchantments are listed on the mouse-hover-over tooltip).
public void addInformation(ItemStack par1ItemStack, EntityPlayer par2EntityPlayer, List par3List, boolean par4) {}
First thing's first, of course, we need our instantiation check:
public void addInformation(ItemStack par1ItemStack, EntityPlayer par2EntityPlayer, List par3List, boolean par4) {
if( par1ItemStack.stackTagCompound == null )
par1ItemStack.setTagCompound( new NBTTagCompound( ) );
}
Now, we have a wealth of functions to call to get data from:
getByte( String s )
getShort( String s )
getInteger( String s )
getLong( String s )
getFloat( String s )
getDouble( String s )
getString( String s )
getByteArray( String s )
getIntArray( String s )
getBoolean( String s )
Of course, there are other get methods since, as you can store complicated groups of things in your complicated group of things, you can read them back too.
Naturally, the string parameter is the key for which you'd like to get the stored value.
Let's say we'll have this ItemStack proudly display who made it:
public void addInformation(ItemStack par1ItemStack, EntityPlayer par2EntityPlayer, List par3List, boolean par4) {
if( par1ItemStack.stackTagCompound == null )
par1ItemStack.setTagCompound( new NBTTagCompound( ) );
par3List.add( par1ItemStack.getString( "WhoCreated" ) );
}
This is well and good. We could call it finished. But, what happens if there isn't a key in the stackTagCompound called "WhoCreated"?
Well, fortunately, these getters will return a default in such a case: 0, array of 0s, false, empty string "", etc. But, we don't always want those defaults, do we? After all, in our case here, this means the ItemStack would display an empty string!
So, that's where a little method called hasKey( String s ) comes in. It lets you check if a key exists and handle the failure situation yourself. In our case, we could do this:
public void addInformation(ItemStack par1ItemStack, EntityPlayer par2EntityPlayer, List par3List, boolean par4) {
if( par1ItemStack.stackTagCompound == null )
par1ItemStack.setTagCompound( new NBTTagCompound( ) );
if( par1ItemStack.stackTagCompound.hasKey( "WhoCreated" ) )
par3List.add( par1ItemStack.getString( "WhoCreated" ) );
else
par3List.add( "Created by Almighty Jeb" );
}
Now, instead of an empty line, we know that it was good ol' Jens Bergensten who made it.
Deleting Stored Info
Just as you can add data to a stackTagCompound, you can remove data from it permanently. This is accomplished through a method called removeTag( String s ). The parameter, of course, being the key that, along with its data, you wish to completely obliterate.
... You don't need an example do you? Okay, fine, fine.
If you can't tell what this one does, it disenchants the item if placed into the inventory of a player named badluckbrian (with the actual casing of any letter ignored).
Miscellaneous Advice Consistent NBT writing leads to wonkiness.
If you are constantly updating the stackTagCompound every tick or couple of ticks, your item will have some wonk to its rendering. It can repeatedly bob up and down or actually bob all the way down for a bit and then bob back up. This is because the NBT is being updated server-side and then the server updates the client. Because the item is "new", the client renders it as swapping to a new item.
At least, that's the best way to explain it. Your move.
Class members (class level variables) in Item-type classes are static regardless.
See this class?
public class ItemYourItem extends Item {
private int foo = 0;
public ItemYourItem( int id ) {
super( id );
}
}
Even though it's not declared static, foo is static. All instances of ItemYourItem share this same variable. That's because all instances are just pointers to one object of type ItemYourItem.
To make it clear, if this was in ItemSword, every sword in the game would share this variable foo. If it's changed anywhere in the class code, it's reflected in all swords in the game everywhere.
-
Well, that's all for now, folks! If there's anything NBT related you'd like to see added to this tutorial, let me know. If you spot any errors, let me know too.
Things I'm considering adding at a later point:
- Enchantments (removing specific enchantments, especially)
- Potions
- "Hard Mode" (How to do some things the hard way, rather than relying on MC's own getters/setters/etc)
- A better way to do timers for items that do things over a span of time.
Rollback Post to RevisionRollBack
Comic artist, indie game dev, lots of things. Click image to check out my latest comic series?
(Warning: series may be NSFC [Not Safe For Church], viewer discretion is advised)
Cool! I was wondering what the heck the tag compound stuff was when I was modding. Thanks for the post it was really helpful!
Rollback Post to RevisionRollBack
If Facebook, Myspace, and Twitter were all destroyed, 90% of teens would go insane. If you're one of the 10% that would be laughing at them, copy this into your signature and hope it happens.
Cool! I was wondering what the heck the tag compound stuff was when I was modding. Thanks for the post it was really helpful!
Glad I helped! :smile.gif:
I'll be updating this tutorial in the near future with a couple more things:
- adding "enchantment glow" such that individual items glow rather than all items of the class (which something like hasEffect() does)
- adding actual enchantments manually
For now though, my brain is burned out and I need a break :laugh.gif:
Rollback Post to RevisionRollBack
Comic artist, indie game dev, lots of things. Click image to check out my latest comic series?
(Warning: series may be NSFC [Not Safe For Church], viewer discretion is advised)
It's possible. I'll add "player stats" to my to-do list of tutorials to add when I get back to this. Should be sometime next week hopefully.
Rollback Post to RevisionRollBack
Comic artist, indie game dev, lots of things. Click image to check out my latest comic series?
(Warning: series may be NSFC [Not Safe For Church], viewer discretion is advised)
Comic artist, indie game dev, lots of things. Click image to check out my latest comic series?
(Warning: series may be NSFC [Not Safe For Church], viewer discretion is advised)
This is an awesome tutorial, but there is just one thing. The onCreated seems to only work if the item is created, not if it's taken out of the creative tab. Is there a second event to check if the item is being taken from the creative tab?
This is an awesome tutorial, but there is just one thing. The onCreated seems to only work if the item is created, not if it's taken out of the creative tab. Is there a second event to check if the item is being taken from the creative tab?
Well, yeah. That's to be expected.
I don't know of any kosher approaches to the creative thing but here's a sort of hackish way to go about it:
public void onUpdate(ItemStack par1ItemStack, World par2World, Entity par3Entity, int par4, boolean par5) {
if( par1ItemStack.stackTagCompound == null ) {
[do your code here]
par1ItemStack.setTagCompound( new NBTTagCompound( ) );
par1ItemStack.stackTagCompound.setBoolean( "firstget", true ) );
} else if( par1ItemStack.stackTagCompound.getBoolean( "firstget" ) == false ) {
[do your code here]
par1ItemStack.stackTagCompound.setBoolean( "firstget", true ) );
}
}
This should theoretically perform your "init" code on the first possible tick that the item is in any player inventory (or other such place where the onUpdate() method gets called). Then it sets a stack tag compound value to true to indicate it did the init code so that subsequent calls to the item's onUpdate() yield nothing.
Rollback Post to RevisionRollBack
Comic artist, indie game dev, lots of things. Click image to check out my latest comic series?
(Warning: series may be NSFC [Not Safe For Church], viewer discretion is advised)
I don't know of any kosher approaches to the creative thing but here's a sort of hackish way to go about it:
public void onUpdate(ItemStack par1ItemStack, World par2World, Entity par3Entity, int par4, boolean par5) {
if( par1ItemStack.stackTagCompound == null ) {
[do your code here]
par1ItemStack.setTagCompound( new NBTTagCompound( ) );
par1ItemStack.stackTagCompound.setBoolean( "firstget", true ) );
} else if( par1ItemStack.stackTagCompound.getBoolean( "firstget" ) == false ) {
[do your code here]
par1ItemStack.stackTagCompound.setBoolean( "firstget", true ) );
}
}
This should theoretically perform your "init" code on the first possible tick that the item is in any player inventory (or other such place where the onUpdate() method gets called). Then it sets a stack tag compound value to true to indicate it did the init code so that subsequent calls to the item's onUpdate() yield nothing.
Thanks for the reply.
Yeah that's what i ended up doing, checking it in the onUpdate, because if someone spawns in the item in creative mode or TMI, it would crash the whole server.
Finally accurate and useful!
What is NBTTagCompound?
NBTTagCompound is a custom data structure used by Mojang for storing primitive data (byte, short, int, float, double, long, String [not primitive, but exceptionally considered such in Java]).
What is stackTagCompound
The ItemStack class has a public member (class-level variable) of the NBTTagCompound type called stackTagCompound. Basically, every ItemStack in the game has the potential to store unlimited amounts of arbitrary primitive data.
Mojang uses this for things such as storing the custom name of an ItemStack renamed on the Anvil. (Yes, even your solitary Diamond Sword is an ItemStack. It's just a stack of size 1.)
An ItemStack's stackTagCompound is serialized to the save data so anything you throw in here is saved forever and ever.
Storing data to stackTagCompound
In any scope where you have access to an ItemStack, you can fiddle with its stackTagCompound. For the sake of this example, we'll work with the Item class's onCreated method, which gets called whenever you take a stack of it out of the result slot of the crafting table.
The first thing you want to do is an instantiation check:
This is absolutely important. For one, the stackTagCompound is only declared by default since not every Item in the game makes use of it. Basically, Java knows an ItemStack can have a stackTagCompound but not everyone actually has one. For two, it's just good practice to make sure the object you're manipulating actually exists. Java will throw a fit if you try to manipulate a null object.
Now comes the meat and potatoes.
NBTTagCompound has a few handy methods:
There are some other set* methods, but those are a bit complicated and allow for literally embedding complicated groups of things into your complicated group of things.
The first String parameter is the unique key for your data. Think of it as the name you're filing it under. It's important that this key doesn't conflict with any other key (they are case-sensitive, of course)! Here's some usage examples:
Getting data from a stackTagcompound
Obviously, if you're storing to it, you want to read from it at some point as well. For this example, we'll work with Item's addInformation method which lets you add custom information to the ItemStack's tooltip (like how an enchanted thing's enchantments are listed on the mouse-hover-over tooltip).
First thing's first, of course, we need our instantiation check:
Now, we have a wealth of functions to call to get data from:
Of course, there are other get methods since, as you can store complicated groups of things in your complicated group of things, you can read them back too.
Naturally, the string parameter is the key for which you'd like to get the stored value.
Let's say we'll have this ItemStack proudly display who made it:
This is well and good. We could call it finished. But, what happens if there isn't a key in the stackTagCompound called "WhoCreated"?
Well, fortunately, these getters will return a default in such a case: 0, array of 0s, false, empty string "", etc. But, we don't always want those defaults, do we? After all, in our case here, this means the ItemStack would display an empty string!
So, that's where a little method called hasKey( String s ) comes in. It lets you check if a key exists and handle the failure situation yourself. In our case, we could do this:
Now, instead of an empty line, we know that it was good ol' Jens Bergensten who made it.
Deleting Stored Info
Just as you can add data to a stackTagCompound, you can remove data from it permanently. This is accomplished through a method called removeTag( String s ). The parameter, of course, being the key that, along with its data, you wish to completely obliterate.
... You don't need an example do you? Okay, fine, fine.
If you can't tell what this one does, it disenchants the item if placed into the inventory of a player named badluckbrian (with the actual casing of any letter ignored).
Miscellaneous Advice
Consistent NBT writing leads to wonkiness.
If you are constantly updating the stackTagCompound every tick or couple of ticks, your item will have some wonk to its rendering. It can repeatedly bob up and down or actually bob all the way down for a bit and then bob back up. This is because the NBT is being updated server-side and then the server updates the client. Because the item is "new", the client renders it as swapping to a new item.
At least, that's the best way to explain it. Your move.
Class members (class level variables) in Item-type classes are static regardless.
See this class?
Even though it's not declared static, foo is static. All instances of ItemYourItem share this same variable. That's because all instances are just pointers to one object of type ItemYourItem.
To make it clear, if this was in ItemSword, every sword in the game would share this variable foo. If it's changed anywhere in the class code, it's reflected in all swords in the game everywhere.
-
Well, that's all for now, folks! If there's anything NBT related you'd like to see added to this tutorial, let me know. If you spot any errors, let me know too.
Things I'm considering adding at a later point:
- Enchantments (removing specific enchantments, especially)
- Potions
- "Hard Mode" (How to do some things the hard way, rather than relying on MC's own getters/setters/etc)
- A better way to do timers for items that do things over a span of time.
(Warning: series may be NSFC [Not Safe For Church], viewer discretion is advised)
Glad I helped! :smile.gif:
I'll be updating this tutorial in the near future with a couple more things:
- adding "enchantment glow" such that individual items glow rather than all items of the class (which something like hasEffect() does)
- adding actual enchantments manually
For now though, my brain is burned out and I need a break :laugh.gif:
(Warning: series may be NSFC [Not Safe For Church], viewer discretion is advised)
(Warning: series may be NSFC [Not Safe For Church], viewer discretion is advised)
Not sure what you mean. Sorry.
(Warning: series may be NSFC [Not Safe For Church], viewer discretion is advised)
Well, yeah. That's to be expected.
I don't know of any kosher approaches to the creative thing but here's a sort of hackish way to go about it:
This should theoretically perform your "init" code on the first possible tick that the item is in any player inventory (or other such place where the onUpdate() method gets called). Then it sets a stack tag compound value to true to indicate it did the init code so that subsequent calls to the item's onUpdate() yield nothing.
(Warning: series may be NSFC [Not Safe For Church], viewer discretion is advised)
Thanks for the reply.
Yeah that's what i ended up doing, checking it in the onUpdate, because if someone spawns in the item in creative mode or TMI, it would crash the whole server.