"""Exercise production catalog expansion with the testserver's Cataclysm DBC records.""" from pathlib import Path import struct,subprocess,tempfile root=Path(__file__).resolve().parents[2] source=(root/'src/server/game/World/AdventureLocationMgr.cpp').read_text() def function(sig): a=source.index(sig);b=source.index('{',a);e=b+1;n=1 while n:n+=(source[e]=='{')-(source[e]=='}');e+=1 return source[a:e] def dbc(name): b=(Path('/home/wow_admin/wow_cata_server/data/dbc/enUS')/name).read_bytes() magic,count,fields,size,strings=struct.unpack_from('<4s4I',b);assert magic==b'WDBC' return [struct.unpack_from('<'+'I'*fields,b,20+n*size)for n in range(count)] maps={33,34,36,469,533,671} lfg=[r for r in dbc('LFGDungeons.dbc')if r[7]in maps] modes=[r for r in dbc('MapDifficulty.dbc')if r[1]in maps] header=(root/'src/server/game/World/AdventureLocationMgr.h').read_text();a=header.index('struct AdventureLocation');b=header.index('struct AdventureMicroAction',a) code=r''' #include #include #include #include #include using uint8=unsigned char;using uint32=unsigned;using uint64=unsigned long long;using Difficulty=unsigned; constexpr unsigned RAID_DIFFICULTY_25MAN_NORMAL=1; struct ClientlessWorldPoint{std::string Id;}; '''+header[a:b]+r''' struct MapEntry{unsigned ID;bool raid;bool IsRaid(){return raid;}bool IsDungeon(){return true;}}; struct Maps{std::unordered_mapentries;MapEntry*LookupEntry(unsigned id){return entries.count(id)?&entries.at(id):nullptr;}}sMapStore; struct MapDifficulty{unsigned maxPlayers;};struct DBC{std::unordered_mapentries;MapDifficulty*GetMapDifficultyData(unsigned id,Difficulty mode){auto key=(uint64(id)<<8)|mode;return entries.count(key)?&entries.at(key):nullptr;}}sDBCManager; struct LFGEntry{unsigned MapID,DifficultyID,MinLevel,Maxlevel;};std::vectorsLFGDungeonStore; struct Access{unsigned levelMin,levelMax;};struct Objects{Access*GetAccessRequirement(unsigned,Difficulty){return nullptr;}}objects;auto sObjectMgr=&objects; struct AdventureLocationMgr{struct Band{unsigned Min=0,Max=0;};std::unordered_map_instanceBands;std::vector_locations;void BuildInstanceVariants(); AdventureLocation const*Find(std::string const&key){for(auto const&l:_locations)if(l.Point.Id==key)return &l;return nullptr;} void LoadBands(){ ''' a=source.index(' _instanceBands.clear();');b=source.index(' if (! WorldDatabase.Query',a) code+=source[a:b]+'}\n};\n'+function('void AdventureLocationMgr::BuildInstanceVariants(')+'\nint main(){\n' for id in maps:code+=f'sMapStore.entries[{id}]={{{id},{str(id in {469,533,671}).lower()}}};\n' for r in modes:code+=f'sDBCManager.entries[({r[1]}ull<<8)|{r[2]}]={{{r[5]}}};\n' for r in lfg:code+=f'sLFGDungeonStore.push_back(new LFGEntry{{{r[7]},{r[8]},{r[2]},{r[3]}}});\n' code+='AdventureLocationMgr mgr;mgr.LoadBands();\n' for id in maps:code+=f'{{AdventureLocation l;l.Point.Id="map{id}";l.Name="Map{id}";l.InstanceMap={id};mgr._locations.push_back(l);}}\n' code+=r''' mgr.BuildInstanceVariants(); for(auto id:{33,36}){ auto normal=mgr.Find("map"+std::to_string(id)),heroic=mgr.Find("map"+std::to_string(id)+"_d1"); assert(normal&&heroic&&normal->InstanceDifficulty==0&&heroic->InstanceDifficulty==1); assert(normal->MinLevel<30&&normal->MaxLevel<30); // excludes obsolete84..85 normal SFK row assert(heroic->MinLevel==85&&heroic->MaxLevel==85&&heroic->InstanceCapacity==5); assert(heroic->BaseKey==normal->Point.Id&&heroic->Name.find("heroisch")!=std::string::npos); } assert(mgr.Find("map34")->MaxLevel==30&&!mgr.Find("map34_d1")); auto classic=mgr.Find("map469");assert(classic->InstanceCapacity==20&&classic->InstanceDifficulty==0&&classic->MinLevel==60); assert(!mgr.Find("map469_d1")&&!mgr.Find("map469_d2")&&!mgr.Find("map469_d3")); // native zero-capacity placeholders assert(mgr.Find("map533")->InstanceCapacity==20&&mgr.Find("map533_d0")->InstanceCapacity==10); for(unsigned mode=0;mode<4;++mode){auto key=mode==1?"map671":"map671_d"+std::to_string(mode);auto l=mgr.Find(key); assert(l&&l->InstanceDifficulty==mode&&l->MinLevel==85&&l->InstanceCapacity==(mode%2?20:10)); assert(l->BaseKey=="map671");} for(auto entry:sLFGDungeonStore)delete entry; } ''' with tempfile.TemporaryDirectory()as d: p=Path(d);(p/'test.cpp').write_text(code) subprocess.run(['c++','-std=c++17',str(p/'test.cpp'),'-o',str(p/'test')],check=True) subprocess.run([str(p/'test')],check=True) print('Real Cataclysm DBC: dungeon normal/heroic, obsolete SFK row, classic raid placeholders, native10/25 normal/heroic and physical entrance reuse passed.')