Jump to content

[Solved][1.9]How to use IGuiHandler to modify state of the custom entity


horsewithnoname

Recommended Posts

Hello everybody!

 

I have a problem with figuring out how to use Gui classes to modify specific variables of a custom entity. I searched for tutorials or code examples (even looked in vanilla) about GUI, but they all seem to be about Inventory, Containers and TileEntitties. What about entities?

 

Here I have a custom entity that on processInteract should open a Gui to change its state:

 

/* Package and imports */

public class CameraEntity extends EntityLiving
{
/* Here are the variables I want to modify */

public float speed = 0.4F;
public float accelerationRate = 0.02F;
public float accelerationMax = 1.5f;
public boolean canFly = true;

protected float acceleration = 0.0F;

/* Constructor and other method that modify entity's state */

/**
 * Processes player's right clicking on the entity
 * 
 * If the player holds camera configuration item, then GUI with camera
 * configuration properties should pop up, otherwise start riding
 */
@Override
public boolean processInteract(EntityPlayer player, EnumHand p_184645_2_, ItemStack stack)
{
	if (!worldObj.isRemote)
	{
		ItemStack item = player.getHeldItemMainhand();

		if (item != null && item.getItem() instanceof CameraConfigItem)
		{
			player.openGui(MyMod.instance, 0, worldObj, (int) posX, (int) posY, (int) posZ);

			return true;
		}

		if (!isBeingRidden())
		{
			return player.startRiding(this);
		}
	}

	return false;
}

/* Riding logic (moveEntityWithHeading, canBeSteered, getControllingPassenger) */
}

 

Basically I want to modify first four public variables (I know that they won't be saved). There's my GUI code:

 

/* Package and imports */

public class GuiCamera extends GuiScreen
{
/* static validator */

protected GuiTextField speed;
protected GuiTextField accelerationRate;
protected GuiTextField accelerationMax;
protected GuiButton canFly;
protected GuiButton done;

public GuiCamera()
{
	/* How can I pass entity here? */
}

@Override
public void initGui()
{
	int x = width/2 - 150;

	speed = new GuiTextField(0, fontRendererObj, x, 50, 300, 20);
	// speed.setText(Double.toString(camera.speed));
	speed.setValidator(validator);

	accelerationRate = new GuiTextField(1, fontRendererObj, x, 90, 300, 20);
	// accelerationRate.setText(Double.toString(camera.accelerationRate));
	accelerationRate.setValidator(validator);

	accelerationMax = new GuiTextField(2, fontRendererObj, x, 130, 300, 20);
	// accelerationMax.setText(Double.toString(camera.accelerationMax));
	accelerationMax.setValidator(validator);

	buttonList.clear();
	buttonList.add(canFly = new GuiButton(3, x, 180, 160, 20, "Can fly"));
	buttonList.add(done = new GuiButton(4, x + 180, 160, 140, 20, "Done"));

	// canFly.displayString = camera.canFly ? "Can fly" : "Can't fly";
}

@Override
protected void actionPerformed(GuiButton button) throws IOException
{
	switch (button.id)
	{
		case 3:
			updateFlyButton();
		break;

		case 4:
			mc.displayGuiScreen(null);
		break;
	}
}

private void updateFlyButton()
{
	if (canFly.displayString == "Can fly")
	{
		canFly.displayString = "Can't fly";
	}
	else 
	{
		canFly.displayString = "Can fly";
	}
}

/* Mouse clicked, key typed and draw screen methods are here */
}

 

There's few issues I'm having here:

 

1. I can't pass entity to the GuiCamera, I tried using world.getEntitiesWithinAABB, but it throws and error saying "Error executing task"

2. I need Container in order to open the GUI, but I don't know how to pass entity in it and I don't know how to use this class to communicate between client and server or how to use it to change state of the entity

 

So now is the question: how can I use IGuiHandler to modify state of the custom entity?

 

Thank you for your attention :)

 

P.S.: I don't ask for ready to use code, I ask for guidance. Thanks.

 

P.S.S.: when I'm pressing "Done" button in GUI, for some reason game starts to glitch a little bit, for example when I use /give command given item/block doesn't appear in the inventory, but after a while it does appear. It looks like game goes out of sync. Is there any other code I should execute before closing the GUI with mc.displayGuiScreen(null);?

