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ésLocationAPI— enregistre des détecteurs de donjons et fournisseurs de protection (alimente les prédicats Luaem.location.*)ScriptedItemAPI— marque des ItemStacks tiers avec les métadonnées d'objets scriptés FMM
Types runtime principaux
ModeledEntityStaticEntityDynamicEntityPropEntity
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èleModeledEntity#getLocation()-- retourne laLocationactuelleModeledEntity#getWorld()-- retourne leWorldModeledEntity#getViewers()-- retourne leHashSet<UUID>des joueurs qui peuvent voir l'entitéModeledEntity#getNametagBones()-- retourneList<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'échelleModeledEntity#remove()-- supprime immédiatement l'entité et tous les osModeledEntity#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. Passeznullpour effacer.ModeledEntity#setViewDistanceOverride(int)/getEffectiveViewDistance()-- surchargeDefaultConfig.maxModelViewDistancepour une seule entité. Passez-1pour 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 hitboxBone#getBoneLocation()
Surface d'événements
Événements d'interaction génériques :
ModeledEntityLeftClickEventModeledEntityRightClickEventModeledEntityHitboxContactEventModeledEntityHitByProjectileEvent
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
DynamicEntityouPropEntityà 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 durantonDisabletandis 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
PIERCINGest 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 comme01_em_flame_swordenFlame 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)-- retournetruesi un.jsonde modèle d'affichage existe pour ce modèlegetRegisteredModels()-- retourne unSet<String>immuable de tous les IDs de modèle qui ont des modèles d'affichage enregistrésshutdown()-- 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ésupdateEquippedScripts(Player player)-- compare les objets équipés aux scripts en cours d'exécution, déclenchant les hooks equip/unequipremovePlayer(Player player)-- arrête tous les scripts pour un joueur (appeler à la déconnexion)getItemDefinitions()-- retourne la map d'ID d'objet versPropScriptConfigFields
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)-- retournetruesi l'ID d'objet est enregistré dans les définitions d'objet de FMMapplyScriptedItemData(ItemStack itemStack, String itemId)-- estampe le tag PDC et l'item model sur un ItemStack existant. Retournetrueen cas de succès,falsesi l'ID d'objet est invalide ou si l'ItemStack n'a pas de meta. Note arc/arbalète : si l'itemIddonné n'a pas de modèle d'affichage mais queitemId + "_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_idlecomme modèle d'affichagegetItemConfig(String itemId)-- retourne lePropScriptConfigFieldspour l'ID d'objet donné, ounullsi non trouvé
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)-- retournefalsesi l'ID de modèle n'est pas chargéundisguise(Player)-- retournetruesi un déguisement a été retiréisDisguised(Player)-- vérification booléenne rapidegetDisguiseModelID(Player)-- retourne l'ID de modèle actif ounullgetDisguisedPlayers()-- 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é retournanttruemarque l'emplacement comme « in dungeon »registerProtectionProvider(String providerName, Predicate<Location> predicate)— tout prédicat enregistré retournanttruemarque 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
| Hook | Déclencheur |
|---|---|
on_spawn | Le prop apparaît dans le monde |
on_game_tick | À chaque tick tant que le prop est vivant |
on_zone_enter | Un joueur entre dans la zone du prop |
on_zone_leave | Un joueur quitte la zone du prop |
on_destroy | Le prop est retiré |
on_left_click | Le joueur clique gauche sur le prop |
on_right_click | Le joueur clique droit sur le prop |
on_projectile_hit | Un projectile touche le prop |
Hooks de script d'objet
Les objets personnalisés (modèles avec material: défini) supportent 22 hooks Lua :
| Hook | Déclencheur |
|---|---|
on_equip | L'objet entre dans un slot d'équipement suivi |
on_unequip | L'objet quitte un slot d'équipement suivi |
on_game_tick | À chaque tick tant que l'objet est équipé |
on_attack_entity | Le joueur attaque une entité en tenant l'objet |
on_kill_entity | Le joueur tue une entité en tenant l'objet |
on_take_damage | Le joueur subit des dégâts pendant que l'objet est équipé |
on_shield_block | Le joueur bloque avec un bouclier |
on_shoot_bow | Le joueur tire à l'arc |
on_projectile_hit | Un projectile tiré par le joueur touche quelque chose |
on_projectile_launch | Le joueur lance un projectile |
on_right_click | Le joueur clique droit avec l'objet |
on_left_click | Le joueur clique gauche avec l'objet |
on_shift_right_click | Le joueur fait shift+clic droit avec l'objet |
on_shift_left_click | Le joueur fait shift+clic gauche avec l'objet |
on_interact_entity | Le joueur clique droit sur une entité avec l'objet |
on_swap_hands | Le joueur échange l'objet entre les mains |
on_drop | Le joueur jette l'objet |
on_break_block | Le joueur casse un bloc en tenant l'objet |
on_consume | Le joueur consomme l'objet |
on_item_damage | L'objet subit des dégâts de durabilité |
on_fish | Le joueur utilise une canne à pêche |
on_death | Le 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 blueprintcurrent_location-- la position actuelle du propplay_animation(name, blend, loop)-- joue l'animation nommée (blend et loop sont àtruepar défaut)stop_animation()-- arrête toutes les animations en courshurt_visual()-- joue le visuel de blessure (flash rouge) sur le proppickup()-- supprime le prop et drop son objet de placementmount(player)-- fait monter un joueur sur le propdismount(player)-- fait descendre un joueur du propget_passengers()-- retourne une liste des joueurs chevauchant actuellement le propspawn_elitemobs_boss(filename, x, y, z)-- fait apparaître un boss EliteMobs relatif au prop
context.event :
- Disponible dans
on_left_click,on_right_clicketon_projectile_hit cancel(),uncancel(),is_cancelledplayer-- le joueur qui a déclenché l'événement
context.world :
spawn_entity(entity_type, location)-- fait apparaître une entité vanillaset_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 joueurconsume_held_item()-- retire un de l'objet tenuhas_item(material)-- vérifie si le joueur a un objetsend_message(text)-- envoie un message de chat au joueurgame_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
LocationQueryRegistryetWorldFolderResolverpartagé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 alimententLocationAPI, et Vault active la boutique de mobilier.