diff --git a/src/main/java/me/ulrich/structure/GAttributeModifier.java b/src/main/java/me/ulrich/structure/GAttributeModifier.java new file mode 100644 index 0000000..c8a6d6e --- /dev/null +++ b/src/main/java/me/ulrich/structure/GAttributeModifier.java @@ -0,0 +1,57 @@ +package me.ulrich.structure; + +import me.ulrich.structure.nbt.tag.CompoundTag; + +public class GAttributeModifier { + private String attributeName; + private String name; + private String slot; + private int operation; + private double amount; + private long UUIDMost; + private long UUIDLeast; + + public void read(CompoundTag tag) { + this.attributeName = tag.getString("AttributeName"); + this.name = tag.getString("Name"); + this.slot = tag.getString("Slot"); + this.operation = tag.getInt("Operation"); + this.amount = tag.getDouble("Amount"); + this.UUIDMost = tag.getLong("UUIDMost"); + this.UUIDLeast = tag.getLong("UUIDLeast"); + } + + public String getAttributeName() { + return attributeName; + } + + public String getName() { + return name; + } + + public String getSlot() { + return slot; + } + + public int getOperation() { + return operation; + } + + public double getAmount() { + return amount; + } + + public long getUUIDMost() { + return UUIDMost; + } + + public long getUUIDLeast() { + return UUIDLeast; + } + + public static GAttributeModifier readNewModifier(CompoundTag tag) { + GAttributeModifier m = new GAttributeModifier(); + m.read(tag); + return m; + } +} diff --git a/src/main/java/me/ulrich/structure/GBlock.java b/src/main/java/me/ulrich/structure/GBlock.java new file mode 100644 index 0000000..d3c808f --- /dev/null +++ b/src/main/java/me/ulrich/structure/GBlock.java @@ -0,0 +1,85 @@ +package me.ulrich.structure; + +import me.ulrich.structure.BlockEntityTag.BlockEntityTag; +import me.ulrich.structure.nbt.tag.CompoundTag; +import me.ulrich.structure.nbt.tag.IntTag; +import me.ulrich.structure.nbt.tag.ListTag; + +public class GBlock { + + // format from https://minecraft.gamepedia.com/Structure_block_file_format + + private int x; + private int y; + private int z; + private int state; + private BlockEntityTag nbt; + + public GBlock(int x, int y, int z, int state) { + this.x = x; + this.y = y; + this.z = z; + this.state = state; + } + + public GBlock() { + this.x = 0; + this.y = 0; + this.z = 0; + this.state = 0; + } + + public void read(CompoundTag tag) { + this.state = tag.getInt("state"); + + ListTag pos = tag.getListTag("pos").asIntTagList(); + this.x = pos.get(0).asInt(); + this.y = pos.get(1).asInt(); + this.z = pos.get(2).asInt(); + + if (tag.containsKey("nbt")) { + nbt = BlockEntityTag.readNewEntity(tag.getCompoundTag("nbt"), null); + } + } + + public BlockEntityTag getNBT() { + return nbt; + } + public int getX() { + return x; + } + + public int getY() { + return y; + } + + public int getZ() { + return z; + } + + public int getState() { + return state; + } + + public void setX(int x) { + this.x = x; + } + + public void setY(int y) { + this.y = y; + } + + public void setZ(int z) { + this.z = z; + } + + public void setState(int state) { + this.state = state; + } + + public static GBlock readNewBlock(CompoundTag tag) { + GBlock b = new GBlock(); + b.read(tag); + return b; + } +} diff --git a/src/main/java/me/ulrich/structure/GCustomPotionEffect.java b/src/main/java/me/ulrich/structure/GCustomPotionEffect.java new file mode 100644 index 0000000..4d6f086 --- /dev/null +++ b/src/main/java/me/ulrich/structure/GCustomPotionEffect.java @@ -0,0 +1,80 @@ +package me.ulrich.structure; + +import org.bukkit.potion.PotionEffect; +import org.bukkit.potion.PotionEffectType; + +import me.ulrich.structure.nbt.tag.CompoundTag; + +public class GCustomPotionEffect { + private byte id; + private byte amplifier; + private int duration; + private byte ambient; + private byte showParticles; + private byte showIcon; + + public void read(CompoundTag tag) { + id = tag.getByte("Id"); + amplifier = tag.getByte("Amplifier"); + duration = tag.getInt("Duration"); + ambient = tag.getByte("Ambient"); + showParticles = tag.getByte("ShowParticles"); + showIcon = tag.getByte("ShowIcon"); + } + + public byte getId() { + return id; + } + + public byte getAmplifier() { + return amplifier; + } + + public int getDuration() { + return duration; + } + + public byte getAmbient() { + return ambient; + } + + public byte getShowParticles() { + return showParticles; + } + + public byte getShowIcon() { + return showIcon; + } + + public static GCustomPotionEffect readNewEffect(CompoundTag tag) { + GCustomPotionEffect e = new GCustomPotionEffect(); + e.read(tag); + return e; + } + + @SuppressWarnings("deprecation") + public PotionEffect get() { + return new PotionEffect(PotionEffectType.getById(id), duration, (int)amplifier, getAmbientAsBoolean(), getShowParticlesAsBoolean(), getShowIconAsBoolean()); + } + + private boolean getShowIconAsBoolean() { + if (showIcon == 1) { + return true; + } + return false; + } + + private boolean getShowParticlesAsBoolean() { + if (showParticles == 1) { + return true; + } + return false; + } + + private boolean getAmbientAsBoolean() { + if (ambient == 1) { + return true; + } + return false; + } +} diff --git a/src/main/java/me/ulrich/structure/GDecoration.java b/src/main/java/me/ulrich/structure/GDecoration.java new file mode 100644 index 0000000..0d8aa7b --- /dev/null +++ b/src/main/java/me/ulrich/structure/GDecoration.java @@ -0,0 +1,45 @@ +package me.ulrich.structure; + +import me.ulrich.structure.nbt.tag.CompoundTag; + +public class GDecoration { + private String id; + private byte type; + private double x; + private double z; + private double rot; + + public void read(CompoundTag tag) { + id = tag.getString("id"); + type = tag.getByte("type"); + x = tag.getDouble("x"); + z = tag.getDouble("z"); + rot = tag.getDouble("rot"); + } + + public String getId() { + return id; + } + + public byte getType() { + return type; + } + + public double getX() { + return x; + } + + public double getZ() { + return z; + } + + public double getRot() { + return rot; + } + + public static GDecoration readNewDecoration(CompoundTag tag) { + GDecoration d = new GDecoration(); + d.read(tag); + return d; + } +} diff --git a/src/main/java/me/ulrich/structure/GEffect.java b/src/main/java/me/ulrich/structure/GEffect.java new file mode 100644 index 0000000..cdd59e4 --- /dev/null +++ b/src/main/java/me/ulrich/structure/GEffect.java @@ -0,0 +1,35 @@ +package me.ulrich.structure; + +import org.bukkit.potion.PotionEffect; +import org.bukkit.potion.PotionEffectType; + +import me.ulrich.structure.nbt.tag.CompoundTag; + +public class GEffect { + private byte effectId; + private int effectDuration; + + public void read(CompoundTag tag) { + effectId = tag.getByte("EffectId"); + effectDuration = tag.getInt("EffectDuration"); + } + + public byte getEffectId() { + return effectId; + } + + public int getEffectDuration() { + return effectDuration; + } + + public static GEffect readNewEffect(CompoundTag tag) { + GEffect e = new GEffect(); + e.read(tag); + return e; + } + + @SuppressWarnings("deprecation") + public PotionEffect get() { + return new PotionEffect(PotionEffectType.getById(effectId), effectDuration, 1); + } +} diff --git a/src/main/java/me/ulrich/structure/GEnchantment.java b/src/main/java/me/ulrich/structure/GEnchantment.java new file mode 100644 index 0000000..8d76e9e --- /dev/null +++ b/src/main/java/me/ulrich/structure/GEnchantment.java @@ -0,0 +1,34 @@ +package me.ulrich.structure; + +import org.bukkit.NamespacedKey; +import org.bukkit.enchantments.Enchantment; + +import me.ulrich.structure.nbt.tag.CompoundTag; + +public class GEnchantment { + private String id; + private int lvl; + + public String getId() { + return id; + } + + public int getLvl() { + return lvl; + } + + public void read(CompoundTag tag) { + this.id = tag.getString("id"); + this.lvl = tag.getShort("lvl"); + } + + public static GEnchantment readNewEnchantment(CompoundTag tag) { + GEnchantment e = new GEnchantment(); + e.read(tag); + return e; + } + + public Enchantment get() { + return Enchantment.getByKey(NamespacedKey.minecraft(id.replaceFirst("minecraft:", ""))); + } +} diff --git a/src/main/java/me/ulrich/structure/GEntity.java b/src/main/java/me/ulrich/structure/GEntity.java new file mode 100644 index 0000000..97ad0e1 --- /dev/null +++ b/src/main/java/me/ulrich/structure/GEntity.java @@ -0,0 +1,96 @@ +package me.ulrich.structure; + +import me.ulrich.structure.nbt.tag.CompoundTag; +import me.ulrich.structure.nbt.tag.DoubleTag; +import me.ulrich.structure.nbt.tag.IntTag; +import me.ulrich.structure.nbt.tag.ListTag; + +public class GEntity { + + // format from https://minecraft.gamepedia.com/Structure_block_file_format + + private double x; + private double y; + private double z; + + private int blockX; + private int blockY; + private int blockZ; + + private GEntityData entityData; + + public void setX(double x) { + this.x = x; + } + + public void setY(double y) { + this.y = y; + } + + public void setZ(double z) { + this.z = z; + } + + public void setBlockX(int blockX) { + this.blockX = blockX; + } + + public void setBlockY(int blockY) { + this.blockY = blockY; + } + + public void setBlockZ(int blockZ) { + this.blockZ = blockZ; + } + + public void setEntityData(GEntityData entityData) { + this.entityData = entityData; + } + + public double getX() { + return x; + } + + public double getY() { + return y; + } + + public double getZ() { + return z; + } + + public int getBlockX() { + return blockX; + } + + public int getBlockY() { + return blockY; + } + + public int getBlockZ() { + return blockZ; + } + + public GEntityData getEntityData() { + return entityData; + } + + public void read(CompoundTag tag) { + ListTag pos = tag.getListTag("pos").asDoubleTagList(); + this.x = pos.get(0).asDouble(); + this.y = pos.get(1).asDouble(); + this.z = pos.get(2).asDouble(); + ListTag blockPos = tag.getListTag("blockPos").asIntTagList(); + this.blockX = blockPos.get(0).asInt(); + this.blockY = blockPos.get(1).asInt(); + this.blockZ = blockPos.get(2).asInt(); + + this.entityData = GEntityData.readNewEntity(tag.getCompoundTag("nbt")); + } + + public static GEntity readNewEntity(CompoundTag tag) { + GEntity e = new GEntity(); + e.read(tag); + return e; + } +} diff --git a/src/main/java/me/ulrich/structure/GEntityData.java b/src/main/java/me/ulrich/structure/GEntityData.java new file mode 100644 index 0000000..7eeb794 --- /dev/null +++ b/src/main/java/me/ulrich/structure/GEntityData.java @@ -0,0 +1,338 @@ +package me.ulrich.structure; + +import java.util.ArrayList; +import java.util.List; + +import me.ulrich.structure.nbt.tag.CompoundTag; +import me.ulrich.structure.nbt.tag.DoubleTag; +import me.ulrich.structure.nbt.tag.FloatTag; +import me.ulrich.structure.nbt.tag.ListTag; +import me.ulrich.structure.nbt.tag.StringTag; + +public class GEntityData { + + // format from https://minecraft.gamepedia.com/Chunk_format#Entity_format + + private String id; + + private double x; + private double y; + private double z; + + private double dX; + private double dY; + private double dZ; + + private float yaw; + private float pitch; + + private float fallDistance; + private short fire; + private short air; + private byte onGround; + + private int dimension; + private byte invulnerable; + private int portalCooldown; + private long UUIDMost; + private long UUIDLeast; + + private String customName; + + private byte customNameVisible; + private byte silent; + + private List passengers; + + private byte glowing; + + private List tags; + + public GEntityData() { + passengers = new ArrayList(); + tags = new ArrayList(); + } + + public void setId(String id) { + this.id = id; + } + + public void setX(double x) { + this.x = x; + } + + public void setY(double y) { + this.y = y; + } + + public void setZ(double z) { + this.z = z; + } + + public void setDX(double dX) { + this.dX = dX; + } + + public void setDY(double dY) { + this.dY = dY; + } + + public void setDZ(double dZ) { + this.dZ = dZ; + } + + public void setYaw(float yaw) { + this.yaw = yaw; + } + + public void setPitch(float pitch) { + this.pitch = pitch; + } + + public void setFallDistance(float fallDistance) { + this.fallDistance = fallDistance; + } + + public void setFire(short fire) { + this.fire = fire; + } + + public void setAir(short air) { + this.air = air; + } + + public void setOnGround(byte onGround) { + this.onGround = onGround; + } + + public void setDimension(int dimension) { + this.dimension = dimension; + } + + public void setInvulnerable(byte invulnerable) { + this.invulnerable = invulnerable; + } + + public void setPortalCooldown(int portalCooldown) { + this.portalCooldown = portalCooldown; + } + + public void setUUIDMost(long UUIDMost) { + this.UUIDMost = UUIDMost; + } + + public void setUUIDLeast(long UUIDLeast) { + this.UUIDLeast = UUIDLeast; + } + + public void setCustomName(String customName) { + this.customName = customName; + } + + public void setCustomNameVisible(byte customNameVisible) { + this.customNameVisible = customNameVisible; + } + + public void setSilent(byte silent) { + this.silent = silent; + } + + public void setPassengers(List passengers) { + this.passengers = passengers; + } + + public void setGlowing(byte glowing) { + this.glowing = glowing; + } + + public void setTags(List tags) { + this.tags = tags; + } + + public String getId() { + return id; + } + + public double getX() { + return x; + } + + public double getY() { + return y; + } + + public double getZ() { + return z; + } + + public double getDX() { + return dX; + } + + public double getDY() { + return dY; + } + + public double getDZ() { + return dZ; + } + + public float getYaw() { + return yaw; + } + + public float getPitch() { + return pitch; + } + + public float getFallDistance() { + return fallDistance; + } + + public short getFire() { + return fire; + } + + public short getAir() { + return air; + } + + public byte getOnGround() { + return onGround; + } + + public int getDimension() { + return dimension; + } + + public byte getInvulnerable() { + return invulnerable; + } + + public boolean getInvulnerableAsBoolean() { + if (invulnerable == 1) { + return true; + } + return false; + } + + public int getPortalCooldown() { + return portalCooldown; + } + + public long getUUIDMost() { + return UUIDMost; + } + + public long getUUIDLeast() { + return UUIDLeast; + } + + public String getCustomName() { + return customName; + } + + public byte getCustomNameVisible() { + return customNameVisible; + } + + public boolean getCustomNameVisibleAsBoolean() { + if (customNameVisible == 1) { + return true; + } + return false; + } + + public byte getSilent() { + return silent; + } + + public boolean getSilentAsBoolean() { + if (silent == 1) { + return true; + } + return false; + } + + public List getPassengers() { + return passengers; + } + + public byte getGlowing() { + return glowing; + } + + public boolean getGlowingAsBoolean() { + if (glowing == 1) { + return true; + } + return false; + } + + public List getTags() { + return tags; + } + + public void read(CompoundTag tag) { + this.id = tag.getString("id"); + + ListTag pos = tag.getListTag("Pos").asDoubleTagList(); + this.x = pos.get(0).asDouble(); + this.y = pos.get(1).asDouble(); + this.z = pos.get(2).asDouble(); + + ListTag motion = tag.getListTag("Motion").asDoubleTagList(); + this.dX = motion.get(0).asDouble(); + this.dY = motion.get(1).asDouble(); + this.dZ = motion.get(2).asDouble(); + + ListTag rotation = tag.getListTag("Rotation").asFloatTagList(); + this.yaw = rotation.get(0).asFloat(); + this.pitch = rotation.get(1).asFloat(); + + this.fallDistance = tag.getFloat("FallDistance"); + + this.fire = tag.getShort("Fire"); + + this.air = tag.getShort("Air"); + + this.onGround = tag.getByte("OnGround"); + + this.dimension = tag.getInt("Dimension"); + + this.invulnerable = tag.getByte("Invulnerable"); + + this.portalCooldown = tag.getInt("PortalCooldown"); + + this.UUIDMost = tag.getLong("UUIDMost"); + this.UUIDLeast = tag.getLong("UUIDLeast"); + + this.customName = tag.getString("CustomName"); + + this.customNameVisible = tag.getByte("CustomNameVisible"); + + this.silent = tag.getByte("Silent"); + + if (tag.containsKey("Passengers")) { + ListTag Passengers = tag.getListTag("Passengers").asCompoundTagList(); + Passengers.forEach((Passenger) -> { + this.passengers.add(GEntityData.readNewEntity(Passenger)); + }); + } + + this.glowing = tag.getByte("Glowing"); + + if (tag.containsKey("Tags")) { + ListTag Tags = tag.getListTag("Tags").asStringTagList(); + Tags.forEach((TagE) -> { + tags.add(TagE.getValue()); + }); + } + } + + public static GEntityData readNewEntity(CompoundTag tag) { + GEntityData e = new GEntityData(); + e.read(tag); + return e; + } +} diff --git a/src/main/java/me/ulrich/structure/GExplosion.java b/src/main/java/me/ulrich/structure/GExplosion.java new file mode 100644 index 0000000..0682d4a --- /dev/null +++ b/src/main/java/me/ulrich/structure/GExplosion.java @@ -0,0 +1,86 @@ +package me.ulrich.structure; + +import java.util.ArrayList; +import java.util.List; + +import org.bukkit.FireworkEffect.Type; + +import me.ulrich.structure.nbt.tag.CompoundTag; + +public class GExplosion { + private byte flicker; + private byte trail; + private byte type; + private List colors; + private List fadeColors; + + public GExplosion() { + colors = new ArrayList(); + fadeColors = new ArrayList(); + } + + public byte getFlicker() { + return flicker; + } + + public byte getTrail() { + return trail; + } + + public byte getType() { + return type; + } + + public List getColors() { + return colors; + } + + public List getFadeColors() { + return fadeColors; + } + + public void read(CompoundTag tag) { + flicker = tag.getByte("Flicker"); + trail = tag.getByte("Trail"); + type = tag.getByte("Type"); + int[] colors = tag.getIntArray("Colors"); + for (int i = 0; i < colors.length; i++) { + this.colors.add(colors[i]); + } + int[] fadeColors = tag.getIntArray("FadeColors"); + for (int i = 0; i < fadeColors.length; i++) { + this.fadeColors.add(fadeColors[i]); + } + } + + public static GExplosion readNewExplosion(CompoundTag tag) { + GExplosion e = new GExplosion(); + e.read(tag); + return e; + } + + public boolean getFlickerAsBoolean() { + if (flicker == 1) { + return true; + } + return false; + } + + public Type getTypeAsType() { + switch (type) { + case 0: return Type.BALL; + case 1: return Type.BALL_LARGE; + case 2: return Type.STAR; + case 3: return Type.CREEPER; + case 4: return Type.BURST; + default: return Type.BALL; + } + } + + public boolean getTrailAsBoolean() { + if (trail == 1) { + return true; + } + return false; + } +} diff --git a/src/main/java/me/ulrich/structure/GHiveEntity.java b/src/main/java/me/ulrich/structure/GHiveEntity.java new file mode 100644 index 0000000..e745fb2 --- /dev/null +++ b/src/main/java/me/ulrich/structure/GHiveEntity.java @@ -0,0 +1,33 @@ +package me.ulrich.structure; + +import me.ulrich.structure.nbt.tag.CompoundTag; + +public class GHiveEntity { + private int minOccupationTicks; + private int ticksInHive; + private GEntityData entityData; + + public void read(CompoundTag tag) { + minOccupationTicks = tag.getInt("MinOccupationTicks"); + ticksInHive = tag.getInt("TicksInHive"); + entityData = GEntityData.readNewEntity(tag.getCompoundTag("EntityData")); + } + + public int getMinOccupationTicks() { + return minOccupationTicks; + } + + public int getTicksInHive() { + return ticksInHive; + } + + public GEntityData getEntityData() { + return entityData; + } + + public static GHiveEntity readNewEntity(CompoundTag tag) { + GHiveEntity e = new GHiveEntity(); + e.read(tag); + return e; + } +} diff --git a/src/main/java/me/ulrich/structure/GItem.java b/src/main/java/me/ulrich/structure/GItem.java new file mode 100644 index 0000000..8b7e172 --- /dev/null +++ b/src/main/java/me/ulrich/structure/GItem.java @@ -0,0 +1,403 @@ +package me.ulrich.structure; + +import java.util.ArrayList; +import java.util.List; +import java.util.UUID; + +import org.bukkit.Color; +import org.bukkit.FireworkEffect; +import org.bukkit.attribute.Attribute; +import org.bukkit.attribute.AttributeModifier; +import org.bukkit.block.BlockState; +import org.bukkit.block.Chest; +import org.bukkit.block.Container; +import org.bukkit.inventory.EquipmentSlot; +import org.bukkit.inventory.ItemStack; +import org.bukkit.inventory.meta.BlockStateMeta; +import org.bukkit.inventory.meta.BookMeta; +import org.bukkit.inventory.meta.CrossbowMeta; +import org.bukkit.inventory.meta.Damageable; +import org.bukkit.inventory.meta.EnchantmentStorageMeta; +import org.bukkit.inventory.meta.FireworkEffectMeta; +import org.bukkit.inventory.meta.FireworkMeta; +import org.bukkit.inventory.meta.ItemMeta; +import org.bukkit.inventory.meta.LeatherArmorMeta; +import org.bukkit.inventory.meta.MapMeta; +import org.bukkit.inventory.meta.PotionMeta; +import org.bukkit.inventory.meta.SkullMeta; +import org.bukkit.inventory.meta.SuspiciousStewMeta; +import org.bukkit.plugin.Plugin; + +import me.ulrich.structure.BlockEntityTag.BlockEntityTag; +import me.ulrich.structure.BlockEntityTag.ChestTag; +import me.ulrich.structure.ItemTag.AttributeModifiersTag; +import me.ulrich.structure.ItemTag.BlockTag; +import me.ulrich.structure.ItemTag.BookAndQuillTag; +import me.ulrich.structure.ItemTag.BucketOfFishTag; +import me.ulrich.structure.ItemTag.CrossbowsTag; +import me.ulrich.structure.ItemTag.DebugStickTag; +import me.ulrich.structure.ItemTag.DisplayPropertiesTag; +import me.ulrich.structure.ItemTag.EnchantmentTag; +import me.ulrich.structure.ItemTag.EntityTag; +import me.ulrich.structure.ItemTag.FireworkStarTag; +import me.ulrich.structure.ItemTag.FireworkTag; +import me.ulrich.structure.ItemTag.GeneralTag; +import me.ulrich.structure.ItemTag.ItemDataTag; +import me.ulrich.structure.ItemTag.MapTag; +import me.ulrich.structure.ItemTag.PlayerHeadTag; +import me.ulrich.structure.ItemTag.PotionEffectsTag; +import me.ulrich.structure.ItemTag.SuspiciousStewTag; +import me.ulrich.structure.ItemTag.WrittenBookTag; +import me.ulrich.structure.nbt.tag.CompoundTag; + +import org.json.JSONObject; + +public class GItem { + private byte count; + private byte slot; + private String id; + private List tags; + + public GItem() { + tags = new ArrayList(); + } + + public void read(CompoundTag tag) { + this.count = tag.getByte("Count"); + this.slot = tag.getByte("Slot"); + this.id = tag.getString("id"); + CompoundTag dataTag = tag.getCompoundTag("tag"); + if (dataTag != null) { + if (dataTag.containsKey("Damage")) { // general tag + GeneralTag t = new GeneralTag(); + t.read(dataTag); + tags.add(t); + } + if (dataTag.containsKey("CanPlaceOn") || dataTag.containsKey("BlockEntityTag") || dataTag.containsKey("BlockStateTag")) { + BlockTag t = new BlockTag(); + t.read(dataTag, id); + tags.add(t); + } + if (dataTag.containsKey("Enchantments") || dataTag.containsKey("StoredEnchantments")) { + EnchantmentTag t = new EnchantmentTag(); + t.read(dataTag); + tags.add(t); + } + if (dataTag.containsKey("AttributeModifiers")) { + AttributeModifiersTag t = new AttributeModifiersTag(); + t.read(dataTag); + tags.add(t); + } + if (dataTag.containsKey("CustomPotionEffects") || dataTag.containsKey("Potion")) { + PotionEffectsTag t = new PotionEffectsTag(); + t.read(dataTag); + tags.add(t); + } + if (dataTag.containsKey("Charged")) { + CrossbowsTag t = new CrossbowsTag(); + t.read(dataTag); + tags.add(t); + } + if (dataTag.containsKey("display")) { + DisplayPropertiesTag t = new DisplayPropertiesTag(); + t.read(dataTag); + tags.add(t); + } + if (dataTag.containsKey("pages")) { + if (dataTag.containsKey("title")) { + WrittenBookTag t = new WrittenBookTag(); + t.read(dataTag); + tags.add(t); + } else { + BookAndQuillTag t = new BookAndQuillTag(); + t.read(dataTag); + tags.add(t); + } + } + if (dataTag.containsKey("SkullOwner")) { + PlayerHeadTag t = new PlayerHeadTag(); + t.read(dataTag); + tags.add(t); + } + if (dataTag.containsKey("Fireworks")) { + FireworkTag t = new FireworkTag(); + t.read(dataTag); + tags.add(t); + } + if (dataTag.containsKey("Explosion")) { + FireworkStarTag t = new FireworkStarTag(); + t.read(dataTag); + tags.add(t); + } + if (dataTag.containsKey("EntityTag")) { + if (dataTag.containsKey("BucketVariantTag")) { + BucketOfFishTag t = new BucketOfFishTag(); + t.read(dataTag); + tags.add(t); + } else { + EntityTag t = new EntityTag(); + t.read(dataTag); + tags.add(t); + } + } + if (dataTag.containsKey("map")) { + MapTag t = new MapTag(); + t.read(dataTag); + tags.add(t); + } + if (dataTag.containsKey("Effects")) { + SuspiciousStewTag t = new SuspiciousStewTag(); + t.read(dataTag); + tags.add(t); + } + if (dataTag.containsKey("DebugProperty")) { + DebugStickTag t = new DebugStickTag(); + t.read(dataTag); + tags.add(t); + } + } + } + + public byte getCount() { + return count; + } + + public byte getSlot() { + return slot; + } + + public String getId() { + return id; + } + + public List getTags() { + return tags; + } + + public static GItem readNewItem(CompoundTag tag) { + GItem i = new GItem(); + i.read(tag); + return i; + } + + @SuppressWarnings("deprecation") + public ItemStack get(Plugin plugin) { + + if (XMaterial.matchXMaterial(id.replaceFirst("minecraft:", "")).get().parseMaterial() != null) { + ItemStack i = new ItemStack(XMaterial.matchXMaterial(id.replaceFirst("minecraft:", "")).get().parseMaterial(), count); + + this.tags.forEach((tag) -> { + org.bukkit.inventory.meta.ItemMeta im = i.getItemMeta(); + boolean setItemMeta = true; + switch(tag.getType()) { + case 0: + if (plugin.getServer().getVersion().contains("1.13") || plugin.getServer().getVersion().contains("1.14") || plugin.getServer().getVersion().contains("1.15")) { + AttributeModifiersTag t = (AttributeModifiersTag) tag; + t.getAttributeModifiers().forEach((modifier) -> { + AttributeModifier.Operation op = null; + switch(modifier.getOperation()) { + case 0: op = AttributeModifier.Operation.ADD_NUMBER; + case 1: op = AttributeModifier.Operation.ADD_SCALAR; + case 2: op = AttributeModifier.Operation.MULTIPLY_SCALAR_1; + } + AttributeModifier am = new AttributeModifier(new UUID(modifier.getUUIDMost(), modifier.getUUIDLeast()), modifier.getName(), modifier.getAmount(), op, EquipmentSlot.valueOf(modifier.getSlot())); + im.addAttributeModifier(Attribute.valueOf(modifier.getAttributeName()), am); + }); + } + break; + case 1: + if (true) { + BlockTag t = (BlockTag) tag; + BlockStateMeta bsm = (BlockStateMeta) i.getItemMeta(); + BlockState bs = bsm.getBlockState(); + t.getBlockStateTag().forEach((name, value) -> { + bs.setMetadata(name, new GMetaDataValue(value, plugin)); + }); + // TODO blockEntityTag + // TODO Make shulker boxes work. + BlockEntityTag bet = t.getBlockEntityTag(); + switch (bet.getId()) { + case "minecraft:shulker_box": + case "shulker_box": + ChestTag ct = (ChestTag) bet; + if (plugin.getServer().getVersion().contains("1.8") || plugin.getServer().getVersion().contains("1.9") || plugin.getServer().getVersion().contains("1.10") || plugin.getServer().getVersion().contains("1.11")) { + Chest box = (Chest) bs; + ct.getItems().forEach((item) -> { + box.getInventory().setItem(item.getSlot(), item.get(plugin)); + }); + } else { + Container box = (Container) bs; + ct.getItems().forEach((item) -> { + box.getInventory().setItem(item.getSlot(), item.get(plugin)); + }); + } + break; + } + bsm.setBlockState(bs); + i.setItemMeta((ItemMeta) bsm); + setItemMeta = false; + } + break; + case 2: + if (true) { + BookAndQuillTag t = (BookAndQuillTag) tag; + BookMeta bm = (BookMeta) im; + for (int ii = 0; ii < t.getPages().size(); ii++) { + bm.addPage(t.getPages().get(ii)); + } + } + break; + case 4: + if (plugin.getServer().getVersion().contains("1.14") || plugin.getServer().getVersion().contains("1.15")) { + CrossbowsTag t = (CrossbowsTag) tag; + CrossbowMeta cm = (CrossbowMeta) im; + t.getChargedProjectiles().forEach((projectile) -> { + cm.addChargedProjectile(projectile.get(plugin)); + }); + } + break; + case 6: + if (true) { + DisplayPropertiesTag t = (DisplayPropertiesTag) tag; + if (im instanceof LeatherArmorMeta) { + LeatherArmorMeta lam = (LeatherArmorMeta) im; + lam.setColor(Color.fromRGB(t.getColor())); + } + if (t.getName() != null && t.getName() != "") { + im.setDisplayName(new JSONObject(t.getName()).getString("text")); + } + im.setLore(t.getLore()); + // TODO hideflags + } + break; + case 7: + if (true) { + EnchantmentTag t = (EnchantmentTag) tag; + if (im instanceof EnchantmentStorageMeta) { + EnchantmentStorageMeta esm = (EnchantmentStorageMeta) im; + t.getStoredEnchantments().forEach((storedEnchantment) -> { + esm.addStoredEnchant(storedEnchantment.get(), storedEnchantment.getLvl(), true); + }); + } else { + t.getEnchantments().forEach((enchantment) -> { + im.addEnchant(enchantment.get(), enchantment.getLvl(), true); + }); + } + // TODO repair cost + } + break; + // TODO spawn eggs + case 9: + if (true) { + FireworkTag t = (FireworkTag) tag; + FireworkMeta fm = (FireworkMeta) im; + t.getExplosions().forEach((explosion) -> { + List c = new ArrayList(); + List fc = new ArrayList(); + explosion.getColors().forEach((color) -> { + c.add(Color.fromRGB(color)); + }); + explosion.getFadeColors().forEach((fadeColor) -> { + fc.add(Color.fromRGB(fadeColor)); + }); + FireworkEffect e = FireworkEffect.builder().flicker(explosion.getFlickerAsBoolean()).withColor(c).withFade(fc).with(explosion.getTypeAsType()).trail(explosion.getTrailAsBoolean()).build(); + fm.addEffect(e); + }); + } + break; + case 10: + if (true) { + GeneralTag t = (GeneralTag) tag; + if ((plugin.getServer().getVersion().contains("1.13") || plugin.getServer().getVersion().contains("1.14") || plugin.getServer().getVersion().contains("1.15")) && im instanceof Damageable) { + ((Damageable)im).setDamage(t.getDamage()); + } + if (plugin.getServer().getVersion().contains("1.11") || plugin.getServer().getVersion().contains("1.12") || plugin.getServer().getVersion().contains("1.13") || plugin.getServer().getVersion().contains("1.14") || plugin.getServer().getVersion().contains("1.15")) { + im.setUnbreakable(t.getUnbreakableAsBoolean()); + } + if (plugin.getServer().getVersion().contains("1.14") || plugin.getServer().getVersion().contains("1.15")) { + im.setCustomModelData(t.getCustomModelData()); + } + // TODO can destroy + } + break; + case 11: + if (true) { + MapTag t = (MapTag) tag; + MapMeta mm = (MapMeta) im; + if (plugin.getServer().getVersion().contains("1.13") || plugin.getServer().getVersion().contains("1.14") || plugin.getServer().getVersion().contains("1.15")) { + mm.setMapId(t.getMap()); + } + if (plugin.getServer().getVersion().contains("1.11") || plugin.getServer().getVersion().contains("1.12") || plugin.getServer().getVersion().contains("1.13") || plugin.getServer().getVersion().contains("1.14") || plugin.getServer().getVersion().contains("1.15")) { + mm.setColor(Color.fromRGB(t.getMapColor())); + } + // TODO map scale + } + break; + case 12: + if (true) { + PlayerHeadTag t = (PlayerHeadTag) tag; + SkullMeta sm = (SkullMeta) im; + sm.setOwner(t.getSkullOwner()); + } + break; + case 13: + if (true) { + PotionEffectsTag t = (PotionEffectsTag) tag; + PotionMeta pm = (PotionMeta) im; + if (t.getCustomPotionColor() != 0 && (plugin.getServer().getVersion().contains("1.11") || plugin.getServer().getVersion().contains("1.12") || plugin.getServer().getVersion().contains("1.13") || plugin.getServer().getVersion().contains("1.14") || plugin.getServer().getVersion().contains("1.15"))) { + pm.setColor(Color.fromRGB(t.getCustomPotionColor())); + } + if (t.getPotion() != null && !plugin.getServer().getVersion().contains("1.8")) { + pm.setBasePotionData(t.getPotionData()); + } + t.getCustomPotionEffects().forEach((potionEffect) -> { + pm.addCustomEffect(potionEffect.get(), true); + }); + } + break; + case 14: + if (plugin.getServer().getVersion().contains("1.14") || plugin.getServer().getVersion().contains("1.15")) { + SuspiciousStewTag t = (SuspiciousStewTag) tag; + SuspiciousStewMeta sm = (SuspiciousStewMeta) im; + t.getEffects().forEach((effect) -> { + sm.addCustomEffect(effect.get(), true); + }); + } + break; + case 15: + if (true) { + WrittenBookTag t = (WrittenBookTag) tag; + BookMeta bm = (BookMeta) im; + for (int ii = 0; ii < t.getPages().size(); ii++) { + bm.addPage(new JSONObject(t.getPages().get(ii)).getString("text")); + } + bm.setAuthor(t.getAuthor()); + bm.setTitle(t.getTitle()); + } + break; + case 16: + if (true) { + FireworkStarTag t = (FireworkStarTag) tag; + FireworkEffectMeta fm = (FireworkEffectMeta) im; + List c = new ArrayList(); + List fc = new ArrayList(); + t.getExplosion().getColors().forEach((color) -> { + c.add(Color.fromRGB(color)); + }); + t.getExplosion().getFadeColors().forEach((fadeColor) -> { + fc.add(Color.fromRGB(fadeColor)); + }); + FireworkEffect e = FireworkEffect.builder().flicker(t.getExplosion().getFlickerAsBoolean()).withColor(c).withFade(fc).with(t.getExplosion().getTypeAsType()).trail(t.getExplosion().getTrailAsBoolean()).build(); + fm.setEffect(e); + } + break; + } + if (setItemMeta) { + i.setItemMeta(im); + } + }); + return i; + } + return null; + } +} diff --git a/src/main/java/me/ulrich/structure/GMetaDataValue.java b/src/main/java/me/ulrich/structure/GMetaDataValue.java new file mode 100644 index 0000000..f2a9754 --- /dev/null +++ b/src/main/java/me/ulrich/structure/GMetaDataValue.java @@ -0,0 +1,72 @@ +package me.ulrich.structure; + +import org.bukkit.metadata.MetadataValue; +import org.bukkit.plugin.Plugin; + +public class GMetaDataValue implements MetadataValue { + + String value; + Plugin plugin; + + public GMetaDataValue(String value, Plugin plugin) { + this.value = value; + this.plugin = plugin; + } + + @Override + public boolean asBoolean() { + if (value.equalsIgnoreCase("true") || value.equalsIgnoreCase("1")) return true; + return false; + } + + @Override + public byte asByte() { + return Byte.valueOf(value); + } + + @Override + public double asDouble() { + return Double.valueOf(value); + } + + @Override + public float asFloat() { + return Float.valueOf(value); + } + + @Override + public int asInt() { + return Integer.valueOf(value); + } + + @Override + public long asLong() { + return Long.valueOf(value); + } + + @Override + public short asShort() { + return Short.valueOf(value); + } + + @Override + public String asString() { + return value; + } + + @Override + public Plugin getOwningPlugin() { + return plugin; + } + + @Override + public void invalidate() { + return; + } + + @Override + public Object value() { + return (Object)value; + } + +} diff --git a/src/main/java/me/ulrich/structure/GPalette.java b/src/main/java/me/ulrich/structure/GPalette.java new file mode 100644 index 0000000..0ae5a03 --- /dev/null +++ b/src/main/java/me/ulrich/structure/GPalette.java @@ -0,0 +1,32 @@ +package me.ulrich.structure; + +import java.util.HashMap; + +public class GPalette { + + // format from https://minecraft.gamepedia.com/Structure_block_file_format + + String name; + + HashMap properties; + + public GPalette() { + properties = new HashMap(); + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public void setProperty(String key, String value) { + properties.put(key, value); + } + + public HashMap getProperties() { + return properties; + } +} diff --git a/src/main/java/me/ulrich/structure/GPattern.java b/src/main/java/me/ulrich/structure/GPattern.java new file mode 100644 index 0000000..0786182 --- /dev/null +++ b/src/main/java/me/ulrich/structure/GPattern.java @@ -0,0 +1,35 @@ +package me.ulrich.structure; + +import org.bukkit.DyeColor; +import org.bukkit.block.banner.Pattern; +import org.bukkit.block.banner.PatternType; + +import me.ulrich.structure.nbt.tag.CompoundTag; + +public class GPattern { + private int color; + private String pattern; + + public void read(CompoundTag tag) { + color = tag.getInt("Color"); + pattern = tag.getString("Pattern"); + } + + public int getColor() { + return color; + } + + public String getPattern() { + return pattern; + } + + public static GPattern readNewPattern(CompoundTag tag) { + GPattern p = new GPattern(); + p.read(tag); + return p; + } + + public Pattern get() { + return new Pattern(DyeColor.values()[color], PatternType.getByIdentifier(pattern)); + } +} diff --git a/src/main/java/me/ulrich/structure/GSpawnPotential.java b/src/main/java/me/ulrich/structure/GSpawnPotential.java new file mode 100644 index 0000000..22d1887 --- /dev/null +++ b/src/main/java/me/ulrich/structure/GSpawnPotential.java @@ -0,0 +1,27 @@ +package me.ulrich.structure; + +import me.ulrich.structure.nbt.tag.CompoundTag; + +public class GSpawnPotential { + private GEntityData entity; + private int weight; + + public void read(CompoundTag tag) { + entity = GEntityData.readNewEntity(tag.getCompoundTag("Entity")); + weight = tag.getInt("Weight"); + } + + public GEntityData getEntity() { + return entity; + } + + public int getWeight() { + return weight; + } + + public static GSpawnPotential readNewPotential(CompoundTag tag) { + GSpawnPotential p = new GSpawnPotential(); + p.read(tag); + return p; + } +} diff --git a/src/main/java/me/ulrich/structure/StructurePaintEvent.java b/src/main/java/me/ulrich/structure/StructurePaintEvent.java new file mode 100644 index 0000000..c27e49e --- /dev/null +++ b/src/main/java/me/ulrich/structure/StructurePaintEvent.java @@ -0,0 +1,67 @@ +package me.ulrich.structure; + +import org.bukkit.Location; +import org.bukkit.block.Block; +import org.bukkit.event.Event; +import org.bukkit.event.HandlerList; + +import me.ulrich.structure.BlockEntityTag.BlockEntityTag; + + +public class StructurePaintEvent extends Event { + + private boolean cancelled = false; + private Location location; + private GPalette palette; + private BlockEntityTag blockEntityTag; + private int buildKey; + private Block block; + + public StructurePaintEvent(Location location, GPalette palette, BlockEntityTag blockEntityTag, int buildKey, Block block) { + this.location = location; + this.palette = palette; + this.blockEntityTag = blockEntityTag; + this.buildKey = buildKey; + this.block = block; + } + + public int getBuildKey() { + return buildKey; + } + + private static final HandlerList HANDLERS = new HandlerList(); + + public HandlerList getHandlers() { + return HANDLERS; + } + + public static HandlerList getHandlerList() { + return HANDLERS; + } + + public boolean getCancelled() { + return cancelled; + } + + public void setCancelled(boolean cancelled) { + this.cancelled = cancelled; + } + + public Location getLocation() { + return location; + } + + public GPalette getPalette() { + return palette; + } + + public BlockEntityTag getBlockEntityTag() { + return blockEntityTag; + } + + public Block getBlock() { + return block; + } + + +} \ No newline at end of file diff --git a/src/main/java/me/ulrich/structure/XMaterial.java b/src/main/java/me/ulrich/structure/XMaterial.java new file mode 100644 index 0000000..ebe4cd6 --- /dev/null +++ b/src/main/java/me/ulrich/structure/XMaterial.java @@ -0,0 +1,2155 @@ +package me.ulrich.structure; + +/* + * The MIT License (MIT) + * + * Copyright (c) 2018 Hex_27 + * Copyright (c) 2021 Crypto Morin + * + * 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. + */ + + +import com.google.common.base.Enums; +import com.google.common.cache.Cache; +import com.google.common.cache.CacheBuilder; +import com.google.common.cache.CacheLoader; +import com.google.common.cache.LoadingCache; +import org.apache.commons.lang.StringUtils; +import org.apache.commons.lang.Validate; +import org.apache.commons.lang.WordUtils; +import org.bukkit.Bukkit; +import org.bukkit.Material; +import org.bukkit.inventory.ItemStack; + +import javax.annotation.Nonnull; +import javax.annotation.Nullable; +import java.util.*; +import java.util.concurrent.TimeUnit; +import java.util.regex.Matcher; +import java.util.regex.Pattern; +import java.util.regex.PatternSyntaxException; + +/** + * XMaterial - Data Values/Pre-flattening
+ * 1.13 and above as priority. + *

+ * This class is mainly designed to support {@link ItemStack}. If you want to use it on blocks, you'll have to use + * XBlock + *

+ * Pre-flattening: https://minecraft.gamepedia.com/Java_Edition_data_values/Pre-flattening + * Materials: https://hub.spigotmc.org/javadocs/spigot/org/bukkit/Material.html + * Materials (1.12): https://helpch.at/docs/1.12.2/index.html?org/bukkit/Material.html + * Material IDs: https://minecraft-ids.grahamedgecombe.com/ + * Material Source Code: https://hub.spigotmc.org/stash/projects/SPIGOT/repos/bukkit/browse/src/main/java/org/bukkit/Material.java + * XMaterial v1: https://www.spigotmc.org/threads/329630/ + *

+ * This class will throw a "unsupported material" error if someone tries to use an item with an invalid data value which can only happen in 1.12 servers and below or when the + * utility is missing a new material in that specific version. + * To get an invalid item, (aka Missing Texture Block) you can use the command + * /give @p minecraft:dirt 1 10 where 1 is the item amount, and 10 is the data value. The material {@link #DIRT} with a data value of {@code 10} doesn't exist. + * + * @author Crypto Morin + * @version 10.1.1 + * @see Material + * @see ItemStack + */ +public enum XMaterial { + ACACIA_BOAT("BOAT_ACACIA"), + ACACIA_BUTTON("WOOD_BUTTON"), + ACACIA_DOOR("ACACIA_DOOR", "ACACIA_DOOR_ITEM"), + ACACIA_FENCE, + ACACIA_FENCE_GATE, + ACACIA_LEAVES("LEAVES_2"), + ACACIA_LOG("LOG_2"), + ACACIA_PLANKS(4, "WOOD"), + ACACIA_PRESSURE_PLATE("WOOD_PLATE"), + ACACIA_SAPLING(4, "SAPLING"), + ACACIA_SIGN("SIGN_POST", "SIGN"), + ACACIA_SLAB(4, "WOOD_DOUBLE_STEP", "WOOD_STEP", "WOODEN_SLAB"), + ACACIA_STAIRS, + ACACIA_TRAPDOOR("TRAP_DOOR"), + ACACIA_WALL_SIGN("WALL_SIGN"), + ACACIA_WOOD("LOG_2"), + ACTIVATOR_RAIL, + /** + * https://minecraft.gamepedia.com/Air + * {@link Material#isAir()} + * + * @see #VOID_AIR + * @see #CAVE_AIR + */ + AIR, + ALLIUM(2, "RED_ROSE"), + AMETHYST_BLOCK, + AMETHYST_CLUSTER, + AMETHYST_SHARD, + ANCIENT_DEBRIS, + ANDESITE(5, "STONE"), + ANDESITE_SLAB, + ANDESITE_STAIRS, + ANDESITE_WALL, + ANVIL, + APPLE, + ARMOR_STAND, + ARROW, + ATTACHED_MELON_STEM(7, "MELON_STEM"), + ATTACHED_PUMPKIN_STEM(7, "PUMPKIN_STEM"), + AXOLOTL_BUCKET, + AXOLOTL_SPAWN_EGG, + AZALEA, + AZALEA_LEAVES, + AZURE_BLUET(3, "RED_ROSE"), + BAKED_POTATO, + BAMBOO, + BAMBOO_SAPLING, + BARREL, + BARRIER, + BASALT, + BAT_SPAWN_EGG(65, "MONSTER_EGG"), + BEACON, + BEDROCK, + BEEF("RAW_BEEF"), + BEEHIVE, + /** + * Beetroot is a known material in pre-1.13 + */ + BEETROOT("BEETROOT_BLOCK"), + BEETROOTS("BEETROOT"), + BEETROOT_SEEDS, + BEETROOT_SOUP, + BEE_NEST, + BEE_SPAWN_EGG, + BELL, + BIG_DRIPLEAF, + BIG_DRIPLEAF_STEM, + BIRCH_BOAT("BOAT_BIRCH"), + BIRCH_BUTTON("WOOD_BUTTON"), + BIRCH_DOOR("BIRCH_DOOR", "BIRCH_DOOR_ITEM"), + BIRCH_FENCE, + BIRCH_FENCE_GATE, + BIRCH_LEAVES(2, "LEAVES"), + BIRCH_LOG(2, "LOG"), + BIRCH_PLANKS(2, "WOOD"), + BIRCH_PRESSURE_PLATE("WOOD_PLATE"), + BIRCH_SAPLING(2, "SAPLING"), + BIRCH_SIGN("SIGN_POST", "SIGN"), + BIRCH_SLAB(2, "WOOD_DOUBLE_STEP", "WOOD_STEP", "WOODEN_SLAB"), + BIRCH_STAIRS("BIRCH_WOOD_STAIRS"), + BIRCH_TRAPDOOR("TRAP_DOOR"), + BIRCH_WALL_SIGN("WALL_SIGN"), + BIRCH_WOOD(2, "LOG"), + BLACKSTONE, + BLACKSTONE_SLAB, + BLACKSTONE_STAIRS, + BLACKSTONE_WALL, + BLACK_BANNER("STANDING_BANNER", "BANNER"), + /** + * Version 1.12+ interprets "BED" as BLACK_BED due to enum alphabetic ordering. + */ + BLACK_BED(supports(12) ? 15 : 0, "BED_BLOCK", "BED"), + BLACK_CANDLE, + BLACK_CANDLE_CAKE, + BLACK_CARPET(15, "CARPET"), + BLACK_CONCRETE(15, "CONCRETE"), + BLACK_CONCRETE_POWDER(15, "CONCRETE_POWDER"), + BLACK_DYE("INK_SACK", "INK_SAC"), + BLACK_GLAZED_TERRACOTTA, + BLACK_SHULKER_BOX, + BLACK_STAINED_GLASS(15, "STAINED_GLASS"), + BLACK_STAINED_GLASS_PANE(15, "STAINED_GLASS_PANE"), + BLACK_TERRACOTTA(15, "STAINED_CLAY"), + BLACK_WALL_BANNER("WALL_BANNER"), + BLACK_WOOL(15, "WOOL"), + BLAST_FURNACE, + BLAZE_POWDER, + BLAZE_ROD, + BLAZE_SPAWN_EGG(61, "MONSTER_EGG"), + BLUE_BANNER(4, "STANDING_BANNER", "BANNER"), + BLUE_BED(supports(12) ? 11 : 0, "BED_BLOCK", "BED"), + BLUE_CANDLE, + BLUE_CANDLE_CAKE, + BLUE_CARPET(11, "CARPET"), + BLUE_CONCRETE(11, "CONCRETE"), + BLUE_CONCRETE_POWDER(11, "CONCRETE_POWDER"), + BLUE_DYE(4, "INK_SACK", "LAPIS_LAZULI"), + BLUE_GLAZED_TERRACOTTA, + BLUE_ICE, + BLUE_ORCHID(1, "RED_ROSE"), + BLUE_SHULKER_BOX, + BLUE_STAINED_GLASS(11, "STAINED_GLASS"), + BLUE_STAINED_GLASS_PANE(11, "THIN_GLASS", "STAINED_GLASS_PANE"), + BLUE_TERRACOTTA(11, "STAINED_CLAY"), + BLUE_WALL_BANNER(4, "WALL_BANNER"), + BLUE_WOOL(11, "WOOL"), + BONE, + BONE_BLOCK, + BONE_MEAL(15, "INK_SACK"), + BOOK, + BOOKSHELF, + BOW, + BOWL, + BRAIN_CORAL, + BRAIN_CORAL_BLOCK, + BRAIN_CORAL_FAN, + BRAIN_CORAL_WALL_FAN, + BREAD, + BREWING_STAND("BREWING_STAND", "BREWING_STAND_ITEM"), + BRICK("CLAY_BRICK"), + BRICKS("BRICKS", "BRICK"), + BRICK_SLAB(4, "STEP"), + BRICK_STAIRS, + BRICK_WALL, + BROWN_BANNER(3, "STANDING_BANNER", "BANNER"), + BROWN_BED(supports(12) ? 12 : 0, "BED_BLOCK", "BED"), + BROWN_CANDLE, + BROWN_CANDLE_CAKE, + BROWN_CARPET(12, "CARPET"), + BROWN_CONCRETE(12, "CONCRETE"), + BROWN_CONCRETE_POWDER(12, "CONCRETE_POWDER"), + BROWN_DYE(3, "INK_SACK", "DYE", "COCOA_BEANS"), + BROWN_GLAZED_TERRACOTTA, + BROWN_MUSHROOM, + BROWN_MUSHROOM_BLOCK("BROWN_MUSHROOM", "HUGE_MUSHROOM_1"), + BROWN_SHULKER_BOX, + BROWN_STAINED_GLASS(12, "STAINED_GLASS"), + BROWN_STAINED_GLASS_PANE(12, "THIN_GLASS", "STAINED_GLASS_PANE"), + BROWN_TERRACOTTA(12, "STAINED_CLAY"), + BROWN_WALL_BANNER(3, "WALL_BANNER"), + BROWN_WOOL(12, "WOOL"), + BUBBLE_COLUMN, + BUBBLE_CORAL, + BUBBLE_CORAL_BLOCK, + BUBBLE_CORAL_FAN, + BUBBLE_CORAL_WALL_FAN, + BUCKET, + BUDDING_AMETHYST, + BUNDLE, + CACTUS, + CAKE("CAKE_BLOCK"), + CALCITE, + CAMPFIRE, + CANDLE, + CANDLE_CAKE, + CARROT("CARROT_ITEM"), + CARROTS("CARROT"), + CARROT_ON_A_STICK("CARROT_STICK"), + CARTOGRAPHY_TABLE, + CARVED_PUMPKIN, + CAT_SPAWN_EGG, + CAULDRON("CAULDRON", "CAULDRON_ITEM"), + /** + * 1.13 tag is not added because it's the same thing as {@link #AIR} + * + * @see #VOID_AIR + */ + CAVE_AIR("AIR"), + CAVE_SPIDER_SPAWN_EGG(59, "MONSTER_EGG"), + CAVE_VINES, + CAVE_VINES_PLANT, + CHAIN, + CHAINMAIL_BOOTS, + CHAINMAIL_CHESTPLATE, + CHAINMAIL_HELMET, + CHAINMAIL_LEGGINGS, + CHAIN_COMMAND_BLOCK("COMMAND", "COMMAND_CHAIN"), + CHARCOAL(1, "COAL"), + CHEST("LOCKED_CHEST"), + CHEST_MINECART("STORAGE_MINECART"), + CHICKEN("RAW_CHICKEN"), + CHICKEN_SPAWN_EGG(93, "MONSTER_EGG"), + CHIPPED_ANVIL(1, "ANVIL"), + CHISELED_DEEPSLATE, + CHISELED_NETHER_BRICKS(1, "NETHER_BRICKS"), + CHISELED_POLISHED_BLACKSTONE("POLISHED_BLACKSTONE"), + CHISELED_QUARTZ_BLOCK(1, "QUARTZ_BLOCK"), + CHISELED_RED_SANDSTONE(1, "RED_SANDSTONE"), + CHISELED_SANDSTONE(1, "SANDSTONE"), + CHISELED_STONE_BRICKS(3, "SMOOTH_BRICK"), + CHORUS_FLOWER, + CHORUS_FRUIT, + CHORUS_PLANT, + CLAY("HARD_CLAY"), + CLAY_BALL, + CLOCK("WATCH"), + COAL, + COAL_BLOCK, + COAL_ORE, + COARSE_DIRT(1, "DIRT"), + COBBLED_DEEPSLATE, + COBBLED_DEEPSLATE_SLAB, + COBBLED_DEEPSLATE_STAIRS, + COBBLED_DEEPSLATE_WALL, + COBBLESTONE, + COBBLESTONE_SLAB(3, "STEP"), + COBBLESTONE_STAIRS, + COBBLESTONE_WALL("COBBLE_WALL"), + COBWEB("WEB"), + COCOA, + COCOA_BEANS(3, "INK_SACK"), + COD("RAW_FISH"), + COD_BUCKET, + COD_SPAWN_EGG, + COMMAND_BLOCK("COMMAND"), + COMMAND_BLOCK_MINECART("COMMAND_MINECART"), + /** + * Unlike redstone torch and redstone lamp... neither REDTONE_COMPARATOR_OFF nor REDSTONE_COMPARATOR_ON + * are items. REDSTONE_COMPARATOR is. + * + * @see #REDSTONE_TORCH + * @see #REDSTONE_LAMP + */ + COMPARATOR("REDSTONE_COMPARATOR_OFF", "REDSTONE_COMPARATOR_ON", "REDSTONE_COMPARATOR"), + COMPASS, + COMPOSTER, + CONDUIT, + COOKED_BEEF, + COOKED_CHICKEN, + COOKED_COD("COOKED_FISH"), + COOKED_MUTTON, + COOKED_PORKCHOP("PORK", "GRILLED_PORK"), + COOKED_RABBIT, + COOKED_SALMON(1, "COOKED_FISH"), + COOKIE, + COPPER_BLOCK, + COPPER_INGOT, + COPPER_ORE, + CORNFLOWER, + COW_SPAWN_EGG(92, "MONSTER_EGG"), + CRACKED_DEEPSLATE_BRICKS, + CRACKED_DEEPSLATE_TILES, + CRACKED_NETHER_BRICKS(2, "NETHER_BRICKS"), + CRACKED_POLISHED_BLACKSTONE_BRICKS("POLISHED_BLACKSTONE_BRICKS"), + CRACKED_STONE_BRICKS(2, "SMOOTH_BRICK"), + CRAFTING_TABLE("WORKBENCH"), + CREEPER_BANNER_PATTERN, + CREEPER_HEAD(4, "SKULL", "SKULL_ITEM"), + CREEPER_SPAWN_EGG(50, "MONSTER_EGG"), + CREEPER_WALL_HEAD(4, "SKULL", "SKULL_ITEM"), + CRIMSON_BUTTON, + CRIMSON_DOOR, + CRIMSON_FENCE, + CRIMSON_FENCE_GATE, + CRIMSON_FUNGUS, + CRIMSON_HYPHAE, + CRIMSON_NYLIUM, + CRIMSON_PLANKS, + CRIMSON_PRESSURE_PLATE, + CRIMSON_ROOTS, + CRIMSON_SIGN("SIGN_POST"), + CRIMSON_SLAB, + CRIMSON_STAIRS, + CRIMSON_STEM, + CRIMSON_TRAPDOOR, + CRIMSON_WALL_SIGN("WALL_SIGN"), + CROSSBOW, + CRYING_OBSIDIAN, + CUT_COPPER, + CUT_COPPER_SLAB, + CUT_COPPER_STAIRS, + CUT_RED_SANDSTONE, + CUT_RED_SANDSTONE_SLAB("STONE_SLAB2"), + CUT_SANDSTONE, + CUT_SANDSTONE_SLAB("STEP"), + CYAN_BANNER(6, "STANDING_BANNER", "BANNER"), + CYAN_BED(supports(12) ? 9 : 0, "BED_BLOCK", "BED"), + CYAN_CANDLE, + CYAN_CANDLE_CAKE, + CYAN_CARPET(9, "CARPET"), + CYAN_CONCRETE(9, "CONCRETE"), + CYAN_CONCRETE_POWDER(9, "CONCRETE_POWDER"), + CYAN_DYE(6, "INK_SACK"), + CYAN_GLAZED_TERRACOTTA, + CYAN_SHULKER_BOX, + CYAN_STAINED_GLASS(9, "STAINED_GLASS"), + CYAN_STAINED_GLASS_PANE(9, "STAINED_GLASS_PANE"), + CYAN_TERRACOTTA(9, "STAINED_CLAY"), + CYAN_WALL_BANNER(6, "WALL_BANNER"), + CYAN_WOOL(9, "WOOL"), + DAMAGED_ANVIL(2, "ANVIL"), + DANDELION("YELLOW_FLOWER"), + DARK_OAK_BOAT("BOAT_DARK_OAK"), + DARK_OAK_BUTTON("WOOD_BUTTON"), + DARK_OAK_DOOR("DARK_OAK_DOOR", "DARK_OAK_DOOR_ITEM"), + DARK_OAK_FENCE, + DARK_OAK_FENCE_GATE, + DARK_OAK_LEAVES(1, "LEAVES_2"), + DARK_OAK_LOG(1, "LOG_2"), + DARK_OAK_PLANKS(5, "WOOD"), + DARK_OAK_PRESSURE_PLATE("WOOD_PLATE"), + DARK_OAK_SAPLING(5, "SAPLING"), + DARK_OAK_SIGN("SIGN_POST", "SIGN"), + DARK_OAK_SLAB(5, "WOOD_DOUBLE_STEP", "WOOD_STEP", "WOODEN_SLAB"), + DARK_OAK_STAIRS, + DARK_OAK_TRAPDOOR("TRAP_DOOR"), + DARK_OAK_WALL_SIGN("WALL_SIGN"), + DARK_OAK_WOOD(1, "LOG_2"), + DARK_PRISMARINE(1, "PRISMARINE"), + DARK_PRISMARINE_SLAB, + DARK_PRISMARINE_STAIRS, + DAYLIGHT_DETECTOR("DAYLIGHT_DETECTOR_INVERTED"), + DEAD_BRAIN_CORAL, + DEAD_BRAIN_CORAL_BLOCK, + DEAD_BRAIN_CORAL_FAN, + DEAD_BRAIN_CORAL_WALL_FAN, + DEAD_BUBBLE_CORAL, + DEAD_BUBBLE_CORAL_BLOCK, + DEAD_BUBBLE_CORAL_FAN, + DEAD_BUBBLE_CORAL_WALL_FAN, + DEAD_BUSH("LONG_GRASS"), + DEAD_FIRE_CORAL, + DEAD_FIRE_CORAL_BLOCK, + DEAD_FIRE_CORAL_FAN, + DEAD_FIRE_CORAL_WALL_FAN, + DEAD_HORN_CORAL, + DEAD_HORN_CORAL_BLOCK, + DEAD_HORN_CORAL_FAN, + DEAD_HORN_CORAL_WALL_FAN, + DEAD_TUBE_CORAL, + DEAD_TUBE_CORAL_BLOCK, + DEAD_TUBE_CORAL_FAN, + DEAD_TUBE_CORAL_WALL_FAN, + DEBUG_STICK, + DEEPSLATE, + DEEPSLATE_BRICKS, + DEEPSLATE_BRICK_SLAB, + DEEPSLATE_BRICK_STAIRS, + DEEPSLATE_BRICK_WALL, + DEEPSLATE_COAL_ORE, + DEEPSLATE_COPPER_ORE, + DEEPSLATE_DIAMOND_ORE, + DEEPSLATE_EMERALD_ORE, + DEEPSLATE_GOLD_ORE, + DEEPSLATE_IRON_ORE, + DEEPSLATE_LAPIS_ORE, + DEEPSLATE_REDSTONE_ORE, + DEEPSLATE_TILES, + DEEPSLATE_TILE_SLAB, + DEEPSLATE_TILE_STAIRS, + DEEPSLATE_TILE_WALL, + DETECTOR_RAIL, + DIAMOND, + DIAMOND_AXE, + DIAMOND_BLOCK, + DIAMOND_BOOTS, + DIAMOND_CHESTPLATE, + DIAMOND_HELMET, + DIAMOND_HOE, + DIAMOND_HORSE_ARMOR("DIAMOND_BARDING"), + DIAMOND_LEGGINGS, + DIAMOND_ORE, + DIAMOND_PICKAXE, + DIAMOND_SHOVEL("DIAMOND_SPADE"), + DIAMOND_SWORD, + DIORITE(3, "STONE"), + DIORITE_SLAB, + DIORITE_STAIRS, + DIORITE_WALL, + DIRT, + /** + * Changed in 1.17 + */ + DIRT_PATH("GRASS_PATH"), + DISPENSER, + DOLPHIN_SPAWN_EGG, + DONKEY_SPAWN_EGG(32, "MONSTER_EGG"), + DRAGON_BREATH("DRAGONS_BREATH"), + DRAGON_EGG, + DRAGON_HEAD("SKULL", "SKULL_ITEM"), + DRAGON_WALL_HEAD(5, "SKULL", "SKULL_ITEM"), + DRIED_KELP, + DRIED_KELP_BLOCK, + DRIPSTONE_BLOCK, + DROPPER, + DROWNED_SPAWN_EGG, + EGG, + ELDER_GUARDIAN_SPAWN_EGG(4, "MONSTER_EGG"), + ELYTRA, + EMERALD, + EMERALD_BLOCK, + EMERALD_ORE, + ENCHANTED_BOOK, + ENCHANTED_GOLDEN_APPLE(1, "GOLDEN_APPLE"), + ENCHANTING_TABLE("ENCHANTMENT_TABLE"), + ENDERMAN_SPAWN_EGG(58, "MONSTER_EGG"), + ENDERMITE_SPAWN_EGG(67, "MONSTER_EGG"), + ENDER_CHEST, + ENDER_EYE("EYE_OF_ENDER"), + ENDER_PEARL, + END_CRYSTAL, + END_GATEWAY, + END_PORTAL("ENDER_PORTAL"), + END_PORTAL_FRAME("ENDER_PORTAL_FRAME"), + END_ROD, + END_STONE("ENDER_STONE"), + END_STONE_BRICKS("END_BRICKS"), + END_STONE_BRICK_SLAB(6, "STEP"), + END_STONE_BRICK_STAIRS("SMOOTH_STAIRS"), + END_STONE_BRICK_WALL, + EVOKER_SPAWN_EGG(34, "MONSTER_EGG"), + EXPERIENCE_BOTTLE("EXP_BOTTLE"), + EXPOSED_COPPER, + EXPOSED_CUT_COPPER, + EXPOSED_CUT_COPPER_SLAB, + EXPOSED_CUT_COPPER_STAIRS, + FARMLAND("SOIL"), + FEATHER, + FERMENTED_SPIDER_EYE, + FERN(2, "LONG_GRASS"), + /** + * For some reasons filled map items are really special. + * Their data value starts from 0 and every time a player + * creates a new map that maps data value increases. + * https://github.com/CryptoMorin/XSeries/issues/91 + */ + FILLED_MAP("MAP"), + FIRE, + FIREWORK_ROCKET("FIREWORK"), + FIREWORK_STAR("FIREWORK_CHARGE"), + FIRE_CHARGE("FIREBALL"), + FIRE_CORAL, + FIRE_CORAL_BLOCK, + FIRE_CORAL_FAN, + FIRE_CORAL_WALL_FAN, + FISHING_ROD, + FLETCHING_TABLE, + FLINT, + FLINT_AND_STEEL, + FLOWERING_AZALEA, + FLOWERING_AZALEA_LEAVES, + FLOWER_BANNER_PATTERN, + FLOWER_POT("FLOWER_POT", "FLOWER_POT_ITEM"), + FOX_SPAWN_EGG, + /** + * This special material cannot be obtained as an item. + */ + FROSTED_ICE, + FURNACE("BURNING_FURNACE"), + FURNACE_MINECART("POWERED_MINECART"), + GHAST_SPAWN_EGG(56, "MONSTER_EGG"), + GHAST_TEAR, + GILDED_BLACKSTONE, + GLASS, + GLASS_BOTTLE, + GLASS_PANE("THIN_GLASS"), + GLISTERING_MELON_SLICE("SPECKLED_MELON"), + GLOBE_BANNER_PATTERN, + GLOWSTONE, + GLOWSTONE_DUST, + GLOW_BERRIES, + GLOW_INK_SAC, + GLOW_ITEM_FRAME, + GLOW_LICHEN, + GLOW_SQUID_SPAWN_EGG, + GOAT_SPAWN_EGG, + GOLDEN_APPLE, + GOLDEN_AXE("GOLD_AXE"), + GOLDEN_BOOTS("GOLD_BOOTS"), + GOLDEN_CARROT, + GOLDEN_CHESTPLATE("GOLD_CHESTPLATE"), + GOLDEN_HELMET("GOLD_HELMET"), + GOLDEN_HOE("GOLD_HOE"), + GOLDEN_HORSE_ARMOR("GOLD_BARDING"), + GOLDEN_LEGGINGS("GOLD_LEGGINGS"), + GOLDEN_PICKAXE("GOLD_PICKAXE"), + GOLDEN_SHOVEL("GOLD_SPADE"), + GOLDEN_SWORD("GOLD_SWORD"), + GOLD_BLOCK, + GOLD_INGOT, + GOLD_NUGGET, + GOLD_ORE, + GRANITE(1, "STONE"), + GRANITE_SLAB, + GRANITE_STAIRS, + GRANITE_WALL, + GRASS(1, "LONG_GRASS"), + GRASS_BLOCK("GRASS"), + GRAVEL, + GRAY_BANNER(8, "STANDING_BANNER", "BANNER"), + GRAY_BED(supports(12) ? 7 : 0, "BED_BLOCK", "BED"), + GRAY_CANDLE, + GRAY_CANDLE_CAKE, + GRAY_CARPET(7, "CARPET"), + GRAY_CONCRETE(7, "CONCRETE"), + GRAY_CONCRETE_POWDER(7, "CONCRETE_POWDER"), + GRAY_DYE(8, "INK_SACK"), + GRAY_GLAZED_TERRACOTTA, + GRAY_SHULKER_BOX, + GRAY_STAINED_GLASS(7, "STAINED_GLASS"), + GRAY_STAINED_GLASS_PANE(7, "THIN_GLASS", "STAINED_GLASS_PANE"), + GRAY_TERRACOTTA(7, "STAINED_CLAY"), + GRAY_WALL_BANNER(8, "WALL_BANNER"), + GRAY_WOOL(7, "WOOL"), + GREEN_BANNER(2, "STANDING_BANNER", "BANNER"), + GREEN_BED(supports(12) ? 13 : 0, "BED_BLOCK", "BED"), + GREEN_CANDLE, + GREEN_CANDLE_CAKE, + GREEN_CARPET(13, "CARPET"), + GREEN_CONCRETE(13, "CONCRETE"), + GREEN_CONCRETE_POWDER(13, "CONCRETE_POWDER"), + GREEN_DYE(2, "INK_SACK", "CACTUS_GREEN"), + GREEN_GLAZED_TERRACOTTA, + GREEN_SHULKER_BOX, + GREEN_STAINED_GLASS(13, "STAINED_GLASS"), + GREEN_STAINED_GLASS_PANE(13, "THIN_GLASS", "STAINED_GLASS_PANE"), + GREEN_TERRACOTTA(13, "STAINED_CLAY"), + GREEN_WALL_BANNER(2, "WALL_BANNER"), + GREEN_WOOL(13, "WOOL"), + GRINDSTONE, + GUARDIAN_SPAWN_EGG(68, "MONSTER_EGG"), + GUNPOWDER("SULPHUR"), + HANGING_ROOTS, + HAY_BLOCK, + HEART_OF_THE_SEA, + HEAVY_WEIGHTED_PRESSURE_PLATE("IRON_PLATE"), + HOGLIN_SPAWN_EGG("MONSTER_EGG"), + HONEYCOMB, + HONEYCOMB_BLOCK, + HONEY_BLOCK, + HONEY_BOTTLE, + HOPPER, + HOPPER_MINECART, + HORN_CORAL, + HORN_CORAL_BLOCK, + HORN_CORAL_FAN, + HORN_CORAL_WALL_FAN, + HORSE_SPAWN_EGG(100, "MONSTER_EGG"), + HUSK_SPAWN_EGG(23, "MONSTER_EGG"), + ICE, + INFESTED_CHISELED_STONE_BRICKS(5, "MONSTER_EGGS"), + INFESTED_COBBLESTONE(1, "MONSTER_EGGS"), + INFESTED_CRACKED_STONE_BRICKS(4, "MONSTER_EGGS"), + INFESTED_DEEPSLATE, + INFESTED_MOSSY_STONE_BRICKS(3, "MONSTER_EGGS"), + INFESTED_STONE("MONSTER_EGGS"), + INFESTED_STONE_BRICKS(2, "MONSTER_EGGS"), + /** + * We will only add "INK_SAC" for {@link #BLACK_DYE} since it's + * the only material (linked with this material) that is added + * after 1.13, which means it can use both INK_SACK and INK_SAC. + */ + INK_SAC("INK_SACK"), + IRON_AXE, + IRON_BARS("IRON_FENCE"), + IRON_BLOCK, + IRON_BOOTS, + IRON_CHESTPLATE, + IRON_DOOR("IRON_DOOR_BLOCK"), + IRON_HELMET, + IRON_HOE, + IRON_HORSE_ARMOR("IRON_BARDING"), + IRON_INGOT, + IRON_LEGGINGS, + IRON_NUGGET, + IRON_ORE, + IRON_PICKAXE, + IRON_SHOVEL("IRON_SPADE"), + IRON_SWORD, + IRON_TRAPDOOR, + ITEM_FRAME, + JACK_O_LANTERN, + JIGSAW, + JUKEBOX, + JUNGLE_BOAT("BOAT_JUNGLE"), + JUNGLE_BUTTON("WOOD_BUTTON"), + JUNGLE_DOOR("JUNGLE_DOOR", "JUNGLE_DOOR_ITEM"), + JUNGLE_FENCE, + JUNGLE_FENCE_GATE, + JUNGLE_LEAVES(3, "LEAVES"), + JUNGLE_LOG(3, "LOG"), + JUNGLE_PLANKS(3, "WOOD"), + JUNGLE_PRESSURE_PLATE("WOOD_PLATE"), + JUNGLE_SAPLING(3, "SAPLING"), + JUNGLE_SIGN("SIGN_POST", "SIGN"), + JUNGLE_SLAB(3, "WOOD_DOUBLE_STEP", "WOOD_STEP", "WOODEN_SLAB"), + JUNGLE_STAIRS("JUNGLE_WOOD_STAIRS"), + JUNGLE_TRAPDOOR("TRAP_DOOR"), + JUNGLE_WALL_SIGN("WALL_SIGN"), + JUNGLE_WOOD(3, "LOG"), + KELP, + KELP_PLANT, + KNOWLEDGE_BOOK("BOOK"), + LADDER, + LANTERN, + LAPIS_BLOCK, + LAPIS_LAZULI(4, "INK_SACK"), + LAPIS_ORE, + LARGE_AMETHYST_BUD, + LARGE_FERN(3, "DOUBLE_PLANT"), + LAVA("STATIONARY_LAVA"), + LAVA_BUCKET, + LAVA_CAULDRON, + LEAD("LEASH"), + LEATHER, + LEATHER_BOOTS, + LEATHER_CHESTPLATE, + LEATHER_HELMET, + LEATHER_HORSE_ARMOR("IRON_HORSE_ARMOR"), + LEATHER_LEGGINGS, + LECTERN, + LEVER, + LIGHT, + LIGHTNING_ROD, + LIGHT_BLUE_BANNER(12, "STANDING_BANNER", "BANNER"), + LIGHT_BLUE_BED(supports(12) ? 3 : 0, "BED_BLOCK", "BED"), + LIGHT_BLUE_CANDLE, + LIGHT_BLUE_CANDLE_CAKE, + LIGHT_BLUE_CARPET(3, "CARPET"), + LIGHT_BLUE_CONCRETE(3, "CONCRETE"), + LIGHT_BLUE_CONCRETE_POWDER(3, "CONCRETE_POWDER"), + LIGHT_BLUE_DYE(12, "INK_SACK"), + LIGHT_BLUE_GLAZED_TERRACOTTA, + LIGHT_BLUE_SHULKER_BOX, + LIGHT_BLUE_STAINED_GLASS(3, "STAINED_GLASS"), + LIGHT_BLUE_STAINED_GLASS_PANE(3, "THIN_GLASS", "STAINED_GLASS_PANE"), + LIGHT_BLUE_TERRACOTTA(3, "STAINED_CLAY"), + LIGHT_BLUE_WALL_BANNER(12, "WALL_BANNER", "STANDING_BANNER", "BANNER"), + LIGHT_BLUE_WOOL(3, "WOOL"), + LIGHT_GRAY_BANNER(7, "STANDING_BANNER", "BANNER"), + LIGHT_GRAY_BED(supports(12) ? 8 : 0, "BED_BLOCK", "BED"), + LIGHT_GRAY_CANDLE, + LIGHT_GRAY_CANDLE_CAKE, + LIGHT_GRAY_CARPET(8, "CARPET"), + LIGHT_GRAY_CONCRETE(8, "CONCRETE"), + LIGHT_GRAY_CONCRETE_POWDER(8, "CONCRETE_POWDER"), + LIGHT_GRAY_DYE(7, "INK_SACK"), + /** + * Renamed to SILVER_GLAZED_TERRACOTTA in 1.12 + * Renamed to LIGHT_GRAY_GLAZED_TERRACOTTA in 1.14 + */ + LIGHT_GRAY_GLAZED_TERRACOTTA("STAINED_CLAY", "LIGHT_GRAY_TERRACOTTA", "SILVER_GLAZED_TERRACOTTA"), + LIGHT_GRAY_SHULKER_BOX("SILVER_SHULKER_BOX"), + LIGHT_GRAY_STAINED_GLASS(8, "STAINED_GLASS"), + LIGHT_GRAY_STAINED_GLASS_PANE(8, "THIN_GLASS", "STAINED_GLASS_PANE"), + LIGHT_GRAY_TERRACOTTA(8, "STAINED_CLAY"), + LIGHT_GRAY_WALL_BANNER(7, "WALL_BANNER"), + LIGHT_GRAY_WOOL(8, "WOOL"), + LIGHT_WEIGHTED_PRESSURE_PLATE("GOLD_PLATE"), + LILAC(1, "DOUBLE_PLANT"), + LILY_OF_THE_VALLEY, + LILY_PAD("WATER_LILY"), + LIME_BANNER(10, "STANDING_BANNER", "BANNER"), + LIME_BED(supports(12) ? 5 : 0, "BED_BLOCK", "BED"), + LIME_CANDLE, + LIME_CANDLE_CAKE, + LIME_CARPET(5, "CARPET"), + LIME_CONCRETE(5, "CONCRETE"), + LIME_CONCRETE_POWDER(5, "CONCRETE_POWDER"), + LIME_DYE(10, "INK_SACK"), + LIME_GLAZED_TERRACOTTA, + LIME_SHULKER_BOX, + LIME_STAINED_GLASS(5, "STAINED_GLASS"), + LIME_STAINED_GLASS_PANE(5, "STAINED_GLASS_PANE"), + LIME_TERRACOTTA(5, "STAINED_CLAY"), + LIME_WALL_BANNER(10, "WALL_BANNER"), + LIME_WOOL(5, "WOOL"), + LINGERING_POTION, + LLAMA_SPAWN_EGG(103, "MONSTER_EGG"), + LODESTONE, + LOOM, + MAGENTA_BANNER(13, "STANDING_BANNER", "BANNER"), + MAGENTA_BED(supports(12) ? 2 : 0, "BED_BLOCK", "BED"), + MAGENTA_CANDLE, + MAGENTA_CANDLE_CAKE, + MAGENTA_CARPET(2, "CARPET"), + MAGENTA_CONCRETE(2, "CONCRETE"), + MAGENTA_CONCRETE_POWDER(2, "CONCRETE_POWDER"), + MAGENTA_DYE(13, "INK_SACK"), + MAGENTA_GLAZED_TERRACOTTA, + MAGENTA_SHULKER_BOX, + MAGENTA_STAINED_GLASS(2, "STAINED_GLASS"), + MAGENTA_STAINED_GLASS_PANE(2, "THIN_GLASS", "STAINED_GLASS_PANE"), + MAGENTA_TERRACOTTA(2, "STAINED_CLAY"), + MAGENTA_WALL_BANNER(13, "WALL_BANNER"), + MAGENTA_WOOL(2, "WOOL"), + MAGMA_BLOCK("MAGMA"), + MAGMA_CREAM, + MAGMA_CUBE_SPAWN_EGG(62, "MONSTER_EGG"), + /** + * Adding this to the duplicated list will give you a filled map + * for 1.13+ versions and removing it from duplicated list will + * still give you a filled map in -1.12 versions. + * Since higher versions are our priority I'll keep 1.13+ support + * until I can come up with something to fix it. + */ + MAP("EMPTY_MAP"), + MEDIUM_AMETHYST_BUD, + MELON("MELON_BLOCK"), + MELON_SEEDS, + MELON_SLICE("MELON"), + MELON_STEM, + MILK_BUCKET, + MINECART, + MOJANG_BANNER_PATTERN, + MOOSHROOM_SPAWN_EGG(96, "MONSTER_EGG"), + MOSSY_COBBLESTONE, + MOSSY_COBBLESTONE_SLAB(3, "STEP"), + MOSSY_COBBLESTONE_STAIRS, + MOSSY_COBBLESTONE_WALL(1, "COBBLE_WALL", "COBBLESTONE_WALL"), + MOSSY_STONE_BRICKS(1, "SMOOTH_BRICK"), + MOSSY_STONE_BRICK_SLAB(5, "STEP"), + MOSSY_STONE_BRICK_STAIRS("SMOOTH_STAIRS"), + MOSSY_STONE_BRICK_WALL, + MOSS_BLOCK, + MOSS_CARPET, + MOVING_PISTON("PISTON_MOVING_PIECE"), + MULE_SPAWN_EGG(32, "MONSTER_EGG"), + MUSHROOM_STEM("BROWN_MUSHROOM"), + MUSHROOM_STEW("MUSHROOM_SOUP"), + MUSIC_DISC_11("GOLD_RECORD"), + MUSIC_DISC_13("GREEN_RECORD"), + MUSIC_DISC_BLOCKS("RECORD_3"), + MUSIC_DISC_CAT("RECORD_4"), + MUSIC_DISC_CHIRP("RECORD_5"), + MUSIC_DISC_FAR("RECORD_6"), + MUSIC_DISC_MALL("RECORD_7"), + MUSIC_DISC_MELLOHI("RECORD_8"), + MUSIC_DISC_OTHERSIDE, + MUSIC_DISC_PIGSTEP, + MUSIC_DISC_STAL("RECORD_9"), + MUSIC_DISC_STRAD("RECORD_10"), + MUSIC_DISC_WAIT("RECORD_11"), + MUSIC_DISC_WARD("RECORD_12"), + MUTTON, + MYCELIUM("MYCEL"), + NAME_TAG, + NAUTILUS_SHELL, + NETHERITE_AXE, + NETHERITE_BLOCK, + NETHERITE_BOOTS, + NETHERITE_CHESTPLATE, + NETHERITE_HELMET, + NETHERITE_HOE, + NETHERITE_INGOT, + NETHERITE_LEGGINGS, + NETHERITE_PICKAXE, + NETHERITE_SCRAP, + NETHERITE_SHOVEL, + NETHERITE_SWORD, + NETHERRACK, + NETHER_BRICK("NETHER_BRICK_ITEM"), + NETHER_BRICKS("NETHER_BRICK"), + NETHER_BRICK_FENCE("NETHER_FENCE"), + NETHER_BRICK_SLAB(6, "STEP"), + NETHER_BRICK_STAIRS, + NETHER_BRICK_WALL, + NETHER_GOLD_ORE, + NETHER_PORTAL("PORTAL"), + NETHER_QUARTZ_ORE("QUARTZ_ORE"), + NETHER_SPROUTS, + NETHER_STAR, + /** + * Just like mentioned in https://minecraft.gamepedia.com/Nether_Wart + * Nether wart is also known as nether stalk in the code. + * NETHER_STALK is the planted state of nether warts. + */ + NETHER_WART("NETHER_WARTS", "NETHER_STALK"), + NETHER_WART_BLOCK, + NOTE_BLOCK, + OAK_BOAT("BOAT"), + OAK_BUTTON("WOOD_BUTTON"), + OAK_DOOR("WOODEN_DOOR", "WOOD_DOOR"), + OAK_FENCE("FENCE"), + OAK_FENCE_GATE("FENCE_GATE"), + OAK_LEAVES("LEAVES"), + OAK_LOG("LOG"), + OAK_PLANKS("WOOD"), + OAK_PRESSURE_PLATE("WOOD_PLATE"), + OAK_SAPLING("SAPLING"), + OAK_SIGN("SIGN_POST", "SIGN"), + OAK_SLAB("WOOD_DOUBLE_STEP", "WOOD_STEP", "WOODEN_SLAB"), + OAK_STAIRS("WOOD_STAIRS"), + OAK_TRAPDOOR("TRAP_DOOR"), + OAK_WALL_SIGN("WALL_SIGN"), + OAK_WOOD("LOG"), + OBSERVER, + OBSIDIAN, + OCELOT_SPAWN_EGG(98, "MONSTER_EGG"), + ORANGE_BANNER(14, "STANDING_BANNER", "BANNER"), + ORANGE_BED(supports(12) ? 1 : 0, "BED_BLOCK", "BED"), + ORANGE_CANDLE, + ORANGE_CANDLE_CAKE, + ORANGE_CARPET(1, "CARPET"), + ORANGE_CONCRETE(1, "CONCRETE"), + ORANGE_CONCRETE_POWDER(1, "CONCRETE_POWDER"), + ORANGE_DYE(14, "INK_SACK"), + ORANGE_GLAZED_TERRACOTTA, + ORANGE_SHULKER_BOX, + ORANGE_STAINED_GLASS(1, "STAINED_GLASS"), + ORANGE_STAINED_GLASS_PANE(1, "STAINED_GLASS_PANE"), + ORANGE_TERRACOTTA(1, "STAINED_CLAY"), + ORANGE_TULIP(5, "RED_ROSE"), + ORANGE_WALL_BANNER(14, "WALL_BANNER"), + ORANGE_WOOL(1, "WOOL"), + OXEYE_DAISY(8, "RED_ROSE"), + OXIDIZED_COPPER, + OXIDIZED_CUT_COPPER, + OXIDIZED_CUT_COPPER_SLAB, + OXIDIZED_CUT_COPPER_STAIRS, + PACKED_ICE, + PAINTING, + PANDA_SPAWN_EGG, + PAPER, + PARROT_SPAWN_EGG(105, "MONSTER_EGG"), + PEONY(5, "DOUBLE_PLANT"), + PETRIFIED_OAK_SLAB("WOOD_STEP"), + PHANTOM_MEMBRANE, + PHANTOM_SPAWN_EGG, + PIGLIN_BANNER_PATTERN, + PIGLIN_BRUTE_SPAWN_EGG, + PIGLIN_SPAWN_EGG(57, "MONSTER_EGG"), + PIG_SPAWN_EGG(90, "MONSTER_EGG"), + PILLAGER_SPAWN_EGG, + PINK_BANNER(9, "STANDING_BANNER", "BANNER"), + PINK_BED(supports(12) ? 6 : 0, "BED_BLOCK", "BED"), + PINK_CANDLE, + PINK_CANDLE_CAKE, + PINK_CARPET(6, "CARPET"), + PINK_CONCRETE(6, "CONCRETE"), + PINK_CONCRETE_POWDER(6, "CONCRETE_POWDER"), + PINK_DYE(9, "INK_SACK"), + PINK_GLAZED_TERRACOTTA, + PINK_SHULKER_BOX, + PINK_STAINED_GLASS(6, "STAINED_GLASS"), + PINK_STAINED_GLASS_PANE(6, "THIN_GLASS", "STAINED_GLASS_PANE"), + PINK_TERRACOTTA(6, "STAINED_CLAY"), + PINK_TULIP(7, "RED_ROSE"), + PINK_WALL_BANNER(9, "WALL_BANNER"), + PINK_WOOL(6, "WOOL"), + PISTON("PISTON_BASE"), + PISTON_HEAD("PISTON_EXTENSION"), + PLAYER_HEAD(3, "SKULL", "SKULL_ITEM"), + PLAYER_WALL_HEAD(3, "SKULL", "SKULL_ITEM"), + PODZOL(2, "DIRT"), + POINTED_DRIPSTONE, + POISONOUS_POTATO, + POLAR_BEAR_SPAWN_EGG(102, "MONSTER_EGG"), + POLISHED_ANDESITE(6, "STONE"), + POLISHED_ANDESITE_SLAB, + POLISHED_ANDESITE_STAIRS, + POLISHED_BASALT, + POLISHED_BLACKSTONE, + POLISHED_BLACKSTONE_BRICKS, + POLISHED_BLACKSTONE_BRICK_SLAB, + POLISHED_BLACKSTONE_BRICK_STAIRS, + POLISHED_BLACKSTONE_BRICK_WALL, + POLISHED_BLACKSTONE_BUTTON, + POLISHED_BLACKSTONE_PRESSURE_PLATE, + POLISHED_BLACKSTONE_SLAB, + POLISHED_BLACKSTONE_STAIRS, + POLISHED_BLACKSTONE_WALL, + POLISHED_DEEPSLATE, + POLISHED_DEEPSLATE_SLAB, + POLISHED_DEEPSLATE_STAIRS, + POLISHED_DEEPSLATE_WALL, + POLISHED_DIORITE(4, "STONE"), + POLISHED_DIORITE_SLAB, + POLISHED_DIORITE_STAIRS, + POLISHED_GRANITE(2, "STONE"), + POLISHED_GRANITE_SLAB, + POLISHED_GRANITE_STAIRS, + POPPED_CHORUS_FRUIT("CHORUS_FRUIT_POPPED"), + POPPY("RED_ROSE"), + PORKCHOP("PORK"), + POTATO("POTATO_ITEM"), + POTATOES("POTATO"), + POTION, + POTTED_ACACIA_SAPLING(4, "FLOWER_POT"), + POTTED_ALLIUM(2, "RED_ROSE", "FLOWER_POT"), + POTTED_AZALEA_BUSH, + POTTED_AZURE_BLUET(3, "RED_ROSE", "FLOWER_POT"), + POTTED_BAMBOO, + POTTED_BIRCH_SAPLING(2, "FLOWER_POT"), + POTTED_BLUE_ORCHID(1, "RED_ROSE", "FLOWER_POT"), + POTTED_BROWN_MUSHROOM("FLOWER_POT"), + POTTED_CACTUS("FLOWER_POT"), + POTTED_CORNFLOWER, + POTTED_CRIMSON_FUNGUS, + POTTED_CRIMSON_ROOTS, + POTTED_DANDELION("YELLOW_FLOWER", "FLOWER_POT"), + POTTED_DARK_OAK_SAPLING(5, "FLOWER_POT"), + POTTED_DEAD_BUSH("FLOWER_POT"), + POTTED_FERN(2, "LONG_GRASS", "FLOWER_POT"), + POTTED_FLOWERING_AZALEA_BUSH, + POTTED_JUNGLE_SAPLING(3, "FLOWER_POT"), + POTTED_LILY_OF_THE_VALLEY, + POTTED_OAK_SAPLING("FLOWER_POT"), + POTTED_ORANGE_TULIP(5, "RED_ROSE", "FLOWER_POT"), + POTTED_OXEYE_DAISY(8, "RED_ROSE", "FLOWER_POT"), + POTTED_PINK_TULIP(7, "RED_ROSE", "FLOWER_POT"), + POTTED_POPPY("RED_ROSE", "FLOWER_POT"), + POTTED_RED_MUSHROOM("FLOWER_POT"), + POTTED_RED_TULIP(4, "RED_ROSE", "FLOWER_POT"), + POTTED_SPRUCE_SAPLING(1, "FLOWER_POT"), + POTTED_WARPED_FUNGUS, + POTTED_WARPED_ROOTS, + POTTED_WHITE_TULIP(6, "RED_ROSE", "FLOWER_POT"), + POTTED_WITHER_ROSE, + POWDER_SNOW, + POWDER_SNOW_BUCKET, + POWDER_SNOW_CAULDRON, + POWERED_RAIL, + PRISMARINE, + PRISMARINE_BRICKS(2, "PRISMARINE"), + PRISMARINE_BRICK_SLAB(4, "STEP"), + PRISMARINE_BRICK_STAIRS, + PRISMARINE_CRYSTALS, + PRISMARINE_SHARD, + PRISMARINE_SLAB, + PRISMARINE_STAIRS, + PRISMARINE_WALL, + PUFFERFISH(3, "RAW_FISH"), + PUFFERFISH_BUCKET, + PUFFERFISH_SPAWN_EGG, + PUMPKIN, + PUMPKIN_PIE, + PUMPKIN_SEEDS, + PUMPKIN_STEM, + PURPLE_BANNER(5, "STANDING_BANNER", "BANNER"), + PURPLE_BED(supports(12) ? 10 : 0, "BED_BLOCK", "BED"), + PURPLE_CANDLE, + PURPLE_CANDLE_CAKE, + PURPLE_CARPET(10, "CARPET"), + PURPLE_CONCRETE(10, "CONCRETE"), + PURPLE_CONCRETE_POWDER(10, "CONCRETE_POWDER"), + PURPLE_DYE(5, "INK_SACK"), + PURPLE_GLAZED_TERRACOTTA, + PURPLE_SHULKER_BOX, + PURPLE_STAINED_GLASS(10, "STAINED_GLASS"), + PURPLE_STAINED_GLASS_PANE(10, "THIN_GLASS", "STAINED_GLASS_PANE"), + PURPLE_TERRACOTTA(10, "STAINED_CLAY"), + PURPLE_WALL_BANNER(5, "WALL_BANNER"), + PURPLE_WOOL(10, "WOOL"), + PURPUR_BLOCK, + PURPUR_PILLAR, + PURPUR_SLAB("PURPUR_DOUBLE_SLAB"), + PURPUR_STAIRS, + QUARTZ, + QUARTZ_BLOCK, + QUARTZ_BRICKS, + QUARTZ_PILLAR(2, "QUARTZ_BLOCK"), + QUARTZ_SLAB(7, "STEP"), + QUARTZ_STAIRS, + RABBIT, + RABBIT_FOOT, + RABBIT_HIDE, + RABBIT_SPAWN_EGG(101, "MONSTER_EGG"), + RABBIT_STEW, + RAIL("RAILS"), + RAVAGER_SPAWN_EGG, + RAW_COPPER, + RAW_COPPER_BLOCK, + RAW_GOLD, + RAW_GOLD_BLOCK, + RAW_IRON, + RAW_IRON_BLOCK, + REDSTONE, + REDSTONE_BLOCK, + /** + * Unlike redstone torch, REDSTONE_LAMP_ON isn't an item. + * The name is just here on the list for matching. + * + * @see #REDSTONE_TORCH + */ + REDSTONE_LAMP("REDSTONE_LAMP_ON", "REDSTONE_LAMP_OFF"), + REDSTONE_ORE("GLOWING_REDSTONE_ORE"), + /** + * REDSTONE_TORCH_OFF isn't an item, but a block. + * But REDSTONE_TORCH_ON is the item. + * The name is just here on the list for matching. + */ + REDSTONE_TORCH("REDSTONE_TORCH_OFF", "REDSTONE_TORCH_ON"), + REDSTONE_WALL_TORCH, + REDSTONE_WIRE, + RED_BANNER(1, "STANDING_BANNER", "BANNER"), + /** + * Data value 14 or 0 + */ + RED_BED(supports(12) ? 14 : 0, "BED_BLOCK", "BED"), + RED_CANDLE, + RED_CANDLE_CAKE, + RED_CARPET(14, "CARPET"), + RED_CONCRETE(14, "CONCRETE"), + RED_CONCRETE_POWDER(14, "CONCRETE_POWDER"), + RED_DYE(1, "INK_SACK", "ROSE_RED"), + RED_GLAZED_TERRACOTTA, + RED_MUSHROOM, + RED_MUSHROOM_BLOCK("RED_MUSHROOM", "HUGE_MUSHROOM_2"), + RED_NETHER_BRICKS("RED_NETHER_BRICK"), + RED_NETHER_BRICK_SLAB(4, "STEP"), + RED_NETHER_BRICK_STAIRS, + RED_NETHER_BRICK_WALL, + RED_SAND(1, "SAND"), + RED_SANDSTONE, + RED_SANDSTONE_SLAB("DOUBLE_STONE_SLAB2", "STONE_SLAB2"), + RED_SANDSTONE_STAIRS, + RED_SANDSTONE_WALL, + RED_SHULKER_BOX, + RED_STAINED_GLASS(14, "STAINED_GLASS"), + RED_STAINED_GLASS_PANE(14, "THIN_GLASS", "STAINED_GLASS_PANE"), + RED_TERRACOTTA(14, "STAINED_CLAY"), + RED_TULIP(4, "RED_ROSE"), + RED_WALL_BANNER(1, "WALL_BANNER"), + RED_WOOL(14, "WOOL"), + REPEATER("DIODE_BLOCK_ON", "DIODE_BLOCK_OFF", "DIODE"), + REPEATING_COMMAND_BLOCK("COMMAND", "COMMAND_REPEATING"), + RESPAWN_ANCHOR, + ROOTED_DIRT, + ROSE_BUSH(4, "DOUBLE_PLANT"), + ROTTEN_FLESH, + SADDLE, + SALMON(1, "RAW_FISH"), + SALMON_BUCKET, + SALMON_SPAWN_EGG, + SAND, + SANDSTONE, + SANDSTONE_SLAB(1, "DOUBLE_STEP", "STEP", "STONE_SLAB"), + SANDSTONE_STAIRS, + SANDSTONE_WALL, + SCAFFOLDING, + SCULK_SENSOR, + SCUTE, + SEAGRASS, + SEA_LANTERN, + SEA_PICKLE, + SHEARS, + SHEEP_SPAWN_EGG(91, "MONSTER_EGG"), + SHIELD, + SHROOMLIGHT, + SHULKER_BOX("PURPLE_SHULKER_BOX"), + SHULKER_SHELL, + SHULKER_SPAWN_EGG(69, "MONSTER_EGG"), + SILVERFISH_SPAWN_EGG(60, "MONSTER_EGG"), + SKELETON_HORSE_SPAWN_EGG(28, "MONSTER_EGG"), + SKELETON_SKULL("SKULL", "SKULL_ITEM"), + SKELETON_SPAWN_EGG(51, "MONSTER_EGG"), + SKELETON_WALL_SKULL("SKULL", "SKULL_ITEM"), + SKULL_BANNER_PATTERN, + SLIME_BALL, + SLIME_BLOCK, + SLIME_SPAWN_EGG(55, "MONSTER_EGG"), + SMALL_AMETHYST_BUD, + SMALL_DRIPLEAF, + SMITHING_TABLE, + SMOKER, + SMOOTH_BASALT, + SMOOTH_QUARTZ, + SMOOTH_QUARTZ_SLAB(7, "STEP"), + SMOOTH_QUARTZ_STAIRS, + SMOOTH_RED_SANDSTONE(2, "RED_SANDSTONE"), + SMOOTH_RED_SANDSTONE_SLAB("STONE_SLAB2"), + SMOOTH_RED_SANDSTONE_STAIRS, + SMOOTH_SANDSTONE(2, "SANDSTONE"), + SMOOTH_SANDSTONE_SLAB("STEP"), + SMOOTH_SANDSTONE_STAIRS, + SMOOTH_STONE("STEP"), + SMOOTH_STONE_SLAB("STEP"), + SNOW, + SNOWBALL("SNOW_BALL"), + SNOW_BLOCK, + SOUL_CAMPFIRE, + SOUL_FIRE, + SOUL_LANTERN, + SOUL_SAND, + SOUL_SOIL, + SOUL_TORCH, + SOUL_WALL_TORCH, + SPAWNER("MOB_SPAWNER"), + SPECTRAL_ARROW, + SPIDER_EYE, + SPIDER_SPAWN_EGG(52, "MONSTER_EGG"), + SPLASH_POTION, + SPONGE, + SPORE_BLOSSOM, + SPRUCE_BOAT("BOAT_SPRUCE"), + SPRUCE_BUTTON("WOOD_BUTTON"), + SPRUCE_DOOR("SPRUCE_DOOR", "SPRUCE_DOOR_ITEM"), + SPRUCE_FENCE, + SPRUCE_FENCE_GATE, + SPRUCE_LEAVES(1, "LEAVES"), + SPRUCE_LOG(1, "LOG"), + SPRUCE_PLANKS(1, "WOOD"), + SPRUCE_PRESSURE_PLATE("WOOD_PLATE"), + SPRUCE_SAPLING(1, "SAPLING"), + SPRUCE_SIGN("SIGN_POST", "SIGN"), + SPRUCE_SLAB(1, "WOOD_DOUBLE_STEP", "WOOD_STEP", "WOODEN_SLAB"), + SPRUCE_STAIRS("SPRUCE_WOOD_STAIRS"), + SPRUCE_TRAPDOOR("TRAP_DOOR"), + SPRUCE_WALL_SIGN("WALL_SIGN"), + SPRUCE_WOOD(1, "LOG"), + SPYGLASS, + SQUID_SPAWN_EGG(94, "MONSTER_EGG"), + STICK, + STICKY_PISTON("PISTON_BASE", "PISTON_STICKY_BASE"), + STONE, + STONECUTTER, + STONE_AXE, + STONE_BRICKS("SMOOTH_BRICK"), + STONE_BRICK_SLAB(4, "DOUBLE_STEP", "STEP", "STONE_SLAB"), + STONE_BRICK_STAIRS("SMOOTH_STAIRS"), + STONE_BRICK_WALL, + STONE_BUTTON, + STONE_HOE, + STONE_PICKAXE, + STONE_PRESSURE_PLATE("STONE_PLATE"), + STONE_SHOVEL("STONE_SPADE"), + STONE_SLAB("DOUBLE_STEP", "STEP"), + STONE_STAIRS, + STONE_SWORD, + STRAY_SPAWN_EGG(6, "MONSTER_EGG"), + STRIDER_SPAWN_EGG, + STRING, + STRIPPED_ACACIA_LOG("LOG_2"), + STRIPPED_ACACIA_WOOD("LOG_2"), + STRIPPED_BIRCH_LOG(2, "LOG"), + STRIPPED_BIRCH_WOOD(2, "LOG"), + STRIPPED_CRIMSON_HYPHAE, + STRIPPED_CRIMSON_STEM, + STRIPPED_DARK_OAK_LOG("LOG"), + STRIPPED_DARK_OAK_WOOD("LOG"), + STRIPPED_JUNGLE_LOG(3, "LOG"), + STRIPPED_JUNGLE_WOOD(3, "LOG"), + STRIPPED_OAK_LOG("LOG"), + STRIPPED_OAK_WOOD("LOG"), + STRIPPED_SPRUCE_LOG(1, "LOG"), + STRIPPED_SPRUCE_WOOD(1, "LOG"), + STRIPPED_WARPED_HYPHAE, + STRIPPED_WARPED_STEM, + STRUCTURE_BLOCK, + /** + * Originally developers used barrier blocks for its purpose. + * So technically this isn't really considered as a suggested material. + */ + STRUCTURE_VOID(10, "BARRIER"), + SUGAR, + /** + * Sugar Cane is a known material in pre-1.13 + */ + SUGAR_CANE("SUGAR_CANE_BLOCK"), + SUNFLOWER("DOUBLE_PLANT"), + SUSPICIOUS_STEW, + SWEET_BERRIES, + SWEET_BERRY_BUSH, + TALL_GRASS(2, "DOUBLE_PLANT"), + TALL_SEAGRASS, + TARGET, + TERRACOTTA("STAINED_CLAY"), + TINTED_GLASS, + TIPPED_ARROW, + TNT, + TNT_MINECART("EXPLOSIVE_MINECART"), + TORCH, + TOTEM_OF_UNDYING("TOTEM"), + TRADER_LLAMA_SPAWN_EGG, + TRAPPED_CHEST, + TRIDENT, + TRIPWIRE, + TRIPWIRE_HOOK, + TROPICAL_FISH(2, "RAW_FISH"), + TROPICAL_FISH_BUCKET("BUCKET", "WATER_BUCKET"), + TROPICAL_FISH_SPAWN_EGG("MONSTER_EGG"), + TUBE_CORAL, + TUBE_CORAL_BLOCK, + TUBE_CORAL_FAN, + TUBE_CORAL_WALL_FAN, + TUFF, + TURTLE_EGG, + TURTLE_HELMET, + TURTLE_SPAWN_EGG, + TWISTING_VINES, + TWISTING_VINES_PLANT, + VEX_SPAWN_EGG(35, "MONSTER_EGG"), + VILLAGER_SPAWN_EGG(120, "MONSTER_EGG"), + VINDICATOR_SPAWN_EGG(36, "MONSTER_EGG"), + VINE, + /** + * 1.13 tag is not added because it's the same thing as {@link #AIR} + * + * @see #CAVE_AIR + */ + VOID_AIR("AIR"), + WALL_TORCH("TORCH"), + WANDERING_TRADER_SPAWN_EGG, + WARPED_BUTTON, + WARPED_DOOR, + WARPED_FENCE, + WARPED_FENCE_GATE, + WARPED_FUNGUS, + WARPED_FUNGUS_ON_A_STICK, + WARPED_HYPHAE, + WARPED_NYLIUM, + WARPED_PLANKS, + WARPED_PRESSURE_PLATE, + WARPED_ROOTS, + WARPED_SIGN("SIGN_POST"), + WARPED_SLAB, + WARPED_STAIRS, + WARPED_STEM, + WARPED_TRAPDOOR, + WARPED_WALL_SIGN("WALL_SIGN"), + WARPED_WART_BLOCK, + /** + * This is used for blocks only. + * In 1.13- WATER will turn into STATIONARY_WATER after it finished spreading. + * After 1.13+ this uses + * https://hub.spigotmc.org/javadocs/spigot/org/bukkit/block/data/Levelled.html water flowing system. + */ + WATER("STATIONARY_WATER"), + WATER_BUCKET, + WATER_CAULDRON, + WAXED_COPPER_BLOCK, + WAXED_CUT_COPPER, + WAXED_CUT_COPPER_SLAB, + WAXED_CUT_COPPER_STAIRS, + WAXED_EXPOSED_COPPER, + WAXED_EXPOSED_CUT_COPPER, + WAXED_EXPOSED_CUT_COPPER_SLAB, + WAXED_EXPOSED_CUT_COPPER_STAIRS, + WAXED_OXIDIZED_COPPER, + WAXED_OXIDIZED_CUT_COPPER, + WAXED_OXIDIZED_CUT_COPPER_SLAB, + WAXED_OXIDIZED_CUT_COPPER_STAIRS, + WAXED_WEATHERED_COPPER, + WAXED_WEATHERED_CUT_COPPER, + WAXED_WEATHERED_CUT_COPPER_SLAB, + WAXED_WEATHERED_CUT_COPPER_STAIRS, + WEATHERED_COPPER, + WEATHERED_CUT_COPPER, + WEATHERED_CUT_COPPER_SLAB, + WEATHERED_CUT_COPPER_STAIRS, + WEEPING_VINES, + WEEPING_VINES_PLANT, + WET_SPONGE(1, "SPONGE"), + /** + * Wheat is a known material in pre-1.13 + */ + WHEAT("CROPS"), + WHEAT_SEEDS("SEEDS"), + WHITE_BANNER(15, "STANDING_BANNER", "BANNER"), + WHITE_BED("BED_BLOCK", "BED"), + WHITE_CANDLE, + WHITE_CANDLE_CAKE, + WHITE_CARPET("CARPET"), + WHITE_CONCRETE("CONCRETE"), + WHITE_CONCRETE_POWDER("CONCRETE_POWDER"), + WHITE_DYE(15, "INK_SACK", "BONE_MEAL"), + WHITE_GLAZED_TERRACOTTA("STAINED_CLAY"), + WHITE_SHULKER_BOX, + WHITE_STAINED_GLASS("STAINED_GLASS"), + WHITE_STAINED_GLASS_PANE("THIN_GLASS", "STAINED_GLASS_PANE"), + WHITE_TERRACOTTA("STAINED_CLAY", "TERRACOTTA"), + WHITE_TULIP(6, "RED_ROSE"), + WHITE_WALL_BANNER(15, "WALL_BANNER"), + WHITE_WOOL("WOOL"), + WITCH_SPAWN_EGG(66, "MONSTER_EGG"), + WITHER_ROSE, + WITHER_SKELETON_SKULL(1, "SKULL", "SKULL_ITEM"), + WITHER_SKELETON_SPAWN_EGG(5, "MONSTER_EGG"), + WITHER_SKELETON_WALL_SKULL(1, "SKULL", "SKULL_ITEM"), + WOLF_SPAWN_EGG(95, "MONSTER_EGG"), + WOODEN_AXE("WOOD_AXE"), + WOODEN_HOE("WOOD_HOE"), + WOODEN_PICKAXE("WOOD_PICKAXE"), + WOODEN_SHOVEL("WOOD_SPADE"), + WOODEN_SWORD("WOOD_SWORD"), + WRITABLE_BOOK("BOOK_AND_QUILL"), + WRITTEN_BOOK, + YELLOW_BANNER(11, "STANDING_BANNER", "BANNER"), + YELLOW_BED(supports(12) ? 4 : 0, "BED_BLOCK", "BED"), + YELLOW_CANDLE, + YELLOW_CANDLE_CAKE, + YELLOW_CARPET(4, "CARPET"), + YELLOW_CONCRETE(4, "CONCRETE"), + YELLOW_CONCRETE_POWDER(4, "CONCRETE_POWDER"), + YELLOW_DYE(11, "INK_SACK", "DANDELION_YELLOW"), + YELLOW_GLAZED_TERRACOTTA(4, "STAINED_CLAY", "YELLOW_TERRACOTTA"), + YELLOW_SHULKER_BOX, + YELLOW_STAINED_GLASS(4, "STAINED_GLASS"), + YELLOW_STAINED_GLASS_PANE(4, "THIN_GLASS", "STAINED_GLASS_PANE"), + YELLOW_TERRACOTTA(4, "STAINED_CLAY"), + YELLOW_WALL_BANNER(11, "WALL_BANNER"), + YELLOW_WOOL(4, "WOOL"), + ZOGLIN_SPAWN_EGG, + ZOMBIE_HEAD(2, "SKULL", "SKULL_ITEM"), + ZOMBIE_HORSE_SPAWN_EGG(29, "MONSTER_EGG"), + ZOMBIE_SPAWN_EGG(54, "MONSTER_EGG"), + ZOMBIE_VILLAGER_SPAWN_EGG(27, "MONSTER_EGG"), + ZOMBIE_WALL_HEAD(2, "SKULL", "SKULL_ITEM"), + ZOMBIFIED_PIGLIN_SPAWN_EGG(57, "MONSTER_EGG", "ZOMBIE_PIGMAN_SPAWN_EGG"); + + + /** + * Cached array of {@link XMaterial#values()} to avoid allocating memory for + * calling the method every time. + * + * @since 2.0.0 + */ + public static final XMaterial[] VALUES = values(); + + /** + * We don't want to use {@link Enums#getIfPresent(Class, String)} to avoid a few checks. + * + * @since 5.1.0 + */ + private static final Map NAMES = new HashMap<>(); + + /** + * Guava (Google Core Libraries for Java)'s cache for performance and timed caches. + * For strings that match a certain XMaterial. Mostly cached for configs. + * + * @since 1.0.0 + */ + private static final Cache NAME_CACHE = CacheBuilder.newBuilder() + .expireAfterAccess(1, TimeUnit.HOURS) + .build(); + /** + * This is used for {@link #isOneOf(Collection)} + * + * @since 3.4.0 + */ + private static final LoadingCache CACHED_REGEX = CacheBuilder.newBuilder() + .expireAfterAccess(3, TimeUnit.HOURS) + .build(new CacheLoader() { + @Override + public Pattern load(@Nonnull String str) { + try { + return Pattern.compile(str); + } catch (PatternSyntaxException ex) { + ex.printStackTrace(); + return null; + } + } + }); + /** + * The maximum data value in the pre-flattening update which belongs to {@link #VILLAGER_SPAWN_EGG}
+ * https://minecraftitemids.com/types/spawn-egg + * + * @see #matchXMaterialWithData(String) + * @since 8.0.0 + */ + private static final byte MAX_DATA_VALUE = 120; + /** + * Used to tell the system that the passed object's (name or material) data value + * is not provided or is invalid. + * + * @since 8.0.0 + */ + private static final byte UNKNOWN_DATA_VALUE = -1; + /** + * The maximum material ID before the pre-flattening update which belongs to {@link #MUSIC_DISC_WAIT} + * + * @since 8.1.0 + */ + private static final short MAX_ID = 2267; + /** + * XMaterial Paradox (Duplication Check) + *

+ * A set of duplicated material names in 1.13 and 1.12 that will conflict with the legacy names. + * Values are the new material names. This map also contains illegal elements. Check the static initializer for more info. + *

+ * Duplications are not useful at all in versions above the flattening update {@link Data#ISFLAT} + * This set is only used for matching materials, for parsing refer to {@link #isDuplicated()} + * + * @since 3.0.0 + */ + private static final Set DUPLICATED; + + static { + for (XMaterial material : VALUES) NAMES.put(material.name(), material); + } + + static { + if (Data.ISFLAT) { + // It's not needed at all if it's the newer version. We can save some memory. + DUPLICATED = null; + } else { + // MELON_SLICE, CARROTS, POTATOES, BEETROOTS, GRASS_BLOCK, BRICKS, NETHER_BRICKS, BROWN_MUSHROOM + // Using the constructor to add elements will decide to allocate more size which we don't need. + DUPLICATED = new HashSet<>(4); + DUPLICATED.add(GRASS.name()); + DUPLICATED.add(MELON.name()); + DUPLICATED.add(BRICK.name()); + DUPLICATED.add(NETHER_BRICK.name()); + } + } + + /** + * The data value of this material https://minecraft.gamepedia.com/Java_Edition_data_values/Pre-flattening + * It's never a negative number. + * + * @see #getData() + */ + private final byte data; + /** + * A list of material names that was being used for older verions. + * + * @see #getLegacy() + */ + @Nonnull + private final String[] legacy; + /** + * The cached Bukkit parsed material. + * + * @see #parseMaterial() + * @since 9.0.0 + */ + @Nullable + private final Material material; + + XMaterial(int data, @Nonnull String... legacy) { + this.data = (byte) data; + this.legacy = legacy; + + Material mat = null; + if ((!Data.ISFLAT && this.isDuplicated()) || (mat = Material.getMaterial(this.name())) == null) { + for (int i = legacy.length - 1; i >= 0; i--) { + mat = Material.getMaterial(legacy[i]); + if (mat != null) break; + } + } + this.material = mat; + } + + XMaterial(String... legacy) { this(0, legacy); } + + /** + * Checks if the version is 1.13 Aquatic Update or higher. + * An invocation of this method yields the cached result from the expression: + *

+ *

+ * {@link #supports(int) 13}} + *
+ * + * @return true if 1.13 or higher. + * @see #getVersion() + * @see #supports(int) + * @since 1.0.0 + * @deprecated Use {@code XMaterial.supports(13)} instead. This method name can be confusing. + */ + @Deprecated + public static boolean isNewVersion() { + return Data.ISFLAT; + } + + /** + * This is just an extra method that can be used for many cases. + * It can be used in {@link org.bukkit.event.player.PlayerInteractEvent} + * or when accessing {@link org.bukkit.entity.Player#getMainHand()}, + * or other compatibility related methods. + *

+ * An invocation of this method yields exactly the same result as the expression: + *

+ *

+ * !{@link #supports(int)} 9 + *
+ * + * @since 2.0.0 + * @deprecated Use {@code !XMaterial.supports(9)} instead. + */ + @Deprecated + public static boolean isOneEight() { + return !supports(9); + } + + /** + * Gets the XMaterial with this name similar to {@link #valueOf(String)} + * without throwing an exception. + * + * @param name the name of the material. + * + * @return an optional that can be empty. + * @since 5.1.0 + */ + @Nonnull + private static Optional getIfPresent(@Nonnull String name) { + return Optional.ofNullable(NAMES.get(name)); + } + + /** + * The current version of the server. + * + * @return the current server version minor number. + * @see #supports(int) + * @since 2.0.0 + */ + public static int getVersion() { + return Data.VERSION; + } + + /** + * When using 1.13+, this helps to find the old material name + * with its data value using a cached search for optimization. + * + * @see #matchDefinedXMaterial(String, byte) + * @since 1.0.0 + */ + @Nullable + private static XMaterial requestOldXMaterial(@Nonnull String name, byte data) { + String holder = name + data; + XMaterial cache = NAME_CACHE.getIfPresent(holder); + if (cache != null) return cache; + + for (XMaterial material : VALUES) { + // Not using material.name().equals(name) check is intended. + if ((data == UNKNOWN_DATA_VALUE || data == material.data) && material.anyMatchLegacy(name)) { + NAME_CACHE.put(holder, material); + return material; + } + } + + return null; + } + + /** + * Parses the given material name as an XMaterial with a given data + * value in the string if attached. Check {@link #matchXMaterialWithData(String)} for more info. + * + * @see #matchXMaterialWithData(String) + * @see #matchDefinedXMaterial(String, byte) + * @since 2.0.0 + */ + @Nonnull + public static Optional matchXMaterial(@Nonnull String name) { + Validate.notEmpty(name, "Cannot match a material with null or empty material name"); + Optional oldMatch = matchXMaterialWithData(name); + return oldMatch.isPresent() ? oldMatch : matchDefinedXMaterial(format(name), UNKNOWN_DATA_VALUE); + } + + /** + * Parses material name and data value from the specified string. + * The separator for the material name and its data value is {@code :} + * Spaces are allowed. Mostly used when getting materials from config for old school minecrafters. + *

+ * Examples + *

+     *     {@code INK_SACK:1 -> RED_DYE}
+     *     {@code WOOL: 14  -> RED_WOOL}
+     * 
+ * + * @param name the material string that consists of the material name, data and separator character. + * + * @return the parsed XMaterial. + * @see #matchXMaterial(String) + * @since 3.0.0 + */ + @Nonnull + private static Optional matchXMaterialWithData(@Nonnull String name) { + int index = name.indexOf(':'); + if (index != -1) { + String mat = format(name.substring(0, index)); + try { + // We don't use Byte.parseByte because we have our own range check. + byte data = (byte) Integer.parseInt(StringUtils.deleteWhitespace(name.substring(index + 1))); + return data >= 0 && data < MAX_DATA_VALUE ? matchDefinedXMaterial(mat, data) : matchDefinedXMaterial(mat, UNKNOWN_DATA_VALUE); + } catch (NumberFormatException ignored) { + return matchDefinedXMaterial(mat, UNKNOWN_DATA_VALUE); + } + } + + return Optional.empty(); + } + + /** + * Parses the given material as an XMaterial. + * + * @throws IllegalArgumentException may be thrown as an unexpected exception. + * @see #matchDefinedXMaterial(String, byte) + * @see #matchXMaterial(ItemStack) + * @since 2.0.0 + */ + @Nonnull + public static XMaterial matchXMaterial(@Nonnull Material material) { + Objects.requireNonNull(material, "Cannot match null material"); + return matchDefinedXMaterial(material.name(), UNKNOWN_DATA_VALUE) + .orElseThrow(() -> new IllegalArgumentException("Unsupported material with no data value: " + material.name())); + } + + /** + * Parses the given item as an XMaterial using its material and data value (durability) + * if not a damageable item {@link ItemStack#getDurability()}. + * + * @param item the ItemStack to match. + * + * @return an XMaterial if matched any. + * @throws IllegalArgumentException may be thrown as an unexpected exception. + * @see #matchXMaterial(Material) + * @since 2.0.0 + */ + @Nonnull + @SuppressWarnings("deprecation") + public static XMaterial matchXMaterial(@Nonnull ItemStack item) { + Objects.requireNonNull(item, "Cannot match null ItemStack"); + String material = item.getType().name(); + byte data = (byte) (Data.ISFLAT || item.getType().getMaxDurability() > 0 ? 0 : item.getDurability()); + + // Check FILLED_MAP enum for more info. + //if (!Data.ISFLAT && item.hasItemMeta() && item.getItemMeta() instanceof org.bukkit.inventory.meta.MapMeta) return FILLED_MAP; + + return matchDefinedXMaterial(material, data) + .orElseThrow(() -> new IllegalArgumentException("Unsupported material from item: " + material + " (" + data + ')')); + } + + /** + * The main method that parses the given material name and data value as an XMaterial. + * All the values passed to this method will not be null or empty and are formatted correctly. + * + * @param name the formatted name of the material. + * @param data the data value of the material. Is always 0 or {@link #UNKNOWN_DATA_VALUE} when {@link Data#ISFLAT} + * + * @return an XMaterial (with the same data value if specified) + * @see #matchXMaterial(Material) + * @see #matchXMaterial(int, byte) + * @see #matchXMaterial(ItemStack) + * @since 3.0.0 + */ + @Nonnull + protected static Optional matchDefinedXMaterial(@Nonnull String name, byte data) { + // if (!Boolean.valueOf(Boolean.getBoolean(Boolean.TRUE.toString())).equals(Boolean.FALSE.booleanValue())) return null; + Boolean duplicated = null; + boolean isAMap = name.equalsIgnoreCase("MAP"); + + // Do basic number and boolean checks before accessing more complex enum stuff. + if (Data.ISFLAT || (!isAMap && data <= 0 && !(duplicated = isDuplicated(name)))) { + Optional xMaterial = getIfPresent(name); + if (xMaterial.isPresent()) return xMaterial; + } + // Usually flat versions wouldn't pass this point, but some special materials do. + + XMaterial oldXMaterial = requestOldXMaterial(name, data); + if (oldXMaterial == null) { + // Special case. Refer to FILLED_MAP for more info. + return (data >= 0 && isAMap) ? Optional.of(FILLED_MAP) : Optional.empty(); + } + + if (!Data.ISFLAT && oldXMaterial.isPlural() && (duplicated == null ? isDuplicated(name) : duplicated)) return getIfPresent(name); + return Optional.of(oldXMaterial); + } + + /** + * XMaterial Paradox (Duplication Check) + * Checks if the material has any duplicates. + *

+ * Example: + *

{@code MELON, CARROT, POTATO, BEETROOT -> true} + * + * @param name the name of the material to check. + * + * @return true if there's a duplicated material for this material, otherwise false. + * @since 2.0.0 + */ + private static boolean isDuplicated(@Nonnull String name) { + // Don't use matchXMaterial() since this method is being called from matchXMaterial() itself and will cause a StackOverflowError. + return DUPLICATED.contains(name); + } + + /** + * Gets the XMaterial based on the material's ID (Magic Value) and data value.
+ * You should avoid using this for performance issues. + * + * @param id the ID (Magic value) of the material. + * @param data the data value of the material. + * + * @return a parsed XMaterial with the same ID and data value. + * @see #matchXMaterial(ItemStack) + * @since 2.0.0 + * @deprecated this method loops through all the available materials and matches their ID using {@link #getId()} + * which takes a really long time. Plugins should no longer support IDs. If you want, you can make a {@link Map} cache yourself. + * This method obviously doesn't work for 1.13+ and will not be supported. This is only here for debugging purposes. + */ + @Nonnull + @Deprecated + public static Optional matchXMaterial(int id, byte data) { + if (id < 0 || id > MAX_ID || data < 0) return Optional.empty(); + for (XMaterial materials : VALUES) { + if (materials.data == data && materials.getId() == id) return Optional.of(materials); + } + return Optional.empty(); + } + + /** + * Attempts to build the string like an enum name. + * Removes all the spaces, and extra non-English characters. Also removes some config/in-game based strings. + * While this method is hard to maintain, it's extremely efficient. It's approximately more than x5 times faster than + * the normal RegEx + String Methods approach for both formatted and unformatted material names. + * + * @param name the material name to modify. + * + * @return an enum name. + * @since 2.0.0 + */ + @Nonnull + protected static String format(@Nonnull String name) { + int len = name.length(); + char[] chs = new char[len]; + int count = 0; + boolean appendUnderline = false; + + for (int i = 0; i < len; i++) { + char ch = name.charAt(i); + + if (!appendUnderline && count != 0 && (ch == '-' || ch == ' ' || ch == '_') && chs[count] != '_') + appendUnderline = true; + else { + boolean number = false; + // Old materials have numbers in them. + if ((ch >= 'A' && ch <= 'Z') || (ch >= 'a' && ch <= 'z') || (number = (ch >= '0' && ch <= '9'))) { + if (appendUnderline) { + chs[count++] = '_'; + appendUnderline = false; + } + + if (number) chs[count++] = ch; + else chs[count++] = (char) (ch & 0x5f); + } + } + } + + return new String(chs, 0, count); + } + + /** + * Checks if the specified version is the same version or higher than the current server version. + * + * @param version the major version to be checked. "1." is ignored. E.g. 1.12 = 12 | 1.9 = 9 + * + * @return true of the version is equal or higher than the current version. + * @since 2.0.0 + */ + public static boolean supports(int version) { + return Data.VERSION >= version; + } + + public String[] getLegacy() { + return this.legacy; + } + + /** + * XMaterial Paradox (Duplication Check) + * I've concluded that this is just an infinite loop that keeps + * going around the Singular Form and the Plural Form materials. A waste of brain cells and a waste of time. + * This solution works just fine anyway. + *

+ * A solution for XMaterial Paradox. + * Manually parses the duplicated materials to find the exact material based on the server version. + * If the name ends with "S" -> Plural Form Material. + * Plural methods are only plural if they're also {@link #DUPLICATED} + *

+ * The only special exceptions are {@link #BRICKS} and {@link #NETHER_BRICKS} + * + * @return true if this material is a plural form material, otherwise false. + * @since 8.0.0 + */ + private boolean isPlural() { + // this.name().charAt(this.name().length() - 1) == 'S' + return this == CARROTS || this == POTATOES; + } + + /** + * Checks if the list of given material names matches the given base material. + * Mostly used for configs. + *

+ * Supports {@link String#contains} {@code CONTAINS:NAME} and Regular Expression {@code REGEX:PATTERN} formats. + *

+ * Example: + *

+     *     XMaterial material = {@link #matchXMaterial(ItemStack)};
+     *     if (material.isOneOf(plugin.getConfig().getStringList("disabled-items")) return;
+     * 
+ *
+ * {@code CONTAINS} Examples: + *
+     *     {@code "CONTAINS:CHEST" -> CHEST, ENDERCHEST, TRAPPED_CHEST -> true}
+     *     {@code "cOnTaINS:dYe" -> GREEN_DYE, YELLOW_DYE, BLUE_DYE, INK_SACK -> true}
+     * 
+ *

+ * {@code REGEX} Examples + *

+     *     {@code "REGEX:^.+_.+_.+$" -> Every Material with 3 underlines or more: SHULKER_SPAWN_EGG, SILVERFISH_SPAWN_EGG, SKELETON_HORSE_SPAWN_EGG}
+     *     {@code "REGEX:^.{1,3}$" -> Material names that have 3 letters only: BED, MAP, AIR}
+     * 
+ *

+ * The reason that there are tags for {@code CONTAINS} and {@code REGEX} is for the performance. + * Although RegEx patterns are cached in this method, + * please avoid using the {@code REGEX} tag if you can use the {@code CONTAINS} tag instead. + * It'll have a huge impact on performance. + * Please avoid using {@code (capturing groups)} there's no use for them in this case. + * If you want to use groups, use {@code (?: non-capturing groups)}. It's faster. + *

+ * Want to learn RegEx? You can mess around in RegExr website. + * + * @param materials the material names to check base material on. + * + * @return true if one of the given material names is similar to the base material. + * @since 3.1.1 + */ + public boolean isOneOf(@Nullable Collection materials) { + if (materials == null || materials.isEmpty()) return false; + String name = this.name(); + + for (String comp : materials) { + String checker = comp.toUpperCase(Locale.ENGLISH); + if (checker.startsWith("CONTAINS:")) { + comp = format(checker.substring(9)); + if (name.contains(comp)) return true; + continue; + } + if (checker.startsWith("REGEX:")) { + comp = comp.substring(6); + Pattern pattern = CACHED_REGEX.getUnchecked(comp); + if (pattern != null && pattern.matcher(name).matches()) return true; + continue; + } + + // Direct Object Equals + Optional xMat = matchXMaterial(comp); + if (xMat.isPresent() && xMat.get() == this) return true; + } + return false; + } + + /** + * Sets the {@link Material} (and data value on older versions) of an item. + * Damageable materials will not have their durability changed. + *

+ * Use {@link #parseItem()} instead when creating new ItemStacks. + * + * @param item the item to change its type. + * + * @see #parseItem() + * @since 3.0.0 + */ + @Nonnull + @SuppressWarnings("deprecation") + public ItemStack setType(@Nonnull ItemStack item) { + Objects.requireNonNull(item, "Cannot set material for null ItemStack"); + Material material = this.parseMaterial(); + Objects.requireNonNull(material, () -> "Unsupported material: " + this.name()); + + item.setType(material); + if (!Data.ISFLAT && material.getMaxDurability() <= 0) item.setDurability(this.data); + return item; + } + + /** + * Checks if the given material name matches any of this xmaterial's legacy material names. + * All the values passed to this method will not be null or empty and are formatted correctly. + * + * @param name the material name to check. + * + * @return true if it's one of the legacy names, otherwise false. + * @since 2.0.0 + */ + private boolean anyMatchLegacy(@Nonnull String name) { + for (int i = this.legacy.length - 1; i >= 0; i--) { + if (name.equals(this.legacy[i])) return true; + } + return false; + } + + /** + * Parses an enum name to a user-friendly name. + * These names will have underlines removed and with each word capitalized. + *

+ * Examples: + *

+     *     {@literal EMERALD                 -> Emerald}
+     *     {@literal EMERALD_BLOCK           -> Emerald Block}
+     *     {@literal ENCHANTED_GOLDEN_APPLE  -> Enchanted Golden Apple}
+     * 
+ * + * @return a more user-friendly enum name. + * @since 3.0.0 + */ + @Override + @Nonnull + public String toString() { + return WordUtils.capitalize(this.name().replace('_', ' ').toLowerCase(Locale.ENGLISH)); + } + + /** + * Gets the ID (Magic value) of the material. + * https://www.minecraftinfo.com/idlist.htm + *

+ * Spigot added material ID support back in 1.16+ + * + * @return the ID of the material or -1 if it's not a legacy material or the server doesn't support the material. + * @see #matchXMaterial(int, byte) + * @since 2.2.0 + */ + @SuppressWarnings("deprecation") + public int getId() { + // https://hub.spigotmc.org/stash/projects/SPIGOT/repos/bukkit/diff/src/main/java/org/bukkit/Material.java?until=1cb03826ebde4ef887519ce37b0a2a341494a183 + // Should start working again in 1.16+ + Material material = this.parseMaterial(); + if (material == null) return -1; + try { + return material.getId(); + } catch (IllegalArgumentException ignored) { + return -1; + } + } + + /** + * The data value of this material pre-flattening. + *

+ * Can be accessed with {@link ItemStack#getData()} then {@code MaterialData#getData()} + * or {@link ItemStack#getDurability()} if not damageable. + * + * @return data of this material, or 0 if none. + * @since 1.0.0 + */ + @SuppressWarnings("deprecation") + public byte getData() { + return data; + } + + /** + * Parses an item from this XMaterial. + * Uses data values on older versions. + * + * @return an ItemStack with the same material (and data value if in older versions.) + * @see #setType(ItemStack) + * @since 2.0.0 + */ + @Nullable + @SuppressWarnings("deprecation") + public ItemStack parseItem() { + Material material = this.parseMaterial(); + if (material == null) return null; + return Data.ISFLAT ? new ItemStack(material) : new ItemStack(material, 1, this.data); + } + + /** + * Parses the material of this XMaterial. + * + * @return the material related to this XMaterial based on the server version. + * @since 1.0.0 + */ + @Nullable + public Material parseMaterial() { + return this.material; + } + + /** + * Checks if an item has the same material (and data value on older versions). + * + * @param item item to check. + * + * @return true if the material is the same as the item's material (and data value if on older versions), otherwise false. + * @since 1.0.0 + */ + @SuppressWarnings("deprecation") + public boolean isSimilar(@Nonnull ItemStack item) { + Objects.requireNonNull(item, "Cannot compare with null ItemStack"); + if (item.getType() != this.parseMaterial()) return false; + return Data.ISFLAT || item.getDurability() == this.data || item.getType().getMaxDurability() > 0; + } + + /** + * Checks if this material is supported in the current version. + * Suggested materials will be ignored. + *

+ * Note that you should use {@link #parseMaterial()} or {@link #parseItem()} and check if it's null + * if you're going to parse and use the material/item later. + * + * @return true if the material exists in {@link Material} list. + * @since 2.0.0 + */ + public boolean isSupported() { + return this.material != null; + } + + /** + * This method is needed due to Java enum initialization limitations. + * It's really inefficient yes, but it's only used for initialization. + *

+ * Yes there are many other ways like comparing the hardcoded ordinal or using a boolean in the enum constructor, + * but it's not really a big deal. + *

+ * This method should not be called if the version is after the flattening update {@link Data#ISFLAT} + * and is only used for parsing materials, not matching, for matching check {@link #DUPLICATED} + */ + private boolean isDuplicated() { + switch (this.name()) { + case "MELON": + case "CARROT": + case "POTATO": + case "GRASS": + case "BRICK": + case "NETHER_BRICK": + + // Illegal Elements + // Since both 1.12 and 1.13 have _DOOR XMaterial will use it + // for 1.12 to parse the material, but it needs _DOOR_ITEM. + // We'll trick XMaterial into thinking this needs to be parsed + // using the old methods. + // Some of these materials have their enum name added to the legacy list as well. + case "DARK_OAK_DOOR": + case "ACACIA_DOOR": + case "BIRCH_DOOR": + case "JUNGLE_DOOR": + case "SPRUCE_DOOR": + case "MAP": + case "CAULDRON": + case "BREWING_STAND": + case "FLOWER_POT": + return true; + default: + return false; + } + } + + /** + * Used for data that need to be accessed during enum initialization. + * + * @since 9.0.0 + */ + private static final class Data { + /** + * The current version of the server in the form of a major version. + * If the static initialization for this fails, you know something's wrong with the server software. + * + * @since 1.0.0 + */ + private static final int VERSION = parseVersion(); + /** + * Cached result if the server version is after the v1.13 flattening update. + * + * @since 3.0.0 + */ + private static final boolean ISFLAT = supports(13); + + /** + * Gets the exact minor version (8, 9, ..., 17, 18) + * It's necessary to use this alternative method instead of static initialization block + * since you can't throw exceptions in them directly. + *

+ * Performance doesn't matter here as the method is only called once. + * + * @return the exact minor version. + * @see #VERSION + * @since 8.5.0 + */ + private static int parseVersion() { + String version = Bukkit.getVersion(); + Matcher matcher = Pattern.compile("MC: \\d\\.(\\d+)").matcher(version); + + if (matcher.find()) return Integer.parseInt(matcher.group(1)); + throw new IllegalArgumentException("Failed to parse server version from: " + version); + } + } +} \ No newline at end of file