"""Run production mixed-group instance/wipe lifecycle with native-world adapters.""" from pathlib import Path import subprocess,tempfile root=Path(__file__).resolve().parents[2] s=(root/'src/server/game/World/AdventureEncounterMgr.cpp').read_text() def function(sig): a=s.index(sig);b=s.index('{',a);e=b+1;depth=1 while depth:depth+=(s[e]=='{')-(s[e]=='}');e+=1 return s[a:e] header=(root/'src/server/game/World/AdventureEncounterMgr.h').read_text() header='\n'.join(l for l in header.splitlines() if not l.startswith('#include')).replace('private:','public:') code=r''' #include #include #include #include #include #include #include #include using uint32=unsigned;using uint8=unsigned char; #define TC_GAME_API struct ObjectGuid{unsigned value=1;unsigned GetCounter(){return value;}}; struct Position{float x=0,y=0,z=0,o=0;void Relocate(float a,float b,float c,float d=0){x=a;y=b;z=c;o=d;}void Relocate(Position const*p){*this=*p;}float GetPositionX()const{return x;}float GetPositionY()const{return y;}float GetPositionZ()const{return z;}float GetOrientation()const{return o;}float GetExactDist(Position const*p)const{return std::hypot(x-p->x,y-p->y);}}; struct Session{bool clientless=true;bool IsClientless(){return clientless;}}; struct Unit:Position{bool alive=true;unsigned map=100;bool IsAlive(){return alive;}unsigned GetMapId(){return map;}}; struct Player; struct Ref{Player*p;Ref*nextRef=nullptr;Player*GetSource(){return p;}Ref*next(){return nextRef;}}; struct Group{Ref*first=nullptr;Ref*GetFirstMember(){return first;}}group; struct Map{bool dungeon=true;bool IsDungeon(){return dungeon;}}inside,outside{false}; constexpr unsigned PLAYER_FLAGS=0,PLAYER_FLAGS_GHOST=1,CMSG_REPOP_REQUEST=2; struct WorldPacket{WorldPacket(int,int){}templateWorldPacket&operator<<(T){return *this;}}; struct Corpse:Position{unsigned GetMapId(){return 100;}unsigned GetInstanceId(){return 10;}}; struct Player:Unit{ ObjectGuid guid;Session session;Group*groupPtr=&group;bool combat=false,ghost=false,requested=false,teleporting=false;unsigned instance=10,teleports=0,revives=0;std::listcompanions; struct PlayerSession:Session{Player*owner=nullptr;void HandleRepopRequestOpcode(WorldPacket&){owner->ghost=true;owner->map=0;owner->Relocate(-50,0,0);}}playerSession; Player(){playerSession.owner=this;}ObjectGuid GetGUID(){return guid;}Group*GetGroup(){return groupPtr;}PlayerSession*GetSession(){return &playerSession;} Corpse*GetCorpse(){return nullptr;}Map*GetMap(){return map==100?&inside:&outside;}unsigned GetInstanceId(){return instance;} bool IsInCombat(){return combat;}bool IsBeingTeleported(){return teleporting;}bool IsInMap(Unit*u){return map==u->map;} bool IsResurrectRequested(){return requested;}void ResurrectUsingRequestData(){alive=true;ghost=false;requested=false;++revives;} bool HasFlag(unsigned,unsigned){return ghost;}void GetPartyMembers(std::list&v){v=companions;} bool TeleportTo(unsigned m,float x,float y,float z,float o){map=m;Relocate(x,y,z,o);++teleports;return true;} void ResurrectPlayer(float hp,bool sickness){assert(hp==.5f && !sickness);alive=true;ghost=false;++revives;}void SpawnCorpseBones(){} }; struct Trigger{unsigned target_mapId;float target_X,target_Y,target_Z,target_Orientation;}; struct MapStore{Map*LookupEntry(unsigned){return &inside;}}sMapStore; struct Objects{Trigger entrance{100,1,0,0,0},exit{0,10,0,0,0};Trigger const*GetMapEntranceTrigger(unsigned){return &entrance;}Trigger const*GetGoBackTrigger(unsigned){return &exit;}}objects;auto sObjectMgr=&objects; enum class ClientlessTravelState{Moving,Arrived,Stuck,PathRejected};struct Status{ClientlessTravelState State=ClientlessTravelState::Arrived;Position Target;}; struct ClientlessPathDiagnostics{bool Complete=true,Suspicious=false;}; struct Travel{bool present=false,path=true,accept=true;unsigned starts=0;Status status;Status*GetStatus(unsigned){return present?&status:nullptr;}void CancelTravel(unsigned){present=false;}bool AnalyzePath(Player*,Position const&,ClientlessPathDiagnostics&){return path;}bool StartTravel(Player*,Position const&p){++starts;if(accept){present=true;status.State=ClientlessTravelState::Moving;status.Target=p;}return accept;}}travel;auto sClientlessTravelMgr=&travel; struct Behavior{unsigned actual=0;void EnterPlayerInstance(Player*){++actual;}}behavior;auto sAdventureBehaviorMgr=&behavior; '''+header+r''' AdventureEncounterMgr* AdventureEncounterMgr::instance(){static AdventureEncounterMgr m;return &m;} bool AdventureEncounterMgr::AvoidGroundDamage(Player*){return false;} bool AdventureEncounterMgr::FollowIntoInstance(Player*){return false;} ''' for name in ('bool AdventureEncounterMgr::MixedGroup(', 'bool AdventureEncounterMgr::Active(', 'void AdventureEncounterMgr::Register(', 'void AdventureEncounterMgr::Offline(', 'bool AdventureEncounterMgr::Update(', 'bool AdventureEncounterMgr::Death('):code+=function(name)+'\n' code+=r''' int main(){ AdventureEncounterMgr mgr;Player bot,human,other;human.playerSession.clientless=false;human.guid.value=2;other.guid.value=3; Ref a{&bot},b{&human},c{&other};a.nextRef=&b;b.nextRef=&c;group.first=&a; assert(mgr.MixedGroup(&bot) && mgr.Active(&bot));human.playerSession.clientless=true;assert(!mgr.Active(&bot));assert(!mgr.Update(&bot,500));assert(mgr._records.empty());human.playerSession.clientless=false; assert(!mgr.Update(&bot,500));assert(mgr._records.count(1));assert(behavior.actual==1); unsigned hook=0;mgr.Register(100,[&](Player*,unsigned){++hook;return true;});assert(mgr.Update(&bot,500) && hook==1);mgr.Register(100,{});mgr._behaviors.clear(); bot.alive=false;bot.Relocate(35,0,0);bot.companions={&human};assert(mgr.Death(&bot));assert(!bot.ghost && bot.teleports==0); // waits while ally lives human.alive=false;other.alive=false;bot.companions.clear();assert(mgr.Death(&bot));assert(bot.ghost && bot.map==0); travel.path=false;assert(mgr.Death(&bot));assert(bot.map==0 && bot.x==10 && bot.teleports==1); // failed route permits entrance fallback assert(mgr.Death(&bot));assert(bot.alive && !bot.ghost && bot.map==100 && bot.revives==1); assert(mgr.Update(&bot,500));assert(travel.starts==0); // gather other dead bots first other.alive=true;other.map=100;assert(mgr.Update(&bot,500));assert(travel.starts==1 && travel.status.Target.x==35); // returns to death point human.alive=true;assert(!mgr.Update(&bot,500));assert(!mgr._records.at(1).Returning); // follows living player instead bot.alive=false;bot.requested=true;assert(mgr.Death(&bot));assert(bot.alive && !bot.requested && bot.revives==2); bot.groupPtr=nullptr;assert(mgr.Update(&bot,500));assert(bot.map==0 && bot.x==10); // kick exits safely assert(!mgr.Update(&bot,500));assert(mgr._records.empty()); bot.groupPtr=&group;bot.map=100;bot.alive=false;bot.requested=false;assert(mgr.Death(&bot));assert(mgr._records.count(1)); // login already dead still establishes context mgr.Offline(&bot);assert(mgr._records.empty()); } ''' 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 encounters: bot-only exclusion, mixed mode, extension hook, wait/revive, wipe fallback/reentry/gather/return, living-player priority, kick exit and offline cleanup passed')