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

11 KiB

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

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:

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:

# 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

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.