Saltar al contenido principal

API y Guía para Desarrolladores de FreeMinecraftModels

FreeMinecraftModels es tanto un plugin independiente como una superficie de API para otros plugins.

Repositorio 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>

Dependencia

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

Úsalo como compileOnly/provided. No incluyas (shade) el plugin dentro de tu propio jar.

Puntos de Entrada Principales

  • ModeledEntityManager.modelExists(String)
  • ModeledEntityManager.reload()
  • ModeledEntityManager.getAllEntities()
  • ModeledEntityManager.getDynamicEntities()
  • ModeledEntityManager.propEntities()
  • DisguiseAPI — disfrazar / quitar el disfraz a jugadores como modelos cargados
  • LocationAPI — registrar detectores de mazmorras y proveedores de protección (alimenta los predicados Lua em.location.*)
  • ScriptedItemAPI — marcar ItemStacks de terceros con metadatos de objeto-script de FMM

ModeledEntityManager.getAllEntities(), getDynamicEntities() y propEntities() devuelven copias puntuales. Mutar el conjunto o el mapa devuelto no modifica los registros vivos de FMM. Usa modelExists(String) para comprobar la existencia en lugar de retener y copiar repetidamente un registro completo.

Tipos Principales del Runtime

  • ModeledEntity
  • StaticEntity
  • DynamicEntity
  • PropEntity

Creando Entidades

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);

Todas las rutas de creación devuelven null si el ID de modelo solicitado no está cargado.

PropEntity.spawnPropEntity además devuelve null cuando ya hay cargado un prop de ese mismo modelo en el bloque de destino — el duplicado se rechaza en lugar de apilarse, y se registra una advertencia [FMM Props] Prevented duplicate prop spawn .... Comprueba siempre si su valor de retorno es nulo; un retorno no nulo es la única confirmación de que el prop se creó realmente.

createWithInvisibility es una variante que aplica una poción de invisibilidad en lugar de ocultar la entidad del cliente. Esto mantiene a la entidad rastreada del lado del cliente, lo cual es necesario para el control de vehículos (usado internamente por /fmm mount).

Métodos Útiles del Runtime

  • ModeledEntity#setDisplayName(String) -- no hace nada en silencio salvo que el modelo contenga un hueso tag_ (consulta la advertencia más abajo)
  • ModeledEntity#setDisplayNameVisible(boolean) -- mismo requisito
  • ModeledEntity#setLeftClickCallback(...)
  • ModeledEntity#setRightClickCallback(...)
  • ModeledEntity#setHitboxContactCallback(...)
  • ModeledEntity#setModeledEntityHitByProjectileCallback(...)
  • ModeledEntity#playAnimation(String, boolean blend, boolean loop) -- devuelve false cuando el nombre no coincide ni con un estado integrado ni con una animación del modelo. blend pone en cola en lugar de fundir; loop solo se aplica a las animaciones personalizadas. Consulta Animaciones
  • ModeledEntity#stopCurrentAnimations()
  • ModeledEntity#hasAnimation(String)
  • ModeledEntity#damage(double) / damage(Entity damager, double) / damage(Entity damager) / damage(Projectile) -- enrutados a través del DamageableComponent de la entidad
  • ModeledEntity#attack(LivingEntity) / attack(LivingEntity, double damage)
  • ModeledEntity#teleport(Location, boolean teleportUnderlyingEntity)
  • ModeledEntity#setUnderlyingEntity(Entity) / ModeledEntity.getModeledEntity(Entity) -- búsqueda inversa estática desde una entidad de Bukkit al modelo adjunto a ella, o null
  • ModeledEntity#showUnderlyingEntity(Player) / hideUnderlyingEntity(Player) -- visibilidad por jugador de la entidad vanilla de respaldo
  • ModeledEntity#getEntityID() -- devuelve el string del ID del modelo
  • ModeledEntity#getModelInstanceId() -- UUID por instancia, estable durante toda la vida del modelo
  • ModeledEntity#isRemoved() / isDying() -- marcas de ciclo de vida
  • ModeledEntity#getLocation() -- devuelve la Location actual
  • ModeledEntity#getSpawnLocation() -- devuelve la Location en la que se creó el modelo
  • ModeledEntity#getWorld() -- devuelve el World
  • ModeledEntity#getSkeleton() / getSkeletonBlueprint() / getMountPointManager() -- acceso a la estructura del runtime
  • ModeledEntity#getInteractionComponent() / getHitboxComponent() / getDamageableComponent() / getAnimationComponent() -- los objetos de componente que hay detrás de los métodos de conveniencia anteriores
  • ModeledEntity.getLoadedModeledEntities() -- conjunto estático y vivo de todas las entidades modeladas cargadas
  • ModeledEntity#getViewers() -- devuelve el HashSet<UUID> de jugadores que pueden ver la entidad
  • ModeledEntity#getNametagBones() -- devuelve List<Bone> de huesos de etiqueta de nombre (útil para colocar texto adicional)
  • ModeledEntity#getScaleModifier() / setScaleModifier(double)
  • ModeledEntity#removeWithDeathAnimation() -- elimina con la animación de muerte (si existe alguna)
  • ModeledEntity#removeWithMinimizedAnimation() -- elimina con una animación de reducción de escala
  • ModeledEntity#remove() -- elimina inmediatamente la entidad y todos los huesos
  • ModeledEntity#setTintColor(Color) / getTintColor() -- aplica un tinte persistente a través del canal de tinte de armadura de cuero. Los destellos de daño sobrescriben brevemente el tinte y luego se desvanecen de vuelta. Pasa null para borrarlo.
  • ModeledEntity#setViewDistanceOverride(int) / getEffectiveViewDistance() -- sobrescribe DefaultConfig.maxModelViewDistance para una única entidad. Pasa -1 para volver al predeterminado global del plugin.
  • DynamicEntity#setSyncMovement(boolean)
  • DynamicEntity#isDamagesOnContact() / setDamagesOnContact(boolean) -- controla si la entidad daña a los jugadores por contacto con la hitbox
  • DynamicEntity.isDynamicEntity(Entity) / DynamicEntity.getDynamicEntity(Entity) -- búsquedas estáticas a partir de una entidad de Bukkit
  • DynamicEntity#getBodyLocation() -- ubicación orientada al cuerpo, distinta de getLocation()
  • Bone#getBoneLocation()

Particularidades de PropEntity

  • PropEntity.isPropEntity(ArmorStand) / PropEntity.getPropEntityID(ArmorStand) -- identifican un prop a partir de su armor stand de respaldo
  • PropEntity.hasLoadedPropOnSameBlock(String entityID, Location) -- la comprobación de duplicados que spawnPropEntity ejecuta internamente; llámala primero si prefieres ramificar en lugar de comprobar si el resultado es null
  • PropEntity.respawnPropEntityFromArmorStand(String entityID, ArmorStand) -- reconstruye un prop alrededor de un armor stand que sobrevivió a una recarga de chunk
  • PropEntity.getPropEntities() -- mapa vivo de los props cargados indexado por el UUID del armor stand
  • PropEntity#setPersistent(boolean) -- alterna la persistencia en el armor stand de respaldo
  • PropEntity#setCustomDataString(NamespacedKey, String) / getCustomDataString(NamespacedKey) -- lee y escribe tus propios valores PDC en el prop. Es el mismo almacén que usan los ayudantes de Lua set_persistent_data / get_persistent_data, bajo el espacio de nombres fmm_lua_<key>
  • PropEntity#remove() / remove(boolean showRealBlocks) -- elimina el modelo pero deja la entrada persistente
  • PropEntity#permanentlyRemove() -- elimina el modelo y su entrada persistente
  • PropEntity#setVoxelizeConfig(boolean voxelize, boolean solidify) / applySolidify() -- los equivalentes en runtime de los campos YML voxelize: / solidify:
  • PropEntity#showFakePropBlocksToPlayer(Player) / showRealBlocksToPlayer(Player) y las variantes ...ToAllPlayers() -- controlan los bloques barrera de solo paquetes que dan colisión del lado del cliente a un prop solidificado
Nametags need a tag_ bone in the model

setDisplayName y setDisplayNameVisible iteran sobre los huesos de nametag del modelo, que solo existen para huesos cuyo nombre empieza por tag_. En un modelo sin ese hueso esa lista está vacía, así que ambas llamadas tienen éxito y no hacen nada — sin excepción, sin línea de log.

No hay ningún respaldo. Una DynamicEntity oculta a los clientes su entidad viva subyacente, así que el nametag vanilla del mob tampoco se muestra. El resultado neto es un mob completamente sin nombre aunque tu plugin haya establecido el nombre sin error.

