I've created a custom throwable class which is basically minecraft's default throwable class with a few values changed. I'm trying to do some math based on where the entity is in relation to the player that threw it. However, the client side copy of the entity has the thrower as null while the server doesn't. This causes null pointer errors when I try to access the thrower and it runs on the client. Any idea how to get the client and server synced correctly?
Taji34EntityThrowable.java:
package com.taji34.troncraft;
import java.util.List;
import net.minecraft.block.Block;
import net.minecraft.entity.Entity;
import net.minecraft.entity.EntityLivingBase;
import net.minecraft.entity.IProjectile;
import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.init.Blocks;
import net.minecraft.nbt.NBTTagCompound;
import net.minecraft.util.AxisAlignedBB;
import net.minecraft.util.MathHelper;
import net.minecraft.util.MovingObjectPosition;
import net.minecraft.util.Vec3;
import net.minecraft.world.World;
import cpw.mods.fml.relauncher.Side;
import cpw.mods.fml.relauncher.SideOnly;
public abstract class Taji34EntityThrowable extends Entity implements IProjectile {
private int field_145788_c = -1;
private int field_145786_d = -1;
private int field_145787_e = -1;
private Block field_145785_f;
protected boolean inGround;
public int throwableShake;
/** The entity that threw this throwable item. */
private EntityLivingBase thrower;
private String throwerName;
private int ticksInGround;
private int ticksInAir;
private static final String __OBFID = "CL_00001723";
public Taji34EntityThrowable(World par1World)
{
super(par1World);
this.setSize(0.25F, 0.25F);
}
protected void entityInit() {}
/**
* Checks if the entity is in range to render by using the past in distance and comparing it to its average edge
* length * 64 * renderDistanceWeight Args: distance
*/
@SideOnly(Side.CLIENT)
public boolean isInRangeToRenderDist(double par1)
{
double d1 = this.boundingBox.getAverageEdgeLength() * 4.0D;
d1 *= 64.0D;
return par1 < d1 * d1;
}
public Taji34EntityThrowable(World par1World, EntityLivingBase par2EntityLivingBase)
{
super(par1World);
this.thrower = par2EntityLivingBase;
this.setSize(0.25F, 0.25F);
this.setLocationAndAngles(par2EntityLivingBase.posX, par2EntityLivingBase.posY + (double)par2EntityLivingBase.getEyeHeight(), par2EntityLivingBase.posZ, par2EntityLivingBase.rotationYaw, par2EntityLivingBase.rotationPitch);
this.posX -= (double)(MathHelper.cos(this.rotationYaw / 180.0F * (float)Math.PI) * 0.16F);
this.posY -= 0.10000000149011612D;
this.posZ -= (double)(MathHelper.sin(this.rotationYaw / 180.0F * (float)Math.PI) * 0.16F);
this.setPosition(this.posX, this.posY, this.posZ);
this.yOffset = 0.0F;
float f = 0.4F;
this.motionX = (double)(-MathHelper.sin(this.rotationYaw / 180.0F * (float)Math.PI) * MathHelper.cos(this.rotationPitch / 180.0F * (float)Math.PI) * f);
this.motionZ = (double)(MathHelper.cos(this.rotationYaw / 180.0F * (float)Math.PI) * MathHelper.cos(this.rotationPitch / 180.0F * (float)Math.PI) * f);
this.motionY = (double)(-MathHelper.sin((this.rotationPitch + this.func_70183_g()) / 180.0F * (float)Math.PI) * f);
this.setThrowableHeading(this.motionX, this.motionY, this.motionZ, this.func_70182_d(), 1.0F);
}
public Taji34EntityThrowable(World par1World, double par2, double par4, double par6)
{
super(par1World);
this.ticksInGround = 0;
this.setSize(0.25F, 0.25F);
this.setPosition(par2, par4, par6);
this.yOffset = 0.0F;
}
protected float func_70182_d()
{
return 1.5F;
}
protected float func_70183_g()
{
return 0.0F;
}
/**
* Similar to setArrowHeading, it's point the throwable entity to a x, y, z direction.
*/
public void setThrowableHeading(double par1, double par3, double par5, float par7, float par8)
{
float f2 = MathHelper.sqrt_double(par1 * par1 + par3 * par3 + par5 * par5);
par1 /= (double)f2;
par3 /= (double)f2;
par5 /= (double)f2;
par1 += this.rand.nextGaussian() * 0.007499999832361937D * (double)par8;
par3 += this.rand.nextGaussian() * 0.007499999832361937D * (double)par8;
par5 += this.rand.nextGaussian() * 0.007499999832361937D * (double)par8;
par1 *= (double)par7;
par3 *= (double)par7;
par5 *= (double)par7;
this.motionX = par1;
this.motionY = par3;
this.motionZ = par5;
float f3 = MathHelper.sqrt_double(par1 * par1 + par5 * par5);
this.prevRotationYaw = this.rotationYaw = (float)(Math.atan2(par1, par5) * 180.0D / Math.PI);
this.prevRotationPitch = this.rotationPitch = (float)(Math.atan2(par3, (double)f3) * 180.0D / Math.PI);
this.ticksInGround = 0;
}
/**
* Sets the velocity to the args. Args: x, y, z
*/
@SideOnly(Side.CLIENT)
public void setVelocity(double par1, double par3, double par5)
{
this.motionX = par1;
this.motionY = par3;
this.motionZ = par5;
if (this.prevRotationPitch == 0.0F && this.prevRotationYaw == 0.0F)
{
float f = MathHelper.sqrt_double(par1 * par1 + par5 * par5);
this.prevRotationYaw = this.rotationYaw = (float)(Math.atan2(par1, par5) * 180.0D / Math.PI);
this.prevRotationPitch = this.rotationPitch = (float)(Math.atan2(par3, (double)f) * 180.0D / Math.PI);
}
}
/**
* Called to update the entity's position/logic.
*/
public void onUpdate()
{
this.lastTickPosX = this.posX;
this.lastTickPosY = this.posY;
this.lastTickPosZ = this.posZ;
super.onUpdate();
if (this.throwableShake > 0)
{
--this.throwableShake;
}
if (this.inGround)
{
if (this.worldObj.getBlock(this.field_145788_c, this.field_145786_d, this.field_145787_e) == this.field_145785_f)
{
++this.ticksInGround;
if (this.ticksInGround == 1200)
{
this.setDead();
}
return;
}
this.inGround = false;
this.motionX *= (double)(this.rand.nextFloat() * 0.2F);
this.motionY *= (double)(this.rand.nextFloat() * 0.2F);
this.motionZ *= (double)(this.rand.nextFloat() * 0.2F);
this.ticksInGround = 0;
this.ticksInAir = 0;
}
else
{
++this.ticksInAir;
}
Vec3 vec3 = Vec3.createVectorHelper(this.posX, this.posY, this.posZ);
Vec3 vec31 = Vec3.createVectorHelper(this.posX + this.motionX, this.posY + this.motionY, this.posZ + this.motionZ);
MovingObjectPosition movingobjectposition = this.worldObj.rayTraceBlocks(vec3, vec31);
vec3 = Vec3.createVectorHelper(this.posX, this.posY, this.posZ);
vec31 = Vec3.createVectorHelper(this.posX + this.motionX, this.posY + this.motionY, this.posZ + this.motionZ);
if (movingobjectposition != null)
{
vec31 = Vec3.createVectorHelper(movingobjectposition.hitVec.xCoord, movingobjectposition.hitVec.yCoord, movingobjectposition.hitVec.zCoord);
}
if (!this.worldObj.isRemote)
{
Entity entity = null;
List list = this.worldObj.getEntitiesWithinAABBExcludingEntity(this, this.boundingBox.addCoord(this.motionX, this.motionY, this.motionZ).expand(1.0D, 1.0D, 1.0D));
double d0 = 0.0D;
EntityLivingBase entitylivingbase = this.getThrower();
for (int j = 0; j < list.size(); ++j)
{
Entity entity1 = (Entity)list.get(j);
if (entity1.canBeCollidedWith() && (entity1 != entitylivingbase || this.ticksInAir >= 5))
{
float f = 0.3F;
AxisAlignedBB axisalignedbb = entity1.boundingBox.expand((double)f, (double)f, (double)f);
MovingObjectPosition movingobjectposition1 = axisalignedbb.calculateIntercept(vec3, vec31);
if (movingobjectposition1 != null)
{
double d1 = vec3.distanceTo(movingobjectposition1.hitVec);
if (d1 < d0 || d0 == 0.0D)
{
entity = entity1;
d0 = d1;
}
}
}
}
if (entity != null)
{
movingobjectposition = new MovingObjectPosition(entity);
}
}
if (movingobjectposition != null)
{
if (movingobjectposition.typeOfHit == MovingObjectPosition.MovingObjectType.BLOCK && this.worldObj.getBlock(movingobjectposition.blockX, movingobjectposition.blockY, movingobjectposition.blockZ) == Blocks.portal)
{
this.setInPortal();
}
else
{
this.onImpact(movingobjectposition);
}
}
this.posX += this.motionX;
this.posY += this.motionY;
this.posZ += this.motionZ;
float f1 = MathHelper.sqrt_double(this.motionX * this.motionX + this.motionZ * this.motionZ);
this.rotationYaw = (float)(Math.atan2(this.motionX, this.motionZ) * 180.0D / Math.PI);
for (this.rotationPitch = (float)(Math.atan2(this.motionY, (double)f1) * 180.0D / Math.PI); this.rotationPitch - this.prevRotationPitch < -180.0F; this.prevRotationPitch -= 360.0F)
{
;
}
while (this.rotationPitch - this.prevRotationPitch >= 180.0F)
{
this.prevRotationPitch += 360.0F;
}
while (this.rotationYaw - this.prevRotationYaw < -180.0F)
{
this.prevRotationYaw -= 360.0F;
}
while (this.rotationYaw - this.prevRotationYaw >= 180.0F)
{
this.prevRotationYaw += 360.0F;
}
this.rotationPitch = this.prevRotationPitch + (this.rotationPitch - this.prevRotationPitch) * 0.2F;
this.rotationYaw = this.prevRotationYaw + (this.rotationYaw - this.prevRotationYaw) * 0.2F;
float f2 = 0.99F;
float f3 = this.getGravityVelocity();
if (this.isInWater())
{
for (int i = 0; i < 4; ++i)
{
float f4 = 0.25F;
this.worldObj.spawnParticle("bubble", this.posX - this.motionX * (double)f4, this.posY - this.motionY * (double)f4, this.posZ - this.motionZ * (double)f4, this.motionX, this.motionY, this.motionZ);
}
f2 = 0.8F;
}
this.motionX *= (double)f2;
this.motionY *= (double)f2;
this.motionZ *= (double)f2;
this.motionY -= (double)f3;
this.setPosition(this.posX, this.posY, this.posZ);
System.out.println(this.thrower);
}
/**
* Gets the amount of gravity to apply to the thrown entity with each tick.
*/
protected float getGravityVelocity()
{
return 0.03F;
}
/**
* Called when this EntityThrowable hits a block or entity.
*/
protected abstract void onImpact(MovingObjectPosition var1);
/**
* (abstract) Protected helper method to write subclass entity data to NBT.
*/
public void writeEntityToNBT(NBTTagCompound par1NBTTagCompound)
{
par1NBTTagCompound.setShort("xTile", (short)this.field_145788_c);
par1NBTTagCompound.setShort("yTile", (short)this.field_145786_d);
par1NBTTagCompound.setShort("zTile", (short)this.field_145787_e);
par1NBTTagCompound.setByte("inTile", (byte)Block.getIdFromBlock(this.field_145785_f));
par1NBTTagCompound.setByte("shake", (byte)this.throwableShake);
par1NBTTagCompound.setByte("inGround", (byte)(this.inGround ? 1 : 0));
if ((this.throwerName == null || this.throwerName.length() == 0) && this.thrower != null && this.thrower instanceof EntityPlayer)
{
this.throwerName = this.thrower.getCommandSenderName();
}
par1NBTTagCompound.setString("ownerName", this.throwerName == null ? "" : this.throwerName);
}
/**
* (abstract) Protected helper method to read subclass entity data from NBT.
*/
public void readEntityFromNBT(NBTTagCompound par1NBTTagCompound)
{
this.field_145788_c = par1NBTTagCompound.getShort("xTile");
this.field_145786_d = par1NBTTagCompound.getShort("yTile");
this.field_145787_e = par1NBTTagCompound.getShort("zTile");
this.field_145785_f = Block.getBlockById(par1NBTTagCompound.getByte("inTile") & 255);
this.throwableShake = par1NBTTagCompound.getByte("shake") & 255;
this.inGround = par1NBTTagCompound.getByte("inGround") == 1;
this.throwerName = par1NBTTagCompound.getString("ownerName");
if (this.throwerName != null && this.throwerName.length() == 0)
{
this.throwerName = null;
}
}
@SideOnly(Side.CLIENT)
public float getShadowSize()
{
return 0.0F;
}
public EntityLivingBase getThrower()
{
if (this.thrower == null && this.throwerName != null && this.throwerName.length() > 0)
{
this.thrower = this.worldObj.getPlayerEntityByName(this.throwerName);
}
return this.thrower;
}
}
Specifically I call
System.out.println(this.thrower);
and my console prints out
[15:00:44] [server thread/INFO] [sTDOUT]: [com.taji34.troncraft.Taji34EntityThrowable:onUpdate:279]: EntityPlayerMP['Player914'/0, l='Test', x=-190.50, y=4.00, z=-385.50]
[15:00:44] [Client thread/INFO] [sTDOUT]: [com.taji34.troncraft.Taji34EntityThrowable:onUpdate:279]: null
[15:00:44] [Client thread/INFO] [sTDOUT]: [com.taji34.troncraft.Taji34EntityThrowable:onUpdate:279]: null
[15:00:44] [server thread/INFO] [sTDOUT]: [com.taji34.troncraft.Taji34EntityThrowable:onUpdate:279]: EntityPlayerMP['Player914'/0, l='Test', x=-190.50, y=4.00, z=-385.50]
[15:00:44] [server thread/INFO] [sTDOUT]: [com.taji34.troncraft.Taji34EntityThrowable:onUpdate:279]: EntityPlayerMP['Player914'/0, l='Test', x=-190.50, y=4.00, z=-385.50]
[15:00:44] [Client thread/INFO] [sTDOUT]: [com.taji34.troncraft.Taji34EntityThrowable:onUpdate:279]: null
[15:00:44] [server thread/INFO] [sTDOUT]: [com.taji34.troncraft.Taji34EntityThrowable:onUpdate:279]: EntityPlayerMP['Player914'/0, l='Test', x=-190.50, y=4.00, z=-385.50]
[15:00:44] [Client thread/INFO] [sTDOUT]: [com.taji34.troncraft.Taji34EntityThrowable:onUpdate:279]: null
[15:00:44] [Client thread/INFO] [sTDOUT]: [com.taji34.troncraft.Taji34EntityThrowable:onUpdate:279]: null
[15:00:44] [server thread/INFO] [sTDOUT]: [com.taji34.troncraft.Taji34EntityThrowable:onUpdate:279]: EntityPlayerMP['Player914'/0, l='Test', x=-190.50, y=4.00, z=-385.50]
[15:00:44] [server thread/INFO] [sTDOUT]: [com.taji34.troncraft.Taji34EntityThrowable:onUpdate:279]: EntityPlayerMP['Player914'/0, l='Test', x=-190.50, y=4.00, z=-385.50]
[15:00:44] [Client thread/INFO] [sTDOUT]: [com.taji34.troncraft.Taji34EntityThrowable:onUpdate:279]: null
[15:00:44] [Client thread/INFO] [sTDOUT]: [com.taji34.troncraft.Taji34EntityThrowable:onUpdate:279]: null
[15:00:44] [server thread/INFO] [sTDOUT]: [com.taji34.troncraft.Taji34EntityThrowable:onUpdate:279]: EntityPlayerMP['Player914'/0, l='Test', x=-190.50, y=4.00, z=-385.50]
[15:00:44] [Client thread/INFO] [sTDOUT]: [com.taji34.troncraft.Taji34EntityThrowable:onUpdate:279]: null
[15:00:44] [server thread/INFO] [sTDOUT]: [com.taji34.troncraft.Taji34EntityThrowable:onUpdate:279]: EntityPlayerMP['Player914'/0, l='Test', x=-190.50, y=4.00, z=-385.50]
[15:00:44] [server thread/INFO] [sTDOUT]: [com.taji34.troncraft.Taji34EntityThrowable:onUpdate:279]: EntityPlayerMP['Player914'/0, l='Test', x=-190.50, y=4.00, z=-385.50]
[15:00:44] [Client thread/INFO] [sTDOUT]: [com.taji34.troncraft.Taji34EntityThrowable:onUpdate:279]: null
[15:00:44] [server thread/INFO] [sTDOUT]: [com.taji34.troncraft.Taji34EntityThrowable:onUpdate:279]: EntityPlayerMP['Player914'/0, l='Test', x=-190.50, y=4.00, z=-385.50]
[15:00:44] [Client thread/INFO] [sTDOUT]: [com.taji34.troncraft.Taji34EntityThrowable:onUpdate:279]: null
[15:00:44] [server thread/INFO] [sTDOUT]: [com.taji34.troncraft.Taji34EntityThrowable:onUpdate:279]: EntityPlayerMP['Player914'/0, l='Test', x=-190.50, y=4.00, z=-385.50]
[15:00:44] [Client thread/INFO] [sTDOUT]: [com.taji34.troncraft.Taji34EntityThrowable:onUpdate:279]: null
The client and server seem out of sync and I don't know how to fix them. Any suggestions?