Aller au contenu principal

API FreeMinecraftModels et guide du développeur

FreeMinecraftModels est à la fois un plugin autonome et une surface d'API pour d'autres plugins.

Dépôt Maven

<repository>
<id>magmaguy-repo-releases</id>
<name>MagmaGuy's Repository</name>
<url>https://repo.magmaguy.com/releases</url>
</repository>
<repository>
<id>magmaguy-repo-snapshots</id>
<name>MagmaGuy's Snapshot Repository</name>
<url>https://repo.magmaguy.com/snapshots</url>
</repository>

Dépendance

<dependency>
<groupId>com.magmaguy</groupId>
<artifactId>FreeMinecraftModels</artifactId>
<version>LATEST.VERSION.HERE</version>
<scope>provided</scope>
</dependency>

Utilisez-le en compileOnly/provided. Ne shadez pas le plugin dans votre propre jar.

Points d'entrée principaux

  • ModeledEntityManager.modelExists(String)
  • ModeledEntityManager.reload()
  • ModeledEntityManager.getAllEntities()
  • ModeledEntityManager.getDynamicEntities()
  • ModeledEntityManager.propEntities()
  • DisguiseAPI — déguise / dé-déguise les joueurs en modèles chargés
  • LocationAPI — enregistre des détecteurs de donjons et fournisseurs de protection (alimente les prédicats Lua em.location.*)
  • ScriptedItemAPI — marque des ItemStacks tiers avec les métadonnées d'objets scriptés FMM

Types runtime principaux

  • ModeledEntity
  • StaticEntity
  • DynamicEntity
  • PropEntity

Création d'entités

StaticEntity preview = StaticEntity.create("example_model", location);
DynamicEntity mobModel = DynamicEntity.create("example_model", livingEntity);
DynamicEntity mount = DynamicEntity.createWithInvisibility("example_model", livingEntity);
PropEntity prop = PropEntity.spawnPropEntity("example_model", location);

Tous les chemins de création retournent null si l'ID de modèle demandé n'est pas chargé.

createWithInvisibility est une variante qui applique une potion d'invisibilité au lieu de masquer l'entité aux clients. Cela maintient l'entité suivie côté client, ce qui est nécessaire pour le contrôle de véhicule (utilisé en interne par /fmm mount).

Méthodes runtime utiles

  • ModeledEntity#setDisplayName(String)
  • ModeledEntity#setDisplayNameVisible(boolean)
  • ModeledEntity#setLeftClickCallback(...)
  • ModeledEntity#setRightClickCallback(...)
  • ModeledEntity#setHitboxContactCallback(...)
  • ModeledEntity#setModeledEntityHitByProjectileCallback(...)
  • ModeledEntity#playAnimation(String, boolean, boolean)
  • ModeledEntity#stopCurrentAnimations()
  • ModeledEntity#hasAnimation(String)
  • ModeledEntity#getEntityID() -- retourne la chaîne d'ID de modèle
  • ModeledEntity#getLocation() -- retourne la Location actuelle
  • ModeledEntity#getWorld() -- retourne le World
  • ModeledEntity#getViewers() -- retourne le HashSet<UUID> des joueurs qui peuvent voir l'entité
  • ModeledEntity#getNametagBones() -- retourne List<Bone> des os de nametag (utile pour placer du texte supplémentaire)
  • ModeledEntity#getScaleModifier() / setScaleModifier(double)
  • ModeledEntity#removeWithDeathAnimation() -- supprime avec l'animation de mort (s'il en existe une)
  • ModeledEntity#removeWithMinimizedAnimation() -- supprime avec une animation de réduction d'échelle
  • ModeledEntity#remove() -- supprime immédiatement l'entité et tous les os
  • ModeledEntity#setTintColor(Color) / getTintColor() -- applique une teinte persistante via le canal de teinte de l'armure de cuir. Les flashs de dégâts remplacent brièvement la teinte puis s'estompent vers elle. Passez null pour effacer.
  • ModeledEntity#setViewDistanceOverride(int) / getEffectiveViewDistance() -- surcharge DefaultConfig.maxModelViewDistance pour une seule entité. Passez -1 pour revenir à la valeur par défaut du plugin.
  • DynamicEntity#setSyncMovement(boolean)
  • DynamicEntity#isDamagesOnContact() / setDamagesOnContact(boolean) -- contrôle si l'entité inflige des dégâts aux joueurs via le contact de la hitbox
  • Bone#getBoneLocation()

Surface d'événements

Événements d'interaction génériques :

  • ModeledEntityLeftClickEvent
  • ModeledEntityRightClickEvent
  • ModeledEntityHitboxContactEvent
  • ModeledEntityHitByProjectileEvent

Des variantes typées existent également pour StaticEntity, DynamicEntity et PropEntity.

Événements de cycle de vie :

  • ResourcePackGenerationEvent -- se déclenche lorsque FMM termine la génération ou régénération du resource pack (au démarrage et sur /fmm reload). Écoutez ceci pour déclencher du post-processing sur le pack généré.

  • FmmReloadedEvent -- se déclenche après que FMM termine sa séquence d'initialisation au démarrage et après chaque /fmm reload. Se déclenche toujours sur le thread principal du serveur.

    Les plugins consommateurs qui détiennent des références DynamicEntity ou PropEntity à long terme (EliteMobs, BetterStructures, etc.) doivent gérer cet événement en recréant leurs attachements de modèle sur les entités sous-jacentes survivantes. Sans cela, ces entités deviennent invisibles après un reload car FMM a démonté les display entities durant onDisable tandis que la référence du consommateur est maintenant obsolète.

    @EventHandler
    public void onFmmReloaded(FmmReloadedEvent event) {
    for (MyTrackedEntity tracked : myEntities) {
    if (tracked.bukkitEntity != null && tracked.bukkitEntity.isValid()) {
    tracked.fmmModel = DynamicEntity.create("my_model", tracked.bukkitEntity);
    }
    }
    }

ModeledEntityHitByProjectileEvent et détection de projectiles OBB

FMM utilise la détection de coup OBB (oriented bounding box) pour les projectiles contre les entités modélisées. Lorsqu'un projectile intersecte la hitbox OBB d'une entité modélisée, FMM déclenche un ModeledEntityHitByProjectileEvent. Il s'agit d'un événement Bukkit standard annulable.

Détails clés :

  • Les dégâts d'arc sont calculés avec le support du bonus d'enchantement POWER
  • L'enchantement PIERCING est respecté -- les flèches peuvent traverser jusqu'à des cibles supplémentaires
  • Annulez l'événement pour empêcher les dégâts et bloquer entièrement le coup
@EventHandler
public void onProjectileHitModel(ModeledEntityHitByProjectileEvent event) {
ModeledEntity target = event.getModeledEntity();
Projectile projectile = event.getProjectile();
// Annuler pour empêcher les dégâts
event.setCancelled(true);
}

Utilitaires d'objets et de modèles

ModelItemFactory

Classe factory pour créer programmatiquement des ItemStacks liés aux modèles.

// Crée un objet de placement de prop (utilise la clé PDC "model_id")
ItemStack placementItem = ModelItemFactory.createModelItem("lamp_post", Material.STICK);

// Crée un objet personnalisé à partir de la config (utilise la clé PDC "fmm_item_id")
PropScriptConfigFields config = ItemScriptManager.getItemDefinitions().get("magic_sword");
ItemStack customItem = ModelItemFactory.createCustomItem("magic_sword", config);
  • createModelItem(String modelId, Material material) -- crée un objet de placement pour les props. Sur 1.21.4+, applique automatiquement le rendu de modèle d'affichage si un JSON d'affichage existe.
  • createCustomItem(String itemId, PropScriptConfigFields config) -- crée un objet personnalisé avec nom, lore, enchantements et modèle d'affichage depuis la config unifiée.
  • formatModelName(String modelId) -- utilitaire qui convertit un ID de modèle comme 01_em_flame_sword en Flame Sword.

DisplayModelRegistry

Registre simple qui suit quels modèles ont un JSON d'affichage disponible.

// Vérifie si un modèle a un JSON de modèle d'affichage enregistré
boolean has3D = DisplayModelRegistry.hasDisplayModel("magic_sword");
  • register(String modelId) -- enregistre un ID de modèle (appelé en interne pendant le reload)
  • hasDisplayModel(String modelId) -- retourne true si un .json de modèle d'affichage existe pour ce modèle
  • getRegisteredModels() -- retourne un Set<String> immuable de tous les IDs de modèle qui ont des modèles d'affichage enregistrés
  • shutdown() -- efface tous les enregistrements

