From b914ed8ac81c7722d54791c6bc2d8369401fb114 Mon Sep 17 00:00:00 2001 From: urban Date: Sat, 7 Mar 2026 17:13:40 -0500 Subject: [PATCH] fix text chat --- Minecraft.Client/ChatScreen.cpp | 49 +++++++++++++++--- Minecraft.Client/ChatScreen.h | 1 + Minecraft.Client/ClientConnection.cpp | 6 +++ Minecraft.Client/Common/UI/UIScene_HUD.cpp | 23 +++------ Minecraft.Client/Gui.cpp | 9 +--- Minecraft.Client/Minecraft.cpp | 5 +- Minecraft.Client/PlayerConnection.cpp | 48 +++++++---------- Minecraft.Client/Screen.cpp | 5 ++ Minecraft.Client/Screen.h | 1 + .../Windows64/Windows64_Minecraft.cpp | 51 +++++++++++++++++++ 10 files changed, 136 insertions(+), 62 deletions(-) diff --git a/Minecraft.Client/ChatScreen.cpp b/Minecraft.Client/ChatScreen.cpp index b68e6cac..78c0504c 100644 --- a/Minecraft.Client/ChatScreen.cpp +++ b/Minecraft.Client/ChatScreen.cpp @@ -9,6 +9,7 @@ const wstring ChatScreen::allowedChars = SharedConstants::acceptableLetters; ChatScreen::ChatScreen() { frame = 0; + caretPos = 0; } void ChatScreen::init() @@ -47,18 +48,46 @@ void ChatScreen::keyPressed(wchar_t ch, int eventKey) minecraft->setScreen(NULL); return; } - if (eventKey == Keyboard::KEY_BACK && message.length() > 0) message = message.substr(0, message.length() - 1); - if (allowedChars.find(ch) >= 0 && message.length() < SharedConstants::maxChatLength) + if (eventKey == Keyboard::KEY_LEFT) + { + if (caretPos > 0) + { + caretPos--; + } + return; + } + if (eventKey == Keyboard::KEY_RIGHT) + { + if (caretPos < message.length()) + { + caretPos++; + } + return; + } + if (eventKey == Keyboard::KEY_BACK && caretPos > 0 && message.length() > 0) + { + message.erase(caretPos - 1, 1); + caretPos--; + return; + } + if (ch >= 32 && SharedConstants::acceptableLetters.find(ch) != wstring::npos && message.length() < SharedConstants::maxChatLength) { - message += ch; + message.insert(caretPos, 1, ch); + caretPos++; } } void ChatScreen::render(int xm, int ym, float a) { + wstring displayMessage = L"> " + message; + if (frame / 6 % 2 == 0) + { + displayMessage.insert(2 + caretPos, 1, L'_'); + } + fill(2, height - 14, width - 2, height - 2, 0x80000000); - drawString(font, L"> " + message + (frame / 6 % 2 == 0 ? L"_" : L""), 4, height - 12, 0xe0e0e0); + drawString(font, displayMessage, 4, height - 12, 0xe0e0e0); Screen::render(xm, ym, a); } @@ -69,15 +98,21 @@ void ChatScreen::mouseClicked(int x, int y, int buttonNum) { if (minecraft->gui->selectedName != L"") // 4J - was NULL comparison { - if (message.length() > 0 && message[message.length()-1]!=L' ') + if (caretPos > 0 && message[caretPos - 1] != L' ') { - message += L" "; + message.insert(caretPos, 1, L' '); + caretPos++; } - message += minecraft->gui->selectedName; + message.insert(caretPos, minecraft->gui->selectedName); + caretPos += (unsigned int)minecraft->gui->selectedName.length(); unsigned int maxLength = SharedConstants::maxChatLength; if (message.length() > maxLength) { message = message.substr(0, maxLength); + if (caretPos > message.length()) + { + caretPos = (unsigned int)message.length(); + } } } else diff --git a/Minecraft.Client/ChatScreen.h b/Minecraft.Client/ChatScreen.h index d7158478..2c755b04 100644 --- a/Minecraft.Client/ChatScreen.h +++ b/Minecraft.Client/ChatScreen.h @@ -8,6 +8,7 @@ class ChatScreen : public Screen wstring message; private: int frame; + unsigned int caretPos; public: ChatScreen(); //4J added diff --git a/Minecraft.Client/ClientConnection.cpp b/Minecraft.Client/ClientConnection.cpp index c9807294..44236774 100644 --- a/Minecraft.Client/ClientConnection.cpp +++ b/Minecraft.Client/ClientConnection.cpp @@ -1598,6 +1598,12 @@ void ClientConnection::handleChat(shared_ptr packet) message = replaceAll(message,L"{*PLAYER*}",playerDisplayName); break; + case ChatPacket::e_ChatCustom: + // Raw text message (e.g. " message") — use first string arg directly + if (!packet->m_stringArgs.empty()) + message = packet->m_stringArgs[0]; + break; + default: message = playerDisplayName; break; diff --git a/Minecraft.Client/Common/UI/UIScene_HUD.cpp b/Minecraft.Client/Common/UI/UIScene_HUD.cpp index 27eeb76b..24c8d944 100644 --- a/Minecraft.Client/Common/UI/UIScene_HUD.cpp +++ b/Minecraft.Client/Common/UI/UIScene_HUD.cpp @@ -656,25 +656,16 @@ void UIScene_HUD::handleTimerComplete(int id) if(pMinecraft->localplayers[m_iPad]!= NULL) { Gui *pGui = pMinecraft->gui; - //DWORD messagesToDisplay = min( CHAT_LINES_COUNT, pGui->getMessagesCount(m_iPad) ); + + // Chat messages are now rendered exclusively by Gui::render() (OpenGL path). + // Disable the Iggy/flash chat labels in the HUD scene to avoid duplicate rendering. for( unsigned int i = 0; i < CHAT_LINES_COUNT; ++i ) { - float opacity = pGui->getOpacity(m_iPad, i); - if( opacity > 0 ) - { - m_controlLabelBackground[i].setOpacity(opacity); - m_labelChatText[i].setOpacity(opacity); - m_labelChatText[i].setLabel( pGui->getMessagesCount(m_iPad) ? pGui->getMessage(m_iPad,i) : L"" ); - - anyVisible = true; - } - else - { - m_controlLabelBackground[i].setOpacity(0); - m_labelChatText[i].setOpacity(0); - m_labelChatText[i].setLabel(L""); - } + m_controlLabelBackground[i].setOpacity(0); + m_labelChatText[i].setOpacity(0); + m_labelChatText[i].setLabel(L""); } + if(pGui->getJukeboxOpacity(m_iPad) > 0) anyVisible = true; m_labelJukebox.setOpacity( pGui->getJukeboxOpacity(m_iPad) ); m_labelJukebox.setLabel( pGui->getJukeboxMessage(m_iPad) ); diff --git a/Minecraft.Client/Gui.cpp b/Minecraft.Client/Gui.cpp index 7d76edbd..f3236d5c 100644 --- a/Minecraft.Client/Gui.cpp +++ b/Minecraft.Client/Gui.cpp @@ -906,13 +906,9 @@ void Gui::render(float a, bool mouseFree, int xMouse, int yMouse) glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); glDisable(GL_ALPHA_TEST); -// 4J Stu - We have moved the chat text to a xui -#if 0 glPushMatrix(); - // 4J-PB we need to move this up a bit because we've moved the quick select - //glTranslatef(0, ((float)screenHeight) - 48, 0); - glTranslatef(0.0f, (float)(screenHeight - iSafezoneYHalf - iTooltipsYOffset - 16 - 3 + 22) - 24.0f, 0.0f); - // glScalef(1.0f / ssc.scale, 1.0f / ssc.scale, 1); + // Position chat messages just above the armor/health bar row (40px above toolbar top) + glTranslatef(0.0f, (float)(screenHeight - iSafezoneYHalf - iTooltipsYOffset) - 40.0f, 0.0f); // 4J-PB - we need gui messages for each of the possible 4 splitscreen players if(bDisplayGui) @@ -951,7 +947,6 @@ void Gui::render(float a, bool mouseFree, int xMouse, int yMouse) } } glPopMatrix(); -#endif // 4J Stu - Copied over but not used #if 0 diff --git a/Minecraft.Client/Minecraft.cpp b/Minecraft.Client/Minecraft.cpp index c189431e..cff08285 100644 --- a/Minecraft.Client/Minecraft.cpp +++ b/Minecraft.Client/Minecraft.cpp @@ -3598,10 +3598,13 @@ void Minecraft::tick(bool bFirst, bool bUpdateTextures) // #endif - if(Keyboard::isKeyDown(options->keyChat->key)) + static bool s_chatKeyWasDown = false; + bool chatKeyDown = Keyboard::isKeyDown(options->keyChat->key) || g_KBMInput.IsKeyDown('T'); + if (screen == NULL && chatKeyDown && !s_chatKeyWasDown) { setScreen(new ChatScreen()); } + s_chatKeyWasDown = chatKeyDown; #if 0 // 4J - TODO - some replacement for input handling... diff --git a/Minecraft.Client/PlayerConnection.cpp b/Minecraft.Client/PlayerConnection.cpp index f0e538a8..89eddbf3 100644 --- a/Minecraft.Client/PlayerConnection.cpp +++ b/Minecraft.Client/PlayerConnection.cpp @@ -126,7 +126,7 @@ void PlayerConnection::disconnect(DisconnectPacket::eDisconnectReason reason) send( shared_ptr( new DisconnectPacket(reason) )); connection->sendAndQuit(); // 4J-PB - removed, since it needs to be localised in the language the client is in - //server->players->broadcastAll( shared_ptr( new ChatPacket(L"§e" + player->name + L" left the game.") ) ); + //server->players->broadcastAll( shared_ptr( new ChatPacket(L"�e" + player->name + L" left the game.") ) ); if(getWasKicked()) { server->getPlayers()->broadcastAll( shared_ptr( new ChatPacket(player->name, ChatPacket::e_ChatPlayerKickedFromGame) ) ); @@ -569,7 +569,7 @@ void PlayerConnection::onDisconnect(DisconnectPacket::eDisconnectReason reason, if( done ) return; // logger.info(player.name + " lost connection: " + reason); // 4J-PB - removed, since it needs to be localised in the language the client is in - //server->players->broadcastAll( shared_ptr( new ChatPacket(L"§e" + player->name + L" left the game.") ) ); + //server->players->broadcastAll( shared_ptr( new ChatPacket(L"�e" + player->name + L" left the game.") ) ); if(getWasKicked()) { server->getPlayers()->broadcastAll( shared_ptr( new ChatPacket(player->name, ChatPacket::e_ChatPlayerKickedFromGame) ) ); @@ -636,38 +636,24 @@ void PlayerConnection::handleSetCarriedItem(shared_ptr pac void PlayerConnection::handleChat(shared_ptr packet) { - // 4J - TODO -#if 0 - wstring message = packet->message; + if (packet->m_stringArgs.empty()) return; + + wstring message = packet->m_stringArgs[0]; if (message.length() > SharedConstants::maxChatLength) { - disconnect(L"Chat message too long"); return; } - message = message.trim(); - for (int i = 0; i < message.length(); i++) - { - if (SharedConstants.acceptableLetters.indexOf(message.charAt(i)) < 0 && (int) message.charAt(i) < 32) - { - disconnect(L"Illegal characters in chat"); - return; - } - } - if (message.startsWith("/")) - { - handleCommand(message); - } else { - message = "<" + player.name + "> " + message; - logger.info(message); - server.players.broadcastAll(new ChatPacket(message)); - } - chatSpamTickCount += SharedConstants::TICKS_PER_SECOND; - if (chatSpamTickCount > SharedConstants::TICKS_PER_SECOND * 10) - { - disconnect("disconnect.spam"); - } -#endif + // Trim leading/trailing spaces + size_t start = message.find_first_not_of(L' '); + size_t end = message.find_last_not_of(L' '); + if (start == wstring::npos) return; + message = message.substr(start, end - start + 1); + if (message.empty()) return; + + // Format as " message" and broadcast to all clients + wstring formatted = L"<" + player->name + L"> " + message; + server->getPlayers()->broadcastAll(shared_ptr(new ChatPacket(formatted))); } void PlayerConnection::handleCommand(const wstring& message) @@ -740,13 +726,13 @@ int PlayerConnection::countDelayedPackets() void PlayerConnection::info(const wstring& string) { // 4J-PB - removed, since it needs to be localised in the language the client is in - //send( shared_ptr( new ChatPacket(L"§7" + string) ) ); + //send( shared_ptr( new ChatPacket(L"�7" + string) ) ); } void PlayerConnection::warn(const wstring& string) { // 4J-PB - removed, since it needs to be localised in the language the client is in - //send( shared_ptr( new ChatPacket(L"§9" + string) ) ); + //send( shared_ptr( new ChatPacket(L"�9" + string) ) ); } wstring PlayerConnection::getConsoleName() diff --git a/Minecraft.Client/Screen.cpp b/Minecraft.Client/Screen.cpp index 0019b54d..81a02ed3 100644 --- a/Minecraft.Client/Screen.cpp +++ b/Minecraft.Client/Screen.cpp @@ -29,6 +29,11 @@ void Screen::render(int xm, int ym, float a) } } +void Screen::injectKeyPressed(wchar_t eventCharacter, int eventKey) +{ + keyPressed(eventCharacter, eventKey); +} + void Screen::keyPressed(wchar_t eventCharacter, int eventKey) { if (eventKey == Keyboard::KEY_ESCAPE) diff --git a/Minecraft.Client/Screen.h b/Minecraft.Client/Screen.h index 50deb1d0..a6aabdfb 100644 --- a/Minecraft.Client/Screen.h +++ b/Minecraft.Client/Screen.h @@ -23,6 +23,7 @@ class Screen : public GuiComponent Screen(); // 4J added virtual void render(int xm, int ym, float a); + void injectKeyPressed(wchar_t eventCharacter, int eventKey); protected: virtual void keyPressed(wchar_t eventCharacter, int eventKey); public: diff --git a/Minecraft.Client/Windows64/Windows64_Minecraft.cpp b/Minecraft.Client/Windows64/Windows64_Minecraft.cpp index 5647f071..da1c3a93 100644 --- a/Minecraft.Client/Windows64/Windows64_Minecraft.cpp +++ b/Minecraft.Client/Windows64/Windows64_Minecraft.cpp @@ -25,6 +25,7 @@ #include "..\..\Minecraft.World\ThreadName.h" #include "..\..\Minecraft.Client\StatsCounter.h" #include "..\ConnectScreen.h" +#include "..\Screen.h" //#include "Social\SocialManager.h" //#include "Leaderboards\LeaderboardManager.h" //#include "XUI\XUI_Scene_Container.h" @@ -301,6 +302,19 @@ HWND GetMinecraftWindowHWND() return g_hWnd; } +static int KeyboardConstFromVK(int vk) +{ + for (int keyConst = Keyboard::KEY_A; keyConst <= Keyboard::KEY_RIGHT; ++keyConst) + { + if (Keyboard::toVK(keyConst) == vk) + { + return keyConst; + } + } + + return -1; +} + static bool g_isFullscreen = false; static RECT g_windowedRect = {}; static LONG g_windowedStyle = 0; @@ -571,6 +585,20 @@ LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) vk = (lParam & (1 << 24)) ? VK_RCONTROL : VK_LCONTROL; else if (vk == VK_MENU) vk = (lParam & (1 << 24)) ? VK_RMENU : VK_LMENU; + Minecraft *pMinecraft = Minecraft::GetInstance(); + if (pMinecraft != NULL && pMinecraft->screen != NULL) + { + int keyConst = KeyboardConstFromVK(vk); + if (keyConst >= 0) + { + if (vk == VK_ESCAPE || vk == VK_BACK || vk == VK_UP || vk == VK_DOWN || vk == VK_LEFT || vk == VK_RIGHT || vk == VK_TAB) + { + pMinecraft->screen->injectKeyPressed(0, keyConst); + return 0; + } + } + } + g_KBMInput.OnKeyDown(vk); break; } @@ -588,6 +616,29 @@ LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) break; } + case WM_CHAR: + { + Minecraft *pMinecraft = Minecraft::GetInstance(); + if (pMinecraft != NULL && pMinecraft->screen != NULL) + { + wchar_t eventCharacter = (wchar_t)wParam; + int eventKey = 0; + + if (wParam == VK_BACK) + { + return 0; + } + else if (wParam == VK_RETURN) + { + eventKey = Keyboard::KEY_RETURN; + } + + pMinecraft->screen->injectKeyPressed(eventCharacter, eventKey); + return 0; + } + break; + } + case WM_LBUTTONDOWN: g_KBMInput.OnMouseButtonDown(KeyboardMouseInput::MOUSE_LEFT); break;