Aller au contenu principal

Guide développeur MagmaCore

La référence Java générée permet de rechercher les classes, méthodes et signatures. La version des sources et le commit utilisés sont indiqués pour chaque module.

MagmaCore est une bibliothèque intégrée dans le JAR des plugins qui l'utilisent par shading. Ce n'est pas un plugin serveur autonome : ne l'ajoutez pas à depend comme si les joueurs pouvaient installer un JAR de plugin MagmaCore.

Dépendance et packaging​

Les coordonnées de développement actuelles sont com.magmaguy:MagmaCore:2.2.0-SNAPSHOT. Elles sont publiées dans le dépôt releases de MagmaGuy, et non dans le dépôt de snapshots, qui ne contient que d'anciens snapshots 1.x. Un snapshot peut changer sans que cette chaîne de version ne change, et le build publié n'est pas actualisé à chaque version d'un plugin : il peut donc être plus ancien que le MagmaCore intégré aux plugins actuels. Le build publié le 5 octobre 2026 inclut les API de mise à jour sans redémarrage et de parties décrites ci-dessous. Pour compiler avec des changements plus récents non publiés, construisez MagmaCore depuis les sources et publiez-le dans Maven Local, comme indiqué dans Recompiler un consommateur après modification de la bibliothèque. Conservez une trace de l'artefact résolu si vous avez besoin de builds reproductibles.

Gradle Kotlin DSL
repositories {
maven("https://repo.magmaguy.com/releases")
}

dependencies {
implementation("com.magmaguy:MagmaCore:2.2.0-SNAPSHOT")
}
Maven sections
<repositories>
<repository>
<id>magmaguy-releases</id>
<url>https://repo.magmaguy.com/releases</url>
</repository>
</repositories>

<dependencies>
<dependency>
<groupId>com.magmaguy</groupId>
<artifactId>MagmaCore</artifactId>
<version>2.2.0-SNAPSHOT</version>
</dependency>
</dependencies>

Ces déclarations permettent de résoudre la dépendance. Configurez la tâche de shading de votre build pour l'inclure dans le JAR final du plugin. Suivez les règles de relocation d'un plugin existant ayant les mêmes besoins d'intégration et vérifiez les recherches réflexives basées sur des chaînes lorsque vous changez les noms de packages. La distribution relocalise org.luaj, org.reflections et javassist sous com.magmaguy.shaded et exclut les classes javax.annotation utilisées uniquement à la compilation.

Chaque plugin peut charger sa propre copie de la bibliothèque. Deux classes Java de même nom, chargées par des chargeurs de classes de plugins distincts, sont des types différents. Utilisez les contrats explicites entre plugins, comme le service de pouvoirs Lua d'EliteMobs ou les adaptateurs du registre de localisations. Ne convertissez pas un objet MagmaCore privé d'un autre plugin vers le type de votre propre copie.

Modules et responsabilités​

ModuleResponsabilité
coreCycle de vie des plugins, configuration, commandes, menus, parties, requêtes de localisation, scripts Lua, contenu Nightbreak et utilitaires partagés
nms:coreNMSManager, NMSAdapter et interfaces EasyMinecraftGoals indépendantes de la version
nms:v*Implémentations du contrat NMS propres aux versions du serveur
distDistribution assemblée par shading, contenant le cœur et les adaptateurs actifs

Les enchantements partagés, les actions d'objets, la gestion du compte, les menus de configuration, les catalogues et les mises à jour relèvent des services du cœur. Réutilisez le composant responsable lorsque vous ajoutez un comportement à un plugin consommateur.

Cycle de vie du plugin​

Le cycle de vie minimal du cœur suit le modèle des plugins existants :

CoreConsumer.java
package example;

import com.magmaguy.magmacore.MagmaCore;
import org.bukkit.plugin.java.JavaPlugin;

public final class CoreConsumer extends JavaPlugin {
@Override
public void onLoad() {
MagmaCore.createInstance(this);
}

@Override
public void onEnable() {
MagmaCore.onEnable(this);
}

@Override
public void onDisable() {
MagmaCore.shutdown(this);
}
}

Cette initialisation ne se limite pas à obtenir un objet utilitaire. Elle initialise la gestion du compte, les commandes partagées, les écouteurs et la prise en charge des blocs temporaires. La surcharge createInstance(plugin, NightbreakPluginSpec) fournit les textes des commandes partagées propres au plugin.

Préférez les méthodes qui prennent explicitement le plugin propriétaire en argument. getInstance() désigne le singleton de la copie chargée de la bibliothèque ; le plugin demandeur est fixé à la création de cette instance. Ne l'utilisez pas pour déduire le propriétaire de tous les consommateurs.

shutdown(plugin) supprime les enregistrements du téléchargeur, du rafraîchissement de contenu, de l'initialisation et des mises à jour de ce plugin, puis arrête également l'environnement d'exécution partagé de la copie chargée. Son action ne se limite pas au retrait d'une entrée de registre. Appelez cette méthode pour votre propre bibliothèque intégrée lors de l'arrêt de votre plugin ; n'arrêtez jamais la bibliothèque d'un autre plugin pour réinitialiser votre intégration.

Appelez createInstance depuis onLoad afin d'enregistrer le propriétaire des archives de configuration du plugin avant qu'un autre plugin puisse importer du contenu pour lui. L'archivage passe par l'implémentation intégrée au plugin cible ; n'interprétez pas ses règles de retrait de configuration avec votre copie de MagmaCore.

Pour un démarrage en plusieurs phases, startInitialization accepte du travail asynchrone, du travail synchrone et des callbacks de réussite et d'échec. Il archive les fichiers de configuration correspondant à un format retiré avant de lancer l'initialisation asynchrone. Réservez les opérations Bukkit sur les mondes et les entités à la phase synchrone. Un travail asynchrone long doit vérifier isShutdownRequested(plugin) entre ses phases. getInitializationState et isPluginReady exposent l'état du gestionnaire d'initialisation.

Mises à jour du plugin sans redémarrage​

NightbreakPluginBootstrap.registerStandardCommands enregistre applypluginupdate avec les autres sous-commandes partagées. Cette commande installe une mise à jour téléchargée en désactivant votre plugin, en remplaçant son jar puis en le réactivant dans la même session du serveur : onDisable doit donc annuler les tâches, désenregistrer les écouteurs et libérer toute référence qui maintiendrait en vie l'ancien chargeur de classes. Les plugins activés dont la classe principale se trouve sous com.magmaguy. et qui dépendent du vôtre, en depend ou en softdepend, sont rechargés avec lui. Tout autre plugin dépendant du vôtre fait refuser le remplacement, qui demande alors un redémarrage. Consultez Appliquer les mises à jour sans redémarrage pour le point de vue de l'administrateur du serveur.

Si une partie de votre plugin ne peut pas être rechargée sur place, appelez NightbreakPluginHotSwap.requireRestartForUpdates(plugin, reason) avant le début des vérifications de mise à jour, et passez false comme second argument de NightbreakPluginUpdater.setAutoDownloadConfigDefault afin que config.yml ne propose pas applyPluginUpdatesWithoutRestart. Le plugin est tout de même rechargé avec son jar actuel lorsqu'un plugin dont il dépend est remplacé. NightbreakPluginUpdater.onPluginUpdateDownloaded(plugin, listener) exécute un écouteur sur le thread de téléchargement une fois qu'une mise à jour validée a atteint le dossier update.

NMS et entités par paquets​

Initialisez NMS uniquement si votre plugin en a besoin, avec NMSManager.initializeAdapter(plugin) de com.magmaguy.easyminecraftgoals. Vérifiez NMSManager.isEnabled() avant d'utiliser un comportement dépendant d'un adaptateur. L'initialisation peut échouer ou rencontrer une version de serveur non prise en charge.

La distribution contient les familles d'adaptateurs suivantes :

AdaptateurVersions de Minecraft
v1_21_R31.21.4
v1_21_R41.21.5
v1_21_R51.21.6, 1.21.7, 1.21.8
v1_21_R61.21.9, 1.21.10
v1_21_R7_spigot, v1_21_R7_paper1.21.11, implémentations Spigot/Paper distinctes
v2626.1 et famille actuelle d'adaptateurs 26.x

La sélection d'un adaptateur ne garantit pas la compatibilité avec toutes les futures versions du serveur. Le sélecteur d'exécution de NMSManager détermine si le serveur installé peut charger un adaptateur. Les versions antérieures à 1.21.4 ne font pas partie de l'ensemble actuel d'adaptateurs.

EasyMinecraftGoals gère les entités qui n'existent que par paquets, leur visibilité, le routage de leurs interactions et les utilitaires de déplacement et de recherche de chemin. Ce ne sont pas des entités ordinaires du monde. Gérez-les via leur API responsable et nettoyez les objets créés par votre plugin tant que l'adaptateur est encore disponible.

Requêtes de localisation et de protection​

com.magmaguy.magmacore.location.LocationQueryRegistry expose des requêtes sur les donjons et les protections de régions, notamment isInAnyDungeon, isInAnyProtectedRegion et canBuild.

Enregistrez les fournisseurs personnalisés avec registerDungeonLocator ou registerProtectionProvider, puis désenregistrez le même fournisseur à la désactivation de son propriétaire. Le registre contient des adaptateurs pour les plugins existants et découvre les fournisseurs de donjons entre plugins sans imposer aux consommateurs un même chargeur de classes MagmaCore.

Choisissez la requête correspondant à l'action. Être en dehors d'un donjon ne signifie pas avoir la permission de construire à cet endroit. Exécutez sur le thread du serveur les requêtes qui utilisent Bukkit ou des plugins de protection.

Parties​

Le cycle de vie des parties se trouve dans com.magmaguy.magmacore.match. Appelez MatchCore.enable(plugin) une fois pendant l'activation, puis étendez Match, transmettez à son constructeur un MatchSettings obtenu avec MatchSettings.builder(), et appelez open() une fois la construction de votre sous-classe terminée. La logique de jeu se place dans les hooks protégés tels que onJoin, onStart, onDeath et onLeave. MagmaCore.shutdown(plugin) détruit les parties restantes du plugin.

