From 04aa87fcfc13dcd6fef5387f1bf64cb22a95af30 Mon Sep 17 00:00:00 2001 From: nico Date: Wed, 28 Oct 2026 11:30:47 +0100 Subject: [PATCH] Add profile-driven AdventureBot character generation --- doc/clientless/SESSION-RESUME.md | 76 + .../2026-10-28-generator/build.txt | 4 + .../2026-10-28-generator/equipment-test.txt | 1 + .../2026-10-28-generator/factory-test.txt | 1 + .../generator-db-helper.py | 30 + .../historical-auth-start-failure.txt | 27 + .../2026-10-28-generator/native-audit.txt | 176 ++ .../2026-10-28-generator/native-details.txt | 42 + .../2026-10-28-generator/ownership-test.txt | 1 + .../2026-10-28-generator/policy-test.txt | 1 + .../2026-10-28-generator/skill-test.txt | 1 + .../2026-10-28-generator/startup-and-live.txt | 1880 +++++++++++++++++ .../2026-10-28-generator/tests.txt | 63 + .../2026-10-28-generator/validation.txt | 17 + doc/clientless/generator.md | 151 ++ .../2026_10_28_00_adventure_generator.sql | 16 + src/server/game/Entities/Creature/Trainer.h | 3 +- .../game/World/AdventureGeneratorMgr.cpp | 416 ++++ src/server/game/World/AdventureGeneratorMgr.h | 22 + .../game/World/AdventureGeneratorPolicy.h | 105 + src/server/game/World/AdventureSupplyMgr.cpp | 7 + src/server/game/World/AdventureSupplyMgr.h | 1 + .../game/World/ClientlessCharacterFactory.cpp | 100 + .../game/World/ClientlessCharacterFactory.h | 27 + src/server/game/World/ClientlessLuaEngine.cpp | 9 +- src/server/game/World/ClientlessLuaEngine.h | 1 + .../scripts/Custom/viano_bot_commands.cpp | 4 + .../test_adventure_generator_equipment.py | 39 + .../test_adventure_generator_ownership.py | 64 + .../test_adventure_generator_policy.py | 50 + .../clientless/test_adventure_supply_items.py | 2 + .../test_clientless_character_factory.py | 90 + .../test_clientless_skill_preparation.py | 40 + 33 files changed, 3465 insertions(+), 2 deletions(-) create mode 100644 doc/clientless/checkpoints/2026-10-28-generator/build.txt create mode 100644 doc/clientless/checkpoints/2026-10-28-generator/equipment-test.txt create mode 100644 doc/clientless/checkpoints/2026-10-28-generator/factory-test.txt create mode 100644 doc/clientless/checkpoints/2026-10-28-generator/generator-db-helper.py create mode 100644 doc/clientless/checkpoints/2026-10-28-generator/historical-auth-start-failure.txt create mode 100644 doc/clientless/checkpoints/2026-10-28-generator/native-audit.txt create mode 100644 doc/clientless/checkpoints/2026-10-28-generator/native-details.txt create mode 100644 doc/clientless/checkpoints/2026-10-28-generator/ownership-test.txt create mode 100644 doc/clientless/checkpoints/2026-10-28-generator/policy-test.txt create mode 100644 doc/clientless/checkpoints/2026-10-28-generator/skill-test.txt create mode 100644 doc/clientless/checkpoints/2026-10-28-generator/startup-and-live.txt create mode 100644 doc/clientless/checkpoints/2026-10-28-generator/tests.txt create mode 100644 doc/clientless/checkpoints/2026-10-28-generator/validation.txt create mode 100644 doc/clientless/generator.md create mode 100644 sql/custom/characters/2026_10_28_00_adventure_generator.sql create mode 100644 src/server/game/World/AdventureGeneratorMgr.cpp create mode 100644 src/server/game/World/AdventureGeneratorMgr.h create mode 100644 src/server/game/World/AdventureGeneratorPolicy.h create mode 100644 src/server/game/World/ClientlessCharacterFactory.cpp create mode 100644 src/server/game/World/ClientlessCharacterFactory.h create mode 100644 tests/clientless/test_adventure_generator_equipment.py create mode 100644 tests/clientless/test_adventure_generator_ownership.py create mode 100644 tests/clientless/test_adventure_generator_policy.py create mode 100644 tests/clientless/test_clientless_character_factory.py create mode 100644 tests/clientless/test_clientless_skill_preparation.py diff --git a/doc/clientless/SESSION-RESUME.md b/doc/clientless/SESSION-RESUME.md index 412f3fbcb3..2353bec08c 100644 --- a/doc/clientless/SESSION-RESUME.md +++ b/doc/clientless/SESSION-RESUME.md @@ -1,3 +1,79 @@ +# Aktuell: Automatische AdventureBot-Erzeugung abgeschlossen + +Stand28.10.2026 auf feature/clientless-player-core. Nutzer beauftragte einen +Generator für neue Bots mit RP-Namen/Profilen, passenden Fähigkeiten, Berufen +und Ausrüstung; Kennzeichnung für spätere temporäre Rotation. Nutzer hat den +lokalen Commit beauftragt; kein Push. SQL-Migration2026_10_28_00_adventure_generator.sql +wegen SQL-Ignore bereits gezielt vorgemerkt; Test-Charakter-DB zweimal angewendet. + +ClientlessCharacterFactory ist generische native Infrastruktur ohne Adventure- +Abhängigkeit: Player::Create, validierte zufällige Optik, Geburt auf Zielstufe +via native Initialisierung statt GiveLevel-Belohnungen, Vorbereitungscallback, +gemeinsamer Save+Profil+Homebind+Ownership-Commit; Cache/Hook erst nach Erfolg. +Neue AdventureGeneratorMgr/Policy: zehn native legale Klassen-Archetypen, +zwölf Völker, kulturelle Namensgrammatiken, Herkunft/Beruf/Personalityprofile. +Profile liegen in Character-DB und werden als generated: im vorhandenen +Lua-Sandboxparser geladen. Handgeschriebene Namensprofile haben Vorrang und ihre +Namen werden bei Erzeugung reserviert. Keine Laufzeitdateien im Profilquellbaum. + +Wichtige Live-Fixes: Berufs-Lernzauber mit Effekt44 wirken vor Welteintritt +nicht zuverlässig. Native SkillRaceClass/SkillTiers + Player::SetSkill bereiten +legale volle Ränge vor; Hilfsfunktion verweigert Online-Spieler. Online- +Selbstversorgung unverändert. Rollen-Ausrüstung prüft native Equipregeln, +Werte, temporäre/reputations-/gebietsgebundene Items, Mana-/Zauber-/physische +Effekte und keine doppelten ausgerüsteten Einträge. Ersetzte Items bleiben in +Taschen. Native Trainer::CanTeachSpell lediglich read-only öffentlich gemacht. +Arms nutzt vorhandenen Talentplan, andere Klassen gültigen Tier-Basisplan. + +Befehle: clientless generate create [level faction archetype +persistent|temporary seed]; optionale fünf Parameter nur gemeinsam. Kurzform +level60, any/any, persistent. Count1..10, level10..85, DK>=55, Account normal +und ohne Netzwerksitzung, normale Realm-Charaktergrenze. Beispiel für Tests: +clientless generate create 6 1 60 alliance warrior temporary 42. +list/profile/login/logout/equip/retire . Erzeugung offline; login explizit. +equip nur eigener online/lebender/ruhiger Bot, native Checks, alte Items behalten. +retire nur eigener offline-temporärer Bot, markiert nur Metadaten, keine Löschung. +Automatische Rotation, Löschung, Accountanlage und Populationssteuerung später. +Vollständige Doku: doc/clientless/generator.md. + +Debug-j10 erfolgreich installiert,62/62 Tests bestanden. Testserver88947 läuft, +frische Ready + Port8085 verifiziert. Vier neue Stufe60-Bots online und mit +individuellen DB-Profilen nach Neustart/Relog wieder geladen: +-29 Aelion, Nachtelf-Krieger, Teldrassil/poi_guard_111. +-30 Zanzan, Troll-Krieger, Durotar/poi_tele_823. +-31 Lenara, Worgen-Magierin, Elwynn/poi_npc_280710. +-32 Aldren, Untoter-Todesritter, Silberwald/poi_npc_321922. +Native Talentbäume746/746/851/400, Talentzeilen13/13/14/12, +Zauberanzahl80/80/87/64. Alle vier Nebenberufe und vier22er-Glutseidenstofftaschen, +alle fünf Vorratsstacks20. Primär: Krieger/DK Mining450+Smith375; Magierin +Tailoring375+Enchant375. Werte stammen aus gegenwärtigen nativen Trainerdaten. +Ausrüstungsauffrischung29..32 nativ erfolgreich gespeichert, alte Items behalten. +NPC-Szenen und lokale soziale Intents laufen; keine visuelle Abnahme behauptet. + +Eigene frühere Testcharaktere27 Dagri und28 Jarana sind retired/offline, mit +vollständig erhaltenen Daten. Retire26 verweigert (kein generierter Besitz), +Login27 verweigert (retired). Inventarzuordnungen/Entries für21 und26 gegenüber +vorher bytegleich (Hash im Checkpoint). Arthoran wurde für Wartung sauber +abgemeldet und bleibt OFFLINE; keine Profiloverrides, keine Teleportierung. +Bestehende Sourceprofile unverändert. Account6 enthält nun7 Charaktere, normale +Realmgrenze beachten; archivierte Charaktere belegen weiterhin Plätze. + +Ein erster Start traf bekannten temporären Auth-DB-Ausfall10.0.0.1. Nicht +reagierender Fehlstart76094 perSIGTERM beendet, zweiter Versuch erfolgreich; +keine VPS-/Produktionskonfiguration geändert. Spätere Starts erfolgreich. +Für alle neuen Klassen gilt weiter: bekannte Fähigkeiten sind keine fertige +Klassen-Kampfrotation; außer Krieger bisher grundlegender Autoangriff. Rollen, +Bot-Gruppenbildung, PvP-Erstangriffe und Rotation bleiben spätere Aufgaben. +Belege: doc/clientless/checkpoints/2026-10-28-generator/. Aktuelle Logs +/tmp/vcore-generator-{build-refit,install-refit}.log; DB-Helfer +/tmp/vcore-generator-db.py audit/details/compare (keine Zugangsdaten ausgeben). + +Nutzer hat alle CMake- und tmux-Befehle sowie git add/commit auf diesem Branch +ausdrücklich dauerhaft autorisiert. Keine erneuten inhaltlichen Rückfragen; +technische Sandboxregeln bleiben maßgeblich. Kein Push ohne ausdrücklichen Auftrag. + +--- + # Aktuell: Interaktionen und spielergeführte Gruppen umgesetzt Stand 28.10.2026. Nutzer beauftragte Umsetzung nach gemeinsamer Planung. diff --git a/doc/clientless/checkpoints/2026-10-28-generator/build.txt b/doc/clientless/checkpoints/2026-10-28-generator/build.txt new file mode 100644 index 0000000000..5ae1ba5952 --- /dev/null +++ b/doc/clientless/checkpoints/2026-10-28-generator/build.txt @@ -0,0 +1,4 @@ +[1/4] cd /home/wow_admin/wow_cata_server/source/TrinityCore && /usr/bin/cmake -DBUILDDIR="/home/wow_admin/wow_cata_server/build/debug" -P /home/wow_admin/wow_cata_server/source/TrinityCore/cmake/genrev.cmake /home/wow_admin/wow_cata_server/build/debug +[2/4] Building CXX object src/server/game/CMakeFiles/game.dir/World/AdventureGeneratorMgr.cpp.o +[3/4] Linking CXX static library src/server/game/libgame.a +[4/4] Linking CXX executable src/server/worldserver/worldserver diff --git a/doc/clientless/checkpoints/2026-10-28-generator/equipment-test.txt b/doc/clientless/checkpoints/2026-10-28-generator/equipment-test.txt new file mode 100644 index 0000000000..a3e3669705 --- /dev/null +++ b/doc/clientless/checkpoints/2026-10-28-generator/equipment-test.txt @@ -0,0 +1 @@ +Production equipment affinity: mana-only/direct/triggered procs, caster/physical stat effects and bounded cycles passed diff --git a/doc/clientless/checkpoints/2026-10-28-generator/factory-test.txt b/doc/clientless/checkpoints/2026-10-28-generator/factory-test.txt new file mode 100644 index 0000000000..11ac8646af --- /dev/null +++ b/doc/clientless/checkpoints/2026-10-28-generator/factory-test.txt @@ -0,0 +1 @@ +Production native factory: character/profile atomicity, failed preparation/commit, cache publication order, account/security/name/cap gates passed diff --git a/doc/clientless/checkpoints/2026-10-28-generator/generator-db-helper.py b/doc/clientless/checkpoints/2026-10-28-generator/generator-db-helper.py new file mode 100644 index 0000000000..50e89e2450 --- /dev/null +++ b/doc/clientless/checkpoints/2026-10-28-generator/generator-db-helper.py @@ -0,0 +1,30 @@ +from pathlib import Path +import os,re,subprocess,sys +root=Path('/home/wow_admin/wow_cata_server/source/TrinityCore') +conf=Path('/home/wow_admin/wow_cata_server/server/debug/etc/worldserver.conf').read_text() +def query(kind,sql): + h,port,u,p,db=re.search(r'^'+kind+r'DatabaseInfo\s*=\s*"([^"]+)"',conf,re.M).group(1).split(';') + env=os.environ.copy();env['MYSQL_PWD']=p + r=subprocess.run(['mysql','--protocol=TCP','--host='+h,'--port='+port,'--user='+u,'--database='+db,'--batch','--connect-timeout=10'],input=sql,text=True,capture_output=True,env=env) + if r.returncode:raise SystemExit(r.stderr.replace(p,'[redacted]')) + return r.stdout +mode=sys.argv[1] +if mode=='inspect': + print(query('World',"SHOW COLUMNS FROM trainer; SHOW COLUMNS FROM trainer_spell; SHOW COLUMNS FROM vcore_clientless_world_point; SELECT l.point_key,l.display_name,l.faction,w.map,w.position_x,w.position_y,w.position_z,w.category FROM vcore_adventure_location l JOIN vcore_clientless_world_point w ON w.point_key=l.point_key WHERE w.enabled=1 AND w.category='city' ORDER BY l.point_key LIMIT 25;")) + print(query('Character','SELECT account,COUNT(*) AS characters FROM characters WHERE account=6 GROUP BY account;')) +elif mode=='migrate': + print(query('Character',(root/'sql/custom/characters/2026_10_28_00_adventure_generator.sql').read_text())) +elif mode=='baseline': + import hashlib + text=query('Character',"SELECT ci.guid,ci.bag,ci.slot,ci.item,i.itemEntry FROM character_inventory ci JOIN item_instance i ON i.guid=ci.item WHERE ci.guid IN (21,26) ORDER BY ci.guid,ci.bag,ci.slot,ci.item;") + Path('/tmp/vcore-generator-existing-items.tsv').write_text(text) + print('Existing item ownership baseline',hashlib.sha256(text.encode()).hexdigest()) +elif mode=='compare': + import hashlib + text=query('Character',"SELECT ci.guid,ci.bag,ci.slot,ci.item,i.itemEntry FROM character_inventory ci JOIN item_instance i ON i.guid=ci.item WHERE ci.guid IN (21,26) ORDER BY ci.guid,ci.bag,ci.slot,ci.item;") + print('Existing item ownership unchanged:',text==Path('/tmp/vcore-generator-existing-items.tsv').read_text(),hashlib.sha256(text.encode()).hexdigest()) +elif mode=='details': + print(query('Character',"SELECT c.guid,c.name,c.level,c.talentTree,c.map,c.instance_id,c.position_x,c.position_y,c.position_z,c.health,c.power1,g.state,g.persistent FROM characters c JOIN vcore_adventure_generated_bot g ON g.character_guid=c.guid ORDER BY c.guid; SELECT ci.guid,ci.slot,i.itemEntry FROM character_inventory ci JOIN item_instance i ON i.guid=ci.item JOIN vcore_adventure_generated_bot g ON g.character_guid=ci.guid WHERE g.state='ready' AND ci.bag=0 AND ci.slot IN (12,13) ORDER BY ci.guid,ci.slot; SELECT ci.guid,i.itemEntry,SUM(i.count) AS count FROM character_inventory ci JOIN item_instance i ON i.guid=ci.item JOIN vcore_adventure_generated_bot g ON g.character_guid=ci.guid WHERE g.state='ready' AND i.itemEntry IN (21991,22829,22832,27854,28399) GROUP BY ci.guid,i.itemEntry ORDER BY ci.guid,i.itemEntry; SELECT g.character_guid,LENGTH(g.profile_lua) AS profile_bytes,c.name,CONCAT('identity name matches: ',g.profile_lua LIKE CONCAT('%name = ',CHAR(34),c.name,CHAR(34),'%')) AS identity_check FROM vcore_adventure_generated_bot g JOIN characters c ON c.guid=g.character_guid WHERE g.state='ready' ORDER BY g.character_guid;")) +elif mode=='audit': + print(query('Character',"SELECT g.character_guid,c.name,c.race,c.class,c.gender,c.level,c.online,g.archetype,g.persistent,g.state,g.seed,c.health FROM vcore_adventure_generated_bot g JOIN characters c ON c.guid=g.character_guid ORDER BY g.character_guid; SELECT ci.guid,ci.slot,i.itemEntry,i.durability FROM character_inventory ci JOIN item_instance i ON i.guid=ci.item JOIN vcore_adventure_generated_bot g ON g.character_guid=ci.guid WHERE ci.bag=0 AND ci.slot<23 ORDER BY ci.guid,ci.slot; SELECT s.guid,s.skill,s.value,s.max FROM character_skills s JOIN vcore_adventure_generated_bot g ON g.character_guid=s.guid WHERE s.skill IN (129,185,356,794,164,186,182,171,197,333,165,393) ORDER BY s.guid,s.skill; SELECT t.guid,COUNT(*) AS talent_spells FROM character_talent t JOIN vcore_adventure_generated_bot g ON g.character_guid=t.guid GROUP BY t.guid; SELECT s.guid,COUNT(*) AS spells FROM character_spell s JOIN vcore_adventure_generated_bot g ON g.character_guid=s.guid GROUP BY s.guid;")) +else:raise SystemExit('Unknown mode') diff --git a/doc/clientless/checkpoints/2026-10-28-generator/historical-auth-start-failure.txt b/doc/clientless/checkpoints/2026-10-28-generator/historical-auth-start-failure.txt new file mode 100644 index 0000000000..1e11be7202 --- /dev/null +++ b/doc/clientless/checkpoints/2026-10-28-generator/historical-auth-start-failure.txt @@ -0,0 +1,27 @@ +TrinityCore rev. e333f2f9fc8b+ 2026-10-28 01:11:54 +0100 (feature/clientless-pla +yer-core branch) (Unix, Debug, Static) (worldserver-daemon) + to stop. + + ______ __ +/\__ _\ __ __/\ \__ +\/_/\ \/ _ __ /\_\ ___ /\_\ \, _\ __ __ + \ \ \/\`'__\/\ \ /' _ `\/\ \ \ \/ /\ \/\ \ + \ \ \ \ \/ \ \ \/\ \/\ \ \ \ \ \_\ \ \_\ \ + \ \_\ \_\ \ \_\ \_\ \_\ \_\ \__\\/`____ \ + \/_/\/_/ \/_/\/_/\/_/\/_/\/__/ `/___/> \ + C O R E /\___/ +http://TrinityCore.org \/__/ + +Using configuration file /home/wow_admin/wow_cata_server/server/debug/etc/worlds +erver.conf. +Using SSL version: OpenSSL 3.5.5 27 Jan 2026 (library: OpenSSL 3.5.5 27 Jan 2026 +) +Using Boost version: 1.90.0 +Automatic database updates are disabled for all databases! +Could not connect to MySQL database at 10.0.0.1: Can't connect to server on '10. +0.0.1' (115) + +DatabasePool Login NOT opened. There were errors opening the MySQL connections. +Check your SQLDriverLogFile for specific errors. Read wiki at http://www.trinity +core.info/display/tc/TrinityCore+Home +server shutdown 0 diff --git a/doc/clientless/checkpoints/2026-10-28-generator/native-audit.txt b/doc/clientless/checkpoints/2026-10-28-generator/native-audit.txt new file mode 100644 index 0000000000..5ff3b22d39 --- /dev/null +++ b/doc/clientless/checkpoints/2026-10-28-generator/native-audit.txt @@ -0,0 +1,176 @@ +character_guid name race class gender level online archetype persistent state seed health +27 Dagri 3 1 1 60 0 warrior 0 retired 20261028 5049 +28 Jarana 8 1 1 60 0 warrior 0 retired 20261029 5309 +29 Aelion 4 1 0 60 1 warrior 0 ready 20261101 5189 +30 Zanzan 8 1 0 60 1 warrior 0 ready 20261102 5409 +31 Lenara 22 8 1 60 1 mage 0 ready 20261103 3600 +32 Aldren 5 6 0 60 1 death_knight 0 ready 20261104 5259 +guid slot itemEntry durability +27 0 23314 80 +27 1 24073 0 +27 2 21805 80 +27 4 23300 135 +27 5 22197 45 +27 6 13498 100 +27 7 31125 65 +27 8 18754 45 +27 9 22714 45 +27 10 19873 0 +27 11 24151 0 +27 12 19930 0 +27 13 19930 0 +27 14 34659 0 +27 15 31134 100 +27 19 54443 0 +27 20 54443 0 +27 21 54443 0 +27 22 54443 0 +28 0 23244 80 +28 1 24073 0 +28 2 77868 80 +28 4 22872 135 +28 5 22197 45 +28 6 21495 100 +28 7 31125 65 +28 8 22205 45 +28 9 22868 45 +28 10 27832 0 +28 11 24151 0 +28 12 19930 0 +28 13 19930 0 +28 14 22337 0 +28 15 24044 100 +28 17 25861 25 +28 19 54443 0 +28 20 54443 0 +28 21 54443 0 +28 22 54443 0 +29 0 77867 80 +29 1 24073 0 +29 2 23315 80 +29 4 77858 135 +29 5 21503 45 +29 6 13498 100 +29 7 24064 65 +29 8 12936 45 +29 9 22714 45 +29 10 27834 0 +29 11 21477 0 +29 12 13213 0 +29 13 22321 0 +29 14 34659 0 +29 15 31134 100 +29 19 54443 0 +29 20 54443 0 +29 21 54443 0 +29 22 54443 0 +30 0 23244 80 +30 1 24073 0 +30 2 23243 80 +30 4 34650 115 +30 5 19051 45 +30 6 21475 100 +30 7 24064 65 +30 8 77861 45 +30 9 22868 45 +30 10 12058 0 +30 11 24151 0 +30 12 20072 0 +30 13 11810 0 +30 14 22337 0 +30 15 31134 100 +30 17 25861 25 +30 19 54443 0 +30 20 54443 0 +30 21 54443 0 +30 22 54443 0 +31 0 22720 80 +31 1 23125 0 +31 2 21468 80 +31 4 16700 135 +31 5 16696 45 +31 6 19683 100 +31 7 21489 65 +31 8 13107 45 +31 9 16391 45 +31 10 24045 0 +31 11 19905 0 +31 12 11819 0 +31 13 21473 0 +31 14 21470 0 +31 15 20654 100 +31 17 21801 65 +31 19 54443 0 +31 20 54443 0 +31 21 54443 0 +31 22 54443 0 +32 0 22223 80 +32 1 24073 0 +32 2 21805 80 +32 4 10845 135 +32 5 21503 45 +32 6 21475 100 +32 7 24064 65 +32 8 12936 45 +32 9 22714 45 +32 10 11934 0 +32 11 19510 0 +32 12 13164 0 +32 13 1973 0 +32 14 22337 0 +32 15 31134 100 +32 19 54443 0 +32 20 54443 0 +32 21 54443 0 +32 22 54443 0 +guid skill value max +27 129 375 375 +27 164 375 375 +27 185 525 525 +27 186 450 450 +27 356 525 525 +27 794 375 375 +28 129 375 375 +28 164 375 375 +28 185 525 525 +28 186 450 450 +28 356 525 525 +28 794 375 375 +29 129 375 375 +29 164 375 375 +29 185 525 525 +29 186 450 450 +29 356 525 525 +29 794 375 375 +30 129 375 375 +30 164 375 375 +30 185 525 525 +30 186 450 450 +30 356 525 525 +30 794 375 375 +31 129 375 375 +31 185 525 525 +31 197 375 375 +31 333 375 375 +31 356 525 525 +31 794 375 375 +32 129 375 375 +32 164 375 375 +32 185 525 525 +32 186 450 450 +32 356 525 525 +32 794 375 375 +guid talent_spells +27 13 +28 13 +29 13 +30 13 +31 14 +32 12 +guid spells +27 80 +28 80 +29 80 +30 80 +31 87 +32 64 diff --git a/doc/clientless/checkpoints/2026-10-28-generator/native-details.txt b/doc/clientless/checkpoints/2026-10-28-generator/native-details.txt new file mode 100644 index 0000000000..7caa9bb3e2 --- /dev/null +++ b/doc/clientless/checkpoints/2026-10-28-generator/native-details.txt @@ -0,0 +1,42 @@ +guid name level talentTree map instance_id position_x position_y position_z health power1 state persistent +27 Dagri 60 746 0 0 0 -5385.04 310.278 394.151 5049 0 retired 0 +28 Jarana 60 746 0 1 0 -813.097 -4880.08 18.995 5309 0 retired 0 +29 Aelion 60 746 0 1 0 9808.68 969.19 1309.75 5189 0 ready 0 +30 Zanzan 60 746 0 1 0 -826.364 -4879.64 19.5037 5409 0 ready 0 +31 Lenara 60 851 0 0 0 -9470.25 25.9936 56.3394 3600 6813 ready 0 +32 Aldren 60 400 0 0 0 1095.38 1587.06 28.5303 5259 0 ready 0 +guid slot itemEntry +29 12 13213 +29 13 22321 +30 12 20072 +30 13 11810 +31 12 11819 +31 13 21473 +32 12 13164 +32 13 1973 +guid itemEntry count +29 21991 20 +29 22829 20 +29 22832 20 +29 27854 20 +29 28399 20 +30 21991 20 +30 22829 20 +30 22832 20 +30 27854 20 +30 28399 20 +31 21991 20 +31 22829 20 +31 22832 20 +31 27854 20 +31 28399 20 +32 21991 20 +32 22829 20 +32 22832 20 +32 27854 20 +32 28399 20 +character_guid profile_bytes name identity_check +29 873 Aelion identity name matches: 1 +30 870 Zanzan identity name matches: 1 +31 858 Lenara identity name matches: 1 +32 886 Aldren identity name matches: 1 diff --git a/doc/clientless/checkpoints/2026-10-28-generator/ownership-test.txt b/doc/clientless/checkpoints/2026-10-28-generator/ownership-test.txt new file mode 100644 index 0000000000..2ac1dbbef2 --- /dev/null +++ b/doc/clientless/checkpoints/2026-10-28-generator/ownership-test.txt @@ -0,0 +1 @@ +Production generator commands: batch bounds, dedicated ownership, persistent/online/removing retirement protection and retired login refusal passed diff --git a/doc/clientless/checkpoints/2026-10-28-generator/policy-test.txt b/doc/clientless/checkpoints/2026-10-28-generator/policy-test.txt new file mode 100644 index 0000000000..9860e29f14 --- /dev/null +++ b/doc/clientless/checkpoints/2026-10-28-generator/policy-test.txt @@ -0,0 +1 @@ +Production generator: 6144 cultural names and 61440 real Lua profiles, profession coherence, personality ranges, escaping and equipment/armor gates passed diff --git a/doc/clientless/checkpoints/2026-10-28-generator/skill-test.txt b/doc/clientless/checkpoints/2026-10-28-generator/skill-test.txt new file mode 100644 index 0000000000..c33d579f53 --- /dev/null +++ b/doc/clientless/checkpoints/2026-10-28-generator/skill-test.txt @@ -0,0 +1 @@ +Production offline skill preparation: native tier caps, no downgrading, rank/level/race/tier gates and no modification of online players passed diff --git a/doc/clientless/checkpoints/2026-10-28-generator/startup-and-live.txt b/doc/clientless/checkpoints/2026-10-28-generator/startup-and-live.txt new file mode 100644 index 0000000000..4cff054242 --- /dev/null +++ b/doc/clientless/checkpoints/2026-10-28-generator/startup-and-live.txt @@ -0,0 +1,1880 @@ +>> Loaded 0 npc damage by class game table entries in 2 ms +Loading Creature Base Stats... +>> Loaded 400 creature base stats in 6 ms +Loading Spawn Group Templates... +>> Loaded 98 spawn group templates in 0 ms +Loading Creature Data... +Table `creature` has creature (GUID: 396679 Entry: 32491) with disallowed `unit_ +flags` 32768, removing incorrect flag. +>> Loaded 221565 creatures in 1185 ms +Loading Temporary Summon Data... +>> Loaded 869 temp summons in 4 ms +Loading pet levelup spells... +>> Loaded 263 pet levelup and default spells for 50 families in 9 ms +Loading pet default spells additional to levelup spells... +>> Loaded addition spells for 0 pet spell data entries in 2 ms +Loading summonable creature templates... +>> Loaded 427 summonable creature templates in 10 ms +Loading Creature Addon Data... +>> Loaded 52002 creature addons in 87 ms +Loading Creature Movement Overrides... +>> Loaded 0 creature movement overrides. DB table `creature_movement_override` i +s empty! +Loading Creature Movement Info... +>> Loaded 270 creature movement info override entries in 0 ms +Loading Gameobject Data... +>> Loaded 78938 gameobjects in 368 ms +Loading Spawn Group Data... +>> Loaded 94612 spawn group members in 55 ms +Loading instance spawn groups... +>> Loaded 27 instance spawn groups in 0 ms +Loading GameObject Addon Data... +>> Loaded 24493 gameobject addons in 35 ms +Loading GameObject Quest Items... +>> Loaded 1352 gameobject quest items in 2 ms +Loading Creature Quest Items... +>> Loaded 5361 creature quest items in 7 ms +Loading Creature Sparring Data... +>> Loaded 0 creature sparring templates in 1 ms +Loading Creature Linked Respawn... +>> Loaded 5239 linked respawns in 7 ms +Loading Weather Data... +>> Loaded 36 weather definitions in 1 ms +Loading Quests... +Quest 29507 has `RequiredCurrencyId1` = 393 but `RequiredCurrencyCount1` = 0, qu +est can't be done. +Quest 29283 has `RequiredCurrencyId1` = 416 but `RequiredCurrencyCount1` = 0, qu +est can't be done. +Quest 29281 has `RequiredCurrencyId1` = 416 but `RequiredCurrencyCount1` = 0, qu +est can't be done. +Quest 29279 has `RequiredCurrencyId1` = 416 but `RequiredCurrencyCount1` = 0, qu +est can't be done. +Quest 29214 has `RequiredCurrencyId1` = 416 but `RequiredCurrencyCount1` = 0, qu +est can't be done. +Quest 29199 has `RequiredCurrencyId1` = 416 but `RequiredCurrencyCount1` = 0, qu +est can't be done. +Quest 29181 has `RequiredCurrencyId1` = 416 but `RequiredCurrencyCount1` = 0, qu +est can't be done. +Quest 13478 has `RequiredCurrencyId1` = 392 but `RequiredCurrencyCount1` = 0, qu +est can't be done. +Quest 13476 has `RequiredCurrencyId1` = 392 but `RequiredCurrencyCount1` = 0, qu +est can't be done. +Quest 8371 has `RequiredCurrencyId1` = 392 but `RequiredCurrencyCount1` = 0, que +st can't be done. +Quest 8368 has `RequiredCurrencyId1` = 392 but `RequiredCurrencyCount1` = 0, que +st can't be done. +Quest 8367 has `RequiredCurrencyId1` = 392 but `RequiredCurrencyCount1` = 0, que +st can't be done. +>> Loaded 14991 quests definitions in 328 ms +Checking Quest Disables +>> Checked 5124 quest disables in 1 ms +Loading Quest POI +>> Loaded 13941 quest POI definitions in 172 ms +Loading Quest Money Rewards +>> Loaded 100 quest money reward entries in 1 ms +Loading Quests Starters and Enders... +Loading GO Start Quest Data... +>> Loaded 618 quest relations from gameobject_queststarter in 1 ms +Loading GO End Quest Data... +>> Loaded 517 quest relations from gameobject_questender in 1 ms +Loading Creature Start Quest Data... +>> Loaded 8725 quest relations from creature_queststarter in 10 ms +Loading Creature End Quest Data... +>> Loaded 9136 quest relations from creature_questender in 12 ms +Loading Quests Greetings... +>> Loaded 0 quest greetings. DB table `quest_greeting` is empty. +Loading Objects Pooling Data... +>> Loaded 1721 objects pools in 2 ms +Loading Creatures Pooling Data... +>> Loaded 835 creatures in pools in 1 ms +Loading Gameobject Pooling Data... +>> Loaded 7404 gameobject in pools in 12 ms +Loading Mother Pooling Data... +>> Loaded 1218 pools in mother pools in 3 ms +Starting objects pooling system... +Loading Quest Pooling Data... +>> Loaded 56 daily, 5 weekly and 0 monthly quest pools in 2 ms +Loading Game Event Data... +>> Loaded 156 holiday dates in 0 ms +>> Loaded 78 game events in 1 ms. +Loading Game Event Saves Data... +>> Loaded 0 game event saves in game events. DB table `game_event_save` is empty +. +Loading Game Event Prerequisite Data... +>> Loaded 0 game event prerequisites in game events. DB table `game_event_prereq +uisite` is empty. +Loading Game Event Creature Data... +>> Loaded 2790 creatures in game events in 1 ms. +Loading Game Event GO Data... +>> Loaded 21863 gameobjects in game events in 9 ms. +Loading Game Event Model/Equipment Change Data... +>> Loaded 22 model/equipment changes in game events in 2 ms. +Loading Game Event Quest Data... +>> Loaded 327 quests additions in game events in 0 ms. +Loading Game Event GO Quest Data... +>> Loaded 189 quests additions in game events in 1 ms. +Loading Game Event Quest Condition Data... +>> Loaded 0 quest event conditions in game events. DB table `game_event_quest_co +ndition` is empty. +Loading Game Event Condition Data... +>> Loaded 0 conditions in game events. DB table `game_event_condition` is empty. +Loading Game Event Condition Save Data... +>> Loaded 0 condition saves in game events. DB table `game_event_condition_save` + is empty. +Loading Game Event NPCflag Data... +>> Loaded 0 npcflags in game events. DB table `game_event_npcflag` is empty. +Loading Game Event Seasonal Quest Relations... +>> Loaded 690 quests additions in game events in 1 ms. +Loading Game Event Vendor Additions Data... +>> Loaded 2 vendor additions in game events in 0 ms. +Loading Game Event Battleground Holiday Data... +>> Loaded 8 battleground holidays in game events in 0 ms. +Loading Game Event Pool Data... +>> Loaded 2 pools for game events in 0 ms. +Loading UNIT_NPC_FLAG_SPELLCLICK Data... +>> Loaded 702 spellclick definitions in 3 ms +Loading Vehicle Template Accessories... +>> Loaded 431 Vehicle Template Accessories in 1 ms +Loading Vehicle Accessories... +>> Loaded 0 Vehicle Accessories in 1 ms +Loading Vehicle Seat Addon Data... +>> Loaded 15 Vehicle Seat Addon entries in 0 ms +Loading SpellArea Data... +>> Loaded 872 spell area requirements in 3 ms +Loading AreaTrigger definitions... +>> Loaded 316 area trigger teleport definitions in 1 ms +Loading Access Requirements... +>> Loaded 152 access requirement definitions in 1 ms +Loading Quest Area Triggers... +>> Loaded 49 quest trigger points in 0 ms +Loading Tavern Area Triggers... +>> Loaded 175 tavern triggers in 1 ms +Loading AreaTrigger script names... +>> Loaded 229 areatrigger scripts in 0 ms +Loading LFG entrance positions... +>> Loaded 68 lfg dungeon templates in 1 ms +Loading Dungeon boss data... +>> Loaded 617 instance encounters in 2 ms +Loading LFG rewards... +>> Loaded 20 lfg dungeon rewards in 0 ms +Loading Graveyard-zone links... +>> Loaded 855 graveyard-zone links in 2 ms +Loading Graveyard Orientations... +>> Loaded 1016 graveyard orientations in 1 ms +Loading spell pet auras... +>> Loaded 34 spell pet auras in 0 ms +Loading Spell target coordinates... +>> Loaded 1438 spell teleport coordinates in 4 ms +Loading linked spells... +>> Loaded 413 linked spells in 1 ms +Loading Player Create Data... +>> Loaded 91 player create definitions in 0 ms +Loading Player Create Items Data... +>> Loaded 1 custom player create items in 1 ms +Loading Player Create Skill Data... +>> Loaded player create skills in 0 ms +Loading Player Create Custom Spell Data... +>> Loaded 4507 custom player create spells in 2 ms +Loading Player Create Cast Spell Data... +>> Loaded 63 player create cast spells in 0 ms +Loading Player Create Action Data... +>> Loaded 352 player create actions in 1 ms +Loading Player Create Level Stats Data... +>> Loaded 850 level stats definitions in 4 ms +Loading Player Create XP Data... +>> Loaded 84 xp for level definitions in 1 ms +Loading Exploration BaseXP Data... +>> Loaded 80 BaseXP definitions in 0 ms +Loading Pet Name Parts... +>> Loaded 313 pet name parts in 1 ms +Loading the max pet number... +>> Loaded the max pet number: 28 in 0 ms +Loading pet level stats... +>> Loaded 2720 level pet stats definitions in 4 ms +Loading Player level dependent mail rewards... +>> Loaded 24 level dependent mail rewards in 0 ms +Loading creature loot templates... +>> Loaded 528193 creature loot templates in 659 ms +Loading fishing loot templates... +>> Loaded 266 fishing loot templates in 2 ms +Loading gameobject loot templates... +>> Loaded 19834 gameobject loot templates in 29 ms +Loading item loot templates... +>> Loaded 5031 item loot templates in 15 ms +Loading mail loot templates... +>> Loaded 90 mail loot templates in 1 ms +Loading milling loot templates... +>> Loaded 51 milling loot templates in 3 ms +Loading pickpocketing loot templates... +>> Loaded 10981 pickpocketing loot templates in 20 ms +Loading skinning loot templates... +>> Loaded 3390 skinning loot templates in 9 ms +Loading disenchanting loot templates... +>> Loaded 180 disenchanting loot templates in 0 ms +Loading prospecting loot templates... +>> Loaded 43 prospecting loot templates in 3 ms +Loading spell loot templates... +>> Loaded 292 spell loot templates in 3 ms +Loading reference loot templates... +>> Loaded refence loot templates in 45 ms +Loading Skill Discovery Table... +>> Loaded 347 skill discovery definitions in 3 ms. +Loading Skill Extra Item Table... +>> Loaded 225 spell specialization definitions in 1 ms +Loading Skill Perfection Data Table... +>> Loaded 72 spell perfection definitions in 0 ms. +Loading Skill Fishing base level requirements... +>> Loaded 95 areas for fishing base skill level in 1 ms +Loading Archaeology store... +>> Loaded 180 archaeology sites in 0 ms +>> Loaded 180 archaeology site polygons in 164 ms +>> Loaded 9 archaeology branches in 0 ms +>> Loaded 139 archaeology projects in 1 ms +>> Calculated 9 archaeology branch chances in 0 ms +Loading Achievements... +>> Loaded 47 achievement references in 0 ms. +Loading Achievement Criteria Lists... +>> Loaded 9803 achievement criteria and 952 guild achievement crieteria in 5 ms. +Loading Achievement Criteria Data... +>> Loaded 1291 additional achievement criteria data in 2 ms +Loading Achievement Rewards... +>> Loaded 120 achievement rewards in 1 ms. +Loading Achievement Reward Locales... +>> Loaded 0 achievement reward locale strings. DB table `achievement_reward_loc +ale` is empty. +Loading Completed Achievements... +>> Loaded 77 realm first completed achievements in 1 ms. +Loading Item Auctions... +>> Loaded 0 auction items. DB table `auctionhouse` or `item_instance` is empty! +Loading Auctions... +>> Loaded 0 auctions. DB table `auctionhouse` is empty. +Loading Guild XP for level... +>> Loaded 24 xp for guild level definitions in 0 ms +Loading Guild rewards... +>> Loaded 42 guild reward definitions in 0 ms +Initializing Guild Profession Data Store... +Loading Guild Challenges... +>> Loaded 4 guild challenge definitions in 0 ms +Loading Guilds... +Loading guilds definitions... +>> Loaded 1 guild definitions in 1 ms +Loading guild ranks... +>> Loaded 5 guild ranks in 0 ms +Loading guild members... +>> Loaded 4 guild members in 5 ms +Loading bank tab rights... +>> Loaded 30 bank tab rights in 0 ms +Loading guild event logs... +>> Loaded 8 guild event logs in 1 ms +Loading guild bank event logs... +>> Loaded 22 guild bank event logs in 0 ms +Loading Guild News... +>> Loaded 0 guild event logs. DB table `guild_newslog` is empty. +Loading guild bank tabs... +>> Loaded 6 guild bank tabs in 0 ms +>> Loaded 22 guild bank tab items in 2 ms +>> Validated data of loaded guilds in 0 ms +Loading guild finder guild-related settings... +>> Loaded 0 guild finder guild-related settings. Table `guild_finder_guild_setti +ngs` is empty. +Loading guild finder membership requests... +>> Loaded 0 guild finder membership requests. Table `guild_finder_applicant` is +empty. +Loading ArenaTeams... +>> Loaded 0 arena teams. DB table `arena_team` is empty! +Loading Groups... +>> Loaded 0 group definitions. DB table `groups` is empty! +Loading ReservedNames... +>> Loaded 0 reserved player names. DB table `reserved_name` is empty! +Loading GameObjects for quests... +>> Loaded 2750 GameObjects for quests in 5 ms +Loading BattleMasters... +>> Loaded 171 battlemaster entries in 2 ms +Loading GameTeleports... +>> Loaded 1531 GameTeleports in 6 ms +Loading Trainers... +>> Loaded 88 Trainers in 8 ms +Loading Gossip menu... +>> Loaded 8225 gossip_menu IDs in 9 ms +Loading Gossip menu options... +>> Loaded 5837 gossip_menu_option entries in 18 ms +Loading Creature Template Gossip... +creature_template_gossip has gossip definitions for menu id 55002 but this menu +doesn't exist +creature_template_gossip has gossip definitions for menu id 55002 but this menu +doesn't exist +creature_template_gossip has gossip definitions for menu id 6268 but this menu d +oesn't exist +creature_template_gossip has gossip definitions for menu id 9856 but this menu d +oesn't exist +creature_template_gossip has gossip definitions for menu id 10844 but this menu +doesn't exist +creature_template_gossip has gossip definitions for menu id 10852 but this menu +doesn't exist +creature_template_gossip has gossip definitions for menu id 2522 but this menu d +oesn't exist +creature_template_gossip has gossip definitions for menu id 12720 but this menu +doesn't exist +creature_template_gossip has gossip definitions for menu id 12718 but this menu +doesn't exist +creature_template_gossip has gossip definitions for menu id 13160 but this menu +doesn't exist +>> Loaded 5435 creature template gossip menus in 7 ms +Loading Creature trainers... +>> Loaded 1187 default trainers in 3 ms +Loading Vendors... +Table `(game_event_)npc_vendor` for Vendor (Entry: 44972) have in item list non- +existed item (60822, type 1), ignore +>> Loaded 48278 Vendors in 68 ms +Loading Waypoints... +>> Loaded 110626 waypoints in 177 ms +Loading Waypoint Addons... +>> Loaded 639 waypoint addon data in 2 ms +Loading SmartAI Waypoints... +>> Loaded 586 SmartAI waypoint paths (total 7064 waypoints) in 10 ms +Loading Creature Formations... +>> Loaded 2517 creatures in formations in 6 ms +Loading World State templates... +Table `world_state` contains a world state 3490 with invalid MapID (2118), map i +gnored +Table `world_state` contains a world state 3490 with invalid AreaID (10176), are +a ignored +Table `world_state` contains a world state 3491 with invalid MapID (2118), map i +gnored +Table `world_state` contains a world state 3491 with invalid AreaID (10176), are +a ignored +Table `world_state` contains a world state 3600 with invalid MapID (1134), map i +gnored +Table `world_state` contains a world state 3600 with invalid MapID (1504), map i +gnored +Table `world_state` contains a world state 3600 with invalid MapID (1505), map i +gnored +Table `world_state` contains a world state 3600 with invalid MapID (1552), map i +gnored +Table `world_state` contains a world state 3600 with invalid MapID (1672), map i +gnored +Table `world_state` contains a world state 3600 with invalid MapID (1825), map i +gnored +Table `world_state` contains a world state 3600 with invalid MapID (1911), map i +gnored +Table `world_state` contains a world state 3600 with invalid MapID (2167), map i +gnored +Table `world_state` contains a world state 3600 with invalid MapID (2373), map i +gnored +Table `world_state` contains a world state 3600 with invalid MapID (2509), map i +gnored +Table `world_state` contains a world state 3600 with invalid MapID (2511), map i +gnored +Table `world_state` contains a world state 3600 with invalid MapID (2547), map i +gnored +Table `world_state` contains a world state 3601 with invalid MapID (1134), map i +gnored +Table `world_state` contains a world state 3601 with invalid MapID (1504), map i +gnored +Table `world_state` contains a world state 3601 with invalid MapID (1505), map i +gnored +Table `world_state` contains a world state 3601 with invalid MapID (1552), map i +gnored +Table `world_state` contains a world state 3601 with invalid MapID (1672), map i +gnored +Table `world_state` contains a world state 3601 with invalid MapID (1825), map i +gnored +Table `world_state` contains a world state 3601 with invalid MapID (1911), map i +gnored +Table `world_state` contains a world state 3601 with invalid MapID (2167), map i +gnored +Table `world_state` contains a world state 3601 with invalid MapID (2373), map i +gnored +Table `world_state` contains a world state 3601 with invalid MapID (2509), map i +gnored +Table `world_state` contains a world state 3601 with invalid MapID (2511), map i +gnored +Table `world_state` contains a world state 3601 with invalid MapID (2547), map i +gnored +Table `world_state` contains a world state 3610 with invalid MapID (1134), map i +gnored +Table `world_state` contains a world state 3610 with invalid MapID (1504), map i +gnored +Table `world_state` contains a world state 3610 with invalid MapID (1505), map i +gnored +Table `world_state` contains a world state 3610 with invalid MapID (1552), map i +gnored +Table `world_state` contains a world state 3610 with invalid MapID (1672), map i +gnored +Table `world_state` contains a world state 3610 with invalid MapID (1825), map i +gnored +Table `world_state` contains a world state 3610 with invalid MapID (1911), map i +gnored +Table `world_state` contains a world state 3610 with invalid MapID (2167), map i +gnored +Table `world_state` contains a world state 3610 with invalid MapID (2373), map i +gnored +Table `world_state` contains a world state 3610 with invalid MapID (2509), map i +gnored +Table `world_state` contains a world state 3610 with invalid MapID (2511), map i +gnored +Table `world_state` contains a world state 3610 with invalid MapID (2547), map i +gnored +Table `world_state` contains a world state 3680 with invalid MapID (2118), map i +gnored +Table `world_state` contains a world state 3680 with invalid AreaID (10176), are +a ignored +Table `world_state` contains a world state 3681 with invalid MapID (2118), map i +gnored +Table `world_state` contains a world state 3681 with invalid AreaID (10176), are +a ignored +Table `world_state` contains a world state 3698 with invalid MapID (2118), map i +gnored +Table `world_state` contains a world state 3698 with invalid AreaID (10176), are +a ignored +Table `world_state` contains a world state 3699 with invalid MapID (2118), map i +gnored +Table `world_state` contains a world state 3699 with invalid AreaID (10176), are +a ignored +Table `world_state` contains a world state 3700 with invalid MapID (2118), map i +gnored +Table `world_state` contains a world state 3700 with invalid AreaID (10176), are +a ignored +Table `world_state` contains a world state 3701 with invalid MapID (2118), map i +gnored +Table `world_state` contains a world state 3701 with invalid AreaID (10176), are +a ignored +Table `world_state` contains a world state 3702 with invalid MapID (2118), map i +gnored +Table `world_state` contains a world state 3702 with invalid AreaID (10176), are +a ignored +Table `world_state` contains a world state 3703 with invalid MapID (2118), map i +gnored +Table `world_state` contains a world state 3703 with invalid AreaID (10176), are +a ignored +Table `world_state` contains a world state 3704 with invalid MapID (2118), map i +gnored +Table `world_state` contains a world state 3704 with invalid AreaID (10176), are +a ignored +Table `world_state` contains a world state 3705 with invalid MapID (2118), map i +gnored +Table `world_state` contains a world state 3705 with invalid AreaID (10176), are +a ignored +Table `world_state` contains a world state 3706 with invalid MapID (2118), map i +gnored +Table `world_state` contains a world state 3706 with invalid AreaID (10176), are +a ignored +Table `world_state` contains a world state 3710 with invalid MapID (2118), map i +gnored +Table `world_state` contains a world state 3710 with invalid AreaID (10176), are +a ignored +Table `world_state` contains a world state 3711 with invalid MapID (2118), map i +gnored +Table `world_state` contains a world state 3711 with invalid AreaID (10176), are +a ignored +Table `world_state` contains a world state 3712 with invalid MapID (2118), map i +gnored +Table `world_state` contains a world state 3712 with invalid AreaID (10176), are +a ignored +Table `world_state` contains a world state 3713 with invalid MapID (2118), map i +gnored +Table `world_state` contains a world state 3713 with invalid AreaID (10176), are +a ignored +Table `world_state` contains a world state 3714 with invalid MapID (2118), map i +gnored +Table `world_state` contains a world state 3714 with invalid AreaID (10176), are +a ignored +Table `world_state` contains a world state 3749 with invalid MapID (2118), map i +gnored +Table `world_state` contains a world state 3749 with invalid AreaID (10176), are +a ignored +Table `world_state` contains a world state 3750 with invalid MapID (2118), map i +gnored +Table `world_state` contains a world state 3750 with invalid AreaID (10176), are +a ignored +Table `world_state` contains a world state 3751 with invalid MapID (2118), map i +gnored +Table `world_state` contains a world state 3751 with invalid AreaID (10176), are +a ignored +Table `world_state` contains a world state 3752 with invalid MapID (2118), map i +gnored +Table `world_state` contains a world state 3752 with invalid AreaID (10176), are +a ignored +Table `world_state` contains a world state 3753 with invalid MapID (2118), map i +gnored +Table `world_state` contains a world state 3753 with invalid AreaID (10176), are +a ignored +Table `world_state` contains a world state 3754 with invalid MapID (2118), map i +gnored +Table `world_state` contains a world state 3754 with invalid AreaID (10176), are +a ignored +Table `world_state` contains a world state 3755 with invalid MapID (2118), map i +gnored +Table `world_state` contains a world state 3755 with invalid AreaID (10176), are +a ignored +Table `world_state` contains a world state 3756 with invalid MapID (2118), map i +gnored +Table `world_state` contains a world state 3756 with invalid AreaID (10176), are +a ignored +Table `world_state` contains a world state 3757 with invalid MapID (2118), map i +gnored +Table `world_state` contains a world state 3757 with invalid AreaID (10176), are +a ignored +Table `world_state` contains a world state 3758 with invalid MapID (2118), map i +gnored +Table `world_state` contains a world state 3758 with invalid AreaID (10176), are +a ignored +Table `world_state` contains a world state 3759 with invalid MapID (2118), map i +gnored +Table `world_state` contains a world state 3759 with invalid AreaID (10176), are +a ignored +Table `world_state` contains a world state 3760 with invalid MapID (2118), map i +gnored +Table `world_state` contains a world state 3760 with invalid AreaID (10176), are +a ignored +Table `world_state` contains a world state 3761 with invalid MapID (2118), map i +gnored +Table `world_state` contains a world state 3761 with invalid AreaID (10176), are +a ignored +Table `world_state` contains a world state 3762 with invalid MapID (2118), map i +gnored +Table `world_state` contains a world state 3762 with invalid AreaID (10176), are +a ignored +Table `world_state` contains a world state 3763 with invalid MapID (2118), map i +gnored +Table `world_state` contains a world state 3763 with invalid AreaID (10176), are +a ignored +Table `world_state` contains a world state 3764 with invalid MapID (2118), map i +gnored +Table `world_state` contains a world state 3764 with invalid AreaID (10176), are +a ignored +Table `world_state` contains a world state 3765 with invalid MapID (2118), map i +gnored +Table `world_state` contains a world state 3765 with invalid AreaID (10176), are +a ignored +Table `world_state` contains a world state 3766 with invalid MapID (2118), map i +gnored +Table `world_state` contains a world state 3766 with invalid AreaID (10176), are +a ignored +Table `world_state` contains a world state 3767 with invalid MapID (2118), map i +gnored +Table `world_state` contains a world state 3767 with invalid AreaID (10176), are +a ignored +Table `world_state` contains a world state 3768 with invalid MapID (2118), map i +gnored +Table `world_state` contains a world state 3768 with invalid AreaID (10176), are +a ignored +Table `world_state` contains a world state 3769 with invalid MapID (2118), map i +gnored +Table `world_state` contains a world state 3769 with invalid AreaID (10176), are +a ignored +Table `world_state` contains a world state 3770 with invalid MapID (2118), map i +gnored +Table `world_state` contains a world state 3770 with invalid AreaID (10176), are +a ignored +Table `world_state` contains a world state 3771 with invalid MapID (2118), map i +gnored +Table `world_state` contains a world state 3771 with invalid AreaID (10176), are +a ignored +Table `world_state` contains a world state 3772 with invalid MapID (2118), map i +gnored +Table `world_state` contains a world state 3772 with invalid AreaID (10176), are +a ignored +Table `world_state` contains a world state 3773 with invalid MapID (2118), map i +gnored +Table `world_state` contains a world state 3773 with invalid AreaID (10176), are +a ignored +Table `world_state` contains a world state 3781 with invalid MapID (2118), map i +gnored +Table `world_state` contains a world state 3781 with invalid AreaID (10176), are +a ignored +Table `world_state` contains a world state 3801 with invalid MapID (2118), map i +gnored +Table `world_state` contains a world state 3801 with invalid AreaID (10176), are +a ignored +Table `world_state` contains a world state 3802 with invalid MapID (2118), map i +gnored +Table `world_state` contains a world state 3802 with invalid AreaID (10176), are +a ignored +Table `world_state` contains a world state 3803 with invalid MapID (2118), map i +gnored +Table `world_state` contains a world state 3803 with invalid AreaID (10176), are +a ignored +Table `world_state` contains a world state 3815 with invalid MapID (1544), map i +gnored +Table `world_state` contains a world state 3816 with invalid MapID (1544), map i +gnored +Table `world_state` contains a world state 4022 with invalid MapID (2118), map i +gnored +Table `world_state` contains a world state 4022 with invalid AreaID (10176), are +a ignored +Table `world_state` contains a world state 4023 with invalid MapID (2118), map i +gnored +Table `world_state` contains a world state 4023 with invalid AreaID (10176), are +a ignored +Table `world_state` contains a world state 4024 with invalid MapID (2118), map i +gnored +Table `world_state` contains a world state 4024 with invalid AreaID (10176), are +a ignored +Table `world_state` contains a world state 4025 with invalid MapID (2118), map i +gnored +Table `world_state` contains a world state 4025 with invalid AreaID (10176), are +a ignored +Table `world_state` contains a world state 4354 with invalid MapID (2118), map i +gnored +Table `world_state` contains a world state 4354 with invalid AreaID (10176), are +a ignored +>> Loaded 526 world state templates 3 ms +>> Loaded 41 saved world state values 0 ms +Loading Persistend World Variables... +>> Loaded 9 world variables in 1 ms +Loading Terrain World Map definitions... +>> Loaded 4 terrain world maps in 0 ms. +Loading Terrain Swap Default definitions... +>> Loaded 4 terrain swap defaults in 1 ms. +Loading Phase Area definitions... +>> Loaded 25 phase areas in 0 ms. +Loading Conditions... +>> Loaded 15912 conditions in 42 ms +Loading faction change achievement pairs... +>> Loaded 124 faction change achievement pairs in 0 ms +Loading faction change spell pairs... +>> Loaded 113 faction change spell pairs in 1 ms +Loading faction change quest pairs... +>> Loaded 250 faction change quest pairs in 0 ms +Loading faction change item pairs... +>> Loaded 752 faction change item pairs in 2 ms +Loading faction change reputation pairs... +>> Loaded 17 faction change reputation pairs in 1 ms +Loading faction change title pairs... +>> Loaded 22 faction change title pairs in 0 ms +Loading GM tickets... +>> Loaded 2 GM tickets in 0 ms +Loading GM surveys... +>> Loaded GM Survey count from database in 1 ms +Loading client addons... +>> Loaded 35 known addons in 0 ms +Returning old mails... +>> No expired mails found. +Loading Autobroadcasts... +>> Loaded 0 autobroadcasts definitions. DB table `autobroadcast` is empty for th +is realm! +Loading spell_scripts... +>> Loaded 143 script definitions in 1 ms +Loading event_scripts... +>> Loaded 461 script definitions in 2 ms +Loading waypoint_scripts... +>> Loaded 422 script definitions in 1 ms +Loading spell script names... +Scriptname: `spell_vcore_dungeon_horn` spell (Id: 120000) does not exist. +Scriptname: `spell_vcore_dungeon_horn` spell (Id: 120001) does not exist. +Scriptname: `spell_vcore_dungeon_horn` spell (Id: 120002) does not exist. +Scriptname: `spell_vcore_dungeon_horn` spell (Id: 120003) does not exist. +>> Loaded 3282 spell script names in 7 ms +Loading Creature Texts... +>> Loaded 16797 creature texts for 3997 creatures in 53 ms +Loading Creature Text Locales... +Loading creature StaticFlags overrides... +>> Loaded 133 creature static flag overrides in 1 ms +Loading Taxi node level definitions... +>> Loaded 274 taxi node level definitions in 0 ms +Initializing Scripts... +Loading Script Waypoints for 54 creature(s)... +>> Loaded 1377 Script Waypoint nodes in 5 ms +>> Loaded spline chain data for 85 chains, consisting of 272 splines with 1414 w +aypoints in 3 ms +Loading C++ scripts +Script named 'world_map_set_faction_worldstates_609' does not have a script name + assigned in database. +Script named 'item_gor_dreks_ointment' does not have a script name assigned in d +atabase. +>> Loaded 4106 C++ scripts in 147 ms +Validating spell scripts... +Spell `85123` Effect `Index: EFFECT_0 Target: 7` of script `spell_siege_cannon` +did not match dbc effect data - handler bound to hook `OnObjectAreaTargetSelect` + of SpellScript won't be executed +>> Validated 3470 scripts in 12 ms +Loading SmartAI scripts... +>> Loaded SmartAIMgr Helpers in 17 ms +>> Loaded 13506 SmartAI scripts in 166 ms +Loading Calendar data... +>> Loaded 0 calendar events in 1 ms +>> Loaded 0 calendar invites in 0 ms +Loading Petitions... +>> Loaded 0 petitions. +Loading Signatures... +>> Loaded 0 Petition signs! +Loading Summon Properties parameter data... +>> Loaded 78 summon properties parameters in 0 ms +Loading Item loot... +>> Loaded 0 stored item loots +>> Loaded 0 stored item money +Initialize query data... +>> Initialized query cache data in 967 ms +Initialize commands... +Initialize game time and timers +Mail timer set to: 360, mail return is called every 1440 minutes +Starting Map System +Starting Game Event system... +Initialize AuctionHouseBot... +Initializing Opcodes... +Starting Arena Season... +Starting Battleground System +>> Loaded 15 battlegrounds in 0 ms +Starting Outdoor PvP System +>> Loaded 5 outdoor PvP definitions in 1 ms +Starting Battlefield System +>> Loaded 2 battlefields in 0 ms +Loading Warden Checks... +Loading Warden Action Overrides... +Deleting expired bans... +Initializing quest reset times... +Calculate random battleground reset time... +Calculate guild limitation(s) reset time... +Calculate next currency reset time... +World initialized in 0 minutes 11 seconds +TC>TC>TrinityCore rev. e333f2f9fc8b+ 2026-10-28 01:11:54 +0100 (feature/clientle +ss-player-core branch) (Unix, Debug, Static) (worldserver-daemon) ready... +clientless generate login 29 +TC>clientless generate login 30 +TC>clientless generate login 31 +TC>clientless generate login 32 +TC>Registered clientless session for character GUID 29 (account 6), state=Loadin +g. +Created clientless player session for character GUID 29 (account 6). +ClientlessPlayerMgr started character GUID 29 (account 6). +TC> Registered clientless session for character GUID 30 (account 6), state=Loadi +ng. +Created clientless player session for character GUID 30 (account 6). +ClientlessPlayerMgr started character GUID 30 (account 6). +TC> Registered clientless session for character GUID 31 (account 6), state=Loadi +ng. +Created clientless player session for character GUID 31 (account 6). +ClientlessPlayerMgr started character GUID 31 (account 6). +TC> Registered clientless session for character GUID 32 (account 6), state=Loadi +ng. +Created clientless player session for character GUID 32 (account 6). +ClientlessPlayerMgr started character GUID 32 (account 6). +TC> Loading saved respawn time of 1790175804 for spawnid (0,323981) - spawn does + not exist, ignoring +Loading saved respawn time of 1790090644 for spawnid (0,396680) - spawn does not + exist, ignoring +Loading saved respawn time of 1790019808 for spawnid (0,396681) - spawn does not + exist, ignoring +Loading saved respawn time of 1790019853 for spawnid (0,396682) - spawn does not + exist, ignoring +Initialized transport GameObject grids: map=0 instance=0 grids=4 candidates=15 +Clientless player character GUID 31 (account 6) reached state=Online. +Initialized transport GameObject grids: map=1 instance=0 grids=13 candidates=17 +Clientless player character GUID 30 (account 6) reached state=Online. +Clientless player character GUID 29 (account 6) reached state=Online. +ClientlessLua initialized, script root '/home/wow_admin/wow_cata_server/source/T +rinityCore/src/server/scripts/Custom/clientless'. +[AdventureBot][Profile] loaded guid=31 name='Lenara' source='generated:31' home= +'elwynn' canGroundMount=0 mounts=0 mountMinDistance=60.0 mountSpeedRate=1.60 per +sonality=8 regionAffinity=1 activityAffinity=7 +PROFILE_PROFESSIONS guid=31 primary=2 +ClientlessLua[31]: adventure profile controller ready for Lenara +ClientlessLua[31]: executed generic adventure script '/home/wow_admin/wow_cata_s +erver/source/TrinityCore/src/server/scripts/Custom/clientless/adventure/generic. +lua'. +ADVENTURE_CATALOG locations=2973 actions=84 lines=76 +INTENT_TRAVEL guid=31 intent='social' location='poi_npc_280710' area=0 +[AdventureBot][Profile] loaded guid=30 name='Zanzan' source='generated:30' home= +'durotar' canGroundMount=0 mounts=0 mountMinDistance=60.0 mountSpeedRate=1.60 pe +rsonality=8 regionAffinity=1 activityAffinity=7 +PROFILE_PROFESSIONS guid=30 primary=2 +ClientlessLua[30]: adventure profile controller ready for Zanzan +ClientlessLua[30]: executed generic adventure script '/home/wow_admin/wow_cata_s +erver/source/TrinityCore/src/server/scripts/Custom/clientless/adventure/generic. +lua'. +INTENT_TRAVEL guid=30 intent='social' location='poi_tele_823' area=0 +[AdventureBot][Profile] loaded guid=29 name='Aelion' source='generated:29' home= +'teldrassil' canGroundMount=0 mounts=0 mountMinDistance=60.0 mountSpeedRate=1.60 + personality=8 regionAffinity=1 activityAffinity=7 +PROFILE_PROFESSIONS guid=29 primary=2 +ClientlessLua[29]: adventure profile controller ready for Aelion +ClientlessLua[29]: executed generic adventure script '/home/wow_admin/wow_cata_s +erver/source/TrinityCore/src/server/scripts/Custom/clientless/adventure/generic. +lua'. +INTENT_TRAVEL guid=29 intent='social' location='poi_guard_111' area=0 +Clientless player character GUID 32 (account 6) reached state=Online. +[AdventureBot] loaded 3966 clientless world points. +[AdventureBot][Profile] loaded guid=32 name='Aldren' source='generated:32' home= +'silverpineforest' canGroundMount=0 mounts=0 mountMinDistance=60.0 mountSpeedRat +e=1.60 personality=8 regionAffinity=1 activityAffinity=7 +PROFILE_PROFESSIONS guid=32 primary=2 +ClientlessLua[32]: adventure profile controller ready for Aldren +ClientlessLua[32]: executed generic adventure script '/home/wow_admin/wow_cata_s +erver/source/TrinityCore/src/server/scripts/Custom/clientless/adventure/generic. +lua'. +INTENT_TRAVEL guid=32 intent='social' location='poi_npc_321922' area=0 +INTENT_ARRIVED guid=31 intent='social' location='poi_npc_280710' +SOCIAL_START guid=31 action='drink' target='GUID Full: 0xf112b55b000001eb Type: +Gameobject Entry: 177499 Low: 491' approach=1 +MICRO_START guid=31 intent='social' location='poi_npc_280710' action='drink' dur +ationMs=20000 +INTENT_ARRIVED guid=30 intent='social' location='poi_tele_823' +MICRO_START guid=30 intent='social' location='poi_tele_823' action='drink' durat +ionMs=19000 +INTENT_ARRIVED guid=29 intent='social' location='poi_guard_111' +SOCIAL_START guid=29 action='social_listen' target='GUID Full: 0xf130a7fd0000081 +0 Type: Creature Entry: 43005 Low: 2064' approach=1 +MICRO_START guid=29 intent='social' location='poi_guard_111' action='social_list +en' durationMs=13000 +INTENT_ARRIVED guid=32 intent='social' location='poi_npc_321922' +SOCIAL_START guid=32 action='social_listen' target='GUID Full: 0xf130b1b8000005c +6 Type: Creature Entry: 45496 Low: 1478' approach=1 +MICRO_START guid=32 intent='social' location='poi_npc_321922' action='social_lis +ten' durationMs=13000 +SOCIAL_FRONT guid=32 target='GUID Full: 0xf130b1b8000005c6 Type: Creature Entry: + 45496 Low: 1478' offset=0.000 distance=2.20 +SOCIAL_CUE guid=32 action='social_listen' cue=4 target='GUID Full: 0xf130b1b8000 +005c6 Type: Creature Entry: 45496 Low: 1478' +SOCIAL_FRONT guid=29 target='GUID Full: 0xf130a7fd00000810 Type: Creature Entry: + 43005 Low: 2064' offset=0.000 distance=2.20 +SOCIAL_CUE guid=29 action='social_listen' cue=4 target='GUID Full: 0xf130a7fd000 +00810 Type: Creature Entry: 43005 Low: 2064' +SOCIAL_SEATED guid=31 chair='GUID Full: 0xf112b55b000001eb Type: Gameobject Entr +y: 177499 Low: 491' position=(-9477.270 28.375 56.343) slotError=0.000 stand=5 +movementGuid=31 +SOCIAL_CUE guid=31 action='drink' cue=10 target='GUID Full: 0xf112b55b000001eb T +ype: Gameobject Entry: 177499 Low: 491' +SOCIAL_FRONT guid=32 target='GUID Full: 0xf130b1b8000005c6 Type: Creature Entry: + 45496 Low: 1478' offset=0.000 distance=2.20 +SOCIAL_CUE guid=32 action='social_listen' cue=6 target='GUID Full: 0xf130b1b8000 +005c6 Type: Creature Entry: 45496 Low: 1478' +SOCIAL_FRONT guid=29 target='GUID Full: 0xf130a7fd00000810 Type: Creature Entry: + 43005 Low: 2064' offset=0.000 distance=2.20 +SOCIAL_CUE guid=29 action='social_listen' cue=6 target='GUID Full: 0xf130a7fd000 +00810 Type: Creature Entry: 43005 Low: 2064' +SOCIAL_FRONT guid=32 target='GUID Full: 0xf130b1b8000005c6 Type: Creature Entry: + 45496 Low: 1478' offset=0.000 distance=2.20 +SOCIAL_CUE guid=32 action='social_listen' cue=4 target='GUID Full: 0xf130b1b8000 +005c6 Type: Creature Entry: 45496 Low: 1478' +SOCIAL_FRONT guid=29 target='GUID Full: 0xf130a7fd00000810 Type: Creature Entry: + 43005 Low: 2064' offset=0.000 distance=2.20 +SOCIAL_CUE guid=29 action='social_listen' cue=4 target='GUID Full: 0xf130a7fd000 +00810 Type: Creature Entry: 43005 Low: 2064' +SOCIAL_CUE guid=31 action='drink' cue=4 target='GUID Full: 0xf112b55b000001eb Ty +pe: Gameobject Entry: 177499 Low: 491' +SOCIAL_START guid=30 action='social_greet' target='GUID Full: 0xf1300ce100000765 + Type: Creature Entry: 3297 Low: 1893' approach=1 +MICRO_START guid=30 intent='social' location='poi_tele_823' action='social_greet +' durationMs=9000 +SOCIAL_END guid=32 action='social_listen' elapsedMs=19000 +SOCIAL_END guid=29 action='social_listen' elapsedMs=23000 +SOCIAL_FRONT guid=30 target='GUID Full: 0xf1300ce100000765 Type: Creature Entry: + 3297 Low: 1893' offset=-0.000 distance=2.21 +SOCIAL_CUE guid=30 action='social_greet' cue=0 target='GUID Full: 0xf1300ce10000 +0765 Type: Creature Entry: 3297 Low: 1893' +SOCIAL_START guid=32 action='campfire_rest' target='GUID Full: 0xf11327b10000023 +d Type: Gameobject Entry: 206769 Low: 573' approach=1 +MICRO_START guid=32 intent='social' location='poi_npc_321922' action='campfire_r +est' durationMs=42000 +MICRO_START guid=29 intent='social' location='poi_guard_111' action='rest' durat +ionMs=21000 +SOCIAL_FRONT guid=30 target='GUID Full: 0xf1300ce100000765 Type: Creature Entry: + 3297 Low: 1893' offset=-0.000 distance=2.21 +SOCIAL_CUE guid=30 action='social_greet' cue=8 target='GUID Full: 0xf1300ce10000 +0765 Type: Creature Entry: 3297 Low: 1893' +SOCIAL_END guid=30 action='social_greet' elapsedMs=14000 +SOCIAL_START guid=30 action='social_npc' target='GUID Full: 0xf13029b400000766 T +ype: Creature Entry: 10676 Low: 1894' approach=1 +MICRO_START guid=30 intent='social' location='poi_tele_823' action='social_npc' +durationMs=23000 +SOCIAL_CUE guid=32 action='campfire_rest' cue=13 target='GUID Full: 0xf11327b100 +00023d Type: Gameobject Entry: 206769 Low: 573' +SOCIAL_FRONT guid=30 target='GUID Full: 0xf13029b400000766 Type: Creature Entry: + 10676 Low: 1894' offset=0.000 distance=2.21 +SOCIAL_CUE guid=30 action='social_npc' cue=0 target='GUID Full: 0xf13029b4000007 +66 Type: Creature Entry: 10676 Low: 1894' +SOCIAL_CUE guid=32 action='campfire_rest' cue=14 target='GUID Full: 0xf11327b100 +00023d Type: Gameobject Entry: 206769 Low: 573' +SOCIAL_END guid=31 action='drink' elapsedMs=39000 +MICRO_START guid=31 intent='social' location='poi_npc_280710' action='nod' durat +ionMs=11000 +SOCIAL_FRONT guid=30 target='GUID Full: 0xf13029b400000766 Type: Creature Entry: + 10676 Low: 1894' offset=0.000 distance=2.21 +SOCIAL_CUE guid=30 action='social_npc' cue=1 target='GUID Full: 0xf13029b4000007 +66 Type: Creature Entry: 10676 Low: 1894' +SOCIAL_FRONT guid=30 target='GUID Full: 0xf13029b400000766 Type: Creature Entry: + 10676 Low: 1894' offset=0.000 distance=2.21 +SOCIAL_CUE guid=30 action='social_npc' cue=4 target='GUID Full: 0xf13029b4000007 +66 Type: Creature Entry: 10676 Low: 1894' +SOCIAL_START guid=29 action='social_npc' target='GUID Full: 0xf1300e1d00000814 T +ype: Creature Entry: 3613 Low: 2068' approach=1 +MICRO_START guid=29 intent='social' location='poi_guard_111' action='social_npc' + durationMs=31000 +SOCIAL_START guid=31 action='social_listen' target='GUID Full: 0xf13001d1000004f +b Type: Creature Entry: 465 Low: 1275' approach=1 +MICRO_START guid=31 intent='social' location='poi_npc_280710' action='social_lis +ten' durationMs=20000 +SOCIAL_FRONT guid=30 target='GUID Full: 0xf13029b400000766 Type: Creature Entry: + 10676 Low: 1894' offset=0.000 distance=2.21 +SOCIAL_CUE guid=30 action='social_npc' cue=5 target='GUID Full: 0xf13029b4000007 +66 Type: Creature Entry: 10676 Low: 1894' +SOCIAL_FRONT guid=29 target='GUID Full: 0xf1300e1d00000814 Type: Creature Entry: + 3613 Low: 2068' offset=-0.000 distance=2.51 +SOCIAL_CUE guid=29 action='social_npc' cue=0 target='GUID Full: 0xf1300e1d000008 +14 Type: Creature Entry: 3613 Low: 2068' +SOCIAL_FRONT guid=30 target='GUID Full: 0xf13029b400000766 Type: Creature Entry: + 10676 Low: 1894' offset=0.000 distance=2.21 +SOCIAL_CUE guid=30 action='social_npc' cue=1 target='GUID Full: 0xf13029b4000007 +66 Type: Creature Entry: 10676 Low: 1894' +SOCIAL_FRONT guid=29 target='GUID Full: 0xf1300e1d00000814 Type: Creature Entry: + 3613 Low: 2068' offset=-0.000 distance=2.51 +SOCIAL_CUE guid=29 action='social_npc' cue=1 target='GUID Full: 0xf1300e1d000008 +14 Type: Creature Entry: 3613 Low: 2068' +clientless generate equip 29 +TC>clientless generate equip 30 +TC>clientless generate equip 31 +TC>clientless generate equip 32 +TC>saveall +TC>SOCIAL_END guid=29 action='social_npc' elapsedMs=13000 +Profile equipment refreshed through native checks; previous items retained in ba +gs. +BOT_EQUIPMENT_REFRESH guid=29 archetype='warrior' +TC> SOCIAL_END guid=30 action='social_npc' elapsedMs=24000 +Profile equipment refreshed through native checks; previous items retained in ba +gs. +BOT_EQUIPMENT_REFRESH guid=30 archetype='warrior' +TC> SOCIAL_END guid=31 action='social_listen' elapsedMs=7000 +Profile equipment refreshed through native checks; previous items retained in ba +gs. +BOT_EQUIPMENT_REFRESH guid=31 archetype='mage' +TC> SOCIAL_END guid=32 action='campfire_rest' elapsedMs=34000 +Profile equipment refreshed through native checks; previous items retained in ba +gs. +BOT_EQUIPMENT_REFRESH guid=32 archetype='death_knight' +TC> All players saved. +TC> INTENT_TRAVEL guid=32 intent='social' location='poi_npc_321922' area=0 +INTENT_TRAVEL guid=31 intent='social' location='poi_npc_280710' area=0 +INTENT_TRAVEL guid=30 intent='social' location='poi_tele_823' area=0 +INTENT_TRAVEL guid=29 intent='social' location='poi_guard_111' area=0 +INTENT_ARRIVED guid=31 intent='social' location='poi_npc_280710' +SOCIAL_START guid=31 action='rest' target='GUID Full: 0xf110591c000001e9 Type: G +ameobject Entry: 22812 Low: 489' approach=1 +MICRO_START guid=31 intent='social' location='poi_npc_280710' action='rest' dura +tionMs=37000 +INTENT_ARRIVED guid=30 intent='social' location='poi_tele_823' +MICRO_START guid=30 intent='social' location='poi_tele_823' action='wave' durati +onMs=18000 +INTENT_ARRIVED guid=29 intent='social' location='poi_guard_111' +MICRO_START guid=29 intent='social' location='poi_guard_111' action='rest' durat +ionMs=34000 +INTENT_ARRIVED guid=32 intent='social' location='poi_npc_321922' +MICRO_START guid=32 intent='social' location='poi_npc_321922' action='wave' dura +tionMs=8000 +SOCIAL_SEATED guid=31 chair='GUID Full: 0xf110591c000001e9 Type: Gameobject Entr +y: 22812 Low: 489' position=(-9475.510 29.792 56.343) slotError=0.000 stand=5 m +ovementGuid=31 +SOCIAL_START guid=32 action='social_react' target='GUID Full: 0xf130b1b8000005c6 + Type: Creature Entry: 45496 Low: 1478' approach=1 +MICRO_START guid=32 intent='social' location='poi_npc_321922' action='social_rea +ct' durationMs=7000 +SOCIAL_FRONT guid=32 target='GUID Full: 0xf130b1b8000005c6 Type: Creature Entry: + 45496 Low: 1478' offset=0.000 distance=2.20 +SOCIAL_CUE guid=32 action='social_react' cue=5 target='GUID Full: 0xf130b1b80000 +05c6 Type: Creature Entry: 45496 Low: 1478' +SOCIAL_FRONT guid=32 target='GUID Full: 0xf130b1b8000005c6 Type: Creature Entry: + 45496 Low: 1478' offset=0.000 distance=2.20 +SOCIAL_CUE guid=32 action='social_react' cue=4 target='GUID Full: 0xf130b1b80000 +05c6 Type: Creature Entry: 45496 Low: 1478' +SOCIAL_START guid=30 action='social_farewell' target='GUID Full: 0xf13029b400000 +766 Type: Creature Entry: 10676 Low: 1894' approach=1 +MICRO_START guid=30 intent='social' location='poi_tele_823' action='social_farew +ell' durationMs=5000 +SOCIAL_END guid=32 action='social_react' elapsedMs=10000 +SOCIAL_FRONT guid=30 target='GUID Full: 0xf13029b400000766 Type: Creature Entry: + 10676 Low: 1894' offset=0.000 distance=2.21 +SOCIAL_CUE guid=30 action='social_farewell' cue=7 target='GUID Full: 0xf13029b40 +0000766 Type: Creature Entry: 10676 Low: 1894' +MICRO_START guid=32 intent='social' location='poi_npc_321922' action='drink' dur +ationMs=17000 +SOCIAL_END guid=30 action='social_farewell' elapsedMs=6000 +SOCIAL_START guid=30 action='social_listen' target='GUID Full: 0xf13029b40000076 +6 Type: Creature Entry: 10676 Low: 1894' approach=0 +MICRO_START guid=30 intent='social' location='poi_tele_823' action='social_liste +n' durationMs=18000 +SOCIAL_FRONT guid=30 target='GUID Full: 0xf13029b400000766 Type: Creature Entry: + 10676 Low: 1894' offset=0.000 distance=2.21 +SOCIAL_CUE guid=30 action='social_listen' cue=4 target='GUID Full: 0xf13029b4000 +00766 Type: Creature Entry: 10676 Low: 1894' +SOCIAL_START guid=29 action='campfire_rest' target='GUID Full: 0x000000000000000 +0 Type: None Low: 0' approach=0 +MICRO_START guid=29 intent='social' location='poi_guard_111' action='campfire_re +st' durationMs=59000 +SOCIAL_FIRE_CAST guid=29 spell=818 +SOCIAL_FRONT guid=30 target='GUID Full: 0xf13029b400000766 Type: Creature Entry: + 10676 Low: 1894' offset=0.000 distance=2.21 +SOCIAL_CUE guid=30 action='social_listen' cue=6 target='GUID Full: 0xf13029b4000 +00766 Type: Creature Entry: 10676 Low: 1894' +SOCIAL_FIRE_CONFIRMED guid=29 spell=818 object='GUID Full: 0xf11074580000075e Ty +pe: Gameobject Entry: 29784 Low: 1886' +MICRO_START guid=32 intent='social' location='poi_npc_321922' action='eat' durat +ionMs=34000 +SOCIAL_CUE guid=29 action='campfire_rest' cue=14 target='GUID Full: 0xf110745800 +00075e Type: Gameobject Entry: 29784 Low: 1886' +SOCIAL_FRONT guid=30 target='GUID Full: 0xf13029b400000766 Type: Creature Entry: + 10676 Low: 1894' offset=0.000 distance=2.21 +SOCIAL_CUE guid=30 action='social_listen' cue=4 target='GUID Full: 0xf13029b4000 +00766 Type: Creature Entry: 10676 Low: 1894' +SOCIAL_END guid=31 action='rest' elapsedMs=46000 +SOCIAL_END guid=30 action='social_listen' elapsedMs=19000 +SOCIAL_START guid=31 action='campfire_rest' target='GUID Full: 0xf110080d000001f +e Type: Gameobject Entry: 2061 Low: 510' approach=1 +MICRO_START guid=31 intent='social' location='poi_npc_280710' action='campfire_r +est' durationMs=63000 +SOCIAL_START guid=30 action='speak' target='GUID Full: 0xf1300ce100000765 Type: +Creature Entry: 3297 Low: 1893' approach=1 +MICRO_START guid=30 intent='social' location='poi_tele_823' action='speak' durat +ionMs=15000 +SOCIAL_FRONT guid=30 target='GUID Full: 0xf1300ce100000765 Type: Creature Entry: + 3297 Low: 1893' offset=-0.000 distance=2.21 +SOCIAL_CUE guid=30 action='speak' cue=0 target='GUID Full: 0xf1300ce100000765 Ty +pe: Creature Entry: 3297 Low: 1893' +SOCIAL_FRONT guid=30 target='GUID Full: 0xf1300ce100000765 Type: Creature Entry: + 3297 Low: 1893' offset=-0.000 distance=2.21 +SOCIAL_CUE guid=30 action='speak' cue=1 target='GUID Full: 0xf1300ce100000765 Ty +pe: Creature Entry: 3297 Low: 1893' +SOCIAL_CUE guid=31 action='campfire_rest' cue=13 target='GUID Full: 0xf110080d00 +0001fe Type: Gameobject Entry: 2061 Low: 510' +SOCIAL_CUE guid=31 action='campfire_rest' cue=14 target='GUID Full: 0xf110080d00 +0001fe Type: Gameobject Entry: 2061 Low: 510' +SOCIAL_FRONT guid=30 target='GUID Full: 0xf1300ce100000765 Type: Creature Entry: + 3297 Low: 1893' offset=-0.000 distance=2.21 +SOCIAL_CUE guid=30 action='speak' cue=4 target='GUID Full: 0xf1300ce100000765 Ty +pe: Creature Entry: 3297 Low: 1893' +MICRO_START guid=32 intent='social' location='poi_npc_321922' action='drink' dur +ationMs=29000 +SOCIAL_FRONT guid=30 target='GUID Full: 0xf1300ce100000765 Type: Creature Entry: + 3297 Low: 1893' offset=-0.000 distance=2.21 +SOCIAL_CUE guid=30 action='speak' cue=5 target='GUID Full: 0xf1300ce100000765 Ty +pe: Creature Entry: 3297 Low: 1893' +SOCIAL_CUE guid=29 action='campfire_rest' cue=10 target='GUID Full: 0xf110745800 +00075e Type: Gameobject Entry: 29784 Low: 1886' +SOCIAL_FRONT guid=30 target='GUID Full: 0xf1300ce100000765 Type: Creature Entry: + 3297 Low: 1893' offset=-0.000 distance=2.21 +SOCIAL_CUE guid=30 action='speak' cue=1 target='GUID Full: 0xf1300ce100000765 Ty +pe: Creature Entry: 3297 Low: 1893' +SOCIAL_FRONT guid=30 target='GUID Full: 0xf1300ce100000765 Type: Creature Entry: + 3297 Low: 1893' offset=-0.000 distance=2.21 +SOCIAL_CUE guid=30 action='speak' cue=7 target='GUID Full: 0xf1300ce100000765 Ty +pe: Creature Entry: 3297 Low: 1893' +SOCIAL_CUE guid=29 action='campfire_rest' cue=14 target='GUID Full: 0xf110745800 +00075e Type: Gameobject Entry: 29784 Low: 1886' +SOCIAL_END guid=30 action='speak' elapsedMs=30000 +MICRO_START guid=30 intent='social' location='poi_tele_823' action='rest' durati +onMs=32000 +SOCIAL_START guid=32 action='social_order' target='GUID Full: 0xf130b1b8000005c6 + Type: Creature Entry: 45496 Low: 1478' approach=0 +MICRO_START guid=32 intent='social' location='poi_npc_321922' action='social_ord +er' durationMs=30000 +SOCIAL_FRONT guid=32 target='GUID Full: 0xf130b1b8000005c6 Type: Creature Entry: + 45496 Low: 1478' offset=0.000 distance=2.20 +SOCIAL_CUE guid=32 action='social_order' cue=0 target='GUID Full: 0xf130b1b80000 +05c6 Type: Creature Entry: 45496 Low: 1478' +SOCIAL_CUE guid=31 action='campfire_rest' cue=10 target='GUID Full: 0xf110080d00 +0001fe Type: Gameobject Entry: 2061 Low: 510' +SOCIAL_FRONT guid=32 target='GUID Full: 0xf130b1b8000005c6 Type: Creature Entry: + 45496 Low: 1478' offset=0.000 distance=2.20 +SOCIAL_CUE guid=32 action='social_order' cue=2 target='GUID Full: 0xf130b1b80000 +05c6 Type: Creature Entry: 45496 Low: 1478' +SOCIAL_CUE guid=31 action='campfire_rest' cue=14 target='GUID Full: 0xf110080d00 +0001fe Type: Gameobject Entry: 2061 Low: 510' +SOCIAL_FRONT guid=32 target='GUID Full: 0xf130b1b8000005c6 Type: Creature Entry: + 45496 Low: 1478' offset=0.000 distance=2.20 +SOCIAL_CUE guid=32 action='social_order' cue=4 target='GUID Full: 0xf130b1b80000 +05c6 Type: Creature Entry: 45496 Low: 1478' +MICRO_START guid=30 intent='social' location='poi_tele_823' action='drink' durat +ionMs=32000 +SOCIAL_END guid=29 action='campfire_rest' elapsedMs=74000 +SOCIAL_FRONT guid=32 target='GUID Full: 0xf130b1b8000005c6 Type: Creature Entry: + 45496 Low: 1478' offset=0.000 distance=2.20 +SOCIAL_CUE guid=32 action='social_order' cue=10 target='GUID Full: 0xf130b1b8000 +005c6 Type: Creature Entry: 45496 Low: 1478' +MICRO_START guid=29 intent='social' location='poi_guard_111' action='drink' dura +tionMs=26000 +SOCIAL_FRONT guid=32 target='GUID Full: 0xf130b1b8000005c6 Type: Creature Entry: + 45496 Low: 1478' offset=0.000 distance=2.20 +SOCIAL_CUE guid=32 action='social_order' cue=7 target='GUID Full: 0xf130b1b80000 +05c6 Type: Creature Entry: 45496 Low: 1478' +SOCIAL_END guid=32 action='social_order' elapsedMs=25000 +SOCIAL_START guid=32 action='social_greet' target='GUID Full: 0xf130b1b8000005c6 + Type: Creature Entry: 45496 Low: 1478' approach=0 +MICRO_START guid=32 intent='social' location='poi_npc_321922' action='social_gre +et' durationMs=7000 +SOCIAL_FRONT guid=32 target='GUID Full: 0xf130b1b8000005c6 Type: Creature Entry: + 45496 Low: 1478' offset=0.000 distance=2.20 +SOCIAL_CUE guid=32 action='social_greet' cue=0 target='GUID Full: 0xf130b1b80000 +05c6 Type: Creature Entry: 45496 Low: 1478' +SOCIAL_FRONT guid=32 target='GUID Full: 0xf130b1b8000005c6 Type: Creature Entry: + 45496 Low: 1478' offset=0.000 distance=2.20 +SOCIAL_CUE guid=32 action='social_greet' cue=8 target='GUID Full: 0xf130b1b80000 +05c6 Type: Creature Entry: 45496 Low: 1478' +SOCIAL_END guid=32 action='social_greet' elapsedMs=9000 +SOCIAL_START guid=29 action='social_npc' target='GUID Full: 0xf1300df300000805 T +ype: Creature Entry: 3571 Low: 2053' approach=1 +MICRO_START guid=29 intent='social' location='poi_guard_111' action='social_npc' + durationMs=35000 +SOCIAL_START guid=32 action='speak' target='GUID Full: 0xf130b1b8000005c6 Type: +Creature Entry: 45496 Low: 1478' approach=0 +MICRO_START guid=32 intent='social' location='poi_npc_321922' action='speak' dur +ationMs=16000 +SOCIAL_FRONT guid=32 target='GUID Full: 0xf130b1b8000005c6 Type: Creature Entry: + 45496 Low: 1478' offset=0.000 distance=2.20 +SOCIAL_CUE guid=32 action='speak' cue=0 target='GUID Full: 0xf130b1b8000005c6 Ty +pe: Creature Entry: 45496 Low: 1478' +SOCIAL_START guid=30 action='social_npc' target='GUID Full: 0xf1300ce100000765 T +ype: Creature Entry: 3297 Low: 1893' approach=0 +MICRO_START guid=30 intent='social' location='poi_tele_823' action='social_npc' +durationMs=35000 +SOCIAL_END guid=31 action='campfire_rest' elapsedMs=83000 +SOCIAL_FRONT guid=30 target='GUID Full: 0xf1300ce100000765 Type: Creature Entry: + 3297 Low: 1893' offset=-0.000 distance=2.21 +SOCIAL_CUE guid=30 action='social_npc' cue=0 target='GUID Full: 0xf1300ce1000007 +65 Type: Creature Entry: 3297 Low: 1893' +SOCIAL_START guid=31 action='speak' target='GUID Full: 0xf1301a7a000004de Type: +Creature Entry: 6778 Low: 1246' approach=1 +MICRO_START guid=31 intent='social' location='poi_npc_280710' action='speak' dur +ationMs=19000 +SOCIAL_FRONT guid=29 target='GUID Full: 0xf1300df300000805 Type: Creature Entry: + 3571 Low: 2053' offset=-0.000 distance=2.38 +SOCIAL_CUE guid=29 action='social_npc' cue=0 target='GUID Full: 0xf1300df3000008 +05 Type: Creature Entry: 3571 Low: 2053' +SOCIAL_FRONT guid=32 target='GUID Full: 0xf130b1b8000005c6 Type: Creature Entry: + 45496 Low: 1478' offset=0.000 distance=2.20 +SOCIAL_CUE guid=32 action='speak' cue=1 target='GUID Full: 0xf130b1b8000005c6 Ty +pe: Creature Entry: 45496 Low: 1478' +SOCIAL_FRONT guid=31 target='GUID Full: 0xf1301a7a000004de Type: Creature Entry: + 6778 Low: 1246' offset=0.000 distance=2.20 +SOCIAL_CUE guid=31 action='speak' cue=0 target='GUID Full: 0xf1301a7a000004de Ty +pe: Creature Entry: 6778 Low: 1246' +SOCIAL_FRONT guid=30 target='GUID Full: 0xf1300ce100000765 Type: Creature Entry: + 3297 Low: 1893' offset=-0.000 distance=2.21 +SOCIAL_CUE guid=30 action='social_npc' cue=1 target='GUID Full: 0xf1300ce1000007 +65 Type: Creature Entry: 3297 Low: 1893' +SOCIAL_FRONT guid=29 target='GUID Full: 0xf1300df300000805 Type: Creature Entry: + 3571 Low: 2053' offset=-0.000 distance=2.38 +SOCIAL_CUE guid=29 action='social_npc' cue=1 target='GUID Full: 0xf1300df3000008 +05 Type: Creature Entry: 3571 Low: 2053' +SOCIAL_FRONT guid=32 target='GUID Full: 0xf130b1b8000005c6 Type: Creature Entry: + 45496 Low: 1478' offset=0.000 distance=2.20 +SOCIAL_CUE guid=32 action='speak' cue=4 target='GUID Full: 0xf130b1b8000005c6 Ty +pe: Creature Entry: 45496 Low: 1478' +SOCIAL_FRONT guid=31 target='GUID Full: 0xf1301a7a000004de Type: Creature Entry: + 6778 Low: 1246' offset=0.000 distance=2.20 +SOCIAL_CUE guid=31 action='speak' cue=1 target='GUID Full: 0xf1301a7a000004de Ty +pe: Creature Entry: 6778 Low: 1246' +SOCIAL_FRONT guid=30 target='GUID Full: 0xf1300ce100000765 Type: Creature Entry: + 3297 Low: 1893' offset=-0.000 distance=2.21 +SOCIAL_CUE guid=30 action='social_npc' cue=4 target='GUID Full: 0xf1300ce1000007 +65 Type: Creature Entry: 3297 Low: 1893' +SOCIAL_FRONT guid=29 target='GUID Full: 0xf1300df300000805 Type: Creature Entry: + 3571 Low: 2053' offset=-0.000 distance=2.38 +SOCIAL_CUE guid=29 action='social_npc' cue=4 target='GUID Full: 0xf1300df3000008 +05 Type: Creature Entry: 3571 Low: 2053' +SOCIAL_FRONT guid=31 target='GUID Full: 0xf1301a7a000004de Type: Creature Entry: + 6778 Low: 1246' offset=0.000 distance=2.20 +SOCIAL_CUE guid=31 action='speak' cue=4 target='GUID Full: 0xf1301a7a000004de Ty +pe: Creature Entry: 6778 Low: 1246' +SOCIAL_FRONT guid=32 target='GUID Full: 0xf130b1b8000005c6 Type: Creature Entry: + 45496 Low: 1478' offset=0.000 distance=2.20 +SOCIAL_CUE guid=32 action='speak' cue=5 target='GUID Full: 0xf130b1b8000005c6 Ty +pe: Creature Entry: 45496 Low: 1478' +SOCIAL_FRONT guid=30 target='GUID Full: 0xf1300ce100000765 Type: Creature Entry: + 3297 Low: 1893' offset=-0.000 distance=2.21 +SOCIAL_CUE guid=30 action='social_npc' cue=5 target='GUID Full: 0xf1300ce1000007 +65 Type: Creature Entry: 3297 Low: 1893' +SOCIAL_FRONT guid=29 target='GUID Full: 0xf1300df300000805 Type: Creature Entry: + 3571 Low: 2053' offset=-0.000 distance=2.38 +SOCIAL_CUE guid=29 action='social_npc' cue=5 target='GUID Full: 0xf1300df3000008 +05 Type: Creature Entry: 3571 Low: 2053' +SOCIAL_FRONT guid=32 target='GUID Full: 0xf130b1b8000005c6 Type: Creature Entry: + 45496 Low: 1478' offset=0.000 distance=2.20 +SOCIAL_CUE guid=32 action='speak' cue=1 target='GUID Full: 0xf130b1b8000005c6 Ty +pe: Creature Entry: 45496 Low: 1478' +SOCIAL_FRONT guid=31 target='GUID Full: 0xf1301a7a000004de Type: Creature Entry: + 6778 Low: 1246' offset=0.000 distance=2.20 +SOCIAL_CUE guid=31 action='speak' cue=5 target='GUID Full: 0xf1301a7a000004de Ty +pe: Creature Entry: 6778 Low: 1246' +SOCIAL_FRONT guid=30 target='GUID Full: 0xf1300ce100000765 Type: Creature Entry: + 3297 Low: 1893' offset=-0.000 distance=2.21 +SOCIAL_CUE guid=30 action='social_npc' cue=1 target='GUID Full: 0xf1300ce1000007 +65 Type: Creature Entry: 3297 Low: 1893' +SOCIAL_FRONT guid=29 target='GUID Full: 0xf1300df300000805 Type: Creature Entry: + 3571 Low: 2053' offset=-0.000 distance=2.38 +SOCIAL_CUE guid=29 action='social_npc' cue=1 target='GUID Full: 0xf1300df3000008 +05 Type: Creature Entry: 3571 Low: 2053' +SOCIAL_FRONT guid=32 target='GUID Full: 0xf130b1b8000005c6 Type: Creature Entry: + 45496 Low: 1478' offset=0.000 distance=2.20 +SOCIAL_CUE guid=32 action='speak' cue=7 target='GUID Full: 0xf130b1b8000005c6 Ty +pe: Creature Entry: 45496 Low: 1478' +SOCIAL_FRONT guid=31 target='GUID Full: 0xf1301a7a000004de Type: Creature Entry: + 6778 Low: 1246' offset=0.000 distance=2.20 +SOCIAL_CUE guid=31 action='speak' cue=1 target='GUID Full: 0xf1301a7a000004de Ty +pe: Creature Entry: 6778 Low: 1246' +SOCIAL_FRONT guid=30 target='GUID Full: 0xf1300ce100000765 Type: Creature Entry: + 3297 Low: 1893' offset=-0.000 distance=2.21 +SOCIAL_CUE guid=30 action='social_npc' cue=7 target='GUID Full: 0xf1300ce1000007 +65 Type: Creature Entry: 3297 Low: 1893' +SOCIAL_END guid=32 action='speak' elapsedMs=29000 +SOCIAL_FRONT guid=29 target='GUID Full: 0xf1300df300000805 Type: Creature Entry: + 3571 Low: 2053' offset=-0.000 distance=2.38 +SOCIAL_CUE guid=29 action='social_npc' cue=7 target='GUID Full: 0xf1300df3000008 +05 Type: Creature Entry: 3571 Low: 2053' +SOCIAL_END guid=30 action='social_npc' elapsedMs=29000 +SOCIAL_FRONT guid=31 target='GUID Full: 0xf1301a7a000004de Type: Creature Entry: + 6778 Low: 1246' offset=0.000 distance=2.20 +SOCIAL_CUE guid=31 action='speak' cue=7 target='GUID Full: 0xf1301a7a000004de Ty +pe: Creature Entry: 6778 Low: 1246' +MICRO_START guid=32 intent='social' location='poi_npc_321922' action='drink' dur +ationMs=32000 +SOCIAL_END guid=29 action='social_npc' elapsedMs=33000 +MICRO_START guid=30 intent='social' location='poi_tele_823' action='eat' duratio +nMs=34000 +SOCIAL_END guid=31 action='speak' elapsedMs=31000 +SOCIAL_START guid=29 action='social_greet' target='GUID Full: 0xf13087c90000080a + Type: Creature Entry: 34761 Low: 2058' approach=1 +MICRO_START guid=29 intent='social' location='poi_guard_111' action='social_gree +t' durationMs=8000 +SOCIAL_START guid=31 action='social_react' target='GUID Full: 0xf13000fd000004f9 + Type: Creature Entry: 253 Low: 1273' approach=1 +MICRO_START guid=31 intent='social' location='poi_npc_280710' action='social_rea +ct' durationMs=6000 +SOCIAL_FRONT guid=29 target='GUID Full: 0xf13087c90000080a Type: Creature Entry: + 34761 Low: 2058' offset=0.000 distance=2.20 +SOCIAL_CUE guid=29 action='social_greet' cue=0 target='GUID Full: 0xf13087c90000 +080a Type: Creature Entry: 34761 Low: 2058' +SOCIAL_FRONT guid=31 target='GUID Full: 0xf13000fd000004f9 Type: Creature Entry: + 253 Low: 1273' offset=-0.000 distance=2.20 +SOCIAL_CUE guid=31 action='social_react' cue=5 target='GUID Full: 0xf13000fd0000 +04f9 Type: Creature Entry: 253 Low: 1273' +SOCIAL_FRONT guid=29 target='GUID Full: 0xf13087c90000080a Type: Creature Entry: + 34761 Low: 2058' offset=0.000 distance=2.20 +SOCIAL_CUE guid=29 action='social_greet' cue=8 target='GUID Full: 0xf13087c90000 +080a Type: Creature Entry: 34761 Low: 2058' +SOCIAL_FRONT guid=31 target='GUID Full: 0xf13000fd000004f9 Type: Creature Entry: + 253 Low: 1273' offset=-0.000 distance=2.20 +SOCIAL_CUE guid=31 action='social_react' cue=4 target='GUID Full: 0xf13000fd0000 +04f9 Type: Creature Entry: 253 Low: 1273' +SOCIAL_END guid=29 action='social_greet' elapsedMs=13000 +SOCIAL_END guid=31 action='social_react' elapsedMs=14000 +SOCIAL_START guid=29 action='social_farewell' target='GUID Full: 0xf13087c900000 +80a Type: Creature Entry: 34761 Low: 2058' approach=0 +MICRO_START guid=29 intent='social' location='poi_guard_111' action='social_fare +well' durationMs=6000 +SOCIAL_FRONT guid=29 target='GUID Full: 0xf13087c90000080a Type: Creature Entry: + 34761 Low: 2058' offset=0.000 distance=2.20 +SOCIAL_CUE guid=29 action='social_farewell' cue=7 target='GUID Full: 0xf13087c90 +000080a Type: Creature Entry: 34761 Low: 2058' +SOCIAL_START guid=31 action='social_counter' target='GUID Full: 0xf1300127000004 +fa Type: Creature Entry: 295 Low: 1274' approach=1 +MICRO_START guid=31 intent='social' location='poi_npc_280710' action='social_cou +nter' durationMs=28000 +SOCIAL_END guid=29 action='social_farewell' elapsedMs=5000 +MICRO_START guid=29 intent='social' location='poi_guard_111' action='look' durat +ionMs=13000 +SOCIAL_FRONT guid=31 target='GUID Full: 0xf1300127000004fa Type: Creature Entry: + 295 Low: 1274' offset=-0.000 distance=2.20 +SOCIAL_CUE guid=31 action='social_counter' cue=8 target='GUID Full: 0xf130012700 +0004fa Type: Creature Entry: 295 Low: 1274' +SOCIAL_START guid=32 action='campfire_rest' target='GUID Full: 0xf11327b10000023 +d Type: Gameobject Entry: 206769 Low: 573' approach=1 +MICRO_START guid=32 intent='social' location='poi_npc_321922' action='campfire_r +est' durationMs=61000 +SOCIAL_START guid=30 action='social_fire' target='GUID Full: 0x0000000000000000 +Type: None Low: 0' approach=0 +MICRO_START guid=30 intent='social' location='poi_tele_823' action='social_fire' + durationMs=39000 +SOCIAL_FRONT guid=31 target='GUID Full: 0xf1300127000004fa Type: Creature Entry: + 295 Low: 1274' offset=-0.000 distance=2.20 +SOCIAL_CUE guid=31 action='social_counter' cue=4 target='GUID Full: 0xf130012700 +0004fa Type: Creature Entry: 295 Low: 1274' +SOCIAL_FIRE_CAST guid=30 spell=818 +SOCIAL_FIRE_CONFIRMED guid=30 spell=818 object='GUID Full: 0xf110745800000760 Ty +pe: Gameobject Entry: 29784 Low: 1888' +SOCIAL_CUE guid=30 action='social_fire' cue=14 target='GUID Full: 0xf11074580000 +0760 Type: Gameobject Entry: 29784 Low: 1888' +MICRO_START guid=29 intent='social' location='poi_guard_111' action='cheer' dura +tionMs=18000 +SOCIAL_CUE guid=32 action='campfire_rest' cue=13 target='GUID Full: 0xf11327b100 +00023d Type: Gameobject Entry: 206769 Low: 573' +SOCIAL_CUE guid=32 action='campfire_rest' cue=14 target='GUID Full: 0xf11327b100 +00023d Type: Gameobject Entry: 206769 Low: 573' +SOCIAL_FRONT guid=31 target='GUID Full: 0xf1300127000004fa Type: Creature Entry: + 295 Low: 1274' offset=-0.000 distance=2.20 +SOCIAL_CUE guid=31 action='social_counter' cue=8 target='GUID Full: 0xf130012700 +0004fa Type: Creature Entry: 295 Low: 1274' +SOCIAL_END guid=31 action='social_counter' elapsedMs=29000 +SOCIAL_START guid=31 action='campfire_rest' target='GUID Full: 0xf110080d0000020 +2 Type: Gameobject Entry: 2061 Low: 514' approach=1 +MICRO_START guid=31 intent='social' location='poi_npc_280710' action='campfire_r +est' durationMs=42000 +SOCIAL_START guid=29 action='speak' target='GUID Full: 0xf1300df300000806 Type: +Creature Entry: 3571 Low: 2054' approach=1 +MICRO_START guid=29 intent='social' location='poi_guard_111' action='speak' dura +tionMs=22000 +SOCIAL_FRONT guid=29 target='GUID Full: 0xf1300df300000806 Type: Creature Entry: + 3571 Low: 2054' offset=0.000 distance=2.20 +SOCIAL_CUE guid=29 action='speak' cue=0 target='GUID Full: 0xf1300df300000806 Ty +pe: Creature Entry: 3571 Low: 2054' +SOCIAL_CUE guid=30 action='social_fire' cue=10 target='GUID Full: 0xf11074580000 +0760 Type: Gameobject Entry: 29784 Low: 1888' +SOCIAL_END guid=31 action='campfire_rest' elapsedMs=8000 +SOCIAL_FRONT guid=29 target='GUID Full: 0xf1300df300000806 Type: Creature Entry: + 3571 Low: 2054' offset=0.000 distance=2.20 +SOCIAL_CUE guid=29 action='speak' cue=1 target='GUID Full: 0xf1300df300000806 Ty +pe: Creature Entry: 3571 Low: 2054' +SOCIAL_START guid=31 action='social_order' target='GUID Full: 0xf1300127000004fa + Type: Creature Entry: 295 Low: 1274' approach=1 +MICRO_START guid=31 intent='social' location='poi_npc_280710' action='social_ord +er' durationMs=25000 +SOCIAL_CUE guid=30 action='social_fire' cue=14 target='GUID Full: 0xf11074580000 +0760 Type: Gameobject Entry: 29784 Low: 1888' +SOCIAL_FRONT guid=29 target='GUID Full: 0xf1300df300000806 Type: Creature Entry: + 3571 Low: 2054' offset=0.000 distance=2.20 +SOCIAL_CUE guid=29 action='speak' cue=4 target='GUID Full: 0xf1300df300000806 Ty +pe: Creature Entry: 3571 Low: 2054' +SOCIAL_FRONT guid=31 target='GUID Full: 0xf1300127000004fa Type: Creature Entry: + 295 Low: 1274' offset=-0.047 distance=2.27 +SOCIAL_CUE guid=31 action='social_order' cue=0 target='GUID Full: 0xf13001270000 +04fa Type: Creature Entry: 295 Low: 1274' +SOCIAL_FRONT guid=31 target='GUID Full: 0xf1300127000004fa Type: Creature Entry: + 295 Low: 1274' offset=-0.047 distance=2.27 +SOCIAL_CUE guid=31 action='social_order' cue=2 target='GUID Full: 0xf13001270000 +04fa Type: Creature Entry: 295 Low: 1274' +SOCIAL_FRONT guid=29 target='GUID Full: 0xf1300df300000806 Type: Creature Entry: + 3571 Low: 2054' offset=0.000 distance=2.20 +SOCIAL_CUE guid=29 action='speak' cue=5 target='GUID Full: 0xf1300df300000806 Ty +pe: Creature Entry: 3571 Low: 2054' +SOCIAL_CUE guid=32 action='campfire_rest' cue=10 target='GUID Full: 0xf11327b100 +00023d Type: Gameobject Entry: 206769 Low: 573' +SOCIAL_FRONT guid=31 target='GUID Full: 0xf1300127000004fa Type: Creature Entry: + 295 Low: 1274' offset=-0.047 distance=2.27 +SOCIAL_CUE guid=31 action='social_order' cue=4 target='GUID Full: 0xf13001270000 +04fa Type: Creature Entry: 295 Low: 1274' +SOCIAL_FRONT guid=29 target='GUID Full: 0xf1300df300000806 Type: Creature Entry: + 3571 Low: 2054' offset=0.000 distance=2.20 +SOCIAL_CUE guid=29 action='speak' cue=1 target='GUID Full: 0xf1300df300000806 Ty +pe: Creature Entry: 3571 Low: 2054' +SOCIAL_CUE guid=32 action='campfire_rest' cue=14 target='GUID Full: 0xf11327b100 +00023d Type: Gameobject Entry: 206769 Low: 573' +SOCIAL_FRONT guid=31 target='GUID Full: 0xf1300127000004fa Type: Creature Entry: + 295 Low: 1274' offset=-0.047 distance=2.27 +SOCIAL_CUE guid=31 action='social_order' cue=10 target='GUID Full: 0xf1300127000 +004fa Type: Creature Entry: 295 Low: 1274' +SOCIAL_FRONT guid=29 target='GUID Full: 0xf1300df300000806 Type: Creature Entry: + 3571 Low: 2054' offset=0.000 distance=2.20 +SOCIAL_CUE guid=29 action='speak' cue=7 target='GUID Full: 0xf1300df300000806 Ty +pe: Creature Entry: 3571 Low: 2054' +SOCIAL_END guid=30 action='social_fire' elapsedMs=53000 +SOCIAL_END guid=29 action='speak' elapsedMs=30000 +SOCIAL_START guid=30 action='social_npc' target='GUID Full: 0xf13029b400000766 T +ype: Creature Entry: 10676 Low: 1894' approach=1 +MICRO_START guid=30 intent='social' location='poi_tele_823' action='social_npc' +durationMs=23000 +MICRO_START guid=29 intent='social' location='poi_guard_111' action='laugh' dura +tionMs=15000 +SOCIAL_FRONT guid=31 target='GUID Full: 0xf1300127000004fa Type: Creature Entry: + 295 Low: 1274' offset=-0.047 distance=2.27 +SOCIAL_CUE guid=31 action='social_order' cue=7 target='GUID Full: 0xf13001270000 +04fa Type: Creature Entry: 295 Low: 1274' +SOCIAL_FRONT guid=30 target='GUID Full: 0xf13029b400000766 Type: Creature Entry: + 10676 Low: 1894' offset=0.000 distance=2.21 +SOCIAL_CUE guid=30 action='social_npc' cue=0 target='GUID Full: 0xf13029b4000007 +66 Type: Creature Entry: 10676 Low: 1894' +SOCIAL_END guid=31 action='social_order' elapsedMs=29000 +SOCIAL_START guid=31 action='social_greet' target='GUID Full: 0xf1300097000004fd + Type: Creature Entry: 151 Low: 1277' approach=1 +MICRO_START guid=31 intent='social' location='poi_npc_280710' action='social_gre +et' durationMs=8000 +SOCIAL_FRONT guid=30 target='GUID Full: 0xf13029b400000766 Type: Creature Entry: + 10676 Low: 1894' offset=0.000 distance=2.21 +SOCIAL_CUE guid=30 action='social_npc' cue=1 target='GUID Full: 0xf13029b4000007 +66 Type: Creature Entry: 10676 Low: 1894' +SOCIAL_FRONT guid=31 target='GUID Full: 0xf1300097000004fd Type: Creature Entry: + 151 Low: 1277' offset=-0.000 distance=2.41 +SOCIAL_CUE guid=31 action='social_greet' cue=0 target='GUID Full: 0xf13000970000 +04fd Type: Creature Entry: 151 Low: 1277' +SOCIAL_FRONT guid=30 target='GUID Full: 0xf13029b400000766 Type: Creature Entry: + 10676 Low: 1894' offset=0.000 distance=2.21 +SOCIAL_CUE guid=30 action='social_npc' cue=4 target='GUID Full: 0xf13029b4000007 +66 Type: Creature Entry: 10676 Low: 1894' +SOCIAL_FRONT guid=31 target='GUID Full: 0xf1300097000004fd Type: Creature Entry: + 151 Low: 1277' offset=-0.000 distance=2.41 +SOCIAL_CUE guid=31 action='social_greet' cue=8 target='GUID Full: 0xf13000970000 +04fd Type: Creature Entry: 151 Low: 1277' +MICRO_START guid=29 intent='social' location='poi_guard_111' action='drink' dura +tionMs=23000 +SOCIAL_END guid=31 action='social_greet' elapsedMs=11000 +SOCIAL_FRONT guid=30 target='GUID Full: 0xf13029b400000766 Type: Creature Entry: + 10676 Low: 1894' offset=0.000 distance=2.21 +SOCIAL_CUE guid=30 action='social_npc' cue=5 target='GUID Full: 0xf13029b4000007 +66 Type: Creature Entry: 10676 Low: 1894' +MICRO_START guid=31 intent='social' location='poi_npc_280710' action='look' dura +tionMs=10000 +SOCIAL_FRONT guid=30 target='GUID Full: 0xf13029b400000766 Type: Creature Entry: + 10676 Low: 1894' offset=0.000 distance=2.21 +SOCIAL_CUE guid=30 action='social_npc' cue=1 target='GUID Full: 0xf13029b4000007 +66 Type: Creature Entry: 10676 Low: 1894' +SOCIAL_END guid=32 action='campfire_rest' elapsedMs=83000 +MICRO_START guid=32 intent='social' location='poi_npc_321922' action='rest' dura +tionMs=33000 +SOCIAL_FRONT guid=30 target='GUID Full: 0xf13029b400000766 Type: Creature Entry: + 10676 Low: 1894' offset=0.000 distance=2.21 +SOCIAL_CUE guid=30 action='social_npc' cue=7 target='GUID Full: 0xf13029b4000007 +66 Type: Creature Entry: 10676 Low: 1894' +SOCIAL_START guid=31 action='speak' target='GUID Full: 0xf1300127000004fa Type: +Creature Entry: 295 Low: 1274' approach=1 +MICRO_START guid=31 intent='social' location='poi_npc_280710' action='speak' dur +ationMs=18000 +SOCIAL_END guid=30 action='social_npc' elapsedMs=30000 +SOCIAL_FRONT guid=31 target='GUID Full: 0xf1300127000004fa Type: Creature Entry: + 295 Low: 1274' offset=-0.000 distance=2.20 +SOCIAL_CUE guid=31 action='speak' cue=0 target='GUID Full: 0xf1300127000004fa Ty +pe: Creature Entry: 295 Low: 1274' +SOCIAL_START guid=30 action='campfire_rest' target='GUID Full: 0xf11074580000076 +0 Type: Gameobject Entry: 29784 Low: 1888' approach=1 +MICRO_START guid=30 intent='social' location='poi_tele_823' action='campfire_res +t' durationMs=65000 +SOCIAL_FRONT guid=31 target='GUID Full: 0xf1300127000004fa Type: Creature Entry: + 295 Low: 1274' offset=-0.000 distance=2.20 +SOCIAL_CUE guid=31 action='speak' cue=1 target='GUID Full: 0xf1300127000004fa Ty +pe: Creature Entry: 295 Low: 1274' +SOCIAL_CUE guid=30 action='campfire_rest' cue=13 target='GUID Full: 0xf110745800 +000760 Type: Gameobject Entry: 29784 Low: 1888' +SOCIAL_CUE guid=30 action='campfire_rest' cue=14 target='GUID Full: 0xf110745800 +000760 Type: Gameobject Entry: 29784 Low: 1888' +SOCIAL_START guid=29 action='social_greet' target='GUID Full: 0xf1300e1a0000080c + Type: Creature Entry: 3610 Low: 2060' approach=1 +MICRO_START guid=29 intent='social' location='poi_guard_111' action='social_gree +t' durationMs=6000 +SOCIAL_FRONT guid=31 target='GUID Full: 0xf1300127000004fa Type: Creature Entry: + 295 Low: 1274' offset=-0.000 distance=2.20 +SOCIAL_CUE guid=31 action='speak' cue=4 target='GUID Full: 0xf1300127000004fa Ty +pe: Creature Entry: 295 Low: 1274' +SOCIAL_FRONT guid=29 target='GUID Full: 0xf1300e1a0000080c Type: Creature Entry: + 3610 Low: 2060' offset=-0.000 distance=2.20 +SOCIAL_CUE guid=29 action='social_greet' cue=0 target='GUID Full: 0xf1300e1a0000 +080c Type: Creature Entry: 3610 Low: 2060' +SOCIAL_FRONT guid=29 target='GUID Full: 0xf1300e1a0000080c Type: Creature Entry: + 3610 Low: 2060' offset=-0.000 distance=2.20 +SOCIAL_CUE guid=29 action='social_greet' cue=8 target='GUID Full: 0xf1300e1a0000 +080c Type: Creature Entry: 3610 Low: 2060' +SOCIAL_FRONT guid=31 target='GUID Full: 0xf1300127000004fa Type: Creature Entry: + 295 Low: 1274' offset=-0.000 distance=2.20 +SOCIAL_CUE guid=31 action='speak' cue=5 target='GUID Full: 0xf1300127000004fa Ty +pe: Creature Entry: 295 Low: 1274' +SOCIAL_END guid=29 action='social_greet' elapsedMs=12000 +SOCIAL_FRONT guid=31 target='GUID Full: 0xf1300127000004fa Type: Creature Entry: + 295 Low: 1274' offset=-0.000 distance=2.20 +SOCIAL_CUE guid=31 action='speak' cue=1 target='GUID Full: 0xf1300127000004fa Ty +pe: Creature Entry: 295 Low: 1274' +SOCIAL_START guid=29 action='social_npc' target='GUID Full: 0xf1300df30000080f T +ype: Creature Entry: 3571 Low: 2063' approach=1 +MICRO_START guid=29 intent='social' location='poi_guard_111' action='social_npc' + durationMs=26000 +SOCIAL_FRONT guid=31 target='GUID Full: 0xf1300127000004fa Type: Creature Entry: + 295 Low: 1274' offset=-0.000 distance=2.20 +SOCIAL_CUE guid=31 action='speak' cue=7 target='GUID Full: 0xf1300127000004fa Ty +pe: Creature Entry: 295 Low: 1274' +SOCIAL_START guid=32 action='social_npc' target='GUID Full: 0xf130af71000005c3 T +ype: Creature Entry: 44913 Low: 1475' approach=1 +MICRO_START guid=32 intent='social' location='poi_npc_321922' action='social_npc +' durationMs=23000 +SOCIAL_FRONT guid=29 target='GUID Full: 0xf1300df30000080f Type: Creature Entry: + 3571 Low: 2063' offset=0.000 distance=2.23 +SOCIAL_CUE guid=29 action='social_npc' cue=0 target='GUID Full: 0xf1300df3000008 +0f Type: Creature Entry: 3571 Low: 2063' +SOCIAL_FRONT guid=32 target='GUID Full: 0xf130af71000005c3 Type: Creature Entry: + 44913 Low: 1475' offset=-0.000 distance=2.20 +SOCIAL_CUE guid=32 action='social_npc' cue=0 target='GUID Full: 0xf130af71000005 +c3 Type: Creature Entry: 44913 Low: 1475' +SOCIAL_END guid=31 action='speak' elapsedMs=30000 +SOCIAL_START guid=31 action='drink' target='GUID Full: 0xf110591d000001e6 Type: +Gameobject Entry: 22813 Low: 486' approach=1 +MICRO_START guid=31 intent='social' location='poi_npc_280710' action='drink' dur +ationMs=20000 +SOCIAL_FRONT guid=29 target='GUID Full: 0xf1300df30000080f Type: Creature Entry: + 3571 Low: 2063' offset=0.000 distance=2.23 +SOCIAL_CUE guid=29 action='social_npc' cue=1 target='GUID Full: 0xf1300df3000008 +0f Type: Creature Entry: 3571 Low: 2063' +SOCIAL_FRONT guid=32 target='GUID Full: 0xf130af71000005c3 Type: Creature Entry: + 44913 Low: 1475' offset=-0.000 distance=2.20 +SOCIAL_CUE guid=32 action='social_npc' cue=1 target='GUID Full: 0xf130af71000005 +c3 Type: Creature Entry: 44913 Low: 1475' +SOCIAL_FRONT guid=29 target='GUID Full: 0xf1300df30000080f Type: Creature Entry: + 3571 Low: 2063' offset=0.000 distance=2.23 +SOCIAL_CUE guid=29 action='social_npc' cue=4 target='GUID Full: 0xf1300df3000008 +0f Type: Creature Entry: 3571 Low: 2063' +SOCIAL_FRONT guid=32 target='GUID Full: 0xf130af71000005c3 Type: Creature Entry: + 44913 Low: 1475' offset=-0.000 distance=2.20 +SOCIAL_CUE guid=32 action='social_npc' cue=4 target='GUID Full: 0xf130af71000005 +c3 Type: Creature Entry: 44913 Low: 1475' +SOCIAL_SEATED guid=31 chair='GUID Full: 0xf110591d000001e6 Type: Gameobject Entr +y: 22813 Low: 486' position=(-9470.650 28.955 56.343) slotError=0.000 stand=5 m +ovementGuid=31 +SOCIAL_CUE guid=31 action='drink' cue=10 target='GUID Full: 0xf110591d000001e6 T +ype: Gameobject Entry: 22813 Low: 486' +SOCIAL_CUE guid=30 action='campfire_rest' cue=10 target='GUID Full: 0xf110745800 +000760 Type: Gameobject Entry: 29784 Low: 1888' +SOCIAL_FRONT guid=29 target='GUID Full: 0xf1300df30000080f Type: Creature Entry: + 3571 Low: 2063' offset=0.000 distance=2.23 +SOCIAL_CUE guid=29 action='social_npc' cue=5 target='GUID Full: 0xf1300df3000008 +0f Type: Creature Entry: 3571 Low: 2063' +SOCIAL_FRONT guid=32 target='GUID Full: 0xf130af71000005c3 Type: Creature Entry: + 44913 Low: 1475' offset=-0.000 distance=2.20 +SOCIAL_CUE guid=32 action='social_npc' cue=5 target='GUID Full: 0xf130af71000005 +c3 Type: Creature Entry: 44913 Low: 1475' +SOCIAL_CUE guid=31 action='drink' cue=4 target='GUID Full: 0xf110591d000001e6 Ty +pe: Gameobject Entry: 22813 Low: 486' +SOCIAL_CUE guid=30 action='campfire_rest' cue=14 target='GUID Full: 0xf110745800 +000760 Type: Gameobject Entry: 29784 Low: 1888' +SOCIAL_FRONT guid=29 target='GUID Full: 0xf1300df30000080f Type: Creature Entry: + 3571 Low: 2063' offset=0.000 distance=2.23 +SOCIAL_CUE guid=29 action='social_npc' cue=1 target='GUID Full: 0xf1300df3000008 +0f Type: Creature Entry: 3571 Low: 2063' +SOCIAL_FRONT guid=32 target='GUID Full: 0xf130af71000005c3 Type: Creature Entry: + 44913 Low: 1475' offset=-0.000 distance=2.20 +SOCIAL_CUE guid=32 action='social_npc' cue=1 target='GUID Full: 0xf130af71000005 +c3 Type: Creature Entry: 44913 Low: 1475' +SOCIAL_FRONT guid=29 target='GUID Full: 0xf1300df30000080f Type: Creature Entry: + 3571 Low: 2063' offset=0.000 distance=2.23 +SOCIAL_CUE guid=29 action='social_npc' cue=7 target='GUID Full: 0xf1300df3000008 +0f Type: Creature Entry: 3571 Low: 2063' +SOCIAL_FRONT guid=32 target='GUID Full: 0xf130af71000005c3 Type: Creature Entry: + 44913 Low: 1475' offset=-0.000 distance=2.20 +SOCIAL_CUE guid=32 action='social_npc' cue=7 target='GUID Full: 0xf130af71000005 +c3 Type: Creature Entry: 44913 Low: 1475' +SOCIAL_END guid=29 action='social_npc' elapsedMs=34000 +SOCIAL_END guid=32 action='social_npc' elapsedMs=30000 +SOCIAL_START guid=29 action='social_listen' target='GUID Full: 0xf1300df30000080 +f Type: Creature Entry: 3571 Low: 2063' approach=0 +MICRO_START guid=29 intent='social' location='poi_guard_111' action='social_list +en' durationMs=19000 +SOCIAL_START guid=32 action='social_comment' target='GUID Full: 0xf130af71000005 +c2 Type: Creature Entry: 44913 Low: 1474' approach=0 +MICRO_START guid=32 intent='social' location='poi_npc_321922' action='social_com +ment' durationMs=14000 +SOCIAL_FRONT guid=29 target='GUID Full: 0xf1300df30000080f Type: Creature Entry: + 3571 Low: 2063' offset=0.000 distance=2.23 +SOCIAL_CUE guid=29 action='social_listen' cue=4 target='GUID Full: 0xf1300df3000 +0080f Type: Creature Entry: 3571 Low: 2063' +SOCIAL_FRONT guid=32 target='GUID Full: 0xf130af71000005c2 Type: Creature Entry: + 44913 Low: 1474' offset=-0.433 distance=3.19 +SOCIAL_CUE guid=32 action='social_comment' cue=3 target='GUID Full: 0xf130af7100 +0005c2 Type: Creature Entry: 44913 Low: 1474' +SOCIAL_FRONT guid=32 target='GUID Full: 0xf130af71000005c2 Type: Creature Entry: + 44913 Low: 1474' offset=-0.433 distance=3.19 +SOCIAL_CUE guid=32 action='social_comment' cue=4 target='GUID Full: 0xf130af7100 +0005c2 Type: Creature Entry: 44913 Low: 1474' +SOCIAL_FRONT guid=29 target='GUID Full: 0xf1300df30000080f Type: Creature Entry: + 3571 Low: 2063' offset=0.000 distance=2.23 +SOCIAL_CUE guid=29 action='social_listen' cue=6 target='GUID Full: 0xf1300df3000 +0080f Type: Creature Entry: 3571 Low: 2063' +SOCIAL_END guid=31 action='drink' elapsedMs=36000 +SOCIAL_END guid=32 action='social_comment' elapsedMs=11000 +SOCIAL_START guid=31 action='eat' target='GUID Full: 0xf112b559000001e7 Type: Ga +meobject Entry: 177497 Low: 487' approach=0 +MICRO_START guid=31 intent='social' location='poi_npc_280710' action='eat' durat +ionMs=24000 +SOCIAL_FRONT guid=29 target='GUID Full: 0xf1300df30000080f Type: Creature Entry: + 3571 Low: 2063' offset=0.000 distance=2.23 +SOCIAL_CUE guid=29 action='social_listen' cue=4 target='GUID Full: 0xf1300df3000 +0080f Type: Creature Entry: 3571 Low: 2063' +SOCIAL_SEATED guid=31 chair='GUID Full: 0xf112b559000001e7 Type: Gameobject Entr +y: 177497 Low: 487' position=(-9472.310 29.428 56.343) slotError=0.000 stand=5 +movementGuid=31 +MICRO_START guid=32 intent='social' location='poi_npc_321922' action='look' dura +tionMs=9000 +SOCIAL_CUE guid=31 action='eat' cue=15 target='GUID Full: 0xf112b559000001e7 Typ +e: Gameobject Entry: 177497 Low: 487' +SOCIAL_END guid=29 action='social_listen' elapsedMs=19000 +SOCIAL_END guid=30 action='campfire_rest' elapsedMs=79000 +MICRO_START guid=29 intent='social' location='poi_guard_111' action='rest' durat +ionMs=49000 +SOCIAL_CUE guid=31 action='eat' cue=4 target='GUID Full: 0xf112b559000001e7 Type +: Gameobject Entry: 177497 Low: 487' +SOCIAL_START guid=32 action='social_listen' target='GUID Full: 0xf130b1ba000005c +4 Type: Creature Entry: 45498 Low: 1476' approach=1 +MICRO_START guid=32 intent='social' location='poi_npc_321922' action='social_lis +ten' durationMs=19000 +MICRO_START guid=30 intent='social' location='poi_tele_823' action='nod' duratio +nMs=10000 +SOCIAL_FRONT guid=32 target='GUID Full: 0xf130b1ba000005c4 Type: Creature Entry: + 45498 Low: 1476' offset=0.000 distance=2.20 +SOCIAL_CUE guid=32 action='social_listen' cue=4 target='GUID Full: 0xf130b1ba000 +005c4 Type: Creature Entry: 45498 Low: 1476' +MoveSplineInitArgs::Validate: expression '_checkPathLengths()' failed for GUID: +312735 Entry: 49728 +SOCIAL_FRONT guid=32 target='GUID Full: 0xf130b1ba000005c4 Type: Creature Entry: + 45498 Low: 1476' offset=0.000 distance=2.20 +SOCIAL_CUE guid=32 action='social_listen' cue=6 target='GUID Full: 0xf130b1ba000 +005c4 Type: Creature Entry: 45498 Low: 1476' +MICRO_START guid=30 intent='social' location='poi_tele_823' action='drink' durat +ionMs=26000 +SOCIAL_FRONT guid=32 target='GUID Full: 0xf130b1ba000005c4 Type: Creature Entry: + 45498 Low: 1476' offset=0.000 distance=2.20 +SOCIAL_CUE guid=32 action='social_listen' cue=4 target='GUID Full: 0xf130b1ba000 +005c4 Type: Creature Entry: 45498 Low: 1476' +SOCIAL_END guid=32 action='social_listen' elapsedMs=22000 +SOCIAL_END guid=31 action='eat' elapsedMs=35000 +MICRO_START guid=32 intent='social' location='poi_npc_321922' action='drink' dur +ationMs=33000 +MICRO_START guid=31 intent='social' location='poi_npc_280710' action='wave' dura +tionMs=17000 +MICRO_START guid=30 intent='social' location='poi_tele_823' action='rest' durati +onMs=23000 +SOCIAL_START guid=31 action='social_greet' target='GUID Full: 0xf13000fd000004f9 + Type: Creature Entry: 253 Low: 1273' approach=1 +MICRO_START guid=31 intent='social' location='poi_npc_280710' action='social_gre +et' durationMs=8000 +SOCIAL_START guid=29 action='speak' target='GUID Full: 0xf1300df30000080b Type: +Creature Entry: 3571 Low: 2059' approach=1 +MICRO_START guid=29 intent='social' location='poi_guard_111' action='speak' dura +tionMs=21000 +SOCIAL_FRONT guid=31 target='GUID Full: 0xf13000fd000004f9 Type: Creature Entry: + 253 Low: 1273' offset=-0.000 distance=2.20 +SOCIAL_CUE guid=31 action='social_greet' cue=0 target='GUID Full: 0xf13000fd0000 +04f9 Type: Creature Entry: 253 Low: 1273' +SOCIAL_FRONT guid=31 target='GUID Full: 0xf13000fd000004f9 Type: Creature Entry: + 253 Low: 1273' offset=-0.000 distance=2.20 +SOCIAL_CUE guid=31 action='social_greet' cue=8 target='GUID Full: 0xf13000fd0000 +04f9 Type: Creature Entry: 253 Low: 1273' +SOCIAL_FRONT guid=29 target='GUID Full: 0xf1300df30000080b Type: Creature Entry: + 3571 Low: 2059' offset=0.000 distance=2.22 +SOCIAL_CUE guid=29 action='speak' cue=0 target='GUID Full: 0xf1300df30000080b Ty +pe: Creature Entry: 3571 Low: 2059' +SOCIAL_END guid=31 action='social_greet' elapsedMs=13000 +SOCIAL_FRONT guid=29 target='GUID Full: 0xf1300df30000080b Type: Creature Entry: + 3571 Low: 2059' offset=0.000 distance=2.22 +SOCIAL_CUE guid=29 action='speak' cue=1 target='GUID Full: 0xf1300df30000080b Ty +pe: Creature Entry: 3571 Low: 2059' +MICRO_START guid=32 intent='social' location='poi_npc_321922' action='cheer' dur +ationMs=23000 +SOCIAL_START guid=31 action='social_comment' target='GUID Full: 0xf1301a7a000004 +de Type: Creature Entry: 6778 Low: 1246' approach=1 +MICRO_START guid=31 intent='social' location='poi_npc_280710' action='social_com +ment' durationMs=8000 +MICRO_START guid=30 intent='social' location='poi_tele_823' action='eat' duratio +nMs=22000 +SOCIAL_FRONT guid=29 target='GUID Full: 0xf1300df30000080b Type: Creature Entry: + 3571 Low: 2059' offset=0.000 distance=2.22 +SOCIAL_CUE guid=29 action='speak' cue=4 target='GUID Full: 0xf1300df30000080b Ty +pe: Creature Entry: 3571 Low: 2059' +SOCIAL_FRONT guid=31 target='GUID Full: 0xf1301a7a000004de Type: Creature Entry: + 6778 Low: 1246' offset=0.000 distance=2.20 +SOCIAL_CUE guid=31 action='social_comment' cue=3 target='GUID Full: 0xf1301a7a00 +0004de Type: Creature Entry: 6778 Low: 1246' +SOCIAL_FRONT guid=31 target='GUID Full: 0xf1301a7a000004de Type: Creature Entry: + 6778 Low: 1246' offset=0.000 distance=2.20 +SOCIAL_CUE guid=31 action='social_comment' cue=4 target='GUID Full: 0xf1301a7a00 +0004de Type: Creature Entry: 6778 Low: 1246' +SOCIAL_FRONT guid=29 target='GUID Full: 0xf1300df30000080b Type: Creature Entry: + 3571 Low: 2059' offset=0.000 distance=2.22 +SOCIAL_CUE guid=29 action='speak' cue=5 target='GUID Full: 0xf1300df30000080b Ty +pe: Creature Entry: 3571 Low: 2059' +clientless generate retire 26 +TC>clientless generate login 27 +TC>clientless generate list +TC>clientless combat supplies 31 +TC>Not a generator-owned character; no action taken. +Incorrect syntax. +TC> Usage: clientless generate create|list|profile |login |logout |equip |retire +Incorrect syntax. +TC> 27 Dagri archetype=warrior persistent=0 state=retired online=0 seed=20261028 +28 Jarana archetype=warrior persistent=0 state=retired online=0 seed=20261029 +29 Aelion archetype=warrior persistent=0 state=ready online=1 seed=20261101 +30 Zanzan archetype=warrior persistent=0 state=ready online=1 seed=20261102 +31 Lenara archetype=mage persistent=0 state=ready online=1 seed=20261103 +32 Aldren archetype=death_knight persistent=0 state=ready online=1 seed=20261104 +TC> Supplies: lastDay=20261028 recovering=0 afterCombat=0 health=3600/3600 +secondary=129 value=375 max=375 +secondary=185 value=525 max=525 +secondary=356 value=525 max=525 +secondary=794 value=375 max=375 +bandage: item=21991 count=20 stack=20 requiredLevel=50 requiredSkill=129/325 usa +ble=1 +healing_potion: item=22829 count=20 stack=20 requiredLevel=55 requiredSkill=0/0 +usable=1 +mana_potion: item=22832 count=20 stack=20 requiredLevel=55 requiredSkill=0/0 usa +ble=1 +food: item=27854 count=20 stack=20 requiredLevel=55 requiredSkill=0/0 usable=1 +drink: item=28399 count=20 stack=20 requiredLevel=60 requiredSkill=0/0 usable=1 +bagSlot=19 item=54443 capacity=22 +bagSlot=20 item=54443 capacity=22 +bagSlot=21 item=54443 capacity=22 +bagSlot=22 item=54443 capacity=22 +TC> SOCIAL_END guid=31 action='social_comment' elapsedMs=14000 +SOCIAL_FRONT guid=29 target='GUID Full: 0xf1300df30000080b Type: Creature Entry: + 3571 Low: 2059' offset=0.000 distance=2.22 +SOCIAL_CUE guid=29 action='speak' cue=1 target='GUID Full: 0xf1300df30000080b Ty +pe: Creature Entry: 3571 Low: 2059' +SOCIAL_START guid=31 action='social_counter' target='GUID Full: 0xf1300127000004 +fa Type: Creature Entry: 295 Low: 1274' approach=1 +MICRO_START guid=31 intent='social' location='poi_npc_280710' action='social_cou +nter' durationMs=26000 +SOCIAL_FRONT guid=29 target='GUID Full: 0xf1300df30000080b Type: Creature Entry: + 3571 Low: 2059' offset=0.000 distance=2.22 +SOCIAL_CUE guid=29 action='speak' cue=7 target='GUID Full: 0xf1300df30000080b Ty +pe: Creature Entry: 3571 Low: 2059' +MICRO_START guid=32 intent='social' location='poi_npc_321922' action='eat' durat +ionMs=35000 +SOCIAL_END guid=29 action='speak' elapsedMs=34000 +SOCIAL_START guid=30 action='social_react' target='GUID Full: 0xf1300ce100000765 + Type: Creature Entry: 3297 Low: 1893' approach=1 +MICRO_START guid=30 intent='social' location='poi_tele_823' action='social_react +' durationMs=11000 +SOCIAL_FRONT guid=31 target='GUID Full: 0xf1300127000004fa Type: Creature Entry: + 295 Low: 1274' offset=-0.000 distance=2.20 +SOCIAL_CUE guid=31 action='social_counter' cue=8 target='GUID Full: 0xf130012700 +0004fa Type: Creature Entry: 295 Low: 1274' +SOCIAL_FRONT guid=30 target='GUID Full: 0xf1300ce100000765 Type: Creature Entry: + 3297 Low: 1893' offset=-0.000 distance=2.21 +SOCIAL_CUE guid=30 action='social_react' cue=5 target='GUID Full: 0xf1300ce10000 +0765 Type: Creature Entry: 3297 Low: 1893' +MICRO_START guid=29 intent='social' location='poi_guard_111' action='look' durat +ionMs=8000 +SOCIAL_FRONT guid=30 target='GUID Full: 0xf1300ce100000765 Type: Creature Entry: + 3297 Low: 1893' offset=-0.000 distance=2.21 +SOCIAL_CUE guid=30 action='social_react' cue=4 target='GUID Full: 0xf1300ce10000 +0765 Type: Creature Entry: 3297 Low: 1893' +SOCIAL_FRONT guid=31 target='GUID Full: 0xf1300127000004fa Type: Creature Entry: + 295 Low: 1274' offset=-0.000 distance=2.20 +SOCIAL_CUE guid=31 action='social_counter' cue=4 target='GUID Full: 0xf130012700 +0004fa Type: Creature Entry: 295 Low: 1274' +MICRO_START guid=29 intent='social' location='poi_guard_111' action='drink' dura +tionMs=31000 +SOCIAL_END guid=30 action='social_react' elapsedMs=11000 +SOCIAL_START guid=30 action='social_farewell' target='GUID Full: 0xf1300ce100000 +765 Type: Creature Entry: 3297 Low: 1893' approach=0 +MICRO_START guid=30 intent='social' location='poi_tele_823' action='social_farew +ell' durationMs=5000 +SOCIAL_FRONT guid=30 target='GUID Full: 0xf1300ce100000765 Type: Creature Entry: + 3297 Low: 1893' offset=-0.000 distance=2.21 +SOCIAL_CUE guid=30 action='social_farewell' cue=7 target='GUID Full: 0xf1300ce10 +0000765 Type: Creature Entry: 3297 Low: 1893' +SOCIAL_FRONT guid=31 target='GUID Full: 0xf1300127000004fa Type: Creature Entry: + 295 Low: 1274' offset=-0.000 distance=2.20 +SOCIAL_CUE guid=31 action='social_counter' cue=8 target='GUID Full: 0xf130012700 +0004fa Type: Creature Entry: 295 Low: 1274' +SOCIAL_END guid=30 action='social_farewell' elapsedMs=5000 +SOCIAL_END guid=31 action='social_counter' elapsedMs=28000 +MICRO_START guid=30 intent='social' location='poi_tele_823' action='drink' durat +ionMs=30000 +MICRO_START guid=31 intent='social' location='poi_npc_280710' action='explore' d +urationMs=24000 +SOCIAL_START guid=32 action='social_npc' target='GUID Full: 0xf130af71000005c3 T +ype: Creature Entry: 44913 Low: 1475' approach=1 +MICRO_START guid=32 intent='social' location='poi_npc_321922' action='social_npc +' durationMs=27000 +SOCIAL_FRONT guid=32 target='GUID Full: 0xf130af71000005c3 Type: Creature Entry: + 44913 Low: 1475' offset=-0.000 distance=2.20 +SOCIAL_CUE guid=32 action='social_npc' cue=0 target='GUID Full: 0xf130af71000005 +c3 Type: Creature Entry: 44913 Low: 1475' +MICRO_START guid=29 intent='social' location='poi_guard_111' action='eat' durati +onMs=17000 +SOCIAL_FRONT guid=32 target='GUID Full: 0xf130af71000005c3 Type: Creature Entry: + 44913 Low: 1475' offset=-0.000 distance=2.20 +SOCIAL_CUE guid=32 action='social_npc' cue=1 target='GUID Full: 0xf130af71000005 +c3 Type: Creature Entry: 44913 Low: 1475' +SOCIAL_FRONT guid=32 target='GUID Full: 0xf130af71000005c3 Type: Creature Entry: + 44913 Low: 1475' offset=-0.000 distance=2.20 +SOCIAL_CUE guid=32 action='social_npc' cue=4 target='GUID Full: 0xf130af71000005 +c3 Type: Creature Entry: 44913 Low: 1475' +SOCIAL_START guid=31 action='campfire_rest' target='GUID Full: 0xf110080d0000020 +2 Type: Gameobject Entry: 2061 Low: 514' approach=1 +MICRO_START guid=31 intent='social' location='poi_npc_280710' action='campfire_r +est' durationMs=63000 +SOCIAL_FRONT guid=32 target='GUID Full: 0xf130af71000005c3 Type: Creature Entry: + 44913 Low: 1475' offset=-0.000 distance=2.20 +SOCIAL_CUE guid=32 action='social_npc' cue=5 target='GUID Full: 0xf130af71000005 +c3 Type: Creature Entry: 44913 Low: 1475' +MICRO_START guid=30 intent='social' location='poi_tele_823' action='wave' durati +onMs=11000 +SOCIAL_END guid=31 action='campfire_rest' elapsedMs=5000 +MICRO_START guid=29 intent='social' location='poi_guard_111' action='drink' dura +tionMs=29000 +SOCIAL_FRONT guid=32 target='GUID Full: 0xf130af71000005c3 Type: Creature Entry: + 44913 Low: 1475' offset=-0.000 distance=2.20 +SOCIAL_CUE guid=32 action='social_npc' cue=1 target='GUID Full: 0xf130af71000005 +c3 Type: Creature Entry: 44913 Low: 1475' +SOCIAL_START guid=31 action='rest' target='GUID Full: 0xf110591300000203 Type: G +ameobject Entry: 22803 Low: 515' approach=1 +MICRO_START guid=31 intent='social' location='poi_npc_280710' action='rest' dura +tionMs=31000 +SOCIAL_START guid=30 action='social_fire' target='GUID Full: 0x0000000000000000 +Type: None Low: 0' approach=0 +MICRO_START guid=30 intent='social' location='poi_tele_823' action='social_fire' + durationMs=27000 +SOCIAL_FIRE_CAST guid=30 spell=818 +SOCIAL_FRONT guid=32 target='GUID Full: 0xf130af71000005c3 Type: Creature Entry: + 44913 Low: 1475' offset=-0.000 distance=2.20 +SOCIAL_CUE guid=32 action='social_npc' cue=7 target='GUID Full: 0xf130af71000005 +c3 Type: Creature Entry: 44913 Low: 1475' +SOCIAL_SEATED guid=31 chair='GUID Full: 0xf110591300000203 Type: Gameobject Entr +y: 22803 Low: 515' position=(-9458.400 16.057 56.975) slotError=0.000 stand=5 m +ovementGuid=31 +SOCIAL_FIRE_CONFIRMED guid=30 spell=818 object='GUID Full: 0xf110745800000846 Ty +pe: Gameobject Entry: 29784 Low: 2118' +SOCIAL_END guid=32 action='social_npc' elapsedMs=33000 +SOCIAL_CUE guid=30 action='social_fire' cue=14 target='GUID Full: 0xf11074580000 +0846 Type: Gameobject Entry: 29784 Low: 2118' +MICRO_START guid=32 intent='social' location='poi_npc_321922' action='laugh' dur +ationMs=14000 +SOCIAL_CUE guid=30 action='social_fire' cue=10 target='GUID Full: 0xf11074580000 +0846 Type: Gameobject Entry: 29784 Low: 2118' +SOCIAL_START guid=32 action='social_fire' target='GUID Full: 0xf11327b10000023d +Type: Gameobject Entry: 206769 Low: 573' approach=0 +MICRO_START guid=32 intent='social' location='poi_npc_321922' action='social_fir +e' durationMs=30000 +SOCIAL_CUE guid=32 action='social_fire' cue=13 target='GUID Full: 0xf11327b10000 +023d Type: Gameobject Entry: 206769 Low: 573' +SOCIAL_START guid=29 action='campfire_rest' target='GUID Full: 0x000000000000000 +0 Type: None Low: 0' approach=0 +MICRO_START guid=29 intent='social' location='poi_guard_111' action='campfire_re +st' durationMs=38000 +SOCIAL_CUE guid=32 action='social_fire' cue=14 target='GUID Full: 0xf11327b10000 +023d Type: Gameobject Entry: 206769 Low: 573' +SOCIAL_FIRE_CAST guid=29 spell=818 +SOCIAL_FIRE_CONFIRMED guid=29 spell=818 object='GUID Full: 0xf110745800000848 Ty +pe: Gameobject Entry: 29784 Low: 2120' +SOCIAL_CUE guid=30 action='social_fire' cue=14 target='GUID Full: 0xf11074580000 +0846 Type: Gameobject Entry: 29784 Low: 2118' +SOCIAL_CUE guid=29 action='campfire_rest' cue=14 target='GUID Full: 0xf110745800 +000848 Type: Gameobject Entry: 29784 Low: 2120' +SOCIAL_END guid=31 action='rest' elapsedMs=35000 +SOCIAL_START guid=31 action='drink' target='GUID Full: 0xf112b55800000209 Type: +Gameobject Entry: 177496 Low: 521' approach=1 +MICRO_START guid=31 intent='social' location='poi_npc_280710' action='drink' dur +ationMs=15000 +SOCIAL_SEATED guid=31 chair='GUID Full: 0xf112b55800000209 Type: Gameobject Entr +y: 177496 Low: 521' position=(-9461.860 13.735 56.975) slotError=0.000 stand=5 +movementGuid=31 +SOCIAL_CUE guid=31 action='drink' cue=10 target='GUID Full: 0xf112b55800000209 T +ype: Gameobject Entry: 177496 Low: 521' +SOCIAL_CUE guid=32 action='social_fire' cue=10 target='GUID Full: 0xf11327b10000 +023d Type: Gameobject Entry: 206769 Low: 573' +SOCIAL_END guid=30 action='social_fire' elapsedMs=42000 +SOCIAL_START guid=30 action='social_npc' target='GUID Full: 0xf1300ce100000765 T +ype: Creature Entry: 3297 Low: 1893' approach=0 +MICRO_START guid=30 intent='social' location='poi_tele_823' action='social_npc' +durationMs=21000 +SOCIAL_FRONT guid=30 target='GUID Full: 0xf1300ce100000765 Type: Creature Entry: + 3297 Low: 1893' offset=-0.000 distance=2.21 +SOCIAL_CUE guid=30 action='social_npc' cue=0 target='GUID Full: 0xf1300ce1000007 +65 Type: Creature Entry: 3297 Low: 1893' +SOCIAL_CUE guid=32 action='social_fire' cue=14 target='GUID Full: 0xf11327b10000 +023d Type: Gameobject Entry: 206769 Low: 573' +SOCIAL_CUE guid=31 action='drink' cue=4 target='GUID Full: 0xf112b55800000209 Ty +pe: Gameobject Entry: 177496 Low: 521' +SOCIAL_CUE guid=29 action='campfire_rest' cue=10 target='GUID Full: 0xf110745800 +000848 Type: Gameobject Entry: 29784 Low: 2120' +SOCIAL_FRONT guid=30 target='GUID Full: 0xf1300ce100000765 Type: Creature Entry: + 3297 Low: 1893' offset=-0.000 distance=2.21 +SOCIAL_CUE guid=30 action='social_npc' cue=1 target='GUID Full: 0xf1300ce1000007 +65 Type: Creature Entry: 3297 Low: 1893' +SOCIAL_FRONT guid=30 target='GUID Full: 0xf1300ce100000765 Type: Creature Entry: + 3297 Low: 1893' offset=-0.000 distance=2.21 +SOCIAL_CUE guid=30 action='social_npc' cue=4 target='GUID Full: 0xf1300ce1000007 +65 Type: Creature Entry: 3297 Low: 1893' +SOCIAL_CUE guid=29 action='campfire_rest' cue=14 target='GUID Full: 0xf110745800 +000848 Type: Gameobject Entry: 29784 Low: 2120' diff --git a/doc/clientless/checkpoints/2026-10-28-generator/tests.txt b/doc/clientless/checkpoints/2026-10-28-generator/tests.txt new file mode 100644 index 0000000000..07da3f8822 --- /dev/null +++ b/doc/clientless/checkpoints/2026-10-28-generator/tests.txt @@ -0,0 +1,63 @@ +test_adventure_area_node.py: PASS +test_adventure_behavior_policy.py: PASS +test_adventure_combat_exercise.py: PASS +test_adventure_combat_preparation.py: PASS +test_adventure_combat_reactions.py: PASS +test_adventure_economy.py: PASS +test_adventure_economy_catalog.py: PASS +test_adventure_exploration.py: PASS +test_adventure_exploration_catalog.py: PASS +test_adventure_generator_equipment.py: PASS +test_adventure_generator_ownership.py: PASS +test_adventure_generator_policy.py: PASS +test_adventure_graph_handover.py: PASS +test_adventure_instance.py: PASS +test_adventure_instance_autoresume.py: PASS +test_adventure_instance_begin.py: PASS +test_adventure_instance_catalog.py: PASS +test_adventure_instance_persistence.py: PASS +test_adventure_intent_state.py: PASS +test_adventure_interactions.py: PASS +test_adventure_leisure.py: PASS +test_adventure_leisure_catalog.py: PASS +test_adventure_location_import.py: PASS +test_adventure_micro_reset.py: PASS +test_adventure_profession_catalog.py: PASS +test_adventure_profession_profile.py: PASS +test_adventure_profession_sightline.py: PASS +test_adventure_profession_targets.py: PASS +test_adventure_repair_catalog.py: PASS +test_adventure_rest.py: PASS +test_adventure_rest_fire.py: PASS +test_adventure_rest_space.py: PASS +test_adventure_rest_targets.py: PASS +test_adventure_social.py: PASS +test_adventure_social_fire.py: PASS +test_adventure_supplies.py: PASS +test_adventure_supply_catalog.py: PASS +test_adventure_supply_items.py: PASS +test_adventure_training_catalog.py: PASS +test_adventure_walk.py: PASS +test_clientless_character_factory.py: PASS +test_clientless_companion.py: PASS +test_clientless_damage_notifications.py: PASS +test_clientless_near_teleport_observer.py: PASS +test_clientless_skill_preparation.py: PASS +test_clientless_social_notifications.py: PASS +test_default_phase.py: PASS +test_flight_area_query.py: PASS +test_flight_handoff.py: PASS +test_flight_policy.py: PASS +test_follow_spline_velocity.py: PASS +test_free_flight.py: PASS +test_free_flight_planner.py: PASS +test_ground_exit_continuation.py: PASS +test_local_spline_segments.py: PASS +test_lua_reload.py: PASS +test_player_pure_max_skill.py: PASS +test_portal_transfer.py: PASS +test_portal_use.py: PASS +test_taxi_import_disabled.py: PASS +test_transport.py: PASS +test_transport_grid_loading.py: PASS +62/62 passed; failures: [] diff --git a/doc/clientless/checkpoints/2026-10-28-generator/validation.txt b/doc/clientless/checkpoints/2026-10-28-generator/validation.txt new file mode 100644 index 0000000000..9067419978 --- /dev/null +++ b/doc/clientless/checkpoints/2026-10-28-generator/validation.txt @@ -0,0 +1,17 @@ +Generator implemented on feature/clientless-player-core, local commit requested by user; no push. +Final Debug-j10 build and installation succeeded. Native test worldserver88947 freshly ready, port8085 verified. +62/62 test scripts passed. +Character migration applied twice successfully (additive/idempotent). +Initial offline profession preparation failed without saving a partial character; fixed with validated native DBC skill tier initialization before world insertion. +Generated four active temporary level60 characters:29 Aelion night elf warrior;30 Zanzan troll warrior;31 Lenara worgen mage;32 Aldren forsaken death knight. +Their profiles load as generated:29..32 after native logout/restart/login. All four online and running local social actions. +Native talent trees:746/746/851/400. Stored talent row counts13/13/14/12; spell counts80/80/87/64. +All four have FirstAid375, Cooking525, Fishing525, Archaeology375 using current native data. Warriors/DK:Mining450+Blacksmithing375. Mage:Tailoring375+Enchanting375. +All four have four22-slot Embersilk Bags and one full20-stack of each of the five supply roles. +Equipment affinity checks include stats, native legality, mana/physical/spell-only procs and duplicate avoidance. Native online equipment refresh29..32 saved; all replaced items retained. +Own earlier fixture characters27/28 were archived, not deleted. Their inventories/profiles retained. +Live generator retirement of Arthoran26 refused (not owned); generator login27 refused (retired). +Existing item ownership/entry/slot snapshot for21 and26 unchanged:sha256 dd5869ffb3f10aa7d25dfc93b8de61d0b982715ab27b28e7d6311f562a6d066c. +First server start in this task hit transient remote Login DB error10.0.0.1; failed process76094 did not respond to console and was terminated with SIGTERM. Second attempt succeeded. No remote DB configuration modified. +Arthoran was logged out for maintenance and remains offline. No temporary profile/source overrides were used. His source profile and Allitester's equipment were not changed. +Rotation/deletion/population targets/restart autologin are not enabled. Non-warrior combat/support AI limitations remain. Final visible gameplay acceptance is user-owned. diff --git a/doc/clientless/generator.md b/doc/clientless/generator.md new file mode 100644 index 0000000000..41ba744953 --- /dev/null +++ b/doc/clientless/generator.md @@ -0,0 +1,151 @@ +# AdventureBot generation + +The GM/console command `clientless generate` creates complete new native player +characters on an existing normal bot account. No source player is cloned and no +authentication account/password is created or changed. The native realm-character +counter is updated for that account after a successful character commit. + +## Commands + +``` +clientless generate create +clientless generate create +clientless generate list +clientless generate profile +clientless generate login +clientless generate logout +clientless generate equip +clientless generate retire +``` + +The short create form defaults to level60, any faction/archetype, persistent, +and a time-based seed. When specifying options, supply all five optional values. +Count is1–10 per request, level10–85 (also limited by server maximum), faction is +`any`, `alliance` or `horde`, and persistence is `persistent` or `temporary`. +Each character is committed separately. A failed batch reports its actual +completed count and retains already completed characters. + +Available archetypes: `warrior`, `paladin`, `hunter`, `rogue`, `priest`, +`death_knight` (minimum55), `shaman`, `mage`, `warlock`, `druid`, or `any`. +Native race/class data and account expansion restrictions determine eligible +combinations. Both factions can use the administrative bot-account pool; this +is not a normal client's character-creation request. + +`equip` refreshes the equipment of an idle online generator-owned bot using its +stored archetype and seed. Native checks still apply, casting/combat/transit are +rejected, and replaced items remain in bags. It requires sufficient bag space; +an error can stop an online refresh partway through. The initial offline create +pipeline is the atomic operation. + +Creation leaves bots offline. Explicit login uses the existing AdventureBot Lua +controller. Initially they perform a local social intent for one hour at their +home. Enable the existing daily planner with `clientless adventure schedule + on` when desired. Rotation and automatic population targeting are not +activated by this change. + +## Identity and profile + +Names use curated cultural syllable combinations for the supported races and +sexes. They contain no numbers, bot markers or numeric collision suffixes. +Native name validation, reserved-name checks, character cache/database checks, +and reservation of hand-written named Lua profiles apply. An exhausted pool +fails cleanly instead of manufacturing a non-roleplay name. + +Profiles include a racial origin, vocation, background, two coherent primary +professions, personality values and regional/activity affinities. The stored +seed makes choices reproducible against unchanged catalogs and name availability. +Existing name collisions legitimately advance the random stream. Equipment +varies within a set of similar high-scoring legal options. + +Generated Lua is stored in the character database, avoiding runtime writes to +source-controlled profile files. The ordinary sandbox/parser loads it with +source `generated:`. Existing hand-written named Lua profiles retain +precedence; ordinary non-generated characters keep the generic fallback. +`identity.background` and `identity.vocation` are also available in the parsed +profile. Generated strings are Lua-escaped, then escaped separately for SQL. + +Home points come from enabled, faction-compatible social tavern/settlement +locations nearest the native racial starting region. Mature goblins and worgen +use mainland orc/human home regions, rather than phased introductory maps. +Death knights also choose racial mainland homes. Map relocation happens before +native talent calculation, so introductory map609 does not reduce their talent +budget. This is administrative creation, not a claim that these bots played or +completed introductory quests. + +## Native preparation + +`ClientlessCharacterFactory` is generic infrastructure with preparation and +transaction-extension callbacks. It has no AdventureBot policy dependency. +It uses native `Player::Create`, validated random appearance, level/skill setup, +new player/item GUID allocators and native `SaveToDB`. Requested levels are +initialized as birth state through native stat/talent/taxi/glyph initialization, +avoiding earned-level reward mails/hooks before the character transaction. Network-active accounts, +privileged accounts, unavailable names and the configured per-realm character +limit are rejected. + +The AdventureBot layer supplies: + +- Native class-trainer spells, using the same read-only `CanTeachSpell` predicate + as ordinary native teaching; weapon/armor proficiencies are race/class/level + checked. Owned weapon skills are raised to their native level cap. +- A solo damage specialization and valid talent allocation through native talent + prerequisites, tiers, ranks and primary-tree gates. Arms uses the existing + explicit build, other archetypes use a legal tier-ordered baseline. These + generic builds are not claimed to be optimized raid/PvP builds. +- Coherent primary profession pairs, trained to their legal native rank/cap; + all four available secondary professions through existing supply preparation. + Before world insertion, effect44 learning casts cannot be relied on to apply + their target effects. The factory therefore initializes validated skill ranks + with native SkillRaceClass/SkillTiers caps and `Player::SetSkill`. This helper + refuses online players; ordinary live supply learning keeps its native casts. + This does not teach every profession recipe or synthesize quest rewards. +- Four native Embersilk Bags and class/stat/level-matching equipment. Candidate + items are normal/uncommon/rare; native use/equip checks remain authoritative. + Armor transitions at40 are respected. Temporary, holiday, reputation-bound, + restricted-map/area and random-affix equipment is excluded. Caster/physical + stat affinity and mana-only/triggered utility effects are checked, so a + warrior does not receive a mana-restoration trinket. Identical equipped + entries are not duplicated, in addition to native unique-equipment checks. Initial items + are retained in bags rather than destroyed. +- Existing daily/post-combat supply restocking upon login. Consumables are native + items and continue to use the existing actual item/cooldown logic. + +No mounts, pets or optimized class rotations are introduced here. Existing +non-warrior combat limitations remain. Being fully trained is distinct from +having class-specific automated combat/support AI. + +## Persistence and future rotation + +Apply `sql/custom/characters/2026_10_28_00_adventure_generator.sql` before using +commands. The additive migration is idempotent. Native character state, inventory, +professions, Lua profile, homebind and generated ownership are committed in one +character-database transaction. Cache publication and create hooks happen only +after confirmed success. Failure before/at commit does not publish a ready bot. + +The registry explicitly records ownership, seed, archetype, creation time, +persistence and state. Existing characters such as Arthoran are not adopted. +Only offline temporary generator-owned characters may be retired. Retirement +marks metadata only; the native character, items and profile remain available +for audit. Generator login refuses retired entries. Persistent characters are +protected. There is no automatic deletion, replacement, new-account allocation +or restart autologin. A later rotating population controller must define those +policies and account/name capacity management explicitly. + +## Verification + +Production-code adapter tests cover factory transaction failure/publication, +account/name/security/character-cap gates, retirement ownership protections, +command bounds, cultural naming, real Lua profile execution, personality/skill +coherence, string escaping and equipment/armor eligibility. + +Compilation and adapters do not establish visible in-game behavior. Live-created +character identities, gear, profession/talent/spell rows and login/profile +results are recorded in the task checkpoint after native server checks. + +Live checkpoint (2026-10-28): four level60 characters are online: Aelion29 +(night elf warrior), Zanzan30 (troll warrior), Lenara31 (worgen mage), Aldren32 +(forsaken death knight). Their generated profiles were reloaded after a server +restart, and native equipment refresh was saved for all four. Earlier temporary +fixtures27/28 remain archived and offline. Existing inventory ownership for +Allitester21 and Arthoran26 matched the pre-generation snapshot. All62 clientless +test scripts passed. See checkpoints/2026-10-28-generator/ for native data/logs. diff --git a/sql/custom/characters/2026_10_28_00_adventure_generator.sql b/sql/custom/characters/2026_10_28_00_adventure_generator.sql new file mode 100644 index 0000000000..2c24892600 --- /dev/null +++ b/sql/custom/characters/2026_10_28_00_adventure_generator.sql @@ -0,0 +1,16 @@ +-- Generated ownership/profile metadata is committed with the native character. +-- Never infer ownership from account/name/level when retiring a bot. +CREATE TABLE IF NOT EXISTS vcore_adventure_generated_bot ( + character_guid INT UNSIGNED NOT NULL, + account_id INT UNSIGNED NOT NULL, + archetype VARCHAR(32) NOT NULL, + seed INT UNSIGNED NOT NULL, + persistent TINYINT UNSIGNED NOT NULL DEFAULT 1, + state VARCHAR(16) NOT NULL DEFAULT 'ready', + profile_lua MEDIUMTEXT NOT NULL, + created_at BIGINT UNSIGNED NOT NULL, + retired_at BIGINT UNSIGNED NOT NULL DEFAULT 0, + PRIMARY KEY (character_guid), + KEY account_state (account_id,state), + KEY rotation_pool (persistent,state) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; diff --git a/src/server/game/Entities/Creature/Trainer.h b/src/server/game/Entities/Creature/Trainer.h index 2a2523809b..031dbc0303 100644 --- a/src/server/game/Entities/Creature/Trainer.h +++ b/src/server/game/Entities/Creature/Trainer.h @@ -75,10 +75,11 @@ namespace Trainer Type GetTrainerType() const { return _type; } uint32 GetTrainerRequirement() const { return _requirement; } bool IsTrainerValidForPlayer(Player const* player) const; + // Read-only eligibility, shared by native teaching and character provisioning. + bool CanTeachSpell(Player const* player, Spell const* trainerSpell) const; private: Spell const* GetSpell(uint32 spellId) const; - bool CanTeachSpell(Player const* player, Spell const* trainerSpell) const; SpellState GetSpellState(Player const* player, Spell const* trainerSpell) const; void SendTeachFailure(Creature const* npc, Player const* player, uint32 spellId, FailReason reason) const; void SendTeachSucceeded(Creature const* npc, Player const* player, uint32 spellId) const; diff --git a/src/server/game/World/AdventureGeneratorMgr.cpp b/src/server/game/World/AdventureGeneratorMgr.cpp new file mode 100644 index 0000000000..e8395e4f66 --- /dev/null +++ b/src/server/game/World/AdventureGeneratorMgr.cpp @@ -0,0 +1,416 @@ +#include "AdventureGeneratorMgr.h" +#include "AdventureCombatPreparationPolicy.h" +#include "AdventureBehaviorMgr.h" +#include "AdventureLocationMgr.h" +#include "AdventureProfessionPolicy.h" +#include "AdventureSupplyMgr.h" +#include "CharacterCache.h" +#include "Chat.h" +#include "ClientlessCharacterFactory.h" +#include "ClientlessLuaEngine.h" +#include "ClientlessPlayerMgr.h" +#include "DBCStores.h" +#include "GameTime.h" +#include "Item.h" +#include "ItemTemplate.h" +#include "Log.h" +#include "MapManager.h" +#include "ObjectMgr.h" +#include "Player.h" +#include "SpellMgr.h" +#include "Stats.h" +#include "Trainer.h" +#include "World.h" +#include "WorldSession.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace +{ + using AdventureGeneratorPolicy::Archetype; + bool Schema() + {return bool(CharacterDatabase.Query("SHOW TABLES LIKE 'vcore_adventure_generated_bot'"));} + uint32 LearnedSkill(uint32 id,uint32& step) + { + if(auto rank=sSpellMgr->GetSpellLearnSkill(id)) + if(rank->step){step=rank->step;return rank->skill;} + if(auto info=sSpellMgr->GetSpellInfo(id))for(auto const& effect:info->Effects) + if(effect.Effect==SPELL_EFFECT_SKILL_STEP && effect.CalcValue()>0) + {step=effect.CalcValue();return effect.MiscValue;} + return 0; + } + bool Learnable(Player* p,uint32 id) + { + auto info=sSpellMgr->GetSpellInfo(id); + return info && info->SpellLevel<=p->getLevel() && info->BaseLevel<=p->getLevel() && + p->IsSpellFitByClassAndRace(id) && SpellMgr::IsSpellValid(info,p,false); + } + std::vector Taught(uint32 id) + { + std::vector result; + if(auto info=sSpellMgr->GetSpellInfo(id)) + { + for(auto const& effect:info->Effects) + if(effect.Effect==SPELL_EFFECT_LEARN_SPELL && effect.TriggerSpell)result.push_back(effect.TriggerSpell); + if(result.empty())result.push_back(id); + } + return result; + } + void ClassTraining(Player* p) + { + if(auto rows=WorldDatabase.Query("SELECT Id FROM trainer WHERE Type=0 ORDER BY Id"))do + { + auto trainer=sObjectMgr->GetTrainer(rows->Fetch()[0].GetUInt32()); + if(!trainer || !trainer->IsTrainerValidForPlayer(p))continue; + // Repeat for prerequisite chains without granting arbitrary class- + // family NPC spells or abilities from a different specialization. + for(unsigned pass=0;pass<8;++pass)for(auto const& spell:trainer->GetSpells()) + { + if(!trainer->CanTeachSpell(p,&spell))continue; + for(auto id:Taught(spell.SpellId)) + if(!p->HasSpell(id) && Learnable(p,id))p->LearnSpell(id,false); + } + }while(rows->NextRow()); + for(auto ability:sSkillLineAbilityStore) + { + auto skill=sSkillLineStore.LookupEntry(ability->SkillLine); + auto rc=sDBCManager.GetSkillRaceClassInfo(ability->SkillLine,p->getRace(),p->getClass()); + if(!skill || !rc || rc->MinLevel>p->getLevel() || + (skill->CategoryID!=SKILL_CATEGORY_ARMOR && skill->CategoryID!=SKILL_CATEGORY_WEAPON))continue; + if(!p->HasSpell(ability->Spell) && Learnable(p,ability->Spell))p->LearnSpell(ability->Spell,false); + } + p->UpdateSkillsForLevel(); + for(auto skill:sSkillLineStore) + if(skill->CategoryID==SKILL_CATEGORY_WEAPON && p->HasSkill(skill->ID)) + if(auto maximum=p->GetPureMaxSkillValue(skill->ID)) + p->SetSkill(skill->ID,p->GetSkillStep(skill->ID),maximum,maximum); + } + bool Talents(Player* p,Archetype const& a) + { + if(p->getLevel()<10)return true; + p->InitTalentForLevel(); + if(!p->LearnPrimaryTalentSpecialization(a.Spec))return false; + auto tabs=sDBCManager.GetTalentTabPages(p->getClass());if(!tabs)return false; + if(a.Class==CLASS_WARRIOR && a.Spec==0) + AdventureCombatPreparationPolicy::SpendArms60([&](uint32 id,unsigned rank){return p->LearnTalent(id,rank);},[&](){return p->GetFreeTalentPoints();}); + // Native LearnTalent enforces tier/dependency/rank and 31-point gates. + // The named solo spec is preferred; secondary trees only use leftovers. + for(unsigned tree=0;treeGetFreeTalentPoints();++tree) + { + uint32 tab=tabs[(a.Spec+tree)%MAX_TALENT_TABS];std::vector talents; + for(auto entry:sTalentStore)if(entry->TabID==tab)talents.push_back(entry); + std::sort(talents.begin(),talents.end(),[](TalentEntry const* x,TalentEntry const* y){return std::tie(x->TierID,x->ColumnIndex,x->ID)TierID,y->ColumnIndex,y->ID);}); + for(unsigned pass=0;pass<50 && p->GetFreeTalentPoints();++pass) + { + bool progress=false; + for(auto talent:talents)for(unsigned rank=0;rankGetFreeTalentPoints();++rank) + if(talent->SpellRank[rank] && p->LearnTalent(talent->ID,rank))progress=true; + if(!progress)break; + } + } + return !p->GetFreeTalentPoints(); + } + bool Profession(Player* p,unsigned wanted) + { + struct Rank {uint32 Id,Step,Level,Required;};std::vector ranks; + if(auto rows=WorldDatabase.Query("SELECT DISTINCT SpellId,ReqLevel,ReqSkillRank FROM trainer_spell"))do + { + auto f=rows->Fetch(); + for(auto id:Taught(f[0].GetUInt32())) + { + uint32 step=0;if(LearnedSkill(id,step)!=wanted || !step)continue; + auto info=sSpellMgr->GetSpellInfo(id);if(!info)continue; + ranks.push_back({id,step,std::max({f[1].GetUInt8(),info->SpellLevel,info->BaseLevel}),f[2].GetUInt32()}); + } + }while(rows->NextRow()); + std::sort(ranks.begin(),ranks.end(),[](Rank const& a,Rank const& b){return std::tie(a.Step,a.Level,a.Id)p->getLevel() || rank.Required>p->GetPureSkillValue(wanted) || !Learnable(p,rank.Id))continue; + if(!p->HasSpell(rank.Id))p->LearnSpell(rank.Id,false); + if(!p->IsInWorld() && !ClientlessCharacterFactory::PrepareSkillRank(p,wanted,rank.Step))continue; + auto maximum=p->GetPureMaxSkillValue(wanted); + if(maximum)p->SetSkill(wanted,p->GetSkillStep(wanted),maximum,maximum); + } + return p->HasSkill(wanted) && p->GetPureSkillValue(wanted)>0; + } + bool UsefulSpell(uint32 id,Archetype const& a,unsigned depth=0) + { + if(depth>3)return true; + auto spell=sSpellMgr->GetSpellInfo(id);if(!spell)return true; + bool caster=a.Attribute==ITEM_MOD_INTELLECT; + for(auto const& effect:spell->Effects) + { + bool manaAura=effect.ApplyAuraName==SPELL_AURA_PERIODIC_ENERGIZE || effect.ApplyAuraName==SPELL_AURA_MOD_POWER_REGEN || + effect.ApplyAuraName==SPELL_AURA_MOD_POWER_REGEN_PERCENT || effect.ApplyAuraName==SPELL_AURA_MOD_INCREASE_ENERGY || effect.ApplyAuraName==SPELL_AURA_MOD_INCREASE_ENERGY_PERCENT; + if(!caster && effect.MiscValue==POWER_MANA && (manaAura || effect.Effect==SPELL_EFFECT_ENERGIZE || effect.Effect==SPELL_EFFECT_ENERGIZE_PCT))return false; + if(!caster && (effect.ApplyAuraName==SPELL_AURA_MOD_SPELL_HIT_CHANCE || effect.ApplyAuraName==SPELL_AURA_MOD_SPELL_CRIT_CHANCE || + effect.ApplyAuraName==SPELL_AURA_MOD_SPELL_CRIT_CHANCE_SCHOOL || effect.ApplyAuraName==SPELL_AURA_MOD_HEALING_DONE || + (effect.ApplyAuraName==SPELL_AURA_MOD_DAMAGE_DONE && !(effect.MiscValue&1))))return false; + if(caster && (effect.ApplyAuraName==SPELL_AURA_MOD_ATTACK_POWER || effect.ApplyAuraName==SPELL_AURA_MOD_RANGED_ATTACK_POWER || + (effect.ApplyAuraName==SPELL_AURA_MOD_DAMAGE_DONE && !(effect.MiscValue&126))))return false; + if(effect.ApplyAuraName==SPELL_AURA_MOD_STAT && + ((!caster && effect.MiscValue==int(StatType::Intellect)) || (caster && (effect.MiscValue==int(StatType::Strength) || effect.MiscValue==int(StatType::Agility)))))return false; + if(effect.TriggerSpell && !UsefulSpell(effect.TriggerSpell,a,depth+1))return false; + } + return true; + } + bool Slot(ItemTemplate const* item,unsigned slot,Archetype const& a) + { + unsigned type=item->GetInventoryType(); + switch(slot) + { + case EQUIPMENT_SLOT_HEAD:return type==INVTYPE_HEAD; + case EQUIPMENT_SLOT_NECK:return type==INVTYPE_NECK; + case EQUIPMENT_SLOT_SHOULDERS:return type==INVTYPE_SHOULDERS; + case EQUIPMENT_SLOT_CHEST:return type==INVTYPE_CHEST || type==INVTYPE_ROBE; + case EQUIPMENT_SLOT_WAIST:return type==INVTYPE_WAIST; + case EQUIPMENT_SLOT_LEGS:return type==INVTYPE_LEGS; + case EQUIPMENT_SLOT_FEET:return type==INVTYPE_FEET; + case EQUIPMENT_SLOT_WRISTS:return type==INVTYPE_WRISTS; + case EQUIPMENT_SLOT_HANDS:return type==INVTYPE_HANDS; + case EQUIPMENT_SLOT_FINGER1:case EQUIPMENT_SLOT_FINGER2:return type==INVTYPE_FINGER; + case EQUIPMENT_SLOT_TRINKET1:case EQUIPMENT_SLOT_TRINKET2:return type==INVTYPE_TRINKET; + case EQUIPMENT_SLOT_BACK:return type==INVTYPE_CLOAK; + case EQUIPMENT_SLOT_MAINHAND: + if(a.Class==CLASS_ROGUE)return type==INVTYPE_WEAPON || type==INVTYPE_WEAPONMAINHAND; + if(a.Attribute==ITEM_MOD_INTELLECT)return type==INVTYPE_2HWEAPON && item->GetSubClass()==ITEM_SUBCLASS_WEAPON_STAFF; + return type==INVTYPE_2HWEAPON; + case EQUIPMENT_SLOT_OFFHAND:return a.Class==CLASS_ROGUE && (type==INVTYPE_WEAPON || type==INVTYPE_WEAPONOFFHAND); + case EQUIPMENT_SLOT_RANGED: + return a.Class==CLASS_HUNTER?item->IsRangedWeapon():a.Armor==ITEM_SUBCLASS_ARMOR_CLOTH && item->GetClass()==ITEM_CLASS_WEAPON && item->GetSubClass()==ITEM_SUBCLASS_WEAPON_WAND; + default:return false; + } + } + bool Equip(Player* p,Archetype const& a,std::mt19937& random,std::string& error) + { + for(unsigned slot=EQUIPMENT_SLOT_START;slot candidates; + uint32 salt=random(); + for(auto const& pair:*sObjectMgr->GetItemTemplateStore()) + { + auto const* item=&pair.second; + if(!Slot(item,slot,a) || p->GetItemCount(pair.first)>0 || + !AdventureGeneratorPolicy::EligibleItem(p->getLevel(),std::max(0,item->GetRequiredLevel()),item->GetQuality(),item->GetBaseItemLevel(), + item->GetDuration() || (item->GetFlags()&ITEM_FLAG_CONJURED),item->GetRequiredReputationFaction() || item->GetArea() || (item->GetMap()!=0 && item->GetMap()!=uint32(-1)) || item->GetHolidayID()!=HOLIDAY_NONE || item->GetRandomProperty() || item->GetRandomSuffix()) || + p->CanUseItem(item)!=EQUIP_ERR_OK)continue; + if(item->GetClass()==ITEM_CLASS_ARMOR && slot!=EQUIPMENT_SLOT_NECK && slot!=EQUIPMENT_SLOT_BACK && + slot!=EQUIPMENT_SLOT_FINGER1 && slot!=EQUIPMENT_SLOT_FINGER2 && slot!=EQUIPMENT_SLOT_TRINKET1 && slot!=EQUIPMENT_SLOT_TRINKET2 && + item->GetSubClass()!=AdventureGeneratorPolicy::Armor(a,p->getLevel()))continue; + bool effects=true; + for(auto const& effect:item->Effects)if(effect.SpellID && !UsefulSpell(effect.SpellID,a))effects=false; + if(!effects)continue; + float stat=0,wrong=0; + for(unsigned index=0;indexGetItemStatType(index);float value=item->GetStatValue(index,p); + if(type==int(a.Attribute))stat+=value; + else if(type==ITEM_MOD_STRENGTH || type==ITEM_MOD_AGILITY || type==ITEM_MOD_INTELLECT || + (a.Attribute==ITEM_MOD_INTELLECT && (type==ITEM_MOD_ATTACK_POWER || type==ITEM_MOD_RANGED_ATTACK_POWER)) || + (a.Attribute!=ITEM_MOD_INTELLECT && (type==ITEM_MOD_SPIRIT || type==ITEM_MOD_MANA || type==ITEM_MOD_MANA_REGENERATION || type==ITEM_MOD_SPELL_POWER || type==ITEM_MOD_SPELL_PENETRATION)))wrong+=value; + } + if(wrong>0 && stat==0)continue; + candidates.push_back({pair.first,item->GetBaseItemLevel()+stat*2-wrong+float((pair.first*2654435761u+salt)%100)/100}); + } + std::sort(candidates.begin(),candidates.end(),[](Candidate const& x,Candidate const& y){return x.Score==y.Score?x.Idy.Score;}); + if(!candidates.empty()) + { + auto end=candidates.begin(); + while(end!=candidates.end() && end-candidates.begin()<8 && end->Score>=candidates.front().Score*0.9f)++end; + std::shuffle(candidates.begin(),end,random); + } + bool equipped=false; + for(auto const& candidate:candidates) + { + auto dispose=[&](Item* item){if(item){RemoveItemFromUpdateQueueOf(item,p);delete item;}}; + std::unique_ptr item(Item::CreateItem(candidate.Id,1,p),dispose);uint16 destination=0; + if(!item || p->CanEquipItem(slot,destination,item.get(),true)!=EQUIP_ERR_OK)continue; + if(auto previous=p->GetItemByPos(INVENTORY_SLOT_BAG_0,slot)) + { + ItemPosCountVec storage; + if(p->CanStoreItem(NULL_BAG,NULL_SLOT,storage,previous,false)!=EQUIP_ERR_OK){error="No space to retain initial equipment.";return false;} + p->RemoveItem(INVENTORY_SLOT_BAG_0,slot,true);p->StoreItem(storage,previous,true); + } + if(!p->EquipItem(destination,item.get(),true)){error="Native equipment placement failed.";return false;} + item.release();equipped=true;break; + } + bool required=slot==EQUIPMENT_SLOT_MAINHAND || (a.Class==CLASS_HUNTER && slot==EQUIPMENT_SLOT_RANGED) || + slot==EQUIPMENT_SLOT_CHEST || slot==EQUIPMENT_SLOT_LEGS || slot==EQUIPMENT_SLOT_FEET || + (p->getLevel()>=30 && slot!=EQUIPMENT_SLOT_RANGED && slot!=EQUIPMENT_SLOT_OFFHAND); + if(!equipped && required){error="No legal profile-matching equipment for slot "+std::to_string(slot);return false;} + } + return true; + } +} +AdventureGeneratorMgr* AdventureGeneratorMgr::instance(){static AdventureGeneratorMgr mgr;return &mgr;} +bool AdventureGeneratorMgr::Profile(uint32 guid,std::string& lua) +{ + if(!Schema())return false; + auto row=CharacterDatabase.PQuery("SELECT profile_lua FROM vcore_adventure_generated_bot WHERE character_guid=%u AND state='ready'",guid); + if(!row)return false; + lua=row->Fetch()[0].GetString(); + return !lua.empty(); +} +bool AdventureGeneratorMgr::Prepare(Player* p,Archetype const& a,std::string& error) +{ + ClassTraining(p); + if(!Talents(p,a)){error="Native talent plan could not be completed.";return false;} + for(auto skill:a.Skills)if(!Profession(p,skill)){error="No native profession rank could be learned for skill "+std::to_string(skill);return false;} + p->SetFreePrimaryProfessions(0); // the generated profile owns exactly two primaries + if(!sAdventureSupplyMgr->PrepareCharacter(p)){error="Could not prepare secondary professions and bags.";return false;} + return true; +} +bool AdventureGeneratorMgr::Create(ChatHandler* handler,uint32 account,uint32 seed,uint8 level,unsigned faction,std::string const& key,bool persistent) +{ + std::mt19937 random(seed); + struct Choice {Archetype const* Type;uint8 Race;};std::vector choices; + for(auto const& a:AdventureGeneratorPolicy::Archetypes()) + { + if(key!="any" && key!=a.Key)continue; + if(a.Class==CLASS_DEATH_KNIGHT && level<55)continue; + for(uint8 race:{1,2,3,4,5,6,7,8,9,10,11,22}) + if((!faction || AdventureGeneratorPolicy::Faction(race)==faction) && sObjectMgr->GetPlayerInfo(race,a.Class))choices.push_back({&a,race}); + } + if(choices.empty()){handler->SendSysMessage("No legal race/class archetype fits this request.");return false;} + auto choice=choices[random()%choices.size()];auto const& a=*choice.Type; + uint8 gender=random()%2;std::string name; + for(unsigned attempt=0;attempt<1024;++attempt) + { + auto candidate=AdventureGeneratorPolicy::Name(choice.Race,gender,random); + if(ObjectMgr::CheckPlayerName(candidate,LOCALE_enUS,true)!=CHAR_NAME_SUCCESS || sObjectMgr->IsReservedName(candidate) || + sCharacterCache->GetCharacterCacheByName(candidate))continue; + std::string lower=candidate; + std::transform(lower.begin(),lower.end(),lower.begin(),[](unsigned char ch){return char(std::tolower(ch));}); + auto path=std::filesystem::path(sClientlessLuaEngine->GetScriptRoot())/"adventure"/"profiles"/(lower+".lua"); + if(std::filesystem::exists(path))continue; // reserve hand-written identities too + std::string escaped=candidate;CharacterDatabase.EscapeString(escaped); + if(!CharacterDatabase.PQuery("SELECT 1 FROM characters WHERE name='%s' LIMIT 1",escaped.c_str())){name=candidate;break;} + } + if(name.empty()){handler->SendSysMessage("Roleplay name pool exhausted; no numeric suffix was substituted.");return false;} + // Mature goblin/worgen travelers have left their phased introduction. + uint8 homeRace=choice.Race==9?2:choice.Race==22?1:choice.Race; + auto info=sObjectMgr->GetPlayerInfo(homeRace,CLASS_WARRIOR); + if(!info){handler->SendSysMessage("Native racial home data unavailable.");return false;} + if(!sAdventureLocationMgr->EnsureLoaded()){handler->SendSysMessage("Adventure location catalog unavailable.");return false;} + AdventureLocation const* home=nullptr;float best=1e30f; + for(auto const& location:sAdventureLocationMgr->Locations()) + { + if(location.IsArea || location.Point.MapId!=info->mapId || + (location.Faction && location.Faction!=AdventureGeneratorPolicy::Faction(choice.Race)) || + (location.Point.Category!="tavern" && location.Point.Category!="settlement") || + sAdventureLocationMgr->Weight(location,"social")<=0)continue; + float dx=location.Point.X-info->positionX,dy=location.Point.Y-info->positionY; + float distance=dx*dx+dy*dy; + if(distanceSendSysMessage("No faction-compatible home near the native racial starting area.");return false;} + std::string profile=AdventureGeneratorPolicy::Profile(name,a,choice.Race,home->Point.Id,home->Region,random); + std::string escapedProfile=profile;CharacterDatabase.EscapeString(escapedProfile); + ClientlessCharacterFactory::Definition definition{name,choice.Race,uint8(a.Class),gender,level,seed}; + auto result=ClientlessCharacterFactory::Create(account,definition, + [&](Player* player,std::string& error) + { + if(player->GetMapId()!=home->Point.MapId) + { + player->ResetMap();player->SetMap(sMapMgr->CreateMap(home->Point.MapId,player)); + } + player->Relocate(home->Point.X,home->Point.Y,home->Point.Z,home->Point.O); + if(!Prepare(player,a,error) || !Equip(player,a,random,error))return false; + // Save its bind row in the SAME + // transaction instead of calling SetHomebind's immediate UPDATE. + return true; + }, + [&](CharacterDatabaseTransaction const& tx,Player* player) + { + tx->PAppend("INSERT INTO vcore_adventure_generated_bot (character_guid,account_id,archetype,seed,persistent,state,profile_lua,created_at) VALUES (%u,%u,'%s',%u,%u,'ready','%s',%llu)", + player->GetGUID().GetCounter(),account,a.Key,seed,persistent?1:0,escapedProfile.c_str(),static_cast(GameTime::GetGameTime())); + tx->PAppend("INSERT INTO character_homebind (guid,mapId,zoneId,posX,posY,posZ) VALUES (%u,%u,%u,%f,%f,%f)", + player->GetGUID().GetCounter(),home->Point.MapId,home->ZoneId,home->Point.X,home->Point.Y,home->Point.Z); + }); + if(!result.Guid){handler->PSendSysMessage("Generation failed name='%s': %s",name.c_str(),result.Error.c_str());return false;} + handler->PSendSysMessage("Generated %s guid=%u race=%u class=%u level=%u archetype=%s persistent=%u seed=%u home=%s (offline).", + name.c_str(),result.Guid,choice.Race,a.Class,level,a.Key,persistent?1:0,seed,home->Point.Id.c_str()); + TC_LOG_INFO("server.adventurebot","BOT_GENERATED guid=%u name='%s' race=%u class=%u level=%u archetype='%s' seed=%u persistent=%u",result.Guid,name.c_str(),choice.Race,a.Class,level,a.Key,seed,persistent?1:0); + return true; +} +bool AdventureGeneratorMgr::Command(ChatHandler* handler,char const* args) +{ + if(!Schema()){handler->SendSysMessage("Apply the AdventureBot generator character migration first.");return false;} + std::istringstream input(args?args:"");std::string op;input>>op; + if(op=="create") + { + uint32 account=0,count=0,level=60,seed=uint32(GameTime::GetGameTime());std::string side="any",key="any",mode="persistent",extra; + if(!(input>>account>>count)){handler->SendSysMessage("Usage: clientless generate create [level 10..85] [any|alliance|horde] [archetype|any] [persistent|temporary] [seed]");return false;} + input>>std::ws; + if(!input.eof()) + { + if(!(input>>level>>side>>key>>mode>>seed) || input>>extra){handler->SendSysMessage("Optional arguments must be supplied together: level faction archetype persistence seed.");return false;} + } + if(count<1 || count>10 || level<10 || level>85 || (side!="any" && side!="alliance" && side!="horde") || (mode!="persistent" && mode!="temporary"))return false; + unsigned created=0; + for(uint32 i=0;iPSendSysMessage("Generation batch complete: %u/%u characters, each atomically saved; no existing characters modified.",created,count); + return created==count; + } + if(op=="list") + { + if(auto rows=CharacterDatabase.Query("SELECT g.character_guid,c.name,g.archetype,g.persistent,g.state,c.online,g.seed FROM vcore_adventure_generated_bot g JOIN characters c ON c.guid=g.character_guid ORDER BY g.character_guid LIMIT 100"))do + { + auto f=rows->Fetch();handler->PSendSysMessage("%u %s archetype=%s persistent=%u state=%s online=%u seed=%u",f[0].GetUInt32(),f[1].GetString().c_str(),f[2].GetString().c_str(),f[3].GetUInt8(),f[4].GetString().c_str(),f[5].GetUInt8(),f[6].GetUInt32()); + }while(rows->NextRow()); + return true; + } + uint32 guid=0;input>>guid; + auto row=CharacterDatabase.PQuery("SELECT g.account_id,g.state,g.persistent,c.online,g.archetype,g.seed FROM vcore_adventure_generated_bot g JOIN characters c ON c.guid=g.character_guid WHERE g.character_guid=%u",guid); + if(!row){handler->SendSysMessage("Not a generator-owned character; no action taken.");return false;} + auto f=row->Fetch(); + if(op=="profile") + { + std::string lua;if(!Profile(guid,lua))return false;handler->SendSysMessage(lua.c_str());return true; + } + if(op=="equip" && f[1].GetString()=="ready") + { + auto session=sWorld->FindClientlessSession(guid);auto player=session?session->GetPlayer():nullptr; + if(!player || !player->IsInWorld() || !player->IsAlive() || player->IsInCombat() || player->IsBeingTeleported() || + player->GetTransport() || player->IsInFlight() || player->HasUnitState(UNIT_STATE_CASTING)) + {handler->SendSysMessage("Equipment refresh requires an idle online generator-owned bot outside combat/transit/casting.");return false;} + for(auto const& archetype:AdventureGeneratorPolicy::Archetypes()) + if(f[4].GetString()==archetype.Key && player->getClass()==archetype.Class) + { + sAdventureBehaviorMgr->InterruptForInteraction(player); + std::mt19937 random(f[5].GetUInt32());std::string error; + if(!Equip(player,archetype,random,error)){handler->PSendSysMessage("Equipment refresh stopped: %s",error.c_str());return false;} + player->UpdateMaxHealth();player->SetFullHealth();player->SetFullPower(POWER_MANA); + auto tx=CharacterDatabase.BeginTransaction();player->SaveToDB(tx); + auto saved=CharacterDatabase.AsyncCommitTransaction(tx);if(!saved.m_future.get())return false; + handler->SendSysMessage("Profile equipment refreshed through native checks; previous items retained in bags."); + TC_LOG_INFO("server.adventurebot","BOT_EQUIPMENT_REFRESH guid=%u archetype='%s'",guid,archetype.Key); + return true; + } + return false; + } + if(op=="login" && f[1].GetString()=="ready")return sClientlessPlayerMgr->StartPlayer(f[0].GetUInt32(),guid,CreateClientlessAdventureLuaController()); + if(op=="logout")return sClientlessPlayerMgr->StopPlayer(guid); + if(op=="retire") + { + if(f[2].GetUInt8() || f[3].GetUInt8() || sWorld->GetClientlessPlayerState(guid)!=ClientlessPlayerState::Offline) + {handler->SendSysMessage("Retirement requires an offline temporary generator-owned character. Persistent characters are protected.");return false;} + auto tx=CharacterDatabase.BeginTransaction();tx->PAppend("UPDATE vcore_adventure_generated_bot SET state='retired',retired_at=%llu WHERE character_guid=%u AND persistent=0 AND state='ready'",static_cast(GameTime::GetGameTime()),guid); + auto result=CharacterDatabase.AsyncCommitTransaction(tx); + if(!result.m_future.get())return false; + handler->SendSysMessage("Temporary bot retired. Native character, items and profile retained; no deletion or automatic replacement.");return true; + } + handler->SendSysMessage("Usage: clientless generate create|list|profile |login |logout |equip |retire ");return false; +} diff --git a/src/server/game/World/AdventureGeneratorMgr.h b/src/server/game/World/AdventureGeneratorMgr.h new file mode 100644 index 0000000000..a698453829 --- /dev/null +++ b/src/server/game/World/AdventureGeneratorMgr.h @@ -0,0 +1,22 @@ +#ifndef VCORE_ADVENTURE_GENERATOR_MGR_H +#define VCORE_ADVENTURE_GENERATOR_MGR_H +#include "Common.h" +#include "AdventureGeneratorPolicy.h" +#include +class Player; +class ChatHandler; +class TC_GAME_API AdventureGeneratorMgr +{ +public: + static AdventureGeneratorMgr* instance(); + bool Command(ChatHandler* handler,char const* args); + // Lua profile stays in the character DB; hand-written named profiles retain + // precedence. A generated identity is never inferred from account/name. + static bool Profile(uint32 guid,std::string& lua); +private: + bool Create(ChatHandler* handler,uint32 account,uint32 seed,uint8 level,unsigned faction, + std::string const& archetype,bool persistent); + bool Prepare(Player* player,AdventureGeneratorPolicy::Archetype const& archetype,std::string& error); +}; +#define sAdventureGeneratorMgr AdventureGeneratorMgr::instance() +#endif diff --git a/src/server/game/World/AdventureGeneratorPolicy.h b/src/server/game/World/AdventureGeneratorPolicy.h new file mode 100644 index 0000000000..13c9aa92bf --- /dev/null +++ b/src/server/game/World/AdventureGeneratorPolicy.h @@ -0,0 +1,105 @@ +#ifndef VCORE_ADVENTURE_GENERATOR_POLICY_H +#define VCORE_ADVENTURE_GENERATOR_POLICY_H +#include +#include +#include +#include +#include +#include +#include "AdventureProfessionPolicy.h" +namespace AdventureGeneratorPolicy +{ + struct Archetype + { + char const* Key;unsigned Class,Spec,Attribute,Armor; + std::array Skills; + char const* Vocation; + }; + inline std::array const& Archetypes() + { + static std::array const values={{{"warrior",1,0,4,4,{186,164},"Wandernder Waffenschmied"}, + {"paladin",2,2,4,4,{186,755},"Pilger und Edelsteinschleifer"}, + {"hunter",3,1,3,3,{393,165},"Faehrtensucher und Lederhandwerker"}, + {"rogue",4,1,3,2,{186,202},"Kundschafter und Tueftler"}, + {"priest",5,2,5,1,{182,773},"Reisender Chronist"}, + {"death_knight",6,2,4,4,{186,164},"Schweigsamer Wanderschmied"}, + {"shaman",7,0,5,3,{182,171},"Pfadfinder und Kraeuterkundiger"}, + {"mage",8,1,5,1,{197,333},"Reisender Gelehrter"}, + {"warlock",9,0,5,1,{197,333},"Sucher vergessener Schriften"}, + {"druid",11,0,5,2,{182,171},"Hueter der wilden Pfade"}}}; + return values; + } + inline unsigned Faction(unsigned race) + {return race==1 || race==3 || race==4 || race==7 || race==11 || race==22?1:2;} + inline char const* Origin(unsigned race) + { + switch(race) + { + case 1:return "Elwynn";case 2:return "Durotar";case 3:return "Dun Morogh"; + case 4:return "Teldrassil";case 5:return "Tirisfal";case 6:return "Mulgore"; + case 7:return "Dun Morogh";case 8:return "den Echoinseln";case 9:return "Kezan"; + case 10:return "dem Immersangwald";case 11:return "der Azurmythosinsel";case 22:return "Gilneas"; + default:return "fernen Landen"; + } + } + inline std::string Name(unsigned race,unsigned gender,std::mt19937& random) + { + // Small cultural grammars, not fragments of arbitrary player/NPC names. + // ASCII names keep native client naming rules and SQL/Lua paths simple. + char const* first=nullptr;char const* last=nullptr; + switch(race) + { + case 2:first=gender?"Gra Sha Mog Drak Ruk Gar Kor Ur":"Gor Thok Drak Rog Mok Gar Kor Ur";last=gender?"sha ra ga dra ka na gra tha":"gar thar mok nak gor rak dan zug";break; + case 3:first=gender?"Hild Bryn Dag Ragn Tor Hel Marn Brin":"Brol Durn Thar Karg Brom Dorn Thol Grun";last=gender?"ra dis na hild ri dra li da":"gar grim din rik gan rum dan rin";break; + case 4:first=gender?"Lun Shae Ely Nara Thala Syl Mira Aela":"Ther Dala Shan Fael Reth Lor Ael Thal";last=gender?"ria na riel dra ra niel sia nor":"ion dris nor rion dor thas riel ron";break; + case 6:first=gender?"Mahu Hara Tahu Nara Asha Oru Runa Yana":"Hamu Tahu Ranu Maku Oru Koru Toru Yaru";last=gender?"na ra la mi sha ni ma wa":"ran mak tar rok nan tam hon kan";break; + case 7:first=gender?"Tilli Bixi Nimi Pelli Fizzi Wini Lilli Miri":"Bim Nib Pib Gim Fizz Tink Wib Nim";last=gender?"na ra li wi mi ni la si":"wick bin win nix mik rik wip lin";break; + case 8:first=gender?"Zan Sha Vola Jara Tali Zula Yala Mala":"Zan Vol Jar Zul Rok Tal Sen Jin";last=gender?"ra jin sha li na ri la ni":"jin rok zan zul tal kan rin mar";break; + case 9:first=gender?"Kiz Vex Nix Rix Gix Zix Bix Tix":"Gaz Rix Niz Vex Gix Zik Bix Kaz";last=gender?"zi ra ni za li ki na si":"nik wick zik rat zle nix bin zek";break; + case 10:first=gender?"Ela Lora Syla Vela Nara Fala Mira Sera":"Vael Aeth Lor Ther Bel Sel Dar Fael";last=gender?"ria riel na ra lin thia sia niel":"rin thas rion dor nor dris ran riel";break; + case 11:first=gender?"Yra Sora Nara Vela Isha Tali Asha Ela":"Vara Mora Aru Tura Iru Dara Kora Baru";last=gender?"na ra li sha la ni sa ri":"dor nun ran vos mar das nol ven";break; + default:first=gender?"Ela Mira Mara Lena Nora Sera Talia Rena":"Ald Gar Ed Bran Cor Dar Ren Hal";last=gender?"na ra lin ria la nda sia nora":"ren ric dan win lan den ron ven";break; + } + auto pick=[&](char const* words) + { + std::string list(words);unsigned choice=random()%8;std::size_t start=0; + for(unsigned i=0;i=3?a.Armor-1:a.Armor;} + inline bool EligibleItem(unsigned level,unsigned required,unsigned quality,unsigned itemLevel,bool temporary,bool restricted) + { + return required<=level && quality>=1 && quality<=3 && !temporary && !restricted && + (required+15>=level || level<20) && itemLevel<=level*5+20; + } + inline std::string Profile(std::string const& name,Archetype const& a,unsigned race, + std::string const& location,std::string const& region,std::mt19937& random) + { + auto quote=LuaString; + std::ostringstream lua;lua<GetSpellInfo(rank.Spell); if(!rc || uint32(rc->MinLevel)>p->getLevel() || !info || !SpellMgr::IsSpellValid(info,p,false))continue; if(!p->HasSpell(rank.Spell))p->LearnSpell(rank.Spell,false); + if(!p->IsInWorld() && !ClientlessCharacterFactory::PrepareSkillRank(p,rank.Skill,rank.Step))continue; auto maximum=p->GetPureMaxSkillValue(rank.Skill); if(maximum && p->GetPureSkillValue(rank.Skill)GetBagByPos(slot))handler->PSendSysMessage("bagSlot=%u item=%u capacity=%u",slot,bag->GetEntry(),bag->GetBagSize()); } + +bool AdventureSupplyMgr::PrepareCharacter(Player* player) +{ + LoadCatalog();LearnSecondaries(player);return Bags(player); +} diff --git a/src/server/game/World/AdventureSupplyMgr.h b/src/server/game/World/AdventureSupplyMgr.h index 226fad80ec..ab3135ea44 100644 --- a/src/server/game/World/AdventureSupplyMgr.h +++ b/src/server/game/World/AdventureSupplyMgr.h @@ -14,6 +14,7 @@ class TC_GAME_API AdventureSupplyMgr public: static AdventureSupplyMgr* instance(); void Online(Player* player); + bool PrepareCharacter(Player* player); void Offline(Player* player); // True only while an out-of-combat recovery owns movement/intent execution. bool Update(Player* player,uint32 diff,bool combatReaction); diff --git a/src/server/game/World/ClientlessCharacterFactory.cpp b/src/server/game/World/ClientlessCharacterFactory.cpp new file mode 100644 index 0000000000..b8587e4586 --- /dev/null +++ b/src/server/game/World/ClientlessCharacterFactory.cpp @@ -0,0 +1,100 @@ +#include "ClientlessCharacterFactory.h" +#include "AccountMgr.h" +#include "CharacterCache.h" +#include "DBCStores.h" +#include "GameTime.h" +#include "MotionMaster.h" +#include "ObjectMgr.h" +#include "Player.h" +#include "Realm.h" +#include "ScriptMgr.h" +#include "World.h" +#include "WorldSession.h" +#include +#include +#include + +namespace +{ + struct CreateInfo final : CharacterCreateInfo + { + explicit CreateInfo(ClientlessCharacterFactory::Definition const& d) + { + Name=d.Name;Race=d.Race;Class=d.Class;Gender=d.Gender; + std::mt19937 random(d.Seed); + // Native appearance checks are authoritative, including hero-class + // and race-specific restrictions. No unvalidated display overrides. + for(unsigned attempt=0;attempt<20000;++attempt) + { + Skin=random()%16;Face=random()%10;HairStyle=random()%20;HairColor=random()%16;FacialHair=random()%9; + if(Player::ValidateAppearance(Race,Class,Gender,HairStyle,HairColor,Face,FacialHair,Skin,true))return; + } + Skin=Face=HairStyle=HairColor=FacialHair=0; // native Create still validates this fallback + } + }; +} +ClientlessCharacterFactory::Result ClientlessCharacterFactory::Create(uint32 account,Definition const& d,Prepare prepare,Append append) +{ + auto fail=[](char const* error){return Result{0,error};}; + if(!account || sWorld->FindSession(account))return fail("Account is missing or has an active network session."); + if(AccountMgr::GetSecurity(account,realm.Id.Realm)!=SEC_PLAYER)return fail("Use a normal dedicated bot account, never a privileged account."); + if(!sObjectMgr->GetPlayerInfo(d.Race,d.Class) || d.Gender>GENDER_FEMALE || + d.Level<1 || d.Level>sWorld->getIntConfig(CONFIG_MAX_PLAYER_LEVEL) || (d.Class==CLASS_DEATH_KNIGHT && d.Level<55)) + return fail("Invalid race/class/gender/level combination."); + std::string name=d.Name; + if(!normalizePlayerName(name) || name!=d.Name || ObjectMgr::CheckPlayerName(name,LOCALE_enUS,true)!=CHAR_NAME_SUCCESS || + sObjectMgr->IsReservedName(name) || sCharacterCache->GetCharacterCacheByName(name))return fail("Name is invalid, reserved or already used."); + CharacterDatabase.EscapeString(name); + if(CharacterDatabase.PQuery("SELECT 1 FROM characters WHERE name='%s' LIMIT 1",name.c_str()))return fail("Name already exists in the character database."); + auto acc=LoginDatabase.PQuery("SELECT username,expansion,locale FROM account WHERE id=%u",account); + if(!acc)return fail("Account does not exist."); + auto f=acc->Fetch(); + auto race=sChrRacesStore.LookupEntry(d.Race);auto cls=sChrClassesStore.LookupEntry(d.Class); + if(!race || !cls || race->Race_related>f[1].GetUInt8() || cls->Required_expansion>f[1].GetUInt8())return fail("Account expansion does not permit this race/class."); + auto count=CharacterDatabase.PQuery("SELECT COUNT(*) FROM characters WHERE account=%u AND deleteDate IS NULL",account); + if(!count || count->Fetch()[0].GetUInt32()>=sWorld->getIntConfig(CONFIG_CHARACTERS_PER_REALM))return fail("Native per-realm character limit reached."); + WorldSession session(account,f[0].GetString(),nullptr,SEC_PLAYER,f[1].GetUInt8(),0, + LocaleConstant(std::min(f[2].GetUInt8(),TOTAL_LOCALES-1)),0,false,WorldSession::WorldSessionMode::Clientless); + auto cleanup=[](Player* player){player->CleanupsBeforeDelete();delete player;}; + std::unique_ptr player(new Player(&session),cleanup); + player->GetMotionMaster()->Initialize();CreateInfo info(d); + if(!player->Create(sObjectMgr->GetGenerator().Generate(),&info))return fail("Native character creation failed."); + player->setCinematic(1); + if(player->getLevel()!=d.Level) + { + // This character is born at its requested level, rather than earning + // levels. Avoid GiveLevel's independent reward-mail transactions/hooks. + player->SetLevel(d.Level);player->InitStatsForLevel(true); + player->InitTalentForLevel();player->InitTaxiNodesForLevel();player->InitGlyphsForLevel(); + } + player->SetCanModifyStats(true); + player->SetMoney(0);player->LearnDefaultSkills();player->UpdateSkillsForLevel(); + std::string error; + if(prepare && !prepare(player.get(),error))return {0,error}; + player->UpdateMaxHealth();player->UpdateMaxPower(POWER_MANA); + player->SetFullHealth();player->SetFullPower(POWER_MANA); + auto tx=CharacterDatabase.BeginTransaction();player->SaveToDB(tx,true); + if(append)append(tx,player.get()); + auto commit=CharacterDatabase.AsyncCommitTransaction(tx); + if(!commit.m_future.get())return fail("Atomic character save failed; no ready character was published."); + uint32 guid=player->GetGUID().GetCounter(); + sCharacterCache->AddCharacterCacheEntry(player->GetGUID(),account,d.Name,d.Gender,d.Race,d.Class,d.Level); + sScriptMgr->OnPlayerCreate(player.get()); + auto realmCount=LoginDatabase.GetPreparedStatement(LOGIN_REP_REALM_CHARACTERS); + realmCount->setUInt32(0,count->Fetch()[0].GetUInt32()+1);realmCount->setUInt32(1,account);realmCount->setUInt32(2,realm.Id.Realm); + LoginDatabase.Execute(realmCount); + return {guid,{}}; +} + +bool ClientlessCharacterFactory::PrepareSkillRank(Player* player,uint32 skill,uint32 step) +{ + if(!player || player->IsInWorld() || !step || step>MAX_SKILL_STEP)return false; + auto rc=sDBCManager.GetSkillRaceClassInfo(skill,player->getRace(),player->getClass()); + if(!rc || rc->MinLevel>player->getLevel())return false; + auto tier=sSkillTiersStore.LookupEntry(rc->SkillTierID); + if(!tier || !tier->Value[step-1])return false; + if(player->GetSkillStep(skill)>step)return true; + auto maximum=tier->Value[step-1]; + player->SetSkill(skill,step,maximum,maximum); + return player->GetPureSkillValue(skill)==maximum && player->GetPureMaxSkillValue(skill)==maximum; +} diff --git a/src/server/game/World/ClientlessCharacterFactory.h b/src/server/game/World/ClientlessCharacterFactory.h new file mode 100644 index 0000000000..dacf2d9b99 --- /dev/null +++ b/src/server/game/World/ClientlessCharacterFactory.h @@ -0,0 +1,27 @@ +#ifndef VCORE_CLIENTLESS_CHARACTER_FACTORY_H +#define VCORE_CLIENTLESS_CHARACTER_FACTORY_H +#include "Common.h" +#include "DatabaseEnv.h" +#include +#include +class Player; +// Native character creation for clientless consumers. No AdventureBot policy, +// source-character cloning, authentication credentials or deletion behavior. +class TC_GAME_API ClientlessCharacterFactory +{ +public: + struct Definition + { + std::string Name; + uint8 Race=0,Class=0,Gender=0,Level=60; + uint32 Seed=0; + }; + struct Result { uint32 Guid=0;std::string Error; }; + using Prepare=std::function; + using Append=std::function; + // Administrative preparation before world insertion; callers still gate + // trainer/rank/level eligibility. Uses the native SkillRaceClass/SkillTiers cap. + static bool PrepareSkillRank(Player* player,uint32 skill,uint32 step); + static Result Create(uint32 account,Definition const& definition,Prepare prepare,Append append); +}; +#endif diff --git a/src/server/game/World/ClientlessLuaEngine.cpp b/src/server/game/World/ClientlessLuaEngine.cpp index ac0360e6ea..0fe7a50205 100644 --- a/src/server/game/World/ClientlessLuaEngine.cpp +++ b/src/server/game/World/ClientlessLuaEngine.cpp @@ -6,6 +6,7 @@ #include "AdventureBehaviorMgr.h" #include "AdventureCombatMgr.h" #include "AdventureInteractionMgr.h" +#include "AdventureGeneratorMgr.h" #include "AdventureSupplyMgr.h" #include "AdventureSocialActions.h" #include "Config.h" @@ -335,8 +336,12 @@ bool ClientlessLuaEngine::LoadAdventureProfile(Player* player) fs::path generic = root / "adventure" / "profiles" / "generic.lua"; fs::path chosen = fs::exists(specific) ? specific : generic; + std::string generated; + bool databaseProfile=!fs::exists(specific) && AdventureGeneratorMgr::Profile(player->GetGUID().GetCounter(),generated); + if(databaseProfile)chosen=fs::path("generated:"+std::to_string(player->GetGUID().GetCounter())); int base = lua_gettop(_state); - if (luaL_loadfile(_state, chosen.string().c_str()) != LUA_OK) + int loaded=databaseProfile?luaL_loadbuffer(_state,generated.data(),generated.size(),chosen.string().c_str()):luaL_loadfile(_state,chosen.string().c_str()); + if (loaded != LUA_OK) { TC_LOG_ERROR("server.adventurebot", "[AdventureBot][Profile] guid=%u name='%s' load failed path='%s': %s", @@ -384,6 +389,8 @@ bool ClientlessLuaEngine::LoadAdventureProfile(Player* player) { profile.CharacterName = ReadStringField(_state, -1, "name", profile.CharacterName); profile.HomeRegion = ReadStringField(_state, -1, "home_region"); + profile.Background = ReadStringField(_state, -1, "background"); + profile.Vocation = ReadStringField(_state, -1, "vocation"); } lua_pop(_state, 1); diff --git a/src/server/game/World/ClientlessLuaEngine.h b/src/server/game/World/ClientlessLuaEngine.h index d8794896ef..3ee63fc114 100644 --- a/src/server/game/World/ClientlessLuaEngine.h +++ b/src/server/game/World/ClientlessLuaEngine.h @@ -27,6 +27,7 @@ struct TC_GAME_API ClientlessAdventureProfile std::unordered_set PrimaryProfessions; std::string CharacterName; std::string HomeRegion; + std::string Background, Vocation; std::string SourcePath; bool CanGroundMount = false; diff --git a/src/server/scripts/Custom/viano_bot_commands.cpp b/src/server/scripts/Custom/viano_bot_commands.cpp index 4a842f4769..1cba74641b 100644 --- a/src/server/scripts/Custom/viano_bot_commands.cpp +++ b/src/server/scripts/Custom/viano_bot_commands.cpp @@ -21,6 +21,7 @@ #include "AdventureBehaviorMgr.h" #include "AdventureCombatMgr.h" #include "AdventureInteractionMgr.h" +#include "AdventureGeneratorMgr.h" #include "AdventureSupplyMgr.h" #include "ClientlessTravelGraphMgr.h" #include "ClientlessRouteRecorderMgr.h" @@ -63,6 +64,8 @@ namespace { + bool GenerateBot(ChatHandler* handler,char const* args) + { return sAdventureGeneratorMgr->Command(handler,args); } bool InteractionStatus(ChatHandler* handler,char const* args) { std::istringstream input(args?args:"");uint32 guid=0;input>>guid; @@ -2518,6 +2521,7 @@ static bool HandleClientlessTravelReload(ChatHandler* handler, char const* /*arg { { "combat", rbac::RBAC_PERM_COMMAND_GM, true, &ClientlessCombatPreparation::Command, "" }, { "interaction", rbac::RBAC_PERM_COMMAND_GM, true, &InteractionStatus, "" }, + { "generate", rbac::RBAC_PERM_COMMAND_GM, true, &GenerateBot, "" }, { "luareload", rbac::RBAC_PERM_COMMAND_GM, true, &HandleClientlessLuaReload, "" }, { "transport", rbac::RBAC_PERM_COMMAND_GM, true, &HandleClientlessTransport, "" }, { "login", rbac::RBAC_PERM_COMMAND_GM, true, &HandleClientlessLogin, "" }, diff --git a/tests/clientless/test_adventure_generator_equipment.py b/tests/clientless/test_adventure_generator_equipment.py new file mode 100644 index 0000000000..c1291f7ad5 --- /dev/null +++ b/tests/clientless/test_adventure_generator_equipment.py @@ -0,0 +1,39 @@ +"""Execute production equipment proc affinity, including mana-trinket rejection.""" +from pathlib import Path +import subprocess,tempfile +root=Path(__file__).resolve().parents[2] +s=(root/'src/server/game/World/AdventureGeneratorMgr.cpp').read_text() +a=s.index(' bool UsefulSpell(');b=s.index('{',a);e=b+1;depth=1 +while depth:depth+=(s[e]=='{')-(s[e]=='}');e+=1 +code=r''' +#include "AdventureGeneratorPolicy.h" +#include +#include +#include +using uint32=unsigned;using Archetype=AdventureGeneratorPolicy::Archetype; +enum {ITEM_MOD_INTELLECT=5,POWER_MANA=0,SPELL_AURA_PERIODIC_ENERGIZE=1,SPELL_AURA_MOD_POWER_REGEN,SPELL_AURA_MOD_POWER_REGEN_PERCENT,SPELL_AURA_MOD_INCREASE_ENERGY,SPELL_AURA_MOD_INCREASE_ENERGY_PERCENT,SPELL_EFFECT_ENERGIZE,SPELL_EFFECT_ENERGIZE_PCT,SPELL_AURA_MOD_STAT,SPELL_AURA_MOD_SPELL_HIT_CHANCE,SPELL_AURA_MOD_SPELL_CRIT_CHANCE,SPELL_AURA_MOD_SPELL_CRIT_CHANCE_SCHOOL,SPELL_AURA_MOD_HEALING_DONE,SPELL_AURA_MOD_DAMAGE_DONE,SPELL_AURA_MOD_ATTACK_POWER,SPELL_AURA_MOD_RANGED_ATTACK_POWER}; +enum class StatType{Strength=0,Agility=1,Intellect=3}; +struct Effect{int ApplyAuraName=0,MiscValue=0,Effect=0;unsigned TriggerSpell=0;}; +struct Info{std::vectorEffects;}; +struct Spells{std::unordered_map data;Info*GetSpellInfo(unsigned id){auto i=data.find(id);return i==data.end()?nullptr:&i->second;}}spells;auto sSpellMgr=&spells; +'''+s[a:e]+r''' +int main(){ + auto warrior=AdventureGeneratorPolicy::Archetypes()[0],mage=AdventureGeneratorPolicy::Archetypes()[7]; + spells.data[1].Effects={{SPELL_AURA_PERIODIC_ENERGIZE,POWER_MANA,0,0}}; + assert(!UsefulSpell(1,warrior));assert(UsefulSpell(1,mage)); + spells.data[2].Effects={{0,POWER_MANA,SPELL_EFFECT_ENERGIZE,0}};assert(!UsefulSpell(2,warrior)); + spells.data[3].Effects={{0,0,0,1}};assert(!UsefulSpell(3,warrior));assert(UsefulSpell(3,mage)); + spells.data[4].Effects={{SPELL_AURA_MOD_STAT,int(StatType::Intellect),0,0}};assert(!UsefulSpell(4,warrior));assert(UsefulSpell(4,mage)); + spells.data[5].Effects={{SPELL_AURA_MOD_STAT,int(StatType::Strength),0,0}};assert(UsefulSpell(5,warrior));assert(!UsefulSpell(5,mage)); + spells.data[6].Effects={{0,0,0,6}};assert(UsefulSpell(6,warrior)); // bounded cyclic trigger metadata + spells.data[7].Effects={{SPELL_AURA_MOD_SPELL_HIT_CHANCE,0,0,0}};assert(!UsefulSpell(7,warrior));assert(UsefulSpell(7,mage)); + spells.data[8].Effects={{SPELL_AURA_MOD_DAMAGE_DONE,2,0,0}};assert(!UsefulSpell(8,warrior));assert(UsefulSpell(8,mage)); + spells.data[9].Effects={{SPELL_AURA_MOD_DAMAGE_DONE,1,0,0}};assert(UsefulSpell(9,warrior));assert(!UsefulSpell(9,mage)); + assert(UsefulSpell(999,warrior)); // missing metadata never grants a spell +} +''' +with tempfile.TemporaryDirectory() as d: + p=Path(d);(p/'test.cpp').write_text(code) + subprocess.run(['g++','-std=c++17','-I',str(root/'src/server/game/World'),str(p/'test.cpp'),'-o',str(p/'test')],check=True) + subprocess.run([str(p/'test')],check=True) +print('Production equipment affinity: mana-only/direct/triggered procs, caster/physical stat effects and bounded cycles passed') diff --git a/tests/clientless/test_adventure_generator_ownership.py b/tests/clientless/test_adventure_generator_ownership.py new file mode 100644 index 0000000000..59bcf1709e --- /dev/null +++ b/tests/clientless/test_adventure_generator_ownership.py @@ -0,0 +1,64 @@ +"""Exercise production generator command gates for ownership/retirement.""" +from pathlib import Path +import subprocess,tempfile +root=Path(__file__).resolve().parents[2] +s=(root/'src/server/game/World/AdventureGeneratorMgr.cpp').read_text() +a=s.index('bool AdventureGeneratorMgr::Command(');b=s.index('{',a);e=b+1;depth=1 +while depth:depth+=(s[e]=='{')-(s[e]=='}');e+=1 +code=r''' +#include +#include "AdventureGeneratorPolicy.h" +#include +#include +#include +#include +using uint32=uint32_t;using uint8=uint8_t; +struct Field{unsigned number=0;std::string str="ready";unsigned GetUInt32(){return number;}unsigned GetUInt8(){return number;}std::string GetString(){return str;}}; +struct Row{Field f[6];Field*Fetch(){return f;}bool NextRow(){return false;}}; +struct Transaction{templatevoid PAppend(char const*,A...){}}; +struct Future{bool get(){return true;}};struct Commit{Future m_future;}; +struct DB{bool owned=true,persistent=false,online=false;int retired=0;std::string state="ready"; + templatestd::shared_ptrPQuery(char const*,A...){if(!owned)return nullptr;auto row=std::make_shared();row->f[0].number=6;row->f[1].str=state;row->f[2].number=persistent;row->f[3].number=online;row->f[4].str="warrior";row->f[5].number=42;return row;} + std::shared_ptrQuery(char const*){return nullptr;}auto BeginTransaction(){return std::make_shared();}Commit AsyncCommitTransaction(std::shared_ptr){++retired;return {};} +}CharacterDatabase; +struct ChatHandler{void SendSysMessage(char const*){}templatevoid PSendSysMessage(char const*,A...){} }; +bool schema=true;bool Schema(){return schema;} +namespace GameTime{unsigned GetGameTime(){return 100;}} +enum class ClientlessPlayerState{Offline,Online,Removing}; +constexpr int UNIT_STATE_CASTING=1,POWER_MANA=0; +struct Player{bool combat=false,casting=false;int saves=0;bool IsInWorld(){return true;}bool IsAlive(){return true;}bool IsInCombat(){return combat;}bool IsBeingTeleported(){return false;}bool GetTransport(){return false;}bool IsInFlight(){return false;}bool HasUnitState(int){return casting;}unsigned getClass(){return 1;}void UpdateMaxHealth(){}void SetFullHealth(){}void SetFullPower(int){}void SaveToDB(std::shared_ptr){++saves;}}; +struct Session{Player player;Player*GetPlayer(){return &player;}}; +int equipped=0;bool Equip(Player*,AdventureGeneratorPolicy::Archetype const&,std::mt19937&,std::string&){++equipped;return true;} +struct Behavior{void InterruptForInteraction(Player*){}}behavior;auto sAdventureBehaviorMgr=&behavior; +#define TC_LOG_INFO(...) ((void)0) +struct World{Session session;bool available=false;Session*FindClientlessSession(unsigned){return available?&session:nullptr;}ClientlessPlayerState state=ClientlessPlayerState::Offline;auto GetClientlessPlayerState(unsigned){return state;}}world;auto sWorld=&world; +struct Core{int starts=0,stops=0;bool StartPlayer(unsigned,unsigned,int){++starts;return true;}bool StopPlayer(unsigned){++stops;return true;}}core;auto sClientlessPlayerMgr=&core;int CreateClientlessAdventureLuaController(){return 0;} +struct AdventureGeneratorMgr{ + int creates=0;bool Command(ChatHandler*,char const*); + bool Create(ChatHandler*,uint32,uint32,uint8,unsigned,std::string const&,bool){++creates;return true;} + bool Profile(unsigned,std::string&lua){lua="return {}";return true;} +}; +'''+s[a:e]+r''' +int main(){ + AdventureGeneratorMgr mgr;ChatHandler handler;auto run=[&](char const*args){return mgr.Command(&handler,args);}; + schema=false;assert(!run("create 6 1"));schema=true; + assert(run("create 6 1"));assert(mgr.creates==1);assert(run("create 6 1 "));assert(mgr.creates==2); + assert(!run("create 6 0"));assert(!run("create 6 11"));assert(!run("create 6 1 256 any any temporary 1"));assert(mgr.creates==2); + assert(run("create 6 2 60 horde warrior temporary 42"));assert(mgr.creates==4); + CharacterDatabase.owned=false;assert(!run("retire 26"));assert(!run("logout 26"));assert(!run("login 26"));assert(!run("equip 26"));assert(!core.stops && !core.starts && !CharacterDatabase.retired); + CharacterDatabase.owned=true;CharacterDatabase.persistent=true;assert(!run("retire 30"));CharacterDatabase.persistent=false; + CharacterDatabase.online=true;assert(!run("retire 30"));CharacterDatabase.online=false; + world.state=ClientlessPlayerState::Removing;assert(!run("retire 30"));world.state=ClientlessPlayerState::Offline; + assert(run("retire 30"));assert(CharacterDatabase.retired==1); + CharacterDatabase.state="retired";assert(!run("login 30"));assert(!core.starts);CharacterDatabase.state="ready"; + assert(run("login 30"));assert(core.starts==1);assert(run("logout 30"));assert(core.stops==1); + assert(!run("equip 30"));world.available=true;world.session.player.combat=true;assert(!run("equip 30"));world.session.player.combat=false; + world.session.player.casting=true;assert(!run("equip 30"));world.session.player.casting=false; + assert(run("equip 30"));assert(equipped==1 && world.session.player.saves==1); +} +''' +with tempfile.TemporaryDirectory() as d: + p=Path(d);(p/'test.cpp').write_text(code) + subprocess.run(['g++','-std=c++17','-I',str(root/'src/server/game/World'),str(p/'test.cpp'),'-o',str(p/'test')],check=True) + subprocess.run([str(p/'test')],check=True) +print('Production generator commands: batch bounds, dedicated ownership, persistent/online/removing retirement protection and retired login refusal passed') diff --git a/tests/clientless/test_adventure_generator_policy.py b/tests/clientless/test_adventure_generator_policy.py new file mode 100644 index 0000000000..3e9e562a64 --- /dev/null +++ b/tests/clientless/test_adventure_generator_policy.py @@ -0,0 +1,50 @@ +"""Execute the actual cultural naming/profile generator with real Lua.""" +from pathlib import Path +import subprocess,tempfile +root=Path(__file__).resolve().parents[2] +code=r''' +#include "AdventureGeneratorPolicy.h" +#include +#include +#include +#include +int main(){ + using namespace AdventureGeneratorPolicy; + std::set global; + lua_State* L=luaL_newstate(); + for(unsigned race:{1,2,3,4,5,6,7,8,9,10,11,22})for(unsigned gender:{0,1}){ + std::set cultural; + for(unsigned seed=0;seed<256;++seed){ + std::mt19937 r(seed),again(seed);auto name=Name(race,gender,r);assert(name==Name(race,gender,again)); + assert(name.size()>=3 && name.size()<=12 && std::isupper(name[0]));for(unsigned i=1;i=.25 && value<=.9);lua_pop(L,1);}lua_pop(L,1); + lua_getfield(L,-1,"behavior");lua_getfield(L,-1,"initial_location");assert(std::string(lua_tostring(L,-1))=="local_tavern");lua_settop(L,0); + } + } + assert(cultural.size()>=50); + } + assert(global.size()>900); + std::string raw="A quote \" slash \\ newline\nreturn os.execute('no')";auto escaped=LuaString(raw); + assert(luaL_dostring(L,("return "+escaped).c_str())==LUA_OK);assert(raw==lua_tostring(L,-1));lua_settop(L,0); + auto warrior=Archetypes()[0];assert(Armor(warrior,39)==3 && Armor(warrior,40)==4); + auto hunter=Archetypes()[2];assert(Armor(hunter,39)==2 && Armor(hunter,40)==3); + assert(EligibleItem(60,58,3,85,false,false));assert(!EligibleItem(60,61,3,85,false,false)); + assert(!EligibleItem(60,58,5,85,false,false));assert(!EligibleItem(60,58,3,85,true,false));assert(!EligibleItem(60,58,3,85,false,true)); + assert(!EligibleItem(60,1,3,85,false,false)); + lua_close(L); +} +''' +with tempfile.TemporaryDirectory() as d: + p=Path(d);(p/'test.cpp').write_text(code) + flags=subprocess.check_output(['pkg-config','--cflags','--libs','lua5.4'],text=True).split() + subprocess.run(['g++','-std=c++17','-I',str(root/'src/server/game/World'),str(p/'test.cpp'),'-o',str(p/'test'),*flags],check=True) + subprocess.run([str(p/'test')],check=True) +print('Production generator: 6144 cultural names and 61440 real Lua profiles, profession coherence, personality ranges, escaping and equipment/armor gates passed') diff --git a/tests/clientless/test_adventure_supply_items.py b/tests/clientless/test_adventure_supply_items.py index b63d750684..05a94c3bd5 100644 --- a/tests/clientless/test_adventure_supply_items.py +++ b/tests/clientless/test_adventure_supply_items.py @@ -56,6 +56,7 @@ struct Player{ unsigned level=60;bool combat=false,casting=false;History history;int requests=0;std::unordered_mapskills,caps,steps;std::unordered_setknown; std::unordered_map>inventory; Guid GetGUID(){return {};}unsigned getLevel(){return level;}unsigned getRace(){return 1;}unsigned getClass(){return 1;} + bool IsInWorld(){return true;} bool IsSpellFitByClassAndRace(unsigned){return true;}bool HasSpell(unsigned id){return known.count(id);} unsigned GetPureSkillValue(unsigned skill){return skills[skill];}unsigned GetPureMaxSkillValue(unsigned skill){return caps[skill];}unsigned GetSkillStep(unsigned skill){return steps[skill];} void LearnSpell(unsigned id,bool){known.insert(id);auto rank=spellMgr.GetSpellLearnSkill(id);if(rank){caps[rank->skill]=rank->maximum;steps[rank->skill]=rank->step;} @@ -75,6 +76,7 @@ struct Player{ void Add(ItemTemplate*p,unsigned count=20){inventory[p->id]=std::make_unique(Item{p,count});} }; struct Spell{Player*p;SpellInfo*info;Item*m_CastItem=nullptr;Spell(Player*q,SpellInfo*i,int):p(q),info(i){}bool CanAutoCast(Player*q){assert(q==p && m_CastItem);return info->autoCast;}}; +struct ClientlessCharacterFactory{static bool PrepareSkillRank(Player*,unsigned,unsigned){assert(false);return false;}}; '''+header+'\n'+function(' bool RoleMatches(')+'\n'+function('ItemTemplate const* AdventureSupplyMgr::Select(')+'\n'+function('AdventureSupplyMgr::UseResult AdventureSupplyMgr::TryUse(')+'\n'+function('void AdventureSupplyMgr::LearnSecondaries(')+r''' int main(){ using U=AdventureSupplyPolicy::Use;AdventureSupplyMgr mgr;Player p;auto&r=mgr._records[26]; diff --git a/tests/clientless/test_clientless_character_factory.py b/tests/clientless/test_clientless_character_factory.py new file mode 100644 index 0000000000..d9cba486e1 --- /dev/null +++ b/tests/clientless/test_clientless_character_factory.py @@ -0,0 +1,90 @@ +"""Execute the production native factory with transactional/publishing adapters.""" +from pathlib import Path +import subprocess,tempfile +root=Path(__file__).resolve().parents[2] +source=(root/'src/server/game/World/ClientlessCharacterFactory.cpp').read_text() +source=source[:source.index('bool ClientlessCharacterFactory::PrepareSkillRank(')] +source='\n'.join(line for line in source.splitlines() if not line.startswith('#include')) +header=(root/'src/server/game/World/ClientlessCharacterFactory.h').read_text() +header='\n'.join(line for line in header.splitlines() if not line.startswith('#include')) +code=r''' +#include +#include +#include +#include +#include +#include +#include +using uint8=uint8_t;using uint32=uint32_t; +#define TC_GAME_API +constexpr int SEC_PLAYER=0,GENDER_FEMALE=1,CLASS_DEATH_KNIGHT=6,CONFIG_MAX_PLAYER_LEVEL=0,CONFIG_CHARACTERS_PER_REALM=1,LOCALE_enUS=0,CHAR_NAME_SUCCESS=0,TOTAL_LOCALES=4,POWER_MANA=0,LOGIN_REP_REALM_CHARACTERS=0; +using LocaleConstant=int; +struct ObjectGuid{unsigned id;unsigned GetCounter()const{return id;}};enum class HighGuid{Player}; +struct Field {uint32 value=0;std::string text="BOT";uint8 GetUInt8(){return value;}uint32 GetUInt32(){return value;}std::string GetString(){return text;}}; +struct Row{Field fields[3];Field*Fetch(){return fields;}};using QueryResult=std::shared_ptr; +struct Transaction{int characterRows=0,profileRows=0;};using CharacterDatabaseTransaction=std::shared_ptr; +struct Future{bool success=true;bool get(){return success;}};struct Commit{Future m_future;}; +struct Statement{void setUInt32(int,uint32){}}; +struct Database { + bool account=true,duplicate=false,success=true;uint32 count=1;int commits=0,savedCharacters=0,savedProfiles=0,realmWrites=0;Statement stmt; + templateQueryResult PQuery(char const*sql,A...){ + std::string query(sql);if(query.find("SELECT 1")!=std::string::npos)return duplicate?std::make_shared():nullptr; + if(query.find("COUNT(*)")!=std::string::npos){auto row=std::make_shared();row->fields[0].value=count;return row;} + if(!account)return nullptr;auto row=std::make_shared();row->fields[1].value=3;return row; + } + void EscapeString(std::string&){}CharacterDatabaseTransaction BeginTransaction(){return std::make_shared();} + Commit AsyncCommitTransaction(CharacterDatabaseTransaction tx){++commits;if(success){savedCharacters+=tx->characterRows;savedProfiles+=tx->profileRows;}return {{success}};} + Statement*GetPreparedStatement(int){return &stmt;}void Execute(Statement*){++realmWrites;} +}CharacterDatabase,LoginDatabase; +struct CharacterCreateInfo {protected:std::string Name;uint8 Race=0,Class=0,Gender=0,Skin=0,Face=0,HairStyle=0,HairColor=0,FacialHair=0;}; +struct Motion {void Initialize(){}}; +struct WorldSession {enum class WorldSessionMode{Clientless};templateWorldSession(T...){} }; +struct Player { + static inline bool createSuccess=true;static inline int cleaned=0;unsigned level=1;ObjectGuid guid{0};Motion motion; + explicit Player(WorldSession*){}void CleanupsBeforeDelete(){++cleaned;}static bool ValidateAppearance(uint8,uint8,uint8,uint8,uint8,uint8,uint8,uint8,bool){return true;} + Motion*GetMotionMaster(){return &motion;}bool Create(unsigned id,CharacterCreateInfo*){guid={id};return createSuccess;}void setCinematic(int){}unsigned getLevel(){return level;}void SetLevel(unsigned l){level=l;}void InitStatsForLevel(bool){}void InitTalentForLevel(){}void InitTaxiNodesForLevel(){}void InitGlyphsForLevel(){}void SetCanModifyStats(bool){}void UpdateMaxHealth(){}void UpdateMaxPower(int){}void SetMoney(int){}void LearnDefaultSkills(){}void UpdateSkillsForLevel(){}void SetFullHealth(){}void SetFullPower(int){} + void SaveToDB(CharacterDatabaseTransaction tx,bool creating){assert(creating);++tx->characterRows;}ObjectGuid GetGUID(){return guid;} +}; +struct World {bool connected=false;bool FindSession(uint32){return connected;}unsigned getIntConfig(int c){return c==CONFIG_MAX_PLAYER_LEVEL?85:10;}}world;auto sWorld=&world; +namespace AccountMgr {int security=0;int GetSecurity(uint32,int){return security;}} +struct Realm {struct{int Realm=1;}Id;}realm; +bool normalizePlayerName(std::string&s){return !s.empty();} +struct Info{unsigned Race_related=0,Required_expansion=0;};struct Store {Info info;Info*LookupEntry(unsigned){return &info;}}sChrRacesStore,sChrClassesStore; +struct ObjectMgr { + bool valid=true,reserved=false;unsigned nextGuid=30;Info info; + Info*GetPlayerInfo(unsigned,unsigned){return valid?&info:nullptr;}bool IsReservedName(std::string const&){return reserved;} + static int CheckPlayerName(std::string const&,int,bool){return CHAR_NAME_SUCCESS;} + struct Generator{unsigned Generate(){return 30;}}generator;templateGenerator&GetGenerator(){return generator;} +}objects;auto sObjectMgr=&objects; +struct Cache {bool duplicate=false;int published=0;bool GetCharacterCacheByName(std::string const&){return duplicate;}templatevoid AddCharacterCacheEntry(T...){++published;}}cache;auto sCharacterCache=&cache; +struct ScriptMgr {int creates=0;void OnPlayerCreate(Player*){++creates;}}scripts;auto sScriptMgr=&scripts; +'''+header+'\n'+source+r''' +int main(){ + using Factory=ClientlessCharacterFactory;Factory::Definition d{"Aldren",1,1,0,60,42};int prepared=0,appended=0; + auto prepare=[&](Player*p,std::string&){++prepared;assert(p->getLevel()==60);return true;}; + auto append=[&](CharacterDatabaseTransaction const&tx,Player*){++appended;++tx->profileRows;}; + auto run=[&](){return Factory::Create(6,d,prepare,append);}; + auto ok=run();assert(ok.Guid==30 && ok.Error.empty());assert(cache.published==1 && scripts.creates==1 && appended==1 && prepared==1); + assert(CharacterDatabase.savedCharacters==1 && CharacterDatabase.savedProfiles==1 && Player::cleaned==1 && LoginDatabase.realmWrites==1); + // Preparation failure never invokes save, appends profile, publishes cache or hooks. + auto commits=CharacterDatabase.commits;auto failed=Factory::Create(6,d,[](Player*,std::string&e){e="gear unavailable";return false;},append); + assert(!failed.Guid && failed.Error=="gear unavailable" && CharacterDatabase.commits==commits && appended==1 && cache.published==1); + // DB rollback cannot publish either half of the generated identity. + CharacterDatabase.success=false;auto rollback=run();assert(!rollback.Guid && cache.published==1 && scripts.creates==1); + assert(CharacterDatabase.savedCharacters==1 && CharacterDatabase.savedProfiles==1 && LoginDatabase.realmWrites==1);CharacterDatabase.success=true; + // Ordinary active players, privileged accounts and exhausted accounts are protected. + auto previous=prepared;world.connected=true;assert(!run().Guid);world.connected=false; + AccountMgr::security=3;assert(!run().Guid);AccountMgr::security=0; + CharacterDatabase.count=10;assert(!run().Guid);CharacterDatabase.count=1; + objects.valid=false;assert(!run().Guid);objects.valid=true;objects.reserved=true;assert(!run().Guid);objects.reserved=false; + cache.duplicate=true;assert(!run().Guid);cache.duplicate=false;CharacterDatabase.duplicate=true;assert(!run().Guid);CharacterDatabase.duplicate=false; + LoginDatabase.account=false;assert(!run().Guid);LoginDatabase.account=true; + d.Level=86;assert(!run().Guid);d.Level=54;d.Class=CLASS_DEATH_KNIGHT;assert(!run().Guid);d.Level=60;d.Class=1; + assert(prepared==previous);assert(cache.published==1 && scripts.creates==1); +} +''' +with tempfile.TemporaryDirectory() as d: + p=Path(d);(p/'test.cpp').write_text(code) + subprocess.run(['g++','-std=c++17',str(p/'test.cpp'),'-o',str(p/'test')],check=True) + subprocess.run([str(p/'test')],check=True) +print('Production native factory: character/profile atomicity, failed preparation/commit, cache publication order, account/security/name/cap gates passed') diff --git a/tests/clientless/test_clientless_skill_preparation.py b/tests/clientless/test_clientless_skill_preparation.py new file mode 100644 index 0000000000..24236c03f4 --- /dev/null +++ b/tests/clientless/test_clientless_skill_preparation.py @@ -0,0 +1,40 @@ +"""Execute native tier-based skill preparation before world insertion.""" +from pathlib import Path +import subprocess,tempfile +root=Path(__file__).resolve().parents[2] +s=(root/'src/server/game/World/ClientlessCharacterFactory.cpp').read_text() +a=s.index('bool ClientlessCharacterFactory::PrepareSkillRank(') +code=r''' +#include +#include +using uint32=unsigned;constexpr unsigned MAX_SKILL_STEP=16; +struct RC{unsigned MinLevel=5,SkillTierID=1;}rc; +struct DBC {bool available=true;RC*GetSkillRaceClassInfo(unsigned,unsigned,unsigned){return available?&rc:nullptr;}}sDBCManager; +struct Tier{unsigned Value[16]={75,150,225,300,375,450,525};}tier; +struct Store{bool available=true;Tier*LookupEntry(unsigned){return available?&tier:nullptr;}}sSkillTiersStore; +struct Player{ + bool inWorld=false;unsigned level=60,step=0,value=0,cap=0;int writes=0; + bool IsInWorld(){return inWorld;}unsigned getRace(){return 1;}unsigned getClass(){return 1;}unsigned getLevel(){return level;} + unsigned GetSkillStep(unsigned){return step;}unsigned GetPureSkillValue(unsigned){return value;}unsigned GetPureMaxSkillValue(unsigned){return cap;} + void SetSkill(unsigned,unsigned s,unsigned v,unsigned c){++writes;step=s;value=v;cap=c;} +}; +struct ClientlessCharacterFactory{static bool PrepareSkillRank(Player*,unsigned,unsigned);}; +'''+s[a:]+r''' +int main(){ + Player p;using F=ClientlessCharacterFactory; + assert(F::PrepareSkillRank(&p,186,1));assert(p.step==1 && p.value==75 && p.cap==75); + assert(F::PrepareSkillRank(&p,186,5));assert(p.step==5 && p.value==375 && p.cap==375); + auto previous=p.writes;assert(F::PrepareSkillRank(&p,186,1));assert(p.step==5 && p.writes==previous); + assert(!F::PrepareSkillRank(&p,186,0));assert(!F::PrepareSkillRank(&p,186,17));assert(!F::PrepareSkillRank(&p,186,8)); + p.inWorld=true;assert(!F::PrepareSkillRank(&p,186,6));p.inWorld=false; + p.level=4;assert(!F::PrepareSkillRank(&p,186,6));p.level=60; + sDBCManager.available=false;assert(!F::PrepareSkillRank(&p,186,6));sDBCManager.available=true; + sSkillTiersStore.available=false;assert(!F::PrepareSkillRank(&p,186,6)); + assert(p.writes==previous);assert(!F::PrepareSkillRank(nullptr,186,1)); +} +''' +with tempfile.TemporaryDirectory() as d: + p=Path(d);(p/'test.cpp').write_text(code) + subprocess.run(['g++','-std=c++17',str(p/'test.cpp'),'-o',str(p/'test')],check=True) + subprocess.run([str(p/'test')],check=True) +print('Production offline skill preparation: native tier caps, no downgrading, rank/level/race/tier gates and no modification of online players passed')