Publish validated group flight landings and extend moving trail handoffs
This commit is contained in:
1 parent
3056063f12
commit
ae0b7513d9
9 files changed
+301
-9
No files matched your search
@@ -1,3 +1,75 @@
|
||||
# Aktuell 02.11.2026, 10:00: Shared-Flight-Landepunkt/Flugpausen korrigiert
|
||||
|
||||
Nutzer meldete Nachfolger kehren über Plattform um und fliegen stockend.
|
||||
Rootcause belegt:159/161/166 abonnieren09:34:55 Leaderspur bevor Winienwi163
|
||||
seine Abflugvorbereitung/Landesuche beendet. Begin kopierte nochleere Landing
|
||||
Position. Spur später korrekt bis837.77 gefolgt, letzterSinkflug falsch geprüft,
|
||||
09:37:49..52 Rückflug. Leader landete09:37:33 und tratInstanz2 bei.
|
||||
Fix: SharedTrail publiziert Landing+LandingReady, Advance wartet/bindet den
|
||||
bestätigtenLandepunkt statt früher Kopie. Geprüfter Lookahead fürFollower und
|
||||
1500msSplinehandoff; keine ungeprüften Eckabkürzungen. AchtTests PASS,
|
||||
Build-j10/Install PASS. Checkpoint2026-11-02-shared-flight/README.md.
|
||||
Loader0 brauchte explizitenPopulationReload (zunächst nachBuildstart korrigiert).
|
||||
SafeLogout09:49:23;14Restbots nach5Min dokumentiert, saubererShutdown,
|
||||
ProzessendevorInstall. Kein Einzellogout erzwungen. NeustartPID258518 ready,
|
||||
Ports8085/8087. Loader1 und PopulationReload restauriert.14Restbots Login,
|
||||
sechs74/77/91/93/102/121 nachungültigemSchwebezustand nativgeborgen; übrige
|
||||
nativeTaxi/Flugbewegungen ungestört. Rohzustände/Recoverybelege aufbewahrt.
|
||||
|
||||
FürLive-Nachprüfung alle10 offlineRose, nurderenalteInstanceVisit-/Handoff-
|
||||
Zustände gesichertundentfernt.09Formation/10Raid6354_d0 wiederaktiviert.
|
||||
Gruppe1 Leader163 undalle10 bestätigt. TempGM-Teleportgelöscht.
|
||||
Beobachter vcore-adventure-raid-observer-20261102-1000.service weiteraktiv
|
||||
bis11MEZ. Live-Retry10Uhr bleibt schoninSW anbekanntemGroundSafety-Fehler hängen:
|
||||
MMAP taxi_2->stormwind_tradecenter komplett189.55yd, AusführungPathRejected,
|
||||
wiederholteAbflugannäherungen unterbrochen.10:02 nochkeinneuerTurmanflug.
|
||||
Keine erfolgreicheFollowerlandung/visuellflüssigeGesamtflügebehaupten.
|
||||
Alle110Bots/Loader1 wiederaktiv, Gruppevollständig. Nächsteunabhängige
|
||||
Baustelle ist lokalerGroundSafety/Recovery. Beobachterbis11weiteraktiv.
|
||||
Flugfix separat lokal versionieren; kein Push. FrühereCommits2778cf9bdd/3056063f12.
|
||||
AnderebenannteBaustellen und abgelehnteGruppennachbesetzung unverändert.
|
||||
|
||||
---
|
||||
|
||||
# Neuer Zehnertest am 02.11.2026, Gruppenbildung09 / Bastion10:00
|
||||
|
||||
Nutzer wollte Wiederholung nur der bekannten10 Bots. Alle159..167/176 wurden
|
||||
nativ zur GoldenenRose teleportiert, alte Gruppen verlassen, Aruendas162 für
|
||||
Teststart wiederbelebt. Slots09 groupformation the_gilded_rose,10 raid
|
||||
instance_visit_at_6354_d0 für alle10 per nativer editplan gesetzt und20DB-Zeilen
|
||||
validiert. Temporärer GM-Teleport nach Benutzung gelöscht/Cache reloaded.
|
||||
Vollständige native Gruppe1 nachgewiesen: Leader Winienwi163, Mitglieder
|
||||
159,160,161,162,163,164,165,166,167,176;2Tank/2Heal/6Damage, Corric dabei.
|
||||
Kein globaler erzwungener Managerlauf, keine weiteren Tagespläne verändert.
|
||||
Bestehende Frühaufbruchlogik unverändert: volle Gruppe kann fünf Minuten nach
|
||||
Vollständigkeit schon vor10 abziehen. Nicht als Fehler oder geänderten10Slot
|
||||
missverstehen. Vorzustände zeigen alte Fehlflüge; nachTeleport allebodennahSW,
|
||||
Gruppenbildungsintent aktiv. Beobachterservice:
|
||||
`vcore-adventure-raid-observer-20261102-1000.service`, read-only bis11MEZ,
|
||||
Snapshot alle2Minuten und inkrementelle Logs ohne den alten Tail-Replayfehler.
|
||||
Daten/Skript unter checkpoints/2026-11-02-1000-raid-test/.
|
||||
Kein Build/Serverrestart für Einrichtung; Loader normal aktiv.
|
||||
|
||||
---
|
||||
|
||||
# Abschließende Testauswertung ohne Bastion-Cohort
|
||||
|
||||
Nutzer verlangte endgültige Auswertung unter Ausschluss der bekannten10 Bots.
|
||||
Erstellt: doc/clientless/2026-11-02-instance-test-final.md. Reproduzierbare
|
||||
Offline-Analyse evaluate_without_raid.py und final-without-raid.json im06Uhr-
|
||||
Testcheckpoint.82 Bots (38A/44H),67Dungeon/15Raid,38 vor07 am fixen Treffpunkt
|
||||
belegt (18A/20H, gleichwertige Inn-Aliase anerkannt; exakt gleiche Schlüssel33), kein Instanzeintritt. Schluss62 gruppiert in22 Gruppen,
|
||||
20solo, nur eine vollständige Fünfergruppe. Rollen7Tank/11Heal/64Damage;
|
||||
Population erlaubt keine vollständige Aufteilung aller82 in strikte Gruppen.
|
||||
35 Bots RecoveryFailed;60 WaitingGroup,16 nochGruppenbildung und1Leisure.
|
||||
Fünfergruppe26/41/43/45/72 ZielVersunkenerTempel scheiterte anReise; drei
|
||||
ab07:56 im Timeout-Rest, keinabsolvierterDungeon. Logsammlerduplikate:
|
||||
Rohereignishäufigkeiten nicht als unabhängige Versuche zählen. Bericht erläutert
|
||||
Zeitlücke bis06:13, künstlichenManagerlauf und dass Flugfix nach08 außerhalb
|
||||
Test liegt. Keine weiteren Source-/Serveränderungen für diese Auswertung.
|
||||
|
||||
---
|
||||
|
||||
# Aktuell 02.11.2026, 08:33 MEZ: erhöhter Landeanflug installiert und live bestätigt
|
||||
|
||||
Kostenmodell zuvor lokal committed: 2778cf9bdd, kein Push. Neuer Auftrag:
|
||||
|
||||
@@ -0,0 +1,42 @@
|
||||
"""Read-only observation of the ten-bot retry until 11:00 CET."""
|
||||
from pathlib import Path
|
||||
import sys,os,re,json,time,datetime
|
||||
sys.path.insert(0,str(Path(__file__).resolve().parents[1]/'2026-11-02-0600-group-test'))
|
||||
import test_driver as d
|
||||
BASE=Path(__file__).resolve().parent
|
||||
END=datetime.datetime.fromisoformat('2026-11-02T11:00:00+01:00')
|
||||
IDS=set(d.RAID)
|
||||
def append(name,v):
|
||||
with (BASE/name).open('a') as f:f.write(json.dumps(v,ensure_ascii=False)+'\n')
|
||||
class Tail:
|
||||
def __init__(self,path):
|
||||
self.path=path;self.f=path.open('rb');self.inode=os.fstat(self.f.fileno()).st_ino;self.f.seek(0,2);self.pending=b''
|
||||
def poll(self):
|
||||
st=self.path.stat()
|
||||
if st.st_ino==self.inode and st.st_size<self.f.tell():self.f.seek(0);self.pending=b''
|
||||
data=self.f.read()
|
||||
if st.st_ino!=self.inode:
|
||||
self.f.close();self.f=self.path.open('rb');self.inode=st.st_ino;data+=self.f.read()
|
||||
parts=(self.pending+data).split(b'\n');self.pending=parts.pop()
|
||||
for line in parts:
|
||||
s=line.decode(errors='replace')
|
||||
if any(int(g) in IDS for g in re.findall(r'\b(?:guid|leader|member|target)=(\d+)\b',s)):
|
||||
append('events.jsonl',{'time':d.now().isoformat(),'source':self.path.name,'line':s})
|
||||
def snapshot():
|
||||
for g in d.RAID:
|
||||
for c in [f'clientless adventure status {g}',f'clientless actioninfo {g}',f'clientless flight status {g}',f'clientless interaction {g}']:d.command(c)
|
||||
time.sleep(.6)
|
||||
stamp=d.now();file='snapshot-'+stamp.strftime('%H%M%S')+'.log';(BASE/file).write_text(d.console(1000))
|
||||
groups=d.query('Character','SELECT g.guid,g.leaderGuid,m.memberGuid,c.name FROM groups g JOIN group_member m ON m.guid=g.guid JOIN characters c ON c.guid=m.memberGuid WHERE g.guid IN(SELECT guid FROM group_member WHERE memberGuid IN(159,160,161,162,163,164,165,166,167,176)) ORDER BY g.guid,m.memberGuid')
|
||||
append('snapshots.jsonl',{'time':stamp.isoformat(),'native_file':file,'groups':groups})
|
||||
if __name__=='__main__':
|
||||
logs=Path('/home/wow_admin/wow_cata_server/server/debug/bin');tails=[Tail(logs/'Server.log'),Tail(logs/'adventurebot-navigation.log')];next_snapshot=0
|
||||
append('lifecycle.jsonl',{'time':d.now().isoformat(),'event':'start','end':END.isoformat()})
|
||||
while d.now()<END:
|
||||
try:
|
||||
for t in tails:t.poll()
|
||||
if time.monotonic()>=next_snapshot:snapshot();next_snapshot=time.monotonic()+120
|
||||
except Exception as e:append('errors.jsonl',{'time':d.now().isoformat(),'error':str(e)})
|
||||
time.sleep(2)
|
||||
for t in tails:t.poll()
|
||||
append('lifecycle.jsonl',{'time':d.now().isoformat(),'event':'finished'})
|
||||
@@ -0,0 +1,75 @@
|
||||
# Shared-flight landing and continuity fix — 02.11.2026
|
||||
|
||||
User observed followers turning away above the Bastion platform and repeated
|
||||
pauses in flight. No group-refill policy changes are part of this fix.
|
||||
|
||||
## Root cause
|
||||
|
||||
09:34:53: leader Winienwi163 walks8yd to open takeoff column. His prepared
|
||||
Flight Record already has Target but does not have a validated Landing yet.
|
||||
09:34:55: followers166/159/161 attach to the leader trail BEFORE the leader's
|
||||
landing-search result is published. Old Begin copied Status.Landing immediately,
|
||||
which at this phase is the default zero Position. The shared pointer to the
|
||||
trail survives preparation, but the copied landing never updated.
|
||||
09:37:33: leader lands correctly at827.765625 and subsequently enters instance2.
|
||||
09:37:49..52: followers reach the hover point837.765625. The zero-goal corridor
|
||||
retains the8yd safety floor margin at the true touchdown and rejects descent.
|
||||
Fail() therefore starts a checked breadcrumb return; this is not a successful
|
||||
arrival or a deliberate new destination. Evidence root-cause-evidence.jsonl.
|
||||
|
||||
## Implementation
|
||||
|
||||
SharedTrail now publishes Landing and LandingReady. Begin does not copy the
|
||||
leader's potentially uninitialised Record Position. Advance binds the validated
|
||||
landing from the shared object, waiting for publication if preparation continues.
|
||||
Followers retain a shared object even after the leader leaves for the instance.
|
||||
Native attachment logs now include landingReady.
|
||||
|
||||
For continuity, handoff begins while up to1500ms remain rather than600ms.
|
||||
Followers may advance to the next published endpoint before stopping at the old
|
||||
endpoint, but only if the complete shortcut corridor is clear. Escape-leg corners
|
||||
are retained. Aborted trails still fail closed. Explicit shared-reject diagnostics
|
||||
include index, point count, abort flag and effective landing.
|
||||
|
||||
Eight targeted tests PASS. New regression reproduces the zero-goal terminal
|
||||
failure, exercises the real Advance landing-binding prefix with delayed
|
||||
publication, tests terminal descent, early running handoff, blocked corners,
|
||||
escape legs and aborted trails. Existing ordinary/elevated flight, native handoff,
|
||||
group follow, policy, landing search and terrain tests retained.
|
||||
|
||||
## Build and recovery
|
||||
|
||||
Build-j10 and install PASS. Safe logout requested09:49:23.822668; configuration
|
||||
loader0 initially did not stop the cached loader. Explicit population reload then
|
||||
confirmed enabled0 (loader-paused.log). This pause correction happened after the
|
||||
build had begun and is documented rather than treating the earlier pause as
|
||||
verified. Before installation, the full five-minute drain elapsed. Remaining14
|
||||
bots, positions, native available flags and diagnostics were preserved in
|
||||
final-remaining.json / final-remaining-native.log / last-available-remaining-flags.json.
|
||||
Five-minute exception allowed clean server shutdown; no individual forced logout
|
||||
or flight interruption was used to install. Process/tmux absence verified before
|
||||
install. Fresh startup ready PID258518, ports8085/8087 verified.
|
||||
|
||||
Original ten-bot test evidence preserved. Only ten test bots' previous handoff and
|
||||
instance-visit records were backed up and cleared to start a fresh run; all ten
|
||||
natively teleported offline to the Golden Rose, reloaded, slots09formation and
|
||||
10raid6354_d0 applied. Complete group1, leader163, all ten confirmed. Normal loader
|
||||
restored (including explicit population reload); all14 recovery bots requested
|
||||
login. Moving native taxi/flight actors left intact. Six unsupported stationary
|
||||
actors74/77/91/93/102/121 received native capital recovery after login checks;
|
||||
post-restart-native.log and recovery-capitals.log preserve findings/results.
|
||||
Temporary GM teleport destination removed and cache reloaded. No production use.
|
||||
Live result of the new10:00 run is recorded separately below when available.
|
||||
|
||||
## Live follow-up limitation
|
||||
|
||||
10:00 retry remains a complete ten-bot group led by163 with unchanged raid goal.
|
||||
The initial route now stalls on the separately known ground safety issue: native
|
||||
MMAP taxi_2 -> stormwind_tradecenter is complete (path189.55yd) but execution
|
||||
returns PathRejected. Repeated departure approaches are interrupted. The group
|
||||
has not reached the tower again as of10:02, so this run does NOT prove the final
|
||||
follower descent or visually smooth flight. New landingReady=1 attachment and
|
||||
short native flights were logged, but they are not a substitute for that proof.
|
||||
All110 bots online, loader enabled1, original group still intact. Existing
|
||||
observer remains active until11:00; no automatic chat report is promised.
|
||||
The next independent blocker is the local ground safety/recovery behavior.
|
||||
@@ -499,17 +499,22 @@ bool ClientlessFlightMgr::Begin(Player * player, Position const & target, std::s
|
||||
record.Status.State = ClientlessFlightState::Mounting;
|
||||
record.Status.MountSpell = existingMount ? existingMount : spell;
|
||||
record.Status.Detail = "waiting for native flying mount";
|
||||
record.Published->Landing=PointOf(record.Status.Landing);
|
||||
record.Published->LandingReady=true;
|
||||
if(!following)if(auto group=player->GetGroup())if(group->GetLeaderGUID()!=player->GetGUID())
|
||||
{
|
||||
auto source=_records.find(group->GetLeaderGUID().GetCounter());
|
||||
if(source!=_records.end() && source->second.Status.MapId==player->GetMapId() &&
|
||||
source->second.Status.Target.GetExactDist(target)<1 && !source->second.Published->Aborted)
|
||||
{
|
||||
record.Following=source->second.Published;record.Status.Landing=source->second.Status.Landing;
|
||||
// The leader may still be walking to takeoff and have no landing
|
||||
// yet. Its published trail survives that preparation. Bind the
|
||||
// verified landing lazily rather than copying an unset Position.
|
||||
record.Following=source->second.Published;
|
||||
std::vector<ObjectGuid> members;for(auto const& member:group->GetMemberSlots())if(member.guid!=group->GetLeaderGUID())members.push_back(member.guid);
|
||||
std::sort(members.begin(),members.end());auto slot=std::find(members.begin(),members.end(),player->GetGUID())-members.begin();
|
||||
record.FormationDelay=3500+uint32(slot)*600; // native shared corridor, staggered single-file departure
|
||||
TC_LOG_INFO("server.adventurebot.navigation","FLIGHT_SHARED_TRAIL guid=%u leader=%u",player->GetGUID().GetCounter(),group->GetLeaderGUID().GetCounter());
|
||||
TC_LOG_INFO("server.adventurebot.navigation","FLIGHT_SHARED_TRAIL guid=%u leader=%u landingReady=%u",player->GetGUID().GetCounter(),group->GetLeaderGUID().GetCounter(),record.Following->LandingReady?1u:0u);
|
||||
}
|
||||
}
|
||||
if(airborne)
|
||||
@@ -625,6 +630,17 @@ void ClientlessFlightMgr::Fail(Player * player, Record & record, std::string con
|
||||
|
||||
bool ClientlessFlightMgr::Advance(Player * player, Record & record, bool handoff)
|
||||
{
|
||||
if(record.Following && !record.Returning)
|
||||
{
|
||||
Point landing;
|
||||
if(!record.Following->Aborted && !record.Following->BindLanding(landing))
|
||||
{record.Status.Detail="waiting for leader validated landing";return true;}
|
||||
if(record.Following->BindLanding(landing))
|
||||
{
|
||||
record.Status.Landing.Relocate(landing.X,landing.Y,landing.Z);
|
||||
record.Published->Landing=landing;record.Published->LandingReady=true;
|
||||
}
|
||||
}
|
||||
Point current = PointOf(* player), goal = PointOf(record.Returning ? record.Start : record.Status.Landing);
|
||||
auto sample =[&](float x, float y)
|
||||
{
|
||||
@@ -665,9 +681,13 @@ bool ClientlessFlightMgr::Advance(Player * player, Record & record, bool handoff
|
||||
}
|
||||
else if(record.Following)
|
||||
{
|
||||
auto shared=FollowTrail(current,goal,record.Settings,*record.Following,record.SharedIndex,sample,ray);
|
||||
auto shared=FollowTrail(current,goal,record.Settings,*record.Following,record.SharedIndex,sample,ray,
|
||||
handoff?player->GetSpeed(MOVE_FLIGHT)*1.5f:0.f);
|
||||
if(shared.Waiting)return true;
|
||||
next=shared.Step;
|
||||
if(!next.Valid)
|
||||
TC_LOG_WARN("server.adventurebot.navigation","FLIGHT_SHARED_REJECT guid=%u index=%u size=%u aborted=%u landing=(%.3f %.3f %.3f)",
|
||||
player->GetGUID().GetCounter(),uint32(record.SharedIndex),uint32(record.Following->Points.size()),record.Following->Aborted?1u:0u,goal.X,goal.Y,goal.Z);
|
||||
if(handoff && next.Valid && Distance(next.Next,PointOf(record.Segment))<1)return true;
|
||||
}
|
||||
else
|
||||
@@ -832,7 +852,7 @@ void ClientlessFlightMgr::Update(uint32 diff)
|
||||
record.Moving = false;
|
||||
Advance(player, record);
|
||||
}
|
||||
else if (!record.Returning && player->movespline->timeRemaining() <= 600 &&
|
||||
else if (!record.Returning && player->movespline->timeRemaining() <= 1500 &&
|
||||
Distance(PointOf(record.Segment), PointOf(record.Status.Landing)) > 0.1f)
|
||||
{
|
||||
// Replace a still-running spline from its native current position.
|
||||
|
||||
@@ -305,15 +305,32 @@ namespace ClientlessFlightPath
|
||||
{
|
||||
std::vector<Point> Points;
|
||||
std::vector<bool> UnderOverhang;
|
||||
Point Landing;
|
||||
bool LandingReady=false;
|
||||
bool Aborted=false;
|
||||
bool BindLanding(Point& goal) const
|
||||
{
|
||||
if(!LandingReady || Aborted)return false;
|
||||
goal=Landing;return true;
|
||||
}
|
||||
};
|
||||
struct SharedResult {Result Step;bool Waiting=false;};
|
||||
inline SharedResult FollowTrail(Point const& current,Point const& goal,Settings const& settings,
|
||||
SharedTrail const& trail,std::size_t& index,Sample const& sample,Ray const& ray)
|
||||
SharedTrail const& trail,std::size_t& index,Sample const& sample,Ray const& ray,float lookaheadDistance=0)
|
||||
{
|
||||
if(trail.Aborted)return {};
|
||||
if(!trail.LandingReady)return {{},true};
|
||||
while(index<trail.Points.size() && Distance(current,trail.Points[index])<0.7f)++index;
|
||||
if(index==trail.Points.size())return {{},true};
|
||||
// Extend a running spline before its endpoint. A corner may only be
|
||||
// skipped if the NEW complete flight volume is verified; otherwise let
|
||||
// the original checked corner finish. Never skip across an escape leg.
|
||||
if(index+1<trail.Points.size() && Distance(current,trail.Points[index])<=lookaheadDistance)
|
||||
{
|
||||
bool escape=(index<trail.UnderOverhang.size() && trail.UnderOverhang[index]) ||
|
||||
(index+1<trail.UnderOverhang.size() && trail.UnderOverhang[index+1]);
|
||||
if(!escape && Corridor(current,trail.Points[index+1],goal,settings,sample,ray,true))++index;
|
||||
}
|
||||
auto next=trail.Points[index];
|
||||
bool escape=index<trail.UnderOverhang.size() && trail.UnderOverhang[index];
|
||||
return {{next,Corridor(current,next,goal,settings,sample,ray,true,escape),false,Distance(next,goal)<0.7f,escape},false};
|
||||
|
||||
@@ -18,7 +18,7 @@ auto rays=[](Point const&a,Point const&b){
|
||||
void run(Point p,bool escapeExpected){
|
||||
Settings cfg;Memory m;m.Departed=true;Point goal{12,0,200};
|
||||
bool climbed=false,escaped=false,crossed=false,descended=false;unsigned steps=0;
|
||||
SharedTrail trail;
|
||||
SharedTrail trail;trail.LandingReady=true;trail.Landing=goal;
|
||||
for(;steps<100 && Distance(p,goal)>.1f;++steps){
|
||||
auto n=Next(p,goal,cfg,m,terrain,rays);assert(n.Valid);
|
||||
assert(Corridor(p,n.Next,goal,cfg,terrain,rays,false,n.UnderOverhang));
|
||||
|
||||
@@ -24,7 +24,7 @@ struct Position {
|
||||
float GetExactDist(Position const&p){return std::hypot(std::hypot(x-p.x,y-p.y),z-p.z);}
|
||||
};
|
||||
Point PointOf(Position const& p){return {p.x,p.y,p.z};}
|
||||
struct Spline {int remaining=500; int timeRemaining()const{return remaining;}};
|
||||
struct Spline {int remaining=1100; int timeRemaining()const{return remaining;}};
|
||||
struct Player:Position {
|
||||
Spline spline; Spline* movespline=&spline;
|
||||
bool alive=true,fly=true,running=true;int stops=0;
|
||||
@@ -75,7 +75,7 @@ int main(){
|
||||
mgr.Update(50);assert(mgr.handoffs==1&&mgr.advances==1&&player.stops==0);
|
||||
// The 200ms collision cadence must not launch a replacement each tick.
|
||||
mgr.Update(50);assert(mgr.advances==1);
|
||||
mgr._records[26].CheckTimer=0;player.spline.remaining=900;
|
||||
mgr._records[26].CheckTimer=0;player.spline.remaining=1900;
|
||||
mgr.Update(50);assert(mgr.advances==1);
|
||||
// Exact landing endpoint must finish normally, never repeatedly extend.
|
||||
auto& r=mgr._records[26];r.CheckTimer=0;r.Status.Landing=r.Segment;
|
||||
|
||||
@@ -73,7 +73,7 @@ int main(){
|
||||
auto highApproach=simulate({0,0,70},{1,0,0},flat,clear);
|
||||
assert(highApproach.Arrived&&highApproach.Steps<20&&highApproach.Peak<=70);
|
||||
// Convoy members replay exactly the published segments, retaining their own index.
|
||||
SharedTrail trail;trail.Points={{20,0,30},{40,0,40}};std::size_t index=0;Settings settings;
|
||||
SharedTrail trail;trail.LandingReady=true;trail.Landing={800,0,0};trail.Points={{20,0,30},{40,0,40}};std::size_t index=0;Settings settings;
|
||||
auto shared=FollowTrail({0,0,0},{800,0,0},settings,trail,index,flat,clear);
|
||||
assert(shared.Step.Valid&&shared.Step.Next.X==20&&index==0);
|
||||
shared=FollowTrail({20,0,30},{800,0,0},settings,trail,index,flat,clear);
|
||||
|
||||
@@ -0,0 +1,66 @@
|
||||
"""Regression: join while leader walks to takeoff; continuous safe trail handoff."""
|
||||
from pathlib import Path
|
||||
import subprocess,tempfile
|
||||
root=Path(__file__).resolve().parents[2]
|
||||
source=(root/'src/server/game/World/ClientlessFlightMgr.cpp').read_text()
|
||||
a=source.index(' if(record.Following && !record.Returning)',source.index('bool ClientlessFlightMgr::Advance('))
|
||||
b=source.index(' Point current =',a)
|
||||
sync=source[a:b]
|
||||
code=r'''
|
||||
#include "ClientlessFlightPath.h"
|
||||
#include <cassert>
|
||||
#include <iostream>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
using namespace ClientlessFlightPath;
|
||||
struct Position{float x=0,y=0,z=0;void Relocate(float a,float b,float c){x=a;y=b;z=c;}};
|
||||
struct Record{std::shared_ptr<SharedTrail> Following;std::shared_ptr<SharedTrail> Published=std::make_shared<SharedTrail>();bool Returning=false;struct{Position Landing;std::string Detail;}Status;};
|
||||
bool Sync(Record& record){
|
||||
''' + sync + r'''
|
||||
return false;
|
||||
}
|
||||
int main(){
|
||||
Record r;r.Following=std::make_shared<SharedTrail>();r.Status.Landing.z=777;
|
||||
assert(Sync(r)&&r.Status.Landing.z==777); // still preparing; no zero landing copied
|
||||
r.Following->Landing={12,34,827.765625f};r.Following->LandingReady=true;
|
||||
assert(!Sync(r)&&r.Status.Landing.x==12&&r.Status.Landing.y==34&&r.Status.Landing.z==827.765625f);
|
||||
assert(r.Published->LandingReady&&r.Published->Landing.Z==827.765625f);
|
||||
|
||||
Settings cfg;auto sample=[](float,float){return Surface{827.765625f,1000,true,0};};
|
||||
auto clear=[](Point const&,Point const&){return true;};
|
||||
// The same shared object first exists while its leader is walking to takeoff.
|
||||
SharedTrail trail;Point landing{0,0,0};std::size_t index=0;
|
||||
assert(!trail.BindLanding(landing));
|
||||
auto pending=FollowTrail({0,0,0},landing,cfg,trail,index,sample,clear);
|
||||
assert(pending.Waiting&&!pending.Step.Valid);
|
||||
// Leader completes admission later. Follower must bind this published landing,
|
||||
// including after the leader record is gone/has entered the instance.
|
||||
trail.Landing={-4905.67f,-4232.75f,827.765625f};trail.LandingReady=true;
|
||||
Point above{trail.Landing.X,trail.Landing.Y,trail.Landing.Z+10};trail.Points={above,trail.Landing};
|
||||
// Old bug: stale zero goal retains the 8-yard floor margin during touchdown.
|
||||
assert(!Corridor(above,trail.Landing,landing,cfg,sample,clear,true));
|
||||
assert(trail.BindLanding(landing));index=1;
|
||||
auto final=FollowTrail(above,landing,cfg,trail,index,sample,clear);
|
||||
assert(final.Step.Valid&&final.Step.Landing&&Distance(final.Step.Next,landing)<.01f);
|
||||
// In open air the next segment is ready before reaching the prior endpoint.
|
||||
SharedTrail cruise;cruise.LandingReady=true;cruise.Landing={100,0,0};
|
||||
cruise.Points={{25,0,60},{50,0,60}};
|
||||
auto flat=[](float,float){return Surface{0,300,true};};
|
||||
index=0;auto moving=FollowTrail({15,0,60},cruise.Landing,cfg,cruise,index,flat,clear,15);
|
||||
assert(moving.Step.Valid&&moving.Step.Next.X==50&&index==1);
|
||||
// No shortcut through a blocked corner, and no early escape-leg shortcut.
|
||||
auto obstacle=[](Point const&a,Point const&b){return !(a.X<20&&b.X>30);};
|
||||
index=0;auto corner=FollowTrail({15,0,60},cruise.Landing,cfg,cruise,index,flat,obstacle,15);
|
||||
assert(corner.Step.Valid&&corner.Step.Next.X==25&&index==0);
|
||||
cruise.UnderOverhang={true,false};index=0;
|
||||
auto escape=FollowTrail({15,0,60},cruise.Landing,cfg,cruise,index,flat,clear,15);
|
||||
assert(escape.Step.Valid&&index==0);
|
||||
trail.Aborted=true;assert(!trail.BindLanding(landing));
|
||||
assert(!FollowTrail(above,landing,cfg,trail,index,sample,clear).Step.Valid);
|
||||
std::cout<<"Shared flight: late landing publication, terminal descent, early handoff, blocked/escape corners and abort PASS\n";
|
||||
}
|
||||
'''
|
||||
with tempfile.TemporaryDirectory() as directory:
|
||||
p=Path(directory);(p/'test.cpp').write_text(code)
|
||||
subprocess.run(['c++','-std=c++17','-I'+str(root/'src/server/game/World'),str(p/'test.cpp'),'-o',str(p/'test')],check=True)
|
||||
subprocess.run([str(p/'test')],check=True)
|
||||
Reference in new issue
Block a user