mirror of
https://github.com/Kelsidavis/WoWee.git
synced 2026-04-15 00:43:52 +00:00
Some checks are pending
Build / Build (arm64) (push) Waiting to run
Build / Build (x86-64) (push) Waiting to run
Build / Build (macOS arm64) (push) Waiting to run
Build / Build (windows-arm64) (push) Waiting to run
Build / Build (windows-x86-64) (push) Waiting to run
Security / CodeQL (C/C++) (push) Waiting to run
Security / Semgrep (push) Waiting to run
Security / Sanitizer Build (ASan/UBSan) (push) Waiting to run
UNIT_FIELD_AURAFLAGS was defined in the UF enum and used in Classic and Turtle JSON files (index 98) but missing from the kUFNames lookup table. The JSON loader silently skipped it, so Classic/Turtle aura flag data from UPDATE_OBJECT was never mapped. This could cause aura display issues on Classic 1.12 and Turtle WoW servers.
158 lines
6.4 KiB
C++
158 lines
6.4 KiB
C++
#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},
|
|
{"OBJECT_FIELD_SCALE_X", UF::OBJECT_FIELD_SCALE_X},
|
|
{"UNIT_FIELD_TARGET_LO", UF::UNIT_FIELD_TARGET_LO},
|
|
{"UNIT_FIELD_TARGET_HI", UF::UNIT_FIELD_TARGET_HI},
|
|
{"UNIT_FIELD_BYTES_0", UF::UNIT_FIELD_BYTES_0},
|
|
{"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_FIELD_AURAS", UF::UNIT_FIELD_AURAS},
|
|
{"UNIT_FIELD_AURAFLAGS", UF::UNIT_FIELD_AURAFLAGS},
|
|
{"UNIT_NPC_FLAGS", UF::UNIT_NPC_FLAGS},
|
|
{"UNIT_DYNAMIC_FLAGS", UF::UNIT_DYNAMIC_FLAGS},
|
|
{"UNIT_FIELD_RESISTANCES", UF::UNIT_FIELD_RESISTANCES},
|
|
{"UNIT_FIELD_STAT0", UF::UNIT_FIELD_STAT0},
|
|
{"UNIT_FIELD_STAT1", UF::UNIT_FIELD_STAT1},
|
|
{"UNIT_FIELD_STAT2", UF::UNIT_FIELD_STAT2},
|
|
{"UNIT_FIELD_STAT3", UF::UNIT_FIELD_STAT3},
|
|
{"UNIT_FIELD_STAT4", UF::UNIT_FIELD_STAT4},
|
|
{"UNIT_END", UF::UNIT_END},
|
|
{"UNIT_FIELD_ATTACK_POWER", UF::UNIT_FIELD_ATTACK_POWER},
|
|
{"UNIT_FIELD_RANGED_ATTACK_POWER", UF::UNIT_FIELD_RANGED_ATTACK_POWER},
|
|
{"PLAYER_FLAGS", UF::PLAYER_FLAGS},
|
|
{"PLAYER_BYTES", UF::PLAYER_BYTES},
|
|
{"PLAYER_BYTES_2", UF::PLAYER_BYTES_2},
|
|
{"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_FIELD_KEYRING_SLOT_1", UF::PLAYER_FIELD_KEYRING_SLOT_1},
|
|
{"PLAYER_FIELD_BANK_SLOT_1", UF::PLAYER_FIELD_BANK_SLOT_1},
|
|
{"PLAYER_FIELD_BANKBAG_SLOT_1", UF::PLAYER_FIELD_BANKBAG_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},
|
|
{"ITEM_FIELD_DURABILITY", UF::ITEM_FIELD_DURABILITY},
|
|
{"ITEM_FIELD_MAXDURABILITY", UF::ITEM_FIELD_MAXDURABILITY},
|
|
{"PLAYER_REST_STATE_EXPERIENCE", UF::PLAYER_REST_STATE_EXPERIENCE},
|
|
{"PLAYER_CHOSEN_TITLE", UF::PLAYER_CHOSEN_TITLE},
|
|
{"PLAYER_FIELD_MOD_DAMAGE_DONE_POS", UF::PLAYER_FIELD_MOD_DAMAGE_DONE_POS},
|
|
{"PLAYER_FIELD_MOD_HEALING_DONE_POS", UF::PLAYER_FIELD_MOD_HEALING_DONE_POS},
|
|
{"PLAYER_BLOCK_PERCENTAGE", UF::PLAYER_BLOCK_PERCENTAGE},
|
|
{"PLAYER_DODGE_PERCENTAGE", UF::PLAYER_DODGE_PERCENTAGE},
|
|
{"PLAYER_PARRY_PERCENTAGE", UF::PLAYER_PARRY_PERCENTAGE},
|
|
{"PLAYER_CRIT_PERCENTAGE", UF::PLAYER_CRIT_PERCENTAGE},
|
|
{"PLAYER_RANGED_CRIT_PERCENTAGE", UF::PLAYER_RANGED_CRIT_PERCENTAGE},
|
|
{"PLAYER_SPELL_CRIT_PERCENTAGE1", UF::PLAYER_SPELL_CRIT_PERCENTAGE1},
|
|
{"PLAYER_FIELD_COMBAT_RATING_1", UF::PLAYER_FIELD_COMBAT_RATING_1},
|
|
{"PLAYER_FIELD_HONOR_CURRENCY", UF::PLAYER_FIELD_HONOR_CURRENCY},
|
|
{"PLAYER_FIELD_ARENA_CURRENCY", UF::PLAYER_FIELD_ARENA_CURRENCY},
|
|
{"CONTAINER_FIELD_NUM_SLOTS", UF::CONTAINER_FIELD_NUM_SLOTS},
|
|
{"CONTAINER_FIELD_SLOT_1", UF::CONTAINER_FIELD_SLOT_1},
|
|
};
|
|
|
|
static constexpr size_t kUFNameCount = sizeof(kUFNames) / sizeof(kUFNames[0]);
|
|
|
|
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;
|
|
}
|
|
|
|
if (loaded == 0) {
|
|
LOG_WARNING("UpdateFieldTable: no fields loaded from ", path);
|
|
return false;
|
|
}
|
|
|
|
LOG_INFO("UpdateFieldTable: loaded ", loaded, " fields from ", path);
|
|
return true;
|
|
}
|
|
|
|
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
|