Jump to content

[1.7.10] [SOLVED] Entity.SetSize cannot be called outside constructor?


Recommended Posts

Posted

Hi guys,

 

I have an issue that haunt me for a while and decided to attack it tonight.

 

I have an entity that can have 5 different sizes. I noticed that if you call SetSize outside the constructor of the entity, it is never set (I checked with F3+B) If I call it within the constructor, then it is set correctly.

 

I tried looking at the setsize code, but I do not really understand the logic in there. Someone would know something I don't?

 

This is a serious issue for me since my entity can have 5 different sizes, so you will understand that I need to be able to change the collision boxes at some point outside the constructor.

 

Thanks guys!

 

 

Posted

My guess is that it works perfectly fine.

 

Problem is most likely in sychronization.

Each - Server and Client Thread, has its own entity. How +/- looks:

1. Whenever you make "new Entity(...)" you (should) do this on server (!world.isRemote).

2. Then, if you World#spawnEntityInWorld(...) (still on server), the server will send spawning packet to client thread that contains all data needed to spawn given entity.

3. That packet will contain "vanilla" data (id, position, etc), but can also contain IEntityAdditionalSpawnData (implementing this interface allows to send special data, ONCE when entity is spawned).

4. Client receives packet and recreates entity from providded data, it also binds entity with temporary (per-world-join) entityId, that links server entity with client entity.

 

Now, somewhere in this logic you need to notify both sides that you want to change entity's size. To do that you need to perform Entity#setSize(...) on BOTH threads.

And to do that you need to send packet! (or use IEntityAdditionalSpawnData, but that is ONLY once, on spawn).

Packets tutorial:

http://www.minecraftforum.net/forums/mapping-and-modding/mapping-and-modding-tutorials/2137055-1-7-2-customizing-packet-handling-with

Short:

http://www.minecraftforge.net/forum/index.php/topic,20135.0.html

Thread safety: Only for 1.8

http://greyminecraftcoder.blogspot.com.au/2015/01/thread-safety-with-network-messages.html

 

EDIT: Your packet:

After learning (tutorial above) about packets, you will need to send:

int entityId; // get: "entity.entityId;" // from world: world.getEntityById(entityId);

float width;

float height;

1.7.10 is no longer supported by forge, you are on your own.

Posted

A possible simple solution could be to just let whatever code you have that calls #setSize run on both sides.

 

Another solution that will allow you to avoid making your own packets is to use World#setEntityState in combination with your entity's #handleHealthUpdate method, like so:

// define a couple flags:
private static final int SIZE_SMALL = 5, SIZE_MEDIUM = 6, SIZE_LARGE = 7, etc.;

// when your entity changes its size on the server:
if (!worldObj.isRemote) {
   // whatever your setSize logic is, e.g.
   switch (currentSize) {
   case SIZE_SMALL: setSize(1, 1); break;
   case SIZE_MEDIUM: setSize(2, 2); break;
   // etc.
   }
   // call #setEntityState to send a flag to the client
   worldObj.setEntityState(currentSize);
}

// override #handleHealthUpdate to handle your cases
@Override
@SideOnly(Side.CLIENT)
public void handleHealthUpdate(int flag) {
switch (flag) {
case SIZE_SMALL: setSize(1, 1); break;
case SIZE_MEDIUM: setSize(2, 2); break;
// etc.
default: super.handleHealthUpdate(flag); // super class often handles various states - check for conflicts
}
}

Just a couple ideas. Sending your own packet should work too, of course.

Posted

My guess is that it works perfectly fine.

 

Problem is most likely in sychronization.

Each - Server and Client Thread, has its own entity. How +/- looks:

1. Whenever you make "new Entity(...)" you (should) do this on server (!world.isRemote).

2. Then, if you World#spawnEntityInWorld(...) (still on server), the server will send spawning packet to client thread that contains all data needed to spawn given entity.

3. That packet will contain "vanilla" data (id, position, etc), but can also contain IEntityAdditionalSpawnData (implementing this interface allows to send special data, ONCE when entity is spawned).

4. Client receives packet and recreates entity from providded data, it also binds entity with temporary (per-world-join) entityId, that links server entity with client entity.

 

Now, somewhere in this logic you need to notify both sides that you want to change entity's size. To do that you need to perform Entity#setSize(...) on BOTH threads.

And to do that you need to send packet! (or use IEntityAdditionalSpawnData, but that is ONLY once, on spawn).

Packets tutorial:

http://www.minecraftforum.net/forums/mapping-and-modding/mapping-and-modding-tutorials/2137055-1-7-2-customizing-packet-handling-with

Short:

http://www.minecraftforge.net/forum/index.php/topic,20135.0.html

Thread safety: Only for 1.8

http://greyminecraftcoder.blogspot.com.au/2015/01/thread-safety-with-network-messages.html

 

EDIT: Your packet:

After learning (tutorial above) about packets, you will need to send:

int entityId; // get: "entity.entityId;" // from world: world.getEntityById(entityId);

float width;

float height;

 

A possible simple solution could be to just let whatever code you have that calls #setSize run on both sides.

 

Another solution that will allow you to avoid making your own packets is to use World#setEntityState in combination with your entity's #handleHealthUpdate method, like so:

// define a couple flags:
private static final int SIZE_SMALL = 5, SIZE_MEDIUM = 6, SIZE_LARGE = 7, etc.;

// when your entity changes its size on the server:
if (!worldObj.isRemote) {
   // whatever your setSize logic is, e.g.
   switch (currentSize) {
   case SIZE_SMALL: setSize(1, 1); break;
   case SIZE_MEDIUM: setSize(2, 2); break;
   // etc.
   }
   // call #setEntityState to send a flag to the client
   worldObj.setEntityState(currentSize);
}

