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.