From 4d42cf1c6dbacc364172ef5c4b66644af68b1fe1 Mon Sep 17 00:00:00 2001 From: Jie-150 <1503745098@qq.com> Date: Tue, 14 Jul 2026 02:12:15 +0800 Subject: [PATCH 1/2] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=20Mojang=20=E8=BF=90?= =?UTF-8?q?=E8=A1=8C=E6=97=B6=E7=B1=BB=E5=90=8D=E8=BD=AC=E8=AF=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../module/nms/remap/RemapTranslation.kt | 22 +++++++++++++++---- 1 file changed, 18 insertions(+), 4 deletions(-) diff --git a/module/bukkit-nms/src/main/kotlin/taboolib/module/nms/remap/RemapTranslation.kt b/module/bukkit-nms/src/main/kotlin/taboolib/module/nms/remap/RemapTranslation.kt index be680ad43..771d1b836 100644 --- a/module/bukkit-nms/src/main/kotlin/taboolib/module/nms/remap/RemapTranslation.kt +++ b/module/bukkit-nms/src/main/kotlin/taboolib/module/nms/remap/RemapTranslation.kt @@ -1,11 +1,12 @@ package taboolib.module.nms.remap -import org.objectweb.asm.commons.Remapper import org.objectweb.asm.ClassReader import org.objectweb.asm.ClassWriter import org.objectweb.asm.Opcodes +import org.objectweb.asm.commons.Remapper import taboolib.common.reflect.ClassHelper import taboolib.module.nms.MinecraftVersion +import taboolib.module.nms.remap.RemapTranslation.Companion.extraTransformers import java.util.concurrent.ConcurrentHashMap import java.util.concurrent.CopyOnWriteArrayList @@ -99,9 +100,7 @@ open class RemapTranslation : Remapper() { if (!MinecraftVersion.isMojangMapping) { translateMojangToSpigotOrKeepRuntime(key) } else { - // 如果为 Mojang Mapping 环境,这里不管是 Spigot.Fullname 还是 Mojang.Fullname 都不需要动 - // 如果是 Spigot.Fullname,Paper PluginRemapper 会进行转译 - key + translateMojangToRuntimeOrKeep(key) } } } else { @@ -137,6 +136,21 @@ open class RemapTranslation : Remapper() { return spigotName } + /** + * 将 Mojang 类名转为 Runtime 类名,运行时已有类名优先保留。 + */ + fun translateMojangToRuntimeOrKeep(key: String): String { + val runtimeName = key.replace('/', '.') + if (hasRuntimeClass(runtimeName)) { + return key + } + val shortName = runtimeName.substringAfterLast('.') + val mappingName = MinecraftVersion.paperMapping.classMapSpigotToMojang[runtimeName] + ?: MinecraftVersion.paperMapping.classMapSpigotToMojang.values.singleOrNull { it.substringAfterLast('.') == shortName } + ?: return key + return if (hasRuntimeClass(mappingName)) mappingName.replace('.', '/') else key + } + /** * 检查类名是否已是当前运行时可直接加载的名称。 * From df06c275d4cf00a25b54be494cc239fa3f9c6e06 Mon Sep 17 00:00:00 2001 From: Jie-150 <1503745098@qq.com> Date: Sat, 19 Sep 2026 13:39:58 +0800 Subject: [PATCH 2/2] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E7=B1=BB=E5=8C=85?= =?UTF-8?q?=E8=B7=AF=E5=BE=84=E5=B7=AE=E5=BC=82=E8=BD=AC=E8=AF=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../module/nms/remap/RemapTranslation.kt | 72 +++++++++++++++++-- 1 file changed, 68 insertions(+), 4 deletions(-) diff --git a/module/bukkit-nms/src/main/kotlin/taboolib/module/nms/remap/RemapTranslation.kt b/module/bukkit-nms/src/main/kotlin/taboolib/module/nms/remap/RemapTranslation.kt index 771d1b836..ba4dea313 100644 --- a/module/bukkit-nms/src/main/kotlin/taboolib/module/nms/remap/RemapTranslation.kt +++ b/module/bukkit-nms/src/main/kotlin/taboolib/module/nms/remap/RemapTranslation.kt @@ -4,6 +4,7 @@ import org.objectweb.asm.ClassReader import org.objectweb.asm.ClassWriter import org.objectweb.asm.Opcodes import org.objectweb.asm.commons.Remapper +import taboolib.common.ClassAppender import taboolib.common.reflect.ClassHelper import taboolib.module.nms.MinecraftVersion import taboolib.module.nms.remap.RemapTranslation.Companion.extraTransformers @@ -32,6 +33,7 @@ open class RemapTranslation : Remapper() { val obc2 = "org/bukkit/craftbukkit/${MinecraftVersion.minecraftVersion}/" val obc3 = "org/bukkit/craftbukkit/" val runtimeClassCache = ConcurrentHashMap() + private val translationClassCache = ConcurrentHashMap() companion object { @@ -125,7 +127,9 @@ open class RemapTranslation : Remapper() { */ fun translateMojangToSpigotOrKeepRuntime(key: String): String { val runtimeName = key.replace('/', '.') - val spigotName = findMojangToSpigotName(key) ?: return key + val spigotName = findMojangToSpigotName(key) + ?: findRuntimeClassBySimpleName(runtimeName)?.replace('.', '/') + ?: return key // 只有映射表确实准备改名时才检查运行时类,避免在普通路径上反复触发类查找。 if (spigotName == key) { return key @@ -145,10 +149,70 @@ open class RemapTranslation : Remapper() { return key } val shortName = runtimeName.substringAfterLast('.') - val mappingName = MinecraftVersion.paperMapping.classMapSpigotToMojang[runtimeName] + val legacyMappingName = MinecraftVersion.paperMapping.classMapSpigotToMojang[runtimeName] ?: MinecraftVersion.paperMapping.classMapSpigotToMojang.values.singleOrNull { it.substringAfterLast('.') == shortName } - ?: return key - return if (hasRuntimeClass(mappingName)) mappingName.replace('.', '/') else key + if (legacyMappingName != null) { + return if (hasRuntimeClass(legacyMappingName)) legacyMappingName.replace('.', '/') else key + } + val mappingName = findRuntimeClassBySimpleName(runtimeName) ?: return key + return mappingName.replace('.', '/') + } + + /** + * 根据当前映射和运行时类加载器,将旧版本中同名但包路径不同的类解析为当前类名。 + * + * Paper 的 Mojang 映射在小版本间可能移动类的包,例如: + * `net.minecraft.world.entity.npc.VillagerType` -> + * `net.minecraft.world.entity.npc.villager.VillagerType`。 + * 旧类名不会出现在当前映射表中,因此不能只按完整类名查找。 + */ + private fun findRuntimeClassBySimpleName(sourceName: String): String? { + if (!sourceName.startsWith("net.minecraft.")) { + return null + } + val simpleName = sourceName.substringAfterLast('.') + val sourcePackage = sourceName.substringBeforeLast('.', "") + val candidates = linkedSetOf() + + MinecraftVersion.spigotMapping.classMapSpigotS2F[simpleName]?.let { candidates += it } + MinecraftVersion.paperMapping.classMapSpigotToMojang.keys + .asSequence() + .filter { it.substringAfterLast('.') == simpleName } + .forEach { candidates += it } + + return candidates.asSequence() + .map { + if (MinecraftVersion.isMojangMapping) { + MinecraftVersion.paperMapping.classMapSpigotToMojang[it] ?: it + } else { + it + } + } + .distinct() + .sortedByDescending { candidate -> + sharedPackageSegments(sourcePackage, candidate.substringBeforeLast('.', "")) + } + .firstOrNull { hasRuntimeClassInTranslationLoader(it) } + } + + /** + * 优先选择与原类名包路径最接近的候选,避免同一简单类名存在于多个 NMS 包时误匹配。 + */ + private fun sharedPackageSegments(first: String, second: String): Int { + val firstSegments = first.split('.') + val secondSegments = second.split('.') + return firstSegments.zip(secondSegments).takeWhile { (a, b) -> a == b }.count() + } + + private fun hasRuntimeClassInTranslationLoader(name: String): Boolean { + return translationClassCache.getOrPut(name) { + try { + Class.forName(name, false, ClassAppender.getClassLoader()) + true + } catch (_: Throwable) { + false + } + } } /**