Transfer ASM coremod to mixins (#96)

This commit is contained in:
Alexdoru authored and GitHub committed 2025-12-08 20:03:08 +09:00
1 parent 43fa73e378
commit 283bb6049c
28 files changed
+281 -1188

No files matched your search

-6
View File
@@ -33,9 +33,3 @@ How To Compile
2. Run ./gradlew setupDecompWorkspace build
3. Your build shall be in build/libs/
How To Debug
----
1. Do step 1 of compile
2. Add "-Dfml.coreMods.load=thaumicenergistics.fml.ThECore", without quotes, to your run & debug configuration.
In Eclipse, Menu: Run > Run|Debug Configurations > Arguments Tab > VM Arguments.
+8 -8
View File
@@ -1,20 +1,20 @@
// Add your dependencies here
dependencies {
implementation("com.github.GTNewHorizons:NotEnoughItems:2.7.84-GTNH:dev")
implementation("com.github.GTNewHorizons:CodeChickenCore:1.4.7:dev")
implementation("com.github.GTNewHorizons:Applied-Energistics-2-Unofficial:rv3-beta-684-GTNH:dev")
implementation("com.github.GTNewHorizons:NotEnoughItems:2.8.40-GTNH:dev")
implementation("com.github.GTNewHorizons:CodeChickenCore:1.4.8:dev")
implementation("com.github.GTNewHorizons:Applied-Energistics-2-Unofficial:rv3-beta-751-GTNH:dev")
implementation("thaumcraft:Thaumcraft:1.7.10-4.2.3.5:dev")
implementation("curse.maven:thaumcraft-nei-plugin-225095:2241913") { transitive = false }
implementation(rfg.deobf("curse.maven:thaumcraft-nei-plugin-225095:2241913")) { transitive = false }
compileOnly('com.github.GTNewHorizons:ExtraCells2:2.5.35:dev') { transitive = false }
compileOnly('com.github.GTNewHorizons:AE2FluidCraft-Rework:1.4.114-gtnh:dev') { transitive = false }
compileOnly("com.github.GTNewHorizons:waila:1.8.12:dev") { transitive = false }
compileOnly("com.github.GTNewHorizons:GT5-Unofficial:5.09.51.454:dev") { transitive = false }
compileOnly('com.github.GTNewHorizons:AE2FluidCraft-Rework:1.5.26-gtnh:dev') { transitive = false }
compileOnly("com.github.GTNewHorizons:waila:1.9.15:dev") { transitive = false }
compileOnly("com.github.GTNewHorizons:GT5-Unofficial:5.09.52.132:dev") { transitive = false }
compileOnly("curse.maven:computercraft-67504:2269339") { transitive = false }
compileOnly("curse.maven:cofh-lib-220333:2388748") { transitive = false }
compileOnly("net.industrial-craft:industrialcraft-2:2.2.828-experimental:dev") { transitive = false }
runtimeOnlyNonPublishable('com.github.GTNewHorizons:GTNHLib:0.6.39:dev')
runtimeOnlyNonPublishable('com.github.GTNewHorizons:GTNHLib:0.8.18:dev')
runtimeOnly("com.github.GTNewHorizons:Baubles:1.0.4:dev")
}
+13 -5
View File
@@ -83,7 +83,7 @@ apiPackage = api
accessTransformersFile =
# Provides setup for Mixins if enabled. If you don't know what mixins are: Keep it disabled!
usesMixins = false
usesMixins = true
# Set to a non-empty string to configure mixins in a separate source set under src/VALUE, instead of src/main.
# This can speed up compile times thanks to not running the mixin annotation processor on all input sources.
@@ -91,18 +91,20 @@ usesMixins = false
separateMixinSourceSet =
# Adds some debug arguments like verbose output and class export.
usesMixinDebug = false
usesMixinDebug = true
# Specify the location of your implementation of IMixinConfigPlugin. Leave it empty otherwise.
mixinPlugin =
# Specify the package that contains all of your Mixins. You may only place Mixins in this package or the build will fail!
mixinsPackage =
# Specify the package that contains all of your Mixins. The package must exist or
# the build will fail. If you have a package property defined in your mixins.<modid>.json,
# it must match with this or the build will fail.
mixinsPackage = mixins
# Specify the core mod entry class if you use a core mod. This class must implement IFMLLoadingPlugin!
# This parameter is for legacy compatibility only
# Example value: (coreModClass = asm.FMLPlugin) + (modGroup = com.myname.mymodid) -> com.myname.mymodid.asm.FMLPlugin
coreModClass = fml.ThECore
coreModClass =
# If your project is only a consolidation of mixins or a core mod and does NOT contain a 'normal' mod ( = some class
# that is annotated with @Mod) you want this to be true. When in doubt: leave it on false!
@@ -172,6 +174,12 @@ curseForgeRelations =
# projects. New projects should not use this parameter.
# customArchiveBaseName =
# Optional parameter to customize the default working directory used by the runClient* tasks. Relative to the project directory.
# runClientWorkingDirectory = run/client
# Optional parameter to customize the default working directory used by the runServer* tasks. Relative to the project directory.
# runServerWorkingDirectory = run/server
# Optional parameter to have the build automatically fail if an illegal version is used.
# This can be useful if you e.g. only want to allow versions in the form of '1.1.xxx'.
# The check is ONLY performed if the version is a git tag.
+1 -1
View File
@@ -1,6 +1,6 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-8.13-bin.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-8.14.3-bin.zip
networkTimeout=10000
validateDistributionUrl=true
zipStoreBase=GRADLE_USER_HOME
+1 -1
View File
@@ -17,7 +17,7 @@ pluginManagement {
}
plugins {
id 'com.gtnewhorizons.gtnhsettingsconvention' version '1.0.42'
id 'com.gtnewhorizons.gtnhsettingsconvention' version '1.0.48'
}
@@ -40,7 +40,7 @@ import thaumicenergistics.common.utils.ThELog;
modid = ThaumicEnergistics.MOD_ID,
name = "Thaumic Energistics",
version = ThaumicEnergistics.VERSION,
dependencies = "required-after:ThE-core;required-after:appliedenergistics2@[rv3-beta-214,);required-after:Thaumcraft@[4.2.3.5,);after:Waila;after:extracells")
dependencies = "required-after:appliedenergistics2@[rv3-beta-214,);required-after:Thaumcraft@[4.2.3.5,);after:Waila;after:extracells")
public class ThaumicEnergistics {
/**
@@ -14,7 +14,6 @@ import thaumicenergistics.common.registries.RecipeRegistry;
import thaumicenergistics.common.registries.ResearchRegistry;
import thaumicenergistics.common.registries.ResearchRegistry.PseudoResearchTypes;
import thaumicenergistics.common.registries.ResearchRegistry.ResearchTypes;
import thaumicenergistics.fml.ThECore;
/**
* {@link ItemGolemWirelessBackpack} feature.
@@ -30,11 +29,6 @@ public class FeatureGolemBackpack extends ThEThaumcraftResearchFeature {
@Override
protected boolean checkConfigs(final IThEConfig theConfig) {
// Did the core fail to transform the required classes?
if (ThECore.golemHooksTransformFailed) {
return false;
}
return theConfig.craftGolemWifiBackpack();
}
@@ -18,7 +18,6 @@ import thaumicenergistics.api.entities.IGolemHookHandler.InteractionLevel;
import thaumicenergistics.api.entities.IGolemHookSyncRegistry;
import thaumicenergistics.common.utils.EffectiveSide;
import thaumicenergistics.common.utils.ThELog;
import thaumicenergistics.fml.ThECore;
/**
* Golem hook system.<br>
@@ -565,11 +564,6 @@ public class GolemHooks {
* @param handler
*/
public static void registerHandler(final @Nonnull IGolemHookHandler handler) {
// Ensure the required transforms are present.
if (ThECore.golemHooksTransformFailed) {
return;
}
try {
// Add the handler
GolemHooks.registeredHandlers.add(handler);
@@ -11,35 +11,24 @@ import cpw.mods.fml.relauncher.Side;
*/
public final class EffectiveSide {
/**
* Cache the handler
*/
private static FMLCommonHandler FCH = FMLCommonHandler.instance();
/**
* True if the thread executing this code is client side.
*
* @return
*/
public static final boolean isClientSide() {
return FCH.getEffectiveSide().isClient();
public static boolean isClientSide() {
return FMLCommonHandler.instance().getEffectiveSide().isClient();
}
/**
* True if the thread executing this code is server side.
*
* @return
*/
public static final boolean isServerSide() {
return FCH.getEffectiveSide().isServer();
public static boolean isServerSide() {
return FMLCommonHandler.instance().getEffectiveSide().isServer();
}
/**
* Returns the effective side for the context in the game.
*
* @return
*/
public static final Side side() {
return FCH.getEffectiveSide();
public static Side side() {
return FMLCommonHandler.instance().getEffectiveSide();
}
}
@@ -1,213 +0,0 @@
package thaumicenergistics.fml;
import java.io.PrintWriter;
import java.io.StringWriter;
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 org.objectweb.asm.util.Printer;
import org.objectweb.asm.util.Textifier;
import org.objectweb.asm.util.TraceMethodVisitor;
import cpw.mods.fml.relauncher.FMLRelaunchLog;
public abstract class AClassTransformer {
protected static final String InstanceConstructorName = "<init>";
private static Printer asmPrinter = new Textifier();
private static TraceMethodVisitor asmVisitor = new TraceMethodVisitor(AClassTransformer.asmPrinter);
/**
* 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;
}
/**
* Finds the next occurrence of the opcode after the specified instruction.
*
* @param fromInstruction
* @param opcode
* @return
*/
protected AbstractInsnNode findNextOpCode(final AbstractInsnNode fromInstruction, final int opcode) {
AbstractInsnNode nextInsn = fromInstruction;
do {
if (nextInsn.getOpcode() == opcode) {
return nextInsn;
}
} while ((nextInsn = nextInsn.getNext()) != null);
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);
}
protected void logInstructionDetails(final AbstractInsnNode insn) {
// Visit the instruction
insn.accept(AClassTransformer.asmVisitor);
// Create the string writer
StringWriter sw = new StringWriter();
// Print the details into the writer
AClassTransformer.asmPrinter.print(new PrintWriter(sw));
// Clear the printer
AClassTransformer.asmPrinter.getText().clear();
// Log the details
this.log(sw.toString());
}
/**
* 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,55 +0,0 @@
package thaumicenergistics.fml;
import java.util.HashMap;
import net.minecraft.launchwrapper.IClassTransformer;
import thaumicenergistics.fml.classtransformers.ae.ClassTransformer_CraftingTreeProcess;
import thaumicenergistics.fml.classtransformers.tc.ClassTransformer_Aspect;
import thaumicenergistics.fml.classtransformers.tc.ClassTransformer_EntityGolemBase;
import thaumicenergistics.fml.classtransformers.tc.ClassTransformer_ItemGolemBell;
import thaumicenergistics.fml.classtransformers.tc.ClassTransformer_ItemGolemPlacer;
import thaumicenergistics.fml.classtransformers.tc.ClassTransformer_RenderGolemBase;
public class ASMTransformer implements IClassTransformer {
/**
* Map of all transformers<br>
* Class name -> Transformer
*/
private final HashMap<String, AClassTransformer> tranformers;
public ASMTransformer() {
// Create the transformers map
this.tranformers = new HashMap<>();
// 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());
}
/**
* Adds a transformer to the map.
*
* @param transformer
*/
private void addTransformer(final AClassTransformer transformer) {
this.tranformers.put(transformer.classCanonicalName, transformer);
}
@Override
public byte[] transform(final String name, final String transformedName, final byte[] basicClass) {
AClassTransformer transformer;
if ((transformer = this.tranformers.getOrDefault(transformedName, null)) != null) {
return transformer.transformClass(basicClass);
}
return basicClass;
}
}
@@ -1,94 +0,0 @@
package thaumicenergistics.fml;
import java.util.Map;
import com.google.common.eventbus.EventBus;
import cpw.mods.fml.common.DummyModContainer;
import cpw.mods.fml.common.LoadController;
import cpw.mods.fml.common.ModMetadata;
import cpw.mods.fml.relauncher.IFMLLoadingPlugin;
import cpw.mods.fml.relauncher.IFMLLoadingPlugin.MCVersion;
/**
* Thaumic Energistics Core Mod.
*
* @author Nividica
*
*/
@MCVersion("1.7.10")
public class ThECore extends DummyModContainer implements IFMLLoadingPlugin {
/**
* Set to true if any classes required for golem hooks can not be transformed.
*/
public static boolean golemHooksTransformFailed = false;
private final ModMetadata metadata = new ModMetadata();
public ThECore() {
this.metadata.autogenerated = false;
this.metadata.authorList.add("Nividica");
this.metadata.credits = "Nividica";
this.metadata.modId = this.getModId();
this.metadata.version = this.getVersion();
this.metadata.name = this.getName();
this.metadata.url = "http://minecraft.curseforge.com/projects/thaumic-energistics";
this.metadata.logoFile = "assets/thaumicenergistics/meta/logo.png";
this.metadata.description = "Embedded Coremod for Thaumic Energistics";
this.metadata.parent = "thaumicenergistics";
}
@Override
public String getAccessTransformerClass() {
return null;
}
@Override
public String[] getASMTransformerClass() {
return new String[] { "thaumicenergistics.fml.ASMTransformer" };
}
@Override
public String getDisplayVersion() {
return this.getVersion();
}
@Override
public ModMetadata getMetadata() {
return this.metadata;
}
@Override
public String getModContainerClass() {
return "thaumicenergistics.fml.ThECore";
}
@Override
public String getModId() {
return "ThE-core";
}
@Override
public String getName() {
return "Thaumic Energistics Core";
}
@Override
public String getSetupClass() {
return null;
}
@Override
public String getVersion() {
return "1.0.0.1";
}
@Override
public void injectData(final Map<String, Object> data) {}
@Override
public boolean registerBus(final EventBus bus, final LoadController controller) {
return true;
}
}
@@ -1,67 +0,0 @@
package thaumicenergistics.fml.classtransformers.ae;
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.tree.AbstractInsnNode;
import org.objectweb.asm.tree.ClassNode;
import org.objectweb.asm.tree.FieldInsnNode;
import org.objectweb.asm.tree.InsnList;
import org.objectweb.asm.tree.MethodNode;
import org.objectweb.asm.tree.VarInsnNode;
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.
}
}
}
}
@@ -1,79 +0,0 @@
package thaumicenergistics.fml.classtransformers.tc;
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.tree.AbstractInsnNode;
import org.objectweb.asm.tree.ClassNode;
import org.objectweb.asm.tree.InsnList;
import org.objectweb.asm.tree.MethodInsnNode;
import org.objectweb.asm.tree.MethodNode;
import org.objectweb.asm.tree.VarInsnNode;
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.
}
}
}
}
}
@@ -1,336 +0,0 @@
package thaumicenergistics.fml.classtransformers.tc;
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.tree.AbstractInsnNode;
import org.objectweb.asm.tree.ClassNode;
import org.objectweb.asm.tree.FieldInsnNode;
import org.objectweb.asm.tree.FieldNode;
import org.objectweb.asm.tree.InsnList;
import org.objectweb.asm.tree.InsnNode;
import org.objectweb.asm.tree.JumpInsnNode;
import org.objectweb.asm.tree.LabelNode;
import org.objectweb.asm.tree.MethodInsnNode;
import org.objectweb.asm.tree.MethodNode;
import org.objectweb.asm.tree.TypeInsnNode;
import org.objectweb.asm.tree.VarInsnNode;
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);
}
}
@@ -1,86 +0,0 @@
package thaumicenergistics.fml.classtransformers.tc;
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.tree.AbstractInsnNode;
import org.objectweb.asm.tree.ClassNode;
import org.objectweb.asm.tree.InsnList;
import org.objectweb.asm.tree.MethodInsnNode;
import org.objectweb.asm.tree.MethodNode;
import org.objectweb.asm.tree.TypeInsnNode;
import org.objectweb.asm.tree.VarInsnNode;
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.
}
}
}
}
@@ -1,82 +0,0 @@
package thaumicenergistics.fml.classtransformers.tc;
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.tree.AbstractInsnNode;
import org.objectweb.asm.tree.ClassNode;
import org.objectweb.asm.tree.InsnList;
import org.objectweb.asm.tree.MethodInsnNode;
import org.objectweb.asm.tree.MethodNode;
import org.objectweb.asm.tree.VarInsnNode;
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.
}
}
}
}
@@ -1,87 +0,0 @@
package thaumicenergistics.fml.classtransformers.tc;
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.tree.AbstractInsnNode;
import org.objectweb.asm.tree.ClassNode;
import org.objectweb.asm.tree.InsnList;
import org.objectweb.asm.tree.MethodInsnNode;
import org.objectweb.asm.tree.MethodNode;
import org.objectweb.asm.tree.VarInsnNode;
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.
}
}
}
}
@@ -1,38 +0,0 @@
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 + ";"));
}
}
@@ -0,0 +1,35 @@
package thaumicenergistics.mixins;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import javax.annotation.Nonnull;
import com.gtnewhorizon.gtnhmixins.ILateMixinLoader;
import com.gtnewhorizon.gtnhmixins.LateMixin;
import cpw.mods.fml.relauncher.FMLLaunchHandler;
@LateMixin
public class ThaumicEnergisticsLateMixinLoader implements ILateMixinLoader {
@Override
public String getMixinConfig() {
return "mixins.thaumicenergistics.late.json";
}
@Nonnull
@Override
public List<String> getMixins(Set<String> loadedMods) {
List<String> mixins = new ArrayList<>();
if (FMLLaunchHandler.side().isClient()) {
mixins.add("thaumcraft.MixinRenderGolemBase");
}
mixins.add("thaumcraft.MixinAspect");
mixins.add("thaumcraft.MixinEntityGolemBase");
mixins.add("thaumcraft.MixinItemGolemBell");
mixins.add("thaumcraft.MixinItemGolemPlacer");
return mixins;
}
}
@@ -0,0 +1,10 @@
package thaumicenergistics.mixins.interfaces;
import java.util.HashMap;
import thaumicenergistics.api.entities.IGolemHookHandler;
public interface EntityGolemBaseExt {
HashMap<IGolemHookHandler, Object> thenergistic$getHandlers();
}
@@ -0,0 +1,24 @@
package thaumicenergistics.mixins.late.thaumcraft;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
import thaumcraft.api.aspects.Aspect;
import thaumicenergistics.common.integration.tc.AspectHooks;
@Mixin(Aspect.class)
public class MixinAspect {
@Inject(
method = "<init>(Ljava/lang/String;I[Lthaumcraft/api/aspects/Aspect;Lnet/minecraft/util/ResourceLocation;I)V",
at = @At(
value = "INVOKE",
target = "Ljava/util/LinkedHashMap;put(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;",
remap = false,
shift = At.Shift.AFTER))
private void injectInit(CallbackInfo ci) {
AspectHooks.hook_AspectInit((Aspect) (Object) this);
}
}
@@ -0,0 +1,81 @@
package thaumicenergistics.mixins.late.thaumcraft;
import java.util.HashMap;
import net.minecraft.entity.monster.EntityGolem;
import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.nbt.NBTTagCompound;
import net.minecraft.world.World;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Unique;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
import com.llamalad7.mixinextras.injector.v2.WrapWithCondition;
import thaumcraft.common.entities.golems.EntityGolemBase;
import thaumicenergistics.api.entities.IGolemHookHandler;
import thaumicenergistics.common.integration.tc.GolemHooks;
import thaumicenergistics.mixins.interfaces.EntityGolemBaseExt;
@Mixin(EntityGolemBase.class)
public class MixinEntityGolemBase extends EntityGolem implements EntityGolemBaseExt {
@Unique
private HashMap<IGolemHookHandler, Object> thenergistic$hookHandlers;
public MixinEntityGolemBase(World world) {
super(world);
}
@Inject(method = "setupGolem", at = @At("TAIL"), remap = false)
private void injectSetupGolem(CallbackInfoReturnable<Boolean> cir) {
GolemHooks.hook_SetupGolem((EntityGolemBase) (Object) this, thenergistic$hookHandlers);
}
@Inject(method = "entityInit", at = @At("TAIL"))
private void injectEntityInit(CallbackInfo ci) {
thenergistic$hookHandlers = new HashMap<>();
GolemHooks.hook_EntityInit((EntityGolemBase) (Object) this, thenergistic$hookHandlers);
}
@Inject(method = "writeEntityToNBT", at = @At("TAIL"))
private void injectWriteNBT(NBTTagCompound nbt, CallbackInfo ci) {
GolemHooks.hook_WriteEntityToNBT((EntityGolemBase) (Object) this, thenergistic$hookHandlers, nbt);
}
@Inject(
method = "readEntityFromNBT",
at = @At(
value = "INVOKE",
target = "Lnet/minecraft/entity/monster/EntityGolem;readEntityFromNBT(Lnet/minecraft/nbt/NBTTagCompound;)V",
shift = At.Shift.AFTER))
private void injectReadNBT(NBTTagCompound nbt, CallbackInfo ci) {
GolemHooks.hook_ReadEntityFromNBT((EntityGolemBase) (Object) this, thenergistic$hookHandlers, nbt);
}
@WrapWithCondition(
method = "customInteraction",
remap = false,
at = @At(
value = "INVOKE",
target = "Lnet/minecraft/entity/player/EntityPlayer;openGui(Ljava/lang/Object;ILnet/minecraft/world/World;III)V"))
private boolean wrapCustomInteraction(EntityPlayer player, Object mod, int modGuiId, World world, int x, int y,
int z) {
return !GolemHooks.hook_CustomInteraction((EntityGolemBase) (Object) this, player, thenergistic$hookHandlers);
}
@Override
public void onEntityUpdate() {
super.onEntityUpdate();
GolemHooks.hook_onEntityUpdate((EntityGolemBase) (Object) this, thenergistic$hookHandlers);
}
@Override
public HashMap<IGolemHookHandler, Object> thenergistic$getHandlers() {
return thenergistic$hookHandlers;
}
}
@@ -0,0 +1,32 @@
package thaumicenergistics.mixins.late.thaumcraft;
import net.minecraft.entity.Entity;
import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.item.ItemStack;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.ModifyVariable;
import thaumcraft.common.entities.golems.EntityGolemBase;
import thaumcraft.common.entities.golems.ItemGolemBell;
import thaumicenergistics.common.integration.tc.GolemHooks;
import thaumicenergistics.mixins.interfaces.EntityGolemBaseExt;
@Mixin(ItemGolemBell.class)
public class MixinItemGolemBell {
@ModifyVariable(
method = "onLeftClickEntity",
remap = false,
at = @At(value = "STORE", ordinal = 1),
name = "dropped")
private ItemStack injectOnLeftClickEntity(ItemStack dropped, ItemStack stack, EntityPlayer player, Entity entity) {
GolemHooks.hook_Bell_OnLeftClickGolem(
(EntityGolemBase) entity,
dropped,
player,
((EntityGolemBaseExt) entity).thenergistic$getHandlers());
return dropped;
}
}
@@ -0,0 +1,33 @@
package thaumicenergistics.mixins.late.thaumcraft;
import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.item.ItemStack;
import net.minecraft.world.World;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
import com.llamalad7.mixinextras.sugar.Local;
import thaumcraft.common.entities.golems.EntityGolemBase;
import thaumcraft.common.entities.golems.ItemGolemPlacer;
import thaumicenergistics.common.integration.tc.GolemHooks;
import thaumicenergistics.mixins.interfaces.EntityGolemBaseExt;
@Mixin(ItemGolemPlacer.class)
public class MixinItemGolemPlacer {
@Inject(
method = "spawnCreature",
at = @At(
value = "INVOKE",
target = "Lthaumcraft/common/entities/golems/EntityGolemBase;setup(I)V",
shift = At.Shift.BEFORE),
remap = false)
private void inject(World par0World, double par2, double par4, double par6, int side, ItemStack stack,
EntityPlayer player, CallbackInfoReturnable<Boolean> cir, @Local(name = "golem") EntityGolemBase golem) {
GolemHooks.hook_Placer_SpawnGolem(golem, stack, ((EntityGolemBaseExt) golem).thenergistic$getHandlers());
}
}
@@ -0,0 +1,21 @@
package thaumicenergistics.mixins.late.thaumcraft;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
import thaumcraft.client.renderers.entity.RenderGolemBase;
import thaumcraft.common.entities.golems.EntityGolemBase;
import thaumicenergistics.common.integration.tc.GolemHooks;
import thaumicenergistics.mixins.interfaces.EntityGolemBaseExt;
@Mixin(RenderGolemBase.class)
public class MixinRenderGolemBase {
@Inject(method = "render", at = @At("RETURN"), remap = false)
private void onRender(EntityGolemBase e, double x, double y, double z, float par8, float partialTicks,
CallbackInfo ci) {
GolemHooks.hook_RenderGolem(e, ((EntityGolemBaseExt) e).thenergistic$getHandlers(), x, y, z, partialTicks);
}
}
@@ -0,0 +1,7 @@
{
"required": true,
"minVersion": "0.8.5-GTNH",
"refmap": "mixins.thaumicenergistics.refmap.json",
"target": "@env(DEFAULT)",
"compatibilityLevel": "JAVA_8"
}
@@ -0,0 +1,8 @@
{
"required": true,
"minVersion": "0.8.5-GTNH",
"package": "thaumicenergistics.mixins.late",
"refmap": "mixins.thaumicenergistics.refmap.json",
"target": "@env(DEFAULT)",
"compatibilityLevel": "JAVA_8"
}