fix(chat): handle SMSG_GM_MESSAGECHAT format, add chat diagnostics
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

SMSG_GM_MESSAGECHAT has an extra gmNameLen+gmName field after the
standard header that was causing misaligned parsing for non-whisper GM
messages. Strip the GM name before forwarding to the regular parser.

Also improve party state notifications (show member names on join) and
add INFO-level logging for all incoming/outgoing chat messages and
WARNING-level logging for group invite/accept packets to diagnose
recurring phantom party state issues.
This commit is contained in:
Kelsi 2026-04-05 05:16:57 -07:00
parent e4bd380c0d
commit a23c2172a8
2 changed files with 40 additions and 6 deletions

View file

@ -20,7 +20,31 @@ void ChatHandler::registerOpcodes(DispatchTable& table) {
if (owner_.getState() == WorldState::IN_WORLD) handleMessageChat(packet);
};
table[Opcode::SMSG_GM_MESSAGECHAT] = [this](network::Packet& packet) {
if (owner_.getState() == WorldState::IN_WORLD) handleMessageChat(packet);
if (owner_.getState() != WorldState::IN_WORLD) return;
// SMSG_GM_MESSAGECHAT has the same header as SMSG_MESSAGECHAT
// (type[1]+lang[4]+senderGuid[8]+unk[4] = 17 bytes) followed by an
// extra gmNameLen[4]+gmName[N] before the type-specific body.
// Strip the GM name field to produce standard SMSG_MESSAGECHAT format.
if (!packet.hasRemaining(21)) return; // 17 header + 4 gmNameLen min
uint8_t type = packet.readUInt8();
uint32_t lang = packet.readUInt32();
uint64_t senderGuid = packet.readUInt64();
uint32_t unk = packet.readUInt32();
uint32_t gmNameLen = packet.readUInt32();
if (!packet.hasRemaining(gmNameLen)) return;
packet.setReadPos(packet.getReadPos() + gmNameLen); // skip gmName
// Rebuild as regular SMSG_MESSAGECHAT (header + remaining body)
network::Packet regular(0);
regular.writeUInt8(type);
regular.writeUInt32(lang);
regular.writeUInt64(senderGuid);
regular.writeUInt32(unk);
const auto& raw = packet.getData();
size_t pos = packet.getReadPos();
if (pos < raw.size())
regular.writeBytes(raw.data() + pos, raw.size() - pos);
handleMessageChat(regular);
};
table[Opcode::SMSG_TEXT_EMOTE] = [this](network::Packet& packet) {
if (owner_.getState() == WorldState::IN_WORLD) handleTextEmote(packet);
@ -120,7 +144,8 @@ void ChatHandler::sendChatMessage(ChatType type, const std::string& message, con
return;
}
LOG_DEBUG("OUTGOING CHAT: type=", static_cast<int>(type), " msg='", message.substr(0, 60), "'");
LOG_INFO("OUTGOING CHAT: type=", static_cast<int>(type),
" (", getChatTypeString(type), ") target='", target, "' msg='", message.substr(0, 60), "'");
// Use the player's faction language. AzerothCore rejects wrong language.
// Alliance races: Human(1), Dwarf(3), NightElf(4), Gnome(7), Draenei(11) → COMMON (7)
@ -165,9 +190,9 @@ void ChatHandler::handleMessageChat(network::Packet& packet) {
LOG_WARNING("Failed to parse SMSG_MESSAGECHAT, size=", packet.getSize());
return;
}
LOG_DEBUG("SMSG_MESSAGECHAT: type=", static_cast<int>(data.type),
" sender='", data.senderName, "' msg='",
data.message.substr(0, 60), "'");
LOG_INFO("INCOMING CHAT: type=", static_cast<int>(data.type),
" (", getChatTypeString(data.type), ") sender=0x", std::hex, data.senderGuid, std::dec,
" '", data.senderName, "' msg='", data.message.substr(0, 60), "'");
// Skip server echo of our own messages (we already added a local echo)
if (data.senderGuid == owner_.playerGuid && data.senderGuid != 0) {

View file

@ -1087,12 +1087,14 @@ void SocialHandler::handleDuelWinner(network::Packet& packet) {
void SocialHandler::inviteToGroup(const std::string& playerName) {
if (owner_.getState() != WorldState::IN_WORLD || !owner_.socket) return;
LOG_WARNING(">>> Sending CMSG_GROUP_INVITE to '", playerName, "'");
auto packet = GroupInvitePacket::build(playerName);
owner_.socket->send(packet);
}
void SocialHandler::acceptGroupInvite() {
if (owner_.getState() != WorldState::IN_WORLD || !owner_.socket) return;
LOG_WARNING(">>> Sending CMSG_GROUP_ACCEPT");
pendingGroupInvite = false;
auto packet = GroupAcceptPacket::build();
owner_.socket->send(packet);
@ -1244,8 +1246,15 @@ void SocialHandler::handleGroupList(network::Packet& packet) {
const bool nowInGroup = !partyData.isEmpty();
if (!nowInGroup && wasInGroup) {
owner_.addSystemChatMessage("You are no longer in a group.");
LOG_INFO("Left group");
} else if (nowInGroup && !wasInGroup) {
owner_.addSystemChatMessage("You are now in a group.");
std::string members;
for (const auto& m : partyData.members) {
if (!members.empty()) members += ", ";
members += m.name.empty() ? "?" : m.name;
}
owner_.addSystemChatMessage("You joined a group with: " + members);
LOG_INFO("Joined group with ", partyData.memberCount, " members: ", members);
}
// Loot method change notification
if (wasInGroup && nowInGroup && partyData.lootMethod != prevLootMethod) {