Blockbuster – simple machinimas and cinematics for Minecraft
Link to comment
Share on other sites

Hello everybody!

 

I have a problem with figuring out how to use Gui classes to modify specific variables of a custom entity. I searched for tutorials or code examples (even looked in vanilla) about GUI, but they all seem to be about Inventory, Containers and TileEntitties. What about entities?

 

Here I have a custom entity that on processInteract should open a Gui to change its state:

 

/* Package and imports */

public class CameraEntity extends EntityLiving
{
/* Here are the variables I want to modify */

public float speed = 0.4F;
public float accelerationRate = 0.02F;
public float accelerationMax = 1.5f;
public boolean canFly = true;

protected float acceleration = 0.0F;

/* Constructor and other method that modify entity's state */

/**
 * Processes player's right clicking on the entity
 * 
 * If the player holds camera configuration item, then GUI with camera
 * configuration properties should pop up, otherwise start riding
 */
@Override
public boolean processInteract(EntityPlayer player, EnumHand p_184645_2_, ItemStack stack)
{
	if (!worldObj.isRemote)
	{
		ItemStack item = player.getHeldItemMainhand();

		if (item != null && item.getItem() instanceof CameraConfigItem)
		{
			player.openGui(MyMod.instance, 0, worldObj, (int) posX, (int) posY, (int) posZ);

			return true;
		}

		if (!isBeingRidden())
		{
			return player.startRiding(this);
		}
	}

	return false;
}

/* Riding logic (moveEntityWithHeading, canBeSteered, getControllingPassenger) */
}

 

Basically I want to modify first four public variables (I know that they won't be saved). There's my GUI code:

 

/* Package and imports */

public class GuiCamera extends GuiScreen
{
/* static validator */

protected GuiTextField speed;
protected GuiTextField accelerationRate;
protected GuiTextField accelerationMax;
protected GuiButton canFly;
protected GuiButton done;

public GuiCamera()
{
	/* How can I pass entity here? */
}

@Override
public void initGui()
{
	int x = width/2 - 150;

	speed = new GuiTextField(0, fontRendererObj, x, 50, 300, 20);
	// speed.setText(Double.toString(camera.speed));
	speed.setValidator(validator);

	accelerationRate = new GuiTextField(1, fontRendererObj, x, 90, 300, 20);
	// accelerationRate.setText(Double.toString(camera.accelerationRate));
	accelerationRate.setValidator(validator);

	accelerationMax = new GuiTextField(2, fontRendererObj, x, 130, 300, 20);
	// accelerationMax.setText(Double.toString(camera.accelerationMax));
	accelerationMax.setValidator(validator);

	buttonList.clear();
	buttonList.add(canFly = new GuiButton(3, x, 180, 160, 20, "Can fly"));
	buttonList.add(done = new GuiButton(4, x + 180, 160, 140, 20, "Done"));

	// canFly.displayString = camera.canFly ? "Can fly" : "Can't fly";
}

@Override
protected void actionPerformed(GuiButton button) throws IOException
{
	switch (button.id)
	{
		case 3:
			updateFlyButton();
		break;

		case 4:
			mc.displayGuiScreen(null);
		break;
	}
}

private void updateFlyButton()
{
	if (canFly.displayString == "Can fly")
	{
		canFly.displayString = "Can't fly";
	}
	else 
	{
		canFly.displayString = "Can fly";
	}
}

/* Mouse clicked, key typed and draw screen methods are here */
}

 

There's few issues I'm having here:

 

1. I can't pass entity to the GuiCamera, I tried using world.getEntitiesWithinAABB, but it throws and error saying "Error executing task"

2. I need Container in order to open the GUI, but I don't know how to pass entity in it and I don't know how to use this class to communicate between client and server or how to use it to change state of the entity

 

So now is the question: how can I use IGuiHandler to modify state of the custom entity?

 

Thank you for your attention :)

 

P.S.: I don't ask for ready to use code, I ask for guidance. Thanks.

 

P.S.S.: when I'm pressing "Done" button in GUI, for some reason game starts to glitch a little bit, for example when I use /give command given item/block doesn't appear in the inventory, but after a while it does appear. It looks like game goes out of sync. Is there any other code I should execute before closing the GUI with mc.displayGuiScreen(null);?

