One thing that I screwed up was when accessing an extended property.
I found that if you use a base class, like EntityLivingBase and pass it to the get(PlayerEntity) function, you will not get the player instance, it will register a whole new invalid instance. This took me 3 hours to figure out. I was able to cast the EntityLivingBase object to EntityPlayer, with no errors, so I assumed all was ok, not so.
One thing that I screwed up was when accessing an extended property.
I found that if you use a base class, like EntityLivingBase and pass it to the get(PlayerEntity) function, you will not get the player instance, it will register a whole new invalid instance. This took me 3 hours to figure out. I was able to cast the EntityLivingBase object to EntityPlayer, with no errors, so I assumed all was ok, not so.
If your extended properties class is for EntityPlayer and the #get method takes an EntityPlayer, then you must have an EntityPlayer instance to pass. EntityPlayer is also an EntityLivingBase, and an Entity, and actually more specifically it's an EntityPlayerMP or EntityClientPlayerMP.
At any rate, before casting any object to another class, you should always first check if it can be cast using instanceof.
Well thanks for the reply. Although you may think I'm a novice at Java, I'm not, just a bit rusty
Not sure what you were getting at. Are you saying a object of EntityLivingBase should or shouldn't work for in place of an object of EntityPlayer?
I do understand that EntityPlayer extends EntityLivingBase, which is why I figured it would be fine, especially since it was the player object, even though I didn't check using instanceof (normally I do).
Well thanks for the reply. Although you may think I'm a novice at Java, I'm not, just a bit rusty
Not sure what you were getting at. Are you saying a object of EntityLivingBase should or shouldn't work for in place of an object of EntityPlayer?
I do understand that EntityPlayer extends EntityLivingBase, which is why I figured it would be fine, especially since it was the player object, even though I didn't check using instanceof (normally I do).
Notice I said 'refresher'
However, the more you write, the clearer it is that you lack understanding of some fundamental Java concepts, specifically class inheritance and object type.
It is impossible to have an object of type EntityLivingBase - that's an abstract class; the reason that many methods take EntityLivingBase as an argument is that it allows every sub-class of EntityLivingBase to then be passed as a valid argument - EntityPlayer, Animals, Mobs, all of these can then use the same method because they inherit from EntityLivingBase, and that is all the specificity the method requires.
The actual run-time type of each entity, however, is as specific as possible: EntityCreeper, EntityBat, etc. This is why you can type-cast at all - if the entity was literally an EntityLivingBase and nothing else, it would be impossible to cast it to a more specific class.
Please read the Java documentation that I linked earlier, as well as other pages on type-casting, inheritance, and similar topics.
I'm sure I do need a refresher. I learned java before it was even 1.0. I also took some Java classes in college, but most of my Java experience is from before the year 2000. So I'm probably assuming I remember more than I do. I've also just been modding for a few months, so my Java is still kind of rusty. I do learn best by doing, so I've just been trying stuff, and reading up on how stuff works as I go. Back when I learned Java, I don't think there was an instanceof operator. It is pretty handy and I have been using it all over the place, although I didn't read up all the details of how it works first.
I think I do understand class inheritance and object type. Although I don't think I have the language all straight. I learned C++ before Java, which works in a similar way, but back in 1985 when I learned it, the language was different on how it was described, so I need to update my lingo, I guess
I do suppose I should read those Forge API java docs, but each time I look at them, I'd rather be programming than reading a bunch of stuff I'll forget a few days later I learn more by doing than just reading a bunch of documentation. I may be wrong on this too, but also I thought that the javadoc didn't contain any documentation that isn't already in the code. So I've just been using the code as my documentation.
I'm still try to figure out the answer to my original question. But you keep bringing up more questions It is starting to all come back to me more clear though, so this is good (maybe someone besides me is learning stuff, which was why I posted in the first place).
So in BlockContainer, there is a method:
void onBlockPlacedBy(World p_149689_1_, int p_149689_2_, int p_149689_3_, int p_149689_4_, EntityLivingBase p_149689_5_, ItemStack p_149689_6_) It has a parameter, EntityLivingBase p_149689_5_. Which is what I probably should have said in the first place, but was being way too inaccurate in my description, sorry about that.
I neglected to use the instanceof (i'll try that out later tonight), but since the player placed the block, that caused the above method to be called, I assumed there was no way p_149689_5_ couldn't be an instance of EntityPlayer. It didn't help that this was in a function that I had an @Override on for a Custom Chest I created, and I renamed p_149689_5_ to player.
If a method gives you an EntityLivingBase, then ANY sub-class of EntityLivingBase might be given as the argument. Block#onBlockPlacedBy, for example, can be called with an EntityEnderman as the entity placing the block, and probably others (modded) as well.
I assume you know what assuming does to u and me, so keep that in mind when type-casting: ALWAYS check instanceof before you cast. Always.
Thanks for your help. Using event handlers and the IExtendedEntityProperties are fairly advanced, and something I wouldn't of tackled if I didn't have a tutorial like yours to refer to. Even so, there are still plenty of ways to mess up if you do use it
One thing I was seeing, is there are many different ways to get a PlayerEntity that will be accepted by this function:
public static final ExtendedPlayer get(EntityPlayer player)
There are things like (I'm doing some of this from memory):
Minecraft.getMinecraft().thePlayer
Also some events have something like event.player
Problem is, I think some of these will "work" in single player, but in multiplayer they will fail. I actually don't have a good handle on this part of how Minecraft works yet, which is why I'm bringing this up. A good tutorial to go along with the two you present here, is one or more that explain the client/server aspect of Minecraft and which variables can be used and when to get a "player object" that will work with the IExtendedEntityProperties "interface". I put those in quotes, because I'm sure I'm not getting the terminology exactly right.
So I now will religiously use the instanceof, but if you insert something like Minecraft.getMinecraft().thePlayer in the get method, it will accept it, although I think it will not work in multiplayer. Am I getting that right?
To answer your question, ANY instance of EntityPlayer (which includes ALL sub-classes thereof) can be used to get the extended properties (for a player or parent class thereof).
Doesn't matter if it's from a method, from an event (which is just a class with various class members, e.g. an EntityPlayer), or anything else. If it's an EntityPlayer, it will work.
BUT, Minecraft.getMinecraft() is CLIENT side ONLY, meaning that you can only use it when your code is running on the client side. You should NEVER use the Minecraft class for anything unless you are absolutely certain your code is confined to the client side, such as in a GUI or render class.
Sooo... I am extremely confused on how to get this working? Tried a lot of different methods and can't get any of them to work.
I want to assign a death counter to players in which it will increment as they die (obviously). When they die NBT data is reset, setting it to 0. I am using the death count to negate health by multiplying it by 2 and subtracting it from 20, then setting the base max health of the player (which is the player base NBT data, which is also another problem because I don't know how to load that either...).
I am new to NBT so please excuse my absolute illiteracy of all of this. I have tried following your tutorials but I just can't get much from them. Any help here? Is there any easier ways to keeping NBT data persistent? Would it just be easier to write a separate .dat file and add a health tracker on there as well and skip the whole persistence thing?
You are going about it the wrong way - you don't want to change the player's actual max health value, you want to apply an AttributeModifier to it. Simply recalculate your health penalty any time the number of deaths changes, as well as each time the player joins the world.
This is done by removing the old modifier, if any, and adding a new one; there are plenty of examples of AttributeModifiers in vanilla code, but you will need IExtendedEntityProperties to store the number of deaths.
You didn't listen at all to what I said in my last reply: DO NOT MODIFY THE PLAYER'S HEALTH.
Apply an AttributeModifier to it instead. If you set the modifier to save, it will save and load automatically so you don't need to do anything when the player dies or loads, all you need to do is de-apply and re-apply the modifier when it changes.
For persisting through death, you simply need to copy the number of times died during PlayerEvent.Clone. In fact, that is the only variable you need: one to store the number of times died.
Another thing, DO NOT use getEntityData() or PERSISTED_NBT_TAG when using IExtendedEntityProperties - just use the NBT tag given to use in the save and load functions.
Hey CoolAlias, I'm having a weird behavior. My code works as intented, but I feel I'm doing something wrong.
On my Villager Tweaks mod, I made zombie villagers have professions. I use the extended properties to save the extra info on the server, but I need to notify the client so it knows what skin should be rendered.
To avoid unecessary network traffic, I used the "OnPlayerStartTracking" event, when a player starts tracking a zombie villager, the server will send him/her a IMessage object containing the entity ID and the profession.
It all works fine, BUT when the world loads for the first time, by the time the client receives that message from the server, the client world hasn't load any entities besides the player, so I have nothing to sync. The way I solved this problem was storing the message in a local HashMap and making a redundant call on the client "OnEntityJoinWorld" event. It works, but doesn't feel right...
Rollback Post to RevisionRollBack
Custom paintings! My latest project, still in BETA.
A small mod to improve your game, but keeping the vanilla flavour. For Minecraft 1.8.
Also check out my Redstone Jukebox mod, now updated to Minecraft 1.7.10!
Hey CoolAlias, I'm having a weird behavior. My code works as intented, but I feel I'm doing something wrong.
On my Villager Tweaks mod, I made zombie villagers have professions. I use the extended properties to save the extra info on the server, but I need to notify the client so it knows what skin should be rendered.
To avoid unecessary network traffic, I used the "OnPlayerStartTracking" event, when a player starts tracking a zombie villager, the server will send him/her a IMessage object containing the entity ID and the profession.
It all works fine, BUT when the world loads for the first time, by the time the client receives that message from the server, the client world hasn't load any entities besides the player, so I have nothing to sync. The way I solved this problem was storing the message in a local HashMap and making a redundant call on the client "OnEntityJoinWorld" event. It works, but doesn't feel right...
If it was your own entity, I'd tell you to use IEntityAddtionalSpawnData... EntityJoinWorldEvent is called for all entities, though, including zombies, so you can check for EntityZombie and send the profession update packet to the client side version of the zombie (using the entity ID).
Another solution would be to use DataWatcher in your IEEP class for the zombies, and that will keep the profession updated to all clients as necessary. While using DataWatcher on a vanilla entity especially can lead to conflicts with other mods, it would let you ditch the unwieldy hashmap.
This is really outdated, could you please revise the packet system for 1.7.10? The forge wiki tutorials for packets are also all outdated.
This is not outdated - everything still works perfectly fine if you follow the instructions and the sections on updating.
As for packets, those are completely unrelated to the point of this tutorial, but I included them to make it easier to follow. If you have trouble with packets, follow a packet tutorial.
Regarding my previous post, I am at a complete loss here when it comes to modifying health ( I am coding my first mod). I have no idea how to use Attribute Modifiers at all.
Could you point me in the right direction on how to do this?
The mod focus is losing a configurable amount of health on death. I can manage with configurations, but saving and reloading the NBT and setting health per individual player is where I am stuck...
Been trying to figure this out for a while and I am going insane >_>
Attribute modifiers work by using a UUID as a unique identifier. Use the createguid page to get a random unique id, then create references to both the UUID and the attribute modifier definition:
[/p]
[p]private static final UUID modifierUUID = UUID.fromString("{uuid from the website link above}");[/p]
[p]// If you'd like, you can setSaved to true, or calculate the bonus each time the player joins the world / respawns, which you should be doing anyway
private static final AttributeModifier healthModifier = (new AttributeModifier(modifierUUID, "Heath Penalty", 1.0D, 0));; //.setSaved(true);[/p]
[p][code][/p]
[p]The '1.0D' is the value of the modifier, in this case 1/2 heart (1 HP), and the second number '0' is the operation, in this case additive. See the wiki for more information. The reason you create an instance here is to reference when removing the modifier later on, so the '1.0D' doesn't really matter.[/p]
[p]Once you have your modifier definition, you can remove/add it at will - I recommend recalculating the bonus each time the player joins the world, since this is after you have read from NBT. Always make sure to remove the bonus if it exists, then add it:
[code][/p]
[p]// Get the player's actual attribute instance:[/p]
[p]IAttributeInstance attributeinstance = player.getEntityAttribute(SharedMonsterAttributes.maxHealth);
if (attributeinstance.getModifier(modifierUUID ) != null) {[/p]
[p] attributeinstance.removeModifier(healthModifier );[/p]
[p]}[/p]
[p]// The following is from your extended properties:[/p]
[p]int n = number of times player has died;[/p]
[p]// Create the new modifier and apply it to the player:[/p]
[p]AttributeModifier newModifier = (new AttributeModifier(modifierUUID , "Health Penalty", n * 1.0D, 0)); //.setSaved(true);
attributeinstance.applyModifier(newModifier);[/p]
[p]// You'll want to do a health check to make sure the current health does not exceed max health:[/p]
[p]player.setHealth(Math.min(player.getHealth(), player.getMaxHealth())); // check method names - I just typed those for convenience[/p]
[p]
The number of times died can be stored in extended properties - please see the tutorial for how to set that up, save, load, etc.
I followed the part on having the NBT persist on player death but that is not the case!
I think it is to do with this line: ExtendedPlayer.saveData(player);
I couldn't find this method anywhere in your thread, so could you please shed some light on the situation?
Cheers
There should not be any #saveData method at all; see my previous post for more information - that will be incorporated into the main tutorial topic someday, but I haven't had time and don't want to mess with the forum editor.
One thing that I screwed up was when accessing an extended property.
I found that if you use a base class, like EntityLivingBase and pass it to the get(PlayerEntity) function, you will not get the player instance, it will register a whole new invalid instance. This took me 3 hours to figure out. I was able to cast the EntityLivingBase object to EntityPlayer, with no errors, so I assumed all was ok, not so.
[url=2482915-wip-arkcraft-survival-evolved-dinos-taming]
If your extended properties class is for EntityPlayer and the #get method takes an EntityPlayer, then you must have an EntityPlayer instance to pass. EntityPlayer is also an EntityLivingBase, and an Entity, and actually more specifically it's an EntityPlayerMP or EntityClientPlayerMP.
At any rate, before casting any object to another class, you should always first check if it can be cast using instanceof.
Perhaps a refresher on class inheritance is in order.
Not sure what you were getting at. Are you saying a object of EntityLivingBase should or shouldn't work for in place of an object of EntityPlayer?
I do understand that EntityPlayer extends EntityLivingBase, which is why I figured it would be fine, especially since it was the player object, even though I didn't check using instanceof (normally I do).
[url=2482915-wip-arkcraft-survival-evolved-dinos-taming]
Notice I said 'refresher'
However, the more you write, the clearer it is that you lack understanding of some fundamental Java concepts, specifically class inheritance and object type.
It is impossible to have an object of type EntityLivingBase - that's an abstract class; the reason that many methods take EntityLivingBase as an argument is that it allows every sub-class of EntityLivingBase to then be passed as a valid argument - EntityPlayer, Animals, Mobs, all of these can then use the same method because they inherit from EntityLivingBase, and that is all the specificity the method requires.
The actual run-time type of each entity, however, is as specific as possible: EntityCreeper, EntityBat, etc. This is why you can type-cast at all - if the entity was literally an EntityLivingBase and nothing else, it would be impossible to cast it to a more specific class.
Please read the Java documentation that I linked earlier, as well as other pages on type-casting, inheritance, and similar topics.
I think I do understand class inheritance and object type. Although I don't think I have the language all straight. I learned C++ before Java, which works in a similar way, but back in 1985 when I learned it, the language was different on how it was described, so I need to update my lingo, I guess
I do suppose I should read those Forge API java docs, but each time I look at them, I'd rather be programming than reading a bunch of stuff I'll forget a few days later I learn more by doing than just reading a bunch of documentation. I may be wrong on this too, but also I thought that the javadoc didn't contain any documentation that isn't already in the code. So I've just been using the code as my documentation.
I'm still try to figure out the answer to my original question. But you keep bringing up more questions It is starting to all come back to me more clear though, so this is good (maybe someone besides me is learning stuff, which was why I posted in the first place).
So in BlockContainer, there is a method:
void onBlockPlacedBy(World p_149689_1_, int p_149689_2_, int p_149689_3_, int p_149689_4_, EntityLivingBase p_149689_5_, ItemStack p_149689_6_) It has a parameter, EntityLivingBase p_149689_5_. Which is what I probably should have said in the first place, but was being way too inaccurate in my description, sorry about that.
I neglected to use the instanceof (i'll try that out later tonight), but since the player placed the block, that caused the above method to be called, I assumed there was no way p_149689_5_ couldn't be an instance of EntityPlayer. It didn't help that this was in a function that I had an @Override on for a Custom Chest I created, and I renamed p_149689_5_ to player.
[url=2482915-wip-arkcraft-survival-evolved-dinos-taming]
I assume you know what assuming does to u and me, so keep that in mind when type-casting: ALWAYS check instanceof before you cast. Always.
One thing I was seeing, is there are many different ways to get a PlayerEntity that will be accepted by this function:
public static final ExtendedPlayer get(EntityPlayer player)
There are things like (I'm doing some of this from memory):
Minecraft.getMinecraft().thePlayer
Also some events have something like event.player
Problem is, I think some of these will "work" in single player, but in multiplayer they will fail. I actually don't have a good handle on this part of how Minecraft works yet, which is why I'm bringing this up. A good tutorial to go along with the two you present here, is one or more that explain the client/server aspect of Minecraft and which variables can be used and when to get a "player object" that will work with the IExtendedEntityProperties "interface". I put those in quotes, because I'm sure I'm not getting the terminology exactly right.
So I now will religiously use the instanceof, but if you insert something like Minecraft.getMinecraft().thePlayer in the get method, it will accept it, although I think it will not work in multiplayer. Am I getting that right?
Well, if anyone has a link to some good tutorials on this subject, I would appreciate it. I do have this one, which has been a big help, but doesn't answer all of my questions:
http://www.minecraftforum.net/forums/mapping-and-modding/mapping-and-modding-tutorials/2195105-a-coherent-guide-to-minecraft-forge-events
Thanks again
[url=2482915-wip-arkcraft-survival-evolved-dinos-taming]
To answer your question, ANY instance of EntityPlayer (which includes ALL sub-classes thereof) can be used to get the extended properties (for a player or parent class thereof).
Doesn't matter if it's from a method, from an event (which is just a class with various class members, e.g. an EntityPlayer), or anything else. If it's an EntityPlayer, it will work.
BUT, Minecraft.getMinecraft() is CLIENT side ONLY, meaning that you can only use it when your code is running on the client side. You should NEVER use the Minecraft class for anything unless you are absolutely certain your code is confined to the client side, such as in a GUI or render class.
Sorry, that part of the tutorial is outdated - don't store anything in the proxy anymore, use PlayerEvent.Clone instead. See explanation, or code.
You are going about it the wrong way - you don't want to change the player's actual max health value, you want to apply an AttributeModifier to it. Simply recalculate your health penalty any time the number of deaths changes, as well as each time the player joins the world.
This is done by removing the old modifier, if any, and adding a new one; there are plenty of examples of AttributeModifiers in vanilla code, but you will need IExtendedEntityProperties to store the number of deaths.
You didn't listen at all to what I said in my last reply: DO NOT MODIFY THE PLAYER'S HEALTH.
Apply an AttributeModifier to it instead. If you set the modifier to save, it will save and load automatically so you don't need to do anything when the player dies or loads, all you need to do is de-apply and re-apply the modifier when it changes.
For persisting through death, you simply need to copy the number of times died during PlayerEvent.Clone. In fact, that is the only variable you need: one to store the number of times died.
Another thing, DO NOT use getEntityData() or PERSISTED_NBT_TAG when using IExtendedEntityProperties - just use the NBT tag given to use in the save and load functions.
Nice Tutorials
Hey CoolAlias, I'm having a weird behavior. My code works as intented, but I feel I'm doing something wrong.
On my Villager Tweaks mod, I made zombie villagers have professions. I use the extended properties to save the extra info on the server, but I need to notify the client so it knows what skin should be rendered.
To avoid unecessary network traffic, I used the "OnPlayerStartTracking" event, when a player starts tracking a zombie villager, the server will send him/her a IMessage object containing the entity ID and the profession.
It all works fine, BUT when the world loads for the first time, by the time the client receives that message from the server, the client world hasn't load any entities besides the player, so I have nothing to sync. The way I solved this problem was storing the message in a local HashMap and making a redundant call on the client "OnEntityJoinWorld" event. It works, but doesn't feel right...
Custom paintings! My latest project, still in BETA.
A small mod to improve your game, but keeping the vanilla flavour. For Minecraft 1.8.
Also check out my Redstone Jukebox mod, now updated to Minecraft 1.7.10!
If it was your own entity, I'd tell you to use IEntityAddtionalSpawnData... EntityJoinWorldEvent is called for all entities, though, including zombies, so you can check for EntityZombie and send the profession update packet to the client side version of the zombie (using the entity ID).
Another solution would be to use DataWatcher in your IEEP class for the zombies, and that will keep the profession updated to all clients as necessary. While using DataWatcher on a vanilla entity especially can lead to conflicts with other mods, it would let you ditch the unwieldy hashmap.
Dude this was so helpful! Thank you!
Not doing mc modding that much anymore because I am making a full blown game that does not have limitations that mc has. (rip Magiology for now)
I may come back if MC fixes it's rendering pipeline.
This is really outdated, could you please revise the packet system for 1.7.10? The forge wiki tutorials for packets are also all outdated.
The packet system is reimplemented into forge code so all what you need to do is register the packet pipe line and the packets you want to use.
SimpleNetworkWrapper NETWORK_CHANNEL=NetworkRegistry.INSTANCE.newSimpleChannel(CHANNEL_NAME);
NETWORK_CHANNEL.registerMessage(IMessageHandler, IMessage, registrationId, Side);
Not doing mc modding that much anymore because I am making a full blown game that does not have limitations that mc has. (rip Magiology for now)
I may come back if MC fixes it's rendering pipeline.
This is not outdated - everything still works perfectly fine if you follow the instructions and the sections on updating.
As for packets, those are completely unrelated to the point of this tutorial, but I included them to make it easier to follow. If you have trouble with packets, follow a packet tutorial.
Attribute modifiers work by using a UUID as a unique identifier. Use the createguid page to get a random unique id, then create references to both the UUID and the attribute modifier definition:
The number of times died can be stored in extended properties - please see the tutorial for how to set that up, save, load, etc.
There should not be any #saveData method at all; see my previous post for more information - that will be incorporated into the main tutorial topic someday, but I haven't had time and don't want to mess with the forum editor.