Jump to content

[1.7.2] Entity spawns on server but not on client


Shalashalska

Recommended Posts

For some odd reason, the entity I created is spawning on the server, but not on the client. I have verified this with breakpoints (The loadedEntityList in the server world has the entity, the one on the client does not have the entity)

 

Code of relevant classes:

 

ClientProxy.java

package com.simpleframes.core.proxy;

import com.simpleframes.client.renderer.entity.RenderMovingBlock;
import com.simpleframes.entity.EntityMovingBlock;

import cpw.mods.fml.client.registry.RenderingRegistry;

public class ClientProxy extends CommonProxy
{
    @Override
    public void registerRenders()
    {
        RenderingRegistry.registerEntityRenderingHandler(EntityMovingBlock.class, new RenderMovingBlock());
    }

    public int addArmor(String armor)
    {
        return RenderingRegistry.addNewArmourRendererPrefix(armor);
    }
}

EntityManager.java

package com.simpleframes.entity;

import net.minecraft.client.renderer.entity.RenderSnowball;
import net.minecraft.item.Item;
import cpw.mods.fml.client.registry.RenderingRegistry;
import cpw.mods.fml.common.registry.EntityRegistry;

public class EntityManager
{
    public static void preInit()
    {
        registerEntities();
    }

    private static void registerEntities()
    {
        createEntity(EntityMovingBlock.class, "MovingBlock");
    }

    public static void createEntity(Class entityClass, String entityName)
    {
        int entityId = EntityRegistry.findGlobalUniqueEntityId();
        EntityRegistry.registerGlobalEntityID(entityClass, entityName, entityId);
    }
}

SimpleFramesMotion.java (This is where the entity is created)

package com.simpleframes.util;

import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

import net.minecraft.block.Block;
import net.minecraft.entity.Entity;
import net.minecraft.nbt.NBTTagCompound;
import net.minecraft.tileentity.TileEntity;
import net.minecraft.world.World;

import com.simpleframes.block.BlockManager;
import com.simpleframes.entity.EntityMovingBlock;
import com.simpleframes.lib.BlockMeta;
import com.simpleframes.lib.Reference;

public class SimpleFrameMotion
{
    private Set<BlockData> movingBlocks = new HashSet<BlockData>();
    private List<Entity>   movingFrame  = new ArrayList<Entity>();
    private World          world;
    private int            addX;
    private int            addY;
    private int            addZ;
    private double         velX;
    private double         velY;
    private double         velZ;

    private static class BlockData
    {
        int            x;
        int            y;
        int            z;
        int            meta;
        Block          block;
        TileEntity     tileEntity;
        NBTTagCompound tileEntityNBT;
        boolean        hadTileEntity;

        BlockData(int x, int y, int z)
        {
            this.x = x;
            this.y = y;
            this.z = z;
        }

        void addData(Block block, int meta, TileEntity tileEntity)
        {
            this.block = block;
            this.meta = meta;
            this.tileEntity = tileEntity;
        }

        public BlockData[] neighbors()
        {
            BlockData[] neighbors = new BlockData[] { new BlockData(x + 1, y, z), new BlockData(x - 1, y, z), new BlockData(x, y + 1, z),
                    new BlockData(x, y - 1, z), new BlockData(x, y, z + 1), new BlockData(x, y, z - 1) };

            return neighbors;
        }

        @Override
        public int hashCode()
        {
            final int prime = 31;
            int result = 1;
            result = prime * result + x;
            result = prime * result + y;
            result = prime * result + z;
            return result;
        }

        @Override
        public boolean equals(Object obj)
        {
            if (this == obj)
                return true;
            if (obj == null)
                return false;
            if (getClass() != obj.getClass())
                return false;
            BlockData other = (BlockData) obj;
            if (x != other.x)
                return false;
            if (y != other.y)
                return false;
            if (z != other.z)
                return false;
            return true;
        }
    }

    private SimpleFrameMotion(World world, int addX, int addY, int addZ, double velX, double velY, double velZ)
    {
        this.world = world;
        this.addX = addX;
        this.addY = addY;
        this.addZ = addZ;
        this.velX = velX;
        this.velY = velY;
        this.velZ = velZ;
    }

    public static void simpleFrameMoveMain(World world, int x, int y, int z, int addX, int addY, int addZ, double velX, double velY, double velZ)
    {
        SimpleFrameMotion simpleFrameMotion = new SimpleFrameMotion(world, addX, addY, addZ, velX, velY, velZ);
        BlockData startingPoint = new BlockData(x, y, z);
        simpleFrameMotion.findBlocksToMoveFirst(startingPoint);
        if (simpleFrameMotion.getCanMoveMain())
        {
            simpleFrameMotion.simpleFrameMove(startingPoint);
            simpleFrameMotion.movingBlocks.clear();
        }
    }

    private void simpleFrameMove(BlockData p)
    {
        // Gets data about the blocks and turns them to air
        for (BlockData movingBlock : movingBlocks)
        {
            movingBlock.addData(world.getBlock(movingBlock.x, movingBlock.y, movingBlock.z),
                    world.getBlockMetadata(movingBlock.x, movingBlock.y, movingBlock.z), world.getTileEntity(movingBlock.x, movingBlock.y, movingBlock.z));
            world.removeTileEntity(movingBlock.x, movingBlock.y, movingBlock.z);
            if (movingBlock.tileEntity != null)
            {
                movingBlock.tileEntityNBT = new NBTTagCompound();
                movingBlock.tileEntity.writeToNBT(movingBlock.tileEntityNBT);
                movingBlock.tileEntity = null;
                movingBlock.hadTileEntity = true;
            }
            world.setBlockToAir(movingBlock.x, movingBlock.y, movingBlock.z);
        }
        // Puts the blocks back in the new positions
        for (BlockData movingBlock : movingBlocks)
        {
            movingFrame.add(new EntityMovingBlock(world, movingBlock.x, movingBlock.y, movingBlock.z, movingBlock.block, velX, velY, velZ, movingBlock.meta, 1));
            world.spawnEntityInWorld(movingFrame.get(movingFrame.size() - 1));
            if (movingBlock.hadTileEntity == true)
            {
                movingBlock.tileEntityNBT.setInteger("x", movingBlock.x + addX);
                movingBlock.tileEntityNBT.setInteger("y", movingBlock.y + addY);
                movingBlock.tileEntityNBT.setInteger("z", movingBlock.z + addZ);
                world.getTileEntity(movingBlock.x + addX, movingBlock.y + addY, movingBlock.z + addZ).readFromNBT(movingBlock.tileEntityNBT);
            }

        }
    }

    private boolean getCanMoveMain()
    {
        for (BlockData movingBlock : movingBlocks)
        {
            if (movingBlock.y >= Reference.CEILING && addY >= 1)
            {
                return false;
            }
            if (!world.isAirBlock(movingBlock.x + addX, movingBlock.y + addY, movingBlock.z + addZ)
                    && !movingBlocks.contains(new BlockData(movingBlock.x + addX, movingBlock.y + addY, movingBlock.z + addZ)))
            {
                return false;
            }
        }
        return true;
    }

    private void findBlocksToMove(BlockData p)
    {
        if (world.isAirBlock(p.x, p.y, p.z))
        {
            return;
        }
        movingBlocks.add(p);
        if (world.getBlock(p.x, p.y, p.z).equals(BlockManager.simpleFrameBlock) && world.getBlockMetadata(p.x, p.y, p.z) == BlockMeta.FRAME_META_ID)
        {
            for (BlockData neighbor : p.neighbors())
            {
                if (!movingBlocks.contains(neighbor))
                {
                    findBlocksToMove(neighbor);

                }
            }
        }
    }

