Skip to content

Commit

Permalink
Abstract InventoryView API for backwards compatibility
Browse files Browse the repository at this point in the history
  • Loading branch information
fullwall committed Jun 26, 2024
1 parent 4c91a4e commit 65e19c6
Show file tree
Hide file tree
Showing 4 changed files with 115 additions and 22 deletions.
7 changes: 4 additions & 3 deletions src/main/java/net/citizensnpcs/api/NMSHelper.java
Original file line number Diff line number Diff line change
Expand Up @@ -4,17 +4,18 @@
import org.bukkit.command.BlockCommandSender;
import org.bukkit.entity.Player;
import org.bukkit.inventory.Inventory;
import org.bukkit.inventory.InventoryView;
import org.bukkit.inventory.meta.SkullMeta;

import net.citizensnpcs.api.util.SpigotUtil.InventoryViewAPI;

public interface NMSHelper {
public OfflinePlayer getPlayer(BlockCommandSender sender);

public String getTexture(SkullMeta meta);

InventoryView openAnvilInventory(Player player, Inventory inventory, String title);
InventoryViewAPI openAnvilInventory(Player player, Inventory inventory, String title);

public void setTexture(String string, SkullMeta meta);

void updateInventoryTitle(Player player, InventoryView view, String newTitle);
void updateInventoryTitle(Player player, InventoryViewAPI view, String newTitle);
}
25 changes: 13 additions & 12 deletions src/main/java/net/citizensnpcs/api/gui/InventoryMenu.java
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@

import net.citizensnpcs.api.CitizensAPI;
import net.citizensnpcs.api.util.Messaging;
import net.citizensnpcs.api.util.SpigotUtil.InventoryViewAPI;

