Jump to content
EdwinMindcraft

Introduction au CoreMods

Recommended Posts

Lorsque l'on code, il y a un moment où l'on a un problème avec une fonction du jeu, par exemple le fait qu'il puisse neiger dans les biomes chaud comme le désert ou le nether (biome Hell)... Heureusement, forge a une fonctionnalité pour remédier a ce problème : les CoreMods.

Attention : ce tutoriel nécessite un niveau convenable en java, il n'est en aucun cas pour les débutants !

A cause du fonctionnement de la librairie ASM, écrire un CoreMod peut être extrêmement dangereux pour la compatibilité de votre mod, si vous pouvais vous en passer, je vous conseille fortement de le faire.

Je ne suis pas responsable si vous faites n'importe quoi avec les CoreMods.

Prérequis : 

-Avoir lu ce tutoriel : Lien

-Avoir un niveau convenable en java

1] Les CoreMods : c'est quoi ?

Les CoreMods sont des mods qui vont être activé avant le lancement de minecraft, il vont vous permettre de changer des fonction dans certaines class pour les faire fonctionner comme vous le voulais.

2] Ecrire un CoreMod

Un CoreMod est composé d'au minimum 2 classes pour la lisibilité, une classe qui va définir le mod, et une autre qui vas être le transformer

2.A] La classe du Mod

La classe du mod sera défini de cette façon :

public class NomDuCoreMod extends DummyModContainer implements IFMLLoadingPlugin

DummyModContainer est ce qui dit a Forge que votre classe est un mod et IFMLLoadingPlugin est l'interface qui sert pour faire des transformation au code de minecraft.

Dans les méthodes générées de cette façon, une seule nous intéresse dans ce tutoriel : 

public String[] getASMTransformerClass()

Cette méthode vas nous permettre de donner le chemin a notre transformer.

pour cela mettez y ce code :

return new String[] {"package.NomDuTransformer"};

2.B] La classe du transformer

La classe du transformer est défini de cette façon :

public class NomDuTransformer implements IClassTransformer

Lorsque vous faite ainsi une méthode va apparaître : 

public byte[] transform(String name, String transformedName, byte[] basicClass)

La première chose a faire pour éviter d’empêcher le jeu de marcher est d'ajouter a la fin du code :

return basicClass;

3] ASM

L'ASM est une libraire de manipulation du bytecode des classes pendant l’exécution de l'application (Site Officiel)

Pour commencer, nous allons devoir importer certains packages :

import static org.objectweb.asm.Opcodes.*;
import org.objectweb.asm.*;
import org.objectweb.asm.tree.*;

Ces trois package vous permettrons d'avoir toutes les classes dont vous avez besoin.

3.A] Accéder a une classe

Accéder a une classe est la première chose a faire (pour des question de lisibilité, je vous conseille de faire une méthode par classe transformée) pour cela, nous allons ajouter ce code a la méthode transform : 

if (transformedName.equals("package.Class")) {
	return transformClass(basicClass);
}

Nous allons donc utiliser une méthode transformClass définie de cette façon :

private byte[] transformClass(byte[] basicClass)

3.B] Lecture et écriture d'une classe

Pour lire le code d'une classe, nous allons utiliser ClassReader (org.objectweb.asm.ClassReader) :

ClassReader cr = new ClassReader(basicClass);
ClassNode cn = new ClassNode();
cr.accept(cn, 0);

Ce code vas stocker la classe dans la variable cn (ClassNode) et vas nous permettre de l'utiliser.

Lorsque vous aurez fini, ajouter ce code a la fin : 

ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_MAXS);
cn.accept(cw);
return cw.toByteArray();

Celui-ci va réécrire la classe pour avec les modification que vous lui aurez apporté.

3.C] Accéder a une méthode

Attention, si vous trouviez que ce tutoriel était dur jusqu’à présent vous n'avez surement pas le niveau nécessaire pour ce tutoriel, car la ça devient vraiment le bazar.

Alors les methodes... le gros probleme c'est qu'elles ont 2 noms : un nom donné par MCP pour que vous puissiez comprendre a quoi elle sert et un nom donné par Mojang sous la forme a, b, c, d, e ou autre. Pour connaitre ces noms vous allez devoir ouvrir un fichier situé dans

Citation

