Saltar para o conteúdo principal

API e guia de desenvolvimento do FreeMinecraftModels

A referência Java gerada permite pesquisar classes, métodos e assinaturas, com a versão de origem e o commit registados para cada módulo.

O FreeMinecraftModels funciona como plugin independente e disponibiliza uma API para outros plugins.

Consulte o índice da API Java para a configuração partilhada de dependências e execução. Este guia usa a versão publicada 2.12.9; o código-fonte atual indica 2.12.9. Compile contra a versão instalada no servidor.

Repositório 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>

Dependência​

<dependency>
<groupId>com.magmaguy</groupId>
<artifactId>FreeMinecraftModels</artifactId>
<version>2.12.9</version>
<scope>provided</scope>
</dependency>

Use compileOnly ou provided. Não incorpore o plugin no seu próprio JAR.

Principais pontos de entrada​

  • ModeledEntityManager.modelExists(String)
  • ModeledEntityManager.reload()
  • ModeledEntityManager.getAllEntities()
  • ModeledEntityManager.getDynamicEntities()
  • ModeledEntityManager.propEntities()
  • DisguiseAPI: aplica e remove disfarces de jogadores com modelos carregados.
  • LocationAPI: regista detetores de dungeons e fornecedores de proteção, usados pelos predicados Lua em.location.*.
  • ScriptedItemAPI: aplica a identidade de um item definido no FMM e os dados de apresentação a ItemStacks de outros plugins.
  • ItemConstructionSnapshot: captura, na thread do servidor, os dados de itens e de modelos de apresentação para construir itens noutra thread.

ModeledEntityManager.getAllEntities(), getDynamicEntities() e propEntities() devolvem cópias do estado no momento da chamada. Alterar o conjunto ou mapa devolvido não modifica os registos ativos do FMM. Para verificar a existência de um modelo, use modelExists(String) em vez de guardar ou copiar repetidamente o registo completo.

Principais tipos de execução​

  • ModeledEntity
  • StaticEntity
  • DynamicEntity
  • PropEntity

Criar 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 estas operações devolvem null se o ID pedido não estiver carregado.

PropEntity.spawnPropEntity também devolve null se já existir um elemento carregado do mesmo modelo no bloco de destino. O duplicado é recusado e surge [FMM Props] Prevented duplicate prop spawn ... na consola. Verifique sempre o retorno: só um valor não nulo confirma que o elemento foi criado.

createWithInvisibility aplica uma poção de invisibilidade em vez de ocultar a entidade dos clientes. Mantém a entidade acompanhada no cliente, o que é necessário para controlar veículos (usado internamente por /fmm mount) e permite que os ossos de apresentação do modelo vão montados na entidade nos clientes Java; consulte displayBonesRideTheirEntity.

