Jump to content

  • Curse Sites
Become a Premium Member! Help
Latest News Article

[1.6.4][Forge][Unsolved] TileEntity of GUI not saved

tileentity gui 1.6.4 not saved

  • Please log in to reply
18 replies to this topic

#1

Spirit_Tracks

Posted 15 April 2014 - 05:20 PM

Hello everyone,

I currently try developing a speaker that is placed adjacent to station platforms that will - upon redstone input - send out a sound signal based on the data it is fed in its GUI. But I ran into following problems:

1) The TileEntity is not being saved.
2) Every block in the world uses the same data -> No specifically configurable block.

Maybe I went full retard and left something (of high importance) out, or I just failed at searching through all tutorials I could find...

Here are the .java files:

TileEntityBlockSpeaker: http://pastebin.com/7CMhExVH
GuiSpeaker: http://pastebin.com/Ga4g0Z8Z
ContainerBlockSpeaker: http://pastebin.com/2hKBU3VE
BlockSpeaker: http://pastebin.com/C6z3z7Jg
CommonProxy: http://pastebin.com/8UJd4vNZ
Main: http://pastebin.com/L5pGZcnb

Thank you for going through all these files, any help is highly appreciated!
If there's need for any other file, let me know.

Register or log in to remove.

#2

YoWazzup
    YoWazzup

    Nether Resident

  • Members
  • 2025 posts
  • Location: Heerhugowaard, The NETHERlands
  • Minecraft: DUDEiFAIL
  • Xbox:XxSeeYourMomxX

Posted 15 April 2014 - 07:15 PM

Not sure if this works.

In you Block class in the onBlockActivated method change TileEntity to your TileEntity class.
Posted Image
Made By: YoWazzup, me.

#3

Spirit_Tracks

Posted 15 April 2014 - 07:57 PM

View PostYoWazzup, on 15 April 2014 - 07:15 PM, said:

Not sure if this works.

In you Block class in the onBlockActivated method change TileEntity to your TileEntity class.

Was worth a try, but sadly no/same result... Any further ideas..? I bet I missed something out, but I don't get what...

#4

freezzerrr

Posted 15 April 2014 - 08:00 PM

Perhaps you can look at a few of my tuts.
I think you need NBT's for your first problem.
And what does 2) have to do then? Like what result does it have
Posted Image

#5

YoWazzup
    YoWazzup

    Nether Resident

  • Members
  • 2025 posts
  • Location: Heerhugowaard, The NETHERlands
  • Minecraft: DUDEiFAIL
  • Xbox:XxSeeYourMomxX

Posted 15 April 2014 - 08:05 PM

View Postfreezzerrr, on 15 April 2014 - 08:00 PM, said:

Perhaps you can look at a few of my tuts.
I think you need NBT's for your first problem.
And what does 2) have to do then? Like what result does it have

NBT's are important yes, they save the data/ items which are inside the entity
Posted Image
Made By: YoWazzup, me.

#6

Spirit_Tracks

Posted 15 April 2014 - 08:14 PM

View Postfreezzerrr, on 15 April 2014 - 08:00 PM, said:

Perhaps you can look at a few of my tuts.
I think you need NBT's for your first problem.
And what does 2) have to do then? Like what result does it have

Firstof, nice website, I love the way you organized the tutorials. I'll have a deeper insight later again, but for now I didn't find something.
Anyhow, the 2) means, that upon placing SpeakerBlock A and changing its data inside the GUI, all other SpeakerBlocks placed have the same. And changing the another one results that all the other SpeakerBlocks have the same data.. But every SpeakerBlock should actually have their own data.. So.. well..

#7

Spirit_Tracks

Posted 16 April 2014 - 12:47 PM

*Bump*

So, I thought about adding the method onGuiClosed() into the GuiSpeaker.java that then refers to saving the NBT data.
Sadly I can't find a way to do this..

#8

coolAlias

Posted 16 April 2014 - 01:45 PM

The reason every block has the same values is because you made your tile entity class fields static:
public static int action;
public static int station;

public static String actionString;
public static String stationString;
Oops. Static means "only one instance of this field for the class, no matter how many class objects exist". You don't want that.

Also, the tile entity data packet is for synchronizing the client with data from the server, not the other way around.

When opening a Gui, you always have the position at which it was opened. In the case of block-activated GUIs, these are the block coordinates, which you can use to get the tile entity and pass to your Gui's constructor. This gives you access to the TileEntity from within your Gui, and I recommend you use it only for retrieving data such as the TileEntity's x/y/z position. You can use this from your button press method to send a custom packet to the server, which should contain the following:

1. x/y/z coordinates of the tile entity that opened the Gui
2. id of the button pressed

When you receive the packet on the server, switch through the button ids to process what you want, and get the server tile entity from the world to set the appropriate values. Finally, mark the block at those coordinates for an update (World method) which will automatically send a description packet to the client, updating it with the data that you set on the server.

May sound complicated, but it just comes down to only allowing the server to set data, and the client only to read data.

Posted Image

Posted Image


#9

Spirit_Tracks

Posted 16 April 2014 - 06:43 PM

View PostcoolAlias, on 16 April 2014 - 01:45 PM, said:

The reason every block has the same values is because you made your tile entity class fields static:
public static int action;
public static int station;

public static String actionString;
public static String stationString;
Oops. Static means "only one instance of this field for the class, no matter how many class objects exist". You don't want that.

Also, the tile entity data packet is for synchronizing the client with data from the server, not the other way around.

When opening a Gui, you always have the position at which it was opened. In the case of block-activated GUIs, these are the block coordinates, which you can use to get the tile entity and pass to your Gui's constructor. This gives you access to the TileEntity from within your Gui, and I recommend you use it only for retrieving data such as the TileEntity's x/y/z position. You can use this from your button press method to send a custom packet to the server, which should contain the following:

1. x/y/z coordinates of the tile entity that opened the Gui
2. id of the button pressed

When you receive the packet on the server, switch through the button ids to process what you want, and get the server tile entity from the world to set the appropriate values. Finally, mark the block at those coordinates for an update (World method) which will automatically send a description packet to the client, updating it with the data that you set on the server.

May sound complicated, but it just comes down to only allowing the server to set data, and the client only to read data.

Okay, firstof thank you very much, your response helped me already quite a few steps.
The packet data and sending of the packet have been added to the onGuiClosed method (to save some RAM, as sending the packet whenever someone clicks on that would be a bit memory expensive, I guess), and the TileEntity now only has the save data for the two ints "station" and "action".
But now, I ran out of ideas how I could get the remaining stuff working.. I'll need to receive and handle the packet in my PacketHandler and forward it to the TileEntity, right? If it is so, how could I retrieve the data again, whenever the GUI is opened? Or is it retrieved immediately with the opening of the GUI?

Once again, the updated GuiSpeaker.java: http://pastebin.com/iYWDf4Tg
My TileEntityBlockSpeaker.java: http://pastebin.com/Z95we8qm
And the PacketHandler: http://pastebin.com/SiwL1mwU

Greetings.

#10

coolAlias

Posted 16 April 2014 - 10:53 PM

View PostSpirit_Tracks, on 16 April 2014 - 06:43 PM, said:

Okay, firstof thank you very much, your response helped me already quite a few steps.
The packet data and sending of the packet have been added to the onGuiClosed method (to save some RAM, as sending the packet whenever someone clicks on that would be a bit memory expensive, I guess), and the TileEntity now only has the save data for the two ints "station" and "action".
But now, I ran out of ideas how I could get the remaining stuff working.. I'll need to receive and handle the packet in my PacketHandler and forward it to the TileEntity, right? If it is so, how could I retrieve the data again, whenever the GUI is opened? Or is it retrieved immediately with the opening of the GUI?

Once again, the updated GuiSpeaker.java: http://pastebin.com/iYWDf4Tg
My TileEntityBlockSpeaker.java: http://pastebin.com/Z95we8qm
And the PacketHandler: http://pastebin.com/SiwL1mwU

Greetings.
It's not that expensive to send a packet; I recommend doing it whenever someone clicks a button. Just think, in regular Minecraft, packets are already sent every single time you click, as well as tons of packets sending data to you about the various states of entities and blocks in the world, etc. One more packet in the mix isn't going to be noticed, especially if you keep it lightweight (as in just a few bits of data).

public Object getServerGuiElement(int ID, EntityPlayer player, World world, int x, int y, int z) {
// see the "x, y, z" parameters? Those were passed to you from your Block coordinates!
// you can use those to get the TileEntity right here and pass it to your Container or Gui
// note that this won't work if you open the gui from somewhere other than the block,
// for example if the "x, y, z" coordinates were the player position instead
TileEntity te = world.getTileEntity(x, y, z);

if (ID == Main.GUI_SPEAKER && te instanceof TileEntityBlockSpeaker)
// you may not need it in your Container, but this is how you can pass your tile entity:
		return new ContainerBlockSpeaker((TileEntityBlockSpeaker) te);
// I recommend doing so for your Gui, at least
else
	   return null;
}