Si tu plugin da nombre a modelos (jefes, NPC, cualquier cosa visible para el usuario), exige un hueso tag_ en los modelos que distribuyas, o comprueba getNametagBones().isEmpty() en el momento de adjuntarlo y avisa al autor del contenido. Consulta Notas de creación de modelos.

Superficie de Eventos

Eventos de interacción genéricos:

  • ModeledEntityLeftClickEvent
  • ModeledEntityRightClickEvent
  • ModeledEntityHitboxContactEvent
  • ModeledEntityHitByProjectileEvent

Los cuatro son eventos de Bukkit cancelables; las rutas de detección integradas de FMM los despachan en el hilo principal del servidor. Cancelar uno impide el callback o el comportamiento predeterminado correspondiente de FMM. Los escaneos de contacto con la hitbox se ejecutan cada dos ticks del servidor; los eventos de clic siguen teniendo una ventana corta de deduplicación por jugador para que las rutas de interacción por paquetes y de raytrace OBB no puedan disparar dos veces la misma acción.

Eventos de ciclo de vida:

  • FmmReloadedEvent -- se dispara después de que FMM termine su secuencia de inicialización al iniciar y después de cada /fmm reload. Siempre se dispara en el hilo principal del servidor.

La superficie pública de interacción es intencionalmente neutral respecto al tipo de modelo. Las antiguas variantes StaticEntity*Event, DynamicEntity*Event y PropEntity*Event, junto con ResourcePackGenerationEvent, ya no forman parte de la API actual. Usa en su lugar los cuatro eventos genéricos de entidad modelada anteriores y FmmReloadedEvent.

Los plugins consumidores que mantienen referencias DynamicEntity o PropEntity de larga vida (EliteMobs, BetterStructures, etc.) deben manejar este evento recreando sus adjuntos de modelo en las entidades subyacentes sobrevivientes. Sin esto, esas entidades se vuelven invisibles tras una recarga porque FMM derribó las display entities durante onDisable mientras la referencia del consumidor ahora está obsoleta.

@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 y Detección de Proyectiles OBB

FMM usa detección de impacto OBB (oriented bounding box) para proyectiles contra entidades modeladas. Cuando un proyectil interseca la hitbox OBB de una entidad modelada, FMM dispara un ModeledEntityHitByProjectileEvent. Este es un evento Bukkit estándar cancelable.

Detalles clave:

  • La detección barre todo el segmento de movimiento del proyectil desde el tick anterior, así que las flechas rápidas no pueden atravesar un modelo fino entre muestras
  • Los candidatos de ese segmento se resuelven del más cercano al más lejano, con independencia del orden de iteración del registro de modelos
  • Un proyectil sin perforación se enruta a un único objetivo modelado. Una flecha con Perforación puede alcanzar pierce level + 1 objetivos modelados distintos, en orden de recorrido, sin disparar dos veces cuando tanto la OBB como las hitboxes vanilla de respaldo lo observan
  • El manejador predeterminado de entidades dinámicas reenvía el impacto a la entidad viva subyacente como un evento real de daño por proyectil, usando la velocidad del proyectil en el momento del impacto. Esto conserva la causa del proyectil, el manejo por parte de plugins de combate y la atribución al tirador
  • Cancelar el evento impide el callback o el daño predeterminado de FMM, pero la colisión sigue gastando el presupuesto de objetivos modelados de ese proyectil. Por tanto, un impacto sin perforación cancelado consume igualmente el proyectil
@EventHandler
public void onProjectileHitModel(ModeledEntityHitByProjectileEvent event) {
ModeledEntity target = event.getModeledEntity();
Projectile projectile = event.getProjectile();
// Cancel to prevent damage
event.setCancelled(true);
}

Utilidades de Objeto y Modelo

ModelItemFactory

Clase factoría para crear ItemStacks relacionados con modelos de forma programática.

// 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) -- crea un objeto de colocación para props. En 1.21.4+, aplica automáticamente el renderizado del modelo de visualización si existe un JSON de display.
  • createCustomItem(String itemId, PropScriptConfigFields config) -- crea un objeto personalizado con nombre, lore, encantamientos y modelo de visualización desde la configuración unificada.
  • formatModelName(String modelId) -- utilidad que convierte un ID de modelo como 01_em_flame_sword en Flame Sword.

