105 lines
6.5 KiB
Python
105 lines
6.5 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&,bool recovery=false)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)));
|
|
// 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);
|
|
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.')
|