#1147: Add experimental armor trim API

By: Parker Hawke <hawkeboyz2@hotmail.com>
This commit is contained in:
CraftBukkit/Spigot 2023-04-07 16:51:00 +10:00
parent ad3da9dbf3
commit 6c852e65e7
10 changed files with 498 additions and 29 deletions

View File

@ -14,9 +14,13 @@ import org.bukkit.NamespacedKey;
import org.bukkit.Registry;
import org.bukkit.craftbukkit.generator.strucutre.CraftStructure;
import org.bukkit.craftbukkit.generator.strucutre.CraftStructureType;
import org.bukkit.craftbukkit.inventory.trim.CraftTrimMaterial;
import org.bukkit.craftbukkit.inventory.trim.CraftTrimPattern;
import org.bukkit.craftbukkit.util.CraftNamespacedKey;
import org.bukkit.generator.structure.Structure;
import org.bukkit.generator.structure.StructureType;
import org.bukkit.inventory.meta.trim.TrimMaterial;
import org.bukkit.inventory.meta.trim.TrimPattern;
public class CraftRegistry<B extends Keyed, M> implements Registry<B> {
@ -27,6 +31,12 @@ public class CraftRegistry<B extends Keyed, M> implements Registry<B> {
if (bukkitClass == StructureType.class) {
return new CraftRegistry<>(BuiltInRegistries.STRUCTURE_TYPE, CraftStructureType::new);
}
if (bukkitClass == TrimMaterial.class) {
return new CraftRegistry<>(registryHolder.registryOrThrow(Registries.TRIM_MATERIAL), CraftTrimMaterial::new);
}
if (bukkitClass == TrimPattern.class) {
return new CraftRegistry<>(registryHolder.registryOrThrow(Registries.TRIM_PATTERN), CraftTrimPattern::new);
}
return null;
}

View File

@ -79,11 +79,34 @@ public final class CraftItemFactory implements ItemFactory {
case ZOMBIE_HEAD:
case ZOMBIE_WALL_HEAD:
return meta instanceof CraftMetaSkull ? meta : new CraftMetaSkull(meta);
case CHAINMAIL_HELMET:
case CHAINMAIL_CHESTPLATE:
case CHAINMAIL_LEGGINGS:
case CHAINMAIL_BOOTS:
case DIAMOND_HELMET:
case DIAMOND_CHESTPLATE:
case DIAMOND_LEGGINGS:
case DIAMOND_BOOTS:
case GOLDEN_HELMET:
case GOLDEN_CHESTPLATE:
case GOLDEN_LEGGINGS:
case GOLDEN_BOOTS:
case IRON_HELMET:
case IRON_CHESTPLATE:
case IRON_LEGGINGS:
case IRON_BOOTS:
case NETHERITE_HELMET:
case NETHERITE_CHESTPLATE:
case NETHERITE_LEGGINGS:
case NETHERITE_BOOTS:
case TURTLE_HELMET:
return meta != null && meta.getClass().equals(CraftMetaArmor.class) ? meta : new CraftMetaArmor(meta);
case LEATHER_HELMET:
case LEATHER_HORSE_ARMOR:
case LEATHER_CHESTPLATE:
case LEATHER_LEGGINGS:
case LEATHER_BOOTS:
return meta instanceof CraftMetaColorableArmor ? meta : new CraftMetaColorableArmor(meta);
case LEATHER_HORSE_ARMOR:
return meta instanceof CraftMetaLeatherArmor ? meta : new CraftMetaLeatherArmor(meta);
case POTION:
case SPLASH_POTION:

View File

@ -342,11 +342,34 @@ public final class CraftItemStack extends ItemStack {
case ZOMBIE_HEAD:
case ZOMBIE_WALL_HEAD:
return new CraftMetaSkull(item.getTag());
case CHAINMAIL_HELMET:
case CHAINMAIL_CHESTPLATE:
case CHAINMAIL_LEGGINGS:
case CHAINMAIL_BOOTS:
case DIAMOND_HELMET:
case DIAMOND_CHESTPLATE:
case DIAMOND_LEGGINGS:
case DIAMOND_BOOTS:
case GOLDEN_HELMET:
case GOLDEN_CHESTPLATE:
case GOLDEN_LEGGINGS:
case GOLDEN_BOOTS:
case IRON_HELMET:
case IRON_CHESTPLATE:
case IRON_LEGGINGS:
case IRON_BOOTS:
case NETHERITE_HELMET:
case NETHERITE_CHESTPLATE:
case NETHERITE_LEGGINGS:
case NETHERITE_BOOTS:
case TURTLE_HELMET:
return new CraftMetaArmor(item.getTag());
case LEATHER_HELMET:
case LEATHER_HORSE_ARMOR:
case LEATHER_CHESTPLATE:
case LEATHER_LEGGINGS:
case LEATHER_BOOTS:
return new CraftMetaColorableArmor(item.getTag());
case LEATHER_HORSE_ARMOR:
return new CraftMetaLeatherArmor(item.getTag());
case POTION:
case SPLASH_POTION:

View File

@ -0,0 +1,194 @@
package org.bukkit.craftbukkit.inventory;
import com.google.common.collect.ImmutableMap.Builder;
import com.google.common.collect.Sets;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import net.minecraft.nbt.NBTTagCompound;
import org.bukkit.Material;
import org.bukkit.NamespacedKey;
import org.bukkit.Registry;
import org.bukkit.configuration.serialization.DelegateDeserialization;
import org.bukkit.inventory.meta.ArmorMeta;
import org.bukkit.inventory.meta.trim.ArmorTrim;
import org.bukkit.inventory.meta.trim.TrimMaterial;
import org.bukkit.inventory.meta.trim.TrimPattern;
@DelegateDeserialization(CraftMetaItem.SerializableMeta.class)
public class CraftMetaArmor extends CraftMetaItem implements ArmorMeta {
private static final Set<Material> ARMOR_MATERIALS = Sets.newHashSet(
Material.CHAINMAIL_HELMET,
Material.CHAINMAIL_CHESTPLATE,
Material.CHAINMAIL_LEGGINGS,
Material.CHAINMAIL_BOOTS,
Material.DIAMOND_HELMET,
Material.DIAMOND_CHESTPLATE,
Material.DIAMOND_LEGGINGS,
Material.DIAMOND_BOOTS,
Material.GOLDEN_HELMET,
Material.GOLDEN_CHESTPLATE,
Material.GOLDEN_LEGGINGS,
Material.GOLDEN_BOOTS,
Material.IRON_HELMET,
Material.IRON_CHESTPLATE,
Material.IRON_LEGGINGS,
Material.IRON_BOOTS,
Material.LEATHER_HELMET,
Material.LEATHER_CHESTPLATE,
Material.LEATHER_LEGGINGS,
Material.LEATHER_BOOTS,
Material.NETHERITE_HELMET,
Material.NETHERITE_CHESTPLATE,
Material.NETHERITE_LEGGINGS,
Material.NETHERITE_BOOTS,
Material.TURTLE_HELMET
);
static final ItemMetaKey TRIM = new ItemMetaKey("Trim", "trim");
static final ItemMetaKey TRIM_MATERIAL = new ItemMetaKey("material");
static final ItemMetaKey TRIM_PATTERN = new ItemMetaKey("pattern");
private ArmorTrim trim;
CraftMetaArmor(CraftMetaItem meta) {
super(meta);
if (meta instanceof CraftMetaArmor armorMeta) {
this.trim = armorMeta.trim;
}
}
CraftMetaArmor(NBTTagCompound tag) {
super(tag);
if (tag.contains(TRIM.NBT)) {
NBTTagCompound trimCompound = tag.getCompound(TRIM.NBT);
if (trimCompound.contains(TRIM_MATERIAL.NBT) && trimCompound.contains(TRIM_PATTERN.NBT)) {
TrimMaterial trimMaterial = Registry.TRIM_MATERIAL.get(NamespacedKey.fromString(trimCompound.getString(TRIM_MATERIAL.NBT)));
TrimPattern trimPattern = Registry.TRIM_PATTERN.get(NamespacedKey.fromString(trimCompound.getString(TRIM_PATTERN.NBT)));
this.trim = new ArmorTrim(trimMaterial, trimPattern);
}
}
}
CraftMetaArmor(Map<String, Object> map) {
super(map);
Map<?, ?> trimData = SerializableMeta.getObject(Map.class, map, TRIM.BUKKIT, true);
if (trimData != null) {
String materialKeyString = SerializableMeta.getString(trimData, TRIM_MATERIAL.BUKKIT, true);
String patternKeyString = SerializableMeta.getString(trimData, TRIM_PATTERN.BUKKIT, true);
if (materialKeyString != null && patternKeyString != null) {
NamespacedKey materialKey = NamespacedKey.fromString(materialKeyString);
NamespacedKey patternKey = NamespacedKey.fromString(patternKeyString);
if (materialKey != null && patternKey != null) {
TrimMaterial trimMaterial = Registry.TRIM_MATERIAL.get(materialKey);
TrimPattern trimPattern = Registry.TRIM_PATTERN.get(patternKey);
if (trimMaterial != null && trimPattern != null) {
this.trim = new ArmorTrim(trimMaterial, trimPattern);
}
}
}
}
}
@Override
void applyToItem(NBTTagCompound itemTag) {
super.applyToItem(itemTag);
if (hasTrim()) {
NBTTagCompound trimCompound = new NBTTagCompound();
trimCompound.putString(TRIM_MATERIAL.NBT, trim.getMaterial().getKey().toString());
trimCompound.putString(TRIM_PATTERN.NBT, trim.getPattern().getKey().toString());
itemTag.put(TRIM.NBT, trimCompound);
}
}
@Override
boolean applicableTo(Material type) {
return ARMOR_MATERIALS.contains(type);
}
@Override
boolean equalsCommon(CraftMetaItem that) {
if (!super.equalsCommon(that)) {
return false;
}
if (that instanceof CraftMetaArmor armorMeta) {
return Objects.equals(trim, armorMeta.trim);
}
return true;
}
@Override
boolean notUncommon(CraftMetaItem meta) {
return super.notUncommon(meta) && (meta instanceof CraftMetaArmor || isArmorEmpty());
}
@Override
boolean isEmpty() {
return super.isEmpty() && isArmorEmpty();
}
private boolean isArmorEmpty() {
return !hasTrim();
}
@Override
int applyHash() {
final int original;
int hash = original = super.applyHash();
if (hasTrim()) {
hash = 61 * hash + trim.hashCode();
}
return original != hash ? CraftMetaArmor.class.hashCode() ^ hash : hash;
}
@Override
public CraftMetaArmor clone() {
CraftMetaArmor meta = (CraftMetaArmor) super.clone();
meta.trim = this.trim;
return meta;
}
@Override
Builder<String, Object> serialize(Builder<String, Object> builder) {
super.serialize(builder);
if (hasTrim()) {
Map<String, String> trimData = new HashMap<>();
trimData.put(TRIM_MATERIAL.BUKKIT, trim.getMaterial().getKey().toString());
trimData.put(TRIM_PATTERN.BUKKIT, trim.getPattern().getKey().toString());
builder.put(TRIM.BUKKIT, trimData);
}
return builder;
}
@Override
public boolean hasTrim() {
return trim != null;
}
@Override
public void setTrim(ArmorTrim trim) {
this.trim = trim;
}
@Override
public ArmorTrim getTrim() {
return trim;
}
}

View File

@ -0,0 +1,118 @@
package org.bukkit.craftbukkit.inventory;
import static org.bukkit.craftbukkit.inventory.CraftItemFactory.*;
import com.google.common.collect.ImmutableMap.Builder;
import com.google.common.collect.Sets;
import java.util.Map;
import java.util.Set;
import net.minecraft.nbt.NBTTagCompound;
import org.bukkit.Color;
import org.bukkit.Material;
import org.bukkit.configuration.serialization.DelegateDeserialization;
import org.bukkit.inventory.meta.ColorableArmorMeta;
@DelegateDeserialization(CraftMetaItem.SerializableMeta.class)
public class CraftMetaColorableArmor extends CraftMetaArmor implements ColorableArmorMeta {
private static final Set<Material> LEATHER_ARMOR_MATERIALS = Sets.newHashSet(
Material.LEATHER_HELMET,
Material.LEATHER_CHESTPLATE,
Material.LEATHER_LEGGINGS,
Material.LEATHER_BOOTS
);
private Color color = DEFAULT_LEATHER_COLOR;
CraftMetaColorableArmor(CraftMetaItem meta) {
super(meta);
CraftMetaLeatherArmor.readColor(this, meta);
}
CraftMetaColorableArmor(NBTTagCompound tag) {
super(tag);
CraftMetaLeatherArmor.readColor(this, tag);
}
CraftMetaColorableArmor(Map<String, Object> map) {
super(map);
CraftMetaLeatherArmor.readColor(this, map);
}
@Override
void applyToItem(NBTTagCompound itemTag) {
super.applyToItem(itemTag);
CraftMetaLeatherArmor.applyColor(this, itemTag);
}
@Override
boolean isEmpty() {
return super.isEmpty() && isLeatherArmorEmpty();
}
boolean isLeatherArmorEmpty() {
return !(hasColor());
}
@Override
boolean applicableTo(Material type) {
return LEATHER_ARMOR_MATERIALS.contains(type);
}
@Override
public CraftMetaColorableArmor clone() {
CraftMetaColorableArmor clone = (CraftMetaColorableArmor) super.clone();
clone.color = this.color;
return clone;
}
@Override
public Color getColor() {
return color;
}
@Override
public void setColor(Color color) {
this.color = color;
}
boolean hasColor() {
return CraftMetaLeatherArmor.hasColor(this);
}
@Override
Builder<String, Object> serialize(Builder<String, Object> builder) {
super.serialize(builder);
CraftMetaLeatherArmor.serialize(this, builder);
return builder;
}
@Override
boolean equalsCommon(CraftMetaItem meta) {
if (!super.equalsCommon(meta)) {
return false;
}
if (meta instanceof CraftMetaColorableArmor) {
CraftMetaColorableArmor that = (CraftMetaColorableArmor) meta;
return color.equals(that.color);
}
return true;
}
@Override
boolean notUncommon(CraftMetaItem meta) {
return super.notUncommon(meta) && (meta instanceof CraftMetaColorableArmor || isLeatherArmorEmpty());
}
@Override
int applyHash() {
final int original;
int hash = original = super.applyHash();
if (hasColor()) {
hash ^= color.hashCode();
}
return original != hash ? CraftMetaColorableArmor.class.hashCode() ^ hash : hash;
}
}

View File

@ -137,6 +137,7 @@ class CraftMetaItem implements ItemMeta, Damageable, Repairable, BlockDataMeta {
static {
classMap = ImmutableMap.<Class<? extends CraftMetaItem>, String>builder()
.put(CraftMetaArmor.class, "ARMOR")
.put(CraftMetaArmorStand.class, "ARMOR_STAND")
.put(CraftMetaBanner.class, "BANNER")
.put(CraftMetaBlockState.class, "TILE_ENTITY")
@ -144,6 +145,7 @@ class CraftMetaItem implements ItemMeta, Damageable, Repairable, BlockDataMeta {
.put(CraftMetaBookSigned.class, "BOOK_SIGNED")
.put(CraftMetaSkull.class, "SKULL")
.put(CraftMetaLeatherArmor.class, "LEATHER_ARMOR")
.put(CraftMetaColorableArmor.class, "COLORABLE_ARMOR")
.put(CraftMetaMap.class, "MAP")
.put(CraftMetaPotion.class, "POTION")
.put(CraftMetaSpawnEgg.class, "SPAWN_EGG")
@ -1391,6 +1393,9 @@ class CraftMetaItem implements ItemMeta, Damageable, Repairable, BlockDataMeta {
ATTRIBUTES_UUID_HIGH.NBT,
ATTRIBUTES_UUID_LOW.NBT,
ATTRIBUTES_SLOT.NBT,
CraftMetaArmor.TRIM.NBT,
CraftMetaArmor.TRIM_MATERIAL.NBT,
CraftMetaArmor.TRIM_PATTERN.NBT,
CraftMetaMap.MAP_SCALING.NBT,
CraftMetaMap.MAP_COLOR.NBT,
CraftMetaMap.MAP_ID.NBT,

View File

@ -30,40 +30,23 @@ class CraftMetaLeatherArmor extends CraftMetaItem implements LeatherArmorMeta {
CraftMetaLeatherArmor(CraftMetaItem meta) {
super(meta);
if (!(meta instanceof CraftMetaLeatherArmor)) {
return;
}
CraftMetaLeatherArmor armorMeta = (CraftMetaLeatherArmor) meta;
this.color = armorMeta.color;
readColor(this, meta);
}
CraftMetaLeatherArmor(NBTTagCompound tag) {
super(tag);
if (tag.contains(DISPLAY.NBT)) {
NBTTagCompound display = tag.getCompound(DISPLAY.NBT);
if (display.contains(COLOR.NBT)) {
try {
color = Color.fromRGB(display.getInt(COLOR.NBT));
} catch (IllegalArgumentException ex) {
// Invalid colour
}
}
}
readColor(this, tag);
}
CraftMetaLeatherArmor(Map<String, Object> map) {
super(map);
setColor(SerializableMeta.getObject(Color.class, map, COLOR.BUKKIT, true));
readColor(this, map);
}
@Override
void applyToItem(NBTTagCompound itemTag) {
super.applyToItem(itemTag);
if (hasColor()) {
setDisplayTag(itemTag, COLOR.NBT, NBTTagInt.valueOf(color.asRGB()));
}
applyColor(this, itemTag);
}
@Override
@ -96,16 +79,14 @@ class CraftMetaLeatherArmor extends CraftMetaItem implements LeatherArmorMeta {
}
boolean hasColor() {
return !DEFAULT_LEATHER_COLOR.equals(color);
return hasColor(this);
}
@Override
Builder<String, Object> serialize(Builder<String, Object> builder) {
super.serialize(builder);
if (hasColor()) {
builder.put(COLOR.BUKKIT, color);
}
serialize(this, builder);
return builder;
}
@ -135,6 +116,47 @@ class CraftMetaLeatherArmor extends CraftMetaItem implements LeatherArmorMeta {
if (hasColor()) {
hash ^= color.hashCode();
}
return original != hash ? CraftMetaSkull.class.hashCode() ^ hash : hash;
return original != hash ? CraftMetaLeatherArmor.class.hashCode() ^ hash : hash;
}
static void readColor(LeatherArmorMeta meta, CraftMetaItem other) {
if (!(other instanceof CraftMetaLeatherArmor armorMeta)) {
return;
}
meta.setColor(armorMeta.color);
}
static void readColor(LeatherArmorMeta meta, NBTTagCompound tag) {
if (tag.contains(DISPLAY.NBT)) {
NBTTagCompound display = tag.getCompound(DISPLAY.NBT);
if (display.contains(COLOR.NBT)) {
try {
meta.setColor(Color.fromRGB(display.getInt(COLOR.NBT)));
} catch (IllegalArgumentException ex) {
// Invalid colour
}
}
}
}
static void readColor(LeatherArmorMeta meta, Map<String, Object> map) {
meta.setColor(SerializableMeta.getObject(Color.class, map, COLOR.BUKKIT, true));
}
static boolean hasColor(LeatherArmorMeta meta) {
return !DEFAULT_LEATHER_COLOR.equals(meta.getColor());
}
static void applyColor(LeatherArmorMeta meta, NBTTagCompound tag) {
if (hasColor(meta)) {
((CraftMetaItem) meta).setDisplayTag(tag, COLOR.NBT, NBTTagInt.valueOf(meta.getColor().asRGB()));
}
}
static void serialize(LeatherArmorMeta meta, Builder<String, Object> builder) {
if (hasColor(meta)) {
builder.put(COLOR.BUKKIT, meta.getColor());
}
}
}

View File

@ -0,0 +1,26 @@
package org.bukkit.craftbukkit.inventory.trim;
import org.bukkit.NamespacedKey;
import org.bukkit.inventory.meta.trim.TrimMaterial;
import org.jetbrains.annotations.NotNull;
public class CraftTrimMaterial implements TrimMaterial {
private final NamespacedKey key;
private final net.minecraft.world.item.armortrim.TrimMaterial handle;
public CraftTrimMaterial(NamespacedKey key, net.minecraft.world.item.armortrim.TrimMaterial handle) {
this.key = key;
this.handle = handle;
}
@Override
@NotNull
public NamespacedKey getKey() {
return key;
}
public net.minecraft.world.item.armortrim.TrimMaterial getHandle() {
return handle;
}
}

View File

@ -0,0 +1,26 @@
package org.bukkit.craftbukkit.inventory.trim;
import org.bukkit.NamespacedKey;
import org.bukkit.inventory.meta.trim.TrimPattern;
import org.jetbrains.annotations.NotNull;
public class CraftTrimPattern implements TrimPattern {
private final NamespacedKey key;
private final net.minecraft.world.item.armortrim.TrimPattern handle;
public CraftTrimPattern(NamespacedKey key, net.minecraft.world.item.armortrim.TrimPattern handle) {
this.key = key;
this.handle = handle;
}
@Override
@NotNull
public NamespacedKey getKey() {
return key;
}
public net.minecraft.world.item.armortrim.TrimPattern getHandle() {
return handle;
}
}

View File

@ -35,12 +35,14 @@ import org.bukkit.enchantments.Enchantment;
import org.bukkit.entity.Axolotl;
import org.bukkit.entity.TropicalFish;
import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.meta.ArmorMeta;
import org.bukkit.inventory.meta.AxolotlBucketMeta;
import org.bukkit.inventory.meta.BannerMeta;
import org.bukkit.inventory.meta.BlockDataMeta;
import org.bukkit.inventory.meta.BlockStateMeta;
import org.bukkit.inventory.meta.BookMeta;
import org.bukkit.inventory.meta.BundleMeta;
import org.bukkit.inventory.meta.ColorableArmorMeta;
import org.bukkit.inventory.meta.CrossbowMeta;
import org.bukkit.inventory.meta.EnchantmentStorageMeta;
import org.bukkit.inventory.meta.FireworkEffectMeta;
@ -52,6 +54,9 @@ import org.bukkit.inventory.meta.MapMeta;
import org.bukkit.inventory.meta.PotionMeta;
import org.bukkit.inventory.meta.SpawnEggMeta;
import org.bukkit.inventory.meta.TropicalFishBucketMeta;
import org.bukkit.inventory.meta.trim.ArmorTrim;
import org.bukkit.inventory.meta.trim.TrimMaterial;
import org.bukkit.inventory.meta.trim.TrimPattern;
import org.bukkit.potion.PotionData;
import org.bukkit.potion.PotionEffectType;
import org.bukkit.potion.PotionType;
@ -243,7 +248,15 @@ public class ItemMetaTest extends AbstractTestingBase {
return cleanStack;
}
},
new StackProvider(Material.LEATHER_BOOTS) {
new StackProvider(Material.DIAMOND_CHESTPLATE) {
@Override ItemStack operate(final ItemStack cleanStack) {
final ArmorMeta meta = (ArmorMeta) cleanStack.getItemMeta();
meta.setTrim(new ArmorTrim(TrimMaterial.AMETHYST, TrimPattern.COAST));
cleanStack.setItemMeta(meta);
return cleanStack;
}
},
new StackProvider(Material.LEATHER_HORSE_ARMOR) {
@Override ItemStack operate(final ItemStack cleanStack) {
final LeatherArmorMeta meta = (LeatherArmorMeta) cleanStack.getItemMeta();
meta.setColor(Color.FUCHSIA);
@ -251,6 +264,15 @@ public class ItemMetaTest extends AbstractTestingBase {
return cleanStack;
}
},
new StackProvider(Material.LEATHER_CHESTPLATE) {
@Override ItemStack operate(final ItemStack cleanStack) {
final ColorableArmorMeta meta = (ColorableArmorMeta) cleanStack.getItemMeta();
meta.setTrim(new ArmorTrim(TrimMaterial.COPPER, TrimPattern.DUNE));
meta.setColor(Color.MAROON);
cleanStack.setItemMeta(meta);
return cleanStack;
}
},
new StackProvider(Material.POTION) {
@Override ItemStack operate(final ItemStack cleanStack) {
final PotionMeta meta = (PotionMeta) cleanStack.getItemMeta();