mirror of
https://github.com/Kelsidavis/WoWee.git
synced 2026-03-22 23:30:14 +00:00
feat: handle SMSG_MEETINGSTONE, LFG timeout, SMSG_WHOIS, and SMSG_MIRRORIMAGE_DATA
Add handlers for 14 previously-unhandled server opcodes: LFG error/timeout states (WotLK Dungeon Finder): - SMSG_LFG_TIMEDOUT: invite timed out, shows message and re-opens LFG UI - SMSG_LFG_OTHER_TIMEDOUT: another player's response timed out - SMSG_LFG_AUTOJOIN_FAILED: auto-join failed with reason code - SMSG_LFG_AUTOJOIN_FAILED_NO_PLAYER: no players available for auto-join - SMSG_LFG_LEADER_IS_LFM: party leader is in LFM mode Meeting Stone (Classic/TBC era group-finding feature): - SMSG_MEETINGSTONE_SETQUEUE: shows zone and level range in chat - SMSG_MEETINGSTONE_COMPLETE: group ready notification - SMSG_MEETINGSTONE_IN_PROGRESS: search ongoing notification - SMSG_MEETINGSTONE_MEMBER_ADDED: player name resolved and shown in chat - SMSG_MEETINGSTONE_JOINFAILED: localized error message (4 reason codes) - SMSG_MEETINGSTONE_LEAVE: queue departure notification Other: - SMSG_WHOIS: displays GM /whois result line-by-line in system chat - SMSG_MIRRORIMAGE_DATA: parses WotLK mirror image unit display ID and applies it to the entity so mirror images render with correct appearance
This commit is contained in:
parent
ebaf95cc42
commit
9b60108fa6
1 changed files with 162 additions and 0 deletions
|
|
@ -1646,6 +1646,29 @@ void GameHandler::handlePacket(network::Packet& packet) {
|
|||
}
|
||||
break;
|
||||
|
||||
case Opcode::SMSG_WHOIS: {
|
||||
// GM/admin response to /whois command: cstring with account/IP info
|
||||
// Format: string (the whois result text, typically "Name: ...\nAccount: ...\nIP: ...")
|
||||
if (packet.getReadPos() < packet.getSize()) {
|
||||
std::string whoisText = packet.readString();
|
||||
if (!whoisText.empty()) {
|
||||
// Display each line of the whois response in system chat
|
||||
std::string line;
|
||||
for (char c : whoisText) {
|
||||
if (c == '\n') {
|
||||
if (!line.empty()) addSystemChatMessage("[Whois] " + line);
|
||||
line.clear();
|
||||
} else {
|
||||
line += c;
|
||||
}
|
||||
}
|
||||
if (!line.empty()) addSystemChatMessage("[Whois] " + line);
|
||||
LOG_INFO("SMSG_WHOIS: ", whoisText);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case Opcode::SMSG_FRIEND_STATUS:
|
||||
if (state == WorldState::IN_WORLD) {
|
||||
handleFriendStatus(packet);
|
||||
|
|
@ -5609,6 +5632,110 @@ void GameHandler::handlePacket(network::Packet& packet) {
|
|||
packet.setReadPos(packet.getSize());
|
||||
break;
|
||||
|
||||
// ---- LFG error/timeout states ----
|
||||
case Opcode::SMSG_LFG_TIMEDOUT:
|
||||
// Server-side LFG invite timed out (no response within time limit)
|
||||
addSystemChatMessage("Dungeon Finder: Invite timed out.");
|
||||
if (openLfgCallback_) openLfgCallback_();
|
||||
packet.setReadPos(packet.getSize());
|
||||
break;
|
||||
case Opcode::SMSG_LFG_OTHER_TIMEDOUT:
|
||||
// Another party member failed to respond to a LFG role-check in time
|
||||
addSystemChatMessage("Dungeon Finder: Another player's invite timed out.");
|
||||
if (openLfgCallback_) openLfgCallback_();
|
||||
packet.setReadPos(packet.getSize());
|
||||
break;
|
||||
case Opcode::SMSG_LFG_AUTOJOIN_FAILED: {
|
||||
// uint32 result — LFG auto-join attempt failed (player selected auto-join at queue time)
|
||||
if (packet.getSize() - packet.getReadPos() >= 4) {
|
||||
uint32_t result = packet.readUInt32();
|
||||
(void)result;
|
||||
}
|
||||
addSystemChatMessage("Dungeon Finder: Auto-join failed.");
|
||||
packet.setReadPos(packet.getSize());
|
||||
break;
|
||||
}
|
||||
case Opcode::SMSG_LFG_AUTOJOIN_FAILED_NO_PLAYER:
|
||||
// No eligible players found for auto-join
|
||||
addSystemChatMessage("Dungeon Finder: No players available for auto-join.");
|
||||
packet.setReadPos(packet.getSize());
|
||||
break;
|
||||
case Opcode::SMSG_LFG_LEADER_IS_LFM:
|
||||
// Party leader is currently set to Looking for More (LFM) mode
|
||||
addSystemChatMessage("Your party leader is currently Looking for More.");
|
||||
packet.setReadPos(packet.getSize());
|
||||
break;
|
||||
|
||||
// ---- Meeting stone (Classic/TBC group-finding via summon stone) ----
|
||||
case Opcode::SMSG_MEETINGSTONE_SETQUEUE: {
|
||||
// uint32 zoneId + uint8 level_min + uint8 level_max — player queued for meeting stone
|
||||
if (packet.getSize() - packet.getReadPos() >= 6) {
|
||||
uint32_t zoneId = packet.readUInt32();
|
||||
uint8_t levelMin = packet.readUInt8();
|
||||
uint8_t levelMax = packet.readUInt8();
|
||||
char buf[128];
|
||||
std::snprintf(buf, sizeof(buf),
|
||||
"You are now in the Meeting Stone queue for zone %u (levels %u-%u).",
|
||||
zoneId, levelMin, levelMax);
|
||||
addSystemChatMessage(buf);
|
||||
LOG_INFO("SMSG_MEETINGSTONE_SETQUEUE: zone=", zoneId,
|
||||
" levels=", (int)levelMin, "-", (int)levelMax);
|
||||
}
|
||||
packet.setReadPos(packet.getSize());
|
||||
break;
|
||||
}
|
||||
case Opcode::SMSG_MEETINGSTONE_COMPLETE:
|
||||
// Server confirms group found and teleport summon is ready
|
||||
addSystemChatMessage("Meeting Stone: Your group is ready! Use the Meeting Stone to summon.");
|
||||
LOG_INFO("SMSG_MEETINGSTONE_COMPLETE");
|
||||
packet.setReadPos(packet.getSize());
|
||||
break;
|
||||
case Opcode::SMSG_MEETINGSTONE_IN_PROGRESS:
|
||||
// Meeting stone search is still ongoing
|
||||
addSystemChatMessage("Meeting Stone: Searching for group members...");
|
||||
LOG_DEBUG("SMSG_MEETINGSTONE_IN_PROGRESS");
|
||||
packet.setReadPos(packet.getSize());
|
||||
break;
|
||||
case Opcode::SMSG_MEETINGSTONE_MEMBER_ADDED: {
|
||||
// uint64 memberGuid — a player was added to your group via meeting stone
|
||||
if (packet.getSize() - packet.getReadPos() >= 8) {
|
||||
uint64_t memberGuid = packet.readUInt64();
|
||||
auto nit = playerNameCache.find(memberGuid);
|
||||
if (nit != playerNameCache.end() && !nit->second.empty()) {
|
||||
addSystemChatMessage("Meeting Stone: " + nit->second +
|
||||
" has been added to your group.");
|
||||
} else {
|
||||
addSystemChatMessage("Meeting Stone: A new player has been added to your group.");
|
||||
}
|
||||
LOG_INFO("SMSG_MEETINGSTONE_MEMBER_ADDED: guid=0x", std::hex, memberGuid, std::dec);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case Opcode::SMSG_MEETINGSTONE_JOINFAILED: {
|
||||
// uint8 reason — failed to join group via meeting stone
|
||||
// 0=target_not_in_lfg, 1=target_in_party, 2=target_invalid_map, 3=target_not_available
|
||||
static const char* kMeetingstoneErrors[] = {
|
||||
"Target player is not using the Meeting Stone.",
|
||||
"Target player is already in a group.",
|
||||
"You are not in a valid zone for that Meeting Stone.",
|
||||
"Target player is not available.",
|
||||
};
|
||||
if (packet.getSize() - packet.getReadPos() >= 1) {
|
||||
uint8_t reason = packet.readUInt8();
|
||||
const char* msg = (reason < 4) ? kMeetingstoneErrors[reason]
|
||||
: "Meeting Stone: Could not join group.";
|
||||
addSystemChatMessage(msg);
|
||||
LOG_INFO("SMSG_MEETINGSTONE_JOINFAILED: reason=", (int)reason);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case Opcode::SMSG_MEETINGSTONE_LEAVE:
|
||||
// Player was removed from the meeting stone queue (left, or group disbanded)
|
||||
addSystemChatMessage("You have left the Meeting Stone queue.");
|
||||
LOG_DEBUG("SMSG_MEETINGSTONE_LEAVE");
|
||||
packet.setReadPos(packet.getSize());
|
||||
break;
|
||||
|
||||
// ---- GM Ticket responses ----
|
||||
case Opcode::SMSG_GMTICKET_CREATE: {
|
||||
if (packet.getSize() - packet.getReadPos() >= 1) {
|
||||
|
|
@ -6596,6 +6723,41 @@ void GameHandler::handlePacket(network::Packet& packet) {
|
|||
packet.setReadPos(packet.getSize());
|
||||
break;
|
||||
|
||||
// ---- Mirror image data (WotLK: Mage ability Mirror Image) ----
|
||||
case Opcode::SMSG_MIRRORIMAGE_DATA: {
|
||||
// WotLK 3.3.5a format:
|
||||
// uint64 mirrorGuid — GUID of the mirror image unit
|
||||
// uint32 displayId — display ID to render the image with
|
||||
// uint8 raceId — race of caster
|
||||
// uint8 genderFlag — gender of caster
|
||||
// uint8 classId — class of caster
|
||||
// uint64 casterGuid — GUID of the player who cast the spell
|
||||
// Followed by equipped item display IDs (11 × uint32) if casterGuid != 0
|
||||
// Purpose: tells client how to render the image (same appearance as caster).
|
||||
// We parse the GUIDs so units render correctly via their existing display IDs.
|
||||
if (packet.getSize() - packet.getReadPos() < 8) break;
|
||||
uint64_t mirrorGuid = packet.readUInt64();
|
||||
if (packet.getSize() - packet.getReadPos() < 4) break;
|
||||
uint32_t displayId = packet.readUInt32();
|
||||
if (packet.getSize() - packet.getReadPos() < 3) break;
|
||||
/*uint8_t raceId =*/ packet.readUInt8();
|
||||
/*uint8_t gender =*/ packet.readUInt8();
|
||||
/*uint8_t classId =*/ packet.readUInt8();
|
||||
// Apply display ID to the mirror image unit so it renders correctly
|
||||
if (mirrorGuid != 0 && displayId != 0) {
|
||||
auto entity = entityManager.getEntity(mirrorGuid);
|
||||
if (entity) {
|
||||
auto unit = std::dynamic_pointer_cast<game::Unit>(entity);
|
||||
if (unit && unit->getDisplayId() == 0)
|
||||
unit->setDisplayId(displayId);
|
||||
}
|
||||
}
|
||||
LOG_DEBUG("SMSG_MIRRORIMAGE_DATA: mirrorGuid=0x", std::hex, mirrorGuid,
|
||||
" displayId=", std::dec, displayId);
|
||||
packet.setReadPos(packet.getSize());
|
||||
break;
|
||||
}
|
||||
|
||||
// ---- Player movement flag changes (server-pushed) ----
|
||||
case Opcode::SMSG_MOVE_GRAVITY_DISABLE:
|
||||
handleForceMoveFlagChange(packet, "GRAVITY_DISABLE", Opcode::CMSG_MOVE_GRAVITY_DISABLE_ACK,
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue