MCPatcher works by modifying Minecraft at the Java bytecode level. Other mod frameworks for Minecraft allow you to add whole new classes to the game, but limit your ability to modify the behavior of existing classes to a few pre-defined "hooks". MCPatcher can potientially modify any part of the game as long as you can write a regular expression for the code you are looking for. To put it another way, MCPatcher isn't particularly well-suited to load Mod Loader mods into Minecraft, but it could potentially insert the Mod Loader framework itself in a version-independent way.
javap -c -private <classname> to disassemble it.
The mcpatcher application jar doubles as a library. Start a new Java project and include the mcpatcher jar in your build path. I suggest having several modules in your project:
Only mod, newcode, and shared are included in the final jar file that you will release for your mod.
After setting up the basic project structure, create a new source file, say MyMod.java:
package com.example.me;
import com.pclewis.mcpatcher.*;
import static javassist.bytecode.Opcode.*;
public class MyMod extends Mod {
public MyMod(MinecraftVersion minecraftVersion) {
name = "My Awesome Mod";
author = "Me";
version = "1.0";
description = "Now with 2.1% more awesome!";
}
}
The Mod class must have a public no-argument constructor.
Once you've built a jar file, put it in <minecraft
dir>/mcpatcher-mods/ and start MCPatcher to try it out. You should
see your mod listed along with HD Textures, etc.
Mod |-- name, author, etc. |-- ClassMod | |-- ClassSignature | |-- FieldMapper | |-- MethodMapper | |-- ClassPatch |-- ClassMap FieldRef MethodRef InterfaceMethodRef ClassRef BinaryRegex
The top-level class for a mod is called Mod. MCPatcher automatically loads all public classes that extend the Mod class. The basic structure of a mod is some general information and a set of ClassMods. A ClassMod is a part of the mod that applies to a single class and contains ClassSignatures and ClassPatches.
ClassSignatures determine what target class the ClassMod should be applied to. The two main subclasses are BytecodeSignature and ConstSignature. BytecodeSignatures match a particular bytecode sequence. ConstSignatures match a constant (string, float, etc.) in the class's constant poll. To match a class, all ClassSignatures in the ClassMod must match, so you can use a mixture of these types. Unless you specify otherwise, each ClassMod must match exactly one class in the input minecraft.jar. 0 or 2+ will cause your Mod to be greyed out in MCPatcher's UI. Once MCPatcher has located the ClassMod's target, it automatically sets up a mapping from your ClassMod's name to the obfuscated class name in minecraft.jar.
NOTE: The order that ClassSignatures are matched depends on the order of the files in minecraft.jar. It is not guaranteed to be the same as the order they are listed in the mod. Do not assume that one ClassSignature will be resolved before another.
FieldMappers and MethodMappers are evaluated in a second pass once all of the ClassSignatures have been successfully resolved. This means they can refer to unobfuscated class names as long as those classes have been mapped by ClassSignatures. FieldMappers and MemberMappers allow you to locate individual members within a class and assign meaningful names to them to refer to later. The default behavior is to map by type, but you can override this behavior to match on other criteria.
ClassPatches do the actual work of making changes to the target class file. There are subclasses to do the most common types changes. BytecodePatch matches a target bytecode sequence and replaces it. MakeMemberPublicPatch and AddMethodPatch do what they say on the tin. If none of these meet your needs, extend ClassMod yourself or use the prePatch and postPatch hooks to do other processing.
A ClassMap contains the mapping from meaningful names like RenderEngine.registerTextureFX to obfuscated names like hf.a. Each mod has its own ClassMap, which avoids naming conflicts with other mods. You probably won't need to add anything to the ClassMap directly, but you will need to be aware of its existence.
The JavaRef subclasses (FieldRef, MethodRef, InterfaceMethodRef, and ClassRef) are used whenever you want to refer to a particular class or class member by name.
BinaryRegex provides methods for creating match expressions for BytecodeSignatures and BytecodePatches. To refer to classes, methods, or fields, use the reference() method with one of the JavaRef subclasses. To refer to string, floating point, or integer constants, use the push() method.
Sometimes the defining feature of a class or class member is how it is used by another class. This is particularly true of interfaces, which have no code of their own to match against. The BytecodeSignature.addXref method allows you to define cross-references from a known class to one you are trying to identify. In the match expression, capture the GETFIELD/INVOKEVIRTUAL/etc. opcode. Then call addXref and provide a corresponding JavaRef with the desired deobfuscated names.
You may want to add a new class or replace an existing class. To do this, add the filename to the filesToAdd list in your Mod's constructor. Pass the full path within your jar minus the leading slash. MCPatcher will get the file's InputStream at the appropriate time during patching.
In the source for your new class file, you can use the descriptive names for any Minecraft classes you refer to. MCPatcher automatically maps any classes, fields, and methods in the ClassMap to their obfuscated names when it injects your new class into the output jar. But first you will need to create stubs for the Minecraft classes you refer to in order to build the new class file itself. This is only to satisfy the compiler however; the stubs do not need to be included in your jar file.
BIPUSH 16 IF_ICMPLT by
itself is not a good way to uniquely identify a class. It could match almost
anywhere. The best matches are unique numerical constants or strings. Next
best is a series of arithmetic operations that is integral to what the class
does. A class doesn't match unless all of its ClassSignatures match,
so a set of several non-specific signatures can be more effective than a single
super-specific one.-loglevel 5 option to see a side-by-side view of the
bytecode before and after each replacement. Add
-ignorebuiltinmods to hide the built-in font and texture
mods.