From b5df2bb80ce2c1ca77283a7ce395f12aebc0a2b4 Mon Sep 17 00:00:00 2001 From: Stijn Bannink Date: Sun, 24 Dec 2023 15:38:40 +0100 Subject: [PATCH 1/8] 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 -- 2.45.3 From 89d4234a8cac80d8d91f2404f78b599df8c94be2 Mon Sep 17 00:00:00 2001 From: Stijn Bannink Date: Sun, 24 Dec 2023 15:39:25 +0100 Subject: [PATCH 2/8] Cleanup --- .../sbdevelopment/mapreflectionapi/MapReflectionAPI.java | 1 - .../sbdevelopment/mapreflectionapi/api/MapController.java | 1 - .../tech/sbdevelopment/mapreflectionapi/api/MapWrapper.java | 4 +++- .../mapreflectionapi/api/MultiMapController.java | 4 ++-- .../api/events/CreativeInventoryMapUpdateEvent.java | 4 ---- .../mapreflectionapi/utils/ReflectionUtil.java | 6 ++++-- 6 files changed, 9 insertions(+), 11 deletions(-) diff --git a/src/main/java/tech/sbdevelopment/mapreflectionapi/MapReflectionAPI.java b/src/main/java/tech/sbdevelopment/mapreflectionapi/MapReflectionAPI.java index 7461be4..2049d7b 100644 --- a/src/main/java/tech/sbdevelopment/mapreflectionapi/MapReflectionAPI.java +++ b/src/main/java/tech/sbdevelopment/mapreflectionapi/MapReflectionAPI.java @@ -30,7 +30,6 @@ import tech.sbdevelopment.mapreflectionapi.listeners.MapListener; import tech.sbdevelopment.mapreflectionapi.listeners.PacketListener; import tech.sbdevelopment.mapreflectionapi.managers.Configuration; import tech.sbdevelopment.mapreflectionapi.utils.MainUtil; -import tech.sbdevelopment.mapreflectionapi.utils.ReflectionUtil; import tech.sbdevelopment.mapreflectionapi.utils.UpdateManager; import java.util.logging.Level; diff --git a/src/main/java/tech/sbdevelopment/mapreflectionapi/api/MapController.java b/src/main/java/tech/sbdevelopment/mapreflectionapi/api/MapController.java index 8618aa4..978e8ac 100644 --- a/src/main/java/tech/sbdevelopment/mapreflectionapi/api/MapController.java +++ b/src/main/java/tech/sbdevelopment/mapreflectionapi/api/MapController.java @@ -19,7 +19,6 @@ package tech.sbdevelopment.mapreflectionapi.api; import org.bukkit.OfflinePlayer; -import org.bukkit.World; import org.bukkit.entity.ItemFrame; import org.bukkit.entity.Player; diff --git a/src/main/java/tech/sbdevelopment/mapreflectionapi/api/MapWrapper.java b/src/main/java/tech/sbdevelopment/mapreflectionapi/api/MapWrapper.java index a7eae14..d943fa5 100644 --- a/src/main/java/tech/sbdevelopment/mapreflectionapi/api/MapWrapper.java +++ b/src/main/java/tech/sbdevelopment/mapreflectionapi/api/MapWrapper.java @@ -19,7 +19,9 @@ package tech.sbdevelopment.mapreflectionapi.api; import lombok.Getter; -import org.bukkit.*; +import org.bukkit.Bukkit; +import org.bukkit.GameMode; +import org.bukkit.OfflinePlayer; import org.bukkit.entity.ItemFrame; import org.bukkit.entity.Player; import org.bukkit.inventory.ItemStack; diff --git a/src/main/java/tech/sbdevelopment/mapreflectionapi/api/MultiMapController.java b/src/main/java/tech/sbdevelopment/mapreflectionapi/api/MultiMapController.java index 537fd54..a027e09 100644 --- a/src/main/java/tech/sbdevelopment/mapreflectionapi/api/MultiMapController.java +++ b/src/main/java/tech/sbdevelopment/mapreflectionapi/api/MultiMapController.java @@ -144,8 +144,8 @@ public interface MultiMapController extends IMapController { * Called to get debug information for a frame * * @param controller the {@link MapController} - * @param row Row of the current frame - * @param column Column of the current frame + * @param row Row of the current frame + * @param column Column of the current frame * @return {@link String} to show when a player looks at the map, or null * @see MapController#showInFrame(Player, int, String) */ 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 561ae96..e2f4334 100644 --- a/src/main/java/tech/sbdevelopment/mapreflectionapi/api/events/CreativeInventoryMapUpdateEvent.java +++ b/src/main/java/tech/sbdevelopment/mapreflectionapi/api/events/CreativeInventoryMapUpdateEvent.java @@ -20,16 +20,12 @@ package tech.sbdevelopment.mapreflectionapi.api.events; import lombok.Getter; import lombok.RequiredArgsConstructor; -import org.bukkit.Bukkit; -import org.bukkit.Material; import org.bukkit.entity.Player; import org.bukkit.inventory.ItemStack; -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; /** diff --git a/src/main/java/tech/sbdevelopment/mapreflectionapi/utils/ReflectionUtil.java b/src/main/java/tech/sbdevelopment/mapreflectionapi/utils/ReflectionUtil.java index a1cbe13..ad81288 100644 --- a/src/main/java/tech/sbdevelopment/mapreflectionapi/utils/ReflectionUtil.java +++ b/src/main/java/tech/sbdevelopment/mapreflectionapi/utils/ReflectionUtil.java @@ -37,14 +37,16 @@ public class ReflectionUtil { * * @param The storage type */ - public static class ListParam extends ArrayList {} + public static class ListParam extends ArrayList { + } /** * Helper class converted to {@link Collection} * * @param The storage type */ - public static class CollectionParam extends ArrayList {} + public static class CollectionParam extends ArrayList { + } private static Class wrapperToPrimitive(Class clazz) { if (clazz == Boolean.class) return boolean.class; -- 2.45.3 From ef49048ee169324c72bcf3b098b628d4c071a787 Mon Sep 17 00:00:00 2001 From: Stijn Bannink Date: Mon, 29 Apr 2024 16:50:10 +0200 Subject: [PATCH 3/8] Started with 1.20.5 support --- pom.xml | 2 +- .../mapreflectionapi/MapReflectionAPI.java | 2 +- .../mapreflectionapi/api/MapSender.java | 20 ++++++++++++++++++- .../listeners/PacketListener.java | 4 ++-- 4 files changed, 23 insertions(+), 5 deletions(-) diff --git a/pom.xml b/pom.xml index 8d7f65d..7ee27dc 100644 --- a/pom.xml +++ b/pom.xml @@ -161,7 +161,7 @@ org.spigotmc spigot-api - 1.20.4-R0.1-SNAPSHOT + 1.20.5-R0.1-SNAPSHOT provided diff --git a/src/main/java/tech/sbdevelopment/mapreflectionapi/MapReflectionAPI.java b/src/main/java/tech/sbdevelopment/mapreflectionapi/MapReflectionAPI.java index 2049d7b..bc41d31 100644 --- a/src/main/java/tech/sbdevelopment/mapreflectionapi/MapReflectionAPI.java +++ b/src/main/java/tech/sbdevelopment/mapreflectionapi/MapReflectionAPI.java @@ -69,7 +69,7 @@ public class MapReflectionAPI extends JavaPlugin { getLogger().info("Made by © Copyright SBDevelopment 2023"); if (!supports(12)) { - getLogger().severe("MapReflectionAPI only supports Minecraft 1.12 - 1.19.4!"); + getLogger().severe("MapReflectionAPI only supports Minecraft 1.12 - 1.20.5!"); Bukkit.getPluginManager().disablePlugin(this); return; } diff --git a/src/main/java/tech/sbdevelopment/mapreflectionapi/api/MapSender.java b/src/main/java/tech/sbdevelopment/mapreflectionapi/api/MapSender.java index 2cb8844..cc35b8a 100644 --- a/src/main/java/tech/sbdevelopment/mapreflectionapi/api/MapSender.java +++ b/src/main/java/tech/sbdevelopment/mapreflectionapi/api/MapSender.java @@ -112,7 +112,25 @@ public class MapSender { final int id = -id0; Object packet; - if (supports(17)) { //1.17+ + if (supports(20, 4)) { //1.20.5+ + //TODO: Implement 1.20.5+ map sending + + Object updateData = ReflectionUtil.callConstructor(worldMapData, + content.minX, //X pos + content.minY, //Y pos + content.maxX, //X size (2nd X pos) + content.maxY, //Y size (2nd Y pos) + content.array //Data + ); + + packet = ReflectionUtil.callConstructor(packetPlayOutMapClass, + id, //ID + (byte) 0, //Scale, 0 = 1 block per pixel + false, //Show icons + new ReflectionUtil.CollectionParam<>(), //Icons + updateData + ); + } else if (supports(17)) { //1.17+ Object updateData = ReflectionUtil.callConstructor(worldMapData, content.minX, //X pos content.minY, //Y pos diff --git a/src/main/java/tech/sbdevelopment/mapreflectionapi/listeners/PacketListener.java b/src/main/java/tech/sbdevelopment/mapreflectionapi/listeners/PacketListener.java index b5c3f17..2804d44 100644 --- a/src/main/java/tech/sbdevelopment/mapreflectionapi/listeners/PacketListener.java +++ b/src/main/java/tech/sbdevelopment/mapreflectionapi/listeners/PacketListener.java @@ -140,8 +140,8 @@ public class PacketListener implements Listener { } else if (packet.getClass().isAssignableFrom(packetPlayInSetCreativeSlotClass)) { Object packetPlayInSetCreativeSlot = packetPlayInSetCreativeSlotClass.cast(packet); - int slot = (int) ReflectionUtil.callDeclaredMethod(packetPlayInSetCreativeSlot, supports(19, 4) ? "a" : supports(13) ? "b" : "a"); //1.19.4 = a, 1.19.3 - 1.13 = b, 1.12 = a - Object nmsStack = ReflectionUtil.callDeclaredMethod(packetPlayInSetCreativeSlot, supports(20, 2) ? "d" : supports(18) ? "c" : "getItemStack"); //1.20.2 = d, >= 1.18 = c, 1.17 = getItemStack + int slot = (int) ReflectionUtil.callDeclaredMethod(packetPlayInSetCreativeSlot, supports(20,4) ? "b" : supports(19, 4) ? "a" : supports(13) ? "b" : "a"); //1.20.4 - 1.19.4 = a, 1.19.3 - 1.13 and 1.20.5 = b, 1.12 = a + Object nmsStack = ReflectionUtil.callDeclaredMethod(packetPlayInSetCreativeSlot, supports(20,4) ? "e" : supports(20, 2) ? "d" : supports(18) ? "c" : "getItemStack"); //1.20.5 = e, 1.20.2-1.20.4 = d, >= 1.18 = c, 1.17 = getItemStack ItemStack craftStack = (ItemStack) ReflectionUtil.callMethod(craftStackClass, "asBukkitCopy", nmsStack); boolean async = !MapReflectionAPI.getInstance().getServer().isPrimaryThread(); -- 2.45.3 From f25c727a150180229c90f0f9bcd1642c1a459c79 Mon Sep 17 00:00:00 2001 From: Stijn Bannink Date: Mon, 29 Apr 2024 18:01:28 +0200 Subject: [PATCH 4/8] Added 1.20.5 support finished --- .../mapreflectionapi/api/MapSender.java | 6 +++--- .../mapreflectionapi/api/MapWrapper.java | 13 +++++++++---- 2 files changed, 12 insertions(+), 7 deletions(-) diff --git a/src/main/java/tech/sbdevelopment/mapreflectionapi/api/MapSender.java b/src/main/java/tech/sbdevelopment/mapreflectionapi/api/MapSender.java index cc35b8a..3d5ae64 100644 --- a/src/main/java/tech/sbdevelopment/mapreflectionapi/api/MapSender.java +++ b/src/main/java/tech/sbdevelopment/mapreflectionapi/api/MapSender.java @@ -113,8 +113,6 @@ public class MapSender { final int id = -id0; Object packet; if (supports(20, 4)) { //1.20.5+ - //TODO: Implement 1.20.5+ map sending - Object updateData = ReflectionUtil.callConstructor(worldMapData, content.minX, //X pos content.minY, //Y pos @@ -123,8 +121,10 @@ public class MapSender { content.array //Data ); + Object mapId = ReflectionUtil.callConstructor(getNMSClass("world.level.saveddata.maps", "MapId"), id); + packet = ReflectionUtil.callConstructor(packetPlayOutMapClass, - id, //ID + mapId, //ID (byte) 0, //Scale, 0 = 1 block per pixel false, //Show icons new ReflectionUtil.CollectionParam<>(), //Icons diff --git a/src/main/java/tech/sbdevelopment/mapreflectionapi/api/MapWrapper.java b/src/main/java/tech/sbdevelopment/mapreflectionapi/api/MapWrapper.java index d943fa5..9c427e2 100644 --- a/src/main/java/tech/sbdevelopment/mapreflectionapi/api/MapWrapper.java +++ b/src/main/java/tech/sbdevelopment/mapreflectionapi/api/MapWrapper.java @@ -165,8 +165,8 @@ public class MapWrapper extends AbstractMapWrapper { String inventoryMenuName; if (supports(20)) { - //>= 1.20.2 = bR, 1.20(.1) = bQ - inventoryMenuName = supports(20, 2) ? "bR" : "bQ"; + //1.20.5 = cb, 1.20.2 - 1.20.4 = bR, 1.20(.1) = bQ + inventoryMenuName = supports(20, 4) ? "cb" : supports(20, 2) ? "bR" : "bQ"; } else if (supports(19)) { //1.19.4 = bO, >= 1.19.3 = bT inventoryMenuName = supports(19, 3) ? "bO" : "bT"; @@ -286,7 +286,12 @@ public class MapWrapper extends AbstractMapWrapper { Object nmsStack = ReflectionUtil.callMethod(craftStackClass, "asNMSCopy", stack); - if (supports(13)) { + //1.20.5 uses new NBT compound system + if (supports(20, 4)) { + Object mapIdComponent = ReflectionUtil.getDeclaredField(getNMSClass("core.component", "DataComponents"), "B"); + Object mapId1 = ReflectionUtil.callConstructor(getNMSClass("world.level.saveddata.maps", "MapId"), mapId); + ReflectionUtil.callMethod(nmsStack, "b", mapIdComponent, mapId1); + } else if (supports(13)) { String nbtObjectName; if (supports(20)) { //1.20 nbtObjectName = "w"; @@ -328,7 +333,7 @@ public class MapWrapper extends AbstractMapWrapper { Object packet; if (supports(19, 3)) { //1.19.3 - Class dataWatcherRecordClass = getNMSClass("network.syncher", "DataWatcher$b"); + Class dataWatcherRecordClass = getNMSClass("network.syncher", supports(20, 4) ? "DataWatcher$c" : "DataWatcher$b"); //1.20.5 = c, lower is b // Sadly not possible to use ReflectionUtil (in its current state), because of the Object parameter Object dataWatcherItem; try { -- 2.45.3 From cba7cbf6e50163bf5b7780db1a754fe48b944fd4 Mon Sep 17 00:00:00 2001 From: Stijn Bannink Date: Mon, 29 Apr 2024 18:11:13 +0200 Subject: [PATCH 5/8] Fixed compilation --- .github/workflows/maven.yml | 4 ++-- .idea/misc.xml | 3 +-- pom.xml | 19 +++++++++++++------ .../mapreflectionapi/utils/XMaterial.java | 8 -------- 4 files changed, 16 insertions(+), 18 deletions(-) diff --git a/.github/workflows/maven.yml b/.github/workflows/maven.yml index c9caadf..d550d86 100644 --- a/.github/workflows/maven.yml +++ b/.github/workflows/maven.yml @@ -10,10 +10,10 @@ jobs: steps: - uses: actions/checkout@v1 - - name: Set up JDK 11 + - name: Set up JDK 21 uses: actions/setup-java@v1 with: - java-version: 11 + java-version: 21 - name: Build with Maven run: mvn -B package --file pom.xml diff --git a/.idea/misc.xml b/.idea/misc.xml index f0021fd..5cacde7 100644 --- a/.idea/misc.xml +++ b/.idea/misc.xml @@ -29,9 +29,8 @@ - - + org.jetbrains annotations-java5 - 23.0.0 + 24.1.0 provided io.netty netty-transport - 4.1.77.Final + 4.1.97.Final provided diff --git a/src/main/java/tech/sbdevelopment/mapreflectionapi/api/MapSender.java b/src/main/java/tech/sbdevelopment/mapreflectionapi/api/MapSender.java index 2cb8844..f7598ae 100644 --- a/src/main/java/tech/sbdevelopment/mapreflectionapi/api/MapSender.java +++ b/src/main/java/tech/sbdevelopment/mapreflectionapi/api/MapSender.java @@ -86,6 +86,7 @@ public class MapSender { private static final Class packetPlayOutMapClass = getNMSClass("network.protocol.game", "PacketPlayOutMap"); private static final Class worldMapData = supports(17) ? getNMSClass("world.level.saveddata.maps", "WorldMap$b") : null; + private static final Class mapId = supports(21) ? getNMSClass("world.level.saveddata.maps", "MapId") : null; /** * Send a map to a player @@ -110,7 +111,6 @@ public class MapSender { return; } - final int id = -id0; Object packet; if (supports(17)) { //1.17+ Object updateData = ReflectionUtil.callConstructor(worldMapData, @@ -121,16 +121,26 @@ public class MapSender { content.array //Data ); - packet = ReflectionUtil.callConstructor(packetPlayOutMapClass, - id, //ID - (byte) 0, //Scale, 0 = 1 block per pixel - false, //Show icons - new ReflectionUtil.CollectionParam<>(), //Icons - updateData - ); + if (supports(21)) { //1.21+ + packet = ReflectionUtil.callConstructor(packetPlayOutMapClass, + ReflectionUtil.callConstructor(mapId, -id0), //ID + (byte) 0, //Scale, 0 = 1 block per pixel + false, //Show icons + new ReflectionUtil.CollectionParam<>(), //Icons + updateData + ); + } else { + packet = ReflectionUtil.callConstructor(packetPlayOutMapClass, + -id0, //ID + (byte) 0, //Scale, 0 = 1 block per pixel + false, //Show icons + new ReflectionUtil.CollectionParam<>(), //Icons + updateData + ); + } } else if (supports(14)) { //1.16-1.14 packet = ReflectionUtil.callConstructor(packetPlayOutMapClass, - id, //ID + -id0, //ID (byte) 0, //Scale, 0 = 1 block per pixel false, //Tracking position false, //Locked @@ -143,7 +153,7 @@ public class MapSender { ); } else { //1.13- packet = ReflectionUtil.callConstructor(packetPlayOutMapClass, - id, //ID + -id0, //ID (byte) 0, //Scale, 0 = 1 block per pixel false, //??? new ReflectionUtil.CollectionParam<>(), //Icons diff --git a/src/main/java/tech/sbdevelopment/mapreflectionapi/api/MapWrapper.java b/src/main/java/tech/sbdevelopment/mapreflectionapi/api/MapWrapper.java index d943fa5..75e94d2 100644 --- a/src/main/java/tech/sbdevelopment/mapreflectionapi/api/MapWrapper.java +++ b/src/main/java/tech/sbdevelopment/mapreflectionapi/api/MapWrapper.java @@ -67,6 +67,8 @@ public class MapWrapper extends AbstractMapWrapper { private static final Class entityMetadataPacketClass = getNMSClass("network.protocol.game", "PacketPlayOutEntityMetadata"); private static final Class entityItemFrameClass = getNMSClass("world.entity.decoration", "EntityItemFrame"); private static final Class dataWatcherItemClass = getNMSClass("network.syncher", "DataWatcher$Item"); + private static final Class minecraftKeyClass = getNMSClass("resources", "MinecraftKey"); + private static final Class builtInRegistriesClass = getNMSClass("core.registries", "BuiltInRegistries"); protected MapController controller = new MapController() { private final Map viewers = new HashMap<>(); @@ -164,7 +166,10 @@ public class MapWrapper extends AbstractMapWrapper { } String inventoryMenuName; - if (supports(20)) { + if (supports(21)) { + //1.21 = cc + inventoryMenuName = "cc"; + } else if (supports(20)) { //>= 1.20.2 = bR, 1.20(.1) = bQ inventoryMenuName = supports(20, 2) ? "bR" : "bQ"; } else if (supports(19)) { @@ -286,7 +291,14 @@ public class MapWrapper extends AbstractMapWrapper { Object nmsStack = ReflectionUtil.callMethod(craftStackClass, "asNMSCopy", stack); - if (supports(13)) { + if (supports(21)) { //1.21 method + Object minecraftKey = ReflectionUtil.callDeclaredMethod(minecraftKeyClass, "a", "minecraft:map_id"); + Object dataComponentTypeRegistery = ReflectionUtil.getDeclaredField(builtInRegistriesClass, "aq"); + Object dataComponentType = ReflectionUtil.callMethod(dataComponentTypeRegistery, "a", minecraftKey); + + Object dataComponentMap = ReflectionUtil.callMethod(nmsStack, "a"); + ReflectionUtil.callMethod(dataComponentMap, "a", dataComponentType, mapId); + } else if (supports(13)) { //1.13 - 1.20 method String nbtObjectName; if (supports(20)) { //1.20 nbtObjectName = "w"; @@ -307,7 +319,9 @@ public class MapWrapper extends AbstractMapWrapper { Object nmsStack = createCraftItemStack(stack, mapId); String dataWatcherObjectName; - if (supports(19, 3)) { //1.19.3 and 1.20(.1) + if (supports(21)) { //1.21 + dataWatcherObjectName = "f"; + } else if (supports(19, 3)) { //1.19.3 and 1.20(.1) dataWatcherObjectName = "g"; } else if (supports(19)) { //1.19-1.19.2 dataWatcherObjectName = "ao"; @@ -328,7 +342,7 @@ public class MapWrapper extends AbstractMapWrapper { Object packet; if (supports(19, 3)) { //1.19.3 - Class dataWatcherRecordClass = getNMSClass("network.syncher", "DataWatcher$b"); + Class dataWatcherRecordClass = getNMSClass("network.syncher", "DataWatcher$" + (supports(21) ? "c" : "b")); // Sadly not possible to use ReflectionUtil (in its current state), because of the Object parameter Object dataWatcherItem; try { diff --git a/src/main/java/tech/sbdevelopment/mapreflectionapi/listeners/PacketListener.java b/src/main/java/tech/sbdevelopment/mapreflectionapi/listeners/PacketListener.java index b5c3f17..89ef9c7 100644 --- a/src/main/java/tech/sbdevelopment/mapreflectionapi/listeners/PacketListener.java +++ b/src/main/java/tech/sbdevelopment/mapreflectionapi/listeners/PacketListener.java @@ -140,8 +140,8 @@ public class PacketListener implements Listener { } else if (packet.getClass().isAssignableFrom(packetPlayInSetCreativeSlotClass)) { Object packetPlayInSetCreativeSlot = packetPlayInSetCreativeSlotClass.cast(packet); - int slot = (int) ReflectionUtil.callDeclaredMethod(packetPlayInSetCreativeSlot, supports(19, 4) ? "a" : supports(13) ? "b" : "a"); //1.19.4 = a, 1.19.3 - 1.13 = b, 1.12 = a - Object nmsStack = ReflectionUtil.callDeclaredMethod(packetPlayInSetCreativeSlot, supports(20, 2) ? "d" : supports(18) ? "c" : "getItemStack"); //1.20.2 = d, >= 1.18 = c, 1.17 = getItemStack + int slot = (int) ReflectionUtil.callDeclaredMethod(packetPlayInSetCreativeSlot, supports(21) ? "b" : supports(19, 4) ? "a" : supports(13) ? "b" : "a"); //1.21 = b, 1.19.4 = a, 1.19.3 - 1.13 = b, 1.12 = a + Object nmsStack = ReflectionUtil.callDeclaredMethod(packetPlayInSetCreativeSlot, supports(21) ? "e" : supports(20, 2) ? "d" : supports(18) ? "c" : "getItemStack"); //1.21 = e, 1.20.2 = d, >= 1.18 = c, 1.17 = getItemStack ItemStack craftStack = (ItemStack) ReflectionUtil.callMethod(craftStackClass, "asBukkitCopy", nmsStack); boolean async = !MapReflectionAPI.getInstance().getServer().isPrimaryThread(); diff --git a/src/main/java/tech/sbdevelopment/mapreflectionapi/utils/MainUtil.java b/src/main/java/tech/sbdevelopment/mapreflectionapi/utils/MainUtil.java index e55bc83..0688c0f 100644 --- a/src/main/java/tech/sbdevelopment/mapreflectionapi/utils/MainUtil.java +++ b/src/main/java/tech/sbdevelopment/mapreflectionapi/utils/MainUtil.java @@ -18,6 +18,9 @@ package tech.sbdevelopment.mapreflectionapi.utils; +import java.util.Map; +import java.util.function.Supplier; + public class MainUtil { private MainUtil() { } diff --git a/src/main/java/tech/sbdevelopment/mapreflectionapi/utils/XMaterial.java b/src/main/java/tech/sbdevelopment/mapreflectionapi/utils/XMaterial.java index 95ecaa0..4c993e3 100644 --- a/src/main/java/tech/sbdevelopment/mapreflectionapi/utils/XMaterial.java +++ b/src/main/java/tech/sbdevelopment/mapreflectionapi/utils/XMaterial.java @@ -2,7 +2,7 @@ * The MIT License (MIT) * * Copyright (c) 2018 Hex_27 - * Copyright (c) 2023 Crypto Morin + * Copyright (c) 2024 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 @@ -30,7 +30,7 @@ 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 org.jetbrains.annotations.ApiStatus; import javax.annotation.Nonnull; import javax.annotation.Nullable; @@ -38,7 +38,6 @@ 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; /** @@ -61,11 +60,11 @@ import java.util.stream.Collectors; * /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 + * @version 12.0.0 * @see Material * @see ItemStack */ -public enum XMaterial { +public enum XMaterial /* implements com.cryptomorin.xseries.abstractions.Material*/ { ACACIA_BOAT("BOAT_ACACIA"), ACACIA_BUTTON("WOOD_BUTTON"), ACACIA_CHEST_BOAT, @@ -108,6 +107,8 @@ public enum XMaterial { ANVIL, APPLE, ARCHER_POTTERY_SHERD, + ARMADILLO_SCUTE, + ARMADILLO_SPAWN_EGG, ARMOR_STAND, ARMS_UP_POTTERY_SHERD, ARROW, @@ -223,6 +224,8 @@ public enum XMaterial { BLUE_TERRACOTTA(11, "STAINED_CLAY"), BLUE_WALL_BANNER(4, "WALL_BANNER"), BLUE_WOOL(11, "WOOL"), + BOGGED_SPAWN_EGG, + BOLT_ARMOR_TRIM_SMITHING_TEMPLATE, BONE, BONE_BLOCK, BONE_MEAL(15, "INK_SACK"), @@ -235,6 +238,7 @@ public enum XMaterial { BRAIN_CORAL_FAN, BRAIN_CORAL_WALL_FAN, BREAD, + BREEZE_ROD, BREEZE_SPAWN_EGG, BREWER_POTTERY_SHERD, BREWING_STAND("BREWING_STAND", "BREWING_STAND_ITEM"), @@ -591,7 +595,7 @@ public enum XMaterial { FERMENTED_SPIDER_EYE, FERN(2, "LONG_GRASS"), /** - * For some reasons filled map items are really special. + * For some reason, 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 @@ -613,6 +617,9 @@ public enum XMaterial { FLOWERING_AZALEA_LEAVES, FLOWER_BANNER_PATTERN, FLOWER_POT("FLOWER_POT", "FLOWER_POT_ITEM"), + FLOW_ARMOR_TRIM_SMITHING_TEMPLATE, + FLOW_BANNER_PATTERN, + FLOW_POTTERY_SHERD, FOX_SPAWN_EGG, FRIEND_POTTERY_SHERD, FROGSPAWN, @@ -699,11 +706,14 @@ public enum XMaterial { GRINDSTONE, GUARDIAN_SPAWN_EGG(68, "MONSTER_EGG"), GUNPOWDER("SULPHUR"), + GUSTER_BANNER_PATTERN, + GUSTER_POTTERY_SHERD, HANGING_ROOTS, HAY_BLOCK, HEARTBREAK_POTTERY_SHERD, HEART_OF_THE_SEA, HEART_POTTERY_SHERD, + HEAVY_CORE, HEAVY_WEIGHTED_PRESSURE_PLATE("IRON_PLATE"), HOGLIN_SPAWN_EGG("MONSTER_EGG"), HONEYCOMB, @@ -856,6 +866,7 @@ public enum XMaterial { LLAMA_SPAWN_EGG(103, "MONSTER_EGG"), LODESTONE, LOOM, + MACE, MAGENTA_BANNER(13, "STANDING_BANNER", "BANNER"), MAGENTA_BED(supports(12) ? 2 : 0, "BED_BLOCK", "BED"), MAGENTA_CANDLE, @@ -939,11 +950,14 @@ public enum XMaterial { MUSIC_DISC_BLOCKS("RECORD_3"), MUSIC_DISC_CAT("GREEN_RECORD"), MUSIC_DISC_CHIRP("RECORD_4"), + MUSIC_DISC_CREATOR, + MUSIC_DISC_CREATOR_MUSIC_BOX, MUSIC_DISC_FAR("RECORD_5"), MUSIC_DISC_MALL("RECORD_6"), MUSIC_DISC_MELLOHI("RECORD_7"), MUSIC_DISC_OTHERSIDE, MUSIC_DISC_PIGSTEP, + MUSIC_DISC_PRECIPICE, MUSIC_DISC_RELIC, MUSIC_DISC_STAL("RECORD_8"), MUSIC_DISC_STRAD("RECORD_9"), @@ -1009,6 +1023,8 @@ public enum XMaterial { OBSIDIAN, OCELOT_SPAWN_EGG(98, "MONSTER_EGG"), OCHRE_FROGLIGHT, + OMINOUS_BOTTLE, + OMINOUS_TRIAL_KEY, ORANGE_BANNER(14, "STANDING_BANNER", "BANNER"), ORANGE_BED(supports(12) ? 1 : 0, "BED_BLOCK", "BED"), ORANGE_CANDLE, @@ -1285,6 +1301,7 @@ public enum XMaterial { SANDSTONE_STAIRS, SANDSTONE_WALL, SCAFFOLDING, + SCRAPE_POTTERY_SHERD, SCULK, SCULK_CATALYST, SCULK_SENSOR, @@ -1479,9 +1496,11 @@ public enum XMaterial { TUFF_WALL, TURTLE_EGG, TURTLE_HELMET, + TURTLE_SCUTE, TURTLE_SPAWN_EGG, TWISTING_VINES, TWISTING_VINES_PLANT, + VAULT, VERDANT_FROGLIGHT, VEX_ARMOR_TRIM_SMITHING_TEMPLATE, VEX_SPAWN_EGG(35, "MONSTER_EGG"), @@ -1598,12 +1617,14 @@ public enum XMaterial { WHITE_WALL_BANNER(15, "WALL_BANNER"), WHITE_WOOL("WOOL"), WILD_ARMOR_TRIM_SMITHING_TEMPLATE, + WIND_CHARGE, 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_ARMOR, WOLF_SPAWN_EGG(95, "MONSTER_EGG"), WOODEN_AXE("WOOD_AXE"), WOODEN_HOE("WOOD_HOE"), @@ -1664,14 +1685,6 @@ public enum XMaterial { 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 @@ -1910,9 +1923,10 @@ public enum XMaterial { // 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; + if (!supports(9) && material.equals("POTION")) { + // Source: v1.8.8 org.bukkit.potion.Potion.fromDamage(int damage) + int damage = item.getDurability(); + return ((damage & 16384) > 0) ? SPLASH_POTION : POTION; } // Refer to the enum for info. @@ -1921,9 +1935,14 @@ public enum XMaterial { // 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; + switch (material) { + case "CACTUS_GREEN": + return GREEN_DYE; + case "ROSE_RED": + return RED_DYE; + case "DANDELION_YELLOW": + return YELLOW_DYE; + } } // Check FILLED_MAP enum for more info. @@ -1935,29 +1954,6 @@ public enum XMaterial { 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. @@ -1966,7 +1962,6 @@ public enum XMaterial { * @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 */ @@ -2053,12 +2048,14 @@ public enum XMaterial { } /** + * This is an internal API. Use {@link com.cryptomorin.xseries.reflection.XReflection#supports(int)} instead. * 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 */ + @ApiStatus.Internal public static boolean supports(int version) { return Data.VERSION >= version; } @@ -2067,78 +2064,6 @@ public enum XMaterial { 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. @@ -2209,7 +2134,6 @@ public enum XMaterial { * 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") @@ -2379,6 +2303,7 @@ public enum XMaterial { * * @since 9.0.0 */ + @ApiStatus.Internal private static final class Data { /** * The current version of the server in the form of a major version. -- 2.45.3 From bd6a24a2426501ff1ed0b085a8dd9b6503642854 Mon Sep 17 00:00:00 2001 From: SBDeveloper Date: Sun, 30 Jun 2024 20:49:09 +0200 Subject: [PATCH 7/8] Bumped XSeries, moved to new reflection API of XSeries --- .idea/misc.xml | 2 +- pom.xml | 10 +- .../bukkit/common/map/MapColorPalette.java | 2 +- .../mapreflectionapi/MapReflectionAPI.java | 2 +- .../mapreflectionapi/api/MapManager.java | 5 +- .../mapreflectionapi/api/MapSender.java | 33 +- .../mapreflectionapi/api/MapWrapper.java | 8 +- .../listeners/PacketListener.java | 3 +- .../utils/ReflectionUtil.java | 18 + .../utils/ReflectionUtils.java | 566 ------------------ 10 files changed, 53 insertions(+), 596 deletions(-) delete mode 100644 src/main/java/tech/sbdevelopment/mapreflectionapi/utils/ReflectionUtils.java diff --git a/.idea/misc.xml b/.idea/misc.xml index 5cacde7..285bc57 100644 --- a/.idea/misc.xml +++ b/.idea/misc.xml @@ -30,7 +30,7 @@ - + \ No newline at end of file diff --git a/pom.xml b/pom.xml index fe21f7f..005712b 100644 --- a/pom.xml +++ b/pom.xml @@ -81,6 +81,10 @@ org.bstats tech.sbdevelopment.mapreflectionapi.libs.bstats + + com.cryptomorin.xseries + tech.sbdevelopment.mapreflectionapi.libs.xseries + @@ -177,13 +181,17 @@ 1.18.34 provided - org.bstats bstats-bukkit 3.0.2 compile + + com.github.cryptomorin + XSeries + 11.2.0 + diff --git a/src/main/java/com/bergerkiller/bukkit/common/map/MapColorPalette.java b/src/main/java/com/bergerkiller/bukkit/common/map/MapColorPalette.java index 3963b7b..f3ea19a 100644 --- a/src/main/java/com/bergerkiller/bukkit/common/map/MapColorPalette.java +++ b/src/main/java/com/bergerkiller/bukkit/common/map/MapColorPalette.java @@ -27,7 +27,7 @@ import java.awt.*; import java.io.InputStream; import java.util.Arrays; -import static tech.sbdevelopment.mapreflectionapi.utils.ReflectionUtils.supports; +import static com.cryptomorin.xseries.reflection.XReflection.supports; /** * Additional functionality on top of Bukkit's MapPalette diff --git a/src/main/java/tech/sbdevelopment/mapreflectionapi/MapReflectionAPI.java b/src/main/java/tech/sbdevelopment/mapreflectionapi/MapReflectionAPI.java index bc41d31..0d76789 100644 --- a/src/main/java/tech/sbdevelopment/mapreflectionapi/MapReflectionAPI.java +++ b/src/main/java/tech/sbdevelopment/mapreflectionapi/MapReflectionAPI.java @@ -34,7 +34,7 @@ import tech.sbdevelopment.mapreflectionapi.utils.UpdateManager; import java.util.logging.Level; -import static tech.sbdevelopment.mapreflectionapi.utils.ReflectionUtils.supports; +import static com.cryptomorin.xseries.reflection.XReflection.supports; public class MapReflectionAPI extends JavaPlugin { private static MapReflectionAPI instance; diff --git a/src/main/java/tech/sbdevelopment/mapreflectionapi/api/MapManager.java b/src/main/java/tech/sbdevelopment/mapreflectionapi/api/MapManager.java index 60fbc15..c3c5c60 100644 --- a/src/main/java/tech/sbdevelopment/mapreflectionapi/api/MapManager.java +++ b/src/main/java/tech/sbdevelopment/mapreflectionapi/api/MapManager.java @@ -33,7 +33,8 @@ import java.util.List; import java.util.Set; import java.util.concurrent.CopyOnWriteArrayList; -import static tech.sbdevelopment.mapreflectionapi.utils.ReflectionUtils.*; +import static com.cryptomorin.xseries.reflection.XReflection.*; +import static com.cryptomorin.xseries.reflection.minecraft.MinecraftConnection.getHandle; /** * The {@link MapManager} manages all the maps. It also contains functions for wrapping. @@ -185,7 +186,7 @@ public class MapManager { * @return The found {@link ItemFrame}, or null */ public ItemFrame getItemFrameById(World world, int entityId) { - Object worldHandle = getHandle(world); + Object worldHandle = ReflectionUtil.getHandle(world); Object nmsEntity = ReflectionUtil.callMethod(worldHandle, supports(18) ? "a" : "getEntity", entityId); if (nmsEntity == null) return null; diff --git a/src/main/java/tech/sbdevelopment/mapreflectionapi/api/MapSender.java b/src/main/java/tech/sbdevelopment/mapreflectionapi/api/MapSender.java index 0caefd0..9e46c98 100644 --- a/src/main/java/tech/sbdevelopment/mapreflectionapi/api/MapSender.java +++ b/src/main/java/tech/sbdevelopment/mapreflectionapi/api/MapSender.java @@ -27,7 +27,8 @@ import tech.sbdevelopment.mapreflectionapi.utils.ReflectionUtil; import java.util.ArrayList; import java.util.List; -import static tech.sbdevelopment.mapreflectionapi.utils.ReflectionUtils.*; +import static com.cryptomorin.xseries.reflection.XReflection.*; +import static com.cryptomorin.xseries.reflection.minecraft.MinecraftConnection.sendPacket; /** * The {@link MapSender} sends the Map packets to players. @@ -111,6 +112,8 @@ public class MapSender { return; } + int id = -id0; + Object packet; if (supports(20, 4)) { //1.20.5+ Object updateData = ReflectionUtil.callConstructor(worldMapData, @@ -139,26 +142,16 @@ public class MapSender { content.array //Data ); - if (supports(21)) { //1.21+ - packet = ReflectionUtil.callConstructor(packetPlayOutMapClass, - ReflectionUtil.callConstructor(mapId, -id0), //ID - (byte) 0, //Scale, 0 = 1 block per pixel - false, //Show icons - new ReflectionUtil.CollectionParam<>(), //Icons - updateData - ); - } else { - packet = ReflectionUtil.callConstructor(packetPlayOutMapClass, - -id0, //ID - (byte) 0, //Scale, 0 = 1 block per pixel - false, //Show icons - new ReflectionUtil.CollectionParam<>(), //Icons - updateData - ); - } + packet = ReflectionUtil.callConstructor(packetPlayOutMapClass, + id, //ID + (byte) 0, //Scale, 0 = 1 block per pixel + false, //Show icons + new ReflectionUtil.CollectionParam<>(), //Icons + updateData + ); } else if (supports(14)) { //1.16-1.14 packet = ReflectionUtil.callConstructor(packetPlayOutMapClass, - -id0, //ID + id, //ID (byte) 0, //Scale, 0 = 1 block per pixel false, //Tracking position false, //Locked @@ -171,7 +164,7 @@ public class MapSender { ); } else { //1.13- packet = ReflectionUtil.callConstructor(packetPlayOutMapClass, - -id0, //ID + id, //ID (byte) 0, //Scale, 0 = 1 block per pixel false, //??? new ReflectionUtil.CollectionParam<>(), //Icons diff --git a/src/main/java/tech/sbdevelopment/mapreflectionapi/api/MapWrapper.java b/src/main/java/tech/sbdevelopment/mapreflectionapi/api/MapWrapper.java index def2c14..32396b9 100644 --- a/src/main/java/tech/sbdevelopment/mapreflectionapi/api/MapWrapper.java +++ b/src/main/java/tech/sbdevelopment/mapreflectionapi/api/MapWrapper.java @@ -41,7 +41,9 @@ import java.util.HashMap; import java.util.Map; import java.util.UUID; -import static tech.sbdevelopment.mapreflectionapi.utils.ReflectionUtils.*; +import static com.cryptomorin.xseries.reflection.XReflection.*; +import static com.cryptomorin.xseries.reflection.minecraft.MinecraftConnection.getHandle; +import static com.cryptomorin.xseries.reflection.minecraft.MinecraftConnection.sendPacket; /** * A {@link MapWrapper} wraps one image. @@ -207,7 +209,7 @@ public class MapWrapper extends AbstractMapWrapper { ); } - sendPacketSync(player, packet); + sendPacket(player, packet); } @Override @@ -371,7 +373,7 @@ public class MapWrapper extends AbstractMapWrapper { ReflectionUtil.setDeclaredField(packet, "b", list); } - sendPacketSync(player, packet); + sendPacket(player, packet); } }; } diff --git a/src/main/java/tech/sbdevelopment/mapreflectionapi/listeners/PacketListener.java b/src/main/java/tech/sbdevelopment/mapreflectionapi/listeners/PacketListener.java index e7914fb..5cf1957 100644 --- a/src/main/java/tech/sbdevelopment/mapreflectionapi/listeners/PacketListener.java +++ b/src/main/java/tech/sbdevelopment/mapreflectionapi/listeners/PacketListener.java @@ -38,8 +38,9 @@ import tech.sbdevelopment.mapreflectionapi.utils.ReflectionUtil; import java.util.concurrent.TimeUnit; +import static com.cryptomorin.xseries.reflection.minecraft.MinecraftConnection.getHandle; import static tech.sbdevelopment.mapreflectionapi.utils.ReflectionUtil.*; -import static tech.sbdevelopment.mapreflectionapi.utils.ReflectionUtils.*; +import static com.cryptomorin.xseries.reflection.XReflection.*; public class PacketListener implements Listener { private static final Class packetPlayOutMapClass = getNMSClass("network.protocol.game", "PacketPlayOutMap"); diff --git a/src/main/java/tech/sbdevelopment/mapreflectionapi/utils/ReflectionUtil.java b/src/main/java/tech/sbdevelopment/mapreflectionapi/utils/ReflectionUtil.java index ad81288..87f7e57 100644 --- a/src/main/java/tech/sbdevelopment/mapreflectionapi/utils/ReflectionUtil.java +++ b/src/main/java/tech/sbdevelopment/mapreflectionapi/utils/ReflectionUtil.java @@ -18,6 +18,7 @@ package tech.sbdevelopment.mapreflectionapi.utils; +import org.bukkit.World; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -27,6 +28,9 @@ import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.util.*; +import static com.cryptomorin.xseries.reflection.XReflection.getCraftClass; +import static com.cryptomorin.xseries.reflection.XReflection.getNMSClass; + public class ReflectionUtil { private static final Map> constructorCache = new HashMap<>(); private static final Map methodCache = new HashMap<>(); @@ -71,6 +75,20 @@ public class ReflectionUtil { .toArray(Class[]::new); } + @Nullable + public static Object getHandle(@NotNull World world) { + Class worldServer = getNMSClass("server.level", "WorldServer"); + Class craftWorld = getCraftClass("CraftWorld"); + try { + Method m = craftWorld.getMethod("getHandle", worldServer); + m.setAccessible(true); + return m.invoke(null, world); + } catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException ex) { + ex.printStackTrace(); + return null; + } + } + @Nullable public static Class getClass(@NotNull String name) { try { diff --git a/src/main/java/tech/sbdevelopment/mapreflectionapi/utils/ReflectionUtils.java b/src/main/java/tech/sbdevelopment/mapreflectionapi/utils/ReflectionUtils.java deleted file mode 100644 index 912a2c4..0000000 --- a/src/main/java/tech/sbdevelopment/mapreflectionapi/utils/ReflectionUtils.java +++ /dev/null @@ -1,566 +0,0 @@ -/* - * The MIT License (MIT) - * - * 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 org.bukkit.Bukkit; -import org.bukkit.World; -import org.bukkit.entity.Player; - -import javax.annotation.Nonnull; -import javax.annotation.Nullable; -import java.lang.invoke.MethodHandle; -import java.lang.invoke.MethodHandles; -import java.lang.invoke.MethodType; -import java.util.Arrays; -import java.util.Objects; -import java.util.concurrent.Callable; -import java.util.concurrent.CompletableFuture; -import java.util.regex.Matcher; -import java.util.regex.Pattern; - -/** - * ReflectionUtils - Reflection handler for NMS and CraftBukkit.
- * Caches the packet related methods and is asynchronous. - *

- * This class does not handle null checks as most of the requests are from the - * other utility classes that already handle null checks. - *

- * Clientbound Packets are considered fake - * updates to the client without changing the actual data. Since all the data is handled - * by the server. - *

- * A useful resource used to compare mappings is Mini's Mapping Viewer - * - * @author Crypto Morin - * @version 7.1.0.0.1 - */ -public final class ReflectionUtils { - /** - * We use reflection mainly to avoid writing a new class for version barrier. - * The version barrier is for NMS that uses the Minecraft version as the main package name. - *

- * E.g. EntityPlayer in 1.15 is in the class {@code net.minecraft.server.v1_15_R1} - * but in 1.14 it's in {@code net.minecraft.server.v1_14_R1} - * In order to maintain cross-version compatibility we cannot import these classes. - *

- * Performance is not a concern for these specific statically initialized values. - *

- * Versions Legacy - */ - public static final String NMS_VERSION; - - static { // This needs to be right below VERSION because of initialization order. - // This package loop is used to avoid implementation-dependant strings like Bukkit.getVersion() or Bukkit.getBukkitVersion() - // which allows easier testing as well. - String found = null; - for (Package pack : Package.getPackages()) { - String name = pack.getName(); - - // .v because there are other packages. - if (name.startsWith("org.bukkit.craftbukkit.v")) { - found = pack.getName().split("\\.")[3]; - - // Just a final guard to make sure it finds this important class. - // As a protection for forge+bukkit implementation that tend to mix versions. - // The real CraftPlayer should exist in the package. - // Note: Doesn't seem to function properly. Will need to separate the version - // handler for NMS and CraftBukkit for softwares like catmc. - try { - Class.forName("org.bukkit.craftbukkit." + found + ".entity.CraftPlayer"); - break; - } catch (ClassNotFoundException e) { - found = null; - } - } - } - if (found == null) - throw new IllegalArgumentException("Failed to parse server version. Could not find any package starting with name: 'org.bukkit.craftbukkit.v'"); - NMS_VERSION = found; - } - - /** - * The raw minor version number. - * E.g. {@code v1_17_R1} to {@code 17} - * - * @see #supports(int) - * @since 4.0.0 - */ - public static final int MINOR_NUMBER; - /** - * The raw patch version number. Refers to the major.minor.patch version scheme. - * E.g. - *

    - *
  • {@code v1.20.4} to {@code 4}
  • - *
  • {@code v1.18.2} to {@code 2}
  • - *
  • {@code v1.19.1} to {@code 1}
  • - *
- *

- * I'd not recommend developers to support individual patches at all. You should always support the latest patch. - * For example, between v1.14.0, v1.14.1, v1.14.2, v1.14.3 and v1.14.4 you should only support v1.14.4 - *

- * This can be used to warn server owners when your plugin will break on older patches. - * - * @see #supportsPatch(int) - * @since 7.0.0 - */ - public static final int PATCH_NUMBER; - - static { - String[] split = NMS_VERSION.substring(1).split("_"); - if (split.length < 1) { - throw new IllegalStateException("Version number division error: " + Arrays.toString(split) + ' ' + getVersionInformation()); - } - - String minorVer = split[1]; - try { - MINOR_NUMBER = Integer.parseInt(minorVer); - if (MINOR_NUMBER < 0) - throw new IllegalStateException("Negative minor number? " + minorVer + ' ' + getVersionInformation()); - } catch (Throwable ex) { - throw new RuntimeException("Failed to parse minor number: " + minorVer + ' ' + getVersionInformation(), ex); - } - - // Bukkit.getBukkitVersion() = "1.12.2-R0.1-SNAPSHOT" - Matcher bukkitVer = Pattern.compile("^\\d+\\.\\d+\\.(\\d+)").matcher(Bukkit.getBukkitVersion()); - if (bukkitVer.find()) { // matches() won't work, we just want to match the start using "^" - try { - // group(0) gives the whole matched string, we just want the captured group. - PATCH_NUMBER = Integer.parseInt(bukkitVer.group(1)); - } catch (Throwable ex) { - throw new RuntimeException("Failed to parse minor number: " + bukkitVer + ' ' + getVersionInformation(), ex); - } - } else { - // 1.8-R0.1-SNAPSHOT - PATCH_NUMBER = 0; - } - } - - /** - * Gets the full version information of the server. Useful for including in errors. - * - * @since 7.0.0 - */ - public static String getVersionInformation() { - return "(NMS: " + NMS_VERSION + " | " + - "Minecraft: " + Bukkit.getVersion() + " | " + - "Bukkit: " + Bukkit.getBukkitVersion() + ')'; - } - - /** - * Gets the latest known patch number of the given minor version. - * For example: 1.14 -> 4, 1.17 -> 10 - * The latest version is expected to get newer patches, so make sure to account for unexpected results. - * - * @param minorVersion the minor version to get the patch number of. - * @return the patch number of the given minor version if recognized, otherwise null. - * @since 7.0.0 - */ - public static Integer getLatestPatchNumberOf(int minorVersion) { - if (minorVersion <= 0) throw new IllegalArgumentException("Minor version must be positive: " + minorVersion); - - // https://minecraft.wiki/w/Java_Edition_version_history - // There are many ways to do this, but this is more visually appealing. - int[] patches = { - /* 1 */ 1, - /* 2 */ 5, - /* 3 */ 2, - /* 4 */ 7, - /* 5 */ 2, - /* 6 */ 4, - /* 7 */ 10, - /* 8 */ 8, // I don't think they released a server version for 1.8.9 - /* 9 */ 4, - - /* 10 */ 2,// ,_ _ _, - /* 11 */ 2,// \o-o/ - /* 12 */ 2,// ,(.-.), - /* 13 */ 2,// _/ |) (| \_ - /* 14 */ 4,// /\=-=/\ - /* 15 */ 2,// ,| \=/ |, - /* 16 */ 5,// _/ \ | / \_ - /* 17 */ 1,// \_!_/ - /* 18 */ 2, - /* 19 */ 4, - /* 20 */ 4, - }; - - if (minorVersion > patches.length) return null; - return patches[minorVersion - 1]; - } - - /** - * Mojang remapped their NMS in 1.17: Spigot Thread - */ - public static final String - CRAFTBUKKIT_PACKAGE = "org.bukkit.craftbukkit." + NMS_VERSION + '.', - NMS_PACKAGE = v(17, "net.minecraft.").orElse("net.minecraft.server." + NMS_VERSION + '.'); - /** - * A nullable public accessible field only available in {@code EntityPlayer}. - * This can be null if the player is offline. - */ - private static final MethodHandle PLAYER_CONNECTION; - /** - * Responsible for getting the NMS handler {@code EntityPlayer} object for the player. - * {@code CraftPlayer} is simply a wrapper for {@code EntityPlayer}. - * Used mainly for handling packet related operations. - *

- * This is also where the famous player {@code ping} field comes from! - */ - private static final MethodHandle GET_HANDLE; - /** - * Responsible for getting the NMS handler {@code WorldServer} object for the world. - * {@code CraftWorld} is simply a wrapper for {@code WorldServer}. - */ - private static final MethodHandle GET_HANDLE_WORLD; - /** - * Sends a packet to the player's client through a {@code NetworkManager} which - * is where {@code ProtocolLib} controls packets by injecting channels! - */ - private static final MethodHandle SEND_PACKET; - - static { - Class entityPlayer = getNMSClass("server.level", "EntityPlayer"); - Class worldServer = getNMSClass("server.level", "WorldServer"); - Class craftPlayer = getCraftClass("entity.CraftPlayer"); - Class craftWorld = getCraftClass("CraftWorld"); - Class playerConnection = getNMSClass("server.network", "PlayerConnection"); - Class playerCommonConnection; - if (supports(20) && supportsPatch(2)) { - // The packet send method has been abstracted from ServerGamePacketListenerImpl to ServerCommonPacketListenerImpl in 1.20.2 - playerCommonConnection = getNMSClass("server.network", "ServerCommonPacketListenerImpl"); - } else { - playerCommonConnection = playerConnection; - } - - MethodHandles.Lookup lookup = MethodHandles.lookup(); - MethodHandle sendPacket = null, getHandle = null, getHandleWorld = null, connection = null; - - try { - connection = lookup.findGetter(entityPlayer, - v(20, "c").v(17, "b").orElse("playerConnection"), playerConnection); - getHandle = lookup.findVirtual(craftPlayer, "getHandle", MethodType.methodType(entityPlayer)); - getHandleWorld = lookup.findVirtual(craftWorld, "getHandle", MethodType.methodType(worldServer)); - sendPacket = lookup.findVirtual(playerCommonConnection, - v(20, 2, "b").v(18, "a").orElse("sendPacket"), - MethodType.methodType(void.class, getNMSClass("network.protocol", "Packet"))); - } catch (NoSuchMethodException | NoSuchFieldException | IllegalAccessException ex) { - ex.printStackTrace(); - } - - PLAYER_CONNECTION = connection; - SEND_PACKET = sendPacket; - GET_HANDLE = getHandle; - GET_HANDLE_WORLD = getHandleWorld; - } - - private ReflectionUtils() { - } - - /** - * Gives the {@code handle} object if the server version is equal or greater than the given version. - * This method is purely for readability and should be always used with {@link VersionHandler#orElse(Object)}. - * - * @see #v(int, int, Object) - * @see VersionHandler#orElse(Object) - * @since 5.0.0 - */ - public static VersionHandler v(int version, T handle) { - return new VersionHandler<>(version, handle); - } - - /** - * Overload for {@link #v(int, T)} that supports patch versions - * - * @since 9.5.0 - */ - public static VersionHandler v(int version, int patch, T handle) { - return new VersionHandler<>(version, patch, handle); - } - - public static CallableVersionHandler v(int version, Callable handle) { - return new CallableVersionHandler<>(version, handle); - } - - /** - * Checks whether the server version is equal or greater than the given version. - * - * @param minorNumber the version to compare the server version with. - * @return true if the version is equal or newer, otherwise false. - * @see #MINOR_NUMBER - * @since 4.0.0 - */ - public static boolean supports(int minorNumber) { - return MINOR_NUMBER >= minorNumber; - } - - /** - * Checks whether the server version is equal or greater than the given version. - * - * @param minorNumber the minor version to compare the server version with. - * @param patchNumber the patch number to compare the server version with. - * @return true if the version is equal or newer, otherwise false. - * @see #MINOR_NUMBER - * @see #PATCH_NUMBER - * @since 7.1.0 - */ - public static boolean supports(int minorNumber, int patchNumber) { - return (MINOR_NUMBER == minorNumber && supportsPatch(patchNumber)) || MINOR_NUMBER > minorNumber; - } - - /** - * Checks whether the server version is equal or greater than the given version. - * - * @param patchNumber the version to compare the server version with. - * @return true if the version is equal or newer, otherwise false. - * @see #PATCH_NUMBER - * @since 7.0.0 - */ - public static boolean supportsPatch(int patchNumber) { - return PATCH_NUMBER >= patchNumber; - } - - /** - * Get a NMS (net.minecraft.server) class which accepts a package for 1.17 compatibility. - * - * @param packageName the 1.17+ package name of this class. - * @param name the name of the class. - * @return the NMS class or null if not found. - * @since 4.0.0 - */ - @Nullable - public static Class getNMSClass(@Nullable String packageName, @Nonnull String name) { - if (packageName != null && supports(17)) name = packageName + '.' + name; - - try { - return Class.forName(NMS_PACKAGE + name); - } catch (ClassNotFoundException ex) { - ex.printStackTrace(); - return null; - } - } - - /** - * Get a NMS {@link #NMS_PACKAGE} class. - * - * @param name the name of the class. - * @return the NMS class or null if not found. - * @since 1.0.0 - */ - @Nullable - public static Class getNMSClass(@Nonnull String name) { - return getNMSClass(null, name); - } - - /** - * Sends a packet to the player asynchronously if they're online. - * Packets are thread-safe. - * - * @param player the player to send the packet to. - * @param packets the packets to send. - * @return the async thread handling the packet. - * @see #sendPacketSync(Player, Object...) - * @since 1.0.0 - */ - @Nonnull - public static CompletableFuture sendPacket(@Nonnull Player player, @Nonnull Object... packets) { - return CompletableFuture.runAsync(() -> sendPacketSync(player, packets)) - .exceptionally(ex -> { - ex.printStackTrace(); - return null; - }); - } - - /** - * Sends a packet to the player synchronously if they're online. - * - * @param player the player to send the packet to. - * @param packets the packets to send. - * @see #sendPacket(Player, Object...) - * @since 2.0.0 - */ - public static void sendPacketSync(@Nonnull Player player, @Nonnull Object... packets) { - try { - Object handle = GET_HANDLE.invoke(player); - Object connection = PLAYER_CONNECTION.invoke(handle); - - // Checking if the connection is not null is enough. There is no need to check if the player is online. - if (connection != null) { - for (Object packet : packets) SEND_PACKET.invoke(connection, packet); - } - } catch (Throwable throwable) { - throwable.printStackTrace(); - } - } - - @Nullable - public static Object getHandle(@Nonnull Player player) { - Objects.requireNonNull(player, "Cannot get handle of null player"); - try { - return GET_HANDLE.invoke(player); - } catch (Throwable throwable) { - throwable.printStackTrace(); - return null; - } - } - - @Nullable - public static Object getHandle(@Nonnull World world) { - Objects.requireNonNull(world, "Cannot get handle of null world"); - try { - return GET_HANDLE_WORLD.invoke(world); - } catch (Throwable throwable) { - throwable.printStackTrace(); - return null; - } - } - - @Nullable - public static Object getConnection(@Nonnull Player player) { - Objects.requireNonNull(player, "Cannot get connection of null player"); - try { - Object handle = GET_HANDLE.invoke(player); - return PLAYER_CONNECTION.invoke(handle); - } catch (Throwable throwable) { - throwable.printStackTrace(); - return null; - } - } - - /** - * Get a CraftBukkit (org.bukkit.craftbukkit) class. - * - * @param name the name of the class to load. - * @return the CraftBukkit class or null if not found. - * @since 1.0.0 - */ - @Nullable - public static Class getCraftClass(@Nonnull String name) { - try { - return Class.forName(CRAFTBUKKIT_PACKAGE + name); - } catch (ClassNotFoundException ex) { - ex.printStackTrace(); - return null; - } - } - - /** - * @deprecated Use {@link #toArrayClass(Class)} instead. - */ - @Deprecated - public static Class getArrayClass(String clazz, boolean nms) { - clazz = "[L" + (nms ? NMS_PACKAGE : CRAFTBUKKIT_PACKAGE) + clazz + ';'; - try { - return Class.forName(clazz); - } catch (ClassNotFoundException ex) { - ex.printStackTrace(); - return null; - } - } - - /** - * Gives an array version of a class. For example if you wanted {@code EntityPlayer[]} you'd use: - *

{@code
-     *     Class EntityPlayer = ReflectionUtils.getNMSClass("...", "EntityPlayer");
-     *     Class EntityPlayerArray = ReflectionUtils.toArrayClass(EntityPlayer);
-     * }
- * - * @param clazz the class to get the array version of. You could use for multi-dimensions arrays too. - */ - public static Class toArrayClass(Class clazz) { - try { - return Class.forName("[L" + clazz.getName() + ';'); - } catch (ClassNotFoundException ex) { - ex.printStackTrace(); - return null; - } - } - - public static final class VersionHandler { - private int version, patch; - private T handle; - - private VersionHandler(int version, T handle) { - this(version, 0, handle); - } - - private VersionHandler(int version, int patch, T handle) { - if (supports(version) && supportsPatch(patch)) { - this.version = version; - this.patch = patch; - this.handle = handle; - } - } - - public VersionHandler v(int version, T handle) { - return v(version, 0, handle); - } - - public VersionHandler v(int version, int patch, T handle) { - if (version == this.version && patch == this.patch) - throw new IllegalArgumentException("Cannot have duplicate version handles for version: " + version + '.' + patch); - if (version > this.version && supports(version) && patch >= this.patch && supportsPatch(patch)) { - this.version = version; - this.patch = patch; - this.handle = handle; - } - return this; - } - - /** - * If none of the previous version checks matched, it'll return this object. - */ - public T orElse(T handle) { - return this.version == 0 ? handle : this.handle; - } - } - - public static final class CallableVersionHandler { - private int version; - private Callable handle; - - private CallableVersionHandler(int version, Callable handle) { - if (supports(version)) { - this.version = version; - this.handle = handle; - } - } - - public CallableVersionHandler v(int version, Callable handle) { - if (version == this.version) - throw new IllegalArgumentException("Cannot have duplicate version handles for version: " + version); - if (version > this.version && supports(version)) { - this.version = version; - this.handle = handle; - } - return this; - } - - public T orElse(Callable handle) { - try { - return (this.version == 0 ? handle : this.handle).call(); - } catch (Exception e) { - e.printStackTrace(); - return null; - } - } - } -} \ No newline at end of file -- 2.45.3 From 3f382583a796e069822387386347d940c8de180d Mon Sep 17 00:00:00 2001 From: SBDeveloper Date: Sun, 30 Jun 2024 22:37:24 +0200 Subject: [PATCH 8/8] Fixed all stupid NMS changes for 1.20.6+ --- .../mapreflectionapi/api/MapManager.java | 1 - .../mapreflectionapi/api/MapWrapper.java | 12 ++++- .../listeners/PacketListener.java | 52 +++++++++++++++---- .../utils/ReflectionUtil.java | 34 ++++++++---- 4 files changed, 75 insertions(+), 24 deletions(-) diff --git a/src/main/java/tech/sbdevelopment/mapreflectionapi/api/MapManager.java b/src/main/java/tech/sbdevelopment/mapreflectionapi/api/MapManager.java index c3c5c60..8b05f72 100644 --- a/src/main/java/tech/sbdevelopment/mapreflectionapi/api/MapManager.java +++ b/src/main/java/tech/sbdevelopment/mapreflectionapi/api/MapManager.java @@ -34,7 +34,6 @@ import java.util.Set; import java.util.concurrent.CopyOnWriteArrayList; import static com.cryptomorin.xseries.reflection.XReflection.*; -import static com.cryptomorin.xseries.reflection.minecraft.MinecraftConnection.getHandle; /** * The {@link MapManager} manages all the maps. It also contains functions for wrapping. diff --git a/src/main/java/tech/sbdevelopment/mapreflectionapi/api/MapWrapper.java b/src/main/java/tech/sbdevelopment/mapreflectionapi/api/MapWrapper.java index 32396b9..534728c 100644 --- a/src/main/java/tech/sbdevelopment/mapreflectionapi/api/MapWrapper.java +++ b/src/main/java/tech/sbdevelopment/mapreflectionapi/api/MapWrapper.java @@ -297,7 +297,17 @@ public class MapWrapper extends AbstractMapWrapper { if (supports(20, 4)) { Object mapIdComponent = ReflectionUtil.getDeclaredField(getNMSClass("core.component", "DataComponents"), "B"); Object mapId1 = ReflectionUtil.callConstructor(getNMSClass("world.level.saveddata.maps", "MapId"), mapId); - ReflectionUtil.callMethod(nmsStack, "b", mapIdComponent, mapId1); + + // Use generic reflection because of generics + // T ItemStack#b(DataComponentType dataComponentType, T t) + try { + Method m = nmsStack.getClass().getMethod("b", getNMSClass("core.component", "DataComponentType"), Object.class); + m.setAccessible(true); + m.invoke(nmsStack, mapIdComponent, mapId1); + } catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException ex) { + ex.printStackTrace(); + return null; + } } else if (supports(13)) { String nbtObjectName; if (supports(20)) { //1.20 diff --git a/src/main/java/tech/sbdevelopment/mapreflectionapi/listeners/PacketListener.java b/src/main/java/tech/sbdevelopment/mapreflectionapi/listeners/PacketListener.java index 5cf1957..ec3daf2 100644 --- a/src/main/java/tech/sbdevelopment/mapreflectionapi/listeners/PacketListener.java +++ b/src/main/java/tech/sbdevelopment/mapreflectionapi/listeners/PacketListener.java @@ -30,14 +30,18 @@ import org.bukkit.event.player.PlayerJoinEvent; import org.bukkit.event.player.PlayerQuitEvent; import org.bukkit.inventory.ItemStack; import org.bukkit.util.Vector; +import sun.misc.Unsafe; import tech.sbdevelopment.mapreflectionapi.MapReflectionAPI; import tech.sbdevelopment.mapreflectionapi.api.events.CreativeInventoryMapUpdateEvent; import tech.sbdevelopment.mapreflectionapi.api.events.MapCancelEvent; import tech.sbdevelopment.mapreflectionapi.api.events.MapInteractEvent; import tech.sbdevelopment.mapreflectionapi.utils.ReflectionUtil; +import java.lang.reflect.Field; +import java.lang.reflect.Modifier; import java.util.concurrent.TimeUnit; +import static com.cryptomorin.xseries.reflection.minecraft.MinecraftConnection.getConnection; import static com.cryptomorin.xseries.reflection.minecraft.MinecraftConnection.getHandle; import static tech.sbdevelopment.mapreflectionapi.utils.ReflectionUtil.*; import static com.cryptomorin.xseries.reflection.XReflection.*; @@ -51,7 +55,7 @@ public class PacketListener implements Listener { private static final Class playerCommonConnection; static { - if (supports(20) && supportsPatch(2)) { + if (supports(20, 2)) { // The packet send method has been abstracted from ServerGamePacketListenerImpl to ServerCommonPacketListenerImpl in 1.20.2 playerCommonConnection = getNMSClass("server.network", "ServerCommonPacketListenerImpl"); } else { @@ -78,11 +82,34 @@ public class PacketListener implements Listener { if (packet.getClass().isAssignableFrom(packetPlayOutMapClass)) { Object packetPlayOutMap = packetPlayOutMapClass.cast(packet); - int id = (int) getDeclaredField(packetPlayOutMap, "a"); - if (id < 0) { - int newId = -id; - setDeclaredField(packetPlayOutMap, "a", newId); + int id; + boolean inv = false; + if (supports(20, 4)) { //1.20.4 uses MapId class and record classes (final fields...) + Object mapId = getDeclaredField(packetPlayOutMap, "b"); + id = (int) getDeclaredField(mapId, "c"); + + if (id < 0) { + Object newMapid = callConstructor(mapId.getClass(), -id); + Object c = getDeclaredField(packetPlayOutMap, "c"); + Object d = getDeclaredField(packetPlayOutMap, "d"); + Object e = getDeclaredField(packetPlayOutMap, "e"); + Object f = getDeclaredField(packetPlayOutMap, "f"); + + packetPlayOutMap = callConstructor(packetPlayOutMapClass, newMapid, c, d, e, f); + packet = packetPlayOutMap; + + inv = true; + } } else { + id = (int) getDeclaredField(packetPlayOutMap, "a"); + + if (id < 0) { + setDeclaredField(packetPlayOutMap, "a", -id); + inv = true; + } + } + + if (!inv) { boolean async = !MapReflectionAPI.getInstance().getServer().isPrimaryThread(); MapCancelEvent event = new MapCancelEvent(player, id, async); if (MapReflectionAPI.getMapManager().isIdUsedBy(player, id)) event.setCancelled(true); @@ -102,13 +129,13 @@ public class PacketListener implements Listener { if (packet.getClass().isAssignableFrom(packetPlayInUseEntityClass)) { Object packetPlayInEntity = packetPlayInUseEntityClass.cast(packet); - int entityId = (int) getDeclaredField(packetPlayInEntity, "a"); + int entityId = (int) getDeclaredField(packetPlayInEntity, supports(20, 4) ? "b" : "a"); Enum actionEnum; Enum hand; Object pos; if (supports(17)) { - Object action = getDeclaredField(packetPlayInEntity, "b"); + Object action = getDeclaredField(packetPlayInEntity, supports(20, 4) ? "c" : "b"); actionEnum = (Enum) callDeclaredMethod(action, "a"); Class d = getNMSClass("network.protocol.game", "PacketPlayInUseEntity$d"); Class e = getNMSClass("network.protocol.game", "PacketPlayInUseEntity$e"); @@ -141,7 +168,12 @@ public class PacketListener implements Listener { } else if (packet.getClass().isAssignableFrom(packetPlayInSetCreativeSlotClass)) { Object packetPlayInSetCreativeSlot = packetPlayInSetCreativeSlotClass.cast(packet); - int slot = (int) ReflectionUtil.callDeclaredMethod(packetPlayInSetCreativeSlot, supports(20, 4) ? "b" : supports(19, 4) ? "a" : supports(13) ? "b" : "a"); //1.20.4 - 1.19.4 = a, 1.19.3 - 1.13 and 1.20.5 = b, 1.12 = a + int slot; + if (supports(20, 4)) { //1.20.4+ uses short + slot = (short) ReflectionUtil.callDeclaredMethod(packetPlayInSetCreativeSlot, "b"); + } else { //1.20.3 and lower uses int + slot = (int) ReflectionUtil.callDeclaredMethod(packetPlayInSetCreativeSlot, supports(19, 4) ? "a" : supports(13) ? "b" : "a"); //1.20.4 - 1.19.4 = a, 1.19.3 - 1.13 and 1.20.5 = b, 1.12 = a + } Object nmsStack = ReflectionUtil.callDeclaredMethod(packetPlayInSetCreativeSlot, supports(20, 4) ? "e" : supports(20, 2) ? "d" : supports(18) ? "c" : "getItemStack"); //1.20.5 = e, 1.20.2-1.20.4 = d, >= 1.18 = c, 1.17 = getItemStack ItemStack craftStack = (ItemStack) ReflectionUtil.callMethod(craftStackClass, "asBukkitCopy", nmsStack); @@ -167,9 +199,7 @@ public class PacketListener implements Listener { } private Channel getChannel(Player player) { - Object playerHandle = getHandle(player); - Object playerConnection = getDeclaredField(playerHandle, supports(20) ? "c" : supports(17) ? "b" : "playerConnection"); //1.20 = c, 1.17-1.19 = b, 1.16 = playerConnection - Object networkManager = getDeclaredField(playerCommonConnection, playerConnection, supports(20, 2) ? "c" : supports(19, 4) ? "h" : supports(19) ? "b" : supports(17) ? "a" : "networkManager"); //1.20.2 = ServerCommonPacketListenerImpl#c, 1.20(.1) & 1.19.4 = h, >= 1.19.3 = b, 1.18 - 1.17 = a, 1.16 = networkManager + Object networkManager = getDeclaredField(playerCommonConnection, getConnection(player), supports(21) ? "e" : supports(20, 2) ? "c" : supports(19, 4) ? "h" : supports(19) ? "b" : supports(17) ? "a" : "networkManager"); //1.20.2 = ServerCommonPacketListenerImpl#c, 1.20(.1) & 1.19.4 = h, >= 1.19.3 = b, 1.18 - 1.17 = a, 1.16 = networkManager return (Channel) getDeclaredField(networkManager, supports(20, 2) ? "n" : supports(18) ? "m" : supports(17) ? "k" : "channel"); //1.20.2 = n, 1.20(.1), 1.19 & 1.18 = m, 1.17 = k, 1.16 = channel } diff --git a/src/main/java/tech/sbdevelopment/mapreflectionapi/utils/ReflectionUtil.java b/src/main/java/tech/sbdevelopment/mapreflectionapi/utils/ReflectionUtil.java index 87f7e57..a723ca9 100644 --- a/src/main/java/tech/sbdevelopment/mapreflectionapi/utils/ReflectionUtil.java +++ b/src/main/java/tech/sbdevelopment/mapreflectionapi/utils/ReflectionUtil.java @@ -35,6 +35,7 @@ public class ReflectionUtil { private static final Map> constructorCache = new HashMap<>(); private static final Map methodCache = new HashMap<>(); private static final Map fieldCache = new HashMap<>(); + private static final Class craftWorld = getCraftClass("CraftWorld"); /** * Helper class converted to {@link List} @@ -76,17 +77,8 @@ public class ReflectionUtil { } @Nullable - public static Object getHandle(@NotNull World world) { - Class worldServer = getNMSClass("server.level", "WorldServer"); - Class craftWorld = getCraftClass("CraftWorld"); - try { - Method m = craftWorld.getMethod("getHandle", worldServer); - m.setAccessible(true); - return m.invoke(null, world); - } catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException ex) { - ex.printStackTrace(); - return null; - } + public static Object getHandle(@NotNull World world) {; + return callDeclaredMethod(craftWorld, world, "getHandle"); } @Nullable @@ -243,6 +235,26 @@ public class ReflectionUtil { } } + @Nullable + public static Object callDeclaredMethod(Class clazz, Object obj, String method, Object... params) { + try { + String cacheKey = "DeclaredMethod:" + clazz.getName() + ":" + method + ":" + Arrays.hashCode(params); + + if (methodCache.containsKey(cacheKey)) { + Method cachedMethod = methodCache.get(cacheKey); + return cachedMethod.invoke(obj, params); + } else { + Method m = clazz.getDeclaredMethod(method, toParamTypes(params)); + m.setAccessible(true); + methodCache.put(cacheKey, m); + return m.invoke(obj, params); + } + } catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException ex) { + ex.printStackTrace(); + return null; + } + } + public static boolean hasField(Object packet, String field) { try { String cacheKey = "HasField:" + packet.getClass().getName() + ":" + field; -- 2.45.3