user17512883
user17512883

Reputation: 43

Minecraft Forge 1.7.10 Custom Entity Not Spawning On The Client?

In my Minecraft Forge mod for 1.7.10. I am aware that this version is arguably old news, but it is my favorite version and all of my modding experience is with this version.

I am creating a custom TNT block. The issue is the primed version is not rendering. When ignited, the TNT disappears, and then shortly later there is an explosion. If the TNT was placed in the air, the explosion is below like it should be due to the primed TNT falling. The issue is that it is not rendering. When I use fn+f3+b to show hitboxes, no hitbox is shown.

The issue is the entity being spawned on the server does not replicate to the client. I know this because:

The BlockTNT class (much of this is derived from vanilla code, but has been designed to take the corresponding custom primed TNT entity class while instantiating, the problem is not here because instantiating a default EntityTNTPrimed instead of the provided primed class does render correctly, but I have included this because this code helped demonstrate the origin of the problem):

class BlockTNT extends net.minecraft.block.BlockTNT {
    private final Class primed;
    public BlockTNT(final Class primed) {
        this.primed = primed;
        stepSound = net.minecraft.block.Block.soundTypeGrass;
    }
    protected EntityTNTPrimed getPrimed(final World world, final int x, final int y, final int z, final EntityLivingBase igniter) {
        try {
            return (EntityTNTPrimed)primed.getDeclaredConstructor(World.class, double.class, double.class, double.class, EntityLivingBase.class).newInstance(world, (double)((float)x+0.5F), (double)((float)y+0.5F), (double)((float)z+0.5F), igniter);
        } catch (Exception exception) {
            return null;
        }
    }
    @Override public void onBlockDestroyedByExplosion(final World world, final int x, final int y, final int z, final Explosion explosion) {
        if(world.isRemote)
            return;
        final EntityTNTPrimed tnt = getPrimed(world, x, y, z, explosion.getExplosivePlacedBy());
        tnt.fuse = world.rand.nextInt(tnt.fuse>>2)+(tnt.fuse>>3);
        world.spawnEntityInWorld(tnt);
    }
    @Override public void func_150114_a(final World world, final int x, final int y, final int z, final int meta, final EntityLivingBase igniter) {
        if(world.isRemote || (meta&1) == 0)
            return;
        final EntityTNTPrimed tnt = getPrimed(world, x, y, z, igniter);
        world.spawnEntityInWorld(tnt);
        world.playSoundAtEntity(tnt, "game.tnt.primed", 1.0F, 1.0F);
    }
}

Inside the main mod class I register the entities:

@Instance public static ExampleMod instance;
@SidedProxy(clientSide="com.examplemod.ClientProxy", serverSide="com.examplemod.CommonProxy") public static com.examplemod.CommonProxy proxy;
private static void registerTnt(final Class primed, String name, final int id) {
    proxy.registerTntRenderer(primed, registerBlock(new BlockTNT(primed), name.toLowerCase()+"_tnt"));
    EntityRegistry.registerGlobalEntityID(primed, name = "PrimedTnt"+name, EntityRegistry.findGlobalUniqueEntityId());
    EntityRegistry.registerModEntity(primed, name, id, instance, 160, 10, true);
}
@EventHandler public void init(FMLInitializationEvent event) {
    registerTnt(com.examplemod.TNTPrimedCharged.class, "Charged", 0);
    // ...
}

And we register the renderer in the client proxy (again tested and works):

@Override public void registerTntRenderer(final Class primed, final Block block) {
    RenderingRegistry.registerEntityRenderingHandler(primed, new com.examplemod.RenderTNTPrimed(block));
}

Here is the class for the custom entities:

public abstract class TNTPrimed extends net.minecraft.entity.item.EntityTNTPrimed {
    public TNTPrimed(final World world, final double x, final double y, final double z, final EntityLivingBase igniter) {
        super(world, x, y, z, igniter);
    }
    public TNTPrimed(final World world) {
        super(world);
    }
    @Override public void onUpdate() {
        prevPosX = posX;
        prevPosY = posY;
        prevPosZ = posZ;
        moveEntity(motionX, motionY -= 0.04D, motionZ);
        motionX *= 0.98D;
        motionY *= 0.98D;
        motionZ *= 0.98D;
        if(onGround) {
            motionX *= 0.7D;
            motionZ *= 0.7D;
            motionY *= -0.5D;
        }
        if(fuse-- <= 0) {
            setDead();
            if(!worldObj.isRemote)
                explode();
        } else
            worldObj.spawnParticle("smoke", posX, posY+0.5D, posZ, 0.0D, 0.0D, 0.0D);
    }
    protected abstract void explode();
}
public class TNTPrimedCharged extends com.examplemod.TNTPrimed {
    public TNTPrimedCharged(final World world, final double x, final double y, final double z, final EntityLivingBase igniter) {
        super(world, x, y, z, igniter);
    }
    public TNTPrimedCharged(final World world) {
        super(world);
    }
    @Override protected void explode() {
        worldObj.newExplosion(this, posX, posY, posZ, 8, false, true);
    }
}

Though I can state categorically that the custom entity renderer class is not the culprit, I am providing it for reference:

class RenderTNTPrimed extends net.minecraft.client.renderer.entity.RenderTNTPrimed {
    private final RenderBlocks renderer = new RenderBlocks();
    private final Block block;
    public RenderTNTPrimed(final Block block) {
        super();
        this.block = block;
    }
    @Override public void doRender(final EntityTNTPrimed entity, final double x, final double y, final double z, final float yaw, final float tick) {
        GL11.glPushMatrix();
        GL11.glTranslatef((float)x, (float)y, (float)z);
        float var0 = entity.fuse-tick+1.0F;
        if (var0 < 10.0F) {
            final float scale = (float)Math.pow(Math.max(Math.min(1-var0/10, 1.0F), 0.0F), 4.0F)*0.3F+1.0F;
            GL11.glScalef(scale, scale, scale);
        }
        bindEntityTexture(entity);
        renderer.renderBlockAsItem(block, 0, entity.getBrightness(tick));
        if (((entity.fuse/5)&1) == 0) {
            GL11.glDisable(GL11.GL_TEXTURE_2D);
            GL11.glDisable(GL11.GL_LIGHTING);
            GL11.glEnable(GL11.GL_BLEND);
            GL11.glBlendFunc(GL11.GL_SRC_ALPHA, GL11.GL_DST_ALPHA);
            GL11.glColor4f(1.0F, 1.0F, 1.0F, (1.0F-var0/100.0F)*0.8F);
            renderer.renderBlockAsItem(block, 0, 1.0F);
            GL11.glColor4f(1.0F, 1.0F, 1.0F, 1.0F);
            GL11.glDisable(GL11.GL_BLEND);
            GL11.glEnable(GL11.GL_LIGHTING);
            GL11.glEnable(GL11.GL_TEXTURE_2D);
        }
        GL11.glPopMatrix();
    }
}

So when the server calls world.spawnEntityInWorld() on my custom entity why does it fail to spawn it on the client as well? How can I fix this because the TNT just disappearing when being ignited is undesirable and I have no idea what else to try. How can I make the custom primed TNT show up on the client?

Upon reverse engineering other similar mods (specifically the Too Much TNT mod), their TNT primed entities successfully render when /summon is used, and they do check for clientside in their TNT block classes. After investigation, I cannot tell what they are doing differently that would cause their entities to render but not mine.

I have spent several unfruitful hours researching this problem, and given that as of this edit this question is second hit for Googling basic search terms like 'Minecraft Forge 1.7.10 Entity Not Rendering' and third for 'Minecraft Forge 1.7.10 Entity Invisible,' it is very likely that further research will be in vain.

The problem is simply that when my mod added entity is spawned on the server thread, it is not also spawned on the client thread, even though it should be. How do I fix this? I tried everything.

Upvotes: 2

Views: 997

Answers (1)

user17512883
user17512883

Reputation: 43

It turns out the problem was actually not the entity not spawning on the client. The issue was that the fuse property was being set to 0 on the client even though it is set to 80 on the server. And thus the client side TNT is instantly setDead()ing itself. Implementing cpw.mods.fml.common.registry.IEntityAdditionalSpawnData and then setting the fuse property to the correct value in readSpawnData seems to have solved the problem.

Upvotes: 2

Related Questions