fix: guard equipment set packets against unsupported expansions

Classic and TBC lack equipment set opcodes, so sending save/use/delete
packets would transmit wire opcode 0xFFFF and potentially disconnect the
client. Now all three methods check wireOpcode != 0xFFFF before sending,
and the Outfits tab is only shown when the expansion supports equipment
sets (via supportsEquipmentSets() check).
This commit is contained in:
Kelsi 2026-03-20 05:12:24 -07:00
parent 9600dd40e3
commit e68a1fa2ec
3 changed files with 16 additions and 5 deletions

View file

@ -1534,6 +1534,7 @@ public:
std::string iconName;
};
const std::vector<EquipmentSetInfo>& getEquipmentSets() const { return equipmentSetInfo_; }
bool supportsEquipmentSets() const;
void useEquipmentSet(uint32_t setId);
void saveEquipmentSet(const std::string& name, const std::string& iconName = "INV_Misc_QuestionMark",
uint64_t existingGuid = 0, uint32_t setIndex = 0xFFFFFFFF);

View file

@ -10675,8 +10675,14 @@ void GameHandler::sendRequestVehicleExit() {
vehicleId_ = 0; // Optimistically clear; server will confirm via SMSG_PLAYER_VEHICLE_DATA(0)
}
bool GameHandler::supportsEquipmentSets() const {
return wireOpcode(Opcode::CMSG_EQUIPMENT_SET_SAVE) != 0xFFFF;
}
void GameHandler::useEquipmentSet(uint32_t setId) {
if (state != WorldState::IN_WORLD || !socket) return;
uint16_t wire = wireOpcode(Opcode::CMSG_EQUIPMENT_SET_USE);
if (wire == 0xFFFF) { addUIError("Equipment sets not supported."); return; }
// Find the equipment set to get target item GUIDs per slot
const EquipmentSet* es = nullptr;
for (const auto& s : equipmentSets_) {
@ -10687,7 +10693,7 @@ void GameHandler::useEquipmentSet(uint32_t setId) {
return;
}
// CMSG_EQUIPMENT_SET_USE: 19 × (PackedGuid itemGuid + uint8 srcBag + uint8 srcSlot)
network::Packet pkt(wireOpcode(Opcode::CMSG_EQUIPMENT_SET_USE));
network::Packet pkt(wire);
for (int slot = 0; slot < 19; ++slot) {
uint64_t itemGuid = es->itemGuids[slot];
MovementPacket::writePackedGuid(pkt, itemGuid);
@ -10733,6 +10739,8 @@ void GameHandler::useEquipmentSet(uint32_t setId) {
void GameHandler::saveEquipmentSet(const std::string& name, const std::string& iconName,
uint64_t existingGuid, uint32_t setIndex) {
if (state != WorldState::IN_WORLD) return;
uint16_t wire = wireOpcode(Opcode::CMSG_EQUIPMENT_SET_SAVE);
if (wire == 0xFFFF) { addUIError("Equipment sets not supported."); return; }
// CMSG_EQUIPMENT_SET_SAVE: uint64 setGuid + uint32 setIndex + string name + string iconName
// + 19 × PackedGuid itemGuid (one per equipment slot, 018)
if (setIndex == 0xFFFFFFFF) {
@ -10742,7 +10750,7 @@ void GameHandler::saveEquipmentSet(const std::string& name, const std::string& i
if (es.setId >= setIndex) setIndex = es.setId + 1;
}
}
network::Packet pkt(wireOpcode(Opcode::CMSG_EQUIPMENT_SET_SAVE));
network::Packet pkt(wire);
pkt.writeUInt64(existingGuid); // 0 = create new, nonzero = update
pkt.writeUInt32(setIndex);
pkt.writeString(name);
@ -10757,8 +10765,10 @@ void GameHandler::saveEquipmentSet(const std::string& name, const std::string& i
void GameHandler::deleteEquipmentSet(uint64_t setGuid) {
if (state != WorldState::IN_WORLD || setGuid == 0) return;
uint16_t wire = wireOpcode(Opcode::CMSG_DELETEEQUIPMENT_SET);
if (wire == 0xFFFF) { addUIError("Equipment sets not supported."); return; }
// CMSG_DELETEEQUIPMENT_SET: uint64 setGuid
network::Packet pkt(wireOpcode(Opcode::CMSG_DELETEEQUIPMENT_SET));
network::Packet pkt(wire);
pkt.writeUInt64(setGuid);
socket->send(pkt);
// Remove locally so UI updates immediately

View file

@ -1438,8 +1438,8 @@ void InventoryScreen::renderCharacterScreen(game::GameHandler& gameHandler) {
ImGui::EndTabItem();
}
// Equipment Sets tab (WotLK — always show so player can create sets)
if (ImGui::BeginTabItem("Outfits")) {
// Equipment Sets tab (WotLK only — requires server support)
if (gameHandler.supportsEquipmentSets() && ImGui::BeginTabItem("Outfits")) {
ImGui::Spacing();
// Save current gear as new set