In your Gui:
// your current code:
public GuiSpeaker(Object mod, int GuiID, World world, int x, int y, int z) {
// this is just a local variable, it will be gone once construction is finished.
TileEntity tileEntity = world.getBlockTileEntity(x, y, z);			  
// errr...this isn't going to do anything:
tileEntity.getDescriptionPacket();
}

// I recommend changing it; first add an extra local variable:
private final TileEntityBlockSpeaker speaker;

// and change your constructor:
public GuiSpeaker(TileEntityBlockSpeaker speaker) {
// now you can store your tile entity and use it anywhere you want in your Gui code:
this.speaker = speaker;
}

// for instance, now when you press a button, you have access to the tile entity:
protected void actionPerformed(GuiButton guiButton) {

// now you can send a packet to the server with the following information:
guiButton // the button pressed
// the coordinates of the tile entity, so you can get the correct tile entity on the server
// when you receive the packet, and set its data according to the button
this.speaker.xCoord
this.speaker.yCoord
this.speaker.zCoord

// you could make a method to help you send the packet even:
YourPacketHandler.sendSpeakerPacket(this.speaker, guiButton);
// that method will be a static void method in your packet handler that handles
// creating, writing, and sending the packet; be sure to give it a unique ID so you
// can distinguish it from your OPEN_GUI packet.

}

In your packet handler, when you receive the packet, it should look something like this:
private void handleSpeakerUpdatePacket(Packet250CustomPayload packet, EntityPlayer player, DataInputStream in) {
int x, y, z, button;
// read all that data in from the packet

TileEntity te = player.worldObj.getBlockTileEntity(x, y, z);
// double check that it's really your tile entity:
if (te instanceof TileEntityBlockSpeaker) {
// great, everything is golden and you now have an instance of your tile entity, on the server!
// now you can either handle the button actions directly here, or pass it on to your tile entity:
((TileEntityBlockSpeaker) te).buttonPressed(button);

// either way, when you finish, mark the block for an update so it will notify the client of changes
// automatically, since you overrode getDescriptionPacket and onDataPacket correctly
// the following line could be used in your TileEntity, but not from the packet handler:
this.worldObj.markBlockForUpdate(xCoord, yCoord, zCoord);
// you can modify it to work from the packet handler, too, though, depending on how you want to
// handle the button pressed step.
}
}

Posted Image

Posted Image


#11

Spirit_Tracks

Posted 17 April 2014 - 09:57 AM

View PostcoolAlias, on 16 April 2014 - 10:53 PM, said:

It's not that expensive to send a packet; I recommend doing it whenever someone clicks a button. Just think, in regular Minecraft, packets are already sent every single time you click, as well as tons of packets sending data to you about the various states of entities and blocks in the world, etc. One more packet in the mix isn't going to be noticed, especially if you keep it lightweight (as in just a few bits of data).

public Object getServerGuiElement(int ID, EntityPlayer player, World world, int x, int y, int z) {
// see the "x, y, z" parameters? Those were passed to you from your Block coordinates!
// you can use those to get the TileEntity right here and pass it to your Container or Gui
// note that this won't work if you open the gui from somewhere other than the block,
// for example if the "x, y, z" coordinates were the player position instead
TileEntity te = world.getTileEntity(x, y, z);

if (ID == Main.GUI_SPEAKER && te instanceof TileEntityBlockSpeaker)
// you may not need it in your Container, but this is how you can pass your tile entity:
return new ContainerBlockSpeaker((TileEntityBlockSpeaker) te);
// I recommend doing so for your Gui, at least
else
return null;
}

In your Gui:
// your current code:
public GuiSpeaker(Object mod, int GuiID, World world, int x, int y, int z) {
// this is just a local variable, it will be gone once construction is finished.
TileEntity tileEntity = world.getBlockTileEntity(x, y, z);
// errr...this isn't going to do anything:
tileEntity.getDescriptionPacket();
}

// I recommend changing it; first add an extra local variable:
private final TileEntityBlockSpeaker speaker;

// and change your constructor:
public GuiSpeaker(TileEntityBlockSpeaker speaker) {
// now you can store your tile entity and use it anywhere you want in your Gui code:
this.speaker = speaker;
}

