Posted May 15, 201510 yr Hello every body ! I'm using forge 11.14.1.1354 and I would like to create a custom entity. I already succeeded in that but I just can't save any new attribute ! I'm using a class implementing IExtendedEntityProperties to save the new fields but it doesn't seem to work. Here is my Custom entity class: I want to save the career field. import java.util.ArrayList; import java.util.Random; import net.minecraft.client.Minecraft; import net.minecraft.entity.EntityLiving; import net.minecraft.entity.ai.EntityAIVillagerInteract; import net.minecraft.entity.ai.EntityAIWander; import net.minecraft.entity.ai.EntityAIWatchClosest; import net.minecraft.entity.ai.EntityAIWatchClosest2; import net.minecraft.entity.monster.EntityMob; import net.minecraft.entity.player.EntityPlayer; import net.minecraft.entity.player.EntityPlayerMP; import net.minecraft.init.Items; import net.minecraft.item.ItemStack; import net.minecraft.nbt.NBTTagCompound; import net.minecraft.nbt.NBTTagList; import net.minecraft.stats.StatList; import net.minecraft.util.ChatComponentText; import net.minecraft.village.MerchantRecipeList; import net.minecraft.world.World; import net.minecraftforge.fml.relauncher.Side; import net.minecraftforge.fml.relauncher.SideOnly; public class EntityNPCM extends EntityMob{ protected short careerId; public EntityNPCM(World par1World) { super(par1World); this.tasks.addTask(9, new EntityAIWatchClosest2(this, EntityPlayer.class, 3.0F, 1.0F)); this.tasks.addTask(9, new EntityAIWander(this, 0.6D)); this.tasks.addTask(10, new EntityAIWatchClosest(this, EntityLiving.class, 8.0F)); this.setCanPickUpLoot(false); if(this.getCustomNameTag().isEmpty()) this.setCustomNameTag("NPC"); } @Override public int getTalkInterval() { return 200; } @Override protected boolean canDespawn() { return false; } public short getCarrerId() { return this.careerId; } public void setCarrerId(short id) { this.careerId = id; } @Override protected String getHurtSound() { return "shuyintestmod:hurtM"; } @Override protected String getDeathSound() { return "shuyintestmod:deathM"; } @Override protected String getLivingSound() { return "shuyintestmod:livingM"; } @Override public boolean interact(EntityPlayer player) { ItemStack itemstack = player.inventory.getCurrentItem(); boolean flag = itemstack != null && (itemstack.getItem() == Items.spawn_egg || itemstack.getItem() == Items.name_tag); if (!flag && this.isEntityAlive()) { if(this.worldObj.isRemote) { if(itemstack.getItem() instanceof ItemTest) { this.setDead(); return true; } if(player.capabilities.isCreativeMode && player.isSneaking()) { this.careerId++; if(careerId > 5) careerId = 0; } else { player.addChatMessage(new ChatComponentText(this.getCustomNameTag() + " : Mon ID = "+ this.careerId + " : " + getUniqueID().toString())); } } return true; } else { return super.interact(player); } } } And here is my ExtendedProperties class : package com.shuyin.test; import net.minecraft.entity.Entity; import net.minecraft.nbt.NBTTagCompound; import net.minecraft.world.World; import net.minecraftforge.common.IExtendedEntityProperties; public class NPCExtendedProperties implements IExtendedEntityProperties { public final static String extendedPropertiesName = "extendedPropertiesNPC"; protected EntityNPCM entity; protected World theWorld; @Override public void saveNBTData(NBTTagCompound compound) { // TODO Auto-generated method stub System.out.println("sauvegarde NPC :" + entity.getCarrerId() +" " + entity.getCustomNameTag() + " : " + entity.getUniqueID().toString()); NBTTagCompound subCompound = new NBTTagCompound(); compound.setTag(extendedPropertiesName, subCompound); subCompound.setShort("career", entity.getCarrerId()); //compound.setShort("career", entity.getCarrerId()); } @Override public void loadNBTData(NBTTagCompound compound) { // TODO Auto-generated method stub NBTTagCompound subcompound = (NBTTagCompound) compound.getTag(extendedPropertiesName); entity.setCarrerId(subcompound.getShort("career")); System.out.println("charge NPC :" + entity.getCarrerId()+" " + entity.getCustomNameTag() + " : " + entity.getUniqueID().toString()); } @Override public void init(Entity entity, World world) { // TODO Auto-generated method stub this.entity = (EntityNPCM)entity; theWorld = world; } } My entity can spawn, send me a message when I right click, It's career lvl change when I shift+right click but it don't save. With the log I can see the custom save and custom load happening but they just always take 0 when they save the field. I'm sorry I juste can't use the code balise. If someone can say me how to use it, I don't understand...
May 15, 201510 yr Change compound.setTag(extendedPropertiesName, subCompound); subCompound.setShort("career", entity.getCarrerId()); To subCompound.setShort("career", entity.getCarrerId()); compound.setTag(extendedPropertiesName, subCompound); Apparently I'm a complete and utter jerk and come to this forum just like to make fun of people, be confrontational, and make your personal life miserable. If you think this is the case, JUST REPORT ME. Otherwise you're just going to get reported when you reply to my posts and point it out, because odds are, I was trying to be nice. Exception: If you do not understand Java, I WILL NOT HELP YOU and your thread will get locked. DO NOT PM ME WITH PROBLEMS. No help will be given.
May 15, 201510 yr Author OK, I changed it but it don't work. I'm not sure but It seems to save correctly but when saving, it got 0 from career but have the good CustomName.(I changed the career level during the game and I'm sure the entity have a career lvl different than 0.
May 15, 201510 yr Few things: 1. Why are you using IEEP when you have your own Entity class - there is ONLY ONE explanation that is good enough to do that and that is - compatibility between all entities on server. - Followin above - you can save fields inside entity, IEEP is mandatory and rather used for vanilla mobs. 2. When using IEEP - you sure you are registering it? 1.7.10 is no longer supported by forge, you are on your own.
May 15, 201510 yr Author OK, you suggest I make something like that ? in my custom entity class @Override public void writeEntityToNBT(NBTTagCompound tagCompound) { super.writeEntityToNBT(tagCompound); tagCompound.setInteger("Career", this.careerId); } @Override public void readEntityFromNBT(NBTTagCompound tagCompund) { super.readEntityFromNBT(tagCompund); this.careerId = tagCompund.getInteger("Career"); } I already tried this and it don't work And I'm sure I registered my IEEP because I can see my logs when loading and saving nbt.
May 15, 201510 yr Are you sure the value is not set? Note that only server loads NBT, the client has to synchronized separately. 1.7.10 is no longer supported by forge, you are on your own.
May 15, 201510 yr Author So I'm testing the save with the two methods and a debug in each : [23:21:48] [server thread/INFO] [sTDOUT/]: [com.shuyin.test.NPCExtendedProperties:loadNBTData:31]: charge NPC :0 POupoule : 3892f46a-f22c-4720-b2ce-41bacbcaf0dc [23:21:48] [server thread/INFO] [sTDOUT/]: [com.shuyin.test.EntityNPCM:func_70037_a:83]: charge NBT NPC :0 POupoule : 3892f46a-f22c-4720-b2ce-41bacbcaf0dc [23:21:50] [server thread/INFO] [sTDOUT/]: [com.shuyin.test.NPCExtendedProperties:loadNBTData:31]: charge NPC :0 NPC : b5b17f2a-b5fc-4b3e-a380-27905785a363 [23:21:50] [server thread/INFO] [sTDOUT/]: [com.shuyin.test.EntityNPCM:func_70037_a:83]: charge NBT NPC :0 NPC : b5b17f2a-b5fc-4b3e-a380-27905785a363 -- Here, I suppose I'm playing, and I set the careerId to 2 for the NPC POupoule and 1 for the other -- [23:22:01] [server thread/INFO] [sTDOUT/]: [com.shuyin.test.NPCExtendedProperties:saveNBTData:17]: sauvegarde NPC :0 POupoule : 3892f46a-f22c-4720-b2ce-41bacbcaf0dc [23:22:01] [server thread/INFO] [sTDOUT/]: [com.shuyin.test.EntityNPCM:func_70014_b:71]: sauvegarde NBT NPC :0 POupoule : 3892f46a-f22c-4720-b2ce-41bacbcaf0dc [23:22:01] [server thread/INFO] [sTDOUT/]: [com.shuyin.test.NPCExtendedProperties:saveNBTData:17]: sauvegarde NPC :0 NPC : b5b17f2a-b5fc-4b3e-a380-27905785a363 [23:22:01] [server thread/INFO] [sTDOUT/]: [com.shuyin.test.EntityNPCM:func_70014_b:71]: sauvegarde NBT NPC :0 NPC : b5b17f2a-b5fc-4b3e-a380-27905785a363 But you can see, the custom name of the entity can be read but not the careerId.(always 0) If I set the careerId to 3 for example during the save, It will load 3 but the careerId will be 0 on my real Entity. EDIT : I noticed that the UUID of the Entity wasn't the same that the UUID showed in the log. In the custom entity class, do you have to specify a serverSide for the Write/read to NBT method ? And for the IEEP, do you have to register them in server side only ?
May 16, 201510 yr Author OK, since when I set this for both sides, when I right click on them, I receive two messages, how do I make sure I execute the execute method only once ?
May 16, 201510 yr Author OK here is my commonProxy class I register my two entities here (both client and server side) I need to register them only server side ? public class CommonProxy { public static Item testItem; public static Item itemProtectedDoor; public static Block protectedDoor; public static void registerEntity(Class entityClass, String name) { int entityID = EntityRegistry.findGlobalUniqueEntityId(); long seed = name.hashCode(); Random rand = new Random(seed); int primaryColor = rand.nextInt() * 16777215; int secondaryColor = rand.nextInt() * 16777215; EntityRegistry.registerGlobalEntityID(entityClass, name, entityID,primaryColor,secondaryColor); EntityRegistry.registerModEntity(entityClass, name, entityID, Test.instance, 64, 1, true); } public void preInit(FMLPreInitializationEvent e) { testItem = new ItemTest(); protectedDoor = new ProtectedDoor(); itemProtectedDoor = new ItemProtectedDoor(protectedDoor); registerEntity(EntityNPCM.class, "entityNPCM"); registerEntity(EntityNPCF.class, "entityNPCF"); } public void init(FMLInitializationEvent e) { registerEventListeners(); registerRenderers(); } public void postInit(FMLPostInitializationEvent e) { } public void registerEventListeners() { MinecraftForge.EVENT_BUS.register(new EventHandlerTest()); } public void registerRenderers() { } } (both client and server proxy extends to this method. I override the registerRenderers only client side) in my custom entity class, I need to modify my interact method like this ? @Override public boolean interact(EntityPlayer player) { ItemStack itemstack = player.inventory.getCurrentItem(); boolean flag = itemstack != null && (itemstack.getItem() == Items.spawn_egg || itemstack.getItem() == Items.name_tag); if (!flag && this.isEntityAlive()) { if(!this.worldObj.isRemote) { if(itemstack != null) { if(itemstack.getItem() instanceof ItemTest) { this.setDead(); return true; } } if(player.capabilities.isCreativeMode && player.isSneaking()) { this.careerId++; if(careerId > 5) careerId = 0; } else { player.addChatMessage(new ChatComponentText(this.getCustomNameTag() + " : Mon ID = "+ this.careerId + " : " + getUniqueID().toString())); } } return true; } else { return super.interact(player); } }
May 16, 201510 yr Author OK what should I use instead of RegsiterGlobal EntityID ? It tried without but I can't see my spawn egg anymore (it's obvious I know) but even with adding EntityList.entityEggs.put(entityID, new EntityList.EntityEggInfo(entityID, primaryColor, secondaryColor)); at the end of my register, I can only see an egg that can't spawn anything !
May 16, 201510 yr You cannot use a vanilla spawn egg. Make your own. This is such a shame, since so many mods add entities, and they all have to write their own implementation - I keep hoping one of these days Forge will provide a way to hook into the vanilla spawn egg / summon command. That said, it's not too difficult. I wrote an implementation that involves a custom entity list and a single item to handle all of those. I just really wish that this type of compatibility layer were already available / standard. http://i.imgur.com/NdrFdld.png[/img]
May 16, 201510 yr Author OK I can spawn my custom entity. But.. should I spawn it Server side, client side or both ? I tried all the solution : - serveur side : spawn the correct entity but I can't interract with it -Client side : spawn an herited entity and I can interract with it but not saving anything -both : spawn the two entities
May 16, 201510 yr Always spawn on server side only. If you coded it well, client will construct it from constructor(World world) automatically. 1.7.10 is no longer supported by forge, you are on your own.
May 16, 201510 yr Author OK It works server side fine now, thank you very much ! But I have a problem : my entities don't render correctly ! They have the default skin but normally when I shift + right click on them, they have to change skin(switch the careerId). It worked before but wasn't saved. Here, the careerId is saved but the entities don't change their skins. Here is my Render class : public class RenderNPC extends RenderLiving { public RenderNPC(ModelBiped model, float shadowSize) { super(Minecraft.getMinecraft().getRenderManager(), model, shadowSize); } @Override protected boolean canRenderName(Entity Entity) { return true; } @Override protected ResourceLocation getEntityTexture(Entity par1Entity) { if(par1Entity instanceof EntityNPCM) { ResourceLocation textureLocation; EntityNPCM entity = (EntityNPCM) par1Entity; if(entity.getCarrerId()<0 || entity.getCarrerId()>5 ) textureLocation = new ResourceLocation(Test.MODID + ":" + "textures/models/entityNPC" + (par1Entity instanceof EntityNPCF ? "F" : "M") + "0.png"); else textureLocation = new ResourceLocation(Test.MODID + ":" + "textures/models/entityNPC" + (par1Entity instanceof EntityNPCF ? "F" : "M") + entity.getCarrerId()+ ".png"); return textureLocation; } else return null; } } which is called client side only RenderingRegistry.registerEntityRenderingHandler(EntityNPCM.class, new RenderNPC(new ModelBiped(), 0.5F)); RenderingRegistry.registerEntityRenderingHandler(EntityNPCF.class, new RenderNPC(new ModelBiped(), 0.5F));
May 16, 201510 yr First things 1st: Do not declare Resource with every render pass, save those as static files somewhere and just reference them. Secondly - print out your getCarrerId() - do they change on client side? Post updated entity code. 1.7.10 is no longer supported by forge, you are on your own.
May 16, 201510 yr Author OK, Client side the getcarrerId returns always 0. Here is my Updated entity class : public class EntityNPCM extends EntityMob{ protected short careerId; public EntityNPCM(World par1World) { super(par1World); this.tasks.addTask(9, new EntityAIWatchClosest2(this, EntityPlayer.class, 3.0F, 1.0F)); this.tasks.addTask(9, new EntityAIWander(this, 0.6D)); this.tasks.addTask(10, new EntityAIWatchClosest(this, EntityLiving.class, 8.0F)); this.setCanPickUpLoot(false); if(this.getCustomNameTag().isEmpty()) this.setCustomNameTag("NPC"); } @Override public int getTalkInterval() { return 200; } @Override protected boolean canDespawn() { return false; } public short getCarrerId() { if(this.worldObj.isRemote) { System.out.println("getting careerId in Client :" + this.careerId); } else { System.out.println("getting careerId in Server :" + this.careerId); } return this.careerId; } public void setCarrerId(short id) { this.careerId = id; } /** * (abstract) Protected helper method to write subclass entity data to NBT. */ @Override public void writeEntityToNBT(NBTTagCompound tagCompound) { super.writeEntityToNBT(tagCompound); tagCompound.setShort("Career", this.careerId); System.out.println("sauvegarde NBT NPC :" + this.careerId +" " + getCustomNameTag() + " : " + getUniqueID().toString()); } /** * (abstract) Protected helper method to read subclass entity data from NBT. */ @Override public void readEntityFromNBT(NBTTagCompound tagCompund) { super.readEntityFromNBT(tagCompund); this.careerId = tagCompund.getShort("Career"); System.out.println("charge NBT NPC :" + this.careerId+" " + getCustomNameTag() + " : " + getUniqueID().toString()); } @Override protected String getHurtSound() { return "shuyintestmod:hurtM"; } @Override protected String getDeathSound() { return "shuyintestmod:deathM"; } @Override protected String getLivingSound() { return "shuyintestmod:livingM"; } @Override public boolean interact(EntityPlayer player) { ItemStack itemstack = player.inventory.getCurrentItem(); boolean flag = itemstack != null && (itemstack.getItem() == Items.spawn_egg || itemstack.getItem() == Items.name_tag); if (!flag && this.isEntityAlive()) { if(!this.worldObj.isRemote) { if(itemstack != null) { if(itemstack.getItem() instanceof ItemTest) { this.setDead(); return true; } } if(player.capabilities.isCreativeMode && player.isSneaking()) { this.careerId++; if(careerId > 5) careerId = 0; } else { player.addChatMessage(new ChatComponentText(this.getCustomNameTag() + " : Mon ID = "+ this.careerId + " : " + getUniqueID().toString())); } } return true; } else { return super.interact(player); } } }
May 16, 201510 yr Author Aand... It works ! Thank you very much for your precious help ! AAahh if only I knew about datawatcher sooner ! Ernio, "Do not declare Resource with every render pass, save those as static files somewhere and just reference them." Do I have to create a list of strings and create the new Resource location in the getEntityTexture Method or It's best to create a list of ResourceLocation directly ? I will post my custom Entity class later for help
May 16, 201510 yr It really doesn't matter how you do it, it can even be ResourceLocation[] that would be accessed with your numbers (carrer). It's more of an optimization stuff - "new" makes new object which is pointless to do in anything that is involved in ticking operations, especially render passes. Simply have static Map<Integer, RL> or RL[] or whatever else, jsut don't call "new" all the time. (RL = ResoLoc) 1.7.10 is no longer supported by forge, you are on your own.
May 16, 201510 yr Author OK I'll do that ! Here is my custom Entity class, I hope this will help someone for the dataWatcher or simply create a custom entity ! public class EntityNPCM extends EntityMob{ protected int careerId; protected boolean canWander; public EntityNPCM(World par1World) { super(par1World); canWander = false; updateTasks(); this.setCanPickUpLoot(false); if(this.getCustomNameTag().isEmpty()) this.setCustomNameTag("MALE_NPC"); } private void updateTasks() { this.tasks.taskEntries.clear(); this.tasks.addTask(1, new EntityAISwimming(this)); this.tasks.addTask(2, new EntityAIWatchClosest2(this, EntityPlayer.class, 3.0F, 1.0F)); this.tasks.addTask(3, new EntityAIWatchClosest(this, EntityLiving.class, 8.0F)); if(canWander) this.tasks.addTask(9, new EntityAIWander(this, 0.4D)); } @Override protected void entityInit() { super.entityInit(); this.dataWatcher.addObject(16, 0); this.dataWatcher.addObject(17, 0); } @Override public int getTalkInterval() { return 200; } @Override protected boolean canDespawn() { return false; } //function used in my render class to get the good careerId @SideOnly(Side.CLIENT) public int getClientCarrerId() { return this.dataWatcher.getWatchableObjectInt(16); } @SideOnly(Side.CLIENT) public boolean isClientMale() { return this.dataWatcher.getWatchableObjectInt(17) == 0; } public void setCarrerId(short id) { this.careerId = id; } /** * (abstract) Protected helper method to write subclass entity data to NBT. */ @Override public void writeEntityToNBT(NBTTagCompound tagCompound) { super.writeEntityToNBT(tagCompound); tagCompound.setInteger("Career", this.careerId); tagCompound.setBoolean("Wander", canWander); } /** * (abstract) Protected helper method to read subclass entity data from NBT. */ @Override public void readEntityFromNBT(NBTTagCompound tagCompund) { super.readEntityFromNBT(tagCompund); this.canWander = tagCompund.getBoolean("Wander"); this.careerId = tagCompund.getInteger("Career"); this.dataWatcher.updateObject(16, this.careerId); updateTasks(); } @Override protected String getHurtSound() { return "shuyintestmod:hurtM"; } @Override protected String getDeathSound() { return "shuyintestmod:deathM"; } @Override protected String getLivingSound() { return "shuyintestmod:livingM"; } @Override public boolean interact(EntityPlayer player) { ItemStack itemstack = player.inventory.getCurrentItem(); boolean flag = itemstack != null && (itemstack.getItem() == Items.spawn_egg || itemstack.getItem() == Items.name_tag); if (!flag && this.isEntityAlive() && !this.worldObj.isRemote) { if(itemstack != null) { if(itemstack.getItem() instanceof ItemTest) { if(player.isSneaking()) { this.setDead(); } else { canWander = !canWander; if(canWander) player.addChatMessage(new ChatComponentText(this.getCustomNameTag() + " will now be wandering.")); else player.addChatMessage(new ChatComponentText(this.getCustomNameTag() + " will stand still.")); updateTasks(); } return true; } } if(player.capabilities.isCreativeMode && player.isSneaking()) { this.careerId++; if(this.careerId > 5) this.careerId = 0; this.dataWatcher.updateObject(16, this.careerId); } else { player.addChatMessage(new ChatComponentText(this.getCustomNameTag() + " : Mon ID = "+ this.careerId + " : " + getUniqueID().toString())); } return true; } else { return super.interact(player); } } }
Join the conversation
You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.