Jump to content

[1.16.5] Write/read custom class to/from NBT


J3ramy

Recommended Posts

Hey guys,

I am trying to save a custom class in my tile entity`s NBT data.

I think I figured out how to save it, but I don't know how to load it.

 

Ingame Server class (Class that should be saved to nbt)

Spoiler
public class Server implements INBTSerializable {

    public enum ServerType{
        CUSTOM
    }

    private boolean isOn = false;
    private ServerType serverType;
    private String ip;
    private BlockPos pos;
    private Database db;

    public Server(ServerType serverType, String ip, BlockPos pos, Database db){
        this.serverType = serverType;
        this.ip = ip;
        this.pos = pos;
        this.db = db;
    }
  
    @Override
    public INBT serializeNBT() {
        CompoundNBT nbt = new CompoundNBT();

        nbt.putBoolean("isOn", this.isOn);
        nbt.putInt("serverType", this.serverType.ordinal());
        nbt.putString("ip", this.ip);
        nbt.put("pos", NBTUtil.writeBlockPos(this.pos));
        nbt.put("db", this.db.serializeNBT());

        return nbt;
    }

    @Override
    public void deserializeNBT(INBT compound) {
        CompoundNBT nbt = (CompoundNBT) compound;

        this.isOn = nbt.getBoolean("isOn");
        this.serverType = ServerType.values()[nbt.getInt("")];
        this.ip = nbt.getString("name");
        this.pos = NBTUtil.readBlockPos(nbt);
        this.db = (Database) nbt.get("db");
    }

 

 

My tile entity class

Spoiler
public class ServerTile extends TileEntity {

    private Server server;

    public ServerTile(TileEntityType<?> tileEntityType) {
        super(tileEntityType);
    }

    public ServerTile(){
        this(ModTileEntities.SERVER_TILE.get());
    }


    @Override
    public void read(BlockState state, CompoundNBT nbt) {

        this.server = ...

        super.read(state, nbt);
    }

    @Override
    public CompoundNBT write(CompoundNBT nbt) {
        nbt.put("server", this.server.serializeNBT());

        return super.write(nbt);
    }

 

 

Thanks for your help :)

Link to comment
Share on other sites

you can create a constructor with a CompoundNBT as parameter, inside the constructor you read the data and set the fields
in this case your fields can be final (if you want that they are), then you can simply create a new instance
the other option would be to add an empty constructor and then call Server#serializeNBT

i personally prefer the first option but you could choose the option you want
i would also recommend you to specify the INBT type in the type arguments of INBTSerializable

last but not least i would recommend you to update to 1.18 since 1.16.5 will fall out of support in the next 2-3 days

Edited by Luis_ST
Link to comment
Share on other sites

Quote

This doesn't look right.

Thanks. I forgot to add the key ^^

 

Quote

INBTSerializable only really works if you have a no-argument constructor, so you can create the instance. I would recommend to instead make a static factory method and not use INBTSerializable.

Do you have an example?

Link to comment
Share on other sites

5 minutes ago, J3ramy said:

Do you have an example?

Spoiler
	public void save() {
		Foo foo = new Foo(10);
		CompoundTag tag = foo.serializeNBT();
		// do stuff
	}
	
	public void load(CompoundTag tag) {
		Foo foo = new Foo();
		foo.deserializeNBT(tag);
	}
	
	public class Foo implements INBTSerializable<CompoundTag> {
		
		protected int data;
		
		public Foo() {
			
		}
		
		public Foo(int data) {
			this.data = data;
		}
		
		@Override
		public CompoundTag serializeNBT() {
			CompoundTag tag = new CompoundTag();
			tag.putInt("data", this.data);
			return tag;
		}

		@Override
		public void deserializeNBT(CompoundTag tag) {
			this.data = tag.getInt("data");
		}
		
	}

this is a simply example, but i would recommend to use a factory method or a constructor instead

Note: create with mojang mappings

Link to comment
Share on other sites

Thank you.

 

I decided for the constructor way and changed the class to

Spoiler

public class Server {

    public enum ServerType{
        CUSTOM
    }

    private boolean isOn = false;
    private boolean isSet = false;
    private final ServerType serverType;
    private final String ip;
    private final BlockPos pos;
    private final Database db;

    public Server(CompoundNBT nbt){
        this.isOn = nbt.getBoolean("isOn");
        this.isSet = nbt.getBoolean("isSet");
        this.serverType = ServerType.valueOf(nbt.getString("serverType"));
        this.ip = nbt.getString("name");
        this.pos = NBTUtil.readBlockPos(nbt);
        this.db = (Database) nbt.get("db");
    }

}

 

But how am I supposed to save/load this in the tile entity write and read method?

 

EDIT:

I created a getData method in Server which returns a CompoundNBT containing the class data.

Now, I do this in my tile entity:

Spoiler
 @Override
    public void read(BlockState state, CompoundNBT nbt) {

        this.server = new Server(nbt);

        super.read(state, nbt);
    }

    @Override
    public CompoundNBT write(CompoundNBT nbt) {

        nbt.put("server", this.server.getData());

        return super.write(nbt);
    }

 

 

Edited by J3ramy
Link to comment
Share on other sites

   public void read(BlockState state, CompoundNBT nbt) {

        this.server = new Server(nbt.getCompound("server");

        super.read(state, nbt);
    }

You need to retrieve the subtag of the tile's nbt. The place where you put it.

 

The read/write happens automatically. Except, if you change data for the tile, you need to call setChanged() so minecraft knows it needs to save it.

Edited by warjort

Boilerplate:

If you don't post your logs/debug.log we can't help you. For curseforge you need to enable the forge debug.log in its minecraft settings. You should also post your crash report if you have one.

If there is no error in the log file and you don't have a crash report then post the launcher_log.txt from the minecraft folder. Again for curseforge this will be in your curseforge/minecraft/Install

Large files should be posted to a file sharing site like https://gist.github.com  You should also read the support forum sticky post.

Link to comment
Share on other sites

Quote

Except, if you change data for the tile, you need to call setChanged() so minecraft knows it needs to save it.

This also notifies neighbouring blocks they may need to do stuff.

e.g. if you output a redstone signal any redstone would recalculate its signal strength.

Edited by warjort

Boilerplate:

If you don't post your logs/debug.log we can't help you. For curseforge you need to enable the forge debug.log in its minecraft settings. You should also post your crash report if you have one.

If there is no error in the log file and you don't have a crash report then post the launcher_log.txt from the minecraft folder. Again for curseforge this will be in your curseforge/minecraft/Install

Large files should be posted to a file sharing site like https://gist.github.com  You should also read the support forum sticky post.

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.



×
×
  • Create New...

Important Information

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