Jump to content

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


Frag

Recommended Posts

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!

 

 

Link to comment
Share on other sites

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.

Link to comment
Share on other sites

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.

Link to comment
Share on other sites

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));
		}    		
    	}	
}

 

 

 

Link to comment
Share on other sites

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

Link to comment
Share on other sites

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!!!

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



  • Recently Browsing

    • No registered users viewing this page.
  • Posts

    • Hi, I want to make a client-only mod, everything is ok, but when I use shaders, none of the textures rendered in RenderLevelStageEvent nor the crow entity model are rendered, I want them to be visible, because it's a horror themed mod Here is how i render the crow model in the CrowEntityRenderer<CrowEntity>, by the time i use this method, i know is not the right method but i don't think this is the cause of the problem, the renderType i'm using is entityCutout @Override public void render(CrowEntity p_entity, float entityYaw, float partialTick, PoseStack poseStack, MultiBufferSource bufferSource, int packedLight) { super.render(p_entity, entityYaw, partialTick, poseStack, bufferSource, packedLight); ClientEventHandler.getClient().crow.renderToBuffer(poseStack, bufferSource.getBuffer(ClientEventHandler.getClient().crow .renderType(TEXTURE)), packedLight, OverlayTexture.NO_OVERLAY, Utils.rgb(255, 255, 255)); } Here renderLevelStage @Override public void renderWorld(RenderLevelStageEvent e) { horrorEvents.draw(e); } Here is how i render every event public void draw(RenderLevelStageEvent e) { for (HorrorEvent event : currentHorrorEvents) { event.tick(e.getPartialTick()); event.draw(e); } } Here is how i render the crow model on the event @Override public void draw(RenderLevelStageEvent e) { if(e.getStage() == RenderLevelStageEvent.Stage.AFTER_ENTITIES) { float arcProgress = getArcProgress(0.25f); int alpha = (int) Mth.lerp(arcProgress, 0, 255); int packedLight = LevelRenderer.getLightColor(Minecraft.getInstance().level, blockPos); VertexConsumer builder = ClientEventHandler.bufferSource.getBuffer(crow); Crow<CreepyBirdHorrorEvent> model = ClientEventHandler .getClient().crow; model.setupAnim(this); RenderHelper.renderModelInWorld(model, position, offset, e.getCamera(), e.getPoseStack(), builder, packedLight, OverlayTexture.NO_OVERLAY, alpha); builder = ClientEventHandler.bufferSource.getBuffer(eyes); RenderHelper.renderModelInWorld(model, position, offset, e.getCamera(), e.getPoseStack(), builder, 15728880, OverlayTexture.NO_OVERLAY, alpha); } } How i render the model public static void renderModelInWorld(Model model, Vector3f pos, Vector3f offset, Camera camera, PoseStack matrix, VertexConsumer builder, int light, int overlay, int alpha) { matrix.pushPose(); Vec3 cameraPos = camera.getPosition(); double finalX = pos.x - cameraPos.x + offset.x; double finalY = pos.y - cameraPos.y + offset.y; double finalZ = pos.z - cameraPos.z + offset.z; matrix.pushPose(); matrix.translate(finalX, finalY, finalZ); matrix.mulPose(Axis.XP.rotationDegrees(180f)); model.renderToBuffer(matrix, builder, light, overlay, Utils .rgba(255, 255, 255, alpha)); matrix.popPose(); matrix.popPose(); } Thanks in advance
    • Here's the link: https://mclo.gs/7L5FibL Here's the link: https://mclo.gs/7L5FibL
    • Also the mod "Connector Extras" modifies Reach-entity-attributes and can cause fatal errors when combined with ValkrienSkies mod. Disable this mod and continue to use Syntra without it.
    • Hi everyone. I was trying modify the vanilla loot of the "short_grass" block, I would like it drops seeds and vegetal fiber (new item of my mod), but I don't found any guide or tutorial on internet. Somebody can help me?
    • On 1.20.1 use ValkrienSkies mod version 2.3.0 Beta 1. I had the same issues as you and it turns out the newer beta versions have tons of unresolved incompatibilities. If you change the version you will not be required to change the versions of eureka or any other additions unless prompted at startup. This will resolve Reach-entity-attributes error sound related error and cowardly errors.
  • Topics

×
×
  • Create New...

Important Information

By using this site, you agree to our Terms of Use.