80 lines
5.4 KiB
Python
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')
|