Skip to content
This repository has been archived by the owner on Jul 1, 2018. It is now read-only.

Commit

Permalink
Change turtle refuel into a class replacer
Browse files Browse the repository at this point in the history
Obfuscation means that bytecode injection for MC sources
is tricky
  • Loading branch information
SquidDev committed Mar 31, 2015
1 parent 497737b commit 95dd8df
Show file tree
Hide file tree
Showing 5 changed files with 61 additions and 143 deletions.
2 changes: 1 addition & 1 deletion build.properties
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
mc_version=1.7.10
forge_version=10.13.2.1291
cc_version=1.73
mod_version=0.1.4.1
mod_version=0.1.4.2
6 changes: 4 additions & 2 deletions src/main/java/squiddev/cctweaks/core/asm/ASMTransformer.java
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,10 @@ public class ASMTransformer implements IClassTransformer {
protected ClassReplacer[] patches = {
new ClassReplacer("org.luaj.vm2.lib.DebugLib"),
new ClassReplacer("org.luaj.vm2.lib.StringLib"),
new ClassReplacer(
"dan200.computercraft.shared.turtle.core.TurtleRefuelCommand",
"squiddev.cctweaks.core.turtle.TurtleRefuelCommand_Rewrite"
),
};

@Override
Expand All @@ -14,8 +18,6 @@ public byte[] transform(String className, String s2, byte[] bytes) {
return PatchComputer.patchLuaMachine(bytes);
} else if (className.equals("dan200.computercraft.core.computer.ComputerThread$1")) {
return PatchComputer.patchLuaThread(bytes);
} else if (className.equals("dan200.computercraft.shared.turtle.core.TurtleRefuelCommand")) {
return PatchTurtle.patchRefuelCommand(bytes);
} else if (className.startsWith("dan200.computercraft.shared.turtle.core.Turtle") && className.endsWith("Command")) {
return PatchTurtle.disableTurtleCommand(className, bytes);
}
Expand Down
18 changes: 14 additions & 4 deletions src/main/java/squiddev/cctweaks/core/asm/ClassReplacer.java
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,11 @@ public class ClassReplacer {
*/
protected final String oldName;

/**
* The name of the class we are replacing with / instead of .
*/
protected final String oldType;

/**
* The name of the class to load
*/
Expand All @@ -31,12 +36,17 @@ public class ClassReplacer {
*/
protected final String loadedType;

public ClassReplacer(String className) {
public ClassReplacer(String className, String actualName) {
oldName = className;
oldNameStart = className.length();
oldType = className.replace('.', '/');

loadedName = className + NAME_SUFFIX;
loadedType = className.replace('.', '/') + NAME_SUFFIX;
loadedName = actualName;
loadedType = actualName.replace('.', '/');
}

public ClassReplacer(String className) {
this(className, className + NAME_SUFFIX);
}

protected final Remapper mapper = new Remapper() {
Expand All @@ -50,7 +60,7 @@ public String map(String typeName) {
if (typeName == null) return null;

if (typeName.contains(loadedType)) {
return typeName.replace(NAME_SUFFIX, "");
return typeName.replace(loadedType, oldType);
}

return super.map(typeName);
Expand Down
136 changes: 0 additions & 136 deletions src/main/java/squiddev/cctweaks/core/asm/PatchTurtle.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,148 +2,12 @@

import org.objectweb.asm.ClassReader;
import org.objectweb.asm.ClassWriter;
import org.objectweb.asm.Label;
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.tree.ClassNode;
import org.objectweb.asm.tree.MethodNode;
import squiddev.cctweaks.core.reference.Config;

public class PatchTurtle implements Opcodes {
/**
* Replace the Refuel command with one using CC-Tweaks' {@see squiddev.cctweaks.core.turtle.ITurtleRefuelSource}
*/
public static byte[] patchRefuelCommand(byte[] bytes) {
// Setup class reader
ClassNode classNode = new ClassNode();
ClassReader classReader = new ClassReader(bytes);
classReader.accept(classNode, 0);

for (MethodNode method : classNode.methods) {
if (method.name.equals("execute") && method.desc.equals("(Ldan200/computercraft/api/turtle/ITurtleAccess;)Ldan200/computercraft/api/turtle/TurtleCommandResult;")) {

/*
Local variables:
this: 0
ITurtleAccess: 1
ItemStack: 2
Iterator: 3
ITurtleRefuelSource: 4
*/

method.instructions.clear();
method.localVariables = null;

/*
ItemStack stack = this.turtle.getInventory().getStackInSlot(turtle.getSelectedSlot());
for (ITurtleRefuelSource source : TurtleRefuelList.refuelList) {
if (source.canRefuel(turtle, stack, m_limit)) {
if (m_limit == 0) {
return TurtleCommandResult.success();
} else {
turtle.addFuel(source.refuel(turtle, stack, m_limit));
turtle.playAnimation(TurtleAnimation.Wait);
return TurtleCommandResult.success();
}
}
}
return TurtleCommandResult.failure("Cannot refuel from this");
*/

// Get current stack
method.visitVarInsn(ALOAD, 1);
method.visitMethodInsn(INVOKEINTERFACE, "dan200/computercraft/api/turtle/ITurtleAccess", "getInventory", "()Lnet/minecraft/inventory/IInventory;", true);
method.visitVarInsn(ALOAD, 1);
method.visitMethodInsn(INVOKEINTERFACE, "dan200/computercraft/api/turtle/ITurtleAccess", "getSelectedSlot", "()I", true);
method.visitMethodInsn(INVOKEINTERFACE, "net/minecraft/inventory/IInventory", "getStackInSlot", "(I)Lnet/minecraft/item/ItemStack;", true);
method.visitVarInsn(ASTORE, 2);

// Get iterator
method.visitFieldInsn(GETSTATIC, "squiddev/cctweaks/core/registry/TurtleRefuelList", "refuelList", "Ljava/util/List;");
method.visitMethodInsn(INVOKEINTERFACE, "java/util/List", "iterator", "()Ljava/util/Iterator;", true);
method.visitVarInsn(ASTORE, 3);

// Prepare iterator
Label labelIteratorStart = new Label();
method.visitLabel(labelIteratorStart);
method.visitFrame(Opcodes.F_APPEND, 2, new Object[]{"net/minecraft/item/ItemStack", "java/util/Iterator"}, 0, null);
method.visitVarInsn(ALOAD, 3);
method.visitMethodInsn(INVOKEINTERFACE, "java/util/Iterator", "hasNext", "()Z", true);

Label labelReturnFailure = new Label();

// Load next item
method.visitJumpInsn(IFEQ, labelReturnFailure);
method.visitVarInsn(ALOAD, 3);
method.visitMethodInsn(INVOKEINTERFACE, "java/util/Iterator", "next", "()Ljava/lang/Object;", true);
method.visitTypeInsn(CHECKCAST, "squiddev/cctweaks/core/turtle/ITurtleRefuelSource");
method.visitVarInsn(ASTORE, 4);

// check if can refuel
method.visitVarInsn(ALOAD, 4);
method.visitVarInsn(ALOAD, 1);
method.visitVarInsn(ALOAD, 2);
method.visitVarInsn(ALOAD, 0);
method.visitFieldInsn(GETFIELD, "dan200/computercraft/shared/turtle/core/TurtleRefuelCommand", "m_limit", "I");
method.visitMethodInsn(INVOKEINTERFACE, "squiddev/cctweaks/core/turtle/ITurtleRefuelSource", "canRefuel", "(Ldan200/computercraft/api/turtle/ITurtleAccess;Lnet/minecraft/item/ItemStack;I)Z", true);

Label labelIteratorEnd = new Label();
method.visitJumpInsn(IFEQ, labelIteratorEnd);

method.visitVarInsn(ALOAD, 0);
method.visitFieldInsn(GETFIELD, "dan200/computercraft/shared/turtle/core/TurtleRefuelCommand", "m_limit", "I");

// If m_limit != 0 then goto actual refuel
Label labelActualRefuel = new Label();
method.visitJumpInsn(IFNE, labelActualRefuel);

// Fake refuel
method.visitMethodInsn(INVOKESTATIC, "dan200/computercraft/api/turtle/TurtleCommandResult", "success", "()Ldan200/computercraft/api/turtle/TurtleCommandResult;", false);
method.visitInsn(ARETURN);

// Refueling:
method.visitLabel(labelActualRefuel);

// Refuel and add fuel
method.visitFrame(Opcodes.F_APPEND, 1, new Object[]{"squiddev/cctweaks/core/turtle/ITurtleRefuelSource"}, 0, null);
method.visitVarInsn(ALOAD, 1);
method.visitVarInsn(ALOAD, 4);
method.visitVarInsn(ALOAD, 1);
method.visitVarInsn(ALOAD, 2);
method.visitVarInsn(ALOAD, 0);
method.visitFieldInsn(GETFIELD, "dan200/computercraft/shared/turtle/core/TurtleRefuelCommand", "m_limit", "I");
method.visitMethodInsn(INVOKEINTERFACE, "squiddev/cctweaks/core/turtle/ITurtleRefuelSource", "refuel", "(Ldan200/computercraft/api/turtle/ITurtleAccess;Lnet/minecraft/item/ItemStack;I)I", true);
method.visitMethodInsn(INVOKEINTERFACE, "dan200/computercraft/api/turtle/ITurtleAccess", "addFuel", "(I)V", true);

// Play 'wait' animation
method.visitVarInsn(ALOAD, 1);
method.visitFieldInsn(GETSTATIC, "dan200/computercraft/api/turtle/TurtleAnimation", "Wait", "Ldan200/computercraft/api/turtle/TurtleAnimation;");
method.visitMethodInsn(INVOKEINTERFACE, "dan200/computercraft/api/turtle/ITurtleAccess", "playAnimation", "(Ldan200/computercraft/api/turtle/TurtleAnimation;)V", true);

// Return success
method.visitMethodInsn(INVOKESTATIC, "dan200/computercraft/api/turtle/TurtleCommandResult", "success", "()Ldan200/computercraft/api/turtle/TurtleCommandResult;", false);
method.visitInsn(ARETURN);


// Next loop item
method.visitLabel(labelIteratorEnd);
method.visitFrame(Opcodes.F_CHOP, 1, null, 0, null);
method.visitJumpInsn(GOTO, labelIteratorStart);

// Return error - no refuel
method.visitLabel(labelReturnFailure);
method.visitFrame(Opcodes.F_CHOP, 1, null, 0, null);
method.visitLdcInsn("Cannot refuel from this");
method.visitMethodInsn(INVOKESTATIC, "dan200/computercraft/api/turtle/TurtleCommandResult", "failure", "(Ljava/lang/String;)Ldan200/computercraft/api/turtle/TurtleCommandResult;", false);
method.visitInsn(ARETURN);
}
}

ClassWriter writer = new ClassWriter(ClassWriter.COMPUTE_MAXS);
classNode.accept(writer);
return writer.toByteArray();
}

/**
* Disable a particular turtle command
*/
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
package squiddev.cctweaks.core.turtle;

import dan200.computercraft.api.turtle.ITurtleAccess;
import dan200.computercraft.api.turtle.ITurtleCommand;
import dan200.computercraft.api.turtle.TurtleAnimation;
import dan200.computercraft.api.turtle.TurtleCommandResult;
import net.minecraft.item.ItemStack;
import squiddev.cctweaks.core.registry.TurtleRefuelList;

/**
* Complete rewrite of {@link dan200.computercraft.shared.turtle.core.TurtleRefuelCommand}
* Uses the turtle refuel registry instead {@link TurtleRefuelList}.
*/
@SuppressWarnings("unused")
public class TurtleRefuelCommand_Rewrite implements ITurtleCommand {
private int m_limit = 0;

public TurtleRefuelCommand_Rewrite(int limit) {
m_limit = limit;
}

public TurtleCommandResult execute(ITurtleAccess turtle) {
ItemStack stack = turtle.getInventory().getStackInSlot(turtle.getSelectedSlot());
if (stack == null) {
return TurtleCommandResult.failure("No items to combust");
}

for (ITurtleRefuelSource source : TurtleRefuelList.refuelList) {
if (source.canRefuel(turtle, stack, m_limit)) {
if (m_limit == 0) {
return TurtleCommandResult.success();
} else {
turtle.addFuel(source.refuel(turtle, stack, m_limit));
turtle.playAnimation(TurtleAnimation.Wait);
return TurtleCommandResult.success();
}
}
}

return TurtleCommandResult.failure("Cannot refuel from this");
}
}

0 comments on commit 95dd8df

Please sign in to comment.