DisplayModelRegistry

Registro simple que rastrea qué modelos tienen un JSON de display disponible.

// Check if a model has a display model JSON registered
boolean has3D = DisplayModelRegistry.hasDisplayModel("magic_sword");
  • register(String modelId) -- registra un ID de modelo (llamado internamente durante la recarga)
  • hasDisplayModel(String modelId) -- devuelve true si existe un .json de modelo de visualización para este modelo
  • getRegisteredModels() -- devuelve un Set<String> inmutable de todos los IDs de modelo que tienen modelos de visualización registrados
  • shutdown() -- borra todos los registros

ItemScriptManager

Gestiona el ciclo de vida de los scripts Lua por jugador para objetos personalizados (modelos con material: establecido en su configuración YML).

// 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) -- escanea las configuraciones YML del modelo en busca de objetos personalizados
  • updateEquippedScripts(Player player) -- compara los objetos equipados con los scripts en ejecución, disparando los hooks de equip/unequip
  • removePlayer(Player player) -- cierra todos los scripts para un jugador (llamar al salir)
  • getItemDefinitions() -- devuelve el mapa de ID de objeto a PropScriptConfigFields

ScriptedItemAPI

API pública para que los plugins externos se integren con el sistema de objetos con script de FMM. Esto permite que otros plugins marquen sus propios ItemStacks con datos de objeto-script de FMM (etiqueta PDC + modelo de objeto) para que los hooks de script Lua de FMM se disparen para esos objetos, sin que FMM sobrescriba el nombre, lore o encantamientos del objeto.

// 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) -- devuelve true si el ID del objeto está registrado en las definiciones de objeto de FMM
  • applyScriptedItemData(ItemStack itemStack, String itemId) -- marca la etiqueta PDC y el modelo de objeto en un ItemStack existente. Devuelve true en caso de éxito, false si el ID del objeto es inválido o el ItemStack no tiene meta. Nota sobre arco/ballesta: si el itemId dado no tiene modelo de visualización pero itemId + "_idle" sí (es decir, el objeto tiene modelos de estado de arco/ballesta), el método usa automáticamente el modelo _idle como modelo de visualización
  • getItemConfig(String itemId) -- devuelve los PropScriptConfigFields para el ID de objeto dado, o null si no se encuentra
Integración con EliteMobs

EliteMobs usa esta API internamente a través del campo de configuración scriptedItem. Cuando un objeto personalizado de EliteMobs establece scriptedItem: flame_blade, EliteMobs construye su objeto normalmente (nombre, lore, encantamientos, nivel) y luego llama a ScriptedItemAPI.applyScriptedItemData() para añadir encima el modelo y el comportamiento de script de FMM.

DisguiseAPI

Punto de entrada público para la función de disfraz de jugador. Los plugins de terceros deberían llamar a esta clase en lugar del DisguiseManager interno para que las refactorizaciones internas permanezcan seguras.

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) -- devuelve false si el ID del modelo no está cargado
  • undisguise(Player) -- devuelve true si se eliminó un disfraz
  • isDisguised(Player) -- comprobación booleana rápida
  • getDisguiseModelID(Player) -- devuelve el ID del modelo activo o null
  • getDisguisedPlayers() -- snapshot inmodificable de los jugadores disfrazados

Los jugadores disfrazados se vuelven invisibles para los demás y permanecen así hasta que se les quita el disfraz — los cubos de leche, los clears de efectos por baliza y otras interacciones similares no rompen la invisibilidad.

LocationAPI

API pública para que los plugins contribuyan con detección de mazmorras y comprobaciones de protección de regiones. Los predicados registrados alimentan las comprobaciones Lua de FMM em.location.is_in_dungeon y em.location.is_protected (usadas por scripts premade como pickupable.lua y storage_double.lua).

Los plugins pasan un Predicate<Location> plano para que ningún tipo FMM con shade cruce los classloaders de plugins.

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) — cualquier predicado registrado que devuelva true marca la ubicación como "en mazmorra"
  • registerProtectionProvider(String providerName, Predicate<Location> predicate) — cualquier predicado registrado que devuelva true marca la ubicación como protegida

Los operadores pueden verificar el registro con /fmm location, que informa el conteo de proveedores activos y prueba ambos predicados contra su ubicación actual.

