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 cargadosLocationAPI— registrar detectores de mazmorras y proveedores de protección (alimenta los predicados Luaem.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
ModeledEntityStaticEntityDynamicEntityPropEntity
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 huesotag_(consulta la advertencia más abajo)ModeledEntity#setDisplayNameVisible(boolean)-- mismo requisitoModeledEntity#setLeftClickCallback(...)ModeledEntity#setRightClickCallback(...)ModeledEntity#setHitboxContactCallback(...)ModeledEntity#setModeledEntityHitByProjectileCallback(...)ModeledEntity#playAnimation(String, boolean blend, boolean loop)-- devuelvefalsecuando el nombre no coincide ni con un estado integrado ni con una animación del modelo.blendpone en cola en lugar de fundir;loopsolo se aplica a las animaciones personalizadas. Consulta AnimacionesModeledEntity#stopCurrentAnimations()ModeledEntity#hasAnimation(String)ModeledEntity#damage(double)/damage(Entity damager, double)/damage(Entity damager)/damage(Projectile)-- enrutados a través delDamageableComponentde la entidadModeledEntity#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, onullModeledEntity#showUnderlyingEntity(Player)/hideUnderlyingEntity(Player)-- visibilidad por jugador de la entidad vanilla de respaldoModeledEntity#getEntityID()-- devuelve el string del ID del modeloModeledEntity#getModelInstanceId()--UUIDpor instancia, estable durante toda la vida del modeloModeledEntity#isRemoved()/isDying()-- marcas de ciclo de vidaModeledEntity#getLocation()-- devuelve laLocationactualModeledEntity#getSpawnLocation()-- devuelve laLocationen la que se creó el modeloModeledEntity#getWorld()-- devuelve elWorldModeledEntity#getSkeleton()/getSkeletonBlueprint()/getMountPointManager()-- acceso a la estructura del runtimeModeledEntity#getInteractionComponent()/getHitboxComponent()/getDamageableComponent()/getAnimationComponent()-- los objetos de componente que hay detrás de los métodos de conveniencia anterioresModeledEntity.getLoadedModeledEntities()-- conjunto estático y vivo de todas las entidades modeladas cargadasModeledEntity#getViewers()-- devuelve elHashSet<UUID>de jugadores que pueden ver la entidadModeledEntity#getNametagBones()-- devuelveList<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 escalaModeledEntity#remove()-- elimina inmediatamente la entidad y todos los huesosModeledEntity#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. Pasanullpara borrarlo.ModeledEntity#setViewDistanceOverride(int)/getEffectiveViewDistance()-- sobrescribeDefaultConfig.maxModelViewDistancepara una única entidad. Pasa-1para 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 hitboxDynamicEntity.isDynamicEntity(Entity)/DynamicEntity.getDynamicEntity(Entity)-- búsquedas estáticas a partir de una entidad de BukkitDynamicEntity#getBodyLocation()-- ubicación orientada al cuerpo, distinta degetLocation()Bone#getBoneLocation()
Particularidades de PropEntity
PropEntity.isPropEntity(ArmorStand)/PropEntity.getPropEntityID(ArmorStand)-- identifican un prop a partir de su armor stand de respaldoPropEntity.hasLoadedPropOnSameBlock(String entityID, Location)-- la comprobación de duplicados quespawnPropEntityejecuta internamente; llámala primero si prefieres ramificar en lugar de comprobar si el resultado esnullPropEntity.respawnPropEntityFromArmorStand(String entityID, ArmorStand)-- reconstruye un prop alrededor de un armor stand que sobrevivió a una recarga de chunkPropEntity.getPropEntities()-- mapa vivo de los props cargados indexado por el UUID del armor standPropEntity#setPersistent(boolean)-- alterna la persistencia en el armor stand de respaldoPropEntity#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 Luaset_persistent_data/get_persistent_data, bajo el espacio de nombresfmm_lua_<key>PropEntity#remove()/remove(boolean showRealBlocks)-- elimina el modelo pero deja la entrada persistentePropEntity#permanentlyRemove()-- elimina el modelo y su entrada persistentePropEntity#setVoxelizeConfig(boolean voxelize, boolean solidify)/applySolidify()-- los equivalentes en runtime de los campos YMLvoxelize:/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
tag_ bone in the modelsetDisplayName 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:
ModeledEntityLeftClickEventModeledEntityRightClickEventModeledEntityHitboxContactEventModeledEntityHitByProjectileEvent
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 + 1objetivos 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 como01_em_flame_swordenFlame 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)-- devuelvetruesi existe un.jsonde modelo de visualización para este modelogetRegisteredModels()-- devuelve unSet<String>inmutable de todos los IDs de modelo que tienen modelos de visualización registradosshutdown()-- 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 personalizadosupdateEquippedScripts(Player player)-- compara los objetos equipados con los scripts en ejecución, disparando los hooks de equip/unequipremovePlayer(Player player)-- cierra todos los scripts para un jugador (llamar al salir)getItemDefinitions()-- devuelve el mapa de ID de objeto aPropScriptConfigFields
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)-- devuelvetruesi el ID del objeto está registrado en las definiciones de objeto de FMMapplyScriptedItemData(ItemStack itemStack, String itemId)-- marca la etiqueta PDC y el modelo de objeto en un ItemStack existente. Devuelvetrueen caso de éxito,falsesi el ID del objeto es inválido o el ItemStack no tiene meta. Nota sobre arco/ballesta: si elitemIddado no tiene modelo de visualización peroitemId + "_idle"sí (es decir, el objeto tiene modelos de estado de arco/ballesta), el método usa automáticamente el modelo_idlecomo modelo de visualizacióngetItemConfig(String itemId)-- devuelve losPropScriptConfigFieldspara el ID de objeto dado, onullsi no se encuentra
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)-- devuelvefalsesi el ID del modelo no está cargadoundisguise(Player)-- devuelvetruesi se eliminó un disfrazisDisguised(Player)-- comprobación booleana rápidagetDisguiseModelID(Player)-- devuelve el ID del modelo activo onullgetDisguisedPlayers()-- 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 devuelvatruemarca la ubicación como "en mazmorra"registerProtectionProvider(String providerName, Predicate<Location> predicate)— cualquier predicado registrado que devuelvatruemarca 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:
| Proveedor | Comportamiento de canBuild |
|---|---|
| Adaptador integrado de WorldGuard | Respeta 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 GriefPrevention | Si 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.registerProtectionProvider | Recae 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
LocationOwnershipsiguen 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
| Hook | Activador |
|---|---|
on_spawn | El prop aparece en el mundo |
on_game_tick | Cada tick mientras el prop está activo |
on_zone_enter | Un jugador entra en la zona del prop |
on_zone_leave | Un jugador sale de la zona del prop |
on_destroy | El prop es eliminado |
on_left_click | El jugador hace clic izquierdo en el prop |
on_right_click | El jugador hace clic derecho en el prop |
on_projectile_hit | Un proyectil impacta en el prop |
Hooks de Script de Objeto
Los objetos personalizados (modelos con material: establecido) soportan 22 hooks Lua:
| Hook | Activador |
|---|---|
on_equip | El objeto entra en una ranura de equipamiento rastreada |
on_unequip | El objeto sale de una ranura de equipamiento rastreada |
on_game_tick | Cada tick mientras el objeto está equipado |
on_attack_entity | El jugador ataca una entidad mientras sostiene el objeto |
on_kill_entity | El jugador mata una entidad mientras sostiene el objeto |
on_take_damage | El jugador recibe daño mientras el objeto está equipado |
on_shield_block | El jugador bloquea con un escudo |
on_shoot_bow | El jugador dispara un arco |
on_projectile_hit | Un proyectil disparado por el jugador impacta algo |
on_projectile_launch | El jugador lanza un proyectil |
on_right_click | El jugador hace clic derecho con el objeto |
on_left_click | El jugador hace clic izquierdo con el objeto |
on_shift_right_click | El jugador hace shift-clic derecho con el objeto |
on_shift_left_click | El jugador hace shift-clic izquierdo con el objeto |
on_interact_entity | El jugador hace clic derecho en una entidad con el objeto |
on_swap_hands | El jugador cambia el objeto entre manos |
on_drop | El jugador suelta el objeto |
on_break_block | El jugador rompe un bloque mientras sostiene el objeto |
on_consume | El jugador consume el objeto |
on_item_damage | El objeto recibe daño de durabilidad |
on_fish | El jugador usa una caña de pescar |
on_death | El 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 blueprintcurrent_location-- la ubicación actual del propplay_animation(name, blend, loop)-- reproduce la animación nombrada (blend y loop por defecto atrue)stop_animation()-- detiene todas las animaciones actualeshurt_visual()-- reproduce el visual de daño (destello rojo) en el proppickup()-- encola la eliminación del prop y suelta su objeto de colocaciónmount(player)-- encola un intento de montaje; un retornotruesignifica que el jugador y el gestor de montaje eran válidos, no que finalmente se asignara un asientodismount(player)-- encola una comprobación de desmontaje; un retornotruesignifica que el jugador y el gestor de montaje eran válidosget_passengers()-- devuelve una lista de jugadores que están montando el prop actualmentespawn_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_enteryon_zone_leave, además de los hooks de ítem provocados por un jugador cancel(),uncancel(),is_cancelledcuando el hook subyacente es cancelableplayer-- el jugador que desencadenó el evento
context.world:
spawn_entity(entity_type, x, y, z)-- genera una entidad vanilla, o devuelvenilsi el tipo de entidad no es válidoset_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 scriptglobal_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;typees el nombre del material de Bukkit en mayúsculasconsume_held_item()-- elimina uno del objeto sostenidohas_item(material)-- comprueba si el jugador tiene un objetosend_message(text)-- envía un mensaje de chat al jugadorgame_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
LocationQueryRegistryyWorldFolderResolvercompartidos. - 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 alimentanLocationAPIy 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.