diff --git a/src/main/scala/li/cil/oc/common/asm/ClassTransformer.scala b/src/main/scala/li/cil/oc/common/asm/ClassTransformer.scala index 3a784cb97..53338ee12 100644 --- a/src/main/scala/li/cil/oc/common/asm/ClassTransformer.scala +++ b/src/main/scala/li/cil/oc/common/asm/ClassTransformer.scala @@ -11,23 +11,38 @@ import org.objectweb.asm.ClassWriter import org.objectweb.asm.Opcodes import org.objectweb.asm.tree._ +import scala.annotation.tailrec import scala.collection.convert.WrapAsJava._ import scala.collection.convert.WrapAsScala._ +object ObfNames { + final val Class_EntityHanging = Array("net/minecraft/entity/EntityHanging", "ss") + final val Class_EntityLiving = Array("net/minecraft/entity/EntityLiving", "sw") + final val Class_RenderLiving = Array("net/minecraft/client/renderer/entity/RenderLiving", "bok") + final val Class_TileEntity = Array("net/minecraft/tileentity/TileEntity", "aor") + final val Field_leashNBTTag = Array("leashNBTTag", "field_110170_bx", "bx") + final val Method_leashedToEntity = Array("leashedToEntity", "field_110168_bw", "bw") + final val Method_recreateLeash = Array("recreateLeash", "func_110165_bF", "bP") + final val Method_recreateLeashDesc = Array("()V") + final val Method_renderHanging = Array("func_110827_b", "b") + final val Method_renderHangingDesc = Array("(Lsw;DDDFF)V", "(Lnet/minecraft/entity/EntityLiving;DDDFF)V") + final val Method_validate = Array("validate", "func_145829_t") + final val Method_invalidate = Array("invalidate", "func_145843_s") + final val Method_onChunkUnload = Array("onChunkUnload", "func_76623_d") + final val Method_readFromNBT = Array("readFromNBT", "func_145839_a") + final val Method_writeToNBT = Array("writeToNBT", "func_145841_b") +} + class ClassTransformer extends IClassTransformer { private val loader = classOf[ClassTransformer].getClassLoader.asInstanceOf[LaunchClassLoader] private val log = LogManager.getLogger("OpenComputers") override def transform(name: String, transformedName: String, basicClass: Array[Byte]): Array[Byte] = { + if (basicClass == null || name.startsWith("scala.")) return basicClass var transformedClass = basicClass try { - if (name == "li.cil.oc.common.tileentity.traits.Computer" || name == "li.cil.oc.common.tileentity.Rack") { - transformedClass = ensureStargateTechCompatibility(transformedClass) - } - - if (transformedClass != null - && !name.startsWith("net.minecraft.") + if (!name.startsWith("net.minecraft.") && !name.startsWith("net.minecraftforge.") && !name.startsWith("li.cil.oc.common.asm.") && !name.startsWith("li.cil.oc.integration.")) { @@ -137,18 +152,24 @@ class ClassTransformer extends IClassTransformer { // if (this.isLeashed && this.field_110170_bx != null && this.leashedToEntity == null) // which should not interfere with any existing logic, but avoid leashing // restored manually in the load phase to not be broken again. - if (transformedClass != null && name == "net.minecraft.entity.EntityLiving") { - insertInto(transformedClass, "recreateLeash", instructions => instructions.toArray.sliding(3, 1).exists { + if (ObfNames.Class_EntityLiving.contains(name.replace('.', '/'))) { + val classNode = newClassNode(transformedClass) + insertInto(classNode, ObfNames.Method_recreateLeash, ObfNames.Method_recreateLeashDesc, instructions => instructions.toArray.sliding(3, 1).exists { case Array(varNode: VarInsnNode, fieldNode: FieldInsnNode, jumpNode: JumpInsnNode) if varNode.getOpcode == Opcodes.ALOAD && varNode.`var` == 0 && - fieldNode.getOpcode == Opcodes.GETFIELD && fieldNode.name == "field_110170_bx" && + fieldNode.getOpcode == Opcodes.GETFIELD && ObfNames.Field_leashNBTTag.contains(fieldNode.name) && jumpNode.getOpcode == Opcodes.IFNULL => - val toInject = new InsnList() - toInject.add(new VarInsnNode(Opcodes.ALOAD, 0)) - toInject.add(new FieldInsnNode(Opcodes.GETFIELD, "net/minecraft/entity/EntityLiving", "leashedToEntity", "Lnet/minecraft/entity/Entity;")) - toInject.add(new JumpInsnNode(Opcodes.IFNONNULL, jumpNode.label)) - instructions.insert(jumpNode, toInject) - true + classNode.fields.find(field => ObfNames.Method_leashedToEntity.contains(field.name)) match { + case Some(field) => + val toInject = new InsnList() + toInject.add(new VarInsnNode(Opcodes.ALOAD, 0)) + toInject.add(new FieldInsnNode(Opcodes.GETFIELD, classNode.name, field.name, field.desc)) + toInject.add(new JumpInsnNode(Opcodes.IFNONNULL, jumpNode.label)) + instructions.insert(jumpNode, toInject) + true + case _ => + false + } case _ => false }) match { @@ -165,11 +186,12 @@ class ClassTransformer extends IClassTransformer { // d7 = -0.75; // } // before the `instanceof EntityHanging` check in func_110827_b. - if (transformedClass != null && name == "net.minecraft.client.renderer.entity.RenderLiving") { - insertInto(transformedClass, "func_110827_b", instructions => instructions.toArray.sliding(3, 1).exists { + if (ObfNames.Class_RenderLiving.contains(name.replace('.', '/'))) { + val classNode = newClassNode(transformedClass) + insertInto(classNode, ObfNames.Method_renderHanging, ObfNames.Method_renderHangingDesc, instructions => instructions.toArray.sliding(3, 1).exists { case Array(varNode: VarInsnNode, typeNode: TypeInsnNode, jumpNode: JumpInsnNode) if varNode.getOpcode == Opcodes.ALOAD && varNode.`var` == 10 && - typeNode.getOpcode == Opcodes.INSTANCEOF && typeNode.desc == "net/minecraft/entity/EntityHanging" && + typeNode.getOpcode == Opcodes.INSTANCEOF && ObfNames.Class_EntityHanging.contains(typeNode.desc) && jumpNode.getOpcode == Opcodes.IFEQ => val toInject = new InsnList() toInject.add(new VarInsnNode(Opcodes.ALOAD, 10)) @@ -202,20 +224,19 @@ class ClassTransformer extends IClassTransformer { } } - private def insertInto(classData: Array[Byte], method: String, inserter: (InsnList) => Boolean): Option[Array[Byte]] = { - val classNode = newClassNode(classData) - classNode.methods.find(_.name == method) match { + private def insertInto(classNode: ClassNode, methodNames: Array[String], methodDescs: Array[String], inserter: (InsnList) => Boolean): Option[Array[Byte]] = { + classNode.methods.find(method => methodNames.contains(method.name) && methodDescs.contains(method.desc)) match { case Some(methodNode) => if (inserter(methodNode.instructions)) { - log.warn(s"Successfully patched ${classNode.name}.$method.") + log.info(s"Successfully patched ${classNode.name}.${methodNames(0)}.") Option(writeClass(classNode, ClassWriter.COMPUTE_MAXS | ClassWriter.COMPUTE_FRAMES)) } else { - log.warn(s"Failed patching ${classNode.name}.$method}, injection point not found.") + log.warn(s"Failed patching ${classNode.name}.${methodNames(0)}, injection point not found.") None } case _ => - log.warn(s"Failed patching ${classNode.name}.$method, method not found.") + log.warn(s"Failed patching ${classNode.name}.${methodNames(0)}, method not found.") None } } @@ -232,16 +253,6 @@ class ClassTransformer extends IClassTransformer { """L([^;]+);""".r.findAllMatchIn(desc).map(_.group(1)).filter(!classExists(_)) } - def ensureStargateTechCompatibility(basicClass: Array[Byte]): Array[Byte] = { - if (!Mods.StargateTech2.isAvailable) { - // No SGT2 or version is too old, abstract bus API doesn't exist. - val classNode = newClassNode(basicClass) - classNode.interfaces.remove("stargatetech2/api/bus/IBusDevice") - writeClass(classNode) - } - else basicClass - } - def injectEnvironmentImplementation(classNode: ClassNode): Array[Byte] = { log.trace(s"Injecting methods from Environment interface into ${classNode.name}.") if (!isTileEntity(classNode)) { @@ -275,7 +286,7 @@ class ClassTransformer extends IClassTransformer { val mapper = FMLDeobfuscatingRemapper.INSTANCE def filter(method: MethodNode) = { val descDeObf = mapper.mapMethodDesc(method.desc) - val methodNameDeObf = mapper.mapMethodName(tileEntityNameObfed, method.name, method.desc) + val methodNameDeObf = mapper.mapMethodName(ObfNames.Class_TileEntity(1), method.name, method.desc) val areSamePlain = method.name + descDeObf == methodName + desc val areSameDeObf = methodNameDeObf + descDeObf == methodNameSrg + desc areSamePlain || areSameDeObf @@ -289,7 +300,7 @@ class ClassTransformer extends IClassTransformer { method.name = methodName + SimpleComponentImpl.PostFix case _ => log.trace(s"No original implementation of '$methodName', will inject override.") - def ensureNonFinalIn(name: String) { + @tailrec def ensureNonFinalIn(name: String) { if (name != null) { val node = classNodeFor(name) if (node != null) { @@ -315,11 +326,11 @@ class ClassTransformer extends IClassTransformer { case _ => throw new AssertionError(s"Couldn't find '$methodName' in template implementation.") } } - replace("validate", "func_145829_t", "()V") - replace("invalidate", "func_145843_s", "()V") - replace("onChunkUnload", "func_76623_d", "()V") - replace("readFromNBT", "func_145839_a", "(Lnet/minecraft/nbt/NBTTagCompound;)V") - replace("writeToNBT", "func_145841_b", "(Lnet/minecraft/nbt/NBTTagCompound;)V") + replace(ObfNames.Method_validate(0), ObfNames.Method_validate(1), "()V") + replace(ObfNames.Method_invalidate(0), ObfNames.Method_invalidate(1), "()V") + replace(ObfNames.Method_onChunkUnload(0), ObfNames.Method_onChunkUnload(1), "()V") + replace(ObfNames.Method_readFromNBT(0), ObfNames.Method_readFromNBT(1), "(Lnet/minecraft/nbt/NBTTagCompound;)V") + replace(ObfNames.Method_writeToNBT(0), ObfNames.Method_writeToNBT(1), "(Lnet/minecraft/nbt/NBTTagCompound;)V") log.trace("Injecting interface.") classNode.interfaces.add("li/cil/oc/common/asm/template/SimpleComponentImpl") @@ -327,14 +338,11 @@ class ClassTransformer extends IClassTransformer { writeClass(classNode, ClassWriter.COMPUTE_MAXS | ClassWriter.COMPUTE_FRAMES) } - val tileEntityNamePlain = "net/minecraft/tileentity/TileEntity" - val tileEntityNameObfed = FMLDeobfuscatingRemapper.INSTANCE.unmap(tileEntityNamePlain) - def isTileEntity(classNode: ClassNode): Boolean = { if (classNode == null) false else { log.trace(s"Checking if class ${classNode.name} is a TileEntity...") - classNode.name == tileEntityNamePlain || classNode.name == tileEntityNameObfed || + ObfNames.Class_TileEntity.contains(classNode.name) || (classNode.superName != null && isTileEntity(classNodeFor(classNode.superName))) } } diff --git a/src/main/scala/li/cil/oc/common/entity/Drone.scala b/src/main/scala/li/cil/oc/common/entity/Drone.scala index 379b19388..eb1925db3 100644 --- a/src/main/scala/li/cil/oc/common/entity/Drone.scala +++ b/src/main/scala/li/cil/oc/common/entity/Drone.scala @@ -320,6 +320,7 @@ class Drone(val world: World) extends Entity(world) with MachineHost with intern if (!world.isRemote) { machine.stop() machine.node.remove() + components.disconnectComponents() components.saveComponents() val stack = api.Items.get("drone").createItemStack(1) info.storedEnergy = control.node.localBuffer.toInt diff --git a/src/main/scala/li/cil/oc/common/init/Items.scala b/src/main/scala/li/cil/oc/common/init/Items.scala index 6639c13b9..df48e7b0e 100644 --- a/src/main/scala/li/cil/oc/common/init/Items.scala +++ b/src/main/scala/li/cil/oc/common/init/Items.scala @@ -151,6 +151,7 @@ object Items extends ItemAPI { get("inventoryControllerUpgrade").createItemStack(1), get("tankUpgrade").createItemStack(1), get("tankControllerUpgrade").createItemStack(1), + get("leashUpgrade").createItemStack(1), get("wlanCard").createItemStack(1),