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

74 lines
5.0 KiB
Python

"""Execute the real five-minute gathering handler with deterministic travel."""
from pathlib import Path
import subprocess,tempfile
root=Path(__file__).resolve().parents[2]
s=(root/'src/server/game/World/AdventureBehaviorMgr.cpp').read_text();a=s.index('bool AdventureBehaviorMgr::UpdateGathering(');b=s.index('\nvoid AdventureBehaviorMgr::Update(',a)
code=r'''
#include "AdventureGroupPolicy.h"
#include <cassert>
#include <ctime>
#include <string>
#include <cmath>
#include <iostream>
#include <vector>
using uint32=unsigned;
#define TC_LOG_INFO(...) ((void)0)
struct Position{float x=0;};struct Guid{unsigned GetCounter(){return 26;}};
struct Player{float x=50;unsigned map=0;bool combat=false;unsigned stopped=0;Guid GetGUID(){return{};}unsigned GetMapId(){return map;}
float GetExactDist(Position const&p){return std::fabs(x-p.x);}void StopMoving(){++stopped;}};
bool Unsafe(Player*p){return p->combat;}
enum class ClientlessTravelState{Moving,Arrived,Rejected};
struct Travel{struct{ClientlessTravelState State=ClientlessTravelState::Rejected;}status;unsigned cancels=0;bool StartTravel(Player*,Position const&){status.State=ClientlessTravelState::Moving;return true;}auto GetStatus(unsigned){return &status;}void CancelTravel(unsigned){++cancels;status.State=ClientlessTravelState::Arrived;}}travel;
auto sClientlessTravelMgr=&travel;
namespace ClientlessPositioning{float clearance=10;bool available=false;unsigned reserves=0;
float Clearance(Player*,Player const&){return clearance;}void Reserve(Player*,Player const&){++reserves;}
bool Nearby(Player*,Position const&,float,Position&spot,std::vector<Position>&){spot.x=3;return available;}}
struct AdventureBehaviorMgr{enum class Phase{Travel,OnSite};struct Record{
std::string Intent="groupformation",Micro="profession_tools",Detail;time_t Deadline=400,GatherRetryAt=0,NextSpacing=0;
bool Gathering=false,Dirty=false;Phase State=Phase::OnSite;unsigned AnchorMap=0,MicroTimer=20000;Position Anchor;
struct{bool Active=false,StopRequested=false;}Leisure;
struct{bool Active=true;}Social;struct{bool Active=false;}Recovery;
};unsigned moves=0,resets=0;bool reachable=true;
bool UpdateGathering(Player*,Record&,time_t);
void ResetMicro(Player*,Record&r){++resets;r.Micro.clear();r.MicroTimer=0;r.Social.Active=r.Recovery.Active=false;}
bool LocalMove(Player*,Record&,Position const&target,float budget){assert(target.x==0&&budget==500);++moves;if(reachable)travel.status.State=ClientlessTravelState::Moving;return reachable;}
};
'''+s[a:b]+r'''
int main(){
using AdventureGroupPolicy::Gathering;
assert(!Gathering("groupformation",401,100));assert(Gathering("groupformation",400,100));
assert(Gathering("groupformation",400,399));assert(!Gathering("groupformation",400,400));
assert(!Gathering("groupformation",400,401));assert(!Gathering("groupformation",0,100));assert(!Gathering("leisure",400,100));
Player p;AdventureBehaviorMgr m;AdventureBehaviorMgr::Record r;
assert(!m.UpdateGathering(&p,r,99)&&m.moves==0);
r.Leisure.Active=true;assert(!m.UpdateGathering(&p,r,100));
assert(r.Gathering&&r.Leisure.StopRequested&&r.MicroTimer==0&&m.resets==0&&m.moves==0);
r.Leisure.Active=false;assert(m.UpdateGathering(&p,r,101));
assert(m.moves==1&&m.resets==1&&!r.Social.Active&&r.Micro.empty());
assert(m.UpdateGathering(&p,r,102)&&m.moves==1);
travel.status.State=ClientlessTravelState::Rejected;m.reachable=false;
assert(m.UpdateGathering(&p,r,105)&&m.moves==1);assert(m.UpdateGathering(&p,r,111)&&m.moves==2);
p.x=0;travel.status.State=ClientlessTravelState::Moving;
assert(m.UpdateGathering(&p,r,112)&&p.stopped==1&&travel.cancels==1);
assert(r.Detail=="waiting at group meeting point before departure");
auto n=m.moves;assert(m.UpdateGathering(&p,r,399)&&m.moves==n); // hold, no new action
ClientlessPositioning::clearance=0;ClientlessPositioning::available=true;
assert(m.UpdateGathering(&p,r,399)&&r.NextSpacing==429&&travel.status.State==ClientlessTravelState::Moving);
auto cancels=travel.cancels;
assert(m.UpdateGathering(&p,r,399)&&travel.cancels==cancels); // spacing walk must finish
p.x=3;travel.status.State=ClientlessTravelState::Arrived;
assert(m.UpdateGathering(&p,r,399)&&travel.cancels==cancels);
assert(!m.UpdateGathering(&p,r,400)&&!r.Gathering&&r.GatherRetryAt==0); // deadline never waits for members
r={};r.State=AdventureBehaviorMgr::Phase::Travel;assert(!m.UpdateGathering(&p,r,100)&&!r.Gathering);
r.State=AdventureBehaviorMgr::Phase::OnSite;r.Dirty=true;assert(!m.UpdateGathering(&p,r,100));
r.Dirty=false;p.combat=true;assert(!m.UpdateGathering(&p,r,100));
p.combat=false;r.Intent="instance";assert(!m.UpdateGathering(&p,r,100));
std::cout<<"Production five-minute gathering: exact cutoff, safe corridor return request, preparation cancellation, bounded retries, meeting hold and deadline release passed\n";
}
'''
with tempfile.TemporaryDirectory()as d:
p=Path(d);(p/'test.cpp').write_text(code)
subprocess.run(['c++','-std=c++17','-I'+str(root/'src/server/game/World'),str(p/'test.cpp'),'-o',str(p/'test')],check=True)
subprocess.run([str(p/'test')],check=True)