Skip to content

Commit bf84bac

Browse files
committed
feat(moderation): Add websocket protocol messages
1 parent 76844b0 commit bf84bac

12 files changed

Lines changed: 369 additions & 14 deletions

File tree

Core/GameEngine/Include/GameNetwork/GameSpyOverlay.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,13 @@
3838
void ClearGSMessageBoxes(); ///< Tear down any GS message boxes (e.g. in case we have a new one to put up)
3939
void GSMessageBoxOk(UnicodeString titleString,UnicodeString bodyString, GameWinMsgBoxFunc okFunc = nullptr); ///< Display a Message box with Ok button and track it
4040
void GSMessageBoxOkCancel(UnicodeString title, UnicodeString message, GameWinMsgBoxFunc okFunc, GameWinMsgBoxFunc cancelFunc); ///< Display a Message box with Ok/Cancel buttons and track it
41+
void GSMessageBoxOkCancelWithLabels(
42+
UnicodeString title,
43+
UnicodeString message,
44+
UnicodeString okLabel,
45+
UnicodeString cancelLabel,
46+
GameWinMsgBoxFunc okFunc,
47+
GameWinMsgBoxFunc cancelFunc); ///< Display a tracked Ok/Cancel message box with caller-provided button labels
4148
void GSMessageBoxYesNo(UnicodeString title, UnicodeString message, GameWinMsgBoxFunc yesFunc, GameWinMsgBoxFunc noFunc); ///< Display a Message box with Yes/No buttons and track it
4249
void RaiseGSMessageBox(); ///< Bring GS message box to the foreground (if we transition screens while a message box is up)
4350

Core/GameEngine/Source/GameNetwork/GameSpyOverlay.cpp

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@
3030
#include "Common/AudioEventRTS.h"
3131

3232
#include "GameClient/GadgetListBox.h"
33+
#include "GameClient/GadgetPushButton.h"
3334
#include "GameClient/GameText.h"
3435
#include "GameClient/MessageBox.h"
3536
#include "GameClient/ShellHooks.h"
@@ -125,6 +126,33 @@ void GSMessageBoxOkCancel(UnicodeString title, UnicodeString message, GameWinMsg
125126
cancelFunc = newCancelFunc;
126127
}
127128

129+
void GSMessageBoxOkCancelWithLabels(
130+
UnicodeString title,
131+
UnicodeString message,
132+
UnicodeString okLabel,
133+
UnicodeString cancelLabel,
134+
GameWinMsgBoxFunc newOkFunc,
135+
GameWinMsgBoxFunc newCancelFunc)
136+
{
137+
GSMessageBoxOkCancel(title, message, newOkFunc, newCancelFunc);
138+
if (messageBoxWindow == nullptr)
139+
{
140+
return;
141+
}
142+
143+
GameWindow* buttonOk = TheWindowManager->winGetWindowFromId(messageBoxWindow, TheNameKeyGenerator->nameToKey("MessageBox.wnd:ButtonOk"));
144+
GameWindow* buttonCancel = TheWindowManager->winGetWindowFromId(messageBoxWindow, TheNameKeyGenerator->nameToKey("MessageBox.wnd:ButtonCancel"));
145+
if (buttonOk != nullptr)
146+
{
147+
GadgetButtonSetText(buttonOk, okLabel);
148+
}
149+
150+
if (buttonCancel != nullptr)
151+
{
152+
GadgetButtonSetText(buttonCancel, cancelLabel);
153+
}
154+
}
155+
128156
/**
129157
* GSMessageBoxYesNo puts up a Yes/No dialog box and saves the
130158
* pointers to it and its callbacks.

GeneralsMD/Code/GameEngine/CMakeLists.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1150,6 +1150,7 @@ set(GAMEENGINE_SRC
11501150
Include/GameNetwork/GeneralsOnline/OnlineServices_Auth.h
11511151
Include/GameNetwork/GeneralsOnline/OnlineServices_Init.h
11521152
Include/GameNetwork/GeneralsOnline/OnlineServices_LobbyInterface.h
1153+
Include/GameNetwork/GeneralsOnline/OnlineServices_Moderation.h
11531154
Include/GameNetwork/GeneralsOnline/OnlineServices_RoomsInterface.h
11541155
Include/GameNetwork/GeneralsOnline/OnlineServices_StatsInterface.h
11551156
Include/GameNetwork/GeneralsOnline/OnlineServices_SocialInterface.h
@@ -1193,6 +1194,7 @@ set(GAMEENGINE_SRC
11931194
Source/GameNetwork/GeneralsOnline/OnlineServices_Init.cpp
11941195
Source/GameNetwork/GeneralsOnline/OnlineServices_Auth.cpp
11951196
Source/GameNetwork/GeneralsOnline/OnlineServices_LobbyInterface.cpp
1197+
Source/GameNetwork/GeneralsOnline/OnlineServices_Moderation.cpp
11961198
Source/GameNetwork/GeneralsOnline/OnlineServices_RoomsInterface.cpp
11971199
Source/GameNetwork/GeneralsOnline/OnlineServices_StatsInterface.cpp
11981200
Source/GameNetwork/GeneralsOnline/OnlineServices_SocialInterface.cpp

GeneralsMD/Code/GameEngine/Include/GameNetwork/GeneralsOnline/NGMP_include.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ void NetworkLog(ELogVerbosity logVerbosity, const char* fmt, ...);
2727

2828
std::string to_utf8(const std::wstring& wstr);
2929
std::wstring from_utf8(const std::string& utf8_str);
30+
std::wstring NormalizeSingleLineText(const std::wstring& text);
3031

3132
int RoundUpLatencyToFrameInterval(int latency, int frameInterval);
3233
int ConvertMSLatencyToFrames(int ms);

GeneralsMD/Code/GameEngine/Include/GameNetwork/GeneralsOnline/OnlineServices_Init.h

Lines changed: 22 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -94,7 +94,10 @@ enum EWebSocketMessageID
9494
AC_REGISTER_PLAYER = 40,
9595
AC_DEREGISTER_PLAYER = 41,
9696
WS_KEEPALIVE = 42,
97-
WS_KEEPALIVE_CLIENT = 43
97+
WS_KEEPALIVE_CLIENT = 43,
98+
MODERATION_NOTICE = 46,
99+
MODERATION_COMMAND = 47,
100+
MODERATION_COMMAND_RESULT = 48
98101
};
99102

100103
enum class EQoSRegions
@@ -124,9 +127,16 @@ enum class EGOTearDownReason
124127
LOST_CONNECTION = 0,
125128
USER_LOGOUT = 1,
126129
USER_REQUESTED_SILENT = 2,
127-
AUTH_FAILED = 3
130+
AUTH_FAILED = 3,
131+
MODERATION_BAN = 4,
132+
MODERATION_KICK = 5
128133
};
129134

135+
constexpr bool IsModerationTeardownReason(EGOTearDownReason reason) noexcept
136+
{
137+
return reason == EGOTearDownReason::MODERATION_BAN || reason == EGOTearDownReason::MODERATION_KICK;
138+
}
139+
130140
class WebSocket
131141
{
132142
public:
@@ -500,7 +510,16 @@ class NGMP_OnlineServicesManager
500510

501511
std::string& GetMOTD() { return m_strMOTD; }
502512

503-
void SetPendingFullTeardown(EGOTearDownReason reason) { m_bPendingFullTeardown = true; m_teardownReason = reason; }
513+
void SetPendingFullTeardown(EGOTearDownReason reason)
514+
{
515+
if (IsModerationTeardownReason(m_teardownReason) && !IsModerationTeardownReason(reason))
516+
{
517+
return;
518+
}
519+
520+
m_bPendingFullTeardown = true;
521+
m_teardownReason = reason;
522+
}
504523
bool IsPendingFullTeardown() const { return m_bPendingFullTeardown; }
505524
EGOTearDownReason GetTeardownReason() const { return m_teardownReason; }
506525
void ConsumePendingFullTeardown() { m_bPendingFullTeardown = false; }
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
#pragma once
2+
3+
#include <string>
4+
5+
enum class EOnlineModerationAction
6+
{
7+
BAN,
8+
KICK
9+
};
10+
11+
void ShowLoginBanDialog(const std::string& reason);
12+
void HandleModerationDisconnect(EOnlineModerationAction action, const std::string& reason);
13+
void HandleModerationNotice(const std::string& actionType, const std::string& reason, const std::string& scopeType);

GeneralsMD/Code/GameEngine/Include/GameNetwork/GeneralsOnline/OnlineServices_RoomsInterface.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -193,10 +193,12 @@ class NGMP_OnlineServices_RoomsInterface
193193
void OnRosterUpdated(std::unordered_map<uint64_t, NetworkRoomMember> mapMembers);
194194

195195
int GetCurrentRoomID() const { return m_CurrentRoomID; }
196+
bool SupportsModerationCommands() const { return m_bSupportsModerationCommands; }
196197

197198

198199
private:
199200
int m_CurrentRoomID = -1;
201+
bool m_bSupportsModerationCommands = false;
200202

201203
std::vector<NetworkRoom> m_vecRooms;
202204

GeneralsMD/Code/GameEngine/Source/Common/GameEngine.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -128,7 +128,7 @@ void TearDownGeneralsOnline()
128128

129129
EGOTearDownReason teardownReason = NGMP_OnlineServicesManager::GetInstance()->GetTeardownReason();
130130

131-
if (teardownReason != EGOTearDownReason::USER_REQUESTED_SILENT)
131+
if (teardownReason != EGOTearDownReason::USER_REQUESTED_SILENT && !IsModerationTeardownReason(teardownReason))
132132
{
133133
UnicodeString title, body;
134134

GeneralsMD/Code/GameEngine/Source/GameNetwork/GeneralsOnline/NGMP_Helpers.cpp

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
#include "GameNetwork/GeneralsOnline/NGMP_include.h"
22
#include <chrono>
3+
#include <cwctype>
34
#include <ctime>
45
#include <mutex>
56
#include <string>
@@ -25,6 +26,31 @@ std::wstring from_utf8(const std::string& utf8_str)
2526
return converter.from_bytes(utf8_str);
2627
}
2728

29+
std::wstring NormalizeSingleLineText(const std::wstring& text)
30+
{
31+
std::wstring normalizedText;
32+
normalizedText.reserve(text.size());
33+
bool pendingSpace = false;
34+
35+
for (wchar_t character : text)
36+
{
37+
if (std::iswspace(character) || std::iswcntrl(character))
38+
{
39+
pendingSpace = !normalizedText.empty();
40+
continue;
41+
}
42+
43+
if (pendingSpace)
44+
{
45+
normalizedText += L' ';
46+
pendingSpace = false;
47+
}
48+
normalizedText += character;
49+
}
50+
51+
return normalizedText;
52+
}
53+
2854
void NetworkLog(ELogVerbosity logVerbosity, const char* fmt, ...)
2955
{
3056
if (!NGMP_OnlineServicesManager::Settings.Debug_VerboseLogging())

GeneralsMD/Code/GameEngine/Source/GameNetwork/GeneralsOnline/OnlineServices_Auth.cpp

Lines changed: 31 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
#include "GameNetwork/GeneralsOnline/HTTP/HTTPManager.h"
44
#include "GameNetwork/GeneralsOnline/HTTP/HTTPRequest.h"
5+
#include "GameNetwork/GeneralsOnline/OnlineServices_Moderation.h"
56
#include "GameNetwork/GeneralsOnline/json.hpp"
67
#include <shellapi.h>
78
#include <algorithm>
@@ -52,6 +53,24 @@ struct AuthResponse
5253
NLOHMANN_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT(AuthResponse, result, session_token, refresh_token, user_id, display_name, ws_uri)
5354
};
5455

56+
namespace
57+
{
58+
std::string GetBanReason(const std::string& responseBody)
59+
{
60+
nlohmann::json jsonObject = nlohmann::json::parse(responseBody, nullptr, false, true);
61+
if (jsonObject.is_object())
62+
{
63+
const auto banReason = jsonObject.find("ban_reason");
64+
if (banReason != jsonObject.end() && banReason->is_string())
65+
{
66+
return banReason->get_ref<const std::string&>();
67+
}
68+
}
69+
70+
return std::string();
71+
}
72+
}
73+
5574
struct MOTDResponse
5675
{
5776
std::string MOTD;
@@ -206,6 +225,16 @@ void NGMP_OnlineServices_AuthInterface::RefreshToken()
206225
{
207226
if (statusCode >= 400 && statusCode < 500)
208227
{
228+
if (statusCode == 423)
229+
{
230+
NetworkLog(ELogVerbosity::LOG_RELEASE, "[AUTH]: Account ban detected during token refresh, tearing down");
231+
m_tokenCreationTime = -1;
232+
m_nextRefreshRetryTime = -1;
233+
m_currentRefreshAttempt = 0;
234+
HandleModerationDisconnect(EOnlineModerationAction::BAN, GetBanReason(strBody));
235+
return;
236+
}
237+
209238
OnRefreshTokenFailed(std::format("HTTP {}", statusCode).c_str(), strBody);
210239
}
211240
else
@@ -306,11 +335,7 @@ void NGMP_OnlineServices_AuthInterface::BeginLogin()
306335
{
307336
if (statusCode == 423)
308337
{
309-
ClearGSMessageBoxes();
310-
GSMessageBoxOk(UnicodeString(L"Account Banned"), UnicodeString(L"You are banned. You can file an appeal in Discord."), []()
311-
{
312-
TheShell->pop();
313-
});
338+
ShowLoginBanDialog(GetBanReason(strBody));
314339
return;
315340
}
316341
else
@@ -506,11 +531,7 @@ void NGMP_OnlineServices_AuthInterface::Tick()
506531
if (statusCode == 423)
507532
{
508533
m_bWaitingLogin = false;
509-
ClearGSMessageBoxes();
510-
GSMessageBoxOk(UnicodeString(L"Account Banned"), UnicodeString(L"You are banned. You can file an appeal in Discord."), []()
511-
{
512-
TheShell->pop();
513-
});
534+
ShowLoginBanDialog(GetBanReason(strBody));
514535
return;
515536
}
516537

0 commit comments

Comments
 (0)