13 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>
clientless test breathing <guid> limited|unlimited|status
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 sowie movement_flags, animation_tier, hover_animation, mounted/flying/swimming/falling/in_water/can_swim (0/1), swim_speed und travel_state |
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 |
travel(guid,x,y,z) |
Normale CPC-Navigation samt Gebiets-/Pfad-/Wasserprüfung; bool und Diagnosetext |
liquid(guid,x,y,z) |
Native Flüssigkeitsprobe mit status, surface, bottom, liquid/safe/swimming (0/1) |
move(guid,x,y,z) |
Native MMAP-/MovePoint-Bewegung ohne Gebietskatalog; bool |
flight(guid,x,y,z,following?) |
Nativer CPC-Flug, mit following=true bewegliches Flugziel einschließlich Halten; bool und Fehlertext. Normale Profil-/Mount-/Gebietsprüfung bleibt aktiv |
land_flight(guid) |
Folgenden Flug über die normale geprüfte Landung beenden; 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.
Atemluft
AdventureBots haben unbegrenzte Atemluft. Registrierte Testbots verwenden standardmäßig die normale begrenzte Atemluft (drei Minuten ohne Modifikatoren), auch bei einem erhöhten Account-Sicherheitsrang. Echte Unterwasseratemzauber und Atemdauer-Modifikatoren wirken weiterhin nativ. Spieler mit Netzwerkclient bleiben unverändert.
clientless test breathing <guid> limited|unlimited|status schaltet die Atemluft für einen registrierten Testbot oder zeigt dessen gespeicherte Einstellung. Die Einstellung bleibt über Logins und Serverneustarts erhalten (registry/<guid>.breathing); ohne Datei gilt limited. Auch manuelle clientless Logins registrierter Testbots übernehmen die Einstellung. Die Testbot-Steuerung muss aktiviert sein.
Lua: testbot.breathing(guid, true) aktiviert begrenzte Atemluft, false unbegrenzte; ohne zweiten Parameter wird die gespeicherte Einstellung abgefragt. Rückgabewert: true bedeutet begrenzt. Nur dem Testlauf zugeordnete Bots sind zulässig. testbot.state liefert breathing_limited, breath_max_ms, breath_remaining_ms, underwater (letzter nativer Tick; -1 = deaktivierter Timer). Unbegrenzt injiziert keinen Zauber und verändert weder Ermüdung noch Lava-/sonstigen Umweltschaden.