Files
Viano-Core/tests/clientless/test_group_flight_follow.py
T
Nico 65e4abb0b9 Fix shared flight trail replacement and safe follower rejoining
Publish replacement landing targets, bound orphaned trail waits, and plan safe connections from the follower position. Support shared-route regroup landings and preserve catch-up flights after the leader lands. Add geometry, lifecycle and group travel regressions.
2026-11-03 19:59:56 +01:00

69 lines
5.5 KiB
Python

"""Execute moving-target flight and safe landing adapters over real geometry."""
from pathlib import Path
import subprocess,tempfile
root=Path(__file__).resolve().parents[2]
s=(root/'src/server/game/World/ClientlessFlightMgr.cpp').read_text()
def method(sig):
a=s.index(sig);b=s.index('{',a);e=b+1;n=1
while n:n+=(s[e]=='{')-(s[e]=='}');e+=1
return s[a:e]
code=r'''
#include "ClientlessFlightPath.h"
#include <cassert>
#include <memory>
#include <string>
#include <unordered_map>
using uint32=unsigned;using namespace ClientlessFlightPath;
constexpr int MAX_HEIGHT=100000,MOVEMENTFLAG_FLYING=1,MOVE_FLIGHT=2;
struct Position{float x=0,y=0,z=0,o=0;void Relocate(float a,float b,float c,float d=0){x=a;y=b;z=c;o=d;}float GetPositionX()const{return x;}float GetPositionY()const{return y;}float GetPositionZ()const{return z;}float GetOrientation()const{return o;}};
Point PointOf(Position const&p){return {p.x,p.y,p.z};}
namespace Trinity{bool IsValidMapCoord(float x,float y,float z){return std::isfinite(x)&&std::isfinite(y)&&std::isfinite(z);}}
struct Map{float floor=0;float GetHeight(int,float,float,float,bool,float){return floor;}}map;
struct Guid{unsigned GetCounter(){return 26;}};
struct Player:Position{bool spline=false;unsigned launches=0,stops=0;unsigned GetMapId(){return 0;}Guid GetGUID(){return {};}bool IsSplineEnabled(){return spline;}void StopMoving(){++stops;spline=false;}void AddUnitMovementFlag(int){}float GetSpeed(int){return 20;}Map*GetMap(){return &map;}int GetPhaseShift(){return 169;}};
struct AreaAccess{bool allowed=true;bool AllowsPosition(Player*,unsigned,Position const&){return allowed;}bool AllowsSegment(Player*,unsigned,Position const&,Position const&){return allowed;}}access;auto sAdventureAreaMgr=&access;
namespace Movement{struct MoveSplineInit{Player*p;Point target;MoveSplineInit(Player*who):p(who){}void MoveTo(float x,float y,float z,bool){target={x,y,z};}void SetFly(){}void SetWalk(bool){}void SetVelocity(float){}int Launch(){++p->launches;p->spline=true;return 1;}};}
enum class ClientlessFlightState{Mounting,Flying,Arrived,Failed};
struct Record{
struct{ClientlessFlightState State=ClientlessFlightState::Flying;bool FollowingPlayer=true;Position Target,Landing;unsigned Segments=0;float Travelled=5000;std::string Detail;}Status;
bool Returning=false,Moving=false;unsigned Elapsed=1000,TargetUpdated=0,SegmentElapsed=0;Position Segment,Start;unsigned SharedWaitSince=0;std::string AbortReason;
ClientlessFlightPath::Settings Settings;ClientlessFlightPath::Memory Memory;
std::shared_ptr<SharedTrail>Following;std::shared_ptr<SharedTrail>Published=std::make_shared<SharedTrail>();std::vector<Position>Breadcrumbs;
};
bool clear=true,valid=true;float ceiling=300;
bool ClearRay(Player*,Point const&,Point const&){return clear;}
struct ClientlessFlightMgr{
std::unordered_map<unsigned,Record>_records;unsigned beginnings=0,failures=0;
Surface Sample(Player*,Record&,float,float){return {map.floor,ceiling,valid};}
bool Begin(Player*,Position const&,std::string&,bool following){assert(following);++beginnings;return true;}
void Fail(Player*,Record&r,std::string const&){++failures;r.Status.State=ClientlessFlightState::Failed;}
void HoldFollowing(Player*p,Record&r){if(r.Moving)p->StopMoving();r.Moving=false;}bool Follow(Player*,Position const&,std::string&);bool LandFollowing(Player*);bool AdvanceFollowing(Player*,Record&);
};
'''
for sig in ['bool ClientlessFlightMgr::Follow(', 'bool ClientlessFlightMgr::LandFollowing(', 'bool ClientlessFlightMgr::AdvanceFollowing(']:code+=method(sig)+'\n'
code+=r'''
int main(){
ClientlessFlightMgr mgr;Player p;p.z=20;Position target;target.Relocate(100,0,20);std::string error;
assert(mgr.Follow(&p,target,error) && mgr.beginnings==1);auto&r=mgr._records[26];
assert(mgr.Follow(&p,target,error));assert(r.TargetUpdated==r.Elapsed && r.Status.Target.x==100);
assert(mgr.AdvanceFollowing(&p,r));assert(p.launches==1 && r.Segment.x>0 && r.Segment.x<=25 && r.Segment.z==20);
// Moving the leader updates the destination without inventing a ground landing.
target.Relocate(110,20,30);r.Elapsed=2000;assert(mgr.Follow(&p,target,error));assert(r.Status.Target.z==30 && r.TargetUpdated==2000);
clear=false;r.Moving=true;assert(!mgr.AdvanceFollowing(&p,r));assert(p.stops==1 && r.Status.State==ClientlessFlightState::Flying);
clear=true;ceiling=25;assert(!mgr.Follow(&p,target,error));ceiling=300;
p.Relocate(110,20,30);r.Moving=true;assert(mgr.AdvanceFollowing(&p,r));assert(!r.Moving && p.stops==2); // holds in air, never dismounts at moving goal
assert(mgr.LandFollowing(&p));assert(!r.Status.FollowingPlayer && r.Status.Landing.z==0 && r.Status.Travelled==0);
assert(!mgr.Follow(&p,target,error)); // landing cannot be replaced by a new pursuit
r.Status.FollowingPlayer=false;r.Following=std::make_shared<SharedTrail>();
assert(mgr.LandFollowing(&p));assert(r.Returning&&!r.Following&&r.Start.z==0&&r.Breadcrumbs.empty());
r.Returning=false;
r.Status.FollowingPlayer=true;r.Status.State=ClientlessFlightState::Mounting;assert(mgr.LandFollowing(&p) && mgr.failures==1);
r.Status.State=ClientlessFlightState::Flying;r.Status.FollowingPlayer=true;valid=false;assert(mgr.LandFollowing(&p) && mgr.failures==2);
}
'''
with tempfile.TemporaryDirectory() as d:
p=Path(d);(p/'test.cpp').write_text(code)
subprocess.run(['g++','-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)
print('Moving-target flight: bounded phased corridors, obstacle hold, ceiling rejection, aerial formation hold, safe landing and return fallback passed')