C:\Users\<Utilisateur>\.gradle\caches\minecraft\de\oceanlabs\mcp\mcp_<mapping>\<version>\srgs\mcp-notch.srg

ou <mapping> est soit stable, soit snapshot et <version> est la version du mapping (voir build.gradle)

Maintenant, pour acceder a la methode, nous allons devoir faire ceci : 

//Exemple de getFloatTemperature dans BiomeGenBase en 1.9
for (MethodNode mn : cn.methods) {
	if ((mn.name.equals("a") || mn.name.equals("getFloatTemperature")) && (mn.desc.equals("(Lcj;)F") || mn.desc.equals("(Lnet/minecraft/util/math/BlockPos;)F"))) {
    }
}

mn.name est le nom de la méthode et mn.desc représente ses paramètres.

Spoiler

Representation des classes en bytecode :

  • I représente "int"
  • F représente "float"
  • D représente "double"
  • B représente "byte"
  • Z représente "boolean"
  • J représente "long"
  • S représente "short"
  • L<package/Class>; représente Class
  • [<type> représente une array

3.D] Instructions (ou la partie avec le nom le moins recherché mais en fait c'est la plus importante)

Une instruction permet de dire au code ce qu'il doit faire, ce sont ces instruction qui vont être exécutées par la JVM.

Spoiler

<TYPE> peut etre A pour tout ce qui n'est pas type de base sinon

  • I pour int, boolean, byte ou short
  • L pour long
  • F pour float
  • D pour double

3.D.1] Variables

Bon... je pense que tout le monde sait ce qu'est une variable...

//Pour charger une variable utiliser ce code
new VarInsnNode(<TYPE>LOAD, <ID>)
//Pour enregister une variable utiliser ce code
new VarInsnNode(<TYPE>STORE, <ID>)

3.D.2] Return

Pour faire l’équivalent de return, il vous faudra utiliser :

new InsnNode(<TYPE>RETURN)

3.D.3] Methodes

Bon pareil les méthodes c'est le bazar : 

insn.add(new MethodInsnNode(<TypeDInvoke>, "<package/Class>", <name>, <desc>, <classEstUneInterface>));

<TypeDInvoke> peut être : 

  • INVOKEVIRTUAL, pour les methodes d'un objet
  • INVOKEINTERFACE, pour les methodes d'une interface
  • INVOKESTATIC, pour les methode statics

3.D.4] Fields

Les fields sont les variables de classe, pour les appeler il faut faire ceci :

new FieldInsnNode(<GETFIELD ou GETSTATIC>, <package/Class>, <name>, <desc>)

3.E] Utiliser des instructions

C'est bien de savoir définir des instructions mais c'est mieux si on peut les utiliser, pour cela il y a 2 solution, soit les insérer une par une, soit créer une InsnList et insérer cette dernière.

Une InsnList fonctionne exactement comme une list, donc je ne vais pas l'expliquer.

Pour insérer les instruction vous pouvais soit en ajouter soit les replacer. La seconde solution est plus simple mais moins plus incompatible.

3.E.1] Insertion

L'insertion nécessite une position, que l'on peut obtenir en allant a travers le instructions (mn.instructions) de la méthode, je vous conseille d'allez voir le code de minecraft pour ce système, lorsque vous avez votre position vous n'avez plus qu'a faire ceci :

mn.instructions.insert(position, instruction);
ou
mn.instructions.insertBefore(position, instruction);

le premier insert après et le second avant la position.

3.E.2] Remplacement

C'est tout de suite plus simple, faite juste

mn.instructions = <InsnList>

4] Charger le CoreMod

Pour que Forge sache qu'il faut loader ce CoreMod ajouter dans les arguments JVM ceci :

-Dfml.coreMods.load=package.ClassDuMod

et ajouter : 

jar {
        manifest {
        attributes 'FMLCorePlugin': 'package.ClassDuMod',
                        'FMLCorePluginContainsFMLMod': 'true'
    }
}

 et voila, vous avez un CoreMod fonctionel !

 

  • Upvote 1

Share this post


Link to post
Share on other sites

Super ! Mais juste, je trouve que ce n'est pas assez éxpliqué. Tu n'éxpliques pas par exemple ce qu'est la desc, tu n'éxpliques pas les labels tout ça, enfin après faut regarder la doc de ASM mais voilà.

  • Upvote 1

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

×