Skip to content

Commit

Permalink
Fix ExprName InventoryView IncompatibleClassChangeError (#6874)
Browse files Browse the repository at this point in the history
  • Loading branch information
APickledWalrus authored Jul 10, 2024
1 parent b0f48b9 commit 94aa3e5
Show file tree
Hide file tree
Showing 4 changed files with 128 additions and 14 deletions.
111 changes: 111 additions & 0 deletions src/main/java/ch/njol/skript/bukkitutil/InventoryUtils.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
package ch.njol.skript.bukkitutil;

import ch.njol.skript.Skript;
import org.bukkit.inventory.Inventory;
import org.bukkit.inventory.InventoryView;
import org.eclipse.jdt.annotation.Nullable;

import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodType;

/**
* Utilities for inventories.
* In newer versions (1.21+), InventoryView is an interface instead of an abstract class
* Directing calling InventoryView#getTitle on 1.20.6 and below results in an IncompatibleClassChangeError
* as an interface, not an abstract class, is expected.
*/
public class InventoryUtils {

private static final @Nullable MethodHandle GET_TITLE;
private static final @Nullable MethodHandle GET_INVENTORY;
private static final @Nullable MethodHandle CONVERT_SLOT;
private static final @Nullable MethodHandle GET_TOP_INVENTORY;
private static final @Nullable MethodHandle GET_BOTTOM_INVENTORY;

static {
MethodHandle getTitle = null;
MethodHandle getInventory = null;
MethodHandle convertSlot = null;
MethodHandle getTopInventory = null;
MethodHandle getBottomInventory = null;
if (!InventoryView.class.isInterface()) { // initialize legacy support as it's likely an abstract class
try {
MethodHandles.Lookup lookup = MethodHandles.lookup();
getTitle = lookup.findVirtual(InventoryView.class, "getTitle", MethodType.methodType(String.class));
getInventory = lookup.findVirtual(InventoryView.class, "getInventory", MethodType.methodType(Inventory.class, int.class));
convertSlot = lookup.findVirtual(InventoryView.class, "convertSlot", MethodType.methodType(int.class, int.class));
getTopInventory = lookup.findVirtual(InventoryView.class, "getTopInventory", MethodType.methodType(Inventory.class));
getBottomInventory = lookup.findVirtual(InventoryView.class, "getBottomInventory", MethodType.methodType(Inventory.class));
} catch (NoSuchMethodException | IllegalAccessException e) {
Skript.exception(e, "Failed to load old inventory view support.");
}
}
GET_TITLE = getTitle;
GET_INVENTORY = getInventory;
CONVERT_SLOT = convertSlot;
GET_TOP_INVENTORY = getTopInventory;
GET_BOTTOM_INVENTORY = getBottomInventory;
}

/**
* @see InventoryView#getTitle()
*/
public static @Nullable String getTitle(InventoryView inventoryView) {
if (GET_TITLE == null)
return inventoryView.getTitle();
try {
return (String) GET_TITLE.invoke(inventoryView);
} catch (Throwable ignored) { }
return null;
}

/**
* @see InventoryView#getInventory(int)
*/
public static @Nullable Inventory getInventory(InventoryView inventoryView, int rawSlot) {
if (GET_INVENTORY == null)
return inventoryView.getInventory(rawSlot);
try {
return (Inventory) GET_INVENTORY.invoke(inventoryView, rawSlot);
} catch (Throwable ignored) { }
return null;
}

/**
* @see InventoryView#convertSlot(int)
*/
public static @Nullable Integer convertSlot(InventoryView inventoryView, int rawSlot) {
if (CONVERT_SLOT == null)
return inventoryView.convertSlot(rawSlot);
try {
return (Integer) CONVERT_SLOT.invoke(inventoryView, rawSlot);
} catch (Throwable ignored) { }
return null;
}

/**
* @see InventoryView#getTopInventory()
*/
public static @Nullable Inventory getTopInventory(InventoryView inventoryView) {
if (GET_TOP_INVENTORY == null)
return inventoryView.getTopInventory();
try {
return (Inventory) GET_TOP_INVENTORY.invoke(inventoryView);
} catch (Throwable ignored) { }
return null;
}

/**
* @see InventoryView#getBottomInventory()
*/
public static @Nullable Inventory getBottomInventory(InventoryView inventoryView) {
if (GET_BOTTOM_INVENTORY == null)
return inventoryView.getBottomInventory();
try {
return (Inventory) GET_BOTTOM_INVENTORY.invoke(inventoryView);
} catch (Throwable ignored) { }
return null;
}

}
13 changes: 8 additions & 5 deletions src/main/java/ch/njol/skript/classes/data/BukkitEventValues.java
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
import ch.njol.skript.Skript;
import ch.njol.skript.aliases.Aliases;
import ch.njol.skript.aliases.ItemType;
import ch.njol.skript.bukkitutil.InventoryUtils;
import ch.njol.skript.command.CommandEvent;
import ch.njol.skript.events.bukkit.ScriptEvent;
import ch.njol.skript.events.bukkit.SkriptStartEvent;
Expand Down Expand Up @@ -1252,15 +1253,15 @@ public Slot[] get(InventoryDragEvent event) {
List<Slot> slots = new ArrayList<>(event.getRawSlots().size());
InventoryView view = event.getView();
for (Integer rawSlot : event.getRawSlots()) {
Inventory inventory = view.getInventory(rawSlot);
int slot = view.convertSlot(rawSlot);
if (inventory == null)
Inventory inventory = InventoryUtils.getInventory(view, rawSlot);
Integer slot = InventoryUtils.convertSlot(view, rawSlot);
if (inventory == null || slot == null)
continue;
// Not all indices point to inventory slots. Equipment, for example
if (inventory instanceof PlayerInventory && slot >= 36) {
slots.add(new ch.njol.skript.util.slot.EquipmentSlot(((PlayerInventory) view.getBottomInventory()).getHolder(), slot));
} else {
slots.add(new InventorySlot(inventory, view.convertSlot(rawSlot)));
slots.add(new InventorySlot(inventory, slot));
}
}
return slots.toArray(new Slot[0]);
Expand All @@ -1280,7 +1281,9 @@ public Inventory[] get(InventoryDragEvent event) {
Set<Inventory> inventories = new HashSet<>();
InventoryView view = event.getView();
for (Integer rawSlot : event.getRawSlots()) {
inventories.add(view.getInventory(rawSlot));
Inventory inventory = InventoryUtils.getInventory(view, rawSlot);
if (inventory != null)
inventories.add(inventory);
}
return inventories.toArray(new Inventory[0]);
}
Expand Down
8 changes: 2 additions & 6 deletions src/main/java/ch/njol/skript/expressions/ExprName.java
Original file line number Diff line number Diff line change
Expand Up @@ -18,12 +18,10 @@
*/
package ch.njol.skript.expressions;

import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodType;
import java.util.ArrayList;
import java.util.List;

import ch.njol.skript.bukkitutil.InventoryUtils;
import ch.njol.skript.bukkitutil.ItemUtils;
import org.bukkit.Bukkit;
import org.bukkit.GameRule;
Expand All @@ -45,7 +43,6 @@
import org.eclipse.jdt.annotation.Nullable;

import ch.njol.skript.Skript;
import ch.njol.skript.aliases.Aliases;
import ch.njol.skript.aliases.ItemType;
import ch.njol.skript.classes.Changer.ChangeMode;
import ch.njol.skript.doc.Description;
Expand All @@ -61,7 +58,6 @@
import ch.njol.util.Kleenean;
import ch.njol.util.coll.CollectionUtils;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.TextComponent;
import net.kyori.adventure.text.serializer.bungeecord.BungeeComponentSerializer;
import net.md_5.bungee.api.ChatColor;
import net.md_5.bungee.api.chat.BaseComponent;
Expand Down Expand Up @@ -182,7 +178,7 @@ public String convert(Object object) {
Inventory inventory = (Inventory) object;
if (inventory.getViewers().isEmpty())
return null;
return inventory.getViewers().get(0).getOpenInventory().getTitle();
return InventoryUtils.getTitle(inventory.getViewers().get(0).getOpenInventory());
} else if (object instanceof Slot) {
ItemStack is = ((Slot) object).getItem();
if (is != null && is.hasItemMeta()) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,11 @@
*/
package ch.njol.skript.expressions;

import ch.njol.skript.bukkitutil.InventoryUtils;
import org.bukkit.entity.Player;
import org.bukkit.event.Event;
import org.bukkit.inventory.Inventory;
import org.bukkit.inventory.InventoryView;
import org.eclipse.jdt.annotation.Nullable;

import ch.njol.skript.Skript;
Expand Down Expand Up @@ -61,10 +63,12 @@ public boolean init(final Expression<?>[] exprs, final int matchedPattern, final
@Override
protected Inventory[] get(Event event, Player[] source) {
return get(source, new Getter<Inventory, Player>() {
@SuppressWarnings("null")
@Override
public Inventory get(final Player player) {
return player.getOpenInventory() != null ? player.getOpenInventory().getTopInventory() : null;
public @Nullable Inventory get(final Player player) {
InventoryView openInventory = player.getOpenInventory();
if (openInventory == null)
return null;
return InventoryUtils.getTopInventory(openInventory);
}
});
}
Expand Down

0 comments on commit 94aa3e5

Please sign in to comment.