Blockbuster – simple machinimas and cinematics for Minecraft
Link to comment
Share on other sites

1. As you noticed from source (I guess): IGuHandler is mainly designed to connect Container with its GuiContainer.

First, you probably understand sides, and if not: http://mcforge.readthedocs.io/en/latest/concepts/sides/

It is important to get that Entity object is on server and client, where they communicate with each other via entityId.

 

Calling player.openGui will: (by thread)

Server:

* If Container is returned, it will be opened and packet will be sent to client (with world, id, x, y, z, where x/y/z can by anything). Packet will call client opening.

* If null - nothing will happen.

Client:

* It will simply call Minecraft#displayGuiScreen (I might have messed up naming).

 

Now - Container are ONLY ever used to display interactive Guis that hold/display ItemStacks. In this case you don't need that.

And yes - in this case IGuiHandler should be only used to centralize code (meaning that you will open all your guis from player.openGui). And yes - you will retur null on server and normal GuiScreen on client. Now - since only client can open that gui (no container), you need to call opening from client thread (world.isRemote) - this is the 1st mistake you made there (in entity class).

 

2. Next - as one could guess - "client always lies" - if you would want to check (on client) if client can even open a gui, it can always lie. That is where you can implement request/order packet system. Basically - since Entity#processInteract is called on both sides, you can perform checks on server and then if passed - send packet to client to call player.openGui.

 

Followup to this will be SimpleNetworkWrapper and thread safety:

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

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

If you don't have those yet - you will be using them in future, so I recommend designing abstraction layer.

 

3. Okay, so now Gui itself:

You simply make constructor have CameraEntity (why not EntityCamera? :P) as arg.

There are (as said) two options:

* Open gui with IGuiHandler (player.openGui).

- Then in your IGuiHandler you will be getting that entity using World#getEntityByID. As said - x/y/z can by anything, so it can also be that ID.

* Open gui with Minecraft#displayGuiScreen.

- In that case you can directly pass Entity instance to new Gui. (and instance you can again get from entityID).

 

4. Regarding request/order packets - you will need to send (server->client):

1st option:

Only entityID (entity.getEntityId())

2nd option:

entityID and guiID (so you can use one packet for multiple guis).

In Handler you will be calling opening gui mentioned in 3rd point.

 

EDIT

 

5. Same rules (packets) apply when sending data from gui (client) to server.

You will need thread safety and will need to send data (including entityId) that will (on server) apply that data onto server's entity (that you get from entityID).

 

6. Logically whenever you make such messaging you should do:

* (request/order) clientRequest -> serverCheck -> serverChanges -> serverAnswer -> clientChanges

or

* (order/update) serverChanges -> serverAnswer -> clientChanges

Where 1st one applies when client would want to change entity data.

 

EDIT 2

 

7. Now synchronizing Entity itself:

* EntityTracker - can be used to get all players tracking given entity. You can use that to update all players seeing it when you change value on server. Simply send packet to all those who track it.

* PlayerEvent.StartTracking - when player starts tracking (seeing) entity, it will be called. event.target is entity being tracked. You can use this to sync data with player starting tracking about entity being tracked. This method should be only ever used to OTHER (not yours, eg vanilla) Entities or if you want to centralize your code (e.g you have same behavir for all entities because you assigned @Capability to all of them).

* IEntityAdditionalSpawnData - implement this to your OWN entities - basically what StartTracking does, but more interanlly.

* SimpleNetworkWrapper - send simple packet updates.

 

NEVER FORGET about THREAD SAFETY.

 

Note: Getting grasp of above allows you to pretty much start doing anything (intermidiate).

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

Link to comment
Share on other sites

1. As you noticed from source (I guess): IGuHandler is mainly designed to connect Container with its GuiContainer.

First, you probably understand sides, and if not: http://mcforge.readthedocs.io/en/latest/concepts/sides/

It is important to get that Entity object is on server and client, where they communicate with each other via entityId.

 

Calling player.openGui will: (by thread)

Server:

* If Container is returned, it will be opened and packet will be sent to client (with world, id, x, y, z, where x/y/z can by anything). Packet will call client opening.

* If null - nothing will happen.

Client:

* It will simply call Minecraft#displayGuiScreen (I might have messed up naming).

 

Now - Container are ONLY ever used to display interactive Guis that hold/display ItemStacks. In this case you don't need that.

And yes - in this case IGuiHandler should be only used to centralize code (meaning that you will open all your guis from player.openGui). And yes - you will retur null on server and normal GuiScreen on client. Now - since only client can open that gui (no container), you need to call opening from client thread (world.isRemote) - this is the 1st mistake you made there (in entity class).

 

2. Next - as one could guess - "client always lies" - if you would want to check (on client) if client can even open a gui, it can always lie. That is where you can implement request/order packet system. Basically - since Entity#processInteract is called on both sides, you can perform checks on server and then if passed - send packet to client to call player.openGui.

 

Followup to this will be SimpleNetworkWrapper and thread safety:

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

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

If you don't have those yet - you will be using them in future, so I recommend designing abstraction layer.

 

3. Okay, so now Gui itself:

You simply make constructor have CameraEntity (why not EntityCamera? :P) as arg.

There are (as said) two options:

* Open gui with IGuiHandler (player.openGui).

- Then in your IGuiHandler you will be getting that entity using World#getEntityByID. As said - x/y/z can by anything, so it can also be that ID.

* Open gui with Minecraft#displayGuiScreen.

- In that case you can directly pass Entity instance to new Gui. (and instance you can again get from entityID).

 

4. Regarding request/order packets - you will need to send (server->client):

1st option:

Only entityID (entity.getEntityId())

2nd option:

entityID and guiID (so you can use one packet for multiple guis).

In Handler you will be calling opening gui mentioned in 3rd point.

 

EDIT

 

5. Same rules (packets) apply when sending data from gui (client) to server.

You will need thread safety and will need to send data (including entityId) that will (on server) apply that data onto server's entity (that you get from entityID).

 

6. Logically whenever you make such messaging you should do:

* (request/order) clientRequest -> serverCheck -> serverChanges -> serverAnswer -> clientChanges

or

* (order/update) serverChanges -> serverAnswer -> clientChanges

Where 1st one applies when client would want to change entity data.

 

EDIT 2

 

7. Now synchronizing Entity itself:

* EntityTracker - can be used to get all players tracking given entity. You can use that to update all players seeing it when you change value on server. Simply send packet to all those who track it.

* PlayerEvent.StartTracking - when player starts tracking (seeing) entity, it will be called. event.target is entity being tracked. You can use this to sync data with player starting tracking about entity being tracked. This method should be only ever used to OTHER (not yours, eg vanilla) Entities or if you want to centralize your code (e.g you have same behavir for all entities because you assigned @Capability to all of them).

* IEntityAdditionalSpawnData - implement this to your OWN entities - basically what StartTracking does, but more interanlly.

* SimpleNetworkWrapper - send simple packet updates.

 

NEVER FORGET about THREAD SAFETY.

 

Note: Getting grasp of above allows you to pretty much start doing anything (intermidiate).

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

Link to comment
Share on other sites

Many thanks! Your reply was really fast!  :)

 

why not EntityCamera?

 

I started naming classes *Block/Entity/Item, and then found out that about the convention...

I'll rename classes later.

 

Next - as one could guess - "client always lies" - if you would want to check (on client) if client can even open a gui, it can always lie.

 

I did some server programming on perl long time ago, so I know about validating data that comes from client. :)

 

I'll try out your suggestions and reply later if I'll have any troubles. Thanks again.

Blockbuster – simple machinimas and cinematics for Minecraft
Link to comment
Share on other sites

Many thanks! Your reply was really fast!  :)

 

why not EntityCamera?

 

I started naming classes *Block/Entity/Item, and then found out that about the convention...

I'll rename classes later.

 

Next - as one could guess - "client always lies" - if you would want to check (on client) if client can even open a gui, it can always lie.

 

I did some server programming on perl long time ago, so I know about validating data that comes from client. :)

 

I'll try out your suggestions and reply later if I'll have any troubles. Thanks again.

Blockbuster – simple machinimas and cinematics for Minecraft
Link to comment
Share on other sites

Thanks, Ernio, for links and suggestions. :) There's only one problem left to be solved and that's the entity tracking. I don't understand how to use EntityTracker. Is it supposed to be activated on client, on server or on both sides (like in CameraEntity's constructor)?

 

Here's what I've come up with:

 

/* Package and imports */