    private void findBlocksToMoveFirst(BlockData blockData)
    {
        if (world.isAirBlock(blockData.x, blockData.y, blockData.z))
        {
            return;
        }
        movingBlocks.add(blockData);
        if (world.getBlock(blockData.x, blockData.y, blockData.z).equals(BlockManager.simpleFrameBlock))
        {
            for (BlockData neighbor : blockData.neighbors())
            {
                if (!movingBlocks.contains(neighbor))
                {
                    findBlocksToMove(neighbor);
                }
            }
        }
    }
}

EntityMovingBlock.java (Mostly copy-pasted from EntityFallingBlock)

package com.simpleframes.entity;

import net.minecraft.block.Block;
import net.minecraft.block.material.Material;
import net.minecraft.crash.CrashReportCategory;
import net.minecraft.entity.Entity;
import net.minecraft.init.Blocks;
import net.minecraft.nbt.NBTTagCompound;
import net.minecraft.world.World;
import cpw.mods.fml.relauncher.Side;
import cpw.mods.fml.relauncher.SideOnly;

//Extends Entity
public class EntityMovingBlock extends Entity
{
    // Declaring a block here because EntityFallingBlock has it set to private,
    // and has no getters for it.
    private Block         block;
    private int           metadata;
    private int           distance;
    private int           ticksExistedNew;
    public int            time;
    public boolean        dropItem;
    private boolean       hurtEntities;
    private int           fallHurtMax;
    private float         fallHurtAmount;
    public NBTTagCompound nbtTag;

    // Constructor. Nothing special going on here.
    public EntityMovingBlock(World world, double x, double y, double z, Block block, double motionX, double motionY, double motionZ, int metadata, int distance)
    {
        super(world);
        this.motionX = motionX;
        this.motionY = motionY;
        this.motionZ = motionZ;
        this.block = block;
        this.metadata = metadata;
        this.distance = distance;
        ticksExistedNew = 0;
        this.dropItem = true;
        this.fallHurtMax = 40;
        this.fallHurtAmount = 2.0F;
        this.preventEntitySpawning = true;
        this.setSize(0.98F, 0.98F);
        this.yOffset = this.height / 2.0F;
        this.setPosition(x, y, z);
        this.prevPosX = x;
        this.prevPosY = y;
        this.prevPosZ = z;
        this.setSize(1, 1);
    }

    // Overrides the default onUpdate for falling blocks so that it doesn't
    // fall.
    @Override
    public void onUpdate()
    {
        System.out.println("IsDead:" + isDead + " PosX:" + posX + " PosY:" + posY + " PosZ:" + posZ);
        prevPosX = posX;
        prevPosY = posY;
        prevPosZ = posZ;
        moveEntity(motionX, motionY, motionZ);
        ticksExistedNew++;
        System.out.println("TicksExisted:" + ticksExistedNew);
        if (ticksExistedNew / distance * Math.abs(motionX + motionY + motionZ) >= 1)
        {
            System.out.println("Arrived");
            arrive();
            setDead();
        }
    }

    @Override
    public void setDead()
    {
        isDead = true;
    }

    public void arrive()
    {
        worldObj.setBlock((int) Math.round(posX), (int) Math.round(posY), (int) Math.round(posZ), block, getMetadata(), 3);
    }

    @Override
    protected void entityInit()
    {
        // TODO Auto-generated method stub

    }

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

    /**
     * Returns true if other Entities should be prevented from moving through
     * this Entity.
     */
    public boolean canBeCollidedWith()
    {
        return true;
    }

    /**
     * (abstract) Protected helper method to write subclass entity data to NBT.
     */
    @Override
    public void writeEntityToNBT(NBTTagCompound par1NBTTagCompound)
    {
        par1NBTTagCompound.setByte("Tile", (byte) Block.getIdFromBlock(this.block));
        par1NBTTagCompound.setInteger("TileID", Block.getIdFromBlock(this.block));
        par1NBTTagCompound.setByte("Data", (byte) this.getMetadata());
        par1NBTTagCompound.setByte("Time", (byte) this.time);
        par1NBTTagCompound.setBoolean("DropItem", this.dropItem);
        par1NBTTagCompound.setBoolean("HurtEntities", this.hurtEntities);
        par1NBTTagCompound.setFloat("FallHurtAmount", this.fallHurtAmount);
        par1NBTTagCompound.setInteger("FallHurtMax", this.fallHurtMax);

        if (this.nbtTag != null)
        {
            par1NBTTagCompound.setTag("TileEntityData", this.nbtTag);
        }
    }

    /**
     * (abstract) Protected helper method to read subclass entity data from NBT.
     */
    @Override
    public void readEntityFromNBT(NBTTagCompound par1NBTTagCompound)
    {
        if (par1NBTTagCompound.hasKey("TileID", 99))
        {
            this.block = Block.getBlockById(par1NBTTagCompound.getInteger("TileID"));
        }
        else
        {
            this.block = Block.getBlockById(par1NBTTagCompound.getByte("Tile") & 255);
        }

        this.metadata = par1NBTTagCompound.getByte("Data") & 255;
        this.time = par1NBTTagCompound.getByte("Time") & 255;

        if (par1NBTTagCompound.hasKey("HurtEntities", 99))
        {
            this.hurtEntities = par1NBTTagCompound.getBoolean("HurtEntities");
            this.fallHurtAmount = par1NBTTagCompound.getFloat("FallHurtAmount");
            this.fallHurtMax = par1NBTTagCompound.getInteger("FallHurtMax");
        }
        else
            if (this.block == Blocks.anvil)
            {
                this.hurtEntities = true;
            }

        if (par1NBTTagCompound.hasKey("DropItem", 99))
        {
            this.dropItem = par1NBTTagCompound.getBoolean("DropItem");
        }

        if (par1NBTTagCompound.hasKey("TileEntityData", 10))
        {
            this.nbtTag = par1NBTTagCompound.getCompoundTag("TileEntityData");
        }

        if (this.block.getMaterial() == Material.air)
        {
            this.block = Blocks.sand;
        }
    }

    public void func_145806_a(boolean p_145806_1_)
    {
        this.hurtEntities = p_145806_1_;
    }

    public void addEntityCrashInfo(CrashReportCategory par1CrashReportCategory)
    {
        super.addEntityCrashInfo(par1CrashReportCategory);
        par1CrashReportCategory.addCrashSection("Immitating block ID", Integer.valueOf(Block.getIdFromBlock(this.block)));
        par1CrashReportCategory.addCrashSection("Immitating block data", Integer.valueOf(this.getMetadata()));
    }

    @SideOnly(Side.CLIENT)
    public float getShadowSize()
    {
        return 1F;
    }

    @SideOnly(Side.CLIENT)
    public World world()
    {
        return this.worldObj;
    }

    /**
     * Return whether this entity should be rendered as on fire.
     */
    @SideOnly(Side.CLIENT)
    public boolean canRenderOnFire()
    {
        return false;
    }

    public Block block()
    {
        return this.block;
    }

    public int getMetadata()
    {
        return metadata;
    }
}

RenderMovingBlock (Entirely copy-pasted from RenderFallingBlock, I want to get rendering working before changing it)

package com.simpleframes.client.renderer.entity;

import net.minecraft.block.Block;
import net.minecraft.block.BlockAnvil;
import net.minecraft.block.BlockDragonEgg;
import net.minecraft.client.renderer.RenderBlocks;
import net.minecraft.client.renderer.Tessellator;
import net.minecraft.client.renderer.entity.Render;
import net.minecraft.client.renderer.texture.TextureMap;
import net.minecraft.entity.Entity;
import net.minecraft.util.MathHelper;
import net.minecraft.util.ResourceLocation;
import net.minecraft.world.World;

import org.lwjgl.opengl.GL11;

