From b5df2bb80ce2c1ca77283a7ce395f12aebc0a2b4 Mon Sep 17 00:00:00 2001 From: Stijn Bannink Date: Sun, 24 Dec 2023 15:38:40 +0100 Subject: [PATCH] Fixed multiple events, still WIP for Interact event --- .../mapreflectionapi/api/MapController.java | 9 - .../mapreflectionapi/api/MapManager.java | 29 + .../mapreflectionapi/api/MapWrapper.java | 42 +- .../CreativeInventoryMapUpdateEvent.java | 25 +- .../api/events/MapCancelEvent.java | 19 +- .../api/events/MapContentUpdateEvent.java | 13 +- .../api/events/MapInteractEvent.java | 28 +- .../api/events/types/CancellableEvent.java | 54 + .../api/events/types/Event.java | 46 + .../listeners/PacketListener.java | 34 +- .../mapreflectionapi/utils/UpdateManager.java | 2 - .../mapreflectionapi/utils/XMaterial.java | 2406 +++++++++++++++++ 12 files changed, 2587 insertions(+), 120 deletions(-) create mode 100644 src/main/java/tech/sbdevelopment/mapreflectionapi/api/events/types/CancellableEvent.java create mode 100644 src/main/java/tech/sbdevelopment/mapreflectionapi/api/events/types/Event.java create mode 100644 src/main/java/tech/sbdevelopment/mapreflectionapi/utils/XMaterial.java diff --git a/src/main/java/tech/sbdevelopment/mapreflectionapi/api/MapController.java b/src/main/java/tech/sbdevelopment/mapreflectionapi/api/MapController.java index 11a71cd..8618aa4 100644 --- a/src/main/java/tech/sbdevelopment/mapreflectionapi/api/MapController.java +++ b/src/main/java/tech/sbdevelopment/mapreflectionapi/api/MapController.java @@ -117,13 +117,4 @@ public interface MapController extends IMapController { * @param frame {@link ItemFrame} to clear */ void clearFrame(Player player, ItemFrame frame); - - /** - * Get an {@link ItemFrame} by its entity ID - * - * @param world The world the {@link ItemFrame} is in - * @param entityId Entity-ID of the {@link ItemFrame} - * @return The found {@link ItemFrame}, or null - */ - ItemFrame getItemFrameById(World world, int entityId); } diff --git a/src/main/java/tech/sbdevelopment/mapreflectionapi/api/MapManager.java b/src/main/java/tech/sbdevelopment/mapreflectionapi/api/MapManager.java index 7120429..60fbc15 100644 --- a/src/main/java/tech/sbdevelopment/mapreflectionapi/api/MapManager.java +++ b/src/main/java/tech/sbdevelopment/mapreflectionapi/api/MapManager.java @@ -19,10 +19,13 @@ package tech.sbdevelopment.mapreflectionapi.api; import org.bukkit.OfflinePlayer; +import org.bukkit.World; +import org.bukkit.entity.ItemFrame; import org.bukkit.entity.Player; import org.jetbrains.annotations.Nullable; import tech.sbdevelopment.mapreflectionapi.api.exceptions.MapLimitExceededException; import tech.sbdevelopment.mapreflectionapi.managers.Configuration; +import tech.sbdevelopment.mapreflectionapi.utils.ReflectionUtil; import java.awt.image.BufferedImage; import java.util.HashSet; @@ -30,6 +33,8 @@ import java.util.List; import java.util.Set; import java.util.concurrent.CopyOnWriteArrayList; +import static tech.sbdevelopment.mapreflectionapi.utils.ReflectionUtils.*; + /** * The {@link MapManager} manages all the maps. It also contains functions for wrapping. */ @@ -172,6 +177,30 @@ public class MapManager { return null; } + /** + * Get an {@link ItemFrame} by its entity ID + * + * @param world The world the {@link ItemFrame} is in + * @param entityId Entity-ID of the {@link ItemFrame} + * @return The found {@link ItemFrame}, or null + */ + public ItemFrame getItemFrameById(World world, int entityId) { + Object worldHandle = getHandle(world); + Object nmsEntity = ReflectionUtil.callMethod(worldHandle, supports(18) ? "a" : "getEntity", entityId); + if (nmsEntity == null) return null; + + Object craftEntity = ReflectionUtil.callMethod(nmsEntity, "getBukkitEntity"); + if (craftEntity == null) return null; + + Class itemFrameClass = getNMSClass("world.entity.decoration", "EntityItemFrame"); + if (itemFrameClass == null) return null; + + if (craftEntity.getClass().isAssignableFrom(itemFrameClass)) + return (ItemFrame) itemFrameClass.cast(craftEntity); + + return null; + } + /** * Register an occupied map ID * diff --git a/src/main/java/tech/sbdevelopment/mapreflectionapi/api/MapWrapper.java b/src/main/java/tech/sbdevelopment/mapreflectionapi/api/MapWrapper.java index eac90da..a7eae14 100644 --- a/src/main/java/tech/sbdevelopment/mapreflectionapi/api/MapWrapper.java +++ b/src/main/java/tech/sbdevelopment/mapreflectionapi/api/MapWrapper.java @@ -31,6 +31,7 @@ import tech.sbdevelopment.mapreflectionapi.api.events.MapContentUpdateEvent; import tech.sbdevelopment.mapreflectionapi.api.exceptions.MapLimitExceededException; import tech.sbdevelopment.mapreflectionapi.managers.Configuration; import tech.sbdevelopment.mapreflectionapi.utils.ReflectionUtil; +import tech.sbdevelopment.mapreflectionapi.utils.XMaterial; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; @@ -45,15 +46,9 @@ import static tech.sbdevelopment.mapreflectionapi.utils.ReflectionUtils.*; */ @Getter public class MapWrapper extends AbstractMapWrapper { - private static final String REFERENCE_METADATA = "MAP_WRAPPER_REF"; + public static final String REFERENCE_METADATA = "MAP_WRAPPER_REF"; protected ArrayImage content; - private static final Material MAP_MATERIAL; - - static { - MAP_MATERIAL = supports(13) ? Material.FILLED_MAP : Material.MAP; - } - /** * Construct a new {@link MapWrapper} * @@ -215,7 +210,7 @@ public class MapWrapper extends AbstractMapWrapper { @Override public void showInHand(Player player, boolean force) { - if (player.getInventory().getItemInMainHand().getType() != MAP_MATERIAL && !force) + if (player.getInventory().getItemInMainHand().getType() != XMaterial.FILLED_MAP.parseMaterial() && !force) return; showInInventory(player, player.getInventory().getHeldItemSlot(), force); @@ -233,7 +228,7 @@ public class MapWrapper extends AbstractMapWrapper { @Override public void showInFrame(Player player, ItemFrame frame, boolean force) { - if (frame.getItem().getType() != MAP_MATERIAL && !force) + if (frame.getItem().getType() != XMaterial.FILLED_MAP.parseMaterial() && !force) return; showInFrame(player, frame.getEntityId()); @@ -248,7 +243,7 @@ public class MapWrapper extends AbstractMapWrapper { public void showInFrame(Player player, int entityId, String debugInfo) { if (!isViewing(player)) return; - ItemStack stack = new ItemStack(MAP_MATERIAL, 1); + ItemStack stack = new ItemStack(XMaterial.FILLED_MAP.parseMaterial(), 1); if (debugInfo != null) { ItemMeta itemMeta = stack.getItemMeta(); itemMeta.setDisplayName(debugInfo); @@ -256,7 +251,7 @@ public class MapWrapper extends AbstractMapWrapper { } Bukkit.getScheduler().runTask(MapReflectionAPI.getInstance(), () -> { - ItemFrame frame = getItemFrameById(player.getWorld(), entityId); + ItemFrame frame = MapReflectionAPI.getMapManager().getItemFrameById(player.getWorld(), entityId); if (frame != null) { frame.removeMetadata(REFERENCE_METADATA, MapReflectionAPI.getInstance()); frame.setMetadata(REFERENCE_METADATA, new FixedMetadataValue(MapReflectionAPI.getInstance(), MapWrapper.this)); @@ -270,7 +265,7 @@ public class MapWrapper extends AbstractMapWrapper { public void clearFrame(Player player, int entityId) { sendItemFramePacket(player, entityId, null, -1); Bukkit.getScheduler().runTask(MapReflectionAPI.getInstance(), () -> { - ItemFrame frame = getItemFrameById(player.getWorld(), entityId); + ItemFrame frame = MapReflectionAPI.getMapManager().getItemFrameById(player.getWorld(), entityId); if (frame != null) frame.removeMetadata(REFERENCE_METADATA, MapReflectionAPI.getInstance()); }); } @@ -280,29 +275,8 @@ public class MapWrapper extends AbstractMapWrapper { clearFrame(player, frame.getEntityId()); } - @Override - public ItemFrame getItemFrameById(World world, int entityId) { - Object worldHandle = getHandle(world); - Object nmsEntity = ReflectionUtil.callMethod(worldHandle, supports(18) ? "a" : "getEntity", entityId); - if (nmsEntity == null) return null; - - Object craftEntity = ReflectionUtil.callMethod(nmsEntity, "getBukkitEntity"); - if (craftEntity == null) return null; - - Class itemFrameClass = getNMSClass("world.entity.decoration", "EntityItemFrame"); - if (itemFrameClass == null) return null; - - if (craftEntity.getClass().isAssignableFrom(itemFrameClass)) - return (ItemFrame) itemFrameClass.cast(craftEntity); - - return null; - } - private Object asCraftItemStack(Player player) { - return createCraftItemStack(supports(13) - ? new ItemStack(MAP_MATERIAL, 1) - : new ItemStack(MAP_MATERIAL, 1, (short) getMapId(player) - ), (short) getMapId(player)); + return createCraftItemStack(new ItemStack(XMaterial.FILLED_MAP.parseMaterial(), 1, (short) getMapId(player)), (short) getMapId(player)); } private Object createCraftItemStack(@NotNull ItemStack stack, int mapId) { diff --git a/src/main/java/tech/sbdevelopment/mapreflectionapi/api/events/CreativeInventoryMapUpdateEvent.java b/src/main/java/tech/sbdevelopment/mapreflectionapi/api/events/CreativeInventoryMapUpdateEvent.java index ffea71f..561ae96 100644 --- a/src/main/java/tech/sbdevelopment/mapreflectionapi/api/events/CreativeInventoryMapUpdateEvent.java +++ b/src/main/java/tech/sbdevelopment/mapreflectionapi/api/events/CreativeInventoryMapUpdateEvent.java @@ -1,6 +1,6 @@ /* * This file is part of MapReflectionAPI. - * Copyright (c) 2022 inventivetalent / SBDevelopment - All Rights Reserved + * Copyright (c) 2022-2023 inventivetalent / SBDevelopment - All Rights Reserved * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -20,28 +20,24 @@ package tech.sbdevelopment.mapreflectionapi.api.events; import lombok.Getter; import lombok.RequiredArgsConstructor; -import lombok.Setter; +import org.bukkit.Bukkit; import org.bukkit.Material; import org.bukkit.entity.Player; -import org.bukkit.event.Cancellable; -import org.bukkit.event.Event; -import org.bukkit.event.HandlerList; import org.bukkit.inventory.ItemStack; -import org.jetbrains.annotations.NotNull; +import org.bukkit.map.MapView; import org.jetbrains.annotations.Nullable; import tech.sbdevelopment.mapreflectionapi.MapReflectionAPI; import tech.sbdevelopment.mapreflectionapi.api.MapWrapper; +import tech.sbdevelopment.mapreflectionapi.api.events.types.CancellableEvent; +import tech.sbdevelopment.mapreflectionapi.utils.ReflectionUtils; +import tech.sbdevelopment.mapreflectionapi.utils.XMaterial; /** * This event gets fired when a map in the creative inventory gets updated */ @RequiredArgsConstructor @Getter -public class CreativeInventoryMapUpdateEvent extends Event implements Cancellable { - private static final HandlerList handlerList = new HandlerList(); - @Setter - private boolean cancelled; - +public class CreativeInventoryMapUpdateEvent extends CancellableEvent { private final Player player; private final int slot; private final ItemStack item; @@ -62,11 +58,6 @@ public class CreativeInventoryMapUpdateEvent extends Event implements Cancellabl this.item = item; } - @Override - public @NotNull HandlerList getHandlers() { - return handlerList; - } - /** * Get the {@link MapWrapper} of the map of this event * @@ -76,7 +67,7 @@ public class CreativeInventoryMapUpdateEvent extends Event implements Cancellabl public MapWrapper getMapWrapper() { if (mapWrapper == null) { if (item == null) return null; - if (item.getType() != Material.MAP) return null; + if (!XMaterial.FILLED_MAP.isSimilar(item)) return null; mapWrapper = MapReflectionAPI.getMapManager().getWrapperForId(player, item.getDurability()); } diff --git a/src/main/java/tech/sbdevelopment/mapreflectionapi/api/events/MapCancelEvent.java b/src/main/java/tech/sbdevelopment/mapreflectionapi/api/events/MapCancelEvent.java index 4cc282f..5fe3ac1 100644 --- a/src/main/java/tech/sbdevelopment/mapreflectionapi/api/events/MapCancelEvent.java +++ b/src/main/java/tech/sbdevelopment/mapreflectionapi/api/events/MapCancelEvent.java @@ -1,6 +1,6 @@ /* * This file is part of MapReflectionAPI. - * Copyright (c) 2022 inventivetalent / SBDevelopment - All Rights Reserved + * Copyright (c) 2022-2023 inventivetalent / SBDevelopment - All Rights Reserved * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -20,23 +20,15 @@ package tech.sbdevelopment.mapreflectionapi.api.events; import lombok.Getter; import lombok.RequiredArgsConstructor; -import lombok.Setter; import org.bukkit.entity.Player; -import org.bukkit.event.Cancellable; -import org.bukkit.event.Event; -import org.bukkit.event.HandlerList; -import org.jetbrains.annotations.NotNull; +import tech.sbdevelopment.mapreflectionapi.api.events.types.CancellableEvent; /** * This event gets fired when a map creation is cancelled */ @RequiredArgsConstructor @Getter -public class MapCancelEvent extends Event implements Cancellable { - private static final HandlerList handlerList = new HandlerList(); - @Setter - private boolean cancelled; - +public class MapCancelEvent extends CancellableEvent { private final Player player; private final int id; @@ -52,9 +44,4 @@ public class MapCancelEvent extends Event implements Cancellable { this.player = player; this.id = id; } - - @Override - public @NotNull HandlerList getHandlers() { - return handlerList; - } } diff --git a/src/main/java/tech/sbdevelopment/mapreflectionapi/api/events/MapContentUpdateEvent.java b/src/main/java/tech/sbdevelopment/mapreflectionapi/api/events/MapContentUpdateEvent.java index d106f06..ce3f59d 100644 --- a/src/main/java/tech/sbdevelopment/mapreflectionapi/api/events/MapContentUpdateEvent.java +++ b/src/main/java/tech/sbdevelopment/mapreflectionapi/api/events/MapContentUpdateEvent.java @@ -1,6 +1,6 @@ /* * This file is part of MapReflectionAPI. - * Copyright (c) 2022 inventivetalent / SBDevelopment - All Rights Reserved + * Copyright (c) 2022-2023 inventivetalent / SBDevelopment - All Rights Reserved * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -21,11 +21,9 @@ package tech.sbdevelopment.mapreflectionapi.api.events; import lombok.Getter; import lombok.RequiredArgsConstructor; import lombok.Setter; -import org.bukkit.event.Event; -import org.bukkit.event.HandlerList; -import org.jetbrains.annotations.NotNull; import tech.sbdevelopment.mapreflectionapi.api.ArrayImage; import tech.sbdevelopment.mapreflectionapi.api.MapWrapper; +import tech.sbdevelopment.mapreflectionapi.api.events.types.Event; /** * This event gets fired when the content of a {@link MapWrapper} is updated @@ -33,8 +31,6 @@ import tech.sbdevelopment.mapreflectionapi.api.MapWrapper; @RequiredArgsConstructor @Getter public class MapContentUpdateEvent extends Event { - private static final HandlerList handlerList = new HandlerList(); - private final MapWrapper wrapper; private final ArrayImage content; @Setter @@ -52,9 +48,4 @@ public class MapContentUpdateEvent extends Event { this.wrapper = wrapper; this.content = content; } - - @Override - public @NotNull HandlerList getHandlers() { - return handlerList; - } } diff --git a/src/main/java/tech/sbdevelopment/mapreflectionapi/api/events/MapInteractEvent.java b/src/main/java/tech/sbdevelopment/mapreflectionapi/api/events/MapInteractEvent.java index 6e2dbb4..99fc4ab 100644 --- a/src/main/java/tech/sbdevelopment/mapreflectionapi/api/events/MapInteractEvent.java +++ b/src/main/java/tech/sbdevelopment/mapreflectionapi/api/events/MapInteractEvent.java @@ -1,6 +1,6 @@ /* * This file is part of MapReflectionAPI. - * Copyright (c) 2022 inventivetalent / SBDevelopment - All Rights Reserved + * Copyright (c) 2022-2023 inventivetalent / SBDevelopment - All Rights Reserved * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -20,28 +20,20 @@ package tech.sbdevelopment.mapreflectionapi.api.events; import lombok.Getter; import lombok.RequiredArgsConstructor; -import lombok.Setter; import org.bukkit.entity.ItemFrame; import org.bukkit.entity.Player; -import org.bukkit.event.Cancellable; -import org.bukkit.event.Event; -import org.bukkit.event.HandlerList; import org.bukkit.util.Vector; -import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import tech.sbdevelopment.mapreflectionapi.MapReflectionAPI; import tech.sbdevelopment.mapreflectionapi.api.MapWrapper; +import tech.sbdevelopment.mapreflectionapi.api.events.types.CancellableEvent; /** * This event gets fired when a player interact with a map */ @RequiredArgsConstructor @Getter -public class MapInteractEvent extends Event implements Cancellable { - private static final HandlerList handlerList = new HandlerList(); - @Setter - private boolean cancelled; - +public class MapInteractEvent extends CancellableEvent { private final Player player; private final int entityID; private final int action; @@ -69,11 +61,6 @@ public class MapInteractEvent extends Event implements Cancellable { this.hand = hand; } - @Override - public @NotNull HandlerList getHandlers() { - return handlerList; - } - /** * Get the {@link ItemFrame} the map is in * @@ -81,10 +68,8 @@ public class MapInteractEvent extends Event implements Cancellable { */ @Nullable public ItemFrame getFrame() { - if (getMapWrapper() == null) return null; - if (frame == null) { - frame = getMapWrapper().getController().getItemFrameById(player.getWorld(), entityID); + frame = MapReflectionAPI.getMapManager().getItemFrameById(player.getWorld(), entityID); } return frame; } @@ -96,10 +81,11 @@ public class MapInteractEvent extends Event implements Cancellable { */ @Nullable public MapWrapper getMapWrapper() { + if (getFrame() == null) return null; if (mapWrapper == null) { - mapWrapper = MapReflectionAPI.getMapManager().getWrapperForId(player, entityID); + if (!frame.hasMetadata(MapWrapper.REFERENCE_METADATA)) return null; + mapWrapper = (MapWrapper) frame.getMetadata(MapWrapper.REFERENCE_METADATA).get(0).value(); } - return mapWrapper; } } diff --git a/src/main/java/tech/sbdevelopment/mapreflectionapi/api/events/types/CancellableEvent.java b/src/main/java/tech/sbdevelopment/mapreflectionapi/api/events/types/CancellableEvent.java new file mode 100644 index 0000000..96987f7 --- /dev/null +++ b/src/main/java/tech/sbdevelopment/mapreflectionapi/api/events/types/CancellableEvent.java @@ -0,0 +1,54 @@ +/* + * This file is part of MapReflectionAPI. + * Copyright (c) 2023 inventivetalent / SBDevelopment - All Rights Reserved + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package tech.sbdevelopment.mapreflectionapi.api.events.types; + +import lombok.NoArgsConstructor; +import org.bukkit.event.Cancellable; + +@NoArgsConstructor +public class CancellableEvent extends Event implements Cancellable { + /** + * If this event gets cancelled. + */ + private boolean cancelled; + + public CancellableEvent(boolean isAsync) { + super(isAsync); + } + + /** + * Check if this event gets cancelled. + * + * @return true if cancelled, false if not + */ + @Override + public boolean isCancelled() { + return cancelled; + } + + /** + * Set if this event gets cancelled. + * + * @param cancelled true if you wish to cancel this event + */ + @Override + public void setCancelled(boolean cancelled) { + this.cancelled = cancelled; + } +} diff --git a/src/main/java/tech/sbdevelopment/mapreflectionapi/api/events/types/Event.java b/src/main/java/tech/sbdevelopment/mapreflectionapi/api/events/types/Event.java new file mode 100644 index 0000000..fac8c14 --- /dev/null +++ b/src/main/java/tech/sbdevelopment/mapreflectionapi/api/events/types/Event.java @@ -0,0 +1,46 @@ +/* + * This file is part of MapReflectionAPI. + * Copyright (c) 2023 inventivetalent / SBDevelopment - All Rights Reserved + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package tech.sbdevelopment.mapreflectionapi.api.events.types; + +import lombok.Getter; +import lombok.NoArgsConstructor; +import org.bukkit.event.HandlerList; + +@NoArgsConstructor +public class Event extends org.bukkit.event.Event { + public Event(boolean isAsync) { + super(isAsync); + } + + /** + * A list of EventHandlers listening to this event. + */ + @Getter + private static final HandlerList handlerList = new HandlerList(); + + /** + * Get the EventHandlers listening to this event. + * + * @return The EventHandlers listening to this event. + */ + @Override + public HandlerList getHandlers() { + return handlerList; + } +} diff --git a/src/main/java/tech/sbdevelopment/mapreflectionapi/listeners/PacketListener.java b/src/main/java/tech/sbdevelopment/mapreflectionapi/listeners/PacketListener.java index fd2f737..b5c3f17 100644 --- a/src/main/java/tech/sbdevelopment/mapreflectionapi/listeners/PacketListener.java +++ b/src/main/java/tech/sbdevelopment/mapreflectionapi/listeners/PacketListener.java @@ -72,6 +72,8 @@ public class PacketListener implements Listener { ChannelDuplexHandler channelDuplexHandler = new ChannelDuplexHandler() { @Override public void write(ChannelHandlerContext ctx, Object packet, ChannelPromise promise) throws Exception { + boolean cancel = false; + if (packet.getClass().isAssignableFrom(packetPlayOutMapClass)) { Object packetPlayOutMap = packetPlayOutMapClass.cast(packet); @@ -83,18 +85,19 @@ public class PacketListener implements Listener { boolean async = !MapReflectionAPI.getInstance().getServer().isPrimaryThread(); MapCancelEvent event = new MapCancelEvent(player, id, async); if (MapReflectionAPI.getMapManager().isIdUsedBy(player, id)) event.setCancelled(true); - if (event.getHandlers().getRegisteredListeners().length > 0) - Bukkit.getPluginManager().callEvent(event); + Bukkit.getPluginManager().callEvent(event); - if (event.isCancelled()) return; + if (event.isCancelled()) cancel = true; } } - super.write(ctx, packet, promise); + if (!cancel) super.write(ctx, packet, promise); } @Override public void channelRead(ChannelHandlerContext ctx, Object packet) throws Exception { + boolean cancel = false; + if (packet.getClass().isAssignableFrom(packetPlayInUseEntityClass)) { Object packetPlayInEntity = packetPlayInUseEntityClass.cast(packet); @@ -105,9 +108,20 @@ public class PacketListener implements Listener { Object pos; if (supports(17)) { Object action = getDeclaredField(packetPlayInEntity, "b"); - actionEnum = (Enum) callDeclaredMethod(action, "a"); //action type - hand = hasField(action, "a") ? (Enum) getDeclaredField(action, "a") : null; - pos = hasField(action, "b") ? getDeclaredField(action, "b") : null; + actionEnum = (Enum) callDeclaredMethod(action, "a"); + Class d = getNMSClass("network.protocol.game", "PacketPlayInUseEntity$d"); + Class e = getNMSClass("network.protocol.game", "PacketPlayInUseEntity$e"); + if (action.getClass().isAssignableFrom(e)) { + hand = (Enum) getDeclaredField(action, "a"); + pos = getDeclaredField(action, "b"); + } else { + pos = null; + if (action.getClass().isAssignableFrom(d)) { + hand = (Enum) getDeclaredField(action, "a"); + } else { + hand = null; + } + } } else { actionEnum = (Enum) callDeclaredMethod(packetPlayInEntity, supports(13) ? "b" : "a"); //1.13 = b, 1.12 = a hand = (Enum) callDeclaredMethod(packetPlayInEntity, supports(13) ? "c" : "b"); //1.13 = c, 1.12 = b @@ -122,7 +136,7 @@ public class PacketListener implements Listener { return event.isCancelled(); } return false; - }).get(1, TimeUnit.SECONDS)) return; + }).get(1, TimeUnit.SECONDS)) cancel = true; } else if (packet.getClass().isAssignableFrom(packetPlayInSetCreativeSlotClass)) { Object packetPlayInSetCreativeSlot = packetPlayInSetCreativeSlotClass.cast(packet); @@ -134,11 +148,11 @@ public class PacketListener implements Listener { CreativeInventoryMapUpdateEvent event = new CreativeInventoryMapUpdateEvent(player, slot, craftStack, async); if (event.getMapWrapper() != null) { Bukkit.getPluginManager().callEvent(event); - if (event.isCancelled()) return; + if (event.isCancelled()) cancel = true; } } - super.channelRead(ctx, packet); + if (!cancel) super.channelRead(ctx, packet); } }; diff --git a/src/main/java/tech/sbdevelopment/mapreflectionapi/utils/UpdateManager.java b/src/main/java/tech/sbdevelopment/mapreflectionapi/utils/UpdateManager.java index 7acc5f3..671a511 100644 --- a/src/main/java/tech/sbdevelopment/mapreflectionapi/utils/UpdateManager.java +++ b/src/main/java/tech/sbdevelopment/mapreflectionapi/utils/UpdateManager.java @@ -143,14 +143,12 @@ public class UpdateManager { File pluginFile = getPluginFile(); // /plugins/XXX.jar if (pluginFile == null) { this.downloadResponse.accept(DownloadResponse.ERROR, null); - Bukkit.getLogger().info("Pluginfile is null"); return; } File updateFolder = Bukkit.getUpdateFolderFile(); if (!updateFolder.exists()) { if (!updateFolder.mkdirs()) { this.downloadResponse.accept(DownloadResponse.ERROR, null); - Bukkit.getLogger().info("Updatefolder doesn't exists, and can't be made"); return; } } diff --git a/src/main/java/tech/sbdevelopment/mapreflectionapi/utils/XMaterial.java b/src/main/java/tech/sbdevelopment/mapreflectionapi/utils/XMaterial.java new file mode 100644 index 0000000..95ecaa0 --- /dev/null +++ b/src/main/java/tech/sbdevelopment/mapreflectionapi/utils/XMaterial.java @@ -0,0 +1,2406 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2018 Hex_27 + * Copyright (c) 2023 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. + */ +package tech.sbdevelopment.mapreflectionapi.utils; + +import com.google.common.base.Enums; +import com.google.common.cache.Cache; +import com.google.common.cache.CacheBuilder; +import org.bukkit.Bukkit; +import org.bukkit.Material; +import org.bukkit.inventory.ItemStack; +import org.bukkit.inventory.meta.ItemMeta; +import org.bukkit.inventory.meta.SpawnEggMeta; +import org.bukkit.potion.Potion; + +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; +import java.util.stream.Collectors; + +/** + * 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.wiki/w/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 an "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 11.5.0 + * @see Material + * @see ItemStack + */ +public enum XMaterial { + ACACIA_BOAT("BOAT_ACACIA"), + ACACIA_BUTTON("WOOD_BUTTON"), + ACACIA_CHEST_BOAT, + ACACIA_DOOR("ACACIA_DOOR", "ACACIA_DOOR_ITEM"), + ACACIA_FENCE, + ACACIA_FENCE_GATE, + ACACIA_HANGING_SIGN, + ACACIA_LEAVES(0, "LEAVES_2"), + ACACIA_LOG(0, "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_HANGING_SIGN, + ACACIA_WALL_SIGN("WALL_SIGN"), + ACACIA_WOOD(0, "LOG_2"), + ACTIVATOR_RAIL, + /** + * Air + * {@link Material#isAir()} + * + * @see #VOID_AIR + * @see #CAVE_AIR + */ + AIR, + ALLAY_SPAWN_EGG, + ALLIUM(2, "RED_ROSE"), + AMETHYST_BLOCK, + AMETHYST_CLUSTER, + AMETHYST_SHARD, + ANCIENT_DEBRIS, + ANDESITE(5, "STONE"), + ANDESITE_SLAB, + ANDESITE_STAIRS, + ANDESITE_WALL, + ANGLER_POTTERY_SHERD, + ANVIL, + APPLE, + ARCHER_POTTERY_SHERD, + ARMOR_STAND, + ARMS_UP_POTTERY_SHERD, + 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_BLOCK, + BAMBOO_BUTTON, + BAMBOO_CHEST_RAFT, + BAMBOO_DOOR, + BAMBOO_FENCE, + BAMBOO_FENCE_GATE, + BAMBOO_HANGING_SIGN, + BAMBOO_MOSAIC, + BAMBOO_MOSAIC_SLAB, + BAMBOO_MOSAIC_STAIRS, + BAMBOO_PLANKS, + BAMBOO_PRESSURE_PLATE, + BAMBOO_RAFT, + BAMBOO_SAPLING, + BAMBOO_SIGN, + BAMBOO_SLAB, + BAMBOO_STAIRS, + BAMBOO_TRAPDOOR, + BAMBOO_WALL_HANGING_SIGN, + BAMBOO_WALL_SIGN, + 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_CHEST_BOAT, + BIRCH_DOOR("BIRCH_DOOR", "BIRCH_DOOR_ITEM"), + BIRCH_FENCE, + BIRCH_FENCE_GATE, + BIRCH_HANGING_SIGN, + 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_HANGING_SIGN, + 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, + 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"), + BLADE_POTTERY_SHERD, + 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, + BREEZE_SPAWN_EGG, + BREWER_POTTERY_SHERD, + BREWING_STAND("BREWING_STAND", "BREWING_STAND_ITEM"), + BRICK("CLAY_BRICK"), + 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"), + BRUSH, + BUBBLE_COLUMN, + BUBBLE_CORAL, + BUBBLE_CORAL_BLOCK, + BUBBLE_CORAL_FAN, + BUBBLE_CORAL_WALL_FAN, + BUCKET, + BUDDING_AMETHYST, + BUNDLE, + BURN_POTTERY_SHERD, + CACTUS, + CAKE("CAKE_BLOCK"), + CALCITE, + CALIBRATED_SCULK_SENSOR, + CAMEL_SPAWN_EGG, + 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"), + CHERRY_BOAT, + CHERRY_BUTTON, + CHERRY_CHEST_BOAT, + CHERRY_DOOR, + CHERRY_FENCE, + CHERRY_FENCE_GATE, + CHERRY_HANGING_SIGN, + CHERRY_LEAVES, + CHERRY_LOG, + CHERRY_PLANKS, + CHERRY_PRESSURE_PLATE, + CHERRY_SAPLING, + CHERRY_SIGN, + CHERRY_SLAB, + CHERRY_STAIRS, + CHERRY_TRAPDOOR, + CHERRY_WALL_HANGING_SIGN, + CHERRY_WALL_SIGN, + CHERRY_WOOD, + CHEST("LOCKED_CHEST"), + CHEST_MINECART("STORAGE_MINECART"), + CHICKEN("RAW_CHICKEN"), + CHICKEN_SPAWN_EGG(93, "MONSTER_EGG"), + CHIPPED_ANVIL(1, "ANVIL"), + CHISELED_BOOKSHELF, + CHISELED_COPPER, + 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"), + CHISELED_TUFF, + CHISELED_TUFF_BRICKS, + CHORUS_FLOWER, + CHORUS_FRUIT, + CHORUS_PLANT, + CLAY, + CLAY_BALL, + CLOCK("WATCH"), + COAL, + COAL_BLOCK, + COAL_ORE, + COARSE_DIRT(1, "DIRT"), + COAST_ARMOR_TRIM_SMITHING_TEMPLATE, + 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("GRILLED_PORK"), + COOKED_RABBIT, + COOKED_SALMON(1, "COOKED_FISH"), + COOKIE, + COPPER_BLOCK, + COPPER_BULB, + COPPER_DOOR, + COPPER_GRATE, + COPPER_INGOT, + COPPER_ORE, + COPPER_TRAPDOOR, + 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"), + CRAFTER, + 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_HANGING_SIGN, + 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_HANGING_SIGN, + 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(1, "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"), + DANGER_POTTERY_SHERD, + DARK_OAK_BOAT("BOAT_DARK_OAK"), + DARK_OAK_BUTTON("WOOD_BUTTON"), + DARK_OAK_CHEST_BOAT, + DARK_OAK_DOOR("DARK_OAK_DOOR", "DARK_OAK_DOOR_ITEM"), + DARK_OAK_FENCE, + DARK_OAK_FENCE_GATE, + DARK_OAK_HANGING_SIGN, + 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_HANGING_SIGN, + DARK_OAK_WALL_SIGN("WALL_SIGN"), + DARK_OAK_WOOD(1, "LOG_2"), + DARK_PRISMARINE(2, "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, + DECORATED_POT, + 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"), + DISC_FRAGMENT_5, + DISPENSER, + DOLPHIN_SPAWN_EGG, + DONKEY_SPAWN_EGG(32, "MONSTER_EGG"), + DRAGON_BREATH("DRAGONS_BREATH"), + DRAGON_EGG, + DRAGON_HEAD(5, "SKULL", "SKULL_ITEM"), + DRAGON_WALL_HEAD(5, "SKULL", "SKULL_ITEM"), + DRIED_KELP, + DRIED_KELP_BLOCK, + DRIPSTONE_BLOCK, + DROPPER, + DROWNED_SPAWN_EGG, + DUNE_ARMOR_TRIM_SMITHING_TEMPLATE, + ECHO_SHARD, + 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_DRAGON_SPAWN_EGG, + 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, + END_STONE_BRICK_STAIRS, + END_STONE_BRICK_WALL, + EVOKER_SPAWN_EGG(34, "MONSTER_EGG"), + EXPERIENCE_BOTTLE("EXP_BOTTLE"), + EXPLORER_POTTERY_SHERD, + EXPOSED_CHISELED_COPPER, + EXPOSED_COPPER, + EXPOSED_COPPER_BULB, + EXPOSED_COPPER_DOOR, + EXPOSED_COPPER_GRATE, + EXPOSED_COPPER_TRAPDOOR, + EXPOSED_CUT_COPPER, + EXPOSED_CUT_COPPER_SLAB, + EXPOSED_CUT_COPPER_STAIRS, + EYE_ARMOR_TRIM_SMITHING_TEMPLATE, + 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. + * GitHub Issue + */ + 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, + FRIEND_POTTERY_SHERD, + FROGSPAWN, + FROG_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_HORN, + 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_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"), + /** + * 1.13 renamed to CACTUS_GREEN + * 1.14 renamed to GREEN_DYE + */ + 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, + HEARTBREAK_POTTERY_SHERD, + HEART_OF_THE_SEA, + HEART_POTTERY_SHERD, + 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"), + HOST_ARMOR_TRIM_SMITHING_TEMPLATE, + HOWL_POTTERY_SHERD, + 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_GOLEM_SPAWN_EGG, + 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_CHEST_BOAT, + JUNGLE_DOOR("JUNGLE_DOOR", "JUNGLE_DOOR_ITEM"), + JUNGLE_FENCE, + JUNGLE_FENCE_GATE, + JUNGLE_HANGING_SIGN, + 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_HANGING_SIGN, + 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("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"), + MANGROVE_BOAT, + MANGROVE_BUTTON, + MANGROVE_CHEST_BOAT, + MANGROVE_DOOR, + MANGROVE_FENCE, + MANGROVE_FENCE_GATE, + MANGROVE_HANGING_SIGN, + MANGROVE_LEAVES, + MANGROVE_LOG, + MANGROVE_PLANKS, + MANGROVE_PRESSURE_PLATE, + MANGROVE_PROPAGULE, + MANGROVE_ROOTS, + MANGROVE_SIGN, + MANGROVE_SLAB, + MANGROVE_STAIRS, + MANGROVE_TRAPDOOR, + MANGROVE_WALL_HANGING_SIGN, + MANGROVE_WALL_SIGN, + MANGROVE_WOOD, + /** + * 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, + MINER_POTTERY_SHERD, + MOJANG_BANNER_PATTERN, + MOOSHROOM_SPAWN_EGG(96, "MONSTER_EGG"), + MOSSY_COBBLESTONE, + MOSSY_COBBLESTONE_SLAB(), + MOSSY_COBBLESTONE_STAIRS, + MOSSY_COBBLESTONE_WALL(1, "COBBLE_WALL", "COBBLESTONE_WALL"), + MOSSY_STONE_BRICKS(1, "SMOOTH_BRICK"), + MOSSY_STONE_BRICK_SLAB, + MOSSY_STONE_BRICK_STAIRS, + MOSSY_STONE_BRICK_WALL, + MOSS_BLOCK, + MOSS_CARPET, + MOURNER_POTTERY_SHERD, + MOVING_PISTON("PISTON_MOVING_PIECE"), + MUD, + MUDDY_MANGROVE_ROOTS, + MUD_BRICKS, + MUD_BRICK_SLAB, + MUD_BRICK_STAIRS, + MUD_BRICK_WALL, + MULE_SPAWN_EGG(32, "MONSTER_EGG"), + MUSHROOM_STEM("BROWN_MUSHROOM"), + MUSHROOM_STEW("MUSHROOM_SOUP"), + MUSIC_DISC_11("RECORD_11"), + MUSIC_DISC_13("GOLD_RECORD"), + MUSIC_DISC_5, + MUSIC_DISC_BLOCKS("RECORD_3"), + MUSIC_DISC_CAT("GREEN_RECORD"), + MUSIC_DISC_CHIRP("RECORD_4"), + MUSIC_DISC_FAR("RECORD_5"), + MUSIC_DISC_MALL("RECORD_6"), + MUSIC_DISC_MELLOHI("RECORD_7"), + MUSIC_DISC_OTHERSIDE, + MUSIC_DISC_PIGSTEP, + MUSIC_DISC_RELIC, + MUSIC_DISC_STAL("RECORD_8"), + MUSIC_DISC_STRAD("RECORD_9"), + MUSIC_DISC_WAIT("RECORD_12"), + MUSIC_DISC_WARD("RECORD_10"), + 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, + NETHERITE_UPGRADE_SMITHING_TEMPLATE, + 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 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_CHEST_BOAT, + OAK_DOOR("WOODEN_DOOR", "WOOD_DOOR"), + OAK_FENCE("FENCE"), + OAK_FENCE_GATE("FENCE_GATE"), + OAK_HANGING_SIGN, + 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_HANGING_SIGN, + OAK_WALL_SIGN("WALL_SIGN"), + OAK_WOOD("LOG"), + OBSERVER, + OBSIDIAN, + OCELOT_SPAWN_EGG(98, "MONSTER_EGG"), + OCHRE_FROGLIGHT, + 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_CHISELED_COPPER, + OXIDIZED_COPPER, + OXIDIZED_COPPER_BULB, + OXIDIZED_COPPER_DOOR, + OXIDIZED_COPPER_GRATE, + OXIDIZED_COPPER_TRAPDOOR, + OXIDIZED_CUT_COPPER, + OXIDIZED_CUT_COPPER_SLAB, + OXIDIZED_CUT_COPPER_STAIRS, + PACKED_ICE, + PACKED_MUD, + PAINTING, + PANDA_SPAWN_EGG, + PAPER, + PARROT_SPAWN_EGG(105, "MONSTER_EGG"), + PEARLESCENT_FROGLIGHT, + PEONY(5, "DOUBLE_PLANT"), + PETRIFIED_OAK_SLAB("WOOD_STEP"), + PHANTOM_MEMBRANE, + PHANTOM_SPAWN_EGG, + PIGLIN_BANNER_PATTERN, + PIGLIN_BRUTE_SPAWN_EGG, + PIGLIN_HEAD, + PIGLIN_SPAWN_EGG(57, "MONSTER_EGG"), + PIGLIN_WALL_HEAD, + 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_PETALS, + 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"), + PITCHER_CROP, + PITCHER_PLANT, + PITCHER_POD, + PLAYER_HEAD(3, "SKULL", "SKULL_ITEM"), + PLAYER_WALL_HEAD(3, "SKULL", "SKULL_ITEM"), + PLENTY_POTTERY_SHERD, + 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, + POLISHED_TUFF, + POLISHED_TUFF_SLAB, + POLISHED_TUFF_STAIRS, + POLISHED_TUFF_WALL, + 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, "FLOWER_POT"), + POTTED_AZALEA_BUSH, + POTTED_AZURE_BLUET(3, "FLOWER_POT"), + POTTED_BAMBOO, + POTTED_BIRCH_SAPLING(2, "FLOWER_POT"), + POTTED_BLUE_ORCHID(1, "FLOWER_POT"), + POTTED_BROWN_MUSHROOM("FLOWER_POT"), + POTTED_CACTUS("FLOWER_POT"), + POTTED_CHERRY_SAPLING, + POTTED_CORNFLOWER, + POTTED_CRIMSON_FUNGUS, + POTTED_CRIMSON_ROOTS, + POTTED_DANDELION("FLOWER_POT"), + POTTED_DARK_OAK_SAPLING(5, "FLOWER_POT"), + POTTED_DEAD_BUSH("FLOWER_POT"), + POTTED_FERN(2, "FLOWER_POT"), + POTTED_FLOWERING_AZALEA_BUSH, + POTTED_JUNGLE_SAPLING(3, "FLOWER_POT"), + POTTED_LILY_OF_THE_VALLEY, + POTTED_MANGROVE_PROPAGULE, + POTTED_OAK_SAPLING("FLOWER_POT"), + POTTED_ORANGE_TULIP(5, "FLOWER_POT"), + POTTED_OXEYE_DAISY(8, "FLOWER_POT"), + POTTED_PINK_TULIP(7, "FLOWER_POT"), + POTTED_POPPY("FLOWER_POT"), + POTTED_RED_MUSHROOM("FLOWER_POT"), + POTTED_RED_TULIP(4, "FLOWER_POT"), + POTTED_SPRUCE_SAPLING(1, "FLOWER_POT"), + POTTED_TORCHFLOWER, + POTTED_WARPED_FUNGUS, + POTTED_WARPED_ROOTS, + POTTED_WHITE_TULIP(6, "FLOWER_POT"), + POTTED_WITHER_ROSE, + POTTERY_SHARD_ARCHER, + POTTERY_SHARD_ARMS_UP, + POTTERY_SHARD_PRIZE, + POTTERY_SHARD_SKULL, + POWDER_SNOW, + POWDER_SNOW_BUCKET, + POWDER_SNOW_CAULDRON, + POWERED_RAIL, + PRISMARINE, + PRISMARINE_BRICKS(1, "PRISMARINE"), + PRISMARINE_BRICK_SLAB, + PRISMARINE_BRICK_STAIRS, + PRISMARINE_CRYSTALS, + PRISMARINE_SHARD, + PRISMARINE_SLAB, + PRISMARINE_STAIRS, + PRISMARINE_WALL, + PRIZE_POTTERY_SHERD, + 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"), + RAISER_ARMOR_TRIM_SMITHING_TEMPLATE, + RAVAGER_SPAWN_EGG, + RAW_COPPER, + RAW_COPPER_BLOCK, + RAW_GOLD, + RAW_GOLD_BLOCK, + RAW_IRON, + RAW_IRON_BLOCK, + RECOVERY_COMPASS, + 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"), + /** + * 1.13 renamed to ROSE_RED + * 1.14 renamed to RED_DYE + */ + 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, + 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"), + REINFORCED_DEEPSLATE, + REPEATER("DIODE_BLOCK_ON", "DIODE_BLOCK_OFF", "DIODE"), + REPEATING_COMMAND_BLOCK("COMMAND", "COMMAND_REPEATING"), + RESPAWN_ANCHOR, + RIB_ARMOR_TRIM_SMITHING_TEMPLATE, + 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, + SCULK_CATALYST, + SCULK_SENSOR, + SCULK_SHRIEKER, + SCULK_VEIN, + SCUTE, + SEAGRASS, + SEA_LANTERN, + SEA_PICKLE, + SENTRY_ARMOR_TRIM_SMITHING_TEMPLATE, + SHAPER_ARMOR_TRIM_SMITHING_TEMPLATE, + SHEAF_POTTERY_SHERD, + SHEARS, + SHEEP_SPAWN_EGG(91, "MONSTER_EGG"), + SHELTER_POTTERY_SHERD, + SHIELD, + /** + * 1.13.0: LONG_GRASS:1 + * 1.20.4: GRASS -> SHORT_GRASS + */ + SHORT_GRASS(1, "GRASS", "LONG_GRASS"), + SHROOMLIGHT, + SHULKER_BOX("PURPLE_SHULKER_BOX"), + SHULKER_SHELL, + SHULKER_SPAWN_EGG(69, "MONSTER_EGG"), + SILENCE_ARMOR_TRIM_SMITHING_TEMPLATE, + 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, + SKULL_POTTERY_SHERD, + 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, + 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, + SMOOTH_SANDSTONE_STAIRS, + SMOOTH_STONE, + SMOOTH_STONE_SLAB, + SNIFFER_EGG, + SNIFFER_SPAWN_EGG, + SNORT_POTTERY_SHERD, + SNOUT_ARMOR_TRIM_SMITHING_TEMPLATE, + SNOW, + SNOWBALL("SNOW_BALL"), + SNOW_BLOCK, + SNOW_GOLEM_SPAWN_EGG, + 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"), + SPIRE_ARMOR_TRIM_SMITHING_TEMPLATE, + SPLASH_POTION("POTION"), + SPONGE, + SPORE_BLOSSOM, + SPRUCE_BOAT("BOAT_SPRUCE"), + SPRUCE_BUTTON("WOOD_BUTTON"), + SPRUCE_CHEST_BOAT, + SPRUCE_DOOR("SPRUCE_DOOR", "SPRUCE_DOOR_ITEM"), + SPRUCE_FENCE, + SPRUCE_FENCE_GATE, + SPRUCE_HANGING_SIGN, + 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_HANGING_SIGN, + 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(5, "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, + STRIPPED_ACACIA_WOOD, + STRIPPED_BAMBOO_BLOCK, + STRIPPED_BIRCH_LOG, + STRIPPED_BIRCH_WOOD, + STRIPPED_CHERRY_LOG, + STRIPPED_CHERRY_WOOD, + STRIPPED_CRIMSON_HYPHAE, + STRIPPED_CRIMSON_STEM, + STRIPPED_DARK_OAK_LOG, + STRIPPED_DARK_OAK_WOOD, + STRIPPED_JUNGLE_LOG, + STRIPPED_JUNGLE_WOOD, + STRIPPED_MANGROVE_LOG, + STRIPPED_MANGROVE_WOOD, + STRIPPED_OAK_LOG, + STRIPPED_OAK_WOOD, + STRIPPED_SPRUCE_LOG, + STRIPPED_SPRUCE_WOOD, + 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_GRAVEL, + SUSPICIOUS_SAND, + SUSPICIOUS_STEW, + SWEET_BERRIES, + SWEET_BERRY_BUSH, + TADPOLE_BUCKET, + TADPOLE_SPAWN_EGG, + TALL_GRASS(2, "DOUBLE_PLANT"), + TALL_SEAGRASS, + TARGET, + TERRACOTTA("HARD_CLAY"), + TIDE_ARMOR_TRIM_SMITHING_TEMPLATE, + TINTED_GLASS, + TIPPED_ARROW, + TNT, + TNT_MINECART("EXPLOSIVE_MINECART"), + TORCH, + TORCHFLOWER, + TORCHFLOWER_CROP, + TORCHFLOWER_SEEDS, + TOTEM_OF_UNDYING("TOTEM"), + TRADER_LLAMA_SPAWN_EGG, + TRAPPED_CHEST, + TRIAL_KEY, + TRIAL_SPAWNER, + 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, + TUFF_BRICKS, + TUFF_BRICK_SLAB, + TUFF_BRICK_STAIRS, + TUFF_BRICK_WALL, + TUFF_SLAB, + TUFF_STAIRS, + TUFF_WALL, + TURTLE_EGG, + TURTLE_HELMET, + TURTLE_SPAWN_EGG, + TWISTING_VINES, + TWISTING_VINES_PLANT, + VERDANT_FROGLIGHT, + VEX_ARMOR_TRIM_SMITHING_TEMPLATE, + 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, + WARDEN_SPAWN_EGG, + WARD_ARMOR_TRIM_SMITHING_TEMPLATE, + WARPED_BUTTON, + WARPED_DOOR, + WARPED_FENCE, + WARPED_FENCE_GATE, + WARPED_FUNGUS, + WARPED_FUNGUS_ON_A_STICK, + WARPED_HANGING_SIGN, + 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_HANGING_SIGN, + 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 + * Levelled water flowing system. + */ + WATER("STATIONARY_WATER"), + WATER_BUCKET, + WATER_CAULDRON, + WAXED_CHISELED_COPPER, + WAXED_COPPER_BLOCK, + WAXED_COPPER_BULB, + WAXED_COPPER_DOOR, + WAXED_COPPER_GRATE, + WAXED_COPPER_TRAPDOOR, + WAXED_CUT_COPPER, + WAXED_CUT_COPPER_SLAB, + WAXED_CUT_COPPER_STAIRS, + WAXED_EXPOSED_CHISELED_COPPER, + WAXED_EXPOSED_COPPER, + WAXED_EXPOSED_COPPER_BULB, + WAXED_EXPOSED_COPPER_DOOR, + WAXED_EXPOSED_COPPER_GRATE, + WAXED_EXPOSED_COPPER_TRAPDOOR, + WAXED_EXPOSED_CUT_COPPER, + WAXED_EXPOSED_CUT_COPPER_SLAB, + WAXED_EXPOSED_CUT_COPPER_STAIRS, + WAXED_OXIDIZED_CHISELED_COPPER, + WAXED_OXIDIZED_COPPER, + WAXED_OXIDIZED_COPPER_BULB, + WAXED_OXIDIZED_COPPER_DOOR, + WAXED_OXIDIZED_COPPER_GRATE, + WAXED_OXIDIZED_COPPER_TRAPDOOR, + WAXED_OXIDIZED_CUT_COPPER, + WAXED_OXIDIZED_CUT_COPPER_SLAB, + WAXED_OXIDIZED_CUT_COPPER_STAIRS, + WAXED_WEATHERED_CHISELED_COPPER, + WAXED_WEATHERED_COPPER, + WAXED_WEATHERED_COPPER_BULB, + WAXED_WEATHERED_COPPER_DOOR, + WAXED_WEATHERED_COPPER_GRATE, + WAXED_WEATHERED_COPPER_TRAPDOOR, + WAXED_WEATHERED_CUT_COPPER, + WAXED_WEATHERED_CUT_COPPER_SLAB, + WAXED_WEATHERED_CUT_COPPER_STAIRS, + WAYFINDER_ARMOR_TRIM_SMITHING_TEMPLATE, + WEATHERED_CHISELED_COPPER, + WEATHERED_COPPER, + WEATHERED_COPPER_BULB, + WEATHERED_COPPER_DOOR, + WEATHERED_COPPER_GRATE, + WEATHERED_COPPER_TRAPDOOR, + 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, + WHITE_SHULKER_BOX, + WHITE_STAINED_GLASS("STAINED_GLASS"), + WHITE_STAINED_GLASS_PANE("THIN_GLASS", "STAINED_GLASS_PANE"), + WHITE_TERRACOTTA("STAINED_CLAY"), + WHITE_TULIP(6, "RED_ROSE"), + WHITE_WALL_BANNER(15, "WALL_BANNER"), + WHITE_WOOL("WOOL"), + WILD_ARMOR_TRIM_SMITHING_TEMPLATE, + 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"), + WITHER_SPAWN_EGG, + 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"), + /** + * 1.13 renamed to DANDELION_YELLOW + * 1.14 renamed to YELLOW_DYE + */ + YELLOW_DYE(11, "INK_SACK", "DANDELION_YELLOW"), + YELLOW_GLAZED_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 Cache CACHED_REGEX = CacheBuilder.newBuilder() + .expireAfterAccess(3, TimeUnit.HOURS) + .build(); + /** + * The maximum data value in the pre-flattening update which belongs to {@link #VILLAGER_SPAWN_EGG}
+ * Spawn Eggs + * + * @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"); + DUPLICATED.add(MELON.name()); + DUPLICATED.add(BRICK.name()); + DUPLICATED.add(NETHER_BRICK.name()); + } + } + + /** + * The data value of this material 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); + } + + /** + * 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 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(name.substring(index + 1).replace(" ", "")); + 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 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) { + if (name == null || name.isEmpty()) + throw new IllegalArgumentException("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 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(); + + // 1.13+ doesn't use data values at all. + // Maps are given different data values for different parts of the map also some plugins use negative values for custom images. + // Items that have durability, such as armor and tools don't use the data value to distinguish their material. + byte data = (byte) (Data.ISFLAT || material.equals("MAP") || item.getType().getMaxDurability() > 0 ? 0 : item.getDurability()); + + // Versions 1.9-1.12 didn't really use the items data value. + if (supports(9) && !supports(13) && item.hasItemMeta() && material.equals("MONSTER_EGG")) { + ItemMeta meta = item.getItemMeta(); + if (meta instanceof SpawnEggMeta) { + SpawnEggMeta egg = (SpawnEggMeta) meta; + material = egg.getSpawnedType().name() + "_SPAWN_EGG"; + } + } + + // Potions used the items data value to store + // information about the type of potion in 1.8 + if (!supports(9) && material.endsWith("ION")) { + // There's also 16000+ data value technique, but this is more reliable. + return Potion.fromItemStack(item).isSplash() ? SPLASH_POTION : POTION; + } + + // Refer to the enum for info. + // Currently, these are the only materials with a non-zero data value + // that has been renamed after the flattening update. + // If this happens to more materials in the future, I might have to change the system. + if (supports(13) && !supports(14)) { + // https://hub.spigotmc.org/stash/projects/SPIGOT/repos/bukkit/diff/src/main/java/org/bukkit/Material.java?until=67d908a9830c71267ee740f5bddd728ce9c64cc7 + if (material.equals("CACTUS_GREEN")) return GREEN_DYE; + if (material.equals("ROSE_RED")) return RED_DYE; + if (material.equals("DANDELION_YELLOW")) return YELLOW_DYE; + } + + // Check FILLED_MAP enum for more info. + // if (!Data.ISFLAT && item.hasItemMeta() && item.getItemMeta() instanceof org.bukkit.inventory.meta.MapMeta) return FILLED_MAP; + + // No orElseThrow, I don't want to deal with Java's final variable bullshit. + Optional result = matchDefinedXMaterial(material, data); + if (result.isPresent()) return result.get(); + throw new IllegalArgumentException("Unsupported material from item: " + material + " (" + data + ')'); + } + + /** + * 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(); + } + + /** + * 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 + */ + @SuppressWarnings({"DanglingJavadoc", "JavadocBlankLines"}) + @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(); + } + + /** + * 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} + * Note: BRICKS was added because + * {@code XMaterial.matchXMaterial("BRICK")} would match {@link #BRICKS} instead in 1.8. + */ + boolean isPlural = oldXMaterial == CARROTS || oldXMaterial == POTATOES || oldXMaterial == BRICKS; + + if (!Data.ISFLAT && isPlural && (duplicated == null ? isDuplicated(name) : duplicated)) + return getIfPresent(name); + return Optional.of(oldXMaterial); + } + + /** + * 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; + } + + /** + * 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 + * @deprecated Use XTag.stringMatcher() instead. + */ + @Deprecated + 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.getIfPresent(comp); + if (pattern == null) { + try { + pattern = Pattern.compile(comp); + CACHED_REGEX.put(comp, pattern); + } catch (PatternSyntaxException ex) { + ex.printStackTrace(); + } + } + 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); + // Splash Potions weren't an official material pre-flattening. + if (!Data.ISFLAT && this == SPLASH_POTION) { + item.setDurability((short) 16384); // Hard-coded as 'data' is only a byte. + } + 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 Arrays.stream(name().split("_")) + .map(t -> t.charAt(0) + t.substring(1).toLowerCase()) + .collect(Collectors.joining(" ")); + } + + /** + * Gets the ID (Magic value) of the material. + * ID List + *

+ * 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; + ItemStack base = Data.ISFLAT ? new ItemStack(material) : new ItemStack(material, 1, this.data); + // Splash Potions weren't an official material pre-flattening. + if (!Data.ISFLAT && this == SPLASH_POTION) { + base.setDurability((short) 16384); // Hard-coded as 'data' is only a byte. + } + return base; + } + + /** + * 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; + // Special case for splash potions. + if (this == SPLASH_POTION) { + return Data.ISFLAT || item.getDurability() == (short) 16384; + } + 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; + } + + /** + * Checks if this material is supported in the current version and + * returns itself if yes. + *

+ * In the other case, the alternate material will get returned, + * no matter if it is supported or not. + * + * @param alternateMaterial the material to get if this one is not supported. + * @return this material or the {@code alternateMaterial} if not supported. + */ + @Nullable + public XMaterial or(@Nullable XMaterial alternateMaterial) { + return isSupported() ? this : alternateMaterial; + } + + /** + * 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); + } + + /** + * 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; + + static { // This needs to be right below VERSION because of initialization order. + String version = Bukkit.getVersion(); + Matcher matcher = Pattern.compile("MC: \\d\\.(\\d+)").matcher(version); + + if (matcher.find()) VERSION = Integer.parseInt(matcher.group(1)); + else throw new IllegalArgumentException("Failed to parse server version from: " + version); + } + + /** + * Cached result if the server version is after the v1.13 flattening update. + * + * @since 3.0.0 + */ + private static final boolean ISFLAT = supports(13); + } +} \ No newline at end of file