ItemScriptManager

Gère le cycle de vie des scripts Lua par joueur pour les objets personnalisés (modèles avec material: défini dans leur config YML).

// Obtient toutes les définitions d'objets personnalisés enregistrées
Map<String, PropScriptConfigFields> items = ItemScriptManager.getItemDefinitions();

// Obtient l'instance de script Lua active pour un joueur + objet
ScriptInstance instance = ItemScriptManager.getActiveScript(playerUUID, "magic_sword");

// Obtient tous les scripts actifs pour un joueur
Map<String, ScriptInstance> scripts = ItemScriptManager.getActiveScripts(playerUUID);
  • scanForCustomItems(File modelsFolder) -- scanne les configs YML de modèles pour les objets personnalisés
  • updateEquippedScripts(Player player) -- compare les objets équipés aux scripts en cours d'exécution, déclenchant les hooks equip/unequip
  • removePlayer(Player player) -- arrête tous les scripts pour un joueur (appeler à la déconnexion)
  • getItemDefinitions() -- retourne la map d'ID d'objet vers PropScriptConfigFields

ScriptedItemAPI

API publique pour les plugins externes pour s'intégrer au système d'objets scriptés de FMM. Cela permet à d'autres plugins de marquer leurs propres ItemStacks avec les données d'objet scripté FMM (tag PDC + item model) afin que les hooks de script Lua de FMM se déclenchent pour ces objets, sans que FMM ne remplace le nom, le lore ou les enchantements de l'objet.

// Vérifie si une définition d'objet scripté existe
boolean exists = ScriptedItemAPI.isValidItemId("flame_blade");

// Applique les données d'objet scripté FMM à un ItemStack existant
// Cela définit :
// - Le tag PDC fmm_item_id (afin que le système de scripts FMM reconnaisse l'objet)
// - L'item model (1.21.4+) depuis le registre de modèles d'affichage de FMM
// Ne modifie PAS le nom, lore, enchantements ou toute autre propriété de l'objet.
boolean success = ScriptedItemAPI.applyScriptedItemData(itemStack, "flame_blade");

// Obtient la config pour un objet scripté
PropScriptConfigFields config = ScriptedItemAPI.getItemConfig("flame_blade");
  • isValidItemId(String itemId) -- retourne true si l'ID d'objet est enregistré dans les définitions d'objet de FMM
  • applyScriptedItemData(ItemStack itemStack, String itemId) -- estampe le tag PDC et l'item model sur un ItemStack existant. Retourne true en cas de succès, false si l'ID d'objet est invalide ou si l'ItemStack n'a pas de meta. Note arc/arbalète : si l'itemId donné n'a pas de modèle d'affichage mais que itemId + "_idle" en a un (c'est-à-dire que l'objet a des modèles d'état d'arc/arbalète), la méthode utilise automatiquement le modèle _idle comme modèle d'affichage
  • getItemConfig(String itemId) -- retourne le PropScriptConfigFields pour l'ID d'objet donné, ou null si non trouvé
Intégration EliteMobs

EliteMobs utilise cette API en interne via le champ de config scriptedItem. Lorsqu'un objet personnalisé EliteMobs définit scriptedItem: flame_blade, EliteMobs construit son objet normalement (nom, lore, enchantements, niveau) puis appelle ScriptedItemAPI.applyScriptedItemData() pour ajouter le modèle et le comportement de script de FMM par-dessus.

DisguiseAPI

Point d'entrée public pour la fonctionnalité de déguisement des joueurs. Les plugins tiers devraient appeler cette classe plutôt que le DisguiseManager interne afin que les refactorisations internes restent sûres.

import com.magmaguy.freeminecraftmodels.api.DisguiseAPI;

// Déguise un joueur en modèle chargé. Remplace proprement tout déguisement existant.
boolean ok = DisguiseAPI.disguise(player, "dragon");

// Dé-déguise (retourne true si un déguisement a été retiré).
DisguiseAPI.undisguise(player);

