Skip to content

Commit d1ac469

Browse files
authored
Implement dynamic remapping for InventoryView methods (#229)
2 parents 69645db + b56271b commit d1ac469

3 files changed

Lines changed: 109 additions & 1 deletion

File tree

gradle.properties

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,6 @@ org.jetbrains.dokka.experimental.gradle.pluginMode=V2Enabled
77
javaVersion=25
88
mcVersion=1.21.11
99
group=dev.slne.surf
10-
version=1.21.11-2.59.3
10+
version=1.21.11-2.59.4
1111
relocationPrefix=dev.slne.surf.surfapi.libs
1212
snapshot=false

surf-api-bukkit/surf-api-bukkit-server/src/main/kotlin/dev/slne/surf/surfapi/bukkit/server/inventory/framework/InventoryLoader.kt

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,10 @@ import dev.slne.surf.surfapi.bukkit.server.plugin
44
import me.devnatan.inventoryframework.ViewFrame
55

66
object InventoryLoader {
7+
init {
8+
InventoryViewRemapper.remap()
9+
}
10+
711
lateinit var viewFrame: ViewFrame
812

913
fun load() {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
package dev.slne.surf.surfapi.bukkit.server.inventory.framework
2+
3+
import net.bytebuddy.ByteBuddy
4+
import net.bytebuddy.asm.AsmVisitorWrapper
5+
import net.bytebuddy.description.field.FieldDescription
6+
import net.bytebuddy.description.field.FieldList
7+
import net.bytebuddy.description.method.MethodList
8+
import net.bytebuddy.description.type.TypeDescription
9+
import net.bytebuddy.dynamic.ClassFileLocator
10+
import net.bytebuddy.dynamic.loading.ClassLoadingStrategy
11+
import net.bytebuddy.implementation.Implementation
12+
import net.bytebuddy.jar.asm.ClassVisitor
13+
import net.bytebuddy.jar.asm.MethodVisitor
14+
import net.bytebuddy.jar.asm.Opcodes
15+
import net.bytebuddy.pool.TypePool
16+
import net.bytebuddy.utility.OpenedClassReader
17+
import org.bukkit.inventory.InventoryView
18+
19+
object InventoryViewRemapper {
20+
private const val INVENTORY_VIEW_INTERNAL = "org/bukkit/inventory/InventoryView"
21+
private const val INVENTORY_UPDATE_INTERNAL =
22+
"dev/slne/surf/surfapi/libs/devnatan/inventoryframework/runtime/thirdparty/InventoryUpdate"
23+
24+
fun isRemapNecessary(): Boolean {
25+
return try {
26+
InventoryView::class.java.isInterface
27+
} catch (_: Throwable) {
28+
false
29+
}
30+
}
31+
32+
fun remap() {
33+
if (!isRemapNecessary()) return
34+
35+
val locator = ClassFileLocator.ForClassLoader.of(javaClass.classLoader)
36+
val typePool = TypePool.Default.of(locator)
37+
val typeDescription = typePool.describe(INVENTORY_UPDATE_INTERNAL.replace("/", ".")).resolve()
38+
39+
ByteBuddy()
40+
.redefine<Any>(typeDescription, locator)
41+
.visit(InventoryViewClassVisitorWrapper())
42+
.make()
43+
.load(javaClass.classLoader, ClassLoadingStrategy.Default.INJECTION)
44+
}
45+
46+
private class InventoryViewClassVisitorWrapper : AsmVisitorWrapper {
47+
override fun mergeWriter(flags: Int): Int = flags
48+
override fun mergeReader(flags: Int): Int = flags
49+
50+
override fun wrap(
51+
instrumentedType: TypeDescription,
52+
classVisitor: ClassVisitor,
53+
implementationContext: Implementation.Context,
54+
typePool: TypePool,
55+
fields: FieldList<FieldDescription.InDefinedShape?>,
56+
methods: MethodList<*>,
57+
writerFlags: Int,
58+
readerFlags: Int
59+
): ClassVisitor {
60+
return InventoryViewClassVisitor(classVisitor)
61+
}
62+
}
63+
64+
private class InventoryViewClassVisitor(
65+
visitor: ClassVisitor
66+
) : ClassVisitor(OpenedClassReader.ASM_API, visitor) {
67+
68+
override fun visitMethod(
69+
access: Int,
70+
name: String,
71+
descriptor: String,
72+
signature: String?,
73+
exceptions: Array<String>?
74+
): MethodVisitor {
75+
val mv = super.visitMethod(access, name, descriptor, signature, exceptions)
76+
return InventoryViewMethodVisitor(mv)
77+
}
78+
}
79+
80+
private class InventoryViewMethodVisitor(
81+
visitor: MethodVisitor
82+
) : MethodVisitor(OpenedClassReader.ASM_API, visitor) {
83+
84+
override fun visitMethodInsn(
85+
opcode: Int,
86+
owner: String,
87+
name: String,
88+
descriptor: String,
89+
isInterface: Boolean
90+
) {
91+
if (opcode == Opcodes.INVOKEVIRTUAL && owner == INVENTORY_VIEW_INTERNAL) {
92+
super.visitMethodInsn(
93+
Opcodes.INVOKEINTERFACE,
94+
owner,
95+
name,
96+
descriptor,
97+
true
98+
)
99+
} else {
100+
super.visitMethodInsn(opcode, owner, name, descriptor, isInterface)
101+
}
102+
}
103+
}
104+
}

0 commit comments

Comments
 (0)