Recover stalled flights locally and fly rejected route approaches
This commit is contained in:
1 parent
f8ee1d0ff6
commit
ba2a4e629c
8 files changed
+573
-4
No files matched your search
@@ -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
|
# Navigationskorrektur zum lokalen Commit freigegeben
|
||||||
|
|
||||||
Der Nutzer hat den lokalen Commit der installierten Shared-flight-Korrektur
|
Der Nutzer hat den lokalen Commit der installierten Shared-flight-Korrektur
|
||||||
|
|||||||
@@ -1563,6 +1563,8 @@ bool ClientlessActionExecutorMgr::StartRouteStep(
|
|||||||
portalSourceApproach))
|
portalSourceApproach))
|
||||||
{
|
{
|
||||||
record.Status.State = ClientlessActionExecutionState::Failed;
|
record.Status.State = ClientlessActionExecutionState::Failed;
|
||||||
|
if (TryRouteApproachFlight(player, characterGuid, record))
|
||||||
|
return true;
|
||||||
record.Status.Detail = "travel start failed for route node " + step.NodeKey;
|
record.Status.Detail = "travel start failed for route node " + step.NodeKey;
|
||||||
record.Active = false;
|
record.Active = false;
|
||||||
return false;
|
return false;
|
||||||
@@ -2256,6 +2258,42 @@ bool ClientlessActionExecutorMgr::TryRescueTravel(Player* player,uint32 characte
|
|||||||
return StartRouteStep(player,characterGuid,record);
|
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(
|
bool ClientlessActionExecutorMgr::RecoverTravel(
|
||||||
Player* player,
|
Player* player,
|
||||||
uint32 characterGuid,
|
uint32 characterGuid,
|
||||||
@@ -2268,6 +2306,9 @@ bool ClientlessActionExecutorMgr::RecoverTravel(
|
|||||||
if (!player || !player->IsInWorld())
|
if (!player || !player->IsInWorld())
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
|
if (reason.rfind("route-local-", 0) == 0 && TryRouteApproachFlight(player, characterGuid, record))
|
||||||
|
return true;
|
||||||
|
|
||||||
if(record.RescueActive)
|
if(record.RescueActive)
|
||||||
{record.RescueActive=false;record.RescueWalkFailed=true;}
|
{record.RescueActive=false;record.RescueWalkFailed=true;}
|
||||||
if (failedEdgeDatabaseId)
|
if (failedEdgeDatabaseId)
|
||||||
|
|||||||
@@ -143,6 +143,7 @@ private:
|
|||||||
bool StartRecordedEdgePath(Player* player, uint32 characterGuid, Record& record);
|
bool StartRecordedEdgePath(Player* player, uint32 characterGuid, Record& record);
|
||||||
bool GetTravelGoal(Record const& record, ClientlessTravelNode& goal) const;
|
bool GetTravelGoal(Record const& record, ClientlessTravelNode& goal) const;
|
||||||
bool TryFallbackFlight(Player* player, uint32 characterGuid, Record& record);
|
bool TryFallbackFlight(Player* player, uint32 characterGuid, Record& record);
|
||||||
|
bool TryRouteApproachFlight(Player* player, uint32 characterGuid, Record& record);
|
||||||
bool TryRescueTravel(Player* player, uint32 characterGuid, Record& record);
|
bool TryRescueTravel(Player* player, uint32 characterGuid, Record& record);
|
||||||
void CheckGroundExit(Player* player, uint32 characterGuid, Record& record);
|
void CheckGroundExit(Player* player, uint32 characterGuid, Record& record);
|
||||||
void UpdateGroundExit(Player* player, uint32 characterGuid, Record& record);
|
void UpdateGroundExit(Player* player, uint32 characterGuid, Record& record);
|
||||||
|
|||||||
@@ -780,7 +780,8 @@ bool ClientlessFlightMgr::Advance(Player * player, Record & record, bool handoff
|
|||||||
init.SetFly();
|
init.SetFly();
|
||||||
init.SetWalk(false);
|
init.SetWalk(false);
|
||||||
init.SetVelocity(player->GetSpeed(MOVE_FLIGHT));
|
init.SetVelocity(player->GetSpeed(MOVE_FLIGHT));
|
||||||
if (init.Launch() <= 0)
|
int32 const duration = init.Launch();
|
||||||
|
if (duration <= 0)
|
||||||
{
|
{
|
||||||
Fail(player, record, "flight spline rejected");
|
Fail(player, record, "flight spline rejected");
|
||||||
return false;
|
return false;
|
||||||
@@ -790,6 +791,7 @@ bool ClientlessFlightMgr::Advance(Player * player, Record & record, bool handoff
|
|||||||
record.SegmentUnderOverhang=next.UnderOverhang;
|
record.SegmentUnderOverhang=next.UnderOverhang;
|
||||||
record.Moving = true;
|
record.Moving = true;
|
||||||
record.SegmentElapsed = 0;
|
record.SegmentElapsed = 0;
|
||||||
|
record.Progress.Begin(Distance(current, next.Next), uint32(duration));
|
||||||
++ record.Status.Segments;
|
++ record.Status.Segments;
|
||||||
record.Status.Avoidances += next.Avoiding ? 1 : 0;
|
record.Status.Avoidances += next.Avoiding ? 1 : 0;
|
||||||
if (handoff) ++record.Status.Handoffs;
|
if (handoff) ++record.Status.Handoffs;
|
||||||
@@ -837,6 +839,8 @@ void ClientlessFlightMgr::Update(uint32 diff)
|
|||||||
// Count actual motion, not overlapping issued spline lengths.
|
// Count actual motion, not overlapping issued spline lengths.
|
||||||
record.Status.Travelled += Distance(PointOf(record.LastPosition), PointOf(*player));
|
record.Status.Travelled += Distance(PointOf(record.LastPosition), PointOf(*player));
|
||||||
record.LastPosition.Relocate(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 (record.Status.State == ClientlessFlightState::Mounting)
|
||||||
{
|
{
|
||||||
if (player->IsMounted() && player->CanFly()) record.Status.State = ClientlessFlightState::Flying;
|
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.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;
|
continue;
|
||||||
}
|
}
|
||||||
auto ray =[&](Point const & a, Point const & b)
|
auto ray =[&](Point const & a, Point const & b)
|
||||||
|
|||||||
@@ -3,6 +3,7 @@
|
|||||||
#include "Common.h"
|
#include "Common.h"
|
||||||
#include "Position.h"
|
#include "Position.h"
|
||||||
#include "ClientlessFlightPath.h"
|
#include "ClientlessFlightPath.h"
|
||||||
|
#include "ClientlessFlightProgress.h"
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <unordered_map>
|
#include <unordered_map>
|
||||||
@@ -57,6 +58,8 @@ class TC_GAME_API ClientlessFlightMgr
|
|||||||
Position Departure;
|
Position Departure;
|
||||||
uint32 TargetUpdated = 0, FormationDelay = 0;
|
uint32 TargetUpdated = 0, FormationDelay = 0;
|
||||||
uint32 DetachedRecoveryCount = 0;
|
uint32 DetachedRecoveryCount = 0;
|
||||||
|
ClientlessFlightProgress::Watch Progress;
|
||||||
|
uint32 StallRecoveries = 0;
|
||||||
bool ActionOwned = false;
|
bool ActionOwned = false;
|
||||||
std::string AbortReason;
|
std::string AbortReason;
|
||||||
std::vector < Position > Breadcrumbs;
|
std::vector < Position > Breadcrumbs;
|
||||||
|
|||||||
@@ -0,0 +1,36 @@
|
|||||||
|
#ifndef VCORE_CLIENTLESS_FLIGHT_PROGRESS_H
|
||||||
|
#define VCORE_CLIENTLESS_FLIGHT_PROGRESS_H
|
||||||
|
#include <algorithm>
|
||||||
|
#include <cmath>
|
||||||
|
#include <cstdint>
|
||||||
|
// 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
|
||||||
@@ -10,6 +10,7 @@ b = source.index('bool ClientlessFlightMgr::Command(', a)
|
|||||||
update = source[a:b]
|
update = source[a:b]
|
||||||
harness = r'''
|
harness = r'''
|
||||||
#include "ClientlessFlightPath.h"
|
#include "ClientlessFlightPath.h"
|
||||||
|
#include "ClientlessFlightProgress.h"
|
||||||
#include <cassert>
|
#include <cassert>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
@@ -44,6 +45,7 @@ auto sWorld=&world;
|
|||||||
enum class ClientlessFlightState {Mounting,Flying,Arrived,Failed};
|
enum class ClientlessFlightState {Mounting,Flying,Arrived,Failed};
|
||||||
struct Record {
|
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;
|
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;
|
Position LastPosition,Start,Segment{25,0,60};Settings Settings;
|
||||||
std::shared_ptr<int> Following;uint32 FormationDelay=0,TargetUpdated=0,Elapsed=0,SegmentElapsed=0,CheckTimer=0;
|
std::shared_ptr<int> 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<SharedTrail> Published=std::make_shared<SharedTrail>();std::vector<Position> Breadcrumbs;
|
bool Moving=true,Returning=false,ApproachingDeparture=false,ActionOwned=false,SegmentUnderOverhang=false;uint32 DetachedRecoveryCount=0;Position Departure;Memory Memory;std::shared_ptr<SharedTrail> Published=std::make_shared<SharedTrail>();std::vector<Position> Breadcrumbs;
|
||||||
@@ -70,7 +72,16 @@ public:
|
|||||||
'''.replace('Settings Settings;', 'ClientlessFlightPath::Settings Settings;').replace('Memory Memory;', 'ClientlessFlightPath::Memory Memory;')
|
'''.replace('Settings Settings;', 'ClientlessFlightPath::Settings Settings;').replace('Memory Memory;', 'ClientlessFlightPath::Memory Memory;')
|
||||||
harness += update + r'''
|
harness += update + r'''
|
||||||
int main(){
|
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]={};
|
ClientlessFlightMgr mgr;mgr._records[26]={};
|
||||||
|
mgr._records[26].Progress.Begin(25,2000);
|
||||||
// Begin replacement while the existing spline is still active: no stop.
|
// Begin replacement while the existing spline is still active: no stop.
|
||||||
mgr.Update(50);assert(mgr.handoffs==1&&mgr.advances==1&&player.stops==0);
|
mgr.Update(50);assert(mgr.handoffs==1&&mgr.advances==1&&player.stops==0);
|
||||||
// The 200ms collision cadence must not launch a replacement each tick.
|
// 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);
|
assert(player.stops==1&&mgr.advances==2&&mgr.handoffs==1);
|
||||||
// A completed spline continues via the normal non-handoff path.
|
// A completed spline continues via the normal non-handoff path.
|
||||||
clear=true;r.CheckTimer=0;mgr.Update(50);assert(mgr.advances==3);
|
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);
|
player.fly=false;mgr.Update(50);assert(mgr.failures==1);
|
||||||
// A ground departure approach must wait for actual arrival before mounting.
|
// A ground departure approach must wait for actual arrival before mounting.
|
||||||
r.Status.State=ClientlessFlightState::Mounting;r.ApproachingDeparture=true;
|
r.Status.State=ClientlessFlightState::Mounting;r.ApproachingDeparture=true;
|
||||||
@@ -106,6 +129,17 @@ int main(){
|
|||||||
r.ApproachingDeparture=true;r.Status.State=ClientlessFlightState::Mounting;
|
r.ApproachingDeparture=true;r.Status.State=ClientlessFlightState::Mounting;
|
||||||
// A stale Arrived flag cannot permit mounting away from the selected spot.
|
// 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);
|
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<int>(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:
|
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)
|
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(['c++', '-std=c++17', '-I', str(root/'src/server/game/World'), str(cpp), '-o', str(binary)], check=True)
|
||||||
subprocess.run([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.')
|
||||||
@@ -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 <cassert>
|
||||||
|
#include <cmath>
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
#include <set>
|
||||||
|
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<Step> Route{Step{},Step{}};unsigned RouteStep=0;std::set<std::string> FailedBridgeKeys;bool DeferredStartActive=true,FlightActive=false,Active=false;std::vector<int> 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.')
|
||||||
Reference in new issue
Block a user