From 7f89bd950a64304b5ff4acb45b92b2d66c749570 Mon Sep 17 00:00:00 2001 From: Kelsi Date: Mon, 9 Mar 2026 15:11:21 -0700 Subject: [PATCH] Handle GM chat, char rename, difficulty change, death/corpse opcodes, force anim - SMSG_GM_MESSAGECHAT: route to handleMessageChat (same wire format as SMSG_MESSAGECHAT) - SMSG_CHAR_RENAME: notify player of name change success/failure - SMSG_BINDZONEREPLY: confirm inn binding or "too far" message - SMSG_CHANGEPLAYER_DIFFICULTY_RESULT: difficulty change success/failure messages - SMSG_CORPSE_NOT_IN_INSTANCE: notify player corpse is outside instance - SMSG_CROSSED_INEBRIATION_THRESHOLD: "You feel rather drunk" message - SMSG_CLEAR_FAR_SIGHT_IMMEDIATE: log far sight cancellation - SMSG_FORCE_ANIM: consume packed GUID + animId - Consume 10 additional minor opcodes (gameobject animations, rune conversion, etc.) --- src/game/game_handler.cpp | 102 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 102 insertions(+) diff --git a/src/game/game_handler.cpp b/src/game/game_handler.cpp index 922dd711..67bdf452 100644 --- a/src/game/game_handler.cpp +++ b/src/game/game_handler.cpp @@ -1390,6 +1390,12 @@ void GameHandler::handlePacket(network::Packet& packet) { handleMessageChat(packet); } break; + case Opcode::SMSG_GM_MESSAGECHAT: + // GM → player message: same wire format as SMSG_MESSAGECHAT + if (state == WorldState::IN_WORLD) { + handleMessageChat(packet); + } + break; case Opcode::SMSG_TEXT_EMOTE: if (state == WorldState::IN_WORLD) { @@ -1886,6 +1892,102 @@ void GameHandler::handlePacket(network::Packet& packet) { break; } + // ---- Character service results ---- + case Opcode::SMSG_CHAR_RENAME: { + // uint32 result (0=success) + uint64 guid + string newName + if (packet.getSize() - packet.getReadPos() >= 13) { + uint32_t result = packet.readUInt32(); + /*uint64_t guid =*/ packet.readUInt64(); + std::string newName = packet.readString(); + if (result == 0) { + addSystemChatMessage("Character name changed to: " + newName); + } else { + addSystemChatMessage("Character rename failed (error " + std::to_string(result) + ")."); + } + LOG_INFO("SMSG_CHAR_RENAME: result=", result, " newName=", newName); + } + break; + } + case Opcode::SMSG_BINDZONEREPLY: { + // uint32 result (0=success, 1=too far) + if (packet.getSize() - packet.getReadPos() >= 4) { + uint32_t result = packet.readUInt32(); + if (result == 0) { + addSystemChatMessage("Your home is now set to this location."); + } else { + addSystemChatMessage("You are too far from the innkeeper."); + } + } + break; + } + case Opcode::SMSG_CHANGEPLAYER_DIFFICULTY_RESULT: { + // uint32 result + if (packet.getSize() - packet.getReadPos() >= 4) { + uint32_t result = packet.readUInt32(); + if (result == 0) { + addSystemChatMessage("Difficulty changed."); + } else { + static const char* reasons[] = { + "", "Error", "Too many members", "Already in dungeon", + "You are in a battleground", "Raid not allowed in heroic", + "You must be in a raid group", "Player not in group" + }; + const char* msg = (result < 8) ? reasons[result] : "Difficulty change failed."; + addSystemChatMessage(std::string("Cannot change difficulty: ") + msg); + } + } + break; + } + case Opcode::SMSG_CORPSE_NOT_IN_INSTANCE: + addSystemChatMessage("Your corpse is outside this instance. Release spirit to retrieve it."); + break; + case Opcode::SMSG_CROSSED_INEBRIATION_THRESHOLD: { + // uint64 playerGuid + uint32 threshold + if (packet.getSize() - packet.getReadPos() >= 12) { + uint64_t guid = packet.readUInt64(); + uint32_t threshold = packet.readUInt32(); + if (guid == playerGuid && threshold > 0) { + addSystemChatMessage("You feel rather drunk."); + } + LOG_DEBUG("SMSG_CROSSED_INEBRIATION_THRESHOLD: guid=0x", std::hex, guid, + std::dec, " threshold=", threshold); + } + break; + } + case Opcode::SMSG_CLEAR_FAR_SIGHT_IMMEDIATE: + // Far sight cancelled; viewport returns to player camera + LOG_DEBUG("SMSG_CLEAR_FAR_SIGHT_IMMEDIATE"); + break; + case Opcode::SMSG_COMBAT_EVENT_FAILED: + // Combat event could not be executed (e.g. invalid target for special ability) + packet.setReadPos(packet.getSize()); + break; + case Opcode::SMSG_FORCE_ANIM: { + // packed_guid + uint32 animId — force entity to play animation + if (packet.getSize() - packet.getReadPos() >= 1) { + (void)UpdateObjectParser::readPackedGuid(packet); + if (packet.getSize() - packet.getReadPos() >= 4) { + /*uint32_t animId =*/ packet.readUInt32(); + } + } + break; + } + case Opcode::SMSG_GAMEOBJECT_DESPAWN_ANIM: + case Opcode::SMSG_GAMEOBJECT_RESET_STATE: + case Opcode::SMSG_FLIGHT_SPLINE_SYNC: + case Opcode::SMSG_FORCE_DISPLAY_UPDATE: + case Opcode::SMSG_FORCE_SEND_QUEUED_PACKETS: + case Opcode::SMSG_FORCE_SET_VEHICLE_REC_ID: + case Opcode::SMSG_CONVERT_RUNE: + case Opcode::SMSG_CORPSE_MAP_POSITION_QUERY_RESPONSE: + case Opcode::SMSG_DAMAGE_CALC_LOG: + case Opcode::SMSG_DYNAMIC_DROP_ROLL_RESULT: + case Opcode::SMSG_DESTRUCTIBLE_BUILDING_DAMAGE: + case Opcode::SMSG_FORCED_DEATH_UPDATE: + // Consume — handled by broader object update or not yet implemented + packet.setReadPos(packet.getSize()); + break; + // ---- Zone defense messages ---- case Opcode::SMSG_DEFENSE_MESSAGE: { // uint32 zoneId + string message — used for PvP zone attack alerts