- 29
- 20
Workbench61 добавил(а) новый ресурс:
Снова трансформеры? - Набор полезных методов для трансформирования классов
Узнать больше об этом ресурсе...
Если вы как я, часто пишете кормоды, или просто интересуетесь темой.
Хуклибы и миксины, для меня, это слишком много и непонятно. Они большие, требуют относительно долгого сетапа, и их невозможно дебажить.
Суть моего проекта - набор сниппетов (live templates), которые я могу вызвать простым шорткатом, в любом месте проекта, над которым я работаю, и получить моментальный результат. Плюсом это дает мне больше независимости и полный контроль над своим кодом.
Сам код:
Набор функций для работы с ASM tree API (ClassNode, MethodNode, InsnList и т.д.)
github.com
Простой класс для ремаппинга методов и полей, чтобы ваши моды не ломались при смене маппингов в воркспейсе
github.com
Опционально реализует интерфейс yopoyka/code-snippets кастомизируйте по вашему желанию.
Использование:
Для новичков:
Если вы еще не знаете как реализовать и задействовать
Ваш
В вашем классе реализующем интрефейс
В моем репозиторие имеется простой трансформер yopoyka/code-snippets вида:
Примеры:
Создаем или импортируем все статические элементы класса yopoyka/code-snippets
Подробнее с его структурой можно ознакомиться на гитхабе
Класс который мы модифицируем:
Результат:
Снова трансформеры? - Набор полезных методов для трансформирования классов
Если вы как я, часто пишете кормоды, или просто интересуетесь темой.
Хуклибы и миксины, для меня, это слишком много и непонятно. Они большие, требуют относительно долгого сетапа, и их невозможно дебажить.
Суть моего проекта - набор сниппетов (live templates), которые я могу вызвать простым шорткатом, в любом месте проекта, над которым я работаю, и получить моментальный результат. Плюсом это дает мне больше независимости и полный контроль над своим кодом.
Сам код:
Набор функций для...
Узнать больше об этом ресурсе...
Если вы как я, часто пишете кормоды, или просто интересуетесь темой.
Хуклибы и миксины, для меня, это слишком много и непонятно. Они большие, требуют относительно долгого сетапа, и их невозможно дебажить.
Суть моего проекта - набор сниппетов (live templates), которые я могу вызвать простым шорткатом, в любом месте проекта, над которым я работаю, и получить моментальный результат. Плюсом это дает мне больше независимости и полный контроль над своим кодом.
Сам код:
Набор функций для работы с ASM tree API (ClassNode, MethodNode, InsnList и т.д.)
yopoyka/code-snippets
Contribute to yopoyka/code-snippets development by creating an account on GitHub.
Простой класс для ремаппинга методов и полей, чтобы ваши моды не ломались при смене маппингов в воркспейсе
yopoyka/code-snippets
Contribute to yopoyka/code-snippets development by creating an account on GitHub.
Использование:
GradleMcp.instance.fromSrg("func_72326_a")
вернет значение согласно маппингам используемым в вашем проекте, либо переданное значение если маппинги не найдены.Для новичков:
Если вы еще не знаете как реализовать и задействовать
IFMLLoadingPlugin
Ваш
build.gradle
Gradle (Groovy):
jar {
manifest {
attributes 'FMLCorePlugin': 'путь.к.вашему.классу'
}
}
runClient {
systemProperty 'fml.coreMods.load', 'путь.к.вашему.классу'
}
runServer {
systemProperty 'fml.coreMods.load', 'путь.к.вашему.классу,на.самом.деле.плагины.можно.добавлять.через.запятую'
}
В вашем классе реализующем интрефейс
IFMLLoadingPlugin
определяем метод String[] getASMTransformerClass()
и добавляем аннотации
Java:
@IFMLLoadingPlugin.Name("My Coremod")
@IFMLLoadingPlugin.MCVersion("1.7.10") // версия майнкрафта
@IFMLLoadingPlugin.SortingIndex(1001) // чтобы работать с srg именами а не с оригинальными a b az
public class Coremod implements IFMLLoadingPlugin {
@Override
public String[] getASMTransformerClass() {
return new String[] { "ваши.класс.трансформеры" };
}
}
В моем репозиторие имеется простой трансформер yopoyka/code-snippets вида:
Java:
public class BasicClassTransformer implements IClassTransformer {
protected java.util.Map<String, net.minecraft.launchwrapper.IClassTransformer> transformers = new java.util.HashMap<>();
{
map.put("имя.класса.для.трансформации", (n, name, basicClass) -> {
// делаем с классом что хотим
return basicClass;
})
}
@Override
public byte[] transform(String name, String transformedName, byte[] basicClass) {
if (transformers.isEmpty()) return basicClass;
final net.minecraft.launchwrapper.IClassTransformer transformer = transformers.remove(transformedName);
if (transformer != null)
return transformer.transform(name, transformedName, basicClass);
return basicClass;
}
}
Примеры:
Создаем или импортируем все статические элементы класса yopoyka/code-snippets
Подробнее с его структурой можно ознакомиться на гитхабе
Java:
package examples;
import static yopoyka.mctool.Asm.*;
public class Examples {
public static void main(String[] args) throws Throwable {
// читаем класс из массива байтов
ClassNode classNode = read(readClass("examples/Ex"));
// простой хук в начало метода getInt
classNode.methods
.stream()
.filter(forMethod("getInt", "()I")) // ищем метод getInt возвращающий int
.findFirst()
.ifPresent(methodNode -> {
InsnList list = new InsnList(); // создаем лист вручную
compose( // метод собирающий Consumer'ы вместе в один большой
getThis(), // равносильно addInst(() -> new VarInsnNode(Opcodes.ALOAD, 0))
// вызываем наш статический хук
callStatic("examples/Examples$Hooks", "getIntHook", "(Lexamples/Ex;)V")
).accept(list); // применяем наш Consumer к списку
// вставляем инструкции перед первой инструкцией оригинального списка
insertFirst(methodNode.instructions, list);
});
// добавляем действие для каждого return'а в методе
classNode.methods
.stream()
// фильтры можно комбинировать
.filter(forMethod("numbers").and(forMethodDesc("(I)Ljava/lang/String;")))
.findFirst()
.ifPresent(methodNode -> {
// выполяет операцию для каждой инструкции
// которая удовлетворяет фильтру
forEach(methodNode.instructions,
// поиск инструкций с опкодом ARETURN
opcode(Opcodes.ARETURN),
// вставляет инструкции перед каждой найденной
insertBefore(supplyCode(compose(
getThis(), // загружает переменную с индексом 0 (ноль)
// вызываем наш хук
callStatic("examples/Examples$Hooks", "numbersHook", "(Ljava/lang/String;Lexamples/Ex;)Ljava/lang/String;")
)))
);
});
// делаем ветвление с невероятной легкостью
classNode.methods
.stream()
.filter(forMethod("days")) // еще один способ
.filter(forMethodDesc("(I)Ljava/lang/String;")) // комбинировать фильтры
.findFirst()
.ifPresent(methodNode -> {
// выполяет операцию для каждой инструкции
// которая удовлетворяет фильтру
forEach(methodNode.instructions,
// поиск инструкций с опкодом ARETURN
opcode(Opcodes.ARETURN),
// вставляет инструкции перед каждой найденной
insertBefore(supplyIf( // создаем ветвление
compose( // инициализуем
addInst(Opcodes.DUP), // дюпаем строчку со стака
// вызываем наш хук который возвращает bolean или int
callStatic("examples/Examples$Hooks", "daysHook", "(Ljava/lang/String;)Z")
),
jumpIfTrue(), // делаем прыжок если хук вернул значение неравное 0 (нулю)
compose( // эти инструкции будут выполнены
// если прыжок не был совершен
addInst(() -> new LdcInsnNode("Garfield doesn't like this day")),
addInst(Opcodes.ARETURN)
),
nothing() // эти инструкции выполняются если прыжек был сделан
// в данном случае не делаем ничего
// метод продолжает выполняться
))
);
});
Class<?> aClass = defineClass(write(classNode));
Object o = aClass.newInstance();
System.out.println(aClass.getDeclaredMethod("getInt").invoke(o));
System.out.println();
for (int i = 0; i < 10; i++) {
System.out.println(aClass.getDeclaredMethod("numbers", int.class).invoke(o, i));
}
for (int i = 0; i < 10; i++) {
System.out.println(aClass.getDeclaredMethod("days", int.class).invoke(o, i));
}
}
public static class Hooks {
public static void getIntHook(Ex ex) {
System.out.println("getIntHook " + ex);
}
public static String numbersHook(String s, Ex ex) {
System.out.println("numbesHook " + s + ' ' + ex);
return "hey";
}
public static boolean daysHook(String day) {
if (day.equals("Monday"))
return false;
return true;
}
}
public static byte[] readClass(String name) {
try (InputStream is = ClassLoader.getSystemResourceAsStream(name.replace('.', '/').concat(".class"))) {
final ByteArrayOutputStream buffer = new ByteArrayOutputStream();
int read;
final byte[] buff = new byte[Short.MAX_VALUE];
while ((read = is.read(buff)) > 0) {
buffer.write(buff, 0, read);
}
return buffer.toByteArray();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
public static Class<?> defineClass(byte[] bytes) {
return new ClassLoader() {
Class<?> defineClass(byte[] bytes) {
return defineClass(bytes, 0, bytes.length);
}
}.defineClass(bytes);
};
}
Класс который мы модифицируем:
Java:
package examples;
public class Ex {
public int getInt() {
System.out.println("getInt");
return 10;
}
public String numbers(int i) {
switch (i) {
case 0: return "zero";
case 1: return "one";
case 2: return "two";
case 3: return "three";
case 4: return "four";
case 5: return "five";
case 6: return "six";
case 7: return "seven";
case 8: return "eight";
case 9: return "nine";
}
return "none";
}
public String days(int i) {
switch (i) {
case 0: return "Monday";
case 1: return "Tuesday";
case 2: return "Wednesday";
case 3: return "Thursday";
case 4: return "Friday";
case 5: return "Saturday";
case 6: return "Sunday";
}
return "none";
}
}
Результат:
Код:
getIntHook examples.Ex@00000000
getInt
10
numbesHook zero examples.Ex@00000000
hey
numbesHook one examples.Ex@00000000
hey
numbesHook two examples.Ex@00000000
hey
numbesHook three examples.Ex@00000000
hey
numbesHook four examples.Ex@00000000
hey
numbesHook five examples.Ex@00000000
hey
numbesHook six examples.Ex@00000000
hey
numbesHook seven examples.Ex@00000000
hey
numbesHook eight examples.Ex@00000000
hey
numbesHook nine examples.Ex@00000000
hey
Garfield doesn't like this day
Tuesday
Wednesday
Thursday
Friday
Saturday
Sunday
none
none
none
Последнее редактирование: