mirror of
https://github.com/Kelsidavis/WoWee.git
synced 2026-05-03 08:03:50 +00:00
Add chat channels, chat settings, and fix missing chat text
Fix WotLK chat parser not stripping null terminators from messages, fix channel message local echo missing channelName, expand default channels to General/Trade/LocalDefense/LookingForGroup with configurable auto-join, add Classic packet format for join/leave channel, display channel index prefix in chat, and add Chat settings tab with timestamps, font size, and auto-join toggles.
This commit is contained in:
parent
54ad8f38b2
commit
6b392a5dd8
8 changed files with 197 additions and 7 deletions
|
|
@ -3912,6 +3912,10 @@ void GameHandler::sendChatMessage(ChatType type, const std::string& message, con
|
|||
echo.type = type;
|
||||
}
|
||||
|
||||
if (type == ChatType::CHANNEL) {
|
||||
echo.channelName = target;
|
||||
}
|
||||
|
||||
addLocalChatMessage(echo);
|
||||
}
|
||||
|
||||
|
|
@ -4075,14 +4079,16 @@ void GameHandler::handleTextEmote(network::Packet& packet) {
|
|||
|
||||
void GameHandler::joinChannel(const std::string& channelName, const std::string& password) {
|
||||
if (state != WorldState::IN_WORLD || !socket) return;
|
||||
auto packet = JoinChannelPacket::build(channelName, password);
|
||||
auto packet = packetParsers_ ? packetParsers_->buildJoinChannel(channelName, password)
|
||||
: JoinChannelPacket::build(channelName, password);
|
||||
socket->send(packet);
|
||||
LOG_INFO("Requesting to join channel: ", channelName);
|
||||
}
|
||||
|
||||
void GameHandler::leaveChannel(const std::string& channelName) {
|
||||
if (state != WorldState::IN_WORLD || !socket) return;
|
||||
auto packet = LeaveChannelPacket::build(channelName);
|
||||
auto packet = packetParsers_ ? packetParsers_->buildLeaveChannel(channelName)
|
||||
: LeaveChannelPacket::build(channelName);
|
||||
socket->send(packet);
|
||||
LOG_INFO("Requesting to leave channel: ", channelName);
|
||||
}
|
||||
|
|
@ -4092,6 +4098,13 @@ std::string GameHandler::getChannelByIndex(int index) const {
|
|||
return joinedChannels_[index - 1];
|
||||
}
|
||||
|
||||
int GameHandler::getChannelIndex(const std::string& channelName) const {
|
||||
for (int i = 0; i < static_cast<int>(joinedChannels_.size()); ++i) {
|
||||
if (joinedChannels_[i] == channelName) return i + 1; // 1-based
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void GameHandler::handleChannelNotify(network::Packet& packet) {
|
||||
ChannelNotifyData data;
|
||||
if (!ChannelNotifyParser::parse(packet, data)) {
|
||||
|
|
@ -4135,8 +4148,10 @@ void GameHandler::handleChannelNotify(network::Packet& packet) {
|
|||
}
|
||||
|
||||
void GameHandler::autoJoinDefaultChannels() {
|
||||
joinChannel("General");
|
||||
joinChannel("Trade");
|
||||
if (chatAutoJoin.general) joinChannel("General");
|
||||
if (chatAutoJoin.trade) joinChannel("Trade");
|
||||
if (chatAutoJoin.localDefense) joinChannel("LocalDefense");
|
||||
if (chatAutoJoin.lfg) joinChannel("LookingForGroup");
|
||||
}
|
||||
|
||||
void GameHandler::setTarget(uint64_t guid) {
|
||||
|
|
|
|||
|
|
@ -487,6 +487,26 @@ bool ClassicPacketParsers::parseMessageChat(network::Packet& packet, MessageChat
|
|||
return true;
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// Classic CMSG_JOIN_CHANNEL / CMSG_LEAVE_CHANNEL
|
||||
// Classic format: just string channelName + string password (no channelId/hasVoice/joinedByZone)
|
||||
// ============================================================================
|
||||
|
||||
network::Packet ClassicPacketParsers::buildJoinChannel(const std::string& channelName, const std::string& password) {
|
||||
network::Packet packet(wireOpcode(Opcode::CMSG_JOIN_CHANNEL));
|
||||
packet.writeString(channelName);
|
||||
packet.writeString(password);
|
||||
LOG_DEBUG("[Classic] Built CMSG_JOIN_CHANNEL: channel=", channelName);
|
||||
return packet;
|
||||
}
|
||||
|
||||
network::Packet ClassicPacketParsers::buildLeaveChannel(const std::string& channelName) {
|
||||
network::Packet packet(wireOpcode(Opcode::CMSG_LEAVE_CHANNEL));
|
||||
packet.writeString(channelName);
|
||||
LOG_DEBUG("[Classic] Built CMSG_LEAVE_CHANNEL: channel=", channelName);
|
||||
return packet;
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// Classic guild roster parser
|
||||
// Differences from WotLK:
|
||||
|
|
|
|||
|
|
@ -1233,6 +1233,10 @@ bool MessageChatParser::parse(network::Packet& packet, MessageChatData& data) {
|
|||
for (uint32_t i = 0; i < messageLen; ++i) {
|
||||
data.message[i] = static_cast<char>(packet.readUInt8());
|
||||
}
|
||||
// Strip trailing null terminator (servers include it in messageLen)
|
||||
if (!data.message.empty() && data.message.back() == '\0') {
|
||||
data.message.pop_back();
|
||||
}
|
||||
}
|
||||
|
||||
// Read chat tag
|
||||
|
|
|
|||
|
|
@ -34,6 +34,8 @@
|
|||
#include <filesystem>
|
||||
#include <fstream>
|
||||
#include <cctype>
|
||||
#include <chrono>
|
||||
#include <ctime>
|
||||
#include <unordered_set>
|
||||
|
||||
namespace {
|
||||
|
|
@ -200,6 +202,12 @@ void GameScreen::render(game::GameHandler& gameHandler) {
|
|||
}
|
||||
}
|
||||
|
||||
// Sync chat auto-join settings to GameHandler
|
||||
gameHandler.chatAutoJoin.general = chatAutoJoinGeneral_;
|
||||
gameHandler.chatAutoJoin.trade = chatAutoJoinTrade_;
|
||||
gameHandler.chatAutoJoin.localDefense = chatAutoJoinLocalDefense_;
|
||||
gameHandler.chatAutoJoin.lfg = chatAutoJoinLFG_;
|
||||
|
||||
// Process targeting input before UI windows
|
||||
processTargetInput(gameHandler);
|
||||
|
||||
|
|
@ -562,6 +570,10 @@ void GameScreen::renderChatWindow(game::GameHandler& gameHandler) {
|
|||
// Chat history
|
||||
const auto& chatHistory = gameHandler.getChatHistory();
|
||||
|
||||
// Apply chat font size scaling
|
||||
float chatScale = chatFontSize_ == 0 ? 0.85f : (chatFontSize_ == 2 ? 1.2f : 1.0f);
|
||||
ImGui::SetWindowFontScale(chatScale);
|
||||
|
||||
ImGui::BeginChild("ChatHistory", ImVec2(0, -70), true, ImGuiWindowFlags_HorizontalScrollbar);
|
||||
bool chatHistoryHovered = ImGui::IsWindowHovered(ImGuiHoveredFlags_AllowWhenBlockedByActiveItem);
|
||||
|
||||
|
|
@ -847,20 +859,54 @@ void GameScreen::renderChatWindow(game::GameHandler& gameHandler) {
|
|||
|
||||
ImVec4 color = getChatTypeColor(msg.type);
|
||||
|
||||
// Optional timestamp prefix
|
||||
std::string tsPrefix;
|
||||
if (chatShowTimestamps_) {
|
||||
auto tt = std::chrono::system_clock::to_time_t(msg.timestamp);
|
||||
std::tm tm{};
|
||||
localtime_r(&tt, &tm);
|
||||
char tsBuf[16];
|
||||
snprintf(tsBuf, sizeof(tsBuf), "[%02d:%02d] ", tm.tm_hour, tm.tm_min);
|
||||
tsPrefix = tsBuf;
|
||||
}
|
||||
|
||||
if (msg.type == game::ChatType::SYSTEM) {
|
||||
if (!tsPrefix.empty()) {
|
||||
ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(0.6f, 0.6f, 0.6f, 1.0f));
|
||||
ImGui::TextWrapped("%s", tsPrefix.c_str());
|
||||
ImGui::PopStyleColor();
|
||||
ImGui::SameLine(0, 0);
|
||||
}
|
||||
renderTextWithLinks(msg.message, color);
|
||||
} else if (msg.type == game::ChatType::TEXT_EMOTE) {
|
||||
if (!tsPrefix.empty()) {
|
||||
ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(0.6f, 0.6f, 0.6f, 1.0f));
|
||||
ImGui::TextWrapped("%s", tsPrefix.c_str());
|
||||
ImGui::PopStyleColor();
|
||||
ImGui::SameLine(0, 0);
|
||||
}
|
||||
renderTextWithLinks(msg.message, color);
|
||||
} else if (!msg.senderName.empty()) {
|
||||
if (msg.type == game::ChatType::MONSTER_SAY || msg.type == game::ChatType::MONSTER_YELL) {
|
||||
std::string prefix = msg.senderName + " says: ";
|
||||
std::string prefix = tsPrefix + msg.senderName + " says: ";
|
||||
ImGui::PushStyleColor(ImGuiCol_Text, color);
|
||||
ImGui::TextWrapped("%s", prefix.c_str());
|
||||
ImGui::PopStyleColor();
|
||||
ImGui::SameLine(0, 0);
|
||||
renderTextWithLinks(msg.message, color);
|
||||
} else if (msg.type == game::ChatType::CHANNEL && !msg.channelName.empty()) {
|
||||
int chIdx = gameHandler.getChannelIndex(msg.channelName);
|
||||
std::string chDisplay = chIdx > 0
|
||||
? "[" + std::to_string(chIdx) + ". " + msg.channelName + "]"
|
||||
: "[" + msg.channelName + "]";
|
||||
std::string prefix = tsPrefix + chDisplay + " [" + msg.senderName + "]: ";
|
||||
ImGui::PushStyleColor(ImGuiCol_Text, color);
|
||||
ImGui::TextWrapped("%s", prefix.c_str());
|
||||
ImGui::PopStyleColor();
|
||||
ImGui::SameLine(0, 0);
|
||||
renderTextWithLinks(msg.message, color);
|
||||
} else {
|
||||
std::string prefix = "[" + std::string(getChatTypeName(msg.type)) + "] " + msg.senderName + ": ";
|
||||
std::string prefix = tsPrefix + "[" + std::string(getChatTypeName(msg.type)) + "] " + msg.senderName + ": ";
|
||||
ImGui::PushStyleColor(ImGuiCol_Text, color);
|
||||
ImGui::TextWrapped("%s", prefix.c_str());
|
||||
ImGui::PopStyleColor();
|
||||
|
|
@ -868,7 +914,7 @@ void GameScreen::renderChatWindow(game::GameHandler& gameHandler) {
|
|||
renderTextWithLinks(msg.message, color);
|
||||
}
|
||||
} else {
|
||||
std::string prefix = "[" + std::string(getChatTypeName(msg.type)) + "] ";
|
||||
std::string prefix = tsPrefix + "[" + std::string(getChatTypeName(msg.type)) + "] ";
|
||||
ImGui::PushStyleColor(ImGuiCol_Text, color);
|
||||
ImGui::TextWrapped("%s", prefix.c_str());
|
||||
ImGui::PopStyleColor();
|
||||
|
|
@ -884,6 +930,9 @@ void GameScreen::renderChatWindow(game::GameHandler& gameHandler) {
|
|||
|
||||
ImGui::EndChild();
|
||||
|
||||
// Reset font scale after chat history
|
||||
ImGui::SetWindowFontScale(1.0f);
|
||||
|
||||
ImGui::Spacing();
|
||||
ImGui::Separator();
|
||||
ImGui::Spacing();
|
||||
|
|
@ -5464,6 +5513,59 @@ void GameScreen::renderSettingsWindow() {
|
|||
ImGui::EndTabItem();
|
||||
}
|
||||
|
||||
// ============================================================
|
||||
// CHAT TAB
|
||||
// ============================================================
|
||||
if (ImGui::BeginTabItem("Chat")) {
|
||||
ImGui::Spacing();
|
||||
|
||||
ImGui::Text("Appearance");
|
||||
ImGui::Separator();
|
||||
|
||||
if (ImGui::Checkbox("Show Timestamps", &chatShowTimestamps_)) {
|
||||
saveSettings();
|
||||
}
|
||||
ImGui::SetItemTooltip("Show [HH:MM] before each chat message");
|
||||
|
||||
const char* fontSizes[] = { "Small", "Medium", "Large" };
|
||||
if (ImGui::Combo("Chat Font Size", &chatFontSize_, fontSizes, 3)) {
|
||||
saveSettings();
|
||||
}
|
||||
|
||||
ImGui::Spacing();
|
||||
ImGui::Spacing();
|
||||
ImGui::Text("Auto-Join Channels");
|
||||
ImGui::Separator();
|
||||
|
||||
if (ImGui::Checkbox("General", &chatAutoJoinGeneral_)) saveSettings();
|
||||
if (ImGui::Checkbox("Trade", &chatAutoJoinTrade_)) saveSettings();
|
||||
if (ImGui::Checkbox("LocalDefense", &chatAutoJoinLocalDefense_)) saveSettings();
|
||||
if (ImGui::Checkbox("LookingForGroup", &chatAutoJoinLFG_)) saveSettings();
|
||||
|
||||
ImGui::Spacing();
|
||||
ImGui::Spacing();
|
||||
ImGui::Text("Joined Channels");
|
||||
ImGui::Separator();
|
||||
|
||||
ImGui::TextDisabled("Use /join and /leave commands in chat to manage channels.");
|
||||
|
||||
ImGui::Spacing();
|
||||
ImGui::Separator();
|
||||
ImGui::Spacing();
|
||||
|
||||
if (ImGui::Button("Restore Chat Defaults", ImVec2(-1, 0))) {
|
||||
chatShowTimestamps_ = false;
|
||||
chatFontSize_ = 1;
|
||||
chatAutoJoinGeneral_ = true;
|
||||
chatAutoJoinTrade_ = true;
|
||||
chatAutoJoinLocalDefense_ = true;
|
||||
chatAutoJoinLFG_ = true;
|
||||
saveSettings();
|
||||
}
|
||||
|
||||
ImGui::EndTabItem();
|
||||
}
|
||||
|
||||
ImGui::EndTabBar();
|
||||
}
|
||||
|
||||
|
|
@ -5920,6 +6022,12 @@ void GameScreen::saveSettings() {
|
|||
|
||||
// Chat
|
||||
out << "chat_active_tab=" << activeChatTab_ << "\n";
|
||||
out << "chat_timestamps=" << (chatShowTimestamps_ ? 1 : 0) << "\n";
|
||||
out << "chat_font_size=" << chatFontSize_ << "\n";
|
||||
out << "chat_autojoin_general=" << (chatAutoJoinGeneral_ ? 1 : 0) << "\n";
|
||||
out << "chat_autojoin_trade=" << (chatAutoJoinTrade_ ? 1 : 0) << "\n";
|
||||
out << "chat_autojoin_localdefense=" << (chatAutoJoinLocalDefense_ ? 1 : 0) << "\n";
|
||||
out << "chat_autojoin_lfg=" << (chatAutoJoinLFG_ ? 1 : 0) << "\n";
|
||||
|
||||
LOG_INFO("Settings saved to ", path);
|
||||
}
|
||||
|
|
@ -5973,6 +6081,12 @@ void GameScreen::loadSettings() {
|
|||
else if (key == "invert_mouse") pendingInvertMouse = (std::stoi(val) != 0);
|
||||
// Chat
|
||||
else if (key == "chat_active_tab") activeChatTab_ = std::clamp(std::stoi(val), 0, 3);
|
||||
else if (key == "chat_timestamps") chatShowTimestamps_ = (std::stoi(val) != 0);
|
||||
else if (key == "chat_font_size") chatFontSize_ = std::clamp(std::stoi(val), 0, 2);
|
||||
else if (key == "chat_autojoin_general") chatAutoJoinGeneral_ = (std::stoi(val) != 0);
|
||||
else if (key == "chat_autojoin_trade") chatAutoJoinTrade_ = (std::stoi(val) != 0);
|
||||
else if (key == "chat_autojoin_localdefense") chatAutoJoinLocalDefense_ = (std::stoi(val) != 0);
|
||||
else if (key == "chat_autojoin_lfg") chatAutoJoinLFG_ = (std::stoi(val) != 0);
|
||||
} catch (...) {}
|
||||
}
|
||||
LOG_INFO("Settings loaded from ", path);
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue