Files
Viano-Core/tests/clientless/test_adventure_day_persistence.py
T
Nico 49712aadfe
GCC PCH / build (push) Waiting to run
clang PCH / build (push) Canceled after 0s
Improve AdventureBot travel recovery and destination eligibility
2026-10-29 11:10:27 +01:00

80 lines
5.4 KiB
Python

"""Execute actual day generation/restoration with the real TINYINT hour schema."""
from pathlib import Path
import subprocess,tempfile
root=Path(__file__).resolve().parents[2]
s=(root/'src/server/game/World/AdventureBehaviorMgr.cpp').read_text()
def fn(sig):
a=s.index(sig);b=s.index('{',a);e=b+1;n=1
while n:n+=(s[e]=='{')-(s[e]=='}');e+=1
return s[a:e]
code=r'''
#include "AdventureBehaviorPolicy.h"
#include "AdventureGroupPolicy.h"
#include <cassert>
#include <memory>
#include <unordered_map>
using uint32=unsigned;using namespace AdventureBehaviorPolicy;
#define TC_LOG_INFO(...) ((void)0)
struct Guid{unsigned GetCounter(){return 99;}};
struct Player{Guid GetGUID(){return {};}};
struct Field{unsigned n=0;std::string s;unsigned GetUInt8(){return n;}unsigned GetUInt32(){return 0;}std::string GetString(){return s;}};
struct Rows{std::vector<std::array<Field,3>> data;unsigned index=0;Field*Fetch(){return data[index].data();}bool NextRow(){return ++index<data.size();}};
struct Tx{unsigned slots=0,deletes=0,repairs=0;void PAppend(char const*,char const*,char const*,unsigned,unsigned,unsigned){++repairs;}void PAppend(char const*,unsigned){++deletes;}void PAppend(char const*,unsigned,unsigned,unsigned,char const*,char const*){++slots;}};
struct DB{void EscapeString(std::string&){}
std::shared_ptr<Rows> rows;std::shared_ptr<Tx> saved;unsigned oldCleanup=0;
auto PQuery(char const*,unsigned,unsigned){if(rows)rows->index=0;return rows;}
void PExecute(char const*,unsigned,unsigned){++oldCleanup;}
auto BeginTransaction(){return std::make_shared<Tx>();}void CommitTransaction(std::shared_ptr<Tx> t){saved=t;}
}WorldDatabase;
struct Profile{std::unordered_map<std::string,float>Personality,ActivityAffinity;}profile;
struct Lua{Profile*GetAdventureProfile(unsigned){return &profile;}}lua;auto sClientlessLuaEngine=&lua;
struct AdventureLocation{} location;
struct Locations{bool valid=true;auto Find(std::string const&){return valid?&location:nullptr;}} locations;auto sAdventureLocationMgr=&locations;
struct Areas{unsigned Revision(){return 1;}} areas;auto sAdventureAreaMgr=&areas;
struct AdventureBehaviorMgr{
struct Record{uint32 Date=0,AreaRevision=0;Day Plan;std::mt19937 Random{9};};
bool Allowed(Player*,AdventureLocation const&,std::string const&){return true;}
int picks=0;float Affinity(std::unordered_map<std::string,float>const&,std::string const&,float);
bool Known(std::string const& s){return s=="social" || s=="adventure" || s=="profession" || s=="rest" || s=="leisure" || s=="economy" || s=="instance" || s=="raid" || s=="groupformation";}
std::string PickLocation(Player*,Record&,std::string const& intent){++picks;return intent+"_"+std::to_string(picks);}
void Generate(Player*,Record&,uint32);
};
'''+fn('float AdventureBehaviorMgr::Affinity(')+fn('void AdventureBehaviorMgr::Generate(')+r'''
int main(){
assert(AdventureGroupPolicy::CapitalFaction(1537)==1 && AdventureGroupPolicy::CapitalFaction(1637)==2 && AdventureGroupPolicy::CapitalFaction(4395)==0);
Player p;AdventureBehaviorMgr mgr;AdventureBehaviorMgr::Record r;
auto rows=std::make_shared<Rows>();
for(unsigned h=0;h<24;++h)rows->data.push_back({Field{h,""},Field{0,h%2?"social":"adventure"},Field{0,"saved_"+std::to_string(h)}});
WorldDatabase.rows=rows;mgr.Generate(&p,r,20261029);
assert(r.Date==20261029 && mgr.picks==0 && !WorldDatabase.saved && WorldDatabase.oldCleanup==1);
for(unsigned h=0;h<24;++h)assert(r.Plan[h].Location=="saved_"+std::to_string(h));
// New login uses the same stored slots instead of rerolling the day.
AdventureBehaviorMgr::Record relog;mgr.Generate(&p,relog,20261029);assert(relog.Plan[23].Location==r.Plan[23].Location && mgr.picks==0);
// A catalog change repairs all illegal destinations and persists them,
// preserving each hour's intent instead of rerolling the entire day.
locations.valid=false;mgr.Generate(&p,relog,20261029);
assert(WorldDatabase.saved && WorldDatabase.saved->repairs==24 && relog.AreaRevision==1);
for(unsigned h=0;h<24;++h){assert(relog.Plan[h].Intent==(h%2?"social":"adventure"));assert(relog.Plan[h].Location!="saved_"+std::to_string(h));}
locations.valid=true;
// No following intent exists at 23:00: repair preparation to rest and persist it.
rows->data[23][1].s="groupformation";mgr.Generate(&p,relog,20261029);
assert(relog.Plan[23].Intent=="rest"&&relog.Plan[23].Location.find("rest_")==0);
assert(WorldDatabase.saved->repairs==1);
rows->data[23][1].s="social";
// Missing/incomplete current day is completely replaced, deleting older dates.
rows->data.resize(4);mgr.Generate(&p,r,20261030);
assert(r.Date==20261030 && mgr.picks>0 && WorldDatabase.saved->slots==24 && WorldDatabase.saved->deletes==1);
for(auto const& slot:r.Plan)assert(!slot.Intent.empty() && !slot.Location.empty());
// Geography remains weighted: curiosity reduces home bias without excluding it.
assert(RegionWeight(5,0)>RegionWeight(5,1));assert(RegionWeight(1,1)==1);
assert(LevelWeight(60,58,63,true)==1 && LevelWeight(60,80,82,true)==0);
assert(LevelWeight(60,1,10,true)<LevelWeight(60,58,63,true));
assert(LevelWeight(60,1,85,false)==1);
}
'''
with tempfile.TemporaryDirectory() as d:
p=Path(d);(p/'test.cpp').write_text(code)
subprocess.run(['g++','-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)
print('Production day planner: all 24 TINYINT slots restored across logins, incomplete/old-day replacement, full transaction and curiosity/level weighting passed')