Scripting Lua: Programas de Comportamento
Um programa de comportamento substitui a IA nativa de um mob por comportamentos Lua que decidem para onde ele se move, para onde olha, que entidade tem como alvo e quando ataca. O EliteMobs executa estes programas no sistema nativo Brain do Minecraft através do runtime Mind do MagmaCore.
Os programas de comportamento não são poderes Lua. Um poder Lua reage a eventos do boss como on_boss_damaged_by_player; um programa de comportamento é executado a cada tick e controla os movimentos do mob. Um boss pode usar ambos: um comportamento pode pedir aos poderes do boss que executem uma ação através de on_mind_action.
Defina behavior num ficheiro de boss personalizado, como descrito em Criar Bosses, ou no ficheiro de propriedades de mob do tipo de entidade. behavior: native mantém a IA vanilla. Se a versão do servidor não tiver suporte nativo para Mind, o EliteMobs regista Native Mind interface is unavailable on this Minecraft version. no arranque. Nesse caso, um boss personalizado que selecione um programa não spawna e regista Cannot spawn <boss file>: .... Um boss cujo behavior indique um programa em falta ou inválido falha da mesma forma em qualquer versão.
Ficheiros
Os ficheiros de comportamento ficam em plugins/EliteMobs/behaviors/. O EliteMobs escreve estes ficheiros incluídos quando estão em falta:
plugins/
EliteMobs/
behaviors/
basic_melee.lua
modules/
target.lua
pursuit.lua
melee.lua
wander.lua
- Um ficheiro
.luadentro de qualquer pasta chamadamodulesé um módulo. Todos os outros ficheiros.luasão programas. - Um boss referencia um programa pelo seu caminho relativo a
behaviors/, por exemplobasic_melee.luaouguards/patrol_guard.lua. - O EliteMobs carrega os ficheiros de comportamento quando o seu serviço Mind arranca. Um ficheiro que falhe a validação é ignorado, e a consola regista
Could not load behavior <file>ouCould not load behavior module <file>com o motivo. - Os IDs de programas e módulos usam o espaço de nomes
elitemobs, comoelitemobs:behavior/basic_melee. Os IDs estão em minúsculas e podem contera-z,0-9,.,_e-, além de/depois dos dois-pontos. Dois programas não podem partilhar um ID, e cada ID de módulo deve ser declarado por um único ficheiro. - Um espaço de nomes contém no máximo 256 módulos. Um módulo pode listar no máximo 32 dependências, um programa pode resolver no máximo 64 módulos, e cada ficheiro de código está limitado a 1.000.000 caracteres.
As mesmas regras de sandbox dos outros scripts Lua aplicam-se aqui. Consulte a Sandbox Lua. Cada mob que executa um programa recebe o seu próprio ambiente Lua, pelo que as variáveis locais do ficheiro não são partilhadas entre mobs.
Ficheiro de programa
Um ficheiro de programa devolve ai.program { ... }:
return ai.program {
id = 'elitemobs:behavior/basic_melee', revision = 1,
modules = {
'elitemobs:behavior/target', 'elitemobs:behavior/pursuit',
'elitemobs:behavior/melee', 'elitemobs:behavior/wander'
},
budget = {
callback_micros = 2000, entity_micros = 4000, server_micros = 5000,
max_callbacks = 20, max_instructions = 12000, max_action_requests = 2
}
}
| Campo | Tipo | Notas |
|---|---|---|
id | string | Obrigatório. ID com espaço de nomes, elitemobs:... para ficheiros em behaviors/. |
revision | inteiro positivo | Obrigatório. |
modules | array de strings | Opcional. IDs dos módulos cujas memórias, sensores e comportamentos se juntam ao programa. As dependências são carregadas antes dos módulos que precisam delas. Duplicados e ciclos de dependências são rejeitados. |
memories, sensors, behaviors | tabelas | Opcional. Um programa pode declarar os seus próprios, no mesmo formato de um módulo. |
budget | tabela | Opcional. Limites flexíveis de agendamento. Consulte Orçamentos. |
runaway | tabela | Opcional. Limites rígidos por callback. Consulte Orçamentos. |
Ficheiro de módulo
Um ficheiro de módulo devolve ai.module { ... } e agrupa memórias, sensores e comportamentos reutilizáveis:
| Campo | Tipo | Notas |
|---|---|---|
id | string | Obrigatório. ID do módulo com espaço de nomes. |
revision | inteiro positivo | Obrigatório. |
dependencies | array de strings | Opcional. IDs de outros módulos de que este módulo precisa. |
memories | tabela | Opcional. Consulte Memórias. |
sensors | array | Opcional. Entradas ai.sensor { ... }. |
behaviors | array | Opcional. Entradas ai.behavior { ... }. |
sensors, behaviors, modules e dependencies têm de ser arrays simples, sem lacunas nem chaves com nome. Os nomes de sensores, comportamentos e memórias têm de ser únicos em todos os módulos de um programa.
Memórias
As memórias são valores tipados que os sensores e os comportamentos partilham para um mob. Declare cada uma pelo nome:
memories = {
candidate = { type = 'uuid', persistent = false },
next_attack = 'integer'
}
| Tipo | Valor Lua |
|---|---|
string | string |
boolean | booleano |
integer | número inteiro |
number | número |
uuid | string de UUID |
position | { world = 'world', x = 0, y = 64, z = 0 } |
Um nome sem dois-pontos é colocado no espaço de nomes do programa. persistent é false por predefinição; true marca a memória para ser serializada com o estado guardado do Mind. Leia e escreva memórias com c.memory:get(name), c.memory:set(name, value, ttl_ticks), c.memory:forget(name) e c.memory:contains(name). O terceiro argumento de set é opcional; quando é fornecido, o valor expira ao fim desse número de ticks. Usar um nome não declarado gera um erro.
Sensores
Um sensor recolhe informação, normalmente para memórias. Os sensores são executados antes dos comportamentos.
ai.sensor {
id = 'elitemobs:behavior/find_target', interval = 20,
sense = function(c)
local target = c.perception:nearest_player(35)
if target then c.memory:set('candidate', target.uuid, 21)
else c.memory:forget('candidate') end
end
}
| Campo | Tipo | Predefinição | Notas |
|---|---|---|---|
id | string | Obrigatório. | |
interval | inteiro positivo | 1 | Ticks entre execuções. |
sense | função | Obrigatório. Recebe o contexto Mind. |
Os sensores não podem usar c.actuator; chamar um método do atuador a partir de um sensor gera um erro.
Comportamentos
Um comportamento atua sobre o mob enquanto detém os controlos que declara.
ai.behavior {
id = 'elitemobs:behavior/attack', priority = 10,
controls = { ai.controls.attack },
can_start = function(c) return c.perception:current_target() ~= nil end,
can_continue = function(c) return c.perception:current_target() ~= nil end,
tick = function(c)
local target = c.perception:current_target()
if target then c.actuator:attack(target) end
end
}
| Campo | Tipo | Predefinição | Notas |
|---|---|---|---|
id | string | Obrigatório. | |
priority | inteiro | 0 | Os números mais baixos têm precedência. |
controls | array | nenhum | Controlos que este comportamento reserva enquanto está ativo. |
can_start | função | sempre true | Tem de devolver true ou false. |
can_continue | função | igual a can_start | Tem de devolver true ou false. |
start | função | nenhuma | Executada uma vez quando o comportamento começa. |
tick | função | Obrigatório. Executada a cada tick enquanto o comportamento está ativo. | |
stop | função | nenhuma | function(c, reason); executada quando o comportamento para. |
Ciclo de vida
- Enquanto está parado, o comportamento verifica
can_start. Quando esta devolvetruee o comportamento consegue reservar todos os controlos declarados,starté executada. - Em cada tick em que está ativo, incluindo o tick em que começou,
can_continueé executada primeiro. Quando devolvefalse, o comportamento para com o motivocompleted; caso contrário,tické executada. stop(c, reason)recebe um decompleted,preempted,program_replaced,entity_removed,callback_failedouhandle_closed. Parar liberta os controlos do comportamento e interrompe o movimento, o alvo ou o ataque que detinha.
Um erro Lua em can_continue, start ou tick para o comportamento com callback_failed. Devolver algo que não seja um booleano em can_start ou can_continue é um erro. A consola regista Mind <program> callback <callback> failed with ... por cada falha.
Controlos
| Controlo | Permite |
|---|---|
ai.controls.move | c.actuator:move_to(...), c.actuator:stop_moving() |
ai.controls.look | c.actuator:look_at(...) |
ai.controls.jump | c.actuator:jump() |
ai.controls.target | c.actuator:set_target(...), c.actuator:clear_target() |
ai.controls.attack | c.actuator:attack(...) |
ai.controls.action | c.actions:request(...) |
ai.controls.use_item | Reservado; o atuador Lua não tem nenhum método para itens. |
Cada controlo pertence a um único comportamento ativo de cada vez. Um comportamento com um número de priority mais baixo tira um controlo a um comportamento com um número mais alto, que para com o motivo preempted. Não pode tirar um controlo detido por um comportamento com um número mais baixo. Entre prioridades iguais, fica com o controlo o comportamento cujo id vem primeiro por ordem alfabética. Chamar um método do atuador sem deter o respetivo controlo gera um erro. c.actuator:stop_all() para apenas o que o comportamento detém.
Nos módulos incluídos, a seleção de alvo usa a prioridade 5, os ataques corpo a corpo 10, a perseguição 20 e o vaguear em repouso 50, pelo que a perseguição tira o movimento ao vaguear assim que existe um alvo.
Pedir ações ao boss
Um comportamento que detém ai.controls.action pode chamar c.actions:request(identifier, payload). O EliteMobs envia o pedido aos poderes Lua do boss através de on_mind_action. O identificador tem de ser uma chave com espaço de nomes, em minúsculas, com no máximo 128 caracteres, como 'elitemobs:slam'. O payload contém no máximo 16 entradas com chaves em minúsculas de no máximo 64 caracteres, e os valores de texto estão limitados a 256 caracteres; um identificador ou payload inválido gera um erro. A chamada devolve accepted, deferred ou rejected; os pedidos que ultrapassem o max_action_requests do programa num tick devolvem deferred. Consulte o contexto de ação Mind para o formato do payload.
Orçamentos
budget define limites flexíveis de agendamento. Quando um mob atinge um deles num tick, o runtime Mind ignora os callbacks restantes desse mob até ao tick seguinte. Um callback que já esteja em execução termina sempre.
| Chave | Predefinição | Notas |
|---|---|---|
callback_micros | 2000 | Um callback que demore mais do que isto termina os callbacks do mob nesse tick. |
entity_micros | 4000 | Tempo total de callbacks por mob e por tick. |
server_micros | 20000 | Os callbacks do mob param nesse tick quando todos os programas Mind juntos tiverem usado este tempo. |
max_callbacks | 128 | Callbacks por mob e por tick. |
max_action_requests | 8 | Pedidos de ação por mob e por tick; no máximo 64. |
Todos os valores têm de ser positivos, e callback_micros não pode exceder entity_micros, que por sua vez não pode exceder server_micros.
runaway define limites rígidos que interrompem um único callback:
| Chave | Predefinição | Notas |
|---|---|---|
cpu_micros | 50000 | Tempo de CPU da thread por callback. |
max_instructions | 50000 | Instruções Lua por callback. |
budget também aceita max_instructions, como no basic_melee.lua incluído. Declare-o em budget ou em runaway, não em ambos. Um callback que ultrapasse um limite de runaway falha como qualquer outro erro de callback.
Próximos passos
- Criar Bosses: behavior -- selecionar um programa para um boss
- Referência da API Lua: contexto de um programa Mind -- todos os métodos de
c.memory,c.perception,c.actuatorec.actions - Hooks e Ciclo de Vida --
on_mind_actione os outros hooks de poderes Lua
