110 lines
6.0 KiB
Python
110 lines
6.0 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 <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=500; 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;
|
|
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 Moving=true,Returning=false,ApproachingDeparture=false,ActionOwned=false,SegmentUnderOverhang=false;uint32 DetachedRecoveryCount=0;Position Departure;Memory Memory;std::shared_ptr<SharedTrail> Published;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;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 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(){
|
|
ClientlessFlightMgr mgr;mgr._records[26]={};
|
|
// 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=900;
|
|
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);
|
|
player.fly=false;mgr.Update(50);assert(mgr.failures==1);
|
|
// 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);
|
|
travel.status.State=ClientlessTravelState::Arrived;mgr.Update(50);assert(mgr.begins==1);
|
|
// 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);
|
|
}
|
|
'''
|
|
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 before spline end, terminal landing, return corners, collision stop and actual distance passed.')
|