ASM Transformer cleanup.

Cleaned up ASM transformer code.
This commit is contained in:
Chris committed 2016-04-15 06:28:13 -04:00
1 parent faf76b8009
commit 3b7d01e83a
13 files changed
+958 -832

No files matched your search

+4 -1
View File
@@ -1,4 +1,7 @@
04/14/2016 1.0.0.3 - Beta
04/15/2016 1.0.0.4 - Beta
Cleaned up ASM transformer code.
04/14/2016 1.0.0.3 - Beta
Completely moved dev environment to Java 8, Java 7 and earlier is no longer supported.
Updated forge to 10.13.4.1566
Fixes #294: Wand move targets for shift+click changed to favor player inventory over ME storage.
+1 -1
View File
@@ -24,7 +24,7 @@ apply plugin: 'java'
sourceCompatibility = 1.8
targetCompatibility = 1.8
version = "1.0.0.3"
version = "1.0.0.4"
group= "thaumicenergistics" // http://maven.apache.org/guides/mini/guide-naming-conventions.html
archivesBaseName = "thaumicenergistics"
@@ -14,12 +14,11 @@ import thaumicenergistics.api.gui.IWidgetHost;
/**
* Widget displaying an AE item.
*
*
* @author Nividica
*
* @deprecated Remove in favor of MESlots
*
* TODO: Deprecated Remove in favor of MESlots
*/
@Deprecated
@SideOnly(Side.CLIENT)
public class WidgetAEItem
extends ThEWidget
@@ -46,7 +45,7 @@ public class WidgetAEItem
/**
* Creates the widget
*
*
* @param hostGUI
* @param xPos
* @param yPos
@@ -98,7 +97,7 @@ public class WidgetAEItem
/**
* Returns the itemstack this widget represents.
*
*
* @return IAEItemstack if the widget has one, null otherwise.
*/
public IAEItemStack getItemStack()
@@ -159,7 +158,7 @@ public class WidgetAEItem
/**
* Set the itemstack this widget represents
*
*
* @param itemStack
*/
public void setItemStack( final IAEItemStack itemStack )
@@ -29,9 +29,9 @@ import thaumicenergistics.common.utils.ThELog;
* <strong>Thaumic Energistics</strong>
* <hr>
* A bridge between Thaumcraft and Applied Energistics. Essentia storage management, transportation, and application.
*
*
* @author Nividica
*
*
*/
@Mod(modid = ThaumicEnergistics.MOD_ID, name = "Thaumic Energistics", version = ThaumicEnergistics.VERSION, dependencies = "required-after:ThE-core;required-after:appliedenergistics2@[rv3-beta-1,);required-after:Thaumcraft@[4.2.3.5,);after:Waila;after:extracells")
public class ThaumicEnergistics
@@ -49,7 +49,7 @@ public class ThaumicEnergistics
/**
* Current version of the mod.
*/
public static final String VERSION = "1.0.0.3"; // NOTE: don't forget to change the build.gradle file as well
public static final String VERSION = "1.0.0.4"; // NOTE: don't forget to change the build.gradle file as well
/**
* Singleton instance
@@ -91,7 +91,7 @@ public class ThaumicEnergistics
* PREINITIALIZATION: PreInit has finished.<br>
* INITIALIZATION: Load has finished.<br>
* POSTINITIALIZATION: PostInit has finished, ThE is fully loaded.
*
*
* @return
*/
public static LoaderState getLoaderState()
@@ -101,7 +101,7 @@ public class ThaumicEnergistics
/**
* Called after the preInit event, and before the post init event.
*
*
* @param event
*/
@EventHandler
@@ -135,7 +135,7 @@ public class ThaumicEnergistics
/**
* Called after the load event.
*
*
* @param event
*/
@EventHandler
@@ -174,7 +174,7 @@ public class ThaumicEnergistics
/**
* Called before the load event.
*
*
* @param event
* @throws Exception
*/
@@ -0,0 +1,193 @@
package thaumicenergistics.fml;
import org.apache.logging.log4j.Level;
import org.objectweb.asm.ClassReader;
import org.objectweb.asm.ClassWriter;
import org.objectweb.asm.tree.AbstractInsnNode;
import org.objectweb.asm.tree.ClassNode;
import org.objectweb.asm.tree.InsnList;
import cpw.mods.fml.relauncher.FMLRelaunchLog;
public abstract class AClassTransformer
{
protected static final String InstanceConstructorName = "<init>";
/**
* Canonical name of the class to be transformed.
*/
public final String classCanonicalName;
protected AClassTransformer( final String classCanonicalName )
{
this.classCanonicalName = classCanonicalName;
}
/**
* Locates the first occurrence of the specified opcode.
*
* @param instructions
* @param opcode
* @return
*/
protected AbstractInsnNode findFirstOpCode( final InsnList instructions, final int opcode )
{
for( int index = 0; index < instructions.size(); ++index )
{
if( instructions.get( index ).getOpcode() == opcode )
{
return instructions.get( index );
}
}
return null;
}
/**
* Locates the last occurrence of the specified opcode.
*
* @param instructions
* @param opcode
* @return
*/
protected AbstractInsnNode findLastOpCode( final InsnList instructions, final int opcode )
{
for( int index = instructions.size() - 1; index > 0; --index )
{
if( instructions.get( index ).getOpcode() == opcode )
{
return instructions.get( index );
}
}
return null;
}
/**
* Locates the last occurrence of the specified type.
*
* @param instructions
* @param type
* @param number
* of occurrences to skip
* @return
*/
protected AbstractInsnNode findLastType( final InsnList instructions, final int type, int skip )
{
for( int index = instructions.size() - 1; index > 0; --index )
{
if( instructions.get( index ).getType() == type )
{
if( --skip < 0 )
{
return instructions.get( index );
}
}
}
return null;
}
/**
* Locates a sequence of instructions.
*
* @param instructions
* @param opSequence
* @param skipNons
* If true all -1 instructions will be skipped
* @return The last instruction in the sequence.
*/
protected AbstractInsnNode findSequence( final InsnList instructions, final int opSequence[], final boolean skipNons )
{
int seqIndex = 0;
AbstractInsnNode insertionPoint = null;
for( int index = 0; index < instructions.size(); ++index )
{
// Get the instruction
AbstractInsnNode ins = instructions.get( index );
if( skipNons && ( ins.getOpcode() == -1 ) )
{
continue;
}
// Does it match the sequence?
if( ins.getOpcode() == opSequence[seqIndex] )
{
// Has the full sequence been found?
if( ++seqIndex == opSequence.length )
{
// Found the full sequence
insertionPoint = ins;
break;
}
}
else if( ins.getOpcode() == opSequence[0] )
{
// Restart sequence
seqIndex = 1;
}
else
{
// Reset sequence
seqIndex = 0;
}
}
return insertionPoint;
}
/**
* Adds an entry to the log.
*
* @param text
*/
protected void log( final String text )
{
FMLRelaunchLog.log( "ThE-Core", Level.INFO, text );
}
/**
* Action to take if the transform fails.
*/
protected abstract void onTransformFailure();
/**
* Internal call to transform the class.
*
* @param classBytes
* @return
*/
protected abstract void transform( ClassNode classNode );
/**
* Transforms the class.
*
* @param classBytes
* @return
*/
public final byte[] transformClass( final byte[] classBytes )
{
try
{
this.log( String.format( "Transforming Class (%s)", this.classCanonicalName ) );
// Create the class node and read in the class
ClassNode classNode = new ClassNode();
ClassReader classReader = new ClassReader( classBytes );
classReader.accept( classNode, 0 );
// Transform
this.transform( classNode );
// Create the writer
ClassWriter writer = new ClassWriter( ClassWriter.COMPUTE_MAXS );
classNode.accept( writer );
// Return the modified class
return writer.toByteArray();
}
catch( Exception e )
{
this.log( String.format( "Unable to transform (%s)", this.classCanonicalName ) );
this.onTransformFailure();
}
return classBytes;
}
}
@@ -1,840 +1,52 @@
package thaumicenergistics.fml;
import org.apache.logging.log4j.Level;
import org.objectweb.asm.ClassReader;
import org.objectweb.asm.ClassWriter;
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.tree.*;
import cpw.mods.fml.relauncher.FMLRelaunchLog;
import java.util.HashMap;
import net.minecraft.launchwrapper.IClassTransformer;
import thaumicenergistics.fml.classtransformers.ae.ClassTransformer_CraftingTreeProcess;
import thaumicenergistics.fml.classtransformers.tc.*;
/**
* <strong>Modifications made by the transformer:</strong></br>
* Thaumcraft class <em>EntityGolemBase</em>
* <ul>
* <li>Adds field <em>hookHandlers</em> of type HashMap</li>
* <li>Adds hook to method <em>setupGolem</em></li>
* <li>Adds hook to method <em>customInteraction</em></li>
* <li>Adds hook to method <em>writeEntityNBT</em></li>
* <li>Adds hook to method <em>readEntityFromNBT</em></li>
* <li>Adds hook to method <em>func_70088_a | entityInit</em></li>
* <li>Adds hook to method <em>func_70030_z | onEntityUpdate</em></li>
* </ul>
* Thaumcraft class <em>ItemGolemBell</em>
* <ul>
* <li>Adds hook to method <em>onLeftClickEntity</em></li>
* </ul>
*
* Thaumcraft class <em>ItemGolemPlacer</em>
* <ul>
* <li>Adds hook to method <em>spawnCreature</em></li>
* </ul>
*
* Thaumcraft class <em>RenderGolemBase</em>
* <ul>
* <li>Adds hook to method <em>render</em></li>
* </ul>
*
* Thaumcraft class <em>Aspect</em>
* <ul>
* <li>Adds hook to <em>constructor</em></li>
* </ul>
*
* @author Nividica
*
*/
public class ASMTransformer
implements IClassTransformer
{
private static final String CLASS_EntityGolemBase = "thaumcraft/common/entities/golems/EntityGolemBase";
private static final String CLASS_Aspect = "thaumcraft/api/aspects/Aspect";
private static final String CLASS_GolemHooks = "thaumicenergistics/common/integration/tc/GolemHooks";
private static final String CLASS_AspectHooks = "thaumicenergistics/common/integration/tc/AspectHooks";
private static final String INTERFACE_IGolemHookHandler = "thaumicenergistics/api/entities/IGolemHookHandler";
private static final String FIELD_EntityGolemBase_hookHandlers = "hookHandlers";
private static final String FIELDTYPE_EntityGolemBase_hookHandlers = "java/util/HashMap";
/**
* Adds a reference to a golems hookHandlers field to the stack.
*
* @param instructionList
* @param golemVar
* @param checkCast
* If the var specified is not type EntityGolemBase(such as just Entity or EntityLiving) a cast check must be performed.
* Map of all transformers<br>
* Class name -> Transformer
*/
private void addGetField_hookHandlers( final InsnList instructionList, final int golemVar, final boolean checkCast )
private final HashMap<String, AClassTransformer> tranformers;
public ASMTransformer()
{
instructionList.add( new VarInsnNode( Opcodes.ALOAD, golemVar ) );
if( checkCast )
{
instructionList.add( new TypeInsnNode( Opcodes.CHECKCAST, CLASS_EntityGolemBase ) );
}
instructionList.add( new FieldInsnNode( Opcodes.GETFIELD,
CLASS_EntityGolemBase,
FIELD_EntityGolemBase_hookHandlers, "L" + FIELDTYPE_EntityGolemBase_hookHandlers + ";" ) );
// Create the transformers map
this.tranformers = new HashMap<String, AClassTransformer>();
// Add Thaumcraft transformers
this.addTransformer( new ClassTransformer_Aspect() );
this.addTransformer( new ClassTransformer_RenderGolemBase() );
this.addTransformer( new ClassTransformer_ItemGolemPlacer() );
this.addTransformer( new ClassTransformer_ItemGolemBell() );
this.addTransformer( new ClassTransformer_EntityGolemBase() );
// Add AE transformers
this.addTransformer( new ClassTransformer_CraftingTreeProcess() );
}
/**
* Locates the first occurrence of the specified opcode.
*
* @param instructions
* @param opcode
* @return
* Adds a transformer to the map.
*
* @param transformer
*/
private AbstractInsnNode findFirstOpCode( final InsnList instructions, final int opcode )
private void addTransformer( final AClassTransformer transformer )
{
for( int index = 0; index < instructions.size(); ++index )
{
if( instructions.get( index ).getOpcode() == opcode )
{
return instructions.get( index );
}
}
return null;
}
/**
* Locates the last occurrence of the specified opcode.
*
* @param instructions
* @param opcode
* @return
*/
private AbstractInsnNode findLastOpCode( final InsnList instructions, final int opcode )
{
for( int index = instructions.size() - 1; index > 0; --index )
{
if( instructions.get( index ).getOpcode() == opcode )
{
return instructions.get( index );
}
}
return null;
}
/**
* Locates the last occurrence of the specified type.
*
* @param instructions
* @param type
* @param number
* of occurrences to skip
* @return
*/
private AbstractInsnNode findLastType( final InsnList instructions, final int type, int skip )
{
for( int index = instructions.size() - 1; index > 0; --index )
{
if( instructions.get( index ).getType() == type )
{
if( --skip < 0 )
{
return instructions.get( index );
}
}
}
return null;
}
/**
* Locates a sequence of instructions.
*
* @param instructions
* @param opSequence
* @param skipNons
* If true all -1 instructions will be skipped
* @return The last instruction in the sequence.
*/
private AbstractInsnNode findSequence( final InsnList instructions, final int opSequence[], final boolean skipNons )
{
int seqIndex = 0;
AbstractInsnNode insertionPoint = null;
for( int index = 0; index < instructions.size(); ++index )
{
// Get the instruction
AbstractInsnNode ins = instructions.get( index );
if( skipNons && ( ins.getOpcode() == -1 ) )
{
continue;
}
// Does it match the sequence?
if( ins.getOpcode() == opSequence[seqIndex] )
{
// Has the full sequence been found?
if( ++seqIndex == opSequence.length )
{
// Found the full sequence
insertionPoint = ins;
break;
}
}
else if( ins.getOpcode() == opSequence[0] )
{
// Restart sequence
seqIndex = 1;
}
else
{
// Reset sequence
seqIndex = 0;
}
}
return insertionPoint;
}
private void insertMethod_GolemBase_onEntityUpdate( final ClassNode classNode, final boolean isObf )
{
// Create the onEntityUpdate() method
String oEU_Name = ( isObf ? "func_70030_z" : "onEntityUpdate" );
MethodNode onEntityUpdate = new MethodNode( Opcodes.ACC_PUBLIC,
oEU_Name, "()V", null, null );
// super.onEntityUpdate()
onEntityUpdate.instructions.add( new VarInsnNode( Opcodes.ALOAD, 0 ) );
onEntityUpdate.instructions.add( new MethodInsnNode( Opcodes.INVOKESPECIAL,
"net/minecraft/entity/monster/EntityGolem", oEU_Name, "()V", false ) );
// GolemHooks.hook_onEntityUpdate( this, this.hookHandlers )
// this
onEntityUpdate.instructions.add( new VarInsnNode( Opcodes.ALOAD, 0 ) );
// this.hookHandlers
this.addGetField_hookHandlers( onEntityUpdate.instructions, 0, false );
// GolemHooks.hook_onEntityUpdate( EntityGolemBase, Hashmap )
onEntityUpdate.instructions.add( new MethodInsnNode( Opcodes.INVOKESTATIC,
CLASS_GolemHooks, "hook_onEntityUpdate",
"(L" + CLASS_EntityGolemBase + ";L" + FIELDTYPE_EntityGolemBase_hookHandlers + ";)V", false ) );
// return
onEntityUpdate.instructions.add( new InsnNode( Opcodes.RETURN ) );
// Add the method to the class
classNode.methods.add( onEntityUpdate );
}
/**
* Adds an entry to the log.
*
* @param text
*/
private void log( final String text )
{
FMLRelaunchLog.log( "ThE-Core", Level.INFO, text );
}
private byte[] transformClass_Aspect( final byte[] classBytes )
{
// Create the class node and read in the class
ClassNode classNode = new ClassNode();
ClassReader classReader = new ClassReader( classBytes );
classReader.accept( classNode, 0 );
// Transform methods
for( MethodNode method : classNode.methods )
{
// Constructor
if( method.name.equals( "<init>" ) )
{
if( this.transformMethod_Aspect_init( method ) )
{
break; // Stop searching.
}
}
}
// Create the writer
ClassWriter writer = new ClassWriter( ClassWriter.COMPUTE_MAXS );
classNode.accept( writer );
// Return the modified class
return writer.toByteArray();
}
private byte[] transformClass_CraftingTreeProcess( final byte[] classBytes )
{
// Create the class node and read in the class
ClassNode classNode = new ClassNode();
ClassReader classReader = new ClassReader( classBytes );
classReader.accept( classNode, 0 );
// Transform methods
for( MethodNode method : classNode.methods )
{
// Constructor
if( method.name.equals( "<init>" ) )
{
this.transformMethod_CraftingTreeProcess_init( method );
break; // Stop searching.
}
}
// Create the writer
ClassWriter writer = new ClassWriter( ClassWriter.COMPUTE_MAXS );
classNode.accept( writer );
// Return the modified class
return writer.toByteArray();
}
private byte[] transformClass_GolemBase( final byte[] classBytes )
{
// Create the class node and read in the class
ClassNode classNode = new ClassNode();
ClassReader classReader = new ClassReader( classBytes );
classReader.accept( classNode, 0 );
// Add the handlers field
// public HashMap<IGolemHookHandler, Object> hookHandlers;
classNode.fields.add( new FieldNode( Opcodes.ACC_PUBLIC,
FIELD_EntityGolemBase_hookHandlers,
"L" + FIELDTYPE_EntityGolemBase_hookHandlers + ";",
"L" + FIELDTYPE_EntityGolemBase_hookHandlers + "<L" + INTERFACE_IGolemHookHandler + ";Ljava/lang/Object;>;",
null ) );
boolean isObf = false;
// Transform methods
for( MethodNode method : classNode.methods )
{
// Look for an obfuscated method
if( method.name.equals( "func_70014_b" ) )
{
isObf = true;
}
// Setup golem
if( method.name.equals( "setupGolem" ) )
{
this.transformMethod_GolemBase_SetupGolem( method );
}
// Custom interaction
else if( method.name.equals( "customInteraction" ) )
{
this.transformMethod_GolemBase_CustomInteraction( method );
}
// Write entity NBT
else if( method.name.equals( "func_70014_b" ) || method.name.equals( "writeEntityToNBT" ) )
{
this.transformMethod_GolemBase_WriteEntityToNBT( method );
}
// Read entity from NBT
else if( method.name.equals( "func_70037_a" ) || method.name.equals( "readEntityFromNBT" ) )
{
this.transformMethod_GolemBase_ReadEntityFromNBT( method );
}
// Entity Init
else if( method.name.equals( "func_70088_a" ) || method.name.equals( "entityInit" ) )
{
this.transformMethod_GolemBase_EntityInit( method );
}
}
// Add the onEntityUpdate method.
this.insertMethod_GolemBase_onEntityUpdate( classNode, isObf );
// Create the writer
ClassWriter writer = new ClassWriter( ClassWriter.COMPUTE_MAXS );
classNode.accept( writer );
// Return the modified class
return writer.toByteArray();
}
private byte[] transformClass_ItemGolemBell( final byte[] classBytes )
{
// Create the class node and read in the class
ClassNode classNode = new ClassNode();
ClassReader classReader = new ClassReader( classBytes );
classReader.accept( classNode, 0 );
// Transform methods
for( MethodNode method : classNode.methods )
{
// Left click entity
if( method.name.equals( "onLeftClickEntity" ) )
{
this.transformMethod_ItemGolemBell_OnLeftClickEntity( method );
break; // Stop searching.
}
}
// Create the writer
ClassWriter writer = new ClassWriter( ClassWriter.COMPUTE_MAXS );
classNode.accept( writer );
// Return the modified class
return writer.toByteArray();
}
private byte[] transformClass_ItemGolemPlacer( final byte[] classBytes )
{
// Create the class node and read in the class
ClassNode classNode = new ClassNode();
ClassReader classReader = new ClassReader( classBytes );
classReader.accept( classNode, 0 );
// Transform methods
for( MethodNode method : classNode.methods )
{
// Spawn creature
if( method.name.equals( "spawnCreature" ) )
{
this.transformMethod_ItemGolemPlacer_SpawnCreature( method );
break; // Stop searching.
}
}
// Create the writer
ClassWriter writer = new ClassWriter( ClassWriter.COMPUTE_MAXS );
classNode.accept( writer );
// Return the modified class
return writer.toByteArray();
}
private byte[] transformClass_RenderGolemBase( final byte[] classBytes )
{
// Create the class node and read in the class
ClassNode classNode = new ClassNode();
ClassReader classReader = new ClassReader( classBytes );
classReader.accept( classNode, 0 );
// Transform methods
for( MethodNode method : classNode.methods )
{
if( method.name.equals( "render" ) )
{
// Render
this.transformMethod_RenderGolemBase_Render( method );
break; // Stop searching.
}
}
// Create the writer
ClassWriter writer = new ClassWriter( ClassWriter.COMPUTE_MAXS );
classNode.accept( writer );
// Return the modified class
return writer.toByteArray();
}
private boolean transformMethod_Aspect_init( final MethodNode method )
{
// Is this the full constructor?
// Check description
if( !"(Ljava/lang/String;I[Lthaumcraft/api/aspects/Aspect;Lnet/minecraft/util/ResourceLocation;I)V".equals( method.desc ) )
{
return false;
}
int opSequence[] = new int[] { Opcodes.GETSTATIC, Opcodes.ALOAD, Opcodes.ALOAD, Opcodes.INVOKEVIRTUAL, Opcodes.POP };
AbstractInsnNode insertionPoint = this.findSequence( method.instructions, opSequence, false );
// Insert the hook
// AspectHooks.hook_AspectInit( this )
InsnList instructionList = new InsnList();
// this
instructionList.add( new VarInsnNode( Opcodes.ALOAD, 0 ) );
// AspectHooks.hook_AspectInit( Aspect )
instructionList.add( new MethodInsnNode( Opcodes.INVOKESTATIC,
CLASS_AspectHooks, "hook_AspectInit",
"(L" + CLASS_Aspect + ";)V", false ) );
// Insert the static call
method.instructions.insert( insertionPoint, instructionList );
return true;
}
private void transformMethod_CraftingTreeProcess_init( final MethodNode method )
{
int opSequence[] = new int[] { Opcodes.ILOAD, Opcodes.PUTFIELD, Opcodes.ALOAD, Opcodes.INVOKEVIRTUAL, Opcodes.ASTORE };
AbstractInsnNode insertionPoint = this.findSequence( method.instructions, opSequence, true );
// Insert this.world = world
InsnList instructionList = new InsnList();
// this
instructionList.add( new VarInsnNode( Opcodes.ALOAD, 0 ) );
// world
instructionList.add( new VarInsnNode( Opcodes.ALOAD, 6 ) );
// this.world = world
instructionList.add( new FieldInsnNode( Opcodes.PUTFIELD,
"appeng/crafting/CraftingTreeProcess", "world", "Lnet/minecraft/world/World;" ) );
// Insert the new code
method.instructions.insert( insertionPoint, instructionList );
}
private void transformMethod_GolemBase_CustomInteraction( final MethodNode method )
{
int opSequence[] = new int[] { Opcodes.ALOAD, Opcodes.INVOKEVIRTUAL, Opcodes.ICONST_M1 };
AbstractInsnNode insertionPoint = this.findSequence( method.instructions, opSequence, false );
// Go two back
insertionPoint = insertionPoint.getPrevious().getPrevious();
// Find the last label
LabelNode lastLabel = (LabelNode)this.findLastType( method.instructions, AbstractInsnNode.LABEL, 1 );
// Insert the hook
// GolemHooks.hook_CustomInteraction( this, player, this.hookHandlers );
InsnList instructionList = new InsnList();
// this
instructionList.add( new VarInsnNode( Opcodes.ALOAD, 0 ) );
// player
instructionList.add( new VarInsnNode( Opcodes.ALOAD, 1 ) );
// this.hookHandlers
this.addGetField_hookHandlers( instructionList, 0, false );
// GolemHooks.setupGolemHook( EntityGolemBase, Hashmap )
instructionList.add( new MethodInsnNode( Opcodes.INVOKESTATIC,
CLASS_GolemHooks, "hook_CustomInteraction",
"(L" + CLASS_EntityGolemBase + ";Lnet/minecraft/entity/player/EntityPlayer;L" + FIELDTYPE_EntityGolemBase_hookHandlers +
";)Z",
false ) );
// Insert the return check
instructionList.add( new JumpInsnNode( Opcodes.IFNE, lastLabel ) );
// Insert the static call
method.instructions.insertBefore( insertionPoint, instructionList );
}
private void transformMethod_GolemBase_EntityInit( final MethodNode method )
{
// Locate the return statement
AbstractInsnNode insertionPoint = this.findLastOpCode( method.instructions, Opcodes.RETURN );
// Insert the hookHandlers initializer
// hookHandlers = new HashMap<IGolemHookHandler, Object>();
InsnList instructionList = new InsnList();
// this
instructionList.add( new VarInsnNode( Opcodes.ALOAD, 0 ) );
// new HashMap
instructionList.add( new TypeInsnNode( Opcodes.NEW, FIELDTYPE_EntityGolemBase_hookHandlers ) );
instructionList.add( new InsnNode( Opcodes.DUP ) );
// Hashmap.<init>
instructionList.add( new MethodInsnNode( Opcodes.INVOKESPECIAL,
FIELDTYPE_EntityGolemBase_hookHandlers, "<init>", "()V", false ) );
// .hookHandlers =
instructionList.add( new FieldInsnNode( Opcodes.PUTFIELD, CLASS_EntityGolemBase,
FIELD_EntityGolemBase_hookHandlers, "L" + FIELDTYPE_EntityGolemBase_hookHandlers + ";" ) );
// Insert the hook
// GolemHooks.hook_EntityInit( this, this.hookHandlers );
// this
instructionList.add( new VarInsnNode( Opcodes.ALOAD, 0 ) );
// this.hookHandlers
this.addGetField_hookHandlers( instructionList, 0, false );
// GolemHooks.hook_EntityInit( EntityGolemBase, Hashmap )
instructionList.add( new MethodInsnNode( Opcodes.INVOKESTATIC,
CLASS_GolemHooks, "hook_EntityInit",
"(L" + CLASS_EntityGolemBase + ";L" + FIELDTYPE_EntityGolemBase_hookHandlers + ";)V", false ) );
// Insert the static call
method.instructions.insertBefore( insertionPoint, instructionList );
}
private void transformMethod_GolemBase_ReadEntityFromNBT( final MethodNode method )
{
// Locate the super call
AbstractInsnNode insertionPoint = this.findFirstOpCode( method.instructions, Opcodes.INVOKESPECIAL );
// Insert the hook
// GolemHooks.hook_ReadEntityFromNBT( this, this.hookHandlers, nbt);
InsnList instructionList = new InsnList();
// this
instructionList.add( new VarInsnNode( Opcodes.ALOAD, 0 ) );
// this.hookHandlers
this.addGetField_hookHandlers( instructionList, 0, false );
// nbt
instructionList.add( new VarInsnNode( Opcodes.ALOAD, 1 ) );
// GolemHooks.hook_ReadEntityFromNBT( EntityGolemBase, Hashmap, NBTTagCompound )
instructionList.add( new MethodInsnNode( Opcodes.INVOKESTATIC,
CLASS_GolemHooks, "hook_ReadEntityFromNBT",
"(L" + CLASS_EntityGolemBase + ";L" + FIELDTYPE_EntityGolemBase_hookHandlers + ";" + "Lnet/minecraft/nbt/NBTTagCompound;" +
")V",
false ) );
// Insert the static call
method.instructions.insert( insertionPoint, instructionList );
}
private void transformMethod_GolemBase_SetupGolem( final MethodNode method )
{
// Set the insertion point to the instruction before return
// Previous node is important, since the method returns a value, the previous
// instruction is what sets that return value.
AbstractInsnNode insertionPoint = this.findLastOpCode( method.instructions, Opcodes.IRETURN );
insertionPoint = insertionPoint.getPrevious();
// Insert the hook
// GolemHooks.setupGolemHook( this, this.hookHandlers );
InsnList instructionList = new InsnList();
// this
instructionList.add( new VarInsnNode( Opcodes.ALOAD, 0 ) );
// this.hookHandlers
this.addGetField_hookHandlers( instructionList, 0, false );
// GolemHooks.setupGolemHook( EntityGolemBase, Hashmap )
instructionList.add( new MethodInsnNode( Opcodes.INVOKESTATIC,
CLASS_GolemHooks, "hook_SetupGolem",
"(L" + CLASS_EntityGolemBase + ";L" + FIELDTYPE_EntityGolemBase_hookHandlers + ";)V", false ) );
// Insert the static call
method.instructions.insertBefore( insertionPoint, instructionList );
}
private void transformMethod_GolemBase_WriteEntityToNBT( final MethodNode method )
{
// Locate the return statement
AbstractInsnNode insertionPoint = this.findLastOpCode( method.instructions, Opcodes.RETURN );
// Insert the hook
// GolemHooks.hook_WriteEntityToNBT( this, this.hookHandlers, nbt);
InsnList instructionList = new InsnList();
// this
instructionList.add( new VarInsnNode( Opcodes.ALOAD, 0 ) );
// this.hookHandlers
this.addGetField_hookHandlers( instructionList, 0, false );
// nbt
instructionList.add( new VarInsnNode( Opcodes.ALOAD, 1 ) );
// GolemHooks.hook_WriteEntityToNBT( EntityGolemBase, Hashmap, NBTTagCompound )
instructionList.add( new MethodInsnNode( Opcodes.INVOKESTATIC,
CLASS_GolemHooks, "hook_WriteEntityToNBT",
"(L" + CLASS_EntityGolemBase + ";L" + FIELDTYPE_EntityGolemBase_hookHandlers + ";" + "Lnet/minecraft/nbt/NBTTagCompound;" +
")V",
false ) );
// Insert the static call
method.instructions.insertBefore( insertionPoint, instructionList );
}
private void transformMethod_ItemGolemBell_OnLeftClickEntity( final MethodNode method )
{
int opSequence[] = new int[] { Opcodes.GETSTATIC, Opcodes.ICONST_1, Opcodes.ILOAD, Opcodes.INVOKESPECIAL, Opcodes.ASTORE };
// Locate the sequence
AbstractInsnNode insertionPoint = this.findSequence( method.instructions, opSequence, true );
// Insert the hook
// GolemHooks.hook_Bell_OnLeftClickGolem( entity, dropped, player, ((EntityGolemBase)entity).hookHandlers )
InsnList instructionList = new InsnList();
// ((EntityGolemBase)entity)
instructionList.add( new VarInsnNode( Opcodes.ALOAD, 3 ) );
instructionList.add( new TypeInsnNode( Opcodes.CHECKCAST, CLASS_EntityGolemBase ) );
// dropped
instructionList.add( new VarInsnNode( Opcodes.ALOAD, 9 ) );
// player
instructionList.add( new VarInsnNode( Opcodes.ALOAD, 2 ) );
// ((EntityGolemBase)entity).hookHandlers
this.addGetField_hookHandlers( instructionList, 3, true );
// GolemHooks.hook_Bell_OnLeftClickGolem( EntityGolemBase, ItemStack, EntityPlayer, Hashmap )
instructionList.add( new MethodInsnNode( Opcodes.INVOKESTATIC,
CLASS_GolemHooks, "hook_Bell_OnLeftClickGolem",
"(L" + CLASS_EntityGolemBase + ";Lnet/minecraft/item/ItemStack;Lnet/minecraft/entity/player/EntityPlayer;L" +
FIELDTYPE_EntityGolemBase_hookHandlers + ";)V",
false ) );
// Insert the static call
method.instructions.insert( insertionPoint, instructionList );
}
private void transformMethod_ItemGolemPlacer_SpawnCreature( final MethodNode method )
{
// Locate "golem.setup(side);"
int opSequence[] = new int[] { Opcodes.PUTFIELD, Opcodes.ALOAD, Opcodes.ILOAD, Opcodes.INVOKEVIRTUAL };
AbstractInsnNode insertionPoint = this.findSequence( method.instructions, opSequence, true );
// Move back to the aload
insertionPoint = insertionPoint.getPrevious().getPrevious();
// Insert the hook
// GolemHooks.hook_Placer_SpawnGolem( golem, stack, golem.hookHandlers )
InsnList instructionList = new InsnList();
// golem
instructionList.add( new VarInsnNode( Opcodes.ALOAD, 12 ) );
// stack
instructionList.add( new VarInsnNode( Opcodes.ALOAD, 9 ) );
// golem.hookHandlers
this.addGetField_hookHandlers( instructionList, 12, false );
// GolemHooks.hook_Placer_SpawnGolem( EntityGolemBase, ItemStack, Hashmap )
instructionList.add( new MethodInsnNode( Opcodes.INVOKESTATIC,
CLASS_GolemHooks, "hook_Placer_SpawnGolem",
"(L" + CLASS_EntityGolemBase + ";Lnet/minecraft/item/ItemStack;L" + FIELDTYPE_EntityGolemBase_hookHandlers + ";)V", false ) );
// Insert the static call
method.instructions.insertBefore( insertionPoint, instructionList );
}
private void transformMethod_RenderGolemBase_Render( final MethodNode method )
{
// Find the return
AbstractInsnNode insertionPoint = this.findLastOpCode( method.instructions, Opcodes.RETURN );
// Insert the hook
// GolemHooks.hook_RenderGolem( e, e.hookHandlers, par2, par4, par6, par9 )
InsnList instructionList = new InsnList();
// e
instructionList.add( new VarInsnNode( Opcodes.ALOAD, 1 ) );
// e.hookHandlers
this.addGetField_hookHandlers( instructionList, 1, false );
// par2
instructionList.add( new VarInsnNode( Opcodes.DLOAD, 2 ) );
// par4
instructionList.add( new VarInsnNode( Opcodes.DLOAD, 4 ) );
// par6
instructionList.add( new VarInsnNode( Opcodes.DLOAD, 6 ) );
// par9
instructionList.add( new VarInsnNode( Opcodes.FLOAD, 9 ) );
// GolemHooks.hook_RenderGolem( EntityGolemBase, Hashmap, double, double, double, float )
instructionList.add( new MethodInsnNode( Opcodes.INVOKESTATIC,
CLASS_GolemHooks, "hook_RenderGolem",
"(L" + CLASS_EntityGolemBase + ";L" + FIELDTYPE_EntityGolemBase_hookHandlers + ";DDDF)V", false ) );
// Insert the static call
method.instructions.insertBefore( insertionPoint, instructionList );
this.tranformers.put( transformer.classCanonicalName, transformer );
}
@Override
public byte[] transform( final String name, final String transformedName, final byte[] basicClass )
{
// AE2 Crafting Process?
if( transformedName.equals( "appeng.crafting.CraftingTreeProcess" ) )
AClassTransformer transformer;
if( ( transformer = this.tranformers.getOrDefault( transformedName, null ) ) != null )
{
try
{
this.log( "Transforming Class (appeng.crafting.CraftingTreeProcess)" );
return this.transformClass_CraftingTreeProcess( basicClass );
}
catch( Exception e )
{
this.log( "Unable to transform (appeng.crafting.CraftingTreeProcess)" );
e.printStackTrace();
}
}
// Thaumcraft class?
else if( transformedName.startsWith( "thaumcraft" ) )
{
if( transformedName.equals( "thaumcraft.common.entities.golems.EntityGolemBase" ) )
{
try
{
this.log( "Transforming Class (thaumcraft.common.entities.golems.EntityGolemBase)" );
return this.transformClass_GolemBase( basicClass );
}
catch( Exception e )
{
this.log( "Unable to transform (thaumcraft.common.entities.golems.EntityGolemBase)" );
ThECore.golemHooksTransformFailed = true;
}
}
else if( transformedName.equals( "thaumcraft.common.entities.golems.ItemGolemBell" ) )
{
try
{
this.log( "Transforming Class (thaumcraft.common.entities.golems.ItemGolemBell)" );
return this.transformClass_ItemGolemBell( basicClass );
}
catch( Exception e )
{
this.log( "Unable to transform (thaumcraft.common.entities.golems.ItemGolemBell)" );
ThECore.golemHooksTransformFailed = true;
}
}
else if( transformedName.equals( "thaumcraft.common.entities.golems.ItemGolemPlacer" ) )
{
try
{
this.log( "Transforming Class (thaumcraft.common.entities.golems.ItemGolemPlacer)" );
return this.transformClass_ItemGolemPlacer( basicClass );
}
catch( Exception e )
{
this.log( "Unable to transform (thaumcraft.common.entities.golems.ItemGolemPlacer)" );
ThECore.golemHooksTransformFailed = true;
}
}
else if( transformedName.equals( "thaumcraft.client.renderers.entity.RenderGolemBase" ) )
{
try
{
this.log( "Transforming Class (thaumcraft/client/renderers/entity/RenderGolemBase)" );
return this.transformClass_RenderGolemBase( basicClass );
}
catch( Exception e )
{
this.log( "Unable to transform (thaumcraft/client/renderers/entity/RenderGolemBase)" );
ThECore.golemHooksTransformFailed = true;
}
}
else if( transformedName.equals( "thaumcraft.api.aspects.Aspect" ) )
{
try
{
this.log( "Transforming Class (thaumcraft.api.aspects.Aspect)" );
return this.transformClass_Aspect( basicClass );
}
catch( Exception e )
{
this.log( "Unable to transform (thaumcraft.api.aspects.Aspect)" );
}
}
return transformer.transformClass( basicClass );
}
return basicClass;
@@ -0,0 +1,66 @@
package thaumicenergistics.fml.classtransformers.ae;
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.tree.*;
import thaumicenergistics.fml.AClassTransformer;
/**
* Applied Energistics class <em>CraftingTreeProcess</em>
* <ul>
* <li>Adds <em>this.world = world</em> to constructor.</li>
* </ul>
*/
public class ClassTransformer_CraftingTreeProcess
extends AClassTransformer
{
public ClassTransformer_CraftingTreeProcess()
{
super( "appeng.crafting.CraftingTreeProcess" );
}
private void transformConstructor( final MethodNode method )
{
int opSequence[] = new int[] { Opcodes.ILOAD, Opcodes.PUTFIELD, Opcodes.ALOAD, Opcodes.INVOKEVIRTUAL, Opcodes.ASTORE };
AbstractInsnNode insertionPoint = this.findSequence( method.instructions, opSequence, true );
// Insert this.world = world
InsnList instructionList = new InsnList();
// this
instructionList.add( new VarInsnNode( Opcodes.ALOAD, 0 ) );
// world
instructionList.add( new VarInsnNode( Opcodes.ALOAD, 6 ) );
// this.world = world
instructionList.add( new FieldInsnNode( Opcodes.PUTFIELD,
"appeng/crafting/CraftingTreeProcess", "world", "Lnet/minecraft/world/World;" ) );
// Insert the new code
method.instructions.insert( insertionPoint, instructionList );
}
@Override
protected void onTransformFailure()
{
this.log( "Recipes containing Thaumcraft's primordial pearl will not function with AE2's crafting system." );
}
@Override
protected void transform( final ClassNode classNode )
{
// Transform methods
for( MethodNode method : classNode.methods )
{
// Constructor
if( method.name.equals( AClassTransformer.InstanceConstructorName ) )
{
this.transformConstructor( method );
break; // Stop searching.
}
}
}
}
@@ -0,0 +1,77 @@
package thaumicenergistics.fml.classtransformers.tc;
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.tree.*;
import thaumicenergistics.fml.AClassTransformer;
/**
* Thaumcraft class <em>Aspect</em>
* <ul>
* <li>Adds hook to <em>constructor</em></li>
* </ul>
*/
public class ClassTransformer_Aspect
extends AClassTransformer
{
private static final String CLASS_Aspect = "thaumcraft/api/aspects/Aspect";
private static final String CLASS_AspectHooks = "thaumicenergistics/common/integration/tc/AspectHooks";
public ClassTransformer_Aspect()
{
super( "thaumcraft.api.aspects.Aspect" );
}
private boolean transformConstructor( final MethodNode method )
{
// Is this the full constructor?
// Check description
if( !"(Ljava/lang/String;I[Lthaumcraft/api/aspects/Aspect;Lnet/minecraft/util/ResourceLocation;I)V".equals( method.desc ) )
{
return false;
}
int opSequence[] = new int[] { Opcodes.GETSTATIC, Opcodes.ALOAD, Opcodes.ALOAD, Opcodes.INVOKEVIRTUAL, Opcodes.POP };
AbstractInsnNode insertionPoint = this.findSequence( method.instructions, opSequence, false );
// Insert the hook
// AspectHooks.hook_AspectInit( this )
InsnList instructionList = new InsnList();
// this
instructionList.add( new VarInsnNode( Opcodes.ALOAD, 0 ) );
// AspectHooks.hook_AspectInit( Aspect )
instructionList.add( new MethodInsnNode( Opcodes.INVOKESTATIC,
ClassTransformer_Aspect.CLASS_AspectHooks, "hook_AspectInit",
"(L" + ClassTransformer_Aspect.CLASS_Aspect + ";)V", false ) );
// Insert the static call
method.instructions.insert( insertionPoint, instructionList );
return true;
}
@Override
protected void onTransformFailure()
{
// No action required.
}
@Override
protected void transform( final ClassNode classNode )
{
// Transform methods
for( MethodNode method : classNode.methods )
{
// Constructor
if( method.name.equals( AClassTransformer.InstanceConstructorName ) )
{
if( this.transformConstructor( method ) )
{
return; // Stop searching.
}
}
}
}
}
@@ -0,0 +1,290 @@
package thaumicenergistics.fml.classtransformers.tc;
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.tree.*;
import thaumicenergistics.fml.AClassTransformer;
import thaumicenergistics.fml.ThECore;
/**
* Thaumcraft class <em>EntityGolemBase</em>
* <ul>
* <li>Adds field <em>hookHandlers</em> of type HashMap</li>
* <li>Adds hook to method <em>setupGolem</em></li>
* <li>Adds hook to method <em>customInteraction</em></li>
* <li>Adds hook to method <em>writeEntityNBT</em></li>
* <li>Adds hook to method <em>readEntityFromNBT</em></li>
* <li>Adds hook to method <em>func_70088_a | entityInit</em></li>
* <li>Adds hook to method <em>func_70030_z | onEntityUpdate</em></li>
* </ul>
*
* @author Nividica
*
*/
public class ClassTransformer_EntityGolemBase
extends AClassTransformer
{
public ClassTransformer_EntityGolemBase()
{
super( "thaumcraft.common.entities.golems.EntityGolemBase" );
}
private void insertMethod_GolemBase_onEntityUpdate( final ClassNode classNode, final boolean isObf )
{
// Create the onEntityUpdate() method
String oEU_Name = ( isObf ? "func_70030_z" : "onEntityUpdate" );
MethodNode onEntityUpdate = new MethodNode( Opcodes.ACC_PUBLIC,
oEU_Name, "()V", null, null );
// super.onEntityUpdate()
onEntityUpdate.instructions.add( new VarInsnNode( Opcodes.ALOAD, 0 ) );
onEntityUpdate.instructions.add( new MethodInsnNode( Opcodes.INVOKESPECIAL,
"net/minecraft/entity/monster/EntityGolem", oEU_Name, "()V", false ) );
// GolemHooks.hook_onEntityUpdate( this, this.hookHandlers )
// this
onEntityUpdate.instructions.add( new VarInsnNode( Opcodes.ALOAD, 0 ) );
// this.hookHandlers
GolemHookTransformHelper.addGetField_hookHandlers( onEntityUpdate.instructions, 0, false );
// GolemHooks.hook_onEntityUpdate( EntityGolemBase, Hashmap )
onEntityUpdate.instructions.add( new MethodInsnNode( Opcodes.INVOKESTATIC,
GolemHookTransformHelper.CLASS_GolemHooks, "hook_onEntityUpdate",
"(L" + GolemHookTransformHelper.CLASS_EntityGolemBase + ";L" + GolemHookTransformHelper.FIELDTYPE_EntityGolemBase_hookHandlers + ";)V", false ) );
// return
onEntityUpdate.instructions.add( new InsnNode( Opcodes.RETURN ) );
// Add the method to the class
classNode.methods.add( onEntityUpdate );
}
private void transformMethod_CustomInteraction( final MethodNode method )
{
int opSequence[] = new int[] { Opcodes.ALOAD, Opcodes.INVOKEVIRTUAL, Opcodes.ICONST_M1 };
AbstractInsnNode insertionPoint = this.findSequence( method.instructions, opSequence, false );
// Go two back
insertionPoint = insertionPoint.getPrevious().getPrevious();
// Find the last label
LabelNode lastLabel = (LabelNode)this.findLastType( method.instructions, AbstractInsnNode.LABEL, 1 );
// Insert the hook
// GolemHooks.hook_CustomInteraction( this, player, this.hookHandlers );
InsnList instructionList = new InsnList();
// this
instructionList.add( new VarInsnNode( Opcodes.ALOAD, 0 ) );
// player
instructionList.add( new VarInsnNode( Opcodes.ALOAD, 1 ) );
// this.hookHandlers
GolemHookTransformHelper.addGetField_hookHandlers( instructionList, 0, false );
// GolemHooks.setupGolemHook( EntityGolemBase, Hashmap )
instructionList.add( new MethodInsnNode( Opcodes.INVOKESTATIC,
GolemHookTransformHelper.CLASS_GolemHooks, "hook_CustomInteraction",
"(L" + GolemHookTransformHelper.CLASS_EntityGolemBase + ";Lnet/minecraft/entity/player/EntityPlayer;L" +
GolemHookTransformHelper.FIELDTYPE_EntityGolemBase_hookHandlers +
";)Z",
false ) );
// Insert the return check
instructionList.add( new JumpInsnNode( Opcodes.IFNE, lastLabel ) );
// Insert the static call
method.instructions.insertBefore( insertionPoint, instructionList );
}
private void transformMethod_EntityInit( final MethodNode method )
{
// Locate the return statement
AbstractInsnNode insertionPoint = this.findLastOpCode( method.instructions, Opcodes.RETURN );
// Insert the hookHandlers initializer
// hookHandlers = new HashMap<IGolemHookHandler, Object>();
InsnList instructionList = new InsnList();
// this
instructionList.add( new VarInsnNode( Opcodes.ALOAD, 0 ) );
// new HashMap
instructionList.add( new TypeInsnNode( Opcodes.NEW, GolemHookTransformHelper.FIELDTYPE_EntityGolemBase_hookHandlers ) );
instructionList.add( new InsnNode( Opcodes.DUP ) );
// Hashmap.<init>
instructionList.add( new MethodInsnNode( Opcodes.INVOKESPECIAL,
GolemHookTransformHelper.FIELDTYPE_EntityGolemBase_hookHandlers, "<init>", "()V", false ) );
// .hookHandlers =
instructionList.add( new FieldInsnNode( Opcodes.PUTFIELD, GolemHookTransformHelper.CLASS_EntityGolemBase,
GolemHookTransformHelper.FIELD_EntityGolemBase_hookHandlers, "L" + GolemHookTransformHelper.FIELDTYPE_EntityGolemBase_hookHandlers + ";" ) );
// Insert the hook
// GolemHooks.hook_EntityInit( this, this.hookHandlers );
// this
instructionList.add( new VarInsnNode( Opcodes.ALOAD, 0 ) );
// this.hookHandlers
GolemHookTransformHelper.addGetField_hookHandlers( instructionList, 0, false );
// GolemHooks.hook_EntityInit( EntityGolemBase, Hashmap )
instructionList.add( new MethodInsnNode( Opcodes.INVOKESTATIC,
GolemHookTransformHelper.CLASS_GolemHooks, "hook_EntityInit",
"(L" + GolemHookTransformHelper.CLASS_EntityGolemBase + ";L" + GolemHookTransformHelper.FIELDTYPE_EntityGolemBase_hookHandlers + ";)V", false ) );
// Insert the static call
method.instructions.insertBefore( insertionPoint, instructionList );
}
private void transformMethod_ReadEntityFromNBT( final MethodNode method )
{
// Locate the super call
AbstractInsnNode insertionPoint = this.findFirstOpCode( method.instructions, Opcodes.INVOKESPECIAL );
// Insert the hook
// GolemHooks.hook_ReadEntityFromNBT( this, this.hookHandlers, nbt);
InsnList instructionList = new InsnList();
// this
instructionList.add( new VarInsnNode( Opcodes.ALOAD, 0 ) );
// this.hookHandlers
GolemHookTransformHelper.addGetField_hookHandlers( instructionList, 0, false );
// nbt
instructionList.add( new VarInsnNode( Opcodes.ALOAD, 1 ) );
// GolemHooks.hook_ReadEntityFromNBT( EntityGolemBase, Hashmap, NBTTagCompound )
instructionList.add( new MethodInsnNode( Opcodes.INVOKESTATIC,
GolemHookTransformHelper.CLASS_GolemHooks, "hook_ReadEntityFromNBT",
"(L" + GolemHookTransformHelper.CLASS_EntityGolemBase + ";L" + GolemHookTransformHelper.FIELDTYPE_EntityGolemBase_hookHandlers + ";" +
"Lnet/minecraft/nbt/NBTTagCompound;" +
")V",
false ) );
// Insert the static call
method.instructions.insert( insertionPoint, instructionList );
}
private void transformMethod_SetupGolem( final MethodNode method )
{
// Set the insertion point to the instruction before return
// Previous node is important, since the method returns a value, the previous
// instruction is what sets that return value.
AbstractInsnNode insertionPoint = this.findLastOpCode( method.instructions, Opcodes.IRETURN );
insertionPoint = insertionPoint.getPrevious();
// Insert the hook
// GolemHooks.setupGolemHook( this, this.hookHandlers );
InsnList instructionList = new InsnList();
// this
instructionList.add( new VarInsnNode( Opcodes.ALOAD, 0 ) );
// this.hookHandlers
GolemHookTransformHelper.addGetField_hookHandlers( instructionList, 0, false );
// GolemHooks.setupGolemHook( EntityGolemBase, Hashmap )
instructionList.add( new MethodInsnNode( Opcodes.INVOKESTATIC,
GolemHookTransformHelper.CLASS_GolemHooks, "hook_SetupGolem",
"(L" + GolemHookTransformHelper.CLASS_EntityGolemBase + ";L" + GolemHookTransformHelper.FIELDTYPE_EntityGolemBase_hookHandlers + ";)V", false ) );
// Insert the static call
method.instructions.insertBefore( insertionPoint, instructionList );
}
private void transformMethod_WriteEntityToNBT( final MethodNode method )
{
// Locate the return statement
AbstractInsnNode insertionPoint = this.findLastOpCode( method.instructions, Opcodes.RETURN );
// Insert the hook
// GolemHooks.hook_WriteEntityToNBT( this, this.hookHandlers, nbt);
InsnList instructionList = new InsnList();
// this
instructionList.add( new VarInsnNode( Opcodes.ALOAD, 0 ) );
// this.hookHandlers
GolemHookTransformHelper.addGetField_hookHandlers( instructionList, 0, false );
// nbt
instructionList.add( new VarInsnNode( Opcodes.ALOAD, 1 ) );
// GolemHooks.hook_WriteEntityToNBT( EntityGolemBase, Hashmap, NBTTagCompound )
instructionList.add( new MethodInsnNode( Opcodes.INVOKESTATIC,
GolemHookTransformHelper.CLASS_GolemHooks, "hook_WriteEntityToNBT",
"(L" + GolemHookTransformHelper.CLASS_EntityGolemBase + ";L" + GolemHookTransformHelper.FIELDTYPE_EntityGolemBase_hookHandlers + ";" +
"Lnet/minecraft/nbt/NBTTagCompound;" +
")V",
false ) );
// Insert the static call
method.instructions.insertBefore( insertionPoint, instructionList );
}
@Override
protected void onTransformFailure()
{
ThECore.golemHooksTransformFailed = true;
}
@Override
protected void transform( final ClassNode classNode )
{
// Add the handlers field
// public HashMap<IGolemHookHandler, Object> hookHandlers;
classNode.fields.add( new FieldNode( Opcodes.ACC_PUBLIC,
GolemHookTransformHelper.FIELD_EntityGolemBase_hookHandlers,
"L" + GolemHookTransformHelper.FIELDTYPE_EntityGolemBase_hookHandlers + ";",
"L" + GolemHookTransformHelper.FIELDTYPE_EntityGolemBase_hookHandlers + "<L" + GolemHookTransformHelper.INTERFACE_IGolemHookHandler +
";Ljava/lang/Object;>;",
null ) );
boolean isObf = false;
// Transform methods
for( MethodNode method : classNode.methods )
{
// Look for an obfuscated method
if( method.name.equals( "func_70014_b" ) )
{
isObf = true;
}
// Setup golem
if( method.name.equals( "setupGolem" ) )
{
this.transformMethod_SetupGolem( method );
}
// Custom interaction
else if( method.name.equals( "customInteraction" ) )
{
this.transformMethod_CustomInteraction( method );
}
// Write entity NBT
else if( method.name.equals( "func_70014_b" ) || method.name.equals( "writeEntityToNBT" ) )
{
this.transformMethod_WriteEntityToNBT( method );
}
// Read entity from NBT
else if( method.name.equals( "func_70037_a" ) || method.name.equals( "readEntityFromNBT" ) )
{
this.transformMethod_ReadEntityFromNBT( method );
}
// Entity Init
else if( method.name.equals( "func_70088_a" ) || method.name.equals( "entityInit" ) )
{
this.transformMethod_EntityInit( method );
}
}
// Add the onEntityUpdate method.
this.insertMethod_GolemBase_onEntityUpdate( classNode, isObf );
}
}
@@ -0,0 +1,82 @@
package thaumicenergistics.fml.classtransformers.tc;
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.tree.*;
import thaumicenergistics.fml.AClassTransformer;
import thaumicenergistics.fml.ThECore;
/**
* Thaumcraft class <em>ItemGolemBell</em>
* <ul>
* <li>Adds hook to method <em>onLeftClickEntity</em></li>
* </ul>
*
* @author Nividica
*
*/
public class ClassTransformer_ItemGolemBell
extends AClassTransformer
{
public ClassTransformer_ItemGolemBell()
{
super( "thaumcraft.common.entities.golems.ItemGolemBell" );
}
private void transformMethod_OnLeftClickEntity( final MethodNode method )
{
int opSequence[] = new int[] { Opcodes.GETSTATIC, Opcodes.ICONST_1, Opcodes.ILOAD, Opcodes.INVOKESPECIAL, Opcodes.ASTORE };
// Locate the sequence
AbstractInsnNode insertionPoint = this.findSequence( method.instructions, opSequence, true );
// Insert the hook
// GolemHooks.hook_Bell_OnLeftClickGolem( entity, dropped, player, ((EntityGolemBase)entity).hookHandlers )
InsnList instructionList = new InsnList();
// ((EntityGolemBase)entity)
instructionList.add( new VarInsnNode( Opcodes.ALOAD, 3 ) );
instructionList.add( new TypeInsnNode( Opcodes.CHECKCAST, GolemHookTransformHelper.CLASS_EntityGolemBase ) );
// dropped
instructionList.add( new VarInsnNode( Opcodes.ALOAD, 9 ) );
// player
instructionList.add( new VarInsnNode( Opcodes.ALOAD, 2 ) );
// ((EntityGolemBase)entity).hookHandlers
GolemHookTransformHelper.addGetField_hookHandlers( instructionList, 3, true );
// GolemHooks.hook_Bell_OnLeftClickGolem( EntityGolemBase, ItemStack, EntityPlayer, Hashmap )
instructionList.add( new MethodInsnNode( Opcodes.INVOKESTATIC,
GolemHookTransformHelper.CLASS_GolemHooks, "hook_Bell_OnLeftClickGolem",
"(L" + GolemHookTransformHelper.CLASS_EntityGolemBase + ";Lnet/minecraft/item/ItemStack;Lnet/minecraft/entity/player/EntityPlayer;L" +
GolemHookTransformHelper.FIELDTYPE_EntityGolemBase_hookHandlers + ";)V",
false ) );
// Insert the static call
method.instructions.insert( insertionPoint, instructionList );
}
@Override
protected void onTransformFailure()
{
ThECore.golemHooksTransformFailed = true;
}
@Override
protected void transform( final ClassNode classNode )
{
// Transform methods
for( MethodNode method : classNode.methods )
{
// Left click entity
if( method.name.equals( "onLeftClickEntity" ) )
{
this.transformMethod_OnLeftClickEntity( method );
return; // Stop searching.
}
}
}
}
@@ -0,0 +1,81 @@
package thaumicenergistics.fml.classtransformers.tc;
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.tree.*;
import thaumicenergistics.fml.AClassTransformer;
import thaumicenergistics.fml.ThECore;
/**
* Thaumcraft class <em>ItemGolemPlacer</em>
* <ul>
* <li>Adds hook to method <em>spawnCreature</em></li>
* </ul>
*
* @author Nividica
*
*/
public class ClassTransformer_ItemGolemPlacer
extends AClassTransformer
{
public ClassTransformer_ItemGolemPlacer()
{
super( "thaumcraft.common.entities.golems.ItemGolemPlacer" );
}
private void transformMethod_SpawnCreature( final MethodNode method )
{
// Locate "golem.setup(side);"
int opSequence[] = new int[] { Opcodes.PUTFIELD, Opcodes.ALOAD, Opcodes.ILOAD, Opcodes.INVOKEVIRTUAL };
AbstractInsnNode insertionPoint = this.findSequence( method.instructions, opSequence, true );
// Move back to the aload
insertionPoint = insertionPoint.getPrevious().getPrevious();
// Insert the hook
// GolemHooks.hook_Placer_SpawnGolem( golem, stack, golem.hookHandlers )
InsnList instructionList = new InsnList();
// golem
instructionList.add( new VarInsnNode( Opcodes.ALOAD, 12 ) );
// stack
instructionList.add( new VarInsnNode( Opcodes.ALOAD, 9 ) );
// golem.hookHandlers
GolemHookTransformHelper.addGetField_hookHandlers( instructionList, 12, false );
// GolemHooks.hook_Placer_SpawnGolem( EntityGolemBase, ItemStack, Hashmap )
instructionList.add( new MethodInsnNode( Opcodes.INVOKESTATIC,
GolemHookTransformHelper.CLASS_GolemHooks, "hook_Placer_SpawnGolem",
"(L" + GolemHookTransformHelper.CLASS_EntityGolemBase + ";Lnet/minecraft/item/ItemStack;L" +
GolemHookTransformHelper.FIELDTYPE_EntityGolemBase_hookHandlers + ";)V",
false ) );
// Insert the static call
method.instructions.insertBefore( insertionPoint, instructionList );
}
@Override
protected void onTransformFailure()
{
ThECore.golemHooksTransformFailed = true;
}
@Override
protected void transform( final ClassNode classNode )
{
// Transform methods
for( MethodNode method : classNode.methods )
{
// Spawn creature
if( method.name.equals( "spawnCreature" ) )
{
this.transformMethod_SpawnCreature( method );
break; // Stop searching.
}
}
}
}
@@ -0,0 +1,84 @@
package thaumicenergistics.fml.classtransformers.tc;
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.tree.*;
import thaumicenergistics.fml.AClassTransformer;
import thaumicenergistics.fml.ThECore;
/**
* Thaumcraft class <em>RenderGolemBase</em>
* <ul>
* <li>Adds hook to method <em>render</em></li>
* </ul>
*
* @author Nividica
*
*/
public class ClassTransformer_RenderGolemBase
extends AClassTransformer
{
public ClassTransformer_RenderGolemBase()
{
super( "thaumcraft.client.renderers.entity.RenderGolemBase" );
}
private void transformMethod_Render( final MethodNode method )
{
// Find the return
AbstractInsnNode insertionPoint = this.findLastOpCode( method.instructions, Opcodes.RETURN );
// Insert the hook
// GolemHooks.hook_RenderGolem( e, e.hookHandlers, par2, par4, par6, par9 )
InsnList instructionList = new InsnList();
// e
instructionList.add( new VarInsnNode( Opcodes.ALOAD, 1 ) );
// e.hookHandlers
GolemHookTransformHelper.addGetField_hookHandlers( instructionList, 1, false );
// par2
instructionList.add( new VarInsnNode( Opcodes.DLOAD, 2 ) );
// par4
instructionList.add( new VarInsnNode( Opcodes.DLOAD, 4 ) );
// par6
instructionList.add( new VarInsnNode( Opcodes.DLOAD, 6 ) );
// par9
instructionList.add( new VarInsnNode( Opcodes.FLOAD, 9 ) );
// GolemHooks.hook_RenderGolem( EntityGolemBase, Hashmap, double, double, double, float )
instructionList.add( new MethodInsnNode( Opcodes.INVOKESTATIC,
GolemHookTransformHelper.CLASS_GolemHooks, "hook_RenderGolem",
"(L" + GolemHookTransformHelper.CLASS_EntityGolemBase + ";L" + GolemHookTransformHelper.FIELDTYPE_EntityGolemBase_hookHandlers + ";DDDF)V",
false ) );
// Insert the static call
method.instructions.insertBefore( insertionPoint, instructionList );
}
@Override
protected void onTransformFailure()
{
ThECore.golemHooksTransformFailed = true;
}
@Override
protected void transform( final ClassNode classNode )
{
// Transform methods
for( MethodNode method : classNode.methods )
{
if( method.name.equals( "render" ) )
{
// Render
this.transformMethod_Render( method );
break; // Stop searching.
}
}
}
}
@@ -0,0 +1,39 @@
package thaumicenergistics.fml.classtransformers.tc;
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.tree.FieldInsnNode;
import org.objectweb.asm.tree.InsnList;
import org.objectweb.asm.tree.TypeInsnNode;
import org.objectweb.asm.tree.VarInsnNode;
class GolemHookTransformHelper
{
public static final String CLASS_EntityGolemBase = "thaumcraft/common/entities/golems/EntityGolemBase";
public static final String CLASS_GolemHooks = "thaumicenergistics/common/integration/tc/GolemHooks";
public static final String INTERFACE_IGolemHookHandler = "thaumicenergistics/api/entities/IGolemHookHandler";
public static final String FIELD_EntityGolemBase_hookHandlers = "hookHandlers";
public static final String FIELDTYPE_EntityGolemBase_hookHandlers = "java/util/HashMap";
/**
* Adds a reference to a golems hookHandlers field to the stack.
*
* @param instructionList
* @param golemVar
* @param checkCast
* If the var specified is not type EntityGolemBase(such as just Entity or EntityLiving) a cast check must be performed.
*/
public static void addGetField_hookHandlers( final InsnList instructionList, final int golemVar, final boolean checkCast )
{
instructionList.add( new VarInsnNode( Opcodes.ALOAD, golemVar ) );
if( checkCast )
{
instructionList.add( new TypeInsnNode( Opcodes.CHECKCAST, GolemHookTransformHelper.CLASS_EntityGolemBase ) );
}
instructionList.add( new FieldInsnNode( Opcodes.GETFIELD,
GolemHookTransformHelper.CLASS_EntityGolemBase,
GolemHookTransformHelper.FIELD_EntityGolemBase_hookHandlers,
"L" + GolemHookTransformHelper.FIELDTYPE_EntityGolemBase_hookHandlers + ";" ) );
}
}