import com.simpleframes.entity.EntityMovingBlock;

import cpw.mods.fml.relauncher.Side;
import cpw.mods.fml.relauncher.SideOnly;

@SideOnly(Side.CLIENT)
public class RenderMovingBlock extends Render
{
    private final RenderBlocks  renderBlocks = new RenderBlocks();
    private static final String __OBFID      = "CL_00000994";

    public RenderMovingBlock()
    {
        this.shadowSize = 1F;
    }

    /**
     * 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<T extends Entity) and this method has signature public
     * void func_76986_a(T entity, double d, double d1, double d2, float f,
     * float f1). But JAD is pre 1.5 so doesn't do that.
     */
    public void doRender(EntityMovingBlock entity, double x, double y, double z, float p_147918_8_, float p_147918_9_)
    {
        World world = entity.world();
        Block block = entity.block();
        int i = MathHelper.floor_double(entity.posX);
        int j = MathHelper.floor_double(entity.posY);
        int k = MathHelper.floor_double(entity.posZ);

        if (block != null)
        {
            GL11.glPushMatrix();
            GL11.glTranslatef((float) x, (float) y, (float) z);
            this.bindEntityTexture(entity);
            GL11.glDisable(GL11.GL_LIGHTING);
            Tessellator tessellator;

            if (block instanceof BlockAnvil)
            {
                this.renderBlocks.blockAccess = world;
                tessellator = Tessellator.instance;
                tessellator.startDrawingQuads();
                tessellator.setTranslation((double) ((float) (-i) - 0.5F), (double) ((float) (-j) - 0.5F), (double) ((float) (-k) - 0.5F));
                this.renderBlocks.renderBlockAnvilMetadata((BlockAnvil) block, i, j, k, entity.getMetadata());
                tessellator.setTranslation(0.0D, 0.0D, 0.0D);
                tessellator.draw();
            }
            else
                if (block instanceof BlockDragonEgg)
                {
                    this.renderBlocks.blockAccess = world;
                    tessellator = Tessellator.instance;
                    tessellator.startDrawingQuads();
                    tessellator.setTranslation((double) ((float) (-i) - 0.5F), (double) ((float) (-j) - 0.5F), (double) ((float) (-k) - 0.5F));
                    this.renderBlocks.renderBlockDragonEgg((BlockDragonEgg) block, i, j, k);
                    tessellator.setTranslation(0.0D, 0.0D, 0.0D);
                    tessellator.draw();
                }
                else
                {
                    this.renderBlocks.setRenderBoundsFromBlock(block);
                    this.renderBlocks.renderBlockSandFalling(block, world, i, j, k, entity.getMetadata());
                }

            GL11.glEnable(GL11.GL_LIGHTING);
            GL11.glPopMatrix();
        }
    }

    /**
     * Returns the location of an entity's texture. Doesn't seem to be called
     * unless you call Render.bindEntityTexture.
     */
    protected ResourceLocation getEntityTexture(EntityMovingBlock movingBlock)
    {
        return TextureMap.locationBlocksTexture;
    }

    /**
     * Returns the location of an entity's texture. Doesn't seem to be called
     * unless you call Render.bindEntityTexture.
     */
    @Override
    protected ResourceLocation getEntityTexture(Entity entity)
    {
        return this.getEntityTexture((EntityMovingBlock) entity);
    }

    /**
     * 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<T extends Entity) and this method has signature public
     * void func_76986_a(T entity, double d, double d1, double d2, float f,
     * float f1). But JAD is pre 1.5 so doesn't do that.
     */
    @Override
    public void doRender(Entity par1Entity, double par2, double par4, double par6, float par8, float par9)
    {
        this.doRender((EntityMovingBlock) par1Entity, par2, par4, par6, par8, par9);
    }
}

 

 

The rest of the code is here: https://www.dropbox.com/s/3rth16yn14qzlk0/SimpleFrames.zip

Link to comment
Share on other sites

@LaugingJackal: No! Do not use @SideOnly. Never ever.

 

ok, why should I never use @SideOnly? I know not for mobs I just checked my code, do you mean never for the entire mod or just the mob stuff?

 

He means (I think) that you should only use it when absolutely necessary, which should be infrequent.  Technically Minecraft and Forge could have let all classes be loaded on both sides (and just not call them when not needed) but as an optimization decided that server doesn't need to load classes related to GUI and user input stuff so used the SideOnly.  Therefore any code of ours that depends on classes with SideOnly technically also need to be SideOnly too, so it is sometimes needed.  It is common modding practice to move as much of that as you can into your sided proxy -- like registering renderers -- since this is major reason that we do the proxy stuff in the first place.  In other words you can put the functionality into the proxy and don't need SideOnly annotation because the proxy is already sided.

 

Otherwise you shouldn't create new hierarchies that are sided, since there really isn't that much use in the optimization of hiding a few classes.  Certainly you may still need to run code on only one side, but can just check for the side using world.isRemote instead.

 

Basically the SideOnly creates "weird" sets of classes that you have to manage and it is normally unnecessary.  It is not outright wrong, just troublesome.

 

At least I think that is part of diesieben07's aversion to it.

Check out my tutorials here: http://jabelarminecraft.blogspot.com/

Link to comment
Share on other sites

@LaugingJackal: No! Do not use @SideOnly. Never ever.

 

@Shalashalska: Don't use registerGlobalEntityId. Use registerModEntity, then you don't need findGlobalUniqueEntityId either, you can choose any number between 0 and 255, because they are reserved for only your mod.

I can't seem to figure out what to put in for the mod object.

 

 

Link to comment
Share on other sites

I made it use the registerModEntity method, but it still doesn't render.

 

New EntityManager.java

package com.simpleframes.entity;

import com.simpleframes.lib.Reference;

import net.minecraft.client.renderer.entity.RenderSnowball;
import net.minecraft.item.Item;
import cpw.mods.fml.client.registry.RenderingRegistry;
import cpw.mods.fml.common.registry.EntityRegistry;

public class EntityManager
{
    public static void preInit()
    {
        registerEntities();
    }

    private static void registerEntities()
    {
        createEntity(EntityMovingBlock.class, "MovingBlock", 0, 100, 1, true);
    }

    public static void createEntity(Class entityClass, String entityName, int entityID, int trackingRange, int updateFrequency, boolean sendVelocityUpdates)
    {
        EntityRegistry.registerModEntity(entityClass, entityName, entityID, Reference.MOD_ID, trackingRange, updateFrequency, sendVelocityUpdates);
    }
}

 

Edit: However, it now does exist in the client world.

 

 

Link to comment
Share on other sites

He means (I think) that you should only use it when absolutely necessary, which should be infrequent.  Technically Minecraft and Forge could have let all classes be loaded on both sides (and just not call them when not needed) but as an optimization decided that server doesn't need to load classes related to GUI and user input stuff so used the SideOnly.
No, I mean you literally should not use it, ever. It's a marker interface by forge that results in the merging process of Client & Server. We get 2 jar files from Mojang, the client & the server. Both contain roughly the same things, but the client has a whole lot more things and the server has some things too that the client have.

The decompilation process merges these two jars into one universal codebase, so that you can make only one mod jar that works on client & server. @SideOnly is a marker for you that tells you "oh, this class / method / field only exists in the Server / Client jar file". That means you can only use such a class / method / field only with care, because it doesn't always exist.

 

I understand that, but don't see how you can say "never".  If I extend a class and override a method that is already SideOnly, don't I have to also be SideOnly in my overriding method?

 

Yes you shouldn't create your own sided stuff, but if you're calling, extending, or copying stuff that is already sided don't you have to follow that?

Check out my tutorials here: http://jabelarminecraft.blogspot.com/

Link to comment
Share on other sites

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.