Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 12 additions & 7 deletions gradle/libs.versions.toml
Original file line number Diff line number Diff line change
@@ -1,17 +1,22 @@
[versions]
jetbrains-annotations = "26.0.2"
# Do not update Spigot/Paper to 1.21, InventoryView changed from class to interface type
# Dynamic Title Update feature throws IncompatibleClassChangeError due to that
# Feel free to implement a workaround on InventoryUpdate.java if u need to use a 1.21+ specific API
spigot = "1.20.6-R0.1-SNAPSHOT"
paperSpigot = "1.20.6-R0.1-SNAPSHOT"
# InventoryView changed from a class to an interface type in 1.21. That used to throw
# IncompatibleClassChangeError on the Dynamic Title Update feature when compiled against an
# older (class-based) InventoryView while running on a newer (interface-based) runtime.
# This is now handled by McVersion#isModern() branching in InventoryUpdate.java, ReflectionUtils.java
# and AnvilInputNMS.java, and by compiling inventory-framework-platform-bukkit/-paper against a
# matching modern Spigot/Paper artifact (see the versions below) instead of the old pinned 1.20.6 one.
# inventory-framework-platform-paper also requires a Java 25 toolchain, since Paper's own published
# artifacts for this version declare that requirement (see its build.gradle.kts).
spigot = "26.1-R0.1-SNAPSHOT"
paperSpigot = "26.1.2.build.+"
junit = "6.0.0"
mockito = "4.11.0"
adventure-api = "4.25.0"
kotlin = "1.9.20"
kotlin = "2.3.21"
plugin-shadowjar = "8.3.8"
plugin-spotless = "7.2.1"
plugin-bukkit = "0.7.1"
plugin-bukkit = "0.8.0"
minestom = "b39badc77b"
folialib = "0.5.1"

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,31 +49,53 @@ class AnvilInputNMS {

static {
try {
final boolean modern = McVersion.isModern();

ANVIL = Objects.requireNonNull(
getNMSClass("world.inventory", "ContainerAnvil"), "ContainerAnvil NMS class not found");
getNMSClass("world.inventory", modern ? "AnvilMenu" : "ContainerAnvil"),
"AnvilMenu/ContainerAnvil NMS class not found");

final Class<?> playerInventoryClass = getNMSClass("world.entity.player", "PlayerInventory");
final Class<?> playerInventoryClass =
getNMSClass("world.entity.player", modern ? "Inventory" : "PlayerInventory");

ANVIL_CONSTRUCTOR = getConstructor(ANVIL, int.class, playerInventoryClass);
CONTAINER_CHECK_REACHABLE = setFieldHandle(CONTAINER, boolean.class, "checkReachable");

final Class<?> containerPlayer = getNMSClass("world.inventory", "ContainerPlayer");
final Class<?> containerPlayer =
getNMSClass("world.inventory", modern ? "InventoryMenu" : "ContainerPlayer");
PLAYER_DEFAULT_CONTAINER = getField(ENTITY_PLAYER, containerPlayer, "inventoryMenu", "bQ", "bR");

final String activeContainerObfuscatedName = ReflectionUtils.supportsMC1202() ? "bS" : "bR";
final String activeContainerFieldName =
modern ? "containerMenu" : (ReflectionUtils.supportsMC1202() ? "bS" : "bR");
SET_PLAYER_ACTIVE_CONTAINER = setField(
ENTITY_PLAYER, containerPlayer, "activeContainer", "containerMenu", activeContainerObfuscatedName);
ENTITY_PLAYER,
containerPlayer,
modern ? "containerMenu" : "activeContainer",
"containerMenu",
activeContainerFieldName);

GET_PLAYER_NEXT_CONTAINER_COUNTER =
getMethod(ENTITY_PLAYER, "nextContainerCounter", MethodType.methodType(int.class));

GET_PLAYER_INVENTORY = getMethod(
ENTITY_PLAYER, "fN", MethodType.methodType(playerInventoryClass), false, "fR", "getInventory");

CONTAINER_WINDOW_ID = setField(CONTAINER, int.class, "windowId", "containerId", "j");
GET_PLAYER_INVENTORY = modern
? getMethod(ENTITY_PLAYER, "getInventory", MethodType.methodType(playerInventoryClass))
: getMethod(
ENTITY_PLAYER,
"fN",
MethodType.methodType(playerInventoryClass),
false,
"fR",
"getInventory");

CONTAINER_WINDOW_ID =
setField(CONTAINER, int.class, modern ? "containerId" : "windowId", "containerId", "j");

final Class<?> slotListenerClass =
getNMSClass("world.inventory", modern ? "ContainerListener" : "ICrafting");
ADD_CONTAINER_SLOT_LISTENER = getMethod(
CONTAINER, "a", MethodType.methodType(void.class, getNMSClass("world.inventory.ICrafting")));
INIT_MENU = getMethod(ENTITY_PLAYER, "a", MethodType.methodType(void.class, CONTAINER));
CONTAINER, modern ? "addSlotListener" : "a", MethodType.methodType(void.class, slotListenerClass));
INIT_MENU =
getMethod(ENTITY_PLAYER, modern ? "initMenu" : "a", MethodType.methodType(void.class, CONTAINER));

