mirror of
https://github.com/GTNewHorizons/ThaumicEnergistics.git
synced 2026-10-25 20:32:27 +00:00
ASM Transformer cleanup.
Cleaned up ASM transformer code.
This commit is contained in:
1 parent
faf76b8009
commit
3b7d01e83a
13 files changed
+958
-832
No files matched your search
+4
-1
@@ -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
@@ -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;
|
||||
|
||||
+66
@@ -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.
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
+77
@@ -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.
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
+290
@@ -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 );
|
||||
}
|
||||
|
||||
}
|
||||
+82
@@ -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.
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
+81
@@ -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.
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
+84
@@ -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.
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
+39
@@ -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 + ";" ) );
|
||||
}
|
||||
|
||||
}
|
||||
Reference in new issue
Block a user