Métodos úteis durante a execução​

  • ModeledEntity#setDisplayName(String): define a etiqueta de nome gerida pelo modelo.
  • ModeledEntity#setDisplayNameVisible(boolean): controla a visibilidade dessa etiqueta.
  • ModeledEntity#setLeftClickCallback(...)
  • ModeledEntity#setRightClickCallback(...)
  • ModeledEntity#setHitboxContactCallback(...)
  • ModeledEntity#setModeledEntityHitByProjectileCallback(...)
  • ModeledEntity#playAnimation(String, boolean blend, boolean loop): devolve false se o nome não corresponder a um estado integrado nem a uma animação do modelo, ou depois de death ter sido pedida. Numa animação personalizada, blend = true reproduz-a na camada de ação por cima de idle ou walk, e false deixa-a assumir toda a pose. loop aplica-se apenas a animações personalizadas. Consulte Combinação de animações.
  • ModeledEntity#stopCurrentAnimations(): termina as animações personalizadas e os ataques e leva o modelo para idle; não faz nada depois de a morte ter começado.
  • ModeledEntity#hasAnimation(String)
  • ModeledEntity#damage(double) / damage(Entity damager, double) / damage(Entity damager) / damage(Projectile): encaminhados para o DamageableComponent da entidade.
  • ModeledEntity#attack(LivingEntity) / attack(LivingEntity, double damage)
  • ModeledEntity#teleport(Location, boolean teleportUnderlyingEntity)
  • ModeledEntity#setUnderlyingEntity(Entity) / ModeledEntity.getModeledEntity(Entity): associação e procura inversa estática do modelo ligado a uma entidade Bukkit, ou null.
  • ModeledEntity#showUnderlyingEntity(Player) / hideUnderlyingEntity(Player): visibilidade da entidade Minecraft subjacente para cada jogador.
  • ModeledEntity#getEntityID(): devolve o ID do modelo como cadeia de texto.
  • ModeledEntity#getModelInstanceId(): UUID da instância, estável durante a vida do modelo.
  • ModeledEntity#isRemoved() / isDying(): indicadores do ciclo de vida.
  • ModeledEntity#getLocation(): devolve a Location atual.
  • ModeledEntity#getSpawnLocation(): devolve a Location onde o modelo foi criado.
  • ModeledEntity#getWorld(): devolve o World.
  • ModeledEntity#getSkeleton() / getSkeletonBlueprint() / getMountPointManager(): acesso à estrutura em execução.
  • ModeledEntity#getInteractionComponent() / getHitboxComponent() / getDamageableComponent() / getAnimationComponent(): componentes que implementam os métodos de conveniência acima.
  • ModeledEntity.getLoadedModeledEntities(): conjunto estático ativo de todas as entidades com modelos carregadas.
  • ModeledEntity#getViewers(): devolve o HashSet<UUID> dos jogadores que podem ver a entidade.
  • ModeledEntity#getNametagBones(): devolve uma List<Bone> de ossos de etiquetas de nome, útil para colocar texto adicional.
  • ModeledEntity#getScaleModifier() / setScaleModifier(double)
  • ModeledEntity#removeWithDeathAnimation(): remove com animação de morte, se existir.
  • ModeledEntity#removeWithMinimizedAnimation(): remove com uma animação de redução de escala.
  • ModeledEntity#remove(): remove imediatamente a entidade e todos os ossos.
  • ModeledEntity#setTintColor(Color) / getTintColor(): aplica uma tonalidade persistente pelo canal de cor da armadura de couro. Os flashes de dano substituem-na brevemente e depois regressam a ela. Use null para limpar.
  • ModeledEntity#setViewDistanceOverride(int) / getEffectiveViewDistance(): substitui DefaultConfig.maxModelViewDistance para uma entidade. Use -1 para regressar à predefinição do plugin.
  • DynamicEntity#setSyncMovement(boolean)
  • DynamicEntity#isDamagesOnContact() / setDamagesOnContact(boolean): controla o dano a jogadores por contacto com a hitbox.
  • DynamicEntity.isDynamicEntity(Entity) / DynamicEntity.getDynamicEntity(Entity): consultas estáticas a partir de uma entidade Bukkit.
  • DynamicEntity#getBodyLocation(): a posição da entidade virada segundo o yaw do modelo. Com weightedTurning ativado, esse yaw segue o yaw do corpo do mob com inércia de rotação, pelo que pode ficar para trás do mob enquanto o modelo roda. getLocation() devolve a mesma posição enquanto a sincronização de movimento está ativa.
  • Bone#getBoneLocation()

Luz, inclinação, centramento e cliques​

  • ModeledEntity#setBrightnessOverride(int blockLight, int skyLight): ilumina todos os ossos de apresentação com um nível fixo, cada um de 0 a 15, em vez da luz do local onde o osso está. 15, 15 é luminosidade total. Os observadores recebem a alteração com a atualização de ossos seguinte. Apenas para ossos de apresentação Java: os ossos Bedrock e de suporte de armadura mantêm a luz do mundo
  • ModeledEntity#clearBrightnessOverride() / hasBrightnessOverride() / getBrightnessOverrideBlockLight() / getBrightnessOverrideSkyLight(): removem ou leem a luz fixa. Os getters devolvem -1 sem substituição
  • DynamicEntity#setTravelOrientation(TravelOrientation) / getTravelOrientation(): inclina o modelo com a sua deslocação. TravelOrientation é um record composto por um Style (UPRIGHT, TOP_FIRST, FRONT_FIRST), startSpeed, fullSpeed, maxTilt e turnSpeed; TravelOrientation.UPRIGHT é a predefinição e null restaura-a. Consulte Orientação de deslocação
  • ModeledEntity#setBodyTilt(Quaterniondc) / getBodyTilt(): inclina o modelo inteiro por uma rotação em coordenadas do mundo em torno do meio dos seus cubos, depois da orientação e das animações. null volta a pô-lo na vertical. Uma orientação de deslocação diferente de UPRIGHT define isto a cada tick; num modelo vertical, uma inclinação que defina permanece
  • ModeledEntity#setCenteredOnBody(boolean) / isCenteredOnBody() / getBodyOffset(): desenha o modelo com o meio dos seus cubos no meio da sua entidade. Consulte Centrar no corpo
  • DynamicEntity#setClickSurface(boolean) / isClickSurface(): quando true, os clientes recebem uma entidade Interaction invisível sobre o modelo, para que os jogadores cliquem no modelo que veem e não na caixa do próprio corpo. Assume o tamanho do osso hitbox do modelo, ou dos limites do próprio modelo quando não tem nenhum, e aplica-se no prazo de dois ticks. Desativado por predefinição, porque uma caixa que cobre um modelo comprido ou rodado também apanha cliques nos seus cantos vazios
  • DynamicEntity#isSteerableMount() / hasSteeringRider() / seatSteeringRider(Player): um modelo num cavalo com sela é uma montada conduzível. seatSteeringRider senta o jogador no próprio cavalo como cavaleiro condutor quando ninguém o monta, e devolve se ele passa a montá-lo. Um clique direito num modelo assim faz isto por predefinição
  • DynamicEntity#liftSaddleTo(Bone): ajusta a escala do mob para que a sua sela fique sobre o osso de assento indicado na pose de repouso do modelo, enquanto o modelo mantém o seu tamanho. O /fmm mount usa-o com o primeiro osso m_ do modelo. Não faz nada quando o servidor não consegue indicar a altura da sela

Etiquetas de nome e alvos do olhar​

ModeledEntity disponibiliza setDisplayNameLines(List<String>), setDisplayNameScale(float), setDisplayNameLineGap(double), setDisplayNameVisible(boolean) e getNameplateLocation(). Um osso de etiqueta é opcional. Sem ele, a etiqueta de nome fica um quarto de bloco (quatro píxeis) acima do cubo mais alto do modelo em repouso, com a escala da entidade aplicada.

Nomes de várias linhas:

  • setDisplayName("Top line\nBottom line") e setDisplayNameLines(List<String>) recebem as linhas por ordem de leitura, de cima para baixo. A linha de baixo fica na âncora e as linhas adicionais estendem-se para cima. As mudanças de linha explícitas definem as linhas; o texto não quebra automaticamente.
  • Um nome vazio oculta a etiqueta de nome. setDisplayNameVisible(false) oculta todas as linhas e mantém-se em vigor quando o texto muda.
  • setDisplayNameScale(float) dimensiona o texto independentemente do tamanho do modelo. setDisplayNameLineGap(double) define o intervalo livre entre linhas, em blocos à escala 1, 0.1 por predefinição. Uma linha tem 0.225 blocos de altura à escala 1, pelo que as linhas sucessivas ficam a 0.325 blocos umas das outras por predefinição.
  • Quando um modelo tem vários ossos tag_, o primeiro por ordem alfabética ancora a etiqueta de nome, incluindo o seu movimento e animação.
  • Os jogadores Java veem displays de texto, que dimensionam tanto os glifos como o espaçamento. Os jogadores Bedrock veem etiquetas separadas em suportes de armadura marcadores, que não podem ser dimensionadas, pelo que o seu espaçamento fica à escala 1. Escolher as etiquetas Bedrock exige o Floodgate ou o Geyser no servidor de backend para identificar os jogadores Bedrock; uma configuração apenas com proxy e sem reencaminhamento de identidade não consegue escolhê-las de forma fiável.
  • O alcance de visualização, a invisibilidade, a remoção e o recarregamento acompanham o modelo. O texto e as posições inalterados não enviam pacotes.

Para saber como o EliteMobs dispõe o nome e a função de um NPC que usa um modelo do FreeMinecraftModels, consulte Criar NPCs.

DynamicEntity.setLookTarget(Location) dá ao esqueleto uma cópia de um alvo visual do olhar, sem rodar nem mover a entidade subjacente. Use null para o libertar.