GET_TOP_INVENTORY =
getMethod(InventoryView.class, "getTopInventory", MethodType.methodType(Inventory.class));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -71,22 +71,40 @@ public final class InventoryUpdate {
public static final Object[] DUMMY_COLOR_MODIFIERS = new Object[0];

static {
final boolean modern = McVersion.isModern();

// Initialize classes.
CHAT_MESSAGE = SUPPORTS_19 ? null : ReflectionUtils.getNMSClass("network.chat", "ChatMessage");
PACKET_PLAY_OUT_OPEN_WINDOW = ReflectionUtils.getNMSClass("network.protocol.game", "PacketPlayOutOpenWindow");
I_CHAT_BASE_COMPONENT = ReflectionUtils.getNMSClass("network.chat", "IChatBaseComponent");
PACKET_PLAY_OUT_OPEN_WINDOW = modern
? ReflectionUtils.getNMSClass("network.protocol.game", "ClientboundOpenScreenPacket")
: ReflectionUtils.getNMSClass("network.protocol.game", "PacketPlayOutOpenWindow");
I_CHAT_BASE_COMPONENT = modern
? ReflectionUtils.getNMSClass("network.chat", "Component")
: ReflectionUtils.getNMSClass("network.chat", "IChatBaseComponent");
CONTAINER = modern
? ReflectionUtils.getNMSClass("world.inventory", "AbstractContainerMenu")
: ReflectionUtils.getNMSClass("world.inventory", "Container");
// Check if we use containers, otherwise, can throw errors on older versions.
CONTAINERS = useContainers() ? ReflectionUtils.getNMSClass("world.inventory", "Containers") : null;
CONTAINER = ReflectionUtils.getNMSClass("world.inventory", "Container");
I_CHAT_MUTABLE_COMPONENT =
SUPPORTS_19 ? ReflectionUtils.getNMSClass("network.chat", "IChatMutableComponent") : null;
CONTAINERS = useContainers()
? (modern
? ReflectionUtils.getNMSClass("world.inventory", "MenuType")
: ReflectionUtils.getNMSClass("world.inventory", "Containers"))
: null;
I_CHAT_MUTABLE_COMPONENT = SUPPORTS_19
? (modern
? ReflectionUtils.getNMSClass("network.chat", "MutableComponent")
: ReflectionUtils.getNMSClass("network.chat", "IChatMutableComponent"))
: null;
MINECRAFT_MENU_TYPE = getClassOrNull("net.minecraft.world.inventory.MenuType");

// Initialize methods.
getBukkitView = getMethod(CONTAINER, "getBukkitView", MethodType.methodType(InventoryView.class));
literal = SUPPORTS_19
? getMethod(
I_CHAT_BASE_COMPONENT, "b", MethodType.methodType(I_CHAT_MUTABLE_COMPONENT, String.class), true)
I_CHAT_BASE_COMPONENT,
modern ? "literal" : "b",
MethodType.methodType(I_CHAT_MUTABLE_COMPONENT, String.class),
true)
: null;

final Class<?> paperAdventure = getClassOrNull("io.papermc.paper.adventure.PaperAdventure");
Expand All @@ -109,9 +127,10 @@ public final class InventoryUpdate {
getConstructor(PACKET_PLAY_OUT_OPEN_WINDOW, int.class, String.class, I_CHAT_BASE_COMPONENT, int.class);

// Initialize fields.
activeContainer =
getField(ENTITY_PLAYER, CONTAINER, "activeContainer", "bR", "bV", "bW", "bU", "bP", "containerMenu");
windowId = getField(CONTAINER, int.class, "windowId", "j", "containerId");
activeContainer = modern
? getField(ENTITY_PLAYER, CONTAINER, "containerMenu")
: getField(ENTITY_PLAYER, CONTAINER, "activeContainer", "bR", "bV", "bW", "bU", "bP", "containerMenu");
windowId = getField(CONTAINER, int.class, modern ? "containerId" : "windowId", "j", "containerId");
}

private static Class<?> getClassOrNull(String className) {
Expand Down Expand Up @@ -177,7 +196,12 @@ public static void updateInventory(Player player, Object newTitle) {
if (container == null) return;

// If the container was added in a newer version than the current, return.
if (container.getContainerVersion() > ReflectionUtils.MINOR_NUMBER && useContainers()) {
// Only applies on 1.x; on the year-based scheme (26+) MINOR_NUMBER is the release's own
// minor (e.g. 1 for 26.1.x) and is unrelated to the container-added-in-1.x versions below,
// so all containers are considered valid there.
if (McVersion.current().getMajor() == 1
&& container.getContainerVersion() > ReflectionUtils.MINOR_NUMBER
&& useContainers()) {
return;
}

Expand Down Expand Up @@ -344,7 +368,7 @@ public static MethodHandle getMethod(
* @return whether to use containers.
*/
public static boolean useContainers() {
return ReflectionUtils.MINOR_NUMBER > 13;
return McVersion.isModern() || ReflectionUtils.MINOR_NUMBER > 13;
}

/**
Expand Down Expand Up @@ -426,6 +450,8 @@ public Object getObject() {
String name = (version == 14 && this == CARTOGRAPHY_TABLE) ? "CARTOGRAPHY" : name();
// Since 1.17, containers go from "a" to "x".
if (version > 16 && version <= 20) name = String.valueOf(alphabet[ordinal()]);
// Modern (26+): MenuType fields use lowercase 'x', e.g. GENERIC_9x3.
if (McVersion.isModern()) name = name.replace("X", "x");

Field field = CONTAINERS.getField(name);
return field.get(null);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -143,4 +143,13 @@ public static boolean supports(int minorNumber) {
public static boolean supports(int minorNumber, int patchNumber) {
return CURRENT_VERSION.isAtLeast(1, minorNumber, patchNumber);
}

/**
* Whether the server is running a "modern" Mojang-mapped NMS naming scheme (1.17+, including
* the year-based versioning scheme introduced afterwards, e.g. 26.x), as opposed to the legacy
* obfuscated/versioned CraftBukkit naming scheme used prior to 1.17.
*/
public static boolean isModern() {
return CURRENT_VERSION.getMajor() > 1 || CURRENT_VERSION.getMinor() >= 17;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -163,31 +163,36 @@ public static String getVersionInformation() {

/**
* The class for the NMS EntityPlayer.
*
* @see net.minecraft.server.level.ServerPlayer
*/
public static final Class<?> ENTITY_PLAYER;

static {
ENTITY_PLAYER = getNMSClass("server.level", "EntityPlayer");
final boolean modern = McVersion.isModern();
ENTITY_PLAYER = getNMSClass("server.level", modern ? "ServerPlayer" : "EntityPlayer");
CRAFT_PLAYER = getCraftClass("entity.CraftPlayer");
Class<?> playerConnection = getNMSClass("server.network", "PlayerConnection");
Class<?> playerConnection =
getNMSClass("server.network", modern ? "ServerGamePacketListenerImpl" : "PlayerConnection");

MethodHandles.Lookup lookup = MethodHandles.lookup();
MethodHandle sendPacket = null, getHandle = null, connection = null;

try {
connection = lookup.findGetter(
ENTITY_PLAYER,
v(21, 3, "f").v(20, "c").v(17, "b").v(21, 6, "g").orElse("playerConnection"),
playerConnection);
String connectionFieldName = modern
? "connection"
: v(21, 3, "f").v(20, "c").v(17, "b").v(21, 6, "g").orElse("playerConnection");
connection = lookup.findGetter(ENTITY_PLAYER, connectionFieldName, playerConnection);

getHandle = lookup.findVirtual(CRAFT_PLAYER, "getHandle", MethodType.methodType(ENTITY_PLAYER));

final VersionHandler<String> methodVersion =
v(21, "send").v(20, "b").v(18, "a");

String sendMethodName = modern ? "send" : methodVersion.orElse("sendPacket");
sendPacket = lookup.findVirtual(
playerConnection,
methodVersion.orElse("sendPacket"),
sendMethodName,
MethodType.methodType(void.class, getNMSClass("network.protocol", "Packet")));
} catch (NoSuchMethodException | NoSuchFieldException | IllegalAccessException ex) {
ex.printStackTrace();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,8 @@ class GlobalClickInterceptor : PipelineInterceptor<VirtualView> {

// inherit cancellation so we can un-cancel it
subject.isCancelled =
event.isCancelled || subject.config.isOptionSet(ViewConfig.CANCEL_ON_CLICK, true)
event.isCancelled ||
subject.config.isOptionSet(ViewConfig.CANCEL_ON_CLICK, true)
subject.root.onClick(subject)
}
}
6 changes: 5 additions & 1 deletion inventory-framework-platform-paper/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,11 @@ dependencies {
}

java {
targetCompatibility = JavaVersion.VERSION_21
sourceCompatibility = JavaVersion.VERSION_25
targetCompatibility = JavaVersion.VERSION_25
toolchain {
languageVersion.set(JavaLanguageVersion.of(25))
}
}

tasks.shadowJar {
Expand Down
5 changes: 5 additions & 0 deletions settings.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,12 @@ pluginManagement {
includeBuild("build-logic")
}

plugins {
id("org.gradle.toolchains.foojay-resolver-convention") version "1.0.0"
}

dependencyResolutionManagement {
repositoriesMode.set(RepositoriesMode.PREFER_SETTINGS)
repositories {
mavenCentral()
maven("https://jitpack.io")
Expand Down
Loading