DataComponent API

Exposes the data component logic used by vanilla ItemStack to API
consumers as a version-specific API.
The types and methods introduced by this patch do not follow the general
API deprecation contracts and will be adapted to each new minecraft
release without backwards compatibility measures.

== AT ==
public net/minecraft/world/item/component/ItemContainerContents MAX_SIZE
public net/minecraft/world/item/component/ItemContainerContents items
This commit is contained in:
Owen1212055
2024-04-28 19:53:01 -04:00
parent 2a20fde332
commit 25c25923e9
64 changed files with 4397 additions and 29 deletions

View File

@@ -20,13 +20,11 @@ import net.minecraft.world.item.enchantment.ItemEnchantments;
import org.bukkit.Material;
import org.bukkit.configuration.serialization.DelegateDeserialization;
import org.bukkit.craftbukkit.enchantments.CraftEnchantment;
import org.bukkit.craftbukkit.util.CraftLegacy;
import org.bukkit.craftbukkit.util.CraftMagicNumbers;
import org.bukkit.enchantments.Enchantment;
import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.meta.ItemMeta;
import org.bukkit.material.MaterialData;
import org.jetbrains.annotations.ApiStatus;
@DelegateDeserialization(ItemStack.class)
public final class CraftItemStack extends ItemStack {
@@ -206,7 +204,7 @@ public final class CraftItemStack extends ItemStack {
this.adjustTagForItemMeta(oldType); // Paper
}
}
this.setData(null);
this.setData((MaterialData) null); // Paper
}
@Override
@@ -245,7 +243,7 @@ public final class CraftItemStack extends ItemStack {
@Override
public int getMaxStackSize() {
return (this.handle == null) ? Material.AIR.getMaxStackSize() : this.handle.getMaxStackSize();
return (this.handle == null) ? Item.DEFAULT_MAX_STACK_SIZE : this.handle.getMaxStackSize(); // Paper - air stacks to 64
}
// Paper start
@@ -267,12 +265,14 @@ public final class CraftItemStack extends ItemStack {
public void addUnsafeEnchantment(Enchantment ench, int level) {
Preconditions.checkArgument(ench != null, "Enchantment cannot be null");
// Paper start - Replace whole method
final ItemMeta itemMeta = this.getItemMeta();
if (itemMeta != null) {
itemMeta.addEnchant(ench, level, true);
this.setItemMeta(itemMeta);
// Paper start
if (this.handle == null) {
return;
}
EnchantmentHelper.updateEnchantments(this.handle, mutable -> { // data component api doesn't really support mutable things once already set yet
mutable.set(CraftEnchantment.bukkitToMinecraftHolder(ench), level);
});
// Paper end
}
@@ -302,17 +302,28 @@ public final class CraftItemStack extends ItemStack {
public int removeEnchantment(Enchantment ench) {
Preconditions.checkArgument(ench != null, "Enchantment cannot be null");
// Paper start - replace entire method
int level = getEnchantmentLevel(ench);
if (level > 0) {
final ItemMeta itemMeta = this.getItemMeta();
if (itemMeta == null) return 0;
itemMeta.removeEnchant(ench);
this.setItemMeta(itemMeta);
// Paper start
if (this.handle == null) {
return 0;
}
// Paper end
return level;
ItemEnchantments itemEnchantments = this.handle.getOrDefault(DataComponents.ENCHANTMENTS, ItemEnchantments.EMPTY);
if (itemEnchantments.isEmpty()) {
return 0;
}
Holder<net.minecraft.world.item.enchantment.Enchantment> removedEnchantment = CraftEnchantment.bukkitToMinecraftHolder(ench);
if (itemEnchantments.keySet().contains(removedEnchantment)) {
int previousLevel = itemEnchantments.getLevel(removedEnchantment);
ItemEnchantments.Mutable mutable = new ItemEnchantments.Mutable(itemEnchantments); // data component api doesn't really support mutable things once already set yet
mutable.removeIf(enchantment -> enchantment.equals(removedEnchantment));
this.handle.set(DataComponents.ENCHANTMENTS, mutable.toImmutable());
return previousLevel;
}
return 0;
// Paper end
}
@Override
@@ -324,7 +335,13 @@ public final class CraftItemStack extends ItemStack {
@Override
public Map<Enchantment, Integer> getEnchantments() {
return this.hasItemMeta() ? this.getItemMeta().getEnchants() : ImmutableMap.<Enchantment, Integer>of(); // Paper - use Item Meta
// Paper start
io.papermc.paper.datacomponent.item.ItemEnchantments itemEnchantments = this.getData(io.papermc.paper.datacomponent.DataComponentTypes.ENCHANTMENTS); // empty constant might be useful here
if (itemEnchantments == null) {
return java.util.Collections.emptyMap();
}
return itemEnchantments.enchantments();
// Paper end
}
static Map<Enchantment, Integer> getEnchantments(net.minecraft.world.item.ItemStack item) {
@@ -526,4 +543,119 @@ public final class CraftItemStack extends ItemStack {
return this.pdcView;
}
// Paper end - pdc
// Paper start - data component API
@Override
public <T> T getData(final io.papermc.paper.datacomponent.DataComponentType.Valued<T> type) {
if (this.isEmpty()) {
return null;
}
return io.papermc.paper.datacomponent.PaperDataComponentType.convertDataComponentValue(this.handle.getComponents(), (io.papermc.paper.datacomponent.PaperDataComponentType.ValuedImpl<T, ?>) type);
}
@Override
public boolean hasData(final io.papermc.paper.datacomponent.DataComponentType type) {
if (this.isEmpty()) {
return false;
}
return this.handle.has(io.papermc.paper.datacomponent.PaperDataComponentType.bukkitToMinecraft(type));
}
@Override
public java.util.Set<io.papermc.paper.datacomponent.DataComponentType> getDataTypes() {
if (this.isEmpty()) {
return java.util.Collections.emptySet();
}
return io.papermc.paper.datacomponent.PaperDataComponentType.minecraftToBukkit(this.handle.getComponents().keySet());
}
@Override
public <T> void setData(final io.papermc.paper.datacomponent.DataComponentType.Valued<T> type, final T value) {
Preconditions.checkArgument(value != null, "value cannot be null");
if (this.isEmpty()) {
return;
}
this.setDataInternal((io.papermc.paper.datacomponent.PaperDataComponentType.ValuedImpl<T, ?>) type, value);
}
@Override
public void setData(final io.papermc.paper.datacomponent.DataComponentType.NonValued type) {
if (this.isEmpty()) {
return;
}
this.setDataInternal((io.papermc.paper.datacomponent.PaperDataComponentType.NonValuedImpl<?, ?>) type, null);
}
private <A, V> void setDataInternal(final io.papermc.paper.datacomponent.PaperDataComponentType<A, V> type, final A value) {
this.handle.set(type.getHandle(), type.getAdapter().toVanilla(value));
}
@Override
public void unsetData(final io.papermc.paper.datacomponent.DataComponentType type) {
if (this.isEmpty()) {
return;
}
this.handle.remove(io.papermc.paper.datacomponent.PaperDataComponentType.bukkitToMinecraft(type));
}
@Override
public void resetData(final io.papermc.paper.datacomponent.DataComponentType type) {
if (this.isEmpty()) {
return;
}
this.resetData((io.papermc.paper.datacomponent.PaperDataComponentType<?, ?>) type);
}
private <M> void resetData(final io.papermc.paper.datacomponent.PaperDataComponentType<?, M> type) {
final net.minecraft.core.component.DataComponentType<M> nms = io.papermc.paper.datacomponent.PaperDataComponentType.bukkitToMinecraft(type);
final M nmsValue = this.handle.getItem().components().get(nms);
// if nmsValue is null, it will clear any set patch
// if nmsValue is not null, it will still clear any set patch because it will equal the default value
this.handle.set(nms, nmsValue);
}
@Override
public boolean isDataOverridden(final io.papermc.paper.datacomponent.DataComponentType type) {
if (this.isEmpty()) {
return false;
}
final net.minecraft.core.component.DataComponentType<?> nms = io.papermc.paper.datacomponent.PaperDataComponentType.bukkitToMinecraft(type);
// maybe a more efficient way is to expose the "patch" map in PatchedDataComponentMap and just check if the type exists as a key
return !java.util.Objects.equals(this.handle.get(nms), this.handle.getPrototype().get(nms));
}
@Override
public boolean matchesWithoutData(final ItemStack item, final java.util.Set<io.papermc.paper.datacomponent.DataComponentType> exclude, final boolean ignoreCount) {
// Extracted from base equals
final CraftItemStack craftStack = getCraftStack(item);
if (this.handle == craftStack.handle) return true;
if (this.handle == null || craftStack.handle == null) return false;
if (this.handle.isEmpty() && craftStack.handle.isEmpty()) return true;
net.minecraft.world.item.ItemStack left = this.handle;
net.minecraft.world.item.ItemStack right = craftStack.handle;
if (!ignoreCount && left.getCount() != right.getCount()) {
return false;
}
if (!left.is(right.getItem())) {
return false;
}
// It can be assumed that the prototype is equal since the type is the same. This way all we need to check is the patch
// Fast path when excluded types is empty
if (exclude.isEmpty()) {
return left.getComponentsPatch().equals(right.getComponentsPatch());
}
// Collect all the NMS types into a set
java.util.Set<net.minecraft.core.component.DataComponentType<?>> skippingTypes = new java.util.HashSet<>(exclude.size());
for (io.papermc.paper.datacomponent.DataComponentType api : exclude) {
skippingTypes.add(io.papermc.paper.datacomponent.PaperDataComponentType.bukkitToMinecraft(api));
}
// Check the patch by first stripping excluded types and then compare the trimmed patches
return left.getComponentsPatch().forget(skippingTypes::contains).equals(right.getComponentsPatch().forget(skippingTypes::contains));
}
// Paper end - data component API
}

View File

@@ -150,7 +150,7 @@ public class CraftItemType<M extends ItemMeta> implements ItemType.Typed<M>, Han
public int getMaxStackSize() {
// Based of the material enum air is only 0, in PerMaterialTest it is also set as special case
// the item info itself would return 64
if (this == AIR) {
if (false && this == AIR) { // Paper - air stacks to 64
return 0;
}
return this.item.components().getOrDefault(DataComponents.MAX_STACK_SIZE, 64);
@@ -270,4 +270,20 @@ public class CraftItemType<M extends ItemMeta> implements ItemType.Typed<M>, Han
return rarity == null ? null : org.bukkit.inventory.ItemRarity.valueOf(rarity.name());
}
// Paper end - expand ItemRarity API
// Paper start - data component API
@Override
public <T> T getDefaultData(final io.papermc.paper.datacomponent.DataComponentType.Valued<T> type) {
return io.papermc.paper.datacomponent.PaperDataComponentType.convertDataComponentValue(this.item.components(), ((io.papermc.paper.datacomponent.PaperDataComponentType.ValuedImpl<T, ?>) type));
}
@Override
public boolean hasDefaultData(final io.papermc.paper.datacomponent.DataComponentType type) {
return this.item.components().has(io.papermc.paper.datacomponent.PaperDataComponentType.bukkitToMinecraft(type));
}
@Override
public java.util.Set<io.papermc.paper.datacomponent.DataComponentType> getDefaultDataTypes() {
return io.papermc.paper.datacomponent.PaperDataComponentType.minecraftToBukkit(this.item.components().keySet());
}
// Paper end - data component API
}

View File

@@ -91,7 +91,7 @@ class CraftMetaFirework extends CraftMetaItem implements FireworkMeta {
this.safelyAddEffects(effects, false); // Paper - limit firework effects
}
static FireworkEffect getEffect(FireworkExplosion explosion) {
public static FireworkEffect getEffect(FireworkExplosion explosion) { // Paper
FireworkEffect.Builder effect = FireworkEffect.builder()
.flicker(explosion.hasTwinkle())
.trail(explosion.hasTrail())
@@ -111,7 +111,7 @@ class CraftMetaFirework extends CraftMetaItem implements FireworkMeta {
return effect.build();
}
static FireworkExplosion getExplosion(FireworkEffect effect) {
public static FireworkExplosion getExplosion(FireworkEffect effect) { // Paper
IntList colors = CraftMetaFirework.addColors(effect.getColors());
IntList fadeColors = CraftMetaFirework.addColors(effect.getFadeColors());