Particularidades de PropEntity​

  • PropEntity.isPropEntity(ArmorStand) / PropEntity.getPropEntityID(ArmorStand): identificam um elemento decorativo a partir do seu suporte de armadura.
  • PropEntity.hasLoadedPropOnSameBlock(String entityID, Location): verificação de duplicados usada por spawnPropEntity. Pode chamá-la antes da criação para decidir o que fazer.
  • PropEntity.respawnPropEntityFromArmorStand(String entityID, ArmorStand): reconstrói um elemento a partir de um suporte que sobreviveu ao recarregamento de um chunk.
  • PropEntity.getPropEntities(): cópia pontual dos elementos carregados, indexada pelo UUID do suporte; alterá-la não modifica o registo ativo.
  • PropEntity#setPersistent(boolean): altera a persistência do suporte de armadura.
  • PropEntity#setCustomDataString(NamespacedKey, String) / getCustomDataString(NamespacedKey): escreve e lê valores PDC próprios. É o armazenamento usado pelos auxiliares Lua set_persistent_data / get_persistent_data, com fmm_lua_<key>.
  • PropEntity#remove() / remove(boolean showRealBlocks): remove o modelo, preservando a entrada persistente.
  • PropEntity#permanentlyRemove(): remove o modelo e a entrada persistente.
  • PropEntity#setVoxelizeConfig(boolean voxelize, boolean solidify) / applySolidify(): equivalentes durante a execução dos campos YML voxelize: / solidify:.
  • PropEntity#showFakePropBlocksToPlayer(Player) / showRealBlocksToPlayer(Player) e variantes ...ToAllPlayers(): controlam as barreiras enviadas apenas por pacotes de rede para criar colisão no cliente.

Integrações de armas mágicas​

Use MagicWeaponAPI.isOperational() antes de depender do serviço de magia, isWeapon(String) para verificar um ID definido e applyWeaponData(ItemStack, String) para aplicar a identidade da arma e a apresentação sem substituir o nome, descrição, nível ou encantamentos do item original. O registo do serviço não confirma que esteja operacional.

Um MagicAttackResolver opcional pode recusar lançamentos, filtrar e ordenar alvos, capturar dados de combate no lançamento e resolver o dano. Deve usar o MagicDamageApplication fornecido, de utilização única, em vez de causar dano diretamente. O FMM gere os controlos, trajeto do projétil, colisões e aplicação do dano.

MagicProjectileTravelEvent é uma consulta síncrona e cancelável de colisão ao longo de um segmento, incluindo uma verificação no impacto. Cancelá-la absorve o projétil sem dano nem explosão. Um segmento pode ser verificado mais de uma vez, pelo que os listeners não devem tratá-la como uma notificação de dano.

Eventos disponíveis​

O clique direito começa por emitir ModeledEntityInteractEvent sobre a entidade real subjacente. Herda os handlers de PlayerInteractEntityEvent, permitindo que os listeners normais de proteção de entidades o cancelem. O cancelamento impede o evento de clique direito do modelo e a respetiva função associada. Esta verificação é independente da permissão para colocar ou partir blocos.

Como as verificações do clique direito se encaixam:

  • As interações com blocos e com modelos são decididas em separado. Cancelar um PlayerInteractEvent mantém cancelada a ação do bloco ou do item, mas o clique direito ainda pode selecionar um modelo através das verificações de alcance e de oclusão por blocos do FMM. O cancelamento do clique esquerdo funciona como antes.
  • ModeledEntityInteractEvent usa a mão principal. O FMM só emite ModeledEntityRightClickEvent depois de essa emissão terminar, e um ModeledEntityRightClickEvent cancelado impede a função do modelo, o comportamento do elemento decorativo e a montagem predefinida.
  • O evento de ponte é uma verificação de permissão, não outro clique. Os encaminhadores de entrada, de armas mágicas e de elementos decorativos do próprio FMM ignoram-no. Um plugin que execute ações em eventos nativos de interação com entidades pode ignorar ModeledEntityInteractEvent para não agir duas vezes; os listeners de proteção devem continuar a tratá-lo.
  • Um modelo sem entidade Bukkit subjacente não pode emitir um evento Bukkit de entidade. Continua a emitir o ModeledEntityRightClickEvent cancelável. Os elementos decorativos persistentes e os NPCs do EliteMobs têm entidades subjacentes e recebem ambas as verificações.

Eventos genéricos de interação:

  • ModeledEntityLeftClickEvent
  • ModeledEntityRightClickEvent
  • ModeledEntityHitboxContactEvent
  • ModeledEntityHitByProjectileEvent

São quatro eventos Bukkit canceláveis, emitidos pelas deteções internas na thread principal. O cancelamento impede a função associada e o comportamento predefinido correspondente. O contacto com hitboxes é verificado a cada dois ticks. Os cliques têm uma pequena janela de eliminação de duplicados por jogador, impedindo que a interação por pacotes de rede e o traçado OBB executem a mesma ação duas vezes.

Eventos do ciclo de vida:

  • FmmReloadedEvent: agendado na thread principal para 40 ticks após uma inicialização completa bem-sucedida, incluindo o arranque e a recarga completa. A recarga limitada ao conteúdo importado também o emite em caso de sucesso. Um pedido rejeitado durante a preparação mantém o sistema ativo e não emite um evento de recarga concluída.

Os eventos públicos de interação são comuns aos tipos de modelo. As antigas variantes StaticEntity*Event, DynamicEntity*Event e PropEntity*Event, bem como ResourcePackGenerationEvent, já não fazem parte da API. Use os quatro eventos genéricos acima e FmmReloadedEvent.

Os plugins que guardam referências duradouras a DynamicEntity ou PropEntity, como EliteMobs e BetterStructures, devem tratar este evento recriando as ligações aos modelos nas entidades subjacentes que sobreviveram. Caso contrário, ficam invisíveis após o recarregamento: o FMM removeu as entidades de apresentação em onDisable e a referência guardada ficou 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 e deteção OBB de projéteis​

O FMM usa caixas delimitadoras orientadas, OBB, para detetar impactos de projéteis em modelos. Quando o projétil atravessa a hitbox OBB, emite o evento Bukkit cancelável ModeledEntityHitByProjectileEvent.

Detalhes:

  • A deteção verifica o segmento entre posições registadas para detetar passagens por modelos finos; sem posição anterior, usa uma verificação de sobreposição.
  • Os candidatos são tratados do mais próximo para o mais distante, independentemente da ordem do registo de modelos.
  • Um projétil sem perfuração afeta um único alvo com modelo. Uma flecha com Perfuração pode atingir um número de alvos distintos igual ao nível de perfuração mais um, por ordem do trajeto, sem duplicar eventos quando a OBB e a hitbox normal detetam o mesmo impacto.
  • O processamento predefinido de entidades dinâmicas encaminha o impacto para a entidade viva subjacente como dano real de projétil, usando a velocidade no impacto. Preserva a causa, as integrações de combate e a atribuição ao atirador.
  • Cancelar o evento impede a função associada e o dano predefinido, mas a colisão continua a consumir a quota de alvos do projétil. Assim, um impacto cancelado sem perfuração consome o projétil.
@EventHandler
public void onProjectileHitModel(ModeledEntityHitByProjectileEvent event) {
ModeledEntity target = event.getEntity();
Projectile projectile = event.getProjectile();
// Cancel to prevent damage
event.setCancelled(true);
}

Utilitários de itens e modelos​

ModelItemFactory​

Classe para criar ItemStacks relacionados com modelos através de código.

// 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): cria um item de colocação. Em 1.21.4+, aplica automaticamente o modelo de apresentação se existir o JSON correspondente.
  • createCustomItem(String itemId, PropScriptConfigFields config): cria um item com nome, descrição, encantamentos e modelo de apresentação da configuração unificada.
  • formatModelName(String modelId): converte um ID como 01_em_flame_sword em Flame Sword.

DisplayModelRegistry​

Registo dos modelos com JSON de apresentação disponível.

// Check if a model has a display model JSON registered
boolean has3D = DisplayModelRegistry.hasDisplayModel("magic_sword");
  • register(String modelId): regista um ID, chamado internamente no recarregamento.
  • hasDisplayModel(String modelId): verifica se o modelo de apresentação foi registado; um .json inválido não é registado só por existir.
  • getRegisteredModels(): devolve um Set<String> imutável dos IDs com apresentação registada.
  • shutdown(): limpa todos os registos.

ItemScriptManager​

Apesar de manter o nome da classe, é agora um catálogo de itens definidos e armas mágicas. getItemDefinitions() expõe as definições de itens registadas e getWeaponCatalog() as definições de armas validadas. As antigas APIs de scripts ativos, atualização de equipamento e remoção de scripts de jogadores foram removidas.

ScriptedItemAPI​

  • isValidItemId(String): verifica o ID exato do item definido, isto é, o nome da configuração sem .yml.
  • getItemConfig(String): devolve a definição ou null.
  • applyScriptedItemData(ItemStack, String): aplica fmm_item_id e o modelo de apresentação registado, se existir. Devolve false para um ID inválido ou item sem metadados.

