diff --git a/GeneralsMD/Code/GameEngine/Source/GameClient/GUI/GUICallbacks/Menus/WOLGameSetupMenu.cpp b/GeneralsMD/Code/GameEngine/Source/GameClient/GUI/GUICallbacks/Menus/WOLGameSetupMenu.cpp index 07f642111ba..d821e1070d3 100644 --- a/GeneralsMD/Code/GameEngine/Source/GameClient/GUI/GUICallbacks/Menus/WOLGameSetupMenu.cpp +++ b/GeneralsMD/Code/GameEngine/Source/GameClient/GUI/GUICallbacks/Menus/WOLGameSetupMenu.cpp @@ -974,8 +974,8 @@ static void StartPressed() if (pMesh->GetAllConnections().size() < numHumanPlayers - 1) { - UnicodeString text(L"The following players are still connecting, please try again soon:"); - GadgetListBoxAddEntryText(listboxGameSetupChat, text, GameMakeColor(255, 0, 0, 255), -1, -1); + UnicodeString text(L"Connections: Some players are still connecting. Try again shortly:"); + GadgetListBoxAddEntryText(listboxGameSetupChat, text, GameMakeColor(255, 194, 15, 255), -1, -1); int64_t myUserID = pAuthInterface->GetUserID(); @@ -997,7 +997,7 @@ static void StartPressed() if (!bFoundLobbyMemberForConnection) { UnicodeString strDisplayName(from_utf8(lobbyMember.display_name).c_str()); - GadgetListBoxAddEntryText(listboxGameSetupChat, strDisplayName, GameMakeColor(255, 0, 0, 255), -1, -1); + GadgetListBoxAddEntryText(listboxGameSetupChat, strDisplayName, GameMakeColor(255, 194, 15, 255), -1, -1); } } } @@ -1117,7 +1117,7 @@ static void StartPressed() if(isReady) { // start full mesh connection check - UnicodeString strInform = UnicodeString(L"Starting full mesh connectivity checks..."); + UnicodeString strInform = UnicodeString(L"Connections: Checking all players..."); GadgetListBoxAddEntryText(listboxGameSetupChat, strInform, GameMakeColor(255, 194, 15, 255), -1, -1); std::shared_ptr pWS = NGMP_OnlineServicesManager::GetWebSocket(); @@ -1132,7 +1132,7 @@ static void StartPressed() { if (bMeshFullyConnected) { - UnicodeString strInform = UnicodeString(L"Mesh is fully connected!"); + UnicodeString strInform = UnicodeString(L"Connections: All players are connected."); GadgetListBoxAddEntryText(listboxGameSetupChat, strInform, GameMakeColor(0, 255, 0, 255), -1, -1); // reset autostart just incase @@ -1175,14 +1175,14 @@ static void StartPressed() } else { - UnicodeString strInform = UnicodeString(L"Mesh is not fully connected yet! Please try again soon"); - GadgetListBoxAddEntryText(listboxGameSetupChat, strInform, GameMakeColor(255, 0, 0, 255), -1, -1); + UnicodeString strInform = UnicodeString(L"Connections: The player network is not ready. Try again shortly."); + GadgetListBoxAddEntryText(listboxGameSetupChat, strInform, GameMakeColor(255, 194, 15, 255), -1, -1); NGMP_OnlineServices_LobbyInterface* pLobbyInterface = NGMP_OnlineServicesManager::GetInterface(); // who is missing who? //std::list> missingConnections - GadgetListBoxAddEntryText(listboxGameSetupChat, UnicodeString(L"The following players are not yet connected: "), GameMakeColor(255, 0, 0, 255), -1, -1); + GadgetListBoxAddEntryText(listboxGameSetupChat, UnicodeString(L"Connections: Missing links:"), GameMakeColor(255, 194, 15, 255), -1, -1); for (auto& missingPair : missingConnections) { bool bFoundPlayer = false; @@ -1195,16 +1195,16 @@ static void StartPressed() bFoundPlayer = true; UnicodeString strMissingConnection; - strMissingConnection.format(L"Player %s is not connected to Player %s", from_utf8(lobbyMemberSource.display_name).c_str(), from_utf8(lobbyMemberTarget.display_name).c_str()); - GadgetListBoxAddEntryText(listboxGameSetupChat, strMissingConnection, GameMakeColor(255, 0, 0, 255), -1, -1); + strMissingConnection.format(L"%s is not connected to %s.", from_utf8(lobbyMemberSource.display_name).c_str(), from_utf8(lobbyMemberTarget.display_name).c_str()); + GadgetListBoxAddEntryText(listboxGameSetupChat, strMissingConnection, GameMakeColor(255, 194, 15, 255), -1, -1); } } if (!bFoundPlayer) // if we couldnt find a display name... show a user ID instead, better than nothing { UnicodeString strMissingConnection; - strMissingConnection.format(L"Player %lld is not connected to Player %lld", missingPair.first, missingPair.second); - GadgetListBoxAddEntryText(listboxGameSetupChat, strMissingConnection, GameMakeColor(255, 0, 0, 255), -1, -1); + strMissingConnection.format(L"Player %lld is not connected to player %lld.", missingPair.first, missingPair.second); + GadgetListBoxAddEntryText(listboxGameSetupChat, strMissingConnection, GameMakeColor(255, 194, 15, 255), -1, -1); } } @@ -1242,7 +1242,7 @@ static void StartPressed() // remote msg UnicodeString strInform = TheGameText->fetch("GUI:HostWantsToStart"); - UnicodeString strInform2 = UnicodeString(L"All players will be forced to ready up in 30 seconds"); + UnicodeString strInform2 = UnicodeString(L"Ready check: All players will be marked ready in 30 seconds."); pLobbyInterface->SendAnnouncementMessageToCurrentLobby(strInform, false); pLobbyInterface->SendAnnouncementMessageToCurrentLobby(strInform2, true); @@ -1254,8 +1254,8 @@ static void StartPressed() } else { - UnicodeString strInform = UnicodeString(L"You have already informed players you want to start. A countdown has begun after which they will be marked as ready."); - GadgetListBoxAddEntryText(listboxGameSetupChat, strInform, GameSpyColor[GSCOLOR_DEFAULT], -1, -1); + UnicodeString strInform = UnicodeString(L"Ready check: A countdown is already running. Players will be marked ready when it ends."); + GadgetListBoxAddEntryText(listboxGameSetupChat, strInform, GameMakeColor(192, 192, 192, 255), -1, -1); } #endif } @@ -1835,8 +1835,11 @@ void WOLGameSetupMenuInit( WindowLayout *layout, void *userData ) if (connState == EConnectionState::CONNECTION_FAILED) { #endif - strConnectionMessage.format(L"Connection state to %s changed to: %hs", strDisplayName.c_str(), strState.c_str()); - GadgetListBoxAddEntryText(listboxGameSetupChat, strConnectionMessage, GameMakeColor(255, 194, 15, 255), -1, -1); + strConnectionMessage.format(L"Connection: %s is now %hs.", strDisplayName.c_str(), strState.c_str()); + const Color connectionColor = connState == EConnectionState::CONNECTION_FAILED + ? GameMakeColor(255, 0, 0, 255) + : GameMakeColor(192, 192, 192, 255); + GadgetListBoxAddEntryText(listboxGameSetupChat, strConnectionMessage, connectionColor, -1, -1); #if !defined(_DEBUG) } @@ -2155,18 +2158,14 @@ void WOLGameSetupMenuInit( WindowLayout *layout, void *userData ) if (!pLobbyInterface->IsHost()) { UnicodeString strInform; - strInform.format(L"NOTE: This lobby has a customized maximum camera height / zoom level of %lu set by the host.", theLobby.max_cam_height); - GadgetListBoxAddEntryText(listboxGameSetupChat, strInform, GameSpyColor[GSCOLOR_CHAT_NORMAL], -1, -1); + strInform.format(L"Camera height: The host set the limit to %lu.", theLobby.max_cam_height); + GadgetListBoxAddEntryText(listboxGameSetupChat, strInform, GameMakeColor(192, 192, 192, 255), -1, -1); } else { UnicodeString strInform; - UnicodeString strInform2; - strInform.format(L"NOTE: This lobby has a customized maximum camera height / zoom level of %lu set as per your preference.", theLobby.max_cam_height); - strInform2.format(L"\tDo /maxcameraheight to change this (e.g. /maxcameraheight 450). Default is 310.", theLobby.max_cam_height); - - GadgetListBoxAddEntryText(listboxGameSetupChat, strInform, GameSpyColor[GSCOLOR_CHAT_NORMAL], -1, -1); - GadgetListBoxAddEntryText(listboxGameSetupChat, strInform2, GameSpyColor[GSCOLOR_CHAT_NORMAL], -1, -1); + strInform.format(L"Camera height: Your limit is %lu. Use /maxcameraheight to change it. Default: 310.", theLobby.max_cam_height); + GadgetListBoxAddEntryText(listboxGameSetupChat, strInform, GameMakeColor(192, 192, 192, 255), -1, -1); } } @@ -2176,7 +2175,7 @@ void WOLGameSetupMenuInit( WindowLayout *layout, void *userData ) { if (pLobbyInterface->IsHost()) { - GadgetListBoxAddEntryText(listboxGameSetupChat, UnicodeString(L"This lobby is open to the public. Use /friendsonly to make it only open to friends."), GameMakeColor(255, 194, 15, 255), -1, -1); + GadgetListBoxAddEntryText(listboxGameSetupChat, UnicodeString(L"Lobby access: Anyone can join. Use /friendsonly to limit the lobby to friends."), GameMakeColor(192, 192, 192, 255), -1, -1); } } @@ -2184,7 +2183,7 @@ void WOLGameSetupMenuInit( WindowLayout *layout, void *userData ) { if (!TheNGMPGame->getAllowObservers()) { - GadgetListBoxAddEntryText(listboxGameSetupChat, UnicodeString(L"NOTE: The host has disabled observers in this lobby."), GameSpyColor[GSCOLOR_CHAT_NORMAL], -1, -1); + GadgetListBoxAddEntryText(listboxGameSetupChat, UnicodeString(L"Observers: Disabled by the host."), GameMakeColor(192, 192, 192, 255), -1, -1); } } #endif @@ -2410,13 +2409,13 @@ void WOLGameSetupMenuUpdate( WindowLayout * layout, void *userData) NetworkLog(ELogVerbosity::LOG_RELEASE, "Host left and server migrated the host to us..."); - GadgetListBoxAddEntryText(listboxGameSetupChat, UnicodeString(L"The previous host has left the lobby. You are now the host."), GameMakeColor(255, 255, 255, 255), -1, -1); + GadgetListBoxAddEntryText(listboxGameSetupChat, UnicodeString(L"Host: The previous host left. You are now the host."), GameMakeColor(192, 192, 192, 255), -1, -1); // NOTE: don't need to mark ourselves ready, the service did it for us upon migration } else { - GadgetListBoxAddEntryText(listboxGameSetupChat, UnicodeString(L"The previous host has left the lobby. a new host has been selected."), GameMakeColor(255, 255, 255, 255), -1, -1); + GadgetListBoxAddEntryText(listboxGameSetupChat, UnicodeString(L"Host: The previous host left. A new host was selected."), GameMakeColor(192, 192, 192, 255), -1, -1); } // re-enable critical buttons for everyone @@ -3508,12 +3507,24 @@ Bool handleGameSetupSlashCommands(UnicodeString uText) #if defined(GENERALS_ONLINE) else if (token == "help" || token == "commands") { - GadgetListBoxAddEntryText(listboxGameSetupChat, UnicodeString(L"The following commands are available:"), GameSpyColor[GSCOLOR_CHAT_NORMAL], -1, -1); - GadgetListBoxAddEntryText(listboxGameSetupChat, UnicodeString(L"/maxcameraheight - Sets the maximum camera zoom out level - Example: /maxcameraheight 650"), GameSpyColor[GSCOLOR_CHAT_NORMAL], -1, -1); - GadgetListBoxAddEntryText(listboxGameSetupChat, UnicodeString(L"/friendsonly - Sets the lobby to only be joinable by friends"), GameSpyColor[GSCOLOR_CHAT_NORMAL], -1, -1); - GadgetListBoxAddEntryText(listboxGameSetupChat, UnicodeString(L"/public - Sets the lobby to be joinable by anyone"), GameSpyColor[GSCOLOR_CHAT_NORMAL], -1, -1); + const Color helpColor = GameMakeColor(127, 127, 127, 255); + GadgetListBoxAddEntryText(listboxGameSetupChat, UnicodeString(L"/me - Send an emote."), helpColor, -1, -1); + GadgetListBoxAddEntryText(listboxGameSetupChat, UnicodeString(L"/friendsonly - Let only friends join (host only)."), helpColor, -1, -1); + GadgetListBoxAddEntryText(listboxGameSetupChat, UnicodeString(L"/public - Let anyone join (host only)."), helpColor, -1, -1); + GadgetListBoxAddEntryText(listboxGameSetupChat, UnicodeString(L"/setpassword - Set a lobby password (host only)."), helpColor, -1, -1); + GadgetListBoxAddEntryText(listboxGameSetupChat, UnicodeString(L"/removepassword - Remove the lobby password (host only)."), helpColor, -1, -1); + GadgetListBoxAddEntryText(listboxGameSetupChat, UnicodeString(L"/maxcameraheight - Set the camera height limit (host only)."), helpColor, -1, -1); + // GadgetListBoxAddEntryText(listboxGameSetupChat, UnicodeString(L"/leave - Return to the main lobby."), helpColor, -1, -1); + // GadgetListBoxAddEntryText(listboxGameSetupChat, UnicodeString(L"/quit - Exit the game."), helpColor, -1, -1); + GadgetListBoxAddEntryText(listboxGameSetupChat, UnicodeString(L"/support - Open the GeneralsOnline Discord."), helpColor, -1, -1); + GadgetListBoxAddEntryText(listboxGameSetupChat, UnicodeString(L"/help - Show these commands. You can also use /commands."), helpColor, -1, -1); return TRUE; // was a slash command } + else if (token == "support") + { + ShellExecuteA(NULL, "open", "https://discord.playgenerals.online", NULL, NULL, SW_SHOWNORMAL); + return TRUE; + } else if (token == "friendsonly") { NGMP_OnlineServicesManager* pOnlineServicesMgr = NGMP_OnlineServicesManager::GetInstance(); @@ -3527,7 +3538,7 @@ Bool handleGameSetupSlashCommands(UnicodeString uText) { if (pLobbyInterface->IsHost()) // NOTE: this is checked service side too, but we might as well not make the call to reduce resource usage { - GadgetListBoxAddEntryText(listboxGameSetupChat, UnicodeString(L"This lobby is now only open to friends. Use /public to make it open to the public."), GameMakeColor(255, 194, 15, 255), -1, -1); + GadgetListBoxAddEntryText(listboxGameSetupChat, UnicodeString(L"Lobby access: Friends only. Use /public to let anyone join."), GameMakeColor(0, 255, 0, 255), -1, -1); pLobbyInterface->SetJoinability(ELobbyJoinability::LobbyJoinability_FriendsOnly); } } @@ -3548,7 +3559,7 @@ Bool handleGameSetupSlashCommands(UnicodeString uText) { if (pLobbyInterface->IsHost()) // NOTE: this is checked service side too, but we might as well not make the call to reduce resource usage { - GadgetListBoxAddEntryText(listboxGameSetupChat, UnicodeString(L"This lobby is now only open to the public. Use /friendsonly to make it open to friends only."), GameMakeColor(255, 194, 15, 255), -1, -1); + GadgetListBoxAddEntryText(listboxGameSetupChat, UnicodeString(L"Lobby access: Anyone can join. Use /friendsonly to limit the lobby to friends."), GameMakeColor(0, 255, 0, 255), -1, -1); pLobbyInterface->SetJoinability(ELobbyJoinability::LobbyJoinability_Public); } } @@ -3593,8 +3604,8 @@ Bool handleGameSetupSlashCommands(UnicodeString uText) if (newCameraHeight < GENERALS_ONLINE_MIN_LOBBY_CAMERA_ZOOM || newCameraHeight > GENERALS_ONLINE_MAX_LOBBY_CAMERA_ZOOM) { UnicodeString msg; - msg.format(L"The camera height must be between %d and %d.", GENERALS_ONLINE_MIN_LOBBY_CAMERA_ZOOM, GENERALS_ONLINE_MAX_LOBBY_CAMERA_ZOOM); - GadgetListBoxAddEntryText(listboxGameSetupChat, msg, GameSpyColor[GSCOLOR_CHAT_NORMAL], -1, -1); + msg.format(L"Camera height: Enter a value from %d to %d.", GENERALS_ONLINE_MIN_LOBBY_CAMERA_ZOOM, GENERALS_ONLINE_MAX_LOBBY_CAMERA_ZOOM); + GadgetListBoxAddEntryText(listboxGameSetupChat, msg, GameMakeColor(255, 0, 0, 255), -1, -1); return TRUE; // was a slash command } else @@ -3608,14 +3619,14 @@ Bool handleGameSetupSlashCommands(UnicodeString uText) } else { - GadgetListBoxAddEntryText(listboxGameSetupChat, UnicodeString(L"The camera height must be a number."), GameSpyColor[GSCOLOR_CHAT_NORMAL], -1, -1); + GadgetListBoxAddEntryText(listboxGameSetupChat, UnicodeString(L"Camera height: Enter a number."), GameMakeColor(255, 0, 0, 255), -1, -1); return TRUE; // was a slash command } } else { - GadgetListBoxAddEntryText(listboxGameSetupChat, UnicodeString(L"You must be the lobby host to modify the maximum camera height."), GameSpyColor[GSCOLOR_CHAT_NORMAL], -1, -1); + GadgetListBoxAddEntryText(listboxGameSetupChat, UnicodeString(L"Camera height: Only the host can change it."), GameMakeColor(255, 0, 0, 255), -1, -1); return TRUE; // was a slash command } } @@ -3625,14 +3636,16 @@ Bool handleGameSetupSlashCommands(UnicodeString uText) return TRUE; // was a slash command } #endif - else if (token == "leave") - { - PopBackToLobby(); - } - else if (token == "quit") - { - TheGameEngine->setQuitting(TRUE); - } + // else if (token == "leave") + // { + // PopBackToLobby(); + // return TRUE; + // } + // else if (token == "quit") + // { + // TheGameEngine->setQuitting(TRUE); + // return TRUE; + // } else if (token == "steam" || token == "advnet") { NetworkLog(ELogVerbosity::LOG_RELEASE, "[ADV NET STATS] Writing advanced networking stats:"); @@ -3645,7 +3658,7 @@ Bool handleGameSetupSlashCommands(UnicodeString uText) NetworkLog(ELogVerbosity::LOG_RELEASE, "[ADV NET STATS] Connection to user %lld: %s", kvPair.first, conn.GetStats().c_str()); } NetworkLog(ELogVerbosity::LOG_RELEASE, "[ADV NET STATS] Advanced networking stats dumped"); - GadgetListBoxAddEntryText(listboxGameSetupChat, UnicodeString(L"Advanced networking debug stats have been written to log file."), GameSpyColor[GSCOLOR_DEFAULT], -1, -1); + GadgetListBoxAddEntryText(listboxGameSetupChat, UnicodeString(L"Network debug: Statistics were written to the log file."), GameMakeColor(192, 192, 192, 255), -1, -1); return TRUE; } @@ -3665,7 +3678,7 @@ Bool handleGameSetupSlashCommands(UnicodeString uText) if (newPassword.getLength() == 0 || newPassword.getLength() > GENERALS_ONLINE_LOBBY_MAX_PASSWORD_LENGTH) { UnicodeString errorMsg; - errorMsg.format(L"The new password must be between 1 and %d characters.", GENERALS_ONLINE_LOBBY_MAX_PASSWORD_LENGTH); + errorMsg.format(L"Lobby password: Use 1 to %d characters.", GENERALS_ONLINE_LOBBY_MAX_PASSWORD_LENGTH); GadgetListBoxAddEntryText(listboxGameSetupChat, errorMsg, GameMakeColor(255, 0, 0, 255), -1, -1); } else @@ -3675,13 +3688,13 @@ Bool handleGameSetupSlashCommands(UnicodeString uText) { pWS->SendData_ChangeLobbyPassword(newPassword); - GadgetListBoxAddEntryText(listboxGameSetupChat, UnicodeString(L"The lobby password has been updated. You can remove the password by using /removepassword"), GameMakeColor(0, 255, 0, 255), -1, -1); + GadgetListBoxAddEntryText(listboxGameSetupChat, UnicodeString(L"Lobby password: Updated. Use /removepassword to remove it."), GameMakeColor(0, 255, 0, 255), -1, -1); } } } else { - GadgetListBoxAddEntryText(listboxGameSetupChat, UnicodeString(L"You are not the lobby owner."), GameMakeColor(255, 0, 0, 255), -1, -1); + GadgetListBoxAddEntryText(listboxGameSetupChat, UnicodeString(L"Lobby password: Only the host can change it."), GameMakeColor(255, 0, 0, 255), -1, -1); } } @@ -3705,17 +3718,17 @@ Bool handleGameSetupSlashCommands(UnicodeString uText) { pWS->SendData_RemoveLobbyPassword(); - GadgetListBoxAddEntryText(listboxGameSetupChat, UnicodeString(L"The lobby password has been removed. You can set a password using /setpassword "), GameMakeColor(0, 255, 0, 255), -1, -1); + GadgetListBoxAddEntryText(listboxGameSetupChat, UnicodeString(L"Lobby password: Removed. Use /setpassword to add one."), GameMakeColor(0, 255, 0, 255), -1, -1); } } else { - GadgetListBoxAddEntryText(listboxGameSetupChat, UnicodeString(L"The lobby is not passworded. You can set a password using /setpassword "), GameMakeColor(255, 0, 0, 255), -1, -1); + GadgetListBoxAddEntryText(listboxGameSetupChat, UnicodeString(L"Lobby password: No password is set. Use /setpassword to add one."), GameMakeColor(192, 192, 192, 255), -1, -1); } } else { - GadgetListBoxAddEntryText(listboxGameSetupChat, UnicodeString(L"You are not the lobby owner."), GameMakeColor(255, 0, 0, 255), -1, -1); + GadgetListBoxAddEntryText(listboxGameSetupChat, UnicodeString(L"Lobby password: Only the host can change it."), GameMakeColor(255, 0, 0, 255), -1, -1); } } @@ -3739,7 +3752,12 @@ Bool handleGameSetupSlashCommands(UnicodeString uText) } #endif // defined(RTS_DEBUG) - return FALSE; // not a slash command +#if defined(GENERALS_ONLINE) + GadgetListBoxAddEntryText(listboxGameSetupChat, UnicodeString(L"Unknown command: Use /help to see all commands."), GameSpyColor[GSCOLOR_CHAT_NORMAL], -1, -1); + return TRUE; +#else + return FALSE; +#endif } static Int getNextSelectablePlayer(Int start) diff --git a/GeneralsMD/Code/GameEngine/Source/GameClient/GUI/GUICallbacks/Menus/WOLLobbyMenu.cpp b/GeneralsMD/Code/GameEngine/Source/GameClient/GUI/GUICallbacks/Menus/WOLLobbyMenu.cpp index c0b15f45d61..6128a8f080e 100644 --- a/GeneralsMD/Code/GameEngine/Source/GameClient/GUI/GUICallbacks/Menus/WOLLobbyMenu.cpp +++ b/GeneralsMD/Code/GameEngine/Source/GameClient/GUI/GUICallbacks/Menus/WOLLobbyMenu.cpp @@ -71,6 +71,8 @@ #include "GameNetwork/RankPointValue.h" #include "GameNetwork/GeneralsOnline/NGMP_interfaces.h" +#include + void refreshGameList( Bool forceRefresh = FALSE ); void refreshPlayerList( Bool forceRefresh = FALSE ); @@ -130,38 +132,39 @@ static Int groupRoomToJoin = 0; static Int initialGadgetDelay = 2; static Bool justEntered = FALSE; -static int64_t s_lobbyLastChatTimeMs = 0; -static const int64_t S_LOBBY_CHAT_INTERVAL_MS = 3000; // how long to wait before we allow sending the next message +// TODO: Remove this client-side limit after server-side rate limiting is deployed. +static std::deque s_lobbyChatMessageTimes; +static std::chrono::steady_clock::time_point s_lastLobbyChatRateLimitNotice; +static Bool s_hasShownLobbyChatRateLimitNotice = FALSE; -static bool LobbyChatSlowmodeAllowsSend() +static bool LobbyChatRateLimitAllowsSend() { using namespace std::chrono; - int64_t nowMs = - duration_cast(utc_clock::now().time_since_epoch()).count(); - - if (nowMs < s_lobbyLastChatTimeMs) + const auto now = steady_clock::now(); + const auto window = seconds(9); + while (!s_lobbyChatMessageTimes.empty() && now - s_lobbyChatMessageTimes.front() >= window) { - s_lobbyLastChatTimeMs = 0; + s_lobbyChatMessageTimes.pop_front(); } - int64_t delta = nowMs - s_lobbyLastChatTimeMs; - if (delta < S_LOBBY_CHAT_INTERVAL_MS) + if (s_lobbyChatMessageTimes.size() >= 3) { - if (listboxLobbyChat) + if (!s_hasShownLobbyChatRateLimitNotice || now - s_lastLobbyChatRateLimitNotice >= window) { GadgetListBoxAddEntryText( listboxLobbyChat, - UnicodeString(L"You are sending messages too quickly. Please wait a moment."), - GameMakeColor(255, 0, 0, 255), + UnicodeString(L"Rate limit: Please wait before sending another message."), + GameMakeColor(255, 194, 15, 255), -1, - -1 - ); + -1); + s_lastLobbyChatRateLimitNotice = now; + s_hasShownLobbyChatRateLimitNotice = TRUE; } return false; } - s_lobbyLastChatTimeMs = nowMs; + s_lobbyChatMessageTimes.push_back(now); return true; } @@ -178,8 +181,11 @@ int getQR2HostingStatus(); } extern int isThreadHosting; -Bool handleLobbySlashCommands(UnicodeString uText) +Bool handleLobbySlashCommands(UnicodeString uText, Bool *wasRateLimited) { + if (wasRateLimited != nullptr) + *wasRateLimited = FALSE; + AsciiString message; message.translate(uText); @@ -205,6 +211,13 @@ Bool handleLobbySlashCommands(UnicodeString uText) } else if (token == "me" && uText.getLength()>4) { + if (!LobbyChatRateLimitAllowsSend()) + { + if (wasRateLimited != nullptr) + *wasRateLimited = TRUE; + return TRUE; + } + UnicodeString msg = UnicodeString(uText.str() + 4); // skip the /me NGMP_OnlineServices_RoomsInterface* pRoomsInterface = NGMP_OnlineServicesManager::GetInterface(); if (pRoomsInterface != nullptr) @@ -2642,23 +2655,26 @@ WindowMsgHandledType WOLLobbyMenuSystem( GameWindow *window, UnsignedInt msg, } else if ( controlID == buttonEmoteID ) { - // read the user's input and clear the entry box + // read the user's input UnicodeString txtInput; txtInput.set(GadgetTextEntryGetText( textEntryChat )); - GadgetTextEntrySetText(textEntryChat, UnicodeString::TheEmptyString); txtInput.trim(); - if (!txtInput.isEmpty()) + if (txtInput.isEmpty()) { - if (!LobbyChatSlowmodeAllowsSend()) - { - break; - } - // Send the message - NGMP_OnlineServices_RoomsInterface* pRoomsInterface = NGMP_OnlineServicesManager::GetInterface(); - if (pRoomsInterface != nullptr) - { - pRoomsInterface->SendChatMessageToCurrentRoom(txtInput, false); - } + GadgetTextEntrySetText(textEntryChat, UnicodeString::TheEmptyString); + break; + } + + if (!LobbyChatRateLimitAllowsSend()) + { + break; + } + + GadgetTextEntrySetText(textEntryChat, UnicodeString::TheEmptyString); + NGMP_OnlineServices_RoomsInterface* pRoomsInterface = NGMP_OnlineServicesManager::GetInterface(); + if (pRoomsInterface != nullptr) + { + pRoomsInterface->SendChatMessageToCurrentRoom(txtInput, false); } } @@ -2998,28 +3014,37 @@ WindowMsgHandledType WOLLobbyMenuSystem( GameWindow *window, UnsignedInt msg, if (buttonPushed) break; - // read the user's input and clear the entry box + // read the user's input UnicodeString txtInput; txtInput.set(GadgetTextEntryGetText( textEntryChat )); - GadgetTextEntrySetText(textEntryChat, UnicodeString::TheEmptyString); txtInput.trim(); - if (!txtInput.isEmpty()) + if (txtInput.isEmpty()) + { + GadgetTextEntrySetText(textEntryChat, UnicodeString::TheEmptyString); + break; + } + + Bool wasRateLimited = FALSE; + if (handleLobbySlashCommands(txtInput, &wasRateLimited)) + { + if (!wasRateLimited) + GadgetTextEntrySetText(textEntryChat, UnicodeString::TheEmptyString); + } + else { - // Send the message - if (!handleLobbySlashCommands(txtInput)) + if (!LobbyChatRateLimitAllowsSend()) { - if (!LobbyChatSlowmodeAllowsSend()) - { - break; - } - std::shared_ptr pWS = NGMP_OnlineServicesManager::GetWebSocket(); - if (pWS != nullptr) - { - pWS->SendData_RoomChatMessage(txtInput, false); - } - // TODO_NGMP: Support private message again - //TheGameSpyInfo->sendChat( txtInput, false, listboxLobbyPlayers ); + break; + } + + GadgetTextEntrySetText(textEntryChat, UnicodeString::TheEmptyString); + std::shared_ptr pWS = NGMP_OnlineServicesManager::GetWebSocket(); + if (pWS != nullptr) + { + pWS->SendData_RoomChatMessage(txtInput, false); } + // TODO_NGMP: Support private message again + //TheGameSpyInfo->sendChat( txtInput, false, listboxLobbyPlayers ); } break; } diff --git a/GeneralsMD/Code/GameEngine/Source/GameNetwork/GeneralsOnline/OnlineServices_LobbyInterface.cpp b/GeneralsMD/Code/GameEngine/Source/GameNetwork/GeneralsOnline/OnlineServices_LobbyInterface.cpp index d4e00b95eb5..9110b403d31 100644 --- a/GeneralsMD/Code/GameEngine/Source/GameNetwork/GeneralsOnline/OnlineServices_LobbyInterface.cpp +++ b/GeneralsMD/Code/GameEngine/Source/GameNetwork/GeneralsOnline/OnlineServices_LobbyInterface.cpp @@ -293,7 +293,7 @@ void NGMP_OnlineServices_LobbyInterface::UpdateCurrentLobbyMaxCameraHeight(uint1 if (IsHost()) { UnicodeString strInform; - strInform.format(L"The host has set the maximum camera height to %lu.", maxCameraHeight); + strInform.format(L"Camera height: The host set the limit to %lu.", maxCameraHeight); SendAnnouncementMessageToCurrentLobby(strInform, true); @@ -325,7 +325,7 @@ void NGMP_OnlineServices_LobbyInterface::SetJoinability(ELobbyJoinability joinab { if (IsHost()) { - UnicodeString strInform(joinabilityFlag == ELobbyJoinability::LobbyJoinability_FriendsOnly ? L"The host has set the lobby joinability to friends only" : L"The host has set the lobby joinability to public"); + UnicodeString strInform(joinabilityFlag == ELobbyJoinability::LobbyJoinability_FriendsOnly ? L"Lobby access: Friends only." : L"Lobby access: Anyone can join."); SendAnnouncementMessageToCurrentLobby(strInform, false); @@ -500,7 +500,7 @@ void NGMP_OnlineServices_LobbyInterface::UpdateCurrentLobby_KickUser(int64_t use NGMP_OnlineServicesManager::GetInstance()->GetHTTPManager()->SendPOSTRequest(strURI.c_str(), EIPProtocolVersion::DONT_CARE, mapHeaders, strPostData.c_str(), [=](bool bSuccess, int statusCode, std::string strBody, HTTPRequest* pReq) { UnicodeString msg; - msg.format(L"'%s' was kicked by the host.", name.str());; + msg.format(L"Lobby: '%s' was removed by the host.", name.str());; SendAnnouncementMessageToCurrentLobby(msg, true); }); } @@ -517,7 +517,7 @@ void NGMP_OnlineServices_LobbyInterface::UpdateCurrentLobby_ForceReady() // convert NGMP_OnlineServicesManager::GetInstance()->GetHTTPManager()->SendPOSTRequest(strURI.c_str(), EIPProtocolVersion::DONT_CARE, mapHeaders, strPostData.c_str(), [=](bool bSuccess, int statusCode, std::string strBody, HTTPRequest* pReq) { - UnicodeString msg = UnicodeString(L"All players have been forced to ready up."); + UnicodeString msg = UnicodeString(L"Ready check: All players are now marked ready."); SendAnnouncementMessageToCurrentLobby(msg, true); }); }