Fixed drones with an active leash causing leash items to be dropped when picked up (shift-rightclicked).

Fixed leash on drones not rendering centered on drone in obfed Minecraft.
This commit is contained in:
Florian Nücke 2015-01-12 03:18:23 +01:00
parent 5239eb3ab0
commit bce4a60394
3 changed files with 55 additions and 45 deletions

View File

@ -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)))
}
}

View File

@ -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

View File

@ -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),