Files
Viano-Core/tests/clientless/test_flight_handoff.py
T
Nico 3aa780662e Complete AdventureBot navigation, instance travel and shutdown fixes
Finish capital waiting travel and adaptive flight entry up to 10,000 yards.
Preserve checked vertical climbs, recover blocked ground departures and find
validated dry landings. Retain group routes and Undercity arrival constraints.

Restore defense from native combat references, coordinate instance rendezvous
and browser reentry, finalize clientless sessions before map destruction and
transfer Halaa capture-point ownership safely.

Include the associated regression tests, investigation reports and reusable
diagnostic scripts. Keep generated runtime evidence and server binaries local
through scoped ignore rules. Validation/build/live evidence is recorded in the
session checkpoint and individual reports.
2026-11-05 10:35:34 +01:00

166 lines
9.8 KiB
Python

"""Exercise production Update with a running native spline, not a string check."""
from pathlib import Path
import subprocess
import tempfile
root = Path(__file__).resolve().parents[2]
source = (root / 'src/server/game/World/ClientlessFlightMgr.cpp').read_text()
a = source.index('void ClientlessFlightMgr::Update(uint32 diff)')
b = source.index('bool ClientlessFlightMgr::Command(', a)
update = source[a:b]
harness = r'''
#include "ClientlessFlightPath.h"
#include "ClientlessFlightProgress.h"
#include <cassert>
#include <memory>
#include <cstdint>
#include <string>
#include <unordered_map>
using uint32 = uint32_t;
using namespace ClientlessFlightPath;
struct Position {
float x=0,y=0,z=60;
float GetPositionX() const{return x;} float GetPositionY() const{return y;} float GetPositionZ() const{return z;}
void Relocate(Position const* p){*this=*p;}
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=1100; int timeRemaining()const{return remaining;}};
struct Player:Position {
Spline spline; Spline* movespline=&spline;
bool alive=true,fly=true,running=true;int stops=0;
bool IsInCombat(){return false;}bool IsInWorld(){return true;} uint32 GetMapId(){return 0;}
bool IsAlive(){return alive;} bool IsBeingTeleported(){return false;}
bool IsMounted(){return true;} bool CanFly(){return fly;}
bool IsSplineEnabled(){return running;}
void StopMoving(){++stops;running=false;}
} player;
struct Session {Player* GetPlayer(){return &player;}} session;
enum class ClientlessPlayerState {Online};
struct World {
Session* FindClientlessSession(uint32){return &session;}
ClientlessPlayerState GetClientlessPlayerState(uint32){return ClientlessPlayerState::Online;}
} world;
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<int> Following;uint32 FormationDelay=0,TargetUpdated=0,Elapsed=0,SegmentElapsed=0,CheckTimer=0;
bool LandingRecovery=false;uint32 NextLandingSearch=0;
bool Moving=true,Returning=false,ApproachingDeparture=false,ActionOwned=false,SegmentUnderOverhang=false,SegmentVerticalClimb=false;uint32 DetachedRecoveryCount=0,DepartureAttempts=0;Position Departure;Memory Memory;std::shared_ptr<SharedTrail> Published=std::make_shared<SharedTrail>();std::vector<Position> Breadcrumbs;
};
enum class ClientlessTravelState {Moving,Arrived,Stuck};
struct Travel {struct{ClientlessTravelState State=ClientlessTravelState::Moving;}status;auto GetStatus(uint32){return &status;}}travel;
auto sClientlessTravelMgr=&travel;
#define TC_LOG_WARN(...) ((void)0)
struct Executor{bool OwnsActiveFlight(uint32){return true;}}executor;auto sClientlessActionExecutorMgr=&executor;
namespace ClientlessTravelTelemetry{void Note(Player*,char const*,std::string const&){} }
bool clear=true;
bool ClearRay(Player*,Point const&,Point const&){return clear;}
class ClientlessFlightMgr {
public:
std::unordered_map<uint32,Record> _records;
int advances=0,handoffs=0,failures=0,begins=0;
bool Begin(Player*,Position const&,std::string&,bool){++begins;auto&r=_records.at(26);r.DepartureAttempts=0;r.ActionOwned=false;r.ApproachingDeparture=false;return true;}
Surface Sample(Player*,Record&,float,float){return {0,300,true};}
void Fail(Player*,Record& r,std::string const&){++failures;r.Status.State=ClientlessFlightState::Failed;}
bool Advance(Player*,Record&,bool handoff=false){++advances;handoffs+=handoff;return true;}
bool FindReturnLanding(Player*,Record&){return false;}
bool LandFollowing(Player*){return true;}bool AdvanceFollowing(Player*,Record&){return true;}
void Update(uint32 diff);
};
'''.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.
mgr.Update(50);assert(mgr.advances==1);
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;
player.spline.remaining=400;mgr.Update(50);assert(mgr.advances==1);
// Reverse trail keeps its original checked corners.
r.Segment={25,0,60};r.Returning=true;r.CheckTimer=0;
mgr.Update(50);assert(mgr.advances==1);
// Distance means measured motion, not unflown overlapping destinations.
player.x=7;r.CheckTimer=0;mgr.Update(50);assert(std::fabs(r.Status.Travelled-7)<0.01);
// A newly blocked corridor still stops/replans immediately.
clear=false;r.CheckTimer=0;mgr.Update(50);
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;
r.SegmentVerticalClimb=true; // vertical takeoff retains the same 60s watchdog
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);
// Preserve the validated vertical climb even in the early handoff window.
r.Status.State=ClientlessFlightState::Flying;r.Returning=false;r.Moving=true;
player.fly=true;player.running=true;player.spline.remaining=1000;r.SegmentVerticalClimb=true;
r.CheckTimer=0;r.Progress.Begin(100,10000);before=mgr.advances;
mgr.Update(200);assert(mgr.advances==before);
// Geometry changes still interrupt that same vertical segment.
clear=false;r.CheckTimer=0;mgr.Update(200);assert(mgr.advances==before+1);clear=true;
// Native spline completion opens the next segment normally.
r.Moving=true;player.running=false;r.CheckTimer=0;before=mgr.advances;
mgr.Update(200);assert(mgr.advances==before+1);
r.SegmentVerticalClimb=false;player.running=true;
// A ground departure approach must wait for actual arrival before mounting.
r.Status.State=ClientlessFlightState::Mounting;r.ApproachingDeparture=true;
r.Elapsed=0;r.Departure=player;travel.status.State=ClientlessTravelState::Moving;
mgr.Update(50);assert(mgr.begins==0&&mgr.failures==1);
r.DepartureAttempts=2;r.ActionOwned=true;travel.status.State=ClientlessTravelState::Arrived;mgr.Update(50);assert(mgr.begins==1&&r.DepartureAttempts==2&&r.ActionOwned);
// A new airborne generation must publish its verified landing immediately
// and leave a replacement link for followers already holding the old trail.
r.ApproachingDeparture=false;r.Status.State=ClientlessFlightState::Flying;
r.Returning=false;r.Status.Segments=12;r.Status.Avoidances=12;r.DetachedRecoveryCount=0;
player.fly=true;auto old=r.Published;r.Status.Landing={800,0,0};
mgr.Update(50);assert(old->Aborted&&old->Replacement==r.Published);
assert(r.Published->LandingReady&&r.Published->Landing.X==800);
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<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:
cpp = Path(folder) / 'test.cpp'
binary = Path(folder) / 'test'
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, collision, 60s measured stall, jitter, vertical progress, long-flight overrun and local recovery passed.')