// Interrogation d'état.
boolean disguised = DisguiseAPI.isDisguised(player);
String modelID = DisguiseAPI.getDisguiseModelID(player); // null si non déguisé

// Snapshot de tous les joueurs actuellement déguisés.
Collection<Player> all = DisguiseAPI.getDisguisedPlayers();
  • disguise(Player, String modelID) -- retourne false si l'ID de modèle n'est pas chargé
  • undisguise(Player) -- retourne true si un déguisement a été retiré
  • isDisguised(Player) -- vérification booléenne rapide
  • getDisguiseModelID(Player) -- retourne l'ID de modèle actif ou null
  • getDisguisedPlayers() -- snapshot non modifiable des joueurs déguisés

Les joueurs déguisés sont rendus invisibles aux autres et le restent jusqu'au dé-déguisement — les seaux de lait, les effets de balise et les interactions similaires ne brisent pas l'invisibilité.

LocationAPI

API publique pour les plugins afin de contribuer à la détection de donjons et aux vérifications de protection de région. Les prédicats enregistrés alimentent les vérifications Lua em.location.is_in_dungeon et em.location.is_protected de FMM (utilisés par les scripts préfaits comme pickupable.lua et storage_double.lua).

Les plugins passent un Predicate<Location> simple afin qu'aucun type FMM shadé ne traverse les classloaders de plugin.

import com.magmaguy.freeminecraftmodels.api.LocationAPI;

// Au démarrage de votre plugin, après que WorldGuard/EliteMobs/etc. soient disponibles.
LocationAPI.registerDungeonLocator("EliteMobs",
location -> EliteMobs.isInsideDungeon(location));

LocationAPI.registerProtectionProvider("WorldGuard",
location -> WorldGuardBridge.isProtected(location));
  • registerDungeonLocator(String providerName, Predicate<Location> predicate) — tout prédicat enregistré retournant true marque l'emplacement comme « in dungeon »
  • registerProtectionProvider(String providerName, Predicate<Location> predicate) — tout prédicat enregistré retournant true marque l'emplacement comme protégé

Les opérateurs peuvent vérifier l'enregistrement avec /fmm location, qui rapporte le nombre de fournisseurs en direct et teste les deux prédicats sur leur position actuelle.

PropScriptConfigFields

Classe de configuration unifiée pour les fichiers de config YML de modèle. Utilisée à la fois par les scripts de prop et les objets personnalisés.

# Exemple : torch_01.yml
isEnabled: true
scripts:
- torch_glow.lua
material: STICK # Si défini, le modèle devient un objet personnalisé
name: "&eMagic Torch" # Nom d'affichage personnalisé (optionnel)
lore: # Lignes de lore personnalisées (optionnel)
- "&7Glows in the dark"
enchantments: # Enchantements (optionnel, format: NAME,LEVEL)
- "FIRE_ASPECT,1"

Méthodes clés : isCustomItem(), getParsedMaterial(), getParsedEnchantments(), getScripts().

Scripting Lua

FreeMinecraftModels supporte les scripts Lua à la fois pour les props et les objets personnalisés via le moteur de scripting MagmaCore 2.0. Les fichiers de scripts sont placés dans plugins/FreeMinecraftModels/scripts/ et sont liés aux modèles via une config YML voisine à côté du fichier de modèle.

Hooks de script de prop

HookDéclencheur
on_spawnLe prop apparaît dans le monde
on_game_tickÀ chaque tick tant que le prop est vivant
on_zone_enterUn joueur entre dans la zone du prop
on_zone_leaveUn joueur quitte la zone du prop
on_destroyLe prop est retiré
on_left_clickLe joueur clique gauche sur le prop
on_right_clickLe joueur clique droit sur le prop
on_projectile_hitUn projectile touche le prop

Hooks de script d'objet

Les objets personnalisés (modèles avec material: défini) supportent 22 hooks Lua :

