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
ModeledEntityManager.getAllEntities(), getDynamicEntities() und propEntities() liefern Momentaufnahmen zurück. Das Verändern der zurückgegebenen Menge oder Map ändert FMMs aktive Registries nicht. Verwende modelExists(String) für eine Existenzprüfung, statt eine vollständige Registry vorzuhalten und wiederholt zu kopieren.
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.
PropEntity.spawnPropEntity gibt zusätzlich null zurück, wenn auf dem Zielblock bereits ein Prop desselben Modells geladen ist — das Duplikat wird abgelehnt statt gestapelt, und eine Warnung [FMM Props] Prevented duplicate prop spawn ... wird protokolliert. Prüfe den Rückgabewert immer auf null; nur eine nicht-null-Rückgabe bestätigt, dass der Prop tatsächlich erstellt wurde.
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)-- tut still nichts, sofern das Modell keinentag_-Knochen enthält (siehe die Warnung unten)ModeledEntity#setDisplayNameVisible(boolean)-- dieselbe VoraussetzungModeledEntity#setLeftClickCallback(...)ModeledEntity#setRightClickCallback(...)ModeledEntity#setHitboxContactCallback(...)ModeledEntity#setModeledEntityHitByProjectileCallback(...)ModeledEntity#playAnimation(String, boolean blend, boolean loop)-- gibtfalsezurück, wenn der Name weder zu einem eingebauten Zustand noch zu einer Animation im Modell passt.blendreiht ein, statt überzublenden;loopgilt nur für benutzerdefinierte Animationen. Siehe AnimationenModeledEntity#stopCurrentAnimations()ModeledEntity#hasAnimation(String)ModeledEntity#damage(double)/damage(Entity damager, double)/damage(Entity damager)/damage(Projectile)-- läuft über dieDamageableComponentder EntitätModeledEntity#attack(LivingEntity)/attack(LivingEntity, double damage)ModeledEntity#teleport(Location, boolean teleportUnderlyingEntity)ModeledEntity#setUnderlyingEntity(Entity)/ModeledEntity.getModeledEntity(Entity)-- statische Rückwärtssuche von einer Bukkit-Entität zum daran angehängten Modell, odernullModeledEntity#showUnderlyingEntity(Player)/hideUnderlyingEntity(Player)-- Sichtbarkeit der zugrunde liegenden Vanilla-Entität pro SpielerModeledEntity#getEntityID()-- gibt den Modell-ID-String zurückModeledEntity#getModelInstanceId()--UUIDpro Instanz, stabil über die Lebensdauer des ModellsModeledEntity#isRemoved()/isDying()-- Lebenszyklus-FlagsModeledEntity#getLocation()-- gibt die aktuelleLocationzurückModeledEntity#getSpawnLocation()-- gibt dieLocationzurück, an der das Modell erstellt wurdeModeledEntity#getWorld()-- gibt dieWorldzurückModeledEntity#getSkeleton()/getSkeletonBlueprint()/getMountPointManager()-- Zugriff auf die LaufzeitstrukturModeledEntity#getInteractionComponent()/getHitboxComponent()/getDamageableComponent()/getAnimationComponent()-- die Komponentenobjekte hinter den obigen KomfortmethodenModeledEntity.getLoadedModeledEntities()-- statische Live-Menge aller geladenen modellierten EntitätenModeledEntity#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ügtDynamicEntity.isDynamicEntity(Entity)/DynamicEntity.getDynamicEntity(Entity)-- statische Suchen ausgehend von einer Bukkit-EntitätDynamicEntity#getBodyLocation()-- körperorientierte Position, verschieden vongetLocation()Bone#getBoneLocation()
Besonderheiten von PropEntity
PropEntity.isPropEntity(ArmorStand)/PropEntity.getPropEntityID(ArmorStand)-- identifiziert einen Prop anhand seines zugrunde liegenden Armor StandsPropEntity.hasLoadedPropOnSameBlock(String entityID, Location)-- die Duplikatprüfung, diespawnPropEntityintern ausführt; rufe sie vorab auf, wenn du verzweigen willst, statt aufnullzu prüfenPropEntity.respawnPropEntityFromArmorStand(String entityID, ArmorStand)-- baut einen Prop um einen Armor Stand herum neu auf, der ein Chunk-Neuladen überlebt hatPropEntity.getPropEntities()-- Live-Map der geladenen Props, indiziert nach Armor-Stand-UUIDPropEntity#setPersistent(boolean)-- schaltet die Persistenz des zugrunde liegenden Armor Stands umPropEntity#setCustomDataString(NamespacedKey, String)/getCustomDataString(NamespacedKey)-- liest/schreibt eigene PDC-Werte am Prop. Das ist derselbe Speicher, den die Lua-Helferset_persistent_data/get_persistent_dataunter dem Namensraumfmm_lua_<key>verwendenPropEntity#remove()/remove(boolean showRealBlocks)-- entfernt das Modell, lässt aber den persistenten Eintrag bestehenPropEntity#permanentlyRemove()-- entfernt das Modell und seinen persistenten EintragPropEntity#setVoxelizeConfig(boolean voxelize, boolean solidify)/applySolidify()-- die Laufzeitentsprechungen der YML-Feldervoxelize:/solidify:PropEntity#showFakePropBlocksToPlayer(Player)/showRealBlocksToPlayer(Player)und die...ToAllPlayers()-Varianten -- steuern die paket-only Barrier-Blöcke, die einem solidifizierten Prop clientseitige Kollision geben
tag_-Bone im ModellsetDisplayName und setDisplayNameVisible iterieren über die Namensschild-Knochen des Modells, die nur für Knochen existieren, deren Name mit tag_ beginnt. Bei einem Modell ohne einen solchen Knochen ist diese Liste leer, sodass beide Aufrufe erfolgreich sind und nichts tun — keine Exception, keine Logzeile.
Es gibt keinen Fallback. Eine DynamicEntity verbirgt ihre zugrunde liegende lebende Entität vor Clients, sodass auch das Vanilla-Mob-Namensschild nicht erscheint. Unterm Strich ergibt das einen völlig namenlosen Mob, obwohl dein Plugin den Namen fehlerfrei gesetzt hat.
Wenn dein Plugin Modelle benennt (Bosse, NPCs, alles für Spieler Sichtbare), verlange einen tag_-Knochen in den Modellen, die du ausliefst, oder prüfe beim Anhängen getNametagBones().isEmpty() und warne den Inhaltsautor. Siehe Hinweise zur Modellerstellung.
Event-Oberfläche
Generische Interaktions-Events:
ModeledEntityLeftClickEventModeledEntityRightClickEventModeledEntityHitboxContactEventModeledEntityHitByProjectileEvent
Alle vier sind cancelbare Bukkit-Events; FMMs eingebaute Erkennungspfade lösen sie im primären Server-Thread aus. Ein abgebrochenes Event verhindert FMMs zugehörigen Callback bzw. das Standardverhalten. Hitbox-Kontakt-Scans laufen alle zwei Server-Ticks; Klick-Events haben weiterhin ein kurzes Deduplizierungsfenster pro Spieler, damit der Paket-Interaktions- und der OBB-Raytrace-Pfad dieselbe Aktion nicht zweimal auslösen können.
Lebenszyklus-Events:
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.
Die öffentliche Interaktions-Oberfläche ist bewusst unabhängig vom Modelltyp. Die älteren Varianten StaticEntity*Event, DynamicEntity*Event und PropEntity*Event sowie ResourcePackGenerationEvent gehören nicht mehr zur aktuellen API. Verwende stattdessen die vier generischen Events für modellierte Entitäten und FmmReloadedEvent.
Verbraucher-Plugins, die langlebige DynamicEntity- oder PropEntity-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ährend onDisable abgebaut 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:
- Die Erkennung überstreicht das gesamte Bewegungssegment des Projektils seit dem letzten Tick, sodass schnelle Pfeile nicht zwischen zwei Abtastungen durch ein dünnes Modell hindurchtunneln können
- Kandidaten auf diesem Segment werden nach Nähe aufgelöst, unabhängig von der Iterationsreihenfolge der Modell-Registry
- Ein nicht durchschlagendes Projektil wird an genau ein modelliertes Ziel geleitet. Ein Pfeil mit Durchschlag kann
Durchschlagsstufe + 1verschiedene modellierte Ziele in Flugreihenfolge treffen, ohne doppelt auszulösen, wenn sowohl die OBB- als auch die zugrunde liegende Vanilla-Hitbox ihn registrieren - Der Standard-Handler für dynamische Entitäten leitet den Einschlag als echtes Projektil-Schadensereignis an die zugrunde liegende lebende Entität weiter und nutzt dabei die Geschwindigkeit des Projektils zum Einschlagszeitpunkt. Das erhält die Projektil-Ursache, die Behandlung durch Kampf-Plugins und die Zuordnung zum Schützen
- Ein Abbruch des Events verhindert FMMs Callback- bzw. Standard-Schadensverhalten, aber die Kollision verbraucht dennoch das Modellziel-Budget dieses Projektils. Ein abgebrochener nicht durchschlagender Treffer verbraucht das Projektil also trotzdem
@EventHandler
public void onProjectileHitModel(ModeledEntityHitByProjectileEvent event) {
ModeledEntity target = event.getModeledEntity();
Projectile projectile = event.getProjectile();
// Cancel to prevent damage
event.setCancelled(true);
}
Item- & Modell-Hilfen
ModelItemFactory
Factory-Klasse zum programmgesteuerten Erstellen modellbezogener ItemStacks.
// Create a prop placement item (uses "model_id" PDC key)
ItemStack placementItem = ModelItemFactory.createModelItem("lamp_post", Material.STICK);
// Create a custom item from config (uses "fmm_item_id" PDC key)
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.
// Check if a model has a display model JSON registered
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).
// Get all registered custom item definitions
Map<String, PropScriptConfigFields> items = ItemScriptManager.getItemDefinitions();
// Get the active Lua script instance for a player + item
ScriptInstance instance = ItemScriptManager.getActiveScript(playerUUID, "magic_sword");
// Get all active scripts for a player
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.
// Check if a scripted item definition exists
boolean exists = ScriptedItemAPI.isValidItemId("flame_blade");
// Apply FMM scripted item data to an existing ItemStack
// This sets:
// - The fmm_item_id PDC tag (so FMM's script system recognizes the item)
// - The item model (1.21.4+) from FMM's display model registry
// Does NOT modify name, lore, enchantments, or any other item properties.
boolean success = ScriptedItemAPI.applyScriptedItemData(itemStack, "flame_blade");
// Get the config for a scripted item
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;
// Disguise a player as a loaded model. Replaces any existing disguise cleanly.
boolean ok = DisguiseAPI.disguise(player, "dragon");
// Undisguise (returns true if a disguise was removed).
DisguiseAPI.undisguise(player);
// Query state.
boolean disguised = DisguiseAPI.isDisguised(player);
String modelID = DisguiseAPI.getDisguiseModelID(player); // null if not disguised
// Snapshot of all currently disguised players.
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;
// On your plugin's enable, after WorldGuard/EliteMobs/etc. are available.
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.
Schutz-Provider und Prop-Platzierung
Dieselben Schutz-Provider treiben auch die Prop-Platzierungsprüfung preventPropPlacementInProtectedRegions an, aber diese Prüfung ruft canBuild(player, location) auf statt des rein ortsbezogenen isProtected(location). Der Unterschied ist wichtig:
| Provider | Verhalten von canBuild |
|---|---|
| Eingebauter WorldGuard-Adapter | Berücksichtigt WorldGuards eigenen Bypass und überlässt dann WorldGuards testBuild die Entscheidung — Regionsmitglieder und -besitzer können also normal bauen |
| Eingebauter GriefPrevention-Adapter | Kein Claim am Ort bedeutet erlaubt; innerhalb eines Claims überlässt er GriefPreventions eigener Baurechtsprüfung für diesen Spieler die Entscheidung |
Über LocationAPI.registerProtectionProvider registrierter Provider | Fällt auf !isProtected(location) zurück — rein ortsbezogen, blockiert alle an einem Ort, den dein Prädikat als geschützt bezeichnet |
Dieser Fallback ist beabsichtigt und konservativ: Ein Predicate<Location> kann keine spielerspezifische Berechtigung ausdrücken, also erfindet FMM keine. Wenn du echtes spielerbewusstes Verhalten für dein eigenes Regionssystem willst, implementiere MagmaCores RegionProtectionProvider direkt, überschreibe canBuild und registriere ihn dann mit LocationQueryRegistry.registerProtectionProvider, statt den Prädikat-Komfort-Wrapper zu verwenden.
Zwei weitere wissenswerte Verhaltensweisen:
- Adapter-Fehler scheitern kontrolliert. Wirft ein Provider während einer Bau-Abfrage einen Fehler, wird die Platzierung abgelehnt und eine Warnung mit dem Namen des Providers protokolliert. Es fällt nie stillschweigend auf „erlaubt" durch.
- Jeder registrierte Provider muss zustimmen. Der erste Provider, der nein sagt, gewinnt; über
LocationOwnershipregistrierte Besitz-Provider sind weiterhin rein ortsbezogen und blockieren alle.
PropScriptConfigFields
Vereinheitlichte Konfigurationsklasse für Modell-YML-Konfigurationsdateien. Wird sowohl von Prop-Skripten als auch von benutzerdefinierten Items verwendet.
# Example: torch_01.yml
isEnabled: true
scripts:
- torch_glow.lua
material: STICK # If set, model becomes a custom item
name: "&eMagic Torch" # Custom display name (optional)
lore: # Custom lore lines (optional)
- "&7Glows in the dark"
enchantments: # Enchantments (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. Die Skriptdatei auf der Festplatte muss auf .lua enden; Konfigurationseinträge dürfen die Endung enthalten oder weglassen.
Props binden jedes unter scripts: aufgeführte Skript als eigenständige Instanz. Benutzerdefinierte Items binden derzeit nur das erste gültige Skript der Liste je Spieler/Item-Paar.
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 eine per Skript erstellte überwachte Zone |
on_zone_leave | Ein Spieler verlässt eine per Skript erstellte überwachte Zone |
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 | Reserviert: wird von der Validierung akzeptiert, aber in der aktuellen Laufzeit nicht an Prop-Skripte weitergeleitet |
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()-- stellt das Entfernen des Props und das Fallenlassen seines Platzierungs-Items in die Warteschlangemount(player)-- stellt einen Aufsteigeversuch in die Warteschlange; ein Rückgabewert vontruebedeutet, dass Spieler und Mount-Manager gültig waren, nicht dass letztlich ein Sitzplatz zugewiesen wurdedismount(player)-- stellt eine Absteigeprüfung in die Warteschlange; ein Rückgabewert vontruebedeutet, dass Spieler und Mount-Manager gültig warenget_passengers()-- gibt eine Liste der derzeit auf dem Prop sitzenden Spieler zurückspawn_elitemobs_boss(filename, x, y, z)-- spawnt einen EliteMobs-Boss an absoluten Koordinaten in der aktuellen Welt des Props
context.event:
- Verfügbar in den Prop-Hooks
on_left_click,on_right_click,on_zone_enterundon_zone_leavesowie in Item-Hooks, die von einem Spieler ausgelöst werden cancel(),uncancel(),is_cancelled, sofern der zugrunde liegende Hook abbrechbar istplayer-- der Spieler, der das Event ausgelöst hat
context.world:
spawn_entity(entity_type, x, y, z)-- spawnt eine Vanilla-Entität oder gibtnilzurück, wenn der Entitätstyp ungültig istset_block_at(x, y, z, material)-- stellt eine Blockänderung in die Warteschlange, sofern das Material gültig ist; nicht geladene Chunks werden übersprungen- Plus Partikel, Sounds, Block-Abfragen, Blitze und Suche nach nahen Entitäten
context.cooldowns:
check_local(key, ticks)-- prüft und startet einen Cooldown pro Skriptglobal_ready()/set_global(ticks)-- gemeinsamer Cooldown für den Prop oder den Spieler-Besitzer
Spieler-Objekte (aus context.player oder context.event.player):
get_held_item()-- gibt das Item zurück, das der Spieler hält;typeist der Bukkit-Materialname in Großbuchstabenconsume_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
return {
api_version = 1,
on_spawn = function(context)
context.prop:play_animation("idle", true, true)
end,
on_right_click = function(context)
if context.cooldowns:check_local("activate", 40) then
context.prop:play_animation("activate", false, false)
end
end
}
Item-Skript-Beispiel
return {
api_version = 1,
on_equip = function(context)
context.player:send_message("&6You equipped the Flame Blade!")
end,
on_attack_entity = function(context)
-- Fire effect on hit
context.player:send_message("&cBurn!")
end,
on_unequip = function(context)
context.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, Vault, floodgate und Geyser-Spigot als
softdepend. Keines davon ist erforderlich, um das Plugin zu starten, aber sie schalten bestimmte Funktionen frei: WorldGuard/WorldEdit/GriefPrevention versorgenLocationAPIund die spielerbewusste Prop-Platzierungsprüfung, Vault ermöglicht den Möbelshop, und floodgate/Geyser-Spigot ermöglichen das Bedrock-Backend pro Modell. ModeledEntityManager.reload()führt einen vollständigen Plugin-Reload-Zyklus aus (onDisable/onLoad/onEnable). Rufe es im Haupt-Server-Thread auf.