Este método não altera nome, descrição ou encantamentos do item original e não associa comportamento Lua. Aplique encantamentos separadamente através do MagmaCore. Os estados de preparação de arcos e bestas pertencem à definição base de apresentação; não acrescente _idle ao ID do item.

ModelItemAPI​

Use ModelItemAPI.applyDisplayModel(ItemStack, String) quando precisar apenas de um modelo de apresentação FMM. Verifica o registo de apresentação sem aplicar a identidade de um item definido. Isto é distinto de escolher uma definição FMM de arma ou item.

ItemConstructionSnapshot​

Destina-se a plugins que constroem itens fora da thread do servidor. Chame ItemConstructionSnapshot.capture() na thread do servidor; copia as definições atuais de itens definidos, os modelos de apresentação e os tipos de armas mágicas. Uma thread de trabalho pode depois chamar item(id) ou weapon(id) e aplicar o ItemData devolvido a um ItemStack com apply(ItemStack), que aplica fmm_item_id a um item definido e o modelo de apresentação quando houver um registado. weapon(id) devolve null a menos que o ID seja uma arma mágica e o serviço de magia estivesse operacional no momento da captura. Antes de publicar os itens construídos, chame requireCurrent() na thread do servidor: lança ConcurrentModificationException se as definições do FMM tiverem mudado desde a captura, por exemplo após um recarregamento.

DisguiseAPI​

Ponto de entrada público dos disfarces. Outros plugins devem usar esta classe em vez do DisguiseManager interno, preservando a integração quando a implementação interna mudar.

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): remove o disfarce anterior antes de tentar substituí-lo; um ID desconhecido devolve false após essa remoção. Passe um jogador ligado e um ID carregado exato; a API não normaliza os IDs nem verifica permissões do comando.
  • undisguise(Player): devolve true se removeu um disfarce.
  • isDisguised(Player): consulta booleana rápida.
  • getDisguiseModelID(Player): devolve o ID ativo ou null.
  • getDisguisedPlayers(): cópia não modificável dos jogadores disfarçados.

O FMM combina a visibilidade predefinida do jogador, uma poção de invisibilidade e alterações na apresentação do equipamento. Só restaura um efeito removido se esse disfarce o tiver adicionado; um efeito que já existia não é assumido nem restaurado. Ao contrário da API, o comando valida o modelo antes da substituição. Consulte os limites do ciclo de vida e da apresentação em Disfarces de jogadores.

LocationAPI​

API pública para registar deteção de dungeons e proteção de regiões. Os predicados alimentam em.location.is_in_dungeon e em.location.is_protected no Lua do FMM, usados em scripts incluídos como pickupable.lua e storage_double.lua.

Os plugins passam um simples Predicate<Location>, evitando a passagem de tipos internos incorporados pelo FMM entre carregadores de classes.

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): basta um predicado devolver true para considerar a posição dentro de uma dungeon.
  • registerProtectionProvider(String providerName, Predicate<Location> predicate): basta um predicado devolver true para considerar a posição protegida.
  • unregisterDungeonLocator(String providerName) e unregisterProtectionProvider(String providerName): removem o predicado registado com esse nome. Chame-os no onDisable do seu plugin para que o FMM deixe de chamar a instância desativada.

Registar novamente com o mesmo nome de fornecedor substitui o predicado anterior, pelo que um plugin que volte a registar a cada ativação não acumula cópias. Um nome de fornecedor null regista um predicado que não pode ser substituído nem removido.

Os operadores podem verificar os registos com /fmm location, que apresenta as contagens atuais de fornecedores e testa ambos os predicados na sua posição.

Fornecedores de proteção e colocação de elementos​

Os mesmos fornecedores participam em preventPropPlacementInProtectedRegions, mas essa verificação chama canBuild(player, location) em vez de isProtected(location). A diferença é:

FornecedorComportamento de canBuild
Adaptador integrado WorldGuardRespeita a exceção do WorldGuard e consulta testBuild, permitindo a construção por membros e proprietários
Adaptador integrado GriefPreventionPermite construir se não houver terreno reclamado; dentro de um terreno, consulta a permissão de construção desse jogador
Registado por LocationAPI.registerProtectionProviderUsa !isProtected(location): considera apenas a posição e bloqueia todos nos locais que o predicado define como protegidos

