Zum Hauptinhalt springen

Lua-Scripting: Verhaltensprogramme

webapp_banner.jpg

Ein Verhaltensprogramm ersetzt die native KI eines Mobs durch Lua-Verhalten, die entscheiden, wohin er sich bewegt, wohin er schaut, welche Entität er anvisiert und wann er angreift. EliteMobs führt diese Programme über die Mind-Laufzeit von MagmaCore auf dem nativen Brain-System von Minecraft aus.

Verhaltensprogramme sind keine Lua-Powers. Eine Lua-Power reagiert auf Boss-Ereignisse wie on_boss_damaged_by_player; ein Verhaltensprogramm läuft jeden Tick und besitzt die Bewegungssteuerung des Mobs. Ein Boss kann beides verwenden: Ein Verhalten kann die Powers des Bosses über on_mind_action bitten, eine Aktion auszuführen.

Ein Programm auswählen

Setze behavior in einer Custom-Boss-Datei, wie unter Bosse erstellen beschrieben, oder in der Mob-Eigenschaftsdatei des Entitätstyps. behavior: native behält die Vanilla-KI bei. Unterstützt die Serverversion kein natives Mind, protokolliert EliteMobs beim Start Native Mind interface is unavailable on this Minecraft version.. Ein Custom Boss, der ein Programm auswählt, spawnt dann nicht und protokolliert Cannot spawn <boss file>: .... Ein Boss, dessen behavior ein fehlendes oder ungültiges Programm nennt, scheitert auf jeder Version auf dieselbe Weise.


Dateien​

Verhaltensdateien liegen in plugins/EliteMobs/behaviors/. EliteMobs schreibt diese mitgelieferten Dateien, wenn sie fehlen:

plugins/
EliteMobs/
behaviors/
basic_melee.lua
modules/
target.lua
pursuit.lua
melee.lua
wander.lua
  • Eine .lua-Datei in einem beliebigen Ordner namens modules ist ein Modul. Jede andere .lua-Datei ist ein Programm.
  • Ein Boss verweist auf ein Programm über dessen Pfad relativ zu behaviors/, zum Beispiel basic_melee.lua oder guards/patrol_guard.lua.
  • EliteMobs lädt Verhaltensdateien, wenn sein Mind-Dienst startet. Eine Datei, die die Prüfung nicht besteht, wird übersprungen, und die Konsole protokolliert Could not load behavior <file> oder Could not load behavior module <file> mit dem Grund.
  • Programm- und Modul-IDs verwenden den Namespace elitemobs, etwa elitemobs:behavior/basic_melee. IDs bestehen aus Kleinbuchstaben und dürfen a-z, 0-9, ., _ und - enthalten, nach dem Doppelpunkt außerdem /. Zwei Programme können sich keine ID teilen, und jede Modul-ID sollte von genau einer Datei deklariert werden.
  • Ein Namespace enthält höchstens 256 Module. Ein Modul kann höchstens 32 Abhängigkeiten auflisten, ein Programm kann höchstens 64 Module auflösen, und jede Quelldatei ist auf 1.000.000 Zeichen begrenzt.

Es gelten dieselben Sandbox-Regeln wie für andere Lua-Skripte. Siehe die Lua-Sandbox. Jeder Mob, der ein Programm ausführt, erhält seine eigene Lua-Umgebung, sodass dateilokale Variablen nicht zwischen Mobs geteilt werden.


Programmdatei​

Eine Programmdatei gibt ai.program { ... } zurück:

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
}
}
FeldTypHinweise
idstringErforderlich. ID mit Namespace, elitemobs:... für Dateien in behaviors/.
revisionpositive GanzzahlErforderlich.
modulesArray aus StringsOptional. Modul-IDs, deren Speicherwerte, Sensoren und Verhalten in das Programm aufgenommen werden. Abhängigkeiten werden vor den Modulen geladen, die sie benötigen. Duplikate und zyklische Abhängigkeiten werden abgelehnt.
memories, sensors, behaviorsTabellenOptional. Ein Programm kann eigene deklarieren, im selben Format wie ein Modul.
budgetTabelleOptionale weiche Planungsgrenzen. Siehe Budgets.
runawayTabelleOptionale harte Grenzen pro Callback. Siehe Budgets.

