diff --git a/build.gradle.kts b/build.gradle.kts index 3ca7fbf..9681564 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -36,6 +36,7 @@ dependencies { compileOnly("me.clip:placeholderapi:2.11.5") compileOnly("net.kyori:adventure-api:4.17.0") compileOnly("xyz.xenondevs.invui:invui-core:$invuiVersion") + compileOnly("net.wesjd:anvilgui:1.10.4-SNAPSHOT") implementation("de.rapha149.signgui:signgui:2.5.0") implementation("com.github.jsixface:yamlconfig:1.2") @@ -59,6 +60,7 @@ tasks { shadowJar { // RELOCATIONS + relocate("net.wesjd", "xyz.ineanto.nicko.libs.anvilgui") relocate("com.github.jsixface", "xyz.ineanto.nicko.libs.yaml") relocate("me.clip", "xyz.ineanto.nicko.libs.placeholderapi") relocate("com.fasterxml.jackson", "xyz.ineanto.nicko.libs.jacksonpr") @@ -87,6 +89,7 @@ tasks { // MINIFY minimize { + exclude(dependency("xyz.xenondevs.invui:.*")) exclude(dependency("org.bstats:.*")) } diff --git a/src/main/java/com/comphenix/protocol/utility/LICENSE b/src/main/java/com/comphenix/protocol/utility/LICENSE deleted file mode 100644 index 2673e3f..0000000 --- a/src/main/java/com/comphenix/protocol/utility/LICENSE +++ /dev/null @@ -1,13 +0,0 @@ -Copyright (C) 2012 Kristian S. Stangeland - -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 2 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, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA -02111-1307 USA diff --git a/src/main/java/com/comphenix/protocol/utility/MinecraftVersion.java b/src/main/java/com/comphenix/protocol/utility/MinecraftVersion.java deleted file mode 100644 index 0f3cd7b..0000000 --- a/src/main/java/com/comphenix/protocol/utility/MinecraftVersion.java +++ /dev/null @@ -1,468 +0,0 @@ -/* - * ProtocolLib - Bukkit server library that allows access to the Minecraft protocol. - * Copyright (C) 2012 Kristian S. Stangeland - * - * 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 2 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, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA - * 02111-1307 USA - */ - -package com.comphenix.protocol.utility; - -import com.comphenix.protocol.ProtocolLibrary; -import com.google.common.collect.ComparisonChain; -import com.google.common.collect.Ordering; -import org.bukkit.Bukkit; -import org.bukkit.Server; - -import java.io.Serializable; -import java.text.SimpleDateFormat; -import java.util.Locale; -import java.util.Objects; -import java.util.regex.Matcher; -import java.util.regex.Pattern; - -/** - * Determine the current Minecraft version. - * - * @author Kristian - */ -public final class MinecraftVersion implements Comparable, Serializable { - /** - * Version 1.21.4 - the garden awakens drop - */ - public static final MinecraftVersion v1_21_4 = new MinecraftVersion("1.21.4"); - - /** - * Version 1.21.2 - the bundles of bravery drop - */ - public static final MinecraftVersion v1_21_2 = new MinecraftVersion("1.21.2"); - - /** - * Version 1.21.0 - the tricky trials update - */ - public static final MinecraftVersion v1_21_0 = new MinecraftVersion("1.21.0"); - - /** - * Version 1.20.5 - the cookie and transfer packet update - */ - public static final MinecraftVersion v1_20_5 = new MinecraftVersion("1.20.5"); - - /** - * Version 1.20.4 - the decorated pot update - */ - public static final MinecraftVersion v1_20_4 = new MinecraftVersion("1.20.4"); - - /** - * Version 1.20.2 - the update that added the configuration protocol phase. - */ - public static final MinecraftVersion CONFIG_PHASE_PROTOCOL_UPDATE = new MinecraftVersion("1.20.2"); - /** - * Version 1.20 - the trails and tails update - */ - public static final MinecraftVersion TRAILS_AND_TAILS = new MinecraftVersion("1.20"); - - /** - * Version 1.19.4 - the rest of the feature preview - */ - public static final MinecraftVersion FEATURE_PREVIEW_2 = new MinecraftVersion("1.19.4"); - - /** - * Version 1.19.3 - introducing feature preview - */ - public static final MinecraftVersion FEATURE_PREVIEW_UPDATE = new MinecraftVersion("1.19.3"); - /** - * Version 1.19 - the wild update - */ - public static final MinecraftVersion WILD_UPDATE = new MinecraftVersion("1.19"); - /** - * Version 1.18 - caves and cliffs part 2 - */ - public static final MinecraftVersion CAVES_CLIFFS_2 = new MinecraftVersion("1.18"); - /** - * Version 1.17 - caves and cliffs part 1 - */ - public static final MinecraftVersion CAVES_CLIFFS_1 = new MinecraftVersion("1.17"); - /** - * Version 1.16.4 - */ - public static final MinecraftVersion NETHER_UPDATE_4 = new MinecraftVersion("1.16.4"); - /** - * Version 1.16.2 - breaking change to the nether update - */ - public static final MinecraftVersion NETHER_UPDATE_2 = new MinecraftVersion("1.16.2"); - /** - * Version 1.16.0 - the nether update - */ - public static final MinecraftVersion NETHER_UPDATE = new MinecraftVersion("1.16"); - /** - * Version 1.15 - the bee update - */ - public static final MinecraftVersion BEE_UPDATE = new MinecraftVersion("1.15"); - /** - * Version 1.14 - village and pillage update. - */ - public static final MinecraftVersion VILLAGE_UPDATE = new MinecraftVersion("1.14"); - /** - * Version 1.13 - update aquatic. - */ - public static final MinecraftVersion AQUATIC_UPDATE = new MinecraftVersion("1.13"); - /** - * Version 1.12 - the world of color update. - */ - public static final MinecraftVersion COLOR_UPDATE = new MinecraftVersion("1.12"); - /** - * Version 1.11 - the exploration update. - */ - public static final MinecraftVersion EXPLORATION_UPDATE = new MinecraftVersion("1.11"); - /** - * Version 1.10 - the frostburn update. - */ - public static final MinecraftVersion FROSTBURN_UPDATE = new MinecraftVersion("1.10"); - /** - * Version 1.9 - the combat update. - */ - public static final MinecraftVersion COMBAT_UPDATE = new MinecraftVersion("1.9"); - /** - * Version 1.8 - the "bountiful" update. - */ - public static final MinecraftVersion BOUNTIFUL_UPDATE = new MinecraftVersion("1.8"); - /** - * Version 1.7.8 - the update that changed the skin format (and distribution - R.I.P. player disguise) - */ - public static final MinecraftVersion SKIN_UPDATE = new MinecraftVersion("1.7.8"); - /** - * Version 1.7.2 - the update that changed the world. - */ - public static final MinecraftVersion WORLD_UPDATE = new MinecraftVersion("1.7.2"); - /** - * Version 1.6.1 - the horse update. - */ - public static final MinecraftVersion HORSE_UPDATE = new MinecraftVersion("1.6.1"); - /** - * Version 1.5.0 - the redstone update. - */ - public static final MinecraftVersion REDSTONE_UPDATE = new MinecraftVersion("1.5.0"); - /** - * Version 1.4.2 - the scary update (Wither Boss). - */ - public static final MinecraftVersion SCARY_UPDATE = new MinecraftVersion("1.4.2"); - - /** - * The latest release version of minecraft. - */ - public static final MinecraftVersion LATEST = v1_21_4; - - // used when serializing - private static final long serialVersionUID = -8695133558996459770L; - - /** - * Regular expression used to parse version strings. - */ - private static final Pattern VERSION_PATTERN = Pattern.compile(".*\\(.*MC.\\s*([a-zA-z0-9\\-.]+).*"); - - /** - * The current version of minecraft, lazy initialized by MinecraftVersion.currentVersion() - */ - private static MinecraftVersion currentVersion; - - private final int major; - private final int minor; - private final int build; - // The development stage - private final String development; - - // Snapshot? - private final SnapshotVersion snapshot; - private volatile Boolean atCurrentOrAbove; - - /** - * Determine the current Minecraft version. - * - * @param server - the Bukkit server that will be used to examine the MC version. - */ - public MinecraftVersion(Server server) { - this(extractVersion(server.getVersion())); - } - - /** - * Construct a version object from the format major.minor.build, or the snapshot format. - * - * @param versionOnly - the version in text form. - */ - public MinecraftVersion(String versionOnly) { - this(versionOnly, true); - } - - /** - * Construct a version format from the standard release version or the snapshot verison. - * - * @param versionOnly - the version. - * @param parseSnapshot - TRUE to parse the snapshot, FALSE otherwise. - */ - private MinecraftVersion(String versionOnly, boolean parseSnapshot) { - String[] section = versionOnly.split("-"); - SnapshotVersion snapshot = null; - int[] numbers = new int[3]; - - try { - numbers = this.parseVersion(section[0]); - } catch (NumberFormatException cause) { - // Skip snapshot parsing - if (!parseSnapshot) { - throw cause; - } - - try { - // Determine if the snapshot is newer than the current release version - snapshot = new SnapshotVersion(section[0]); - SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd", Locale.US); - - MinecraftVersion latest = new MinecraftVersion(ProtocolLibrary.MAXIMUM_MINECRAFT_VERSION, false); - boolean newer = snapshot.getSnapshotDate().compareTo( - format.parse(ProtocolLibrary.MINECRAFT_LAST_RELEASE_DATE)) > 0; - - numbers[0] = latest.getMajor(); - numbers[1] = latest.getMinor() + (newer ? 1 : -1); - } catch (Exception e) { - throw new IllegalStateException("Cannot parse " + section[0], e); - } - } - - this.major = numbers[0]; - this.minor = numbers[1]; - this.build = numbers[2]; - this.development = section.length > 1 ? section[1] : (snapshot != null ? "snapshot" : null); - this.snapshot = snapshot; - } - - /** - * Construct a version object directly. - * - * @param major - major version number. - * @param minor - minor version number. - * @param build - build version number. - */ - public MinecraftVersion(int major, int minor, int build) { - this(major, minor, build, null); - } - - /** - * Construct a version object directly. - * - * @param major - major version number. - * @param minor - minor version number. - * @param build - build version number. - * @param development - development stage. - */ - public MinecraftVersion(int major, int minor, int build, String development) { - this.major = major; - this.minor = minor; - this.build = build; - this.development = development; - this.snapshot = null; - } - - /** - * Extract the Minecraft version from CraftBukkit itself. - * - * @param text - the server version in text form. - * @return The underlying MC version. - * @throws IllegalStateException If we could not parse the version string. - */ - public static String extractVersion(String text) { - Matcher version = VERSION_PATTERN.matcher(text); - - if (version.matches() && version.group(1) != null) { - return version.group(1); - } else { - throw new IllegalStateException("Cannot parse version String '" + text + "'"); - } - } - - /** - * Parse the given server version into a Minecraft version. - * - * @param serverVersion - the server version. - * @return The resulting Minecraft version. - */ - public static MinecraftVersion fromServerVersion(String serverVersion) { - return new MinecraftVersion(extractVersion(serverVersion)); - } - - public static MinecraftVersion getCurrentVersion() { - if (currentVersion == null) { - currentVersion = fromServerVersion(Bukkit.getVersion()); - } - - return currentVersion; - } - - public static void setCurrentVersion(MinecraftVersion version) { - currentVersion = version; - } - - private static boolean atOrAbove(MinecraftVersion version) { - return getCurrentVersion().isAtLeast(version); - } - - private int[] parseVersion(String version) { - String[] elements = version.split("\\."); - int[] numbers = new int[3]; - - // Make sure it's even a valid version - if (elements.length < 1) { - throw new IllegalStateException("Corrupt MC version: " + version); - } - - // The String 1 or 1.2 is interpreted as 1.0.0 and 1.2.0 respectively. - for (int i = 0; i < Math.min(numbers.length, elements.length); i++) { - numbers[i] = Integer.parseInt(elements[i].trim()); - } - return numbers; - } - - /** - * Major version number - * - * @return Current major version number. - */ - public int getMajor() { - return this.major; - } - - /** - * Minor version number - * - * @return Current minor version number. - */ - public int getMinor() { - return this.minor; - } - - /** - * Build version number - * - * @return Current build version number. - */ - public int getBuild() { - return this.build; - } - - /** - * Retrieve the development stage. - * - * @return Development stage, or NULL if this is a release. - */ - public String getDevelopmentStage() { - return this.development; - } - - /** - * Retrieve the snapshot version, or NULL if this is a release. - * - * @return The snapshot version. - */ - public SnapshotVersion getSnapshot() { - return this.snapshot; - } - - /** - * Determine if this version is a snapshot. - * - * @return The snapshot version. - */ - public boolean isSnapshot() { - return this.snapshot != null; - } - - /** - * Checks if this version is at or above the current version the server is running. - * - * @return true if this version is equal or newer than the server version, false otherwise. - */ - public boolean atOrAbove() { - if (this.atCurrentOrAbove == null) { - this.atCurrentOrAbove = atOrAbove(this); - } - - return this.atCurrentOrAbove; - } - - /** - * Retrieve the version String (major.minor.build) only. - * - * @return A normal version string. - */ - public String getVersion() { - if (this.getDevelopmentStage() == null) { - return String.format("%s.%s.%s", this.getMajor(), this.getMinor(), this.getBuild()); - } else { - return String.format("%s.%s.%s-%s%s", this.getMajor(), this.getMinor(), this.getBuild(), - this.getDevelopmentStage(), this.isSnapshot() ? this.snapshot : ""); - } - } - - @Override - public int compareTo(MinecraftVersion o) { - if (o == null) { - return 1; - } - - return ComparisonChain.start() - .compare(this.getMajor(), o.getMajor()) - .compare(this.getMinor(), o.getMinor()) - .compare(this.getBuild(), o.getBuild()) - .compare(this.getDevelopmentStage(), o.getDevelopmentStage(), Ordering.natural().nullsLast()) - .compare(this.getSnapshot(), o.getSnapshot(), Ordering.natural().nullsFirst()) - .result(); - } - - public boolean isAtLeast(MinecraftVersion other) { - if (other == null) { - return false; - } - - return this.compareTo(other) >= 0; - } - - @Override - public boolean equals(Object obj) { - if (obj == null) { - return false; - } - if (obj == this) { - return true; - } - - if (obj instanceof MinecraftVersion) { - MinecraftVersion other = (MinecraftVersion) obj; - - return this.getMajor() == other.getMajor() && - this.getMinor() == other.getMinor() && - this.getBuild() == other.getBuild() && - Objects.equals(this.getDevelopmentStage(), other.getDevelopmentStage()); - } - - return false; - } - - @Override - public int hashCode() { - return Objects.hash(this.getMajor(), this.getMinor(), this.getBuild()); - } - - @Override - public String toString() { - // Convert to a String that we can parse back again - return String.format("(MC: %s)", this.getVersion()); - } -} diff --git a/src/main/java/com/comphenix/protocol/utility/SnapshotVersion.java b/src/main/java/com/comphenix/protocol/utility/SnapshotVersion.java deleted file mode 100644 index dc4cdfd..0000000 --- a/src/main/java/com/comphenix/protocol/utility/SnapshotVersion.java +++ /dev/null @@ -1,150 +0,0 @@ -/* - * ProtocolLib - Bukkit server library that allows access to the Minecraft protocol. - * Copyright (C) 2012 Kristian S. Stangeland - * - * 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 2 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, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA - * 02111-1307 USA - */ - -package com.comphenix.protocol.utility; - -import com.google.common.collect.ComparisonChain; - -import java.io.Serial; -import java.io.Serializable; -import java.text.ParseException; -import java.text.SimpleDateFormat; -import java.util.Calendar; -import java.util.Date; -import java.util.Locale; -import java.util.Objects; -import java.util.regex.Matcher; -import java.util.regex.Pattern; - -/** - * Used to parse a snapshot version. - * - * @author Kristian - */ -public class SnapshotVersion implements Comparable, Serializable { - - @Serial - private static final long serialVersionUID = 2778655372579322310L; - private static final Pattern SNAPSHOT_PATTERN = Pattern.compile("(\\d{2}w\\d{2})([a-z])"); - - private final Date snapshotDate; - private final int snapshotWeekVersion; - - private transient String rawString; - - public SnapshotVersion(String version) { - Matcher matcher = SNAPSHOT_PATTERN.matcher(version.trim()); - - if (matcher.matches()) { - try { - this.snapshotDate = getDateFormat().parse(matcher.group(1)); - this.snapshotWeekVersion = matcher.group(2).charAt(0) - 'a'; - this.rawString = version; - } catch (ParseException e) { - throw new IllegalArgumentException("Date implied by snapshot version is invalid.", e); - } - } else { - throw new IllegalArgumentException("Cannot parse " + version + " as a snapshot version."); - } - } - - /** - * Retrieve the snapshot date parser. - *

- * We have to create a new instance of SimpleDateFormat every time as it is not thread safe. - * - * @return The date formatter. - */ - private static SimpleDateFormat getDateFormat() { - SimpleDateFormat format = new SimpleDateFormat("yy'w'ww", Locale.US); - format.setLenient(false); - return format; - } - - /** - * Retrieve the snapshot version within a week, starting at zero. - * - * @return The weekly version - */ - public int getSnapshotWeekVersion() { - return this.snapshotWeekVersion; - } - - /** - * Retrieve the week this snapshot was released. - * - * @return The week. - */ - public Date getSnapshotDate() { - return this.snapshotDate; - } - - /** - * Retrieve the raw snapshot string (yy'w'ww[a-z]). - * - * @return The snapshot string. - */ - public String getSnapshotString() { - if (this.rawString == null) { - // It's essential that we use the same locale - Calendar current = Calendar.getInstance(Locale.US); - current.setTime(this.snapshotDate); - this.rawString = String.format("%02dw%02d%s", - current.get(Calendar.YEAR) % 100, - current.get(Calendar.WEEK_OF_YEAR), - (char) ('a' + this.snapshotWeekVersion)); - } - return this.rawString; - } - - @Override - public int compareTo(SnapshotVersion o) { - if (o == null) { - return 1; - } - - return ComparisonChain.start() - .compare(this.snapshotDate, o.getSnapshotDate()) - .compare(this.snapshotWeekVersion, o.getSnapshotWeekVersion()) - .result(); - } - - @Override - public boolean equals(Object obj) { - if (obj == this) { - return true; - } - - if (obj instanceof SnapshotVersion) { - SnapshotVersion other = (SnapshotVersion) obj; - return Objects.equals(this.snapshotDate, other.getSnapshotDate()) - && this.snapshotWeekVersion == other.getSnapshotWeekVersion(); - } - - return false; - } - - @Override - public int hashCode() { - return Objects.hash(this.snapshotDate, this.snapshotWeekVersion); - } - - @Override - public String toString() { - return this.getSnapshotString(); - } -} diff --git a/src/main/java/net/wesjd/anvilgui/AnvilGUI.java b/src/main/java/net/wesjd/anvilgui/AnvilGUI.java deleted file mode 100644 index 6beaacc..0000000 --- a/src/main/java/net/wesjd/anvilgui/AnvilGUI.java +++ /dev/null @@ -1,889 +0,0 @@ -package net.wesjd.anvilgui; - -import net.md_5.bungee.api.chat.BaseComponent; -import net.wesjd.anvilgui.version.VersionWrapper; -import net.wesjd.anvilgui.version.VersionMatcher; -import org.apache.commons.lang.Validate; -import org.bukkit.Bukkit; -import org.bukkit.Material; -import org.bukkit.entity.Player; -import org.bukkit.event.EventHandler; -import org.bukkit.event.HandlerList; -import org.bukkit.event.Listener; -import org.bukkit.event.inventory.*; -import org.bukkit.inventory.Inventory; -import org.bukkit.inventory.ItemStack; -import org.bukkit.inventory.meta.ItemMeta; -import org.bukkit.plugin.Plugin; - -import java.util.*; -import java.util.concurrent.CompletableFuture; -import java.util.concurrent.Executor; -import java.util.function.BiConsumer; -import java.util.function.BiFunction; -import java.util.function.Consumer; -import java.util.function.Function; -import java.util.logging.Level; - -/** - * An anvil gui, used for gathering a user's input - * - * @author Wesley Smith - * @since 1.0 - */ -public class AnvilGUI { - - /** - * The local {@link VersionWrapper} object for the server's version - */ - private static final VersionWrapper WRAPPER = new VersionMatcher().match(); - - /** - * The variable containing an item with air. Used when the item would be null. - * To keep the heap clean, this object only gets iniziaised once - */ - private static final ItemStack AIR = new ItemStack(Material.AIR); - /** - * If the given ItemStack is null, return an air ItemStack, otherwise return the given ItemStack - * - * @param stack The ItemStack to check - * @return air or the given ItemStack - */ - private static ItemStack itemNotNull(ItemStack stack) { - return stack == null ? AIR : stack; - } - - /** - * The {@link Plugin} that this anvil GUI is associated with - */ - private final Plugin plugin; - /** - * The player who has the GUI open - */ - private final Player player; - /** - * An {@link Executor} that executes tasks on the main server thread - */ - private final Executor mainThreadExecutor; - /** - * The title of the anvil inventory - */ - private final Object titleComponent; - /** - * The initial contents of the inventory - */ - private final ItemStack[] initialContents; - /** - * A state that decides where the anvil GUI is able to get closed by the user - */ - private final boolean preventClose; - /** - * A state that decides whether compatibility with Geyser software is enabled - */ - private final boolean geyserCompatibility; - /** - * A set of slot numbers that are permitted to be interacted with by the user. An interactable - * slot is one that is able to be minipulated by the player, i.e. clicking and picking up an item, - * placing in a new one, etc. - */ - private final Set interactableSlots; - - /** An {@link Consumer} that is called when the anvil GUI is close */ - private final Consumer closeListener; - /** A flag that decides whether the async click handler can be run concurrently */ - private final boolean concurrentClickHandlerExecution; - /** An {@link BiFunction} that is called when a slot is clicked */ - private final ClickHandler clickHandler; - - /** - * The container id of the inventory, used for NMS methods - */ - private int containerId; - - /** - * The inventory that is used on the Bukkit side of things - */ - private Inventory inventory; - /** - * The listener holder class - */ - private final ListenUp listener = new ListenUp(); - - /** - * Represents the state of the inventory being open - */ - private boolean open; - - /** - * The actual container backing the Anvil GUI - */ - private VersionWrapper.AnvilContainerWrapper container; - - /** - * Create an AnvilGUI - * - * @param plugin A {@link org.bukkit.plugin.java.JavaPlugin} instance - * @param player The {@link Player} to open the inventory for - * @param mainThreadExecutor An {@link Executor} that executes on the main server thread - * @param titleComponent What to have the text already set to - * @param initialContents The initial contents of the inventory - * @param preventClose Whether to prevent the inventory from closing - * @param geyserCompatibility Whether to enable compatibility with Geyser software - * @param closeListener A {@link Consumer} when the inventory closes - * @param concurrentClickHandlerExecution Flag to allow concurrent execution of the click handler - * @param clickHandler A {@link ClickHandler} that is called when the player clicks a slot - */ - private AnvilGUI( - Plugin plugin, - Player player, - Executor mainThreadExecutor, - Object titleComponent, - ItemStack[] initialContents, - boolean preventClose, - boolean geyserCompatibility, - Set interactableSlots, - Consumer closeListener, - boolean concurrentClickHandlerExecution, - ClickHandler clickHandler) { - this.plugin = plugin; - this.player = player; - this.mainThreadExecutor = mainThreadExecutor; - this.titleComponent = titleComponent; - this.initialContents = initialContents; - this.preventClose = preventClose; - this.geyserCompatibility = geyserCompatibility; - this.interactableSlots = Collections.unmodifiableSet(interactableSlots); - this.closeListener = closeListener; - this.concurrentClickHandlerExecution = concurrentClickHandlerExecution; - this.clickHandler = clickHandler; - } - - /** - * Opens the anvil GUI - */ - private void openInventory() { - Bukkit.getPluginManager().registerEvents(listener, plugin); - - container = WRAPPER.newContainerAnvil(player, titleComponent); - - inventory = container.getBukkitInventory(); - // We need to use setItem instead of setContents because a Minecraft ContainerAnvil - // contains two separate inventories: the result inventory and the ingredients inventory. - // The setContents method only updates the ingredients inventory unfortunately, - // but setItem handles the index going into the result inventory. - for (int i = 0; i < initialContents.length; i++) { - inventory.setItem(i, initialContents[i]); - } - - containerId = WRAPPER.getNextContainerId(player, container); - WRAPPER.handleInventoryCloseEvent(player); - WRAPPER.sendPacketOpenWindow(player, containerId, titleComponent); - WRAPPER.setActiveContainer(player, container); - WRAPPER.setActiveContainerId(container, containerId); - WRAPPER.addActiveContainerSlotListener(container, player); - - open = true; - } - - /** - * Closes the inventory if it's open. - */ - public void closeInventory() { - closeInventory(true); - } - - /** - * Closes the inventory if it's open, only sending the close inventory packets if the arg is true - * - * @param sendClosePacket Whether to send the close inventory event, packet, etc - */ - private void closeInventory(boolean sendClosePacket) { - if (!open) { - return; - } - - open = false; - - HandlerList.unregisterAll(listener); - - if (sendClosePacket) { - WRAPPER.handleInventoryCloseEvent(player); - WRAPPER.setActiveContainerDefault(player); - WRAPPER.sendPacketCloseWindow(player, containerId); - } - - if (closeListener != null) { - closeListener.accept(StateSnapshot.fromAnvilGUI(this)); - } - } - - /** - * Updates the title of the AnvilGUI to the new one. - * - * @param literalTitle The title to use as literal text - * @param preserveRenameText Whether to preserve the entered rename text - * @throws IllegalArgumentException when literalTitle is null - * @see Builder#title(String) - */ - public void setTitle(String literalTitle, boolean preserveRenameText) { - Validate.notNull(literalTitle, "literalTitle cannot be null"); - setTitle(WRAPPER.literalChatComponent(literalTitle), preserveRenameText); - } - - /** - * Updates the title of the AnvilGUI to the new one. - * - * @param json The json used to parse into a rich chat component - * @param preserveRenameText Whether to preserve the entered rename text - * @throws IllegalArgumentException when json is null - * @see Builder#jsonTitle(String) - */ - public void setJsonTitle(String json, boolean preserveRenameText) { - Validate.notNull(json, "json cannot be null"); - setTitle(WRAPPER.jsonChatComponent(json), preserveRenameText); - } - - /** - * Updates the title of the AnvilGUI to the new one. - * - * @param title The title as a NMS ChatComponent - * @param preserveRenameText Whether to preserve the entered rename text - */ - private void setTitle(Object title, boolean preserveRenameText) { - if (!WRAPPER.isCustomTitleSupported()) { - return; - } - String renameText = container.getRenameText(); - WRAPPER.sendPacketOpenWindow(player, containerId, title); - if (preserveRenameText) { - // The renameText field is marked as @Nullable in newer versions - container.setRenameText(renameText == null ? "" : renameText); - } - } - - /** - * Returns the Bukkit inventory for this anvil gui - * - * @return the {@link Inventory} for this anvil gui - */ - public Inventory getInventory() { - return inventory; - } - - /** - * Simply holds the listeners for the GUI - */ - private class ListenUp implements Listener { - - /** - * Boolean storing the running status of the latest click handler to prevent double execution. - * All accesses to this boolean will be from the main server thread, except for the rare event - * that the plugin is disabled and the mainThreadExecutor throws an exception - */ - private boolean clickHandlerRunning = false; - - @EventHandler - public void onInventoryClick(InventoryClickEvent event) { - if (!event.getInventory().equals(inventory)) { - return; - } - - final int rawSlot = event.getRawSlot(); - // ignore items dropped outside the window - if (rawSlot == -999) return; - - final Player clicker = (Player) event.getWhoClicked(); - final Inventory clickedInventory = event.getClickedInventory(); - - if (clickedInventory != null) { - if (clickedInventory.equals(clicker.getInventory())) { - // prevent players from merging items from the anvil inventory - if (event.getClick().equals(ClickType.DOUBLE_CLICK)) { - event.setCancelled(true); - return; - } - // prevent shift moving items from players inv to the anvil inventory - if (event.isShiftClick()) { - event.setCancelled(true); - return; - } - } - // prevent players from swapping items in the anvil gui - if ((event.getCursor() != null && event.getCursor().getType() != Material.AIR) - && !interactableSlots.contains(rawSlot) - && event.getClickedInventory().equals(inventory)) { - event.setCancelled(true); - return; - } - } - - if (rawSlot < 3 && rawSlot >= 0 || event.getAction().equals(InventoryAction.MOVE_TO_OTHER_INVENTORY)) { - event.setCancelled(!interactableSlots.contains(rawSlot)); - if (clickHandlerRunning && !concurrentClickHandlerExecution) { - // A click handler is running, don't launch another one - return; - } - - final CompletableFuture> actionsFuture = - clickHandler.apply(rawSlot, StateSnapshot.fromAnvilGUI(AnvilGUI.this)); - - final Consumer> actionsConsumer = actions -> { - for (final ResponseAction action : actions) { - action.accept(AnvilGUI.this, clicker); - } - }; - - if (actionsFuture.isDone()) { - // Fast-path without scheduling if clickHandler is performed in sync - // Because the future is already completed, .join() will not block the server thread - actionsFuture.thenAccept(actionsConsumer).join(); - } else { - clickHandlerRunning = true; - // If the plugin is disabled and the Executor throws an exception, the exception will be passed to - // the .handle method - actionsFuture - .thenAcceptAsync(actionsConsumer, mainThreadExecutor) - .handle((results, exception) -> { - if (exception != null) { - plugin.getLogger() - .log( - Level.SEVERE, - "An exception occurred in the AnvilGUI clickHandler", - exception); - } - // Whether an exception occurred or not, set running to false - clickHandlerRunning = false; - return null; - }); - } - } - } - - @EventHandler - public void onInventoryDrag(InventoryDragEvent event) { - if (event.getInventory().equals(inventory)) { - for (int slot : Slot.values()) { - if (event.getRawSlots().contains(slot)) { - event.setCancelled(!interactableSlots.contains(slot)); - break; - } - } - } - } - - @EventHandler - public void onInventoryClose(InventoryCloseEvent event) { - if (open && event.getInventory().equals(inventory)) { - closeInventory(false); - if (preventClose) { - mainThreadExecutor.execute(AnvilGUI.this::openInventory); - } - } - } - } - - /** A builder class for an {@link AnvilGUI} object */ - public static class Builder { - - /** An {@link Executor} that executes tasks on the main server thread */ - private Executor mainThreadExecutor; - /** An {@link Consumer} that is called when the anvil GUI is close */ - private Consumer closeListener; - /** A flag that decides whether the async click handler can be run concurrently */ - private boolean concurrentClickHandlerExecution = false; - /** An {@link Function} that is called when a slot in the inventory has been clicked */ - private ClickHandler clickHandler; - /** A state that decides where the anvil GUI is able to be closed by the user */ - private boolean preventClose = false; - /** A state that determines whether support for Geyser software is enabled */ - private boolean geyserCompatibility = true; - /** A set of integers containing the slot numbers that should be modifiable by the user. */ - private Set interactableSlots = Collections.emptySet(); - /** The {@link Plugin} that this anvil GUI is associated with */ - private Plugin plugin; - /** The text that will be displayed to the user */ - private Object titleComponent = WRAPPER.literalChatComponent("Repair & Name"); - /** The starting text on the item */ - private String itemText; - /** An {@link ItemStack} to be put in the left input slot */ - private ItemStack itemLeft; - /** An {@link ItemStack} to be put in the right input slot */ - private ItemStack itemRight; - /** An {@link ItemStack} to be placed in the output slot */ - private ItemStack itemOutput; - - /** - * Set a custom main server thread executor. Useful for plugins targeting Folia. - * - * @param executor The executor to run tasks on - * @return The {@link Builder} instance - * @throws IllegalArgumentException when the executor is null - */ - public Builder mainThreadExecutor(Executor executor) { - Validate.notNull(executor, "Executor cannot be null"); - this.mainThreadExecutor = executor; - return this; - } - - /** - * Prevents the closing of the anvil GUI by the user - * - * @return The {@link Builder} instance - */ - public Builder preventClose() { - preventClose = true; - return this; - } - - /** - * Disables compatibility with Geyser software - */ - public Builder disableGeyserCompat() { - geyserCompatibility = false; - return this; - } - - /** - * Permit the user to modify (take items in and out) the slot numbers provided. - * - * @param slots A varags param for the slot numbers. You can avoid relying on magic constants by using - * the {@link AnvilGUI.Slot} class. - * @return The {@link Builder} instance - */ - public Builder interactableSlots(int... slots) { - final Set newValue = new HashSet<>(); - for (int slot : slots) { - newValue.add(slot); - } - interactableSlots = newValue; - return this; - } - - /** - * Listens for when the inventory is closed - * - * @param closeListener An {@link Consumer} that is called when the anvil GUI is closed - * @return The {@link Builder} instance - * @throws IllegalArgumentException when the closeListener is null - */ - public Builder onClose(Consumer closeListener) { - Validate.notNull(closeListener, "closeListener cannot be null"); - this.closeListener = closeListener; - return this; - } - - /** - * Do an action when a slot is clicked in the inventory - *

- * The ClickHandler is only called when the previous execution of the ClickHandler has finished. - * To alter this behaviour use {@link #allowConcurrentClickHandlerExecution()} - * - * @param clickHandler A {@link ClickHandler} that is called when the user clicks a slot. The - * {@link Integer} is the slot number corresponding to {@link Slot}, the - * {@link StateSnapshot} contains information about the current state of the anvil, - * and the response is a {@link CompletableFuture} that will eventually return a - * list of {@link ResponseAction} to execute in the order that they are supplied. - * @return The {@link Builder} instance - * @throws IllegalArgumentException when the function supplied is null - */ - public Builder onClickAsync(ClickHandler clickHandler) { - Validate.notNull(clickHandler, "click function cannot be null"); - this.clickHandler = clickHandler; - return this; - } - - /** - * By default, the {@link #onClickAsync(ClickHandler) async click handler} will not run concurrently - * and instead wait for the previous {@link CompletableFuture} to finish before executing it again. - *

- * If this trait is desired, it can be enabled by calling this method but may lead to inconsistent - * behaviour if not handled properly. - * - * @return The {@link Builder} instance - */ - public Builder allowConcurrentClickHandlerExecution() { - this.concurrentClickHandlerExecution = true; - return this; - } - - /** - * Do an action when a slot is clicked in the inventory - * - * @param clickHandler A {@link BiFunction} that is called when the user clicks a slot. The - * {@link Integer} is the slot number corresponding to {@link Slot}, the - * {@link StateSnapshot} contains information about the current state of the anvil, - * and the response is a list of {@link ResponseAction} to execute in the order - * that they are supplied. - * @return The {@link Builder} instance - * @throws IllegalArgumentException when the function supplied is null - */ - public Builder onClick(BiFunction> clickHandler) { - Validate.notNull(clickHandler, "click function cannot be null"); - this.clickHandler = - (slot, stateSnapshot) -> CompletableFuture.completedFuture(clickHandler.apply(slot, stateSnapshot)); - return this; - } - - /** - * Sets the plugin for the {@link AnvilGUI} - * - * @param plugin The {@link Plugin} this anvil GUI is associated with - * @return The {@link Builder} instance - * @throws IllegalArgumentException if the plugin is null - */ - public Builder plugin(Plugin plugin) { - Validate.notNull(plugin, "Plugin cannot be null"); - this.plugin = plugin; - return this; - } - - /** - * Sets the initial item-text that is displayed to the user. - *

- * If the usage of Adventure Components is desired, you must create an item, set the displayname of it - * and put it into the AnvilGUI via {@link #itemLeft(ItemStack)} manually. - * - * @param text The initial name of the item in the anvil - * @return The {@link Builder} instance - * @throws IllegalArgumentException if the text is null - */ - public Builder text(String text) { - Validate.notNull(text, "Text cannot be null"); - this.itemText = text; - return this; - } - - /** - * Sets the AnvilGUI title that is to be displayed to the user. - *
- * The provided title will be treated as literal text. - * - * @param title The title that is to be displayed to the user - * @return The {@link Builder} instance - * @throws IllegalArgumentException if the title is null - */ - public Builder title(String title) { - Validate.notNull(title, "title cannot be null"); - this.titleComponent = WRAPPER.literalChatComponent(title); - return this; - } - - /** - * Sets the AnvilGUI title that is to be displayed to the user. - *
- * The provided json will be parsed into rich chat components. - * - * @param json The title that is to be displayed to the user - * @return The {@link Builder} instance - * @throws IllegalArgumentException if the title is null - * @see net.md_5.bungee.chat.ComponentSerializer#toString(BaseComponent) - */ - public Builder jsonTitle(String json) { - Validate.notNull(json, "json cannot be null"); - this.titleComponent = WRAPPER.jsonChatComponent(json); - return this; - } - - /** - * Sets the {@link ItemStack} to be put in the first slot - * - * @param item The {@link ItemStack} to be put in the first slot - * @return The {@link Builder} instance - * @throws IllegalArgumentException if the {@link ItemStack} is null - */ - public Builder itemLeft(ItemStack item) { - Validate.notNull(item, "item cannot be null"); - this.itemLeft = item; - return this; - } - - /** - * Sets the {@link ItemStack} to be put in the second slot - * - * @param item The {@link ItemStack} to be put in the second slot - * @return The {@link Builder} instance - */ - public Builder itemRight(ItemStack item) { - this.itemRight = item; - return this; - } - - /** - * Sets the {@link ItemStack} to be put in the output slot - * - * @param item The {@link ItemStack} to be put in the output slot - * @return The {@link Builder} instance - */ - public Builder itemOutput(ItemStack item) { - this.itemOutput = item; - return this; - } - - /** - * Creates the anvil GUI and opens it for the player - * - * @param player The {@link Player} the anvil GUI should open for - * @return The {@link AnvilGUI} instance from this builder - * @throws IllegalArgumentException when the onClick function, plugin, or player is null - */ - public AnvilGUI open(Player player) { - Validate.notNull(plugin, "Plugin cannot be null"); - Validate.notNull(clickHandler, "click handler cannot be null"); - Validate.notNull(player, "Player cannot be null"); - - if (itemText != null) { - if (itemLeft == null) { - itemLeft = new ItemStack(Material.PAPER); - } - - ItemMeta paperMeta = itemLeft.getItemMeta(); - paperMeta.setDisplayName(itemText); - itemLeft.setItemMeta(paperMeta); - } - - // If no executor is specified, execute all tasks with the BukkitScheduler - if (mainThreadExecutor == null) { - mainThreadExecutor = task -> Bukkit.getScheduler().runTask(plugin, task); - } - - final AnvilGUI anvilGUI = new AnvilGUI( - plugin, - player, - mainThreadExecutor, - titleComponent, - new ItemStack[] {itemLeft, itemRight, itemOutput}, - preventClose, - geyserCompatibility, - interactableSlots, - closeListener, - concurrentClickHandlerExecution, - clickHandler); - anvilGUI.openInventory(); - return anvilGUI; - } - } - - /** - * A handler that is called when the user clicks a slot. The - * {@link Integer} is the slot number corresponding to {@link Slot}, the - * {@link StateSnapshot} contains information about the current state of the anvil, - * and the response is a {@link CompletableFuture} that will eventually return a - * list of {@link ResponseAction} to execute in the order that they are supplied. - */ - @FunctionalInterface - public interface ClickHandler extends BiFunction>> {} - - /** An action to run in response to a player clicking the output slot in the GUI. This interface is public - * and permits you, the developer, to add additional response features easily to your custom AnvilGUIs. */ - @FunctionalInterface - public interface ResponseAction extends BiConsumer { - - /** - * Replace the input text box value with the provided text value. - * - * Before using this method, it must be verified by the caller that items are either in - * {@link Slot#INPUT_LEFT} or {@link Slot#OUTPUT} present. - * - * @param text The text to write in the input box - * @return The {@link ResponseAction} to achieve the text replacement - * @throws IllegalArgumentException when the text is null - * @throws IllegalStateException when the slots {@link Slot#INPUT_LEFT} and {@link Slot#OUTPUT} are null - */ - static ResponseAction replaceInputText(String text) { - Validate.notNull(text, "text cannot be null"); - return (anvilgui, player) -> { - ItemStack item = anvilgui.getInventory().getItem(Slot.OUTPUT); - if (item == null) { - // Fallback on left input slot if player hasn't typed anything yet - item = anvilgui.getInventory().getItem(Slot.INPUT_LEFT); - } - if (item == null) { - throw new IllegalStateException( - "replaceInputText can only be used if slots OUTPUT or INPUT_LEFT are not empty"); - } - - final ItemStack cloned = item.clone(); - final ItemMeta meta = cloned.getItemMeta(); - meta.setDisplayName(text); - cloned.setItemMeta(meta); - anvilgui.getInventory().setItem(Slot.INPUT_LEFT, cloned); - }; - } - - /** - * Updates the title of the AnvilGUI to the new one. - * - * @param literalTitle The title to use as literal text - * @param preserveRenameText Whether to preserve the entered rename text - * @throws IllegalArgumentException when literalTitle is null - * @see Builder#title(String) - */ - static ResponseAction updateTitle(String literalTitle, boolean preserveRenameText) { - Validate.notNull(literalTitle, "literalTitle cannot be null"); - return (anvilGUI, player) -> anvilGUI.setTitle(literalTitle, preserveRenameText); - } - - /** - * Updates the title of the AnvilGUI to the new one. - * - * @param json The json used to parse into a rich chat component - * @param preserveRenameText Whether to preserve the entered rename text - * @throws IllegalArgumentException when json is null - * @see Builder#jsonTitle(String) - */ - static ResponseAction updateJsonTitle(String json, boolean preserveRenameText) { - Validate.notNull(json, "json cannot be null"); - return (anvilGUI, player) -> anvilGUI.setJsonTitle(json, preserveRenameText); - } - - /** - * Open another inventory - * @param otherInventory The inventory to open - * @return The {@link ResponseAction} to achieve the inventory open - * @throws IllegalArgumentException when the otherInventory is null - */ - static ResponseAction openInventory(Inventory otherInventory) { - Validate.notNull(otherInventory, "otherInventory cannot be null"); - return (anvilgui, player) -> player.openInventory(otherInventory); - } - - /** - * Close the AnvilGUI - * @return The {@link ResponseAction} to achieve closing the AnvilGUI - */ - static ResponseAction close() { - return (anvilgui, player) -> anvilgui.closeInventory(); - } - - /** - * Run the provided runnable - * @param runnable The runnable to run - * @return The {@link ResponseAction} to achieve running the runnable - * @throws IllegalArgumentException when the runnable is null - */ - static ResponseAction run(Runnable runnable) { - Validate.notNull(runnable, "runnable cannot be null"); - return (anvilgui, player) -> runnable.run(); - } - } - - /** - * Class wrapping the magic constants of slot numbers in an anvil GUI - */ - public static class Slot { - - private static final int[] values = new int[] {Slot.INPUT_LEFT, Slot.INPUT_RIGHT, Slot.OUTPUT}; - - /** - * The slot on the far left, where the first input is inserted. An {@link ItemStack} is always inserted - * here to be renamed - */ - public static final int INPUT_LEFT = 0; - /** - * Not used, but in a real anvil you are able to put the second item you want to combine here - */ - public static final int INPUT_RIGHT = 1; - /** - * The output slot, where an item is put when two items are combined from {@link #INPUT_LEFT} and - * {@link #INPUT_RIGHT} or {@link #INPUT_LEFT} is renamed - */ - public static final int OUTPUT = 2; - - /** - * Get all anvil slot values - * - * @return The array containing all possible anvil slots - */ - public static int[] values() { - return values; - } - } - - /** Represents a snapshot of the state of an AnvilGUI */ - public static final class StateSnapshot { - - /** - * Create an {@link StateSnapshot} from the current state of an {@link AnvilGUI} - * @param anvilGUI The instance to take the snapshot of - * @return The snapshot - */ - private static StateSnapshot fromAnvilGUI(AnvilGUI anvilGUI) { - final Inventory inventory = anvilGUI.getInventory(); - return new StateSnapshot( - itemNotNull(inventory.getItem(Slot.INPUT_LEFT)).clone(), - itemNotNull(inventory.getItem(Slot.INPUT_RIGHT)).clone(), - itemNotNull(inventory.getItem(Slot.OUTPUT)).clone(), - anvilGUI.player); - } - - /** - * The {@link ItemStack} in the anvilGui slots - */ - private final ItemStack leftItem, rightItem, outputItem; - - /** - * The {@link Player} that clicked the output slot - */ - private final Player player; - - /** - * The event parameter constructor - * @param leftItem The left item in the combine slot of the anvilGUI - * @param rightItem The right item in the combine slot of the anvilGUI - * @param outputItem The item that would have been outputted, when the items would have been combined - * @param player The player that clicked the output slot - */ - public StateSnapshot(ItemStack leftItem, ItemStack rightItem, ItemStack outputItem, Player player) { - this.leftItem = leftItem; - this.rightItem = rightItem; - this.outputItem = outputItem; - this.player = player; - } - - /** - * It returns the item in the left combine slot of the gui - * - * @return The leftItem - */ - public ItemStack getLeftItem() { - return leftItem; - } - - /** - * It returns the item in the right combine slot of the gui - * - * @return The rightItem - */ - public ItemStack getRightItem() { - return rightItem; - } - - /** - * It returns the output item that would have been the result - * by combining the left and right one - * - * @return The outputItem - */ - public ItemStack getOutputItem() { - return outputItem; - } - - /** - * It returns the player that clicked onto the output slot - * - * @return The player - */ - public Player getPlayer() { - return player; - } - - /** - * It returns the text the player typed into the rename field - * - * @return The text of the rename field - */ - public String getText() { - return outputItem.hasItemMeta() ? outputItem.getItemMeta().getDisplayName() : ""; - } - } -} diff --git a/src/main/java/net/wesjd/anvilgui/LICENSE b/src/main/java/net/wesjd/anvilgui/LICENSE deleted file mode 100644 index c735359..0000000 --- a/src/main/java/net/wesjd/anvilgui/LICENSE +++ /dev/null @@ -1,21 +0,0 @@ -MIT License - -Copyright (c) 2016 Wesley Smith - -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. \ No newline at end of file diff --git a/src/main/java/net/wesjd/anvilgui/version/PaperWrapper1_21_R4.java b/src/main/java/net/wesjd/anvilgui/version/PaperWrapper1_21_R4.java deleted file mode 100644 index 11bc655..0000000 --- a/src/main/java/net/wesjd/anvilgui/version/PaperWrapper1_21_R4.java +++ /dev/null @@ -1,135 +0,0 @@ -package net.wesjd.anvilgui.version; - -import net.minecraft.core.BlockPos; -import net.minecraft.core.RegistryAccess; -import net.minecraft.core.component.DataComponentPatch; -import net.minecraft.core.component.DataComponents; -import net.minecraft.network.chat.Component; -import net.minecraft.network.protocol.game.ClientboundContainerClosePacket; -import net.minecraft.network.protocol.game.ClientboundOpenScreenPacket; -import net.minecraft.network.protocol.game.ClientboundSetExperiencePacket; -import net.minecraft.server.level.ServerPlayer; -import net.minecraft.world.inventory.*; -import org.bukkit.craftbukkit.entity.CraftPlayer; -import org.bukkit.entity.Player; -import org.bukkit.inventory.Inventory; -import org.jetbrains.annotations.NotNull; - -public final class PaperWrapper1_21_R4 implements VersionWrapper { - private int getRealNextContainerId(Player player) { - return toNMS(player).nextContainerCounter(); - } - - - private ServerPlayer toNMS(Player player) { - return ((CraftPlayer) player).getHandle(); - } - - @Override - public int getNextContainerId(Player player, AnvilContainerWrapper container) { - return ((AnvilContainer) container).containerId; - } - - @Override - public void handleInventoryCloseEvent(Player player) { - //CraftEventFactory.handleInventoryCloseEvent(toNMS(player)); - toNMS(player).doCloseContainer(); // q -> doCloseContainer - } - - @Override - public void sendPacketOpenWindow(Player player, int containerId, Object inventoryTitle) { - toNMS(player).connection.send(new ClientboundOpenScreenPacket(containerId, MenuType.ANVIL, Component.literal(inventoryTitle.toString()))); - } - - @Override - public void sendPacketCloseWindow(Player player, int containerId) { - toNMS(player).connection.send(new ClientboundContainerClosePacket(containerId)); - } - - @Override - public void sendPacketExperienceChange(Player player, int experienceLevel) { - toNMS(player).connection.send(new ClientboundSetExperiencePacket(0f, 0, experienceLevel)); - } - - @Override - public void setActiveContainerDefault(Player player) { - toNMS(player).containerMenu = toNMS(player).inventoryMenu; // cd -> containerMenu, cc -> inventoryMenu - } - - @Override - public void setActiveContainer(Player player, AnvilContainerWrapper container) { - toNMS(player).containerMenu = (AbstractContainerMenu) container; - } - - @Override - public void setActiveContainerId(AnvilContainerWrapper container, int containerId) { } - - @Override - public void addActiveContainerSlotListener(AnvilContainerWrapper container, Player player) { - toNMS(player).initMenu((AbstractContainerMenu) container); - } - - @Override - public AnvilContainerWrapper newContainerAnvil(Player player, Object title) { - return new AnvilContainer(player, getRealNextContainerId(player), Component.literal(title.toString())); - } - - @Override - public Object literalChatComponent(String content) { - return Component.literal(content); // IChatBaseComponent.b -> Component.literal - } - - @Override - public Object jsonChatComponent(String json) { - return Component.Serializer.toJson(Component.literal(json), RegistryAccess.EMPTY); - } - - private static class AnvilContainer extends AnvilMenu implements AnvilContainerWrapper { - public AnvilContainer(Player player, int containerId, Component guiTitle) { - super( - containerId, - ((CraftPlayer) player).getHandle().getInventory(), - ContainerLevelAccess.create(((CraftPlayer) player).getHandle().level(), BlockPos.ZERO) - ); - this.checkReachable = false; - setTitle(guiTitle); - } - - @Override - public void createResult() { - // If the output is empty, copy the left input into the output - Slot output = getSlot(2); // b -> getSlot - if (!output.hasItem()) { // h -> hasItem - output.set(getSlot(0).getItem().copy()); // f -> set, g -> getItem, v -> copy - } - - this.cost.set(0); // y -> cost, a -> set - - // Sync to the client - this.sendAllDataToRemote(); // b -> sendAllDataToRemote - this.broadcastChanges(); // d -> broadcastChanges - } - - @Override - public boolean setItemName(@NotNull String itemName) { - Slot inputLeft = getSlot(0); - if (inputLeft.hasItem()) { - inputLeft - .getItem() - .applyComponents(DataComponentPatch - .builder() - .set(DataComponents.CUSTOM_NAME, Component.literal(itemName)) - .build() - ); - return true; - } - - return false; - } - - @Override - public Inventory getBukkitInventory() { - return this.getBukkitView().getTopInventory(); - } - } -} diff --git a/src/main/java/net/wesjd/anvilgui/version/VersionMatcher.java b/src/main/java/net/wesjd/anvilgui/version/VersionMatcher.java deleted file mode 100644 index c4f1bcc..0000000 --- a/src/main/java/net/wesjd/anvilgui/version/VersionMatcher.java +++ /dev/null @@ -1,51 +0,0 @@ -package net.wesjd.anvilgui.version; - -import org.bukkit.Bukkit; - -import java.util.HashMap; -import java.util.Map; - -/** - * Matches the server's NMS version to its {@link VersionWrapper} - * - * @author Wesley Smith - * @since 1.2.1 - */ -public class VersionMatcher { - /** Maps a Minecraft version string to the corresponding revision string */ - private static final Map VERSION_TO_REVISION = new HashMap() { - { - this.put("1.21.4", "1_21_R4"); - } - }; - /* This needs to be updated to reflect the newest available version wrapper */ - private static final String FALLBACK_REVISION = "1_21_R4"; - - /** - * Matches the server version to it's {@link VersionWrapper} - * - * @return The {@link VersionWrapper} for this server - * @throws IllegalStateException If the version wrapper failed to be instantiated or is unable to be found - */ - public VersionWrapper match() { - String craftBukkitPackage = Bukkit.getServer().getClass().getPackage().getName(); - - String rVersion; - if (!craftBukkitPackage.contains(".v")) { // cb package not relocated (i.e. paper 1.20.5+) - final String version = Bukkit.getBukkitVersion().split("-")[0]; - rVersion = VERSION_TO_REVISION.getOrDefault(version, FALLBACK_REVISION); - } else { - rVersion = craftBukkitPackage.split("\\.")[3].substring(1); - } - - try { - return (VersionWrapper) Class.forName(getClass().getPackage().getName() + ".PaperWrapper" + rVersion) - .getDeclaredConstructor() - .newInstance(); - } catch (ClassNotFoundException exception) { - throw new IllegalStateException("AnvilGUI does not support server version \"" + rVersion + "\"", exception); - } catch (ReflectiveOperationException exception) { - throw new IllegalStateException("Failed to instantiate version wrapper for version " + rVersion, exception); - } - } -} diff --git a/src/main/java/net/wesjd/anvilgui/version/VersionWrapper.java b/src/main/java/net/wesjd/anvilgui/version/VersionWrapper.java deleted file mode 100644 index 5efe2c5..0000000 --- a/src/main/java/net/wesjd/anvilgui/version/VersionWrapper.java +++ /dev/null @@ -1,150 +0,0 @@ -package net.wesjd.anvilgui.version; - -import org.bukkit.entity.Player; -import org.bukkit.inventory.Inventory; - -/** - * Wraps versions to be able to easily use different NMS server versions - * - * @author Wesley Smith - * @since 1.0 - */ -public interface VersionWrapper { - - /** - * Gets the next available NMS container id for the player - * - * @param player The player to get the next container id of - * @param container The container that a new id is being generated for - * @return The next available NMS container id - */ - int getNextContainerId(Player player, AnvilContainerWrapper container); - - /** - * Closes the current inventory for the player - * - * @param player The player that needs their current inventory closed - */ - void handleInventoryCloseEvent(Player player); - - /** - * Sends PacketPlayOutOpenWindow to the player with the container id and window title - * - * @param player The player to send the packet to - * @param containerId The container id to open - * @param inventoryTitle The title of the inventory to be opened (only works in Minecraft 1.14 and above) - */ - void sendPacketOpenWindow(Player player, int containerId, Object inventoryTitle); - - /** - * Sends PacketPlayOutCloseWindow to the player with the container id - * - * @param player The player to send the packet to - * @param containerId The container id to close - */ - void sendPacketCloseWindow(Player player, int containerId); - - /** - * Sends PacketPlayOutExperience to the player with the experience level - * - * @param player The player to send the packet to - * @param experienceLevel The experience level to set - */ - void sendPacketExperienceChange(Player player, int experienceLevel); - - /** - * Sets the NMS player's active container to the default one - * - * @param player The player to set the active container of - */ - void setActiveContainerDefault(Player player); - - /** - * Sets the NMS player's active container to the one supplied - * - * @param player The player to set the active container of - * @param container The container to set as active - */ - void setActiveContainer(Player player, AnvilContainerWrapper container); - - /** - * Sets the supplied windowId of the supplied Container - * - * @param container The container to set the windowId of - * @param containerId The new windowId - */ - void setActiveContainerId(AnvilContainerWrapper container, int containerId); - - /** - * Adds a slot listener to the supplied container for the player - * - * @param container The container to add the slot listener to - * @param player The player to have as a listener - */ - void addActiveContainerSlotListener(AnvilContainerWrapper container, Player player); - - /** - * Creates a new ContainerAnvil - * - * @param player The player to get the container of - * @param title The title of the anvil inventory - * @return The Container instance - */ - AnvilContainerWrapper newContainerAnvil(Player player, Object title); - - /** - * Checks if the current Minecraft version actually supports custom titles - * - * @return The current supported state - */ - default boolean isCustomTitleSupported() { - return true; - } - - /** - * Creates a new chat component that does not handle the content in any special way - * - * @param content The content to display - * @return Version-specific ChatComponent instance - */ - Object literalChatComponent(String content); - - /** - * Creates a new rich chat component from the provided json - * - * @param json The component to parse - * @return Version-specific ChatComponent instance - */ - Object jsonChatComponent(String json); - - /** - * Interface implemented by the custom NMS AnvilContainer used to interact with it directly - */ - interface AnvilContainerWrapper { - - /** - * Retrieves the raw text that has been entered into the Anvil at the moment - *

- * This field is marked as public in the Minecraft AnvilContainer only from Minecraft 1.11 and upwards - * - * @return The raw text in the rename field - */ - default String getRenameText() { - return null; - } - - /** - * Sets the provided text as the literal hovername of the item in the left input slot - * - * @param text The text to set - */ - default void setRenameText(String text) {} - - /** - * Gets the {@link Inventory} wrapper of the NMS container - * - * @return The inventory of the NMS container - */ - Inventory getBukkitInventory(); - } -} diff --git a/src/main/java/xyz/ineanto/nicko/gui/prompt/AnvilPrompt.java b/src/main/java/xyz/ineanto/nicko/gui/prompt/AnvilPrompt.java index 41a5f84..8238faa 100644 --- a/src/main/java/xyz/ineanto/nicko/gui/prompt/AnvilPrompt.java +++ b/src/main/java/xyz/ineanto/nicko/gui/prompt/AnvilPrompt.java @@ -15,6 +15,10 @@ import java.util.Collections; import java.util.Optional; import java.util.concurrent.atomic.AtomicReference; +/** + * This is currently unused, I'm waiting for AnvilGUI + * to be updated to compile with Paper mappings. + */ public class AnvilPrompt implements Prompt { private final Player player; private final PlayerLanguage playerLanguage;