68 lines
4.5 KiB
Python
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.')
|