public class GuiCamera extends GuiScreen
{
protected GuiSlider speed;
protected GuiSlider accelerationRate;
protected GuiSlider accelerationMax;
protected GuiButton canFly;
protected GuiButton done;

private CameraEntity camera;

public GuiCamera(CameraEntity entity)
{
	camera = entity;
}

/* initialize GUI */

@Override
protected void actionPerformed(GuiButton button) throws IOException
{
	switch (button.id)
	{
		case 3: updateFlyButton(); break;
		case 4: saveAndExit(); break;
	}
}

private void saveAndExit()
{
	float cSpeed = (float)speed.getValue();
	float cRate = (float)accelerationRate.getValue();
	float cMax = (float)accelerationMax.getValue();
	boolean cCanFly = canFly.displayString == "Can fly";

	Mod.channel.sendToServer(new CameraAttributesUpdate(camera.getEntityId(), cSpeed, cRate, cMax, cCanFly));

	camera.speed = cSpeed;
	camera.accelerationRate = cRate;
	camera.accelerationMax = cMax;
	camera.canFly = cCanFly;

	mc.displayGuiScreen(null);
}

/* Drawing and clicking logic */
}

 

As you can see I setup SimpleNetworkWrapper and sending message to handle by handler it (on server). This way camera is only synchronized only on client that sent the message and on server, but how to do it to all other connected players?

 

Here's my IMessage and IMessageHandler implementing classes:

 

/* Package and imports */

public class CameraAttributesUpdate implements IMessage
{
protected int id;
protected float speed;
protected float accelerationRate;
protected float accelerationMax;
protected boolean canFly; 

public CameraAttributesUpdate() {}

public CameraAttributesUpdate(int eid, float speeed, float rate, float max, boolean fly)
{
	id = eid;
	speed = speeed;
	accelerationRate = rate;
	accelerationMax = max;
	canFly = fly;
}

@Override
public void fromBytes(ByteBuf buf)
{
	id = buf.readInt();
	speed = buf.readFloat();
	accelerationRate = buf.readFloat();
	accelerationMax = buf.readFloat();
	canFly = buf.readBoolean();
}

@Override
public void toBytes(ByteBuf buf)
{
	buf.writeInt(id);
	buf.writeFloat(speed);
	buf.writeFloat(accelerationRate);
	buf.writeFloat(accelerationMax);
	buf.writeBoolean(canFly);
}

public static class Handler implements IMessageHandler<CameraAttributesUpdate, IMessage>
{
	@Override
	public IMessage onMessage(final CameraAttributesUpdate message, final MessageContext ctx)
	{
		IThreadListener world = (WorldServer)ctx.getServerHandler().playerEntity.worldObj;

		world.addScheduledTask(new Runnable()
		{
			@Override
			public void run()
			{
				Entity entity = ctx.getServerHandler().playerEntity.worldObj.getEntityByID(message.id);
				CameraEntity camera = (CameraEntity)entity;

				camera.speed = (float)message.speed;
				camera.accelerationRate = (float)message.accelerationRate;
				camera.accelerationMax = (float)message.accelerationMax;
				camera.canFly = message.canFly;
			}
		});

		return null;
	}
}
}

 

Maybe I can return another message from CameraAttributesUpdate.Handler that will notify somehow other (and maybe the client that sent the message) players?

Blockbuster – simple machinimas and cinematics for Minecraft
Link to comment
Share on other sites

Thanks, Ernio, for links and suggestions. :) There's only one problem left to be solved and that's the entity tracking. I don't understand how to use EntityTracker. Is it supposed to be activated on client, on server or on both sides (like in CameraEntity's constructor)?

 

Here's what I've come up with:

 

/* Package and imports */

public class GuiCamera extends GuiScreen
{
protected GuiSlider speed;
protected GuiSlider accelerationRate;
protected GuiSlider accelerationMax;
protected GuiButton canFly;
protected GuiButton done;

private CameraEntity camera;

public GuiCamera(CameraEntity entity)
{
	camera = entity;
}

/* initialize GUI */

@Override
protected void actionPerformed(GuiButton button) throws IOException
{
	switch (button.id)
	{
		case 3: updateFlyButton(); break;
		case 4: saveAndExit(); break;
	}
}

private void saveAndExit()
{
	float cSpeed = (float)speed.getValue();
	float cRate = (float)accelerationRate.getValue();
	float cMax = (float)accelerationMax.getValue();
	boolean cCanFly = canFly.displayString == "Can fly";

	Mod.channel.sendToServer(new CameraAttributesUpdate(camera.getEntityId(), cSpeed, cRate, cMax, cCanFly));

	camera.speed = cSpeed;
	camera.accelerationRate = cRate;
	camera.accelerationMax = cMax;
	camera.canFly = cCanFly;

	mc.displayGuiScreen(null);
}

/* Drawing and clicking logic */
}

 

