Appearance
L'utilisation d'un item
Bienvenue dans cette section fondamentale de notre cursus de modding. Jusqu'ici, vous avez configuré des objets simples, créé des armes et manipulé des fichiers de configuration graphique (les textures et les modèles JSON). Tout cela s'est déroulé de manière très linéaire.
Aujourd'hui, nous abordons le concept le plus fondamental, mais aussi le plus complexe pour un créateur de mod : la séparation structurelle entre le Client et le Serveur. Comprendre cette mécanique vous évitera 99 % des plantages (crashs) inexplicables et des comportements bizarres, communément appelés « bugs fantômes ».
1. Le Grand Paradoxe : Minecraft est un jeu multijoueur masqué
Même lorsque vous jouez seul dans votre monde de jeu en mode "Solo" (Singleplayer), Minecraft fait tourner deux instances distinctes de lui-même en arrière-plan dans votre ordinateur. C'est ce qu'on appelle l'architecture dédoublée ou asynchrone.
| L'Instance CLIENT (Le Corps) | L'Instance SERVEUR (Le Cerveau) |
|---|---|
| • Rôle : S'occupe uniquement du rendu visuel et sonore. C'est l'interface entre l'être humain et la machine. |
• Gère : Les textures PNG, les modèles 3D JSON, l'affichage de l'interface graphique (les fenêtres, l'inventaire à l'écran), les animations de particules, les sons et les inputs clavier/souris.
• Ne sait rien de la logique globale. Si vous changez une valeur ici, elle ne sera pas enregistrée. | • Rôle : Détient l'autorité absolue sur la logique de simulation du monde. C'est le garant de la vérité mathématique du jeu.
• Gère : Le calcul des points de vie (HP), l'intelligence artificielle des monstres, l'attribution réelle des objets dans l'inventaire, la détection des collisions, la sauvegarde des fichiers sur le disque dur.
• N'a aucune idée de ce qu'est un pixel ou une carte graphique. |
📜 Règle d'or du modding : > Le Client propose une action graphique (comme un clic), mais seul le Serveur dispose du droit de l'exécuter, de valider les mathématiques du jeu et de propager le résultat à tous les joueurs connectés.
2. Le Piège Absolu : Les Objets Fantômes
Que se passe-t-il si vous écrivez du code qui s'exécute par erreur sur le Client au lieu du Serveur ? C'est l'apparition immédiate des phénomènes fantômes.
- Exemple typique : Vous créez un objet magique qui redonne toute sa vie au joueur lors d'un clic droit.
- Si vous appliquez la méthode
player.setHealth()sur le côté Client, votre barre de vie s'affichera instantanément pleine à l'écran. - Cependant, le Serveur (qui n'a pas reçu l'information) considère toujours que vous êtes à l'article de la mort.
- Dès qu'un zombie vous effleurera, le Serveur recalculera vos dégâts réels et vous mourrez instantanément, alors même que votre écran affichait toute votre vie. C'est une désynchronisation fatale.
3. L'Outil Clé : level.isClientSide
Pour contrôler sur quel côté votre code s'exécute, Mojang met à notre disposition une variable booléenne primordiale au sein de l'objet Level (le monde Minecraft) : level.isClientSide.
- Si
level.isClientSidevauttrue: Le code s'exécute actuellement sur le Client (il faut restreindre les actions à la cosmétique ou aux animations). - Si
level.isClientSidevautfalse: Le code s'exécute actuellement sur le Serveur (c'est le moment idéal pour modifier l'inventaire, soigner, ou détruire des blocs).
La Syntaxe de l'Inversion Booléenne
Par convention de codage professionnelle dans le modding de Minecraft, nous cherchons presque toujours à isoler la logique pure pour qu'elle s'exécute exclusivement côté serveur. Pour ce faire, nous utilisons l'inversion avec l'opérateur d'exclamation (!), ce qui donne :
java
@Override
public InteractionResultHolder<ItemStack> use(Level level, Player player, InteractionHand hand) {
// 1. On intercepte et on isole le code. Si on est sur le client, on arrête l'exécution !
if (!level.isClientSide) {
// 2. Tout ce qui est écrit à l'intérieur de ce bloc IF s'exécute UNIQUEMENT sur le Serveur.
// C'est ici que s'opère la magie sécurisée.
player.getInventory().add(new ItemStack(Items.DIAMOND)); // Don sécurisé d'un diamant
}
return InteractionResultHolder.sidedSuccess(player.getItemInHand(hand), level.isClientSide);
}🛑 Alerte de Crash Critique (Le type Physique vs Logique) > Ne confondez jamais le fait qu'un code tourne côté client avec l'importation de classes exclusives au client. Si vous écrivez une méthode qui appelle une classe purement graphique de Minecraft (comme
Minecraft.getInstance().screen) à l'intérieur d'une classe commune (comme vos items), votre mod plantera instantanément dès qu'il sera installé sur un serveur distant dédié (sans interface de jeu). Nous verrons au cours suivant comment utiliser la classeDistExecutorpour éviter cela.
4. Intercepter le Clic Droit Immédiat : La méthode use
Pour déclencher une action immédiate lorsqu'un joueur utilise un objet dans le vide, Mojang utilise une méthode maîtresse appelée use(...). Surcharger correctement cette méthode permet d'envoyer le bon signal de retour au jeu pour éviter les bugs d'animation et forcer le mouvement visuel du bras du personnage.
La structure de la méthode use
Lorsque vous créez une classe personnalisée qui hérite de Item, vous devez écraser (surcharger) la méthode par défaut de Minecraft :
java
@Override
public InteractionResultHolder<ItemStack> use(Level level, Player player, InteractionHand hand) {
// Votre code ici
return super.use(level, player, hand);
}Comprendre les paramètres reçus :
Level level: Le monde dans lequel se trouve le joueur. Indispensable pour notre fameuxlevel.isClientSide.Player player: L'entité du joueur qui vient de faire le clic droit. Permet de modifier son inventaire, sa position, sa vie, etc.InteractionHand hand: Une énumération qui indique avec quelle main le joueur a cliqué. Elle possède deux états possibles :MAIN_HAND(main principale) ouOFF_HAND(seconde main / bouclier).
Le conteneur de résultat : InteractionResultHolder
Contrairement à d'autres langages ou anciennes versions où une méthode d'action renvoyait un simple booléen ou rien du tout (void), la méthode use exige que vous renvoyiez un objet d'état combiné avec l'item utilisé.
Cet objet s'appelle un InteractionResultHolder. Il sert à dire à Minecraft : "L'action s'est bien passée, voici le statut de l'objet et ce que le moteur graphique doit faire de l'animation."
Nous utilisons principalement la méthode d'usine InteractionResultHolder.sidedSuccess(itemStack, isClientSide). Elle gère intelligemment la transition :
- Sur le Client : elle retourne un état qui valide visuellement l'action (consommation du clic).
- Sur le Serveur : elle valide les changements logiques apportés au monde.
Animer mécaniquement le bras : player.swing(hand)
Par défaut, si vous créez un objet et faites un clic droit, l'action logique peut s'exécuter, mais le bras de votre personnage restera totalement immobile à l'écran. C'est très perturbant pour le joueur.
Pour corriger cela, il faut appeler le système d'animation de balancement du bras via la méthode : player.swing(hand);
⚠️ Attention : Cette méthode doit être appelée de manière synchrone. L'idéal est de la laisser s'exécuter globalement (sans restriction de côté) ou de s'assurer qu'elle est bien répercutée pour que tous les autres joueurs voient aussi votre bras bouger en multijoueur.
5. Exercices Pratiques Guidés
Exercice 1 : Le Bâton de Vérification de Réalité
Objectif : Créez un Item personnalisé nommé RealityStickItem. Lorsque le joueur fait un clic droit avec cet objet, le mod doit :
- Envoyer un message texte vert dans le chat du joueur s'exécutant côté Serveur :
"[Serveur] : Données validées et synchronisées." - Faire apparaître une particule de fumée autour du joueur uniquement s'il est exécuté côté Client.
📂 Solution de l'Exercice 1 :
java
package com.tutoriel.monmod.item;
import net.minecraft.network.chat.Component;
import net.minecraft.core.particles.ParticleTypes;
import net.minecraft.world.InteractionHand;
import net.minecraft.world.InteractionResultHolder;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.item.Item;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.level.Level;
public class RealityStickItem extends Item {
public RealityStickItem(Properties properties) {
super(properties);
}
@Override
public InteractionResultHolder<ItemStack> use(Level level, Player player, InteractionHand hand) {
ItemStack itemstack = player.getItemInHand(hand);
// VÉRIFICATION DU SERVEUR
if (!level.isClientSide) {
// Ce message est envoyé par le serveur. Il est officiel et sécurisé.
player.sendSystemMessage(Component.literal("§2[Serveur]: Données validées et synchronisées."));
}
// VÉRIFICATION DU CLIENT (Pour le visuel)
if (level.isClientSide) {
// Génération d'une particule visuelle uniquement là où l'œil humain regarde.
level.addParticle(ParticleTypes.SMOKE, player.getX(), player.getY() + 0.5, player.getZ(), 0.0D, 0.0D, 0.0D);
}
return InteractionResultHolder.sidedSuccess(itemstack, level.isClientSide);
}
}Exercice 2 : La Relique de Téléportation Instantanée
Cahier des charges : Créer un item nommé TeleporterRelicItem. Lors d'un clic droit dans le vide :
- Le joueur doit être propulsé de 5 blocs vers le haut (téléportation verticale).
- Le bras du joueur doit s'animer (mouvement de balancement).
- Le joueur doit subir un léger temps de recharge (cooldown) de 3 secondes sur l'item pour éviter de s'envoler indéfiniment.
📂 Solution de l'Exercice 2 :
java
package com.tutoriel.monmod.item;
import net.minecraft.world.InteractionHand;
import net.minecraft.world.InteractionResultHolder;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.item.Item;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.level.Level;
public class TeleporterRelicItem extends Item {
public TeleporterRelicItem(Properties properties) {
super(properties);
}
@Override
public InteractionResultHolder<ItemStack> use(Level level, Player player, InteractionHand hand) {
// Récupération de l'objet actuellement en main (main principale ou secondaire)
ItemStack itemstack = player.getItemInHand(hand);
// ÉTAPE 1: Logique métier sur le SERVEUR uniquement
if (!level.isClientSide) {
// Calcul de la nouvelle coordonnée Y (Y actuel + 5 blocs)
double newY = player.getY() + 5.0D;
// Téléportation forcée et sécurisée de l'entité du joueur
player.teleportTo(player.getX(), newY, player.getZ());
// ÉTAPE 2: Ajout du Cooldown (Temps de recharge)
// Le temps s'exprime en Ticks. (20 Ticks = 1 Seconde). 3 secondes = 60 Ticks.
player.getCooldowns().addCooldown(this, 60);
}
// ÉTAPE 3: Animation du bras (S'exécute sur les deux instances pour la synchronisation)
player.swing(hand);
// ÉTAPE 4: Retourner le conteneur d'état pour valider la réussite de l'action
return InteractionResultHolder.sidedSuccess(itemstack, level.isClientSide);
}
}6. Résumé global des connaissances à retenir
| Élément Clé | Utilité & Fonctionnement |
|---|---|
isClientSide == true | Environnement d'affichage graphique. On ne modifie jamais les variables de jeu d'ici sous peine de créer un bug de désynchronisation (objets ou barres de vie "fantômes"). |
isClientSide == false | Environnement logique et d'autorité (le Serveur). C'est le seul endroit où l'on modifie la santé, l'inventaire ou le monde de manière persistante. |
if (!level.isClientSide) | La fondation indispensable de toute programmation d'action interactive (armes magiques, consommables spéciaux, artefacts). |
**Méthode use** | Déclenchée immédiatement dès qu'un clic droit est détecté dans le vide avec l'objet en main. |
player.swing(hand) | Force le moteur de rendu à jouer l'animation de balancement de la main spécifiée. |
player.getCooldowns() | Permet d'appliquer une superposition grise sur l'item dans l'inventaire pour bloquer son utilisation répétitive pendant un nombre de ticks donné. |