mirror of
https://github.com/Kelsidavis/WoWee.git
synced 2026-03-25 16:30:15 +00:00
Add multi-expansion support with data-driven protocol layer
Replace hardcoded WotLK protocol constants with a data-driven architecture supporting Classic 1.12.1, TBC 2.4.3, and WotLK 3.3.5a. Each expansion has JSON profiles for opcodes, update fields, and DBC layouts, plus C++ polymorphic packet parsers for binary format differences (movement flags, speed fields, transport data, spline format, char enum layout). Key components: - ExpansionRegistry: scans Data/expansions/*/expansion.json at startup - OpcodeTable: logical enum <-> wire values loaded from JSON - UpdateFieldTable: field indices loaded from JSON per expansion - DBCLayout: schema-driven DBC field lookups replacing magic numbers - PacketParsers: WotLK/TBC/Classic parsers with correct flag positions - Multi-manifest AssetManager: layered manifests with priority ordering - HDPackManager: overlay texture packs with expansion compatibility - Auth screen expansion picker replacing hardcoded version dropdown
This commit is contained in:
parent
aa16a687c2
commit
7092844b5e
51 changed files with 5258 additions and 887 deletions
156
src/game/update_field_table.cpp
Normal file
156
src/game/update_field_table.cpp
Normal file
|
|
@ -0,0 +1,156 @@
|
|||
#include "game/update_field_table.hpp"
|
||||
#include "core/logger.hpp"
|
||||
#include <fstream>
|
||||
#include <sstream>
|
||||
#include <algorithm>
|
||||
|
||||
namespace wowee {
|
||||
namespace game {
|
||||
|
||||
static const UpdateFieldTable* g_activeUpdateFieldTable = nullptr;
|
||||
|
||||
void setActiveUpdateFieldTable(const UpdateFieldTable* table) { g_activeUpdateFieldTable = table; }
|
||||
const UpdateFieldTable* getActiveUpdateFieldTable() { return g_activeUpdateFieldTable; }
|
||||
|
||||
struct UFNameEntry {
|
||||
const char* name;
|
||||
UF field;
|
||||
};
|
||||
|
||||
static const UFNameEntry kUFNames[] = {
|
||||
{"OBJECT_FIELD_ENTRY", UF::OBJECT_FIELD_ENTRY},
|
||||
{"UNIT_FIELD_TARGET_LO", UF::UNIT_FIELD_TARGET_LO},
|
||||
{"UNIT_FIELD_TARGET_HI", UF::UNIT_FIELD_TARGET_HI},
|
||||
{"UNIT_FIELD_HEALTH", UF::UNIT_FIELD_HEALTH},
|
||||
{"UNIT_FIELD_POWER1", UF::UNIT_FIELD_POWER1},
|
||||
{"UNIT_FIELD_MAXHEALTH", UF::UNIT_FIELD_MAXHEALTH},
|
||||
{"UNIT_FIELD_MAXPOWER1", UF::UNIT_FIELD_MAXPOWER1},
|
||||
{"UNIT_FIELD_LEVEL", UF::UNIT_FIELD_LEVEL},
|
||||
{"UNIT_FIELD_FACTIONTEMPLATE", UF::UNIT_FIELD_FACTIONTEMPLATE},
|
||||
{"UNIT_FIELD_FLAGS", UF::UNIT_FIELD_FLAGS},
|
||||
{"UNIT_FIELD_FLAGS_2", UF::UNIT_FIELD_FLAGS_2},
|
||||
{"UNIT_FIELD_DISPLAYID", UF::UNIT_FIELD_DISPLAYID},
|
||||
{"UNIT_FIELD_MOUNTDISPLAYID", UF::UNIT_FIELD_MOUNTDISPLAYID},
|
||||
{"UNIT_NPC_FLAGS", UF::UNIT_NPC_FLAGS},
|
||||
{"UNIT_DYNAMIC_FLAGS", UF::UNIT_DYNAMIC_FLAGS},
|
||||
{"UNIT_END", UF::UNIT_END},
|
||||
{"PLAYER_FLAGS", UF::PLAYER_FLAGS},
|
||||
{"PLAYER_XP", UF::PLAYER_XP},
|
||||
{"PLAYER_NEXT_LEVEL_XP", UF::PLAYER_NEXT_LEVEL_XP},
|
||||
{"PLAYER_FIELD_COINAGE", UF::PLAYER_FIELD_COINAGE},
|
||||
{"PLAYER_QUEST_LOG_START", UF::PLAYER_QUEST_LOG_START},
|
||||
{"PLAYER_FIELD_INV_SLOT_HEAD", UF::PLAYER_FIELD_INV_SLOT_HEAD},
|
||||
{"PLAYER_FIELD_PACK_SLOT_1", UF::PLAYER_FIELD_PACK_SLOT_1},
|
||||
{"PLAYER_SKILL_INFO_START", UF::PLAYER_SKILL_INFO_START},
|
||||
{"PLAYER_EXPLORED_ZONES_START", UF::PLAYER_EXPLORED_ZONES_START},
|
||||
{"GAMEOBJECT_DISPLAYID", UF::GAMEOBJECT_DISPLAYID},
|
||||
{"ITEM_FIELD_STACK_COUNT", UF::ITEM_FIELD_STACK_COUNT},
|
||||
};
|
||||
|
||||
static constexpr size_t kUFNameCount = sizeof(kUFNames) / sizeof(kUFNames[0]);
|
||||
|
||||
void UpdateFieldTable::loadWotlkDefaults() {
|
||||
fieldMap_.clear();
|
||||
struct { UF field; uint16_t idx; } defaults[] = {
|
||||
{UF::OBJECT_FIELD_ENTRY, 3},
|
||||
{UF::UNIT_FIELD_TARGET_LO, 6},
|
||||
{UF::UNIT_FIELD_TARGET_HI, 7},
|
||||
{UF::UNIT_FIELD_HEALTH, 24},
|
||||
{UF::UNIT_FIELD_POWER1, 25},
|
||||
{UF::UNIT_FIELD_MAXHEALTH, 32},
|
||||
{UF::UNIT_FIELD_MAXPOWER1, 33},
|
||||
{UF::UNIT_FIELD_LEVEL, 54},
|
||||
{UF::UNIT_FIELD_FACTIONTEMPLATE, 55},
|
||||
{UF::UNIT_FIELD_FLAGS, 59},
|
||||
{UF::UNIT_FIELD_FLAGS_2, 60},
|
||||
{UF::UNIT_FIELD_DISPLAYID, 67},
|
||||
{UF::UNIT_FIELD_MOUNTDISPLAYID, 69},
|
||||
{UF::UNIT_NPC_FLAGS, 82},
|
||||
{UF::UNIT_DYNAMIC_FLAGS, 147},
|
||||
{UF::UNIT_END, 148},
|
||||
{UF::PLAYER_FLAGS, 150},
|
||||
{UF::PLAYER_XP, 634},
|
||||
{UF::PLAYER_NEXT_LEVEL_XP, 635},
|
||||
{UF::PLAYER_FIELD_COINAGE, 1170},
|
||||
{UF::PLAYER_QUEST_LOG_START, 158},
|
||||
{UF::PLAYER_FIELD_INV_SLOT_HEAD, 324},
|
||||
{UF::PLAYER_FIELD_PACK_SLOT_1, 370},
|
||||
{UF::PLAYER_SKILL_INFO_START, 636},
|
||||
{UF::PLAYER_EXPLORED_ZONES_START, 1041},
|
||||
{UF::GAMEOBJECT_DISPLAYID, 8},
|
||||
{UF::ITEM_FIELD_STACK_COUNT, 14},
|
||||
};
|
||||
for (auto& d : defaults) {
|
||||
fieldMap_[static_cast<uint16_t>(d.field)] = d.idx;
|
||||
}
|
||||
LOG_INFO("UpdateFieldTable: loaded ", fieldMap_.size(), " WotLK default fields");
|
||||
}
|
||||
|
||||
bool UpdateFieldTable::loadFromJson(const std::string& path) {
|
||||
std::ifstream f(path);
|
||||
if (!f.is_open()) {
|
||||
LOG_WARNING("UpdateFieldTable: cannot open ", path);
|
||||
return false;
|
||||
}
|
||||
|
||||
std::string json((std::istreambuf_iterator<char>(f)), std::istreambuf_iterator<char>());
|
||||
|
||||
fieldMap_.clear();
|
||||
size_t loaded = 0;
|
||||
size_t pos = 0;
|
||||
|
||||
while (pos < json.size()) {
|
||||
size_t keyStart = json.find('"', pos);
|
||||
if (keyStart == std::string::npos) break;
|
||||
size_t keyEnd = json.find('"', keyStart + 1);
|
||||
if (keyEnd == std::string::npos) break;
|
||||
std::string key = json.substr(keyStart + 1, keyEnd - keyStart - 1);
|
||||
|
||||
size_t colon = json.find(':', keyEnd);
|
||||
if (colon == std::string::npos) break;
|
||||
|
||||
size_t valStart = colon + 1;
|
||||
while (valStart < json.size() && (json[valStart] == ' ' || json[valStart] == '\t' ||
|
||||
json[valStart] == '\r' || json[valStart] == '\n'))
|
||||
++valStart;
|
||||
|
||||
size_t valEnd = json.find_first_of(",}\r\n", valStart);
|
||||
if (valEnd == std::string::npos) valEnd = json.size();
|
||||
std::string valStr = json.substr(valStart, valEnd - valStart);
|
||||
// Trim whitespace
|
||||
while (!valStr.empty() && (valStr.back() == ' ' || valStr.back() == '\t'))
|
||||
valStr.pop_back();
|
||||
|
||||
uint16_t idx = 0;
|
||||
try { idx = static_cast<uint16_t>(std::stoul(valStr)); } catch (...) {
|
||||
pos = valEnd + 1;
|
||||
continue;
|
||||
}
|
||||
|
||||
// Find matching UF enum
|
||||
for (size_t i = 0; i < kUFNameCount; ++i) {
|
||||
if (key == kUFNames[i].name) {
|
||||
fieldMap_[static_cast<uint16_t>(kUFNames[i].field)] = idx;
|
||||
++loaded;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
pos = valEnd + 1;
|
||||
}
|
||||
|
||||
LOG_INFO("UpdateFieldTable: loaded ", loaded, " fields from ", path);
|
||||
return loaded > 0;
|
||||
}
|
||||
|
||||
uint16_t UpdateFieldTable::index(UF field) const {
|
||||
auto it = fieldMap_.find(static_cast<uint16_t>(field));
|
||||
return (it != fieldMap_.end()) ? it->second : 0xFFFF;
|
||||
}
|
||||
|
||||
bool UpdateFieldTable::hasField(UF field) const {
|
||||
return fieldMap_.count(static_cast<uint16_t>(field)) > 0;
|
||||
}
|
||||
|
||||
} // namespace game
|
||||
} // namespace wowee
|
||||
Loading…
Add table
Add a link
Reference in a new issue