Le cœur ne déclenche aucun événement Bukkit de lui-même. Il appelle le MatchApi défini dans les paramètres : c'est donc votre plugin qui décide des événements à exposer. Un callback annulable met son veto à l'étape en renvoyant false ou en levant une exception. La copie de chaque plugin ne suit que ses propres parties ; un marqueur de métadonnées du joueur, magmacore_match, indique aux autres copies qu'un joueur participe déjà à une partie.

MatchSettings.builder().permission(...) définit la permission dont un joueur a besoin pour rejoindre. Redéfinissez hasJoinPermission(player) pour décider joueur par joueur, par exemple pour laisser entrer le staff sans elle. Un participant qui part parce qu'il est mort (LeaveReason.DIED) est soigné à pleine santé avant d'être déplacé hors de la partie : si un bonus de santé maximale est retiré à la sortie, sa santé reste ainsi au maximum. Les autres sorties en cours de partie conservent les dégâts subis, et les joueurs encore présents à la fin de la partie sont soignés. Ces deux comportements sont plus récents que la version publiée le 5 octobre 2026.

Les anciennes classes de parties de com.magmaguy.magmacore.instance, dont MatchInstance, ont été supprimées. InstanceProtector y reste et est enregistré par MagmaCore.enableWorldProtections(plugin).

Hôtes Lua Mind​

MagmaCore compile des programmes Mind écrits en Lua pour les plugins hôtes. Le format des scripts est décrit dans Programmes de comportement. Un hôte crée son propre LuaMindModuleRegistry, ouvre un propriétaire pour son espace de noms avec openOwner(namespace), enregistre les modules avec le registerModule du propriétaire, et obtient un LuaMindDefinition grâce au validateProgram du propriétaire. Chaque appel à instantiate sur la définition construit un programme neuf, avec son propre environnement Lua, pour une entité. Les hooks ci-dessous sont plus récents que le build publié le 5 octobre 2026.

  • Inspecter un module. LuaMindModuleRegistry.inspectModule(sourceName, source) lit l'id, la révision, les dépendances et les valeurs par défaut de settings d'un module sans l'enregistrer. LuaMindModuleDescriptor.settings() contient des valeurs Double, Boolean, String ou List<String>. MagmaCore ne transmet pas les réglages aux scripts. Un hôte qui veut des valeurs par créature fusionne les siennes par-dessus ces valeurs par défaut et les expose, comme les familiers de FreeMinecraftModels le font avec c.settings.
  • Étendre les contextes de callback. LuaMindDefinition.instantiate(LuaMindContextExtension) ajoute vos champs à la table de contexte de chaque callback. extend(table, context, contribution) reçoit la table avant que Lua ne la voie, le MindContext du callback, et l'id du module qui a déclaré le callback, ou l'id propre du programme. Elle s'exécute sur le thread serveur dans le budget d'exécution du callback : son temps CPU compte donc dans le cpu_micros du programme, et une exception fait échouer le callback. Ajoutez des champs avec rawset, ou avec LuaTableSupport.lazyField, qui conserve le __index existant de la table pour toutes les autres clés. Ne remplacez ni la métatable de la table ni son __index, car les sections intégrées sont lues à travers eux, et n'écrasez pas tick, generation, entity, memory, perception, actuator ni actions. Lire une section intégrée depuis extend la construit pour ce callback. Passez une extension distincte à chaque appel à instantiate lorsqu'elle contient un état propre à chaque entité.
  • Intervalle de sondage. Un comportement Java peut redéfinir MindBehavior.probeInterval(), l'équivalent du probe_interval de Lua, pour n'interroger canStart que tous les N ticks tant qu'il est arrêté. La valeur doit aller de 1 à MindBehavior.MAX_PROBE_INTERVAL (200) ; MindProgram refuse les autres valeurs. La valeur par défaut de 1 interroge à chaque tick. Un MindSensor détecte au premier tick où son corps est actualisé, puis tous les intervalTicks() ticks selon une phase propre à chaque entité.
  • Vol. MindActuator.steerFlight(Vector), qui figurait déjà dans ce build, fait voler un corps FLYING selon cette vitesse pendant un tick et nécessite les réservations MOVE et LOOK. Son plafond et le steer_flight de Lua sont nouveaux : MindActuator.MAX_FLIGHT_SPEED vaut 4 blocs par tick, et une vitesse supérieure y est ramenée. RouteFlight y plafonne aussi sa vitesse : une route réglée à plus de 80 blocs par seconde vole donc à 80.

Recompiler un consommateur après modification de la bibliothèque​

Utilisez le JDK 21 et publiez MagmaCore dans Maven Local avec ./gradlew publishToMavenLocal, ou .\gradlew.bat publishToMavenLocal sous Windows. Vérifiez que le plugin consommateur résout ce dépôt et la version voulue, puis reconstruisez son JAR avec shading. Remplacer une bibliothèque dans Maven Local ne met pas à jour un plugin déjà compilé.

Consultez l'index développeur pour les API des plugins installés. Ces intégrations utilisent généralement provided ou compileOnly ; le contrat de bibliothèque intégrée de MagmaCore est différent.