HookDéclencheur
on_equipL'objet entre dans un slot d'équipement suivi
on_unequipL'objet quitte un slot d'équipement suivi
on_game_tickÀ chaque tick tant que l'objet est équipé
on_attack_entityLe joueur attaque une entité en tenant l'objet
on_kill_entityLe joueur tue une entité en tenant l'objet
on_take_damageLe joueur subit des dégâts pendant que l'objet est équipé
on_shield_blockLe joueur bloque avec un bouclier
on_shoot_bowLe joueur tire à l'arc
on_projectile_hitUn projectile tiré par le joueur touche quelque chose
on_projectile_launchLe joueur lance un projectile
on_right_clickLe joueur clique droit avec l'objet
on_left_clickLe joueur clique gauche avec l'objet
on_shift_right_clickLe joueur fait shift+clic droit avec l'objet
on_shift_left_clickLe joueur fait shift+clic gauche avec l'objet
on_interact_entityLe joueur clique droit sur une entité avec l'objet
on_swap_handsLe joueur échange l'objet entre les mains
on_dropLe joueur jette l'objet
on_break_blockLe joueur casse un bloc en tenant l'objet
on_consumeLe joueur consomme l'objet
on_item_damageL'objet subit des dégâts de durabilité
on_fishLe joueur utilise une canne à pêche
on_deathLe joueur meurt pendant que l'objet est équipé

Les scripts d'objet reçoivent context.item (avec l'ID d'objet et les infos joueur) au lieu de context.prop.

Table de contexte des scripts de prop

Les scripts de prop reçoivent une table context. Voici un résumé des APIs clés -- voir API Prop Lua pour tous les détails.

context.prop :

  • model_id -- le nom de modèle blueprint
  • current_location -- la position actuelle du prop
  • play_animation(name, blend, loop) -- joue l'animation nommée (blend et loop sont à true par défaut)
  • stop_animation() -- arrête toutes les animations en cours
  • hurt_visual() -- joue le visuel de blessure (flash rouge) sur le prop
  • pickup() -- supprime le prop et drop son objet de placement
  • mount(player) -- fait monter un joueur sur le prop
  • dismount(player) -- fait descendre un joueur du prop
  • get_passengers() -- retourne une liste des joueurs chevauchant actuellement le prop
  • spawn_elitemobs_boss(filename, x, y, z) -- fait apparaître un boss EliteMobs relatif au prop

context.event :

  • Disponible dans on_left_click, on_right_click et on_projectile_hit
  • cancel(), uncancel(), is_cancelled
  • player -- le joueur qui a déclenché l'événement

context.world :

  • spawn_entity(entity_type, location) -- fait apparaître une entité vanilla
  • set_block_at(location, material) -- définit un bloc dans le monde
  • Plus particules, sons, requêtes de blocs, foudre et recherches d'entités proches

Objets joueur (depuis context.event.player) :

  • get_held_item() -- retourne l'objet que tient le joueur
  • consume_held_item() -- retire un de l'objet tenu
  • has_item(material) -- vérifie si le joueur a un objet
  • send_message(text) -- envoie un message de chat au joueur
  • game_mode -- le mode de jeu actuel du joueur

Exemple de script de prop

function on_spawn(context)
context.prop.play_animation("idle", true, true)
end

function on_right_click(context)
context.prop.play_animation("activate", false, false)
end

Exemple de script d'objet

function on_equip(context)
context.event.player.send_message("&6You equipped the Flame Blade!")
end

function on_attack_entity(context)
-- Effet de feu au coup
context.event.player.send_message("&cBurn!")
end

function on_unequip(context)
context.event.player.send_message("&7Flame Blade sheathed.")
end

Notes

  • FreeMinecraftModels est une dépendance plugin installée, pas une bibliothèque embarquable.
  • Si votre plugin a besoin de modèles fraîchement importés, appelez ModeledEntityManager.reload() au lieu d'essayer de reconstruire l'état de FreeMinecraftModels vous-même.
  • Tous les plugins de l'écosystème Nightbreak dépendent désormais de MagmaCore 2.2.0-SNAPSHOT, qui inclut le moteur de scripting Lua partagé utilisé par les scripts de prop FreeMinecraftModels et les pouvoirs Lua d'EliteMobs, plus les LocationQueryRegistry et WorldFolderResolver partagés.
  • FreeMinecraftModels déclare WorldGuard, WorldEdit, GriefPrevention et Vault en tant que softdepend. Aucun n'est requis pour démarrer le plugin, mais ils débloquent des fonctionnalités spécifiques : WorldGuard/WorldEdit/GriefPrevention alimentent LocationAPI, et Vault active la boutique de mobilier.