// for instance, now when you press a button, you have access to the tile entity:
protected void actionPerformed(GuiButton guiButton) {

// now you can send a packet to the server with the following information:
guiButton // the button pressed
// the coordinates of the tile entity, so you can get the correct tile entity on the server
// when you receive the packet, and set its data according to the button
this.speaker.xCoord
this.speaker.yCoord
this.speaker.zCoord

// you could make a method to help you send the packet even:
YourPacketHandler.sendSpeakerPacket(this.speaker, guiButton);
// that method will be a static void method in your packet handler that handles
// creating, writing, and sending the packet; be sure to give it a unique ID so you
// can distinguish it from your OPEN_GUI packet.

}

In your packet handler, when you receive the packet, it should look something like this:
private void handleSpeakerUpdatePacket(Packet250CustomPayload packet, EntityPlayer player, DataInputStream in) {
int x, y, z, button;
// read all that data in from the packet

TileEntity te = player.worldObj.getBlockTileEntity(x, y, z);
// double check that it's really your tile entity:
if (te instanceof TileEntityBlockSpeaker) {
// great, everything is golden and you now have an instance of your tile entity, on the server!
// now you can either handle the button actions directly here, or pass it on to your tile entity:
((TileEntityBlockSpeaker) te).buttonPressed(button);

// either way, when you finish, mark the block for an update so it will notify the client of changes
// automatically, since you overrode getDescriptionPacket and onDataPacket correctly
// the following line could be used in your TileEntity, but not from the packet handler:
this.worldObj.markBlockForUpdate(xCoord, yCoord, zCoord);
// you can modify it to work from the packet handler, too, though, depending on how you want to
// handle the button pressed step.
}
}

So, once more, thank you very much! [I even understand that packet handling stuff way better than before...]
Now I did as you told me and added everything in. (Hopefully correct, though. ^^)
The only problem left to me is now: No matter what I do, the NBT data of TileEntityBlockSpeaker won't save... Any ideas?

The updated data:
TileEntityBlockSpeaker: http://pastebin.com/diqasYCY
GuiSpeaker: http://pastebin.com/LGuhFd8c
ModPacketHandler: http://pastebin.com/JAYkNZ1m
CommonProxy: http://pastebin.com/akz35jyS

[I hope everything was added as you meaned..]

Greetings!

#12

coolAlias

Posted 17 April 2014 - 10:23 AM

View PostSpirit_Tracks, on 17 April 2014 - 09:57 AM, said:

So, once more, thank you very much! [I even understand that packet handling stuff way better than before...]
Now I did as you told me and added everything in. (Hopefully correct, though. ^^)
The only problem left to me is now: No matter what I do, the NBT data of TileEntityBlockSpeaker won't save... Any ideas?

The updated data:
TileEntityBlockSpeaker: http://pastebin.com/diqasYCY
GuiSpeaker: http://pastebin.com/LGuhFd8c
ModPacketHandler: http://pastebin.com/JAYkNZ1m
CommonProxy: http://pastebin.com/akz35jyS

[I hope everything was added as you meaned..]

Greetings!
By "won't save", do you mean when you re-load the world, the data is not the same, or the tile entity data is not showing up in your Gui while you are using it? Are you at least seeing all your console messages for reading and writing NBT? You should be.

If it's the latter case, then it's probably because you never tell the client about the new state of the tile entity after a button is pressed. You forgot to add the "worldObj.markBlockForUpdate(xCoord, yCoord, zCoord);" at the end of your tile entity's "buttonPressed" method.

This will send an update packet automatically to the client, so you will now have the correct data on the client as well; however, since you update the buttons before the new data has a chance to arrive, you will likely always be one behind. Luckily, you've stored the tile entity's previous action state in local fields, so during the draw screen method, you can do a quick check:
@Override
public void drawScreen(int x, int y, float f) {
if (this.action != this.tileEntitySpeaker.getAction()) {
// uh oh, out of sync! refresh your buttons and you should be ok

// don't forget to store the new value for next time:
this.action = this.tileEntitySpeaker.getAction();
} else if (this.station != this.tileEntitySpeaker.getStation()) {
// same as above, but for the station
}
}
Another trick you can do is to increment action or station when the button is pressed in the Gui, in anticipation of its new value, since the value isn't absolutely critical to keep exact. This way you can update the button list immediately when a button is pressed, with a good chance that the value will be correct when the packets all get processed.

