Jump to content

[Solved] writeToNBT does not get called for the client TileEntity, only server


Lycanus Darkbinder

Recommended Posts

I have TileEntity with a field called isRemote that gets set in onBlockPlacedBy() based on world.isRemote.

 

When a block is placed, two TileEntity are created and the fields set to true and false. When I press ESC, writeToNBT() gets called only once and isRemote is false indicating this is the server TileEntity.

 

It seems that the TileEntity where isRemote is true never gets a call to writeToNBT(). I print to the console the value of isRemote every time the function is called and it never prints "true".

Link to comment
Share on other sites

I have TileEntity with a field called isRemote that gets set in onBlockPlacedBy() based on world.isRemote.

Why that?
TileEntity

has a field

worldObj

. Use that.

 

Well for one, worldObj is not available while in the constructor. All properties from the TileEntity super class are NULL while in the constructor. My constructor accepts a boolean.

 

When a block is placed, two TileEntity are created and the fields set to true and false. When I press ESC, writeToNBT() gets called only once and isRemote is false indicating this is the server TileEntity.

Of course. Why should the client save any NBT data? The server is the side that manages the game, saves the world, etc. The client only has dummy values for everything.

Think of a dedicated server (connect via IP): Where should the client save the world to if it would?

 

On a singleplayer world, the chunkcache attempts to load the TileEntity for the client but fails so it builds a new one.

 

After the server TileEntity is loaded with readFromNBT, the call stack looks like this:

 

TileEntitySmartLight.<init>(World) line: 48   

BlockSmartLight.createTileEntity(World, int) line: 375   

Chunk.getChunkBlockTileEntity(int, int, int) line: 995  

ChunkCache.getBlockTileEntity(int, int, int) line: 115   

WorldRenderer.updateRenderer() line: 208   

 

The bolded part is where it falls down because the TileEntity is not saved so it builds a brand new one. Here's the piece from Chunk.getChunkBlockTileEntity

            if (tileentity == null)
            {
                tileentity = Block.blocksList[l].createTileEntity(this.worldObj, meta);
                this.worldObj.setBlockTileEntity(this.xPosition * 16 + par1, par2, this.zPosition * 16 + par3, tileentity);
            }

 

In the above code, this.worldObj is a WorldClient. Why is the client expecting chunkcache to have a TileEntity if, as you say, they're not supposed to be saved?

 

 

More importantly, could you please point me in the direction of some up-to-date documentation on how people save and load TileEntities? My TileEntity isn't even that fancy but it seems like it's a lot more work than advertised in the wiki.

Link to comment
Share on other sites

More importantly, could you please point me in the direction of some up-to-date documentation on how people save and load TileEntities? My TileEntity isn't even that fancy but it seems like it's a lot more work than advertised in the wiki.

It seems like you are doing or understanding something very wrong here.

To save a TE put the data you need into the NBTTagCompound passed to writeToNBT and read the data back in readFromNBT. Done.

If you need the data on the client, too, you need to sync it with packets.

 

Let me explain what's happening so you can better understand my issue:

 

1. Click "Single Player"

2. Choose a world and click "Play Selected World"

3. createAndLoadEntity() gets called (TileEntity.java)

4. This calls the default constructor of my TileEntitySmartLight (only here for breakpoint purposes)

5. Then my readFromNBT() gets called which indicates the server is requesting this TileEntity

6. Then WorldRenderer.updateRenderer() checks the chunkcache for a client side TileEntity

7. ChunkCache.getBlockTileEntity() calls chunk.getTileEntity()

8. This leads to BlockSmartLight.createTileEntity() indicating we're creating a client TileEntity

9. BlockSmartLight.getLightValue() gets called which has a parameter of IBlockAccess

10. IBlockAccess.getBlockTileEntity() returns the client TileEntity from #8, not the server TileEntity from #5

 

So, how do I synchronize the client TileEntity that getLightValue() is asking for when this is happening at world load and there is no function override between steps #5 and steps #6 when the game requests the two TileEntities?

 

It wouldn't be a problem if getLightValue() didn't use IBlockAccess but that always gets a client version of the TileEntity, it never loads a server version.

 

Any advice would be appreciated. I don't see how packets can help the situation but since I've not seen any good documentation on packets I can't really comment.

 

 

PS:

 

As for the constructor, I kept getting console messages "Ignoring entity with TileEntitySmartLight" until I added the constructor. Once I put it in (even an empty one) the messages stopped.

Link to comment
Share on other sites

You need to send packets in order for the client to get the TE from the server on entering the world, check out this thread, it may be of interest to you:

http://www.minecraftforum.net/topic/1969772-solvedhow-to-load-data-from-tileentity-before-activating-its-block/

 

Thanks for that. It's still not clicking in my brain how that all works.

 

createTileEntity() has to be called for the client because it doesn't exist yet. This is where my disconnect comes in. I don't understand how those packet functions tell the server to give me a copy of it's TileEntity so I can stuff the data into the client version.

 

TileEntity.getDescriptionPacket() only gets called for server TileEntities (worldObj.isRemote == false) and TileEntity.onDataPacket() never got called at all.

 

edit: oops, I was returning null from getDescriptionPacket() instead of my actual packet...

 

I'm sure it's one of those "why didn't I think of that" moments when it clicks but right now it's just eluding me.

Link to comment
Share on other sites

Thanks for the help everyone, this did the trick:

 

   @Override
    public Packet getDescriptionPacket()
    {
      // The server calls this function and if we return the NBT filled
      //  with data, this.onDataPacket() will be called for the client with
      //  the server's NBT info
      
      NBTTagCompound tag = new NBTTagCompound();
      this.writeToNBT(tag);
      
        return new Packet132TileEntityData(this.xCoord, this.yCoord, this.zCoord, 1, tag);
    }
   
   @Override
    public void onDataPacket(INetworkManager net, Packet132TileEntityData pkt)
    {
      //  PKT contains an NBTTagCompound with the server's TileEntity info.
      //  We can parse that into our local variables so we stay in sync when our various
      //  "get" methods are called to access TileEntity data.
      
      this.readFromNBT(pkt.customParam1);
    }

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.