Um Predicate<Location> não pode expressar permissões específicas por jogador. Para as suportar no seu sistema de regiões, implemente diretamente RegionProtectionProvider do MagmaCore, substitua canBuild e registe-o com LocationQueryRegistry.registerProtectionProvider.

Tenha também em conta que:

  • Uma falha recusa a colocação. Se um fornecedor lançar uma exceção na consulta de construção, a colocação é recusada e um aviso identifica o fornecedor.
  • Todos os fornecedores devem permitir a ação. A primeira recusa prevalece. Os fornecedores de propriedade registados através de LocationOwnership continuam a considerar apenas a posição e bloqueiam todos.

A colocação por objeto verifica primeiro a célula adjacente. Os elementos voxelizados verificam também a área ocupada após a rotação, usando dimensões arredondadas da hitbox com um mínimo de um bloco; o volume das barreiras enviadas por pacotes usa outro cálculo. Chamadas diretas a PropEntity.spawnPropEntity não executam as verificações de colocação do objeto do jogador. Consulte Configuração antes de disponibilizar colocação através de outro plugin.

PropScriptConfigFields​

Lê o YAML junto dos modelos, usado por elementos decorativos e itens definidos. As listas scripts: dos elementos associam scripts Lua independentes. Uma definição material: regista, em alternativa, um item para segurar e não pode ter uma lista scripts: não vazia.

Consulte Configuração de modelos para exemplos separados e Armas mágicas para o esquema de armas.

Scripts Lua​

O FMM suporta scripts Lua de elementos decorativos através do motor MagmaCore. Os ficheiros ficam em plugins/FreeMinecraftModels/scripts/ e são associados através de um YML junto do modelo. O ficheiro no disco tem de terminar em .lua; a configuração pode incluir ou omitir a extensão.

Os elementos decorativos associam todos os scripts da lista scripts: como instâncias independentes. Os efeitos de itens de mão usam o ambiente separado de fornecedores de encantamentos e não podem usar uma lista scripts: de item não vazia.

Eventos dos scripts de elementos decorativos​

EventoAtivação
on_spawnCriação do elemento no mundo
on_game_tickCada tick enquanto o elemento existe
on_zone_enterEntrada de um jogador numa zona observada criada pelo script
on_zone_leaveSaída de um jogador dessa zona
on_destroyRemoção definitiva do elemento (não ao descarregar o chunk nem ao recarregar)
on_left_clickClique esquerdo de um jogador
on_right_clickClique direito de um jogador
on_projectile_hitUm projétil atinge o elemento; context.projectile é o projétil e context.event.player o atirador

Eventos de scripts de itens​

Os eventos Lua de itens equipados e o respetivo context.item foram removidos. Use Definições de encantamentos para efeitos de itens. Os eventos de elementos decorativos continuam suportados.

Tabela de contexto dos scripts​

Os scripts de elementos decorativos recebem uma tabela context. Segue-se um resumo; consulte API Lua de elementos decorativos para todos os detalhes.

context.prop:

  • model_id: nome do modelo da definição.
  • current_location: posição atual do elemento.
  • play_animation(name, blend, loop): solicita a animação; blend e loop usam true por predefinição, o que sobrepõe uma animação em repetição ao idle do elemento decorativo. O pedido é aplicado no tick seguinte do modelo.
  • stop_animation(): termina as animações personalizadas e regressa a idle, se existir. Não interrompe o estado terminal de morte.
  • hurt_visual(): reproduz o flash vermelho de dano.
  • pickup(): agenda a remoção do elemento e a queda do item de colocação.
  • mount(player): agenda uma tentativa de montar. true confirma que o jogador e o gestor eram válidos, não que foi atribuído um assento.
  • dismount(player): agenda a verificação de desmontagem. true confirma que o jogador e o gestor eram válidos.
  • get_passengers(): lista os jogadores montados no elemento.
  • spawn_elitemobs_boss(filename, x, y, z): cria um boss EliteMobs em coordenadas absolutas no mundo atual do elemento.

context.event:

  • Disponível em on_left_click, on_right_click, on_zone_enter, on_zone_leave e on_projectile_hit.
  • cancel(), uncancel() e is_cancelled, quando o evento permite cancelamento.
  • player: jogador que provocou o evento; em on_projectile_hit, a entidade viva que disparou o projétil.