// TODO: class-based injection? sub-inventory pages
/**
Expand Down Expand Up @@ -69,7 +70,7 @@ public class InventoryMenu implements Listener, Runnable {
private int pickupAmount = -1;
private final Deque<PageContext> stack = Queues.newArrayDeque();
private boolean transitioning;
private Collection<InventoryView> views = Lists.newArrayList();
private Collection<InventoryViewAPI> views = Lists.newArrayList();

public InventoryMenu(InventoryMenuInfo info, InventoryMenuPage instance) {
transition(info, instance, Maps.newHashMap());
Expand Down Expand Up @@ -98,7 +99,7 @@ public void close() {
HandlerList.unregisterAll(this);
closingViews = true;
runViewerModifier(() -> {
for (InventoryView view : views) {
for (InventoryViewAPI view : views) {
if (page != null) {
page.page.onClose(view.getPlayer());
}
Expand All @@ -115,9 +116,9 @@ public void close() {
public void close(HumanEntity entity) {
closingViews = true;
runViewerModifier(() -> {
Iterator<InventoryView> itr = views.iterator();
Iterator<InventoryViewAPI> itr = views.iterator();
while (itr.hasNext()) {
InventoryView view = itr.next();
InventoryViewAPI view = itr.next();
if (view.getPlayer() == entity) {
view.close();
itr.remove();
Expand Down Expand Up @@ -361,16 +362,16 @@ public void onInventoryDrag(InventoryDragEvent event) {
}
}

private InventoryView openInventory(HumanEntity player, Inventory inventory, String title) {
private InventoryViewAPI openInventory(HumanEntity player, Inventory inventory, String title) {
InventoryView view;
if (inventory.getType() == InventoryType.ANVIL) {
view = CitizensAPI.getNMSHelper().openAnvilInventory((Player) player, inventory, title);
view = CitizensAPI.getNMSHelper().openAnvilInventory((Player) player, inventory, title).getView();
} else {
view = player.openInventory(inventory);
}
if (view == null)
throw new RuntimeException("null inventory opened " + player + " " + inventory + " " + title);
return view;
return new InventoryViewAPI(view);
}

private InventoryMenuPattern parsePattern(int[] dim, List<InventoryMenuTransition> transitions,
Expand Down Expand Up @@ -565,7 +566,7 @@ public void transition(InventoryMenuPage instance, Map<String, Object> context)
public void transitionBack() {
if (page == null)
return;
for (InventoryView view : views) {
for (InventoryViewAPI view : views) {
page.page.onClose(view.getPlayer());
}
Map<String, Object> data = page.ctx.data();
Expand All @@ -588,9 +589,9 @@ private void transitionViewersToInventory(Inventory inventory) {
return;
transitioning = true;
runViewerModifier(() -> {
Collection<InventoryView> old = views;
Collection<InventoryViewAPI> old = views;
views = Lists.newArrayListWithExpectedSize(old.size());
for (InventoryView view : old) {
for (InventoryViewAPI view : old) {
view.close();
if (!view.getPlayer().isValid() || inventory == null)
continue;
Expand All @@ -602,8 +603,8 @@ private void transitionViewersToInventory(Inventory inventory) {
}

void updateTitle(String newTitle) {
for (InventoryView view : views) {
CitizensAPI.getNMSHelper().updateInventoryTitle((Player) view.getPlayer(), view, newTitle);
for (InventoryViewAPI view : views) {
CitizensAPI.getNMSHelper().updateInventoryTitle(view.getPlayer(), view, newTitle);
}
}

Expand Down
14 changes: 7 additions & 7 deletions src/main/java/net/citizensnpcs/api/trait/trait/Inventory.java
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@
import org.bukkit.event.Listener;
import org.bukkit.event.inventory.InventoryCloseEvent;
import org.bukkit.inventory.InventoryHolder;
import org.bukkit.inventory.InventoryView;
import org.bukkit.inventory.ItemStack;

import net.citizensnpcs.api.CitizensAPI;
Expand All @@ -26,6 +25,7 @@
import net.citizensnpcs.api.trait.trait.Equipment.EquipmentSlot;
import net.citizensnpcs.api.util.DataKey;
import net.citizensnpcs.api.util.ItemStorage;
import net.citizensnpcs.api.util.SpigotUtil.InventoryViewAPI;

/**
* Represents an NPC's inventory.
Expand All @@ -35,7 +35,7 @@ public class Inventory extends Trait {
private ItemStack[] contents;
private boolean registeredListener;
private org.bukkit.inventory.Inventory view;
private final Set<InventoryView> viewers = new HashSet<>();
private final Set<InventoryViewAPI> viewers = new HashSet<>();

public Inventory() {
super("inventory");
Expand Down Expand Up @@ -101,7 +101,7 @@ public void openInventory(Player sender) {
Bukkit.getPluginManager().registerEvents(new Listener() {
@EventHandler(ignoreCancelled = true)
public void inventoryCloseEvent(InventoryCloseEvent event) {
if (!viewers.contains(event.getView()))
if (!viewers.contains(new InventoryViewAPI(event.getView())))
return;
ItemStack[] contents = event.getInventory().getContents();
for (int i = 0; i < Inventory.this.contents.length; i++) {
Expand All @@ -126,7 +126,7 @@ public void inventoryCloseEvent(InventoryCloseEvent event) {
.setContents(Arrays.copyOf(contents, maxSize));
}
}
viewers.remove(event.getView());
viewers.remove(new InventoryViewAPI(event.getView()));
}
}, CitizensAPI.getPlugin());
}
Expand All @@ -136,7 +136,7 @@ public void inventoryCloseEvent(InventoryCloseEvent event) {

view.setItem(i, contents[i]);
}
viewers.add(sender.openInventory(view));
viewers.add(new InventoryViewAPI(sender.openInventory(view)));
}

private ItemStack[] parseContents(DataKey key) throws NPCLoadException {
Expand All @@ -151,9 +151,9 @@ private ItemStack[] parseContents(DataKey key) throws NPCLoadException {
public void run() {
if (viewers.isEmpty())
return;
Iterator<InventoryView> itr = viewers.iterator();
Iterator<InventoryViewAPI> itr = viewers.iterator();
while (itr.hasNext()) {
InventoryView iview = itr.next();
InventoryViewAPI iview = itr.next();
if (!iview.getPlayer().isValid()) {
iview.close();
itr.remove();
Expand Down
91 changes: 91 additions & 0 deletions src/main/java/net/citizensnpcs/api/util/SpigotUtil.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
package net.citizensnpcs.api.util;

import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.reflect.Method;
import java.time.Duration;
import java.time.temporal.ChronoUnit;
import java.util.concurrent.TimeUnit;
Expand All @@ -9,11 +12,99 @@
import org.bukkit.World;
import org.bukkit.enchantments.Enchantment;
import org.bukkit.entity.EntityType;
import org.bukkit.entity.Player;
import org.bukkit.inventory.Inventory;
import org.bukkit.inventory.InventoryView;

import com.google.common.primitives.Ints;
import com.google.common.primitives.Longs;

public class SpigotUtil {
/**
* Spigot has changed InventoryViews to be an abstract class instead of an interface necessitating an abstraction
* over the existing API to avoid java runtime errors. This class is subject to change as required.
*/
public static class InventoryViewAPI {
private final InventoryView view;

public InventoryViewAPI(InventoryView view) {
this.view = view;
}

public void close() {
try {
CLOSE.invoke(view);
} catch (Throwable e) {
e.printStackTrace();
}
}

@Override
public boolean equals(Object obj) {
if (this == obj || obj == null) {
return true;
}
if (getClass() != obj.getClass()) {
return false;
}
InventoryViewAPI other = (InventoryViewAPI) obj;
if (view == null) {
if (other.view != null) {
return false;
}
} else if (!view.equals(other.view)) {
return false;
}
return true;
}

public Player getPlayer() {
try {
return (Player) GET_PLAYER.invoke(view);
} catch (Throwable e) {
e.printStackTrace();
return null;
}
}

public Inventory getTopInventory() {
try {
return (Inventory) TOP_INVENTORY.invoke(view);
} catch (Throwable e) {
e.printStackTrace();
return null;
}
}

public InventoryView getView() {
return view;
}

@Override
public int hashCode() {
return 31 + ((view == null) ? 0 : view.hashCode());
}

private static MethodHandle getMethod(Class<?> clazz, String method, Class<?>... params) {
try {
Method f = null;
try {
f = clazz.getMethod(method, params);
} catch (Exception e) {
e.printStackTrace();
}
return MethodHandles.publicLookup().unreflect(f);
} catch (Exception e) {
e.printStackTrace();
return null;
}
}

private static final MethodHandle CLOSE = getMethod(InventoryView.class, "close");
private static final MethodHandle GET_PLAYER = getMethod(InventoryView.class, "getPlayer");
private static final MethodHandle TOP_INVENTORY = getMethod(InventoryView.class, "getTopInventory");
}

private static interface ThrowingConsumer<T> {
void accept(T t) throws ClassNotFoundException;
}
Expand Down

0 comments on commit 65e19c6

Please sign in to comment.