minecraft-mod-tutorials/player-read-write-nbt.java

96 lines
4.3 KiB
Java
Raw Normal View History

package org.example.examplemod.mixin;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.UUID;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.nbt.NbtCompound;
import net.minecraft.nbt.NbtElement;
import net.minecraft.nbt.NbtList;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Unique;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
@Mixin(PlayerEntity.class)
public abstract class PlayerEntityMixin {
@Unique
public List<MyObject> myList = new ArrayList<>();
// I recommend you read writeMyDataToNbt first before trying to understand readCustomDataFromNbt
@Inject(method = "readCustomDataFromNbt", at = @At("TAIL"))
public void readMyDataFromNbt(NbtCompound playerNbt, CallbackInfo ci) {
// read our serialized list
NbtList myListNbt = playerNbt.getList("MyList", NbtElement.COMPOUND_TYPE);
// iterate over its items
for (int i = 0; i < myListNbt.size(); i++) {
// construct the object we need to deserialize
MyObject myObject = new MyObject();
// read the serialized object
NbtCompound myObjectNbt = myListNbt.getCompound(i);
// read properties that are supported and write them to our object
myObject.mySupportedObject = myObjectNbt.getInt("MySupportedObject");
// if mySupportedList was int[], we could've just used getIntArray, but since it's a List<Integer>, we have to do something else
Arrays.stream(myObjectNbt.getIntArray("MySupportedList")).forEach(myInteger -> myObject.mySupportedList.add(myInteger));
// read properties that aren't supported
MyAnotherObject myAnotherObject = new MyAnotherObject();
NbtCompound myNotSupportedObjectNbt = myObjectNbt.getCompound("MyNotSupportedObject");
myAnotherObject.myUuid = myNotSupportedObjectNbt.getUuid("MyUUID");
// write them to our object
myObject.myNotSupportedObject = myAnotherObject;
// finally, add our constructed object to our list
myList.add(myObject);
}
}
@Inject(method = "writeCustomDataToNbt", at = @At("TAIL"))
public void writeMyDataToNbt(NbtCompound playerNbt, CallbackInfo ci) {
// create a new NBT record for our list
NbtCompound myListNbt = new NbtCompound();
// you can use a regular for-loop if you want, there's no difference. FabricMC wiki uses Iterable#forEach so that's why it's used here
myList.forEach(myObject -> {
// create a new NBT record for our object
NbtCompound myObjectNbt = new NbtCompound();
// write properties that are supported
myObjectNbt.putInt("MySupportedObject", myObject.mySupportedObject);
myObjectNbt.putIntArray("MySupportedList", myObject.mySupportedList);
// write myNotSupportedObject, which doesn't have a method for reading/writing in NbtCompound
NbtCompound myNotSupportedObjectNbt = new NbtCompound();
myNotSupportedObjectNbt.putUuid("MyUUID", myObject.myNotSupportedObject.myUuid);
// you can nest NBT records infinitely
myObjectNbt.put("MyNotSupportedObject", myNotSupportedObjectNbt);
// put our newly created record into the list record
myListNbt.put("MyObject", myObjectNbt);
});
// finally, put our list record into the player's NBT
playerNbt.put("MyList", myListNbt);
}
}
// these classes are in the same file for simplicity sake. you should make them public and put them in their own files
class MyObject {
// the NbtCompound class has methods to write and read this object - putInt and getInt
public int mySupportedObject = 0;
// there are even some supported lists! this one uses putIntArray
public List<Integer> mySupportedList = new ArrayList<>();
// if there's no method in NbtCompound to handle your class,
// you'll have to write their properties separately and, when reading, construct the objects using the written properties
// just how are we currently doing with this object
public MyAnotherObject myNotSupportedObject;
}
class MyAnotherObject {
public UUID myUuid;
}