Files
Viano-Core/doc/clientless/testbots.md
T

183 lines
11 KiB
Markdown

# Lua-gesteuerte Testbots
Testbots sind ein eigener Verbraucher des Clientless Player Core. Sie laden keine
Adventure-Profile, keinen Tagesplan, keine Versorgungs-, Gruppen- oder Kampf-KI.
Die Adventure-Population verwaltet diese Charaktere nicht. Ausführung erfolgt im
Worldthread durch einen getrennten Lua-Zustand pro Testlauf. Normale Serverphysik
und Prüfungen echter Spieleraktionen bleiben wirksam; administrative Vorbereitung
ist davon getrennt. DungeonBots bleiben unabhängig.
## Einstieg und Ergebnis
```ini
Clientless.TestBot.Enabled = 1
Clientless.TestBot.Root = "/path/to/testbots"
Clientless.TestBot.TimeoutSeconds = 300
```
Der Root enthält `requests`, `scripts`, `results`, `archive` und `registry`.
Keine Installation und kein Neustart für neue Lua-Testskripte erforderlich.
Dateiaufträge zuerst außerhalb von `requests` schreiben und atomar nach
`requests/<eindeutige-id>.lua` verschieben. Alternativ:
```text
clientless test pause
clientless test resume
clientless test run <eindeutige-id> <skriptname>
clientless test submit <eindeutige-id> <base64-lua>
clientless test status <id>
clientless test result <id>
clientless test cancel <id>
```
`run` lädt `scripts/<skriptname>.lua`. `submit` nimmt bis64KiB Lua entgegen und
stellt es in die Dateiwarteschlange. Die vorhandenen GM-/SOAP-Rechte schützen
diesen administrativen Einstieg; es wird kein neuer Netzwerkdienst geöffnet.
Skripte sind administrative Testaufträge für vertrauenswürdige Betreiber.
Ein Test läuft zur Zeit, kann aber mehrere Bots steuern. Dateiaufträge warten;
ihre Reihenfolge ist nicht garantiert. Abhängige Fälle erst nach abgeschlossenem
Vorgänger einreichen.
`pause` hält die Annahme neuer Läufe an; laufende Tests werden separat mit
`cancel` beendet. Vor Builds Testannahme pausieren, aktive Tests beenden und
dann den sicheren Logout aller Clientless-Sessions abwarten.
IDs werden nie wiederverwendet. `results/<id>.json` wird per atomarem Umbenennen
veröffentlicht: Schema1, Run-ID, Zustand, Startzeit, Dauer, Prüfungs-/Fehlerzahl,
Fehlermeldung, Bot-GUIDs und Beobachtungen. Terminalzustände sind `passed`,
`failed`, `cancelled`, `timed_out`, `interrupted`. Bei Neustart werden zuvor
laufende Tests als `interrupted` markiert und nicht still wiederholt.
`record`/`check` publizieren mit dem nächsten Yield bzw. Abschluss.
Ende/Fehler/Abbruch fordert den Logout ausschließlich der zum Lauf gehörenden
Testbots an. Charaktere und deren regulär gespeicherter Quest-/Inventarstand
bleiben zur Untersuchung und Wiederverwendung erhalten. Eine Ausgabe `passed`
bedeutet nur, dass das Skript ohne Fehler und ohne fehlgeschlagene `check`-Prüfung
endete; die Aussagekraft hängt von den tatsächlich formulierten Prüfungen ab.
Der Python-Adapter kann über vorhandenes SOAP ohne tmux-/Datenbankzugriff arbeiten:
```bash
# TESTBOT_PASSWORD über die Umgebung des ausführenden Systems bereitstellen.
python3 tools/testbots/client.py quest-001 case.lua \
--endpoint http://127.0.0.1:7878/ --user operator --output result.json
```
SOAP bleibt standardmäßig deaktiviert; der Adapter verwendet die vorhandene
Trinity-SOAP-Konfiguration und Berechtigungen. Dateieingang und lokale Konsole
funktionieren unabhängig von SOAP. Exitcode0: passed,1: anderer Terminalzustand,
2: Übertragungs-/Clientfehler. `--timeout` setzt die Client-Wartefrist. Ein Clienttimeout beendet den serverseitigen Lauf
nicht; er kann anschließend abgefragt oder abgebrochen werden.
## Charakterinitialisierung vollständig in Lua
```lua
local bot = testbot.create(account_id, "Testrion", 1, 1, 10, 0)
-- Parameter: normaler Testaccount, Name, Rasse, Klasse, Level, Geschlecht.
-- Rückkehr erst nach asynchronem Erzeugen und abgeschlossener Anmeldung.
testbot.level(bot, 10)
testbot.clear_slot(bot, 15) -- vorhandene Haupthand bewusst entfernen
assert(testbot.equip(bot, 15, 25))
testbot.stat(bot, 0, 100) -- Grundwert Stärke; 0..4: Stärke bis Willenskraft
-- Lebens-/Ressourcenwerte zuletzt setzen, nach Level/Ausrüstung/Grundwerten:
testbot.stats(bot, 5000, 100)
assert(testbot.teleport(bot, 0, -8912, -138, 80.9, 0))
testbot.await(function() return testbot.online(bot) end, 10000, "Teleport")
testbot.record("start", testbot.state(bot))
testbot.check(testbot.state(bot).level == 10, "Level vorbereitet")
```
Charaktererzeugung nutzt die generische native CharacterFactory, keine
Adventure-Generierung. Account muss normal sein und einen freien Charakterslot
haben; Rasse/Klasse/Name müssen für den Core gültig sein. Ein erzeugter Charakter
wird im Testbot-Register hinterlegt. Später `testbot.login(guid)` verwenden.
Nur registrierte Testcharaktere dürfen angemeldet werden; fremde Spieler und
AdventureBots werden nicht übernommen. Keine vorhandenen Figuren werden geklont.
Aufgabenspezifische Stats werden bei Bedarf in jedem Lauf erneut gesetzt.
## Lua-API
Alle Bot-GUIDs stammen aus `create` oder `login`. Jede Aktion überprüft die
Zugehörigkeit zum aktuellen Lauf. Nach Teleports auf `online` warten.
| Funktion | Wirkung / Rückgabe |
|---|---|
| `create(account,name,race,class,level,gender)` | Charakter erzeugen und anmelden; GUID |
| `login(guid)` | Registrierten Testcharakter anmelden, auf Login warten; GUID |
| `online(guid)` | Anmeldung und Teleport abgeschlossen |
| `wait(ms)` | Coroutine abgeben,0..60000ms; blockiert den Server nicht |
| `await(predicate,timeout_ms,label)` | Bedingung mit echter Laufzeitfrist prüfen |
| `now_ms()` | Monotone Millisekunden seit Laufbeginn |
| `state(guid)` | Map,XYZ,Level,HP/Ressource,alive/combat/moving/transport (0/1) |
| `teleport(guid,map,x,y,z,o)` | Administrativer nativer Teleport; bool |
| `level(guid,level)` | Level1..85 samt Talentgrundinitialisierung |
| `stat(guid,index,value)` | Grundwert0..4 über natives Stats-System setzen |
| `stats(guid,max_hp,max_power?)` | Lebens-/aktuelle Klassenressource auffüllen |
| `learn(guid,spell)` | Vorbereitung: einen gültigen Zauber lernen |
| `item(guid,item,count)` | Vorbereitung: Inventargegenstände bereitstellen; bool |
| `clear_slot(guid,slot)` | Vorbereitung: Gegenstand im Ausrüstungs-/Taschenslot0..22 löschen |
| `equip(guid,slot,item)` | Vorbereitung: leeren Slot nativ ausrüsten; bool |
| `item_count(guid,item)` | Aktuelle Inventaranzahl ohne Bank |
| `move(guid,x,y,z)` | Native MMAP-/MovePoint-Bewegung ohne Gebietskatalog; bool |
| `stop(guid)` | Angriff und aktuelle Bewegung stoppen |
| `creature(guid,spawn)` / `object(guid,spawn)` | Sichtbarer geladener Spawn; vollständige GUID als String oder nil |
| `target_state(guid,target)` | Geladenes Unit-Ziel: HP,alive,Distanz,XYZ; sonst nil |
| `face(guid,target)` | Zum geladenen Ziel ausrichten |
| `attack(guid,target)` | Nativen Nahkampfangriff beginnen; bool |
| `cast(guid,spell,target?)` | Nicht getriggerter nativer Cast, Standardziel selbst; SpellCastResult (Erfolg255) |
| `use_item(guid,bag,slot,spell,target?)` | Nativer Spieler-UseItem-Handler |
| `use(guid,object)` | Nativer GameObject-Klick |
| `gossip(guid,target,menu,option,code?)` | Native Gossip-Auswahl, optional Texteingabe |
| `hello(guid,target)` | Nativen Questgeberdialog öffnen |
| `accept(guid,target,quest)` | Normale Questannahme über Spieler-Handler |
| `abandon(guid,quest)` | Quest über nativen Questlog-Handler abbrechen |
| `request_reward(guid,target,quest)` | Nativen Abgabedialog anfordern; erzwingt keine Zielerfüllung |
| `reward(guid,target,quest,choice)` | Native Belohnungswahl0..5; Eligibility bleibt erhalten |
| `quest(guid,quest)` | Status und rewarded; NONE0,COMPLETE1,INCOMPLETE3,FAILED5 |
| `objectives(guid,quest)` | Vier native Questlog-Zähler (Lua-Indizes1..4) |
| `loot(guid,target)` | Native Lootöffnung einer Kreaturenleiche; Truhen über den passenden Opening-Zauber öffnen |
| `loot_item(guid,slot)` / `loot_release(guid,target)` | Lootgegenstand aufnehmen / Fenster schließen |
| `record(label,value)` | JSON-Beobachtung; Skalare oder begrenzte Tabellen |
| `check(condition,label)` | Prüfung protokollieren; false macht Endergebnis failed |
Zusätzliche Eingaben und Beobachtungen für mehrstufige Questtests:
| Funktion | Wirkung / Rückgabe |
|---|---|
| `skill(guid,id,value,max)` | Vorbereitung: gültige Fertigkeit nativ setzen; bool |
| `remove_item(guid,item,count)` | Vorbereitung: Gegenstände entfernen |
| `inventory(guid)` | Gegenstände mit bag/slot/item/count, GUID-String und spells[{id,trigger}] |
| `nearest_creature(guid,entry,radius)` / `nearest_object(guid,entry,radius)` | Sichtbares Ziel innerhalb maximal100Yards; GUID-String oder nil |
| `npc_hello(guid,target)` | Nativen Gossip-Dialog öffnen |
| `menu(guid)` | Aktuelle id/source_guid und options[{id,text,action,coded}] |
| `spellclick(guid,target)` | Nativer NPC-Zauberklick, etwa Questfahrzeuge |
| `exit_vehicle(guid)` | Normalen Fahrzeugausstieg anfordern |
| `request_cast(guid,spell,target?)` | Normales Client-Cast-Paket, Unit-/GO-Ziel automatisch |
| `request_cast_at(guid,spell,x,y,z)` | Client-Cast mit Bodenziel |
| `use_item_at(guid,bag,slot,spell,x,y,z)` | Itembenutzung mit Bodenziel |
| `area_trigger(guid,id)` | Nativer AreaTrigger-Handler; echte Position wird geprüft |
`state` enthält zusätzlich area/zone, loot_guid, vehicle_entry/vehicle_guid und
mover_guid. `target_state` enthält zusätzlich entry/level/max_health. Ein Testbot
bestätigt seine erlaubte kontrollierte Einheit über den normalen ActiveMover-
Handler, auch beim Wechsel in ein Fahrzeug. Das umgeht keine Bewegungsrechte.
Ein GameObject-Klick öffnet nicht automatisch jede Truhe: Dafür muss wie beim
Client der zu Lock.dbc passende Opening-Zauber verwendet werden. Native
Unterbrechungen durch Kampf gelten auch für solche Zauber.
GUID-Strings von `creature`/`object` umgehen Lua-Präzisionsverlust bei64Bit-GUIDs.
Aktionen mit `void`/ohne Rückgabe bestätigen nur den Aufruf: anschließend Status,
Inventar oder Questfortschritt prüfen. Ein versendeter Handleraufruf allein ist
kein Erfolgsnachweis. Quests werden nicht durch künstliche Credits abgeschlossen.
Normale Casts prüfen Kosten, Reichweite, Cooldowns usw.; Vorbereitung darf diese
Bedingungen gezielt durch Level, Stats, Items und gelernte Zauber herstellen.
Native MMAP-Bewegung braucht begehbaren Boden; Teleport steht unabhängig davon
zur Verfügung. Das konkrete Questbeispiel steht unter
`src/server/scripts/Custom/clientless/testbots/quest_accept.lua`.
Lua hat pro Lauf8MiB und pro Ausführungsschritt100000Instruktionen, danach muss
es mit `wait`/`await` abgeben. Keine Datei-/OS-/Package-/Debug-Funktionen und kein
Abfangen des Ressourcenabbruchs durch `pcall`/eigene Coroutines. Maximal1000
Beobachtungen; Tabellen werden für Ergebnisse begrenzt serialisiert. Laufzeit
ist per Config1..86400Sekunden einstellbar. Das ist eine administrative
Testumgebung, keine Isolation für beliebigen fremden Code oder Corefehler.