106 lines
4.3 KiB
Java
106 lines
4.3 KiB
Java
import java.nio.file.Files;
|
|
import java.nio.file.Path;
|
|
import java.nio.file.Paths;
|
|
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.InsnNode;
|
|
import org.objectweb.asm.tree.InsnList;
|
|
import org.objectweb.asm.tree.MethodInsnNode;
|
|
import org.objectweb.asm.tree.MethodNode;
|
|
import org.objectweb.asm.Opcodes;
|
|
|
|
/**
|
|
* Patches zombie.vehicles.BaseVehicle so addPointConstraint() no longer force-breaks
|
|
* both vehicles before creating a new constraint.
|
|
*/
|
|
public final class BaseVehicleConstraintPatch {
|
|
private static final String TARGET_NAME = "addPointConstraint";
|
|
private static final String BREAK_DESC_OBJECT_BOOL = "(ZLjava/lang/Boolean;)V";
|
|
private static final String BREAK_DESC_PRIMITIVE_BOOL = "(ZZ)V";
|
|
private static final String BASE_VEHICLE_OWNER = "zombie/vehicles/BaseVehicle";
|
|
|
|
private BaseVehicleConstraintPatch() {
|
|
}
|
|
|
|
public static void main(String[] args) throws Exception {
|
|
if (args.length != 2) {
|
|
System.err.println("Usage: BaseVehicleConstraintPatch <input BaseVehicle.class> <output BaseVehicle.class>");
|
|
System.exit(2);
|
|
}
|
|
|
|
Path input = Paths.get(args[0]);
|
|
Path output = Paths.get(args[1]);
|
|
byte[] original = Files.readAllBytes(input);
|
|
|
|
ClassNode classNode = new ClassNode();
|
|
new ClassReader(original).accept(classNode, 0);
|
|
|
|
int removedCalls = 0;
|
|
int inspectedAddPointMethods = 0;
|
|
for (MethodNode method : classNode.methods) {
|
|
if (!TARGET_NAME.equals(method.name) || !isTargetAddPointConstraint(method.desc)) {
|
|
continue;
|
|
}
|
|
inspectedAddPointMethods++;
|
|
removedCalls += patchAddPointConstraint(method);
|
|
}
|
|
|
|
if (removedCalls < 2) {
|
|
throw new IllegalStateException(
|
|
"Expected to remove 2 breakConstraint calls, removed "
|
|
+ removedCalls
|
|
+ " (inspected addPoint methods: "
|
|
+ inspectedAddPointMethods
|
|
+ ")");
|
|
}
|
|
|
|
ClassWriter writer = new ClassWriter(0);
|
|
classNode.accept(writer);
|
|
|
|
Files.createDirectories(output.getParent());
|
|
Files.write(output, writer.toByteArray());
|
|
System.out.println("Patched BaseVehicle.class; removed breakConstraint calls: " + removedCalls);
|
|
}
|
|
|
|
private static boolean isTargetAddPointConstraint(String methodDesc) {
|
|
// We only want the 5-arg overload:
|
|
// (IsoPlayer, BaseVehicle, String, String, boolean|Boolean) -> void
|
|
return "(Lzombie/characters/IsoPlayer;Lzombie/vehicles/BaseVehicle;Ljava/lang/String;Ljava/lang/String;Z)V"
|
|
.equals(methodDesc)
|
|
|| "(Lzombie/characters/IsoPlayer;Lzombie/vehicles/BaseVehicle;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Boolean;)V"
|
|
.equals(methodDesc);
|
|
}
|
|
|
|
private static int patchAddPointConstraint(MethodNode method) {
|
|
int patched = 0;
|
|
InsnList insns = method.instructions;
|
|
|
|
for (AbstractInsnNode node = insns.getFirst(); node != null; ) {
|
|
AbstractInsnNode next = node.getNext();
|
|
if (node instanceof MethodInsnNode call
|
|
&& BASE_VEHICLE_OWNER.equals(call.owner)
|
|
&& "breakConstraint".equals(call.name)) {
|
|
if (!(BREAK_DESC_OBJECT_BOOL.equals(call.desc)
|
|
|| BREAK_DESC_PRIMITIVE_BOOL.equals(call.desc))) {
|
|
node = next;
|
|
continue;
|
|
}
|
|
// Keep stack-map frames valid by preserving stack effect:
|
|
// breakConstraint(...) consumes objectref + 2 args and returns void.
|
|
// Replace invoke with POP2 + POP (consume 3 category-1 stack slots).
|
|
InsnList replacement = new InsnList();
|
|
replacement.add(new InsnNode(Opcodes.POP2));
|
|
replacement.add(new InsnNode(Opcodes.POP));
|
|
insns.insert(node, replacement);
|
|
insns.remove(node);
|
|
patched++;
|
|
}
|
|
node = next;
|
|
}
|
|
|
|
return patched;
|
|
}
|
|
}
|