diff --git a/doc/clientless/SESSION-RESUME.md b/doc/clientless/SESSION-RESUME.md index dc9f43181e..0c27c578b1 100644 --- a/doc/clientless/SESSION-RESUME.md +++ b/doc/clientless/SESSION-RESUME.md @@ -1,3 +1,380 @@ +# 2026-11-04 – Lokale Commits auf Nutzerwunsch + +Instanzgruppen-Schutz separat committed als f8ee1d0ff6. Flugfortschritts- +überwachung (60s echter Stillstand, native Dauerdiagnose, lokale Recovery vor +Rückkehr) und Fluganschluss zum bestehenden Routenpunkt werden zusammen mit +Tests und diesem Checkpoint im folgenden Navigationscommit gesichert. +Build/Test/Deploymentzustand siehe Eintrag darunter, unverändert. Keine neuen +Builds/Neustarts nötig, keine Pushes. Große Dumps/Binaries und operative +Snapshotverzeichnisse bleiben außerhalb der Commits erhalten. + +--- + +# 2026-11-04 01:13 – Flugfortschritt und lokaler Fluganschluss installiert + +Voriges Checkpoint ergänzt: Build installiert und Neustart verifiziert. +Alter Shutdown löste bekannten SIGSEGV GameObject::RemoveFromWorld:651 +(m_zoneScript callback) aus, NOCH altes Binary; passiver GDB schrieb Core +/server/debug/crash/worldserver-20261103-231350.core und Backtrace. Passendes +Binary checkpoints/2026-11-04-flight-progress/worldserver-before-flight-fix +bewahrt. Erst nach Ende Worldserver+GDB installiert. 1.Startlauncher scheiterte +fehlendem Execute-Bit (kein Server gestartet); 2.Start explizit via bash klappt. +Neuer PID128950, Bereitschaft im FRISCHEN Server.log (Appender w; Console +Loglevel3 zeigt INFO-ready nicht), Ports8085+8087 verifiziert, Prozess stabil. + +Nach 5min sicherer Logoutdrain verbliebene Roster+Probes vorShutdown gesichert +(26Rosterzeilen; native28Sessions im zeitversetzten Sampling, ggfRemoving). +Alle ursprünglichen118Bots wiederonline, Scheduleon, SafeLogoutresume, +Loader wieder1. Nachlogin Restbots erneut auf Bewegung/Flight/Transport geprüft: +36Edwin und99Shadra standen ohne Fortschritt auf alter ungestützter Position, +nativer FlightFailed/0Segmente/flying0; nachOrgrimmar teleportiert und native +Bodenposition bestätigt. Andere aktiveTaxi/Flüge erhalten, keineZwangslogouts. +ROUTE_APPROACH_FLIGHT im Livebetrieb bereits aufgetreten (u.a166), keine +vollständigeGameplayabnahme/erzwungene60sStallprobe, Nutzer prüft imSpiel. +10gezielteTests bestanden; bekannterBaselinefehler test_flight_departure +separat aufHEAD bestätigt. Ergebnis/Recovery/Ready/Tests/Build/Install im +Checkpointverzeichnis. Keine Commits/Pushes. Früherer AFK/Entfernungsschutz-WIP +weiter erhalten und deployed. Neue Quellheader+Tests teils untracked: NICHT +vergessen beim späteren gewünschten Commit. + +--- + +# 2026-11-04 01:07 – Flugfortschritt und lokaler Fluganschluss implementiert, Installation ausstehend + +Autorisiert: echte Stillstandserkennung+60s pro Segment, erwartete native +Flugdauer berücksichtigen, lokal wiederherstellen vor Rückkehr; außerdem +vorgemerkten Bodenanschluss/Flug-Fallback bei gleicher Gruppenroute umsetzen. +Source: ClientlessFlightProgress.h (0.5yd kumulierter Distanzfortschritt zum +3D-Segmentziel, 60s ohne Fortschritt; native Spline-Dauer als Erwartung/Diagnose, +KEIN hartes Laufzeitlimit für fortschreitenden Flug). FlightMgr misst jeden Tick, +versucht bei Stillstand bis zu2 lokale Neuplanungen mit beibehaltenem Following, +Ziel, Breadcrumbs, Start und Departurezustand. Erst erneuter Stillstand nach +2Versuchen oder ungültiger lokaler Korridor löst bestehenden sicheren Fail aus. +ActionExecutor versucht bei abgelehntem lokalen Start/Bridgebodenweg zuerst +FlightMgr::Start zum GLEICHEN Routenpunkt; Route/Step/Modalprofil bleiben. +Native Flugberechtigung, Korridor, solide Landung+MMAP-Anschluss bleiben aktiv. +Ein Versuch proRoutenpunkt, kein Bypass von aufgezeichneten Straßen/Transport. + +Build -j10 abgeschlossen erfolgreich; gezielte Tests10/11 bestanden, neues +Produktionsmethoden-Harness test_route_approach_flight und erweiterter +Update-Harness test_flight_handoff bestanden (60s/jitter/vertikal/weiteFlüge/ +2RecoveryVersuche/Leadertrail erhalten). test_flight_departure scheitert +unordered_map::at bereits unverändert auf HEAD (separat reproduziert), veraltete +Erwartung für overhead/openFromFeet-Zulassung, NICHT durch diese Änderungen. +Bestehende WIP des AFK/Entfernungsschutzes unangetastet, keine Commits/Pushes. + +Loader1->0, sichere Logouts angefordert 2026-11-04T01:03:26.709253+01:00. +Operation/Roster/Build/Tests in checkpoints/2026-11-04-flight-progress/. +Original118Bots. 01:06 noch33Sessions,25Flug,6Kampf. 5min-Schutdownausnahme +frühestens01:08:27; VORHER NICHT installieren oder neustarten. Verbliebene +Bots vorShutdown erneut sichern (Roster/frischeFlightProbes/Transportstatus/ +verfügbare Telemetrie). PassiveGDB-Crashlauncher erhalten. Nach Installation +Startbereitschaft+Ports prüfen, Loader durchStart pausiert lassen; erfasste +Restbots nachRelog prüfen, bei Transport/Flugrest ggf Kapitalteleport und +verifizieren. Danach vorherigen Botbetrieb118 wiederherstellen, SafeLogout +resume/Scheduleon/Loader1. Aktuelle Welt läuft noch PID106171. Kein Gameplay- +Akzeptanznachweis für neue Builds vor Installation. + +--- + +# 2026-11-04 – Vorgemerkte Flug-/Bodenprüfung und Winienwi-Diagnose + +Nutzer ausdrücklich: merken, Flug kann Höhenunterschiede überwinden; 1.75yd +Bodenpfadtoleranz darf nicht Flugbewegung begrenzen. Getrennte Prüfungen für +freien Flugkorridor, sichere erreichbare Landung und tatsächlichen Bodenweg. +Bei abgelehntem lokalem Bodenanschluss fliegerische Alternative prüfen, bevor +Makroroute wechseln. Unterirdische Ziele benötigen erreichbaren Eingang und +Bodenanschluss. Vorgemerkt, noch NICHT implementiert. + +Winienwi163 benutzt SW-TH-Portal00:42:26 erfolgreich. Problem danach am Flug +zur Bastion: Start00:42:28, gemeinsamer Trail Leader160. 00:45:31 Abbruch +'flight segment made no confirmed progress' bei(-4897.04,-4485.46,668.396), +ca263yd horizontal/159yd unter Ziel. Letzter Segmentstart00:45:20. Code prüft +hier nur SegmentElapsed>10000 bei aktiver Spline, keine gemessene Bewegungs- +oder Distanzentwicklung! Exakte Ursache warum Spline dort nicht fertig wird +nicht belegt; Meldung beweist keinen blockierten Korridor. Shared-Wait und +Handoff können ebenfalls ohne neues Segment zurückkehren. Rückkehr bis +00:49:44 zum Abflugort, Neuversuch00:49:58, Landung00:53:21 auf Plattform +(-4905.67,-4232.752,827.766), Bodenanschluss12yd erfolgreich. Eintritt00:53:34 +map671 instance4. Nativ action Completed / waiting just inside private instance +entrance bestätigt. Kein Eingriff/Build; nur Diagnose und Checkpoint. + +--- + +# 2026-11-04 – Renrens Portalroute und abweichende Recovery rekonstruiert + +Nur Diagnose auf Nutzerfrage, keine Code- oder Laufzeitänderung. Renren159 +übernimmt zunächst den Flug von Leader160 zum Schattenhochlandportal200627 +(00:37:57). Leader scheitert am Anflug, landet nach Airborne-Recovery lokal +und startet 00:38:34 einen Flug zu anderem Ziel/Anker220366. FlightMgr::Store +bricht alte Published-Spur ab; Replacement nur bei gleichem Ziel (<1yd). +Renren versucht deshalb lokale Landung, findet keine sichere und kehrt entlang +validierter Flugspur zurück (00:38:34; Rückkehr Handelszentrum00:39:22). +Konkrete geometrische Ablehnung dieser ersten lokalen Landung nicht geloggt. +Leader benutzt TH-Portal erfolgreich00:39:24 (PORTAL_USE_START1801/200627). +Renren hat somit keine protokollierte Portalbenutzungs-/Berechtigungsablehnung. + +00:41:13 nochmals TRAVEL_SHARED_ROUTE159 leader160 steps5 belegt. Bei lokaler +Annäherung scheitert Bodenpfad; Recovery versucht TH-Portal nochmals direkt. +00:41:17 GROUP_GROUND_REJECT bei(-8275.555,454.338,115.548), floor113.391: +heightError2.157 > allowed1.750. Recovery sperrt fehlgeschlagene Startanker, +plant eigenständig via PlanAdaptiveRoute mit beibehaltenen JourneyCosts, ruft +AdoptRoute NICHT erneut auf. Danach auch Vashjir-Anker abgelehnt, Recovery3 +führt zum Freiflug auf portal_at_2171_target (unterirdischer Trameingang). +Dies erklärt Abweichen trotz anfänglicher Leaderroute. Kein Nachweis, dass +die vollständige Route tatsächlich Tramtransport benutzt; Knoten ist Anker. +Nutzer bestätigt Untergrund als Hindernis des Sinkflugs. Ungelöst: Erreichbarkeit +unterirdischer Fluganker, Oszillation und gruppenerhaltende lokale Recovery. +Belege in adventurebot-navigation.log und Rotation .2026-11-04_00-40-33. + +--- + +# 2026-11-04 00:42 – Renren hängt in Sturmwinder Flugausweichschleife + +Nutzer meldetRenren159verlässtSturmwindnicht. Nativgroup6leader160, +RaidzielBastionweiteraktiv. AktuellerMakroschritt free_flight zu +portal_at_2171_target = Stormwind-TramExitTarget001(-8359.27,517.585,84.4087). +Landesucheverschiebtauf(-8399.27,586.87,92.69). KeingeplanterTramtransport +belegt: KanteistFreiflug, KnotennamealleinsagtkeinVerkehrsmittel. +Start00:41:19 bei(-8303.14,475.129,123.352), ReplanwegenAusweichanteil00:41:23 +(12Segmente/12Avoid). Ab00:41:31abwechselndSinkflugziel(-8347,534,138) und +Ausweichziel(-8380,567,159), vielevorzeitigeHandoffs. Nach40sec145Segmente, +weiterhinengerSturmwindbereichstattFortschritt. NativeStatus00:41:33Pos +(-8361.517,551.054,146.645),mounted1/flying1. KeinSharedLandingWait, +keinBerechtigungsproblem. GreedyLokalausweichen+vorzeitigeSpline-Neuplanung +oszilliert; konkretegeometrischeAblehnungdesjeweiligenSinkkorridorsnochNICHT +mitDetailprobeisoliert. EinmaligeAirborneRecoverybereitsverbraucht,weitere +OszillationwirdnurspätdurchgesamtTravelBudget/Timeoutbegrenzt. +NurDiagnose,keineSourceänderung/Teleport/Reset. NutzerentscheidetFix/Recovery. + +--- + +# 2026-11-04 00:31 – Botbetrieb wieder aktiv, 00/01-Uhr-Test eingerichtet + +Nutzer verlangt alleBots wiederonline und exaktbekannten10erTest erneut: +00:00Gruppenbildung anGoldenerRose,01:00RaidBastiondesZwielichts normal10. +Auftragkam00:26;00Uhr istlaufenderStundenblock, keine rückwirkendeAusführung. +SafeLogout mit clientless logout resume aufgehoben (Allfalse/Pendingleer), +118ursprünglicheBotsaus Lag-Checkpoint wiederangemeldet. Tagespläneauchfür +nochverwalteteStop-Bots wiederScheduleon. Loaderwieder1+populationreload, +nativ pending0/sessions118 geprüft. Lagpause ist AUSDRÜCKLICHAUFGEHOBEN; +nicht beimnächstenTurnerneutpausieren ohne neuenAnlass. Lagursache bleibt offen. + +BekannteTestgruppe159–167 und176,alle85,2Tanks167/176,2Heiler161/162,6DPS. +VorReset altePlan-/Instanz-/Handoffzuständegesichert. ZuerstPlänefürheute +20261104 Stunde0 groupformation/the_gilded_rose und1 raid/ +instance_visit_at_6354_d0 gesetzt. DanngezielterTestlogout, alteVisit-/Handoff- +Datennurdieser10bereinigt, offlineSturmwindGoldeneRosepositiongesetzt. +NachRelogkurzBehaviorStop+nativeLeave (außerhalbInstanz), alteGruppenleer +geprüft, Scheduleonfüralle10. KeineanderenBotplänegeändert. +Alle20Planzeilenverifiziert. NativALLE10 groupformation/GoldeneRose,Schedule1, +Deadline01:00 bestätigt, PositioneninSW/Gasthausumfeld. ReguläreGruppensuchgänge +könnenkurzvorderTaverneführen. NativeGruppenmanagerübernimmtZuteilung, keine +künstlicheRaidfüllung. ServerPID106171läuft,keinBuild/Restart/Codechange. +KeineDauerbeobachtung/keineAuswertungsautomatik eingerichtet. + +Artefakte checkpoints/2026-11-04-0100-raid-test/:Manifest,Originalroster, +VorherSnapshots,20verifiziertePläne,OfflineRosepositionen undNativeFormation. +OffeneSource-WIPdesAFK-/Entfernungsschutzes unverändertuncommittet erhalten. +KeinCommit/Push in diesemTurnangefordert. + +--- + +# 2026-11-04 00:02 – Heftige Lags, Botbetrieb zur Entlastung pausiert + +Nutzer meldet starke Lags. ServerPID106171 aktiv. Logs: wiederholte teure +Routenplanung, Sessionticks bis571ms, CPC slowGuid60 bis743ms. Keine großen +Gitprozesse (nur normale kleine UI-Diffs), vmstat kein akuter Swap-/IO-Stau, +ca4GiRAM verfügbar trotz2.1Gi belegtemSwap. Route-/Flight-/Actionlast belegt, +alleinige Ursache noch nicht abschließend isoliert. Mitternachtswechsel als +Auslöser möglich, aber nicht belegt. + +Zur reversiblen Entlastung Loader1->0,populationreload,safeLogoutall angefordert, +KEIN Build/Restart/ForceLogout. Vorher118Bots, ursprünglicherRoster/Configstatus +checkpoints/2026-11-04-lag/ gesichert. NativeSessionzahl nimmt rasch ab; +letztePhase42aktiveBots,981Ticks/30s gegenüber118Bots/691Ticks zuvor. +Loader und SafeLogoutAll vorerst AKTIV PAUSIERT LASSEN, nicht automatisch +wieder118Bots einschalten solangeLagursache offen/NutzerkeineEntwarnung gibt. +Kämpfe/Flüge/Taxi/Transport dürfen natürlich abschließen. Bei nächster +Wiederaufnahme aktuellen logoutstatus abfragen (DBonline kann kurzfristig +von NativeSessions abweichen). Genauere CPU-/Planungsdiagnose bleibt offen. + +--- + +# 2026-11-03 – Instanzgruppen gegen AFK-/Entfernungsaustritte geschützt + +Auf Nutzerauftrag implementiert und installiert. AdventureBehaviorMgr prüft +mit ProtectInstanceGroupLeave gruppenweit Online-Mitglieder: tatsächlicher +Dungeon/Raid-Aufenthalt (auch menschlicher Leader ohne Botdatensatz), aktiver +Instanzbesuch auf Anreise/Vorbereitung/Eintritt/Inside/Returning oder gültiger +Raid-/Dungeon-Anreiseintent vor Beginn des Visits. Beendete Rest-Phase und +Offline-Mitglieder halten Schutz nicht dauerhaft aktiv. IsInWorld-Absicherung +verhindert Zugriff auf noch nicht initialisierte Maps beim Login. +AdventureInteractionMgr unterdrückt damit ausschließlich leader-afk und +leader-distance und setzt währenddessen die Idle-/AFK-Uhren zurück. Nach Ende +beginnt die10min-Frist neu. Intentwechsel, manuelle Austritte und bestehende +Kampf-/Transport-/Formationsregeln bleiben unverändert. + +Gezielte Tests test_instance_group_protection.py und erweiterter + test_adventure_interactions.py bestanden: Raid-/Dungeon-Reisephasen, +menschlicher Leader bereits innen, verspätetes Mitglied draußen, Offline, +Besuchsende, ungültige/abgelaufene Intents, Entfernungs-/AFK-Sperre, neue Frist +und weiterhin erlaubter Intentwechsel. Build -j10 und Installation PASS. +Gameplayabnahme im nächsten Nutzerlauf noch offen; keinen10min-Livetest behaupten. + +Loader vor beiden Builds pausiert, SafeLogoutall angefordert. Letzte Anfrage +23:05:19, Shutdown erst deutlich nach23:10:19. Restliste inkl. Positionen, +Fraktion und verfügbaren Native-Flags gesichert. Generierte Online-Bots vor +Shutdown zusätzlich ohne Ready-Filter abgefragt; menschlicher Test6 durch +nativeInfo als nichtclientless identifiziert und nicht als Recoverybot behandelt. +Bekannter separater Shutdownfehler GameObject::RemoveFromWorld651 erneut +aufgetreten; Core worldserver-20261103-194258.core und passende alte Executable +worldserver-before-fix im Checkpoint gesichert. Nicht behoben. + +Neuer Server PID106171, frische ready-Meldung, Ports8085/8087 und Fortbestand +geprüft. Alle118 ursprünglichen Adventure-Bots online, Loader1,pending0. +Nach wirklichem Online Restbots geprüft; aktuelle Koordinaten bei schwierigen +Mehrfachflächen nochmals geprobt. Fehlgeschlagene/stationäre abgerissene Flüge +36Edwin/74Nimbin/83Mahuwa/102Corren/104Lillini/112Aeliriel/132Braneron/ +166Corric/168Elanora/169Bromdan nativ in Fraktionshauptstadt zurückgesetzt, +Ergebnispositionen bestätigt.112 und166 danach bereits wieder regulär fliegend. +Laufende Flüge161/186, normale Taxifahrten und bereits weiterbewegte84/109/163 +nicht unterbrochen. Keine neue Testzeit oder Dauerbeobachtung eingerichtet. + +Artefakte checkpoints/2026-11-03-instance-group-protection/ (Roster, +Operation, Visitbackup, verbleibende Native-Zustände, Build/Install/Start, +Recovery/result.json). Neuer Test test_instance_group_protection.py noch +untracked; Sourceänderungen noch uncommittet, in diesem Turn kein Commit/Push +angefordert. Alle vorherigen Änderungen erhalten. Separater Admission-Retry- +Fehler nach bereits erfolgter Ablehnung und Wartungs-Resume bleiben offen; +diese Aufgabe betrifft gezielt Gruppenerhalt bei aktiver Instanzreise. + +--- + +# 2026-11-03 – AFK-/Entfernungsregeln bei Instanzreise in Arbeit + +Nutzer beauftragt: 10-Minuten-AFK und leader-distance sollen nicht gelten, +wenn Gruppe zur Instanz reist oder ein Mitglied bereits drinnen ist. +ProtectInstanceGroupLeave prüft gruppenweit Online-Mitglieder: tatsächlicher +Dungeon/Raid-Aufenthalt (auch menschlicher Leader ohne Botrecord), aktiver +Visit in Travel/Preparing/Entering/Inside/Returning, oder gültiger noch nicht +abgelaufener Raid-/Instanz-Anreiseintent. Rest/abgeschlossene Besuche und +Offline-Mitglieder schützen nicht dauerhaft. Keine Änderung anderer +Austrittsgründe. InteractionMgr setzt dabei Idle/AFK-Uhren zurück und sperrt +nur leader-distance/leader-afk, nicht intent-change/manuellen Austritt. + +Produktionscode-Regressionsprüfung test_instance_group_protection.py PASS; +erweiterter test_adventure_interactions.py PASS (Entfernung+AFK während Reise, +neue10min-Frist nach Ende, Entfernung danach aktiv, Intentwechsel unverändert). +Build1 PASS. Build-final läuft nach zusätzlicher IsInWorld-Absicherung und +kompiliert AdventureBehaviorMgr.cpp erneut (keine alte Objektdatei verwenden). +Loader0+safeLogoutall vor JEDEM Build angefordert. Operation/Roster/Visitbackup +unter checkpoints/2026-11-03-instance-group-protection/. +VorBuild war22Uhr-Test bereits beendet: nur einandererVisitStageTravel gespeichert, +keine Inside-Sitzungen mehr. LetzteSafeLogout-Frist ausoperation.json beachten: +volle5min vorShutdown bei Restbots. Noch35Sessions beiZweitbuild. Installation, +Neustart,Recovery undLoaderRestore nochOFFEN. SeparateZoneScript-Crash-Dumps +weiteraufzeichnen,keineanderenDiensteberühren. KeinCommit/Pushbeauftragt. + +--- + +# 22:39 – Eintrittsablehnung beider Bots geklärt + +Beide Denial-Traces belegen group0/leader0: Winienwi16322:17:59 imrotierten +Navlog adventurebot-navigation.log.2026-11-03_22-22-06, Taliaarnora16422:30:33 +imaktuellenLog. Instance.IgnoreRaid0, nativeMap::PlayerCannotEnter verlangt +für671eineRaidgruppe (CANNOT_ENTER_NOT_IN_RAID). Achtanderekonntenhinein. +Serverlogbelegt fürbeide zuvor INTERACTION_GROUP_LEAVE reasonleader-afk. +AdventureInteractionMgr prüft10minLeaderExpired, Bewegungs-/Aktionslosigkeit +zählt; SchutzForming/Travel greift nichtmehr überall während verspäteterAnreise. +Aruendas162wartetalsLeaderderAchtergruppeplanmäßiginnen; dieserperformative +Wartezustand wird fürNachzüglernichtalsaktiveRaidteilnahmegeschützt. +SpäterGruppe4aus163/164gebildet, aberbeideBesuche nachDeniedbereitsRest5: +keinautomatischerneuerEintrittnachGruppenbildung. Somit Gruppenerhalt+ +AdmissionRetry, keinNavigations-/Rollen-/Bindungsproblem. ExakterDenyEnum +nichtimLog, aberSolozustand+nativgeltendeRegel belegen Zugangsbedingung. +KeineDaten-/Codeänderung, nurweiterführendeDiagnose. UserentscheidetFix. + +--- + +# 2026-11-03 22:36 – Teststatus: acht drinnen, zwei am Eingang abgewiesen + +22Uhr-Test:159/160/161/162/165/166/167/176 warten korrekt in Bastion671, +Instanz3,PhaseInside3,injury0,schedule1. Winienwi163 undTaliaarnora164 sind +aufEingangsplattform(-4899.67,-4222.36,827.764) angekommen, FlugArrived und +RouteCompleted; BesuchPhaseRest5,instance_id0,injury0 => NICHTwiederausgetreten, +sondernEintrittabgewiesen. INSTANCE_ENTRY_DENIED beider imServerlog. +Taliaarnora konkret22:30:33 imTracegroup0/leader0(allein) amEintritt. Aktuell +nativeGruppenaufgeteilt: Gruppe1Leader162Aruendas hat8Bots; Gruppe4Leader163 +Winienwi hat163+164, beidegroupsType2(Raid),normalSchwierigkeit0. Keinen +fehlendenHeiler/Tank alsZugangsvoraussetzung behaupten (performativeRaids!). +Gruppenbildung nicht zumgewünschteneinheitlichen10erRaid zusammengeführt. +Keinepersönlichencharacter_instance-Bindungenfür163/164 an671gefunden, +Gruppe4nochkeineInstanzbindung; Gruppe1boundInstanz3. ExakternativeDenyReason +nichtgeloggt,keinenBindungskonfliktbehaupten. BeiTaliaarnoraNoGroupamVersuch +belegt; fürWinienwientsprechendenZeitpunktnochgesondertprüfen. +RaidblockEnde23:00,regulärerReturn22:55. Read-onlyStatus,keineBewegung/Änderung. +Snapshot checkpoints/2026-11-03-2200-raid-test/status-2236/. NavigationbisEingang +aller10gelungen; alsvollständigen10erTest NICHTerfolgreichwerten, solange +Gruppen-/Eintrittszustand2Botsausgrenzt. NutzerentscheidetnächsteSchritte. + +--- + +# 2026-11-03 21:36 – Neuer Test 21/22 Uhr vollständig eingerichtet + +Nutzer meldete Unterbrechung des vorherigen20/21-Tests und verlangte erneut +Vorbereitung, jetzt21:00Gruppenbildung und22:00Raid. AktuelleServerzeit bei +Beginn21:34, daher21Uhr ist der laufendeStundenblock. Bekannte zehn85er: +159Renren,160Marndra,161Marnda,162Aruendas,163Winienwi,164Taliaarnora, +165Brynda,166Corric,167Ragnorli,176Ashaelri;2Tanks167/176,2Heiler161/162,6DPS. + +Vorher neun noch inRaid671, Renren schonSturmwind. ZuerstPläne21formation/ +the_gilded_rose und22raid/instance_visit_at_6354_d0 gesetzt und20Zeilen geprüft, +danngezielterTestlogout/PositionsresetGoldeneRose. AlteInstanceVisit-/Handoff- +Zustände nurdieser10gelöscht;keineNativeRaidbindungen/anderenBots verändert. +RequestLeaveinderInstanzwurdekorrektabgelehnt;nachLogininSWkurzBehaviorStop, +dannnativeLeavefüralle10. AlteGruppenmitgliedschaften danachleer geprüft, +Schedulefüralle10wiederon. ZuletztALLE10nativ groupformation/GoldeneRose, +Schedule1 undDeadline22:00 bestätigt, Positionen imGasthaus/Umfeld. Winienwi +läuft vomkurzenGruppensuchgangzurückzurTaverne, keinFernflug/alteAnreise. + +Loadernurkurzpausiert, jetztwieder1+populationreload. ServerPID84679läuft, +keinBuild/Restart/Codechange. Nutzer beobachtet, KEINE Dauerbeobachtung oder +Auswertungsautomatik. AktuellesManifest/Backups/Offlinepositionen/20verifizierte +Planzeilen undformation-verified.log unter +checkpoints/2026-11-03-2200-raid-test/. Vorherige20/21-Planung ist fürdiesen +Testüberschrieben. NichtbehauptenStartbereits21:00gemessen;Setuperst21:35/36. + +--- + +# 20:02 – Raidgruppe nach Wartungsrestart in Innen-/Außengruppe geteilt + +Nutzer fragt warum sechs wieder draußen. Snapshot: draußen159Renren/161Marnda/ +164Taliaarnora/165Brynda/166Corric/167Ragnorli, PhaseRest5,injury_applied1, +schedule0; drinnen160Marndra/162Aruendas/163Winienwi/176Ashaelri, PhaseInside3, +injury_applied0,schedule1. Alle derselbeRaid671/Instanz6,Blockende21:00, +regulärerReturnAt20:55, somit kein geplantes20:00-Raidende. + +Die sechs waren schon vor Navigationsbuild inInstanz6 (Roster19:30belegt). +SafeLogout ruft adventure stop auf, setztSchedulefalse und beiInstanz +Abort=true. OfflineSave speichertSchedulefalse+PhaseInside. NachRestart +INSTANCE_RESUME diesersechs zeigt gespeichertePhase3, aber tatsächlichen +Playermap0/instance0. InstanzUpdate wertet diesen Kartenmismatch alsRückkehr, +führtReturning->Rest aus und wendet performativenHealthverlust an; dadurch +keinWiedereintrittbisBlockende. Die vier anderen kamen erstnachRestart hinein +und warten korrektinnen. NativeLoadFromDB kann beiInstanz-Loginablehnung oder +fehlendemSave zumEingangsetzen; welcher denyReason hier genau vorlag, wurde +nichtgeloggt und ist nichtbelegt. Nichtbehaupten dieBots hätten sich wegen +Kampf/Unvollständigkeit/Timer bewusstzurückgezogen. + +Fehler ist Wartungslogout-/Wiederaufnahmebehandlung: Stop verliertSchedule und +Resume behandelt gespeicherteInside+tatsächlichOutside als regulärabgeschlossenen +Besuch. KünftigpausierenstattIntentabbrechen; Resume muss dieselbeInstanzreise +sauberwiederaufnehmen stattInjury/Rest. NochNICHTbeauftragt/implementiert. +NurRead-onlyDiagnose, keineBotsbewegt. Artefakteinstance-resume-split/. + +--- + # Navigationskorrektur zum lokalen Commit freigegeben Der Nutzer hat den lokalen Commit der installierten Shared-flight-Korrektur diff --git a/src/server/game/World/ClientlessActionExecutorMgr.cpp b/src/server/game/World/ClientlessActionExecutorMgr.cpp index c1af1b9b82..506df3ae1d 100644 --- a/src/server/game/World/ClientlessActionExecutorMgr.cpp +++ b/src/server/game/World/ClientlessActionExecutorMgr.cpp @@ -1563,6 +1563,8 @@ bool ClientlessActionExecutorMgr::StartRouteStep( portalSourceApproach)) { record.Status.State = ClientlessActionExecutionState::Failed; + if (TryRouteApproachFlight(player, characterGuid, record)) + return true; record.Status.Detail = "travel start failed for route node " + step.NodeKey; record.Active = false; return false; @@ -2256,6 +2258,42 @@ bool ClientlessActionExecutorMgr::TryRescueTravel(Player* player,uint32 characte return StartRouteStep(player,characterGuid,record); } +bool ClientlessActionExecutorMgr::TryRouteApproachFlight(Player* player, uint32 characterGuid, Record& record) +{ + if (record.RouteStep >= record.Route.size()) + return false; + auto const& step = record.Route[record.RouteStep]; + // A local connector may be flown without changing the group's macro route. + // Recorded roads and transport edges retain their own execution semantics. + if (step.MapId != player->GetMapId() || step.EdgeDatabaseId || + (step.EdgeType != "start" && step.EdgeType != "local_bridge") || + player->GetExactDist(step.X, step.Y, step.Z) < 5.0f) + return false; + std::string const key = "approach-flight:" + step.NodeKey; + if (!record.FailedBridgeKeys.insert(key).second) + return false; + Position target; + target.Relocate(step.X, step.Y, step.Z, step.O); + std::string error; + // Start validates native flying permissions, collision, solid landing and + // an MMAP connection from the landing to the actual (possibly indoor) node. + if (!sClientlessFlightMgr->Start(player, target, error, true)) + { + TC_LOG_INFO("server.adventurebot.navigation", "ROUTE_APPROACH_FLIGHT_REJECT guid=%u node='%s' reason='%s'", + characterGuid, step.NodeKey.c_str(), error.c_str()); + return false; + } + record.DeferredStartActive = false; + record.EdgePath.clear(); + record.FlightActive = true; + record.Active = true; + record.Status.State = ClientlessActionExecutionState::Moving; + record.Status.Detail = "flying to existing route node after rejected ground approach"; + TC_LOG_INFO("server.adventurebot.navigation", "ROUTE_APPROACH_FLIGHT guid=%u node='%s' step=%u", + characterGuid, step.NodeKey.c_str(), uint32(record.RouteStep)); + return true; +} + bool ClientlessActionExecutorMgr::RecoverTravel( Player* player, uint32 characterGuid, @@ -2268,6 +2306,9 @@ bool ClientlessActionExecutorMgr::RecoverTravel( if (!player || !player->IsInWorld()) return false; + if (reason.rfind("route-local-", 0) == 0 && TryRouteApproachFlight(player, characterGuid, record)) + return true; + if(record.RescueActive) {record.RescueActive=false;record.RescueWalkFailed=true;} if (failedEdgeDatabaseId) diff --git a/src/server/game/World/ClientlessActionExecutorMgr.h b/src/server/game/World/ClientlessActionExecutorMgr.h index 0215c6b425..62ad01fd80 100644 --- a/src/server/game/World/ClientlessActionExecutorMgr.h +++ b/src/server/game/World/ClientlessActionExecutorMgr.h @@ -143,6 +143,7 @@ private: bool StartRecordedEdgePath(Player* player, uint32 characterGuid, Record& record); bool GetTravelGoal(Record const& record, ClientlessTravelNode& goal) const; bool TryFallbackFlight(Player* player, uint32 characterGuid, Record& record); + bool TryRouteApproachFlight(Player* player, uint32 characterGuid, Record& record); bool TryRescueTravel(Player* player, uint32 characterGuid, Record& record); void CheckGroundExit(Player* player, uint32 characterGuid, Record& record); void UpdateGroundExit(Player* player, uint32 characterGuid, Record& record); diff --git a/src/server/game/World/ClientlessFlightMgr.cpp b/src/server/game/World/ClientlessFlightMgr.cpp index b55e176c03..62146ba35a 100644 --- a/src/server/game/World/ClientlessFlightMgr.cpp +++ b/src/server/game/World/ClientlessFlightMgr.cpp @@ -780,7 +780,8 @@ bool ClientlessFlightMgr::Advance(Player * player, Record & record, bool handoff init.SetFly(); init.SetWalk(false); init.SetVelocity(player->GetSpeed(MOVE_FLIGHT)); - if (init.Launch() <= 0) + int32 const duration = init.Launch(); + if (duration <= 0) { Fail(player, record, "flight spline rejected"); return false; @@ -790,6 +791,7 @@ bool ClientlessFlightMgr::Advance(Player * player, Record & record, bool handoff record.SegmentUnderOverhang=next.UnderOverhang; record.Moving = true; record.SegmentElapsed = 0; + record.Progress.Begin(Distance(current, next.Next), uint32(duration)); ++ record.Status.Segments; record.Status.Avoidances += next.Avoiding ? 1 : 0; if (handoff) ++record.Status.Handoffs; @@ -837,6 +839,8 @@ void ClientlessFlightMgr::Update(uint32 diff) // Count actual motion, not overlapping issued spline lengths. record.Status.Travelled += Distance(PointOf(record.LastPosition), PointOf(*player)); record.LastPosition.Relocate(player); + if (record.Status.State == ClientlessFlightState::Flying && record.Moving) + record.Progress.Observe(Distance(PointOf(*player), PointOf(record.Segment)), diff); if (record.Status.State == ClientlessFlightState::Mounting) { if (player->IsMounted() && player->CanFly()) record.Status.State = ClientlessFlightState::Flying; @@ -882,9 +886,27 @@ void ClientlessFlightMgr::Update(uint32 diff) } if (record.Moving && player->IsSplineEnabled()) { - if (record.SegmentElapsed > 10000) + if (record.Progress.Stalled()) { - Fail(player, record, "flight segment made no confirmed progress"); + TC_LOG_WARN("server.adventurebot.navigation", + "FLIGHT_STALLED guid=%u idleMs=%u segmentMs=%u expectedMs=%u overdue=%u recovery=%u", + guid, record.Progress.IdleMs, record.Progress.ElapsedMs, record.Progress.ExpectedMs, + record.Progress.Overdue()?1u:0u, record.StallRecoveries); + if (record.Returning || record.StallRecoveries >= 2) + { + Fail(player, record, "flight stalled for 60 seconds; local recovery exhausted"); + continue; + } + ++record.StallRecoveries; + player->StopMoving(); + record.Moving = false; + // Keep the leader trail and destination. Re-evaluate the local + // join/corridor; never replace the macro route or return yet. + record.Join = {}; + bool const departed = record.Memory.Departed; + record.Memory = {}; + record.Memory.Departed = departed; + Advance(player, record); continue; } auto ray =[&](Point const & a, Point const & b) diff --git a/src/server/game/World/ClientlessFlightMgr.h b/src/server/game/World/ClientlessFlightMgr.h index 634816d1e3..f0b21c1b0a 100644 --- a/src/server/game/World/ClientlessFlightMgr.h +++ b/src/server/game/World/ClientlessFlightMgr.h @@ -3,6 +3,7 @@ #include "Common.h" #include "Position.h" #include "ClientlessFlightPath.h" +#include "ClientlessFlightProgress.h" #include #include #include @@ -57,6 +58,8 @@ class TC_GAME_API ClientlessFlightMgr Position Departure; uint32 TargetUpdated = 0, FormationDelay = 0; uint32 DetachedRecoveryCount = 0; + ClientlessFlightProgress::Watch Progress; + uint32 StallRecoveries = 0; bool ActionOwned = false; std::string AbortReason; std::vector < Position > Breadcrumbs; diff --git a/src/server/game/World/ClientlessFlightProgress.h b/src/server/game/World/ClientlessFlightProgress.h new file mode 100644 index 0000000000..39cf5d28b1 --- /dev/null +++ b/src/server/game/World/ClientlessFlightProgress.h @@ -0,0 +1,36 @@ +#ifndef VCORE_CLIENTLESS_FLIGHT_PROGRESS_H +#define VCORE_CLIENTLESS_FLIGHT_PROGRESS_H +#include +#include +#include +// Progress is measured against a fixed segment endpoint, not issued splines. +namespace ClientlessFlightProgress +{ + struct Watch + { + unsigned IdleMs = 0, ElapsedMs = 0, ExpectedMs = 0; + float BestDistance = 0; + void Begin(float distance, unsigned expectedMs) + { + BestDistance = distance; + ExpectedMs = expectedMs; + IdleMs = ElapsedMs = 0; + } + void Observe(float distance, unsigned diff) + { + ElapsedMs += std::min(diff, 30000u); + // Accumulate sub-tick motion; jitter or moving away is not progress. + if (std::isfinite(distance) && BestDistance - distance >= 0.5f) + { + BestDistance = distance; + IdleMs = 0; + } + else + IdleMs = std::min(60000u, IdleMs + std::min(diff, 30000u)); + } + bool Stalled() const { return IdleMs >= 60000; } + // Duration overruns are diagnostic only: a progressing flight is healthy. + bool Overdue() const { return ElapsedMs > ExpectedMs + 5000u; } + }; +} +#endif diff --git a/tests/clientless/test_flight_handoff.py b/tests/clientless/test_flight_handoff.py index 792ea386cf..042ea19841 100644 --- a/tests/clientless/test_flight_handoff.py +++ b/tests/clientless/test_flight_handoff.py @@ -10,6 +10,7 @@ b = source.index('bool ClientlessFlightMgr::Command(', a) update = source[a:b] harness = r''' #include "ClientlessFlightPath.h" +#include "ClientlessFlightProgress.h" #include #include #include @@ -44,6 +45,7 @@ auto sWorld=&world; enum class ClientlessFlightState {Mounting,Flying,Arrived,Failed}; struct Record { struct {bool FollowingPlayer=false;ClientlessFlightState State=ClientlessFlightState::Flying;uint32 MapId=0;Position Landing{800,0,0},Target{800,0,0};float Travelled=0;uint32 Segments=0,Avoidances=0;std::string Detail;} Status; + ClientlessFlightProgress::Watch Progress;uint32 StallRecoveries=0;SharedJoin Join; Position LastPosition,Start,Segment{25,0,60};Settings Settings; std::shared_ptr Following;uint32 FormationDelay=0,TargetUpdated=0,Elapsed=0,SegmentElapsed=0,CheckTimer=0; bool Moving=true,Returning=false,ApproachingDeparture=false,ActionOwned=false,SegmentUnderOverhang=false;uint32 DetachedRecoveryCount=0;Position Departure;Memory Memory;std::shared_ptr Published=std::make_shared();std::vector Breadcrumbs; @@ -70,7 +72,16 @@ public: '''.replace('Settings Settings;', 'ClientlessFlightPath::Settings Settings;').replace('Memory Memory;', 'ClientlessFlightPath::Memory Memory;') harness += update + r''' int main(){ + // Small real movements accumulate; oscillating distance does not reset best progress. + ClientlessFlightProgress::Watch watch;watch.Begin(100,120000); + for(int i=0;i<59;++i)watch.Observe(100+(i%2?0.1f:-0.1f),1000); + assert(!watch.Stalled());watch.Observe(100,1000);assert(watch.Stalled()); + watch.Observe(99.4f,200);assert(!watch.Stalled()&&watch.IdleMs==0); + // Vertical movement is measured with the same 3D distance as horizontal flight. + watch.Begin(Distance({0,0,0},{0,0,200}),20000); + watch.Observe(Distance({0,0,1},{0,0,200}),1000);assert(watch.IdleMs==0); ClientlessFlightMgr mgr;mgr._records[26]={}; + mgr._records[26].Progress.Begin(25,2000); // Begin replacement while the existing spline is still active: no stop. mgr.Update(50);assert(mgr.handoffs==1&&mgr.advances==1&&player.stops==0); // The 200ms collision cadence must not launch a replacement each tick. @@ -90,6 +101,18 @@ int main(){ assert(player.stops==1&&mgr.advances==2&&mgr.handoffs==1); // A completed spline continues via the normal non-handoff path. clear=true;r.CheckTimer=0;mgr.Update(50);assert(mgr.advances==3); + // Long flights keep running when actual progress continues, even overdue. + r.Returning=false;r.Moving=true;player.running=true;player.spline.remaining=3000; + r.Progress.Begin(1000,20000);r.Segment={1007,0,60}; + for(int i=0;i<90;++i){player.x+=1;r.CheckTimer=0;mgr.Update(1000);} + assert(mgr.failures==0&&r.Progress.Overdue()&&r.Progress.IdleMs==0); + // A genuinely stuck spline survives 59s, then replans locally at 60s. + r.Progress.Begin(Distance(PointOf(player),PointOf(r.Segment)),20000);r.StallRecoveries=0; + int before=mgr.advances; + for(int i=0;i<59;++i){r.CheckTimer=0;mgr.Update(1000);} + assert(mgr.advances==before&&mgr.failures==0); + r.CheckTimer=0;mgr.Update(1000); + assert(mgr.advances==before+1&&r.StallRecoveries==1&&mgr.failures==0); player.fly=false;mgr.Update(50);assert(mgr.failures==1); // A ground departure approach must wait for actual arrival before mounting. r.Status.State=ClientlessFlightState::Mounting;r.ApproachingDeparture=true; @@ -106,6 +129,17 @@ int main(){ r.ApproachingDeparture=true;r.Status.State=ClientlessFlightState::Mounting; // A stale Arrived flag cannot permit mounting away from the selected spot. r.Departure.x=player.x+10;mgr.Update(50);assert(mgr.failures==2&&mgr.begins==1); + // Repeated genuine stalls exhaust local recovery, without dropping the leader trail. + ClientlessFlightMgr stuck;auto& sr=stuck._records[77]; + sr.Following=std::make_shared(1);auto leaderTrail=sr.Following; + sr.Segment={player.x+100,0,60};player.fly=true;player.spline.remaining=3000; + for(int attempt=0;attempt<3;++attempt){ + sr.Moving=true;player.running=true;sr.Progress.Begin(100,4000); + for(int i=0;i<60;++i){sr.CheckTimer=0;stuck.Update(1000);} + assert(sr.Following==leaderTrail); + assert(stuck.failures==(attempt==2?1:0)); + } + assert(stuck.advances==2&&sr.StallRecoveries==2); } ''' with tempfile.TemporaryDirectory(prefix='vcore-flight-handoff-') as folder: @@ -114,4 +148,4 @@ with tempfile.TemporaryDirectory(prefix='vcore-flight-handoff-') as folder: cpp.write_text(harness) subprocess.run(['c++', '-std=c++17', '-I', str(root/'src/server/game/World'), str(cpp), '-o', str(binary)], check=True) subprocess.run([str(binary)], check=True) -print('Flight handoff before spline end, terminal landing, return corners, collision stop and actual distance passed.') +print('Flight handoff, collision, 60s measured stall, jitter, vertical progress, long-flight overrun and local recovery passed.') diff --git a/tests/clientless/test_route_approach_flight.py b/tests/clientless/test_route_approach_flight.py new file mode 100644 index 0000000000..be81cdd176 --- /dev/null +++ b/tests/clientless/test_route_approach_flight.py @@ -0,0 +1,55 @@ +"""Run the production local-flight adapter; preserve macro route and native checks.""" +from pathlib import Path +import subprocess +import tempfile + +root = Path(__file__).resolve().parents[2] +src = (root/'src/server/game/World/ClientlessActionExecutorMgr.cpp').read_text() +a = src.index('bool ClientlessActionExecutorMgr::TryRouteApproachFlight(') +b = src.index('bool ClientlessActionExecutorMgr::RecoverTravel(', a) +method = src[a:b] +harness = r''' +#include +#include +#include +#include +#include +using uint32=unsigned; +struct Position {float x=0,y=0,z=0;void Relocate(float a,float b,float c,float){x=a;y=b;z=c;}}; +struct Player {unsigned GetMapId(){return 0;}float GetExactDist(float x,float y,float z){return std::sqrt(x*x+y*y+z*z);}}; +struct Step {std::string NodeKey="portal",EdgeType="start";unsigned MapId=0,EdgeDatabaseId=0;float X=100,Y=0,Z=10,O=0;}; +enum class ClientlessActionExecutionState{Moving,Failed}; +struct Record {std::vector Route{Step{},Step{}};unsigned RouteStep=0;std::set FailedBridgeKeys;bool DeferredStartActive=true,FlightActive=false,Active=false;std::vector EdgePath{1,2};struct {ClientlessActionExecutionState State=ClientlessActionExecutionState::Failed;std::string Detail;}Status;}; +struct Flight {bool available=true;unsigned calls=0;Position last;bool Start(Player*,Position const&p,std::string&error,bool owned){assert(owned);++calls;last=p;if(!available)error="no safe landing";return available;}}flight; +auto sClientlessFlightMgr=&flight; +#define TC_LOG_INFO(...) ((void)0) +struct ClientlessActionExecutorMgr {bool TryRouteApproachFlight(Player*,uint32,Record&);}; +''' +harness += method + r''' +int main(){ + Player p;Record r;ClientlessActionExecutorMgr mgr; + // Fly to the rejected connector, preserving the following portal edge. + assert(mgr.TryRouteApproachFlight(&p,163,r)); + assert(r.FlightActive&&r.Active&&!r.DeferredStartActive&&r.EdgePath.empty()); + assert(r.RouteStep==0&&r.Route.size()==2&&r.Route[0].EdgeType=="start"); + assert(flight.last.x==100&&flight.last.z==10&&flight.calls==1); + assert(!mgr.TryRouteApproachFlight(&p,163,r)&&flight.calls==1); + // No flight permission/solid landing/ground connection means no fallback. + r=Record{};flight.available=false; + assert(!mgr.TryRouteApproachFlight(&p,163,r)&&!r.FlightActive&&r.Route.size()==2); + r=Record{};flight.available=true;r.Route[0].MapId=1; + assert(!mgr.TryRouteApproachFlight(&p,163,r)); + r=Record{};r.Route[0].EdgeDatabaseId=42; + assert(!mgr.TryRouteApproachFlight(&p,163,r)); + for(auto type:{"portal_use","taxi","free_flight","ship"}){ + r=Record{};r.Route[0].EdgeType=type;assert(!mgr.TryRouteApproachFlight(&p,163,r)); + } + r=Record{};r.Route[0].EdgeType="local_bridge";assert(mgr.TryRouteApproachFlight(&p,163,r)); +} +''' +with tempfile.TemporaryDirectory(prefix='vcore-route-flight-') as folder: + cpp=Path(folder)/'test.cpp'; exe=Path(folder)/'test' + cpp.write_text(harness) + subprocess.run(['c++','-std=c++17',str(cpp),'-o',str(exe)],check=True) + subprocess.run([str(exe)],check=True) +print('Route approach flight: original route retained, one attempt, native refusal and transport boundaries passed.')