Files
Viano-Core/tests/clientless/test_adventure_instance_variants.py
T

68 lines
4.5 KiB
Python

"""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 <cassert>
#include <algorithm>
#include <string>
#include <vector>
#include <unordered_map>
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_map<unsigned,MapEntry>entries;MapEntry*LookupEntry(unsigned id){return entries.count(id)?&entries.at(id):nullptr;}}sMapStore;
struct MapDifficulty{unsigned maxPlayers;};struct DBC{std::unordered_map<uint64,MapDifficulty>entries;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::vector<LFGEntry*>sLFGDungeonStore;
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<uint64,Band>_instanceBands;std::vector<AdventureLocation>_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.')