From a6572242c6adcc21b4c6b5f3dfc0b88c28b51df3 Mon Sep 17 00:00:00 2001 From: nico Date: Tue, 3 Nov 2026 18:56:16 +0100 Subject: [PATCH] Fix flight departure recovery and Wintergrasp mount permissions Keep takeoff clearance until actual ascent, blacklist rejected dynamic bridges, and diagnose group ground-path rejection. Synchronize native Wintergrasp client mount permission with battle state and fix duplicate Zangarmarsh graveyard ownership. Add regression tests and generic testbot aura inspection. --- doc/clientless/SESSION-RESUME.md | 864 ++++++++++++++++++ .../game/Battlefield/BattlefieldMgr.cpp | 53 ++ src/server/game/Battlefield/BattlefieldMgr.h | 3 + src/server/game/Entities/Player/Player.cpp | 1 + src/server/game/Entities/Unit/Unit.cpp | 4 + src/server/game/Spells/SpellInfo.cpp | 5 + .../game/World/AdventureGroupTravelMgr.cpp | 19 +- .../World/ClientlessActionExecutorMgr.cpp | 7 + src/server/game/World/ClientlessFlightMgr.cpp | 5 +- src/server/game/World/ClientlessFlightPath.h | 7 +- .../game/World/ClientlessTestBotMgr.cpp | 4 +- .../scripts/Battlefield/BattlefieldWG.cpp | 2 + .../scripts/OutdoorPvP/OutdoorPvPZM.cpp | 4 +- src/server/scripts/OutdoorPvP/OutdoorPvPZM.h | 3 +- .../clientless/test_adventure_group_travel.py | 1 + .../test_dynamic_bridge_recovery.py | 30 + tests/clientless/test_flight_policy.py | 8 + .../clientless/test_flight_takeoff_handoff.py | 16 + .../test_wintergrasp_client_permission.py | 39 + tests/clientless/test_wintergrasp_flight.py | 25 + .../clientless/test_wintergrasp_mount_cast.py | 29 + .../clientless/test_zm_graveyard_ownership.py | 40 + 22 files changed, 1159 insertions(+), 10 deletions(-) create mode 100644 tests/clientless/test_dynamic_bridge_recovery.py create mode 100644 tests/clientless/test_flight_takeoff_handoff.py create mode 100644 tests/clientless/test_wintergrasp_client_permission.py create mode 100644 tests/clientless/test_wintergrasp_flight.py create mode 100644 tests/clientless/test_wintergrasp_mount_cast.py create mode 100644 tests/clientless/test_zm_graveyard_ownership.py diff --git a/doc/clientless/SESSION-RESUME.md b/doc/clientless/SESSION-RESUME.md index d040fcfca2..d3ff1b96be 100644 --- a/doc/clientless/SESSION-RESUME.md +++ b/doc/clientless/SESSION-RESUME.md @@ -1,3 +1,867 @@ +# 2026-11-03 – Automatische Tausendwinter-Clientfreigabe abgeschlossen + +Der Nutzer bestätigte: Allitester kann den Zwielichtboten mit Aura 91262 nutzen. +Die Freigabe wird jetzt automatisch über den direkten AddAura-Pfad gesetzt, +wenn Tausendwinter außerhalb einer aktiven Schlacht betreten wird. Sie wird +beim Verlassen oder Schlachtbeginn entfernt und nach Schlachtende neu vergeben. +Fremde Mount-Einschränkungen ohne Flugfreigabe werden nicht übergangen. +Gemeinsame serverseitige Mount-/Zauber-/Planerprüfung bleibt erhalten. + +Gezielte Tests ClientPermission, WGFlight und MountCast bestanden. Der echte +Live-Test wg_auto_verified_1829 bestand alle drei expliziten Prüfungen: +1. Aura 91262 nach Gebietswechsel automatisch vorhanden. +2. Nach Entfernen aller alten Mount-Auren Zwielichtbote 107844 frisch über + CMSG_CAST_SPELL beschworen; dessen Aura ausdrücklich vorhanden. +3. Nach Verlassen Tausendwinters Aura 91262 nicht mehr vorhanden. +Testcharakter Morvath 150 wurde anschließend automatisch ausgeloggt. +Testbot-API dafür generisch um has_aura/remove_aura ergänzt, ohne Botprofile +oder AdventureBot-Steuerung als Abhängigkeit einzuführen. + +Build -j10 und Installation erfolgreich. Der erste Restart dieser Aufgabe +hatte noch den separaten GameObject::RemoveFromWorld/ZoneScript-Absturz; +Core worldserver-20261103-180256.core und passendes worldserver-before-fix +im Checkpoint sind erhalten. Beim ergänzenden Build war der Loader weiterhin +pausiert, SafeLogout erneut angefordert und null Sessions nachgewiesen. +Der anschließende Shutdown von PID68290 endete NORMAL. Kein weiterer Core. +Matching Binary worldserver-before-direct-aura ist gesichert. + +Finaler Server PID71783: frische ready-Meldung, Ports 8085/8087 und Fortbestand +bestätigt. 118 Adventure-Bots online, Loader wieder 1, SafeLogout pending 0. +Restbots vor dem ersten Shutdown mit GUID/Fraktion/Position/Flags gesichert. +Recovery nach Wiederanlauf: Gargar93 und Volakmar128 hingen ohne Flugzustand +über dem Boden; beide nativ nach Orgrimmar teleportiert, Ergebnis geprüft. +Andere normale Taxi-/Transportzustände unverändert. Allitester nicht manuell +teleportiert oder verändert; Clientfreigabe wird nun automatisch verwaltet. + +Artefakte: checkpoints/2026-11-03-wg-client-permission/ (Live-Ergebnis, +Testskript, Build/Install/Startup, Roster, Recovery und result-final.json). +Große Artefakte bleiben lokal per .git/info/exclude von der UI-Erfassung +getrennt; keine großen Git-RAM-Jobs mehr beobachtet. Keine Dateien gelöscht, +keine Commits/Pushes. Alle früheren Source-WIP sind erhalten. +Die erneute visuelle Abnahme mit Allitester steht noch aus. Der separate +ZoneScript-Lebensdauerfehler beim Shutdown ist noch nicht behoben. + +--- + +# WG-Clientpermission Nachprüfung/zweiterBuild läuft + +ErsterClientpermissionBuild/Install erfolgreich, PID68290 ready+Ports bestätigt. +Loader weiterhin0, AdventureBotsnochNICHTwiederhergestellt,Online0. +ErsterLiveTest nurmounted/canFly,DBAuraSaveKeinNachweis(kein91262 gespeichert), +vorhandeneMountAura32243machtaltenTestnichtaussagekräftigfürfrischen107844. +DaherVergabe vonCastSpell aufAddAura umgestellt (direkterGM-AuraPfad). +TestbotAPIgenerisch umhas_aura/remove_aura erweitert, um echteaktiveAuren zu +prüfen stattvorhandeneMountsfehlzuinterpretieren. NeuesScript + automatic_permission_verified.lua:Altmounts+91262entfernen,WGbetreten, +HasAura91262prüfen,nativeRequest107844,HasAura107844prüfen,Orgrimmar, +HasAura91262mussfehlen. DreiChecksimResult verpflichtend! +VorZweitbuildpopulationreload+safeLogoutall erneutangefordert,Online0;Build-j10 +läuft(build-direct-aura.log),Install/RestartnochOFFEN. Keineerneute5minnötig, +weilkeineSessions. AktuellenMatchingBinaryvorInstallalsworldserver-before-direct- +aura sichern. BisherigerShutdown182? wiederGameObject::RemoveFromWorld651 +m_zoneScript->OnGameObjectRemove,Core180256+Matchingworldserver-before-fixim +client-permissionCheckpoint gesichert. OffenerZonenScriptLebensdauerbug. +AllealtenSourceWIP erhalten. LoaderamEndewieder1+vorherige118Botwiederherstellen. + +--- + +# WG-Clientfreigabe nach bestätigtem Allitester-Auratest in Arbeit + +Nutzer bestätigt .aura91262=>Zwielichtbotegeht. AutomatischeSynchronization nun +BattlefieldMgr::UpdateWintergraspFlight(Player*/Map*): setzt91262beiWG-Frieden, +entferntbeiVerlassen/Schlachtbeginn; OnBattleStart/End refreshtSpielerimGebiet, +Player::UpdateArea refreshtbeimGebietswechsel. Fremde336-AurenohneFlug2werden +nichtübergangen. BestehendeServermount/SpellInfo/Planner-Ausnahmenbleiben. +Tests ClientPermission/WGFlight/MountCast PASS. Testbot150hatvorher KEINE +91262incharacter_aura, ermöglichtNachweisautomatischerVergabe nachRestart. +Build-j10läuft. Loader1->0+reload+SafeLogoutlautoperation.json, volle5min-Regel; +Artefakte checkpoints/2026-11-03-wg-client-permission/. Installation/Restart/ +Recovery/LoaderRestore+automatischeAura-Liveprüfung noch ausstehend. +Gitgroßartefakteweiterlokalausgeschlossen,keineGitRAMjobs. AlleanderenWIPerhalten. + +--- + +# Allitester21: GoldenerGreifgeht, Zwielichtbote107844nicht + +Nutzerkorrektur: GoldenerGreif nutzbar, Zwielichtbote MeldungIhrkönntdashiernicht +verwenden. Allitester21 lvl85 map571zone4197 pos4503.28/1941.55/435.29, +Riding375/375,54197+90265+90267aktiv,107844gelernt. DBgespeicherteAura2458, +keineMountRestriction-Aura. Native Testbot150lvl80/riding375/54197/107844: +CMSG_CAST_SPELL amBodenWG erfolgreich mounted1/canFly1. Testscript autoLogout, +Ergebnis twilight-harbinger-native-test.json. Somit serverseitiger Aufruf beider +Mounts erlaubt, Client-Freigabe weiter unvollständig. DBCMountType107844=238, +GoldenerGreif32235=248. AreaMountFlags13imClient fehlenweiterFlug2. +Client-Freigabe-Aura91262 Wintergrasp-InactiveEnableFlyingMounts hat336/misc15, +91285ActiveEnableFlyingMounts ebenfalls15,86973PermittedFlightArea15. +Keine dieserAuren aktuellinspell_area. AsynchroneDiagnosefrageanNutzer: +Allitester SELBST anwählen,.aura91262,Zwielichtbotetesten;entfernbar.unaura91262. +AntwortnochAUSSTEHEND. Keine neue Source-/DBänderung für diese Nachdiagnose, +kein weitererBuild/Restart. UsernachweisdesClienttests vor3.Korrekturabwarten. +Wennbestätigt: ClientFreigabe an friedensabhängigeWG-Regel koppeln, bei +Schlachtbeginn sicherentfernen,beimVerlassenauch. ServerseitigeAura-Priorität +in Unit/SpellInfo beachten,damit91262währendBattlekeinFluglochöffnet. + +--- + +# WG-Mountcast und Git-Speicher korrigiert, 03.11.2026 + +SpellInfo::CheckLocation ONLY_FLYING_AREAS verwendet jetzt ebenfallsWG-Regel; +expliziteMountRestriction-Auren weiter vorrangig. Test_wintergrasp_mount_cast +mitextrahiertemProduktionsblock PASS. Build-j10/Install PASS. SafeLogout17:56:10, +volle5min,15Restbots+Flags/Positionssnapshotgesichert. Shutdown andererCrash: +Creature::RemoveFromWorld329 GetZoneScript()->OnCreatureRemove(this), Core +worldserver-20261103-174952.core+Backtrace, matchingworldserver-before-fix +imwg-mount-castCheckpoint. ZM-DoubleDelete nichtimneuenStack. Nochzuuntersuchen. +NeuerServer PID61228 ready+Ports8085/8087 bestätigt. 118erRoster wiederhergestellt, +Loader1,pending0. Suspendierte160/176/186 nachRestart Fraktionshauptstadt, +Positionen nativ bestätigt,160fliegtanschließendnormal. KeineanderenTransport- +Übergängeunterbrochen. Testbot150MorvathLevel80/riding225/coldweather/32243 +amBodenWG(4059.27,2747.79,354.88): RequestCast=nativesCMSG_CAST_SPELL, +nativeProbe mounted1/canFly1/falling0. Scriptpassed,autoLogoutabgeschlossen. +Kopielive-test.json+native-evidence.log imCheckpoint. EchterSpieler-Abnahmeoffen. +Git-RAM:UItemporaryIndexgitadd verarbeitete1.1GBbackups. GroßeDiagnosedateien +lokal.git/info/exclude,keinDateiverlust/keinnormalerGitIndexeingriff. ZweiUIjobs +nachenv/cwdPrüfungbeendet,keineweiterengitProzesse danach,RAMentlastet. +NeuegroßeArtefakteweiterlokalausschließen! KeinCommit/Push dieserWIP. + +--- + +# 03.11.2026 weiterer WG-Mountpfad und UI-Git-RAM in Arbeit + +Nutzer meldet Einfluggeht, Aufsitzennicht+mehrere1GB-Gitprozesse. +Übersehener SpellInfo::CheckLocation ONLY_FLYING_AREAS-Pfad liestMountFlagsdirekt. +Jetzt ebenfalls gemeinsameWG-Regel eingebunden, expliziteAuraMountRestriktionen +bleiben vorrangig. Produktionsextrakt-Test test_wintergrasp_mount_cast.py PASS +fürHaupt/Untergebiet/Frieden/Schlacht/Ende/anderesGebiet/Auravorrang. +Build-j10läuft. Loader1->0+reload+SafeLogout17:56:10+01, volle5min-Regel. +Artefakte checkpoints/2026-11-03-wg-mount-cast/. Install/Restart/Recoveryoffen. +Gitprozesse sind CodexUI-diff gitadd mit GIT_INDEX_FILE=/tmp/.../index. +Mehrere1.1GBServerbackups +100MBLogs lagenuntrackedimRepo=>UIhashtealles. +Große>10MBArtefakte individuell inlokaler.git/info/exclude ausgeschlossen, +auchZukunftsmusterworldserver-before-*. KeineDateigelöscht/keinrealerIndexgeändert. +Zwei laufendeUIgitadd58568/58654 nachPrüfungcwd+tempindex perSIGTERMbeendet, +keineweiterengitProzesse danach. Vorher mehrereGBRAM, Swapvoll; nunentlastet. +AlleSourceWIPerhalten; keinCommit/Push. + +--- + +# Tausendwinter-Flugkorrektur installiert und nativ geprüft + +Gemeinsame BattlefieldMgr-Regel in Unit-Mountprüfung und ClientlessFlight-Estimate. +Frieden/disabled/fehlend erlaubt WG+Untergebiete, aktive Schlacht gesperrt; +explizite MountRestriction-Auren bleiben unangetastet. Tests WG/FlightPolicy/ +TakeoffHandoff/FreeFlight PASS, Build-j10 und Install PASS. +Wichtig: Während Build um17:46:55 Kernel-OOM-Kill PID54176 (journalgesichert), +KEIN normaler Shutdown/keinCore; gestartetes SafeLogout17:46:15 hatte bis Kill +nahezu alle ausgeloggt, DBRest42+169 gesichert. Loader blieb0 bis frischemStart. +Keine forcierte Einzelabmeldung, keine Binaryinstallation am laufendenProzess. +Nach erfolgreichemBuild frischerGDBStart PID57089, ready+Ports8085/8087 bestätigt, +118Bots wiederonline, Loader1/population reload,pending0. Kernel-OOM-Evidenz +checkpoints/2026-11-03-wg-flight/kernel-oom.log. -j10 weiterhin Nutzerregel, +Parallelbuild mit11GiRAM/vollemSwap hat hier OOM ausgelöst, nicht Codecrash. +Nativ35Karggan/162Aruendas/176Ashaelri an ORIGINALEN WGPositionen nachLogin: +alle mounted1/canFly1/falling0, keineTeleportation dieser3. VolleReisefortsetzung +noch nichtverifiziert. OOMRest42geerdet,169Bromdan suspendiertohneFlugzustand: +RecoveryStormwind, Position+anschließend regulär flying1 bestätigt. +Testartefakte/Operation/Roster/NativeEvidence/result.json imwg-flightCheckpoint. +KeineCommits/Push, alle bisherigen anderenWIPbewahrt. User-Spielabnahme offen. + +--- + +# 2026-11-03 Tausendwinter-Flugkorrektur in Arbeit + +Nutzer beauftragt Fix. Gemeinsame BattlefieldMgr::IsWintergraspFlightAllowed +prüft map571+DBCManager::IsInArea(4197), erlaubt bei fehlendem/deaktiviertem +Battlefield oder Frieden, verbietet aktive Schlacht. Unit::GetMountCapability +ergänzt Flugflag nur im normalen Gebietspfad, explizite MountRestriction-Auren +bleiben vorrangig. ClientlessFlightMgr::Estimate nutzt dieselbe Regel auch für +Untergebiete und lehnt aktive Schlacht ab. Keine DBC-/DB-Änderung. +Test_wintergrasp_flight.py und FlightPolicy PASS, Abflug-Handoff PASS. +Loader1->0,population reload,safe logout all17:46:15+01; Build-j10 läuft, +volle5min bis Shutdown bei Restbots. Roster/Operation/Logs: +checkpoints/2026-11-03-wg-flight/. Install/Restart/Recovery/Restore ausstehend. +Alle anderen WIP erhalten. Nicht committen/pushen ohne Nutzerauftrag. + +--- + +# Tausendwinter: permanentes Flugverbot im Gebietspfad identifiziert + +Nutzer selbst beim Einfliegen abgemountet, Bots35/162/176 suspendiert. +Konfig Wintergrasp.Enable0; BattlefieldMgr::GetBattlefieldToZoneId ignoriert +Disabled, Update ignoriertDisabled, daher kein Beleg laufender Schlacht. +Statische enUS AreaTable.dbc:4197 UND alle Untergebiete MountFlags13(0x0D), +AllowFlyingMounts0x02 FEHLT durchgehend. Unit::GetMountCapability nutzt diese +Flags ohne wartimeabhängige WG-Ausnahme (außer allgemeinen MountRestriction-Auren), +Player::UpdateArea ruft UpdateMountCapability, dadurch Flugfähigkeitverlust bei +Zoneneintritt unabhängig von Battlefield-IsEnabled/IsWarTime möglich. +ClientlessFlightMgr nutzt ebenfalls statische AreaMountFlags zur Planung. +Navlog bestätigt17617:10:24/16217:20:06 flight permission lost or total timeout, +map571 Z397.55/Z385.85 an vomNutzer beobachteter Stelle. +91604-SpellArea selbst ist durch Enabled/WarTime geschützt; alleinige Änderung +CanFlyIn wäre keine Lösung. Nächster Fix muss gemeinsame Mountfähigkeitsprüfung +und Botplaner mit identischer tatsächlicher WG-Freiflugregel versorgen, reguläres +Wartimeverbot erhalten. Keine Änderung/Build/Teleport für diese Diagnose. + +--- + +# ZM-Ownershipkorrektur abgeschlossen, 03.11.2026 + +ASan100Zyklen PASS, alte Variante heap-use-after-free reproduziert; Build-j10 und +Install PASS. SafeLogout17:20:08, volle5min eingehalten; verbleibende21 mit +GUID/Fraktion/Position/Diagnosen gesichert. Alter Server beim Shutdown erneut +bekannter Crash, Core164154+passendes Binary im zm-ownershipCheckpoint gesichert. +Neuer Server PID54176, frische ready-Meldung,Ports8085/8087 und Fortbestand +bestätigt. Vorherigen118erRoster wieder eingeloggt, Loader1 wiederhergestellt, +safe logout pending0. Recovery bei suspendierten Bots ohne Flugzustand: +73/92/112/118/161/163/164/165/167/186 nativ Fraktionshauptstadt teleportiert, +Positionen nachher bestätigt;118/164 fliegen anschließend wieder regulär, +keine Fallbewegung. Übrige reguläre Taxi-/Transportzustände erhalten. +Gezielter ASan-Abbautest verifiziert Fix; vollständiger Shutdown der neuen +Version noch nicht durchgeführt. Keine weiteren Build-/Neustartschleifen nötig. +Kein Commit/Push beauftragt. Alle bisherigen anderen WIP erhalten. +Artefakte checkpoints/2026-11-03-zm-ownership/; Regression + tests/clientless/test_zm_graveyard_ownership.py. + +--- + +# 2026-11-03 ZM-Ownershipkorrektur in Arbeit + +Nutzer beauftragt Fix. m_Graveyard nun nichtbesitzender initialisierter Zeiger; +AddCapturePoint übernimmt einziges Eigentum. Regression extrahiert echte +Member-/Setup-/AddCapturePoint-Implementierung: ASan100Zyklen PASS, alte Variante +ASan heap-use-after-free reproduziert. Build-j10 läuft. Loader1->0+population +reload+safe logout all um17:20:08+01 angefordert; volle5min-Regel. Roster/Logs +checkpoints/2026-11-03-zm-ownership/. Installation/Restart/Recovery/Loaderrestore +noch ausstehend. Alle vorhandenen anderen WIP bleiben erhalten. + +--- + +# Shutdown-Dump untersucht: doppelte Ownership des Zangarmarschen-Friedhofs + +Core144731 mit exakt gesichertem worldserver-before-fix geladen. Crash Nullcall +beim delete OPvPCapturePoint0x7ffeba24c740, Mapkey0. Stack OutdoorPvPZM-Destruktor +->OutdoorPvP-Destruktor->m_capturePoints-unique_ptr-delete. SetupOutdoorPvP +OutdoorPvPZM.cpp196/197 erzeugt m_Graveyard als unique_ptr und registriert .get() +mit AddCapturePoint. OutdoorPvP.cpp427 reset(cp) übernimmt denselben Zeiger +nochmals in einen unique_ptr. Abgeleiteter Destruktor löscht m_Graveyard zuerst, +Basisklassenmap löscht den bereits zerstörten CapturePoint erneut. Dump bestätigt +m_Graveyard=null im Basisklassenabbau, m_TypeId4, CapturePointmapkey0, Nullcall. +Rootcause doppelte Ownership, unabhängig von AdventureBot-Navigation. Zonenladen +kann Instanziierung herbeiführen; zum Fehler reicht anschließender Objektabbau. +Korrektur: ausschließlich CapturePointmap besitzt Objekt; ZM m_Graveyard als +nichtbesitzender Verweis. Noch nicht implementiert, nur Diagnose beauftragt. +Gezielte GDB-Ausgabe checkpoints/2026-11-03-shutdown-analysis/gdb.txt. +Kein Build/Restart/Änderung am laufenden Server. + +--- + +Abschluss: PID51247 bleibt aktiv,118 Bots online,Loader1/pending0. +Recovery102+159 nativ in Sturmwind(-8833.07,622.778,93.932),keineFallbewegung +verifiziert. Build/Tests bestanden wie dokumentiert, Spielabnahme bleibt offen. + +# 2026-11-03 Korrekturen installiert + +Abflug/verspätete Bridge-Sperre implementiert, gezielte Regressionen PASS, +Build-j10/Install erfolgreich. SafeLogout16:35:11, nach voller5min Rest19 +mit DBRoster und nativen info/flight-probe/transport-Ausgaben protokolliert. +Shutdown erneut bekannter separater Destruktorcrash: Core144731+Backtrace, +passendes altes Binary im takeoff-recovery-Checkpoint gesichert. +Neustart PID51247, frische ready-Meldung+Ports8085/8087+Prozess bestätigt. +119erRoster nativ wieder eingeloggt; zunächst Hallan158 fehlend, Login nachgeholt. +Loader wieder1, native population reload, safe logout pending0. +Restbots nachLogin geprüft. Renren159 und Corren102 ohne aktiven Flug, +aber über Boden suspendiert: Recoveryteleport Hauptstadt Sturmwind ausgeführt. +Corren versehentlich zuerst Orgrimmar angefordert, sofort nach Sturmwind korrigiert. +Andere laufende Flüge/Taxirecovery nicht abgebrochen. Abschließende Verifikation +noch ausstehend. Keine Commits/Push in dieser Aufgabe. Spielabnahme offen. + +--- + +# 2026-11-03 Abflug- und Recoverykorrektur in Arbeit + +Nutzer beauftragt beide Korrekturen. ClientlessFlightPath setzt Departed nicht +mehr bei Segmentausgabe, sondern beim folgenden Planen anhand tatsächlicher +Bodenfreiheit >=Safety. Geprüfte Departure-Korridore/Vertikalfallback gelten +bis dahin weiter. RecoverTravel sperrt verspätet abgelehnte dynamische +local_bridge-Kanten mit from->to auch bei route-local-PathRejected. +Neue Regressionen test_flight_takeoff_handoff.py und test_dynamic_bridge_recovery.py +PASS. Free-flight, elevated, handoff, shared/group flight, policy, planner, +group-travel, ground-exit und recovery-policy PASS. Unveränderter bestehender +Abflugtest test_flight_departure.py scheitert mit unordered_map::at in seinem +Testdouble; ClientlessFlightMgr.cpp wurde in dieser Aufgabe nicht verändert. +Loader1->0, population reload, safe logout all um16:35:11+01:00 angefordert. +Build-j10 läuft; keine erneute 60s-Ausnahme: volle 5min vor Shutdown bei Restbots. +Operation/119erRoster/Buildlog: checkpoints/2026-11-03-takeoff-recovery/. +Installation/Restart/Recovery/Loaderwiederherstellung noch ausstehend. + +--- + +# Erster Abflug: Diagnose und ausdrücklich gemerkter Recovery-Punkt + +Nutzer stimmt dem Recovery-Befund zu und verlangt, ihn zu merken: verspätete +PathRejected-Fehler dynamischer local_bridge-Kanten (DbId0) müssen die konkrete +Verbindung für die Reise sperren, damit Recovery Alternativen sucht. Als offener, +abgestimmter Folgepunkt festgehalten; noch KEINE Implementierung beauftragt. + +Erster Abflug 03.11.2026 14:28:59, Ashaelri176: erster Segmentendpunkt +(-8811.23,607.63,109.21), Abbruch bereits bei(-8826.7,622.306,96.4122). +ClientlessFlightPath::Next setzt Memory.Departed=true bereits bei AUSGABE des +ersten Segments. Update plant bei <=1500ms Restlaufzeit schon währenddessen +weiter. Folgende Next-Aufrufe behandeln den noch bodennahen Bot als Reiseflug: +Corridor prüft auch i=0 und fordert Safety8.0. Read-only native flight probe mit +Renren159/map0 an exakt der historischen XY-Position liefert ground94.314, +ceiling550.000. Somit Z+0.5=96.9122 < ground+8=102.314: JEDER normale Kandidat +scheitert bereits an seinem Startpunkt. Departure-only Vertikal-Fallback ist +wegen Departed=true ebenfalls aus. Kein Beleg für ein unpassierbares Hindernis +als Ursache dieses Abbruchs: der erste Korridor war zuvor freigegeben worden. +Beleg first-departure-analysis.json und native Probe im startup.log unter +2026-11-03-ground-validator. Probe176 war auf map530 unbrauchbar; nur Probe159 +auf map0 für die Rechnung verwendet. Bots dabei nicht bewegt/umgeplant. +Korrekturvorschlag: Abhebezustand bis tatsächlich ausreichender Bodenfreiheit +beibehalten, anschließende Korridore weiter mit geprüfter Abflugfreiheit planen; +Handoff während Abheben konsistent behandeln. Keine Sourceänderung/Build/Restart. + +--- + +# Greifenmeisterroute rekonstruiert (keine neuen Sourceänderungen) + +NutzerfragtwarumTaxi2/warumkeinGreif/warumgleicheRetries. RotiertesNavlog +adventurebot-navigation.log.2026-11-03_15-10-07 enthältOriginalablauf. +14:28:49Rose->Tradecenter;14:28:58FreiflugzumPortalGOspawn200627, +DBentry207694PortaltoTwilightHighlands(-8186.15/413.729/116.75). +14:28:59keinbegrenztersichererFlugkorridor,14:29:00sicherzurückTradecenter. +RecoverywähltTaxi2ALSSTARTANKER,läuftdorthin(19sec),nächsteKanteist +weiterhinfree_flightzumPortal,nichtTaxi. DortAbflugpunktsucheliefert106.2yd +Bodenweg64ydSuchradius,derownerValidatorunterbricht. NächsteRecoveryroute +Taxi2->Tradecenterlocal_bridge(Id0) scheitert5malwiebekannt. + +GenauerRetrybug: StartTravelgibt trotzsofortStatusPathRejectedtruezurück, +daherlocal_bridgeStartzweigmitFailedBridgeKeys.insert(from->to)wirdübersprungen. +SpäterallgemeinerUpdateMoving-Zweigmeldetroute-local-PathRejected/DbId0; +RecoverTravelblockiertnurDBIds!=0. SonderblockfürStartankerbetrifftnurRouteStep0, +hierStep1=>keineSperre. ReiseKostenprofil/JitterbleibtproReisefest(gewollt),also +bleibtungesperrteBrückebesteRoute. KeineTaxi-Fahrtangefordert/fehlgeschlagen. +NumerischenhistorischenKostenvergleichPortalvsGreifnichtbehaupten; Logbelegt +gewähltePortalroute,keinegespeichertenAlternativkosten. Artefakteoriginal-route.log, +route-before-stuck.log,original-route-explanation.jsonimground-validatorCheckpoint. +NurRead-onlyAuswertung+Checkpoint,keinweitererBuild/Teleport/Reset. + +--- + +# Gruppensicherheitsdiagnose abgeschlossen: konkrete LOS-Kollision + +NeuesteAufgabe: Nutzer verlangt genauenAblehnungsgrund fürAshaelri176/Corric166/ +Taliaarnora164 amSWGreifenmeister. MangelsDetaildiagBuildangefragt; Nutzer +explizitJA+einmaligeFrist60s statt300s (nurfürdiesenDiagBuild!). +19ZeilenWARN-DiagnoseinAdventureGroupTravelMgr.cpp plusTestmacro ergänzt; +keineBewegungs-/Sicherheits-/Planungsregelngeändert. Test_adventure_group_travel +undBuild-j10PASS,InstallPASS. Loaderpausiert+SafeLogoutall vorBuild14:41:37, +22Restbots+verfügbareFlagsgesichert, erstnach>202snormalShutdown10. Kein +EinzelForceLogout/FlightAbort. WiederShutdownNULLcall,Core121738+passendesELF +worldserver-before-diagnosticsgesichert. Standard5MinfürkünftigeBuildsbeibehalten. + +Befundnativerstfüralle3gleich: LOSsegment9von(-8856.588,517.633,109.881) +nach(-8858.637,521.069,109.340) scheitert. VorigeBodensamplesbestanden. +OfflineRay-Triangle-NachrechnungmitechtenVMAPDateien reproduziertTrefferin +Sw_Tradedistrict.wmo spawn46365,group13/id27435,Tri37+43,erstTreffer +(-8856.671,517.772,109.859). Zwei senkrechteFlächenaneinerModellecke; +welchersichtbareBauteilunbekannt. NahesFass46585nichtgetroffen. +Daher ursprünglicheBlockadekonkreteSichtlinienkollision,keinMaxSlope-/Height- +Schwellwertfehler. SpätereErsatzpfadehabenandereBoden/LOSAblehnungen. +KompletterMMAPPfadenthältgeradenTeilstreckenstrahl,derechteVMAPgeometrie +schneidet. RecoverywähltdieselbeID0local_bridge=>5fachExhausted. +NochKEINNavigationsfixvorgenommen; sinnvolleKorrekturwäregeometriesichere +lokaleUmgehung/Stützpunktwahl+RecoveryfürsynthetischeBrücken,keinblindes +LockernvonPrüfungen. Artefakte checkpoints/2026-11-03-ground-validator/README.md, +result.json,confirmed-rejections.log,check_los_mesh.py,los-triangle-hits.json. + +Server40815/GDB40804ready/Ports/Coreunlimited.119vorherigeBotsrestauriert, +Loader1/Pending0/native119online.22Restzuständegeprüft;42/99/118ausgescheiterter +HängefluglageinHauptstadtgeborgenundPositionsfortschrittgeprüft. DreiRaidbots +176/166/164NICHTteleportiert,zurDiagnoseanOriginalpositionangemeldet; +natürlicherPlanwechselnachRestartführtesieaufandereWege,keinenFixbehaupten. +Raidschedule15für03Novbleibtgesetzt,keineZeit-/Tagesplanänderungen; +Nutzerbeobachtetselbst,KEINVollzeitobserver. SourceDiagundTestuncommitted. +LetzterCommitfe03ea8ca5 VMAPFix unverändert,CrashBasisjetzt144731. + +--- + +# Raidtest: dreiBotsamSWGreifenmeister blockiert, Diagnosegesichert + +NutzermeldetAshaelri176/Corric166/Taliaarnora164fest. Read-onlyNativeab14:31: +alleamTaxi2(-8841.06/489.656/109.607),Gruppenleader176,group6. +LokaleBrückeTaxi2->stormwind_tradecenter (DbEdgeId0) MMAPkomplett/nichtverdächtig, +48Punkte/189.55yd/maxDeltaZ2.88; StartTravelweistOwnerGroundSafetyValidatorab. +Owner=AdventureGroupTravelMgr::ValidateGroundPath, prüftBodensamplesalle2yd +undVMAPLoS. WelcherSample/LOSgenauabgelehntwirdnochNICHTdiagnostiziert; +keinenfalschpositivenFilterbehaupten. Flugabflugvorbereitunggroundapproach +interrupted Folgefehler. FünfRecoveryReplanswählendieselbeId0Brücke, +blockedEdges0=>RecoveryExhausted. BekannteoffeneGroundSafety/RecoveryBaustelle +nunlivewiederbeobachtet,nichtdurchVMAP-LifetimeFixbehobenbehaupten. +Rohlogssowiejsonuntercheckpoints/2026-11-03-1500-raid-test/gryphon-master-*. +KeineTeleports/Reset/Sourceänderung/Build/RestartoderVollzeitbeobachtung. +NutzerbeobachtetlaufendenTestselbst,AuswertungnuraufAnforderung. + +--- + +# NeuerZehnerRaidtest03.11.2026 eingerichtet, Nutzerbeobachtet + +NutzerstartetWiederholungundverlangtKEINEVollzeitbeobachtung/automatischeAuswertung. +Gleiche10GUIDs159/160/161/162/163/164/165/166/167/176,alle85, +2Tank(167/176),2Heal(161/162),6Damage. AlleofflinezurGoldenenRoseposition +map0 -8864.73/669.015/97.9031 teleportiertundverifiziert,alle10nativangemeldet; +zusätzlichonlineStandortgesetzt,alteReiseexecutorendurchLogoutneugestartet. +NativeGruppenaltzustand2nur159/162überinteractionleavegelöst,keineSQLGroup- +Manipulation/zwangsweiseRaidzuteilung. AlteHandoff/InstanceVisitnurfürdiese10 +vorhergesichertundofflinegelöscht. Tagesplänenativeditplanerst15raid/ +instance_visit_at_6354_d0,dann14groupformation/the_gilded_rosegesetzt; +alle20Rowsfür20261103verifiziert. Normaler10erModus,2/2/6. +Gruppenbildensichnativ(anfangsevtTeilgruppen2/5),keinenFormationManagerrun +manuellausgelöst. NativeRebalance/FrühabreisebleibenwievorigenTestsaktiv. + +TemporärerGM-Teleportwiederentfernt+Reload. LoaderwährendSetup0,amEnde1+ +ConfigReload/PopulationReloadrestauriert. AnderePläne/Profileunverändert. +Checkpoint checkpoints/2026-11-03-1500-raid-test/manifest.json,verified-plans, +roster-before,offline-rose,configured-native,groups-at-setupundBackups. +KEINObserver/Timer/Evaluationjob gestartet. ErstaufNutzerwunschAuswertung. +AktuellerServer33318/passivGDB/Crashaufzeichnungbleibtaktiv,keinBuild/Restart. + +--- + +# VMAP-Fix lokal committed: fe03ea8ca5 + +Auf Nutzerauftrag Commit fe03ea8ca5 auf feature/clientless-player-core erstellt: +Fix VMAP probe handle leaks and tile model ownership. +Enthält MapTree.cpp/.h und test_vmap_tile_lifetime.py. Nicht gepusht. +Installierter und geprüfter Sourcezustand entspricht dem Commit; kein zusätzlicher +Build/Restart erforderlich. Lokale Session-/Diagnose-/Coreartefakte bleiben erhalten. + +--- + +# Abgeschlossen03.11.2026: VMAP-Crashursache korrigiert, Server wiederonline + +Nutzerauftrag Serverofflineuntersuchen: Core011325von06:08:42 gesichert, +passendesELF untercheckpoints/2026-11-03-crash-0608/worldserver.crashed. +ObjektShards210760/entry138492/display1667,Arathi45/map0, +Floatingpurplecrystal01.m2; alterWorldModel-Speicher enthältSpline. +Kausalkette: CanLoadMap leaked äußerenTile-FILE bei jedemProbe; +Core4075stdioStreams/4069unbenutzteReadStreams/MAXFD4095beiLimit4096. +Modellacquirekonnte wegenFDmangel scheitern; LoadMapTilezählte dennochSpawn, +UnloadMapTilegabblindModellnamenfrei undkonnte spätererworbeneGameObject-Ref +freigeben. BeideFehlerinProduktionsmethodentestsgegen6f8f59ec6dreproduziert. + +FixMapTree.h/.cpp: ursprünglichenFILEprüfen+schließen; proTileLedgerder +wirklicherworbenenReferenzen, keineFreigabenfehlgeschlagenerLoads; +UnloadohneDateireopen,partial/shared/duplicate/parentfälleabgedeckt. +NeuerTest tests/clientless/test_vmap_tile_lifetime.py, --baseline6f8f59ec6d +reproduziertbeidealtenFehler; neuefd/lifetime/sharedTests PASS. +Build-j10/Install PASS. KeineBotspezifik-AbhängigkeitimCorehinzugefügt. +ServerwarbeimAuftragbereitsoffline,0liveSessions; Loader0vorBuildgesetzt, +keinBinarylaufenderProzesseüberschrieben. DanachLoader1+beideReloadsrestauriert. + +NEUERServer33318unterGDB33307, ready/Ports8085+8087/Coreunlimitedverifiziert. +Native110Bots/Pools50/5/50/5/pending0/errorleer. FD-Zahlbleibt26unterBotload, +final26FDs/keineoffenenVMAPTiledateien. AlleBefunde/Testlogs/Build/Install/ +FDsamplesinCheckpointREADME.md/result.json/final-status.log. +Crashaufzeichnungpassivweiteraktiv,neueBasisworldserver-20261103-121738. +Langzeitstabilitätnichtalsbewiesenmelden; Stundenreproweiterbeobachtbar. +SeparaterShutdownOPvPCapturePoint-Crashnichtgezieltkorrigiert. +SourcefixundTestuncommitted,keinPush. LetzterCommit6f8f59ec6dbetrifftFahrstühle. + +--- + +# Crash-Fix installiert, Population wird wiederhergestellt (03.11.2026) + +Neuer Test-Worldserver PID33318 unter passivem GDB, Start12:17:37 erfolgreich: +frische ready-Meldung, Ports8085/8087 und unlimitedCore bestätigt. +Loader1 mit ConfigReload+PopulationReload restauriert, Ziele50/5/50/5. +Zuletzt39Bots online, Handles bleiben26, keine dauerhaft offenen vmtile-Dateien. +Noch bis110Bots prüfen und FD-Endwert/Result.json/README aktualisieren. + +Rootcause und Code: siehe checkpoints/2026-11-03-crash-0608/README.md. +MapTree.h/.cpp und neuer Test test_vmap_tile_lifetime.py uncommitted. +CanLoadMap-FILE-Leck plus unberechtigte Freigabe nach fehlgeschlagenem Modell- +Acquire reproduziert (Baseline6f8f59ec6d); korrigierter Code alle3Tests PASS. +Build-j10/Install PASS, alter Core+kompatibles ELF erhalten. Keine Pushes. +Schlussbericht muss Langzeitstabilität ausdrücklich noch offen lassen; +Stdio-Kernbefund4075/MaxFD4095, neuerServer26FDs auch nach mehreren Kartenloads. + +--- + +# Aktuell03.11.2026: Crash06:08Ursachegefunden/behoben/Neustartläuft + +Nutzer meldeteServeroffline. NeuerCore011325von06:08:42+passendesELFgesichert +untercheckpoints/2026-11-03-crash-0608/. ZUERSTdortREADME.mdlesen. +WiederstalesWorldModelinGameObjectModelDestruktion; diesmalShards210760, +map0Arathi45, ModellFloatingpurplecrystal01.m2; alteAdresseenthältSpline. +ImCore4075offeneStdioStreams/4069unbenutzteReadStreams/MAXFD4095=>Limit4096. +ROOT:CanLoadMapschließtäußeresTileFILEnicht+LoadMapTilezähltauchfehlgeschlagene +ModelAcquires,UnloadTilegibtblindFilenamefrei=>kannspätergeladenesdynamisches +Modellvorzeitiglöschen. BeideFehlergegenHEAD6f8f59ec6dmitproduktionsmethoden- +Testsreproduziert. FixCanLoadMapschließtursprünglichenFILE; proTileLedgerder +wirklicherworbenenModellreferenzen,keineFreigabefehlgeschlagenerLadevorgänge, +keinDateireopenbeimUnload;Duplicate-/Partial-/Parent-/Sharedfälleabgedeckt. +NeueDateitests/clientless/test_vmap_tile_lifetime.py+MapTree.h/.cppuncommitted. +Testsfd/lifetime/sharedPASS, baselinebeideFehlerreproduziert. Build-j10/InstallPASS. + +ServerwarbeimAuftragbereitsoffline,keineClientlessSessionsfürSafeLogout; +Loader0vorTests/Build/InstallgesetztundProzessabwesenheitgeprüft. KeineBinaries +laufenderServerüberschrieben. PassiveGDBneugestartet12:17:37, startup.logim +Checkpoint. NächsterSchrittReady/Ports/Corelimitprüfen,Loader1+Config/Population- +Reload+110Botsrestaurieren,FDzahlunterBotloadbeobachten(bisherLeck=>4096). +Cores/alteELFnichtlöschen. KeinFixfürseparatenShutdownCapturePointCrashbehaupten. + +--- + +# Lokal committed: Fahrstuhlfix 6f8f59ec6d + +Auf Nutzerauftrag lokal auf feature/clientless-player-core committed: +6f8f59ec6d Fix short-stop elevator travel and recover stranded passengers. +Enthält3TransportSourcefiles,2Regressionstests,FahrstuhlanleitungundTransportdoku. +KeinPush. SESSION-RESUME und lokale Diagnose-/Build-/Coreartefakte bewusst +außerhalbdesFeaturecommits erhalten. Installierter/testeterSourcezustandentspricht +Commit; keinweitererBuild/RestartfürGitCommitnötig. Runtime15926/GDB15915, +110Bots undCrashaufzeichnung unverändert. + +--- + +# Abgeschlossen03.11.2026: Fahrstuhlfixinstalliert/livegeprüft/110Botsonline + +AktuellfinaleNutzerregel: nativeType11Einstieg>=500msReststandzeit,Ausstieg +keineRestzeitprüfung; korrekterHaltweiterPflicht.5MinabwirklicherPassagierzeit +nativTeleportoriginalZielnode(Gegenetage),selbstwennAbbruchdasArbeitszieländert. +Schiff/Zeppelin/Tramunverändert. AbfahrtwährendÜberganghältPassagierbeiType11 +fest/versuchtnächstenHaltstattungewolltemAbort; Landrestwegabschließbar. +DeterministischeTestsGrenze499/500,exakt300000/beideRichtungen/keineSchiff- +Ausnahme/Fehlteleport/Abbruchziel/Kurzstop PASS; Deckreturn/GridPASS. +FinalBuild3-j10undInstallationPASS,server15926unterpassivGDB15915, +ready/Corelimitunlimited/Ports8085+8087geprüft. NeueDumpbasis011325. + +ECHTERTestbotMorvath150 (registriert,keinAdventureBot) livebeideRichtungen: +run elevator-final-20261103passed(128192ms);nativeArrivedoben01:14:52, +unten01:16:16. ZwischenzeitlichabgefahrenerLiftkorrektalsPassagierüberstanden, +nächsterHaltAusstiegArrived.Kein5MinRescuefürLivetesterzwungen(Fallbackunittested). +Testbot150wiederoffline. Mokrak117benutztenatürlichweiterenLift219022, +oben->unten01:19:05nativeArrived,keineAdminsteuerungdiesesAdventureBots. +Original110Population50/5/50/5vollonline/pending0/Loader1/schema1/errorleer +verifiziert. AlleRecoverylisten/Ergebnisse/Logs/Sourcehinweiseunter +checkpoints/2026-11-03-elevator-exit/(README.md,result.json,Tests,BuildundInstall). + +Buildresourcepolicybeachtet:Loaderpausiert+SafeLogoutvorjedemBuild, +kontinuierlicherDrain,Rest10bzw8nach>300secvorShutdownmitverfügbarenFlags +persistiert.KeineEinzelForceLogouts/FlightAbortsfürCompile.InstallnurbeiProzessende. +ShutdownNullcall-Cores003512und010220gesichert;passendeBinarysicherungen +worldserver.crashed(vorigerCheckpoint)undworldserver-first-elevator vorhanden. +RestbotsnachRestartLogin/Positions/TaxiFortschrittgeprüft;35/121nativin +Fraktionshauptstadtgeborgenundverifiziert;48/52Taxi/72Ziel/81Bodenbewegung +verifiziert. KeineTagesplan-/Profilmanipulation/Charakterlöschung. +Nochbekannt: Lags/Shutdown-OPvPCapturePoint-Crash nichtmitdiesemFixbehoben; +Logger.misc3weiterfürnativePhasenmessung,CrashaufzeichnungohneZusatzBreakpoint. +Sourceänderungenuncommitted,keinPush. NutzerfinaleIngameAbnahmeausstehend. + +--- + +# FinalerFahrstuhlbuild3PASS, zweiterShutdowninCoreaufzeichnung + +NeuesteRegelimplementiert:Type11Einstieg>=500msRestpause,Ausstieg0ms, +nurwirklicherStillstand/korrekterDock. ÜbergangsabfahrtlöstbeiType11keinen +ungewolltenAbortmehr; LandrestwegauchbeibewegtemLiftabschließbar.5Minpassenger- +FallbackauforiginalZielaktiv. tests_transport-3PASS + vorherDeck/GridPASS, +Build3-j10PASS. Sourcezustandfinal, keineweitereÄnderungnötigvorInstall. + +Loaderdurchgängig0/SafeLogoutab01:06:36,erneuteAnforderungvorbuild3protokolliert. +Nach311sec8RestbotsvollständigmitverfügbarenFlagsinshutdown-exception-2.json +undpre-shutdown-2-native.loggesichert; keineEinzelforceLogouts/FlightAborts. +Server13384/GDB13373 normalshutdown10angefordert,erneuterbekannterNULLcall +beimShutdown,Core010220wirdgeschrieben. ProzessendevorInstallationprüfen! +PassendesBinaryworldserver-first-elevatorimCheckpointgesichert(600). +Danncmake--installbuild/debug, passiveGDBLauncherweiter,umask077Consolelog +startup-final.log. Loaderbleibt0bisStartup+8RestbotsRecoverygeprüft. +8GUIDs43/48/52/61/72/81/88/94. Vorherige10RestbotsbeimerstenRestartgeborgen, +35/121Hauptstadtteleportsbereitsverifiziert,jetztweitersicheroffline. + +LiveMorvath150ersterRun elevator-roundtrip-20261103physischpassedaberPhaseFailed +beideFahrten; dieseFehlermeldungistnunmitZusatzfixkorrigiert(unittested). +FinaleWiederholungneueRunIDnötig,live-roundtrip.luaenthältjetzt1000msWartenan +jederAnkunft,damitTerminalPhaseauchvorevtTestteardowngeprüftwerdenkann. +VerwendeechtenregistriertenTestbot150,nichtAdventureBots. +NachLiveRunNativeArrivedbeideRichtungenverifizieren,5MinFallbackUnitexakt +300000msgetestet,keinunnötigesLiveFestklemmen. DannLoader1+Config/PopulationReload +und110vollePopulationwiederherstellen/prüfen. KeinCommit/Pushangefordert. +Artefakte checkpoints/2026-11-03-elevator-exit/. + +--- + +# Laufender Fahrstuhlfix: Nutzer ergänzt500msEinstieg, finalerBuildläuft + +NeuesteNutzeranforderung: EinstiegType11 mind.500msReststandzeit; Ausstieg +keineRestzeitprüfung, jeweilsnurwirklicherHalt.5MinPassagierFallbackunverändert. +ImplementiertAtDockwiederexaktBudget; Waiting/RequiredPauseType11=500, +RidingExitBudgetType11=0; DeckreturnType11keineRestreserve. Native5MinFallback +undimmutableTargetbereitsinMgr. tests/clientless/test_transport.py inkl499/500 +Grenze+beideRichtungen+exakt300000ms+Schiffschutz PASS. + +ErsterBuildinstalliertundLiveMorvath150echterTestbotHin/Rückfahrtphysischerfolgreich, +aberNativePhaseFailed, weilAbfahrtwährendspätemEinstiegAbortsetzte. +Zusatzfix: Type11unterbrochenerÜbergang behältFahrt stattAbort; finaleLandstrecke +unabhängigvomabgefahrenenFahrstuhlabschließbar. UnitRegressiondazuPASS. +NochNICHTfinalinstalliert. build-2PASS, build-3-j10läuft/session4663. +KeineanderenunerwartetenSourceänderungen; Branchfeature/clientless-player-core. + +AktuellerServer13384mitGDBpassiv/ready/Ports, enthältnurERSTEImplementierung. +Loader0/SafeLogoutall seit01:06ca(build-operation-2.jsonexakt), durchgängig +pausiert; vorbuild3SafeLogouterneutangefordert(build-operation-3.json). +Native8Restbots/combat4/flight4/transport0; keinEinzelforceLogout. +VorInstallationBuild3PASSund5MinabkontinuierlicherDrain-Anforderungprüfen, +RestbotsnochmalsGUIDNameFactionPos+verfügbareFlags/NativeBelegepersistieren, +nativenShutdownundProzessendeabwarten. Shutdowncrashbekannt/Corenichtabbrechen. +PassivesGDBStartscript checkpoints/2026-11-03-passive-restart/start-passive-diagnostic.sh. +Artefakte checkpoints/2026-11-03-elevator-exit/ (build3,test3,Lua,etc). +ErsterMorvathLauf elevator-roundtrip-20261103 istpassed/150offline; Ergebnis +live-roundtrip-first-result.json. FürfinaleLiveprüfungneueseindeutigeRunID, +selbesLua-Skriptmit150verwenden. Vonready_bottomperConsole +clientless transport travel150 orgrimmar_ele_top1; nacharrived_topreverseto +orgrimmar_ele_bottom1. Ergebnis+NativePhaseArrivedproRichtungprüfen. + +NacherstemRestart10Restbotsgeprüft;35und121nativnachStormwind/Orgrimmargeborgen +(erneuteProbesbestätigtenBodenstatus), andereTaxi/Autonomieunverändert. +NachfinalemRestartRecoverylistwiederprüfen, Loader1+Config/PopulationReload +undSoll110(50/5/50/5)restaurieren. UserhatvollePopulationwiedererlaubt, recordingfertig. +KeinePush/Commitsangefordert. NeuerShutdownCore003512(altbinaryae0b+)gesichert; +kompatiblealteBinarykopievorhandenworldserver.crashed. ErsteFixBinary13384vor +nächsterInstallfürneuenShutdownCorebeiBedarfseparatsichern. + +--- + +# Fahrstuhlfix implementiert/build+TestsPASS, Installation steht an + +Nutzer verlangt Type11 Ein-/Ausstieg ohneReststandzeit, aber nur bei echtemHalt; +5MinutenalsFahrgastfesthängen=>nativTeleportoriginalZielnodeGegenetage. +Source ClientlessTransportMgr.h/.cpp+DeckMgr.cpp geändert. AtDockType11ignoriert +Pausebudget, Deckreturnebenso; neuerPassengerTimer+originalDestination+ACK- +gestützterFallback. KeineAusnahmefürSchiff/Zeppelin/Tram. ResumedPassagierohne +bekanntenEinstieg verwendetnächsteEtage+eindeutigeGegenetagefürFallback. +Tests test_transport.py (beideRichtungen,Stillstand,exakte300000ms,keineSchiffs- +Ausnahme,Abbruchzielbleibt,unzulässigeZiele/Teleportfehler), deckreturnundgrid +PASS. Build-j10PASS. SourcekeineanderenunerwartetenÄnderungen. + +VorBuildLoader0 Config+PopulationReload bestätigt; SafeLogoutall00:55:25CET. +Nach>300sec10RestbotsvollständigmitverfügbarenFlags/Positionenpersistiert: +checkpoints/2026-11-03-elevator-exit/shutdown-exception.json+nativeLogs. +KeinEinzelForceLogout/AbbruchvonFlug/Fahrt. Normalshutdown10angefordert; +beimShutdownwiederNULLcall-Crash, GDBschreibtCore +server/debug/crash/worldserver-20261103-003512.core/.backtrace.log. +VorInstallationvollständigesEndevon8437/GDB8425prüfen. PassendesaltesBinary +identischmitcheckpoint2026-11-02-crash-140558/worldserver.crashed(cmpPASS). +Erstdann cmake--installbuild/debug, passiveGDBLauncherweiterverwenden. +Loaderbleibt0durchInstall/Restart. Startupready/Portsverifizieren,10Restbots +manuellonline/Bewegungsstatusprüfen, ggf.nativinFraktionshauptstadtbergen. +SeparaterTestbotMorvath150 fürLiveRoundtripvorbereitet unterelevator-exit/ +live-roundtrip.lua, nochNICHTgestartet. KeineAdventureBotsalsTestbotverwenden. +DanachLoader1+Config/PopulationReload und110Populationwiederherstellen. +OriginalConfigpopulation50/5/50/5. KeinPush/Commitbeauftragt. + +--- + +# Mokrak117 Fahrstuhlausstieg diagnostiziert, nochkeinSourcefix + +Nutzer vermutete zuhoheReststandzeitsicherung; bestätigt. NativeTransportStatus +Riding top1->bottom1, spawn219021/entry206610, echterPassagier seit00:45:33. +ClientlessTransportMgr.cpp674 ExitBudget=PathLength/run+points*max(100,2diff)+2500. +Unten4Punkte => schon2900ms ohneLaufzeit. DBCAnimation206610 erkenntunten +Stopfenster2133ms(0..2133) und2533ms(30800..33333), oben5166ms. +Daher untenAtDock(...exitBudget) undDeckMgrReadyunmöglich. KeinKoordinaten- +oderfehlenderRoutenbefund erforderlich. Rohdaten/nominalbudgetin +checkpoints/2026-11-03-elevator-exit/diagnosis.json. KeineSourceänderung, +keinBuild/Restart/BotTeleport odererzwungenesDetach. NächsteKorrektur sollte +realephysischeAusstiegsdauer+begründeteReserve nutzen, Punkt-für-PunktTick- +Wartekosten durch kontinuierlicheRückroute vermeiden; nichtbloßRestzeitprüfung +entfernen oderBudgetaufStandzeitkappen. Schiffe/Zeppelineweiterabsichern. + +--- + +# 03.11.2026 Neustart abgeschlossen: alle110Bots wiederonline + +Nutzer beendeteFahrstuhlaufnahme und erlaubteNeustart+vollePopulation. +Worldserver8437 unterGDB8425, frischeReady/Ports8085+8087/Corelimitunlimited +verifiziert. ZusätzlicherModellfreigabeBreakpointentfernt; GDBCore+volleThread- +stacks bleibenaktiv. NeueDumpbasis worldserver-20261103-003512. +NativeAbschlussstatus: Loader1/schema1/busy0/errorleer; Pools50/5/50/5alle +vollständigonline, pending0. Insgesamt110Bots, keine128vollausreizen. +Checkpoint checkpoints/2026-11-03-passive-restart/result.json, startup.log, +final-status.log. KeinBuild/Install/Sourcepatch. Logger.misc3 bleibtfürLagdiagnose. +LadephasespikesUpdateSessionsbis7571ms auchohneBreakpoint; LangzeitLagsNICHT +alsbehobenmelden. WährendShutdown00:34erneuterNULLcallbeiOPvPCapturePoint- +Destruktion, Core9.3GB+volleStacks gesichertuntervorigerDumpbasis +worldserver-20261102-215114; separateBaustelle, keinFixbehaupten. +AlterWorldserverbeendetundtmuxwegvorNeustart. AktuellerServeroperational. + +--- + +# 03.11.2026 Neustart nach abgeschlossener Aufnahme läuft + +Nutzer bestätigt Aufnahme vorerst beendet und erlaubt sämtlicheBots wieder. +OriginalPopulation50/5/50/5 wiederhergestellt. VorShutdown SafeLogoutall, +dann normalserver shutdown10. ShutdownSIGSEGV in OPvPCapturePoint-Destruktion +(NULLcall), Core9.3GB und volleStacks unterserver/debug/crash/ +worldserver-20261102-215114.core/.backtrace.log gesichert. KeinSourcefix. +AlteProzesse2302/2220 vollständigbeendet, tmuxweg, erst dann Neustart. +NeuerWorldserver8437/GDB8425, passiveGDBohneModellfreigabeBreakpoint, +frischeReady/Ports8085+8087/Corelimitunlimited verifiziert. ConfigLoader1 +undConfig+PopulationReload ausgeführt; automatischePopulation lädtnoch. +NeueDumpbasis worldserver-20261103-003512. Checkpoint +checkpoints/2026-11-03-passive-restart/, startup.log undStartscriptdarin. +NächsterSchritt: bisSollbestand110verifizieren, kurzePhasenzeitmessung, +Ergebnisnotieren. Logger.misc3 bleibtfürLags aktiv, keinLagfixbehaupten. + +--- + +# Laufende Nutzeraufnahme: KEIN NEUSTART bei Lagdiagnose + +Nutzer meldet massive Lags nach Population10. Auf Rückfrage explizit: +„Nein, Aufnahme läuft noch“. Keinen Restart/DebuggerDetach/RecordingReset. +Details checkpoints/2026-11-02-lag-diagnostics/README.md. NativePhaselogging +via temporäremLogger.misc3 aktiviert, PausenMapMgrbis1817ms/Terrain206ms belegt; +Botcontrollersnur~0.35-0.4s/30s. Rootcauseoffen, GDBbreakpointnichtbewiesen. +Keine aktuelle Swaplast; VMausreichendRAM. KeinBuild/Sourcepatch. +Start-passive-diagnostic.sh für späterenVergleich ohneModellFreigabeBreakpoint +vorbereitet, NICHT gestartet. CoreDump/Threadstacks bleibenauchdannaktiv. +BisAufnahmegespeichert bleibtWorldserver2302/GDB2220 unverändert, zehnBots, +Loader0. MesslogsundProzesssamplesgesichert. KeinLagfixbehaupten. + +--- + +# Aktuell: Population auf zehn Bots reduziert für Fahrstuhlaufzeichnung + +Nutzer meldete Lag beim Aufzeichnen und verlangte zehnBots. Configzielwerte +Alliance.Persistent5/Temporary0/Horde.Persistent5/Temporary0 gesetzt. +Loader0 per Config+PopulationReload während laufender Nutzeraufzeichnung +beibehalten; keine automatische Auffüllung/Rotation. VorherigeWerte im +checkpoints/2026-11-02-population-10/operation.json (50/5/50/5,Enabled1). +100Bots Logout angefordert:82 sicher ausgeloggt;18 nach verbleibendem +Combat/Transport/Flight über normalen nativen clientless logout ausgeloggt. +Keine Prozessbeendigung, kein Restart, keine Einzeldatenbank-Logoutmanipulation. +Abschluss NATIV bestätigt pending0/sessions10, beidepersistentPools5online, +temporärePools0online, CPC_PERF players10. Worldserver2302/GDB2220 aktiv. +Verbleibend26/33/35/39/41 Allianz und34/36/40/42/44 Horde. Keine Botlöschung, +keine Tagesplan-/Profiländerung. Nach Nutzeraufzeichnung Loader ggf.wieder +aktivieren, Zehner-Ziel beibehalten bis andersbeauftragt; nicht110restaurieren. + +--- + +# Volakmar nach Orgrimmar zurückgebracht und wieder online + +Nutzerkorrektur: Volakmar128 ist regulärer AdventureBot, kein separater Testbot. +Auf ausdrücklichen Auftrag nativ offline nach Orgrimmar teleportiert, +DB map1/1569.97/-4397.41/16.0472 bestätigt; danach clientless login128. +DB online1 an derselben Position bestätigt. Loader wiederauf1 restauriert, +reload config+clientless population reload ausgeführt. Keine Profil-/Planänderung. +GDB-Worldserver2302 und Crashaufzeichnung unverändert aktiv. +Für künftige gezielte Reproversuche separaten Testbot verwenden. + +--- + +# Objekt-Repro: Level80 Volakmar128 hingebracht und ausgeloggt + +Nutzerauftrag erfüllt: zur Spawn59235-Position map5715988.04/4473.78/-87.3617 +nativ teleportiert. Native Inspect kurz danach5981.088/4477.738/-87.755, +Bot wurde lokal von28373 angegriffen, geriet in defensiveCombat und floh. +SafeLogout wartete aufCombat; anschließend auf expliziten Nutzerlogoutauftrag +normalen clientless logout128 ausgeführt, native Offline und DBonline0 bestätigt. +Letzte gespeicherte Position5971.01/4495.2/-92.1969, health877. Kein Todbelegt. +Temporären GM-Teleport vollständig entfernt+Reload. Worldserver2302 läuftweiter. +Loader vorTest von1auf0+Config/PopulationReload pausiert, bleibt0 damit Testbot +nicht automatisch in den eben verlassenen Grid zurückkehrt. Andere Bots laufen +weiter. Nach gezieltem Reprofenster Loader1+beideReloads restaurieren; keine +sonstigen Pläne/Profile geändert. before/after/evidence unter +checkpoints/2026-11-02-crash-140558/object-repro/. Debugger weiterhin aktiv. + +--- + +# VM-Neustart 02.11.2026 21:51 CET: Diagnosebetrieb wieder gestartet + +Auf Nutzerwunsch identischer GDB-Launcher samt Modellfreigabe-Logpoint gestartet. +Vorher keine Worldserver-/GDB-Prozesse oder tmuxSession vorhanden. +Worldserver PID2302 unter GDB2220, tmuxvcore-worldserver. Aktuelle ready-Meldung +in frischem Server.log, Ports8085/8087 LISTEN, Prozess lebendig, Corelimitunlimited +bestätigt. Loader.Enabled=1 unverändert. Keine Builds/Source-/DB-/Planänderungen. +Neue Diagnosekonsole: checkpoints/2026-11-02-crash-140558/vm-restart-console.log. +Bei Crash automatisch server/debug/crash/worldserver-20261102-215114.core +und worldserver-20261102-215114.backtrace.log (inkl. aller Threadstacks). +Modellfreigaben weiterhin über model-lifetime.gdb erfasst. Bei Crash zuerst +Core/Binary sichern und auswerten; keine konkrete Fehlerbehebung behauptet. + +--- + +# Aktuell 02.11.2026: erneuten Crash ausgewertet + +Nutzer meldete Server offline. GDB266570/Worldserver266615 waren nach Crash +14:05:58 beendet. Core und vollständige Stacks erfolgreich gesichert; passende +Executable separat kopiert. ZUERST checkpoint2026-11-02-crash-140558/README.md +lesen. SIGSEGV std::_Hash_bytes beim GridUnload eines Unstable Explosives, +Spawn59235/map571. GameObjectModel verweist auf Speicher, der nun +Movement::Spline enthält; Modellcache hat dasselbe Modell unter anderer +Adresse. Stale Pointer/Allocation reuse belegt, konkrete vorzeitige Freigabe +noch NICHT zugeordnet. Keine spekulative Sourcekorrektur und kein Build. +Original11:05 vectorassert ist nicht als gleiche Ursache bewiesen. + +Neustart unverändertes Binary mit GDB und zusätzlichem nichtstoppenden +Freigabelogpoint für Sc_Plaguebomb_Orange.m2 (inkl. NUL-Variante). +Launcher/startup log im neuen Checkpoint. Loaderoriginal1 restauriert; +Startupprüfung und native PopulationReload siehe neuesten Checkpointabschluss. +Keinen Raidtest neu gestartet. Auf erneuten Crash muss nicht aktiv gewartetwerden. + +--- + +# Nutzerpause: Crashdiagnose wartet auf erneuten Absturz + +Zwischenauftrag erledigt: Anleitung für Fahrstuhlaufnahmen unter +`doc/clientless/fahrstuehle-aufzeichnen.md` erstellt, Befehle am aktuellen +TransportMgr/Recorder geprüft. AllgemeineTransportdoku hinsichtlich neuem +Entfernungskostenmodell aktualisiert. Keine Server-/DB-/Sourceänderungen, +keinBuild/Restart; laufende GDB-Crashaufzeichnung unverändert. + +Nutzer ausdrücklich: Crash kann Stunden dauern, nicht aktiv weiterwarten; +er ruft erneut auf, sobald er wieder auftritt. Raidtest hat aktuell keinen Vorrang. +Gesicherter Stand: checkpoints/2026-11-02-crash-110519/README.md — ZUERST LESEN. +Worldserver266615 unter GDB266570 in tmuxvcore-worldserver, ready/Ports8085+8087 +und corelimitunlimited verifiziert. Gleiche110Bots manuellgeladen, normale +Autonomie läuft; Loader0 friert Bestand fürRepro ein. KeinBuild/Sourcefix erfolgt. +NächsterfatalerSignalstop erzeugt automatisch +server/debug/crash/worldserver-20261102-114738.core und .backtrace.log +(volleThreadstacks). Binary bisAuswertungnichtaustauschen. +Original11:05:19: conststd::vector Indexassert, dannSIGABRT6, +AbortHandler erzeugt bewusstSIGSEGV. TatsächlicherAufrufernochunbekannt. +Originalcorefehltewegenlimit0; jetzigeAufzeichnungistgeprüftaktiv. +StatischeBIHIndexprüfung27646Bäume ohneFehler. Spawnprüfung1ungeprüfteAnomalie +undfehlendeModelle protokolliert; keineursächlicheZuordnungbehaupten. +BeiWiederaufnahmeaktuellenGDB-Backtrace/Coreauswerten, nichtalteNov1Core. +AktiveBeobachterRaid1100beendet, eigeneCrash-Konsolewirdweiteraufgezeichnet. +LoaderamEndederUntersuchungaufaltenWert1restaurieren+PopulationReload. + +--- + +# Wiederholung 02.11.2026, Gruppenbildung10 / Raid11:00 + +Nutzer: selbenZehnertest erneut durchführen. Alle10 pergezieltemSafeLogout +sauberofflinegebracht, temporärerLoaderpause mitPopulationReload verifiziert. +NurderenalteHandoff-/InstanceVisit-Zustände gesichert/entfernt, nativeTeleports +zurGoldenenRose, Tagespläne10groupformation/the_gilded_rose und11raid/ +instance_visit_at_6354_d0 füralle20Zeilenvalidiert. Alle10wiederonline. +Um10:52vollständig Gruppe2 LeaderBrynda165,159,160,161,162,163,164,165,166, +167,176.2Tank/2Heal/6Damage. NativealleOnSite/waitingatthemeetingpoint. +NatürlicheFrühaufbruchlogik armiert10:56:44MEZ; Raidblockbleibt11:00. +KeineerzwungeneManagerzuweisung, keinBuild/Serverrestart, anderePläneunverändert. +OriginalLoaderkonfiguration restauriert/PopulationReload; Tempteleportgelöscht. +NeueRead-onlyBeobachtung bis12MEZ: +service vcore-adventure-raid-observer-20261102-1100.service, +checkpoints/2026-11-02-1100-raid-test/ (eigeneRohlogs, Snapshots, Manifest). +Voriger10UhrObserverbeendetundGrenzeimLifecycleprotokolliert. Nochkeine +Ankunft/Instanzeintritt fürdiesenneuenLaufbehaupten. Flugfixae0b7513d9 +installiert; bekannterGroundSafety-Fehler bleibtunverändert. + +--- + # Aktuell 02.11.2026, 10:00: Shared-Flight-Landepunkt/Flugpausen korrigiert Nutzer meldete Nachfolger kehren über Plattform um und fliegen stockend. diff --git a/src/server/game/Battlefield/BattlefieldMgr.cpp b/src/server/game/Battlefield/BattlefieldMgr.cpp index 6749ce2cb0..60c56a8067 100644 --- a/src/server/game/Battlefield/BattlefieldMgr.cpp +++ b/src/server/game/Battlefield/BattlefieldMgr.cpp @@ -18,11 +18,13 @@ #include "BattlefieldMgr.h" #include "Containers.h" #include "DatabaseEnv.h" +#include "DBCStores.h" #include "Log.h" #include "Map.h" #include "ObjectMgr.h" #include "Player.h" #include "ScriptMgr.h" +#include "SpellAuraEffects.h" namespace { @@ -141,6 +143,57 @@ bool BattlefieldMgr::IsWorldPvpArea(uint32 zoneId) const return std::find(BattlefieldIdToZoneId.begin(), BattlefieldIdToZoneId.end(), zoneId) != BattlefieldIdToZoneId.end(); } +bool BattlefieldMgr::IsWintergraspFlightAllowed(uint32 mapId, uint32 areaId) const +{ + // Cataclysm's static mount flags omit flight throughout Wintergrasp. + // The actual restriction is temporary and belongs to the battle state. + if (mapId != 571 || !DBCManager::IsInArea(areaId, 4197)) + return false; + for (auto const& [map, battlefields] : _battlefieldsByMap) + if (map->GetId() == mapId) + for (auto const& battlefield : battlefields) + if (battlefield->GetZoneId() == 4197 && battlefield->IsEnabled() && + !battlefield->CanFlyIn()) + return false; + return true; +} + +void BattlefieldMgr::UpdateWintergraspFlight(Player* player) const +{ + // The client also needs this native mount-permission aura; server-only + // AreaTable exceptions do not enable every mount in the client UI. + constexpr uint32 flightPermission = 91262; + bool allowed = IsWintergraspFlightAllowed(player->GetMapId(), player->GetAreaId()); + uint32 otherRestrictions = 0; + bool hasOtherRestrictions = false; + for (AuraEffect const* effect : player->GetAuraEffectsByType(SPELL_AURA_MOUNT_RESTRICTIONS)) + if (effect->GetId() != flightPermission) + { + hasOtherRestrictions = true; + otherRestrictions |= uint32(effect->GetMiscValue()); + } + if (hasOtherRestrictions && !(otherRestrictions & uint32(AreaMountFlags::AllowFlyingMounts))) + allowed = false; + if (allowed) + { + if (!player->HasAura(flightPermission)) + player->AddAura(flightPermission, player); + } + else + player->RemoveAurasDueToSpell(flightPermission); +} + +void BattlefieldMgr::UpdateWintergraspFlight(Map* map) const +{ + for (auto const& reference : map->GetPlayers()) + if (Player* player = reference.GetSource()) + if (player->GetZoneId() == 4197) + { + UpdateWintergraspFlight(player); + player->UpdateMountCapability(); + } +} + Battlefield* BattlefieldMgr::GetBattlefieldToZoneId(Map const* map, uint32 zoneId) { auto itr = _battlefieldsByZone.find({ map, zoneId }); diff --git a/src/server/game/Battlefield/BattlefieldMgr.h b/src/server/game/Battlefield/BattlefieldMgr.h index 9f34d419dd..48d8c8f932 100644 --- a/src/server/game/Battlefield/BattlefieldMgr.h +++ b/src/server/game/Battlefield/BattlefieldMgr.h @@ -52,6 +52,9 @@ class TC_GAME_API BattlefieldMgr void HandlePlayerLeaveZone(Player* player, uint32 zoneId); bool IsWorldPvpArea(uint32 zoneId) const; + bool IsWintergraspFlightAllowed(uint32 mapId, uint32 areaId) const; + void UpdateWintergraspFlight(Player* player) const; + void UpdateWintergraspFlight(Map* map) const; // return assigned battlefield Battlefield* GetBattlefieldToZoneId(Map const* map, uint32 zoneId); diff --git a/src/server/game/Entities/Player/Player.cpp b/src/server/game/Entities/Player/Player.cpp index fa99f37113..15e940e3c5 100644 --- a/src/server/game/Entities/Player/Player.cpp +++ b/src/server/game/Entities/Player/Player.cpp @@ -7086,6 +7086,7 @@ void Player::UpdateArea(uint32 newArea) else RemoveRestFlag(REST_FLAG_IN_FACTION_AREA); + sBattlefieldMgr->UpdateWintergraspFlight(this); UpdateMountCapability(); } diff --git a/src/server/game/Entities/Unit/Unit.cpp b/src/server/game/Entities/Unit/Unit.cpp index e8c03cbfac..edc4a4bfa2 100644 --- a/src/server/game/Entities/Unit/Unit.cpp +++ b/src/server/game/Entities/Unit/Unit.cpp @@ -7911,7 +7911,11 @@ MountCapabilityEntry const* Unit::GetMountCapability(uint32 mountType) const mountFlags |= AreaMountFlags(auraEffect->GetMiscValue()); } else if (AreaTableEntry const* areaTable = sAreaTableStore.LookupEntry(areaId)) + { mountFlags = areaTable->GetMountFlags(); + if (sBattlefieldMgr->IsWintergraspFlightAllowed(GetMapId(), areaId)) + mountFlags |= AreaMountFlags::AllowFlyingMounts; + } ZLiquidStatus liquidStatus = GetLiquidStatus(); isUnderwater = (liquidStatus & LIQUID_MAP_UNDER_WATER) != 0; diff --git a/src/server/game/Spells/SpellInfo.cpp b/src/server/game/Spells/SpellInfo.cpp index 28b5d013da..158fff791f 100644 --- a/src/server/game/Spells/SpellInfo.cpp +++ b/src/server/game/Spells/SpellInfo.cpp @@ -17,6 +17,7 @@ #include "SpellInfo.h" #include "Battleground.h" +#include "BattlefieldMgr.h" #include "Corpse.h" #include "Creature.h" #include "DBCStores.h" @@ -1797,7 +1798,11 @@ SpellCastResult SpellInfo::CheckLocation(uint32 map_id, uint32 zone_id, uint32 a mountFlags |= AreaMountFlags(auraEffect->GetMiscValue()); } else if (AreaTableEntry const* areaTable = sAreaTableStore.LookupEntry(area_id)) + { mountFlags = areaTable->GetMountFlags(); + if (sBattlefieldMgr->IsWintergraspFlightAllowed(map_id, area_id)) + mountFlags |= AreaMountFlags::AllowFlyingMounts; + } if (!(mountFlags.HasFlag(AreaMountFlags::AllowFlyingMounts))) return SPELL_FAILED_INCORRECT_AREA; diff --git a/src/server/game/World/AdventureGroupTravelMgr.cpp b/src/server/game/World/AdventureGroupTravelMgr.cpp index 403f9c1580..c8e99afa60 100644 --- a/src/server/game/World/AdventureGroupTravelMgr.cpp +++ b/src/server/game/World/AdventureGroupTravelMgr.cpp @@ -471,10 +471,25 @@ bool AdventureGroupTravelMgr::ValidateGroundPath(Player* player,std::vectorGetMap()->GetHeight(player->GetPhaseShift(),x,y,z+2,true,6); - if(floor<=INVALID_HEIGHT || !AdventureGroupTravelPolicy::GroundSample(z,floor,last,length/steps))return false; + if(floor<=INVALID_HEIGHT || !AdventureGroupTravelPolicy::GroundSample(z,floor,last,length/steps)) + { + TC_LOG_WARN("server.adventurebot.navigation", + "GROUP_GROUND_REJECT guid=%u map=%u check='ground' segment=%u sample=%u/%u position=(%.3f %.3f %.3f) floor=%.3f previousFloor=%.3f heightError=%.3f floorStep=%.3f allowedHeightError=%.3f allowedFloorStep=%.3f", + player->GetGUID().GetCounter(),player->GetMapId(),uint32(i),step,steps,x,y,z,floor,last, + std::fabs(floor-z),std::fabs(floor-last),AdventureGroupTravelPolicy::MaxGroundStep, + AdventureGroupTravelPolicy::MaxGroundStep+AdventureGroupTravelPolicy::MaxGroundSlope*length/steps); + return false; + } last=floor; } - if(!player->GetMap()->isInLineOfSight(player->GetPhaseShift(),a.GetPositionX(),a.GetPositionY(),a.GetPositionZ()+1,b.GetPositionX(),b.GetPositionY(),b.GetPositionZ()+1,LINEOFSIGHT_ALL_CHECKS,VMAP::ModelIgnoreFlags(0)))return false; + if(!player->GetMap()->isInLineOfSight(player->GetPhaseShift(),a.GetPositionX(),a.GetPositionY(),a.GetPositionZ()+1,b.GetPositionX(),b.GetPositionY(),b.GetPositionZ()+1,LINEOFSIGHT_ALL_CHECKS,VMAP::ModelIgnoreFlags(0))) + { + TC_LOG_WARN("server.adventurebot.navigation", + "GROUP_GROUND_REJECT guid=%u map=%u check='line-of-sight' segment=%u from=(%.3f %.3f %.3f) to=(%.3f %.3f %.3f)", + player->GetGUID().GetCounter(),player->GetMapId(),uint32(i),a.GetPositionX(),a.GetPositionY(), + a.GetPositionZ()+1,b.GetPositionX(),b.GetPositionY(),b.GetPositionZ()+1); + return false; + } } return true; } diff --git a/src/server/game/World/ClientlessActionExecutorMgr.cpp b/src/server/game/World/ClientlessActionExecutorMgr.cpp index 9e10f71a7f..c1af1b9b82 100644 --- a/src/server/game/World/ClientlessActionExecutorMgr.cpp +++ b/src/server/game/World/ClientlessActionExecutorMgr.cpp @@ -2272,6 +2272,13 @@ bool ClientlessActionExecutorMgr::RecoverTravel( {record.RescueActive=false;record.RescueWalkFailed=true;} if (failedEdgeDatabaseId) record.FailedEdgeDatabaseIds.insert(failedEdgeDatabaseId); + // StartTravel can accept a request whose first spline is subsequently + // rejected by the owner validator. Remember dynamic bridges here as well. + if (!failedEdgeDatabaseId && reason.rfind("route-local-", 0) == 0 && + record.RouteStep > 0 && record.RouteStep < record.Route.size() && + record.Route[record.RouteStep].EdgeType == "local_bridge") + record.FailedBridgeKeys.insert(record.Route[record.RouteStep - 1].NodeKey + + "->" + record.Route[record.RouteStep].NodeKey); // A failed initial local approach must not be tried five times at the same spot. if (!failedEdgeDatabaseId && record.RouteStep == 0 && !record.Route.empty() && reason.rfind("route-local-", 0) == 0) diff --git a/src/server/game/World/ClientlessFlightMgr.cpp b/src/server/game/World/ClientlessFlightMgr.cpp index 947f37f94e..77d9419262 100644 --- a/src/server/game/World/ClientlessFlightMgr.cpp +++ b/src/server/game/World/ClientlessFlightMgr.cpp @@ -159,7 +159,10 @@ float ClientlessFlightMgr::Estimate(Player const * player, uint32 map, Position uint32 const area = AreaZone(player, map, from).first; if (auto const * data = sAreaTableStore.LookupEntry(area)) { - bool const dynamicRules = data->World_pvp_ID != 0 ||(map == player->GetMapId() && area == player->GetAreaId() && player->HasAuraType(SPELL_AURA_MOUNT_RESTRICTIONS)); + bool const wintergrasp = map == 571 && DBCManager::IsInArea(area, 4197); + bool const wintergraspFlight = sBattlefieldMgr->IsWintergraspFlightAllowed(map, area); + if (wintergrasp && !wintergraspFlight) return Infinity(); + bool const dynamicRules = wintergraspFlight || data->World_pvp_ID != 0 ||(map == player->GetMapId() && area == player->GetAreaId() && player->HasAuraType(SPELL_AURA_MOUNT_RESTRICTIONS)); if (! dynamicRules && ! data->GetMountFlags().HasFlag(AreaMountFlags::AllowFlyingMounts)) return Infinity(); } // Unknown terrain permission is an estimate only; native mount capability diff --git a/src/server/game/World/ClientlessFlightPath.h b/src/server/game/World/ClientlessFlightPath.h index e7fe4bda0a..55a67a0768 100644 --- a/src/server/game/World/ClientlessFlightPath.h +++ b/src/server/game/World/ClientlessFlightPath.h @@ -124,6 +124,10 @@ namespace ClientlessFlightPath if (! here.Valid || ! end.Valid || std::fabs(end.Ground - goal.Z) > 2.5f) return { }; + // Issuing a takeoff spline is not proof of actual clearance. Handoffs + // may replan while that spline is still close to its support surface. + if (!memory.Departed && from.Z >= here.Ground + cfg.Safety) + memory.Departed = true; using Phase = Memory::ElevatedPhase; if (memory.Elevated != Phase::None && Distance(memory.ElevatedGoal,goal)>0.1f) memory.Elevated=Phase::None; @@ -280,7 +284,6 @@ namespace ClientlessFlightPath // must not postpone descent until directly above the destination. if (openApproach) { - memory.Departed = true; return best; } } @@ -294,11 +297,9 @@ namespace ClientlessFlightPath Point vertical{from.X,from.Y,top}; if (top > from.Z + 2.0f && Corridor(from,vertical,goal,cfg,sample,ray,true)) { - memory.Departed=true; return {vertical,true,false,false}; } } - if (best.Valid) memory.Departed = true; return best; } struct SharedTrail diff --git a/src/server/game/World/ClientlessTestBotMgr.cpp b/src/server/game/World/ClientlessTestBotMgr.cpp index 81feb9be2d..9e1f98f146 100644 --- a/src/server/game/World/ClientlessTestBotMgr.cpp +++ b/src/server/game/World/ClientlessTestBotMgr.cpp @@ -231,6 +231,8 @@ int Skill(lua_State* L) if(!sSkillLineStore.LookupEntry(skill) || value>maximum)return luaL_error(L,"invalid skill or skill value exceeds maximum"); p->SetSkill(uint16(skill),p->GetSkillStep(uint16(skill)),uint16(value),uint16(maximum));lua_pushboolean(L,p->GetPureSkillValue(skill)==value);return 1; } +int HasAura(lua_State* L){auto p=Actor(L);lua_pushboolean(L,p->HasAura(Number(L,2)));return 1;} +int RemoveAura(lua_State* L){auto p=Actor(L);p->RemoveAurasDueToSpell(Number(L,2));return 0;} int Count(lua_State* L){auto p=Actor(L);lua_pushinteger(L,p->GetItemCount(Number(L,2),false));return 1;} int Quest(lua_State* L){auto p=Actor(L);auto id=Number(L,2);lua_pushinteger(L,p->GetQuestStatus(id));lua_pushboolean(L,p->GetQuestRewardStatus(id));return 2;} int Find(lua_State* L) @@ -425,7 +427,7 @@ bool Start(std::string const& id,fs::path path,std::string& error) for(auto name:{"io","os","package","debug","dofile","loadfile","require","load","collectgarbage","coroutine","pcall","xpcall"}){lua_pushnil(L);lua_setglobal(L,name);} lua_newtable(L); for(auto const& fn:std::initializer_list>{ - {"npc_hello",NpcHello},{"menu",Menu},{"spellclick",SpellClick},{"exit_vehicle",ExitVehicle},{"request_cast",RequestCast},{"request_cast_at",RequestCastAt},{"use_item_at",UseItemAt},{"area_trigger",AreaTrigger},{"inventory",Inventory},{"stop",Stop},{"now_ms",Now},{"spawn",Spawn},{"online",Online},{"_create",Create},{"_create_result",CreateResult},{"wait",Wait},{"state",Snapshot},{"teleport",Teleport},{"level",Level},{"stats",Stats},{"stat",Stat},{"learn",Learn},{"item",Item},{"equip",Equip},{"clear_slot",ClearSlot},{"remove_item",RemoveItem},{"skill",Skill},{"item_count",Count},{"quest",Quest},{"use",Use},{"hello",Hello},{"accept",Accept},{"abandon",Abandon},{"reward",Reward},{"gossip",Gossip},{"cast",Cast},{"move",Move},{"face",Face},{"attack",Attack},{"target_state",TargetState},{"objectives",Objectives},{"request_reward",RequestReward},{"loot",Loot},{"loot_item",LootItem},{"loot_release",LootRelease},{"use_item",UseItem},{"record",Record},{"check",Check}})Bind(L,r.get(),fn.first,fn.second); + {"npc_hello",NpcHello},{"menu",Menu},{"spellclick",SpellClick},{"exit_vehicle",ExitVehicle},{"request_cast",RequestCast},{"request_cast_at",RequestCastAt},{"use_item_at",UseItemAt},{"area_trigger",AreaTrigger},{"inventory",Inventory},{"stop",Stop},{"now_ms",Now},{"spawn",Spawn},{"online",Online},{"_create",Create},{"_create_result",CreateResult},{"wait",Wait},{"state",Snapshot},{"teleport",Teleport},{"level",Level},{"stats",Stats},{"stat",Stat},{"learn",Learn},{"item",Item},{"equip",Equip},{"clear_slot",ClearSlot},{"remove_item",RemoveItem},{"skill",Skill},{"has_aura",HasAura},{"remove_aura",RemoveAura},{"item_count",Count},{"quest",Quest},{"use",Use},{"hello",Hello},{"accept",Accept},{"abandon",Abandon},{"reward",Reward},{"gossip",Gossip},{"cast",Cast},{"move",Move},{"face",Face},{"attack",Attack},{"target_state",TargetState},{"objectives",Objectives},{"request_reward",RequestReward},{"loot",Loot},{"loot_item",LootItem},{"loot_release",LootRelease},{"use_item",UseItem},{"record",Record},{"check",Check}})Bind(L,r.get(),fn.first,fn.second); Bind(L,r.get(),"nearest_creature",Nearest);Bind(L,r.get(),"nearest_object",Nearest,1); Bind(L,r.get(),"creature",Find);Bind(L,r.get(),"object",Find,1);lua_setglobal(L,"testbot"); char const* helpers=R"lua( diff --git a/src/server/scripts/Battlefield/BattlefieldWG.cpp b/src/server/scripts/Battlefield/BattlefieldWG.cpp index 873e43c410..298dae0e53 100644 --- a/src/server/scripts/Battlefield/BattlefieldWG.cpp +++ b/src/server/scripts/Battlefield/BattlefieldWG.cpp @@ -540,6 +540,7 @@ bool BattlefieldWG::SetupBattlefield() void BattlefieldWG::OnBattleStart() { + sBattlefieldMgr->UpdateWintergraspFlight(m_Map); // Spawn titan relic if (GameObject* relic = SpawnGameObject(GO_WINTERGRASP_TITAN_S_RELIC, WintergraspRelicPos, WintergraspRelicRot)) { @@ -628,6 +629,7 @@ void BattlefieldWG::UpdateCounterVehicle(bool init) void BattlefieldWG::OnBattleEnd(bool endByTimer) { + sBattlefieldMgr->UpdateWintergraspFlight(m_Map); // Remove relic if (m_titansRelicGUID) if (GameObject* relic = GetGameObject(m_titansRelicGUID)) diff --git a/src/server/scripts/OutdoorPvP/OutdoorPvPZM.cpp b/src/server/scripts/OutdoorPvP/OutdoorPvPZM.cpp index be789d7469..7315b86f42 100644 --- a/src/server/scripts/OutdoorPvP/OutdoorPvPZM.cpp +++ b/src/server/scripts/OutdoorPvP/OutdoorPvPZM.cpp @@ -193,8 +193,8 @@ bool OutdoorPvPZM::SetupOutdoorPvP() for (uint8 i = 0; i < OutdoorPvPZMBuffZonesNum; ++i) RegisterZone(OutdoorPvPZMBuffZones[i]); - m_Graveyard = std::make_unique(this); - AddCapturePoint(m_Graveyard.get()); // though the update function isn't used, the handleusego is! + m_Graveyard = new OPvPCapturePointZM_Graveyard(this); + AddCapturePoint(m_Graveyard); // though the update function isn't used, the handleusego is! return true; } diff --git a/src/server/scripts/OutdoorPvP/OutdoorPvPZM.h b/src/server/scripts/OutdoorPvP/OutdoorPvPZM.h index 5d55247634..bbf5f9dcc0 100644 --- a/src/server/scripts/OutdoorPvP/OutdoorPvPZM.h +++ b/src/server/scripts/OutdoorPvP/OutdoorPvPZM.h @@ -169,7 +169,8 @@ class OutdoorPvPZM : public OutdoorPvP void SetHordeTowersControlled(uint32 count); private: - std::unique_ptr m_Graveyard; + // Owned by OutdoorPvP::m_capturePoints after AddCapturePoint. + OPvPCapturePointZM_Graveyard* m_Graveyard = nullptr; uint32 m_AllianceTowersControlled; uint32 m_HordeTowersControlled; diff --git a/tests/clientless/test_adventure_group_travel.py b/tests/clientless/test_adventure_group_travel.py index 83977f10eb..7bd51d85e6 100644 --- a/tests/clientless/test_adventure_group_travel.py +++ b/tests/clientless/test_adventure_group_travel.py @@ -23,6 +23,7 @@ code=r''' #include using uint32=unsigned; #define TC_GAME_API +#define TC_LOG_WARN(...) ((void)0) constexpr float INVALID_HEIGHT=-100000; constexpr int LINEOFSIGHT_ALL_CHECKS=0,CMSG_AREATRIGGER=0; namespace VMAP{enum class ModelIgnoreFlags{None};} diff --git a/tests/clientless/test_dynamic_bridge_recovery.py b/tests/clientless/test_dynamic_bridge_recovery.py new file mode 100644 index 0000000000..7fc9b517fa --- /dev/null +++ b/tests/clientless/test_dynamic_bridge_recovery.py @@ -0,0 +1,30 @@ +"""Verify delayed dynamic bridge rejection blocks only its directed connection.""" +from pathlib import Path +import subprocess, tempfile +root = Path(__file__).resolve().parents[2] +source = (root / "src/server/game/World/ClientlessActionExecutorMgr.cpp").read_text() +start = source.index(' if (!failedEdgeDatabaseId && reason.rfind("route-local-", 0) == 0 &&') +end = source.index(' // A failed initial local approach', start) +code = r''' +#include +#include +#include +#include +struct Step {std::string NodeKey,EdgeType;}; +struct Record {unsigned RouteStep;std::vector Route;std::unordered_set FailedBridgeKeys;}; +void reject(Record& record,unsigned failedEdgeDatabaseId,std::string reason){ +''' + source[start:end] + r''' +} +int main(){Record r{1,{{"taxi_2","start"},{"stormwind_tradecenter","local_bridge"}}, {}}; +reject(r,0,"route-local-PathRejected");assert(r.FailedBridgeKeys.count("taxi_2->stormwind_tradecenter")); +assert(!r.FailedBridgeKeys.count("stormwind_tradecenter->taxi_2")); +r.FailedBridgeKeys.clear();reject(r,15,"route-local-PathRejected");assert(r.FailedBridgeKeys.empty()); +r.RouteStep=0;reject(r,0,"route-local-PathRejected");assert(r.FailedBridgeKeys.empty()); +r.RouteStep=1;r.Route[1].EdgeType="free_flight";reject(r,0,"route-local-PathRejected");assert(r.FailedBridgeKeys.empty());} +''' +with tempfile.TemporaryDirectory(prefix="vcore-dynamic-bridge-") as directory: + path=Path(directory) + (path/"test.cpp").write_text(code) + subprocess.run(["c++","-std=c++17",str(path/"test.cpp"),"-o",str(path/"test")],check=True) + subprocess.run([str(path/"test")],check=True) +print("Delayed bridge rejection, direction, persistent edges and non-bridge exclusions PASS") diff --git a/tests/clientless/test_flight_policy.py b/tests/clientless/test_flight_policy.py index 9d0ad2bd25..0efba3113f 100644 --- a/tests/clientless/test_flight_policy.py +++ b/tests/clientless/test_flight_policy.py @@ -51,6 +51,8 @@ struct Profile{bool CanFreeFly=true;std::vector FlightMounts{{}};} profil struct Lua{auto GetAdventureProfile(uint32){return &profile;}} lua; 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 DBCManager{static bool IsInArea(uint32 area,uint32 zone){return area==zone || (zone==4197&&area==4538);}}; +struct Battlefields{bool war=false;bool IsWintergraspFlightAllowed(uint32 map,uint32 area){return map==571&&DBCManager::IsInArea(area,4197)&&!war;}} battlefield;auto sBattlefieldMgr=&battlefield; struct SpellMgr{void* GetSpellInfo(uint32){return this;}} spells;auto sSpellMgr=&spells; uint32 FlightType(void*){return 1;} struct MapEntry{uint32 MapType=0;} entry;struct Maps{auto LookupEntry(uint32){return &entry;}} maps;auto& sMapStore=maps; @@ -123,6 +125,12 @@ int main(){ profile.CanFreeFly=false;assert(!mgr.Enabled(&p)); mgr.BeginPlanning();assert(!std::isfinite(mgr.Estimate(&p,0,a,b))); profile.CanFreeFly=true; + // Static Wintergrasp subareas lack flight flags, but peace permits flight. + mgr.areaCode=4538;mgr.zoneCode=4197;area.fly=false;p.cold=true; + assert(std::isfinite(mgr.Estimate(&p,571,a,b))); + battlefield.war=true;assert(!std::isfinite(mgr.Estimate(&p,571,a,b))); + battlefield.war=false;assert(std::isfinite(mgr.Estimate(&p,571,a,b))); + mgr.areaCode=87;mgr.zoneCode=12;assert(!std::isfinite(mgr.Estimate(&p,571,a,b)));area.fly=true; int beforeInterrupts=p.interrupts;Record approach;approach.ApproachingDeparture=true;approach.Status.State=ClientlessFlightState::Mounting;mgr.Fail(&p,approach,"interrupted");assert(travel.cancels==1&&!approach.ApproachingDeparture&&approach.Status.State==ClientlessFlightState::Failed&&p.interrupts==beforeInterrupts); 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_flight_takeoff_handoff.py b/tests/clientless/test_flight_takeoff_handoff.py new file mode 100644 index 0000000000..5524dd6200 --- /dev/null +++ b/tests/clientless/test_flight_takeoff_handoff.py @@ -0,0 +1,16 @@ +"""Replan during takeoff without prematurely demanding cruise clearance.""" +from pathlib import Path +import subprocess, tempfile +root = Path(__file__).resolve().parents[2] +code = r''' +#include "ClientlessFlightPath.h" +#include +using namespace ClientlessFlightPath; +int main(){Settings c;Memory m;auto sample=[](float,float){return Surface{94.314f,550,true};};auto ray=[](Point const&,Point const&){return true;};Point goal{-8186,419,94.314f};auto n=Next({-8829,624,94.314f},goal,c,m,sample,ray);assert(n.Valid&&!m.Departed);n=Next({-8826.7f,622.306f,96.4122f},goal,c,m,sample,ray);assert(n.Valid&&!m.Departed);n=Next({-8800,610,104},goal,c,m,sample,ray);assert(n.Valid&&m.Departed);} +''' +with tempfile.TemporaryDirectory(prefix="vcore-takeoff-handoff-") as directory: + path = Path(directory) + (path / "test.cpp").write_text(code) + subprocess.run(["c++", "-std=c++17", "-I" + str(root / "src/server/game/World"), str(path / "test.cpp"), "-o", str(path / "test")], check=True) + subprocess.run([str(path / "test")], check=True) +print("Takeoff handoff at two yards and promotion at real cruise clearance PASS") diff --git a/tests/clientless/test_wintergrasp_client_permission.py b/tests/clientless/test_wintergrasp_client_permission.py new file mode 100644 index 0000000000..d1426614bb --- /dev/null +++ b/tests/clientless/test_wintergrasp_client_permission.py @@ -0,0 +1,39 @@ +"""Run production WG client permission synchronization and battle refresh.""" +from pathlib import Path +import subprocess,tempfile +root=Path(__file__).resolve().parents[2] +s=(root/"src/server/game/Battlefield/BattlefieldMgr.cpp").read_text() +a=s.index("void BattlefieldMgr::UpdateWintergraspFlight(Player*") +b=s.index("\nBattlefield* BattlefieldMgr::GetBattlefieldToZoneId",a) +code=r''' +#include +#include +#include +using uint32=unsigned; +constexpr unsigned SPELL_AURA_MOUNT_RESTRICTIONS=336; +enum class AreaMountFlags:unsigned{AllowFlyingMounts=2}; +struct AuraEffect {unsigned id,misc;unsigned GetId()const{return id;}int GetMiscValue()const{return misc;}}; +struct Player {unsigned zone=4197,area=4197,map=571;int casts=0,updates=0;std::set auras;std::vector effects; + unsigned GetMapId()const{return map;}unsigned GetAreaId()const{return area;}unsigned GetZoneId()const{return zone;} + auto const& GetAuraEffectsByType(unsigned)const{return effects;}bool HasAura(unsigned id)const{return auras.count(id);} + void AddAura(unsigned id,Player*){auras.insert(id);++casts;}void RemoveAurasDueToSpell(unsigned id){auras.erase(id);}void UpdateMountCapability(){++updates;}}; +struct Reference {Player* player;Player* GetSource()const{return player;}}; +struct Map {std::vector players;auto const& GetPlayers()const{return players;}}; +struct BattlefieldMgr {bool permitted=true;bool IsWintergraspFlightAllowed(unsigned map,unsigned area)const{return permitted&&map==571&&(area==4197||area==4538);}void UpdateWintergraspFlight(Player*)const;void UpdateWintergraspFlight(Map*)const;}; +'''+s[a:b]+r''' +int main(){BattlefieldMgr m;Player p,out;out.zone=210;out.area=210;Map map{{{&p},{&out}}}; + m.UpdateWintergraspFlight(&map);assert(p.HasAura(91262)&&p.casts==1&&p.updates==1&&!out.HasAura(91262)); + AuraEffect own{91262,15};p.effects={&own};m.UpdateWintergraspFlight(&p);assert(p.casts==1&&p.HasAura(91262)); + m.permitted=false;m.UpdateWintergraspFlight(&map);assert(!p.HasAura(91262)&&p.updates==2); + m.permitted=true;m.UpdateWintergraspFlight(&map);assert(p.HasAura(91262)&&p.casts==2); + p.area=4538;m.UpdateWintergraspFlight(&p);assert(p.HasAura(91262)); + p.area=210;m.UpdateWintergraspFlight(&p);assert(!p.HasAura(91262)); + p.area=4197;AuraEffect restriction{91124,13};p.effects={&restriction};m.UpdateWintergraspFlight(&p);assert(!p.HasAura(91262)); + AuraEffect permission{86973,15};p.effects={&permission};m.UpdateWintergraspFlight(&p);assert(p.HasAura(91262)); + p.effects={&own,&restriction};m.UpdateWintergraspFlight(&p);assert(!p.HasAura(91262));} +''' +with tempfile.TemporaryDirectory(prefix="vcore-wg-client-permission-") as d: + p=Path(d);(p/"test.cpp").write_text(code) + subprocess.run(["c++","-std=c++17",str(p/"test.cpp"),"-o",str(p/"test")],check=True) + subprocess.run([str(p/"test")],check=True) +print("WG permission: entry/subarea/idempotence/battle start/end/exit/aura precedence PASS") diff --git a/tests/clientless/test_wintergrasp_flight.py b/tests/clientless/test_wintergrasp_flight.py new file mode 100644 index 0000000000..af025657d8 --- /dev/null +++ b/tests/clientless/test_wintergrasp_flight.py @@ -0,0 +1,25 @@ +"""Exercise shared Wintergrasp mount permission with actual manager method.""" +from pathlib import Path +import subprocess,tempfile +root=Path(__file__).resolve().parents[2] +s=(root/"src/server/game/Battlefield/BattlefieldMgr.cpp").read_text() +a=s.index("bool BattlefieldMgr::IsWintergraspFlightAllowed(") +b=s.index("\nvoid BattlefieldMgr::UpdateWintergraspFlight",a) +code=r''' +#include +#include +#include +#include +using uint32=unsigned; +struct DBCManager{static bool IsInArea(unsigned a,unsigned z){return a==z||(z==4197&&a==4538);}}; +struct Map{unsigned id=571;unsigned GetId()const{return id;}}; +struct Battlefield{bool enabled=true,war=false;unsigned zone=4197;unsigned GetZoneId()const{return zone;}bool IsEnabled()const{return enabled;}bool CanFlyIn()const{return !war;}}; +struct BattlefieldMgr{std::map>> _battlefieldsByMap;bool IsWintergraspFlightAllowed(uint32,uint32)const;}; +'''+s[a:b]+r''' +int main(){BattlefieldMgr m;Map map;assert(m.IsWintergraspFlightAllowed(571,4197));assert(m.IsWintergraspFlightAllowed(571,4538));assert(!m.IsWintergraspFlightAllowed(0,4197));assert(!m.IsWintergraspFlightAllowed(571,210));auto bf=std::make_unique();auto p=bf.get();m._battlefieldsByMap[&map].push_back(std::move(bf));assert(m.IsWintergraspFlightAllowed(571,4538));p->war=true;assert(!m.IsWintergraspFlightAllowed(571,4197));assert(!m.IsWintergraspFlightAllowed(571,4538));p->enabled=false;assert(m.IsWintergraspFlightAllowed(571,4538));p->enabled=true;p->war=false;assert(m.IsWintergraspFlightAllowed(571,4197));} +''' +with tempfile.TemporaryDirectory(prefix="vcore-wg-flight-") as d: + p=Path(d);(p/"test.cpp").write_text(code) + subprocess.run(["c++","-std=c++17",str(p/"test.cpp"),"-o",str(p/"test")],check=True) + subprocess.run([str(p/"test")],check=True) +print("WG main/subarea: absent, disabled, peace, war, end and unrelated areas PASS") diff --git a/tests/clientless/test_wintergrasp_mount_cast.py b/tests/clientless/test_wintergrasp_mount_cast.py new file mode 100644 index 0000000000..09db17d53c --- /dev/null +++ b/tests/clientless/test_wintergrasp_mount_cast.py @@ -0,0 +1,29 @@ +"""Run the production flying-area cast check, including explicit aura restrictions.""" +from pathlib import Path +import subprocess,tempfile +root=Path(__file__).resolve().parents[2] +s=(root/"src/server/game/Spells/SpellInfo.cpp").read_text() +a=s.index(" // continent limitation (virtual continent)",s.index("SpellInfo::CheckLocation")) +b=s.index(" // raid instance limitation",a) +code=r''' +#include +#include +using uint32=unsigned; +enum class AreaMountFlags{None=0,AllowFlyingMounts=2}; +templatestruct EnumFlag{int bits;EnumFlag(T v):bits(int(v)){}void operator|=(T v){bits|=int(v);}bool HasFlag(T v){return bits&int(v);}}; +constexpr int SPELL_AURA_MOUNT_RESTRICTIONS=1,SPELL_ATTR4_ONLY_FLYING_AREAS=2,SPELL_FAILED_INCORRECT_AREA=3; +struct AuraEffect{int mask;int GetMiscValue()const{return mask;}}; +struct Player{bool restrict=false;std::vector auras;bool HasAuraType(int)const{return restrict;}auto const& GetAuraEffectsByType(int)const{return auras;}}; +struct AreaTableEntry{int flags=13;EnumFlag GetMountFlags()const{return AreaMountFlags(flags);}} area; +struct Areas{AreaTableEntry const* LookupEntry(unsigned){return &area;}} sAreaTableStore; +struct Battlefields{bool war=false;bool IsWintergraspFlightAllowed(unsigned map,unsigned id){return map==571&&(id==4197||id==4538)&&!war;}} battlefield;auto sBattlefieldMgr=&battlefield; +struct SpellInfo{bool HasAttribute(int)const{return true;}int CheckLocation(unsigned map_id,unsigned area_id,Player const* player){ +'''+s[a:b]+r''' +return 0;}}; +int main(){SpellInfo s;Player p;assert(s.CheckLocation(571,4197,&p)==0);assert(s.CheckLocation(571,4538,&p)==0);battlefield.war=true;assert(s.CheckLocation(571,4538,&p)==SPELL_FAILED_INCORRECT_AREA);battlefield.war=false;assert(s.CheckLocation(571,4538,&p)==0);assert(s.CheckLocation(571,210,&p)==SPELL_FAILED_INCORRECT_AREA);p.restrict=true;AuraEffect noFlight{1};p.auras={&noFlight};assert(s.CheckLocation(571,4197,&p)==SPELL_FAILED_INCORRECT_AREA);AuraEffect flight{3};p.auras={&flight};assert(s.CheckLocation(571,4197,&p)==0);} +''' +with tempfile.TemporaryDirectory(prefix="vcore-wg-mount-cast-") as directory: + p=Path(directory);(p/"test.cpp").write_text(code) + subprocess.run(["c++","-std=c++17",str(p/"test.cpp"),"-o",str(p/"test")],check=True) + subprocess.run([str(p/"test")],check=True) +print("Production mount cast: WG/main/subarea peace/war/end, unrelated zone, aura precedence PASS") diff --git a/tests/clientless/test_zm_graveyard_ownership.py b/tests/clientless/test_zm_graveyard_ownership.py new file mode 100644 index 0000000000..917cad0a40 --- /dev/null +++ b/tests/clientless/test_zm_graveyard_ownership.py @@ -0,0 +1,40 @@ +"""Exercise ZM graveyard ownership with production registration and member setup.""" +from pathlib import Path +import subprocess, tempfile +root = Path(__file__).resolve().parents[2] +header = (root / "src/server/scripts/OutdoorPvP/OutdoorPvPZM.h").read_text() +script = (root / "src/server/scripts/OutdoorPvP/OutdoorPvPZM.cpp").read_text() +source = (root / "src/server/game/OutdoorPvP/OutdoorPvP.cpp").read_text() +member = next(line for line in header.splitlines() if " m_Graveyard" in line and "State" not in line) +a = script.index(" m_Graveyard =", script.index("bool OutdoorPvPZM::SetupOutdoorPvP")) +b = script.index("\n", script.index("AddCapturePoint(",a)) +setup = script[a:b] +a = source.index("void OutdoorPvP::AddCapturePoint(") +b = source.index("\nOPvPCapturePoint* OutdoorPvP::GetCapturePoint",a) +registration = source[a:b] +code = r''' +#include +#include +#include +#define TC_LOG_ERROR(...) ((void)0) +struct OutdoorPvP; +int destroyed=0; +struct OPvPCapturePoint {unsigned m_capturePointSpawnId=0; virtual ~OPvPCapturePoint(){++destroyed;}}; +struct OPvPCapturePointZM_Graveyard:OPvPCapturePoint {explicit OPvPCapturePointZM_Graveyard(OutdoorPvP*){}}; +struct OutdoorPvP { + using OPvPCapturePointMap=std::map>; + OPvPCapturePointMap m_capturePoints; + virtual ~OutdoorPvP()=default; + void AddCapturePoint(OPvPCapturePoint* cp); +}; +''' + registration + "struct OutdoorPvPZM: OutdoorPvP {\n" + member + "\nvoid setup(){\n" + setup + r''' +} +}; +int main(){for(int i=0;i<100;++i){auto p=std::make_unique();p->setup();assert(p->m_capturePoints.size()==1);assert(p->m_capturePoints.begin()->second.get()==p->m_Graveyard);}assert(destroyed==100);} +''' +with tempfile.TemporaryDirectory(prefix="vcore-zm-ownership-") as directory: + path=Path(directory) + (path/"test.cpp").write_text(code) + subprocess.run(["c++","-std=c++17","-fsanitize=address","-fno-omit-frame-pointer",str(path/"test.cpp"),"-o",str(path/"test")],check=True) + subprocess.run([str(path/"test")],check=True) +print("ZM graveyard: 100 lifecycles, exactly one destruction each, AddressSanitizer PASS")