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 Luaem.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
ModeledEntityStaticEntityDynamicEntityPropEntity
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): devolvefalsese o nome não corresponder a um estado integrado nem a uma animação do modelo, ou depois dedeathter sido pedida. Numa animação personalizada,blend = truereproduz-a na camada de ação por cima deidleouwalk, efalsedeixa-a assumir toda a pose.loopaplica-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 paraidle; 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 oDamageableComponentda 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, ounull.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():UUIDda instância, estável durante a vida do modelo.ModeledEntity#isRemoved()/isDying(): indicadores do ciclo de vida.ModeledEntity#getLocation(): devolve aLocationatual.ModeledEntity#getSpawnLocation(): devolve aLocationonde o modelo foi criado.ModeledEntity#getWorld(): devolve oWorld.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 oHashSet<UUID>dos jogadores que podem ver a entidade.ModeledEntity#getNametagBones(): devolve umaList<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. Usenullpara limpar.ModeledEntity#setViewDistanceOverride(int)/getEffectiveViewDistance(): substituiDefaultConfig.maxModelViewDistancepara uma entidade. Use-1para 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. ComweightedTurningativado, 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 de0a15, 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 mundoModeledEntity#clearBrightnessOverride()/hasBrightnessOverride()/getBrightnessOverrideBlockLight()/getBrightnessOverrideSkyLight(): removem ou leem a luz fixa. Os getters devolvem-1sem substituiçãoDynamicEntity#setTravelOrientation(TravelOrientation)/getTravelOrientation(): inclina o modelo com a sua deslocação.TravelOrientationé um record composto por umStyle(UPRIGHT,TOP_FIRST,FRONT_FIRST),startSpeed,fullSpeed,maxTilteturnSpeed;TravelOrientation.UPRIGHTé a predefinição enullrestaura-a. Consulte Orientação de deslocaçãoModeledEntity#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.nullvolta a pô-lo na vertical. Uma orientação de deslocação diferente deUPRIGHTdefine isto a cada tick; num modelo vertical, uma inclinação que defina permaneceModeledEntity#setCenteredOnBody(boolean)/isCenteredOnBody()/getBodyOffset(): desenha o modelo com o meio dos seus cubos no meio da sua entidade. Consulte Centrar no corpoDynamicEntity#setClickSurface(boolean)/isClickSurface(): quandotrue, 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 ossohitboxdo 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 vaziosDynamicEntity#isSteerableMount()/hasSteeringRider()/seatSteeringRider(Player): um modelo num cavalo com sela é uma montada conduzível.seatSteeringRidersenta 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çãoDynamicEntity#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 mountusa-o com o primeiro ossom_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")esetDisplayNameLines(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.1por predefinição. Uma linha tem0.225blocos de altura à escala 1, pelo que as linhas sucessivas ficam a0.325blocos 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 porspawnPropEntity. 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 Luaset_persistent_data/get_persistent_data, comfmm_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 YMLvoxelize:/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
PlayerInteractEventmanté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. ModeledEntityInteractEventusa a mão principal. O FMM só emiteModeledEntityRightClickEventdepois de essa emissão terminar, e umModeledEntityRightClickEventcancelado 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
ModeledEntityInteractEventpara 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
ModeledEntityRightClickEventcancelá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:
ModeledEntityLeftClickEventModeledEntityRightClickEventModeledEntityHitboxContactEventModeledEntityHitByProjectileEvent
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 como01_em_flame_swordemFlame 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.jsoninválido não é registado só por existir.getRegisteredModels(): devolve umSet<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 ounull.applyScriptedItemData(ItemStack, String): aplicafmm_item_ide o modelo de apresentação registado, se existir. Devolvefalsepara 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 devolvefalseapó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): devolvetruese removeu um disfarce.isDisguised(Player): consulta booleana rápida.getDisguiseModelID(Player): devolve o ID ativo ounull.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 devolvertruepara considerar a posição dentro de uma dungeon.registerProtectionProvider(String providerName, Predicate<Location> predicate): basta um predicado devolvertruepara considerar a posição protegida.unregisterDungeonLocator(String providerName)eunregisterProtectionProvider(String providerName): removem o predicado registado com esse nome. Chame-os noonDisabledo 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 é:
| Fornecedor | Comportamento de canBuild |
|---|---|
| Adaptador integrado WorldGuard | Respeita a exceção do WorldGuard e consulta testBuild, permitindo a construção por membros e proprietários |
| Adaptador integrado GriefPrevention | Permite construir se não houver terreno reclamado; dentro de um terreno, consulta a permissão de construção desse jogador |
Registado por LocationAPI.registerProtectionProvider | Usa !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
LocationOwnershipcontinuam 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
| Evento | Ativação |
|---|---|
on_spawn | Criação do elemento no mundo |
on_game_tick | Cada tick enquanto o elemento existe |
on_zone_enter | Entrada de um jogador numa zona observada criada pelo script |
on_zone_leave | Saída de um jogador dessa zona |
on_destroy | Remoção definitiva do elemento (não ao descarregar o chunk nem ao recarregar) |
on_left_click | Clique esquerdo de um jogador |
on_right_click | Clique direito de um jogador |
on_projectile_hit | Um 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;blendeloopusamtruepor predefinição, o que sobrepõe uma animação em repetição aoidledo elemento decorativo. O pedido é aplicado no tick seguinte do modelo.stop_animation(): termina as animações personalizadas e regressa aidle, 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.trueconfirma 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.trueconfirma 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_leaveeon_projectile_hit. cancel(),uncancel()eis_cancelled, quando o evento permite cancelamento.player: jogador que provocou o evento; emon_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 devolvenilse 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, comoshroomwhispers_shears.yml. Os nomes apresentados e as traduções não afetam esta verificação. Devolvefalsepara 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 paracom.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-SNAPSHOTa 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_DIRestá 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,LocationQueryRegistryeWorldFolderResolver. - 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 retornovoid. Conteúdo candidato inválido pode fazer rejeitar a recarga, mantendo o sistema ativo.