Moduldatei​

Eine Moduldatei gibt ai.module { ... } zurück und bündelt wiederverwendbare Speicherwerte, Sensoren und Verhalten:

FeldTypHinweise
idstringErforderlich. Modul-ID mit Namespace.
revisionpositive GanzzahlErforderlich.
dependenciesArray aus StringsOptional. Andere Modul-IDs, die dieses Modul benötigt.
memoriesTabelleOptional. Siehe Speicherwerte.
sensorsArrayOptional. Einträge vom Typ ai.sensor { ... }.
behaviorsArrayOptional. Einträge vom Typ ai.behavior { ... }.

sensors, behaviors, modules und dependencies müssen einfache Arrays ohne Lücken oder benannte Schlüssel sein. Namen von Sensoren, Verhalten und Speicherwerten müssen über alle Module eines Programms hinweg eindeutig sein.


Speicherwerte​

Speicherwerte (Memories) sind typisierte Werte, die Sensoren und Verhalten für einen Mob gemeinsam nutzen. Deklariere jeden mit seinem Namen:

memories = {
candidate = { type = 'uuid', persistent = false },
next_attack = 'integer'
}
TypLua-Wert
stringString
booleanBoolean
integerGanzzahl
numberZahl
uuidUUID-String
position{ world = 'world', x = 0, y = 64, z = 0 }

Ein Name ohne Doppelpunkt wird in den Namespace des Programms gelegt. persistent ist standardmäßig false; true markiert den Speicherwert für die Serialisierung mit dem gespeicherten Zustand des Minds. Lies und schreibe Speicherwerte über c.memory:get(name), c.memory:set(name, value, ttl_ticks), c.memory:forget(name) und c.memory:contains(name). Das dritte Argument von set ist optional; wird es angegeben, läuft der Wert nach so vielen Ticks ab. Ein nicht deklarierter Name löst einen Fehler aus.


Sensoren​

Ein Sensor sammelt Informationen, meist in Speicherwerte. Sensoren laufen vor den Verhalten.

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
}
FeldTypStandardHinweise
idstringErforderlich.
intervalpositive Ganzzahl1Ticks zwischen den Durchläufen.
sensefunctionErforderlich. Erhält den Mind-Kontext.

Sensoren können c.actuator nicht verwenden; der Aufruf einer Aktuator-Methode aus einem Sensor löst einen Fehler aus.


Verhalten​

Ein Verhalten wirkt auf den Mob, solange es die von ihm deklarierten Steuerungen hält.

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
}
FeldTypStandardHinweise
idstringErforderlich.
priorityGanzzahl0Niedrigere Zahlen haben Vorrang.
controlsArraykeineSteuerungen, die dieses Verhalten belegt, solange es läuft.
can_startfunctionimmer trueMuss true oder false zurückgeben.
can_continuefunctionwie can_startMuss true oder false zurückgeben.
startfunctionkeineLäuft einmal, wenn das Verhalten startet.
tickfunctionErforderlich. Läuft jeden Tick, solange das Verhalten läuft.
stopfunctionkeinefunction(c, reason); läuft, wenn das Verhalten stoppt.

Lebenszyklus​

  1. Solange das Verhalten gestoppt ist, prüft es can_start. Gibt die Funktion true zurück und kann das Verhalten jede deklarierte Steuerung belegen, läuft start.
  2. In jedem Tick, in dem es läuft, einschließlich des Ticks, in dem es gestartet ist, läuft zuerst can_continue. Gibt die Funktion false zurück, stoppt das Verhalten mit dem Grund completed; andernfalls läuft tick.
  3. stop(c, reason) erhält einen der Werte completed, preempted, program_replaced, entity_removed, callback_failed oder handle_closed. Das Stoppen gibt die Steuerungen des Verhaltens frei und beendet die Bewegung, das Ziel oder den Angriff, den es hielt.

