FreeMinecraftModels API und Entwicklerhandbuch
FreeMinecraftModels ist sowohl ein eigenständiges Plugin als auch eine API-Oberfläche für andere Plugins.
Maven Repository
<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>
Abhängigkeit
<dependency>
<groupId>com.magmaguy</groupId>
<artifactId>FreeMinecraftModels</artifactId>
<version>LATEST.VERSION.HERE</version>
<scope>provided</scope>
</dependency>
Verwende es als compileOnly/provided. Schließe das Plugin nicht in deine eigene JAR ein (kein Shading).
Zentrale Einstiegspunkte
ModeledEntityManager.modelExists(String)ModeledEntityManager.reload()ModeledEntityManager.getAllEntities()ModeledEntityManager.getDynamicEntities()ModeledEntityManager.propEntities()DisguiseAPI— Spieler als geladene Modelle verkleiden / Verkleidung entfernenLocationAPI— Dungeon-Detektoren und Schutzanbieter registrieren (versorgt Luaem.location.*-Prädikate)ScriptedItemAPI— Drittanbieter-ItemStacks mit FMM-skriptfähigem Item-Metadata stempeln
Zentrale Laufzeittypen
ModeledEntityStaticEntityDynamicEntityPropEntity
Entitäten erstellen
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);
Alle Erstellungspfade geben null zurück, wenn die angeforderte Modell-ID nicht geladen ist.
createWithInvisibility ist eine Variante, die einen Unsichtbarkeitstrank anwendet, anstatt die Entität vor Clients zu verbergen. Dadurch bleibt die Entität clientseitig getrackt, was für die Fahrzeugsteuerung erforderlich ist (intern von /fmm mount verwendet).
Nützliche Laufzeit-Methoden
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()-- gibt den Modell-ID-String zurückModeledEntity#getLocation()-- gibt die aktuelleLocationzurückModeledEntity#getWorld()-- gibt dieWorldzurückModeledEntity#getViewers()-- gibt dasHashSet<UUID>der Spieler zurück, die die Entität sehen könnenModeledEntity#getNametagBones()-- gibtList<Bone>der Nametag-Bones zurück (nützlich zum Platzieren zusätzlichen Texts)ModeledEntity#getScaleModifier()/setScaleModifier(double)ModeledEntity#removeWithDeathAnimation()-- entfernt mit der Todesanimation (falls vorhanden)ModeledEntity#removeWithMinimizedAnimation()-- entfernt mit einer schrumpfenden AnimationModeledEntity#remove()-- entfernt die Entität und alle Bones sofortModeledEntity#setTintColor(Color)/getTintColor()-- wendet eine dauerhafte Färbung über den Lederrüstung-Färbekanal an. Schadensblitze überschreiben die Färbung kurzzeitig und blenden dann zurück. Übergibnull, um zu löschen.ModeledEntity#setViewDistanceOverride(int)/getEffectiveViewDistance()-- überschreibtDefaultConfig.maxModelViewDistancefür eine einzelne Entität. Übergib-1, um zum pluginweiten Standard zurückzukehren.DynamicEntity#setSyncMovement(boolean)DynamicEntity#isDamagesOnContact()/setDamagesOnContact(boolean)-- steuert, ob die Entität Spielern durch Hitbox-Kontakt Schaden zufügtBone#getBoneLocation()
Event-Oberfläche
Generische Interaktions-Events:
ModeledEntityLeftClickEventModeledEntityRightClickEventModeledEntityHitboxContactEventModeledEntityHitByProjectileEvent
Typisierte Varianten existieren auch für StaticEntity, DynamicEntity und PropEntity.
Lebenszyklus-Events:
-
ResourcePackGenerationEvent-- wird ausgelöst, wenn FMM die Generierung oder Regenerierung des Ressourcenpakets abschließt (beim Start und bei/fmm reload). Höre auf dieses Event, um Nachbearbeitung am generierten Paket auszulösen. -
FmmReloadedEvent-- wird ausgelöst, nachdem FMM seine Initialisierungssequenz beim Start beendet hat und nach jedem/fmm reload. Wird stets im Haupt-Server-Thread ausgelöst.Verbraucher-Plugins, die langlebige
DynamicEntity- oderPropEntity-Referenzen halten (EliteMobs, BetterStructures usw.), müssen dieses Event behandeln, indem sie ihre Modellanhänge an den überlebenden zugrunde liegenden Entitäten neu erstellen. Ohne dies werden diese Entitäten nach einem Reload unsichtbar, weil FMM die Display-Entitäten währendonDisableabgebaut hat, während die Referenz des Verbrauchers nun veraltet ist.@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 und OBB-Projektil-Erkennung
FMM verwendet OBB-Erkennung (Oriented Bounding Box) für Projektile gegen modellierte Entitäten. Wenn ein Projektil mit der OBB-Hitbox einer modellierten Entität kollidiert, löst FMM ein ModeledEntityHitByProjectileEvent aus. Dies ist ein standardmäßiges, cancelbares Bukkit-Event.
Wichtige Details:
- Pfeilschaden wird mit Unterstützung des
POWER-Verzauberungsbonus berechnet PIERCING-Verzauberung wird respektiert -- Pfeile können zu weiteren Zielen durchschlagen- Brich das Event ab, um Schaden zu verhindern und den Treffer vollständig zu blockieren
@EventHandler
public void onProjectileHitModel(ModeledEntityHitByProjectileEvent event) {
ModeledEntity target = event.getModeledEntity();
Projectile projectile = event.getProjectile();
// Abbrechen, um Schaden zu verhindern
event.setCancelled(true);
}
Item- & Modell-Hilfen
ModelItemFactory
Factory-Klasse zum programmgesteuerten Erstellen modellbezogener ItemStacks.
// Ein Prop-Platzierungs-Item erstellen (verwendet den "model_id"-PDC-Schlüssel)
ItemStack placementItem = ModelItemFactory.createModelItem("lamp_post", Material.STICK);
// Ein benutzerdefiniertes Item aus der Konfiguration erstellen (verwendet den "fmm_item_id"-PDC-Schlüssel)
PropScriptConfigFields config = ItemScriptManager.getItemDefinitions().get("magic_sword");
ItemStack customItem = ModelItemFactory.createCustomItem("magic_sword", config);
createModelItem(String modelId, Material material)-- erstellt ein Platzierungs-Item für Props. Auf 1.21.4+ wird automatisch das Display-Modell-Rendering angewendet, falls ein Display-JSON existiert.createCustomItem(String itemId, PropScriptConfigFields config)-- erstellt ein benutzerdefiniertes Item mit Name, Lore, Verzauberungen und Display-Modell aus der vereinheitlichten Konfiguration.formatModelName(String modelId)-- Hilfsmittel, das eine Modell-ID wie01_em_flame_swordinFlame Swordkonvertiert.
DisplayModelRegistry
Einfache Registry, die nachverfolgt, welche Modelle ein Display-JSON verfügbar haben.
// Prüfen, ob ein Modell ein Display-Modell-JSON registriert hat
boolean has3D = DisplayModelRegistry.hasDisplayModel("magic_sword");
register(String modelId)-- registriert eine Modell-ID (wird intern während des Reloads aufgerufen)hasDisplayModel(String modelId)-- gibttruezurück, wenn ein.json-Display-Modell für dieses Modell existiertgetRegisteredModels()-- gibt ein unveränderlichesSet<String>aller Modell-IDs zurück, die Display-Modelle registriert habenshutdown()-- löscht alle Registrierungen
ItemScriptManager
Verwaltet den Lebenszyklus von pro-Spieler-Lua-Skripten für benutzerdefinierte Items (Modelle mit gesetztem material: in ihrer YML-Konfiguration).
// Alle registrierten benutzerdefinierten Item-Definitionen abrufen
Map<String, PropScriptConfigFields> items = ItemScriptManager.getItemDefinitions();
// Die aktive Lua-Skript-Instanz für einen Spieler + Item abrufen
ScriptInstance instance = ItemScriptManager.getActiveScript(playerUUID, "magic_sword");
// Alle aktiven Skripte für einen Spieler abrufen
Map<String, ScriptInstance> scripts = ItemScriptManager.getActiveScripts(playerUUID);
scanForCustomItems(File modelsFolder)-- durchsucht Modell-YML-Konfigurationen nach benutzerdefinierten ItemsupdateEquippedScripts(Player player)-- vergleicht ausgerüstete Items mit laufenden Skripten und löst Equip/Unequip-Hooks ausremovePlayer(Player player)-- fährt alle Skripte für einen Spieler herunter (bei Quit aufrufen)getItemDefinitions()-- gibt die Map von Item-ID zuPropScriptConfigFieldszurück
ScriptedItemAPI
Öffentliche API für externe Plugins zur Integration mit FMMs skriptfähigem Item-System. Das erlaubt anderen Plugins, ihre eigenen ItemStacks mit FMM-skriptfähigen Item-Daten (PDC-Tag + Item-Modell) zu stempeln, sodass FMMs Lua-Skript-Hooks für diese Items ausgelöst werden, ohne dass FMM den Namen, die Lore oder die Verzauberungen des Items überschreibt.
// Prüfen, ob eine skriptfähige Item-Definition existiert
boolean exists = ScriptedItemAPI.isValidItemId("flame_blade");
// FMM-skriptfähige Item-Daten auf einen bestehenden ItemStack anwenden
// Dies setzt:
// - Das fmm_item_id PDC-Tag (damit FMMs Skriptsystem das Item erkennt)
// - Das Item-Modell (1.21.4+) aus FMMs Display-Modell-Registry
// Verändert NICHT Name, Lore, Verzauberungen oder andere Item-Eigenschaften.
boolean success = ScriptedItemAPI.applyScriptedItemData(itemStack, "flame_blade");
// Die Konfiguration für ein skriptfähiges Item abrufen
PropScriptConfigFields config = ScriptedItemAPI.getItemConfig("flame_blade");
isValidItemId(String itemId)-- gibttruezurück, wenn die Item-ID in FMMs Item-Definitionen registriert istapplyScriptedItemData(ItemStack itemStack, String itemId)-- stempelt PDC-Tag und Item-Modell auf einen bestehenden ItemStack. Gibttruebei Erfolg zurück,falsewenn die Item-ID ungültig ist oder der ItemStack keine Meta hat. Hinweis zu Bogen/Armbrust: falls die angegebeneitemIdkein Display-Modell hat, aberitemId + "_idle"schon (d. h. das Item hat Bogen-/Armbrust-Zustandsmodelle), verwendet die Methode automatisch das_idle-Modell als Display-ModellgetItemConfig(String itemId)-- gibt diePropScriptConfigFieldsfür die angegebene Item-ID zurück, odernullwenn nicht gefunden
EliteMobs verwendet diese API intern über das Konfigurationsfeld scriptedItem. Wenn ein benutzerdefiniertes EliteMobs-Item scriptedItem: flame_blade setzt, baut EliteMobs sein Item normal auf (Name, Lore, Verzauberungen, Level) und ruft dann ScriptedItemAPI.applyScriptedItemData() auf, um darüber FMMs Modell und Skriptverhalten hinzuzufügen.
DisguiseAPI
Öffentlicher Einstiegspunkt für die Spieler-Verkleidungsfunktion. Drittanbieter-Plugins sollten diese Klasse aufrufen statt des internen DisguiseManager, damit interne Refactorings sicher bleiben.
import com.magmaguy.freeminecraftmodels.api.DisguiseAPI;
// Einen Spieler als geladenes Modell verkleiden. Ersetzt eine vorhandene Verkleidung sauber.
boolean ok = DisguiseAPI.disguise(player, "dragon");
// Verkleidung aufheben (gibt true zurück, wenn eine Verkleidung entfernt wurde).
DisguiseAPI.undisguise(player);
// Status abfragen.
boolean disguised = DisguiseAPI.isDisguised(player);
String modelID = DisguiseAPI.getDisguiseModelID(player); // null wenn nicht verkleidet
// Momentaufnahme aller derzeit verkleideten Spieler.
Collection<Player> all = DisguiseAPI.getDisguisedPlayers();
disguise(Player, String modelID)-- gibtfalsezurück, wenn die Modell-ID nicht geladen istundisguise(Player)-- gibttruezurück, wenn eine Verkleidung entfernt wurdeisDisguised(Player)-- schnelle Bool-PrüfunggetDisguiseModelID(Player)-- gibt die aktive Modell-ID odernullzurückgetDisguisedPlayers()-- unveränderliche Momentaufnahme der verkleideten Spieler
Verkleidete Spieler werden für andere unsichtbar gemacht und bleiben es, bis die Verkleidung aufgehoben wird — Milcheimer, Beacon-Effekt-Lösungen und ähnliche Interaktionen brechen die Unsichtbarkeit nicht.
LocationAPI
Öffentliche API, mit der Plugins zur Dungeon-Erkennung und zu Regions-Schutzprüfungen beitragen können. Die registrierten Prädikate versorgen FMMs Lua-Prüfungen em.location.is_in_dungeon und em.location.is_protected (verwendet von vorgefertigten Skripten wie pickupable.lua und storage_double.lua).
Plugins übergeben ein einfaches Predicate<Location>, sodass keine geshadeten FMM-Typen Plugin-Klassenlader überqueren.
import com.magmaguy.freeminecraftmodels.api.LocationAPI;
// Beim Aktivieren deines Plugins, nachdem WorldGuard/EliteMobs/usw. verfügbar sind.
LocationAPI.registerDungeonLocator("EliteMobs",
location -> EliteMobs.isInsideDungeon(location));
LocationAPI.registerProtectionProvider("WorldGuard",
location -> WorldGuardBridge.isProtected(location));
registerDungeonLocator(String providerName, Predicate<Location> predicate)— jedes registrierte Prädikat, dastruezurückgibt, markiert den Ort als „in einem Dungeon"registerProtectionProvider(String providerName, Predicate<Location> predicate)— jedes registrierte Prädikat, dastruezurückgibt, markiert den Ort als geschützt
Operatoren können die Registrierung mit /fmm location überprüfen, das die Anzahl der aktiven Provider meldet und beide Prädikate an ihrem aktuellen Standort testet.
PropScriptConfigFields
Vereinheitlichte Konfigurationsklasse für Modell-YML-Konfigurationsdateien. Wird sowohl von Prop-Skripten als auch von benutzerdefinierten Items verwendet.
# Beispiel: torch_01.yml
isEnabled: true
scripts:
- torch_glow.lua
material: STICK # Falls gesetzt, wird das Modell zu einem benutzerdefinierten Item
name: "&eMagic Torch" # Benutzerdefinierter Anzeigename (optional)
lore: # Benutzerdefinierte Lore-Zeilen (optional)
- "&7Glows in the dark"
enchantments: # Verzauberungen (optional, Format: NAME,LEVEL)
- "FIRE_ASPECT,1"
Wichtige Methoden: isCustomItem(), getParsedMaterial(), getParsedEnchantments(), getScripts().
Lua-Skripting
FreeMinecraftModels unterstützt Lua-Skripte sowohl für Props als auch für benutzerdefinierte Items über die MagmaCore 2.0 Skript-Engine. Skriptdateien werden in plugins/FreeMinecraftModels/scripts/ abgelegt und über eine begleitende YML-Konfiguration neben der Modelldatei an Modelle gebunden.
Prop-Skript-Hooks
| Hook | Auslöser |
|---|---|
on_spawn | Prop wird in die Welt gespawnt |
on_game_tick | Jeden Tick, während der Prop existiert |
on_zone_enter | Ein Spieler betritt die Zone des Props |
on_zone_leave | Ein Spieler verlässt die Zone des Props |
on_destroy | Prop wird entfernt |
on_left_click | Spieler klickt den Prop links an |
on_right_click | Spieler klickt den Prop rechts an |
on_projectile_hit | Ein Projektil trifft den Prop |
Item-Skript-Hooks
Benutzerdefinierte Items (Modelle mit gesetztem material:) unterstützen 22 Lua-Hooks:
| Hook | Auslöser |
|---|---|
on_equip | Item kommt in einen getrackten Ausrüstungsslot |
on_unequip | Item verlässt einen getrackten Ausrüstungsslot |
on_game_tick | Jeden Tick, während das Item ausgerüstet ist |
on_attack_entity | Spieler greift eine Entität an, während er das Item hält |
on_kill_entity | Spieler tötet eine Entität, während er das Item hält |
on_take_damage | Spieler erleidet Schaden, während das Item ausgerüstet ist |
on_shield_block | Spieler blockt mit einem Schild |
on_shoot_bow | Spieler schießt mit einem Bogen |
on_projectile_hit | Ein vom Spieler abgefeuertes Projektil trifft etwas |
on_projectile_launch | Spieler startet ein Projektil |
on_right_click | Spieler klickt rechts mit dem Item |
on_left_click | Spieler klickt links mit dem Item |
on_shift_right_click | Spieler Shift-Rechtsklick mit dem Item |
on_shift_left_click | Spieler Shift-Linksklick mit dem Item |
on_interact_entity | Spieler klickt eine Entität rechts an mit dem Item |
on_swap_hands | Spieler tauscht das Item zwischen den Händen |
on_drop | Spieler lässt das Item fallen |
on_break_block | Spieler bricht einen Block, während er das Item hält |
on_consume | Spieler konsumiert das Item |
on_item_damage | Item nimmt Haltbarkeitsschaden |
on_fish | Spieler benutzt eine Angel |
on_death | Spieler stirbt, während das Item ausgerüstet ist |
Item-Skripte erhalten context.item (mit der Item-ID und Spielerinformationen) anstelle von context.prop.
Prop-Skript-Context-Tabelle
Prop-Skripte erhalten eine context-Tabelle. Hier ist eine Zusammenfassung der wichtigsten APIs -- siehe Lua Prop API für alle Details.
context.prop:
model_id-- der Blueprint-Modellnamecurrent_location-- der aktuelle Standort des Propsplay_animation(name, blend, loop)-- spielt die genannte Animation ab (blend und loop sind standardmäßigtrue)stop_animation()-- stoppt alle laufenden Animationenhurt_visual()-- spielt den Verletzungs-(rotes Blinken)-Visualeffekt am Prop abpickup()-- entfernt den Prop und lässt sein Platzierungs-Item fallenmount(player)-- lässt einen Spieler den Prop reitendismount(player)-- entfernt einen Spieler vom Propget_passengers()-- gibt eine Liste der derzeit auf dem Prop sitzenden Spieler zurückspawn_elitemobs_boss(filename, x, y, z)-- spawnt einen EliteMobs-Boss relativ zum Prop
context.event:
- Verfügbar in
on_left_click,on_right_clickundon_projectile_hit cancel(),uncancel(),is_cancelledplayer-- der Spieler, der das Event ausgelöst hat
context.world:
spawn_entity(entity_type, location)-- spawnt eine Vanilla-Entitätset_block_at(location, material)-- setzt einen Block in der Welt- Plus Partikel, Sounds, Block-Abfragen, Blitze und Suche nach nahen Entitäten
Spieler-Objekte (aus context.event.player):
get_held_item()-- gibt das Item zurück, das der Spieler hältconsume_held_item()-- entfernt eines vom gehaltenen Itemhas_item(material)-- prüft, ob der Spieler ein Item hatsend_message(text)-- sendet eine Chat-Nachricht an den Spielergame_mode-- der aktuelle Spielmodus des Spielers
Prop-Skript-Beispiel
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
Item-Skript-Beispiel
function on_equip(context)
context.event.player.send_message("&6You equipped the Flame Blade!")
end
function on_attack_entity(context)
-- Feuer-Effekt beim Treffer
context.event.player.send_message("&cBurn!")
end
function on_unequip(context)
context.event.player.send_message("&7Flame Blade sheathed.")
end
Hinweise
- FreeMinecraftModels ist eine installierte Plugin-Abhängigkeit, keine einbettbare Bibliothek.
- Wenn dein Plugin frisch importierte Modelle benötigt, rufe
ModeledEntityManager.reload()auf, anstatt zu versuchen, den FreeMinecraftModels-Status selbst neu aufzubauen. - Alle Plugins im Nightbreak-Ökosystem hängen jetzt von MagmaCore 2.2.0-SNAPSHOT ab, das die gemeinsame Lua-Skript-Engine enthält, die von FreeMinecraftModels-Prop-Skripten und EliteMobs-Lua-Kräften verwendet wird, plus die gemeinsamen
LocationQueryRegistryundWorldFolderResolver. - FreeMinecraftModels deklariert WorldGuard, WorldEdit, GriefPrevention und Vault als
softdepend. Keines davon ist erforderlich, um das Plugin zu starten, aber sie schalten bestimmte Funktionen frei: WorldGuard/WorldEdit/GriefPrevention versorgenLocationAPI, und Vault ermöglicht den Möbelshop.