Skip to content

Implement dynamic remapping for InventoryView methods#229

Merged
ammodev merged 1 commit intoversion/1.21.11from
fix/inventory-framework
Mar 2, 2026
Merged

Implement dynamic remapping for InventoryView methods#229
ammodev merged 1 commit intoversion/1.21.11from
fix/inventory-framework

Conversation

@twisti-dev
Copy link
Contributor

This pull request introduces a new runtime class remapping mechanism to address compatibility issues with InventoryView in the Bukkit server inventory framework, and ensures this remapping is triggered during inventory framework initialization. It also bumps the project version.

InventoryView compatibility improvements:

  • Added a new InventoryViewRemapper object in InventoryViewRemapper.kt that uses ByteBuddy to dynamically remap method calls from INVOKEVIRTUAL to INVOKEINTERFACE for InventoryView, ensuring compatibility when InventoryView is an interface.
  • Updated InventoryLoader to invoke InventoryViewRemapper.remap() during initialization, so remapping occurs automatically when the inventory framework is loaded.

Project metadata:

  • Incremented the project version in gradle.properties from 1.21.11-2.59.3 to 1.21.11-2.59.4.

@twisti-dev twisti-dev self-assigned this Mar 2, 2026
Copilot AI review requested due to automatic review settings March 2, 2026 21:50
@ammodev ammodev merged commit d1ac469 into version/1.21.11 Mar 2, 2026
6 checks passed
@ammodev ammodev deleted the fix/inventory-framework branch March 2, 2026 21:51
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds a runtime ByteBuddy-based bytecode remapping hook to improve Bukkit InventoryView compatibility (when InventoryView is an interface at runtime) and wires it into the inventory framework initialization, alongside a patch version bump.

Changes:

  • Introduces InventoryViewRemapper to redefine a shaded InventoryFramework runtime class and rewrite INVOKEVIRTUALINVOKEINTERFACE calls targeting org.bukkit.inventory.InventoryView.
  • Triggers the remapping during InventoryLoader initialization.
  • Bumps project version to 1.21.11-2.59.4.

Reviewed changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated 3 comments.

File Description
surf-api-bukkit/surf-api-bukkit-server/src/main/kotlin/dev/slne/surf/surfapi/bukkit/server/inventory/framework/InventoryViewRemapper.kt Adds ByteBuddy/ASM-based runtime class redefinition to patch InventoryView callsites.
surf-api-bukkit/surf-api-bukkit-server/src/main/kotlin/dev/slne/surf/surfapi/bukkit/server/inventory/framework/InventoryLoader.kt Ensures remapping runs automatically as part of inventory framework setup.
gradle.properties Patch version bump.

Comment on lines +32 to +44
fun remap() {
if (!isRemapNecessary()) return

val locator = ClassFileLocator.ForClassLoader.of(javaClass.classLoader)
val typePool = TypePool.Default.of(locator)
val typeDescription = typePool.describe(INVENTORY_UPDATE_INTERNAL.replace("/", ".")).resolve()

ByteBuddy()
.redefine<Any>(typeDescription, locator)
.visit(InventoryViewClassVisitorWrapper())
.make()
.load(javaClass.classLoader, ClassLoadingStrategy.Default.INJECTION)
}
Copy link

Copilot AI Mar 2, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

remap() can throw (e.g., TypePool#resolve() if the class name changes / isn't present, or load(...) if injection fails). Because this runs during plugin startup, any uncaught exception here will fail inventory framework initialization (or even plugin load). Consider wrapping the resolve/redefine/load sequence in a try/catch and logging a warning/severe message so the plugin can continue (ideally with a clear indication that InventoryView compatibility patching failed).

Copilot uses AI. Check for mistakes.
Comment on lines +18 to +22

object InventoryViewRemapper {
private const val INVENTORY_VIEW_INTERNAL = "org/bukkit/inventory/InventoryView"
private const val INVENTORY_UPDATE_INTERNAL =
"dev/slne/surf/surfapi/libs/devnatan/inventoryframework/runtime/thirdparty/InventoryUpdate"
Copy link

Copilot AI Mar 2, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

INVENTORY_UPDATE_INTERNAL hard-codes the relocated internal class name, which is tightly coupled to the Shadow relocation configuration (relocationPrefix + relocate("me.devnatan.inventoryframework", ...)). This is easy to break if the relocation prefix or library package structure changes. Consider deriving this name from an actual type reference (if available) or centralizing the relocated prefix/string in one place so build config and runtime remapping can’t drift.

Suggested change
object InventoryViewRemapper {
private const val INVENTORY_VIEW_INTERNAL = "org/bukkit/inventory/InventoryView"
private const val INVENTORY_UPDATE_INTERNAL =
"dev/slne/surf/surfapi/libs/devnatan/inventoryframework/runtime/thirdparty/InventoryUpdate"
import dev.slne.surf.surfapi.libs.devnatan.inventoryframework.runtime.thirdparty.InventoryUpdate
object InventoryViewRemapper {
private val INVENTORY_VIEW_INTERNAL: String = InventoryView::class.java.name.replace('.', '/')
private val INVENTORY_UPDATE_INTERNAL: String =
InventoryUpdate::class.java.name.replace('.', '/')

Copilot uses AI. Check for mistakes.
Comment on lines 5 to +8

object InventoryLoader {
init {
InventoryViewRemapper.remap()
Copy link

Copilot AI Mar 2, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Calling InventoryViewRemapper.remap() in the object initializer means any exception during remapping will prevent InventoryLoader from initializing (and then break InventoryLoader.load() / enable() calls). Even if remap() is made more defensive, it’s safer to ensure failures here are contained (e.g., handle/log exceptions inside remap() or around this call) so inventory framework startup doesn’t hard-fail.

Suggested change
object InventoryLoader {
init {
InventoryViewRemapper.remap()
import java.util.logging.Level
object InventoryLoader {
init {
try {
InventoryViewRemapper.remap()
} catch (t: Throwable) {
plugin.logger.log(
Level.SEVERE,
"Failed to remap inventory views during InventoryLoader initialization. " +
"Inventory framework will continue to load, but some inventories may not behave as expected.",
t
)
}

Copilot uses AI. Check for mistakes.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants