mirror of
https://github.com/Kelsidavis/WoWee.git
synced 2026-03-23 07:40:14 +00:00
Add server info commands: /time, /played, and /who
- Add CMSG_QUERY_TIME (0x1CE) and SMSG_QUERY_TIME_RESPONSE (0x1CF) opcodes - Add CMSG_REQUEST_PLAYED_TIME (0x1CC) and SMSG_PLAYED_TIME (0x1CD) opcodes - Add CMSG_WHO (0x062) and SMSG_WHO (0x063) opcodes - Implement /time command to query and display server time - Implement /played command to show total and level playtime statistics - Implement /who [name] command to list online players with level and guild - Add packet builders: QueryTimePacket, RequestPlayedTimePacket, WhoPacket - Add response parsers for all three server info packet types - Add handlers that format and display responses in chat as system messages - Format played time as "X days, Y hours, Z minutes" for readability - Format server time as "YYYY-MM-DD HH:MM:SS" for readability
This commit is contained in:
parent
8b8e32e716
commit
f9c4cbddee
6 changed files with 287 additions and 0 deletions
|
|
@ -7,6 +7,7 @@
|
|||
#include <algorithm>
|
||||
#include <cmath>
|
||||
#include <cctype>
|
||||
#include <ctime>
|
||||
#include <random>
|
||||
#include <chrono>
|
||||
#include <filesystem>
|
||||
|
|
@ -286,6 +287,24 @@ void GameHandler::handlePacket(network::Packet& packet) {
|
|||
}
|
||||
break;
|
||||
|
||||
case Opcode::SMSG_QUERY_TIME_RESPONSE:
|
||||
if (state == WorldState::IN_WORLD) {
|
||||
handleQueryTimeResponse(packet);
|
||||
}
|
||||
break;
|
||||
|
||||
case Opcode::SMSG_PLAYED_TIME:
|
||||
if (state == WorldState::IN_WORLD) {
|
||||
handlePlayedTime(packet);
|
||||
}
|
||||
break;
|
||||
|
||||
case Opcode::SMSG_WHO:
|
||||
if (state == WorldState::IN_WORLD) {
|
||||
handleWho(packet);
|
||||
}
|
||||
break;
|
||||
|
||||
// ---- Phase 1: Foundation ----
|
||||
case Opcode::SMSG_NAME_QUERY_RESPONSE:
|
||||
handleNameQueryResponse(packet);
|
||||
|
|
@ -1530,6 +1549,39 @@ void GameHandler::inspectTarget() {
|
|||
LOG_INFO("Sent inspect request for player: ", name, " (GUID: 0x", std::hex, targetGuid, std::dec, ")");
|
||||
}
|
||||
|
||||
void GameHandler::queryServerTime() {
|
||||
if (state != WorldState::IN_WORLD || !socket) {
|
||||
LOG_WARNING("Cannot query time: not in world or not connected");
|
||||
return;
|
||||
}
|
||||
|
||||
auto packet = QueryTimePacket::build();
|
||||
socket->send(packet);
|
||||
LOG_INFO("Requested server time");
|
||||
}
|
||||
|
||||
void GameHandler::requestPlayedTime() {
|
||||
if (state != WorldState::IN_WORLD || !socket) {
|
||||
LOG_WARNING("Cannot request played time: not in world or not connected");
|
||||
return;
|
||||
}
|
||||
|
||||
auto packet = RequestPlayedTimePacket::build(true);
|
||||
socket->send(packet);
|
||||
LOG_INFO("Requested played time");
|
||||
}
|
||||
|
||||
void GameHandler::queryWho(const std::string& playerName) {
|
||||
if (state != WorldState::IN_WORLD || !socket) {
|
||||
LOG_WARNING("Cannot query who: not in world or not connected");
|
||||
return;
|
||||
}
|
||||
|
||||
auto packet = WhoPacket::build(0, 0, playerName);
|
||||
socket->send(packet);
|
||||
LOG_INFO("Sent WHO query", playerName.empty() ? "" : " for: " + playerName);
|
||||
}
|
||||
|
||||
void GameHandler::releaseSpirit() {
|
||||
if (!playerDead_) return;
|
||||
if (socket && state == WorldState::IN_WORLD) {
|
||||
|
|
@ -2792,6 +2844,97 @@ void GameHandler::addSystemChatMessage(const std::string& message) {
|
|||
addLocalChatMessage(msg);
|
||||
}
|
||||
|
||||
// ============================================================
|
||||
// Server Info Command Handlers
|
||||
// ============================================================
|
||||
|
||||
void GameHandler::handleQueryTimeResponse(network::Packet& packet) {
|
||||
QueryTimeResponseData data;
|
||||
if (!QueryTimeResponseParser::parse(packet, data)) {
|
||||
LOG_WARNING("Failed to parse SMSG_QUERY_TIME_RESPONSE");
|
||||
return;
|
||||
}
|
||||
|
||||
// Convert Unix timestamp to readable format
|
||||
time_t serverTime = static_cast<time_t>(data.serverTime);
|
||||
struct tm* timeInfo = localtime(&serverTime);
|
||||
char timeStr[64];
|
||||
strftime(timeStr, sizeof(timeStr), "%Y-%m-%d %H:%M:%S", timeInfo);
|
||||
|
||||
std::string msg = "Server time: " + std::string(timeStr);
|
||||
addSystemChatMessage(msg);
|
||||
LOG_INFO("Server time: ", data.serverTime, " (", timeStr, ")");
|
||||
}
|
||||
|
||||
void GameHandler::handlePlayedTime(network::Packet& packet) {
|
||||
PlayedTimeData data;
|
||||
if (!PlayedTimeParser::parse(packet, data)) {
|
||||
LOG_WARNING("Failed to parse SMSG_PLAYED_TIME");
|
||||
return;
|
||||
}
|
||||
|
||||
if (data.triggerMessage) {
|
||||
// Format total time played
|
||||
uint32_t totalDays = data.totalTimePlayed / 86400;
|
||||
uint32_t totalHours = (data.totalTimePlayed % 86400) / 3600;
|
||||
uint32_t totalMinutes = (data.totalTimePlayed % 3600) / 60;
|
||||
|
||||
// Format level time played
|
||||
uint32_t levelDays = data.levelTimePlayed / 86400;
|
||||
uint32_t levelHours = (data.levelTimePlayed % 86400) / 3600;
|
||||
uint32_t levelMinutes = (data.levelTimePlayed % 3600) / 60;
|
||||
|
||||
std::string totalMsg = "Total time played: ";
|
||||
if (totalDays > 0) totalMsg += std::to_string(totalDays) + " days, ";
|
||||
if (totalHours > 0 || totalDays > 0) totalMsg += std::to_string(totalHours) + " hours, ";
|
||||
totalMsg += std::to_string(totalMinutes) + " minutes";
|
||||
|
||||
std::string levelMsg = "Time played this level: ";
|
||||
if (levelDays > 0) levelMsg += std::to_string(levelDays) + " days, ";
|
||||
if (levelHours > 0 || levelDays > 0) levelMsg += std::to_string(levelHours) + " hours, ";
|
||||
levelMsg += std::to_string(levelMinutes) + " minutes";
|
||||
|
||||
addSystemChatMessage(totalMsg);
|
||||
addSystemChatMessage(levelMsg);
|
||||
}
|
||||
|
||||
LOG_INFO("Played time: total=", data.totalTimePlayed, "s, level=", data.levelTimePlayed, "s");
|
||||
}
|
||||
|
||||
void GameHandler::handleWho(network::Packet& packet) {
|
||||
// Parse WHO response
|
||||
uint32_t displayCount = packet.readUInt32();
|
||||
uint32_t onlineCount = packet.readUInt32();
|
||||
|
||||
LOG_INFO("WHO response: ", displayCount, " players displayed, ", onlineCount, " total online");
|
||||
|
||||
if (displayCount == 0) {
|
||||
addSystemChatMessage("No players found.");
|
||||
return;
|
||||
}
|
||||
|
||||
addSystemChatMessage(std::to_string(onlineCount) + " player(s) online:");
|
||||
|
||||
for (uint32_t i = 0; i < displayCount; ++i) {
|
||||
std::string playerName = packet.readString();
|
||||
std::string guildName = packet.readString();
|
||||
uint32_t level = packet.readUInt32();
|
||||
uint32_t classId = packet.readUInt32();
|
||||
uint32_t raceId = packet.readUInt32();
|
||||
packet.readUInt8(); // gender (unused)
|
||||
packet.readUInt32(); // zoneId (unused)
|
||||
|
||||
std::string msg = " " + playerName;
|
||||
if (!guildName.empty()) {
|
||||
msg += " <" + guildName + ">";
|
||||
}
|
||||
msg += " - Level " + std::to_string(level);
|
||||
|
||||
addSystemChatMessage(msg);
|
||||
LOG_INFO(" ", playerName, " (", guildName, ") Lv", level, " Class:", classId, " Race:", raceId);
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t GameHandler::generateClientSeed() {
|
||||
// Generate cryptographically random seed
|
||||
std::random_device rd;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue