Files
Viano-Core/tests/clientless/test_flight_policy.py
T
Nico 8df9591c99 Implement dynamic clientless free-flight travel
Integrate transient flight alternatives with graph routing, native flying mounts, phased terrain and collision checks, bounded avoidance and confirmed landing. Add safe-return recovery, node and location integration, profile settings, diagnostic commands, regression tests and flight documentation.
2026-10-26 17:35:58 +01:00

99 lines
6.0 KiB
Python

"""Compile production flight eligibility/cost and fail-safe abort decisions with Core doubles."""
from pathlib import Path
import subprocess,tempfile
root=Path(__file__).resolve().parents[2]
source=(root/'src/server/game/World/ClientlessFlightMgr.cpp').read_text()
def function(signature):
a=source.index(signature);opening=source.index('{',a);depth=1;b=opening+1
while depth:
depth+=(source[b]=='{')-(source[b]=='}');b+=1
return source[a:b]
harness=r'''
#include "ClientlessFlightPath.h"
#include <cassert>
#include <cstdint>
#include <string>
#include <vector>
#include <sstream>
#include <limits>
using uint32=uint32_t;
using namespace ClientlessFlightPath;
#define TC_LOG_WARN(...) ((void)0)
constexpr int SKILL_RIDING=762,MAP_COMMON=0,SPELL_AURA_MOUNT_RESTRICTIONS=1;
struct Position{float x=0,y=0,z=0;float GetPositionX()const{return x;}float GetPositionY()const{return y;}float GetPositionZ()const{return z;}};
Point PointOf(Position const&p){return {p.x,p.y,p.z};}float Infinity(){return std::numeric_limits<float>::infinity();}
struct Guid{uint32 GetCounter()const{return 26;}};
struct Player{
bool license=true,cold=true,learned=true,alive=true,canFly=true,restrictions=false;
uint32 skill=225,map=0,area=87;int stops=0,interrupts=0;
Guid GetGUID()const{return {};}
uint32 GetSkillValue(int)const{return skill;}
bool HasSpell(uint32 s)const{return s==90267?license:s==54197?cold:learned;}
uint32 GetMapId()const{return map;}uint32 GetAreaId()const{return area;}
bool HasAuraType(int)const{return restrictions;}
bool CanFly(){return canFly;}bool IsAlive(){return alive;}
void StopMoving(){++stops;}void InterruptNonMeleeSpells(bool){++interrupts;}
float GetPositionX(){return 0;}float GetPositionY(){return 0;}float GetPositionZ(){return 50;}
};
struct Mount{uint32 DisplayId=32235,Weight=100;};
struct Profile{bool CanFreeFly=true;std::vector<Mount> FlightMounts{{}};} profile;
struct Lua{auto GetAdventureProfile(uint32){return &profile;}} lua;
struct Config{bool enabled=true;bool GetBoolDefault(const char*,bool){return enabled;}float GetFloatDefault(const char*,float v){return v;}std::string GetStringDefault(const char*,const char*v){return v;}} config;
auto sClientlessLuaEngine=&lua;auto sConfigMgr=&config;
struct SpellMgr{void* GetSpellInfo(uint32){return this;}} spells;auto sSpellMgr=&spells;
uint32 FlightType(void*){return 1;}
struct MapEntry{uint32 MapType=0;} entry;struct Maps{auto LookupEntry(uint32){return &entry;}} maps;auto& sMapStore=maps;
enum class AreaMountFlags{AllowFlyingMounts};
struct Flags{bool fly=true;bool HasFlag(AreaMountFlags){return fly;}};
struct Area{bool fly=true;uint32 World_pvp_ID=0;Flags GetMountFlags()const{return {fly};}} area;
struct Areas{bool known=true;auto LookupEntry(uint32){return known?&area:nullptr;}} areas;auto& sAreaTableStore=areas;
enum class ClientlessFlightState{Mounting,Flying,Arrived,Failed};
struct Record{
struct{ClientlessFlightState State=ClientlessFlightState::Flying;uint32 MapId=0;std::string Detail;}Status;
bool Moving=true,Returning=false;std::vector<Position> Breadcrumbs{{}};std::string AbortReason;
};
class ClientlessFlightMgr{public:
uint32 areaCode=87,zoneCode=12;
std::pair<uint32,uint32> AreaZone(Player const*,uint32,Position const&)const{return {areaCode,zoneCode};}
bool Enabled(Player const*)const;bool Preferred(Player const*,uint32,Position const&)const;
float Estimate(Player const*,uint32,Position const&,Position const&)const;
void Fail(Player*,Record&,std::string const&);
};
'''
for signature in ('bool ClientlessFlightMgr::Enabled(', 'bool ClientlessFlightMgr::Preferred(', 'float ClientlessFlightMgr::Estimate(', 'void ClientlessFlightMgr::Fail('):
harness+=function(signature)+'\n'
harness+=r'''
int main(){
ClientlessFlightMgr mgr;Player p;Position a,b;b.x=1000;
float roadBias=mgr.Estimate(&p,0,a,b);assert(roadBias>1000&&std::isfinite(roadBias));
p.license=false;assert(!std::isfinite(mgr.Estimate(&p,0,a,b)));p.license=true;
p.cold=false;assert(!std::isfinite(mgr.Estimate(&p,571,a,b)));p.cold=true;
p.skill=150;assert(!mgr.Enabled(&p));p.skill=225;
p.learned=false;assert(!mgr.Enabled(&p));p.learned=true;
profile.CanFreeFly=false;assert(!mgr.Enabled(&p));profile.CanFreeFly=true;
entry.MapType=1;assert(!std::isfinite(mgr.Estimate(&p,0,a,b)));entry.MapType=0;
// Eversong/Ghostlands and Deeprun use native mount flags 13, without bit 2.
area.fly=false;assert(!std::isfinite(mgr.Estimate(&p,530,a,b)));
assert(!std::isfinite(mgr.Estimate(&p,369,a,b)));
area.fly=true;mgr.zoneCode=210;
assert(mgr.Estimate(&p,571,a,b)<roadBias); // explicit zone preference
b.x=90;assert(std::isfinite(mgr.Estimate(&p,571,a,b)));
mgr.zoneCode=12;assert(!std::isfinite(mgr.Estimate(&p,0,a,b)));
// Unknown metadata is not an invented blanket prohibition; actual native
// mount capability/cast at departure still decides in Start().
areas.known=false;b.x=1000;assert(std::isfinite(mgr.Estimate(&p,0,a,b)));areas.known=true;
area.fly=false;area.World_pvp_ID=1;assert(std::isfinite(mgr.Estimate(&p,571,a,b)));area.World_pvp_ID=0;
// An airborne abort first returns. A blocked return must not dismount/drop.
Record r;mgr.Fail(&p,r,"blocked");assert(r.Returning&&r.Status.State==ClientlessFlightState::Flying&&p.stops==1);
mgr.Fail(&p,r,"reverse blocked");assert(r.Status.State==ClientlessFlightState::Failed);
Record mounting;mounting.Status.State=ClientlessFlightState::Mounting;mgr.Fail(&p,mounting,"cast rejected");
assert(mounting.Status.State==ClientlessFlightState::Failed&&p.interrupts==1&&!mounting.Returning);
Record revoked;p.canFly=false;mgr.Fail(&p,revoked,"permission lost");assert(revoked.Status.State==ClientlessFlightState::Failed&&!revoked.Returning);
}
'''
with tempfile.TemporaryDirectory(prefix='vcore-flight-policy-') 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 native eligibility, license/profile/zone policy, RP costs and fail-safe return decisions passed.')