diff --git a/src/main/java/org/spongepowered/common/item/ItemStackDataComponentsUpdater.java b/src/main/java/org/spongepowered/common/item/ItemStackDataComponentsUpdater.java index fe0ed3442a8..6406dc8f576 100644 --- a/src/main/java/org/spongepowered/common/item/ItemStackDataComponentsUpdater.java +++ b/src/main/java/org/spongepowered/common/item/ItemStackDataComponentsUpdater.java @@ -56,11 +56,11 @@ public DataView update(final DataView content) { final String type = content.getString(Constants.ItemStack.V2.TYPE).get(); final DataContainer updated = DataContainer.createNew(); - updated.set(Constants.ItemStack.TYPE, type); - updated.set(Constants.ItemStack.COUNT, count); + updated.set(Constants.ItemStack.V3.TYPE, type); + updated.set(Constants.ItemStack.V3.COUNT, count); final DataContainer components = DataContainer.createNew(); - content.getInt(Constants.ItemStack.V2.DAMAGE_VALUE).filter(dmg -> dmg != 0).ifPresent(dmg -> components.set(Constants.ItemStack.DAMAGE, dmg)); + content.getInt(Constants.ItemStack.V2.DAMAGE_VALUE).filter(dmg -> dmg != 0).ifPresent(dmg -> components.set(Constants.ItemStack.V3.DAMAGE, dmg)); content.getView(Constants.Sponge.UNSAFE_NBT).ifPresent(unsafe -> { // Get unsafe nbt data @@ -77,7 +77,7 @@ public DataView update(final DataView content) { newComponents.values(false).forEach(components::set); }); - updated.set(Constants.ItemStack.COMPONENTS, components); + updated.set(Constants.ItemStack.V3.COMPONENTS, components); updated.set(Queries.CONTENT_VERSION, this.outputVersion()); return updated; diff --git a/src/main/java/org/spongepowered/common/item/ItemStackDataVersionUpdater.java b/src/main/java/org/spongepowered/common/item/ItemStackDataVersionUpdater.java new file mode 100644 index 00000000000..d77024377cc --- /dev/null +++ b/src/main/java/org/spongepowered/common/item/ItemStackDataVersionUpdater.java @@ -0,0 +1,72 @@ +/* + * This file is part of Sponge, licensed under the MIT License (MIT). + * + * Copyright (c) SpongePowered + * Copyright (c) contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package org.spongepowered.common.item; + +import org.spongepowered.api.data.persistence.DataContainer; +import org.spongepowered.api.data.persistence.DataContentUpdater; +import org.spongepowered.api.data.persistence.DataView; +import org.spongepowered.api.data.persistence.Queries; +import org.spongepowered.api.item.ItemTypes; +import org.spongepowered.api.registry.RegistryTypes; +import org.spongepowered.common.util.Constants; + +public final class ItemStackDataVersionUpdater implements DataContentUpdater { + + public static final DataContentUpdater INSTANCE = new ItemStackDataVersionUpdater(); + + @Override + public int inputVersion() { + return Constants.ItemStack.Data.DATA_COMPONENTS; + } + + @Override + public int outputVersion() { + return Constants.ItemStack.Data.DATA_VERSIONED; + } + + @Override + public DataView update(final DataView content) { + final boolean isAir = content.getRegistryValue(Constants.ItemStack.V3.TYPE, RegistryTypes.ITEM_TYPE) + .map(i -> i == ItemTypes.AIR.get()) + .orElse(false); + + final DataContainer updated = DataContainer.createNew(); + updated.set(Queries.CONTENT_VERSION, this.outputVersion()); + updated.set(Constants.ItemStack.V4.DATA_VERSION, 3833); // The version from the previous ItemStackDataComponentsUpdater. + + if (isAir) { + return updated.set(Constants.ItemStack.V4.DATA, DataContainer.createNew()); + } + + final DataContainer data = DataContainer.createNew() + .set(Constants.ItemStack.V3.TYPE, content.getString(Constants.ItemStack.V3.TYPE).get()) + .set(Constants.ItemStack.V3.COUNT, content.getInt(Constants.ItemStack.V3.COUNT).get()); + + content.getView(Constants.ItemStack.V3.COMPONENTS) + .ifPresent(components -> data.set(Constants.ItemStack.V3.COMPONENTS, components)); + + return updated.set(Constants.ItemStack.V4.DATA, data); + } +} diff --git a/src/main/java/org/spongepowered/common/item/SpongeItemStack.java b/src/main/java/org/spongepowered/common/item/SpongeItemStack.java index 04adf4a5b78..4c0cb5d25cc 100644 --- a/src/main/java/org/spongepowered/common/item/SpongeItemStack.java +++ b/src/main/java/org/spongepowered/common/item/SpongeItemStack.java @@ -26,8 +26,8 @@ import com.google.common.collect.ImmutableList; -import com.mojang.datafixers.util.Pair; import com.mojang.serialization.Dynamic; +import net.minecraft.SharedConstants; import net.minecraft.core.component.DataComponentPatch; import net.minecraft.core.component.DataComponentType; import net.minecraft.core.component.DataComponents; @@ -35,8 +35,10 @@ import net.minecraft.core.registries.Registries; import net.minecraft.nbt.CompoundTag; import net.minecraft.nbt.NbtOps; -import net.minecraft.resources.RegistryOps; +import net.minecraft.nbt.Tag; import net.minecraft.resources.ResourceLocation; +import net.minecraft.util.datafix.DataFixers; +import net.minecraft.util.datafix.fixes.References; import net.minecraft.world.entity.EquipmentSlot; import net.minecraft.world.entity.EquipmentSlotGroup; import net.minecraft.world.entity.ai.attributes.Attribute; @@ -64,7 +66,6 @@ import org.spongepowered.api.item.inventory.ItemStack; import org.spongepowered.api.item.inventory.ItemStackSnapshot; import org.spongepowered.api.item.inventory.equipment.EquipmentType; -import org.spongepowered.api.registry.RegistryTypes; import org.spongepowered.common.SpongeCommon; import org.spongepowered.common.block.SpongeBlockSnapshot; import org.spongepowered.common.data.DataUpdaterDelegate; @@ -273,20 +274,13 @@ public ItemStack empty() { @NotNull public static DataContainer getDataContainer(final net.minecraft.world.item.ItemStack mcStack) { - final ResourceLocation key = BuiltInRegistries.ITEM.getKey(mcStack.getItem()); - final DataContainer container = DataContainer.createNew() - .set(Queries.CONTENT_VERSION, ((ItemStack) (Object) mcStack).contentVersion()) - .set(Constants.ItemStack.TYPE, key) - .set(Constants.ItemStack.COUNT, mcStack.getCount()); // Cleanup Old Custom Data SpongeItemStack.cleanupOldCustomData(mcStack); - // Serialize all DataComponents... - - var ops = RegistryOps.create(NbtOps.INSTANCE, SpongeCommon.server().registryAccess()); - var componentsTag = DataComponentPatch.CODEC.encodeStart(ops, mcStack.getComponentsPatch()); - var components = NBTTranslator.INSTANCE.translate((CompoundTag) componentsTag.getOrThrow()); - container.set(Constants.ItemStack.COMPONENTS, components); - return container; + final CompoundTag stackData = (CompoundTag) mcStack.saveOptional(SpongeCommon.server().registryAccess()); + return DataContainer.createNew() + .set(Queries.CONTENT_VERSION, ((ItemStack) (Object) mcStack).contentVersion()) + .set(Constants.ItemStack.V4.DATA_VERSION, SharedConstants.getCurrentVersion().getDataVersion().getVersion()) + .set(Constants.ItemStack.V4.DATA, NBTTranslator.INSTANCE.translate(stackData)); } @NotNull @@ -313,7 +307,8 @@ private static void cleanupOldCustomData(final net.minecraft.world.item.ItemStac // TODO updater for old (maybe V2?) Constants.Sponge.DATA_MANIPULATORS public static final ImmutableList STACK_UPDATERS = ImmutableList.of( ItemStackSnapshotDuplicateManipulatorUpdater.INSTANCE, - ItemStackDataComponentsUpdater.INSTANCE); + ItemStackDataComponentsUpdater.INSTANCE, + ItemStackDataVersionUpdater.INSTANCE); @NotNull public static Optional createItemStack(final DataView container) { @@ -333,27 +328,24 @@ public static Optional createItemStack(final DataView container) { final DataUpdaterDelegate delegate = new DataUpdaterDelegate(builder.build(), version, Constants.ItemStack.Data.CURRENT_VERSION); final DataView updatedContainer = delegate.update(container); - // Check if we have valid updated stack - if (!updatedContainer.contains(Constants.ItemStack.TYPE, Constants.ItemStack.COUNT)) { + if (!updatedContainer.contains(Constants.ItemStack.V4.DATA_VERSION, Constants.ItemStack.V4.DATA)) { return Optional.empty(); } - final int count = updatedContainer.getInt(Constants.ItemStack.COUNT).get(); - - final ItemType itemType = updatedContainer.getRegistryValue(Constants.ItemStack.TYPE, RegistryTypes.ITEM_TYPE) - .orElseThrow(() -> new IllegalStateException("Unable to find item with id: ")); - final var mcStack = new net.minecraft.world.item.ItemStack((Item) itemType, count); - if (!mcStack.isEmpty()) { // ignore components when the stack is empty anyways - // Read and apply components from container to mc stack - mcStack.applyComponents(SpongeItemStack.patchFromData(updatedContainer)); + final CompoundTag stackData = updatedContainer.getView(Constants.ItemStack.V4.DATA) + .map(NBTTranslator.INSTANCE::translate) + .orElseThrow(() -> new InvalidDataException("Unable retrieve item stack data")); + if (stackData.isEmpty()) { + return Optional.of(ItemStack.empty()); } - return Optional.of((ItemStack) (Object) mcStack); - } - - public static DataComponentPatch patchFromData(final DataView container) { - return container.getView(Constants.ItemStack.COMPONENTS).map(NBTTranslator.INSTANCE::translate).flatMap(compound -> { - var dynamic = new Dynamic<>(RegistryOps.create(NbtOps.INSTANCE, SpongeCommon.server().registryAccess()), compound); - return DataComponentPatch.CODEC.decode(dynamic).result().map(Pair::getFirst); - }).orElse(DataComponentPatch.EMPTY); + final int dataVersion = updatedContainer.getInt(Constants.ItemStack.V4.DATA_VERSION).get(); + final Dynamic fixedData = DataFixers.getDataFixer().update( + References.ITEM_STACK, + new Dynamic<>(NbtOps.INSTANCE, stackData), + dataVersion, + SharedConstants.getCurrentVersion().getDataVersion().getVersion() + ); + return net.minecraft.world.item.ItemStack.parse(SpongeCommon.server().registryAccess(), fixedData.getValue()) + .map(ItemStack.class::cast); } } diff --git a/src/main/java/org/spongepowered/common/util/Constants.java b/src/main/java/org/spongepowered/common/util/Constants.java index f82d15b4e47..268e562e6c2 100644 --- a/src/main/java/org/spongepowered/common/util/Constants.java +++ b/src/main/java/org/spongepowered/common/util/Constants.java @@ -1192,12 +1192,6 @@ public static final class Fluids { public static final class ItemStack { - public static final DataQuery COUNT = of("count"); - public static final DataQuery TYPE = of("id"); - public static final DataQuery COMPONENTS = of("components"); - public static final DataQuery CUSTOM_DATA = of("minecraft:custom_data"); - public static final DataQuery DAMAGE = of("minecraft:damage"); - @Deprecated public static final class V2 { @@ -1207,13 +1201,29 @@ public static final class V2 { public static final DataQuery TYPE = of("ItemType"); } + @Deprecated + public static final class V3 { + + public static final DataQuery COUNT = of("count"); + public static final DataQuery TYPE = of("id"); + public static final DataQuery COMPONENTS = of("components"); + public static final DataQuery DAMAGE = of("minecraft:damage"); + } + + public static final class V4 { + + public static final DataQuery DATA = of("Data"); + public static final DataQuery DATA_VERSION = of("DataVersion"); + } + // Previously only ItemStackSnapshot public static final class Data { public static final int DUPLICATE_MANIPULATOR_DATA_VERSION = 1; public static final int REMOVED_DUPLICATE_DATA = 2; public static final int DATA_COMPONENTS = 3; - public static final int CURRENT_VERSION = Data.DATA_COMPONENTS; + public static final int DATA_VERSIONED = 4; + public static final int CURRENT_VERSION = Data.DATA_VERSIONED; } } diff --git a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/item/ItemStackMixin_API.java b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/item/ItemStackMixin_API.java index cca0e04b102..ed069a6a2a7 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/item/ItemStackMixin_API.java +++ b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/item/ItemStackMixin_API.java @@ -29,6 +29,7 @@ import net.kyori.adventure.text.ComponentLike; import net.kyori.adventure.text.event.HoverEvent; import net.kyori.adventure.text.event.HoverEventSource; +import net.minecraft.core.component.DataComponentMap; import net.minecraft.core.component.DataComponentPatch; import net.minecraft.core.component.DataComponentType; import net.minecraft.core.component.DataComponents; @@ -80,6 +81,7 @@ public abstract class ItemStackMixin_API implements SerializableDataHolder.Mutab @Shadow public abstract Item shadow$getItem(); @Shadow public abstract net.minecraft.network.chat.Component shadow$getDisplayName(); @Shadow public abstract void shadow$applyComponents(final DataComponentPatch $$0); + @Shadow public abstract void shadow$applyComponents(final DataComponentMap $$0); @Shadow public abstract DataComponentPatch shadow$getComponentsPatch(); @Shadow @Nullable public abstract T shadow$update(final DataComponentType $$0, final T $$1, final UnaryOperator $$2); @@ -100,7 +102,7 @@ public abstract class ItemStackMixin_API implements SerializableDataHolder.Mutab @Override public boolean validateRawData(final DataView container) { Objects.requireNonNull(container); - return false; + return SpongeItemStack.createItemStack(container).isPresent(); } @Override @@ -111,8 +113,12 @@ public void setRawData(final DataView container) throws InvalidDataException { throw new IllegalArgumentException("Cannot set data on empty item stacks!"); } + final net.minecraft.world.item.ItemStack updated = SpongeItemStack.createItemStack(container) + .map(net.minecraft.world.item.ItemStack.class::cast) + .orElseThrow(() -> new InvalidDataException("Unable to deserialize item stack data")); + try { - this.shadow$applyComponents(SpongeItemStack.patchFromData(container)); + this.shadow$applyComponents(updated.getComponents()); } catch (final Exception e) { throw new InvalidDataException("Unable to set raw data or translate raw data for ItemStack setting", e); } @@ -168,7 +174,7 @@ public SerializableDataHolder.Mutable copy() { @Override public int contentVersion() { - return 3; + return 4; } @Override