context.world:

  • spawn_entity(entity_type, x, y, z): cria uma entidade normal do Minecraft ou devolve nil se o tipo for inválido.
  • set_block_at(x, y, z, material): agenda uma alteração de bloco se o material for válido. Ignora chunks descarregados.
  • Também oferece partículas, sons, consulta de blocos, relâmpagos e procura de entidades próximas.

context.cooldowns:

  • check_local(key, ticks): verifica e inicia um tempo de recarga por script.
  • global_ready() / set_global(ticks): tempo de recarga partilhado do elemento decorativo.

Objetos de jogador, obtidos em context.player ou context.event.player:

  • get_held_item(): devolve o item na mão; type é o nome do material Bukkit em maiúsculas.
  • is_holding_elitemobs_item(filename): verifica a etiqueta de nome de ficheiro EliteMobs do item na mão principal, como shroomwhispers_shears.yml. Os nomes apresentados e as traduções não afetam esta verificação. Devolve false para uma mão vazia, um item diferente ou uma integração EliteMobs indisponível.
  • consume_held_item(): agenda a remoção de uma unidade da mão principal tal como estiver quando a tarefa correr; não devolve um resultado de pagamento.
  • has_item(material): verifica se o jogador tem um item.
  • send_message(text): envia uma mensagem de chat.
  • game_mode: modo de jogo atual.

Use a identidade pelo nome de ficheiro para os itens de missão do EliteMobs:

if not player:is_holding_elitemobs_item("shroomwhispers_shears.yml") then
return
end

Este método é fornecido pela integração EliteMobs do FMM. Mudar o nome de um item normal não lhe dá a etiqueta de nome de ficheiro necessária.

Exemplo de script de elemento decorativo​

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
}

Exemplo de script de item​

O antigo exemplo de item equipado já não é suportado. Consulte Armas mágicas e Definições de encantamentos para criar itens com o sistema atual.

Compilar a partir do código-fonte​

O código-fonte está no GitHub. O FreeMinecraftModels é um projeto Maven para Java 21:

mvn -DskipTests package
  • O jar do plugin é escrito em target/FreeMinecraftModels.jar.
  • O MagmaCore, o bStats e as outras bibliotecas incluídas são integrados por shading na fase package. O MagmaCore é realocado para com.magmaguy.freeminecraftmodels.magmacore, pelo que o servidor não precisa de uma instalação separada do MagmaCore.
  • O Maven resolve o MagmaCore 2.2.0-SNAPSHOT a partir do repositório do MagmaGuy. Quando o código-fonte precisa de alterações ao MagmaCore que ainda não estão publicadas aí, compile primeiro o MagmaCore e publique-o no seu repositório Maven local. -Dmagmacore.version=<version> compila contra outra versão do MagmaCore.
  • Quando a variável de ambiente MC_DIST_DIR está definida, a compilação copia também o jar para essa pasta.

Os modelos, texturas, fontes e pacotes de conteúdo importados mantêm as licenças dos seus autores. Gerar um pacote de recursos a partir deles não altera esses direitos.

Notas​

  • O FreeMinecraftModels é uma dependência instalada como plugin, não uma biblioteca para incorporar.
  • Para usar modelos recém-importados, chame ModeledEntityManager.reload() em vez de tentar reconstruir o estado do FMM.
  • O código atual do FMM depende de MagmaCore 2.2.0-SNAPSHOT, que fornece o motor Lua partilhado, LocationQueryRegistry e WorldFolderResolver.
  • O FreeMinecraftModels declara WorldGuard, WorldEdit, GriefPrevention, Vault, floodgate e Geyser-Spigot como softdepend. WorldGuard e GriefPrevention fornecem os adaptadores integrados de proteção por jogador; WorldEdit é uma dependência do WorldGuard, não um adaptador independente do FMM. A loja requer Vault, um fornecedor de economia e configuração de loja ativa. A apresentação Bedrock também precisa do pacote compatível e da respetiva integração.
  • ModeledEntityManager.reload() solicita a mesma preparação assíncrona e o ciclo completo de /fmm reload, sem esperar pela conclusão. Chame-o na thread principal e use a confirmação do ciclo concluído com sucesso como prova, não o retorno void. Conteúdo candidato inválido pode fazer rejeitar a recarga, mantendo o sistema ativo.