Files
Viano-Core/tests/clientless/test_rescue_executor.py
T

71 lines
4.7 KiB
Python

"""Exercise the real rescue executor with explicit graph and native transfer doubles."""
from pathlib import Path
import subprocess,tempfile
root=Path(__file__).resolve().parents[2]
s=(root/'src/server/game/World/ClientlessActionExecutorMgr.cpp').read_text()
a=s.index('bool ClientlessActionExecutorMgr::TryRescueTravel(');b=s.index('bool ClientlessActionExecutorMgr::RecoverTravel(',a)
code=r'''
#include <cassert>
#include <string>
#include <vector>
#include <unordered_set>
#include <utility>
using uint32=unsigned;
#define TC_LOG_WARN(...) ((void)0)
struct Node{unsigned MapId=369;std::string Type="rescue";float X=33,Y=40,Z=-4,O=0;}node;
struct Session{unsigned acks=0;void CompleteClientlessTeleport(){++acks;}}session;
struct Player{
unsigned map=369,transfers=0,stops=0;bool alive=true,combat=false,teleport=false,transport=false,taxi=false,flying=false,transferOK=true;
bool IsAlive(){return alive;}bool IsInCombat(){return combat;}bool IsBeingTeleported(){return teleport;}bool GetTransport(){return transport;}
bool IsInFlight(){return taxi;}bool IsFlying(){return flying;}unsigned GetMapId(){return map;}
void StopMoving(){++stops;}Session*GetSession(){return &session;}
bool TeleportTo(unsigned m,float,float,float,float){assert(m==369);++transfers;return transferOK;}
};
struct ClientlessTravelRouteStep{std::string NodeKey;};
struct Graph{bool available=true,transfer=false,forceSeen=false;unsigned calls=0;
bool PlanRescueRoute(Player*,std::vector<ClientlessTravelRouteStep>&r,std::string&t,bool force,std::unordered_set<uint32>const*){
++calls;forceSeen=force;if(!available)return false;r={{"rescue"},{"exit"}};t=(transfer||force)?"rescue":"";return true;}
Node*FindNode(std::string const&){return &node;}
}graph;auto sClientlessTravelGraphMgr=&graph;
struct Areas{bool allowed=true;bool AllowsNode(Player*,Node const&){return allowed;}}areas;auto sAdventureAreaMgr=&areas;
struct Core{bool managed=true;bool IsManaged(unsigned){return managed;}}core;auto sClientlessPlayerMgr=&core;
struct Travel{unsigned cancels=0;void CancelTravel(unsigned){++cancels;}}travel;auto sClientlessTravelMgr=&travel;
enum class ClientlessActionExecutionState{Moving,Failed};
struct ClientlessActionExecutorMgr{
struct Record{
bool RescueActive=false,RescueWalkFailed=false,StopAtNode=true,Active=false;
unsigned RescueAttempts=0;std::unordered_set<unsigned>FailedEdgeDatabaseIds;
std::vector<ClientlessTravelRouteStep>Route;unsigned RouteStep=99;std::vector<int>EdgePath;
bool DeferredStartActive=true,GroundExitActive=true,GroundResumeActive=true,TaxiActive=true,RecoveryWanderActive=true;
bool TransportActive=true,FlightActive=true,FlightApproachActive=true,PortalUseActive=true,FormationWaiting=true,GroundChunk=true,ReplanAtGroundEnd=true,GroundFlightExit=true;
bool FinalLocalLegActive=true,FinalLocalLegPending=true,RouteComplete=false;
std::string originalGoal="original-intent";
struct{ClientlessActionExecutionState State=ClientlessActionExecutionState::Failed;std::string Detail;}Status;
};unsigned starts=0;bool StartRouteStep(Player*,unsigned,Record&){++starts;return true;}
bool TryRescueTravel(Player*,unsigned,Record&);
};
'''+s[a:b]+r'''
int main(){
ClientlessActionExecutorMgr m;Player p;ClientlessActionExecutorMgr::Record r;
assert(m.TryRescueTravel(&p,58,r)&&r.RescueActive&&r.RescueAttempts==1&&p.transfers==0);
assert(r.Route.size()==2&&r.RouteStep==0&&!r.StopAtNode&&!r.FinalLocalLegPending&&!r.TransportActive&&!r.ReplanAtGroundEnd);
assert(r.originalGoal=="original-intent");assert(!m.TryRescueTravel(&p,58,r));
r.RescueWalkFailed=true;assert(m.TryRescueTravel(&p,58,r)&&graph.forceSeen&&p.transfers==1&&session.acks==1);
assert(!m.TryRescueTravel(&p,58,r)); // bounded attempts
r={};graph.transfer=true;p.map=0;assert(!m.TryRescueTravel(&p,58,r)&&p.transfers==1);p.map=369;
r={};areas.allowed=false;assert(!m.TryRescueTravel(&p,58,r));areas.allowed=true;
r={};node.Type="ground";assert(!m.TryRescueTravel(&p,58,r));node.Type="rescue";
r={};p.combat=true;auto calls=graph.calls;assert(!m.TryRescueTravel(&p,58,r)&&graph.calls==calls);p.combat=false;
p.transport=true;assert(!m.TryRescueTravel(&p,58,r));p.transport=false;
p.flying=true;assert(!m.TryRescueTravel(&p,58,r));p.flying=false;
p.alive=false;assert(!m.TryRescueTravel(&p,58,r));p.alive=true;
core.managed=false;assert(!m.TryRescueTravel(&p,58,r));core.managed=true;
graph.available=false;assert(!m.TryRescueTravel(&p,58,r));
}
'''
with tempfile.TemporaryDirectory()as d:
p=Path(d);(p/'test.cpp').write_text(code)
subprocess.run(['c++','-std=c++17',str(p/'test.cpp'),'-o',str(p/'test')],check=True)
subprocess.run([str(p/'test')],check=True)
print('Rescue executor: walk first, tram-only validated transfer, native ACK, original goal retention, state reset, access and bounded retry guards passed')