Thanks, but I would say Mithion is the best person in the world, since he made the code for IExtendedEntityProperties
I updated it a little to allow for saving multiple properties to a player / providing inter-mod compatibility. Basically, just append your extended properties name to the username so it has a unique identifier.
Merci beaucoup mon ami! Those are some great tips! I'm just a hobby / novice coder, so it's always welcome to get advice like this from someone who really knows
I realized the mistake I made with the non-unique identifiers and fixed it earlier basically the same way, but the rest of your comments I didn't even consider. Thanks for the help! I'll update it soon!
EDIT: Alright - all updated. Thanks again - these are awesome! I'm not very familiar with manipulating classes like you do in your packet handler, but I added it in anyway as a more sophisticated example. I'll have to read up on that stuff
And wow, I'm honored to be the recipient of your first post! Bienvenue!
You're a really good programmer - makes me look like the amateur that I am
Thanks for another really wonderful post. You should consider making your own tutorials, though posting your code like that is almost as good. I'm learning a ton just by looking through it. Thanks!
Ok, I had a strange bug in my mod : the teleporters was displayed in both teleporter and stargate lists !
I needed some time to understand what was going on.
In fact teleporters and stargates was saved in the same "list" tag.
That happened because the tag you get as argument of the "loadNBTData(NBTTagCompound)" and "saveNBTData(NBTTagCompound)" methods in your IExtendedEntityProperties class isn't a tag created for your property, but the entity global tag !
To avoid problems with other property setting a tag with the same name, be sure to create a subtag with a unique name, for exemple the identifier of your property.
I updated the code of PlayerDataList and PlayerTeleporterData to show how I resolved the problem.
I don't know why, but the code is now displayed correctly indented, so I also updated the rest of the code.
This is absolutely correct. IF you don't make a new tag with a custom name, there will be no way to distinguish between properties. I forgot to mention about that in the tutorial, sorry! Putting it in now
Alright finished. Also, it's important to note that even if you have only one IExtendedProperty, you should still set it to a custom tag to avoid possible conflicts between tag names and vanilla tags.
For instance, if you want to add "Health" or some new "Active Effects", both of those tag names are already used by EntityLiving (and thus EntityPlayer), so you would have some pretty nasty side-effects.
Creating a custom tag compound first, you can use these same names without any conflict, because it is stored in a hierarchy: "PropertyName/Active Effects".
Really sorry about that oversight on my part! Thanks again Seigneur_Necron! Everyone remember to up the green arrow on his posts. He deserves it!
I doubt this is a packet issue - you're getting a Null Pointer Exception on line 29 for some reason. Are you sure you've registered your 'utility' properties? How about the code for getShieldCurrent() ? That could be the cause of the null pointer as well.
If you're trying to retrieve the data with a packet, then has the data been initialized on the side you're trying to retrieve it from? That could also cause a null pointer. For instance, sometimes client-side data is null or not correct, because important things are mainly stored on the server. If you're trying to get information from the client, it may be that you have to send a packet from the server first to tell the client the correct information, but in that case, you might as well just ignore packets and get the data directly, since you'd be server side anyways.
Sorry if that's a bit confusing. Post your utility code and I'll see if anything jumps out at me.
Try removing the 'if (!world.isRemote)' statement in your onItemRightClick method. That cleared up some issues for me with the Gui not syncing properly.
EDIT: Nevermind - I do still have this in mine; the problem was solved by sending packets whenever mana value is changed. See below.
Also, I don't know if you noticed as I changed some stuff later on, but in section 3.3 I sync the data whenever I change mana, which I notice you don't do for your shields. I'd recommend making some custom packets that send only the information for updating shields, which I would put in an array like this:
// the reason for the array is that it makes the following packet much easier to handle
private int[] shields = new int[12] or whatever number of shields;
public static final int SHIELD_CURRENT = 0, SHIELD_MAX = 1, etc; // Enum type would also be good, but int is easier to use
// you could also make an array for the names of each shield index, then use this list length to set the array shields = new int[names.length]
// then for the packet
public PacketShieldData(byte shield_type, int value) {
// shield_type is one of the above values, e.g. SHIELD_CURRENT
// value is the amount currently stored in that type, so 25 for current shields or whatever
}
// you'll need read, write and process packet methods - look in vanilla network/packet classes for this
Then you can send it to the player with PacketDispatcher.sendPacketToPlayer(new PacketShieldData(shield_type, value), player));
EDIT: I have tried this on the Eclipse server and the mana / item all function properly with the tutorial code as is, even with serverSideRequired=false (though I'm guessing you'll still need this set to true when trying it on a real server).
HOWEVER, I have NOT tested my code out multiplayer, so there may indeed be some error that I am unaware of. It's strange that you're getting an NPE only in multiplayer. If you haven't already, try putting println's in as many places as you can surrounding the lines that throw NPE.
Also, where do you set the value of Shield_Max? In your constructor, you say "Shield_Current = Shield_Max", but it looks to me like Shield_Max has not been initialized. Don't know if that has anything to do with it, but something to note.
Interesting. So in your Item, does it print 'TRUE' or 'TRUE2' ? What's the difference between 'utility' and 'Evy_Extend_EntityPlayer' classes? When you use the latter, it's not null, but when you use utility, it is? If so, then I'd look there for your reason.
Also, I don't have any experience modding for multiplayer, but could it be that the server doesn't have the mod and thus can't access the information? If the server didn't have the mod installed, it would have no way of accessing 'utility', right? So it would always be null server-side and sending a packet wouldn't fix it. Try requiring your mod server side as well and see if that changes anything.
It's also been pointed out to me that using 'Keyboard' methods will crash servers in multiplayer, so instead you need to make a KeyHandler as well as send packets from the client to the server telling the server which key was pressed, and then the server does what needs to be done and sends the results back to the client.
I haven't personally done this as of yet, but when I get around to it I will be sure to add it in to the tutorial.
I'm no expert on server/client stuff, so everyone please correct me wherever I'm wrong, but this is how I currently understand it:
World.isRemote, when true, means that you are client side, and false is server side (as in 'not remote from the server'). Usually, all methods are called twice, once on the client side and once on the server side, which generally keeps data synchronized.
Sometimes, however, you only want to do the operation on one side and send just the result to the other side; this usually goes server to client. The server decides what is and isn't possible, figures the outcome for an action and relays that outcome to the client, where the client then updates based on the received results.
A stereotypical case is spawning a new entity - it's almost always done only when the world is not remote (i.e. on the server) and then the server automatically sends packets to the client telling it about the new entity. The entity on the client side, however, typically doesn't store any vital information, it's just a 'dummy' of sorts for rendering purposes, at least as far as I understand it.
Basically what's happening is client-side is the side with the mod setting up extended properties, but you never tell the server what information you have, which is opposite of the normal scenario. Therefore, shield_current = 0 = shield_max = 0 so you print number 3 on the server side
In single-player, the server is the one dealing with all the ExtendedProperties and other data, not the client, so we send packets from the server updating the client.
Apparently for multiplayer, it's reversed for some reason, unless maybe you require your mod server side in which case it would work? I really am not sure, having never coded something for multiplayer.
At any rate, good work getting it to work. Be careful that it really is working as intended, meaning when the server sends information such as damage or whatever to the client, both the client and the server should have the same information.
It's generally not recommended for the client to tell the server what information is correct, so I'm not sure what the best way to handle it in this scenario would be. Hopefully others with more knowledge will comment.
Well, I tried using my mana tutorial on a LAN server and it worked perfectly, but then the mod is server side as well there. If you can get the server to host your mod (aka serverSideRequired = true), I wonder if that would solve the problem?
If you can't get it on the server, then does anyone know a way around it that keeps the server in charge of the information, or is the only solution to have a 'rogue' mod clientside calling all the shots?
If your mod only change some display, like adding a mini map, you don't need to install it on the server. Informations are saved on the client.
But If your mod adds new data to the game, like mana or shield for entities, you need to install it on both client and server. Your mod must have the following annotation :
The server will initialize, load, maintain and save the data. The client will just display.
If that new data is displayed client side, like with a mana/shield bar, you need the server to send packets to the client (or to all clients if the data is visible by all players) so the clients know the last state of the data.
If new data can be changed client side, for exemple naming an entity in a gui, you needs the client to send packets to the server too.
And there we have it confirmed, folks. Using IExtendedEntityProperties will require a server-side instance of your mod. Thank you once again, Seigneur_Necron. I'll put a note about this in the main tutorial.
@Pawaox Thanks for bumping it up And thanks for posting your problem here, as it helped add more useful information to the tutorial!
Hello, I just happened to come across this tutorial while looking for something else and noticed the extended entity properties tutorial and thought, "That sounds useful!" It is well written and helpful with many things other than extended entity properties!
I just thought I'd mention the player persisted NBT tag. I haven't tested it for extended entity properties, but the compound returned by
is automatically copied upon player respawn to the new player entity. That should greatly simplify the work needed to keep player data after death.
I hope it works, because I'm about to go use it!
Hello, I just happened to come across this tutorial while looking for something else and noticed the extended entity properties tutorial and thought, "That sounds useful!" It is well written and helpful with many things other than extended entity properties!
I just thought I'd mention the player persisted NBT tag. I haven't tested it for extended entity properties, but the compound returned by
is automatically copied upon player respawn to the new player entity. That should greatly simplify the work needed to keep player data after death.
I hope it works, because I'm about to go use it!
Nice find! That should cut out all the steps involving the proxy, but we'll still have to save on death and load on spawn. Much easier, though, you're right. I'll try it out later today and update the tutorial if it works. I don't see any reason why it wouldn't
@Pawaox: I think your issue may have to do with syncing on EntityJoinWorldEvent - doesn't look like you did that, and also in your setShield methods - be sure you sync there and only use those methods to set values (don't do something like this.Shield_Max = 100 - it won't sync properly). Check my full code below and compare to see what may have been the issue for you.
If anyone else is having issues with multi-player compatibility, here is the full code I used for this tutorial that works properly in the Eclipse server environment.
When testing from Eclipse server, it's not necessary to set 'serverSideRequired = true', since the mod is on the server anyway, but you'll need to do so in a real server situation or your data will be null.
ItemUseMana - exactly as in tutorial
ClientProxy - nothing here, just a shell 'registerRenderer()' method that currently does nothing
CommonProxy
public class CommonProxy implements IGuiHandler
{
/** Used to store IExtendedEntityProperties data temporarily between player death and respawn or dimension change */
private static final Map<String, NBTTagCompound> extendedEntityData = new HashMap<String, NBTTagCompound>();
public void registerRenderers() {}
@Override
public Object getServerGuiElement(int guiId, EntityPlayer player, World world, int x, int y, int z)
{
return null;
}
@Override
public Object getClientGuiElement(int guiId, EntityPlayer player, World world, int x, int y, int z)
{
return null;
}
/**
* Adds an entity's custom data to the map for temporary storage
*/
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);
}
}
ExtendedPlayer
public class ExtendedPlayer implements IExtendedEntityProperties
{
public final static String EXT_PROP_NAME = "ExtendedPlayer";
private final EntityPlayer player;
private int currentMana, maxMana;
public ExtendedPlayer(EntityPlayer player)
{
this.player = player;
this.currentMana = this.maxMana = 50;
}
/**
* Used to register these extended properties for the player during EntityConstructing event
*/
public static final void register(EntityPlayer player)
{
player.registerExtendedProperties(ExtendedPlayer.EXT_PROP_NAME, new ExtendedPlayer(player));
}
/**
* Returns ExtendedPlayer properties for player
*/
public static final ExtendedPlayer get(EntityPlayer player)
{
return (ExtendedPlayer) player.getExtendedProperties(EXT_PROP_NAME);
}
@Override
public final void saveNBTData(NBTTagCompound compound)
{
NBTTagCompound properties = new NBTTagCompound();
properties.setInteger("CurrentMana", this.currentMana);
properties.setInteger("MaxMana", this.maxMana);
compound.setTag(EXT_PROP_NAME, properties);
}
@Override
public final void loadNBTData(NBTTagCompound compound)
{
NBTTagCompound properties = (NBTTagCompound) compound.getTag(EXT_PROP_NAME);
this.currentMana = properties.getInteger("CurrentMana");
this.maxMana = properties.getInteger("MaxMana");
System.out.println("[TUT PROPS] Mana from NBT: " + this.currentMana + "/" + this.maxMana);
}
@Override
public void init(Entity entity, World world)
{
}
/**
* Returns true if the amount of mana was consumed or false
* if the player's current mana was insufficient
*/
public final boolean consumeMana(int amount)
{
boolean sufficient = amount <= this.currentMana;
this.currentMana -= (amount < this.currentMana ? amount : this.currentMana);
this.sync();
return sufficient;
}
/**
* Simple method sets current mana to max mana
*/
public final void replenishMana()
{
this.currentMana = this.maxMana;
this.sync();
}
/**
* Returns current mana amount
*/
public final int getCurrentMana()
{
return this.currentMana;
}
/**
* Sets current mana to amount or maxMana, whichever is lesser
*/
public final void setCurrentMana(int amount)
{
this.currentMana = (amount < this.maxMana ? amount : this.maxMana);
this.sync();
}
/**
* Returns max mana amount
*/
public final int getMaxMana()
{
return this.maxMana;
}
/**
* Sets max mana to amount or 0 if amount is less than 0
*/
public final void setMaxMana(int amount)
{
this.maxMana = (amount > 0 ? amount : 0);
this.sync();
}
/**
* Makes it look nicer in the methods save/loadProxyData
*/
private static final String getSaveKey(EntityPlayer player)
{
return player.username + ":" + EXT_PROP_NAME;
}
/**
* Does everything I did in onLivingDeathEvent and it's static,
* so you now only need to use the following in the above event:
* ExtendedPlayer.saveProxyData((EntityPlayer) event.entity));
*/
public static final void saveProxyData(EntityPlayer player)
{
ExtendedPlayer playerData = ExtendedPlayer.get(player);
NBTTagCompound savedData = new NBTTagCompound();
playerData.saveNBTData(savedData);
CommonProxy.storeEntityData(getSaveKey(player), savedData);
}
/**
* This cleans up the onEntityJoinWorld event by replacing most of the code
* with a single line: ExtendedPlayer.loadProxyData((EntityPlayer) event.entity));
*/
public static final void loadProxyData(EntityPlayer player)
{
ExtendedPlayer playerData = ExtendedPlayer.get(player);
NBTTagCompound savedData = CommonProxy.getEntityData(getSaveKey(player));
if(savedData != null) {
playerData.loadNBTData(savedData);
}
playerData.sync();
}
public final void sync()
{
// We only ever care about getting data from the server to the client
if (FMLCommonHandler.instance().getEffectiveSide() == Side.SERVER)
{
ByteArrayOutputStream bos = new ByteArrayOutputStream(8);
DataOutputStream outputStream = new DataOutputStream(bos);
try {
outputStream.writeInt(this.maxMana);
outputStream.writeInt(this.currentMana);
} catch (Exception ex) {
ex.printStackTrace();
}
Packet250CustomPayload packet = new Packet250CustomPayload();
packet.channel = "tutchannel";
packet.data = bos.toByteArray();
packet.length = bos.size();
EntityPlayerMP player1 = (EntityPlayerMP) player;
PacketDispatcher.sendPacketToPlayer(packet, (Player) player1);
}
}
}
TutEventHandler
public class TutEventHandler
{
@ForgeSubscribe
public void onEntityConstructing(EntityConstructing event)
{
if (event.entity instanceof EntityPlayer)
{
if (ExtendedPlayer.get((EntityPlayer) event.entity) == null)
ExtendedPlayer.register((EntityPlayer) event.entity);
}
}
@ForgeSubscribe
public void onEntityJoinWorld(EntityJoinWorldEvent event)
{
if (!event.entity.worldObj.isRemote && event.entity instanceof EntityPlayer)
{
// This will sync automatically, as well as keep data persistent across player death
// if you don't care about persistent data, you should use "ExtendedPlayer.get((EntityPlayer) event.entity).sync()" instead
ExtendedPlayer.loadProxyData((EntityPlayer) event.entity);
}
}
@ForgeSubscribe
public void onLivingDeathEvent(LivingDeathEvent event)
{
if (!event.entity.worldObj.isRemote && event.entity instanceof EntityPlayer)
{
ExtendedPlayer.saveProxyData((EntityPlayer) event.entity);
}
}
@ForgeSubscribe
public void onLivingFallEvent(LivingFallEvent event)
{
if (event.entity instanceof EntityPlayer)
{
ExtendedPlayer props = ExtendedPlayer.get((EntityPlayer) event.entity);
if (event.distance > 3.0F && props.getCurrentMana() > 0)
{
System.out.println("[EVENT] Fall distance: " + event.distance);
System.out.println("[EVENT] Current mana: " + props.getCurrentMana());
float reduceby = props.getCurrentMana() < (event.distance - 3.0F) ? props.getCurrentMana() : (event.distance - 3.0F);
event.distance -= reduceby;
props.consumeMana((int) reduceby);
System.out.println("[EVENT] Adjusted fall distance: " + event.distance);
}
}
}
}
PacketHandler
public class TutorialPacketHandler implements IPacketHandler
{
@Override
public void onPacketData(INetworkManager manager, Packet250CustomPayload packet, Player player)
{
if (packet.channel.equals("tutchannel")) {
handleExtendedProperties(packet, player);
}
}
private void handleExtendedProperties(Packet250CustomPayload packet, Player player)
{
DataInputStream inputStream = new DataInputStream(new ByteArrayInputStream(packet.data));
ExtendedPlayer props = ExtendedPlayer.get((EntityPlayer) player);
try {
props.setMaxMana(inputStream.readInt());
props.setCurrentMana(inputStream.readInt());
} catch (IOException e) {
e.printStackTrace();
return;
}
System.out.println("[PACKET] Mana from packet: " + props.getCurrentMana() + "/" + props.getMaxMana());
}
}
GuiManaBar
@SideOnly(Side.CLIENT)
public class GuiManaBar extends Gui
{
private Minecraft mc;
private static final ResourceLocation texturepath = new ResourceLocation("tutorial", "textures/gui/mana_bar.png");
public GuiManaBar(Minecraft mc)
{
super();
this.mc = mc;
}
@ForgeSubscribe(priority = EventPriority.NORMAL)
public void onRenderExperienceBar(RenderGameOverlayEvent event)
{
if (event.isCancelable() || event.type != ElementType.EXPERIENCE) { return; }
ExtendedPlayer props = (ExtendedPlayer) this.mc.thePlayer.getExtendedProperties(ExtendedPlayer.EXT_PROP_NAME);
if (props == null || props.getMaxMana() == 0) { return; }
int xPos = 2;
int yPos = 2;
GL11.glColor4f(1.0F, 1.0F, 1.0F, 1.0F);
GL11.glDisable(GL11.GL_LIGHTING);
this.mc.func_110434_K().func_110577_a(texturepath);
this.drawTexturedModalRect(xPos, yPos, 0, 0, 52, 5);
int manabarwidth = (int)(((float) props.getCurrentMana() / props.getMaxMana()) * 50);
this.drawTexturedModalRect(xPos + 1, yPos + 1, 0, 5, manabarwidth, 3);
}
}
I followed the one on github and if you follow the instructions it works fine in multiplayer
Thanks this is gonna be the basis for my enchantment overhaul
I followed the one on github and if you follow the instructions it works fine in multiplayer
Thanks this is gonna be the basis for my enchantment overhaul
That's a cool-looking mod! Glad to have helped out I especially like the healing enchantment for chest-pieces - I hate waiting around or having to eat to heal
And a fine job as always
Thanks, but I would say Mithion is the best person in the world, since he made the code for IExtendedEntityProperties
I updated it a little to allow for saving multiple properties to a player / providing inter-mod compatibility. Basically, just append your extended properties name to the username so it has a unique identifier.
Merci beaucoup mon ami! Those are some great tips! I'm just a hobby / novice coder, so it's always welcome to get advice like this from someone who really knows
I realized the mistake I made with the non-unique identifiers and fixed it earlier basically the same way, but the rest of your comments I didn't even consider. Thanks for the help! I'll update it soon!
EDIT: Alright - all updated. Thanks again - these are awesome! I'm not very familiar with manipulating classes like you do in your packet handler, but I added it in anyway as a more sophisticated example. I'll have to read up on that stuff
And wow, I'm honored to be the recipient of your first post! Bienvenue!
You're a really good programmer - makes me look like the amateur that I am
Thanks for another really wonderful post. You should consider making your own tutorials, though posting your code like that is almost as good. I'm learning a ton just by looking through it. Thanks!
This is absolutely correct. IF you don't make a new tag with a custom name, there will be no way to distinguish between properties. I forgot to mention about that in the tutorial, sorry! Putting it in now
Alright finished. Also, it's important to note that even if you have only one IExtendedProperty, you should still set it to a custom tag to avoid possible conflicts between tag names and vanilla tags.
For instance, if you want to add "Health" or some new "Active Effects", both of those tag names are already used by EntityLiving (and thus EntityPlayer), so you would have some pretty nasty side-effects.
Creating a custom tag compound first, you can use these same names without any conflict, because it is stored in a hierarchy: "PropertyName/Active Effects".
Really sorry about that oversight on my part! Thanks again Seigneur_Necron! Everyone remember to up the green arrow on his posts. He deserves it!
If you're trying to retrieve the data with a packet, then has the data been initialized on the side you're trying to retrieve it from? That could also cause a null pointer. For instance, sometimes client-side data is null or not correct, because important things are mainly stored on the server. If you're trying to get information from the client, it may be that you have to send a packet from the server first to tell the client the correct information, but in that case, you might as well just ignore packets and get the data directly, since you'd be server side anyways.
Sorry if that's a bit confusing. Post your utility code and I'll see if anything jumps out at me.
EDIT: Nevermind - I do still have this in mine; the problem was solved by sending packets whenever mana value is changed. See below.
Then you can send it to the player with PacketDispatcher.sendPacketToPlayer(new PacketShieldData(shield_type, value), player));
EDIT: I have tried this on the Eclipse server and the mana / item all function properly with the tutorial code as is, even with serverSideRequired=false (though I'm guessing you'll still need this set to true when trying it on a real server).
HOWEVER, I have NOT tested my code out multiplayer, so there may indeed be some error that I am unaware of. It's strange that you're getting an NPE only in multiplayer. If you haven't already, try putting println's in as many places as you can surrounding the lines that throw NPE.Also, I don't have any experience modding for multiplayer, but could it be that the server doesn't have the mod and thus can't access the information? If the server didn't have the mod installed, it would have no way of accessing 'utility', right? So it would always be null server-side and sending a packet wouldn't fix it. Try requiring your mod server side as well and see if that changes anything.
I haven't personally done this as of yet, but when I get around to it I will be sure to add it in to the tutorial.
World.isRemote, when true, means that you are client side, and false is server side (as in 'not remote from the server'). Usually, all methods are called twice, once on the client side and once on the server side, which generally keeps data synchronized.
Sometimes, however, you only want to do the operation on one side and send just the result to the other side; this usually goes server to client. The server decides what is and isn't possible, figures the outcome for an action and relays that outcome to the client, where the client then updates based on the received results.
A stereotypical case is spawning a new entity - it's almost always done only when the world is not remote (i.e. on the server) and then the server automatically sends packets to the client telling it about the new entity. The entity on the client side, however, typically doesn't store any vital information, it's just a 'dummy' of sorts for rendering purposes, at least as far as I understand it.
Basically what's happening is client-side is the side with the mod setting up extended properties, but you never tell the server what information you have, which is opposite of the normal scenario. Therefore, shield_current = 0 = shield_max = 0 so you print number 3 on the server side
In single-player, the server is the one dealing with all the ExtendedProperties and other data, not the client, so we send packets from the server updating the client.
Apparently for multiplayer, it's reversed for some reason, unless maybe you require your mod server side in which case it would work? I really am not sure, having never coded something for multiplayer.
At any rate, good work getting it to work. Be careful that it really is working as intended, meaning when the server sends information such as damage or whatever to the client, both the client and the server should have the same information.
It's generally not recommended for the client to tell the server what information is correct, so I'm not sure what the best way to handle it in this scenario would be. Hopefully others with more knowledge will comment.
If you can't get it on the server, then does anyone know a way around it that keeps the server in charge of the information, or is the only solution to have a 'rogue' mod clientside calling all the shots?
And there we have it confirmed, folks. Using IExtendedEntityProperties will require a server-side instance of your mod. Thank you once again, Seigneur_Necron. I'll put a note about this in the main tutorial.
@Pawaox Thanks for bumping it up And thanks for posting your problem here, as it helped add more useful information to the tutorial!
I just thought I'd mention the player persisted NBT tag. I haven't tested it for extended entity properties, but the compound returned by
is automatically copied upon player respawn to the new player entity. That should greatly simplify the work needed to keep player data after death.
I hope it works, because I'm about to go use it!
Nice find! That should cut out all the steps involving the proxy, but we'll still have to save on death and load on spawn. Much easier, though, you're right. I'll try it out later today and update the tutorial if it works. I don't see any reason why it wouldn't
If anyone else is having issues with multi-player compatibility, here is the full code I used for this tutorial that works properly in the Eclipse server environment.
When testing from Eclipse server, it's not necessary to set 'serverSideRequired = true', since the mod is on the server anyway, but you'll need to do so in a real server situation or your data will be null.
ItemUseMana - exactly as in tutorial
ClientProxy - nothing here, just a shell 'registerRenderer()' method that currently does nothing
CommonProxy
Thanks this is gonna be the basis for my enchantment overhaul
That's a cool-looking mod! Glad to have helped out I especially like the healing enchantment for chest-pieces - I hate waiting around or having to eat to heal