From af3df4c6363653c0ef303d326e0c92114210838d Mon Sep 17 00:00:00 2001 From: DrPerkyLegit Date: Thu, 9 Apr 2026 20:54:16 -0400 Subject: [PATCH] html character serialization, normal color format support --- Minecraft.Client/ClientConnection.cpp | 13 ++-- Minecraft.Client/Common/Consoles_App.cpp | 81 ++++++++++++++++++++++++ Minecraft.Client/Common/Consoles_App.h | 4 +- Minecraft.Client/PlayerConnection.cpp | 2 +- Minecraft.World/SharedConstants.h | 2 +- 5 files changed, 91 insertions(+), 11 deletions(-) diff --git a/Minecraft.Client/ClientConnection.cpp b/Minecraft.Client/ClientConnection.cpp index 691b3d576..95b9d9fb2 100644 --- a/Minecraft.Client/ClientConnection.cpp +++ b/Minecraft.Client/ClientConnection.cpp @@ -1561,21 +1561,18 @@ void ClientConnection::handleChat(shared_ptr packet) switch(packet->m_messageType) { case ChatPacket::e_ChatCustom: - if (stringArgsSize > 1) { + if (stringArgsSize >= 1) { message = packet->m_stringArgs[0]; - for (int i = 1; i < stringArgsSize; i++) { //skip the first string index, thats main message - message = replaceAll(message, L"{*ARGS_" + std::to_wstring(i) + L"*}", packet->m_stringArgs[i]); - } - std::wsmatch match; while (std::regex_search(message, match, IDS_Pattern)) { message = replaceAll(message, match[0], app.GetString(std::stoi(match[1].str()))); } - message = app.FormatHTMLString(m_userIndex, message); - } else if (packet->m_stringArgs.size() == 1) { - message = (packet->m_stringArgs.size() >= 1) ? packet->m_stringArgs[0] : L""; + message = app.EscapeHTMLString(message); //do this to enforce escaped string + message = app.FormatChatMessage(message); //this needs to be last cause it converts colors to html colors that would have been escaped + } else { + message = L"empty message"; } break; case ChatPacket::e_ChatBedOccupied: diff --git a/Minecraft.Client/Common/Consoles_App.cpp b/Minecraft.Client/Common/Consoles_App.cpp index 0a2fd159a..c736e545b 100644 --- a/Minecraft.Client/Common/Consoles_App.cpp +++ b/Minecraft.Client/Common/Consoles_App.cpp @@ -6595,6 +6595,87 @@ wstring CMinecraftApp::FormatHTMLString(int iPad, const wstring &desc, int shado return text; } +//found list of html escapes at https://stackoverflow.com/questions/7381974/which-characters-need-to-be-escaped-in-html +wstring CMinecraftApp::EscapeHTMLString(const wstring& desc) +{ + static std::unordered_map replacementMap = { + {L'&', L"&"}, + {L'<', L"<"}, + {L'>', L">"}, + {L'\"', L"""}, + {L'\'', L"'"}, + }; + + wstring finalString = L""; + for (int i = 0; i < desc.size(); i++) { + wchar_t _char = desc[i]; + auto it = replacementMap.find(_char); + + if (it != replacementMap.end()) finalString += it->second; + else finalString += _char; + } + + return finalString; +} + +wstring CMinecraftApp::FormatChatMessage(const wstring& desc) +{ + static std::wstring_view colorFormatString = L""; + + wstring results = desc; + wchar_t replacements[64]; + + swprintf(replacements, 64, colorFormatString.data(), GetHTMLColour(eHTMLColor_0), 0xFFFFFFFF); + results = replaceAll(results, L"§0", replacements); + + swprintf(replacements, 64, colorFormatString.data(), GetHTMLColour(eHTMLColor_1), 0xFFFFFFFF); + results = replaceAll(results, L"§1", replacements); + + swprintf(replacements, 64, colorFormatString.data(), GetHTMLColour(eHTMLColor_2), 0xFFFFFFFF); + results = replaceAll(results, L"§2", replacements); + + swprintf(replacements, 64, colorFormatString.data(), GetHTMLColour(eHTMLColor_3), 0xFFFFFFFF); + results = replaceAll(results, L"§3", replacements); + + swprintf(replacements, 64, colorFormatString.data(), GetHTMLColour(eHTMLColor_4), 0xFFFFFFFF); + results = replaceAll(results, L"§4", replacements); + + swprintf(replacements, 64, colorFormatString.data(), GetHTMLColour(eHTMLColor_5), 0xFFFFFFFF); + results = replaceAll(results, L"§5", replacements); + + swprintf(replacements, 64, colorFormatString.data(), GetHTMLColour(eHTMLColor_6), 0xFFFFFFFF); + results = replaceAll(results, L"§6", replacements); + + swprintf(replacements, 64, colorFormatString.data(), GetHTMLColour(eHTMLColor_7), 0xFFFFFFFF); + results = replaceAll(results, L"§7", replacements); + + swprintf(replacements, 64, colorFormatString.data(), GetHTMLColour(eHTMLColor_8), 0xFFFFFFFF); + results = replaceAll(results, L"§8", replacements); + + swprintf(replacements, 64, colorFormatString.data(), GetHTMLColour(eHTMLColor_9), 0xFFFFFFFF); + results = replaceAll(results, L"§9", replacements); + + swprintf(replacements, 64, colorFormatString.data(), GetHTMLColour(eHTMLColor_a), 0xFFFFFFFF); + results = replaceAll(results, L"§a", replacements); + + swprintf(replacements, 64, colorFormatString.data(), GetHTMLColour(eHTMLColor_b), 0xFFFFFFFF); + results = replaceAll(results, L"§b", replacements); + + swprintf(replacements, 64, colorFormatString.data(), GetHTMLColour(eHTMLColor_c), 0xFFFFFFFF); + results = replaceAll(results, L"§c", replacements); + + swprintf(replacements, 64, colorFormatString.data(), GetHTMLColour(eHTMLColor_d), 0xFFFFFFFF); + results = replaceAll(results, L"§d", replacements); + + swprintf(replacements, 64, colorFormatString.data(), GetHTMLColour(eHTMLColor_e), 0xFFFFFFFF); + results = replaceAll(results, L"§e", replacements); + + swprintf(replacements, 64, colorFormatString.data(), GetHTMLColour(eHTMLColor_f), 0xFFFFFFFF); + results = replaceAll(results, L"§f", replacements); + + return results; +} + wstring CMinecraftApp::GetActionReplacement(int iPad, unsigned char ucAction) { unsigned int input = InputManager.GetGameJoypadMaps(InputManager.GetJoypadMapVal(iPad) ,ucAction); diff --git a/Minecraft.Client/Common/Consoles_App.h b/Minecraft.Client/Common/Consoles_App.h index 0c1c261ef..7634e5976 100644 --- a/Minecraft.Client/Common/Consoles_App.h +++ b/Minecraft.Client/Common/Consoles_App.h @@ -564,7 +564,9 @@ public: int GetHTMLColour(eMinecraftColour colour); int GetHTMLColor(eMinecraftColour colour) { return GetHTMLColour(colour); } int GetHTMLFontSize(EHTMLFontSize size); - wstring FormatHTMLString(int iPad, const wstring &desc, int shadowColour = 0xFFFFFFFF); + wstring FormatHTMLString(int iPad, const wstring& desc, int shadowColour = 0xFFFFFFFF); + wstring EscapeHTMLString(const wstring &desc); + wstring FormatChatMessage(const wstring& desc); wstring GetActionReplacement(int iPad, unsigned char ucAction); wstring GetVKReplacement(unsigned int uiVKey); wstring GetIconReplacement(unsigned int uiIcon); diff --git a/Minecraft.Client/PlayerConnection.cpp b/Minecraft.Client/PlayerConnection.cpp index 546b77d82..1fb7c3988 100644 --- a/Minecraft.Client/PlayerConnection.cpp +++ b/Minecraft.Client/PlayerConnection.cpp @@ -678,7 +678,7 @@ void PlayerConnection::handleChat(shared_ptr packet) handleCommand(message); return; } - wstring formatted = L"<" + player->name + L"> " + message; + wstring formatted = L"<" + player->name + L"> " + message; server->getPlayers()->broadcastAll(shared_ptr(new ChatPacket(formatted))); chatSpamTickCount += SharedConstants::TICKS_PER_SECOND; if (chatSpamTickCount > SharedConstants::TICKS_PER_SECOND * 10) diff --git a/Minecraft.World/SharedConstants.h b/Minecraft.World/SharedConstants.h index a8924e47c..0c91379cf 100644 --- a/Minecraft.World/SharedConstants.h +++ b/Minecraft.World/SharedConstants.h @@ -20,7 +20,7 @@ class SharedConstants static wstring readAcceptableChars(); public: - static const int maxChatLength = 100; + static const int maxChatLength = 1024; static wstring acceptableLetters; static const int ILLEGAL_FILE_CHARACTERS_LENGTH = 15;