From 9bc1f0bd8b5fa4c3fee001e684d23ea657153bbf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mathieu=20Gon=C3=A7alves?= Date: Tue, 28 Jul 2026 21:09:04 +0200 Subject: [PATCH] Chat mod based on Muffin's requests. - Added a bat file to generate the build files for the most recent Visual Studio. - Added vars to disable specific chats. - Added a little display when doing the command say_team_mod, as nothing was returned before. - Added checks inside CG_Chat_f about banned word paterns and if a chat is disabled. - Added assets files in the setup menu to have UI elements instead of puttings the variables manually in the console. --- CreateVisualStudio2026Projects.bat | 18 +++++ assets/japro/ui/jamp/ingame_setup.menu | 108 +++++++++++++++++++++++++ codemp/cgame/cg_servercmds.c | 98 ++++++++++++++++++++++ codemp/cgame/cg_xcvar.h | 4 + codemp/game/g_cmds.c | 9 +++ 5 files changed, 237 insertions(+) create mode 100644 CreateVisualStudio2026Projects.bat diff --git a/CreateVisualStudio2026Projects.bat b/CreateVisualStudio2026Projects.bat new file mode 100644 index 0000000000..2b494bff52 --- /dev/null +++ b/CreateVisualStudio2026Projects.bat @@ -0,0 +1,18 @@ +@REM Create OpenJK projects for Visual Studio 2026 using CMake +@echo off +for %%X in (cmake.exe) do (set FOUND=%%~$PATH:X) +if not defined FOUND ( + echo CMake was not found on your system. Please make sure you have installed CMake + echo from http://www.cmake.org/ and cmake.exe is installed to your system's PATH + echo environment variable. + echo. + pause + exit /b 1 +) else ( + echo Found CMake! +) +if not exist build\nul (mkdir build) +pushd build +cmake -G "Visual Studio 18 2026" -A Win32 -DCMAKE_POLICY_VERSION_MINIMUM=3.5 -D CMAKE_INSTALL_PREFIX=../install .. +popd +pause \ No newline at end of file diff --git a/assets/japro/ui/jamp/ingame_setup.menu b/assets/japro/ui/jamp/ingame_setup.menu index e91e698c48..583e0cdf8b 100644 --- a/assets/japro/ui/jamp/ingame_setup.menu +++ b/assets/japro/ui/jamp/ingame_setup.menu @@ -4533,6 +4533,114 @@ mouseenter { show highlight9 } mouseexit { hide highlight9 } } + + itemDef + { + name chatdisableteam + group network + type ITEM_TYPE_MULTI + text "Disable the 'team' chat" + descText "Disable the 'team' chat" + cvar "cg_chatdisableTeam" + cvarFloatList + { + @MENUS_OFF 0 + @MENUS_ON 1 + } + rect 230 243 300 20 + textalign ITEM_ALIGN_RIGHT + textalignx 165 + textaligny 0 + font 4 + textscale 1 + forecolor .615 .615 .956 1 + visible 0 + + action + { + play "sound/interface/button1.wav" + } + mouseenter + { + show highlight11 + } + mouseexit + { + hide highlight11 + } + } + + itemDef + { + name chatdisableall + group network + type ITEM_TYPE_MULTI + text "Disable the 'all' chat" + descText "Disable the 'all' chat and whispers" + cvar "cg_chatdisableAll" + cvarFloatList + { + @MENUS_OFF 0 + @MENUS_ON 1 + } + rect 230 263 300 20 + textalign ITEM_ALIGN_RIGHT + textalignx 165 + textaligny 0 + font 4 + textscale 1 + forecolor .615 .615 .956 1 + visible 0 + + action + { + play "sound/interface/button1.wav" + } + mouseenter + { + show highlight12 + } + mouseexit + { + hide highlight12 + } + } + + itemDef + { + name chatdisableclan + group network + type ITEM_TYPE_MULTI + text "Disable the 'clan' chat" + descText "Disable the 'clan' chat" + cvar "cg_chatdisableClan" + cvarFloatList + { + @MENUS_OFF 0 + @MENUS_ON 1 + } + rect 230 283 300 20 + textalign ITEM_ALIGN_RIGHT + textalignx 165 + textaligny 0 + font 4 + textscale 1 + forecolor .615 .615 .956 1 + visible 0 + + action + { + play "sound/interface/button1.wav" + } + mouseenter + { + show highlight13 + } + mouseexit + { + hide highlight13 + } + } // APPLY CHANGES BUTTON itemDef diff --git a/codemp/cgame/cg_servercmds.c b/codemp/cgame/cg_servercmds.c index e84b7d0779..c37cb48883 100644 --- a/codemp/cgame/cg_servercmds.c +++ b/codemp/cgame/cg_servercmds.c @@ -1666,6 +1666,94 @@ static void CG_Print_f( void ) { CG_LogPrintf(cg.log.file, "%s\n", strEd); //Log server console prints? } + +// Variables that hold the information, initialized when calling the funciton for the first time and needs a reboot to reload the file +#define MAX_BAN_WORDS 128 +#define MAX_BAN_WORD_LEN 32 +static char banWords[MAX_BAN_WORDS][MAX_BAN_WORD_LEN]; +static int numBanWords = 0; +static qboolean banWordArrayReady = qfalse; + +// Heavily inspired by FS_ReadFile +static void CG_LoadBanWords(void) +{ + fileHandle_t file; + int length; + char buffer[8192]; + char* ptr; + char* line; + + if (banWordArrayReady) + return; + + length = trap->FS_Open("banwords.txt", &file, FS_READ); + + if (!file || length <= 0) // File don't exist or in bad folder (base INSIDE Debug, not only base in the git repo) + return; + + trap->FS_Read(buffer, length, file); + trap->FS_Close(file); + + // guarantee that it will have a trailing 0 for string operations + buffer[length] = 0; + + ptr = buffer; + + // COM_ParseString copy : if COM_ParseExt returns nothing, then we have ended the buffer + while ((line = COM_ParseExt(&ptr, qtrue)) != NULL && line[0]) // If qfalse, it only reads the first line, different than COM_ParseString + { + if (numBanWords >= MAX_BAN_WORDS) + break; + + Q_strncpyz(banWords[numBanWords], line, MAX_BAN_WORD_LEN); + numBanWords++; + } + + banWordArrayReady = qtrue; +} +static void CG_Chat_CheckBanWord(char* text) +{ + CG_LoadBanWords(); + + for (int i = 0; i < numBanWords; i++) + { + // Q_stristr because it musn't be case sensitive, people will write as they wish on the .txt + // Else I would have used strstr again + char* match = Q_stristr(text, banWords[i]); + + while (match) // Found a match in the current word of the list ! + { + int len = strlen(banWords[i]); + + for (int j = 0; j < len; j++) + match[j] = '*'; + + match = Q_stristr(match + len, banWords[i]); // Recheck if the same word is present multiple times + } + } +} + +static void CG_Chat_CheckDisabled(char* cmd, char* text) +{ + // Same size as text + char* textToDisplay[MAX_NETNAME + MAX_SAY_TEXT] = { 0 }; + + // Clan : cmd : chat ; message : ^1^7(Posto^7): ^1co + // Team : cmd : tchat ; message : (Posto^7): ^5to + // All : cmd : chat ; message : Posto^7: ^2yo + // Whispers are considered "all chat" when hitting this function, only change is color and the server choosing to whom to deliver them + + if (cg_chatDisableClan.integer && Q_stricmp(cmd, "chat") == 0 && strstr(text, "") != NULL || // If it's a clan chat + cg_chatDisableTeam.integer && Q_stricmp(cmd, "tchat") == 0 || // If it's a team chat + cg_chatDisableAll.integer && Q_stricmp(cmd, "chat") == 0 && strstr(text, "") == NULL ) // If it's a all chat + { + Q_strncpyz(textToDisplay, "", sizeof(textToDisplay)); + strcpy(text, textToDisplay); + } + + return; +} + void CG_ChatBox_AddString(char *chatStr); static void CG_Chat_f( void ) { char cmd[MAX_STRING_CHARS] = {0}, text[MAX_NETNAME+MAX_SAY_TEXT] = {0}, logtext[MAX_NETNAME+MAX_SAY_TEXT] = {0}; @@ -1680,6 +1768,9 @@ static void CG_Chat_f( void ) { { CG_RemoveChatEscapeChar( text ); + CG_Chat_CheckDisabled(cmd, text); + CG_Chat_CheckBanWord(text); + if (cg_cleanChatbox.integer) { char cleanMsg[MAX_NETNAME + MAX_SAY_TEXT]; @@ -1717,6 +1808,9 @@ static void CG_Chat_f( void ) { { CG_RemoveChatEscapeChar( text ); + CG_Chat_CheckDisabled(cmd, text); + CG_Chat_CheckBanWord(text); + if (cg_cleanChatbox.integer && !Q_strncmp(text, cg.lastChatMsg, strlen(text))) {//Same exact msg/sender as previous //replace this with q_strcmp in entire function..? return; } @@ -1751,11 +1845,15 @@ static void CG_Chat_f( void ) { if ( !Q_stricmp( cmd, "lchat" ) && !cg_teamChatsOnly.integer ) { Com_sprintf( text, sizeof( text ), "%s" S_COLOR_WHITE "<%s> ^%s%s", name, loc, color, message ); CG_RemoveChatEscapeChar( text ); + CG_Chat_CheckDisabled(cmd, text); + CG_Chat_CheckBanWord(text); CG_ChatBox_AddString( text ); } else if ( !Q_stricmp( cmd, "ltchat" ) ) { Com_sprintf( text, sizeof( text ), "%s" S_COLOR_WHITE "<%s> ^%s%s", name, loc, color, message ); CG_RemoveChatEscapeChar( text ); + CG_Chat_CheckDisabled(cmd, text); + CG_Chat_CheckBanWord(text); CG_ChatBox_AddString( text ); } } diff --git a/codemp/cgame/cg_xcvar.h b/codemp/cgame/cg_xcvar.h index 14c3b4fba8..5b0de083a3 100644 --- a/codemp/cgame/cg_xcvar.h +++ b/codemp/cgame/cg_xcvar.h @@ -379,6 +379,10 @@ XCVAR_DEF( ui_tm2_c4_cnt, "0", NULL, CVAR_ROM|CVAR_INTERNAL ) XCVAR_DEF( ui_tm2_c5_cnt, "0", NULL, CVAR_ROM|CVAR_INTERNAL ) XCVAR_DEF( ui_tm2_cnt, "0", NULL, CVAR_ROM|CVAR_INTERNAL ) XCVAR_DEF( ui_tm3_cnt, "0", NULL, CVAR_ROM|CVAR_INTERNAL ) +// Chat related variables +XCVAR_DEF(cg_chatDisableClan, "0", NULL, CVAR_ARCHIVE) +XCVAR_DEF(cg_chatDisableAll, "0", NULL, CVAR_ARCHIVE) +XCVAR_DEF(cg_chatDisableTeam, "0", NULL, CVAR_ARCHIVE) // Redundant because of cg_teamChatsOnly ? #undef XCVAR_DEF diff --git a/codemp/game/g_cmds.c b/codemp/game/g_cmds.c index 4c25ba7bd3..44d2ac717e 100644 --- a/codemp/game/g_cmds.c +++ b/codemp/game/g_cmds.c @@ -4875,11 +4875,20 @@ void Cmd_SayTeamMod_f(gentity_t *ent)//clanpass } if (trap->Argc() == 2) { if (!Q_stricmp(type, "normal")) + { + Com_Printf("Changing team chat to mode : ^7normal\n"); ent->client->sess.sayteammod = 0; + } else if (!Q_stricmp(type, "clan")) + { + Com_Printf("Changing team chat to mode : ^7clan\n"); ent->client->sess.sayteammod = 1; + } else if (!Q_stricmp(type, "admin")) + { + Com_Printf("Changing team chat to mode : ^7admin\n"); ent->client->sess.sayteammod = 2; + } } }