diff --git a/Minecraft.Client/ChatScreen.cpp b/Minecraft.Client/ChatScreen.cpp index 53c9072242..fffa1562fb 100644 --- a/Minecraft.Client/ChatScreen.cpp +++ b/Minecraft.Client/ChatScreen.cpp @@ -131,7 +131,7 @@ void ChatScreen::keyPressed(wchar_t ch, int eventKey) cursorIndex--; return; } - if (isAllowedChatChar(ch) && static_cast(message.length()) < SharedConstants::maxChatLength) + if (isAllowedChatChar(ch) && static_cast(message.length()) < SharedConstants::maxVisibleLength) { message.insert(cursorIndex, 1, ch); cursorIndex++; diff --git a/Minecraft.Client/ClientConnection.cpp b/Minecraft.Client/ClientConnection.cpp index a80af5d2c9..603c8a0799 100644 --- a/Minecraft.Client/ClientConnection.cpp +++ b/Minecraft.Client/ClientConnection.cpp @@ -65,6 +65,7 @@ #include "..\Minecraft.World\DurangoStats.h" #include "..\Minecraft.World\GenericStats.h" #endif +#include ClientConnection::ClientConnection(Minecraft *minecraft, const wstring& ip, int port) { @@ -1546,17 +1547,35 @@ void ClientConnection::handleChat(shared_ptr packet) bool replaceEntitySource = false; bool replaceItem = false; + static std::wregex IDS_Pattern(LR"(\{\*IDS_(\d+)\*\})"); //maybe theres a better way to do translateable IDS + + int stringArgsSize = packet->m_stringArgs.size(); + wstring playerDisplayName = L""; wstring sourceDisplayName = L""; // On platforms other than Xbox One this just sets display name to gamertag - if (packet->m_stringArgs.size() >= 1) playerDisplayName = GetDisplayNameByGamertag(packet->m_stringArgs[0]); - if (packet->m_stringArgs.size() >= 2) sourceDisplayName = GetDisplayNameByGamertag(packet->m_stringArgs[1]); + if (stringArgsSize >= 1) playerDisplayName = GetDisplayNameByGamertag(packet->m_stringArgs[0]); + if (stringArgsSize >= 2) sourceDisplayName = GetDisplayNameByGamertag(packet->m_stringArgs[1]); switch(packet->m_messageType) { case ChatPacket::e_ChatCustom: - message = (packet->m_stringArgs.size() >= 1) ? packet->m_stringArgs[0] : L""; + case ChatPacket::e_ChatActionBar: + if (stringArgsSize >= 1) { + message = packet->m_stringArgs[0]; + + 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.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"; + } + displayOnGui = (packet->m_messageType == ChatPacket::e_ChatCustom); break; case ChatPacket::e_ChatBedOccupied: message = app.GetString(IDS_TILE_BED_OCCUPIED); @@ -1906,7 +1925,7 @@ void ClientConnection::handleChat(shared_ptr packet) if(replacePlayer) { - message = replaceAll(message,L"{*PLAYER*}",playerDisplayName); + message = replaceAll(message,L"{*PLAYER*}", playerDisplayName); } if(replaceEntitySource) @@ -1941,7 +1960,9 @@ void ClientConnection::handleChat(shared_ptr packet) // flag that a message is a death message bool bIsDeathMessage = (packet->m_messageType>=ChatPacket::e_ChatDeathInFire) && (packet->m_messageType<=ChatPacket::e_ChatDeathIndirectMagicItem); - if( displayOnGui ) minecraft->gui->addMessage(message,m_userIndex, bIsDeathMessage); + if( displayOnGui ) minecraft->gui->addMessage(message, m_userIndex, bIsDeathMessage); + + if (!displayOnGui && !message.empty()) minecraft->gui->setActionBarMessage(message); } void ClientConnection::handleAnimate(shared_ptr packet) diff --git a/Minecraft.Client/Common/Consoles_App.cpp b/Minecraft.Client/Common/Consoles_App.cpp index 0a2fd159a4..21b032cef6 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, bool applyColor) +{ + static std::wstring_view colorFormatString = L""; + + wstring results = desc; + wchar_t replacements[64]; + + swprintf(replacements, 64, (applyColor ? colorFormatString.data() : L""), GetHTMLColour(eHTMLColor_0), 0xFFFFFFFF); + results = replaceAll(results, L"§0", replacements); + + swprintf(replacements, 64, (applyColor ? colorFormatString.data() : L""), GetHTMLColour(eHTMLColor_1), 0xFFFFFFFF); + results = replaceAll(results, L"§1", replacements); + + swprintf(replacements, 64, (applyColor ? colorFormatString.data() : L""), GetHTMLColour(eHTMLColor_2), 0xFFFFFFFF); + results = replaceAll(results, L"§2", replacements); + + swprintf(replacements, 64, (applyColor ? colorFormatString.data() : L""), GetHTMLColour(eHTMLColor_3), 0xFFFFFFFF); + results = replaceAll(results, L"§3", replacements); + + swprintf(replacements, 64, (applyColor ? colorFormatString.data() : L""), GetHTMLColour(eHTMLColor_4), 0xFFFFFFFF); + results = replaceAll(results, L"§4", replacements); + + swprintf(replacements, 64, (applyColor ? colorFormatString.data() : L""), GetHTMLColour(eHTMLColor_5), 0xFFFFFFFF); + results = replaceAll(results, L"§5", replacements); + + swprintf(replacements, 64, (applyColor ? colorFormatString.data() : L""), GetHTMLColour(eHTMLColor_6), 0xFFFFFFFF); + results = replaceAll(results, L"§6", replacements); + + swprintf(replacements, 64, (applyColor ? colorFormatString.data() : L""), GetHTMLColour(eHTMLColor_7), 0xFFFFFFFF); + results = replaceAll(results, L"§7", replacements); + + swprintf(replacements, 64, (applyColor ? colorFormatString.data() : L""), GetHTMLColour(eHTMLColor_8), 0xFFFFFFFF); + results = replaceAll(results, L"§8", replacements); + + swprintf(replacements, 64, (applyColor ? colorFormatString.data() : L""), GetHTMLColour(eHTMLColor_9), 0xFFFFFFFF); + results = replaceAll(results, L"§9", replacements); + + swprintf(replacements, 64, (applyColor ? colorFormatString.data() : L""), GetHTMLColour(eHTMLColor_a), 0xFFFFFFFF); + results = replaceAll(results, L"§a", replacements); + + swprintf(replacements, 64, (applyColor ? colorFormatString.data() : L""), GetHTMLColour(eHTMLColor_b), 0xFFFFFFFF); + results = replaceAll(results, L"§b", replacements); + + swprintf(replacements, 64, (applyColor ? colorFormatString.data() : L""), GetHTMLColour(eHTMLColor_c), 0xFFFFFFFF); + results = replaceAll(results, L"§c", replacements); + + swprintf(replacements, 64, (applyColor ? colorFormatString.data() : L""), GetHTMLColour(eHTMLColor_d), 0xFFFFFFFF); + results = replaceAll(results, L"§d", replacements); + + swprintf(replacements, 64, (applyColor ? colorFormatString.data() : L""), GetHTMLColour(eHTMLColor_e), 0xFFFFFFFF); + results = replaceAll(results, L"§e", replacements); + + swprintf(replacements, 64, (applyColor ? colorFormatString.data() : L""), 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 0c1c261efd..eb59ded893 100644 --- a/Minecraft.Client/Common/Consoles_App.h +++ b/Minecraft.Client/Common/Consoles_App.h @@ -564,7 +564,9 @@ class CMinecraftApp 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, bool applyColor = true); wstring GetActionReplacement(int iPad, unsigned char ucAction); wstring GetVKReplacement(unsigned int uiVKey); wstring GetIconReplacement(unsigned int uiIcon); diff --git a/Minecraft.Client/Common/UI/UIScene_HUD.cpp b/Minecraft.Client/Common/UI/UIScene_HUD.cpp index 213caa8dc6..4c8c66df42 100644 --- a/Minecraft.Client/Common/UI/UIScene_HUD.cpp +++ b/Minecraft.Client/Common/UI/UIScene_HUD.cpp @@ -23,8 +23,10 @@ UIScene_HUD::UIScene_HUD(int iPad, void *initData, UILayer *parentLayer) : UISce for(unsigned int i = 0; i < CHAT_LINES_COUNT; ++i) { m_labelChatText[i].init(L""); + IggyValueSetBooleanRS(m_labelChatText[i].getIggyValuePath(), 0, "m_bUseHtmlText", true); } m_labelJukebox.init(L""); + IggyValueSetBooleanRS(m_labelJukebox.getIggyValuePath(), 0, "m_bUseHtmlText", true); addTimer(0, 100); } diff --git a/Minecraft.Client/Common/UI/UIScene_HUD.h b/Minecraft.Client/Common/UI/UIScene_HUD.h index 04468c8ecd..caadb50afc 100644 --- a/Minecraft.Client/Common/UI/UIScene_HUD.h +++ b/Minecraft.Client/Common/UI/UIScene_HUD.h @@ -11,7 +11,7 @@ class UIScene_HUD : public UIScene, public IUIScene_HUD bool m_bSplitscreen; protected: - UIControl_Label m_labelChatText[CHAT_LINES_COUNT]; + UIControl_HTMLLabel m_labelChatText[CHAT_LINES_COUNT]; UIControl_Label m_labelJukebox; UIControl m_controlLabelBackground[CHAT_LINES_COUNT]; UIControl_Label m_labelDisplayName; diff --git a/Minecraft.Client/Gui.cpp b/Minecraft.Client/Gui.cpp index 5e3a954fe3..103bfb7bf1 100644 --- a/Minecraft.Client/Gui.cpp +++ b/Minecraft.Client/Gui.cpp @@ -1575,6 +1575,13 @@ float Gui::getOpacity(int iPad, DWORD index) return opacityPercentage; } +//just like java functionality it overwrites the jukebox label +void Gui::setActionBarMessage(wstring message) +{ + overlayMessageString = message; + overlayMessageTime = 20 * 4; //idk how long it should last, need to check java usage +} + float Gui::getJukeboxOpacity(int iPad) { float t = overlayMessageTime - lastTickA; @@ -1590,7 +1597,7 @@ void Gui::setNowPlaying(const wstring& string) // overlayMessageString = L"Now playing: " + string; overlayMessageString = app.GetString(IDS_NOWPLAYING) + string; overlayMessageTime = 20 * 3; - animateOverlayMessageColor = true; + animateOverlayMessageColor = true; //appears to be unused, @DrPerkyLegit plans to add in later pr } void Gui::displayClientMessage(int messageId, int iPad) diff --git a/Minecraft.Client/Gui.h b/Minecraft.Client/Gui.h index 64b8dfbe8e..5397823bc4 100644 --- a/Minecraft.Client/Gui.h +++ b/Minecraft.Client/Gui.h @@ -63,6 +63,8 @@ class Gui : public GuiComponent wstring getMessage(int iPad, DWORD index) { return guiMessages[iPad].at(index).string; } float getOpacity(int iPad, DWORD index); + void setActionBarMessage(wstring message); //uses jukebox label + wstring getJukeboxMessage(int iPad) { return overlayMessageString; } float getJukeboxOpacity(int iPad); diff --git a/Minecraft.Client/PlayerConnection.cpp b/Minecraft.Client/PlayerConnection.cpp index 1fb7c39888..b23b699947 100644 --- a/Minecraft.Client/PlayerConnection.cpp +++ b/Minecraft.Client/PlayerConnection.cpp @@ -679,7 +679,7 @@ void PlayerConnection::handleChat(shared_ptr packet) return; } wstring formatted = L"<" + player->name + L"> " + message; - server->getPlayers()->broadcastAll(shared_ptr(new ChatPacket(formatted))); + server->getPlayers()->broadcastAll(shared_ptr(new ChatPacket(app.FormatChatMessage(formatted, false)))); chatSpamTickCount += SharedConstants::TICKS_PER_SECOND; if (chatSpamTickCount > SharedConstants::TICKS_PER_SECOND * 10) { diff --git a/Minecraft.World/ChatPacket.h b/Minecraft.World/ChatPacket.h index ea9b38061d..7ffebc5e05 100644 --- a/Minecraft.World/ChatPacket.h +++ b/Minecraft.World/ChatPacket.h @@ -98,6 +98,7 @@ class ChatPacket : public Packet, public enable_shared_from_this e_ChatCommandTeleportMe, e_ChatCommandTeleportToMe, + e_ChatActionBar, }; public: diff --git a/Minecraft.World/SharedConstants.h b/Minecraft.World/SharedConstants.h index a8924e47c0..c4db88ae81 100644 --- a/Minecraft.World/SharedConstants.h +++ b/Minecraft.World/SharedConstants.h @@ -20,7 +20,8 @@ class SharedConstants static wstring readAcceptableChars(); public: - static const int maxChatLength = 100; + static const int maxChatLength = 255; + static const int maxVisibleLength = 100; //to be changed static wstring acceptableLetters; static const int ILLEGAL_FILE_CHARACTERS_LENGTH = 15;