63 lines
4.8 KiB
Python
63 lines
4.8 KiB
Python
"""Execute production rotation cleanup with failed ownership/save/delete phases."""
|
|
from pathlib import Path
|
|
import subprocess,tempfile
|
|
root=Path(__file__).resolve().parents[2]
|
|
s=(root/'src/server/game/World/AdventurePopulationMgr.cpp').read_text()
|
|
a=s.index('void AdventurePopulationMgr::DeleteRotated()');b=s.index('{',a);e=b+1;n=1
|
|
while n:n+=(s[e]=='{')-(s[e]=='}');e+=1
|
|
code=r'''
|
|
#include <cassert>
|
|
#include <functional>
|
|
#include <memory>
|
|
#include <string>
|
|
#include <vector>
|
|
#include <algorithm>
|
|
#include <cstdio>
|
|
using uint32=unsigned;
|
|
#define TC_LOG_INFO(...) ((void)0)
|
|
namespace Trinity{template<class...A>std::string StringFormat(char const* f,A...args){char b[4096];std::snprintf(b,sizeof(b),f,args...);return b;}}
|
|
struct ObjectGuid{unsigned id;ObjectGuid(int,unsigned v):id(v){}};
|
|
namespace HighGuid{constexpr int Player=0;}
|
|
namespace ObjectAccessor{bool connected=false;bool FindConnectedPlayer(ObjectGuid){return connected;}}
|
|
enum class ClientlessPlayerState{Offline,Online};
|
|
struct World{ClientlessPlayerState state=ClientlessPlayerState::Offline;auto GetClientlessPlayerState(unsigned){return state;}}world;auto sWorld=&world;
|
|
struct Field{unsigned GetUInt32(){return 6;}};
|
|
struct Row{Field f;Field*Fetch(){return &f;}};using QueryResult=std::shared_ptr<Row>;
|
|
struct Callback{std::function<void()> invoke;};
|
|
struct Processor{std::vector<Callback>pending;void AddCallback(Callback c){pending.push_back(std::move(c));}void run(){auto batch=std::move(pending);pending.clear();for(auto&c:batch)c.invoke();}};
|
|
struct Query{QueryResult row;template<class F>Callback WithCallback(F f){return {[row=row,f]{f(row);}};}};
|
|
struct Tx{std::vector<std::string>sql;template<class...A>void PAppend(char const*f,A...a){sql.push_back(Trinity::StringFormat(f,a...));}};
|
|
struct Save{bool ok;std::function<void(bool)>done;template<class F>void AfterComplete(F f){done=f;}operator Callback(){return {[ok=ok,done=done]{done(ok);}};}};
|
|
struct DB{
|
|
bool world=false,owns=true,exists=true,ok=true;int queries=0;std::vector<std::string>committed;
|
|
Query AsyncQuery(char const*sql){++queries;bool present=std::string(sql).find("SELECT guid FROM characters")!=std::string::npos?exists:owns;return {present?std::make_shared<Row>():nullptr};}
|
|
auto BeginTransaction(){return std::make_shared<Tx>();}
|
|
Save AsyncCommitTransaction(std::shared_ptr<Tx>tx){if(ok)for(auto const&s:tx->sql)committed.push_back(s);return {ok,{}};}
|
|
}CharacterDatabase,WorldDatabase;
|
|
struct Player{static int deletes;static bool fail;static void DeleteFromDB(ObjectGuid,unsigned,bool,bool){++deletes;if(!fail)CharacterDatabase.exists=false;}};int Player::deletes=0;bool Player::fail=false;
|
|
struct AdventurePopulationMgr{
|
|
struct Rotation{unsigned Old=10,New=11,Account=6;bool Submitted=false;};std::vector<Rotation>_rotations{{}};
|
|
bool _busy=false;unsigned _refresh=99;Processor _queries,_saves;std::string error;
|
|
void Failed(std::string s){_busy=false;error=s;}void DeleteRotated();
|
|
void drain(){for(int i=0;i<15;++i){_queries.run();_saves.run();}}
|
|
};
|
|
'''+s[a:e]+r'''
|
|
int main(){
|
|
AdventurePopulationMgr mgr;world.state=ClientlessPlayerState::Online;mgr.DeleteRotated();assert(!mgr._busy && !CharacterDatabase.queries && !Player::deletes);
|
|
world.state=ClientlessPlayerState::Offline;ObjectAccessor::connected=true;mgr.DeleteRotated();assert(!CharacterDatabase.queries);ObjectAccessor::connected=false;
|
|
CharacterDatabase.owns=false;mgr.DeleteRotated();mgr.drain();assert(!mgr.error.empty() && !Player::deletes && mgr._rotations.size()==1);
|
|
mgr={};CharacterDatabase.owns=true;Player::fail=true;mgr.DeleteRotated();mgr.drain();assert(Player::deletes==1 && !mgr.error.empty() && mgr._rotations.size()==1 && WorldDatabase.committed.empty());
|
|
mgr={};Player::fail=false;WorldDatabase.ok=false;mgr.DeleteRotated();mgr.drain();assert(!CharacterDatabase.exists && mgr._rotations.size()==1 && !mgr.error.empty());
|
|
assert(CharacterDatabase.committed.size()==2); // native deletion barriers only; profile/journal retained
|
|
mgr={};WorldDatabase.ok=true;mgr.DeleteRotated();mgr.drain();assert(mgr._rotations.empty() && !mgr._busy && !mgr._refresh);
|
|
auto contains=[](auto const& list,char const*text){return std::any_of(list.begin(),list.end(),[&](auto const&s){return s.find(text)!=std::string::npos;});};
|
|
assert(contains(WorldDatabase.committed,"vcore_adventure_day"));
|
|
for(auto name:{"vcore_adventure_generated_bot","vcore_adventure_supply_state","vcore_adventure_instance_visit","vcore_adventure_rotation"})assert(contains(CharacterDatabase.committed,name));
|
|
}
|
|
'''
|
|
with tempfile.TemporaryDirectory() as d:
|
|
p=Path(d);(p/'test.cpp').write_text(code)
|
|
subprocess.run(['g++','-std=c++17',str(p/'test.cpp'),'-o',str(p/'test')],check=True)
|
|
subprocess.run([str(p/'test')],check=True)
|
|
print('Production rotation: connected/native-online protection, ownership refusal, native delete failure, world cleanup failure and journal-retained retry before successful profile/plan/state removal passed')
|