Proveedores de protección y colocación de props

Los mismos proveedores de protección también impulsan la comprobación de colocación de props preventPropPlacementInProtectedRegions, pero esa comprobación llama a canBuild(player, location) en lugar del isProtected(location) basado solo en ubicación. La distinción importa:

ProveedorComportamiento de canBuild
Adaptador integrado de WorldGuardRespeta el propio bypass de WorldGuard y después delega en el testBuild de WorldGuard — así que los miembros y propietarios de la región pueden construir con normalidad
Adaptador integrado de GriefPreventionSi no hay reclamación en la ubicación se permite; dentro de una reclamación delega en el permiso de construcción de GriefPrevention para ese jugador
Proveedor registrado vía LocationAPI.registerProtectionProviderRecae en !isProtected(location)solo por ubicación, bloquea a todo el mundo en una ubicación que tu predicado considere protegida

Ese respaldo es intencionado y conservador: un Predicate<Location> no puede expresar permisos específicos por jugador, así que FMM no se los inventa. Si quieres un comportamiento genuinamente consciente del jugador para tu propio sistema de regiones, implementa directamente el RegionProtectionProvider de MagmaCore y sobrescribe canBuild, y después regístralo con LocationQueryRegistry.registerProtectionProvider en lugar de pasar por el envoltorio de conveniencia del predicado.

Dos comportamientos más que conviene conocer:

  • Los fallos del adaptador fallan de forma cerrada. Si un proveedor lanza una excepción durante una consulta de construcción, la colocación se rechaza y se registra una advertencia que indica el proveedor. Nunca cae en silencio a "permitido".
  • Todos los proveedores registrados deben estar de acuerdo. Gana el primer proveedor que diga que no; los proveedores de propiedad registrados a través de LocationOwnership siguen siendo solo por ubicación y bloquean a todo el mundo.

PropScriptConfigFields

Clase de configuración unificada para los archivos de configuración YML del modelo. Usada tanto por scripts de prop como por objetos personalizados.

# 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"

Métodos clave: isCustomItem(), getParsedMaterial(), getParsedEnchantments(), getScripts().

Scripting Lua

FreeMinecraftModels soporta scripts Lua tanto para props como para objetos personalizados a través del motor de scripting MagmaCore 2.0. Los archivos de script se colocan en plugins/FreeMinecraftModels/scripts/ y se vinculan a modelos a través de un YML compañero junto al archivo del modelo. El archivo de script en disco debe terminar en .lua; las entradas de configuración pueden incluir la extensión u omitirla.

Los props vinculan todos los scripts listados en scripts: como instancias independientes. Los objetos personalizados vinculan actualmente solo el primer script válido de la lista para cada par jugador/objeto.

Hooks de Script de Prop

HookActivador
on_spawnEl prop aparece en el mundo
on_game_tickCada tick mientras el prop está activo
on_zone_enterUn jugador entra en la zona del prop
on_zone_leaveUn jugador sale de la zona del prop
on_destroyEl prop es eliminado
on_left_clickEl jugador hace clic izquierdo en el prop
on_right_clickEl jugador hace clic derecho en el prop
on_projectile_hitUn proyectil impacta en el prop

Hooks de Script de Objeto

Los objetos personalizados (modelos con material: establecido) soportan 22 hooks Lua:

HookActivador
on_equipEl objeto entra en una ranura de equipamiento rastreada
on_unequipEl objeto sale de una ranura de equipamiento rastreada
on_game_tickCada tick mientras el objeto está equipado
on_attack_entityEl jugador ataca una entidad mientras sostiene el objeto
on_kill_entityEl jugador mata una entidad mientras sostiene el objeto
on_take_damageEl jugador recibe daño mientras el objeto está equipado
on_shield_blockEl jugador bloquea con un escudo
on_shoot_bowEl jugador dispara un arco
on_projectile_hitUn proyectil disparado por el jugador impacta algo
on_projectile_launchEl jugador lanza un proyectil
on_right_clickEl jugador hace clic derecho con el objeto
on_left_clickEl jugador hace clic izquierdo con el objeto
on_shift_right_clickEl jugador hace shift-clic derecho con el objeto
on_shift_left_clickEl jugador hace shift-clic izquierdo con el objeto
on_interact_entityEl jugador hace clic derecho en una entidad con el objeto
on_swap_handsEl jugador cambia el objeto entre manos
on_dropEl jugador suelta el objeto
on_break_blockEl jugador rompe un bloque mientras sostiene el objeto
on_consumeEl jugador consume el objeto
on_item_damageEl objeto recibe daño de durabilidad
on_fishEl jugador usa una caña de pescar
on_deathEl jugador muere mientras el objeto está equipado

