Lua-Scripting: Verhaltensprogramme
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.
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 namensmodulesist ein Modul. Jede andere.lua-Datei ist ein Programm. - Ein Boss verweist auf ein Programm über dessen Pfad relativ zu
behaviors/, zum Beispielbasic_melee.luaoderguards/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>oderCould not load behavior module <file>mit dem Grund. - Programm- und Modul-IDs verwenden den Namespace
elitemobs, etwaelitemobs:behavior/basic_melee. IDs bestehen aus Kleinbuchstaben und dürfena-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
}
}
| Feld | Typ | Hinweise |
|---|---|---|
id | string | Erforderlich. ID mit Namespace, elitemobs:... für Dateien in behaviors/. |
revision | positive Ganzzahl | Erforderlich. |
modules | Array aus Strings | Optional. 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, behaviors | Tabellen | Optional. Ein Programm kann eigene deklarieren, im selben Format wie ein Modul. |
budget | Tabelle | Optionale weiche Planungsgrenzen. Siehe Budgets. |
runaway | Tabelle | Optionale harte Grenzen pro Callback. Siehe Budgets. |
Moduldatei
Eine Moduldatei gibt ai.module { ... } zurück und bündelt wiederverwendbare Speicherwerte, Sensoren und Verhalten:
| Feld | Typ | Hinweise |
|---|---|---|
id | string | Erforderlich. Modul-ID mit Namespace. |
revision | positive Ganzzahl | Erforderlich. |
dependencies | Array aus Strings | Optional. Andere Modul-IDs, die dieses Modul benötigt. |
memories | Tabelle | Optional. Siehe Speicherwerte. |
sensors | Array | Optional. Einträge vom Typ ai.sensor { ... }. |
behaviors | Array | Optional. 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'
}
| Typ | Lua-Wert |
|---|---|
string | String |
boolean | Boolean |
integer | Ganzzahl |
number | Zahl |
uuid | UUID-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
}
| Feld | Typ | Standard | Hinweise |
|---|---|---|---|
id | string | Erforderlich. | |
interval | positive Ganzzahl | 1 | Ticks zwischen den Durchläufen. |
sense | function | Erforderlich. 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
}
| Feld | Typ | Standard | Hinweise |
|---|---|---|---|
id | string | Erforderlich. | |
priority | Ganzzahl | 0 | Niedrigere Zahlen haben Vorrang. |
controls | Array | keine | Steuerungen, die dieses Verhalten belegt, solange es läuft. |
can_start | function | immer true | Muss true oder false zurückgeben. |
can_continue | function | wie can_start | Muss true oder false zurückgeben. |
start | function | keine | Läuft einmal, wenn das Verhalten startet. |
tick | function | Erforderlich. Läuft jeden Tick, solange das Verhalten läuft. | |
stop | function | keine | function(c, reason); läuft, wenn das Verhalten stoppt. |
Lebenszyklus
- Solange das Verhalten gestoppt ist, prüft es
can_start. Gibt die Funktiontruezurück und kann das Verhalten jede deklarierte Steuerung belegen, läuftstart. - In jedem Tick, in dem es läuft, einschließlich des Ticks, in dem es gestartet ist, läuft zuerst
can_continue. Gibt die Funktionfalsezurück, stoppt das Verhalten mit dem Grundcompleted; andernfalls läufttick. stop(c, reason)erhält einen der Wertecompleted,preempted,program_replaced,entity_removed,callback_failedoderhandle_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
| Steuerung | Erlaubt |
|---|---|
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 | Reserviert; 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üssel | Standard | Hinweise |
|---|---|---|
callback_micros | 2000 | Ein Callback, der länger läuft, beendet die Callbacks des Mobs für diesen Tick. |
entity_micros | 4000 | Gesamte Callback-Zeit pro Mob und Tick. |
server_micros | 20000 | Die Callbacks des Mobs stoppen für diesen Tick, sobald alle Mind-Programme zusammen so viel Zeit verbraucht haben. |
max_callbacks | 128 | Callbacks pro Mob und Tick. |
max_action_requests | 8 | Aktionsanfragen 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üssel | Standard | Hinweise |
|---|---|---|
cpu_micros | 50000 | CPU-Zeit des Threads pro Callback. |
max_instructions | 50000 | Lua-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
- Bosse erstellen: behavior -- ein Programm für einen Boss auswählen
- Lua-API-Referenz: Kontext eines Mind-Programms -- alle Methoden von
c.memory,c.perception,c.actuatorundc.actions - Hooks & Lebenszyklus --
on_mind_actionund die anderen Hooks für Lua-Powers
