From 0173a434f49d7f17367a258713018ceaab16c9e0 Mon Sep 17 00:00:00 2001 From: nico Date: Thu, 29 Oct 2026 01:36:35 +0100 Subject: [PATCH] Improve AdventureBot recruitment, world presence and performance Manage Arthoran as a persistent level-60 tank profile, add Worgen forms and mounts, enable PvP and recruit through native LFG with preparation actions and player invitations. Reduce repeated route and flight-planning work, bound failed-travel retries and add runtime timing diagnostics. Include 86 passing tests and measurements with 110 online bots. --- doc/clientless/SESSION-RESUME.md | 273 ++++++++- .../2026-10-29-performance/baseline.log | 13 + .../2026-10-29-performance/build.log | 28 + .../2026-10-29-performance/cpu.json | 19 + .../2026-10-29-performance/native-status.log | 20 + .../2026-10-29-performance/optimized.log | 560 ++++++++++++++++++ .../route-bottleneck.log | 173 ++++++ .../2026-10-29-performance/tests.log | 189 ++++++ doc/clientless/performance.md | 74 +++ doc/clientless/worgen-recruitment.md | 48 ++ ...6_10_29_01_adventure_group_preparation.sql | 9 + .../game/World/AdventureBehaviorMgr.cpp | 17 +- .../game/World/AdventureBehaviorPolicy.h | 7 + src/server/game/World/AdventureCombatMgr.cpp | 5 +- .../game/World/AdventureGeneratorMgr.cpp | 8 +- src/server/game/World/AdventureGroupMgr.cpp | 50 +- src/server/game/World/AdventureGroupMgr.h | 4 + .../game/World/AdventurePopulationMgr.cpp | 2 +- src/server/game/World/ClientlessFlightMgr.cpp | 26 +- src/server/game/World/ClientlessFlightMgr.h | 8 +- .../game/World/ClientlessGraphProbeCache.h | 41 ++ src/server/game/World/ClientlessLuaEngine.cpp | 47 +- src/server/game/World/ClientlessLuaEngine.h | 5 + src/server/game/World/ClientlessPlayerMgr.cpp | 35 +- .../game/World/ClientlessTravelGraphMgr.cpp | 74 ++- .../game/World/ClientlessTravelGraphMgr.h | 3 + .../adventure/profiles/arthoran.lua | 32 +- .../test_adventure_combat_reactions.py | 9 +- tests/clientless/test_adventure_groups.py | 16 +- .../clientless/test_adventure_intent_state.py | 1 + tests/clientless/test_adventure_leisure.py | 12 +- tests/clientless/test_adventure_native_lfg.py | 33 ++ tests/clientless/test_adventure_population.py | 15 +- .../clientless/test_adventure_retry_policy.py | 24 + .../test_adventure_world_presence.py | 73 +++ tests/clientless/test_flight_area_query.py | 5 +- tests/clientless/test_flight_policy.py | 21 +- tests/clientless/test_free_flight_planner.py | 25 +- tests/clientless/test_graph_probe_cache.py | 28 + 39 files changed, 1958 insertions(+), 74 deletions(-) create mode 100644 doc/clientless/checkpoints/2026-10-29-performance/baseline.log create mode 100644 doc/clientless/checkpoints/2026-10-29-performance/build.log create mode 100644 doc/clientless/checkpoints/2026-10-29-performance/cpu.json create mode 100644 doc/clientless/checkpoints/2026-10-29-performance/native-status.log create mode 100644 doc/clientless/checkpoints/2026-10-29-performance/optimized.log create mode 100644 doc/clientless/checkpoints/2026-10-29-performance/route-bottleneck.log create mode 100644 doc/clientless/checkpoints/2026-10-29-performance/tests.log create mode 100644 doc/clientless/performance.md create mode 100644 doc/clientless/worgen-recruitment.md create mode 100644 sql/custom/world/2026_10_29_01_adventure_group_preparation.sql create mode 100644 src/server/game/World/ClientlessGraphProbeCache.h create mode 100644 tests/clientless/test_adventure_native_lfg.py create mode 100644 tests/clientless/test_adventure_retry_policy.py create mode 100644 tests/clientless/test_adventure_world_presence.py create mode 100644 tests/clientless/test_graph_probe_cache.py diff --git a/doc/clientless/SESSION-RESUME.md b/doc/clientless/SESSION-RESUME.md index 5c3d0080cf..59765ff3a3 100644 --- a/doc/clientless/SESSION-RESUME.md +++ b/doc/clientless/SESSION-RESUME.md @@ -1,3 +1,271 @@ +# Aktuell: Performance-Verbesserungen implementiert und mit110Bots gemessen + +Nutzer beauftragte geringere Last/bessere Mehrkernnutzung im Hinblick auf spätere +Produktion. Branch feature/clientless-player-core, letzter Commit cac4142ec1. +Sämtliche vorherigen Arthoran/Worgen/PvP/nativer-LFG-Änderungen enthalten. +Dieser Stand wird auf ausdrücklichen Nutzerauftrag lokal committet; kein Push. +Doku doc/clientless/performance.md, Nachweise checkpoints/2026-10-29-performance/. + +Aktuelle Installation: letzter erfolgreicher Build /tmp/vcore-performance-retry-build.log, +Install /tmp/vcore-performance-retry-install.log. Testserver PID214924 in tmux +vcore-worldserver, frischer Start /tmp/vcore-performance-retry-start1.log; aktuelle +ready-Meldung, Prozess und TCP8085 LISTEN bestätigt. Keine Produktion berührt. +Aktive Config MapUpdate.Threads=4 (vorher1), Network.Threads=1, Loader weiter +50 persistent+5 temporär pro Fraktion. ALLE110 online, pending0, error leer. +Gruppenfindungstest-Deadline01:00 abgelaufen; kein Setup-Helfer aktiv. Normale +persistente Tagespläne werden nach Neustart verfolgt, Test nicht heimlich verlängert. + +Letzte /proc-Messung10.069s bei110Bots: Hauptthread26.2% eines logischen Kerns, +vier Mapworker38.6/37.6/38.1/38.5%. Vorherige Beobachtung95.1% Hauptthread, +9.9% einzelner Mapworker. Verschiedene laufende Situationen, KEIN deterministischer +Benchmark/keine garantierte Prozentverbesserung. CPU-Datei im Checkpoint. +Letztes30.011s CPC-Fenster110Bots: Actions7.762s, Controllers1.612s. Baseline +16Bots/31.923s: Actions24.381s, Controllers1.032s. Zeiten sind Wandzeiten, +keine CPU-Prozente. Debug-CMake unverändert -g OHNE Optimierung; Produktion +benötigt gesonderten optimierten Build/Lasttest, keine1,000Bot-Freigabe behaupten. + +Hauptursache gemessen: Flugkostenbewertung im Graphen (21,456Calls,292.767ms +von355.109ms warmer Suche). Scoped Flight-Planning-Snapshot reduziert wiederholte +Config-/Profilabfragen auf einmal pro Suche; exakte binäre Gebietsschlüssel +statt float-Textformatierung. Native tatsächliche Start-/Lizenz-/Gebietsregeln +bleiben wirksam. Spätere ähnliche20k-Calls-Suchen34..37ms Flugbewertung, ca100ms +Gesamtsuche. Kalte Terrain-/Loginvorgänge können weiterhin Einzelspitzen erzeugen. + +Weitere Änderungen: ClientlessGraphProbeCache erfolgreiche statische feste MMAP- +Verbindungen (Map/Instanz/Richtung/exakteKoordinaten), max32768, Graph-Reload leert; +max4096 feste Graphnachbarschaften als Würfel-Superset mit allen bisherigen +Radius/Sperrlisten/Wegeprüfungen. Nur innerhalb EINER Suche Startpfade teilen, +auch über Suchtiers; keine globalen Playerpfade/negativen/transienten Proben. +Reisefehlerbackoff2..30s mit Jitter, neuer Intent setzt Retry zurück, Zeitblock- +Deadline unverändert, Kampf und Benachrichtigungen nicht verzögert. Verhindert +beobachtete heiße Schleifen mit>50Fehlzielen innerhalb weniger Minuten. +CPC_PERF alle30s + CPC_ROUTE für Suchanfragen>=50ms bleiben als Diagnose. + +Alle86 Clientless-Tests bestanden, /tmp/vcore-performance-retry-tests.log, +inklusive neuer Cache-/Retry-/Scoped-Planning-/Koordinaten-/Routenregressionen, +16parallel schreibenden Mapworker-Notifications und native Masterpfade. Gameplay- +Abnahme und produktionsnaher optimierter Lasttest bleiben getrennt offen. +Bot/Lua/Player-Manager bleiben Worldthread-eigen; native Mapjobs parallel, einzelne +Map weiterhin seriell. Kein blindes Parallelisieren geteilter Zustände. + +--- + +# Performance-Fortschritt: Cache/Mapworker bereit, Vergleich noch offen + +Interne Baseline-CPC_PERF bestätigt Actions als Engpass:16Bots/31.923s Fenster +24.381s Actions,1.032s Controllers,27ms Perception. Bei20Bots nur10Ticks/31.537s, +27.872s Actions. Bei59Bots12Ticks/32.301s,26.331s Actions. Baseline-Logs +/tmp/vcore-perf-baseline-start1.log und derzeitiges Server.log (vor Neustart sichern). + +Implementiert: ClientlessGraphProbeCache.h, maximal32768 erfolgreiche statische +MMAP-Proben, genaue Floatbits/direktional/Map-/Instanzschlüssel. Keine Playerpfade, +Detour-Pointer, negativen/unvollständigen/transienten Ergebnisse speichern. +In PlanAdaptiveRoute nur zukünftige feste Graphpunkte. Aktuelle Einstiegswege +und alle ausführenden Wegprüfungen bleiben nativ. Reload leert Cache. +Zusätzlich maximal4096 Graph-Nachbarschaften nach Ausgangsknoten/Radius; Würfel- +Superset, ursprüngliche genaue Radius-/Sperrlisten-/Pfadregeln bleiben aktiv. +CPC_ROUTE-Trace für Suchanfragen>=50ms mit Gesamt-/Navzeit/Probezahl/Cachehits. + +Cache-Unit, Produktionsplaner (kalte/warme identische Route, weniger Proben, +Sperrlisten und Cacheleerung), 16-Thread-Damage-Handoff erfolgreich. Social-Handoff +und Gruppenreise zielgerichtet laufen/Ergebnisse prüfen. Baseline-Build cache1 +war erfolgreich; aktueller abschließender cache2-Build enthält zusätzliche +Routenzeiten: /tmp/vcore-performance-cache-build2.log, Session65518, vorher +pgrep ninja vor weiterem Build! Native Baseline-Server204663 vollständig beendet. +Testconfig MapUpdate.Threads=4 gesetzt (vorher1), Netzwerk unverändert1; nur +Testserver, Produktion unverändert. Noch NICHT installiert/neu gestartet. + +Nächster Schritt: Build/Tests fertig prüfen, installieren, frisch starten, +Ready/Port/4Mapworker prüfen. CPC_PERF/CPC_ROUTE und CPU-Deltas nach Cache-Warmup +bei dokumentierter Botzahl vergleichen. Noch KEINE Verbesserung behaupten; +falls Navigation nicht dominant im CPC_ROUTE-Trace, weiteren Engpass messen. +Alle früheren WIP erhalten, kein Commit/Push, keine Subagents. + +--- + +# Aktuell: Performance/Mehrkern-Optimierung in Arbeit + +Nutzer beauftragt Verbesserung der Last/CPU-Verteilung für spätere Produktion. +Alle vorherigen uncommitteten Arthoran/Worgen/PvP/LFG-Änderungen bewahren. +Branch feature/clientless-player-core geprüft. Keine Subagents erlaubt; kein Push. +Baseline vor Änderungen PID201380: Hauptthread95.1% eines logischen Kerns, +Mapworker9.9%,12logische CPUs. MapUpdate.Threads=1,Network.Threads=1. +CMake Debug mit -g OHNE -O; Produktionslast dadurch nicht direkt ableitbar. +perf und gdb attach durch OS-Rechte gesperrt, sudo -n ebenfalls nicht verfügbar. +Keine OS-Rechte geändert. Stattdessen gezielte Laufzeitmessung eingeführt. + +ClientlessPlayerMgr.cpp Update misst Travel/Perception/Transport/Flight/Actions/ +Controllers inklusive Online-Initialisierung, protokolliert CPC_PERF alle30s +mit Fensterdauer, Tickzahl, Botzahl und langsamstem Einzelcontroller. +Build /tmp/vcore-performance-instrument-build.log erfolgreich und installiert. +Alter Server201380 vollständig beendet. Aktueller Baseline-Start läuft unter +tmux vcore-worldserver, /tmp/vcore-perf-baseline-start1.log. Readiness/Port und +erste CPC_PERF-Fenster jetzt prüfen; noch keine Optimierung/Mehrkern-Config geändert. +Loader bleibt50/5/50/5;110 gespeicherte Bots. Frühere Gruppenfindung bis01:00, +Helfer war zuletzt nicht mehr aktiv; Vergleichslast sorgfältig dokumentieren. + +Nächste Schritte: Messfenster auswerten, gezielt teuerste Arbeit verbessern, +sichere Mapworker-Konfiguration prüfen. Bot/Lua/Player-Manager nicht blind parallel +aufrufen! Welt-/Mapthread-Grenzen beibehalten. TravelGraph-Routing enthält sehr +teure zukünftige Punkt-zu-Punkt-MMAP-Brücken und ForwardEntry-Probes; Cache bisher +nur pro einzelner Suchanfrage (validBridgeLengths/rejectedBridges). Falls Messung +Actions bestätigt, positiver geometrischer Cache über Anfragen mit Graph-Reload- +Invalidierung/Instanzschlüssel sinnvoll; keine transienten negativen Ergebnisse +unbegrenzt speichern. Noch NICHT implementiert. Nach Änderungen sinnvoll testen, +bauen/installieren/neustarten und Last mit Botzahl/Fenster/Debug-Einschränkung +vergleichen. Keine ungemessenen Prozentverbesserungen behaupten. + +--- + +# Aktuell: Korrektur auf nativen SucheNachGruppe-Kanal + +Nutzer korrigiert: SucheNachGruppe ist eingebauter automatisch betretenener +Hauptstadtkanal; vorheriger /join-Hinweis und Ersatzkanal waren falsch. +Lokale deDE ChatChannels.dbc bestätigt ID26, NameSucheNachGruppe, Flags262201. +AdventureGroupMgr.Speak jetzt Lookup26 + native Zonenberechtigung und +GetJoinChannel(entry->ID,"",zone), tatsächlichen Beitritt vor Say überprüfen. +Kein benutzerdefinierter reservierter Kanal mehr. Bestehende Limits erhalten. +Aktive Testconfig Channel.RestrictedLfg von1 auf0 gesetzt und reload config +bereits ausgeführt; bisheriger NativeCode wies gruppierte normale Accounts +beim LFG-Beitritt ab (GM/RBAC-Ausnahmen beachten). Keine Änderung allgemeiner +Player-/Client-Joinlogik. Clientanzeige nach Stadtwechsel/Relog noch abnehmen. +Doku worgen-recruitment.md korrigiert, frühere Customkanal-Angaben historisch. + +Neuer echter Speak-Adaptertest test_adventure_native_lfg.py besteht: ID26, +Zonenberechtigung, Joinbestätigung, Cooldown. test_adventure_groups.py ebenfalls +bestanden. Abschließender Build2 /tmp/vcore-native-lfg-build2.log erfolgreich und installiert. +Vorheriger Testworldserver197361 vollständig beendet. Neuer Server PID201380, +/tmp/vcore-native-lfg-start1.log; frische ready-Meldung in aktuellem Server.log, +Port8085 LISTEN, Prozess stabil bestätigt. Config RestrictedLfg=0 bestätigt. +Gruppenfindungs-Helfer mit vorhandener01:00 Deadline wieder gestartet, Log +/tmp/vcore-groupfinding-native-lfg.log; Arthoran26 bereits vorbereitet. +Clientanzeige/echter Nachrichtenempfang noch nicht visuell abgenommen. Kein +Commit/Push; alle vorigen Arthoran/Worgen/PvP-WIP erhalten. Branch geprüft. + +--- + +# Aktuell: Worgen, aktive Gruppenfindung und PvP implementiert + +Nutzer beauftragte Worgengestalten/Reittiere, Vorbereitungs-Mikroaktionen, +Kapital-Suchchat und aktive Spielereinladungen mit vorheriger Zielinfo sowie +permanentes freiwilliges PvP. Schiffspositionen ausdrücklich unverändert lassen. +Vorherige uncommittete Arthoran-Änderungen vollständig erhalten. Kein Commit/Push. +Branch feature/clientless-player-core, letzter Commit cac4142ec1. +Doku doc/clientless/worgen-recruitment.md. + +ClientlessLuaEngine.UpdateWorldPresence pflegt native PvP-Wunsch-/Playerflags, +Worgen nach Intent/Kampf/Instanz und zufälliger ruhiger Ankunft, sichere verzögerte +Wechsel mit5s Retry, Zustand bei Logout gelöscht. PrepareGroundMount verwendet +native Wilde Hatz87840 bei Worgengestalt, Pferd2404/Zauber458 in Menschengestalt; +Dismount entfernt Aura. Neue generierte Worgenprofile Pferde statt Volkspferde; +alte leere Mount-Dictionaries bekommen funktionierenden Lauf-/Pferdefallback. + +Gruppenfindung beendet nicht länger Mikroaktionen vorzeitig; zusätzliche SQL- +Migration2026_10_29_01_adventure_group_preparation.sql bereits in Test-WorldDB +angewendet (SQL-Pfad ignoriert, per git add -N -f zur Nachverfolgung aufgenommen). +Vorbereitung/Suchwege lokal mit18Y Pathbudget, auch innen. GroupMgr announciert +nächsten Intent + tatsächliches Ziel im vorhandenen sucheNachGruppe-Kanal, +max1 Nachricht/15s populationsweit und3..5min pro Werber. Spieler müssen Kanal +beitreten. Nahe passende Menschen werden mit vorherigem Whisper eingeladen; +10min Cooldown pro Mensch populationsweit. Native Gruppe/Einladung/Kapazität, +Fraktion/Sicht/AFK/Kampf/Reiseschutz und bisherige Spielerführung beibehalten. + +Build4 /tmp/vcore-worgen-recruitment-build4.log erfolgreich, installiert; +Testserver nach vollständigem Stopp frisch gestartet. PID197361, tmux +vcore-worldserver, /tmp/vcore-worgen-recruitment-start1.log. Frische ready-Meldung, +Port8085 LISTEN und Prozess stabil bestätigt. Loader aktiv50/5/50/5;110 gespeicherte +Bots vorhanden, Online-Aufbau läuft. Native gespeicherte PvP-Wunschflags bei +Arthoran26/Aelion29/Zanzan30/Lenara31 jeweils1. Worgen31/38 Pferd458+ZweiGestalten68996 +in Spellbook bestätigt; native Script-Zuordnung87840/68996 überprüft. + +Regression /tmp/vcore-worgen-recruitment-tests.log:82/83 erfolgreich, einzig alter +intent_state-Adapter fehlte neuer Lua-Ankunftsaufruf. Adapter ergänzt, Einzeltest +anschließend bestanden. Neues world_presence und erweiterte groups/leisure Tests +anschließend bestanden, einschließlich begrenzter Innenraum-Suchwege, menschlicher +Einladung/Zieltext/Abklingzeit, Gestalt in Kampf/Mischinstanzen und Mount-Aura-Cleanup. +Keine visuelle Abnahme von Chat/Morph/Einladung behaupten; native Kanal-/Micro- +Logs waren in erster Ramp-up-Stichprobe noch nicht aufgetreten (Bots reisen). + +Vorherige Gruppenfindungs-Deadline00:00 war abgelaufen. Für fortgesetzten Test nach +Neustart gemeinsame Deadline auf01:00 Europe/Berlin (1793232000) gesetzt. Helfer +/tmp/vcore-groupfinding-setup.py läuft weiter, Log /tmp/vcore-groupfinding-worgen.log, +Manifest /tmp/vcore-groupfinding-test.json. Neue online Bots automatisch vorbereiten, +bei110 online stoppt Helfer; DB-Transienten werden erneut versucht. Nicht parallel +starten! Früheres Manifest /tmp/vcore-groupfinding-test-first-block.json gesichert. +Native performative Instanzaufenthalte können sichere Übergabe noch verzögern. +Kein Push/Commit beauftragt. Gameplay-Tankverhalten/Schiffe bleiben gesondert offen. + +--- + +# Aktuell: Arthoran in normale persistente Verwaltung übernommen + +Auftrag: Arthoran normaler AdventureBot, Krieger60, Gruppenrolle Tank, +Tavernen/Geselligkeit stärker gewichten als Abenteuer, keinem Kampf ausweichen. +Branch feature/clientless-player-core; Ausgangscommit cac4142ec1. Bereits +vorhandene Änderung dieses Dokuments gehört zum Lasttest und wurde bewahrt. + +arthoran.lua jetzt ProfileVersion2, Level60/Class1, SchutzSpec2/Tank, SoloArms0, +Tagesplan aktiv, Elwynn/Sturmwind Heimat, verstärkte Tavernen-/Sozialgewichte, +Aggression1/DangerTolerance1, behavior.allow_retreat=false. Neue optionale +AllowRetreat-Eigenschaft (Defaulttrue) beendet Flucht bei ausdrücklicher Ablehnung; +Gruppenflucht weiterhin aus. Normale CPC-Infrastruktur unverändert. +Generator Reconcile/ProtectedEquipment/PrepareCombat und Population.Online lassen +vollständige v2-Dateiprofile wie generierte Profile verwalten; Legacy-Profile +bleiben unverändert. Arthoran26 explizit in CharacterDB-Registry übernommen, +persistent/ready/warrior/seed26, eigener Lua-Profiltext ebenfalls gesichert. +Kein neuer Charakter, keine Level-/Klassenumwandlung. Backup des Zustands/Inventars +vorher /tmp/vcore-arthoran-before-adoption.tsv. 26 zählt zur Allianz-Persistentquote. + +PvP-Frage geprüft: kein erzwungenes PvP in Adventure-Lifecycle; zuletzt0/96 +Registry-Bots CHARACTER_FLAG_PVP_DESIRED gesetzt, ebenso Arthoran nicht. Native +Kampf-/Gebietsflagging bleibt. Globale PvP-Einstellung NICHT verändert. + +Build /tmp/vcore-arthoran-managed-build.log erfolgreich und installiert. Betroffene +Tests combat_reactions (zusätzlich ausdrückliches Fluchtverbot), population +(zusätzlich benanntes v2-Profil vs Legacy/unvollständig), combat_preparation, +profile_selection, profession_profile erfolgreich. Bisheriger Server183811 +vollständig beendet; Neustart /tmp/vcore-arthoran-managed-start1.log. +Startbereitschaft bestätigt (frische ready-Meldung, PID191926, Port8085 LISTEN). +Arthoran26 online/persistent/ready, Level60/Class1, SoloArms-Talentbaum746, +103 Zauber,14 gelernte Talentzauber, Zweihandwaffe31134. Tankrolle/SchutzSpec2 +im Profil; tatsächlicher Gruppen-Tankwechsel noch nicht im Spiel abgenommen. +Erste Reconcile-Prüfung scheiterte am alten Mining-Lehrzauber ohne Fertigkeit. +26 nativ ausgeloggt, ausschließlich fehlende Skills186/164 mit375/375 im Offline- +Zustand ergänzt, nativ neu eingeloggt; danach Reconcile ohne neue Fehlermeldung. +Mining nativ auf450 erhöht, Blacksmith375; bestehende Nebenskills erhalten. +Gruppenfindung für26 erneut bis00:00 angefordert; sichere Übergabe aus Social +kann noch laufen. Kein Gameplay-Tanktest behaupten. + +Gruppenfindungs-Setup hatte wegen temporärem DB-TLS-Reset angehalten. Native +DB-Query-Retry ergänzt, nach Neustart Manifest prepared zurückgesetzt und Setup +wieder gestartet: /tmp/vcore-groupfinding-resume.log; ursprüngliche Deadline +00:00 beibehalten. Laufenden Setup-Prozess nicht doppelt starten! Loader aktiv +50/5/50/5; keine anderen Nutzercharaktere übernommen. Kein Commit/Push beauftragt. + +--- + +# Aktiver Test: Gruppenfindung für Lasttestpopulation + +Nutzer fordert alle Bots zunächst in Gruppenfindung, danach Instanz/Raid; +keine C++-Änderung. Vorhandenes natives `clientless adventure prepare` verwendet, +keine Lua-/Profiländerung nötig. Aufruf pro online Bot: prepare + . Allianz poi_guard_22 (Sturmwind), Horde poi_guard_179 +(Orgrimmar), Level>=60 raid, darunter instance. Gemeinsame Deadline nächste +volle Stunde: 2026-10-29 00:00 Europe/Berlin (epoch1793228400), danach1h Folgeintent. +Bestehende Kampf-/Tod-/Reiseübergaben bleiben aktiv. Test überschreibt den +aktiven Ablauf (schedule=0), gespeicherte normale Tagespläne/Profilwerte bleiben +erhalten. Nach Test mit `clientless adventure schedule on` zurückstellen. + +Setup /tmp/vcore-groupfinding-setup.py, Log /tmp/vcore-groupfinding-setup.log, +Manifest /tmp/vcore-groupfinding-test.json. Pollt neue online generierte Bots +alle10s bis mindestens110 online, höchstens30min bzw. vor Deadline. Nicht doppelt +starten oder Manifest überschreiben. Bisher80 native prepare-Erfolgsbestätigungen; Aelion29, Lenara31 und Karggan35 +melden groupformation + next=raid. Zuletzt80 online (35/5/35/5), +Loader noch busy beim weiteren Erzeugen, kein Loaderfehler. Setup läuft weiter; +110er Abschluss noch nicht bestätigt. Finales Manifest/Bestätigungen prüfen. Kein Push/Commit beauftragt. + +--- + # Aktuell: Gemeinsame Gruppenreisen implementiert Auftrag: Spieler- und Botgruppen mit sicheren Formationen, Reittieren, freiem @@ -22,7 +290,10 @@ mit bestehender CMake-Konfiguration erfolgreich; vorheriger Server vollständig beendet. Neuer Start läuft: /tmp/vcore-group-travel-start1.log, PID183811, tmux vcore-worldserver. Startbereitschaft bestätigt: frische ready-Meldung, Prozess stabil, Port8085 LISTEN. Loader24 online,10/2/10/2, pending0, error leer. -Loader muss weiterhin aktiv mit10 persistent+2 temporär je Fraktion bleiben. +Aktueller Lasttestauftrag: Loader aktiv mit50 persistent+5 temporär je Fraktion +(Soll110). Aktive worldserver.conf angepasst und reload config sowie +clientless population reload ausgeführt; Zielwerte50/5/50/5 bestätigt. +Population wird schrittweise aufgefüllt; frühere24er Angaben sind historisch. Native Spielerreisen und visuelle Formation sind noch nicht im Spiel abgenommen. Unbekannte/scriptgesteuerte Portale oder fehlende Serviceaufzeichnungen werden diff --git a/doc/clientless/checkpoints/2026-10-29-performance/baseline.log b/doc/clientless/checkpoints/2026-10-29-performance/baseline.log new file mode 100644 index 0000000000..67c867eecd --- /dev/null +++ b/doc/clientless/checkpoints/2026-10-29-performance/baseline.log @@ -0,0 +1,13 @@ +CPC_PERF windowMs=31708 ticks=879 players=7 travelMs=0.484 perceptionMs=4.428 transportMs=0.144 flightMs=2.252 actionsMs=16067.435 controllersMs=269.654 slowGuid=26 slowMs=121.738 +CPC_PERF windowMs=31923 ticks=313 players=16 travelMs=11.469 perceptionMs=27.059 transportMs=0.140 flightMs=0.126 actionsMs=24381.331 controllersMs=1032.270 slowGuid=42 slowMs=397.391 +CPC_PERF windowMs=31537 ticks=10 players=20 travelMs=17.874 perceptionMs=13.710 transportMs=0.028 flightMs=0.027 actionsMs=27872.181 controllersMs=294.503 slowGuid=44 slowMs=58.861 +CPC_PERF windowMs=30370 ticks=170 players=32 travelMs=29.956 perceptionMs=75.262 transportMs=0.130 flightMs=0.141 actionsMs=20011.291 controllersMs=1036.558 slowGuid=50 slowMs=75.739 +CPC_PERF windowMs=32064 ticks=207 players=43 travelMs=29.812 perceptionMs=120.900 transportMs=0.254 flightMs=0.286 actionsMs=21126.712 controllersMs=1134.949 slowGuid=70 slowMs=44.644 +CPC_PERF windowMs=32397 ticks=22 players=54 travelMs=35.146 perceptionMs=107.803 transportMs=0.055 flightMs=0.056 actionsMs=25415.937 controllersMs=1086.701 slowGuid=75 slowMs=150.957 +CPC_PERF windowMs=32301 ticks=12 players=59 travelMs=25.198 perceptionMs=76.121 transportMs=0.030 flightMs=0.040 actionsMs=26331.046 controllersMs=697.158 slowGuid=40 slowMs=62.984 +CPC_PERF windowMs=31515 ticks=8 players=66 travelMs=31.520 perceptionMs=702.414 transportMs=0.501 flightMs=0.235 actionsMs=17198.952 controllersMs=2651.010 slowGuid=43 slowMs=349.990 +CPC_PERF windowMs=31835 ticks=4 players=68 travelMs=89.253 perceptionMs=265.259 transportMs=0.011 flightMs=0.013 actionsMs=15172.937 controllersMs=1872.802 slowGuid=93 slowMs=267.053 +CPC_PERF windowMs=33394 ticks=7 players=72 travelMs=21.174 perceptionMs=203.776 transportMs=0.423 flightMs=0.019 actionsMs=24499.114 controllersMs=867.084 slowGuid=96 slowMs=73.350 +CPC_PERF windowMs=31643 ticks=9 players=80 travelMs=38.652 perceptionMs=120.948 transportMs=0.135 flightMs=0.383 actionsMs=20782.250 controllersMs=2069.232 slowGuid=100 slowMs=707.549 +CPC_PERF windowMs=34049 ticks=5 players=81 travelMs=24.132 perceptionMs=73.970 transportMs=0.129 flightMs=0.015 actionsMs=26621.404 controllersMs=894.816 slowGuid=106 slowMs=53.148 +CPC_PERF windowMs=34358 ticks=6 players=86 travelMs=24.375 perceptionMs=82.137 transportMs=0.501 flightMs=0.017 actionsMs=24773.949 controllersMs=2714.716 slowGuid=80 slowMs=595.751 diff --git a/doc/clientless/checkpoints/2026-10-29-performance/build.log b/doc/clientless/checkpoints/2026-10-29-performance/build.log new file mode 100644 index 0000000000..e8ebed3950 --- /dev/null +++ b/doc/clientless/checkpoints/2026-10-29-performance/build.log @@ -0,0 +1,28 @@ +[1/16] 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/16] Building CXX object src/server/game/CMakeFiles/game.dir/World/AdventureLocationMgr.cpp.o +[3/16] Building CXX object src/server/game/CMakeFiles/game.dir/World/AdventureEncounterMgr.cpp.o +[4/16] Building CXX object src/server/game/CMakeFiles/game.dir/World/AdventureInteractionMgr.cpp.o +[5/16] Building CXX object src/server/game/CMakeFiles/game.dir/World/AdventureGroupMgr.cpp.o +[6/16] Building CXX object src/server/game/CMakeFiles/game.dir/World/AdventurePopulationMgr.cpp.o +[7/16] Building CXX object src/server/game/CMakeFiles/game.dir/World/AdventureSocialActions.cpp.o +[8/16] Building CXX object src/server/game/CMakeFiles/game.dir/World/AdventureSupplyMgr.cpp.o +[9/16] Building CXX object src/server/game/CMakeFiles/game.dir/World/AdventureCombatMgr.cpp.o +[10/16] Building CXX object src/server/game/CMakeFiles/game.dir/World/AdventureGeneratorMgr.cpp.o +[11/16] Building CXX object src/server/game/CMakeFiles/game.dir/World/AdventureBehaviorMgr.cpp.o +[12/16] Building CXX object src/server/game/CMakeFiles/game.dir/World/ClientlessLuaEngine.cpp.o +[13/16] Building CXX object src/server/scripts/CMakeFiles/scripts.dir/Custom/viano_bot_commands.cpp.o +/home/wow_admin/wow_cata_server/source/TrinityCore/src/server/scripts/Custom/viano_bot_commands.cpp: In function ‘bool VianoBotCommands::HandleClientlessLocationAdd(ChatHandler*, const char*)’: +/home/wow_admin/wow_cata_server/source/TrinityCore/src/server/scripts/Custom/viano_bot_commands.cpp:2352:20: warning: unused variable ‘loaded’ [-Wunused-variable] + 2352 | bool const loaded = sClientlessWorldPointMgr->Reload(); + | ^~~~~~ +/home/wow_admin/wow_cata_server/source/TrinityCore/src/server/scripts/Custom/viano_bot_commands.cpp: In function ‘bool VianoBotCommands::HandleClientlessLocationMove(ChatHandler*, const char*)’: +/home/wow_admin/wow_cata_server/source/TrinityCore/src/server/scripts/Custom/viano_bot_commands.cpp:2389:20: warning: unused variable ‘loaded’ [-Wunused-variable] + 2389 | bool const loaded = sClientlessWorldPointMgr->Reload(); + | ^~~~~~ +/home/wow_admin/wow_cata_server/source/TrinityCore/src/server/scripts/Custom/viano_bot_commands.cpp: In function ‘bool VianoBotCommands::HandleClientlessLocationRemove(ChatHandler*, const char*)’: +/home/wow_admin/wow_cata_server/source/TrinityCore/src/server/scripts/Custom/viano_bot_commands.cpp:2413:20: warning: unused variable ‘loaded’ [-Wunused-variable] + 2413 | bool const loaded = sClientlessWorldPointMgr->Reload(); + | ^~~~~~ +[14/16] Linking CXX static library src/server/game/libgame.a +[15/16] Linking CXX static library src/server/scripts/libscripts.a +[16/16] Linking CXX executable src/server/worldserver/worldserver diff --git a/doc/clientless/checkpoints/2026-10-29-performance/cpu.json b/doc/clientless/checkpoints/2026-10-29-performance/cpu.json new file mode 100644 index 0000000000..69be0b6423 --- /dev/null +++ b/doc/clientless/checkpoints/2026-10-29-performance/cpu.json @@ -0,0 +1,19 @@ +{ + "pid": 214924, + "window_seconds": 10.068513992999215, + "logical_cpus": 12, + "threads_percent_of_one_cpu": { + "214924": 26.2, + "214927": 0.0, + "214928": 0.0, + "214929": 0.0, + "214933": 0.9, + "214936": 0.0, + "215050": 38.6, + "215051": 37.6, + "215052": 38.1, + "215053": 38.5, + "215054": 1.4, + "215055": 0.0 + } +} diff --git a/doc/clientless/checkpoints/2026-10-29-performance/native-status.log b/doc/clientless/checkpoints/2026-10-29-performance/native-status.log new file mode 100644 index 0000000000..d0ecbc9a69 --- /dev/null +++ b/doc/clientless/checkpoints/2026-10-29-performance/native-status.log @@ -0,0 +1,20 @@ +TrinityCore rev. cac4142ec1f9+ 2026-10-28 23:30:28 +0100 (feature/clientless-player-core branch) (Unix, Debug, Static) (worldserver-daemon) +World initialized in 0 minutes 11 seconds +TrinityCore rev. cac4142ec1f9+ 2026-10-28 23:30:28 +0100 (feature/clientless-player-core branch) (Unix, Debug, Static) (worldserver-daemon) ready... +Adventure loader enabled=1 schema=1 busy=0 pools=50/5/50/5 known=110 rotations=0 manager=50 error='' +pool=0 faction=alliance persistent=1 target=50 online=21 pending=0 saved=50 +pool=1 faction=alliance persistent=0 target=5 online=5 pending=0 saved=5 +pool=2 faction=horde persistent=1 target=50 online=20 pending=0 saved=50 +pool=3 faction=horde persistent=0 target=5 online=5 pending=0 saved=5 +TC>Adventure loader enabled=1 schema=1 busy=0 pools=50/5/50/5 known=110 rotations=0 manager=94 error='' +pool=0 faction=alliance persistent=1 target=50 online=42 pending=0 saved=50 +pool=1 faction=alliance persistent=0 target=5 online=5 pending=0 saved=5 +pool=2 faction=horde persistent=1 target=50 online=42 pending=0 saved=50 +pool=3 faction=horde persistent=0 target=5 online=5 pending=0 saved=5 +TC>Adventure loader enabled=1 schema=1 busy=0 pools=50/5/50/5 known=110 rotations=0 manager=110 error='' +pool=0 faction=alliance persistent=1 target=50 online=50 pending=0 saved=50 +pool=1 faction=alliance persistent=0 target=5 online=5 pending=0 saved=5 +pool=2 faction=horde persistent=1 target=50 online=50 pending=0 saved=50 +pool=3 faction=horde persistent=0 target=5 online=5 pending=0 saved=5 +PID214924 remains running, TCP8085 LISTEN. +MapUpdate.Threads=4, Network.Threads=1. diff --git a/doc/clientless/checkpoints/2026-10-29-performance/optimized.log b/doc/clientless/checkpoints/2026-10-29-performance/optimized.log new file mode 100644 index 0000000000..7c8cf091aa --- /dev/null +++ b/doc/clientless/checkpoints/2026-10-29-performance/optimized.log @@ -0,0 +1,560 @@ +CPC_ROUTE target='taxi_456' totalMs=2105.169 navMs=1.184 probes=41 cacheHits=2 cacheSize=34 startMs=0.655 startCalls=2 flightMs=2030.334 flightCalls=21457 +CPC_ROUTE target='taxi_456' totalMs=107.186 navMs=0.374 probes=7 cacheHits=36 cacheSize=34 startMs=0.613 startCalls=2 flightMs=36.226 flightCalls=21456 +CPC_ROUTE target='taxi_456' totalMs=106.400 navMs=0.353 probes=7 cacheHits=35 cacheSize=34 startMs=0.760 startCalls=2 flightMs=35.953 flightCalls=21079 +CPC_ROUTE target='taxi_456' totalMs=110.871 navMs=1.564 probes=27 cacheHits=35 cacheSize=46 startMs=0.587 startCalls=2 flightMs=36.327 flightCalls=22018 +CPC_ROUTE target='taxi_456' totalMs=113.010 navMs=1.690 probes=19 cacheHits=47 cacheSize=67 startMs=0.577 startCalls=2 flightMs=35.992 flightCalls=22582 +CPC_ROUTE target='taxi_456' totalMs=112.903 navMs=1.734 probes=19 cacheHits=47 cacheSize=67 startMs=0.585 startCalls=2 flightMs=35.525 flightCalls=22581 +CPC_ROUTE target='portal_at_2530_target' totalMs=104.526 navMs=0.371 probes=7 cacheHits=37 cacheSize=67 startMs=0.537 startCalls=1 flightMs=36.092 flightCalls=20893 +CPC_ROUTE target='goldshire_center' totalMs=3187.008 navMs=4.050 probes=66 cacheHits=64 cacheSize=94 startMs=55.222 startCalls=7 flightMs=2969.975 flightCalls=52524 +CPC_ROUTE target='taxi_181' totalMs=110.979 navMs=0.493 probes=11 cacheHits=41 cacheSize=98 startMs=0.619 startCalls=2 flightMs=36.173 flightCalls=22329 +CPC_ROUTE target='taxi_181' totalMs=263.941 navMs=7.715 probes=154 cacheHits=121 cacheSize=139 startMs=0.710 startCalls=2 flightMs=65.081 flightCalls=64572 +CPC_ROUTE target='taxi_181' totalMs=262.019 navMs=6.961 probes=113 cacheHits=162 cacheSize=141 startMs=0.602 startCalls=2 flightMs=62.437 flightCalls=64571 +CPC_ROUTE target='taxi_181' totalMs=128.702 navMs=2.860 probes=41 cacheHits=72 cacheSize=141 startMs=0.735 startCalls=2 flightMs=35.746 flightCalls=26996 +CPC_ROUTE target='taxi_181' totalMs=266.560 navMs=6.929 probes=113 cacheHits=162 cacheSize=141 startMs=0.722 startCalls=2 flightMs=63.395 flightCalls=64381 +CPC_ROUTE target='taxi_181' totalMs=267.246 navMs=6.971 probes=114 cacheHits=162 cacheSize=142 startMs=0.722 startCalls=2 flightMs=62.364 flightCalls=64756 +CPC_ROUTE target='portal_at_5379_source' totalMs=964.010 navMs=25.702 probes=2206 cacheHits=444 cacheSize=227 startMs=0.980 startCalls=3 flightMs=295.552 flightCalls=205764 +CPC_ROUTE target='goldshire_center' totalMs=227.882 navMs=2.788 probes=37 cacheHits=90 cacheSize=227 startMs=5.959 startCalls=16 flightMs=63.046 flightCalls=52277 +CPC_ROUTE target='taxi_388' totalMs=106.705 navMs=0.351 probes=7 cacheHits=36 cacheSize=227 startMs=0.588 startCalls=2 flightMs=35.332 flightCalls=21457 +CPC_ROUTE target='taxi_388' totalMs=105.893 navMs=0.360 probes=7 cacheHits=36 cacheSize=227 startMs=0.589 startCalls=2 flightMs=34.586 flightCalls=21456 +CPC_ROUTE target='taxi_388' totalMs=106.071 navMs=0.351 probes=7 cacheHits=35 cacheSize=227 startMs=0.713 startCalls=2 flightMs=34.931 flightCalls=21079 +CPC_ROUTE target='taxi_388' totalMs=111.333 navMs=1.514 probes=15 cacheHits=47 cacheSize=227 startMs=0.699 startCalls=2 flightMs=34.963 flightCalls=22017 +CPC_ROUTE target='taxi_388' totalMs=113.074 navMs=1.713 probes=19 cacheHits=47 cacheSize=227 startMs=0.709 startCalls=2 flightMs=34.564 flightCalls=22581 +CPC_ROUTE target='taxi_395' totalMs=538.346 navMs=10.513 probes=856 cacheHits=251 cacheSize=227 startMs=0.000 startCalls=0 flightMs=248.340 flightCalls=89377 +CPC_PERF windowMs=30296 ticks=2534 players=9 travelMs=2.539 perceptionMs=8.129 transportMs=0.198 flightMs=3.132 actionsMs=9829.145 controllersMs=438.308 slowGuid=26 slowMs=122.949 +CPC_ROUTE target='goldshire_center' totalMs=246.250 navMs=3.272 probes=39 cacheHits=91 cacheSize=227 startMs=2.939 startCalls=7 flightMs=80.862 flightCalls=52524 +CPC_ROUTE target='taxi_128' totalMs=621.379 navMs=12.087 probes=920 cacheHits=454 cacheSize=227 startMs=2.841 startCalls=16 flightMs=271.062 flightCalls=102683 +CPC_ROUTE target='taxi_388' totalMs=103.128 navMs=0.372 probes=7 cacheHits=36 cacheSize=227 startMs=0.743 startCalls=2 flightMs=34.677 flightCalls=20329 +CPC_ROUTE target='taxi_388' totalMs=103.549 navMs=0.352 probes=7 cacheHits=36 cacheSize=227 startMs=0.744 startCalls=2 flightMs=34.605 flightCalls=20328 +CPC_ROUTE target='taxi_388' totalMs=102.574 navMs=0.362 probes=7 cacheHits=35 cacheSize=227 startMs=0.704 startCalls=2 flightMs=34.226 flightCalls=19951 +CPC_ROUTE target='taxi_388' totalMs=103.285 navMs=0.688 probes=8 cacheHits=38 cacheSize=227 startMs=0.713 startCalls=2 flightMs=34.058 flightCalls=19950 +CPC_ROUTE target='taxi_388' totalMs=106.172 navMs=0.977 probes=9 cacheHits=41 cacheSize=227 startMs=0.729 startCalls=2 flightMs=34.820 flightCalls=20325 +CPC_ROUTE target='taxi_388' totalMs=109.921 navMs=1.778 probes=18 cacheHits=47 cacheSize=227 startMs=0.610 startCalls=2 flightMs=34.559 flightCalls=21452 +CPC_ROUTE target='goldshire_center' totalMs=2274.889 navMs=2.838 probes=35 cacheHits=90 cacheSize=227 startMs=184.012 startCalls=16 flightMs=1931.143 flightCalls=52028 +CPC_ROUTE target='taxi_128' totalMs=2140.725 navMs=11.794 probes=920 cacheHits=444 cacheSize=229 startMs=1.435 startCalls=15 flightMs=1798.710 flightCalls=102683 +CPC_ROUTE target='portal_at_4367_target' totalMs=602.221 navMs=12.423 probes=937 cacheHits=264 cacheSize=229 startMs=1.837 startCalls=3 flightMs=256.800 flightCalls=97847 +CPC_ROUTE target='taxi_128' totalMs=623.249 navMs=12.523 probes=920 cacheHits=444 cacheSize=229 startMs=1.065 startCalls=15 flightMs=267.029 flightCalls=102683 +CPC_ROUTE target='taxi_388' totalMs=126.011 navMs=2.660 probes=40 cacheHits=70 cacheSize=229 startMs=0.596 startCalls=2 flightMs=35.603 flightCalls=26999 +CPC_ROUTE target='taxi_388' totalMs=128.763 navMs=3.002 probes=41 cacheHits=73 cacheSize=229 startMs=0.576 startCalls=2 flightMs=36.126 flightCalls=27186 +CPC_ROUTE target='taxi_388' totalMs=118.621 navMs=1.516 probes=26 cacheHits=63 cacheSize=229 startMs=0.732 startCalls=2 flightMs=36.762 flightCalls=23837 +CPC_ROUTE target='taxi_388' totalMs=127.143 navMs=2.823 probes=41 cacheHits=72 cacheSize=229 startMs=0.727 startCalls=2 flightMs=35.044 flightCalls=26808 +CPC_ROUTE target='taxi_388' totalMs=131.725 navMs=3.246 probes=45 cacheHits=73 cacheSize=229 startMs=0.707 startCalls=2 flightMs=35.744 flightCalls=27748 +CPC_ROUTE target='taxi_388' totalMs=118.326 navMs=1.764 probes=21 cacheHits=58 cacheSize=229 startMs=0.727 startCalls=2 flightMs=34.913 flightCalls=24018 +CPC_PERF windowMs=30023 ticks=709 players=18 travelMs=17.383 perceptionMs=19.454 transportMs=0.106 flightMs=0.134 actionsMs=8078.631 controllersMs=871.467 slowGuid=42 slowMs=357.761 +CPC_ROUTE target='goldshire_center' totalMs=240.435 navMs=3.141 probes=39 cacheHits=91 cacheSize=229 startMs=2.640 startCalls=7 flightMs=78.815 flightCalls=52524 +CPC_ROUTE target='goldshire_center' totalMs=226.239 navMs=2.762 probes=36 cacheHits=91 cacheSize=229 startMs=2.272 startCalls=6 flightMs=64.598 flightCalls=52275 +CPC_ROUTE target='goldshire_center' totalMs=226.588 navMs=2.109 probes=34 cacheHits=90 cacheSize=229 startMs=1.972 startCalls=5 flightMs=65.070 flightCalls=52026 +CPC_ROUTE target='goldshire_center' totalMs=228.640 navMs=2.101 probes=28 cacheHits=90 cacheSize=229 startMs=1.735 startCalls=4 flightMs=67.339 flightCalls=51775 +CPC_ROUTE target='goldshire_center' totalMs=232.947 navMs=1.625 probes=23 cacheHits=89 cacheSize=229 startMs=5.000 startCalls=12 flightMs=65.604 flightCalls=51088 +CPC_ROUTE target='portal_at_4367_target' totalMs=593.838 navMs=11.612 probes=937 cacheHits=264 cacheSize=229 startMs=0.985 startCalls=2 flightMs=269.939 flightCalls=97846 +CPC_ROUTE target='taxi_36' totalMs=240.035 navMs=2.449 probes=52 cacheHits=130 cacheSize=231 startMs=2.905 startCalls=20 flightMs=67.022 flightCalls=56337 +CPC_ROUTE target='portal_at_4367_target' totalMs=605.861 navMs=11.408 probes=937 cacheHits=264 cacheSize=231 startMs=37.511 startCalls=80 flightMs=237.635 flightCalls=97846 +CPC_ROUTE target='taxi_36' totalMs=246.040 navMs=2.470 probes=50 cacheHits=135 cacheSize=231 startMs=1.912 startCalls=20 flightMs=71.316 flightCalls=56834 +CPC_ROUTE target='taxi_36' totalMs=247.221 navMs=2.455 probes=50 cacheHits=138 cacheSize=231 startMs=2.084 startCalls=20 flightMs=68.534 flightCalls=58327 +CPC_ROUTE target='taxi_128' totalMs=633.342 navMs=12.916 probes=920 cacheHits=360 cacheSize=231 startMs=2.727 startCalls=18 flightMs=278.827 flightCalls=102683 +CPC_ROUTE target='portal_at_2530_target' totalMs=59.325 navMs=3.073 probes=82 cacheHits=354 cacheSize=231 startMs=3.873 startCalls=17 flightMs=0.000 flightCalls=0 +CPC_ROUTE target='taxi_36' totalMs=268.470 navMs=2.914 probes=49 cacheHits=132 cacheSize=231 startMs=7.712 startCalls=20 flightMs=85.262 flightCalls=56585 +CPC_ROUTE target='portal_at_2530_target' totalMs=51.727 navMs=3.528 probes=87 cacheHits=262 cacheSize=231 startMs=1.734 startCalls=3 flightMs=0.000 flightCalls=0 +CPC_ROUTE target='taxi_36' totalMs=253.735 navMs=2.874 probes=49 cacheHits=132 cacheSize=231 startMs=7.561 startCalls=20 flightMs=69.640 flightCalls=56584 +CPC_ROUTE target='taxi_36' totalMs=62.127 navMs=1.555 probes=23 cacheHits=94 cacheSize=231 startMs=35.083 startCalls=80 flightMs=0.000 flightCalls=0 +CPC_ROUTE target='taxi_36' totalMs=62.383 navMs=1.486 probes=23 cacheHits=94 cacheSize=231 startMs=34.498 startCalls=79 flightMs=0.000 flightCalls=0 +CPC_ROUTE target='taxi_36' totalMs=62.144 navMs=1.550 probes=23 cacheHits=94 cacheSize=231 startMs=34.727 startCalls=78 flightMs=0.000 flightCalls=0 +CPC_ROUTE target='taxi_36' totalMs=61.085 navMs=1.519 probes=23 cacheHits=94 cacheSize=231 startMs=34.272 startCalls=77 flightMs=0.000 flightCalls=0 +CPC_ROUTE target='portal_at_4367_target' totalMs=611.292 navMs=12.282 probes=938 cacheHits=265 cacheSize=231 startMs=2.841 startCalls=4 flightMs=278.524 flightCalls=98342 +CPC_ROUTE target='taxi_388' totalMs=138.736 navMs=3.234 probes=65 cacheHits=95 cacheSize=231 startMs=0.618 startCalls=2 flightMs=37.301 flightCalls=29804 +CPC_ROUTE target='taxi_388' totalMs=259.508 navMs=6.922 probes=113 cacheHits=162 cacheSize=231 startMs=0.590 startCalls=2 flightMs=62.689 flightCalls=64384 +CPC_ROUTE target='taxi_388' totalMs=263.249 navMs=7.041 probes=113 cacheHits=162 cacheSize=231 startMs=0.716 startCalls=2 flightMs=63.342 flightCalls=64383 +CPC_ROUTE target='taxi_388' totalMs=265.017 navMs=6.924 probes=113 cacheHits=162 cacheSize=231 startMs=0.593 startCalls=2 flightMs=64.416 flightCalls=64382 +CPC_ROUTE target='taxi_388' totalMs=266.288 navMs=6.941 probes=113 cacheHits=163 cacheSize=231 startMs=0.610 startCalls=2 flightMs=63.447 flightCalls=64757 +CPC_PERF windowMs=30118 ticks=1418 players=29 travelMs=24.794 perceptionMs=62.862 transportMs=0.237 flightMs=0.344 actionsMs=6960.956 controllersMs=762.444 slowGuid=50 slowMs=73.912 +CPC_ROUTE target='taxi_388' totalMs=145.412 navMs=3.532 probes=69 cacheHits=97 cacheSize=231 startMs=0.731 startCalls=2 flightMs=37.331 flightCalls=30929 +CPC_ROUTE target='taxi_170' totalMs=615.412 navMs=12.344 probes=1023 cacheHits=264 cacheSize=231 startMs=0.000 startCalls=0 flightMs=267.805 flightCalls=100414 +CPC_ROUTE target='taxi_170' totalMs=591.513 navMs=12.174 probes=1023 cacheHits=264 cacheSize=231 startMs=0.000 startCalls=0 flightMs=256.635 flightCalls=100413 +CPC_ROUTE target='portal_at_2530_target' totalMs=53.104 navMs=3.413 probes=87 cacheHits=262 cacheSize=231 startMs=2.684 startCalls=4 flightMs=0.000 flightCalls=0 +CPC_ROUTE target='taxi_170' totalMs=603.082 navMs=12.224 probes=1023 cacheHits=264 cacheSize=231 startMs=0.000 startCalls=0 flightMs=265.445 flightCalls=100412 +CPC_ROUTE target='taxi_395' totalMs=63.738 navMs=1.508 probes=41 cacheHits=131 cacheSize=231 startMs=35.349 startCalls=81 flightMs=0.000 flightCalls=0 +CPC_ROUTE target='taxi_395' totalMs=62.178 navMs=1.455 probes=41 cacheHits=131 cacheSize=231 startMs=34.409 startCalls=80 flightMs=0.000 flightCalls=0 +CPC_ROUTE target='taxi_395' totalMs=61.587 navMs=1.387 probes=41 cacheHits=131 cacheSize=231 startMs=34.215 startCalls=79 flightMs=0.000 flightCalls=0 +CPC_ROUTE target='ironforge_gate' totalMs=64.652 navMs=1.557 probes=23 cacheHits=108 cacheSize=231 startMs=35.797 startCalls=80 flightMs=0.000 flightCalls=0 +CPC_ROUTE target='ironforge_gate' totalMs=61.805 navMs=1.442 probes=23 cacheHits=108 cacheSize=231 startMs=35.385 startCalls=79 flightMs=0.000 flightCalls=0 +CPC_ROUTE target='ironforge_gate' totalMs=61.519 navMs=1.469 probes=23 cacheHits=108 cacheSize=231 startMs=35.011 startCalls=78 flightMs=0.000 flightCalls=0 +CPC_ROUTE target='ironforge_gate' totalMs=61.521 navMs=1.406 probes=23 cacheHits=108 cacheSize=231 startMs=34.715 startCalls=77 flightMs=0.000 flightCalls=0 +CPC_ROUTE target='ironforge_gate' totalMs=63.872 navMs=1.610 probes=23 cacheHits=108 cacheSize=231 startMs=36.603 startCalls=81 flightMs=0.000 flightCalls=0 +CPC_ROUTE target='ironforge_gate' totalMs=62.326 navMs=1.533 probes=23 cacheHits=108 cacheSize=231 startMs=35.314 startCalls=80 flightMs=0.000 flightCalls=0 +CPC_ROUTE target='ironforge_gate' totalMs=62.435 navMs=1.384 probes=23 cacheHits=108 cacheSize=231 startMs=35.265 startCalls=79 flightMs=0.000 flightCalls=0 +CPC_ROUTE target='taxi_128' totalMs=511.222 navMs=10.570 probes=856 cacheHits=324 cacheSize=248 startMs=2.238 startCalls=16 flightMs=237.005 flightCalls=91970 +CPC_ROUTE target='taxi_310' totalMs=714.972 navMs=35.584 probes=2002 cacheHits=740 cacheSize=326 startMs=2.184 startCalls=14 flightMs=22.116 flightCalls=219968 +CPC_ROUTE target='taxi_601' totalMs=160.111 navMs=1.623 probes=18 cacheHits=95 cacheSize=326 startMs=2.760 startCalls=13 flightMs=9.161 flightCalls=54638 +CPC_ROUTE target='portal_at_322_target' totalMs=581.553 navMs=1.044 probes=36 cacheHits=136 cacheSize=326 startMs=550.483 startCalls=81 flightMs=0.000 flightCalls=0 +CPC_ROUTE target='portal_at_322_target' totalMs=68.901 navMs=0.949 probes=36 cacheHits=136 cacheSize=326 startMs=39.146 startCalls=80 flightMs=0.000 flightCalls=0 +CPC_ROUTE target='portal_at_322_target' totalMs=68.291 navMs=1.078 probes=36 cacheHits=136 cacheSize=326 startMs=38.211 startCalls=79 flightMs=0.000 flightCalls=0 +CPC_ROUTE target='ironforge_gate' totalMs=66.096 navMs=1.073 probes=18 cacheHits=102 cacheSize=326 startMs=37.569 startCalls=80 flightMs=0.000 flightCalls=0 +CPC_ROUTE target='ironforge_gate' totalMs=65.862 navMs=0.810 probes=18 cacheHits=102 cacheSize=326 startMs=37.875 startCalls=79 flightMs=0.000 flightCalls=0 +CPC_ROUTE target='ironforge_gate' totalMs=63.775 navMs=0.929 probes=18 cacheHits=102 cacheSize=326 startMs=36.774 startCalls=78 flightMs=0.000 flightCalls=0 +CPC_ROUTE target='ironforge_gate' totalMs=65.413 navMs=0.885 probes=18 cacheHits=102 cacheSize=326 startMs=38.072 startCalls=77 flightMs=0.000 flightCalls=0 +CPC_ROUTE target='taxi_128' totalMs=1808.266 navMs=10.286 probes=839 cacheHits=341 cacheSize=326 startMs=2.632 startCalls=16 flightMs=1519.821 flightCalls=91970 +CPC_ROUTE target='taxi_23' totalMs=55.219 navMs=3.549 probes=77 cacheHits=272 cacheSize=326 startMs=1.996 startCalls=3 flightMs=0.000 flightCalls=0 +CPC_ROUTE target='portal_at_4151_source' totalMs=76.275 navMs=1.728 probes=36 cacheHits=136 cacheSize=326 startMs=39.016 startCalls=80 flightMs=0.000 flightCalls=0 +CPC_ROUTE target='portal_at_4151_source' totalMs=69.649 navMs=1.759 probes=36 cacheHits=136 cacheSize=326 startMs=38.589 startCalls=79 flightMs=0.000 flightCalls=0 +CPC_ROUTE target='portal_at_4151_source' totalMs=65.728 navMs=1.711 probes=36 cacheHits=136 cacheSize=326 startMs=36.765 startCalls=78 flightMs=0.000 flightCalls=0 +CPC_ROUTE target='portal_at_4151_source' totalMs=63.469 navMs=1.531 probes=36 cacheHits=136 cacheSize=326 startMs=34.879 startCalls=77 flightMs=0.000 flightCalls=0 +CPC_ROUTE target='taxi_627' totalMs=67.697 navMs=1.632 probes=18 cacheHits=97 cacheSize=326 startMs=39.189 startCalls=78 flightMs=0.000 flightCalls=0 +CPC_ROUTE target='portal_at_503_target' totalMs=1750.862 navMs=1.717 probes=22 cacheHits=113 cacheSize=326 startMs=0.000 startCalls=0 flightMs=1597.391 flightCalls=56075 +CPC_PERF windowMs=30001 ticks=4260 players=44 travelMs=42.846 perceptionMs=131.690 transportMs=0.353 flightMs=3.459 actionsMs=10288.297 controllersMs=1106.764 slowGuid=53 slowMs=47.618 +CPC_ROUTE target='taxi_23' totalMs=57.022 navMs=3.378 probes=72 cacheHits=272 cacheSize=326 startMs=1.886 startCalls=4 flightMs=0.000 flightCalls=0 +CPC_ROUTE target='taxi_181' totalMs=84.171 navMs=2.019 probes=23 cacheHits=68 cacheSize=326 startMs=0.627 startCalls=2 flightMs=6.628 flightCalls=24975 +CPC_ROUTE target='taxi_181' totalMs=84.098 navMs=2.086 probes=23 cacheHits=68 cacheSize=326 startMs=0.603 startCalls=2 flightMs=6.949 flightCalls=24974 +CPC_ROUTE target='taxi_181' totalMs=84.063 navMs=2.239 probes=28 cacheHits=70 cacheSize=326 startMs=0.822 startCalls=2 flightMs=6.190 flightCalls=24973 +CPC_ROUTE target='taxi_181' totalMs=76.840 navMs=1.423 probes=11 cacheHits=55 cacheSize=326 startMs=0.606 startCalls=2 flightMs=6.739 flightCalls=22642 +CPC_ROUTE target='taxi_181' totalMs=88.911 navMs=2.885 probes=32 cacheHits=70 cacheSize=326 startMs=0.656 startCalls=2 flightMs=6.906 flightCalls=25536 +CPC_ROUTE target='taxi_181' totalMs=83.413 navMs=1.980 probes=17 cacheHits=62 cacheSize=326 startMs=0.754 startCalls=2 flightMs=6.832 flightCalls=24394 +CPC_ROUTE target='taxi_457' totalMs=162.984 navMs=1.599 probes=18 cacheHits=135 cacheSize=326 startMs=2.242 startCalls=14 flightMs=7.647 flightCalls=56805 +CPC_ROUTE target='portal_at_324_source' totalMs=62.977 navMs=1.531 probes=36 cacheHits=136 cacheSize=326 startMs=35.213 startCalls=80 flightMs=0.000 flightCalls=0 +CPC_ROUTE target='portal_at_324_source' totalMs=61.999 navMs=1.451 probes=36 cacheHits=136 cacheSize=326 startMs=34.720 startCalls=79 flightMs=0.000 flightCalls=0 +CPC_ROUTE target='portal_at_324_source' totalMs=61.927 navMs=1.440 probes=36 cacheHits=136 cacheSize=326 startMs=34.442 startCalls=78 flightMs=0.000 flightCalls=0 +CPC_ROUTE target='portal_at_324_source' totalMs=61.652 navMs=1.309 probes=36 cacheHits=136 cacheSize=326 startMs=34.297 startCalls=77 flightMs=0.000 flightCalls=0 +CPC_ROUTE target='ironforge_gate' totalMs=60.957 navMs=1.350 probes=18 cacheHits=102 cacheSize=326 startMs=35.193 startCalls=80 flightMs=0.000 flightCalls=0 +CPC_ROUTE target='ironforge_gate' totalMs=60.204 navMs=1.366 probes=18 cacheHits=102 cacheSize=326 startMs=34.712 startCalls=79 flightMs=0.000 flightCalls=0 +CPC_ROUTE target='ironforge_gate' totalMs=59.730 navMs=1.328 probes=18 cacheHits=102 cacheSize=326 startMs=34.265 startCalls=78 flightMs=0.000 flightCalls=0 +CPC_ROUTE target='ironforge_gate' totalMs=59.389 navMs=1.284 probes=18 cacheHits=102 cacheSize=326 startMs=33.867 startCalls=77 flightMs=0.000 flightCalls=0 +CPC_ROUTE target='portal_at_2530_target' totalMs=59.603 navMs=3.131 probes=72 cacheHits=364 cacheSize=326 startMs=4.767 startCalls=17 flightMs=0.000 flightCalls=0 +CPC_ROUTE target='portal_at_4367_target' totalMs=302.005 navMs=11.610 probes=909 cacheHits=283 cacheSize=326 startMs=2.750 startCalls=5 flightMs=13.884 flightCalls=97240 +CPC_ROUTE target='taxi_23' totalMs=51.635 navMs=3.335 probes=77 cacheHits=272 cacheSize=326 startMs=2.619 startCalls=4 flightMs=0.000 flightCalls=0 +CPC_ROUTE target='portal_at_4367_target' totalMs=299.534 navMs=11.445 probes=909 cacheHits=283 cacheSize=326 startMs=2.976 startCalls=5 flightMs=13.134 flightCalls=97240 +CPC_ROUTE target='portal_at_523_source' totalMs=63.511 navMs=1.561 probes=36 cacheHits=136 cacheSize=326 startMs=35.406 startCalls=80 flightMs=0.000 flightCalls=0 +CPC_ROUTE target='portal_at_523_source' totalMs=61.961 navMs=1.483 probes=36 cacheHits=136 cacheSize=326 startMs=34.653 startCalls=79 flightMs=0.000 flightCalls=0 +CPC_ROUTE target='portal_at_523_source' totalMs=63.486 navMs=1.551 probes=36 cacheHits=136 cacheSize=326 startMs=35.175 startCalls=78 flightMs=0.000 flightCalls=0 +CPC_ROUTE target='portal_at_523_source' totalMs=61.548 navMs=1.528 probes=36 cacheHits=136 cacheSize=326 startMs=34.065 startCalls=77 flightMs=0.000 flightCalls=0 +CPC_ROUTE target='taxi_601' totalMs=158.980 navMs=1.612 probes=18 cacheHits=95 cacheSize=326 startMs=2.677 startCalls=13 flightMs=10.054 flightCalls=54638 +CPC_ROUTE target='taxi_457' totalMs=59.900 navMs=2.976 probes=72 cacheHits=412 cacheSize=326 startMs=4.139 startCalls=13 flightMs=0.000 flightCalls=0 +CPC_ROUTE target='taxi_569' totalMs=142.079 navMs=1.643 probes=18 cacheHits=97 cacheSize=326 startMs=0.000 startCalls=0 flightMs=6.609 flightCalls=49785 +CPC_PERF windowMs=30005 ticks=3897 players=57 travelMs=48.734 perceptionMs=188.265 transportMs=2.207 flightMs=1.015 actionsMs=3331.908 controllersMs=1349.896 slowGuid=75 slowMs=75.810 +CPC_ROUTE target='taxi_388' totalMs=82.773 navMs=2.863 probes=37 cacheHits=77 cacheSize=326 startMs=0.601 startCalls=2 flightMs=3.579 flightCalls=27187 +CPC_ROUTE target='taxi_388' totalMs=83.809 navMs=2.873 probes=37 cacheHits=77 cacheSize=326 startMs=0.743 startCalls=2 flightMs=4.016 flightCalls=27186 +CPC_ROUTE target='taxi_388' totalMs=82.816 navMs=2.907 probes=37 cacheHits=76 cacheSize=326 startMs=0.605 startCalls=2 flightMs=3.216 flightCalls=26809 +CPC_ROUTE target='taxi_388' totalMs=83.805 navMs=2.995 probes=37 cacheHits=76 cacheSize=326 startMs=0.587 startCalls=2 flightMs=3.532 flightCalls=26808 +CPC_ROUTE target='taxi_388' totalMs=87.505 navMs=3.185 probes=41 cacheHits=77 cacheSize=326 startMs=0.723 startCalls=2 flightMs=3.626 flightCalls=27748 +CPC_ROUTE target='taxi_388' totalMs=75.330 navMs=1.810 probes=17 cacheHits=62 cacheSize=326 startMs=0.745 startCalls=2 flightMs=3.157 flightCalls=24206 +CPC_ROUTE target='ironforge_gate' totalMs=61.640 navMs=1.562 probes=18 cacheHits=102 cacheSize=326 startMs=35.584 startCalls=81 flightMs=0.000 flightCalls=0 +CPC_ROUTE target='ironforge_gate' totalMs=61.067 navMs=1.467 probes=18 cacheHits=102 cacheSize=326 startMs=35.051 startCalls=80 flightMs=0.000 flightCalls=0 +CPC_ROUTE target='ironforge_gate' totalMs=60.773 navMs=1.433 probes=18 cacheHits=102 cacheSize=326 startMs=34.794 startCalls=79 flightMs=0.000 flightCalls=0 +CPC_ROUTE target='portal_at_2166_target' totalMs=61.458 navMs=1.643 probes=18 cacheHits=97 cacheSize=326 startMs=35.247 startCalls=80 flightMs=0.000 flightCalls=0 +CPC_ROUTE target='portal_at_2166_target' totalMs=61.203 navMs=1.506 probes=18 cacheHits=97 cacheSize=326 startMs=35.105 startCalls=79 flightMs=0.000 flightCalls=0 +CPC_ROUTE target='portal_at_2166_target' totalMs=60.349 navMs=1.546 probes=18 cacheHits=97 cacheSize=326 startMs=34.630 startCalls=78 flightMs=0.000 flightCalls=0 +CPC_ROUTE target='portal_at_2166_target' totalMs=59.658 navMs=1.442 probes=18 cacheHits=97 cacheSize=326 startMs=34.020 startCalls=77 flightMs=0.000 flightCalls=0 +CPC_ROUTE target='taxi_23' totalMs=51.803 navMs=3.138 probes=72 cacheHits=272 cacheSize=326 startMs=1.717 startCalls=4 flightMs=0.000 flightCalls=0 +CPC_ROUTE target='portal_at_2530_target' totalMs=59.814 navMs=3.052 probes=72 cacheHits=364 cacheSize=326 startMs=5.270 startCalls=17 flightMs=0.000 flightCalls=0 +CPC_ROUTE target='taxi_457' totalMs=64.249 navMs=1.562 probes=36 cacheHits=136 cacheSize=326 startMs=36.392 startCalls=79 flightMs=0.000 flightCalls=0 +CPC_ROUTE target='taxi_128' totalMs=457.065 navMs=9.277 probes=839 cacheHits=341 cacheSize=326 startMs=2.999 startCalls=18 flightMs=185.216 flightCalls=91970 +CPC_ROUTE target='portal_at_4520_target' totalMs=252.372 navMs=7.596 probes=770 cacheHits=205 cacheSize=326 startMs=0.977 startCalls=2 flightMs=27.641 flightCalls=75718 +CPC_ROUTE target='portal_at_4367_target' totalMs=301.114 navMs=11.537 probes=909 cacheHits=283 cacheSize=326 startMs=0.991 startCalls=2 flightMs=12.511 flightCalls=97239 +CPC_ROUTE target='portal_at_4367_target' totalMs=345.522 navMs=11.622 probes=909 cacheHits=283 cacheSize=326 startMs=38.337 startCalls=80 flightMs=12.538 flightCalls=97239 +CPC_ROUTE target='portal_at_1466_source' totalMs=62.813 navMs=1.509 probes=36 cacheHits=136 cacheSize=326 startMs=35.237 startCalls=80 flightMs=0.000 flightCalls=0 +CPC_ROUTE target='portal_at_1466_source' totalMs=61.793 navMs=1.480 probes=36 cacheHits=136 cacheSize=326 startMs=34.599 startCalls=79 flightMs=0.000 flightCalls=0 +CPC_ROUTE target='portal_at_1466_source' totalMs=62.160 navMs=1.547 probes=36 cacheHits=136 cacheSize=326 startMs=34.721 startCalls=78 flightMs=0.000 flightCalls=0 +CPC_ROUTE target='portal_at_1466_source' totalMs=61.649 navMs=1.476 probes=36 cacheHits=136 cacheSize=326 startMs=34.135 startCalls=77 flightMs=0.000 flightCalls=0 +CPC_ROUTE target='taxi_601' totalMs=160.181 navMs=1.569 probes=18 cacheHits=95 cacheSize=326 startMs=3.227 startCalls=13 flightMs=10.878 flightCalls=54638 +CPC_ROUTE target='portal_at_2530_target' totalMs=60.817 navMs=0.375 probes=3 cacheHits=41 cacheSize=326 startMs=0.948 startCalls=1 flightMs=2.596 flightCalls=20893 +CPC_ROUTE target='taxi_82' totalMs=57.350 navMs=3.033 probes=72 cacheHits=364 cacheSize=326 startMs=3.568 startCalls=17 flightMs=0.000 flightCalls=0 +CPC_ROUTE target='portal_at_4320_source' totalMs=199.564 navMs=6.240 probes=93 cacheHits=210 cacheSize=326 startMs=0.905 startCalls=1 flightMs=14.995 flightCalls=64180 +CPC_ROUTE target='taxi_128' totalMs=283.110 navMs=9.254 probes=839 cacheHits=397 cacheSize=326 startMs=3.008 startCalls=18 flightMs=11.083 flightCalls=91970 +CPC_ROUTE target='portal_at_2530_target' totalMs=59.184 navMs=3.016 probes=72 cacheHits=364 cacheSize=326 startMs=5.067 startCalls=17 flightMs=0.000 flightCalls=0 +CPC_ROUTE target='taxi_17' totalMs=1732.652 navMs=8.839 probes=836 cacheHits=292 cacheSize=326 startMs=1.959 startCalls=17 flightMs=1466.805 flightCalls=90943 +CPC_ROUTE target='portal_at_4404_source' totalMs=1413.896 navMs=15.480 probes=951 cacheHits=381 cacheSize=326 startMs=2.136 startCalls=14 flightMs=1088.904 flightCalls=107177 +CPC_ROUTE target='taxi_23' totalMs=51.567 navMs=3.376 probes=77 cacheHits=272 cacheSize=326 startMs=2.627 startCalls=4 flightMs=0.000 flightCalls=0 +CPC_ROUTE target='ironforge_gate' totalMs=62.072 navMs=1.345 probes=18 cacheHits=113 cacheSize=326 startMs=35.942 startCalls=81 flightMs=0.000 flightCalls=0 +CPC_ROUTE target='ironforge_gate' totalMs=61.702 navMs=1.418 probes=18 cacheHits=113 cacheSize=326 startMs=35.189 startCalls=80 flightMs=0.000 flightCalls=0 +CPC_ROUTE target='ironforge_gate' totalMs=60.693 navMs=1.346 probes=18 cacheHits=113 cacheSize=326 startMs=34.550 startCalls=79 flightMs=0.000 flightCalls=0 +CPC_ROUTE target='taxi_82' totalMs=189.461 navMs=7.137 probes=115 cacheHits=167 cacheSize=326 startMs=0.000 startCalls=0 flightMs=8.191 flightCalls=64435 +CPC_ROUTE target='taxi_601' totalMs=159.470 navMs=1.639 probes=18 cacheHits=95 cacheSize=326 startMs=2.971 startCalls=13 flightMs=9.588 flightCalls=54638 +CPC_ROUTE target='portal_at_4367_target' totalMs=307.886 navMs=11.460 probes=910 cacheHits=284 cacheSize=326 startMs=2.836 startCalls=4 flightMs=13.414 flightCalls=97735 +CPC_PERF windowMs=30007 ticks=2107 players=72 travelMs=42.527 perceptionMs=213.655 transportMs=9.568 flightMs=0.850 actionsMs=8387.918 controllersMs=1317.407 slowGuid=96 slowMs=61.970 +CPC_ROUTE target='taxi_388' totalMs=78.247 navMs=1.976 probes=25 cacheHits=68 cacheSize=326 startMs=0.646 startCalls=2 flightMs=3.575 flightCalls=24987 +CPC_ROUTE target='taxi_388' totalMs=77.146 navMs=1.864 probes=25 cacheHits=68 cacheSize=326 startMs=0.584 startCalls=2 flightMs=3.811 flightCalls=24986 +CPC_ROUTE target='taxi_388' totalMs=78.583 navMs=2.119 probes=30 cacheHits=70 cacheSize=326 startMs=0.745 startCalls=2 flightMs=3.745 flightCalls=24985 +CPC_ROUTE target='taxi_388' totalMs=70.869 navMs=1.476 probes=11 cacheHits=60 cacheSize=326 startMs=0.738 startCalls=2 flightMs=3.492 flightCalls=22702 +CPC_ROUTE target='taxi_388' totalMs=81.475 navMs=2.434 probes=34 cacheHits=70 cacheSize=326 startMs=0.738 startCalls=2 flightMs=3.983 flightCalls=25548 +CPC_ROUTE target='taxi_388' totalMs=84.263 navMs=2.503 probes=34 cacheHits=72 cacheSize=326 startMs=0.710 startCalls=2 flightMs=4.071 flightCalls=26299 +CPC_ROUTE target='portal_at_1468_source' totalMs=63.530 navMs=1.540 probes=36 cacheHits=136 cacheSize=326 startMs=35.452 startCalls=80 flightMs=0.000 flightCalls=0 +CPC_ROUTE target='portal_at_1468_source' totalMs=63.117 navMs=1.494 probes=36 cacheHits=136 cacheSize=326 startMs=35.101 startCalls=79 flightMs=0.000 flightCalls=0 +CPC_ROUTE target='portal_at_1468_source' totalMs=62.731 navMs=1.541 probes=36 cacheHits=136 cacheSize=326 startMs=34.742 startCalls=78 flightMs=0.000 flightCalls=0 +CPC_ROUTE target='portal_at_1468_source' totalMs=62.575 navMs=1.484 probes=36 cacheHits=136 cacheSize=326 startMs=34.595 startCalls=77 flightMs=0.000 flightCalls=0 +CPC_ROUTE target='taxi_128' totalMs=288.189 navMs=9.567 probes=842 cacheHits=438 cacheSize=326 startMs=3.143 startCalls=18 flightMs=13.936 flightCalls=92467 +CPC_ROUTE target='stormwind_gate_inner' totalMs=172.309 navMs=1.586 probes=18 cacheHits=195 cacheSize=326 startMs=3.813 startCalls=16 flightMs=11.824 flightCalls=58124 +CPC_ROUTE target='taxi_310' totalMs=348.251 navMs=15.614 probes=962 cacheHits=364 cacheSize=326 startMs=1.505 startCalls=5 flightMs=14.372 flightCalls=104280 +CPC_ROUTE target='portal_at_2530_target' totalMs=52.085 navMs=3.468 probes=77 cacheHits=272 cacheSize=326 startMs=2.018 startCalls=3 flightMs=0.000 flightCalls=0 +CPC_ROUTE target='taxi_310' totalMs=332.564 navMs=15.730 probes=963 cacheHits=363 cacheSize=326 startMs=1.440 startCalls=5 flightMs=13.691 flightCalls=104280 +CPC_ROUTE target='taxi_395' totalMs=63.915 navMs=1.652 probes=36 cacheHits=136 cacheSize=326 startMs=35.394 startCalls=80 flightMs=0.000 flightCalls=0 +CPC_ROUTE target='taxi_395' totalMs=63.033 navMs=1.558 probes=36 cacheHits=136 cacheSize=326 startMs=35.209 startCalls=79 flightMs=0.000 flightCalls=0 +CPC_ROUTE target='taxi_395' totalMs=62.876 navMs=1.597 probes=36 cacheHits=136 cacheSize=326 startMs=34.602 startCalls=78 flightMs=0.000 flightCalls=0 +CPC_ROUTE target='taxi_395' totalMs=63.494 navMs=1.657 probes=36 cacheHits=136 cacheSize=326 startMs=34.928 startCalls=77 flightMs=0.000 flightCalls=0 +CPC_ROUTE target='portal_at_2171_target' totalMs=66.091 navMs=1.626 probes=36 cacheHits=126 cacheSize=326 startMs=36.771 startCalls=80 flightMs=0.000 flightCalls=0 +CPC_ROUTE target='portal_at_2171_target' totalMs=63.290 navMs=1.552 probes=36 cacheHits=126 cacheSize=326 startMs=35.573 startCalls=79 flightMs=0.000 flightCalls=0 +CPC_ROUTE target='portal_at_2171_target' totalMs=62.396 navMs=1.579 probes=36 cacheHits=126 cacheSize=326 startMs=34.701 startCalls=78 flightMs=0.000 flightCalls=0 +CPC_ROUTE target='portal_at_2171_target' totalMs=62.925 navMs=1.594 probes=36 cacheHits=126 cacheSize=326 startMs=34.798 startCalls=77 flightMs=0.000 flightCalls=0 +CPC_ROUTE target='ironforge_gate' totalMs=61.461 navMs=1.499 probes=18 cacheHits=102 cacheSize=326 startMs=35.444 startCalls=81 flightMs=0.000 flightCalls=0 +CPC_ROUTE target='ironforge_gate' totalMs=60.868 navMs=1.353 probes=18 cacheHits=102 cacheSize=326 startMs=35.006 startCalls=80 flightMs=0.000 flightCalls=0 +CPC_ROUTE target='ironforge_gate' totalMs=61.368 navMs=1.506 probes=18 cacheHits=102 cacheSize=326 startMs=34.840 startCalls=79 flightMs=0.000 flightCalls=0 +CPC_ROUTE target='taxi_310' totalMs=335.712 navMs=15.754 probes=962 cacheHits=364 cacheSize=326 startMs=1.401 startCalls=5 flightMs=15.682 flightCalls=104280 +CPC_ROUTE target='taxi_310' totalMs=340.001 navMs=15.991 probes=963 cacheHits=363 cacheSize=326 startMs=1.393 startCalls=5 flightMs=17.465 flightCalls=104280 +CPC_ROUTE target='taxi_310' totalMs=334.287 navMs=15.959 probes=962 cacheHits=364 cacheSize=326 startMs=1.418 startCalls=5 flightMs=15.534 flightCalls=104280 +CPC_ROUTE target='taxi_310' totalMs=334.042 navMs=16.013 probes=962 cacheHits=364 cacheSize=326 startMs=1.078 startCalls=4 flightMs=13.264 flightCalls=104280 +CPC_ROUTE target='taxi_310' totalMs=375.173 navMs=15.817 probes=962 cacheHits=363 cacheSize=326 startMs=36.101 startCalls=80 flightMs=13.463 flightCalls=104032 +CPC_ROUTE target='taxi_310' totalMs=378.398 navMs=15.963 probes=962 cacheHits=363 cacheSize=326 startMs=35.369 startCalls=79 flightMs=13.756 flightCalls=104032 +CPC_ROUTE target='taxi_310' totalMs=382.018 navMs=15.861 probes=962 cacheHits=363 cacheSize=326 startMs=35.677 startCalls=78 flightMs=14.178 flightCalls=104032 +CPC_ROUTE target='taxi_310' totalMs=382.199 navMs=15.886 probes=962 cacheHits=363 cacheSize=326 startMs=34.674 startCalls=77 flightMs=13.939 flightCalls=104032 +CPC_ROUTE target='portal_at_2530_target' totalMs=50.156 navMs=2.952 probes=72 cacheHits=272 cacheSize=326 startMs=1.737 startCalls=4 flightMs=0.000 flightCalls=0 +CPC_ROUTE target='ironforge_gate' totalMs=163.280 navMs=1.577 probes=19 cacheHits=117 cacheSize=326 startMs=1.592 startCalls=5 flightMs=11.459 flightCalls=56819 +CPC_ROUTE target='taxi_601' totalMs=159.280 navMs=1.622 probes=18 cacheHits=95 cacheSize=326 startMs=2.655 startCalls=13 flightMs=10.420 flightCalls=54638 +CPC_ROUTE target='ironforge_gate' totalMs=163.392 navMs=1.599 probes=21 cacheHits=101 cacheSize=326 startMs=1.440 startCalls=5 flightMs=12.448 flightCalls=56072 +CPC_ROUTE target='ironforge_gate' totalMs=164.849 navMs=1.640 probes=19 cacheHits=117 cacheSize=326 startMs=1.442 startCalls=5 flightMs=11.163 flightCalls=56819 +CPC_ROUTE target='ironforge_gate' totalMs=160.798 navMs=1.611 probes=21 cacheHits=101 cacheSize=326 startMs=1.464 startCalls=5 flightMs=10.069 flightCalls=56072 +CPC_PERF windowMs=30006 ticks=1625 players=84 travelMs=36.768 perceptionMs=245.261 transportMs=8.367 flightMs=73.778 actionsMs=7113.584 controllersMs=1847.963 slowGuid=104 slowMs=175.814 +CPC_ROUTE target='portal_at_5505_source' totalMs=663.380 navMs=31.453 probes=1924 cacheHits=726 cacheSize=326 startMs=0.000 startCalls=0 flightMs=20.814 flightCalls=208064 +CPC_ROUTE target='ironforge_gate' totalMs=163.624 navMs=1.610 probes=19 cacheHits=117 cacheSize=326 startMs=1.393 startCalls=5 flightMs=10.603 flightCalls=56819 +CPC_ROUTE target='ironforge_gate' totalMs=164.798 navMs=1.641 probes=19 cacheHits=116 cacheSize=326 startMs=1.090 startCalls=4 flightMs=12.364 flightCalls=56570 +CPC_ROUTE target='ironforge_gate' totalMs=201.248 navMs=1.581 probes=18 cacheHits=113 cacheSize=326 startMs=35.334 startCalls=80 flightMs=10.761 flightCalls=56070 +CPC_ROUTE target='ironforge_gate' totalMs=202.325 navMs=1.533 probes=18 cacheHits=113 cacheSize=326 startMs=35.098 startCalls=79 flightMs=10.375 flightCalls=56070 +CPC_ROUTE target='ironforge_gate' totalMs=204.303 navMs=1.582 probes=18 cacheHits=113 cacheSize=326 startMs=34.855 startCalls=78 flightMs=10.306 flightCalls=56070 +CPC_ROUTE target='ironforge_gate' totalMs=205.083 navMs=1.593 probes=18 cacheHits=113 cacheSize=326 startMs=34.600 startCalls=77 flightMs=10.395 flightCalls=56070 +CPC_ROUTE target='kharanos' totalMs=60.970 navMs=1.552 probes=18 cacheHits=97 cacheSize=326 startMs=35.172 startCalls=80 flightMs=0.000 flightCalls=0 +CPC_ROUTE target='kharanos' totalMs=59.625 navMs=1.475 probes=18 cacheHits=97 cacheSize=326 startMs=34.401 startCalls=79 flightMs=0.000 flightCalls=0 +CPC_ROUTE target='kharanos' totalMs=59.722 navMs=1.445 probes=18 cacheHits=97 cacheSize=326 startMs=34.136 startCalls=78 flightMs=0.000 flightCalls=0 +CPC_ROUTE target='kharanos' totalMs=59.715 navMs=1.468 probes=18 cacheHits=97 cacheSize=326 startMs=34.176 startCalls=77 flightMs=0.000 flightCalls=0 +CPC_ROUTE target='taxi_128' totalMs=289.580 navMs=9.694 probes=842 cacheHits=438 cacheSize=326 startMs=3.317 startCalls=18 flightMs=14.219 flightCalls=92467 +CPC_ROUTE target='taxi_55' totalMs=69.456 navMs=0.983 probes=11 cacheHits=57 cacheSize=326 startMs=0.640 startCalls=2 flightMs=3.734 flightCalls=22897 +CPC_ROUTE target='taxi_55' totalMs=69.418 navMs=0.978 probes=11 cacheHits=57 cacheSize=326 startMs=0.688 startCalls=2 flightMs=3.542 flightCalls=22896 +CPC_ROUTE target='taxi_55' totalMs=70.920 navMs=1.516 probes=17 cacheHits=62 cacheSize=326 startMs=0.785 startCalls=2 flightMs=3.351 flightCalls=23083 +CPC_ROUTE target='taxi_55' totalMs=71.000 navMs=1.505 probes=12 cacheHits=60 cacheSize=326 startMs=0.740 startCalls=2 flightMs=3.313 flightCalls=23078 +CPC_ROUTE target='taxi_55' totalMs=74.738 navMs=1.991 probes=21 cacheHits=62 cacheSize=326 startMs=0.723 startCalls=2 flightMs=3.419 flightCalls=23646 +CPC_ROUTE target='taxi_55' totalMs=76.242 navMs=1.848 probes=21 cacheHits=64 cacheSize=326 startMs=0.582 startCalls=2 flightMs=3.190 flightCalls=24397 +CPC_ROUTE target='portal_at_2530_target' totalMs=51.710 navMs=3.310 probes=77 cacheHits=272 cacheSize=326 startMs=2.630 startCalls=4 flightMs=0.000 flightCalls=0 +CPC_ROUTE target='ironforge_gate' totalMs=170.757 navMs=1.649 probes=19 cacheHits=106 cacheSize=326 startMs=1.684 startCalls=5 flightMs=19.547 flightCalls=56072 +CPC_ROUTE target='ironforge_gate' totalMs=162.505 navMs=1.639 probes=21 cacheHits=101 cacheSize=326 startMs=1.688 startCalls=5 flightMs=11.230 flightCalls=56072 +CPC_ROUTE target='taxi_601' totalMs=160.423 navMs=1.577 probes=18 cacheHits=95 cacheSize=326 startMs=2.711 startCalls=13 flightMs=10.070 flightCalls=54638 +CPC_ROUTE target='ironforge_gate' totalMs=62.481 navMs=1.558 probes=18 cacheHits=102 cacheSize=326 startMs=36.480 startCalls=81 flightMs=0.000 flightCalls=0 +CPC_ROUTE target='ironforge_gate' totalMs=60.975 navMs=1.467 probes=18 cacheHits=102 cacheSize=326 startMs=34.648 startCalls=80 flightMs=0.000 flightCalls=0 +CPC_ROUTE target='ironforge_gate' totalMs=60.321 navMs=1.363 probes=18 cacheHits=102 cacheSize=326 startMs=34.482 startCalls=79 flightMs=0.000 flightCalls=0 +CPC_ROUTE target='ironforge_gate' totalMs=160.029 navMs=1.508 probes=19 cacheHits=106 cacheSize=326 startMs=1.410 startCalls=5 flightMs=10.265 flightCalls=56072 +CPC_ROUTE target='taxi_38' totalMs=150.391 navMs=1.584 probes=20 cacheHits=99 cacheSize=326 startMs=1.834 startCalls=3 flightMs=6.330 flightCalls=53148 +CPC_ROUTE target='ironforge_gate' totalMs=162.662 navMs=1.619 probes=21 cacheHits=101 cacheSize=326 startMs=1.435 startCalls=5 flightMs=11.579 flightCalls=56072 +CPC_ROUTE target='taxi_82' totalMs=50.425 navMs=3.179 probes=72 cacheHits=272 cacheSize=326 startMs=0.431 startCalls=1 flightMs=0.000 flightCalls=0 +CPC_ROUTE target='ironforge_gate' totalMs=165.774 navMs=1.644 probes=19 cacheHits=106 cacheSize=326 startMs=1.561 startCalls=5 flightMs=13.405 flightCalls=56072 +CPC_ROUTE target='ironforge_gate' totalMs=178.453 navMs=1.655 probes=19 cacheHits=105 cacheSize=326 startMs=1.353 startCalls=4 flightMs=20.378 flightCalls=55823 +CPC_ROUTE target='ironforge_gate' totalMs=222.283 navMs=1.705 probes=18 cacheHits=102 cacheSize=326 startMs=39.135 startCalls=80 flightMs=20.722 flightCalls=55323 +CPC_ROUTE target='ironforge_gate' totalMs=210.050 navMs=1.836 probes=18 cacheHits=102 cacheSize=326 startMs=39.007 startCalls=79 flightMs=13.799 flightCalls=55323 +CPC_ROUTE target='ironforge_gate' totalMs=209.872 navMs=1.651 probes=18 cacheHits=102 cacheSize=326 startMs=35.244 startCalls=78 flightMs=16.559 flightCalls=55323 +CPC_ROUTE target='ironforge_gate' totalMs=221.756 navMs=1.647 probes=18 cacheHits=102 cacheSize=326 startMs=35.951 startCalls=77 flightMs=18.490 flightCalls=55323 +CPC_ROUTE target='portal_at_6108_source' totalMs=69.821 navMs=1.722 probes=36 cacheHits=136 cacheSize=326 startMs=38.893 startCalls=80 flightMs=0.000 flightCalls=0 +CPC_ROUTE target='portal_at_6108_source' totalMs=69.058 navMs=1.656 probes=36 cacheHits=136 cacheSize=326 startMs=39.161 startCalls=79 flightMs=0.000 flightCalls=0 +CPC_ROUTE target='portal_at_6108_source' totalMs=66.841 navMs=1.715 probes=36 cacheHits=136 cacheSize=326 startMs=36.776 startCalls=78 flightMs=0.000 flightCalls=0 +CPC_ROUTE target='portal_at_6108_source' totalMs=66.447 navMs=1.588 probes=36 cacheHits=136 cacheSize=326 startMs=36.895 startCalls=77 flightMs=0.000 flightCalls=0 +CPC_ROUTE target='ironforge_gate' totalMs=68.278 navMs=1.719 probes=18 cacheHits=102 cacheSize=326 startMs=39.121 startCalls=81 flightMs=0.000 flightCalls=0 +CPC_ROUTE target='ironforge_gate' totalMs=67.314 navMs=1.689 probes=18 cacheHits=102 cacheSize=326 startMs=38.173 startCalls=80 flightMs=0.000 flightCalls=0 +CPC_ROUTE target='ironforge_gate' totalMs=65.352 navMs=1.685 probes=18 cacheHits=102 cacheSize=326 startMs=37.548 startCalls=79 flightMs=0.000 flightCalls=0 +CPC_ROUTE target='portal_at_4367_target' totalMs=1828.142 navMs=11.845 probes=909 cacheHits=283 cacheSize=326 startMs=2.853 startCalls=5 flightMs=1537.222 flightCalls=97240 +CPC_ROUTE target='taxi_601' totalMs=1277.671 navMs=1.654 probes=18 cacheHits=95 cacheSize=326 startMs=2.810 startCalls=13 flightMs=1125.724 flightCalls=54638 +CPC_ROUTE target='portal_at_2171_target' totalMs=63.181 navMs=1.590 probes=36 cacheHits=126 cacheSize=326 startMs=35.175 startCalls=80 flightMs=0.000 flightCalls=0 +CPC_ROUTE target='portal_at_2171_target' totalMs=61.586 navMs=1.440 probes=36 cacheHits=126 cacheSize=326 startMs=34.551 startCalls=79 flightMs=0.000 flightCalls=0 +CPC_ROUTE target='portal_at_2171_target' totalMs=61.758 navMs=1.429 probes=36 cacheHits=126 cacheSize=326 startMs=34.333 startCalls=78 flightMs=0.000 flightCalls=0 +CPC_ROUTE target='portal_at_2171_target' totalMs=61.472 navMs=1.456 probes=36 cacheHits=126 cacheSize=326 startMs=33.974 startCalls=77 flightMs=0.000 flightCalls=0 +CPC_ROUTE target='ironforge_gate' totalMs=162.249 navMs=1.580 probes=19 cacheHits=106 cacheSize=326 startMs=1.435 startCalls=5 flightMs=12.506 flightCalls=56072 +CPC_ROUTE target='portal_at_2530_target' totalMs=50.369 navMs=3.062 probes=72 cacheHits=272 cacheSize=326 startMs=1.735 startCalls=4 flightMs=0.000 flightCalls=0 +CPC_PERF windowMs=30013 ticks=1248 players=99 travelMs=38.541 perceptionMs=261.382 transportMs=7.342 flightMs=73.418 actionsMs=10444.503 controllersMs=1917.755 slowGuid=120 slowMs=147.087 +CPC_ROUTE target='ironforge_gate' totalMs=162.546 navMs=1.611 probes=21 cacheHits=104 cacheSize=326 startMs=1.420 startCalls=5 flightMs=11.205 flightCalls=56321 +CPC_ROUTE target='kharanos' totalMs=61.551 navMs=1.623 probes=18 cacheHits=94 cacheSize=326 startMs=35.470 startCalls=80 flightMs=0.000 flightCalls=0 +CPC_ROUTE target='kharanos' totalMs=60.460 navMs=1.490 probes=18 cacheHits=94 cacheSize=326 startMs=35.151 startCalls=79 flightMs=0.000 flightCalls=0 +CPC_ROUTE target='kharanos' totalMs=60.044 navMs=1.469 probes=18 cacheHits=94 cacheSize=326 startMs=34.569 startCalls=78 flightMs=0.000 flightCalls=0 +CPC_ROUTE target='kharanos' totalMs=60.124 navMs=1.467 probes=18 cacheHits=94 cacheSize=326 startMs=34.429 startCalls=77 flightMs=0.000 flightCalls=0 +CPC_ROUTE target='taxi_521' totalMs=295.569 navMs=10.298 probes=848 cacheHits=302 cacheSize=326 startMs=2.630 startCalls=15 flightMs=22.254 flightCalls=93305 +CPC_ROUTE target='ironforge_gate' totalMs=163.084 navMs=1.604 probes=19 cacheHits=106 cacheSize=326 startMs=1.729 startCalls=5 flightMs=11.891 flightCalls=56072 +CPC_ROUTE target='portal_at_2530_target' totalMs=83.741 navMs=2.673 probes=32 cacheHits=73 cacheSize=326 startMs=2.041 startCalls=3 flightMs=5.171 flightCalls=26059 +CPC_ROUTE target='taxi_128' totalMs=283.759 navMs=9.451 probes=839 cacheHits=341 cacheSize=326 startMs=2.920 startCalls=18 flightMs=12.866 flightCalls=91970 +CPC_ROUTE target='ironforge_gate' totalMs=166.231 navMs=1.689 probes=21 cacheHits=104 cacheSize=326 startMs=1.667 startCalls=5 flightMs=13.772 flightCalls=56321 +CPC_ROUTE target='taxi_82' totalMs=166.269 navMs=2.068 probes=20 cacheHits=97 cacheSize=326 startMs=0.645 startCalls=1 flightMs=16.278 flightCalls=52745 +CPC_ROUTE target='ironforge_gate' totalMs=187.505 navMs=1.907 probes=19 cacheHits=106 cacheSize=326 startMs=1.630 startCalls=5 flightMs=24.564 flightCalls=56072 +CPC_ROUTE target='ironforge_gate' totalMs=182.197 navMs=1.905 probes=19 cacheHits=105 cacheSize=326 startMs=1.298 startCalls=4 flightMs=20.683 flightCalls=55823 +CPC_ROUTE target='ironforge_gate' totalMs=235.239 navMs=1.981 probes=18 cacheHits=102 cacheSize=326 startMs=39.386 startCalls=80 flightMs=25.943 flightCalls=55323 +CPC_ROUTE target='ironforge_gate' totalMs=215.249 navMs=1.836 probes=18 cacheHits=102 cacheSize=326 startMs=39.269 startCalls=79 flightMs=17.132 flightCalls=55323 +CPC_ROUTE target='ironforge_gate' totalMs=204.349 navMs=1.659 probes=18 cacheHits=102 cacheSize=326 startMs=35.208 startCalls=78 flightMs=11.743 flightCalls=55323 +CPC_ROUTE target='ironforge_gate' totalMs=206.213 navMs=1.625 probes=18 cacheHits=102 cacheSize=326 startMs=34.921 startCalls=77 flightMs=11.953 flightCalls=55323 +CPC_ROUTE target='taxi_181' totalMs=221.744 navMs=7.748 probes=125 cacheHits=176 cacheSize=326 startMs=0.703 startCalls=2 flightMs=18.705 flightCalls=67018 +CPC_ROUTE target='taxi_181' totalMs=221.263 navMs=7.643 probes=125 cacheHits=176 cacheSize=326 startMs=0.863 startCalls=2 flightMs=17.633 flightCalls=67017 +CPC_ROUTE target='taxi_181' totalMs=220.328 navMs=7.587 probes=112 cacheHits=167 cacheSize=326 startMs=0.699 startCalls=2 flightMs=21.716 flightCalls=64576 +CPC_ROUTE target='taxi_181' totalMs=228.781 navMs=8.185 probes=125 cacheHits=176 cacheSize=326 startMs=0.633 startCalls=2 flightMs=20.127 flightCalls=67015 +CPC_ROUTE target='taxi_181' totalMs=226.745 navMs=7.594 probes=122 cacheHits=175 cacheSize=326 startMs=0.860 startCalls=2 flightMs=17.630 flightCalls=67011 +CPC_ROUTE target='taxi_181' totalMs=233.746 navMs=7.755 probes=125 cacheHits=177 cacheSize=326 startMs=0.747 startCalls=2 flightMs=20.668 flightCalls=67201 +CPC_ROUTE target='portal_at_7151_source' totalMs=68.457 navMs=1.858 probes=18 cacheHits=99 cacheSize=326 startMs=38.844 startCalls=80 flightMs=0.000 flightCalls=0 +CPC_ROUTE target='portal_at_7151_source' totalMs=68.034 navMs=1.793 probes=18 cacheHits=99 cacheSize=326 startMs=38.776 startCalls=79 flightMs=0.000 flightCalls=0 +CPC_ROUTE target='portal_at_7151_source' totalMs=66.806 navMs=1.675 probes=18 cacheHits=99 cacheSize=326 startMs=38.103 startCalls=78 flightMs=0.000 flightCalls=0 +CPC_ROUTE target='portal_at_7151_source' totalMs=66.364 navMs=1.694 probes=18 cacheHits=99 cacheSize=326 startMs=37.836 startCalls=77 flightMs=0.000 flightCalls=0 +CPC_ROUTE target='taxi_11' totalMs=175.388 navMs=2.072 probes=18 cacheHits=97 cacheSize=328 startMs=0.844 startCalls=2 flightMs=21.636 flightCalls=52769 +CPC_ROUTE target='taxi_601' totalMs=176.589 navMs=1.835 probes=18 cacheHits=95 cacheSize=328 startMs=2.825 startCalls=13 flightMs=16.754 flightCalls=54638 +CPC_ROUTE target='portal_at_4367_target' totalMs=335.289 navMs=13.353 probes=909 cacheHits=283 cacheSize=328 startMs=1.088 startCalls=2 flightMs=26.730 flightCalls=97239 +CPC_ROUTE target='portal_at_4367_target' totalMs=385.493 navMs=13.280 probes=909 cacheHits=283 cacheSize=328 startMs=41.052 startCalls=80 flightMs=26.465 flightCalls=97239 +CPC_ROUTE target='portal_at_2530_target' totalMs=59.125 navMs=3.874 probes=77 cacheHits=272 cacheSize=328 startMs=2.969 startCalls=4 flightMs=0.000 flightCalls=0 +CPC_ROUTE target='portal_at_2530_target' totalMs=55.584 navMs=3.138 probes=75 cacheHits=272 cacheSize=328 startMs=1.913 startCalls=3 flightMs=0.000 flightCalls=0 +CPC_ROUTE target='portal_at_2166_target' totalMs=69.248 navMs=1.828 probes=18 cacheHits=97 cacheSize=328 startMs=39.938 startCalls=81 flightMs=0.000 flightCalls=0 +CPC_ROUTE target='portal_at_2166_target' totalMs=67.173 navMs=1.556 probes=18 cacheHits=97 cacheSize=328 startMs=38.798 startCalls=80 flightMs=0.000 flightCalls=0 +CPC_ROUTE target='portal_at_2166_target' totalMs=67.035 navMs=1.680 probes=18 cacheHits=97 cacheSize=328 startMs=38.413 startCalls=79 flightMs=0.000 flightCalls=0 +CPC_ROUTE target='portal_at_2230_source' totalMs=57.670 navMs=3.541 probes=72 cacheHits=272 cacheSize=328 startMs=0.708 startCalls=1 flightMs=0.000 flightCalls=0 +CPC_ROUTE target='portal_at_5193_source' totalMs=756.751 navMs=36.748 probes=1924 cacheHits=726 cacheSize=328 startMs=0.000 startCalls=0 flightMs=67.153 flightCalls=208064 +CPC_ROUTE target='taxi_181' totalMs=67.506 navMs=0.475 probes=3 cacheHits=40 cacheSize=328 startMs=1.564 startCalls=2 flightMs=6.862 flightCalls=20141 +CPC_ROUTE target='portal_at_324_source' totalMs=73.571 navMs=1.715 probes=36 cacheHits=136 cacheSize=328 startMs=41.194 startCalls=80 flightMs=0.000 flightCalls=0 +CPC_ROUTE target='portal_at_324_source' totalMs=69.901 navMs=1.630 probes=36 cacheHits=136 cacheSize=328 startMs=39.142 startCalls=79 flightMs=0.000 flightCalls=0 +CPC_ROUTE target='portal_at_324_source' totalMs=73.083 navMs=1.930 probes=36 cacheHits=136 cacheSize=328 startMs=40.777 startCalls=78 flightMs=0.000 flightCalls=0 +CPC_ROUTE target='portal_at_324_source' totalMs=71.533 navMs=1.819 probes=36 cacheHits=136 cacheSize=328 startMs=39.369 startCalls=77 flightMs=0.000 flightCalls=0 +CPC_ROUTE target='portal_at_2530_target' totalMs=59.233 navMs=4.020 probes=77 cacheHits=272 cacheSize=328 startMs=2.344 startCalls=3 flightMs=0.000 flightCalls=0 +CPC_ROUTE target='portal_at_2166_target' totalMs=177.830 navMs=1.820 probes=19 cacheHits=99 cacheSize=328 startMs=1.580 startCalls=5 flightMs=20.999 flightCalls=54390 +CPC_ROUTE target='taxi_82' totalMs=165.842 navMs=1.874 probes=20 cacheHits=99 cacheSize=328 startMs=1.080 startCalls=2 flightMs=12.407 flightCalls=52992 +CPC_ROUTE target='taxi_82' totalMs=215.190 navMs=1.795 probes=18 cacheHits=97 cacheSize=328 startMs=42.616 startCalls=80 flightMs=14.537 flightCalls=52246 +CPC_ROUTE target='portal_at_101_source' totalMs=72.074 navMs=1.856 probes=27 cacheHits=118 cacheSize=328 startMs=40.870 startCalls=80 flightMs=0.000 flightCalls=0 +CPC_ROUTE target='portal_at_101_source' totalMs=74.042 navMs=1.614 probes=27 cacheHits=118 cacheSize=328 startMs=40.243 startCalls=79 flightMs=0.000 flightCalls=0 +CPC_ROUTE target='portal_at_101_source' totalMs=71.419 navMs=1.853 probes=27 cacheHits=118 cacheSize=328 startMs=39.759 startCalls=78 flightMs=0.000 flightCalls=0 +CPC_ROUTE target='portal_at_101_source' totalMs=70.532 navMs=1.779 probes=27 cacheHits=118 cacheSize=328 startMs=39.050 startCalls=77 flightMs=0.000 flightCalls=0 +CPC_ROUTE target='portal_at_2166_target' totalMs=185.802 navMs=1.864 probes=21 cacheHits=99 cacheSize=328 startMs=1.763 startCalls=5 flightMs=25.719 flightCalls=54888 +CPC_ROUTE target='taxi_64' totalMs=85.558 navMs=0.464 probes=3 cacheHits=33 cacheSize=328 startMs=23.900 startCalls=45 flightMs=4.126 flightCalls=18825 +CPC_ROUTE target='portal_at_2166_target' totalMs=181.556 navMs=1.887 probes=19 cacheHits=99 cacheSize=328 startMs=1.734 startCalls=5 flightMs=22.518 flightCalls=54390 +CPC_PERF windowMs=30015 ticks=781 players=110 travelMs=62.087 perceptionMs=408.276 transportMs=12.536 flightMs=136.368 actionsMs=9366.665 controllersMs=1866.717 slowGuid=123 slowMs=105.831 +CPC_ROUTE target='portal_at_2166_target' totalMs=179.170 navMs=1.855 probes=21 cacheHits=99 cacheSize=328 startMs=1.700 startCalls=5 flightMs=20.224 flightCalls=54888 +CPC_ROUTE target='portal_at_2530_target' totalMs=56.085 navMs=3.513 probes=72 cacheHits=272 cacheSize=328 startMs=2.270 startCalls=4 flightMs=0.000 flightCalls=0 +CPC_ROUTE target='portal_at_2166_target' totalMs=175.785 navMs=1.759 probes=19 cacheHits=99 cacheSize=328 startMs=1.695 startCalls=5 flightMs=19.443 flightCalls=54390 +CPC_ROUTE target='portal_at_2166_target' totalMs=174.353 navMs=1.989 probes=19 cacheHits=98 cacheSize=328 startMs=1.185 startCalls=4 flightMs=17.913 flightCalls=54141 +CPC_ROUTE target='portal_at_2166_target' totalMs=220.401 navMs=1.822 probes=18 cacheHits=97 cacheSize=328 startMs=40.616 startCalls=80 flightMs=19.010 flightCalls=53641 +CPC_ROUTE target='portal_at_2166_target' totalMs=219.999 navMs=1.709 probes=18 cacheHits=97 cacheSize=328 startMs=39.234 startCalls=79 flightMs=18.401 flightCalls=53641 +CPC_ROUTE target='portal_at_2166_target' totalMs=221.736 navMs=1.807 probes=18 cacheHits=97 cacheSize=328 startMs=39.048 startCalls=78 flightMs=19.129 flightCalls=53641 +CPC_ROUTE target='portal_at_2166_target' totalMs=223.316 navMs=1.783 probes=18 cacheHits=97 cacheSize=328 startMs=38.491 startCalls=77 flightMs=20.531 flightCalls=53641 +CPC_ROUTE target='portal_at_4367_target' totalMs=344.719 navMs=14.115 probes=910 cacheHits=284 cacheSize=328 startMs=3.018 startCalls=4 flightMs=27.284 flightCalls=97735 +CPC_ROUTE target='portal_at_523_source' totalMs=71.051 navMs=1.682 probes=36 cacheHits=136 cacheSize=328 startMs=39.107 startCalls=80 flightMs=0.000 flightCalls=0 +CPC_ROUTE target='portal_at_523_source' totalMs=71.471 navMs=1.847 probes=36 cacheHits=136 cacheSize=328 startMs=38.822 startCalls=79 flightMs=0.000 flightCalls=0 +CPC_ROUTE target='portal_at_523_source' totalMs=71.736 navMs=1.690 probes=36 cacheHits=136 cacheSize=328 startMs=40.124 startCalls=78 flightMs=0.000 flightCalls=0 +CPC_ROUTE target='portal_at_523_source' totalMs=68.656 navMs=1.674 probes=36 cacheHits=136 cacheSize=328 startMs=37.817 startCalls=77 flightMs=0.000 flightCalls=0 +CPC_ROUTE target='taxi_36' totalMs=69.191 navMs=1.992 probes=18 cacheHits=99 cacheSize=328 startMs=39.015 startCalls=80 flightMs=0.000 flightCalls=0 +CPC_ROUTE target='taxi_36' totalMs=68.683 navMs=1.769 probes=18 cacheHits=99 cacheSize=328 startMs=39.373 startCalls=79 flightMs=0.000 flightCalls=0 +CPC_ROUTE target='taxi_36' totalMs=68.237 navMs=1.701 probes=18 cacheHits=99 cacheSize=328 startMs=38.309 startCalls=78 flightMs=0.000 flightCalls=0 +CPC_ROUTE target='taxi_36' totalMs=67.345 navMs=1.695 probes=18 cacheHits=99 cacheSize=328 startMs=38.062 startCalls=77 flightMs=0.000 flightCalls=0 +CPC_ROUTE target='ironforge_gate' totalMs=71.241 navMs=1.843 probes=18 cacheHits=102 cacheSize=328 startMs=39.855 startCalls=80 flightMs=0.000 flightCalls=0 +CPC_ROUTE target='ironforge_gate' totalMs=68.556 navMs=1.935 probes=18 cacheHits=102 cacheSize=328 startMs=38.705 startCalls=79 flightMs=0.000 flightCalls=0 +CPC_ROUTE target='ironforge_gate' totalMs=69.149 navMs=1.659 probes=18 cacheHits=102 cacheSize=328 startMs=39.988 startCalls=78 flightMs=0.000 flightCalls=0 +CPC_ROUTE target='ironforge_gate' totalMs=68.117 navMs=1.659 probes=18 cacheHits=102 cacheSize=328 startMs=38.867 startCalls=77 flightMs=0.000 flightCalls=0 +CPC_ROUTE target='portal_at_2530_target' totalMs=56.985 navMs=3.919 probes=77 cacheHits=272 cacheSize=328 startMs=2.143 startCalls=3 flightMs=0.000 flightCalls=0 +CPC_ROUTE target='taxi_181' totalMs=74.342 navMs=0.357 probes=3 cacheHits=40 cacheSize=328 startMs=0.797 startCalls=2 flightMs=10.893 flightCalls=21645 +CPC_ROUTE target='taxi_181' totalMs=70.036 navMs=0.417 probes=3 cacheHits=40 cacheSize=328 startMs=0.863 startCalls=2 flightMs=5.470 flightCalls=21644 +CPC_ROUTE target='taxi_181' totalMs=69.377 navMs=0.443 probes=3 cacheHits=39 cacheSize=328 startMs=0.668 startCalls=2 flightMs=5.511 flightCalls=21267 +CPC_ROUTE target='taxi_181' totalMs=74.212 navMs=1.667 probes=11 cacheHits=51 cacheSize=328 startMs=0.717 startCalls=2 flightMs=5.696 flightCalls=22206 +CPC_ROUTE target='taxi_181' totalMs=79.746 navMs=2.152 probes=15 cacheHits=51 cacheSize=328 startMs=0.844 startCalls=2 flightMs=8.000 flightCalls=22770 +CPC_ROUTE target='taxi_181' totalMs=77.461 navMs=2.003 probes=15 cacheHits=51 cacheSize=328 startMs=0.881 startCalls=2 flightMs=6.095 flightCalls=22769 +CPC_ROUTE target='taxi_128' totalMs=516.637 navMs=11.699 probes=839 cacheHits=341 cacheSize=328 startMs=2.601 startCalls=16 flightMs=224.614 flightCalls=91970 +CPC_ROUTE target='portal_at_2530_target' totalMs=56.015 navMs=3.559 probes=72 cacheHits=272 cacheSize=328 startMs=2.245 startCalls=4 flightMs=0.000 flightCalls=0 +CPC_ROUTE target='taxi_82' totalMs=176.188 navMs=1.839 probes=22 cacheHits=101 cacheSize=328 startMs=4.037 startCalls=4 flightMs=14.138 flightCalls=53739 +CPC_ROUTE target='taxi_601' totalMs=1229.272 navMs=1.672 probes=18 cacheHits=95 cacheSize=328 startMs=2.965 startCalls=13 flightMs=1073.934 flightCalls=54638 +CPC_ROUTE target='taxi_64' totalMs=66.838 navMs=0.990 probes=5 cacheHits=39 cacheSize=328 startMs=3.543 startCalls=7 flightMs=6.857 flightCalls=19389 +CPC_ROUTE target='ironforge_gate' totalMs=69.400 navMs=1.673 probes=18 cacheHits=113 cacheSize=328 startMs=40.983 startCalls=80 flightMs=0.000 flightCalls=0 +CPC_ROUTE target='taxi_181' totalMs=1110.666 navMs=0.415 probes=3 cacheHits=40 cacheSize=328 startMs=0.039 startCalls=1 flightMs=1052.306 flightCalls=20141 +CPC_ROUTE target='taxi_181' totalMs=60.350 navMs=0.410 probes=3 cacheHits=40 cacheSize=328 startMs=0.098 startCalls=1 flightMs=3.670 flightCalls=20141 +CPC_ROUTE target='portal_at_1466_source' totalMs=67.267 navMs=1.662 probes=36 cacheHits=136 cacheSize=328 startMs=37.777 startCalls=80 flightMs=0.000 flightCalls=0 +CPC_ROUTE target='portal_at_1466_source' totalMs=65.352 navMs=1.668 probes=36 cacheHits=136 cacheSize=328 startMs=36.375 startCalls=79 flightMs=0.000 flightCalls=0 +CPC_ROUTE target='portal_at_1466_source' totalMs=66.391 navMs=1.686 probes=36 cacheHits=136 cacheSize=328 startMs=36.625 startCalls=78 flightMs=0.000 flightCalls=0 +CPC_ROUTE target='portal_at_1466_source' totalMs=65.407 navMs=1.591 probes=36 cacheHits=136 cacheSize=328 startMs=35.994 startCalls=77 flightMs=0.000 flightCalls=0 +CPC_ROUTE target='portal_at_2530_target' totalMs=96.207 navMs=3.042 probes=56 cacheHits=95 cacheSize=328 startMs=0.000 startCalls=0 flightMs=7.308 flightCalls=28674 +CPC_ROUTE target='taxi_181' totalMs=213.523 navMs=7.375 probes=133 cacheHits=191 cacheSize=328 startMs=0.081 startCalls=1 flightMs=12.512 flightCalls=69705 +CPC_PERF windowMs=30011 ticks=1258 players=110 travelMs=55.301 perceptionMs=420.305 transportMs=18.870 flightMs=74.420 actionsMs=7762.125 controllersMs=1611.802 slowGuid=61 slowMs=18.412 +CPC_ROUTE target='portal_at_2530_target' totalMs=52.766 navMs=3.526 probes=77 cacheHits=272 cacheSize=328 startMs=2.684 startCalls=4 flightMs=0.000 flightCalls=0 +CPC_ROUTE target='portal_at_2530_target' totalMs=54.048 navMs=3.611 probes=77 cacheHits=272 cacheSize=328 startMs=2.677 startCalls=4 flightMs=0.000 flightCalls=0 +CPC_ROUTE target='portal_at_2171_target' totalMs=181.023 navMs=1.706 probes=37 cacheHits=132 cacheSize=328 startMs=1.329 startCalls=5 flightMs=19.464 flightCalls=58831 +CPC_ROUTE target='portal_at_286_source' totalMs=65.614 navMs=1.596 probes=36 cacheHits=136 cacheSize=328 startMs=36.905 startCalls=80 flightMs=0.000 flightCalls=0 +CPC_ROUTE target='portal_at_286_source' totalMs=64.449 navMs=1.618 probes=36 cacheHits=136 cacheSize=328 startMs=35.939 startCalls=79 flightMs=0.000 flightCalls=0 +CPC_ROUTE target='portal_at_286_source' totalMs=64.109 navMs=1.626 probes=36 cacheHits=136 cacheSize=328 startMs=35.354 startCalls=78 flightMs=0.000 flightCalls=0 +CPC_ROUTE target='portal_at_286_source' totalMs=63.671 navMs=1.713 probes=36 cacheHits=136 cacheSize=328 startMs=34.972 startCalls=77 flightMs=0.000 flightCalls=0 +CPC_ROUTE target='portal_at_2171_target' totalMs=64.681 navMs=1.611 probes=36 cacheHits=126 cacheSize=328 startMs=36.077 startCalls=80 flightMs=0.000 flightCalls=0 +CPC_ROUTE target='portal_at_2171_target' totalMs=64.168 navMs=1.595 probes=36 cacheHits=126 cacheSize=328 startMs=35.951 startCalls=79 flightMs=0.000 flightCalls=0 +CPC_ROUTE target='portal_at_2171_target' totalMs=64.734 navMs=1.699 probes=36 cacheHits=126 cacheSize=328 startMs=35.756 startCalls=78 flightMs=0.000 flightCalls=0 +CPC_ROUTE target='portal_at_2171_target' totalMs=64.416 navMs=1.752 probes=36 cacheHits=126 cacheSize=328 startMs=35.542 startCalls=77 flightMs=0.000 flightCalls=0 +CPC_ROUTE target='portal_at_2171_target' totalMs=176.666 navMs=1.731 probes=39 cacheHits=130 cacheSize=328 startMs=1.748 startCalls=5 flightMs=15.814 flightCalls=59080 +CPC_ROUTE target='portal_at_2171_target' totalMs=175.548 navMs=1.713 probes=37 cacheHits=132 cacheSize=328 startMs=1.576 startCalls=5 flightMs=15.404 flightCalls=58831 +CPC_ROUTE target='taxi_601' totalMs=162.117 navMs=1.683 probes=18 cacheHits=95 cacheSize=328 startMs=2.705 startCalls=13 flightMs=11.117 flightCalls=54638 +CPC_ROUTE target='taxi_128' totalMs=285.494 navMs=9.507 probes=839 cacheHits=341 cacheSize=328 startMs=2.025 startCalls=16 flightMs=12.996 flightCalls=91970 +CPC_ROUTE target='portal_at_2171_target' totalMs=176.487 navMs=1.708 probes=39 cacheHits=130 cacheSize=328 startMs=1.656 startCalls=5 flightMs=14.901 flightCalls=59080 +CPC_ROUTE target='taxi_181' totalMs=62.917 navMs=0.369 probes=3 cacheHits=40 cacheSize=328 startMs=0.743 startCalls=2 flightMs=3.082 flightCalls=21645 +CPC_ROUTE target='taxi_181' totalMs=63.963 navMs=0.360 probes=3 cacheHits=40 cacheSize=328 startMs=0.682 startCalls=2 flightMs=3.053 flightCalls=21644 +CPC_ROUTE target='taxi_181' totalMs=63.146 navMs=0.380 probes=3 cacheHits=39 cacheSize=328 startMs=0.788 startCalls=2 flightMs=2.943 flightCalls=21267 +CPC_ROUTE target='taxi_181' totalMs=67.325 navMs=1.575 probes=11 cacheHits=51 cacheSize=328 startMs=0.591 startCalls=2 flightMs=3.162 flightCalls=22206 +CPC_ROUTE target='taxi_181' totalMs=69.559 navMs=1.866 probes=15 cacheHits=51 cacheSize=328 startMs=0.739 startCalls=2 flightMs=2.606 flightCalls=22770 +CPC_ROUTE target='taxi_181' totalMs=70.559 navMs=1.884 probes=15 cacheHits=51 cacheSize=328 startMs=0.766 startCalls=2 flightMs=3.075 flightCalls=22769 +CPC_ROUTE target='portal_at_2171_target' totalMs=176.366 navMs=1.693 probes=37 cacheHits=132 cacheSize=328 startMs=1.472 startCalls=5 flightMs=15.948 flightCalls=58831 +CPC_ROUTE target='portal_at_2171_target' totalMs=175.566 navMs=1.680 probes=37 cacheHits=131 cacheSize=328 startMs=1.083 startCalls=4 flightMs=15.518 flightCalls=58582 +CPC_ROUTE target='portal_at_2171_target' totalMs=216.523 navMs=1.676 probes=36 cacheHits=126 cacheSize=328 startMs=36.293 startCalls=80 flightMs=17.086 flightCalls=57833 +CPC_ROUTE target='portal_at_2171_target' totalMs=212.781 navMs=1.624 probes=36 cacheHits=126 cacheSize=328 startMs=36.239 startCalls=79 flightMs=13.980 flightCalls=57833 +CPC_ROUTE target='portal_at_2171_target' totalMs=214.938 navMs=1.657 probes=36 cacheHits=126 cacheSize=328 startMs=35.019 startCalls=78 flightMs=14.814 flightCalls=57833 +CPC_ROUTE target='portal_at_2171_target' totalMs=215.493 navMs=1.655 probes=36 cacheHits=126 cacheSize=328 startMs=34.728 startCalls=77 flightMs=14.153 flightCalls=57833 +CPC_ROUTE target='taxi_36' totalMs=65.389 navMs=1.606 probes=18 cacheHits=97 cacheSize=328 startMs=36.539 startCalls=80 flightMs=0.000 flightCalls=0 +CPC_ROUTE target='taxi_36' totalMs=62.278 navMs=1.581 probes=18 cacheHits=97 cacheSize=328 startMs=35.539 startCalls=79 flightMs=0.000 flightCalls=0 +CPC_ROUTE target='taxi_36' totalMs=61.723 navMs=1.634 probes=18 cacheHits=97 cacheSize=328 startMs=34.823 startCalls=78 flightMs=0.000 flightCalls=0 +CPC_ROUTE target='taxi_36' totalMs=61.602 navMs=1.564 probes=18 cacheHits=97 cacheSize=328 startMs=34.566 startCalls=77 flightMs=0.000 flightCalls=0 +CPC_ROUTE target='portal_at_4367_target' totalMs=309.099 navMs=12.717 probes=909 cacheHits=283 cacheSize=328 startMs=2.817 startCalls=5 flightMs=16.354 flightCalls=97240 +CPC_ROUTE target='taxi_128' totalMs=286.388 navMs=10.150 probes=839 cacheHits=341 cacheSize=328 startMs=2.102 startCalls=16 flightMs=12.923 flightCalls=91970 +CPC_ROUTE target='ironforge_gate' totalMs=62.540 navMs=1.616 probes=18 cacheHits=102 cacheSize=328 startMs=35.728 startCalls=80 flightMs=0.000 flightCalls=0 +CPC_ROUTE target='ironforge_gate' totalMs=62.142 navMs=1.599 probes=18 cacheHits=102 cacheSize=328 startMs=35.622 startCalls=79 flightMs=0.000 flightCalls=0 +CPC_ROUTE target='ironforge_gate' totalMs=61.533 navMs=1.563 probes=18 cacheHits=102 cacheSize=328 startMs=35.222 startCalls=78 flightMs=0.000 flightCalls=0 +CPC_ROUTE target='ironforge_gate' totalMs=61.166 navMs=1.649 probes=18 cacheHits=102 cacheSize=328 startMs=34.637 startCalls=77 flightMs=0.000 flightCalls=0 +CPC_ROUTE target='portal_at_2530_target' totalMs=51.323 navMs=3.133 probes=72 cacheHits=272 cacheSize=328 startMs=1.868 startCalls=4 flightMs=0.000 flightCalls=0 +CPC_ROUTE target='portal_at_1468_source' totalMs=64.412 navMs=1.588 probes=36 cacheHits=136 cacheSize=328 startMs=35.818 startCalls=80 flightMs=0.000 flightCalls=0 +CPC_ROUTE target='portal_at_1468_source' totalMs=63.988 navMs=1.588 probes=36 cacheHits=136 cacheSize=328 startMs=35.799 startCalls=79 flightMs=0.000 flightCalls=0 +CPC_ROUTE target='portal_at_1468_source' totalMs=63.691 navMs=1.625 probes=36 cacheHits=136 cacheSize=328 startMs=35.254 startCalls=78 flightMs=0.000 flightCalls=0 +CPC_ROUTE target='portal_at_1468_source' totalMs=63.575 navMs=1.611 probes=36 cacheHits=136 cacheSize=328 startMs=34.908 startCalls=77 flightMs=0.000 flightCalls=0 +CPC_ROUTE target='taxi_601' totalMs=163.303 navMs=1.643 probes=18 cacheHits=95 cacheSize=328 startMs=2.767 startCalls=13 flightMs=11.013 flightCalls=54638 +CPC_PERF windowMs=30027 ticks=1600 players=110 travelMs=39.262 perceptionMs=364.001 transportMs=20.546 flightMs=4.953 actionsMs=5744.874 controllersMs=1729.274 slowGuid=61 slowMs=14.842 +CPC_ROUTE target='portal_at_4367_target' totalMs=311.033 navMs=12.659 probes=909 cacheHits=283 cacheSize=328 startMs=3.399 startCalls=5 flightMs=18.913 flightCalls=97240 +CPC_ROUTE target='taxi_82' totalMs=152.516 navMs=1.654 probes=20 cacheHits=97 cacheSize=328 startMs=3.048 startCalls=5 flightMs=6.591 flightCalls=52745 +CPC_ROUTE target='portal_at_2530_target' totalMs=50.594 navMs=3.131 probes=72 cacheHits=272 cacheSize=328 startMs=1.730 startCalls=4 flightMs=0.000 flightCalls=0 +CPC_ROUTE target='ironforge_gate' totalMs=65.306 navMs=1.644 probes=18 cacheHits=113 cacheSize=328 startMs=36.577 startCalls=79 flightMs=0.000 flightCalls=0 +CPC_ROUTE target='ironforge_gate' totalMs=63.013 navMs=1.591 probes=18 cacheHits=113 cacheSize=328 startMs=35.876 startCalls=78 flightMs=0.000 flightCalls=0 +CPC_ROUTE target='ironforge_gate' totalMs=62.949 navMs=1.590 probes=18 cacheHits=113 cacheSize=328 startMs=35.426 startCalls=77 flightMs=0.000 flightCalls=0 +CPC_ROUTE target='portal_at_2530_target' totalMs=54.166 navMs=3.569 probes=77 cacheHits=272 cacheSize=328 startMs=2.666 startCalls=4 flightMs=0.000 flightCalls=0 +CPC_ROUTE target='portal_at_2530_target' totalMs=52.712 navMs=3.289 probes=75 cacheHits=272 cacheSize=328 startMs=2.311 startCalls=3 flightMs=0.000 flightCalls=0 +CPC_ROUTE target='taxi_181' totalMs=69.976 navMs=0.380 probes=3 cacheHits=42 cacheSize=328 startMs=0.739 startCalls=2 flightMs=8.544 flightCalls=21833 +CPC_ROUTE target='taxi_181' totalMs=64.214 navMs=0.374 probes=3 cacheHits=42 cacheSize=328 startMs=0.740 startCalls=2 flightMs=3.049 flightCalls=21832 +CPC_ROUTE target='taxi_181' totalMs=63.857 navMs=0.378 probes=3 cacheHits=41 cacheSize=328 startMs=0.748 startCalls=2 flightMs=2.857 flightCalls=21455 +CPC_ROUTE target='taxi_181' totalMs=68.788 navMs=1.515 probes=11 cacheHits=53 cacheSize=328 startMs=0.744 startCalls=2 flightMs=3.477 flightCalls=22394 +CPC_ROUTE target='taxi_181' totalMs=71.130 navMs=1.891 probes=15 cacheHits=51 cacheSize=328 startMs=0.734 startCalls=2 flightMs=3.500 flightCalls=22770 +CPC_ROUTE target='taxi_181' totalMs=71.490 navMs=1.836 probes=15 cacheHits=53 cacheSize=328 startMs=0.761 startCalls=2 flightMs=2.966 flightCalls=22957 +CPC_ROUTE target='portal_at_4320_source' totalMs=184.971 navMs=3.013 probes=44 cacheHits=151 cacheSize=328 startMs=7.705 startCalls=20 flightMs=18.792 flightCalls=56328 +CPC_ROUTE target='portal_at_6179_source' totalMs=65.389 navMs=1.610 probes=36 cacheHits=136 cacheSize=328 startMs=36.284 startCalls=80 flightMs=0.000 flightCalls=0 +CPC_ROUTE target='portal_at_6179_source' totalMs=64.475 navMs=1.599 probes=36 cacheHits=136 cacheSize=328 startMs=35.636 startCalls=79 flightMs=0.000 flightCalls=0 +CPC_ROUTE target='portal_at_6179_source' totalMs=64.836 navMs=1.665 probes=36 cacheHits=136 cacheSize=328 startMs=35.724 startCalls=78 flightMs=0.000 flightCalls=0 +CPC_ROUTE target='portal_at_6179_source' totalMs=64.096 navMs=1.590 probes=36 cacheHits=136 cacheSize=328 startMs=35.366 startCalls=77 flightMs=0.000 flightCalls=0 +CPC_ROUTE target='portal_at_2171_target' totalMs=177.456 navMs=1.714 probes=37 cacheHits=132 cacheSize=328 startMs=1.278 startCalls=5 flightMs=17.642 flightCalls=58831 +CPC_ROUTE target='portal_at_4320_source' totalMs=178.886 navMs=2.959 probes=44 cacheHits=149 cacheSize=328 startMs=8.145 startCalls=20 flightMs=11.423 flightCalls=56327 +CPC_ROUTE target='portal_at_4320_source' totalMs=178.720 navMs=2.994 probes=44 cacheHits=149 cacheSize=328 startMs=7.762 startCalls=20 flightMs=10.449 flightCalls=56326 +CPC_ROUTE target='portal_at_2171_target' totalMs=65.183 navMs=1.666 probes=36 cacheHits=126 cacheSize=328 startMs=36.162 startCalls=80 flightMs=0.000 flightCalls=0 +CPC_ROUTE target='portal_at_2171_target' totalMs=64.201 navMs=1.581 probes=36 cacheHits=126 cacheSize=328 startMs=35.927 startCalls=79 flightMs=0.000 flightCalls=0 +CPC_ROUTE target='portal_at_2171_target' totalMs=64.274 navMs=1.583 probes=36 cacheHits=126 cacheSize=328 startMs=35.634 startCalls=78 flightMs=0.000 flightCalls=0 +CPC_ROUTE target='portal_at_2171_target' totalMs=63.786 navMs=1.595 probes=36 cacheHits=126 cacheSize=328 startMs=35.146 startCalls=77 flightMs=0.000 flightCalls=0 +CPC_ROUTE target='portal_at_4320_source' totalMs=178.781 navMs=2.928 probes=44 cacheHits=149 cacheSize=328 startMs=7.702 startCalls=20 flightMs=9.916 flightCalls=56325 +CPC_ROUTE target='taxi_128' totalMs=450.910 navMs=10.125 probes=839 cacheHits=341 cacheSize=328 startMs=2.240 startCalls=16 flightMs=178.375 flightCalls=91970 +CPC_ROUTE target='portal_at_2171_target' totalMs=173.651 navMs=1.694 probes=39 cacheHits=130 cacheSize=328 startMs=1.260 startCalls=5 flightMs=13.712 flightCalls=59080 +CPC_ROUTE target='portal_at_4320_source' totalMs=174.979 navMs=2.476 probes=45 cacheHits=152 cacheSize=328 startMs=2.925 startCalls=20 flightMs=9.488 flightCalls=56574 +CPC_ROUTE target='ironforge_gate' totalMs=150.035 navMs=1.233 probes=18 cacheHits=102 cacheSize=328 startMs=123.419 startCalls=80 flightMs=0.000 flightCalls=0 +CPC_ROUTE target='ironforge_gate' totalMs=62.107 navMs=1.268 probes=18 cacheHits=102 cacheSize=328 startMs=35.544 startCalls=79 flightMs=0.000 flightCalls=0 +CPC_ROUTE target='ironforge_gate' totalMs=61.862 navMs=1.228 probes=18 cacheHits=102 cacheSize=328 startMs=35.485 startCalls=78 flightMs=0.000 flightCalls=0 +CPC_ROUTE target='ironforge_gate' totalMs=60.548 navMs=1.226 probes=18 cacheHits=102 cacheSize=328 startMs=34.511 startCalls=77 flightMs=0.000 flightCalls=0 +CPC_ROUTE target='portal_at_2171_target' totalMs=1106.113 navMs=1.738 probes=37 cacheHits=132 cacheSize=328 startMs=1.320 startCalls=5 flightMs=944.865 flightCalls=58831 +CPC_ROUTE target='portal_at_4520_target' totalMs=260.781 navMs=7.317 probes=770 cacheHits=205 cacheSize=328 startMs=1.888 startCalls=4 flightMs=37.730 flightCalls=75718 +CPC_ROUTE target='portal_at_2171_target' totalMs=174.490 navMs=1.698 probes=39 cacheHits=130 cacheSize=328 startMs=1.411 startCalls=5 flightMs=13.989 flightCalls=59080 +CPC_ROUTE target='portal_at_4367_target' totalMs=1216.573 navMs=12.305 probes=909 cacheHits=283 cacheSize=328 startMs=1.003 startCalls=2 flightMs=923.621 flightCalls=97239 +CPC_ROUTE target='portal_at_4367_target' totalMs=350.910 navMs=12.357 probes=909 cacheHits=283 cacheSize=328 startMs=38.369 startCalls=80 flightMs=13.889 flightCalls=97239 +CPC_ROUTE target='portal_at_2171_target' totalMs=175.082 navMs=1.712 probes=37 cacheHits=132 cacheSize=328 startMs=1.512 startCalls=5 flightMs=14.713 flightCalls=58831 +CPC_ROUTE target='portal_at_2171_target' totalMs=175.784 navMs=1.685 probes=37 cacheHits=131 cacheSize=328 startMs=1.050 startCalls=4 flightMs=15.215 flightCalls=58582 +CPC_ROUTE target='portal_at_2171_target' totalMs=217.013 navMs=1.718 probes=36 cacheHits=126 cacheSize=328 startMs=36.175 startCalls=80 flightMs=15.901 flightCalls=57833 +CPC_ROUTE target='portal_at_2171_target' totalMs=216.475 navMs=1.680 probes=36 cacheHits=126 cacheSize=328 startMs=35.735 startCalls=79 flightMs=15.434 flightCalls=57833 +CPC_ROUTE target='portal_at_2171_target' totalMs=220.812 navMs=1.682 probes=36 cacheHits=126 cacheSize=328 startMs=37.566 startCalls=78 flightMs=15.122 flightCalls=57833 +CPC_ROUTE target='portal_at_2171_target' totalMs=218.269 navMs=1.669 probes=36 cacheHits=126 cacheSize=328 startMs=35.013 startCalls=77 flightMs=14.710 flightCalls=57833 +CPC_ROUTE target='taxi_128' totalMs=286.463 navMs=9.826 probes=839 cacheHits=341 cacheSize=328 startMs=2.245 startCalls=16 flightMs=12.534 flightCalls=91970 +CPC_ROUTE target='taxi_181' totalMs=233.451 navMs=8.262 probes=133 cacheHits=194 cacheSize=328 startMs=0.104 startCalls=2 flightMs=18.720 flightCalls=69951 +CPC_ROUTE target='taxi_368' totalMs=54.233 navMs=3.498 probes=72 cacheHits=274 cacheSize=328 startMs=0.375 startCalls=2 flightMs=0.000 flightCalls=0 +CPC_ROUTE target='taxi_181' totalMs=237.620 navMs=8.011 probes=133 cacheHits=194 cacheSize=328 startMs=0.107 startCalls=1 flightMs=19.508 flightCalls=69950 +CPC_PERF windowMs=30003 ticks=1124 players=110 travelMs=35.716 perceptionMs=377.288 transportMs=18.094 flightMs=2.354 actionsMs=9618.555 controllersMs=1708.803 slowGuid=102 slowMs=71.538 +CPC_ROUTE target='portal_at_2534_target' totalMs=70.407 navMs=1.720 probes=18 cacheHits=97 cacheSize=328 startMs=40.339 startCalls=80 flightMs=0.000 flightCalls=0 +CPC_ROUTE target='portal_at_2534_target' totalMs=69.118 navMs=1.701 probes=18 cacheHits=97 cacheSize=328 startMs=38.855 startCalls=79 flightMs=0.000 flightCalls=0 +CPC_ROUTE target='portal_at_2534_target' totalMs=67.958 navMs=1.742 probes=18 cacheHits=97 cacheSize=328 startMs=38.210 startCalls=78 flightMs=0.000 flightCalls=0 +CPC_ROUTE target='portal_at_2534_target' totalMs=68.533 navMs=1.632 probes=18 cacheHits=97 cacheSize=328 startMs=38.253 startCalls=77 flightMs=0.000 flightCalls=0 +CPC_ROUTE target='portal_at_101_source' totalMs=200.178 navMs=2.995 probes=45 cacheHits=154 cacheSize=328 startMs=1.985 startCalls=20 flightMs=23.068 flightCalls=58828 +CPC_ROUTE target='taxi_181' totalMs=223.906 navMs=7.449 probes=136 cacheHits=216 cacheSize=328 startMs=0.053 startCalls=1 flightMs=10.862 flightCalls=71754 +CPC_ROUTE target='taxi_601' totalMs=177.376 navMs=2.005 probes=18 cacheHits=95 cacheSize=328 startMs=2.702 startCalls=13 flightMs=17.671 flightCalls=54638 +CPC_ROUTE target='portal_at_2166_target' totalMs=69.842 navMs=1.872 probes=18 cacheHits=97 cacheSize=328 startMs=39.605 startCalls=80 flightMs=0.000 flightCalls=0 +CPC_ROUTE target='portal_at_2166_target' totalMs=68.216 navMs=1.726 probes=18 cacheHits=97 cacheSize=328 startMs=39.296 startCalls=79 flightMs=0.000 flightCalls=0 +CPC_ROUTE target='portal_at_2166_target' totalMs=66.341 navMs=1.766 probes=18 cacheHits=97 cacheSize=328 startMs=37.778 startCalls=78 flightMs=0.000 flightCalls=0 +CPC_ROUTE target='portal_at_2166_target' totalMs=67.185 navMs=1.852 probes=18 cacheHits=97 cacheSize=328 startMs=38.018 startCalls=77 flightMs=0.000 flightCalls=0 +CPC_ROUTE target='portal_at_6108_source' totalMs=72.369 navMs=1.695 probes=36 cacheHits=136 cacheSize=328 startMs=40.316 startCalls=80 flightMs=0.000 flightCalls=0 +CPC_ROUTE target='portal_at_6108_source' totalMs=70.330 navMs=1.872 probes=36 cacheHits=136 cacheSize=328 startMs=38.691 startCalls=79 flightMs=0.000 flightCalls=0 +CPC_ROUTE target='portal_at_6108_source' totalMs=70.484 navMs=1.784 probes=36 cacheHits=136 cacheSize=328 startMs=38.366 startCalls=78 flightMs=0.000 flightCalls=0 +CPC_ROUTE target='portal_at_6108_source' totalMs=69.727 navMs=1.913 probes=36 cacheHits=136 cacheSize=328 startMs=37.411 startCalls=77 flightMs=0.000 flightCalls=0 +CPC_ROUTE target='portal_at_101_source' totalMs=210.274 navMs=3.183 probes=44 cacheHits=140 cacheSize=328 startMs=8.504 startCalls=20 flightMs=25.289 flightCalls=58080 +CPC_ROUTE target='portal_at_101_source' totalMs=194.419 navMs=2.999 probes=44 cacheHits=147 cacheSize=328 startMs=8.467 startCalls=20 flightMs=16.939 flightCalls=58141 +CPC_ROUTE target='portal_at_2166_target' totalMs=62.800 navMs=1.577 probes=18 cacheHits=97 cacheSize=328 startMs=36.200 startCalls=80 flightMs=0.000 flightCalls=0 +CPC_ROUTE target='portal_at_2166_target' totalMs=62.213 navMs=1.615 probes=18 cacheHits=97 cacheSize=328 startMs=35.759 startCalls=79 flightMs=0.000 flightCalls=0 +CPC_ROUTE target='portal_at_2166_target' totalMs=62.108 navMs=1.633 probes=18 cacheHits=97 cacheSize=328 startMs=35.417 startCalls=78 flightMs=0.000 flightCalls=0 +CPC_ROUTE target='portal_at_2166_target' totalMs=61.557 navMs=1.598 probes=18 cacheHits=97 cacheSize=328 startMs=34.992 startCalls=77 flightMs=0.000 flightCalls=0 +CPC_ROUTE target='taxi_82' totalMs=163.627 navMs=1.954 probes=20 cacheHits=97 cacheSize=328 startMs=3.729 startCalls=5 flightMs=10.516 flightCalls=52745 +CPC_ROUTE target='portal_at_2530_target' totalMs=58.219 navMs=3.834 probes=77 cacheHits=272 cacheSize=328 startMs=2.850 startCalls=4 flightMs=0.000 flightCalls=0 +CPC_ROUTE target='portal_at_2530_target' totalMs=55.652 navMs=3.513 probes=75 cacheHits=272 cacheSize=328 startMs=1.960 startCalls=3 flightMs=0.000 flightCalls=0 +CPC_ROUTE target='portal_at_78_source' totalMs=271.676 navMs=10.177 probes=824 cacheHits=241 cacheSize=328 startMs=3.099 startCalls=20 flightMs=19.772 flightCalls=84551 +CPC_ROUTE target='portal_at_2530_target' totalMs=50.926 navMs=3.488 probes=77 cacheHits=272 cacheSize=328 startMs=1.760 startCalls=3 flightMs=0.000 flightCalls=0 +CPC_ROUTE target='portal_at_78_source' totalMs=276.942 navMs=10.578 probes=825 cacheHits=240 cacheSize=328 startMs=7.828 startCalls=20 flightMs=18.750 flightCalls=84550 +CPC_ROUTE target='portal_at_78_source' totalMs=281.313 navMs=10.707 probes=825 cacheHits=240 cacheSize=328 startMs=8.393 startCalls=20 flightMs=19.325 flightCalls=84549 +CPC_ROUTE target='portal_at_78_source' totalMs=275.244 navMs=10.099 probes=824 cacheHits=241 cacheSize=328 startMs=2.914 startCalls=20 flightMs=18.122 flightCalls=84549 +CPC_ROUTE target='taxi_181' totalMs=62.754 navMs=0.363 probes=3 cacheHits=42 cacheSize=328 startMs=0.592 startCalls=2 flightMs=2.539 flightCalls=21833 +CPC_ROUTE target='taxi_181' totalMs=63.682 navMs=0.363 probes=3 cacheHits=42 cacheSize=328 startMs=0.606 startCalls=2 flightMs=2.866 flightCalls=21832 +CPC_ROUTE target='taxi_181' totalMs=64.291 navMs=0.396 probes=3 cacheHits=41 cacheSize=328 startMs=0.812 startCalls=2 flightMs=3.189 flightCalls=21455 +CPC_ROUTE target='taxi_181' totalMs=67.608 navMs=1.499 probes=11 cacheHits=53 cacheSize=328 startMs=0.609 startCalls=2 flightMs=2.871 flightCalls=22394 +CPC_ROUTE target='portal_at_4367_target' totalMs=320.338 navMs=13.243 probes=910 cacheHits=284 cacheSize=328 startMs=2.891 startCalls=4 flightMs=16.410 flightCalls=97735 +CPC_ROUTE target='portal_at_78_source' totalMs=305.210 navMs=11.622 probes=824 cacheHits=241 cacheSize=328 startMs=3.631 startCalls=20 flightMs=29.436 flightCalls=84548 +CPC_ROUTE target='portal_at_78_source' totalMs=307.897 navMs=10.873 probes=824 cacheHits=241 cacheSize=328 startMs=3.573 startCalls=20 flightMs=30.696 flightCalls=84547 +CPC_ROUTE target='taxi_181' totalMs=75.624 navMs=2.003 probes=15 cacheHits=51 cacheSize=328 startMs=0.721 startCalls=2 flightMs=5.455 flightCalls=22770 +CPC_ROUTE target='taxi_181' totalMs=71.471 navMs=1.878 probes=15 cacheHits=53 cacheSize=328 startMs=0.607 startCalls=2 flightMs=3.120 flightCalls=22957 +CPC_ROUTE target='portal_at_4520_target' totalMs=263.794 navMs=7.913 probes=770 cacheHits=200 cacheSize=328 startMs=2.278 startCalls=4 flightMs=32.191 flightCalls=75469 +CPC_ROUTE target='taxi_601' totalMs=180.605 navMs=1.718 probes=18 cacheHits=95 cacheSize=328 startMs=2.787 startCalls=13 flightMs=20.688 flightCalls=54638 +CPC_ROUTE target='taxi_368' totalMs=54.312 navMs=3.355 probes=72 cacheHits=274 cacheSize=328 startMs=0.466 startCalls=2 flightMs=0.000 flightCalls=0 +CPC_ROUTE target='portal_at_902_source' totalMs=65.035 navMs=1.563 probes=18 cacheHits=97 cacheSize=328 startMs=38.585 startCalls=80 flightMs=0.000 flightCalls=0 +CPC_ROUTE target='portal_at_902_source' totalMs=62.092 navMs=1.564 probes=18 cacheHits=97 cacheSize=328 startMs=35.897 startCalls=79 flightMs=0.000 flightCalls=0 +CPC_ROUTE target='portal_at_902_source' totalMs=61.616 navMs=1.617 probes=18 cacheHits=97 cacheSize=328 startMs=35.515 startCalls=78 flightMs=0.000 flightCalls=0 +CPC_ROUTE target='portal_at_902_source' totalMs=60.952 navMs=1.557 probes=18 cacheHits=97 cacheSize=328 startMs=34.938 startCalls=77 flightMs=0.000 flightCalls=0 +CPC_ROUTE target='taxi_36' totalMs=62.812 navMs=1.546 probes=18 cacheHits=99 cacheSize=328 startMs=35.655 startCalls=80 flightMs=0.000 flightCalls=0 +CPC_ROUTE target='taxi_36' totalMs=62.155 navMs=1.550 probes=18 cacheHits=99 cacheSize=328 startMs=35.397 startCalls=79 flightMs=0.000 flightCalls=0 +CPC_ROUTE target='taxi_36' totalMs=61.859 navMs=1.594 probes=18 cacheHits=99 cacheSize=328 startMs=34.873 startCalls=78 flightMs=0.000 flightCalls=0 +CPC_ROUTE target='taxi_36' totalMs=61.735 navMs=1.689 probes=18 cacheHits=99 cacheSize=328 startMs=34.671 startCalls=77 flightMs=0.000 flightCalls=0 +CPC_ROUTE target='portal_at_2530_target' totalMs=52.294 navMs=3.388 probes=77 cacheHits=272 cacheSize=328 startMs=2.646 startCalls=4 flightMs=0.000 flightCalls=0 +CPC_ROUTE target='taxi_36' totalMs=164.754 navMs=1.661 probes=19 cacheHits=103 cacheSize=328 startMs=1.320 startCalls=5 flightMs=12.486 flightCalls=55823 +CPC_ROUTE target='portal_at_1466_source' totalMs=213.435 navMs=7.757 probes=117 cacheHits=190 cacheSize=328 startMs=2.939 startCalls=20 flightMs=20.390 flightCalls=66259 +CPC_ROUTE target='taxi_82' totalMs=149.869 navMs=1.673 probes=20 cacheHits=99 cacheSize=328 startMs=1.025 startCalls=2 flightMs=5.685 flightCalls=52992 +CPC_ROUTE target='taxi_82' totalMs=203.652 navMs=1.796 probes=18 cacheHits=97 cacheSize=328 startMs=38.346 startCalls=80 flightMs=13.235 flightCalls=52246 +CPC_ROUTE target='portal_at_1466_source' totalMs=222.882 navMs=8.266 probes=119 cacheHits=189 cacheSize=328 startMs=8.558 startCalls=20 flightMs=20.472 flightCalls=66259 +CPC_ROUTE target='taxi_36' totalMs=163.618 navMs=1.668 probes=21 cacheHits=101 cacheSize=328 startMs=1.337 startCalls=5 flightMs=12.046 flightCalls=56072 +CPC_ROUTE target='portal_at_1466_source' totalMs=217.534 navMs=8.193 probes=119 cacheHits=190 cacheSize=328 startMs=7.667 startCalls=20 flightMs=16.466 flightCalls=66445 +CPC_ROUTE target='portal_at_1466_source' totalMs=231.056 navMs=8.601 probes=118 cacheHits=190 cacheSize=328 startMs=2.953 startCalls=20 flightMs=24.577 flightCalls=66258 +CPC_ROUTE target='portal_at_1466_source' totalMs=236.173 navMs=8.689 probes=118 cacheHits=191 cacheSize=328 startMs=3.484 startCalls=20 flightMs=24.983 flightCalls=66444 +CPC_ROUTE target='portal_at_1466_source' totalMs=237.060 navMs=8.499 probes=120 cacheHits=191 cacheSize=328 startMs=3.205 startCalls=20 flightMs=24.301 flightCalls=66642 +CPC_PERF windowMs=30025 ticks=1163 players=110 travelMs=35.822 perceptionMs=427.297 transportMs=18.235 flightMs=2.627 actionsMs=8787.500 controllersMs=1723.486 slowGuid=61 slowMs=16.274 +CPC_ROUTE target='taxi_36' totalMs=176.430 navMs=1.713 probes=19 cacheHits=103 cacheSize=328 startMs=1.429 startCalls=5 flightMs=17.723 flightCalls=55823 +CPC_ROUTE target='taxi_36' totalMs=177.276 navMs=1.825 probes=21 cacheHits=101 cacheSize=328 startMs=1.324 startCalls=5 flightMs=18.555 flightCalls=56072 +CPC_ROUTE target='portal_at_2171_target' totalMs=70.512 navMs=1.816 probes=36 cacheHits=126 cacheSize=328 startMs=39.175 startCalls=80 flightMs=0.000 flightCalls=0 +CPC_ROUTE target='portal_at_2171_target' totalMs=70.918 navMs=1.803 probes=36 cacheHits=126 cacheSize=328 startMs=39.740 startCalls=79 flightMs=0.000 flightCalls=0 +CPC_ROUTE target='portal_at_2171_target' totalMs=69.391 navMs=1.748 probes=36 cacheHits=126 cacheSize=328 startMs=38.094 startCalls=78 flightMs=0.000 flightCalls=0 +CPC_ROUTE target='portal_at_2171_target' totalMs=69.754 navMs=1.827 probes=36 cacheHits=126 cacheSize=328 startMs=39.246 startCalls=77 flightMs=0.000 flightCalls=0 +CPC_ROUTE target='taxi_36' totalMs=180.880 navMs=1.730 probes=19 cacheHits=103 cacheSize=328 startMs=1.545 startCalls=5 flightMs=21.338 flightCalls=55823 +CPC_ROUTE target='taxi_36' totalMs=178.010 navMs=1.706 probes=19 cacheHits=102 cacheSize=328 startMs=1.132 startCalls=4 flightMs=19.101 flightCalls=55574 +CPC_ROUTE target='taxi_36' totalMs=223.673 navMs=1.876 probes=18 cacheHits=99 cacheSize=328 startMs=40.286 startCalls=80 flightMs=19.569 flightCalls=55074 +CPC_ROUTE target='taxi_36' totalMs=221.946 navMs=1.768 probes=18 cacheHits=99 cacheSize=328 startMs=38.127 startCalls=79 flightMs=19.464 flightCalls=55074 +CPC_ROUTE target='taxi_36' totalMs=224.075 navMs=1.704 probes=18 cacheHits=99 cacheSize=328 startMs=38.883 startCalls=78 flightMs=18.442 flightCalls=55074 +CPC_ROUTE target='taxi_36' totalMs=225.034 navMs=1.906 probes=18 cacheHits=99 cacheSize=328 startMs=38.003 startCalls=77 flightMs=18.590 flightCalls=55074 +CPC_ROUTE target='portal_at_2226_target' totalMs=52.385 navMs=3.553 probes=74 cacheHits=272 cacheSize=328 startMs=2.201 startCalls=3 flightMs=0.000 flightCalls=0 +CPC_ROUTE target='taxi_128' totalMs=445.535 navMs=9.749 probes=839 cacheHits=341 cacheSize=328 startMs=2.163 startCalls=16 flightMs=170.645 flightCalls=91970 +CPC_ROUTE target='taxi_601' totalMs=162.956 navMs=1.644 probes=18 cacheHits=95 cacheSize=328 startMs=3.006 startCalls=13 flightMs=10.808 flightCalls=54638 +CPC_ROUTE target='portal_at_2226_target' totalMs=51.902 navMs=3.537 probes=77 cacheHits=272 cacheSize=328 startMs=1.712 startCalls=3 flightMs=0.000 flightCalls=0 +CPC_ROUTE target='portal_at_2534_target' totalMs=64.623 navMs=1.587 probes=18 cacheHits=97 cacheSize=328 startMs=36.755 startCalls=80 flightMs=0.000 flightCalls=0 +CPC_ROUTE target='portal_at_2534_target' totalMs=64.572 navMs=1.614 probes=18 cacheHits=97 cacheSize=328 startMs=36.152 startCalls=79 flightMs=0.000 flightCalls=0 +CPC_ROUTE target='portal_at_2534_target' totalMs=62.550 navMs=1.602 probes=18 cacheHits=97 cacheSize=328 startMs=35.446 startCalls=78 flightMs=0.000 flightCalls=0 +CPC_ROUTE target='portal_at_2534_target' totalMs=62.500 navMs=1.592 probes=18 cacheHits=97 cacheSize=328 startMs=35.013 startCalls=77 flightMs=0.000 flightCalls=0 +CPC_ROUTE target='taxi_7' totalMs=62.875 navMs=1.605 probes=18 cacheHits=94 cacheSize=328 startMs=36.075 startCalls=80 flightMs=0.000 flightCalls=0 +CPC_ROUTE target='taxi_7' totalMs=61.912 navMs=1.577 probes=18 cacheHits=94 cacheSize=328 startMs=35.868 startCalls=79 flightMs=0.000 flightCalls=0 +CPC_ROUTE target='taxi_7' totalMs=62.063 navMs=1.605 probes=18 cacheHits=94 cacheSize=328 startMs=35.859 startCalls=78 flightMs=0.000 flightCalls=0 +CPC_ROUTE target='taxi_7' totalMs=61.247 navMs=1.630 probes=18 cacheHits=94 cacheSize=328 startMs=35.048 startCalls=77 flightMs=0.000 flightCalls=0 +CPC_ROUTE target='portal_at_101_source' totalMs=64.442 navMs=1.562 probes=27 cacheHits=118 cacheSize=328 startMs=35.820 startCalls=80 flightMs=0.000 flightCalls=0 +CPC_ROUTE target='portal_at_101_source' totalMs=63.925 navMs=1.617 probes=27 cacheHits=118 cacheSize=328 startMs=35.699 startCalls=79 flightMs=0.000 flightCalls=0 +CPC_ROUTE target='portal_at_101_source' totalMs=62.870 navMs=1.575 probes=27 cacheHits=118 cacheSize=328 startMs=35.070 startCalls=78 flightMs=0.000 flightCalls=0 +CPC_ROUTE target='portal_at_101_source' totalMs=62.948 navMs=1.590 probes=27 cacheHits=118 cacheSize=328 startMs=34.759 startCalls=77 flightMs=0.000 flightCalls=0 +CPC_ROUTE target='portal_at_2226_target' totalMs=51.574 navMs=3.187 probes=72 cacheHits=272 cacheSize=328 startMs=1.759 startCalls=4 flightMs=0.000 flightCalls=0 +CPC_ROUTE target='portal_at_1468_source' totalMs=1282.089 navMs=7.986 probes=96 cacheHits=189 cacheSize=328 startMs=3.315 startCalls=20 flightMs=1082.774 flightCalls=65129 +CPC_ROUTE target='taxi_181' totalMs=67.394 navMs=0.367 probes=3 cacheHits=40 cacheSize=328 startMs=0.593 startCalls=2 flightMs=5.003 flightCalls=21645 +CPC_ROUTE target='taxi_82' totalMs=172.518 navMs=1.794 probes=22 cacheHits=101 cacheSize=328 startMs=3.152 startCalls=4 flightMs=13.989 flightCalls=53739 +CPC_ROUTE target='taxi_181' totalMs=68.317 navMs=0.446 probes=3 cacheHits=40 cacheSize=328 startMs=0.625 startCalls=2 flightMs=5.176 flightCalls=21644 +CPC_ROUTE target='portal_at_4520_target' totalMs=258.986 navMs=7.929 probes=770 cacheHits=200 cacheSize=328 startMs=2.178 startCalls=4 flightMs=25.512 flightCalls=75469 +CPC_ROUTE target='taxi_181' totalMs=68.437 navMs=0.471 probes=3 cacheHits=39 cacheSize=328 startMs=0.687 startCalls=2 flightMs=5.450 flightCalls=21267 +CPC_ROUTE target='portal_at_2226_target' totalMs=56.352 navMs=3.718 probes=72 cacheHits=272 cacheSize=328 startMs=1.737 startCalls=4 flightMs=0.000 flightCalls=0 +CPC_ROUTE target='portal_at_1468_source' totalMs=232.014 navMs=8.735 probes=98 cacheHits=189 cacheSize=328 startMs=8.514 startCalls=20 flightMs=26.017 flightCalls=65318 +CPC_ROUTE target='taxi_181' totalMs=72.781 navMs=1.660 probes=11 cacheHits=51 cacheSize=328 startMs=0.655 startCalls=2 flightMs=5.219 flightCalls=22206 +CPC_ROUTE target='taxi_181' totalMs=80.389 navMs=2.312 probes=15 cacheHits=51 cacheSize=328 startMs=0.868 startCalls=2 flightMs=7.716 flightCalls=22770 +CPC_ROUTE target='taxi_181' totalMs=79.249 navMs=2.033 probes=15 cacheHits=51 cacheSize=328 startMs=0.882 startCalls=2 flightMs=6.121 flightCalls=22769 +CPC_ROUTE target='portal_at_1468_source' totalMs=235.690 navMs=8.731 probes=98 cacheHits=189 cacheSize=328 startMs=8.394 startCalls=20 flightMs=26.250 flightCalls=65317 +CPC_ROUTE target='portal_at_1468_source' totalMs=230.758 navMs=7.880 probes=97 cacheHits=190 cacheSize=328 startMs=3.167 startCalls=20 flightMs=26.285 flightCalls=65317 +CPC_ROUTE target='portal_at_1468_source' totalMs=232.732 navMs=7.918 probes=97 cacheHits=190 cacheSize=328 startMs=3.259 startCalls=20 flightMs=26.451 flightCalls=65316 +CPC_ROUTE target='portal_at_1468_source' totalMs=234.959 navMs=8.575 probes=98 cacheHits=190 cacheSize=328 startMs=3.304 startCalls=20 flightMs=25.585 flightCalls=65502 diff --git a/doc/clientless/checkpoints/2026-10-29-performance/route-bottleneck.log b/doc/clientless/checkpoints/2026-10-29-performance/route-bottleneck.log new file mode 100644 index 0000000000..efc35b71e6 --- /dev/null +++ b/doc/clientless/checkpoints/2026-10-29-performance/route-bottleneck.log @@ -0,0 +1,173 @@ +CPC_ROUTE target='taxi_456' totalMs=2214.858 navMs=1.314 probes=41 cacheHits=2 cacheSize=34 startMs=0.645 startCalls=2 flightMs=2148.827 flightCalls=21457 +CPC_ROUTE target='taxi_456' totalMs=355.109 navMs=0.365 probes=7 cacheHits=36 cacheSize=34 startMs=0.637 startCalls=2 flightMs=292.767 flightCalls=21456 +CPC_ROUTE target='taxi_456' totalMs=352.997 navMs=0.417 probes=7 cacheHits=35 cacheSize=34 startMs=0.741 startCalls=2 flightMs=291.048 flightCalls=21079 +CPC_ROUTE target='taxi_456' totalMs=365.696 navMs=1.689 probes=27 cacheHits=35 cacheSize=46 startMs=0.742 startCalls=2 flightMs=300.568 flightCalls=22018 +CPC_ROUTE target='taxi_456' totalMs=386.600 navMs=2.325 probes=19 cacheHits=47 cacheSize=67 startMs=0.608 startCalls=2 flightMs=315.423 flightCalls=22582 +CPC_ROUTE target='taxi_456' totalMs=374.697 navMs=1.899 probes=19 cacheHits=47 cacheSize=67 startMs=0.678 startCalls=2 flightMs=306.477 flightCalls=22581 +CPC_ROUTE target='goldshire_center' totalMs=3867.203 navMs=4.198 probes=66 cacheHits=64 cacheSize=94 startMs=53.354 startCalls=7 flightMs=3672.038 flightCalls=52524 +CPC_ROUTE target='portal_at_2530_target' totalMs=348.080 navMs=0.394 probes=7 cacheHits=37 cacheSize=94 startMs=0.000 startCalls=0 flightMs=288.229 flightCalls=20893 +CPC_ROUTE target='taxi_181' totalMs=366.703 navMs=0.487 probes=11 cacheHits=41 cacheSize=98 startMs=0.604 startCalls=2 flightMs=302.451 flightCalls=22329 +CPC_ROUTE target='taxi_181' totalMs=1006.625 navMs=8.239 probes=154 cacheHits=121 cacheSize=139 startMs=0.734 startCalls=2 flightMs=830.220 flightCalls=64572 +CPC_ROUTE target='taxi_181' totalMs=1008.236 navMs=7.298 probes=113 cacheHits=162 cacheSize=141 startMs=0.620 startCalls=2 flightMs=835.049 flightCalls=64571 +CPC_ROUTE target='taxi_181' totalMs=438.898 navMs=3.017 probes=41 cacheHits=72 cacheSize=141 startMs=0.658 startCalls=2 flightMs=355.826 flightCalls=26996 +CPC_ROUTE target='taxi_181' totalMs=1008.990 navMs=7.100 probes=113 cacheHits=162 cacheSize=141 startMs=0.674 startCalls=2 flightMs=835.934 flightCalls=64381 +CPC_ROUTE target='taxi_181' totalMs=1009.359 navMs=7.109 probes=114 cacheHits=162 cacheSize=142 startMs=0.737 startCalls=2 flightMs=831.265 flightCalls=64756 +CPC_ROUTE target='taxi_36' totalMs=66.146 navMs=1.299 probes=9 cacheHits=5 cacheSize=143 startMs=9.039 startCalls=21 flightMs=44.930 flightCalls=3271 +CPC_ROUTE target='goldshire_center' totalMs=828.349 navMs=2.907 probes=37 cacheHits=90 cacheSize=143 startMs=6.016 startCalls=16 flightMs=685.080 flightCalls=52277 +CPC_ROUTE target='portal_at_5379_source' totalMs=3269.795 navMs=26.716 probes=2206 cacheHits=444 cacheSize=227 startMs=1.050 startCalls=3 flightMs=2684.359 flightCalls=205764 +CPC_ROUTE target='taxi_388' totalMs=351.810 navMs=0.378 probes=7 cacheHits=36 cacheSize=227 startMs=0.623 startCalls=2 flightMs=291.863 flightCalls=21457 +CPC_PERF windowMs=33650 ticks=2177 players=7 travelMs=2.189 perceptionMs=4.992 transportMs=0.288 flightMs=2.549 actionsMs=17838.969 controllersMs=341.025 slowGuid=26 slowMs=125.890 +CPC_ROUTE target='taxi_388' totalMs=352.889 navMs=0.386 probes=7 cacheHits=36 cacheSize=227 startMs=0.613 startCalls=2 flightMs=290.815 flightCalls=21456 +CPC_ROUTE target='taxi_388' totalMs=347.788 navMs=0.385 probes=7 cacheHits=35 cacheSize=227 startMs=0.659 startCalls=2 flightMs=285.779 flightCalls=21079 +CPC_ROUTE target='portal_at_5379_source' totalMs=3315.129 navMs=27.010 probes=2122 cacheHits=528 cacheSize=227 startMs=0.490 startCalls=1 flightMs=2730.767 flightCalls=205764 +CPC_ROUTE target='taxi_388' totalMs=365.517 navMs=1.488 probes=15 cacheHits=47 cacheSize=227 startMs=0.644 startCalls=2 flightMs=299.178 flightCalls=22017 +CPC_ROUTE target='taxi_388' totalMs=374.289 navMs=1.884 probes=19 cacheHits=47 cacheSize=227 startMs=0.750 startCalls=2 flightMs=304.493 flightCalls=22581 +CPC_ROUTE target='goldshire_center' totalMs=825.604 navMs=3.228 probes=39 cacheHits=91 cacheSize=227 startMs=2.671 startCalls=7 flightMs=687.870 flightCalls=52524 +CPC_ROUTE target='taxi_388' totalMs=339.390 navMs=0.364 probes=7 cacheHits=36 cacheSize=227 startMs=0.602 startCalls=2 flightMs=280.987 flightCalls=20329 +CPC_ROUTE target='taxi_388' totalMs=339.961 navMs=0.386 probes=7 cacheHits=36 cacheSize=227 startMs=0.700 startCalls=2 flightMs=278.712 flightCalls=20328 +CPC_ROUTE target='taxi_388' totalMs=334.244 navMs=0.388 probes=7 cacheHits=35 cacheSize=227 startMs=0.755 startCalls=2 flightMs=275.034 flightCalls=19951 +CPC_ROUTE target='taxi_388' totalMs=335.390 navMs=0.717 probes=8 cacheHits=38 cacheSize=227 startMs=0.598 startCalls=2 flightMs=275.492 flightCalls=19950 +CPC_ROUTE target='taxi_388' totalMs=341.568 navMs=1.002 probes=9 cacheHits=41 cacheSize=227 startMs=0.612 startCalls=2 flightMs=280.585 flightCalls=20325 +CPC_ROUTE target='taxi_388' totalMs=355.505 navMs=1.856 probes=18 cacheHits=47 cacheSize=227 startMs=0.633 startCalls=2 flightMs=288.773 flightCalls=21452 +CPC_ROUTE target='taxi_388' totalMs=437.472 navMs=2.665 probes=40 cacheHits=70 cacheSize=227 startMs=0.608 startCalls=2 flightMs=359.287 flightCalls=26999 +CPC_ROUTE target='taxi_388' totalMs=438.356 navMs=2.920 probes=41 cacheHits=73 cacheSize=227 startMs=0.599 startCalls=2 flightMs=356.948 flightCalls=27186 +CPC_ROUTE target='taxi_388' totalMs=390.013 navMs=1.529 probes=26 cacheHits=63 cacheSize=227 startMs=0.638 startCalls=2 flightMs=318.018 flightCalls=23837 +CPC_ROUTE target='taxi_388' totalMs=434.769 navMs=2.957 probes=41 cacheHits=72 cacheSize=227 startMs=0.673 startCalls=2 flightMs=352.308 flightCalls=26808 +CPC_ROUTE target='taxi_388' totalMs=451.490 navMs=3.334 probes=45 cacheHits=73 cacheSize=227 startMs=0.624 startCalls=2 flightMs=366.583 flightCalls=27748 +CPC_ROUTE target='taxi_128' totalMs=1796.072 navMs=12.401 probes=920 cacheHits=416 cacheSize=227 startMs=0.930 startCalls=15 flightMs=1492.389 flightCalls=102683 +CPC_ROUTE target='taxi_388' totalMs=395.661 navMs=1.845 probes=21 cacheHits=58 cacheSize=227 startMs=0.642 startCalls=2 flightMs=322.357 flightCalls=24018 +CPC_ROUTE target='goldshire_center' totalMs=928.417 navMs=3.438 probes=37 cacheHits=90 cacheSize=227 startMs=6.154 startCalls=16 flightMs=767.927 flightCalls=52277 +CPC_ROUTE target='taxi_388' totalMs=528.031 navMs=3.336 probes=65 cacheHits=95 cacheSize=227 startMs=0.862 startCalls=2 flightMs=433.350 flightCalls=29804 +CPC_ROUTE target='taxi_388' totalMs=1019.907 navMs=7.859 probes=113 cacheHits=162 cacheSize=227 startMs=0.633 startCalls=2 flightMs=840.089 flightCalls=64384 +CPC_ROUTE target='taxi_388' totalMs=1014.268 navMs=7.338 probes=113 cacheHits=162 cacheSize=227 startMs=0.629 startCalls=2 flightMs=841.819 flightCalls=64383 +CPC_ROUTE target='taxi_388' totalMs=1016.920 navMs=7.526 probes=113 cacheHits=162 cacheSize=227 startMs=0.608 startCalls=2 flightMs=841.615 flightCalls=64382 +CPC_ROUTE target='portal_at_6581_source' totalMs=992.963 navMs=3.750 probes=95 cacheHits=152 cacheSize=227 startMs=1.056 startCalls=3 flightMs=830.411 flightCalls=62709 +CPC_ROUTE target='taxi_388' totalMs=1013.281 navMs=7.446 probes=113 cacheHits=163 cacheSize=227 startMs=0.609 startCalls=2 flightMs=834.491 flightCalls=64757 +CPC_ROUTE target='taxi_388' totalMs=3557.661 navMs=3.699 probes=69 cacheHits=97 cacheSize=227 startMs=0.603 startCalls=2 flightMs=3457.871 flightCalls=30929 +CPC_ROUTE target='portal_at_4367_target' totalMs=1700.270 navMs=11.733 probes=937 cacheHits=264 cacheSize=229 startMs=2.024 startCalls=3 flightMs=1408.310 flightCalls=97847 +CPC_PERF windowMs=30330 ticks=257 players=16 travelMs=17.117 perceptionMs=18.270 transportMs=0.126 flightMs=0.181 actionsMs=23911.821 controllersMs=882.219 slowGuid=42 slowMs=372.233 +CPC_ROUTE target='goldshire_center' totalMs=950.699 navMs=3.396 probes=39 cacheHits=91 cacheSize=229 startMs=2.700 startCalls=7 flightMs=808.811 flightCalls=52524 +CPC_ROUTE target='goldshire_center' totalMs=839.236 navMs=2.791 probes=36 cacheHits=91 cacheSize=229 startMs=2.339 startCalls=6 flightMs=695.923 flightCalls=52275 +CPC_ROUTE target='goldshire_center' totalMs=839.685 navMs=2.321 probes=34 cacheHits=90 cacheSize=229 startMs=2.078 startCalls=5 flightMs=697.528 flightCalls=52026 +CPC_ROUTE target='goldshire_center' totalMs=837.566 navMs=2.388 probes=28 cacheHits=90 cacheSize=229 startMs=1.734 startCalls=4 flightMs=694.310 flightCalls=51775 +CPC_ROUTE target='goldshire_center' totalMs=861.368 navMs=1.763 probes=23 cacheHits=89 cacheSize=229 startMs=5.048 startCalls=12 flightMs=707.412 flightCalls=51088 +CPC_ROUTE target='taxi_181' totalMs=440.682 navMs=1.914 probes=27 cacheHits=64 cacheSize=229 startMs=0.648 startCalls=2 flightMs=362.693 flightCalls=24975 +CPC_ROUTE target='portal_at_6581_source' totalMs=999.918 navMs=3.855 probes=102 cacheHits=154 cacheSize=229 startMs=0.507 startCalls=1 flightMs=835.102 flightCalls=62900 +CPC_ROUTE target='taxi_181' totalMs=433.767 navMs=1.960 probes=27 cacheHits=64 cacheSize=229 startMs=0.593 startCalls=2 flightMs=353.015 flightCalls=24974 +CPC_ROUTE target='taxi_36' totalMs=907.594 navMs=2.816 probes=52 cacheHits=130 cacheSize=231 startMs=3.063 startCalls=20 flightMs=753.096 flightCalls=56337 +CPC_ROUTE target='portal_at_6581_source' totalMs=989.096 navMs=3.637 probes=102 cacheHits=154 cacheSize=231 startMs=0.500 startCalls=1 flightMs=827.862 flightCalls=62900 +CPC_ROUTE target='taxi_181' totalMs=412.041 navMs=2.131 probes=32 cacheHits=66 cacheSize=231 startMs=0.609 startCalls=2 flightMs=336.491 flightCalls=24973 +CPC_ROUTE target='taxi_181' totalMs=382.943 navMs=1.650 probes=15 cacheHits=51 cacheSize=231 startMs=0.606 startCalls=2 flightMs=312.595 flightCalls=22642 +CPC_ROUTE target='taxi_128' totalMs=1900.826 navMs=12.987 probes=920 cacheHits=360 cacheSize=231 startMs=2.891 startCalls=17 flightMs=1567.846 flightCalls=102683 +CPC_ROUTE target='taxi_181' totalMs=425.473 navMs=2.544 probes=36 cacheHits=66 cacheSize=231 startMs=0.617 startCalls=2 flightMs=347.065 flightCalls=25536 +CPC_ROUTE target='taxi_36' totalMs=915.483 navMs=2.613 probes=50 cacheHits=135 cacheSize=231 startMs=1.911 startCalls=20 flightMs=759.527 flightCalls=56834 +CPC_ROUTE target='taxi_181' totalMs=404.432 navMs=2.032 probes=21 cacheHits=58 cacheSize=231 startMs=0.636 startCalls=2 flightMs=329.880 flightCalls=24394 +CPC_ROUTE target='portal_at_4367_target' totalMs=1687.337 navMs=12.226 probes=937 cacheHits=264 cacheSize=231 startMs=1.009 startCalls=2 flightMs=1399.451 flightCalls=97846 +CPC_ROUTE target='taxi_36' totalMs=932.673 navMs=2.733 probes=50 cacheHits=138 cacheSize=231 startMs=1.969 startCalls=20 flightMs=773.682 flightCalls=58327 +CPC_ROUTE target='portal_at_4367_target' totalMs=1808.298 navMs=12.645 probes=937 cacheHits=264 cacheSize=231 startMs=38.847 startCalls=80 flightMs=1464.229 flightCalls=97846 +CPC_ROUTE target='taxi_395' totalMs=64.844 navMs=1.553 probes=41 cacheHits=131 cacheSize=231 startMs=36.555 startCalls=80 flightMs=0.000 flightCalls=0 +CPC_ROUTE target='taxi_395' totalMs=63.426 navMs=1.357 probes=41 cacheHits=131 cacheSize=231 startMs=35.536 startCalls=79 flightMs=0.000 flightCalls=0 +CPC_ROUTE target='taxi_395' totalMs=62.412 navMs=1.256 probes=41 cacheHits=131 cacheSize=231 startMs=34.870 startCalls=78 flightMs=0.000 flightCalls=0 +CPC_ROUTE target='taxi_395' totalMs=62.916 navMs=1.568 probes=41 cacheHits=131 cacheSize=231 startMs=34.692 startCalls=77 flightMs=0.000 flightCalls=0 +CPC_ROUTE target='taxi_388' totalMs=446.987 navMs=3.015 probes=41 cacheHits=73 cacheSize=231 startMs=0.600 startCalls=2 flightMs=368.376 flightCalls=27187 +CPC_ROUTE target='taxi_388' totalMs=441.798 navMs=2.985 probes=41 cacheHits=73 cacheSize=231 startMs=0.619 startCalls=2 flightMs=360.639 flightCalls=27186 +CPC_ROUTE target='taxi_128' totalMs=1795.166 navMs=12.147 probes=920 cacheHits=444 cacheSize=231 startMs=1.067 startCalls=15 flightMs=1499.299 flightCalls=102683 +CPC_ROUTE target='taxi_36' totalMs=911.093 navMs=3.230 probes=49 cacheHits=132 cacheSize=231 startMs=7.806 startCalls=20 flightMs=748.218 flightCalls=56585 +CPC_ROUTE target='taxi_388' totalMs=436.665 navMs=2.947 probes=41 cacheHits=72 cacheSize=231 startMs=0.620 startCalls=2 flightMs=355.027 flightCalls=26809 +CPC_ROUTE target='taxi_36' totalMs=904.089 navMs=3.115 probes=49 cacheHits=132 cacheSize=231 startMs=7.624 startCalls=20 flightMs=740.913 flightCalls=56584 +CPC_ROUTE target='taxi_388' totalMs=440.630 navMs=2.973 probes=41 cacheHits=72 cacheSize=231 startMs=0.599 startCalls=2 flightMs=360.589 flightCalls=26808 +CPC_ROUTE target='taxi_388' totalMs=457.026 navMs=3.648 probes=45 cacheHits=73 cacheSize=231 startMs=0.676 startCalls=2 flightMs=371.320 flightCalls=27748 +CPC_ROUTE target='portal_at_702_target' totalMs=59.898 navMs=0.846 probes=9 cacheHits=7 cacheSize=231 startMs=2.903 startCalls=20 flightMs=45.397 flightCalls=3271 +CPC_ROUTE target='taxi_388' totalMs=411.799 navMs=1.898 probes=21 cacheHits=58 cacheSize=231 startMs=0.599 startCalls=2 flightMs=334.826 flightCalls=24206 +CPC_ROUTE target='portal_at_2530_target' totalMs=53.145 navMs=3.224 probes=87 cacheHits=262 cacheSize=231 startMs=2.141 startCalls=3 flightMs=0.000 flightCalls=0 +CPC_ROUTE target='portal_at_6354_source' totalMs=1493.130 navMs=9.602 probes=798 cacheHits=211 cacheSize=231 startMs=1.029 startCalls=3 flightMs=1255.668 flightCalls=79836 +CPC_ROUTE target='taxi_388' totalMs=419.594 navMs=1.915 probes=29 cacheHits=64 cacheSize=231 startMs=0.619 startCalls=2 flightMs=345.306 flightCalls=24987 +CPC_ROUTE target='taxi_388' totalMs=445.307 navMs=2.031 probes=29 cacheHits=64 cacheSize=231 startMs=0.751 startCalls=2 flightMs=364.946 flightCalls=24986 +CPC_PERF windowMs=30377 ticks=20 players=23 travelMs=17.975 perceptionMs=25.773 transportMs=0.048 flightMs=0.058 actionsMs=26443.439 controllersMs=506.857 slowGuid=44 slowMs=64.554 +CPC_ROUTE target='taxi_388' totalMs=429.092 navMs=2.395 probes=34 cacheHits=66 cacheSize=231 startMs=0.618 startCalls=2 flightMs=350.595 flightCalls=24985 +CPC_ROUTE target='portal_at_322_target' totalMs=67.201 navMs=1.579 probes=41 cacheHits=131 cacheSize=231 startMs=37.613 startCalls=80 flightMs=0.000 flightCalls=0 +CPC_ROUTE target='portal_at_322_target' totalMs=66.358 navMs=1.528 probes=41 cacheHits=131 cacheSize=231 startMs=37.372 startCalls=79 flightMs=0.000 flightCalls=0 +CPC_ROUTE target='portal_at_322_target' totalMs=65.916 navMs=1.638 probes=41 cacheHits=131 cacheSize=231 startMs=36.682 startCalls=78 flightMs=0.000 flightCalls=0 +CPC_ROUTE target='portal_at_322_target' totalMs=64.859 navMs=1.457 probes=41 cacheHits=131 cacheSize=231 startMs=35.947 startCalls=77 flightMs=0.000 flightCalls=0 +CPC_ROUTE target='taxi_388' totalMs=387.530 navMs=1.539 probes=15 cacheHits=56 cacheSize=231 startMs=0.624 startCalls=2 flightMs=317.524 flightCalls=22702 +CPC_ROUTE target='taxi_388' totalMs=438.113 navMs=2.783 probes=38 cacheHits=66 cacheSize=231 startMs=0.642 startCalls=2 flightMs=357.206 flightCalls=25548 +CPC_ROUTE target='portal_at_4367_target' totalMs=1779.669 navMs=12.998 probes=938 cacheHits=265 cacheSize=231 startMs=2.988 startCalls=4 flightMs=1479.668 flightCalls=98342 +CPC_ROUTE target='taxi_388' totalMs=446.276 navMs=2.627 probes=38 cacheHits=68 cacheSize=231 startMs=0.603 startCalls=2 flightMs=363.392 flightCalls=26299 +CPC_ROUTE target='ironforge_gate' totalMs=65.538 navMs=1.668 probes=23 cacheHits=94 cacheSize=231 startMs=37.570 startCalls=81 flightMs=0.000 flightCalls=0 +CPC_ROUTE target='ironforge_gate' totalMs=63.948 navMs=1.618 probes=23 cacheHits=94 cacheSize=231 startMs=36.394 startCalls=80 flightMs=0.000 flightCalls=0 +CPC_ROUTE target='ironforge_gate' totalMs=64.270 navMs=1.566 probes=23 cacheHits=94 cacheSize=231 startMs=36.757 startCalls=79 flightMs=0.000 flightCalls=0 +CPC_ROUTE target='taxi_128' totalMs=1849.705 navMs=13.206 probes=920 cacheHits=360 cacheSize=231 startMs=2.819 startCalls=18 flightMs=1533.219 flightCalls=102683 +CPC_ROUTE target='taxi_55' totalMs=391.835 navMs=1.064 probes=15 cacheHits=53 cacheSize=231 startMs=0.692 startCalls=2 flightMs=322.400 flightCalls=22897 +CPC_ROUTE target='taxi_55' totalMs=389.674 navMs=1.063 probes=15 cacheHits=53 cacheSize=231 startMs=0.748 startCalls=2 flightMs=319.684 flightCalls=22896 +CPC_ROUTE target='portal_at_322_target' totalMs=66.016 navMs=1.614 probes=41 cacheHits=131 cacheSize=231 startMs=36.805 startCalls=81 flightMs=0.000 flightCalls=0 +CPC_ROUTE target='portal_at_322_target' totalMs=66.346 navMs=1.565 probes=41 cacheHits=131 cacheSize=231 startMs=37.279 startCalls=80 flightMs=0.000 flightCalls=0 +CPC_ROUTE target='portal_at_322_target' totalMs=64.695 navMs=1.580 probes=41 cacheHits=131 cacheSize=231 startMs=35.934 startCalls=79 flightMs=0.000 flightCalls=0 +CPC_ROUTE target='taxi_55' totalMs=390.493 navMs=1.579 probes=21 cacheHits=58 cacheSize=231 startMs=0.638 startCalls=2 flightMs=320.259 flightCalls=23083 +CPC_ROUTE target='taxi_55' totalMs=419.426 navMs=1.660 probes=16 cacheHits=56 cacheSize=231 startMs=0.603 startCalls=2 flightMs=343.306 flightCalls=23078 +CPC_ROUTE target='taxi_55' totalMs=405.946 navMs=1.920 probes=25 cacheHits=58 cacheSize=231 startMs=0.612 startCalls=2 flightMs=331.939 flightCalls=23646 +CPC_ROUTE target='taxi_55' totalMs=405.629 navMs=1.905 probes=25 cacheHits=60 cacheSize=231 startMs=0.601 startCalls=2 flightMs=332.701 flightCalls=24397 +CPC_ROUTE target='taxi_36' totalMs=63.988 navMs=1.584 probes=23 cacheHits=94 cacheSize=231 startMs=36.716 startCalls=80 flightMs=0.000 flightCalls=0 +CPC_ROUTE target='taxi_36' totalMs=62.725 navMs=1.465 probes=23 cacheHits=94 cacheSize=231 startMs=35.816 startCalls=79 flightMs=0.000 flightCalls=0 +CPC_ROUTE target='taxi_36' totalMs=62.194 navMs=1.435 probes=23 cacheHits=94 cacheSize=231 startMs=35.432 startCalls=78 flightMs=0.000 flightCalls=0 +CPC_ROUTE target='taxi_36' totalMs=63.981 navMs=1.700 probes=23 cacheHits=94 cacheSize=231 startMs=35.064 startCalls=77 flightMs=0.000 flightCalls=0 +CPC_ROUTE target='portal_at_2530_target' totalMs=52.710 navMs=3.088 probes=87 cacheHits=262 cacheSize=231 startMs=3.037 startCalls=4 flightMs=0.000 flightCalls=0 +CPC_ROUTE target='taxi_181' totalMs=1050.896 navMs=7.390 probes=130 cacheHits=171 cacheSize=231 startMs=0.622 startCalls=2 flightMs=874.481 flightCalls=67018 +CPC_ROUTE target='taxi_181' totalMs=1060.096 navMs=7.858 probes=130 cacheHits=171 cacheSize=231 startMs=0.622 startCalls=2 flightMs=876.981 flightCalls=67017 +CPC_ROUTE target='taxi_181' totalMs=1040.842 navMs=7.682 probes=117 cacheHits=162 cacheSize=231 startMs=0.610 startCalls=2 flightMs=860.158 flightCalls=64576 +CPC_ROUTE target='taxi_181' totalMs=1358.371 navMs=9.771 probes=130 cacheHits=171 cacheSize=231 startMs=0.708 startCalls=2 flightMs=1076.454 flightCalls=67015 +CPC_ROUTE target='taxi_128' totalMs=2873.536 navMs=18.123 probes=920 cacheHits=444 cacheSize=231 startMs=1.445 startCalls=15 flightMs=2382.915 flightCalls=102683 +CPC_ROUTE target='taxi_181' totalMs=1456.702 navMs=10.368 probes=127 cacheHits=170 cacheSize=231 startMs=1.009 startCalls=2 flightMs=1172.415 flightCalls=67011 +CPC_ROUTE target='taxi_181' totalMs=1158.368 navMs=8.871 probes=130 cacheHits=172 cacheSize=231 startMs=0.917 startCalls=2 flightMs=947.491 flightCalls=67201 +CPC_ROUTE target='taxi_601' totalMs=6813.609 navMs=2.425 probes=23 cacheHits=90 cacheSize=231 startMs=3.301 startCalls=13 flightMs=6606.420 flightCalls=54638 +CPC_PERF windowMs=32728 ticks=246 players=32 travelMs=34.747 perceptionMs=48.057 transportMs=0.090 flightMs=0.103 actionsMs=26365.464 controllersMs=731.614 slowGuid=50 slowMs=74.783 +CPC_ROUTE target='ironforge_gate' totalMs=72.716 navMs=1.732 probes=23 cacheHits=108 cacheSize=231 startMs=39.323 startCalls=80 flightMs=0.000 flightCalls=0 +CPC_ROUTE target='ironforge_gate' totalMs=70.580 navMs=1.773 probes=23 cacheHits=108 cacheSize=231 startMs=39.258 startCalls=79 flightMs=0.000 flightCalls=0 +CPC_ROUTE target='ironforge_gate' totalMs=70.403 navMs=1.851 probes=23 cacheHits=108 cacheSize=231 startMs=39.527 startCalls=78 flightMs=0.000 flightCalls=0 +CPC_ROUTE target='ironforge_gate' totalMs=71.045 navMs=1.689 probes=23 cacheHits=108 cacheSize=231 startMs=40.298 startCalls=77 flightMs=0.000 flightCalls=0 +CPC_ROUTE target='taxi_170' totalMs=1950.251 navMs=13.290 probes=1023 cacheHits=308 cacheSize=231 startMs=3.118 startCalls=16 flightMs=1603.719 flightCalls=106366 +CPC_ROUTE target='taxi_601' totalMs=958.686 navMs=2.278 probes=23 cacheHits=90 cacheSize=231 startMs=3.555 startCalls=12 flightMs=769.965 flightCalls=54638 +CPC_ROUTE target='taxi_181' totalMs=389.998 navMs=0.581 probes=7 cacheHits=36 cacheSize=231 startMs=0.694 startCalls=2 flightMs=313.944 flightCalls=21645 +CPC_ROUTE target='portal_at_4319_source' totalMs=1610.486 navMs=12.391 probes=868 cacheHits=236 cacheSize=231 startMs=1.097 startCalls=3 flightMs=1315.143 flightCalls=84888 +CPC_ROUTE target='taxi_181' totalMs=389.309 navMs=0.420 probes=7 cacheHits=36 cacheSize=231 startMs=0.644 startCalls=2 flightMs=312.377 flightCalls=21644 +CPC_ROUTE target='taxi_181' totalMs=378.313 navMs=0.416 probes=7 cacheHits=35 cacheSize=231 startMs=0.610 startCalls=2 flightMs=302.771 flightCalls=21267 +CPC_ROUTE target='taxi_181' totalMs=396.136 navMs=2.085 probes=15 cacheHits=47 cacheSize=231 startMs=0.761 startCalls=2 flightMs=316.087 flightCalls=22206 +CPC_ROUTE target='taxi_181' totalMs=404.777 navMs=2.247 probes=19 cacheHits=47 cacheSize=231 startMs=0.642 startCalls=2 flightMs=324.399 flightCalls=22770 +CPC_ROUTE target='taxi_181' totalMs=410.924 navMs=2.537 probes=19 cacheHits=47 cacheSize=231 startMs=0.640 startCalls=2 flightMs=329.153 flightCalls=22769 +CPC_ROUTE target='taxi_128' totalMs=1923.169 navMs=13.909 probes=920 cacheHits=454 cacheSize=231 startMs=4.204 startCalls=18 flightMs=1572.602 flightCalls=102683 +CPC_ROUTE target='taxi_23' totalMs=57.387 navMs=3.922 probes=87 cacheHits=262 cacheSize=231 startMs=1.902 startCalls=3 flightMs=0.000 flightCalls=0 +CPC_ROUTE target='taxi_181' totalMs=546.985 navMs=0.731 probes=7 cacheHits=36 cacheSize=231 startMs=2.066 startCalls=2 flightMs=434.947 flightCalls=21645 +CPC_ROUTE target='taxi_181' totalMs=469.655 navMs=0.609 probes=7 cacheHits=36 cacheSize=235 startMs=0.912 startCalls=2 flightMs=379.540 flightCalls=21644 +CPC_ROUTE target='ironforge_gate' totalMs=70.336 navMs=1.826 probes=19 cacheHits=101 cacheSize=235 startMs=40.145 startCalls=80 flightMs=0.000 flightCalls=0 +CPC_ROUTE target='ironforge_gate' totalMs=70.823 navMs=1.806 probes=19 cacheHits=101 cacheSize=235 startMs=40.828 startCalls=79 flightMs=0.000 flightCalls=0 +CPC_ROUTE target='ironforge_gate' totalMs=80.744 navMs=1.833 probes=19 cacheHits=101 cacheSize=235 startMs=48.158 startCalls=78 flightMs=0.000 flightCalls=0 +CPC_ROUTE target='ironforge_gate' totalMs=68.158 navMs=1.755 probes=19 cacheHits=101 cacheSize=235 startMs=37.602 startCalls=77 flightMs=0.000 flightCalls=0 +CPC_ROUTE target='taxi_181' totalMs=314.097 navMs=0.417 probes=3 cacheHits=39 cacheSize=235 startMs=0.597 startCalls=2 flightMs=262.588 flightCalls=21267 +CPC_ROUTE target='taxi_181' totalMs=329.218 navMs=1.804 probes=11 cacheHits=51 cacheSize=235 startMs=0.621 startCalls=2 flightMs=273.416 flightCalls=22206 +CPC_ROUTE target='taxi_23' totalMs=54.151 navMs=3.330 probes=74 cacheHits=270 cacheSize=235 startMs=1.711 startCalls=4 flightMs=0.000 flightCalls=0 +CPC_ROUTE target='taxi_181' totalMs=338.334 navMs=2.017 probes=15 cacheHits=51 cacheSize=235 startMs=0.618 startCalls=2 flightMs=280.241 flightCalls=22770 +CPC_ROUTE target='taxi_181' totalMs=338.514 navMs=2.154 probes=15 cacheHits=51 cacheSize=235 startMs=0.627 startCalls=2 flightMs=277.723 flightCalls=22769 +CPC_ROUTE target='taxi_310' totalMs=3372.660 navMs=38.337 probes=2021 cacheHits=637 cacheSize=326 startMs=9.153 startCalls=21 flightMs=2743.776 flightCalls=208560 +CPC_ROUTE target='taxi_181' totalMs=351.639 navMs=0.385 probes=3 cacheHits=42 cacheSize=326 startMs=0.707 startCalls=2 flightMs=294.906 flightCalls=21833 +CPC_ROUTE target='taxi_181' totalMs=352.368 navMs=0.385 probes=3 cacheHits=42 cacheSize=326 startMs=0.679 startCalls=2 flightMs=294.131 flightCalls=21832 +CPC_ROUTE target='portal_at_4151_source' totalMs=71.722 navMs=1.868 probes=36 cacheHits=136 cacheSize=326 startMs=39.923 startCalls=80 flightMs=0.000 flightCalls=0 +CPC_ROUTE target='portal_at_4151_source' totalMs=72.085 navMs=1.823 probes=36 cacheHits=136 cacheSize=326 startMs=40.510 startCalls=79 flightMs=0.000 flightCalls=0 +CPC_ROUTE target='portal_at_4151_source' totalMs=71.600 navMs=1.776 probes=36 cacheHits=136 cacheSize=326 startMs=40.024 startCalls=78 flightMs=0.000 flightCalls=0 +CPC_ROUTE target='portal_at_4151_source' totalMs=65.400 navMs=1.656 probes=36 cacheHits=136 cacheSize=326 startMs=36.448 startCalls=77 flightMs=0.000 flightCalls=0 +CPC_ROUTE target='taxi_181' totalMs=313.389 navMs=0.400 probes=3 cacheHits=41 cacheSize=326 startMs=0.620 startCalls=2 flightMs=262.560 flightCalls=21455 +CPC_ROUTE target='taxi_627' totalMs=66.384 navMs=1.796 probes=18 cacheHits=97 cacheSize=326 startMs=37.792 startCalls=78 flightMs=0.000 flightCalls=0 +CPC_ROUTE target='taxi_181' totalMs=334.102 navMs=1.674 probes=11 cacheHits=53 cacheSize=326 startMs=0.809 startCalls=2 flightMs=277.205 flightCalls=22394 +CPC_ROUTE target='taxi_181' totalMs=337.939 navMs=1.989 probes=15 cacheHits=51 cacheSize=326 startMs=0.613 startCalls=2 flightMs=280.709 flightCalls=22770 +CPC_ROUTE target='taxi_181' totalMs=349.130 navMs=2.100 probes=15 cacheHits=53 cacheSize=326 startMs=0.605 startCalls=2 flightMs=285.402 flightCalls=22957 +CPC_ROUTE target='portal_at_7155_source' totalMs=880.780 navMs=3.946 probes=66 cacheHits=148 cacheSize=326 startMs=1.011 startCalls=3 flightMs=734.175 flightCalls=58385 +CPC_ROUTE target='taxi_181' totalMs=340.819 navMs=0.395 probes=3 cacheHits=42 cacheSize=326 startMs=0.755 startCalls=2 flightMs=282.663 flightCalls=21833 +CPC_ROUTE target='taxi_181' totalMs=341.864 navMs=0.546 probes=3 cacheHits=42 cacheSize=326 startMs=0.611 startCalls=2 flightMs=282.281 flightCalls=21832 +CPC_ROUTE target='ironforge_gate' totalMs=73.997 navMs=2.026 probes=18 cacheHits=102 cacheSize=326 startMs=41.525 startCalls=80 flightMs=0.000 flightCalls=0 +CPC_ROUTE target='ironforge_gate' totalMs=72.531 navMs=1.779 probes=18 cacheHits=102 cacheSize=326 startMs=40.446 startCalls=79 flightMs=0.000 flightCalls=0 +CPC_ROUTE target='ironforge_gate' totalMs=69.623 navMs=1.763 probes=18 cacheHits=102 cacheSize=326 startMs=39.845 startCalls=78 flightMs=0.000 flightCalls=0 +CPC_ROUTE target='ironforge_gate' totalMs=71.164 navMs=2.174 probes=18 cacheHits=102 cacheSize=326 startMs=37.811 startCalls=77 flightMs=0.000 flightCalls=0 +CPC_ROUTE target='portal_at_4367_target' totalMs=1517.644 navMs=13.297 probes=909 cacheHits=283 cacheSize=326 startMs=3.320 startCalls=5 flightMs=1240.960 flightCalls=97240 +CPC_ROUTE target='taxi_181' totalMs=344.380 navMs=0.415 probes=3 cacheHits=41 cacheSize=326 startMs=0.756 startCalls=2 flightMs=284.913 flightCalls=21455 +CPC_PERF windowMs=34228 ticks=30 players=41 travelMs=34.890 perceptionMs=74.125 transportMs=0.111 flightMs=0.098 actionsMs=23621.628 controllersMs=3348.595 slowGuid=26 slowMs=1434.777 +CPC_ROUTE target='taxi_181' totalMs=355.210 navMs=2.714 probes=11 cacheHits=53 cacheSize=326 startMs=0.604 startCalls=2 flightMs=290.524 flightCalls=22394 +CPC_ROUTE target='taxi_181' totalMs=335.283 navMs=1.963 probes=15 cacheHits=51 cacheSize=326 startMs=0.597 startCalls=2 flightMs=276.075 flightCalls=22770 +CPC_ROUTE target='taxi_181' totalMs=346.000 navMs=2.158 probes=15 cacheHits=53 cacheSize=326 startMs=0.837 startCalls=2 flightMs=285.424 flightCalls=22957 diff --git a/doc/clientless/checkpoints/2026-10-29-performance/tests.log b/doc/clientless/checkpoints/2026-10-29-performance/tests.log new file mode 100644 index 0000000000..fce6eb1bbc --- /dev/null +++ b/doc/clientless/checkpoints/2026-10-29-performance/tests.log @@ -0,0 +1,189 @@ +TEST tests/clientless/test_adventure_area_node.py +Production area destination excludes outside and unreachable graph nodes. +TEST tests/clientless/test_adventure_async_database.py +Async creation DB contracts: prepared connection masks, native security column names, no future waits, successful-save cache publication and world-update-only loader hook passed +TEST tests/clientless/test_adventure_behavior_policy.py +24-hour generation, shared intents with changing destinations, broad location classification and repetition penalties passed. +TEST tests/clientless/test_adventure_class_rotation.py +Production class rotations: persistent PvE solo pools, native rank aura IDs, combo/holy power/proc/execute/resource gates, Eclipse and healer coverage passed +TEST tests/clientless/test_adventure_combat_exercise.py +Production exercise: stance/charge/melee/casts, native validation delegation, bounded lifetime, invalid targets and unchanged native phases passed. +TEST tests/clientless/test_adventure_combat_preparation.py +Production talent learner: native tiers/dependencies/class gates, all 26 legal level60 points, no capstone bypass and repeat safety passed. +TEST tests/clientless/test_adventure_combat_reactions.py +Production combat focus, damage ordering, risk filters, corpse radius/delay, flight/group override, blocked escape, danger avoidance and malus-free persisted fallback passed. +TEST tests/clientless/test_adventure_day_persistence.py +Production day planner: all 24 TINYINT slots restored across logins, incomplete/old-day replacement, full transaction and curiosity/level weighting passed +TEST tests/clientless/test_adventure_economy.py +Economy: native service roles, friendly/phase/LOS/bounds gates, no bot service targets and coherent performative cues passed. +TEST tests/clientless/test_adventure_economy_catalog.py +Economy catalog: eleven actions, three faction-bound service anchors, native flag gates and idempotent preservation passed. +TEST tests/clientless/test_adventure_encounter.py +Production encounters: bot-only exclusion, mixed mode, extension hook, wait/revive, wipe fallback/reentry/gather/return, living-player priority, kick exit and offline cleanup passed +TEST tests/clientless/test_adventure_equipment_maintenance.py +Production gear maintenance: same birth/retention gate rejects wrong armor/stats, malformed effects, level, temporary, reputation, map, holiday, native-use and slot mismatches passed +TEST tests/clientless/test_adventure_exploration.py +Exploration: visible native props only, no chests/traps/furniture, phase/map/LOS/range gates and contextual preparation passed. +TEST tests/clientless/test_adventure_exploration_catalog.py +Exploration catalog: ten actions, shared scenes, default wander replacement, explicit category eligibility and preserved edits passed. +TEST tests/clientless/test_adventure_generator_equipment.py +Production equipment affinity: mana-only/direct/triggered procs, caster/physical stat effects and bounded cycles passed +TEST tests/clientless/test_adventure_generator_ownership.py +Production generator commands: batch bounds, dedicated ownership, persistent/online/removing retirement protection and retired login refusal passed +TEST tests/clientless/test_adventure_generator_policy.py +Production generator: 6144 cultural names and 61440 real Lua profiles, profession coherence, personality ranges, escaping and equipment/armor gates passed +TEST tests/clientless/test_adventure_graph_handover.py +Production graph handover waits for ship/taxi/flight/teleport and stops at safe road/node boundaries. +TEST tests/clientless/test_adventure_ground_avoidance.py +Production ground avoidance: harmful/friendly, phase169, height, reachable safe endpoint, rejected paths and reuse without cast/movement thrashing passed +TEST tests/clientless/test_adventure_group_plan.py +Production group plans: leader meeting/goal/deadline inheritance, untouched personal day, manual preparation and instance-state guards passed. +TEST tests/clientless/test_adventure_group_travel.py +Production group travel: distinct slots, phase169 cliff safety, width, mounts, flight/kick landing, taxi grace, portal queue, bounded separation and recorded-road validation passed +TEST tests/clientless/test_adventure_groups.py +Production recruitment: capitals, different goals, native inviter leadership, joining existing groups, capacity/reservations, raid conversion, planned transition and local contact passed. +TEST tests/clientless/test_adventure_instance.py +Production instance lifecycle: time budget, true entry, denied/shared entry, native rear exit, restart, expired copy, late arrival, HP and return replay passed. +TEST tests/clientless/test_adventure_instance_autoresume.py +Deferred instance script: missing schema/visits stays idle; no login work on startup; saved visitors resume with Adventure controllers and native accounts. +TEST tests/clientless/test_adventure_instance_begin.py +Production entrance travel uses the existing location planner, requires mapping and retains normal social/area travel. +TEST tests/clientless/test_adventure_instance_catalog.py +Instance migrations: outside entrances only, native graph preserved, disabled/missing transitions excluded, edits and saved visits preserved. +TEST tests/clientless/test_adventure_instance_loader.py +Production instance recovery: no startup DB/login work, asynchronous delayed/paced manual recovery, generated-bot quota ownership, absent-schema compatibility and existing preparation ticks passed +TEST tests/clientless/test_adventure_instance_persistence.py +Production instance persistence: phases, original deadline, instance ID, transactional HP marker, failed commit and intent release passed. +TEST tests/clientless/test_adventure_intent_state.py +Production intent arrival, failed/partial travel, deadlines and safe schedule handover passed. +TEST tests/clientless/test_adventure_interactions.py +/tmp/tmpdotxz1_t/test.cpp: In member function ‘void AdventureInteractionMgr::Leave(Player*, Record&, const char*)’: +/tmp/tmpdotxz1_t/test.cpp:143:69: warning: unused parameter ‘reason’ [-Wunused-parameter] + 143 | void AdventureInteractionMgr::Leave(Player* p,Record& r,char const* reason) + | ~~~~~~~~~~~~^~~~~~ +production interaction manager: invitations, factions, reservations, follow, AFK and deferred departure passed +TEST tests/clientless/test_adventure_leisure.py +Leisure: substantial curved MMAP routes, mirrored return, interrupted return, rejected corridors, actual arrival, companion follow, riding, flight and fishing passed. +TEST tests/clientless/test_adventure_leisure_catalog.py +Leisure migration: native city anchor, shared scene references, pet-walk mapping and preservation of edited weights passed. +TEST tests/clientless/test_adventure_location_import.py +Locations: actual Debug SMALLINT reads retain continent IDs; explicit location eligibility and edited weight overrides passed. +TEST tests/clientless/test_adventure_manager_inventory.py +Production inventory maintenance: only full backpacks, native stack trimming, level-appropriate supplies, tools, leash, fishing pole, containers and untouched equipped slots passed +TEST tests/clientless/test_adventure_micro_reset.py +Micro cleanup ends physical local movement, preserves transit and does not stand up dead players. +TEST tests/clientless/test_adventure_native_lfg.py +Native LFG: ID26, localized standard channel, zone eligibility, join verification and population throttle passed +TEST tests/clientless/test_adventure_population.py +Production population: four independent faction/persistence targets, fair selection, pending reservations, retries, existing inventory, once-per-boot rotation and connected/persistent protection passed +TEST tests/clientless/test_adventure_profession_catalog.py +Profession catalog: native workstation roles, no workshop/resource inference, idempotence and preserved edits passed. +TEST tests/clientless/test_adventure_profession_profile.py +Production profession parser, real Lua profiles, primary restrictions and secondary permissions passed. +TEST tests/clientless/test_adventure_profession_sightline.py +Native workstation working-surface sightline and blocked geometry rejection passed. +TEST tests/clientless/test_adventure_profession_targets.py +Production profession targets: native locks, phase/spawn/loot, stations, corpses, history and trainers passed. +TEST tests/clientless/test_adventure_profile_selection.py +Production profile selection: real Lua ID dictionaries, exact 9:1 companion weighting, zero/negative/infinite/NaN/oversized weights rejected and legacy companion fallback passed +TEST tests/clientless/test_adventure_pvp.py +Production PvP selector: level boundaries, radius, factions, native legality, navigation, intent, personality and resources passed. +TEST tests/clientless/test_adventure_repair_catalog.py +Repair action catalog: proper native service requirement, economy-only intent and preserved administrator weights passed. +TEST tests/clientless/test_adventure_rest.py +Recovery: authored places, bounds, arrival, occupancy, full rest duration, failure, reload and interruption passed. +TEST tests/clientless/test_adventure_rest_fire.py +Rest fire: shared scene mapping, additive migration, preserved weights and authored creation-place policy passed. +TEST tests/clientless/test_adventure_rest_space.py +Sleep footprint: furniture, body extent, overhead collision, ledges, stairs, missing heights, rotation and scale passed. +TEST tests/clientless/test_adventure_rest_targets.py +Native sleep targets: chair exclusion, phase, despawn, floor search, indoors, water, storey, occupancy, bed/surface preference passed. +TEST tests/clientless/test_adventure_retry_policy.py +Travel failures: exponential/jittered retry spacing, strict 30s bound and overflow safety passed +TEST tests/clientless/test_adventure_rotation.py +/tmp/tmpkjywp8re/test.cpp: In instantiation of ‘std::string Trinity::StringFormat(const char*, A ...) [with A = {}; std::string = std::__cxx11::basic_string]’: +/tmp/tmpkjywp8re/test.cpp:22:126: required from ‘void Tx::PAppend(const char*, A ...) [with A = {}]’ + 22 | struct Tx{std::vectorsql;templatevoid PAppend(char const*f,A...a){sql.push_back(Trinity::StringFormat(f,a...));}}; + | ~~~~~~~~~~~~~~~~~~~~~^~~~~~~~ +/tmp/tmpkjywp8re/test.cpp:51:75: required from here + 51 | auto barrier=CharacterDatabase.BeginTransaction();barrier->PAppend("DO 0"); + | ~~~~~~~~~~~~~~~~^~~~~~~~ +/tmp/tmpkjywp8re/test.cpp:11:113: warning: format not a string literal and no format arguments [-Wformat-security] + 11 | namespace Trinity{templatestd::string StringFormat(char const* f,A...args){char b[4096];std::snprintf(b,sizeof(b),f,args...);return b;}} + | ~~~~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~ +Production rotation: connected/native-online protection, ownership refusal, native delete failure, world cleanup failure and journal-retained retry before successful profile/plan/state removal passed +TEST tests/clientless/test_adventure_social.py +Production social flow: approach before talk, coherent greeting/talk/listen/farewell, lost targets, native chair confirmation and real fire confirmation passed. +TEST tests/clientless/test_adventure_social_fire.py +Shared fire: native detection/provisioning, buildings/city/parent/zone restrictions, existing fires and outdoor/water checks passed. +TEST tests/clientless/test_adventure_supplies.py +Production supplies: exact10% boundary, bandage-before-food, native lockout fallback, recovery completion, combat potions, mana-only drinking and daily/post-combat top-ups passed. +TEST tests/clientless/test_adventure_supply_catalog.py +Supply migrations: all five roles, level60 netherweave tier, editable/disabled catalog preservation and persistent daily stamp passed. +TEST tests/clientless/test_adventure_supply_items.py +Production native-item delegation: usable bandage tiers, native cooldown/lockout gates, real inventory consumption, self targeting and level60 secondary caps passed. +TEST tests/clientless/test_adventure_talent_templates.py +Canonical Cata PvE builds: 4.3.4 class/tree/rank/prerequisite gates, all 1..41 point budgets, solo selection coverage passed +TEST tests/clientless/test_adventure_training_catalog.py +Training catalog: real dummy requirement, leisure-only intent, bounded duration and preserved administrator weights passed. +TEST tests/clientless/test_adventure_walk.py +Production travel gait reaches native spline, survives repaths, prevents mounting and preserves legacy running defaults. +TEST tests/clientless/test_adventure_world_presence.py +Production world presence: persistent PvP, Worgen intent/arrival decisions, travel deferral, native Running Wild, human horse and aura cleanup passed +TEST tests/clientless/test_clientless_character_factory.py +Production native factory: character/profile atomicity, failed preparation/commit, cache publication order, account/security/name/cap gates passed +TEST tests/clientless/test_clientless_companion.py +Native companion: provisioning, delayed summon, ownership, real item target, leash confirmation/timeout, reuse and cleanup passed. +TEST tests/clientless/test_clientless_damage_notifications.py +Production damage handoff: 16 concurrent map-worker writers, ordered GUID-only events, clientless-only/positive damage gates and post-health notification passed. +TEST tests/clientless/test_clientless_near_teleport_observer.py +Production near-teleport observer packet includes actual mover GUID and destination; clientless timestamp refreshed, regular client timestamp preserved. +TEST tests/clientless/test_clientless_skill_preparation.py +Production offline skill preparation: native tier caps, no downgrading, rank/level/race/tier gates and no modification of online players passed +TEST tests/clientless/test_clientless_social_notifications.py +Production social/helper queues: concurrent GUID-only delivery, bounded queues, human/self/zero-damage gates, pet attribution and lethal-cleanup ordering passed +TEST tests/clientless/test_default_phase.py +Native PhaseShift: implicit/default 169 visibility, genuine custom-phase separation and reference updates passed. +TEST tests/clientless/test_empty_spell_effect.py +Native empty spell effects: poisoned-memory scaling initialization, Lock and Load cleared slot, active effect values and all-empty spell values passed +TEST tests/clientless/test_flight_area_query.py +Native terrain query map/phase, area/zone ordering and cache passed. +TEST tests/clientless/test_flight_handoff.py +Flight handoff before spline end, terminal landing, return corners, collision stop and actual distance passed. +TEST tests/clientless/test_flight_policy.py +Flight native eligibility, license/profile/zone policy, RP costs and fail-safe return decisions passed. +TEST tests/clientless/test_follow_spline_velocity.py +Native follow: actual player/creature spline velocity, client gait and independent follower speeds passed. +TEST tests/clientless/test_free_flight.py +Flight geometry passed: takeoff/landing, smoothed peak, ceiling, lateral/climbing avoidance, missing terrain and blocked corridors. +TEST tests/clientless/test_free_flight_planner.py +ground planner regressions and dynamic flight/node/location/virtual-exit integration passed +TEST tests/clientless/test_graph_probe_cache.py +Graph probe cache: exact directed/map/instance keys, successful geometry only, invalidation and bounded storage passed +TEST tests/clientless/test_ground_exit_continuation.py +optional exits: repeated probes, retained route, delayed success, forward resume, flight fallback passed +TEST tests/clientless/test_group_flight_follow.py +Moving-target flight: bounded phased corridors, obstacle hold, ceiling rejection, aerial formation hold, safe landing and return fallback passed +TEST tests/clientless/test_item_equip_cooldown.py +Native item equip cooldown: zero/sentinel/missing spells safely skipped, valid use cooldown preserved and longer/disabled cooldowns untouched passed +TEST tests/clientless/test_local_spline_segments.py +Production local spline clipping passes native path-length validation, tiny remainders and 1000 sampled paths. +TEST tests/clientless/test_lua_reload.py +Lua reload regression tests passed (production reload/bindings, real Lua 5.4). +TEST tests/clientless/test_player_pure_max_skill.py +Native pure skill cap: learned skills return stored cap; missing/deleted/zero-rank skills return0. +TEST tests/clientless/test_portal_transfer.py +Portal near/far ACK and denied same-map transfer regressions passed. +TEST tests/clientless/test_portal_use.py +Interactive portal native click, near/far/global ACK, denial, timeout and target checks passed. +Native portal destination discovery and unsupported/ambiguous spell rejection passed. +Production clientless ACK completion sequence increments only after completed near/far transfers. +TEST tests/clientless/test_shared_group_route.py +Production generic shared routes: same edges/taxi/free flight, independent execution state, final local goal, late catch-up, permissions and stale-goal rejection passed. +TEST tests/clientless/test_taxi_import_disabled.py +Taxi importer preserves disabled rows and their reasons. +TEST tests/clientless/test_transport.py +Transport dock, frame, recorder, cross-map copy and ride regressions passed +TEST tests/clientless/test_transport_grid_loading.py +Core transport grid initialization regressions passed (no CPC dependency). +TOTAL 86 FAILED [] diff --git a/doc/clientless/performance.md b/doc/clientless/performance.md new file mode 100644 index 0000000000..fa7bbcd5f8 --- /dev/null +++ b/doc/clientless/performance.md @@ -0,0 +1,74 @@ +# Clientless performance, 2026-10-29 + +The test installation has twelve logical CPUs. The initial process sample showed +95.1% of one CPU in the world thread and about9.9% in the single map worker. +The existing build is Debug (`-g`, no optimization); these are not production +Release/RelWithDebInfo capacity numbers. Production infrastructure was untouched. + +## Observed bottleneck + +External perf/ptrace sampling was denied by OS permissions. No kernel settings +were changed. CPC_PERF now reports steady-clock time in travel, perception, +transport, flight, actions and controllers every30 seconds. Times include blocking +and are not CPU percentages. The controller bucket includes login preparation. +CPC_ROUTE reports searches taking at least50ms, separating future MMAP probes, +current-player entry probes and dynamic flight-cost evaluations. + +An initial31.923-second window with16 managed characters spent24.381 seconds +inside actions. Perception used27ms; controllers1.032 seconds. A later20-character +window spent27.872 seconds in actions over31.537 seconds. A traced warm search +made21,456 flight-cost calls and spent292.767ms there out of355.109ms total. +Static future MMAP probes used only0.365ms and current entry probes0.637ms. + +## Changes + +* Flight planning snapshots profile eligibility, minimum-distance configuration + and preferred zones once per synchronous planning scope. Scope exit clears it. + Actual departure checks still use live eligibility. Map licenses, area mount + rules and dynamic restrictions remain evaluated. Area keys now use exact binary + coordinates instead of repeatedly formatting and parsing floating-point text. +* Successful static ground probes between graph points are reused across plans. + Cache keys include map, instance, direction and exact endpoints. Storage is + bounded to32,768 proofs. No Player paths, negative/transient results, Detour + objects or tile pointers are retained. Graph reload clears the cache. Reload + the graph after replacing MMAP data. Execution still validates actual movement. +* Fixed graph neighborhoods are reused, bounded to4,096 entries. A cube shortlist + preserves the original exact distance, route-blocklist and path checks. +* Current-player entry probes are shared only within the same immutable planning + request, including its two search tiers. They never become global route proofs. +* Consecutive failed destinations back off from2–3 seconds to a maximum30 seconds, + with per-bot jitter. New intents reset this state. Deadlines are unchanged; + combat and immediate social/damage notifications are not delayed. +* The active test config uses MapUpdate.Threads=4, previously1. Network and database + worker settings were retained. Native map jobs can run concurrently; bot/Lua/ + Player-manager work remains on its owning world thread. A single map update + is still serial. Arbitrarily parallelizing those managers would introduce races. + +## Verification + +All86 clientless tests pass. Tests cover scoped configuration reuse without extra +reads over1,000 estimates, fresh departure eligibility after the scope, exact area +coordinates and phase-aware terrain queries, unchanged routes, route-specific +blocklists, cold/warm proof reuse, map/instance isolation, bounded caches, two-way +map-thread notice handoff, bounded retry timing and immediate new-intent reset. + +Native warm searches with about20,000 flight-cost calls fell to roughly34–37ms +in flight evaluation and around100ms total. Targets and actor positions differ +between live windows: this is an observed workload comparison, not a deterministic +before/after benchmark. Early cold queries can still take longer. +With retry backoff, a30.006-second window at84 managed characters spent7.114 seconds +in actions and1.848 seconds in controllers. With all110 bots online (50/5/50/5, no pending logins or loader error), the next +30.011-second window spent7.762 seconds in actions and1.612 seconds in controllers. +A10.069-second /proc sample recorded26.2% of one CPU in the main thread and +38.6%,37.6%,38.1%,38.5% in the four map workers. The process remained ready and +TCP8085 listening. Samples cover different live workloads; they do not establish +a fixed speedup or guaranteed capacity. Evidence is in checkpoints/2026-10-29-performance. + +## Remaining limits + +Compiler optimization is still required for a meaningful production capacity test. +No existing Debug CMake configuration was replaced. Native map updates, initial +character preparation and cold terrain loads can still produce individual spikes. +Four map workers do not parallelize one continent, and the bot controllers are +still world-thread owned. No claim is made that1,000 bots or production load has +been accepted. User in-game acceptance remains separate from automated tests. diff --git a/doc/clientless/worgen-recruitment.md b/doc/clientless/worgen-recruitment.md new file mode 100644 index 0000000000..63ffcb56d4 --- /dev/null +++ b/doc/clientless/worgen-recruitment.md @@ -0,0 +1,48 @@ +# AdventureBot world presence and recruitment + +Adventure controllers maintain native voluntary PvP flags, including the saved +CHARACTER_FLAG_PVP_DESIRED flag. Native faction, sanctuary and attack-validity +checks still decide whether a target can be attacked. DungeonBots are unchanged. + +Worgen use native Altered Form during adventure, dungeon and raid activity, +combat and mixed player instances. At arrival for quiet activities, one random +form is chosen and retained; mounting, transport, flying, casting and death defer +the change until safe. Failed changes retry at most once every five seconds. +Logout clears pending presentation state. + +A Worgen in altered form uses native Running Wild (87840) for ground travel. +Human form uses a horse; Brown Horse (458) and Two Forms (68996) are learned +where appropriate. Existing generated profiles with empty mount dictionaries +can still use these Worgen mounts. New generated Worgen profiles contain horses. +Dismounting removes the Running Wild aura before restoring normal run speed. +Flying continues through the existing native flight subsystem. + +Recruitment uses native localized LookingForGroup channel ID26 (`SucheNachGruppe` +in deDE), with native capital eligibility. Test config Channel.RestrictedLfg=0 +allows already grouped players and recruiting bot leaders to join. No custom +channel with a reserved standard-channel name is created. Human chat text is not parsed as +bot recruitment commands. Bot coordination uses structured intent and party +state. Announcements now include the actual next activity and destination; +logs contain GROUP_CHANNEL for diagnosis. The entire bot population shares a +15-second channel limit, with each advertiser waiting three to five minutes. + +During groupformation, the normal microaction executor can now run. The additive +migration sql/custom/world/2026_10_29_01_adventure_group_preparation.sql adds gear +checks, listening, local NPC contact, gestures and bounded searches. Search walks +use verified MMAP paths, remain within an 18-yard path budget and return to their +origin; they also work indoors. Combat, native travel and group following retain +priority. Meeting points and group transition deadlines remain unchanged. + +Bot leaders can invite nearby, visible, same-faction human players who are not +AFK, grouped, already invited, in combat or in transit. A whisper describing the +next activity and actual destination precedes the native party invitation. A +shared ten-minute per-player cooldown limits invitations across all bots. Native +party capacity and pending invitations are respected. On acceptance, the existing +player-leadership policy still transfers leadership to the human player. + +Compilation and production-method adapter tests cover PvP persistence requests, +Worgen decisions and deferral, Running Wild/horse selection, dismount cleanup, +bounded indoor searches, intent handover and human recruitment/cooldowns. The +native server verifies startup and saved PvP flags; visual form transitions, +animations, channel visibility and invitation UI require in-game acceptance. +Ship boarding stands and transport routes are unchanged by this task. diff --git a/sql/custom/world/2026_10_29_01_adventure_group_preparation.sql b/sql/custom/world/2026_10_29_01_adventure_group_preparation.sql new file mode 100644 index 0000000000..064c547cc3 --- /dev/null +++ b/sql/custom/world/2026_10_29_01_adventure_group_preparation.sql @@ -0,0 +1,9 @@ +-- Visible preparation at the existing capital meeting point; native local paths. +INSERT IGNORE INTO vcore_adventure_action +(action_key,kind,min_seconds,max_seconds,requirement,emote_id) VALUES +('group_search_walk','leisure',35,65,'walk',0), +('group_ready','emote',12,24,'',1); +INSERT IGNORE INTO vcore_adventure_action_intent(action_key,intent,weight) VALUES +('group_search_walk','groupformation',5),('group_ready','groupformation',2), +('profession_tools','groupformation',3),('social_listen','groupformation',1), +('social_npc','groupformation',1); diff --git a/src/server/game/World/AdventureBehaviorMgr.cpp b/src/server/game/World/AdventureBehaviorMgr.cpp index e0f81121e8..edb38c7a28 100644 --- a/src/server/game/World/AdventureBehaviorMgr.cpp +++ b/src/server/game/World/AdventureBehaviorMgr.cpp @@ -347,6 +347,7 @@ void AdventureBehaviorMgr::SetWanted(Player * player, Record & r, std::string co r.Deadline = deadline; r.Dirty = true; r.Tried.clear(); + r.RetryTimer=0; if (r.Instance.Active) { if (r.Instance.Stage>=AdventureInstancePolicy::Phase::Entering && @@ -485,8 +486,8 @@ void AdventureBehaviorMgr::Fallback(Player * player, Record & r) r.WantedLocation = nearest->Point.Id; r.Dirty = true; r.State = Phase::Idle; - r.RetryTimer = 1000; - TC_LOG_WARN("server.adventurebot", "INTENT_FALLBACK guid=%u from='%s' to='%s' tried=%u", player->GetGUID().GetCounter(), r.Location.c_str(), nearest->Point.Id.c_str(), uint32(r.Tried.size())); + r.RetryTimer = AdventureBehaviorPolicy::TravelRetryMs(uint32(r.Tried.size()),std::uniform_int_distribution(0,1000)(r.Random)); + TC_LOG_WARN("server.adventurebot", "INTENT_FALLBACK guid=%u from='%s' to='%s' tried=%u retryMs=%u", player->GetGUID().GetCounter(), r.Location.c_str(), nearest->Point.Id.c_str(), uint32(r.Tried.size()),r.RetryTimer); } else { @@ -599,6 +600,7 @@ bool AdventureBehaviorMgr::LeisureAvailable(Player* p,Record const& r,AdventureM auto rod=p->GetWeaponForAttack(BASE_ATTACK,true);float direction=0; return rod && rod->GetTemplate()->GetSubClass()==ITEM_SUBCLASS_WEAPON_FISHING_POLE && FishingDirection(p,direction); } + if (r.Intent=="groupformation") return a.Requirement=="walk"; if (r.Intent!="leisure") return false; auto profile=sClientlessLuaEngine->GetAdventureProfile(p->GetGUID().GetCounter()); auto location=sAdventureLocationMgr->Find(r.Location); @@ -628,12 +630,12 @@ bool AdventureBehaviorMgr::PlanLeisureWalk(Player* p, Record& r) // Choose one substantial destination, then follow its actual ground corridor. // Randomness selects the outing, never the next individual footstep. float const offset=std::uniform_real_distribution(0,6.2831853f)(r.Random); - float const budget=std::min(220.0f,std::max(100.0f,float(r.MicroTimer)*0.001f*1.0f)); + float const budget=r.Intent=="groupformation"?18.0f:std::min(220.0f,std::max(100.0f,float(r.MicroTimer)*0.001f*1.0f)); float best=0; for (unsigned i=0;i<16;++i) { float const angle=offset+float(i%8)*6.2831853f/8; - float const radius=i<8?std::min(150.0f,budget*0.7f):80.0f; + float const radius=r.Intent=="groupformation"?(i<8?12.0f:8.0f):(i<8?std::min(150.0f,budget*0.7f):80.0f); Position goal; goal.Relocate(s.Origin.GetPositionX()+std::cos(angle)*radius, s.Origin.GetPositionY()+std::sin(angle)*radius,s.Origin.GetPositionZ()); @@ -660,12 +662,12 @@ bool AdventureBehaviorMgr::PlanLeisureWalk(Player* p, Record& r) p->GetMap()->GetTerrain()->GetFullTerrainStatusForPosition(p->GetPhaseShift(),p->GetMapId(),point.x,point.y,point.z,terrain); uint32 zone=0,area=0; p->GetMap()->GetTerrain()->GetZoneAndAreaId(p->GetPhaseShift(),p->GetMapId(),zone,area,target); - if (!terrain.outdoors || (boundary && area!=boundary && zone!=boundary)) {valid=false;break;} + if ((r.Intent!="groupformation" && !terrain.outdoors) || (boundary && area!=boundary && zone!=boundary)) {valid=false;break;} length+=step;section+=step;last=target; if (section>=20) {outbound.push_back(target);section=0;} } float const span=s.Origin.GetExactDist(last); - if (!valid || length>budget || span<65 || length>span*2.5f || last.GetExactDist(goal)>1.5f) continue; + if (!valid || length>budget || span<(r.Intent=="groupformation"?4.0f:65.0f) || length>span*2.5f || last.GetExactDist(goal)>1.5f) continue; if (outbound.empty() || outbound.back().GetExactDist(last)>2) outbound.push_back(last); else outbound.back()=last; if (span>best) @@ -1388,12 +1390,13 @@ void AdventureBehaviorMgr::Update(Player * player, uint32 diff) r.AnchorMap = player->GetMapId(); r.State = Phase::OnSite; r.Detail = "performing intent at destination"; + sClientlessLuaEngine->UpdateWorldPresence(player,r.Intent,true); TC_LOG_INFO("server.adventurebot", "INTENT_ARRIVED guid=%u intent='%s' location='%s'", guid, r.Intent.c_str(), r.Location.c_str()); } if (r.State == Phase::OnSite) { if ((r.Intent=="instance" || r.Intent=="raid")) {UpdateInstance(player,r,now);return;} - if(r.Intent=="groupformation")return; + if(sAdventureGroupMgr->FollowMember(player))return; if (r.Recovery.Active) { diff --git a/src/server/game/World/AdventureBehaviorPolicy.h b/src/server/game/World/AdventureBehaviorPolicy.h index 8eedafa1f1..352083276d 100644 --- a/src/server/game/World/AdventureBehaviorPolicy.h +++ b/src/server/game/World/AdventureBehaviorPolicy.h @@ -11,6 +11,13 @@ namespace AdventureBehaviorPolicy { +// Failed destinations may share the same disconnected local graph. Avoid a +// population-wide retry storm without suppressing combat or extending schedules. +inline unsigned TravelRetryMs(unsigned attempts,unsigned jitter) +{ + unsigned shift=std::min(attempts?attempts-1:0u,4u); + return std::min(30000u,2000u*(1u<GetGroup()) + auto profile=sClientlessLuaEngine->GetAdventureProfile(p->GetGUID().GetCounter()); + if(p->GetGroup() || (profile && !profile->AllowRetreat)) { if(r.Fleeing) { sClientlessTravelMgr->CancelTravel(p->GetGUID().GetCounter());p->StopMoving(); r.Fleeing=false;r.ThreatPositions.clear(); - TC_LOG_INFO("server.adventurebot","COMBAT_FLEE_CANCEL guid=%u reason='group'",p->GetGUID().GetCounter()); + TC_LOG_INFO("server.adventurebot","COMBAT_FLEE_CANCEL guid=%u reason='%s'",p->GetGUID().GetCounter(),p->GetGroup()?"group":"profile"); } return false; } diff --git a/src/server/game/World/AdventureGeneratorMgr.cpp b/src/server/game/World/AdventureGeneratorMgr.cpp index 21619a17b0..573922e680 100644 --- a/src/server/game/World/AdventureGeneratorMgr.cpp +++ b/src/server/game/World/AdventureGeneratorMgr.cpp @@ -243,7 +243,7 @@ namespace case 8:ground={10796,10799,8395};break; case 10:ground={34795,35018,35020};break; case 9:ground={87090};break; - case 22:ground={103195,103196};break; + case 22:ground={458,470,472};break; default:ground={458,470,472};break; } std::ostringstream lua;lua<<"movement = { can_ground_mount = "<<(level>=20?"true":"false") @@ -525,7 +525,7 @@ std::unordered_set AdventureGeneratorMgr::ProtectedEquipment(Player* pla { std::unordered_set result; auto profile=sClientlessLuaEngine->GetAdventureProfile(player->GetGUID().GetCounter()); - if(!profile || profile->SourcePath.find("generated:")!=0)return result; + if(!profile || profile->ProfileVersion<2 || !profile->Class)return result; std::vector items; for(uint8 slot=INVENTORY_SLOT_ITEM_START;slotGetItemByPos(INVENTORY_SLOT_BAG_0,slot))items.push_back(item); @@ -559,7 +559,7 @@ bool AdventureGeneratorMgr::PrepareCombat(Player* player,std::string& error) { auto profile=sClientlessLuaEngine->GetAdventureProfile(player->GetGUID().GetCounter()); if(!profile)return false; - if(profile->Class && profile->SourcePath.find("generated:")==0)return Reconcile(player,error); + if(profile->ProfileVersion>=2 && profile->Class)return Reconcile(player,error); // Named Adventure profiles share the canonical combat templates, while // retaining their manually assigned inventory, professions and level. auto found=std::find_if(AdventureGeneratorPolicy::Archetypes().begin(),AdventureGeneratorPolicy::Archetypes().end(), @@ -576,7 +576,7 @@ bool AdventureGeneratorMgr::PrepareCombat(Player* player,std::string& error) bool AdventureGeneratorMgr::Reconcile(Player* player,std::string& error) { auto profile=sClientlessLuaEngine->GetAdventureProfile(player->GetGUID().GetCounter()); - if(!profile || !profile->Level || profile->SourcePath.find("generated:")!=0)return true; + if(!profile || !profile->Level || !profile->Class || profile->ProfileVersion<2)return true; if(profile->Class!=player->getClass()){error="Profile class mismatch; no class conversion performed.";return false;} auto found=std::find_if(AdventureGeneratorPolicy::Archetypes().begin(),AdventureGeneratorPolicy::Archetypes().end(), [&](Archetype const& a){return a.Class==profile->Class;}); diff --git a/src/server/game/World/AdventureGroupMgr.cpp b/src/server/game/World/AdventureGroupMgr.cpp index b0d5ea640a..5cd84583cd 100644 --- a/src/server/game/World/AdventureGroupMgr.cpp +++ b/src/server/game/World/AdventureGroupMgr.cpp @@ -10,6 +10,7 @@ #include "ClientlessTravelGraphMgr.h" #include "Channel.h" #include "ChannelMgr.h" +#include "DBCStores.h" #include "Chat.h" #include "GameTime.h" #include "Group.h" @@ -65,12 +66,44 @@ bool AdventureGroupMgr::PlannedTransition(Player* p) } void AdventureGroupMgr::Speak(Player* p,std::string const& message) { - if(!AdventureGroupPolicy::Capital(p->GetZoneId()))return; + if(!AdventureGroupPolicy::Capital(p->GetZoneId()) || GameTime::GetGameTime()<_nextChannelMessage)return; + _nextChannelMessage=GameTime::GetGameTime()+15; auto manager=ChannelMgr::forTeam(p->GetTeam());if(!manager)return; - auto channel=manager->GetJoinChannel(0,"sucheNachGruppe");if(!channel)return; + auto entry=sChatChannelsStore.LookupEntry(26); // Native LookingForGroup, localized by the client. + auto zone=sAreaTableStore.LookupEntry(p->GetZoneId()); + if(!entry || !zone || !p->CanJoinConstantChannelInZone(entry,zone))return; + auto channel=manager->GetJoinChannel(entry->ID,"",zone);if(!channel)return; if(std::find(p->GetJoinedChannels().begin(),p->GetJoinedChannels().end(),channel)==p->GetJoinedChannels().end())channel->JoinChannel(p,""); + if(std::find(p->GetJoinedChannels().begin(),p->GetJoinedChannels().end(),channel)==p->GetJoinedChannels().end())return; channel->Say(p->GetGUID(),message,p->GetTeam()==ALLIANCE?LANG_COMMON:LANG_ORCISH); + TC_LOG_INFO("server.adventurebot","GROUP_CHANNEL guid=%u zone=%u text='%s'",p->GetGUID().GetCounter(),p->GetZoneId(),message.c_str()); } +std::string AdventureGroupMgr::RecruitmentText(Player* player) +{ + auto plan=sAdventureBehaviorMgr->GroupPlan(player); + auto activity=Activity(plan); + auto destination=sAdventureLocationMgr->Find(plan.Forming?plan.NextLocation:plan.Location); + std::string label=activity=="raid"?"einen Schlachtzug":activity=="instance"?"einen Instanzbesuch": + activity=="profession"?"gemeinsame Berufsarbeit":activity=="social"?"eine gesellige Runde":"ein Abenteuer"; + return "Ich suche Gefaehrten fuer "+label+". Ziel: "+(destination?destination->Name:std::string("noch offen"))+"."; +} +bool AdventureGroupMgr::RecruitPlayer(Player* player, std::string const& intent) +{ + auto now=GameTime::GetGameTime(); + for(auto it=_playerInvites.begin();it!=_playerInvites.end();)if(it->second<=now)it=_playerInvites.erase(it);else ++it; + std::vector people;player->GetPlayerListInGrid(people,25); + for(auto other:people) + { + if(other==player || other->GetSession()->IsClientless() || !Ready(other) || other->isAFK() || + other->GetGroup() || other->GetGroupInvite() || other->GetTeam()!=player->GetTeam() || + !player->IsInMap(other) || !player->IsInPhase(other) || !player->CanSeeOrDetect(other) || + !player->IsWithinLOSInMap(other) || _playerInvites.count(other->GetGUID()))continue; + _playerInvites[other->GetGUID()]=now+600; + Invite(player,other,intent,true);return true; + } + return false; +} + void AdventureGroupMgr::Offline(Player* p) { // Native player logout owns channel membership cleanup. @@ -120,15 +153,14 @@ void AdventureGroupMgr::Update(Player* p) if(plan.Forming) { if(!AdventureGroupPolicy::Capital(p->GetZoneId()))return; - if(now>=r.SpeakAt) + if(now>=r.SpeakAt && now>=_nextChannelMessage) { - r.SpeakAt=now+urand(120,240); - auto place=sAdventureLocationMgr->Find(plan.Location); - std::string activity=intent=="instance"?"einen Instanzbesuch":intent=="raid"?"einen Schlachtzug":intent=="profession"?"gemeinsame Berufsarbeit":intent=="social"?"eine gesellige Runde":"ein Abenteuer"; - Speak(p,"Ich suche Gefaehrten fuer "+activity+" in der naechsten Stunde. Treffpunkt: "+(place?place->Name:std::string("hier in der Stadt"))+"."); + r.SpeakAt=now+urand(180,300); + Speak(p,RecruitmentText(p)); } } if(frand(0,1)>Sociability(p))return; + if(plan.Forming && RecruitPlayer(p,intent))return; // Snapshot GUIDs: callbacks/native invitations may update manager records. std::vector candidates;for(auto const& entry:_records)candidates.push_back(entry.first); std::sort(candidates.begin(),candidates.end()); @@ -196,8 +228,10 @@ void AdventureGroupMgr::Invite(Player* p,Player* other,std::string const& intent peer.Partners[p->GetGUID()]=now+600;r.Partners[other->GetGUID()]=now+600; WorldPackets::Party::PartyInviteClient packet{WorldPacket(CMSG_PARTY_INVITE)}; packet.TargetName=other->GetName();packet.TargetGUID=other->GetGUID(); + if(!other->GetSession()->IsClientless()) + p->Whisper(RecruitmentText(p)+" Moechtest du mitkommen?",p->GetTeam()==ALLIANCE?LANG_COMMON:LANG_ORCISH,other); p->GetSession()->HandlePartyInviteOpcode(packet); - if(forming && other->GetGroupInvite())Speak(other,"Ich wuerde mich euch anschliessen."); + if(forming && other->GetSession()->IsClientless() && other->GetGroupInvite())Speak(other,"Ich wuerde mich euch anschliessen."); else if(other->GetGroupInvite()){p->SetFacingToObject(other);p->HandleEmoteCommand(EMOTE_ONESHOT_WAVE);} TC_LOG_INFO("server.adventurebot","GROUP_BOT_INVITE leader=%u target=%u intent='%s' forming=%u",p->GetGUID().GetCounter(),other->GetGUID().GetCounter(),intent.c_str(),forming); } diff --git a/src/server/game/World/AdventureGroupMgr.h b/src/server/game/World/AdventureGroupMgr.h index aebd10dff8..ff7b2a3c4d 100644 --- a/src/server/game/World/AdventureGroupMgr.h +++ b/src/server/game/World/AdventureGroupMgr.h @@ -39,6 +39,10 @@ private: }; void Invite(Player* leader,Player* member,std::string const& intent,bool forming); void Speak(Player* player,std::string const& message); + std::string RecruitmentText(Player* player); + bool RecruitPlayer(Player* player,std::string const& intent); + std::unordered_map _playerInvites; + time_t _nextChannelMessage=0; std::unordered_map _records; }; #define sAdventureGroupMgr AdventureGroupMgr::instance() diff --git a/src/server/game/World/AdventurePopulationMgr.cpp b/src/server/game/World/AdventurePopulationMgr.cpp index 88168971cb..0746d3be6a 100644 --- a/src/server/game/World/AdventurePopulationMgr.cpp +++ b/src/server/game/World/AdventurePopulationMgr.cpp @@ -221,7 +221,7 @@ void AdventurePopulationMgr::DeleteRotated() void AdventurePopulationMgr::Online(Player* p) { auto profile=sClientlessLuaEngine->GetAdventureProfile(p->GetGUID().GetCounter()); - if(profile && profile->ProfileVersion>=2 && profile->SourcePath.find("generated:")==0)_checks[p->GetGUID().GetCounter()]=0; + if(profile && profile->ProfileVersion>=2 && profile->Class && profile->Level)_checks[p->GetGUID().GetCounter()]=0; } void AdventurePopulationMgr::Offline(Player* p){_checks.erase(p->GetGUID().GetCounter());} void AdventurePopulationMgr::CleanInventory(Player* p) diff --git a/src/server/game/World/ClientlessFlightMgr.cpp b/src/server/game/World/ClientlessFlightMgr.cpp index ba6170fe0c..2b4181a673 100644 --- a/src/server/game/World/ClientlessFlightMgr.cpp +++ b/src/server/game/World/ClientlessFlightMgr.cpp @@ -1,4 +1,5 @@ #include "ClientlessFlightMgr.h" +#include #include "ClientlessLuaEngine.h" #include "ClientlessTravelMgr.h" #include "ClientlessActionExecutorMgr.h" @@ -75,15 +76,26 @@ ClientlessFlightMgr * ClientlessFlightMgr::instance() return & mgr; } -void ClientlessFlightMgr::BeginPlanning() +void ClientlessFlightMgr::BeginPlanning(Player const* player) { - _planningAreas.clear(); - _planningTerrain.clear(); + _planningAreas.clear();_planningTerrain.clear();_planningPreferredZones.clear(); + _planningPlayer=player;_planningEnabled=player && Enabled(player); + if(player) + { + _planningMinDistance=sConfigMgr->GetFloatDefault("Clientless.Flight.MinDistance",600); + std::istringstream list(sConfigMgr->GetStringDefault("Clientless.Flight.PreferredZones","210 4197")); + uint32 zone;while(list>>zone)_planningPreferredZones.insert(zone); + } } std::pair < uint32, uint32 > ClientlessFlightMgr::AreaZone(Player const * player, uint32 map, Position const & point) const { - std::string key = std::to_string(map) + ":" + std::to_string(point.GetPositionX()) + ":" + std::to_string(point.GetPositionY()) + ":" + std::to_string(point.GetPositionZ()); + // Exact binary coordinates avoid repeated floating-point text formatting + // in the O(nodes^2) flight alternatives loop, and do not merge nearby points. + std::string key(sizeof(map)+3*sizeof(float),'\0'); + float coordinates[]{point.GetPositionX(),point.GetPositionY(),point.GetPositionZ()}; + std::memcpy(key.data(),&map,sizeof(map)); + std::memcpy(key.data()+sizeof(map),coordinates,sizeof(coordinates)); auto found = _planningAreas.find(key); if (found != _planningAreas.end()) return found->second; auto & terrain = _planningTerrain[map]; @@ -103,6 +115,7 @@ bool ClientlessFlightMgr::Enabled(Player const * player) const bool ClientlessFlightMgr::Preferred(Player const * player, uint32 map, Position const & position) const { + if(_planningPlayer==player)return _planningPreferredZones.count(AreaZone(player,map,position).second)!=0; std::istringstream list(sConfigMgr->GetStringDefault("Clientless.Flight.PreferredZones", "210 4197")); uint32 zone = AreaZone(player, map, position).second, candidate; while (list >> candidate) if (zone == candidate) return true; @@ -112,7 +125,8 @@ bool ClientlessFlightMgr::Preferred(Player const * player, uint32 map, Position float ClientlessFlightMgr::Estimate(Player const * player, uint32 map, Position const & from, Position const & to, bool recovery) const { auto const * entry = sMapStore.LookupEntry(map); - if (! Enabled(player) || ! entry || entry->MapType != MAP_COMMON) return Infinity(); + bool planning=_planningPlayer==player; + if (!(planning?_planningEnabled:Enabled(player)) || ! entry || entry->MapType != MAP_COMMON) return Infinity(); if ((map == 0 || map == 1) && ! player->HasSpell(90267)) return Infinity(); if (map == 571 && ! player->HasSpell(54197)) return Infinity(); uint32 const area = AreaZone(player, map, from).first; @@ -127,7 +141,7 @@ float ClientlessFlightMgr::Estimate(Player const * player, uint32 map, Position float const distance = Distance(PointOf(from), PointOf(to)); // Recovery keeps native eligibility, but has no RP // distance threshold or zone preference. Start still validates the flight. - float minimum = recovery ? 0.0f : preferred ? 60.0f : std::max(100.0f, sConfigMgr->GetFloatDefault("Clientless.Flight.MinDistance", 600)); + float minimum = recovery ? 0.0f : preferred ? 60.0f : std::max(100.0f, planning?_planningMinDistance:sConfigMgr->GetFloatDefault("Clientless.Flight.MinDistance", 600)); if (! std::isfinite(distance) || distance < minimum || (!recovery && distance > 20000)) return Infinity(); return recovery ? distance : preferred ? 40 + distance * 0.4f : 180 + distance * 1.5f; } diff --git a/src/server/game/World/ClientlessFlightMgr.h b/src/server/game/World/ClientlessFlightMgr.h index d72eef79b1..5e1592a34c 100644 --- a/src/server/game/World/ClientlessFlightMgr.h +++ b/src/server/game/World/ClientlessFlightMgr.h @@ -6,6 +6,7 @@ #include #include #include +#include class Player; class ChatHandler; class TerrainInfo; @@ -29,7 +30,7 @@ class TC_GAME_API ClientlessFlightMgr float Estimate(Player const * player, uint32 mapId, Position const & from, Position const & to, bool recovery = false) const; bool Preferred(Player const * player, uint32 mapId, Position const & position) const; bool Enabled(Player const * player) const; - void BeginPlanning(); + void BeginPlanning(Player const* player = nullptr); bool Start(Player * player, Position const & target, std::string & error); bool Follow(Player* player, Position const& target, std::string& error); bool LandFollowing(Player* player); @@ -59,6 +60,11 @@ class TC_GAME_API ClientlessFlightMgr bool AdvanceFollowing(Player* player, Record& record); void Fail(Player * player, Record & record, std::string const & reason); std::pair < uint32, uint32 > AreaZone(Player const * player, uint32 map, Position const & point) const; + // Immutable only for one explicit world-thread graph-planning scope. + Player const* _planningPlayer = nullptr; + bool _planningEnabled = false; + float _planningMinDistance = 600; + std::unordered_set _planningPreferredZones; mutable std::unordered_map < std::string, std::pair < uint32, uint32 >> _planningAreas; mutable std::unordered_map < uint32, std::shared_ptr < TerrainInfo >> _planningTerrain; std::unordered_map < uint32, Record > _records; diff --git a/src/server/game/World/ClientlessGraphProbeCache.h b/src/server/game/World/ClientlessGraphProbeCache.h new file mode 100644 index 0000000000..b35211267c --- /dev/null +++ b/src/server/game/World/ClientlessGraphProbeCache.h @@ -0,0 +1,41 @@ +#ifndef VCORE_CLIENTLESS_GRAPH_PROBE_CACHE_H +#define VCORE_CLIENTLESS_GRAPH_PROBE_CACHE_H +#include +#include +#include +#include +#include + +// World-thread only. Store successful STATIC ground geometry, never Player paths, +// transient failures, Detour objects or tile pointers. Clear on graph reload. +class ClientlessGraphProbeCache +{ +public: + using Key=std::array; + struct Value {float Length,Final,Direct;}; + static Key MakeKey(std::uint32_t map,std::uint32_t instance,float ax,float ay,float az,float bx,float by,float bz) + { + Key key{map,instance};float coordinates[]{ax,ay,az,bx,by,bz}; + for(unsigned i=0;i<6;++i)std::memcpy(&key[i+2],&coordinates[i],sizeof(float)); + return key; + } + Value const* Find(Key const& key) + { + auto found=_values.find(key); + if(found==_values.end()){++Misses;return nullptr;} + ++Hits;return &found->second; + } + void Record(Key const& key,bool complete,float length,float final,float direct) + { + if(!complete || !std::isfinite(length) || length<0 || !std::isfinite(final) || final>1.5f || final<0 || !std::isfinite(direct))return; + if(_values.size()>=32768)_values.clear(); + _values.insert_or_assign(key,Value{length,final,direct}); + } + void Clear(){_values.clear();Hits=Misses=0;} + std::size_t Size()const{return _values.size();} + std::uint64_t Hits=0,Misses=0; +private: + struct Hash {std::size_t operator()(Key const& key)const{std::size_t hash=0;for(auto value:key)hash^=std::hash{}(value)+0x9e3779b9+(hash<<6)+(hash>>2);return hash;}}; + std::unordered_map _values; +}; +#endif diff --git a/src/server/game/World/ClientlessLuaEngine.cpp b/src/server/game/World/ClientlessLuaEngine.cpp index 0f36a4eb80..cfaa91ed4d 100644 --- a/src/server/game/World/ClientlessLuaEngine.cpp +++ b/src/server/game/World/ClientlessLuaEngine.cpp @@ -17,6 +17,8 @@ #include "Log.h" #include "Player.h" #include "Random.h" +#include "GameTime.h" +#include "Map.h" #include #include #include @@ -230,6 +232,7 @@ public: { sAdventureBehaviorMgr->IntentChanged(player, intent); } void Update(Player* player, uint32 diff) override { + sClientlessLuaEngine->UpdateWorldPresence(player,sAdventureBehaviorMgr->InteractionIntent(player)); sAdventureGroupTravelMgr->Observe(player,diff); sAdventurePopulationMgr->Maintain(player,diff); if(sAdventureGroupTravelMgr->Airborne(player)){sAdventureGroupTravelMgr->Follow(player);return;} @@ -246,7 +249,7 @@ public: void OnDamageTaken(Player* player, Unit* attacker, uint32 damage) override { sAdventureCombatMgr->DamageTaken(player, attacker, damage); } void OnPlayerLogout(Player* player) override - { sAdventurePopulationMgr->Offline(player); sAdventureInteractionMgr->Offline(player); sAdventureSupplyMgr->Offline(player); sAdventureCombatMgr->Offline(player); sAdventureBehaviorMgr->Offline(player); } + { sClientlessLuaEngine->ForgetWorldPresence(player->GetGUID().GetCounter()); sAdventurePopulationMgr->Offline(player); sAdventureInteractionMgr->Offline(player); sAdventureSupplyMgr->Offline(player); sAdventureCombatMgr->Offline(player); sAdventureBehaviorMgr->Offline(player); } }; } @@ -472,6 +475,7 @@ bool ClientlessLuaEngine::LoadAdventureProfile(Player* player) { profile.AutonomousBehavior = ReadBoolField(_state, -1, "enabled", false); profile.DailySchedule = ReadBoolField(_state, -1, "daily_schedule", false); + profile.AllowRetreat = ReadBoolField(_state, -1, "allow_retreat", true); profile.InitialIntent = ReadStringField(_state, -1, "initial_intent"); profile.InitialLocation = ReadStringField(_state, -1, "initial_location"); profile.Locale = ReadStringField(_state, -1, "locale", "enUS"); @@ -563,20 +567,56 @@ uint32 ClientlessLuaEngine::SelectCompanionSpell(uint32 guid) const return 0; } +void ClientlessLuaEngine::UpdateWorldPresence(Player* player, std::string const& intent, bool arrived) +{ + if (!player || !player->IsInWorld()) return; + player->AddCharacterFlag(CHARACTER_FLAG_PVP_DESIRED); + player->SetFlag(PLAYER_FLAGS, PLAYER_FLAGS_IN_PVP); + if (!player->IsPvP() || player->pvpInfo.EndTimer) player->UpdatePvP(true,true); + if (player->getRace()!=RACE_WORGEN) return; + if (!player->HasSpell(SPELL_TWO_FORMS_RACIAL)) player->LearnSpell(SPELL_TWO_FORMS_RACIAL,false); + if (player->getLevel()>=20 && !player->HasSpell(458)) player->LearnSpell(458,false); + bool active=intent=="adventure" || intent=="instance" || intent=="raid" || player->IsInCombat() || player->GetMap()->IsDungeon(); + auto guid=player->GetGUID().GetCounter(); + auto wanted=_worgenForms.find(guid); + if (arrived && !active) wanted=_worgenForms.insert_or_assign(guid,urand(0,1)!=0).first; + if (active) wanted=_worgenForms.insert_or_assign(guid,true).first; + if (wanted==_worgenForms.end() || !player->IsAlive() || player->IsMounted() || + player->GetTransport() || player->IsInFlight() || player->IsFlying() || player->IsBeingTeleported() || + player->HasUnitState(UNIT_STATE_CASTING)) return; + bool altered=player->HasAuraType(SPELL_AURA_WORGEN_ALTERED_FORM); + if (altered==wanted->second || _worgenNextAttempt[guid]>GameTime::GetGameTime()) return; + _worgenNextAttempt[guid]=GameTime::GetGameTime()+5; + if (wanted->second) player->CastSpell(player,SPELL_ALTERED_FORM_RACIAL,true); + else if (player->HasSpell(SPELL_TWO_FORMS_RACIAL)) player->CastSpell(player,SPELL_TWO_FORMS_RACIAL,true); + TC_LOG_INFO("server.adventurebot","WORGEN_FORM guid=%u intent='%s' desired=%u arrived=%u",guid,intent.c_str(),wanted->second,arrived); +} + bool ClientlessLuaEngine::PrepareGroundMount(Player* player, float travelDistance, char const* context) { if (!player || !player->IsInWorld()) return false; ClientlessAdventureProfile const* profile = GetAdventureProfile(player->GetGUID().GetCounter()); - if (!profile || !profile->CanGroundMount || profile->GroundMounts.empty()) + if (!profile || !profile->CanGroundMount || (profile->GroundMounts.empty() && player->getRace()!=RACE_WORGEN)) return false; if (travelDistance < profile->GroundMountMinDistance || !player->IsOutdoors()) return false; if (player->IsMounted()) return true; - uint32 const displayId = SelectGroundMountDisplayId(player->GetGUID().GetCounter()); + if (player->getRace()==RACE_WORGEN && player->HasAuraType(SPELL_AURA_WORGEN_ALTERED_FORM) && player->getLevel()>=20) + { + if (!player->HasSpell(87840)) player->LearnSpell(87840,false); + player->CastSpell(player,87840,true); + if (player->HasAura(87840) && player->IsMounted()) + { + TC_LOG_INFO("server.adventurebot.navigation","GROUND_RUNNING_WILD guid=%u",player->GetGUID().GetCounter()); + return true; + } + return false; + } + uint32 const displayId = player->getRace()==RACE_WORGEN?2404:SelectGroundMountDisplayId(player->GetGUID().GetCounter()); if (!displayId) return false; @@ -598,6 +638,7 @@ void ClientlessLuaEngine::DismountGroundMount(Player* player, char const* reason return; uint32 const oldDisplay = player->GetMountID(); + player->RemoveAurasDueToSpell(87840); player->Dismount(); player->UpdateSpeed(MOVE_RUN); diff --git a/src/server/game/World/ClientlessLuaEngine.h b/src/server/game/World/ClientlessLuaEngine.h index 7305c02211..cf64c1c6d1 100644 --- a/src/server/game/World/ClientlessLuaEngine.h +++ b/src/server/game/World/ClientlessLuaEngine.h @@ -53,6 +53,7 @@ struct TC_GAME_API ClientlessAdventureProfile std::unordered_map MicroActionAffinity; bool AutonomousBehavior = false; bool DailySchedule = false; + bool AllowRetreat = true; std::string InitialIntent, InitialLocation, Locale = "enUS"; uint32 InitialDurationSeconds = 3600; }; @@ -71,6 +72,8 @@ public: ClientlessAdventureProfile const* GetAdventureProfile(uint32 characterGuid) const; uint32 SelectGroundMountDisplayId(uint32 characterGuid) const; uint32 SelectCompanionSpell(uint32 characterGuid) const; + void ForgetWorldPresence(uint32 guid) { _worgenForms.erase(guid); _worgenNextAttempt.erase(guid); } + void UpdateWorldPresence(Player* player, std::string const& intent, bool arrived = false); bool PrepareGroundMount(Player* player, float travelDistance, char const* context); void DismountGroundMount(Player* player, char const* reason); @@ -84,6 +87,8 @@ private: lua_State* _state = nullptr; std::string _scriptRoot; std::unordered_map _profiles; + std::unordered_map _worgenForms; + std::unordered_map _worgenNextAttempt; }; TC_GAME_API std::unique_ptr CreateClientlessAdventureLuaController(); diff --git a/src/server/game/World/ClientlessPlayerMgr.cpp b/src/server/game/World/ClientlessPlayerMgr.cpp index e832a62d52..17e8e238cc 100644 --- a/src/server/game/World/ClientlessPlayerMgr.cpp +++ b/src/server/game/World/ClientlessPlayerMgr.cpp @@ -10,6 +10,8 @@ #include "ObjectAccessor.h" #include "World.h" #include "WorldSession.h" +#include +#include ClientlessPlayerMgr* ClientlessPlayerMgr::instance() { @@ -152,6 +154,17 @@ std::deque const* ClientlessPlayerMgr::GetQueuedActions(uint32 void ClientlessPlayerMgr::Update(uint32 diff) { + using Clock=std::chrono::steady_clock; + static std::array costs{}; + static auto window=Clock::now(); + static uint32 ticks=0,slowGuid=0; + static uint64 slowUs=0; + ++ticks; + auto measure=[&](unsigned slot,auto&& update) + { + auto begin=Clock::now();update(); + costs[slot]+=std::chrono::duration_cast(Clock::now()-begin).count(); + }; // Damage can arrive from different map workers. Hand GUID-only notices to // the world-thread controller, preserving order within each character. decltype(_damageNotices) damageNotices; @@ -161,13 +174,13 @@ void ClientlessPlayerMgr::Update(uint32 diff) { std::lock_guard lock(_damageMutex); damageNotices.swap(_damageNotices); emoteNotices.swap(_emoteNotices); helpNotices.swap(_helpNotices); groupJoinNotices.swap(_groupJoinNotices); } // CLIENTLESS_PLAYER_CORE_C8_2_TRAVEL_ROBUSTNESS - sClientlessTravelMgr->Update(diff); + measure(0,[&]{sClientlessTravelMgr->Update(diff);}); // CLIENTLESS_PLAYER_CORE_C8_3_LOCAL_PERCEPTION - sClientlessPerceptionMgr->Update(diff); + measure(1,[&]{sClientlessPerceptionMgr->Update(diff);}); // CLIENTLESS_PLAYER_CORE_C8_4_ACTION_EXECUTOR - sClientlessTransportMgr->Update(diff); - sClientlessFlightMgr->Update(diff); - sClientlessActionExecutorMgr->Update(diff); + measure(2,[&]{sClientlessTransportMgr->Update(diff);}); + measure(3,[&]{sClientlessFlightMgr->Update(diff);}); + measure(4,[&]{sClientlessActionExecutorMgr->Update(diff);}); for (auto itr = _records.begin(); itr != _records.end(); ) { @@ -188,6 +201,7 @@ void ClientlessPlayerMgr::Update(uint32 diff) if (state == ClientlessPlayerState::Online && player) { + auto begin=Clock::now(); if (!record.OnlineNotified) { record.OnlineNotified = true; @@ -214,6 +228,9 @@ void ClientlessPlayerMgr::Update(uint32 diff) record.Controller->OnGroupJoinRequest(player,ObjectAccessor::FindPlayer(source)); record.Controller->Update(player, diff); } + auto elapsed=uint64(std::chrono::duration_cast(Clock::now()-begin).count()); + costs[5]+=elapsed; + if(elapsed>slowUs){slowUs=elapsed;slowGuid=characterGuid;} } else if (state == ClientlessPlayerState::Removing && !record.LogoutNotified) { @@ -224,6 +241,14 @@ void ClientlessPlayerMgr::Update(uint32 diff) ++itr; } + auto now=Clock::now(); + auto wall=std::chrono::duration_cast(now-window).count(); + if(wall>=30000) + { + TC_LOG_INFO("server.worldserver","CPC_PERF windowMs=%llu ticks=%u players=%u travelMs=%.3f perceptionMs=%.3f transportMs=%.3f flightMs=%.3f actionsMs=%.3f controllersMs=%.3f slowGuid=%u slowMs=%.3f", + static_cast(wall),ticks,uint32(_records.size()),costs[0]/1000.0,costs[1]/1000.0,costs[2]/1000.0,costs[3]/1000.0,costs[4]/1000.0,costs[5]/1000.0,slowGuid,slowUs/1000.0); + costs={};ticks=0;slowUs=0;slowGuid=0;window=now; + } } void ClientlessPlayerMgr::DamageTaken(Player* player, Unit* attacker, uint32 damage) diff --git a/src/server/game/World/ClientlessTravelGraphMgr.cpp b/src/server/game/World/ClientlessTravelGraphMgr.cpp index 27919bebe4..8755b97d9a 100644 --- a/src/server/game/World/ClientlessTravelGraphMgr.cpp +++ b/src/server/game/World/ClientlessTravelGraphMgr.cpp @@ -12,6 +12,8 @@ #include "SpellInfo.h" #include +#include +#include #include #include #include @@ -101,6 +103,8 @@ ClientlessPortalUse const* ClientlessTravelGraphMgr::GetPortalUse(uint32 edgeDat bool ClientlessTravelGraphMgr::Reload() { + _positionProbes.Clear(); + _bridgeNeighborhoods.clear(); _nodes.clear(); _edges.clear(); _edgePoints.clear(); @@ -516,7 +520,31 @@ bool ClientlessTravelGraphMgr::PlanAdaptiveRoute( std::unordered_set const* blockedBridgeKeys, ClientlessTravelLocationGoal const* locationGoal) { - sClientlessFlightMgr->BeginPlanning(); + struct Trace + { + ClientlessGraphProbeCache& Cache;std::string const& Target; + std::chrono::steady_clock::time_point Start=std::chrono::steady_clock::now(); + std::uint64_t Hits=Cache.Hits,Calls=0,NavUs=0,StartUs=0,StartCalls=0,FlightUs=0,FlightCalls=0; + ~Trace() + { + auto us=std::chrono::duration_cast(std::chrono::steady_clock::now()-Start).count(); + if(us>=50000)TC_LOG_INFO("server.worldserver","CPC_ROUTE target='%s' totalMs=%.3f navMs=%.3f probes=%llu cacheHits=%llu cacheSize=%u startMs=%.3f startCalls=%llu flightMs=%.3f flightCalls=%llu",Target.c_str(),us/1000.0,NavUs/1000.0,static_cast(Calls),static_cast(Cache.Hits-Hits),uint32(Cache.Size()),StartUs/1000.0,static_cast(StartCalls),FlightUs/1000.0,static_cast(FlightCalls)); + } + } trace{_positionProbes,destinationNode}; + + // Reuse all start-point observations only WITHIN this immutable planning + // call, including its two search tiers. Never cache live Player paths globally. + std::map startProbes; + auto analyzeStart=[&](Position const& target,ClientlessPathDiagnostics& result) + { + auto key=ClientlessGraphProbeCache::MakeKey(player->GetMapId(),player->GetInstanceId(),player->GetPositionX(),player->GetPositionY(),player->GetPositionZ(),target.GetPositionX(),target.GetPositionY(),target.GetPositionZ()); + auto found=startProbes.find(key);if(found!=startProbes.end()){result=found->second;return;} + auto begin=std::chrono::steady_clock::now();++trace.StartCalls; + sClientlessTravelMgr->AnalyzePath(const_cast(player),target,result); + trace.StartUs+=std::chrono::duration_cast(std::chrono::steady_clock::now()-begin).count(); + startProbes.emplace(key,result); + }; + sClientlessFlightMgr->BeginPlanning(player); struct FlightPlanningScope { ~FlightPlanningScope() { sClientlessFlightMgr->BeginPlanning(); } @@ -551,7 +579,10 @@ bool ClientlessTravelGraphMgr::PlanAdaptiveRoute( if (!flightEnabled || from.MapId != map) return std::numeric_limits::infinity(); Position a,b; a.Relocate(from.X,from.Y,from.Z,from.O); b.Relocate(x,y,z); - return sClientlessFlightMgr->Estimate(player,map,a,b); + auto begin=std::chrono::steady_clock::now();++trace.FlightCalls; + float result=sClientlessFlightMgr->Estimate(player,map,a,b); + trace.FlightUs+=std::chrono::duration_cast(std::chrono::steady_clock::now()-begin).count(); + return result; }; if (startRadius <= 0.0f) startRadius = 500.0f; @@ -663,7 +694,7 @@ bool ClientlessTravelGraphMgr::PlanAdaptiveRoute( Position entryTarget; entryTarget.Relocate(node.X, node.Y, node.Z, node.O); ClientlessPathDiagnostics entryNav; - sClientlessTravelMgr->AnalyzePath(const_cast(player), entryTarget, entryNav); + analyzeStart(entryTarget,entryNav); bool const reachable = entryNav.Calculated && entryNav.Complete && entryNav.FinalDistance <= 1.5f && entryNav.MaxSegmentLength <= 8.0f && entryNav.MaxZDelta <= 5.0f && entryNav.PathLength <= 500.0f; @@ -789,7 +820,7 @@ bool ClientlessTravelGraphMgr::PlanAdaptiveRoute( bool const alreadyThere = candidate.DistanceSq <= 2.25f; if (!alreadyThere) { - sClientlessTravelMgr->AnalyzePath(const_cast(player), target, nav); + analyzeStart(target,nav); if (!nav.Calculated || !nav.Complete || nav.Suspicious || nav.FinalDistance > 1.5f || nav.PathLength > 500.0f) continue; @@ -856,8 +887,18 @@ bool ClientlessTravelGraphMgr::PlanAdaptiveRoute( // from Arthoran's current position. No Player/WorldObject is required. if (targetMap != from.MapId) return VcorePositionPathResult{}; - return PathGenerator::AnalyzePositions(from.MapId, from.MapId, - player->GetInstanceId(), a, b, NAV_GROUND); + auto key=ClientlessGraphProbeCache::MakeKey(from.MapId,player->GetInstanceId(),from.X,from.Y,from.Z,x,y,z); + if(auto cached=_positionProbes.Find(key)) + { + VcorePositionPathResult result;result.Result=VcorePositionPathResult::Status::Complete; + result.PathLength=cached->Length;result.FinalDistance=cached->Final;result.DirectDistance=cached->Direct; + return result; + } + auto started=std::chrono::steady_clock::now();++trace.Calls; + auto result=PathGenerator::AnalyzePositions(from.MapId, from.MapId,player->GetInstanceId(),a,b,NAV_GROUND); + trace.NavUs+=std::chrono::duration_cast(std::chrono::steady_clock::now()-started).count(); + _positionProbes.Record(key,result.IsComplete(),result.PathLength,result.FinalDistance,result.DirectDistance); + return result; }; // Select the closest eligible waypoint per directed road geometrically. @@ -1319,9 +1360,26 @@ bool ClientlessTravelGraphMgr::PlanAdaptiveRoute( } } - for (auto const& pair : _nodes) + // Fixed graph-node neighborhoods are shared across plans. Keep a cube + // superset: the original exact sphere/path/block-list checks still run. + uint32 radiusBits=0;std::memcpy(&radiusBits,&bridgeRadius,sizeof(float)); + std::string neighborhoodKey=from.Key+":"+std::to_string(radiusBits); + auto neighborhood=_bridgeNeighborhoods.find(neighborhoodKey); + if(neighborhood==_bridgeNeighborhoods.end()) { - ClientlessTravelNode const& to = pair.second; + if(_bridgeNeighborhoods.size()>=4096)_bridgeNeighborhoods.clear(); + std::vector candidates; + for(auto const& entry:_nodes) + { + auto const& to=entry.second; + if(to.MapId==from.MapId && std::fabs(to.X-from.X)<=bridgeRadius && + std::fabs(to.Y-from.Y)<=bridgeRadius && std::fabs(to.Z-from.Z)<=bridgeRadius)candidates.push_back(entry.first); + } + neighborhood=_bridgeNeighborhoods.emplace(neighborhoodKey,std::move(candidates)).first; + } + for (auto const& key : neighborhood->second) + { + ClientlessTravelNode const& to = _nodes.at(key); if (to.Key == from.Key || to.MapId != from.MapId) continue; diff --git a/src/server/game/World/ClientlessTravelGraphMgr.h b/src/server/game/World/ClientlessTravelGraphMgr.h index 7e3cbc70ec..204d80f918 100644 --- a/src/server/game/World/ClientlessTravelGraphMgr.h +++ b/src/server/game/World/ClientlessTravelGraphMgr.h @@ -11,6 +11,7 @@ #define VCORE_CLIENTLESS_TRAVEL_GRAPH_MGR_H #include "Common.h" +#include "ClientlessGraphProbeCache.h" #include #include @@ -171,6 +172,8 @@ public: private: std::string FindNearestStartNode(Player const* player, float maxDistance) const; + ClientlessGraphProbeCache _positionProbes; + std::unordered_map> _bridgeNeighborhoods; bool _loaded = false; uint32 _edgeCount = 0; diff --git a/src/server/scripts/Custom/clientless/adventure/profiles/arthoran.lua b/src/server/scripts/Custom/clientless/adventure/profiles/arthoran.lua index 290c2c884d..723c62796c 100644 --- a/src/server/scripts/Custom/clientless/adventure/profiles/arthoran.lua +++ b/src/server/scripts/Custom/clientless/adventure/profiles/arthoran.lua @@ -1,14 +1,19 @@ --- Arthoran: first named AdventureBot character profile. +-- Arthoran: persistent managed level-60 warrior; tank in groups, Arms solo. -- These values bias future weighted decisions; they do not prescribe a route. return { identity = { name = "Arthoran", + profile_version = 2, level = 60, class = 1, + specialization = 2, solo_specialization = 0, role = "tank", seed = 26, + home_zone = 12, home_capital = 1519, + favorite_zone = 139, favorite_region = "eastern_plaguelands", home_region = "elwynn", }, behavior = { enabled = true, - daily_schedule = false, -- First acceptance: social intent, planner remains inactive. + daily_schedule = true, + allow_retreat = false, -- Arthoran holds his ground even when outmatched. initial_intent = "social", initial_location = "goldshire_tavern", initial_duration_seconds = 3600, @@ -36,14 +41,19 @@ return { personality = { group_loyalty = 0.35, -- Chance to stay when the scheduled intent changes. sociability = 0.65, - adventurousness = 0.70, - wanderlust = 0.30, - tavern_affinity = 0.85, - wilderness_affinity = 0.65, + aggression = 1.00, + home_attachment = 0.80, + adventurousness = 0.20, + wanderlust = 0.20, + tavern_affinity = 1.00, + wilderness_affinity = 0.25, city_affinity = 0.35, - danger_tolerance = 0.70, + danger_tolerance = 1.00, }, + inventory = { keep_items = {37460, 6256, 5956, 2901, 7005, 6217} }, + zone_affinity = { [12] = 4, [1519] = 5, [139] = 3 }, + region_affinity = { elwynn = 1.00, stormwind = 0.70, @@ -52,11 +62,13 @@ return { }, activity_affinity = { - tavern = 0.90, - hunting = 0.75, + adventure = 0.25, + instance = 0.60, raid = 0.25, rest = 1.00, profession = 0.60, + tavern = 3.00, + hunting = 0.35, travelling = 0.35, city_visit = 0.35, - social = 0.60, + social = 2.00, leisure = 0.60, economy = 0.45, }, diff --git a/tests/clientless/test_adventure_combat_reactions.py b/tests/clientless/test_adventure_combat_reactions.py index eaf45aec77..1dce40e396 100644 --- a/tests/clientless/test_adventure_combat_reactions.py +++ b/tests/clientless/test_adventure_combat_reactions.py @@ -94,8 +94,8 @@ struct Travel{ struct Behavior{int interrupts=0;void InterruptForCombat(Player*){++interrupts;}}behavior;auto sAdventureBehaviorMgr=&behavior; ''' code+=r''' -struct Profile{unsigned Class=0,Specialization=0,SoloSpecialization=0;std::string SourcePath;}; -struct Lua{Profile*GetAdventureProfile(unsigned){return nullptr;}void DismountGroundMount(Player*,char const*){}}lua;auto sClientlessLuaEngine=&lua; +struct Profile{bool AllowRetreat=true;unsigned Class=0,Specialization=0,SoloSpecialization=0;std::string SourcePath;}; +struct Lua{Profile* profile=nullptr;Profile*GetAdventureProfile(unsigned){return profile;}void DismountGroundMount(Player*,char const*){}}lua;auto sClientlessLuaEngine=&lua; ''' # production Update uses direct Position members and creature near point. code=code.replace('float x=0,y=0,z=0;','union{float x=0;float m_positionX;};union{float y=0;float m_positionY;};union{float z=0;float m_positionZ;};',1) @@ -197,6 +197,11 @@ int main(){ // Group join aborts immediately, even while critically injured. p.grouped=true;p.health=1;assert(!mgr.Flee(&p,r)&&!r.Fleeing&&!travel.present); assert(!mgr.Flee(&p,r));p.grouped=false;p.health=100; + // Explicit profile refusal cancels retreat without suppressing combat. + Profile steadfast;steadfast.AllowRetreat=false;lua.profile=&steadfast; + p.health=1;r.Fleeing=true;travel.present=true; + assert(!mgr.Flee(&p,r)&&!r.Fleeing&&!travel.present&&r.AI); + lua.profile=nullptr;p.health=100; // Blocked paths preserve native self-defence; no teleport/resurrection. travel={};travel.path=false;GameTime::now=1004; int ticks=r.AI->ticks;assert(mgr.Flee(&p,r)&&r.Fleeing&&travel.starts==0&&r.AI->ticks>ticks); diff --git a/tests/clientless/test_adventure_groups.py b/tests/clientless/test_adventure_groups.py index 0384a31f04..169b19144c 100644 --- a/tests/clientless/test_adventure_groups.py +++ b/tests/clientless/test_adventure_groups.py @@ -29,7 +29,7 @@ struct Map{bool IsDungeon(){return false;}}map; struct Member{Player*player=nullptr;Member*nextMember=nullptr;Player*GetSource(){return player;}Member*next(){return nextMember;}}; constexpr int GROUP_REMOVEMETHOD_LEAVE=1; struct Group{struct Slot{ObjectGuid guid;};std::vectorslots;auto const&GetMemberSlots(){return slots;}void RemoveMember(ObjectGuid,int);Member*members=nullptr;Member*GetFirstMember(){return members;}void ChangeLeader(ObjectGuid g){leader=g;}void SendUpdate(){}ObjectGuid leader;unsigned count=2;bool raid=false;ObjectGuid GetLeaderGUID(){return leader;}unsigned GetMembersCount(){return count;}bool isRaidGroup(){return raid;}void ConvertToRaid(){raid=true;}void ConvertToGroup(){raid=false;}}; -struct WorldPacket{unsigned value=0;WorldPacket(int,int=0){}WorldPacket&operator<<(unsigned n){value=n;return *this;}};constexpr int MSG_SET_RAID_DIFFICULTY=2,RAID_DIFFICULTY_25MAN_NORMAL=1;constexpr int CMSG_PARTY_INVITE=1,EMOTE_ONESHOT_WAVE=1; +struct WorldPacket{unsigned value=0;WorldPacket(int,int=0){}WorldPacket&operator<<(unsigned n){value=n;return *this;}};constexpr int MSG_SET_RAID_DIFFICULTY=2,RAID_DIFFICULTY_25MAN_NORMAL=1;constexpr int ALLIANCE=0,LANG_COMMON=7,LANG_ORCISH=1;constexpr int CMSG_PARTY_INVITE=1,EMOTE_ONESHOT_WAVE=1; namespace WorldPackets{namespace Party{struct PartyInviteClient{PartyInviteClient(WorldPacket&&){}std::string TargetName;ObjectGuid TargetGUID;};}} struct Session{Player*owner;bool clientless=true;bool IsClientless(){return clientless;}void HandlePartyInviteOpcode(WorldPackets::Party::PartyInviteClient&);void HandleSetRaidDifficultyOpcode(WorldPacket&);}; struct Player{ObjectGuid guid;Group*group=nullptr,*invite=nullptr;Session session{this};bool combat=false;unsigned zone=1519,level=60,team=0;float distance=10; @@ -39,6 +39,7 @@ struct Player{ObjectGuid guid;Group*group=nullptr,*invite=nullptr;Session sessio Group*GetGroup(){return group;}Group*GetGroupInvite(){return invite;}Session*GetSession(){return &session;}std::string GetName(){return std::to_string(guid.id);} bool IsInMap(Player*o){return zone==o->zone;}bool IsInPhase(Player*){return true;}bool CanSeeOrDetect(Player*){return true;} float GetExactDist(Player*){return distance;}bool IsWithinLOSInMap(Player*){return true;} + std::vector people;bool afk=false;std::string whisper;bool isAFK(){return afk;}void GetPlayerListInGrid(std::vector& out,int){out=people;}void Whisper(std::string const& s,int,Player*){whisper=s;} void SetFacingToObject(Player*){}void HandleEmoteCommand(int){} }; namespace ObjectAccessor{std::unordered_map players;Player*FindPlayer(ObjectGuid g){auto it=players.find(g);return it==players.end()?nullptr:it->second;}} @@ -58,7 +59,7 @@ int messages=0; void AdventureGroupMgr::Speak(Player*,std::string const&){++messages;} ''' code+=fun('float Sociability(')+'\n'+fun('bool Ready(')+'\n'+fun('std::string Activity(') -for name in ['bool AdventureGroupMgr::BotLed(', 'bool AdventureGroupMgr::Forming(', 'bool AdventureGroupMgr::PlannedTransition(', 'bool AdventureGroupMgr::Accept(', 'void AdventureGroupMgr::Update(', 'void AdventureGroupMgr::Invite(', 'void AdventureGroupMgr::RequestPlayerAdmission(', 'bool AdventureGroupMgr::PlayerLeadership(', 'bool AdventureGroupMgr::EnforceCapacity(']:code+='\n'+fun(name) +for name in ['std::string AdventureGroupMgr::RecruitmentText(', 'bool AdventureGroupMgr::RecruitPlayer(', 'bool AdventureGroupMgr::BotLed(', 'bool AdventureGroupMgr::Forming(', 'bool AdventureGroupMgr::PlannedTransition(', 'bool AdventureGroupMgr::Accept(', 'void AdventureGroupMgr::Update(', 'void AdventureGroupMgr::Invite(', 'void AdventureGroupMgr::RequestPlayerAdmission(', 'bool AdventureGroupMgr::PlayerLeadership(', 'bool AdventureGroupMgr::EnforceCapacity(']:code+='\n'+fun(name) code+=r''' int main(){ using namespace AdventureGroupPolicy; @@ -112,6 +113,17 @@ int main(){ mgr.RequestPlayerAdmission(&a,&human);assert(invites==5&&human.invite==&party); human.group=&party;Member humanSlot;humanSlot.player=&human;party.members=&humanSlot; mgr.Update(&a);assert(party.leader==human.guid&&!mgr.BotLed(&a)); + // Inform nearby humans of the actual NEXT destination before invitation. + {mgr={};Player human;human.guid={999};human.session.clientless=false; + ObjectAccessor::players[human.guid]=&human;a.group=nullptr;a.people={&human}; + behavior.plans[a.guid.id]={true,true,"groupformation","capital","raid","target"}; + int before=invites;assert(mgr.RecruitPlayer(&a,"raid")); + assert(invites==before+1&&human.invite&&a.whisper.find("Schlachtzug")!=std::string::npos&&a.whisper.find("Stormwind")!=std::string::npos); + human.invite=nullptr;assert(!mgr.RecruitPlayer(&a,"raid")); // shared cooldown even after decline + GameTime::now+=601;human.afk=true;assert(!mgr.RecruitPlayer(&a,"raid")); + human.afk=false;human.team=1;assert(!mgr.RecruitPlayer(&a,"raid")); + human.team=0;human.group=&pending;assert(!mgr.RecruitPlayer(&a,"raid"));} + } ''' with tempfile.TemporaryDirectory() as d: diff --git a/tests/clientless/test_adventure_intent_state.py b/tests/clientless/test_adventure_intent_state.py index 1917541f96..6bd85ada03 100644 --- a/tests/clientless/test_adventure_intent_state.py +++ b/tests/clientless/test_adventure_intent_state.py @@ -30,6 +30,7 @@ struct Player { Map* GetMap(){return ↦} } player; namespace ObjectAccessor{Player*FindPlayer(Guid){return nullptr;}} +struct Lua{void UpdateWorldPresence(Player*,std::string const&,bool){}}lua;auto sClientlessLuaEngine=&lua; struct BotGroups{bool BotLed(Player*){return false;}bool FollowMember(Player*){return false;}}botGroups;auto sAdventureGroupMgr=&botGroups; struct Encounters{bool active=false;bool Active(Player*){return active;}}encounters;auto sAdventureEncounterMgr=&encounters; bool unsafe=false;bool Unsafe(Player*){return unsafe;} diff --git a/tests/clientless/test_adventure_leisure.py b/tests/clientless/test_adventure_leisure.py index 2ef03f30a8..74cf64de8a 100644 --- a/tests/clientless/test_adventure_leisure.py +++ b/tests/clientless/test_adventure_leisure.py @@ -53,11 +53,11 @@ bool Unsafe(Player* p){return p->combat||p->flying;} constexpr int PATHFIND_NORMAL=1; struct PathGenerator{ struct Point{float x,y,z;};std::vector path; - static inline bool reachable=true,broken=false;static inline int type=1; + static inline bool reachable=true,broken=false;static inline int type=1;static inline float bend=20; PathGenerator(Player*){}void SetPathLengthLimit(float){} bool CalculatePath(Position const&a,Position const&b){ path.clear();int n=int(a.GetExactDist(b)/4)+1; - for(int i=0;i<=n;++i){float t=float(i)/n;path.push_back({a.m_positionX+(b.m_positionX-a.m_positionX)*t,a.m_positionY+(b.m_positionY-a.m_positionY)*t+20*std::sin(3.14159265f*t),a.m_positionZ+(b.m_positionZ-a.m_positionZ)*t});} + for(int i=0;i<=n;++i){float t=float(i)/n;path.push_back({a.m_positionX+(b.m_positionX-a.m_positionX)*t,a.m_positionY+(b.m_positionY-a.m_positionY)*t+bend*std::sin(3.14159265f*t),a.m_positionZ+(b.m_positionZ-a.m_positionZ)*t});} if(broken && path.size()>5) path[5].z+=10; return reachable; } @@ -89,7 +89,7 @@ class AdventureBehaviorMgr{public: enum class Phase{Idle,OnSite,Switching}; struct Record{ AdventureInstancePolicy::Visit Instance; - std::string Intent="leisure",Location="city",WantedIntent,WantedLocation,Detail;Position Anchor;uint32 AnchorMap=0,MicroTimer=240000;std::mt19937 Random{26}; + std::string Intent="leisure",Location="city",WantedIntent,WantedLocation,Detail;Position Anchor;uint32 AnchorMap=0,MicroTimer=240000,RetryTimer=0;std::mt19937 Random{26}; time_t Deadline=0;bool Dirty=false;std::unordered_set Tried;Phase State=Phase::OnSite; struct LeisureScene{bool Active=false,Flying=false,Moving=false,StopRequested=false,Returning=false,PlannedWalk=false,Progressive=false;std::string Kind;Position Origin;std::vector Route,WalkTrace;std::size_t Step=0,OutboundSteps=0;uint32 Elapsed=0,LegElapsed=0,PauseMs=0,CompanionWait=0,CompletedLegs=0;ClientlessCompanionScene Companion;}Leisure; }; @@ -219,6 +219,12 @@ int main(){ combatMgr.available=true;assert(mgr.LeisureAvailable(&p,r,{"training"})); assert(mgr.StartLeisure(&p,r,{"training"}) && combatMgr.starts==1); r.Intent="adventure";assert(!mgr.LeisureAvailable(&p,r,{"training"})); + // Recruitment walks stay near the meeting point, also inside a tavern. + mgr.ResetLeisure(&p,r);r.Intent="groupformation";r.MicroTimer=45000;terrain.outdoors=false;p.outdoors=false;PathGenerator::bend=2; + assert(mgr.StartLeisure(&p,r,{"walk"})); + for(auto const& point:r.Leisure.Route)assert(point.GetExactDist(r.Leisure.Origin)<=18); + assert(!mgr.LeisureAvailable(&p,r,{"ride"}));mgr.ResetLeisure(&p,r);terrain.outdoors=true;PathGenerator::bend=20; + r.RetryTimer=30000;r.Tried.insert("old");mgr.SetWanted(&p,r,"rest","new",1234);assert(r.RetryTimer==0&&r.Tried.empty()&&r.Deadline==1234); } ''' diff --git a/tests/clientless/test_adventure_native_lfg.py b/tests/clientless/test_adventure_native_lfg.py new file mode 100644 index 0000000000..90fc1b0ba9 --- /dev/null +++ b/tests/clientless/test_adventure_native_lfg.py @@ -0,0 +1,33 @@ +"""Run real bot Speak against native channel/zone adapters.""" +from pathlib import Path +import subprocess,tempfile +root=Path(__file__).resolve().parents[2];s=(root/'src/server/game/World/AdventureGroupMgr.cpp').read_text() +a=s.index('void AdventureGroupMgr::Speak(');b=s.index('std::string AdventureGroupMgr::RecruitmentText(',a) +code=r''' +#include +#include +#include +#include +using uint32=unsigned;using time_t=long; +#define TC_LOG_INFO(...) ((void)0) +constexpr int ALLIANCE=0,LANG_COMMON=7,LANG_ORCISH=1; +namespace AdventureGroupPolicy{bool Capital(unsigned z){return z==1519;}} +namespace GameTime{time_t now=100;time_t GetGameTime(){return now;}} +struct Entry{unsigned ID=26;}entry; +struct Store{unsigned expected;Entry*LookupEntry(unsigned id){assert(id==expected);return &entry;}}sChatChannelsStore{26},sAreaTableStore{1519}; +struct Channel; +struct Player{bool allowed=true;std::vector channels;unsigned GetZoneId(){return 1519;}unsigned GetTeam(){return 0;}unsigned GetGUID(){return 1;}bool CanJoinConstantChannelInZone(Entry*,Entry*){return allowed;}auto&GetJoinedChannels(){return channels;}}; +struct Channel{int messages=0;bool deny=false;void JoinChannel(Player*p,char const*){if(!deny)p->channels.push_back(this);}void Say(unsigned,std::string const&,int){++messages;}}channel; +struct Manager{Channel*GetJoinChannel(unsigned id,char const* name,Entry*){assert(id==26 && std::string(name).empty());return &channel;}}manager; +namespace ChannelMgr{Manager*forTeam(unsigned){return &manager;}} +struct AdventureGroupMgr{time_t _nextChannelMessage=0;void Speak(Player*,std::string const&);}; +'''+s[a:b]+r''' +int main(){AdventureGroupMgr m;Player p;m.Speak(&p,"test");assert(channel.messages==1&&p.channels.size()==1);m.Speak(&p,"test");assert(channel.messages==1); + GameTime::now+=16;p.allowed=false;m.Speak(&p,"test");assert(channel.messages==1); + GameTime::now+=16;p.allowed=true;p.channels.clear();channel.deny=true;m.Speak(&p,"test");assert(channel.messages==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('Native LFG: ID26, localized standard channel, zone eligibility, join verification and population throttle passed') diff --git a/tests/clientless/test_adventure_population.py b/tests/clientless/test_adventure_population.py index 016e1d6fce..24626fb7a7 100644 --- a/tests/clientless/test_adventure_population.py +++ b/tests/clientless/test_adventure_population.py @@ -17,7 +17,7 @@ code=r''' using uint32=unsigned;using uint64=unsigned long long; using namespace AdventurePopulationPolicy; namespace GameTime{time_t now=100000;time_t GetGameTime(){return now;}} -struct ObjectGuid{enum{Player};unsigned id;ObjectGuid(int,unsigned i):id(i){}}; +struct ObjectGuid{enum{Player};unsigned id;ObjectGuid(int,unsigned i):id(i){} unsigned GetCounter(){return id;}}; namespace HighGuid{constexpr int Player=0;} namespace ObjectAccessor{std::unordered_setconnected;bool FindConnectedPlayer(ObjectGuid g){return connected.count(g.id);}} enum class ClientlessPlayerState{Offline,Loading,Online,Removing}; @@ -26,14 +26,18 @@ struct Core{bool succeed=true;std::vectorstarted;bool StartPlayer(unsi int CreateClientlessAdventureLuaController(){return 0;} float sample=.5;float frand(int,int){return sample;} #define TC_LOG_INFO(...) ((void)0) +struct Profile{unsigned ProfileVersion=0,Class=0,Level=0;std::string SourcePath;}; +struct Player{ObjectGuid GetGUID(){return ObjectGuid{ObjectGuid::Player,26};}}; +struct Lua{Profile* profile=nullptr;Profile* GetAdventureProfile(unsigned){return profile;}}lua;auto sClientlessLuaEngine=&lua; struct AdventurePopulationMgr{ struct Bot{uint32 Guid,Account,Pool;uint64 Created;uint32 Failures=0;time_t Retry=0;}; unsigned _cursor=0,_rotationAge=86400;double _rotationChance=.15; std::array_targets{};std::vector_bots;std::unordered_set_considered; std::vector>generated; + std::unordered_map _checks;void Online(Player*); void Step();void Generate(unsigned pool,unsigned replace=0){generated.push_back({pool,replace});} }; -'''+function('void AdventurePopulationMgr::Step()')+r''' +'''+function('void AdventurePopulationMgr::Step()')+function('void AdventurePopulationMgr::Online(')+r''' int main(){ assert(Pool(1,true)==0 && Pool(1,false)==1 && Pool(2,true)==2 && Pool(2,false)==3); assert(!Replace(true,true,false,100,0,0,1));assert(!Replace(false,false,false,100,0,0,1)); @@ -41,6 +45,13 @@ int main(){ assert(Replace(false,true,false,100,100,.1,.2));assert(!Replace(false,true,false,100,0,.3,.2)); assert(!Deficit(5,3,2) && Deficit(5,2,1)==2 && !Deficit(0,2,0)); AdventurePopulationMgr mgr; + Player named;Profile profile;lua.profile=&profile; + mgr.Online(&named);assert(mgr._checks.empty()); + profile.ProfileVersion=2;profile.Class=1;profile.Level=60;profile.SourcePath="profiles/arthoran.lua"; + mgr.Online(&named);assert(mgr._checks.count(26)); + mgr._checks.clear();profile.Level=0;mgr.Online(&named);assert(mgr._checks.empty()); + profile.Level=60;profile.ProfileVersion=0;mgr.Online(&named);assert(mgr._checks.empty()); + lua.profile=nullptr;mgr.Online(&named);assert(mgr._checks.empty()); mgr.Step();assert(mgr.generated.empty() && core.started.empty()); mgr._targets={1,2,3,4}; mgr.Step();assert(mgr.generated.back().first==0);mgr.Step();assert(mgr.generated.back().first==1); diff --git a/tests/clientless/test_adventure_retry_policy.py b/tests/clientless/test_adventure_retry_policy.py new file mode 100644 index 0000000000..5946b39661 --- /dev/null +++ b/tests/clientless/test_adventure_retry_policy.py @@ -0,0 +1,24 @@ +"""Population retry spacing is bounded and increases only after failures.""" +from pathlib import Path +import subprocess,tempfile +root=Path(__file__).resolve().parents[2] +code=r''' +#include "AdventureBehaviorPolicy.h" +#include +#include +int main(){using AdventureBehaviorPolicy::TravelRetryMs; + assert(TravelRetryMs(1,0)==2000&&TravelRetryMs(2,0)==4000); + assert(TravelRetryMs(3,0)==8000&&TravelRetryMs(4,0)==16000); + assert(TravelRetryMs(5,0)==30000&&TravelRetryMs(1000,1000)==30000); + assert(TravelRetryMs(std::numeric_limits::max(),1000)==30000); + assert(TravelRetryMs(1,1000)==3000&&TravelRetryMs(1,10000)==3000); + for(unsigned count=1;count<100;++count)for(unsigned jitter=0;jitter<=1000;jitter+=25){ + auto value=TravelRetryMs(count,jitter);assert(value>=2000&&value<=30000); + } +} +''' +with tempfile.TemporaryDirectory() as d: + p=Path(d);(p/'test.cpp').write_text(code) + subprocess.run(['c++','-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('Travel failures: exponential/jittered retry spacing, strict 30s bound and overflow safety passed') diff --git a/tests/clientless/test_adventure_world_presence.py b/tests/clientless/test_adventure_world_presence.py new file mode 100644 index 0000000000..8310f7a65d --- /dev/null +++ b/tests/clientless/test_adventure_world_presence.py @@ -0,0 +1,73 @@ +"""Execute production PvP/Worgen presence and ground-mount methods.""" +from pathlib import Path +import subprocess,tempfile +root=Path(__file__).resolve().parents[2] +s=(root/'src/server/game/World/ClientlessLuaEngine.cpp').read_text() +def fn(sig): + a=s.index(sig);b=s.index('{',a);e=b+1;n=1 + while n:n+=(s[e]=='{')-(s[e]=='}');e+=1 + return s[a:e] +code=r''' +#include +#include +#include +#include +#include +#include +using uint32=unsigned; +#define TC_LOG_INFO(...) ((void)0) +constexpr int CHARACTER_FLAG_PVP_DESIRED=65536,PLAYER_FLAGS=1,PLAYER_FLAGS_IN_PVP=512,RACE_WORGEN=22, + SPELL_TWO_FORMS_RACIAL=68996,SPELL_ALTERED_FORM_RACIAL=97709,SPELL_AURA_WORGEN_ALTERED_FORM=1,UNIT_STATE_CASTING=2,MOVE_RUN=3; +namespace GameTime{time_t now=100;time_t GetGameTime(){return now;}} +unsigned roll=0;unsigned urand(unsigned,unsigned){return roll;} +struct Guid{unsigned GetCounter(){return 26;}}; +struct Map{bool dungeon=false;bool IsDungeon(){return dungeon;}}map; +struct Player{ + unsigned race=22,level=60,flags=0,characterFlags=0,display=0,casts=0,updates=0; + bool world=true,alive=true,combat=false,mounted=false,transport=false,taxi=false,flying=false,teleport=false,casting=false,outdoors=true,pvp=false,fail=false; + float speed=1;std::unordered_setspells,auras;struct{unsigned EndTimer=10;}pvpInfo; + Map*GetMap(){return ↦}Guid GetGUID(){return {};}std::string GetName(){return "Test";}bool IsInWorld(){return world;}void AddCharacterFlag(int f){characterFlags|=f;}void SetFlag(int,int f){flags|=f;} + bool IsPvP(){return pvp;}void UpdatePvP(bool on,bool){pvp=on;pvpInfo.EndTimer=0;++updates;} + unsigned getRace(){return race;}unsigned getLevel(){return level;}bool IsAlive(){return alive;}bool IsInCombat(){return combat;} + bool IsMounted(){return mounted;}bool GetTransport(){return transport;}bool IsInFlight(){return taxi;}bool IsFlying(){return flying;} + bool IsBeingTeleported(){return teleport;}bool HasUnitState(int){return casting;}bool HasAuraType(int){return auras.count(97709);} + bool HasSpell(unsigned id){return spells.count(id);}void LearnSpell(unsigned id,bool){spells.insert(id);} + bool HasAura(unsigned id){return auras.count(id);}bool IsOutdoors(){return outdoors;} + void CastSpell(Player*,unsigned id,bool){++casts;if(fail)return;if(id==97709)auras.insert(id);if(id==68996)auras.erase(97709);if(id==87840){auras.insert(id);mounted=true;display=29422;speed=2;}} + void Mount(unsigned id){display=id;mounted=true;}void SetSpeedRate(int,float v){speed=v;}unsigned GetMountID(){return display;} + void RemoveAurasDueToSpell(unsigned id){auras.erase(id);}void Dismount(){mounted=false;display=0;}void UpdateSpeed(int){speed=1;} +}; +struct ClientlessAdventureProfile{bool CanGroundMount=true;std::vectorGroundMounts{2404};float GroundMountMinDistance=40,GroundMountSpeedRate=2;}profile; +struct ClientlessLuaEngine{ + std::unordered_map_worgenForms;std::unordered_map_worgenNextAttempt; + ClientlessAdventureProfile*GetAdventureProfile(unsigned){return &profile;}unsigned SelectGroundMountDisplayId(unsigned){return 2404;} + void UpdateWorldPresence(Player*,std::string const&,bool=false);bool PrepareGroundMount(Player*,float,char const*);void DismountGroundMount(Player*,char const*); +}; +'''+fn('void ClientlessLuaEngine::UpdateWorldPresence(')+fn('bool ClientlessLuaEngine::PrepareGroundMount(')+fn('void ClientlessLuaEngine::DismountGroundMount(')+r''' +int main(){ + ClientlessLuaEngine e;Player p;e.UpdateWorldPresence(&p,"adventure"); + assert(p.pvp&&p.pvpInfo.EndTimer==0&&(p.flags&512)&&(p.characterFlags&65536)&&p.HasAura(97709)); + assert(e.PrepareGroundMount(&p,100,"test")&&p.HasAura(87840)&&p.IsMounted()&&p.display==29422); + e.DismountGroundMount(&p,"test");assert(!p.IsMounted()&&!p.HasAura(87840)&&p.speed==1); + GameTime::now+=6;roll=0;e.UpdateWorldPresence(&p,"leisure",true);assert(!p.HasAura(97709)); + assert(e.PrepareGroundMount(&p,100,"test")&&p.display==2404&&!p.HasAura(87840)); + e.DismountGroundMount(&p,"test"); + p.transport=true;GameTime::now+=6;e.UpdateWorldPresence(&p,"raid");assert(!p.HasAura(97709)); + p.transport=false;e.UpdateWorldPresence(&p,"raid");assert(p.HasAura(97709)); + // Decide quiet arrival once, defer while mounted, never reroll every update. + p.mounted=true;roll=0;e.UpdateWorldPresence(&p,"rest",true);assert(p.HasAura(97709)); + p.mounted=false;GameTime::now+=6;roll=1;e.UpdateWorldPresence(&p,"rest");assert(!p.HasAura(97709)); + // Combat and mixed player instances require Worgen even with a quiet personal intent. + GameTime::now+=6;p.combat=true;e.UpdateWorldPresence(&p,"rest");assert(p.HasAura(97709));p.combat=false; + p.auras.clear();map.dungeon=true;GameTime::now+=6;e.UpdateWorldPresence(&p,"rest");assert(p.HasAura(97709));map.dungeon=false;p.auras.clear(); + // Failed native casts are throttled, not spammed every world tick. + p.fail=true;GameTime::now+=6;e.UpdateWorldPresence(&p,"instance");auto casts=p.casts; + e.UpdateWorldPresence(&p,"instance");assert(p.casts==casts); + p.race=1;p.pvp=false;p.pvpInfo.EndTimer=5;e.UpdateWorldPresence(&p,"rest");assert(p.pvp&&!p.pvpInfo.EndTimer); +} +''' +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 world presence: persistent PvP, Worgen intent/arrival decisions, travel deferral, native Running Wild, human horse and aura cleanup passed') diff --git a/tests/clientless/test_flight_area_query.py b/tests/clientless/test_flight_area_query.py index af9d82a685..eae6c75643 100644 --- a/tests/clientless/test_flight_area_query.py +++ b/tests/clientless/test_flight_area_query.py @@ -7,12 +7,13 @@ a=s.index('std::pair < uint32, uint32 > ClientlessFlightMgr::AreaZone(') b=s.index('bool ClientlessFlightMgr::Enabled(',a) code=r''' #include +#include #include #include #include #include using uint32=uint32_t; -struct Position{float GetPositionX()const{return 1;}float GetPositionY()const{return 2;}float GetPositionZ()const{return 3;}}; +struct Position{float x=1,y=2,z=3;float GetPositionX()const{return x;}float GetPositionY()const{return y;}float GetPositionZ()const{return z;}}; struct Player{int GetPhaseShift()const{return 17;}}; struct TerrainInfo{int calls=0;void GetZoneAndAreaId(int phase,uint32 map,uint32& zone,uint32& area,Position const&){assert(phase==17&&map==530);++calls;zone=3430;area=3460;}}; struct TerrainMgr{std::shared_ptr value=std::make_shared();auto LoadTerrain(uint32 map){assert(map==530);return value;}} sTerrainMgr; @@ -23,7 +24,7 @@ class ClientlessFlightMgr{public: }; ''' code+=s[a:b]+r''' -int main(){ClientlessFlightMgr mgr;Player p;Position pos;auto r=mgr.AreaZone(&p,530,pos);assert(r.first==3460&&r.second==3430);assert(mgr.AreaZone(&p,530,pos)==r);assert(sTerrainMgr.value->calls==1);} +int main(){ClientlessFlightMgr mgr;Player p;Position pos;auto r=mgr.AreaZone(&p,530,pos);assert(r.first==3460&&r.second==3430);assert(mgr.AreaZone(&p,530,pos)==r);assert(sTerrainMgr.value->calls==1);pos.x=1.0000001f;mgr.AreaZone(&p,530,pos);assert(sTerrainMgr.value->calls==2);} ''' with tempfile.TemporaryDirectory(prefix='vcore-area-query-') as folder: cpp=Path(folder)/'test.cpp';binary=Path(folder)/'test';cpp.write_text(code) diff --git a/tests/clientless/test_flight_policy.py b/tests/clientless/test_flight_policy.py index 0a79ef990c..bcd4618acc 100644 --- a/tests/clientless/test_flight_policy.py +++ b/tests/clientless/test_flight_policy.py @@ -16,6 +16,8 @@ harness=r''' #include #include #include +#include +#include #include using uint32=uint32_t; using namespace ClientlessFlightPath; @@ -39,7 +41,7 @@ struct Player{ struct Mount{uint32 DisplayId=32235,Weight=100;}; struct Profile{bool CanFreeFly=true;std::vector FlightMounts{{}};} profile; struct Lua{auto GetAdventureProfile(uint32){return &profile;}} lua; -struct Config{bool enabled=true;bool GetBoolDefault(const char*,bool){return enabled;}float GetFloatDefault(const char*,float v){return v;}std::string GetStringDefault(const char*,const char*v){return v;}} config; +struct Config{int reads=0;bool enabled=true;bool GetBoolDefault(const char*,bool){++reads;return enabled;}float GetFloatDefault(const char*,float v){++reads;return v;}std::string GetStringDefault(const char*,const char*v){++reads;return v;}} config; auto sClientlessLuaEngine=&lua;auto sConfigMgr=&config; struct SpellMgr{void* GetSpellInfo(uint32){return this;}} spells;auto sSpellMgr=&spells; uint32 FlightType(void*){return 1;} @@ -54,6 +56,10 @@ struct Record{std::shared_ptrPublished=std::m bool Moving=true,Returning=false;std::vector Breadcrumbs{{}};std::string AbortReason; }; class ClientlessFlightMgr{public: + Player const* _planningPlayer=nullptr;bool _planningEnabled=false;float _planningMinDistance=600; + std::unordered_set _planningPreferredZones; + std::unordered_map _planningAreas,_planningTerrain; + void BeginPlanning(Player const* = nullptr); uint32 areaCode=87,zoneCode=12; std::pair AreaZone(Player const*,uint32,Position const&)const{return {areaCode,zoneCode};} bool Enabled(Player const*)const;bool Preferred(Player const*,uint32,Position const&)const; @@ -61,7 +67,7 @@ class ClientlessFlightMgr{public: void Fail(Player*,Record&,std::string const&); }; ''' -for signature in ('bool ClientlessFlightMgr::Enabled(', 'bool ClientlessFlightMgr::Preferred(', 'float ClientlessFlightMgr::Estimate(', 'void ClientlessFlightMgr::Fail('): +for signature in ('void ClientlessFlightMgr::BeginPlanning(', 'bool ClientlessFlightMgr::Enabled(', 'bool ClientlessFlightMgr::Preferred(', 'float ClientlessFlightMgr::Estimate(', 'void ClientlessFlightMgr::Fail('): harness+=function(signature)+'\n' harness+=r''' int main(){ @@ -95,6 +101,17 @@ int main(){ mgr.Fail(&p,r,"reverse blocked");assert(r.Status.State==ClientlessFlightState::Failed); Record mounting;mounting.Status.State=ClientlessFlightState::Mounting;mgr.Fail(&p,mounting,"cast rejected"); assert(mounting.Status.State==ClientlessFlightState::Failed&&p.interrupts==1&&!mounting.Returning); + // A planning scope snapshots repeated config/profile work, while actual + // departure checks and per-map licenses remain live. + profile.CanFreeFly=true;p.skill=225;p.learned=true;p.license=true;config.enabled=true; + area.fly=true;areas.known=true;p.restrictions=false;mgr.zoneCode=12;b.x=1000; + mgr.BeginPlanning(&p);int reads=config.reads; + for(int i=0;i<1000;++i)assert(std::isfinite(mgr.Estimate(&p,0,a,b))); + assert(config.reads==reads); + p.license=false;assert(!std::isfinite(mgr.Estimate(&p,0,a,b)));p.license=true; + profile.CanFreeFly=false;assert(!mgr.Enabled(&p)); + mgr.BeginPlanning();assert(!std::isfinite(mgr.Estimate(&p,0,a,b))); + profile.CanFreeFly=true; Record revoked;p.canFly=false;mgr.Fail(&p,revoked,"permission lost");assert(revoked.Status.State==ClientlessFlightState::Failed&&!revoked.Returning); } ''' diff --git a/tests/clientless/test_free_flight_planner.py b/tests/clientless/test_free_flight_planner.py index 9a9d777dfe..6e0d5eaba8 100644 --- a/tests/clientless/test_free_flight_planner.py +++ b/tests/clientless/test_free_flight_planner.py @@ -8,6 +8,9 @@ structs=h[h.index('struct TC_GAME_API ClientlessTravelNode'):h.index('class TC_G method=s[s.index('bool ClientlessTravelGraphMgr::PlanAdaptiveRoute('):s.index('bool ClientlessTravelGraphMgr::PlanRouteToPosition(')] signature=h[h.index(' bool PlanAdaptiveRoute('):h.index(' bool FindNearestNodeToPosition(')] preamble=r''' +#include "ClientlessGraphProbeCache.h" +#include +#include #include #include #include @@ -24,7 +27,7 @@ using uint32=uint32_t; using int32=int32_t; #define TC_LOG_DEBUG(...) do {} while(0) #define TC_LOG_WARN(...) do {} while(0) constexpr uint32 NAV_GROUND=1, PATHFIND_INCOMPLETE=2, PATHFIND_NOPATH=4; -struct Position { float x=0,y=0,z=0; void Relocate(float a,float b,float c,float=0){x=a;y=b;z=c;} }; +struct Position { float x=0,y=0,z=0;float GetPositionX()const{return x;}float GetPositionY()const{return y;}float GetPositionZ()const{return z;} void Relocate(float a,float b,float c,float=0){x=a;y=b;z=c;} }; struct Player { float x=0,y=0,z=0; bool IsInWorld() const{return true;} uint32 GetMapId()const{return 0;} uint32 GetInstanceId()const{return 0;} float GetPositionX()const{return x;} float GetPositionY()const{return y;} float GetPositionZ()const{return z;} }; float dist(Position a,Position b){return std::sqrt((a.x-b.x)*(a.x-b.x)+(a.y-b.y)*(a.y-b.y)+(a.z-b.z)*(a.z-b.z));} struct ClientlessPathDiagnostics {bool Calculated=true, Complete=true,Suspicious=false; uint32 PathType=1,PointCount=2; float DirectDistance=0,PathLength=0,MaxSegmentLength=0,MaxZDelta=0,FinalDistance=0; std::string Reason;}; @@ -32,18 +35,19 @@ struct Travel {void AnalyzePath(Player* p,Position b,ClientlessPathDiagnostics& auto sClientlessTravelMgr=&travel; struct Flight { bool enabled=false;float minimumX=-10000; - void BeginPlanning(){} + void BeginPlanning(Player const* = nullptr){} bool Enabled(Player const*){return enabled;} float Estimate(Player const*,uint32 map,Position a,Position b){ float d=dist(a,b);return enabled&&map==0&&a.x>=minimumX&&d>=60?180+d*1.5f:std::numeric_limits::infinity(); } } flight; auto sClientlessFlightMgr=&flight; -struct VcorePositionPathResult {bool complete=false; uint32 Result=2;float FinalDistance=0,PathLength=0;bool IsComplete()const{return complete;}}; -struct PathGenerator {static inline bool available=false; static inline float maxDistance=10000;static VcorePositionPathResult AnalyzePositions(uint32,uint32,uint32,Position a,Position b,uint32){float d=dist(a,b);return {available && d<=maxDistance,2,0,d};}}; +struct VcorePositionPathResult {enum class Status{Complete,Partial};Status Result=Status::Partial;float FinalDistance=0,PathLength=0,DirectDistance=0;bool IsComplete()const{return Result==Status::Complete;}}; +struct PathGenerator {static inline unsigned probes=0;static inline bool available=false; static inline float maxDistance=10000;static VcorePositionPathResult AnalyzePositions(uint32,uint32,uint32,Position a,Position b,uint32){++probes;float d=dist(a,b);return {available && d<=maxDistance?VcorePositionPathResult::Status::Complete:VcorePositionPathResult::Status::Partial,0,d,d};}}; std::string MakeLocalBridgeKey(std::string const&a,std::string const&b){return a+"->"+b;} ''' cls='class ClientlessTravelGraphMgr { public: bool EnsureLoaded(){return true;}\n'+signature+''' +ClientlessGraphProbeCache _positionProbes;std::unordered_map> _bridgeNeighborhoods; std::unordered_map _nodes; std::unordered_map> _edges; std::unordered_map> _edgePoints; @@ -107,6 +111,17 @@ assert(exits.PlanAdaptiveRoute(&start,"D",1,200,route,done,endKey,why)); assert(done&&route.back().NodeKey=="D"&&route.back().EdgeType=="free_flight"); assert(route[route.size()-2].NodeKey=="virtual_ground_exit"); for(auto const& step:route)if(step.EdgeType=="free_flight")assert(step.NodeKey!="virtual_ground_entry"); +// A second plan reuses static proofs, but still honors query-specific failures. +flight.enabled=false;PathGenerator::available=true;PathGenerator::maxDistance=10000; +ClientlessTravelGraphMgr cached;node(cached,"A",0);node(cached,"B",150); +assert(cached.PlanAdaptiveRoute(&start,"B",1,200,route,done,endKey,why)&&done); +auto calls=PathGenerator::probes;auto size=route.size(); +assert(cached.PlanAdaptiveRoute(&start,"B",1,200,route,done,endKey,why)&&done&&route.size()==size); +assert(PathGenerator::probes==calls&&cached._positionProbes.Hits>0); +std::unordered_set forbidden{"A->B"}; +assert(cached.PlanAdaptiveRoute(&start,"B",1,200,route,done,endKey,why,nullptr,&forbidden)&&!done); +cached._positionProbes.Clear();cached._bridgeNeighborhoods.clear();PathGenerator::available=false; +assert(cached.PlanAdaptiveRoute(&start,"B",1,200,route,done,endKey,why)&&!done); std::cout<<"ground planner regressions and dynamic flight/node/location/virtual-exit integration passed\n"; } ''' @@ -115,5 +130,5 @@ tests=tests.replace('r=plan(g,p,goal,1);assert(r.back().GroundStartIndex<0); // with tempfile.TemporaryDirectory(prefix="vcore-flight-planner-") as folder: cpp=Path(folder)/"test.cpp";binary=Path(folder)/"test" cpp.write_text(preamble+structs+cls+method+tests) - subprocess.run(["c++","-std=c++17","-O0",str(cpp),"-o",str(binary)],check=True) + subprocess.run(["c++","-std=c++17","-O0","-I"+str(root/"src/server/game/World"),str(cpp),"-o",str(binary)],check=True) subprocess.run([str(binary)],check=True) diff --git a/tests/clientless/test_graph_probe_cache.py b/tests/clientless/test_graph_probe_cache.py new file mode 100644 index 0000000000..a0c7fe709a --- /dev/null +++ b/tests/clientless/test_graph_probe_cache.py @@ -0,0 +1,28 @@ +"""Static MMAP proof cache isolation, failure handling and bounded lifetime.""" +from pathlib import Path +import subprocess,tempfile +root=Path(__file__).resolve().parents[2] +code=r''' +#include "ClientlessGraphProbeCache.h" +#include +#include +int main(){ + ClientlessGraphProbeCache c;auto key=c.MakeKey(0,1,1,2,3,4,5,6); + assert(!c.Find(key));c.Record(key,false,10,0,10);assert(!c.Find(key)); + c.Record(key,true,10,2,10);assert(!c.Find(key)); + c.Record(key,true,std::numeric_limits::infinity(),0,10);assert(!c.Find(key)); + c.Record(key,true,10,0,9);assert(c.Find(key)&&c.Find(key)->Length==10); + assert(!c.Find(c.MakeKey(1,1,1,2,3,4,5,6))); // map isolation + assert(!c.Find(c.MakeKey(0,2,1,2,3,4,5,6))); // instance isolation + assert(!c.Find(c.MakeKey(0,1,4,5,6,1,2,3))); // directed connection + assert(!c.Find(c.MakeKey(0,1,1.0000001f,2,3,4,5,6))); // exact coordinates + c.Clear();assert(!c.Find(key)&&c.Size()==0); + for(unsigned i=0;i<32769;++i)c.Record(c.MakeKey(i,1,1,2,3,4,5,6),true,10,0,9); + assert(c.Size()==1&&c.Find(c.MakeKey(32768,1,1,2,3,4,5,6))); +} +''' +with tempfile.TemporaryDirectory() as d: + p=Path(d);(p/'test.cpp').write_text(code) + subprocess.run(['c++','-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('Graph probe cache: exact directed/map/instance keys, successful geometry only, invalidation and bounded storage passed')