Ein Lua-Fehler in can_continue, start oder tick stoppt das Verhalten mit callback_failed. Gibt can_start oder can_continue etwas anderes als einen Boolean zurück, ist das ein Fehler. Die Konsole protokolliert für jeden Fehler Mind <program> callback <callback> failed with ....

Steuerungen​

SteuerungErlaubt
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_itemReserviert; der Lua-Aktuator hat keine Item-Methode.

Jede Steuerung gehört jeweils genau einem laufenden Verhalten. Ein Verhalten mit einer niedrigeren priority-Zahl nimmt einem Verhalten mit einer höheren Zahl die Steuerung ab; dieses stoppt dann mit dem Grund preempted. Eine Steuerung, die ein Verhalten mit einer niedrigeren Zahl hält, kann es nicht übernehmen. Bei gleicher Priorität erhält das Verhalten die Steuerung, dessen id alphabetisch zuerst kommt. Der Aufruf einer Aktuator-Methode ohne die zugehörige Steuerung löst einen Fehler aus. c.actuator:stop_all() stoppt nur, was das Verhalten hält.

In den mitgelieferten Modulen verwendet die Zielauswahl die Priorität 5, Nahkampfangriffe 10, die Verfolgung 20 und das ziellose Umherwandern 50. Die Verfolgung übernimmt die Bewegung vom Umherwandern also, sobald ein Ziel existiert.

Boss-Aktionen anfordern​

Ein Verhalten, das ai.controls.action hält, kann c.actions:request(identifier, payload) aufrufen. EliteMobs sendet die Anfrage über on_mind_action an die Lua-Powers des Bosses. Die Kennung muss ein kleingeschriebener Schlüssel mit Namespace und höchstens 128 Zeichen sein, etwa 'elitemobs:slam'. Die Nutzdaten enthalten höchstens 16 Einträge mit kleingeschriebenen Schlüsseln von höchstens 64 Zeichen, und String-Werte sind auf 256 Zeichen begrenzt; eine ungültige Kennung oder ungültige Nutzdaten lösen einen Fehler aus. Der Aufruf gibt accepted, deferred oder rejected zurück; Anfragen, die in einem Tick über max_action_requests des Programms hinausgehen, geben deferred zurück. Das Format der Nutzdaten steht unter Mind-Aktionskontext.


Budgets​

budget legt weiche Planungsgrenzen fest. Erreicht ein Mob in einem Tick eine davon, überspringt die Mind-Laufzeit die restlichen Callbacks dieses Mobs bis zum nächsten Tick. Ein bereits laufender Callback wird immer zu Ende ausgeführt.

SchlüsselStandardHinweise
callback_micros2000Ein Callback, der länger läuft, beendet die Callbacks des Mobs für diesen Tick.
entity_micros4000Gesamte Callback-Zeit pro Mob und Tick.
server_micros20000Die Callbacks des Mobs stoppen für diesen Tick, sobald alle Mind-Programme zusammen so viel Zeit verbraucht haben.
max_callbacks128Callbacks pro Mob und Tick.
max_action_requests8Aktionsanfragen pro Mob und Tick; höchstens 64.

Alle Werte müssen positiv sein, und callback_micros darf entity_micros nicht überschreiten, das wiederum server_micros nicht überschreiten darf.

runaway legt harte Grenzen fest, die einen einzelnen Callback unterbrechen:

SchlüsselStandardHinweise
cpu_micros50000CPU-Zeit des Threads pro Callback.
max_instructions50000Lua-Anweisungen pro Callback.

budget akzeptiert auch max_instructions, wie im mitgelieferten basic_melee.lua. Deklariere den Wert in budget oder in runaway, nicht in beiden. Ein Callback, der eine Runaway-Grenze überschreitet, schlägt wie jeder andere fehlerhafte Callback fehl.


Nächste Schritte​