Saltar para o conteúdo principal

Scripting Lua: Programas de Comportamento

webapp_banner.jpg

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.

Selecionar um programa

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 .lua dentro de qualquer pasta chamada modules é um módulo. Todos os outros ficheiros .lua são programas.
  • Um boss referencia um programa pelo seu caminho relativo a behaviors/, por exemplo basic_melee.lua ou guards/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> ou Could not load behavior module <file> com o motivo.
  • Os IDs de programas e módulos usam o espaço de nomes elitemobs, como elitemobs:behavior/basic_melee. Os IDs estão em minúsculas e podem conter a-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
}
}
CampoTipoNotas
idstringObrigatório. ID com espaço de nomes, elitemobs:... para ficheiros em behaviors/.
revisioninteiro positivoObrigatório.
modulesarray de stringsOpcional. 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, behaviorstabelasOpcional. Um programa pode declarar os seus próprios, no mesmo formato de um módulo.
budgettabelaOpcional. Limites flexíveis de agendamento. Consulte Orçamentos.
runawaytabelaOpcional. 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:

CampoTipoNotas
idstringObrigatório. ID do módulo com espaço de nomes.
revisioninteiro positivoObrigatório.
dependenciesarray de stringsOpcional. IDs de outros módulos de que este módulo precisa.
memoriestabelaOpcional. Consulte Memórias.
sensorsarrayOpcional. Entradas ai.sensor { ... }.
behaviorsarrayOpcional. 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'
}
TipoValor Lua
stringstring
booleanbooleano
integernúmero inteiro
numbernúmero
uuidstring 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
}
CampoTipoPredefiniçãoNotas
idstringObrigatório.
intervalinteiro positivo1Ticks entre execuções.
sensefunçãoObrigató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
}
CampoTipoPredefiniçãoNotas
idstringObrigatório.
priorityinteiro0Os números mais baixos têm precedência.
controlsarraynenhumControlos que este comportamento reserva enquanto está ativo.
can_startfunçãosempre trueTem de devolver true ou false.
can_continuefunçãoigual a can_startTem de devolver true ou false.
startfunçãonenhumaExecutada uma vez quando o comportamento começa.
tickfunçãoObrigatório. Executada a cada tick enquanto o comportamento está ativo.
stopfunçãonenhumafunction(c, reason); executada quando o comportamento para.

Ciclo de vida​

  1. Enquanto está parado, o comportamento verifica can_start. Quando esta devolve true e o comportamento consegue reservar todos os controlos declarados, start é executada.
  2. Em cada tick em que está ativo, incluindo o tick em que começou, can_continue é executada primeiro. Quando devolve false, o comportamento para com o motivo completed; caso contrário, tick é executada.
  3. stop(c, reason) recebe um de completed, preempted, program_replaced, entity_removed, callback_failed ou handle_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​

ControloPermite
ai.controls.movec.actuator:move_to(...), c.actuator:stop_moving()
ai.controls.lookc.actuator:look_at(...)
ai.controls.jumpc.actuator:jump()
ai.controls.targetc.actuator:set_target(...), c.actuator:clear_target()
ai.controls.attackc.actuator:attack(...)
ai.controls.actionc.actions:request(...)
ai.controls.use_itemReservado; 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.

ChavePredefiniçãoNotas
callback_micros2000Um callback que demore mais do que isto termina os callbacks do mob nesse tick.
entity_micros4000Tempo total de callbacks por mob e por tick.
server_micros20000Os callbacks do mob param nesse tick quando todos os programas Mind juntos tiverem usado este tempo.
max_callbacks128Callbacks por mob e por tick.
max_action_requests8Pedidos 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:

ChavePredefiniçãoNotas
cpu_micros50000Tempo de CPU da thread por callback.
max_instructions50000Instruçõ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​