As you can see I setup SimpleNetworkWrapper and sending message to handle by handler it (on server). This way camera is only synchronized only on client that sent the message and on server, but how to do it to all other connected players?

 

Here's my IMessage and IMessageHandler implementing classes:

 

/* Package and imports */

public class CameraAttributesUpdate implements IMessage
{
protected int id;
protected float speed;
protected float accelerationRate;
protected float accelerationMax;
protected boolean canFly; 

public CameraAttributesUpdate() {}

public CameraAttributesUpdate(int eid, float speeed, float rate, float max, boolean fly)
{
	id = eid;
	speed = speeed;
	accelerationRate = rate;
	accelerationMax = max;
	canFly = fly;
}

@Override
public void fromBytes(ByteBuf buf)
{
	id = buf.readInt();
	speed = buf.readFloat();
	accelerationRate = buf.readFloat();
	accelerationMax = buf.readFloat();
	canFly = buf.readBoolean();
}

@Override
public void toBytes(ByteBuf buf)
{
	buf.writeInt(id);
	buf.writeFloat(speed);
	buf.writeFloat(accelerationRate);
	buf.writeFloat(accelerationMax);
	buf.writeBoolean(canFly);
}

public static class Handler implements IMessageHandler<CameraAttributesUpdate, IMessage>
{
	@Override
	public IMessage onMessage(final CameraAttributesUpdate message, final MessageContext ctx)
	{
		IThreadListener world = (WorldServer)ctx.getServerHandler().playerEntity.worldObj;

		world.addScheduledTask(new Runnable()
		{
			@Override
			public void run()
			{
				Entity entity = ctx.getServerHandler().playerEntity.worldObj.getEntityByID(message.id);
				CameraEntity camera = (CameraEntity)entity;

				camera.speed = (float)message.speed;
				camera.accelerationRate = (float)message.accelerationRate;
				camera.accelerationMax = (float)message.accelerationMax;
				camera.canFly = message.canFly;
			}
		});

		return null;
	}
}
}

 

Maybe I can return another message from CameraAttributesUpdate.Handler that will notify somehow other (and maybe the client that sent the message) players?

Blockbuster – simple machinimas and cinematics for Minecraft
Link to comment
Share on other sites

You code looks well (dude, trust me, there are so many people who wouldn't know what to do with all that I wrote, but that is what I got used to since a lot of ppl who come here don't know Java).

 

Direct answers:

* EntityTracker - can be used to get all players tracking given entity. You can use that to update all players seeing it when you change value on server. Simply send packet to all those who track it.

 

1. Yes, this is server side thingy that keeps track of which player should receive updates about which entity.

Best design for those things is to make:

@Override
public final void updateTrackers(Entity entity, IMessage message) // method in my SimpleNetworkWrapper
{
	EntityTracker et = ((WorldServer) entity.worldObj).getEntityTracker();
	et.func_151248_b(entity, this.getInstance().getPacketFrom(message)); // this = SimpleNetworkWrapper
}

 

What this does is basically get "tracker" of "entity" and "func_151248_b" (note: maybe they finally renamed it) will send given IMessage to all EntityPlayer seeing this "entity". SNW#getPacketFrom(IMessage) converts IMessage to vanilla format Packet.

 

How it should be used?

Best design is probably something like sided setter in Entity class:

private int something;
public void setSomething(int value, boolean notify)
{
    this.something = value;
    if (!this.worldObj.isRemote && notify)
    {
        SNW#updateTrackers(this, new IMessage(this.entityId, this.something)); // calls my method in SNW
    }
}

 

Some people do this using methods such as SimpleNetworkWrapper#sendToAllAround(IMessage message, NetworkRegistry.TargetPoint point) which is provided by Forge itself, but I think it is simply bad. EntityTracker is superior.

 

