Remove @Optional annotations that specify our lowercase mod ID (#2661)

Fixes #2649. Explanation:
* Other mods can integrate with us by implementing the SimpleComponent
  interface. They annotate its methods with @Optional to avoid a hard
  dependency on us.
* Our mod ID changed from "OpenComputers" in 1.10 to "opencomputers" in 1.11.
* Some mods (like RFTools) can't refer to our mod by the right-case mod ID,
  because the same .jar works on both 1.10 and 1.11.
* In most cases, using the wrong-case mod ID would just mean that integration
  wouldn't work, but for us, it causes a crash.
* The crash is because we use ASM to inject some extra methods and make the
  class implement a different interface. This means that if the @Optional mod
  ID is wrong, our new interface will still be there but some of the original
  methods won't, which leads to AbstractMethodErrors like McJty/RFTools#1466.
* Forge looks for the @Optional annotations before our coremod can do
  anything, so we can't just remove them from the class. Instead, we need to
  remove them from Forge's internal list.
This commit is contained in:
Joseph C. Sible 2017-12-06 09:42:10 -05:00 committed by payonel
parent 2279b734a8
commit 673facee90

View File

@ -1,10 +1,13 @@
package li.cil.oc.common.asm
import com.google.common.collect.ListMultimap
import li.cil.oc.common.asm.template.SimpleComponentImpl
import li.cil.oc.integration.Mods
import net.minecraft.launchwrapper.IClassTransformer
import net.minecraft.launchwrapper.LaunchClassLoader
import net.minecraftforge.fml.common.asm.transformers.ModAPITransformer
import net.minecraftforge.fml.common.asm.transformers.deobf.FMLDeobfuscatingRemapper
import net.minecraftforge.fml.common.discovery.ASMDataTable.ASMData
import org.apache.logging.log4j.LogManager
import org.objectweb.asm.ClassReader
import org.objectweb.asm.ClassWriter
@ -42,6 +45,19 @@ class ClassTransformer extends IClassTransformer {
private val loader = classOf[ClassTransformer].getClassLoader.asInstanceOf[LaunchClassLoader]
private val log = LogManager.getLogger("OpenComputers")
// Stuff to help us remove the @Optional annotations below. We set
// this up outside of the method so that it only has to be done once.
private val optionalsField = classOf[ModAPITransformer].getDeclaredField("optionals")
optionalsField.setAccessible(true)
private val transformers = loader.getTransformers()
private var foundModAPITransformer = false
// If these weren't lazy, they'd run before the ModAPITransformer gets added.
private lazy val modAPITransformer = {
foundModAPITransformer = true
transformers.find(_.isInstanceOf[ModAPITransformer]).get
}
private lazy val optionals = optionalsField.get(modAPITransformer).asInstanceOf[ListMultimap[String, ASMData]]
override def transform(name: String, transformedName: String, basicClass: Array[Byte]): Array[Byte] = {
if (basicClass == null || name.startsWith("scala.")) return basicClass
var transformedClass = basicClass
@ -146,6 +162,17 @@ class ClassTransformer extends IClassTransformer {
}
}
}
if(foundModAPITransformer || transformers.find(_.isInstanceOf[ModAPITransformer]).isDefined) {
// Forge makes the list of @Optional annotations before we can remove
// them from the class, so we need to remove them from its list instead.
val lookupName = if(name.endsWith("$class")) name.substring(0, name.length() - 6) else name
val ourOptionals = optionals.get(lookupName)
val filteredOptionals = ourOptionals.filter(_.getAnnotationInfo().get("modid") == "opencomputers")
if(!filteredOptionals.isEmpty) {
filteredOptionals.foreach(ourOptionals.remove)
log.info(s"Successfully removed our lowercase @Optional annotations from class $name.")
}
}
}
// Inject some code into the EntityLiving classes recreateLeash method to allow