Posted June 29, 20169 yr I'm currently attempting to make a throwable knife, however one of the fields in the entity class isn't propagating to the client. This is in 1.8, using Forge 11.14.4.1577. I would update, however the mod is large enough that this would not be the work of a few minutes, and I'd have to discuss it with my fellow dev. The problem: The field EntityThrownKnife#knife, which stores the thrown knife is null clientside. The expected behaviour: EntityThrownKnife#knife is sent to the client in the entity's NBT. The actual behaviour: EntityThrownKnife#knife has the correct value server-side, but it is null client-side. ItemKnife.java package net.einsteinsci.betterbeginnings.items; import net.einsteinsci.betterbeginnings.entity.projectile.EntityThrownKnife; import net.einsteinsci.betterbeginnings.register.IBBName; import net.minecraft.block.Block; import net.minecraft.entity.Entity; import net.minecraft.entity.player.EntityPlayer; import net.minecraft.init.Blocks; import net.minecraft.item.EnumAction; import net.minecraft.item.ItemStack; import net.minecraft.item.ItemTool; import net.minecraft.world.World; import java.util.HashSet; import java.util.Set; public abstract class ItemKnife extends ItemTool implements IBBName { public static final float DAMAGE = 3.0f; public ItemKnife(ToolMaterial material) { super(DAMAGE, material, getBreakable()); } public static Set getBreakable() { Set<Block> s = new HashSet<>(); // s.add(Blocks.log); // s.add(Blocks.log2); // s.add(Blocks.planks); s.add(Blocks.pumpkin); s.add(Blocks.lit_pumpkin); s.add(Blocks.melon_block); s.add(Blocks.clay); s.add(Blocks.grass); s.add(Blocks.mycelium); s.add(Blocks.leaves); s.add(Blocks.leaves2); s.add(Blocks.brown_mushroom_block); s.add(Blocks.red_mushroom_block); s.add(Blocks.glass); s.add(Blocks.glass_pane); s.add(Blocks.soul_sand); s.add(Blocks.stained_glass); s.add(Blocks.stained_glass_pane); s.add(Blocks.cactus); return s; } @Override public ItemStack onItemRightClick(ItemStack itemStackIn, World worldIn, EntityPlayer playerIn) { playerIn.setItemInUse(itemStackIn, this.getMaxItemUseDuration(itemStackIn)); return super.onItemRightClick(itemStackIn, worldIn, playerIn); } @Override public void onPlayerStoppedUsing(ItemStack stack, World worldIn, EntityPlayer playerIn, int timeLeft) { if(!worldIn.isRemote) { worldIn.spawnEntityInWorld(new EntityThrownKnife(worldIn, playerIn, stack)); playerIn.destroyCurrentEquippedItem(); } } @Override public EnumAction getItemUseAction(ItemStack stack) { return EnumAction.BOW; } @Override public int getMaxItemUseDuration(ItemStack stack) { return 72000; } @Override public boolean shouldRotateAroundWhenRendering() { return true; } @Override public int getHarvestLevel(ItemStack stack, String toolClass) { return toolMaterial.getHarvestLevel(); } @Override public Set<String> getToolClasses(ItemStack stack) { Set<String> res = new HashSet<>(); res.add("knife"); return res; } // ...which also requires this... @Override public ItemStack getContainerItem(ItemStack itemStack) { ItemStack result = itemStack.copy(); result.setItemDamage(itemStack.getItemDamage() + 1); return result; } // Allows durability-based crafting. @Override public boolean hasContainerItem(ItemStack stack) { return true; } @Override public abstract String getName(); } EntityThrownKnife.java package net.einsteinsci.betterbeginnings.entity.projectile; import net.einsteinsci.betterbeginnings.items.ItemKnife; import net.einsteinsci.betterbeginnings.register.RegisterItems; import net.minecraft.block.Block; import net.minecraft.entity.EntityLivingBase; import net.minecraft.entity.item.EntityItem; import net.minecraft.entity.projectile.EntityThrowable; import net.minecraft.init.Items; import net.minecraft.item.Item; import net.minecraft.item.ItemStack; import net.minecraft.item.ItemTool; import net.minecraft.nbt.NBTTagCompound; import net.minecraft.util.BlockPos; import net.minecraft.util.DamageSource; import net.minecraft.util.EnumFacing; import net.minecraft.util.EnumParticleTypes; import net.minecraft.util.MovingObjectPosition; import net.minecraft.world.World; public class EntityThrownKnife extends EntityThrowable { private static final String TAG_THROWN_KNIFE = "ThrownKnife"; private ItemStack knife; public EntityThrownKnife(World worldIn) { super(worldIn); } public EntityThrownKnife(World world, EntityLivingBase thrower, ItemStack knife) { super(world, thrower); this.knife = knife; } @Override protected void onImpact(MovingObjectPosition mop) { switch(mop.typeOfHit) { case BLOCK: if(this.knife != null && !worldObj.isRemote && !this.damageKnifeFromBlock(mop.getBlockPos(), knife)) { System.out.println("Block Hit"); EntityItem droppedKnife = new EntityItem(worldObj, posX, posY, posZ, knife); worldObj.spawnEntityInWorld(droppedKnife); } break; case ENTITY: mop.entityHit.attackEntityFrom(DamageSource.generic, ((ItemTool)knife.getItem()).getToolMaterial().getDamageVsEntity() + ItemKnife.DAMAGE); if(this.knife != null && !worldObj.isRemote && !knife.attemptDamageItem(2, rand)) { EntityItem droppedKnife = new EntityItem(worldObj, posX, posY, posZ, knife); worldObj.spawnEntityInWorld(droppedKnife); } break; default: break; } this.setDead(); } private boolean damageKnifeFromBlock(BlockPos pos, ItemStack knife) { Block blockHit = worldObj.getBlockState(pos).getBlock(); int damage = Math.round(blockHit.getBlockHardness(worldObj, pos)); if (damage == -1) damage = knife.getMaxDamage() + 1; return knife.attemptDamageItem(damage, rand); } @Override public void readEntityFromNBT(NBTTagCompound tagCompound) { super.readEntityFromNBT(tagCompound); knife = ItemStack.loadItemStackFromNBT(tagCompound.getCompoundTag(TAG_THROWN_KNIFE)); } @Override public void writeEntityToNBT(NBTTagCompound tagCompound) { super.writeEntityToNBT(tagCompound); if(knife != null) { NBTTagCompound thrownKnife = knife.writeToNBT(new NBTTagCompound()); tagCompound.setTag(TAG_THROWN_KNIFE, thrownKnife); System.out.println(tagCompound); } } public ItemStack getKnife() { return knife != null ? knife : new ItemStack(Items.stick); } } RenderThrownKnife.java package net.einsteinsci.betterbeginnings.client; import net.einsteinsci.betterbeginnings.entity.projectile.EntityThrownKnife; import net.minecraft.client.Minecraft; import net.minecraft.client.renderer.entity.RenderSnowball; import net.minecraft.entity.Entity; import net.minecraft.init.Items; import net.minecraft.item.ItemStack; public class RenderThrownKnife extends RenderSnowball { public RenderThrownKnife(Minecraft minecraft) { super(minecraft.getRenderManager(), Items.stick, minecraft.getRenderItem()); } @Override public ItemStack func_177082_d(Entity entityIn) { return ((EntityThrownKnife) entityIn).getKnife(); } } An alternate version of EntityThrownKnife which uses the DataWatcher package net.einsteinsci.betterbeginnings.entity.projectile; import net.einsteinsci.betterbeginnings.items.ItemKnife; import net.einsteinsci.betterbeginnings.register.RegisterItems; import net.minecraft.block.Block; import net.minecraft.entity.EntityLivingBase; import net.minecraft.entity.item.EntityItem; import net.minecraft.entity.projectile.EntityThrowable; import net.minecraft.init.Items; import net.minecraft.item.Item; import net.minecraft.item.ItemStack; import net.minecraft.item.ItemTool; import net.minecraft.nbt.NBTTagCompound; import net.minecraft.util.BlockPos; import net.minecraft.util.DamageSource; import net.minecraft.util.EnumFacing; import net.minecraft.util.EnumParticleTypes; import net.minecraft.util.MovingObjectPosition; import net.minecraft.world.World; public class EntityThrownKnife extends EntityThrowable { private static final String TAG_THROWN_KNIFE = "ThrownKnife"; private static final int DATA_THROWN_KNIFE = 16; private ItemStack knife; public EntityThrownKnife(World worldIn) { super(worldIn); } public EntityThrownKnife(World world, EntityLivingBase thrower, ItemStack knife) { super(world, thrower); this.knife = knife; } @Override protected void entityInit() { this.dataWatcher.addObjectByDataType(DATA_THROWN_KNIFE, 5); if(knife != null) { this.dataWatcher.updateObject(DATA_THROWN_KNIFE, knife); this.dataWatcher.setObjectWatched(DATA_THROWN_KNIFE); } } @Override protected void onImpact(MovingObjectPosition mop) { switch(mop.typeOfHit) { case BLOCK: if(this.knife != null && !worldObj.isRemote && !this.damageKnifeFromBlock(mop.getBlockPos(), knife)) { System.out.println("Block Hit"); EntityItem droppedKnife = new EntityItem(worldObj, posX, posY, posZ, knife); worldObj.spawnEntityInWorld(droppedKnife); } break; case ENTITY: mop.entityHit.attackEntityFrom(DamageSource.generic, ((ItemTool)knife.getItem()).getToolMaterial().getDamageVsEntity() + ItemKnife.DAMAGE); if(this.knife != null && !worldObj.isRemote && !knife.attemptDamageItem(2, rand)) { EntityItem droppedKnife = new EntityItem(worldObj, posX, posY, posZ, knife); worldObj.spawnEntityInWorld(droppedKnife); } break; default: break; } this.setDead(); } private boolean damageKnifeFromBlock(BlockPos pos, ItemStack knife) { Block blockHit = worldObj.getBlockState(pos).getBlock(); int damage = Math.round(blockHit.getBlockHardness(worldObj, pos)); if (damage == -1) damage = knife.getMaxDamage() + 1; return knife.attemptDamageItem(damage, rand); } @Override public void readEntityFromNBT(NBTTagCompound tagCompound) { super.readEntityFromNBT(tagCompound); knife = ItemStack.loadItemStackFromNBT(tagCompound.getCompoundTag(TAG_THROWN_KNIFE)); } @Override public void writeEntityToNBT(NBTTagCompound tagCompound) { super.writeEntityToNBT(tagCompound); if(knife != null) { NBTTagCompound thrownKnife = knife.writeToNBT(new NBTTagCompound()); tagCompound.setTag(TAG_THROWN_KNIFE, thrownKnife); System.out.println(tagCompound); } } public ItemStack getKnife() { return this.dataWatcher.getWatchableObjectItemStack(DATA_THROWN_KNIFE); } private enum KnifeType { GOLD(RegisterItems.goldKnife), FLINT(RegisterItems.flintKnife), BONE(RegisterItems.boneKnife), IRON(RegisterItems.ironKnife), DIAMOND(RegisterItems.diamondKnife); private Item item; private KnifeType(Item item) { this.item = item; } public Item getItem() { return item; } public static int getKnifeId(Item item) { return GOLD.getItem() == item ? GOLD.ordinal() : FLINT.getItem() == item ? FLINT.ordinal() : BONE.getItem() == item ? BONE.ordinal() : IRON.getItem() == item ? IRON.ordinal() : DIAMOND.getItem() == item ? DIAMOND.ordinal() : -1; } } }
June 29, 20169 yr Author First, this has nothing to do with NBT. NBT is for saving to disk. If you want the field to be synced to the client, you have to do it explicitly. If the field will never change after the entity is spawned, implement IEntityAdditionalSpawnData to write additional data into the spawn packet. If it can change, use a data watcher entry, but in that case you don't have the field itself, the data watcher holds the value. IEntityAdditionalSpawnData is what I was looking for, thanks.
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.