So basically:

* EntityTracker - solves existance updating (changes made to entity when someone sees (tracks) it alredy). Server only.

* IEntityAdditionalSpawnData (IEASD) - solves spawn updating (data sent precisely when you start tracking entity).

* PlayerEvent.StartTracking - like IEASD - allows doing the same for not your entities, it also allows other things (think about it). Not it fires server only.

 

EDIT

 

Bog note - my techniques regarding EntityTracker, while still good, might be outdated. Please lookup class itself as it now provides methods such as getTrackingPlayers and sendToAllTrackingEntity. As to one I have - func_151248_b is vanilla's one - also note that "tracking entities" are also the the entity itself (If passed Entity is EntityPlayer, it tracks itself).

 

Additional info:

I forgot to re-point out:

6. Logically whenever you make such messaging you should do:

* (request/order) clientRequest -> serverCheck -> serverChanges -> serverAnswer -> clientChanges

or

* (order/update) serverChanges -> serverAnswer -> clientChanges

Where 1st one applies when client would want to change entity data.

 

private void saveAndExit()
{
	float cSpeed = (float)speed.getValue();
	float cRate = (float)accelerationRate.getValue();
	float cMax = (float)accelerationMax.getValue();
	boolean cCanFly = canFly.displayString == "Can fly";

	Mod.channel.sendToServer(new CameraAttributesUpdate(camera.getEntityId(), cSpeed, cRate, cMax, cCanFly));

// DON'T DO THIS (START)

	camera.speed = cSpeed;
	camera.accelerationRate = cRate;
	camera.accelerationMax = cMax;
	camera.canFly = cCanFly;

// DON'T DO THIS (END)

	mc.displayGuiScreen(null);
}

 

This should be taken care of from server's point of view. How do you know the server will acceps sent values? And if not - it may lead to your client having bad data.

 

EDIT 2

 

Just a reminder - not everything needs to be synced at all times. If all player seeing camera don't actually need some values, you can simply not update them with those. You could e.g send those values only when they will start interaction (just before you send openGui order-packet).

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

Link to comment
Share on other sites

Thanks for another reply!  :)

 

You code looks well (dude, trust me, there are so many people who would know what to do with all that I wrote, but that is what I got used to since a lot of ppl who come here don't know Java).

 

Sorry to disappoint you, but my problem isn't about knowledge of the language. Java is just like any other languages with built-in support of OOP. My problem is that I'm not familiar with minecraft's API. I started developing my mod a week and a half ago.

 

So, now about the EntityTracker. I read some Forge and Minecraft source code, and now I think I get how to use EntityTracker. From forge.fml.common.registry.EntityRegistry I get that Forge is automatically adds entity to tracking when I register my custom mod, but it seems to me that EntityTracker isn't going to help me with syncing my custom fields from server to the client. EntityTrackerEntry

 

By the way, my mod is targeted towards single-player experience, so I don't really need to do all those validation checks.

Blockbuster – simple machinimas and cinematics for Minecraft
Link to comment
Share on other sites

Thanks for another reply!  :)

 