// override #handleHealthUpdate to handle your cases
@Override
@SideOnly(Side.CLIENT)
public void handleHealthUpdate(int flag) {
switch (flag) {
case SIZE_SMALL: setSize(1, 1); break;
case SIZE_MEDIUM: setSize(2, 2); break;
// etc.
default: super.handleHealthUpdate(flag); // super class often handles various states - check for conflicts
}
}

Just a couple ideas. Sending your own packet should work too, of course.

 

You guys are both awesome. I already knew how to handle messages, I am pretty sure you can't live with those. So yes it works ... but I have a new issue now. Maybe you have an idea ...

 

So ok the call works ...but now I have a new issue. When the method is called after the readfromnbt (when the world reload the entities) ...it seems that the entity cannot get the message yet. I guess it is not fully created yet. Any idea where I could make the call after the entity is created on the server and client? I knew it worked by changing the size of my entity by setting it on interact(EntityPlayer player). So I see it works ... just need to find where to call it now.

 

So here was my message, in case someone would have the same issue.

 

package fantastic.network;


import io.netty.buffer.ByteBuf; 
import net.minecraft.entity.Entity;
import net.minecraft.entity.player.EntityPlayer; 



import cpw.mods.fml.common.network.simpleimpl.IMessage; 
import cpw.mods.fml.common.network.simpleimpl.IMessageHandler; 
import cpw.mods.fml.common.network.simpleimpl.MessageContext; 
import cpw.mods.fml.relauncher.Side; 
import fantastic.FantasticDebug;
import fantastic.FantasticMod;
import fantastic.entities.EntityFantasticFish;


public class FishSetSizeMessage implements IMessage, IMessageHandler<FishSetSizeMessage, IMessage> 
{ 
    private int entityId;
    private float height;
    private float width;


    public FishSetSizeMessage() 
    { 
    } 


    public FishSetSizeMessage(int anEntityId,float aHeight, float aWidth) 
    { 
        entityId=anEntityId;
        height=aHeight;
        width=aWidth;


    } 


    @Override 
    public void fromBytes(ByteBuf buf) 
    { 
        entityId = buf.readInt(); 
        height = buf.readFloat(); 
        width = buf.readFloat();
    } 


    @Override 
    public void toBytes(ByteBuf buf) 
    { 
        buf.writeInt(entityId); 
        buf.writeFloat(height);
        buf.writeFloat(width);
        
    } 


@Override
public IMessage onMessage(FishSetSizeMessage message, MessageContext ctx) 
{
	if (message!=null)
	{
		FantasticDebug.Output("MESSAGE RECEIVED. Entity: "+Integer.toString(message.entityId)+" Height:"+Float.toString(message.height)+" Width:"+Float.toString(message.width),true );
		EntityPlayer player = FantasticMod.proxy.getPlayerFromMessage(ctx);
		if (player!=null)
		{
			Entity _ent = player.worldObj.getEntityByID(message.entityId);
			if (_ent!=null)
			{
				((EntityFantasticFish)_ent).SetSizeCollisionBox(message.height, message.width);
				FantasticDebug.Output("New size set to entity: "+Integer.toString(message.entityId)+" Height:"+Float.toString(message.height)+" Width:"+Float.toString(message.width),true );
				return null;

			}
			else
			{
				FantasticDebug.Output("Entity IS NULL! ",true);
			}

		}
		else
		{
			FantasticDebug.Output("PLAYER IS NULL!");
		}
	}
	else
	{
		FantasticDebug.Output("MESSAGE IS NULL!",true);
	}
	return null;	
} 
} 

 

And here is my SetSizeCollisionBoxe in my entity class

 

public void SetSizeCollisionBox(float aHeight, float aWidth)
{
    	if ((aHeight!=this.height) || (aWidth!=this.width))
    	{
    		this.setSize(aHeight, aWidth);
		if (!this.worldObj.isRemote)
		{

			FantasticNetwork.network.sendToAll(new FishSetSizeMessage(this.getEntityId(), aHeight, aWidth));
			//FantasticNetwork.network.sendToAllAround(new FishSpeedMessage(this.getEntityId(), currentSpeed),new TargetPoint(this.worldObj.provider.dimensionId,this.posX,this.posY,this.posZ,30));
		}    		
    	}	
}

 

 

 

Posted

You are correct - the client side entity does not exist when the server side reads from NBT, but there are 2 possible solutions:

 

1. Send the packet from EntityJoinWorldEvent instead of from the readFromNbt method

 

2. OR, and this is much better, implement IEntityAdditionalSpawnData and send the entity's size with the spawn packet; this data is sent every time the entity's spawn packet is sent to the client

Posted

Why don't you store the length and width in a data watcher? Then every tick client side set the entity bounding box based on those values. Seems a lot easier to me.

 

"you seem to be THE best modder I've seen imo."

~spynathan

 

ლ(́◉◞౪◟◉‵ლ

Posted

You are correct - the client side entity does not exist when the server side reads from NBT, but there are 2 possible solutions:

 

1. Send the packet from EntityJoinWorldEvent instead of from the readFromNbt method

 

2. OR, and this is much better, implement IEntityAdditionalSpawnData and send the entity's size with the spawn packet; this data is sent every time the entity's spawn packet is sent to the client

 

I had to go with option #1 even though I understood what you meant in the #2. The reason why I went with #1 is because I do not know yet what the size of my mob will be on a new spawn from minecraft ... I catch the entityjoinworld event and deicide there what size it will be. So it finally works there.

 

Thanks for your help guys :) This forum is a bless!!!

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.