diff --git a/player-read-write-nbt.java b/player-read-write-nbt.java new file mode 100644 index 0000000..935873b --- /dev/null +++ b/player-read-write-nbt.java @@ -0,0 +1,96 @@ +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 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, 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 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; +} \ No newline at end of file