Add preparation outings and equipment checks with a five-minute return to the meeting point. Correct static portal visitor casting and level conditions, with native interaction diagnostics. Recover suspended flying mounts, walk to verified takeoff sites, and validate landing and return volumes. Include SQL migrations, targeted regression tests and live validation checkpoints.
135 lines
8.7 KiB
Python
135 lines
8.7 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 <memory>
|
|
#include <cstdint>
|
|
#include <string>
|
|
#include <vector>
|
|
#include <sstream>
|
|
#include <unordered_set>
|
|
#include <unordered_map>
|
|
#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;}};
|
|
struct Player;
|
|
struct AreaAccess {bool allowed=true,preferred=false;
|
|
bool AllowsSegment(Player const*,uint32,Position const&,Position const&)const;
|
|
bool PreferredFlight(Player const*,uint32,Position const&)const;
|
|
};
|
|
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;}
|
|
};
|
|
bool AreaAccess::AllowsSegment(Player const*,uint32,Position const&,Position const&)const{return allowed;}
|
|
bool AreaAccess::PreferredFlight(Player const*,uint32,Position const&)const{return preferred;}
|
|
AreaAccess access;auto sAdventureAreaMgr=&access;
|
|
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{int reads=0;bool enabled=true;bool GetBoolDefault(const char*,bool){++reads;return enabled;}float GetFloatDefault(const char*,float v){++reads;return v;}std::string GetStringDefault(const char*,const char*v){++reads;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{std::shared_ptr<ClientlessFlightPath::SharedTrail>Published=std::make_shared<ClientlessFlightPath::SharedTrail>();
|
|
struct{bool FollowingPlayer=false;ClientlessFlightState State=ClientlessFlightState::Flying;uint32 MapId=0;std::string Detail;}Status;
|
|
bool Moving=true,Returning=false,ApproachingDeparture=false;std::vector<Position> Breadcrumbs{{}};std::string AbortReason;
|
|
};
|
|
struct Travel{int cancels=0;void CancelTravel(uint32){++cancels;}}travel;auto sClientlessTravelMgr=&travel;
|
|
class ClientlessFlightMgr{public:
|
|
Player const* _planningPlayer=nullptr;bool _planningEnabled=false;float _planningMinDistance=600;
|
|
std::unordered_set<uint32> _planningPreferredZones;
|
|
std::unordered_map<int,int> _planningAreas,_planningTerrain;
|
|
void BeginPlanning(Player const* = nullptr);
|
|
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&,bool recovery=false)const;
|
|
void Fail(Player*,Record&,std::string const&);
|
|
};
|
|
'''
|
|
for signature in ('void ClientlessFlightMgr::BeginPlanning(', '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));
|
|
access.allowed=false;assert(!std::isfinite(mgr.Estimate(&p,0,a,b)));access.allowed=true;
|
|
access.preferred=true;assert(mgr.Estimate(&p,0,a,b)<roadBias);access.preferred=false;
|
|
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)));
|
|
// Recovery ignores normal RP minimum distance and zone preference, not native permissions.
|
|
assert(std::isfinite(mgr.Estimate(&p,0,a,b,true)));
|
|
p.license=false;assert(!std::isfinite(mgr.Estimate(&p,0,a,b,true)));p.license=true;
|
|
area.fly=false;assert(!std::isfinite(mgr.Estimate(&p,0,a,b,true)));area.fly=true;
|
|
b.x=21000;assert(!std::isfinite(mgr.Estimate(&p,0,a,b)));
|
|
assert(std::isfinite(mgr.Estimate(&p,0,a,b,true)));
|
|
// 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);
|
|
// A planning scope snapshots repeated config/profile work, while actual
|
|
// departure checks and per-map licenses remain live.
|
|
profile.CanFreeFly=true;p.skill=225;p.learned=true;p.license=true;config.enabled=true;
|
|
area.fly=true;areas.known=true;p.restrictions=false;mgr.zoneCode=12;b.x=1000;
|
|
mgr.BeginPlanning(&p);int reads=config.reads;
|
|
for(int i=0;i<1000;++i)assert(std::isfinite(mgr.Estimate(&p,0,a,b)));
|
|
assert(config.reads==reads);
|
|
p.license=false;assert(!std::isfinite(mgr.Estimate(&p,0,a,b)));p.license=true;
|
|
profile.CanFreeFly=false;assert(!mgr.Enabled(&p));
|
|
mgr.BeginPlanning();assert(!std::isfinite(mgr.Estimate(&p,0,a,b)));
|
|
profile.CanFreeFly=true;
|
|
int beforeInterrupts=p.interrupts;Record approach;approach.ApproachingDeparture=true;approach.Status.State=ClientlessFlightState::Mounting;mgr.Fail(&p,approach,"interrupted");assert(travel.cancels==1&&!approach.ApproachingDeparture&&approach.Status.State==ClientlessFlightState::Failed&&p.interrupts==beforeInterrupts);
|
|
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.')
|