You code looks well (dude, trust me, there are so many people who would know what to do with all that I wrote, but that is what I got used to since a lot of ppl who come here don't know Java).

 

Sorry to disappoint you, but my problem isn't about knowledge of the language. Java is just like any other languages with built-in support of OOP. My problem is that I'm not familiar with minecraft's API. I started developing my mod a week and a half ago.

 

So, now about the EntityTracker. I tried to copy-paste your code examples, but it doesn't seem to work. When I open GUI again it shows the old values in GUI, but on server it prints the sent values. I don't really understand how does SNW#getPacketFrom(message) can magically convert my message into right ordered packet and then on the client side inject those values into correct fields.

 

I read some Forge and Minecraft source code, and now I think I get how to use EntityTracker. From forge.fml.common.registry.EntityRegistry I get that Forge is automatically adds entity to tracking when I register my custom mod, but it seems to me that EntityTracker isn't going to help me with syncing my custom fields from server to the client. EntityTrackerEntry in net.minecraft.entity have a lot of fields, but it looks like it syncing only entity position and rotation. Is that right? Is EntityTracker is responsible only for syncing entity's position and rotation? If so, I can't use EntityTracker to sync my custom data. Correct me if I'm wrong.

 

Also, I decided to add writeEntityToNBT and corresponding reading method to my CameraEntity, and it looks like those custom fields are ok on the server side, but on client side, they're always equals to default, even if I save and quit and then relog again. What am I doing wrong? Fixed by implementing IEntityAdditionSpawnData, but it still doesn't sync while I'm online.

 

This is how my Message looks like now:

 

public class CameraAttributesUpdate implements IMessage
{
protected int id;
protected float speed;
protected float accelerationRate;
protected float accelerationMax;
protected boolean canFly; 

public CameraAttributesUpdate() {}

public CameraAttributesUpdate(int eid, float speeed, float rate, float max, boolean fly)
{
	id = eid;
	speed = speeed;
	accelerationRate = rate;
	accelerationMax = max;
	canFly = fly;
}

@Override
public void fromBytes(ByteBuf buf)
{
	id = buf.readInt();
	speed = buf.readFloat();
	accelerationRate = buf.readFloat();
	accelerationMax = buf.readFloat();
	canFly = buf.readBoolean();
}

@Override
public void toBytes(ByteBuf buf)
{
	buf.writeInt(id);
	buf.writeFloat(speed);
	buf.writeFloat(accelerationRate);
	buf.writeFloat(accelerationMax);
	buf.writeBoolean(canFly);
}

public static class Handler implements IMessageHandler<CameraAttributesUpdate, IMessage>
{
	@Override
	public IMessage onMessage(final CameraAttributesUpdate message, final MessageContext ctx)
	{
		IThreadListener world = (WorldServer)ctx.getServerHandler().playerEntity.worldObj;

		world.addScheduledTask(new Runnable()
		{
			@Override
			public void run()
			{
				WorldServer world = (WorldServer)ctx.getServerHandler().playerEntity.worldObj;
				Entity entity = world.getEntityByID(message.id);
				CameraEntity camera = (CameraEntity)entity;

				camera.setConfiguration(message.speed, message.accelerationRate, message.accelerationMax, message.canFly);

				world.getEntityTracker().func_151248_b(camera, Mod.channel.getPacketFrom(message));
			}
		});

		return null;
	}
}
}

 

By the way, my mod is targeted towards single-player experience, so I don't really need to do all those validation checks.

 

P.S.: Sorry if I'm being ignorant, but I just not familiar enough with minecraft and forge code base.  :(

Blockbuster – simple machinimas and cinematics for Minecraft
Link to comment
Share on other sites

Skype: ernio333

TeamViewer if you can so I can quickly explain code + examples.

 

That is if earlier post are not enough (they should be, there is not much really to say, but only to show some things).

 

And lol me - "dude, trust me, there are so many people who would know what to do with all that I wrote"

it was supposed to be "who wouldn't" - I messed up, actually your code is (almost) good.

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

Link to comment
Share on other sites

Thanks to Ernio who helped me to overcome this problem, so there's report:

 

To create a GUI that changes state of the custom entity you have to:

 

1. Create a GUI class that extends GuiScreen (look at GuiCommandBlock for example for click)

2. In your entity's processInteract method on client side (world.isRemote == true) open Gui either via openGui (but first setup IGuiHandler) or via Minecraft.getMinecraft().displayGuiScreen

3. On Gui exit (when you trigger mc.displayGuiScreen(null)), send the message with new values to server side via SimpleNetworkWrapper (as in my second post, see GuiCamera#saveAndExit)

4. On server side in message handler (see CameraAttributesUpdate.Handler#onMessage), you invoke a setter (see Ernio's post where he shows his SNW#updateTracker) which in turn sends another message via your SimpleNetworkWrapper's wrapper to the client

5. On client side you should have another message handler that updates the client entity

 

There's updated SNW#updateTrackers of Ernio:

 

    public static void updateTrackers(Entity entity, IMessage message)
    {
        EntityTracker et = ((WorldServer) entity.worldObj).getEntityTracker();

        for (EntityPlayer player : et.getTrackingPlayers(entity))
        {
            DISPATCHER.sendTo(message, (EntityPlayerMP) player);
        }
    }

 

I hope this gonna be helpful in the future :)

 

P.S.: See also Ernio's links and suggestions for more information

Blockbuster – simple machinimas and cinematics for Minecraft
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.