Skip to content

Guide de Modding Minecraft 1.20.1 avec Forge

Module 2 : Anatomie Totale de la Classe Principale et Système de Registres

Dans ce deuxième module, nous plongeons au cœur du fichier Java le plus crucial de votre mod : la Classe Principale (souvent nommée NomMod.java).

C'est le centre nerveux de votre projet. Nous allons décortiquer son fonctionnement, son interaction avec le cycle de démarrage de Forge, et surtout, maîtriser le système impératif des Registres (Deferred Register) qui permet d'injecter vos futurs blocs, objets et entités dans Minecraft sans provoquer de conflits.


1. Rôle Exact et Détection par Forge

Minecraft ne sait pas initialement que votre code existe. Au démarrage, Forge scanne tous les fichiers JAR présents dans le dossier mods à la recherche de l'annotation @Mod.

Dès qu'il trouve cette annotation, Forge instancie la classe associée en exécutant son Constructeur. C'est à ce moment précis que vous devez brancher votre mod sur les différents "canaux" de communication de Forge appelés Event Buses (Bus d'Événements).


2. La Différence Cruciale entre les deux Bus d'Événements

Une erreur classique consiste à mélanger les bus d'événements, ce qui empêche le mod de démarrer. Forge sépare les actions en deux catégories distinctes :

Bus d'ÉvénementOrigine / RécupérationUtilisation Principale / Événements liés
Mod Event Bus (Bus du Mod)FMLJavaModLoadingContext.get().getModEventBus()Uniquement au chargement/cycle de vie de votre mod (Enregistrement des objets, des blocs, configurations, initialisations communes). S'exécute une seule fois au démarrage.
Forge Event Bus (Bus de Forge)MinecraftForge.EVENT_BUSLiés au gameplay actif et aux interactions dans le monde Minecraft (Quand un joueur casse un bloc, attaque une entité, ouvre un coffre, tick du serveur). Événements répétitifs.

3. Le Mécanisme des Registres : DeferredRegister

Depuis plusieurs versions majeures, Forge impose l'utilisation du DeferredRegister (Registre Différé). Minecraft contient des milliers d'éléments (la pierre, l'épée en diamant, le zombie). Chacun possède un identifiant numérique interne et un identifiant textuel.

Pour éviter que deux mods n'enregistrent un objet en même temps et corrompent la mémoire du jeu, le DeferredRegister agit comme une "liste d'attente sécurisée". Vous déclarez vos objets dans ce registre, et Forge se charge de les injecter de manière ordonnée au moment idéal requis par le moteur de Minecraft.

La Règle d'Or du RegistryObject

Un registre ne renvoie pas directement votre objet (ex: un Item), mais un conteneur sécurisé appelé RegistryObject<Item>. Pour manipuler le véritable objet plus tard dans votre code, vous devrez utiliser la méthode .get() (ex: MON_ITEM.get()).


4. Le Code Source Intégral et Commenté d'une Classe Principale Moderne

Voici la structure complète, propre et optimisée pour l'initialisation de vos registres en 1.20.1. C'est ce modèle précis que vous copierez et ferez évoluer tout au long de votre création :

java
package net.pseudo.tutorialmod;

import net.minecraft.world.item.CreativeModeTabs;
import net.minecraft.world.item.Item;
import net.minecraft.world.level.block.Block;
import net.minecraft.world.level.block.Blocks;
import net.minecraft.world.level.block.state.BlockBehaviour;
import net.minecraft.world.level.material.MapColor;
import net.minecraftforge.common.MinecraftForge;
import net.minecraftforge.event.BuildCreativeModeTabContentsEvent;
import net.minecraftforge.event.server.ServerStartingEvent;
import net.minecraftforge.eventbus.api.IEventBus;
import net.minecraftforge.eventbus.api.SubscribeEvent;
import net.minecraftforge.fml.common.Mod;
import net.minecraftforge.fml.event.lifecycle.FMLClientSetupEvent;
import net.minecraftforge.fml.event.lifecycle.FMLCommonSetupEvent;
import net.minecraftforge.fml.javafmlmod.FMLJavaModLoadingContext;
import net.minecraftforge.registries.DeferredRegister;
import net.minecraftforge.registries.ForgeRegistries;
import net.minecraftforge.registries.RegistryObject;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

@Mod(TutorialMod.MOD_ID)
public class TutorialMod {
    
    // 1. Identifiant unique global
    public static final String MOD_ID = "tutorialmod";
    private static final Logger LOGGER = LogManager.getLogger();

    // 2. CRÉATION DES REGISTRES (Exemples pour les Blocs et les Objets)
    // On lie chaque registre au registre central de Forge correspondant via ForgeRegistries
    public static final DeferredRegister<Block> BLOCKS = DeferredRegister.create(ForgeRegistries.BLOCKS, MOD_ID);
    public static final DeferredRegister<Item> ITEMS = DeferredRegister.create(ForgeRegistries.ITEMS, MOD_ID);

    // 3. EXEMPLES D'ENREGISTREMENT D'OBJET ET DE BLOC
    // Ces variables statiques seront accessibles partout dans votre projet
    public static final RegistryObject<Item> MON_ITEM = ITEMS.register("mon_item", () -> new Item(new Item.Properties()));
    public static final RegistryObject<Block> MON_BLOC = BLOCKS.register("mon_bloc", () -> new Block(BlockBehaviour.Properties.of().mapColor(MapColor.STONE).stoneSoundTicks()));

    // 4. LE CONSTRUCTEUR (Point de départ exécuté by Forge)
    public TutorialMod() {
        // Récupération du bus d'événements du Mod
        IEventBus modEventBus = FMLJavaModLoadingContext.get().getModEventBus();

        // TRÈS IMPORTANT: On ordonne aux registres de se brancher sur le bus du Mod
        // Sans ces lignes, vos objets et blocs n'existeront JAMAIS dans le jeu !
        BLOCKS.register(modEventBus);
        ITEMS.register(modEventBus);

        // Enregistrement des méthodes du cycle de vie du mod
        modEventBus.addListener(this::commonSetup);
        modEventBus.addListener(this::addCreativeTab);

        // Enregistrement du bus de Forge global pour le gameplay/serveur
        MinecraftForge.EVENT_BUS.register(this);
    }

    // 5. PHASE DE CONFIGURATION COMMUNE (Code exécuté côté Client ET Serveur)
    private void commonSetup(final FMLCommonSetupEvent event) {
        LOGGER.info("### CONFIGURATION COMMUNE EN COURS ###");
        LOGGER.info("Vérification de notre bloc de test " + MON_BLOC.getId());
    }

    // 6. INJECTION DANS LES ONGLETS CRÉATIFS (Nouveauté moderne de Minecraft)
    // Permet d'ajouter votre objet directement dans un onglet existant du jeu de base
    private void addCreativeTab(BuildCreativeModeTabContentsEvent event) {
        // Exemple : On ajoute notre objet personnalisé dans l'onglet des Ingrédients
        if (event.getTabKey() == CreativeModeTabs.INGREDIENTS) {
            event.accept(MON_ITEM);
        }
        // Exemple : On ajoute notre bloc personnalisé dans l'onglet des Blocs Naturels
        if (event.getTabKey() == CreativeModeTabs.NATURAL_BLOCKS) {
            event.accept(MON_BLOC);
        }
    }

    // 7. ÉVÉNEMENT DU BUS DE FORGE (Gameplay / Serveur)
    // Notez l'utilisation de @SubscribeEvent car cette méthode est abonnée via MinecraftForge.EVENT_BUS
    @SubscribeEvent
    public void onServerStarting(ServerStartingEvent event) {
        LOGGER.info("Le serveur démarre. Le mod " + MOD_ID + " est prêt à surveiller le monde.");
    }

    // 8. ÉVÉNEMENT CLIENT UNIQUEMENT
    // Déclaré ici de manière interne, idéal pour enregistrer des rendus graphiques spécifiques au joueur
    @Mod.EventBusSubscriber(modid = MOD_ID, bus = Mod.EventBusSubscriber.Bus.MOD, value = net.minecraftforge.api.distmarker.Dist.CLIENT)
    public static class ClientModEvents {
        @SubscribeEvent
        public static void onClientSetup(FMLClientSetupEvent event) {
            LOGGER.info("Le client Minecraft s'initialise (Rendu graphique, textures...)");
        }
    }
}

⚠️ Erreur Fatale à Éviter: Le plantage Client/Serveur Regardez attentivement le point 8 (ClientModEvents). L'annotation possède l'argument value = Dist.CLIENT. Cela indique à Forge que ce code ne doit être lu que sur l'ordinateur du joueur. Si Forge tente de lire du code graphique sur un serveur dédié (sans écran), le serveur crashera instantanément avec une erreur de type ClassNotFoundException.


5. Check-list de Structuration pour vos Prochains Fichiers

Bien que vous puissiez mettre tous vos futurs objets directement dans cette classe principale, elle deviendrait vite illisible (plus de 5000 lignes pour un gros mod).

La bonne pratique consiste à utiliser cette classe comme un hub central, et à déporter les registres dans des classes dédiées. Pour vos futurs modules, voici l'organisation propre des fichiers que vous allez adopter :

  1. net.pseudo.tutorialmod.init.ItemInit : Contiendra uniquement le DeferredRegister<Item> et la liste de vos objets.
  2. net.pseudo.tutorialmod.init.BlockInit : Contiendra uniquement le DeferredRegister<Block> et la liste de vos blocs.
  3. La classe principale se contentera d'appeler ItemInit.ITEMS.register(modEventBus) pour centraliser l'activation.