If it's not going to change, implementing IAdditionalEntitySpawnData is the best way to go - it adds additional data to the spawn packet that FML sends to the client when your entity is spawned, provided you registered your entity to FML's EntityRegistry using registerModEntity.
There are just two methods, one to write and one to read:
// assuming 'amount' is an integer
@Override
public void writeSpawnData(ByteBuf buffer) {
buffer.writeInt(boonDollar.amount);
}
@Override
public void readSpawnData(ByteBuf buffer) {
this.amount = buffer.readInt();
}
Okay, I have it in my entity file now, but nothing is different, and I have absolutely no idea where to use the read, and where to use the write
Then you probably ought to start by learning Java. It should be pretty obvious what you need to read and what you need to write, as I even used it in my example code...
// assuming 'amount' is an integer
@Override
public void writeSpawnData(ByteBuf buffer) {
buffer.writeInt(boonDollar.amount);
}
So this one is writes the amount that the boonDollar is worth (5,10,15,20); So if the boonDollar was worth 20, you would put 20 for the parameter. So get the value from the item as I'm assuming the value is stored inside the item.
@Override
public void readSpawnData(ByteBuf buffer) {
this.amount = buffer.readInt();
}
This one reads it. So it sets the boonDollars amount to whatever you wrote to the buffer in the previous method
You don't call the method, by implementing IAdditionalEntitySpawnData, I'm pretty sure that the world(?) calls the method when the Entity itself it spawned.
So; They should be called automatically by the world, and you use them by updating the amount int so that it is stored inside the entity, allowing you to have different values for amount.
Exactly as JiffyJay said - the whole point of Interfaces is that you get to implement each method, but the methods themselves are called automatically somewhere else. In this case, FML does some trickery in the background to call the read and write methods as necessary during the spawn packet life cycle, so that when the packet is being created, the write method is called, and when it is being processed on the client, the read method is called.
All you have to do is implement the interface and tell it what to do when the methods are called, just like in the example.
Your class declaration has to declare any interfaces you intend to implement - you should have gotten errors when you added the methods I showed you, but you removed the @Override instead of actually solving the error.
public class Boondollar extends Entity implements IAdditionalEntitySpawnData
I just followed what Eclipse recommended, but okay
Eclipse recommendations can be useful, but they quite frequently provide the incorrect solution - sure, you got rid of the 'error' message, but by doing so you ensured your code would not work.
If you have a method that was inherited, whether from an interface or a super class, it should have the @Override annotation. This ensures that you have the right method, which means that it will be called when you expect it to be called.
For example, your class Boondollar inherits from Entity, so any methods that you use that were declared in Entity, such as onUpdate, should have the @Override annotation. If you didn't have the @Override and wrote 'public void update()', well, that method doesn't exist in Entity and your code won't work; if you had the @Override annotation, it would give you a warning saying 'update does not exist in Entity' or some such, and you would know you have a problem.
In your case, the problem was that you didn't declare your class as implementing IAdditionalEntitySpawnData, so the methods were not 'overriding' anything - they didn't belong to any classes that Boondollar inherits from.
Okay, I've added everything and did some minor tinkering, and I've discovered that each orb does, in fact, retain its value now, yet the renderer is still not getting the values, so still no texture change.
I even changed the variable to a solid number rather than the getTextureFromXp() method to test it, and that worked.
That means the issue lies in the renderer getting the values.
I'll post the updated-ish code:
Render file:
package dialgex.SburbCraft.Render;
import cpw.mods.fml.relauncher.Side;
import cpw.mods.fml.relauncher.SideOnly;
import net.minecraft.client.renderer.OpenGlHelper;
import net.minecraft.client.renderer.Tessellator;
import net.minecraft.client.renderer.entity.Render;
import net.minecraft.entity.Entity;
import net.minecraft.util.MathHelper;
import net.minecraft.util.ResourceLocation;
import cpw.mods.fml.common.FMLLog;
import org.lwjgl.opengl.GL11;
import org.lwjgl.opengl.GL12;
import dialgex.SburbCraft.Entities.Boondollars.Boondollar;
@SideOnly(Side.CLIENT)
public class RenderBoondollar extends Render
{
private static final ResourceLocation BoondollarTextures = new ResourceLocation("sburbsurvival", "textures/entities/BoondollarTex.png");
private static final String __OBFID = "CL_00000993";
public RenderBoondollar()
{
this.shadowSize = 0.15F;
this.shadowOpaque = 0.75F;
}
/**
* Actually renders the given argument. This is a synthetic bridge method, always casting down its argument and then
* handing it off to a worker function which does the actual work. In all probabilty, the class Render is generic
* (Render * double d2, float f, float f1). But JAD is pre 1.5 so doesn't do that.
*/
public void doRender(Boondollar p_76986_1_, double p_76986_2_, double p_76986_4_, double p_76986_6_, float p_76986_8_, float p_76986_9_)
{
GL11.glPushMatrix();
GL11.glTranslatef((float)p_76986_2_, (float)p_76986_4_, (float)p_76986_6_);
this.bindEntityTexture(p_76986_1_);
int i = p_76986_1_.getTextureByXP();
float f2 = (float)(i * 64 + 0) / 256.0F;
float f3 = (float)(i * 64 + 64) / 256.0F;
float f4 = (float)(i * 256 + 0) / 256.0F;
float f5 = (float)(i * 256 + 256) / 256.0F;
float f6 = 1.0F;
float f7 = 0.5F;
float f8 = 0.25F;
int j = p_76986_1_.getBrightnessForRender(p_76986_9_);
int k = j % 65536;
int l = j / 65536;
OpenGlHelper.setLightmapTextureCoords(OpenGlHelper.lightmapTexUnit, (float)k / 1.0F, (float)l / 1.0F);
GL11.glColor4f(1.0F, 1.0F, 1.0F, 1.0F);
float f10 = 255.0F;
float f11 = ((float)p_76986_1_.xpColor + p_76986_9_) / 2.0F;
l = (int)((MathHelper.sin(f11 + 0.0F) + 1.0F) * 0.5F * f10);
int i1 = (int)f10;
int j1 = (int)((MathHelper.sin(f11 + 4.1887903F) + 1.0F) * 0.1F * f10);
int k1 = l << 16 | i1 << 8 | j1;
GL11.glRotatef(180.0F - this.renderManager.playerViewY, 0.0F, 1.0F, 0.0F);
GL11.glRotatef(-this.renderManager.playerViewX, 1.0F, 0.0F, 0.0F);
float f9 = 0.9F;
GL11.glScalef(f9, f9, f9);
Tessellator tessellator = Tessellator.instance;
tessellator.startDrawingQuads();
//tessellator.setColorRGBA_I(k1, 128);
tessellator.setNormal(0.0F, 1.0F, 0.0F);
tessellator.addVertexWithUV((double)(0.0F - f7), (double)(0.0F - f8), 0.0D, (double)f2, (double)f5);
tessellator.addVertexWithUV((double)(f6 - f7), (double)(0.0F - f8), 0.0D, (double)f3, (double)f5);
tessellator.addVertexWithUV((double)(f6 - f7), (double)(1.0F - f8), 0.0D, (double)f3, (double)f4);
tessellator.addVertexWithUV((double)(0.0F - f7), (double)(1.0F - f8), 0.0D, (double)f2, (double)f4);
tessellator.draw();
GL11.glDisable(GL11.GL_BLEND);
GL11.glDisable(GL12.GL_RESCALE_NORMAL);
GL11.glPopMatrix();
}
/**
* Returns the location of an entity's texture. Doesn't seem to be called unless you call Render.bindEntityTexture.
*/
protected ResourceLocation getEntityTexture(Boondollar p_110775_1_)
{
return BoondollarTextures;
}
/**
* Returns the location of an entity's texture. Doesn't seem to be called unless you call Render.bindEntityTexture.
*/
protected ResourceLocation getEntityTexture(Entity p_110775_1_)
{
return this.getEntityTexture((Boondollar)p_110775_1_);
}
/**
* Actually renders the given argument. This is a synthetic bridge method, always casting down its argument and then
* handing it off to a worker function which does the actual work. In all probability, the class Render is generic
* (Render * double d2, float f, float f1). But JAD is pre 1.5 so doesn't do that.
*/
public void doRender(Entity p_76986_1_, double p_76986_2_, double p_76986_4_, double p_76986_6_, float p_76986_8_, float p_76986_9_)
{
this.doRender((Boondollar)p_76986_1_, p_76986_2_, p_76986_4_, p_76986_6_, p_76986_8_, p_76986_9_);
}
}
Entity file:
package dialgex.SburbCraft.Entities.Boondollars;
import io.netty.buffer.ByteBuf;
import cpw.mods.fml.common.FMLLog;
import cpw.mods.fml.relauncher.Side;
import cpw.mods.fml.relauncher.SideOnly;
import net.minecraft.block.material.Material;
import net.minecraft.entity.Entity;
import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.nbt.NBTTagCompound;
import net.minecraft.util.DamageSource;
import net.minecraft.util.MathHelper;
import net.minecraft.world.World;
import net.minecraftforge.common.MinecraftForge;
import dialgex.SburbCraft.Events.PlayerPickupBoondollarEvent;
public class Boondollar extends Entity implements IAdditionalEntitySpawnData
{
/**
* A constantly increasing value that RenderXPOrb uses to control the colour shifting (Green / yellow)
*/
public int xpColor;
/** The age of the XP orb in ticks. */
public int xpOrbAge;
public int field_70532_c;
/** The health of this XP orb. */
private int xpOrbHealth = 5;
/** This is how much XP this orb has. */
public int xpValue;
/** The closest EntityPlayer to this orb. */
private EntityPlayer closestPlayer;
/** Threshold color for tracking players */
private int xpTargetColor;
private static final String __OBFID = "CL_00001544";
public Boondollar(World p_i1585_1_, double p_i1585_2_, double p_i1585_4_, double p_i1585_6_, int p_i1585_8_)
{
super(p_i1585_1_);
this.setSize(0.5F, 0.5F);
this.yOffset = this.height / 2.0F;
this.setPosition(p_i1585_2_, p_i1585_4_, p_i1585_6_);
this.rotationYaw = (float)(Math.random() * 360.0D);
this.motionX = (double)((float)(Math.random() * 0.20000000298023224D - 0.10000000149011612D) * 2.0F);
this.motionY = (double)((float)(Math.random() * 0.2D) * 2.0F);
this.motionZ = (double)((float)(Math.random() * 0.20000000298023224D - 0.10000000149011612D) * 2.0F);
xpValue = p_i1585_8_;
}
/**
* returns if this entity triggers Block.onEntityWalking on the blocks they walk on. used for spiders and wolves to
* prevent them from trampling crops
*/
protected boolean canTriggerWalking()
{
return false;
}
public Boondollar(World p_i1586_1_)
{
super(p_i1586_1_);
this.setSize(0.25F, 0.25F);
this.yOffset = this.height / 2.0F;
}
protected void entityInit() {}
@SideOnly(Side.CLIENT)
public int getBrightnessForRender(float p_70070_1_)
{
float f1 = 0.5F;
if (f1 < 0.0F)
{
f1 = 0.0F;
}
if (f1 > 1.0F)
{
f1 = 1.0F;
}
int i = super.getBrightnessForRender(p_70070_1_);
int j = i & 255;
int k = i >> 16 & 255;
j += (int)(f1 * 15.0F * 16.0F);
if (j > 240)
{
j = 240;
}
return j | k << 16;
}
/**
* Called to update the entity's position/logic.
*/
public void onUpdate()
{
super.onUpdate();
if (this.field_70532_c > 0)
{
--this.field_70532_c;
}
this.prevPosX = this.posX;
this.prevPosY = this.posY;
this.prevPosZ = this.posZ;
this.motionY -= 0.029999999329447746D;
if (this.worldObj.getBlock(MathHelper.floor_double(this.posX), MathHelper.floor_double(this.posY), MathHelper.floor_double(this.posZ)).getMaterial() == Material.lava)
{
this.motionY = 0.20000000298023224D;
this.motionX = (double)((this.rand.nextFloat() - this.rand.nextFloat()) * 0.2F);
this.motionZ = (double)((this.rand.nextFloat() - this.rand.nextFloat()) * 0.2F);
this.playSound("random.fizz", 0.4F, 2.0F + this.rand.nextFloat() * 0.4F);
}
this.func_145771_j(this.posX, (this.boundingBox.minY + this.boundingBox.maxY) / 2.0D, this.posZ);
double d0 = 8.0D;
if (this.xpTargetColor < this.xpColor - 20 + this.getEntityId() % 100)
{
if (this.closestPlayer == null || this.closestPlayer.getDistanceSqToEntity(this) > d0 * d0)
{
this.closestPlayer = this.worldObj.getClosestPlayerToEntity(this, d0);
}
this.xpTargetColor = this.xpColor;
}
if (this.closestPlayer != null)
{
double d1 = (this.closestPlayer.posX - this.posX) / d0;
double d2 = (this.closestPlayer.posY + (double)this.closestPlayer.getEyeHeight() - this.posY) / d0;
double d3 = (this.closestPlayer.posZ - this.posZ) / d0;
double d4 = Math.sqrt(d1 * d1 + d2 * d2 + d3 * d3);
double d5 = 1.0D - d4;
if (d5 > 0.0D)
{
d5 *= d5;
this.motionX += d1 / d4 * d5 * 0.1D;
this.motionY += d2 / d4 * d5 * 0.1D;
this.motionZ += d3 / d4 * d5 * 0.1D;
}
}
this.moveEntity(this.motionX, this.motionY, this.motionZ);
float f = 0.98F;
if (this.onGround)
{
f = this.worldObj.getBlock(MathHelper.floor_double(this.posX), MathHelper.floor_double(this.boundingBox.minY) - 1, MathHelper.floor_double(this.posZ)).slipperiness * 0.98F;
}
this.motionX *= (double)f;
this.motionY *= 0.9800000190734863D;
this.motionZ *= (double)f;
if (this.onGround)
{
this.motionY *= -0.8999999761581421D;
}
//++this.xpColor;
++this.xpOrbAge;
if (this.xpOrbAge >= 6000)
{
this.setDead();
}
}
/**
* Returns if this entity is in water and will end up adding the waters velocity to the entity
*/
public boolean handleWaterMovement()
{
return this.worldObj.handleMaterialAcceleration(this.boundingBox, Material.water, this);
}
/**
* Will deal the specified amount of damage to the entity if the entity isn't immune to fire damage. Args:
* amountDamage
*/
protected void dealFireDamage(int p_70081_1_)
{
this.attackEntityFrom(DamageSource.inFire, (float)p_70081_1_);
}
/**
* Called when the entity is attacked.
*/
public boolean attackEntityFrom(DamageSource p_70097_1_, float p_70097_2_)
{
if (this.isEntityInvulnerable())
{
return false;
}
else
{
this.setBeenAttacked();
this.xpOrbHealth = (int)((float)this.xpOrbHealth - p_70097_2_);
if (this.xpOrbHealth <= 0)
{
this.setDead();
}
return false;
}
}
/**
* (abstract) Protected helper method to write subclass entity data to NBT.
*/
public void writeEntityToNBT(NBTTagCompound p_70014_1_)
{
p_70014_1_.setShort("Health", (short)((byte)this.xpOrbHealth));
p_70014_1_.setShort("Age", (short)this.xpOrbAge);
p_70014_1_.setShort("Value", (short)this.xpValue);
}
/**
* (abstract) Protected helper method to read subclass entity data from NBT.
*/
public void readEntityFromNBT(NBTTagCompound p_70037_1_)
{
this.xpOrbHealth = p_70037_1_.getShort("Health") & 255;
this.xpOrbAge = p_70037_1_.getShort("Age");
this.xpValue = p_70037_1_.getShort("Value");
}
@Override
public void writeSpawnData(ByteBuf buffer) {
buffer.writeInt(this.xpValue);
}
@Override
public void readSpawnData(ByteBuf buffer) {
this.xpValue = buffer.readInt();
}
/**
* Called by a player entity when they collide with an entity
*/
public void onCollideWithPlayer(EntityPlayer p_70100_1_)
{
if (!this.worldObj.isRemote)
{
if (this.field_70532_c == 0 && p_70100_1_.xpCooldown == 0)
{
if (MinecraftForge.EVENT_BUS.post(new PlayerPickupBoondollarEvent(p_70100_1_, this))) return;
p_70100_1_.xpCooldown = 2;
this.worldObj.playSoundAtEntity(p_70100_1_, "random.orb", 0.1F, 0.5F * ((this.rand.nextFloat() - this.rand.nextFloat()) * 0.7F + 1.8F));
p_70100_1_.onItemPickup(this, 1);
p_70100_1_.addExperience(this.xpValue);
FMLLog.info("PlayerPickup "+this.xpValue);
this.setDead();
}
}
}
/**
* Returns the XP value of this XP orb.
*/
public int getXpValue()
{
return this.xpValue;
}
/**
* Returns a number from 1 to 10 based on how much XP this orb is worth. This is used by RenderXPOrb to determine
* what texture to use.
*/
@SideOnly(Side.CLIENT)
public int getTextureByXP(){
int returnxp = -1;
if(this.xpValue == 50){
returnxp = 3;
FMLLog.info("Entity "+returnxp);
}else if(this.xpValue == 25){
returnxp = 2;
FMLLog.info("Entity "+returnxp);
}else if(this.xpValue == 10){
returnxp = 1;
FMLLog.info("Entity "+returnxp);
}else if(this.xpValue == 5){
returnxp = 0;
FMLLog.info("Entity "+returnxp);
}
return returnxp;
}
/**
* Get a fragment of the maximum experience points value for the supplied value of experience points value.
*/
public static int getXPSplit(int p_70527_0_)
{
return p_70527_0_ == 4 ? 4 : (p_70527_0_ == 3 ? 3 : (p_70527_0_ == 2 ? 2 : (p_70527_0_ == 1 ? 1 : 0)));
}
/**
* If returns false, the item will not inflict any damage against entities.
*/
public boolean canAttackWithItem()
{
return false;
}
}
DropEvent file:
package dialgex.SburbCraft.Events;
import java.util.Random;
import cpw.mods.fml.client.FMLClientHandler;
import cpw.mods.fml.common.FMLLog;
import cpw.mods.fml.common.eventhandler.SubscribeEvent;
import net.minecraft.client.renderer.entity.Render;
import net.minecraft.entity.EntityLiving;
import net.minecraft.entity.IEntityLivingData;
import net.minecraft.entity.item.EntityItem;
import net.minecraft.item.Item;
import net.minecraft.server.MinecraftServer;
import net.minecraft.util.MathHelper;
import net.minecraft.world.World;
import net.minecraftforge.event.entity.living.LivingDropsEvent;
import dialgex.SburbCraft.Entities.Boondollars.*;
import dialgex.SburbCraft.Items.Grist.*;
import dialgex.SburbCraft.Render.RenderBoondollar;
import net.minecraft.entity.passive.*;
public class GristDropEvent{
private static int rNum(int min, int max) {
Random r = new Random();
int randomNum = r.nextInt((max - min) + 1) + min;
return randomNum;
}
public static double rand;
@SubscribeEvent
public void onEntityDrop(LivingDropsEvent event){
World world = event.entity.worldObj;
int r4 = rNum(1,4);
int r3 = rNum(1,3);
int r2 = rNum(1,2);
if (event.source.getDamageType().equals("player")){
double tx = event.entityLiving.posX;
double ty = event.entityLiving.posY;
double tz = event.entityLiving.posZ;
rand = Math.random();
if (event.entityLiving instanceof EntityPig){
Boondollar b0 = new Boondollar(world, tx, ty, tz, 5);
Boondollar b1 = new Boondollar(world, tx, ty, tz, 10);
Boondollar b2 = new Boondollar(world, tx, ty, tz, 25);
Boondollar b3 = new Boondollar(world, tx, ty, tz, 50);
world.spawnEntityInWorld(b0);
world.spawnEntityInWorld(b1);
world.spawnEntityInWorld(b2);
world.spawnEntityInWorld(b3);
/*if(rand < 0.66d){
if(rand < 0.33d){
Boondollar b4 = new Boondollar(world, tx, ty, tz, 10);
world.spawnEntityInWorld(b4);
}
}*/
}
if (event.entityLiving instanceof EntityCow){
event.entityLiving.dropItem(Grist.BuildGrist, 1);
/*event.entityLiving.dropItem(Boondollar.Boondollar50, 1);
if(rand < 0.66d){
event.entityLiving.dropItem(Boondollar.Boondollar5, r4);
if(rand < 0.33d){
event.entityLiving.dropItem(Boondollar.Boondollar10, r2);
}
}*/
}
if (event.entityLiving instanceof EntityChicken){
event.entityLiving.dropItem(Grist.BuildGrist, 3);
/*event.entityLiving.dropItem(Boondollar.Boondollar25, 1);
if(rand < 0.66d){
event.entityLiving.dropItem(Boondollar.Boondollar5, r4);
if(rand < 0.33d){
event.entityLiving.dropItem(Boondollar.Boondollar10, r2);
}
}*/
}
if (event.entityLiving instanceof EntitySheep){
event.entityLiving.dropItem(Grist.BuildGrist, 1);
/*event.entityLiving.dropItem(Boondollar.Boondollar50, 1);
if(rand < 0.66d){
event.entityLiving.dropItem(Boondollar.Boondollar5, r4);
if(rand < 0.33d){
event.entityLiving.dropItem(Boondollar.Boondollar10, r2);
}
}*/
}
if (event.entityLiving instanceof EntityHorse){
event.entityLiving.dropItem(Grist.BuildGrist, 1);
/*event.entityLiving.dropItem(Boondollar.Boondollar10, 2);
event.entityLiving.dropItem(Boondollar.Boondollar50, 2);
if(rand < 0.66d){
event.entityLiving.dropItem(Boondollar.Boondollar5, r4);
if(rand < 0.33d){
event.entityLiving.dropItem(Boondollar.Boondollar10, r2);
}
}*/
}
///////////////////////////////////////////
}
}
}
ClientProxy file:
package dialgex.SburbCraft.Proxies;
import net.minecraft.client.renderer.tileentity.TileEntitySpecialRenderer;
import cpw.mods.fml.client.registry.ClientRegistry;
import cpw.mods.fml.client.registry.RenderingRegistry;
import dialgex.SburbCraft.Entities.Boondollars.Boondollar;
import dialgex.SburbCraft.Proxies.CommonProxy;
import dialgex.SburbCraft.Render.*;
public class ClientProxy extends CommonProxy{
@Override
public void registerRenderers(){
RenderingRegistry.registerEntityRenderingHandler(Boondollar.class, new RenderBoondollar());
}
}
Erm... you were not supposed to create your own Interface for IAdditionalEntitySpawnData - how do you expect Minecraft to call that when it doesn't know about it? The one you want to use is:
There are just two methods, one to write and one to read:
Yes, in the entity for which you need additional information sent to the client when it spawns.
Then you probably ought to start by learning Java. It should be pretty obvious what you need to read and what you need to write, as I even used it in my example code...
Writing is practically self-explanatory, but where do I use the read.
How do I call them.
How do I apply their use
So; They should be called automatically by the world, and you use them by updating the amount int so that it is stored inside the entity, allowing you to have different values for amount.
All you have to do is implement the interface and tell it what to do when the methods are called, just like in the example.
The renderer still isn't getting the values, and the texture still isn't changing
Also, as I said earlier, it ONLY WORKS provided you registered your entity to FML's EntityRegistry using registerModEntity.
First line in PreInit in the Mod file.
Entity file:
Render file:
DropEvent file:
ClientProxy:
Mod file:
Please read some Java tutorials.
Eclipse recommendations can be useful, but they quite frequently provide the incorrect solution - sure, you got rid of the 'error' message, but by doing so you ensured your code would not work.
If you have a method that was inherited, whether from an interface or a super class, it should have the @Override annotation. This ensures that you have the right method, which means that it will be called when you expect it to be called.
For example, your class Boondollar inherits from Entity, so any methods that you use that were declared in Entity, such as onUpdate, should have the @Override annotation. If you didn't have the @Override and wrote 'public void update()', well, that method doesn't exist in Entity and your code won't work; if you had the @Override annotation, it would give you a warning saying 'update does not exist in Entity' or some such, and you would know you have a problem.
In your case, the problem was that you didn't declare your class as implementing IAdditionalEntitySpawnData, so the methods were not 'overriding' anything - they didn't belong to any classes that Boondollar inherits from.
I even changed the variable to a solid number rather than the getTextureFromXp() method to test it, and that worked.
That means the issue lies in the renderer getting the values.
I'll post the updated-ish code:
Sorry for being such a total noob, but that's why I came here. For help.
EDIT: Because it was miswritten.
I was given
Not
And that's why my search found nothing
EDIT2: Everything's working flawlessly now. Thanks a bunch!
Oops :$ Sorry about that. Glad it works now.