@ -21,16 +21,22 @@ package net.william278.husksync.data;
import com.google.gson.reflect.TypeToken ;
import com.google.gson.reflect.TypeToken ;
import de.tr7zw.changeme.nbtapi.NBT ;
import de.tr7zw.changeme.nbtapi.NBT ;
import de.tr7zw.changeme.nbtapi.NBTCompound ;
import de.tr7zw.changeme.nbtapi.NBTContainer ;
import de.tr7zw.changeme.nbtapi.NBTContainer ;
import de.tr7zw.changeme.nbtapi.iface.ReadWriteNBT ;
import de.tr7zw.changeme.nbtapi.iface.ReadWriteNBT ;
import de.tr7zw.changeme.nbtapi.iface.ReadWriteNBTCompoundList ;
import de.tr7zw.changeme.nbtapi.utils.DataFixerUtil ;
import lombok.AccessLevel ;
import lombok.AccessLevel ;
import lombok.AllArgsConstructor ;
import lombok.AllArgsConstructor ;
import net.william278.desertwell.util.Version ;
import net.william278.husksync.HuskSync ;
import net.william278.husksync.HuskSync ;
import net.william278.husksync.adapter.Adaptable ;
import net.william278.husksync.adapter.Adaptable ;
import net.william278.husksync.api.HuskSyncAPI ;
import net.william278.husksync.api.HuskSyncAPI ;
import org.bukkit.Material ;
import org.bukkit.inventory.ItemStack ;
import org.bukkit.inventory.ItemStack ;
import org.jetbrains.annotations.ApiStatus ;
import org.jetbrains.annotations.ApiStatus ;
import org.jetbrains.annotations.NotNull ;
import org.jetbrains.annotations.NotNull ;
import org.jetbrains.annotations.Nullable ;
import java.util.List ;
import java.util.List ;
@ -52,7 +58,8 @@ public class BukkitSerializer {
return plugin ;
return plugin ;
}
}
public static class Inventory extends BukkitSerializer implements Serializer < BukkitData . Items . Inventory > {
public static class Inventory extends BukkitSerializer implements Serializer < BukkitData . Items . Inventory > ,
ItemDeserializer {
private static final String ITEMS_TAG = "items" ;
private static final String ITEMS_TAG = "items" ;
private static final String HELD_ITEM_SLOT_TAG = "held_item_slot" ;
private static final String HELD_ITEM_SLOT_TAG = "held_item_slot" ;
@ -61,16 +68,21 @@ public class BukkitSerializer {
}
}
@Override
@Override
public BukkitData . Items . Inventory deserialize ( @NotNull String serialized ) throws DeserializationException {
public BukkitData . Items . Inventory deserialize ( @NotNull String serialized , @NotNull Version dataMcVersion )
throws DeserializationException {
final ReadWriteNBT root = NBT . parseNBT ( serialized ) ;
final ReadWriteNBT root = NBT . parseNBT ( serialized ) ;
final ItemStack [ ] items = root . getItemStackArray ( ITEMS_TAG ) ;
final ReadWriteNBT items = root . hasTag ( ITEMS_TAG ) ? root . getCompound ( ITEMS_TAG ) : null ;
final int heldItemSlot = root . getInteger ( HELD_ITEM_SLOT_TAG ) ;
return BukkitData . Items . Inventory . from (
return BukkitData . Items . Inventory . from (
items == null ? new ItemStack [ INVENTORY_SLOT_COUNT ] : items ,
items != null ? getItems ( items , dataMcVersion ) : new ItemStack [ INVENTORY_SLOT_COUNT ] ,
heldItemSlot
root. getInteger ( HELD_ITEM_SLOT_TAG )
) ;
) ;
}
}
@Override
public BukkitData . Items . Inventory deserialize ( @NotNull String serialized ) {
return deserialize ( serialized , plugin . getMinecraftVersion ( ) ) ;
}
@NotNull
@NotNull
@Override
@Override
public String serialize ( @NotNull BukkitData . Items . Inventory data ) throws SerializationException {
public String serialize ( @NotNull BukkitData . Items . Inventory data ) throws SerializationException {
@ -82,18 +94,25 @@ public class BukkitSerializer {
}
}
public static class EnderChest extends BukkitSerializer implements Serializer < BukkitData . Items . EnderChest > {
public static class EnderChest extends BukkitSerializer implements Serializer < BukkitData . Items . EnderChest > ,
ItemDeserializer {
public EnderChest ( @NotNull HuskSync plugin ) {
public EnderChest ( @NotNull HuskSync plugin ) {
super ( plugin ) ;
super ( plugin ) ;
}
}
@Override
@Override
public BukkitData . Items . EnderChest deserialize ( @NotNull String serialized ) throws DeserializationException {
public BukkitData . Items . EnderChest deserialize ( @NotNull String serialized , @NotNull Version dataMcVersion )
final ItemStack [ ] items = NBT . itemStackArrayFromNBT ( NBT . parseNBT ( serialized ) ) ;
throws DeserializationException {
final ItemStack [ ] items = getItems ( NBT . parseNBT ( serialized ) , dataMcVersion ) ;
return items = = null ? BukkitData . Items . EnderChest . empty ( ) : BukkitData . Items . EnderChest . adapt ( items ) ;
return items = = null ? BukkitData . Items . EnderChest . empty ( ) : BukkitData . Items . EnderChest . adapt ( items ) ;
}
}
@Override
public BukkitData . Items . EnderChest deserialize ( @NotNull String serialized ) {
return deserialize ( serialized , plugin . getMinecraftVersion ( ) ) ;
}
@NotNull
@NotNull
@Override
@Override
public String serialize ( @NotNull BukkitData . Items . EnderChest data ) throws SerializationException {
public String serialize ( @NotNull BukkitData . Items . EnderChest data ) throws SerializationException {
@ -101,6 +120,57 @@ public class BukkitSerializer {
}
}
}
}
// Utility interface for deserializing and upgrading item stacks from legacy versions
private interface ItemDeserializer {
@Nullable
default ItemStack [ ] getItems ( @NotNull ReadWriteNBT tag , @NotNull Version mcVersion ) {
if ( mcVersion . compareTo ( getPlugin ( ) . getMinecraftVersion ( ) ) < 0 ) {
return upgradeItemStack ( ( NBTCompound ) tag , mcVersion ) ;
}
return NBT . itemStackArrayFromNBT ( tag ) ;
}
@NotNull
private ItemStack @NotNull [ ] upgradeItemStack ( @NotNull NBTCompound compound , @NotNull Version mcVersion ) {
final ReadWriteNBTCompoundList items = compound . getCompoundList ( "items" ) ;
final ItemStack [ ] itemStacks = new ItemStack [ compound . getInteger ( "size" ) ] ;
for ( int i = 0 ; i < items . size ( ) ; i + + ) {
if ( items . get ( i ) = = null ) {
itemStacks [ i ] = new ItemStack ( Material . AIR ) ;
continue ;
}
try {
itemStacks [ i ] = NBT . itemStackFromNBT ( upgradeItemData ( items . get ( i ) , mcVersion ) ) ;
} catch ( Throwable e ) {
itemStacks [ i ] = new ItemStack ( Material . AIR ) ;
}
}
return itemStacks ;
}
@NotNull
private ReadWriteNBT upgradeItemData ( @NotNull ReadWriteNBT tag , @NotNull Version mcVersion )
throws NoSuchFieldException , IllegalAccessException {
return DataFixerUtil . fixUpItemData ( tag , getDataVersion ( mcVersion ) , DataFixerUtil . getCurrentVersion ( ) ) ;
}
private int getDataVersion ( @NotNull Version mcVersion ) {
return switch ( mcVersion . toStringWithoutMetadata ( ) ) {
case "1.16" , "1.16.1" , "1.16.2" , "1.16.3" , "1.16.4" , "1.16.5" - > DataFixerUtil . VERSION1_16_5 ;
case "1.17" , "1.17.1" - > DataFixerUtil . VERSION1_17_1 ;
case "1.18" , "1.18.1" , "1.18.2" - > DataFixerUtil . VERSION1_18_2 ;
case "1.19" , "1.19.1" , "1.19.2" - > DataFixerUtil . VERSION1_19_2 ;
case "1.20" , "1.20.1" , "1.20.2" - > DataFixerUtil . VERSION1_20_2 ;
case "1.20.3" , "1.20.4" - > DataFixerUtil . VERSION1_20_4 ;
default - > DataFixerUtil . getCurrentVersion ( ) ;
} ;
}
@NotNull
HuskSync getPlugin ( ) ;
}
public static class PotionEffects extends BukkitSerializer implements Serializer < BukkitData . PotionEffects > {
public static class PotionEffects extends BukkitSerializer implements Serializer < BukkitData . PotionEffects > {
private static final TypeToken < List < Data . PotionEffects . Effect > > TYPE = new TypeToken < > ( ) {
private static final TypeToken < List < Data . PotionEffects . Effect > > TYPE = new TypeToken < > ( ) {