Files
WoWee/include/game/opcode_table.hpp
Kelsi e00c629bb6 Opcode registry: move to generated canonical+alias pipeline
Introduce data-driven opcode registry with canonical and alias sources:

- Added Data/opcodes/canonical.json as the single canonical LogicalOpcode set.

- Added Data/opcodes/aliases.json for cross-core naming aliases (CMaNGOS/AzerothCore/local legacy).

Added generator and generated include fragments:

- tools/gen_opcode_registry.py emits include/game/opcode_enum_generated.inc, include/game/opcode_names_generated.inc, and include/game/opcode_aliases_generated.inc.

- include/game/opcode_table.hpp now consumes generated enum entries.

- src/game/opcode_table.cpp now consumes generated name and alias tables.

Loader canonicalization behavior:

- OpcodeTable::nameToLogical canonicalizes incoming JSON opcode names via alias table before enum lookup, so implementation code stays stable while expansion maps can use different core spellings.

Validation and build integration:

- Added tools/validate_opcode_maps.py to validate canonical contract across expansions.

- Added CMake targets opcodes-generate and opcodes-validate.

- wowee target now depends on opcodes-generate so generated headers stay current.

Validation/build run:

- cmake -S . -B build

- cmake --build build --target opcodes-generate opcodes-validate

- cmake --build build -j32
2026-02-20 03:02:31 -08:00

79 lines
2.3 KiB
C++

#pragma once
#include <cstdint>
#include <string>
#include <unordered_map>
#include <optional>
namespace wowee {
namespace game {
/**
* Logical opcode identifiers (expansion-agnostic).
*
* These are compile-time enum values used in switch statements.
* The actual wire values depend on the active expansion and are
* loaded from JSON at runtime via OpcodeTable.
*/
enum class LogicalOpcode : uint16_t {
// Generated from Data/opcodes/canonical.json
#include "game/opcode_enum_generated.inc"
// Sentinel
COUNT
};
/**
* Maps LogicalOpcode ↔ expansion-specific wire values.
*
* Loaded from JSON (e.g. Data/expansions/wotlk/opcodes.json).
* Used for sending packets (toWire) and receiving them (fromWire).
*/
class OpcodeTable {
public:
/**
* Load opcode mappings from a JSON file.
* Format: { "CMSG_PING": "0x1DC", "SMSG_AUTH_CHALLENGE": "0x1EC", ... }
*/
bool loadFromJson(const std::string& path);
/** LogicalOpcode → wire value for sending packets. Returns 0xFFFF if unknown. */
uint16_t toWire(LogicalOpcode op) const;
/** Wire value → LogicalOpcode for receiving packets. Returns nullopt if unknown. */
std::optional<LogicalOpcode> fromWire(uint16_t wireValue) const;
/** Check if a logical opcode has a wire mapping. */
bool hasOpcode(LogicalOpcode op) const;
/** Number of mapped opcodes. */
size_t size() const { return logicalToWire_.size(); }
private:
std::unordered_map<uint16_t, uint16_t> logicalToWire_; // LogicalOpcode → wire
std::unordered_map<uint16_t, uint16_t> wireToLogical_; // wire → LogicalOpcode
static std::optional<LogicalOpcode> nameToLogical(const std::string& name);
static const char* logicalToName(LogicalOpcode op);
};
/**
* Global active opcode table pointer (set by GameHandler at startup).
* Used by world_packets.cpp and other code that needs to send packets
* without direct access to a GameHandler instance.
*/
void setActiveOpcodeTable(const OpcodeTable* table);
const OpcodeTable* getActiveOpcodeTable();
/**
* Get the wire value for a logical opcode using the active table.
* Convenience helper for packet construction code.
*/
inline uint16_t wireOpcode(LogicalOpcode op) {
const auto* table = getActiveOpcodeTable();
return table ? table->toWire(op) : 0xFFFF;
}
} // namespace game
} // namespace wowee