Jump to content

Recommended Posts

Posted

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;  
	}
}
}

 

Posted

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.

Guest
Unfortunately, your content contains terms that we do not allow. Please edit your content to remove the highlighted words below.
Reply to this topic...

×   Pasted as rich text.   Restore formatting

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

Announcements



×
×
  • Create New...

Important Information

By using this site, you agree to our Terms of Use.