Side Note: Do you know about modular division? Try this:
action = (action + 1) % 4;
// always returns a value between 0 and 3, in incremental order
Modular division returns the remainder of a value divided by some number, so % (mod) 4 returns (value) / 4 's remainder.
0 % 4 = 0;
1 % 4 = 1;
2 % 4 = 2;
3 % 4 = 3;
4 % 4 = 0;

Pretty handy for many many situations, this being one of them :P

Posted Image

Posted Image


#13

Spirit_Tracks

Posted 17 April 2014 - 11:02 AM

View PostcoolAlias, on 17 April 2014 - 10:23 AM, said:

By "won't save", do you mean when you re-load the world, the data is not the same, or the tile entity data is not showing up in your Gui while you are using it? Are you at least seeing all your console messages for reading and writing NBT? You should be.

- snip -

Yes, it rather is the case that upon relog, the data has been reset to the normal case(s).
E.g.: I set the data from "Arrival" and "Heiwa no Toshi" to "Gothrough" and "Spawn (Central)", after relogging, the data is reset to "Arrival" and "Heiwa no Toshi". I already played around with the readFromNBT and writeToNBT method as they were LEGOs, but obviously with no significant outcome.. ^^'

And well, I kinda know modular division, but I leave it out because it breaks my head. O.o

Greetings!

#14

Spirit_Tracks

Posted 18 April 2014 - 11:01 AM

So.. Anyone an idea why the NBT is not saved?

#15

coolAlias

Posted 18 April 2014 - 01:47 PM

View PostSpirit_Tracks, on 18 April 2014 - 11:01 AM, said:

So.. Anyone an idea why the NBT is not saved?
Do the debugging messages that you put in the methods print to the console?

Do these print when you press "esc" in game to go to the options menu?
System.out.println("Saved action " + action);
System.out.println("Saved station " + station);

If you quit and load the same game, do these print to the console? Do they have the same values you just saw before you quit?
System.out.println("Loaded action " + action);
System.out.println("Loaded station " + station);

If that doesn't help you figure it out: run your debug client, load the world, mess around with your tile entity, then exit to the main menu only, and load the game again. Then exit and copy / paste the contents of your console log here.

Posted Image

Posted Image


#16

Spirit_Tracks

Posted 18 April 2014 - 02:47 PM

View PostcoolAlias, on 18 April 2014 - 01:47 PM, said:

Do the debugging messages that you put in the methods print to the console?

Do these print when you press "esc" in game to go to the options menu?
System.out.println("Saved action " + action);
System.out.println("Saved station " + station);

If you quit and load the same game, do these print to the console? Do they have the same values you just saw before you quit?
System.out.println("Loaded action " + action);
System.out.println("Loaded station " + station);

If that doesn't help you figure it out: run your debug client, load the world, mess around with your tile entity, then exit to the main menu only, and load the game again. Then exit and copy / paste the contents of your console log here.

Yes, I added these messages. They pop up three times on game load, and each time something has been edited. But when the game is closed/ the world is left, they don't appear at all.

The debug client:

http://pastebin.com/0y4V4UzM

Whenever I open the Gui and click something, the "TRYING TO SEND THE PACKET!" and "SENDING THE SPEAKER PACKET!" messages pop up, after closing, for each button-click I made, the "
FORWARDING PACKET TO SPEAKER PACKET HANDLER!", "HANDLING THE SPEAKER PACKET!" and "RECEIVED A BUTTON ID!" messages show up. Also the action/ station integer is displayed, depending on what has been clicked on.
After closing the GUI, the messages indicating "Saved action/ station INT" show up, followed by a "onDataPacket METHOD HAS BEEN CALLED", finished with "Loaded action/ station INT".

I have a very slight guess of the problem and I'll let you know if I was right...

Happy easter, everyone!


EDIT: Nope, no result.. <.<'

#17

Spirit_Tracks

Posted 18 April 2014 - 09:11 PM

So, I just (accidentially) found out, that upon chunkreload, the NBT is being saved properly. Also, after saving the NBT like this, I can leave and re-join the world without fearing data loss.
Example: I change the value from "Departure" to "Arrival", leave the chunk, reload the chunk and leave then. I rejoin and find the data correctly saved.
The remaining problem is that the NBT is not being saved whenever I simply leave the world.

#18

Spirit_Tracks

Posted 19 April 2014 - 11:51 AM

Err... Bump?
Still the same problem: NBT resets when the world is left and rejoined, even though it actually is being shown as saved.

#19

Spirit_Tracks

Posted Today, 11:36 AM

*Bump*