Los scripts de objeto reciben context.item (con el ID del objeto e información del jugador) en lugar de context.prop.

Tabla Context del Script de Prop

Los scripts de prop reciben una tabla context. Aquí hay un resumen de las APIs clave -- consulta Lua Prop API para más detalles.

context.prop:

  • model_id -- el nombre del modelo blueprint
  • current_location -- la ubicación actual del prop
  • play_animation(name, blend, loop) -- reproduce la animación nombrada (blend y loop por defecto a true)
  • stop_animation() -- detiene todas las animaciones actuales
  • hurt_visual() -- reproduce el visual de daño (destello rojo) en el prop
  • pickup() -- encola la eliminación del prop y suelta su objeto de colocación
  • mount(player) -- encola un intento de montaje; un retorno true significa que el jugador y el gestor de montaje eran válidos, no que finalmente se asignara un asiento
  • dismount(player) -- encola una comprobación de desmontaje; un retorno true significa que el jugador y el gestor de montaje eran válidos
  • get_passengers() -- devuelve una lista de jugadores que están montando el prop actualmente
  • spawn_elitemobs_boss(filename, x, y, z) -- genera un jefe de EliteMobs en coordenadas absolutas del mundo actual del prop

context.event:

  • Disponible en los hooks de prop on_left_click, on_right_click, on_zone_enter y on_zone_leave, además de los hooks de ítem provocados por un jugador
  • cancel(), uncancel(), is_cancelled cuando el hook subyacente es cancelable
  • player -- el jugador que desencadenó el evento

context.world:

  • spawn_entity(entity_type, x, y, z) -- genera una entidad vanilla, o devuelve nil si el tipo de entidad no es válido
  • set_block_at(x, y, z, material) -- encola un cambio de bloque si el material es válido; los chunks descargados se omiten
  • Además partículas, sonidos, consultas de bloque, rayos y búsquedas de entidades cercanas

context.cooldowns:

  • check_local(key, ticks) -- comprueba e inicia un cooldown por script
  • global_ready() / set_global(ticks) -- cooldown compartido para el prop o el jugador propietario

Objetos de jugador (desde context.player o context.event.player):

  • get_held_item() -- devuelve el objeto que el jugador sostiene; type es el nombre del material de Bukkit en mayúsculas
  • consume_held_item() -- elimina uno del objeto sostenido
  • has_item(material) -- comprueba si el jugador tiene un objeto
  • send_message(text) -- envía un mensaje de chat al jugador
  • game_mode -- el modo de juego actual del jugador

Ejemplo de Script de Prop

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
}

Ejemplo de Script de Objeto

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
}

Notas

  • FreeMinecraftModels es una dependencia de plugin instalado, no una librería integrable.
  • Si tu plugin necesita modelos recién importados, llama a ModeledEntityManager.reload() en lugar de intentar reconstruir el estado de FreeMinecraftModels tú mismo.
  • Todos los plugins del ecosistema Nightbreak ahora dependen de MagmaCore 2.2.0-SNAPSHOT, que incluye el motor de scripting Lua compartido usado por los scripts de prop de FreeMinecraftModels y los poderes Lua de EliteMobs, además del LocationQueryRegistry y WorldFolderResolver compartidos.
  • FreeMinecraftModels declara WorldGuard, WorldEdit, GriefPrevention, Vault, floodgate y Geyser-Spigot como softdepend. Ninguno es necesario para iniciar el plugin, pero desbloquean funciones específicas: WorldGuard/WorldEdit/GriefPrevention alimentan LocationAPI y la comprobación de colocación de props con reconocimiento de jugador, Vault habilita la tienda de muebles, y floodgate/Geyser-Spigot habilitan el backend de Bedrock por modelo.
  • ModeledEntityManager.reload() realiza un ciclo completo de recarga del plugin (onDisable / onLoad / onEnable). Llámalo en el hilo principal del servidor.