From 5b4725a0116a660893d1772535bae1168d064ab7 Mon Sep 17 00:00:00 2001 From: AJ Martinez Date: Sat, 22 Apr 2023 21:02:36 -0700 Subject: [PATCH 01/16] WIP: Social restrictions until you've played some games --- src/d_clisrv.c | 18 ++++++++++-------- src/k_pwrlv.c | 2 -- src/k_serverstats.c | 40 +++++++++++++++++++++++++++++++++++++++- src/k_serverstats.h | 9 +++++++-- src/y_inter.c | 2 ++ 5 files changed, 58 insertions(+), 13 deletions(-) diff --git a/src/d_clisrv.c b/src/d_clisrv.c index c9099d78c..b6a46a8fd 100644 --- a/src/d_clisrv.c +++ b/src/d_clisrv.c @@ -2984,6 +2984,8 @@ static void Command_Nodes(void) CONS_Printf(" [%.4d PWR]", clientpowerlevels[i][K_UsingPowerLevels()]); } + CONS_Printf(" [%d games]", SV_GetStatsByPlayerIndex(i)->finishedrounds); + CONS_Printf(" [RRID-%s]", GetPrettyRRID(players[i].public_key, true)); @@ -4195,10 +4197,10 @@ boolean SV_SpawnServer(void) SINT8 node = 0; for (; node < MAXNETNODES; node++) result |= SV_AddWaitingPlayers(node, availabilitiesbuffer, - cv_playername[0].zstring, PR_GetLocalPlayerProfile(0)->public_key, SV_RetrieveStats(PR_GetLocalPlayerProfile(0)->public_key)->powerlevels, - cv_playername[1].zstring, PR_GetLocalPlayerProfile(1)->public_key, SV_RetrieveStats(PR_GetLocalPlayerProfile(1)->public_key)->powerlevels, - cv_playername[2].zstring, PR_GetLocalPlayerProfile(2)->public_key, SV_RetrieveStats(PR_GetLocalPlayerProfile(2)->public_key)->powerlevels, - cv_playername[3].zstring, PR_GetLocalPlayerProfile(3)->public_key, SV_RetrieveStats(PR_GetLocalPlayerProfile(3)->public_key)->powerlevels); + cv_playername[0].zstring, PR_GetLocalPlayerProfile(0)->public_key, SV_GetStatsByKey(PR_GetLocalPlayerProfile(0)->public_key)->powerlevels, + cv_playername[1].zstring, PR_GetLocalPlayerProfile(1)->public_key, SV_GetStatsByKey(PR_GetLocalPlayerProfile(1)->public_key)->powerlevels, + cv_playername[2].zstring, PR_GetLocalPlayerProfile(2)->public_key, SV_GetStatsByKey(PR_GetLocalPlayerProfile(2)->public_key)->powerlevels, + cv_playername[3].zstring, PR_GetLocalPlayerProfile(3)->public_key, SV_GetStatsByKey(PR_GetLocalPlayerProfile(3)->public_key)->powerlevels); } return result; #endif @@ -4517,10 +4519,10 @@ static void HandleConnect(SINT8 node) } SV_AddWaitingPlayers(node, availabilitiesbuffer, - names[0], lastReceivedKey[node][0], SV_RetrieveStats(lastReceivedKey[node][0])->powerlevels, - names[1], lastReceivedKey[node][1], SV_RetrieveStats(lastReceivedKey[node][1])->powerlevels, - names[2], lastReceivedKey[node][2], SV_RetrieveStats(lastReceivedKey[node][2])->powerlevels, - names[3], lastReceivedKey[node][3], SV_RetrieveStats(lastReceivedKey[node][3])->powerlevels); + names[0], lastReceivedKey[node][0], SV_GetStatsByKey(lastReceivedKey[node][0])->powerlevels, + names[1], lastReceivedKey[node][1], SV_GetStatsByKey(lastReceivedKey[node][1])->powerlevels, + names[2], lastReceivedKey[node][2], SV_GetStatsByKey(lastReceivedKey[node][2])->powerlevels, + names[3], lastReceivedKey[node][3], SV_GetStatsByKey(lastReceivedKey[node][3])->powerlevels); joindelay += cv_joindelay.value * TICRATE; player_joining = true; } diff --git a/src/k_pwrlv.c b/src/k_pwrlv.c index 394e91c50..2500a64f1 100644 --- a/src/k_pwrlv.c +++ b/src/k_pwrlv.c @@ -430,8 +430,6 @@ void K_CashInPowerLevels(void) //CONS_Printf("%s: %d -> %d (%d)\n", player_names[i], clientpowerlevels[i][powerType] - inc, clientpowerlevels[i][powerType], inc); } - - clientPowerAdd[i] = 0; } SV_UpdateStats(); diff --git a/src/k_serverstats.c b/src/k_serverstats.c index 7959d3dc5..28aad5192 100644 --- a/src/k_serverstats.c +++ b/src/k_serverstats.c @@ -112,6 +112,13 @@ void SV_LoadStats(void) { trackedList[i].powerlevels[j] = READUINT16(save.p); } + + // Migration 1 -> 2: Add finishedrounds + if (version < 2) + trackedList[i].finishedrounds = 0; + else + trackedList[i].finishedrounds = READUINT32(save.p); + trackedList[i].hash = quickncasehash((char*)trackedList[i].public_key, PUBKEYLENGTH); } } @@ -149,6 +156,7 @@ void SV_SaveStats(void) { WRITEUINT16(save.p, trackedList[i].powerlevels[j]); } + WRITEUINT32(save.p, trackedList[i].finishedrounds); } length = save.p - save.buffer; @@ -162,7 +170,7 @@ void SV_SaveStats(void) } // New player, grab their stats from trackedList or initialize new ones if they're new -serverplayer_t *SV_RetrieveStats(uint8_t *key) +serverplayer_t *SV_GetStatsByKey(uint8_t *key) { UINT32 j, hash; @@ -189,6 +197,7 @@ serverplayer_t *SV_RetrieveStats(uint8_t *key) { trackedList[numtracked].powerlevels[j] = PR_IsKeyGuest(key) ? 0 : PWRLVRECORD_START; } + trackedList[numtracked].finishedrounds = 0; trackedList[numtracked].hash = quickncasehash((char*)key, PUBKEYLENGTH); numtracked++; @@ -196,7 +205,18 @@ serverplayer_t *SV_RetrieveStats(uint8_t *key) return &trackedList[numtracked - 1]; } +serverplayer_t *SV_GetStatsByPlayerIndex(UINT8 p) +{ + return SV_GetStatsByKey(players[p].public_key); +} + +serverplayer_t *SV_GetStats(player_t *player) +{ + return SV_GetStatsByKey(player->public_key); +} + // Write player stats to trackedList, then save to disk +// (NB: Some stats changes are made directly to trackedList via K_CashInPowerLevels) void SV_UpdateStats(void) { UINT32 i, j, hash; @@ -234,3 +254,21 @@ void SV_UpdateStats(void) SV_SaveStats(); } + +void SV_BumpMatchStats(void) +{ + int i; + + for (i = 0; i < MAXPLAYERS; i++) + { + if (!playeringame[i]) + continue; + if (players[i].spectator) + continue; + + serverplayer_t *stat = SV_GetStatsByPlayerIndex(i); + + if (!(players[i].pflags & PF_NOCONTEST)) + stat->finishedrounds++; + } +} \ No newline at end of file diff --git a/src/k_serverstats.h b/src/k_serverstats.h index d739a5185..b7e1c5cd8 100644 --- a/src/k_serverstats.h +++ b/src/k_serverstats.h @@ -27,13 +27,14 @@ extern "C" { #define SERVERSTATSFILE "srvstats.dat" #define SERVERSTATSHEADER "Doctor Robotnik's Ring Racers Server Stats" -#define SERVERSTATSVER 1 +#define SERVERSTATSVER 2 struct serverplayer_t { uint8_t public_key[PUBKEYLENGTH]; UINT32 lastseen; UINT16 powerlevels[PWRLV_NUMTYPES]; + UINT32 finishedrounds; UINT32 hash; // Not persisted! Used for early outs during key comparisons }; @@ -42,10 +43,14 @@ void SV_SaveStats(void); void SV_LoadStats(void); -serverplayer_t *SV_RetrieveStats(uint8_t *key); +serverplayer_t *SV_GetStatsByKey(uint8_t *key); +serverplayer_t *SV_GetStatsByPlayerIndex(UINT8 p); +serverplayer_t *SV_GetStats(player_t *player); void SV_UpdateStats(void); +void SV_BumpMatchStats(void); + #ifdef __cplusplus } // extern "C" #endif diff --git a/src/y_inter.c b/src/y_inter.c index 3636834e2..6c6c4e8e5 100644 --- a/src/y_inter.c +++ b/src/y_inter.c @@ -47,6 +47,7 @@ #include "k_boss.h" #include "k_pwrlv.h" #include "k_grandprix.h" +#include "k_serverstats.h" // SV_BumpMatchStats #ifdef HWRENDER #include "hardware/hw_main.h" @@ -847,6 +848,7 @@ void Y_StartIntermission(void) } K_CashInPowerLevels(); + SV_BumpMatchStats(); } Automate_Run(AEV_INTERMISSIONSTART); From b752ccdfe78399aec7db40568f3cc394a54fc5e6 Mon Sep 17 00:00:00 2001 From: AJ Martinez Date: Mon, 24 Apr 2023 20:16:03 -0700 Subject: [PATCH 02/16] Clarify some guest stuff --- src/k_serverstats.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/k_serverstats.c b/src/k_serverstats.c index 28aad5192..77ba7bb4a 100644 --- a/src/k_serverstats.c +++ b/src/k_serverstats.c @@ -191,6 +191,9 @@ serverplayer_t *SV_GetStatsByKey(uint8_t *key) SV_ExpandStats(numtracked+1); // Default stats + // (NB: This will make a GUEST record if someone tries to retrieve GUEST stats, because + // at the very least we should try to provide other codepaths the right _data type_, + // but it will not be written back.) trackedList[numtracked].lastseen = time(NULL); memcpy(&trackedList[numtracked].public_key, key, PUBKEYLENGTH); for(j = 0; j < PWRLV_NUMTYPES; j++) @@ -215,8 +218,8 @@ serverplayer_t *SV_GetStats(player_t *player) return SV_GetStatsByKey(player->public_key); } -// Write player stats to trackedList, then save to disk -// (NB: Some stats changes are made directly to trackedList via K_CashInPowerLevels) +// Write clientpowerlevels and timestamps back to matching trackedList entries, then save trackedList to disk +// (NB: Stats changes can be made directly to trackedList through other paths, but will only write to disk here) void SV_UpdateStats(void) { UINT32 i, j, hash; @@ -265,6 +268,8 @@ void SV_BumpMatchStats(void) continue; if (players[i].spectator) continue; + if (PR_IsKeyGuest(players[i].public_key)) + continue; serverplayer_t *stat = SV_GetStatsByPlayerIndex(i); From 00e25ee593c9ca462b1faa325607ce410dbb6229 Mon Sep 17 00:00:00 2001 From: AJ Martinez Date: Fri, 28 Apr 2023 02:10:50 -0700 Subject: [PATCH 03/16] WIP: XD_REQSAY --- src/d_clisrv.c | 3 +++ src/d_netcmd.c | 73 +++++++++++++++++++++++++++++++++++++++++++++++++- src/d_netcmd.h | 3 +++ src/hu_stuff.c | 60 ++--------------------------------------- src/hu_stuff.h | 8 ++++++ 5 files changed, 88 insertions(+), 59 deletions(-) diff --git a/src/d_clisrv.c b/src/d_clisrv.c index b6a46a8fd..cc87ff24b 100644 --- a/src/d_clisrv.c +++ b/src/d_clisrv.c @@ -3625,6 +3625,9 @@ void D_ClientServerInit(void) RegisterNetXCmd(XD_ADDPLAYER, Got_AddPlayer); RegisterNetXCmd(XD_REMOVEPLAYER, Got_RemovePlayer); RegisterNetXCmd(XD_ADDBOT, Got_AddBot); + RegisterNetXCmd(XD_SAY, Got_Saycmd); + RegisterNetXCmd(XD_REQSAY, Got_RequestSaycmd); + #ifdef DUMPCONSISTENCY CV_RegisterVar(&cv_dumpconsistency); #endif diff --git a/src/d_netcmd.c b/src/d_netcmd.c index 0f351c201..44fc30368 100644 --- a/src/d_netcmd.c +++ b/src/d_netcmd.c @@ -65,6 +65,7 @@ #include "k_race.h" #include "g_party.h" #include "k_vote.h" +#include "hu_stuff.h" // HU_ flags #ifdef SRB2_CONFIG_ENABLE_WEBM_MOVIES #include "m_avrecorder.h" @@ -579,7 +580,6 @@ size_t schedule_len = 0; // Automation commands char *automate_commands[AEV__MAX]; - const char *automate_names[AEV__MAX] = { "RoundStart", // AEV_ROUNDSTART @@ -589,6 +589,8 @@ const char *automate_names[AEV__MAX] = UINT32 livestudioaudience_timer = 90; +static tic_t stop_spamming[MAXPLAYERS]; + /// \warning Keep this up-to-date if you add/remove/rename net text commands const char *netxcmdnames[MAXNETXCMD - 1] = { @@ -632,6 +634,7 @@ const char *netxcmdnames[MAXNETXCMD - 1] = "AUTOMATE", // XD_AUTOMATE "REQMAPQUEUE", // XD_REQMAPQUEUE "MAPQUEUE", // XD_MAPQUEUE + "REQSAY", // XD_REQSAY }; // ========================================================================= @@ -3488,6 +3491,74 @@ static void Got_MapQueuecmd(UINT8 **cp, INT32 playernum) CONS_Printf("queuemap: A map was added to the round queue (pos. %u)\n", queueposition+1); } +void Got_RequestSaycmd(UINT8 **p, INT32 playernum) +{ + SINT8 target; + UINT8 flags; + const char *dispname; + char *msg; + boolean action = false; + char *ptr; + INT32 spam_eatmsg = 0; + + UINT8 *original = *p; + + // Only the server processes this message. + if (client) + return; + + CONS_Debug(DBG_NETPLAY,"Received REQSAY cmd from Player %d (%s)\n", playernum+1, player_names[playernum]); + + target = READSINT8(*p); + flags = READUINT8(*p); + msg = (char *)*p; + SKIPSTRINGL(*p, HU_MAXMSGLEN + 1); + + if ((cv_mute.value || flags & (HU_CSAY|HU_SHOUT)) && playernum != serverplayer && !(IsPlayerAdmin(playernum))) + { + CONS_Alert(CONS_WARNING, cv_mute.value ? + M_GetText("Illegal reqsay command received from %s while muted\n") : M_GetText("Illegal csay command received from non-admin %s\n"), + player_names[playernum]); + if (server) + SendKick(playernum, KICK_MSG_CON_FAIL); + return; + } + + //check for invalid characters (0x80 or above) + { + size_t i; + const size_t j = strlen(msg); + for (i = 0; i < j; i++) + { + if (msg[i] & 0x80) + { + CONS_Alert(CONS_WARNING, M_GetText("Illegal reqsay command received from %s containing invalid characters\n"), player_names[playernum]); + if (server) + SendKick(playernum, KICK_MSG_CON_FAIL); + return; + } + } + } + + // before we do anything, let's verify the guy isn't spamming, get this easier on us. + + //if (stop_spamming[playernum] != 0 && cv_chatspamprotection.value && !(flags & HU_CSAY)) + if (stop_spamming[playernum] != 0 && consoleplayer != playernum && cv_chatspamprotection.value && !(flags & (HU_CSAY|HU_SHOUT))) + { + CONS_Debug(DBG_NETPLAY,"Received REQSAY cmd too quickly from Player %d (%s), assuming as spam and blocking message.\n", playernum+1, player_names[playernum]); + stop_spamming[playernum] = 4; + spam_eatmsg = 1; + } + else + stop_spamming[playernum] = 4; // you can hold off for 4 tics, can you? + + if (spam_eatmsg) + return; // don't proceed if we were supposed to eat the message. + + SendNetXCmd(XD_SAY, original, *p - original); +} + + static void Command_Pause(void) { UINT8 buf[2]; diff --git a/src/d_netcmd.h b/src/d_netcmd.h index 94883ce92..8e9d11d9f 100644 --- a/src/d_netcmd.h +++ b/src/d_netcmd.h @@ -181,6 +181,7 @@ typedef enum XD_AUTOMATE, // 36 XD_REQMAPQUEUE, // 37 XD_MAPQUEUE, // 38 + XD_REQSAY, // 39 MAXNETXCMD } netxcmd_t; @@ -288,6 +289,8 @@ void D_Cheat(INT32 playernum, INT32 cheat, ...); // used for the player setup menu boolean CanChangeSkin(INT32 playernum); +void Got_RequestSaycmd(UINT8 **cp, INT32 playernum); + #ifdef __cplusplus } // extern "C" #endif diff --git a/src/hu_stuff.c b/src/hu_stuff.c index 5b028662d..33c824e79 100644 --- a/src/hu_stuff.c +++ b/src/hu_stuff.c @@ -65,12 +65,6 @@ #define HU_INPUTX 0 #define HU_INPUTY 0 -typedef enum -{ - HU_SHOUT = 1, // Shout message - HU_CSAY = 1<<1, // Middle-of-screen server message -} sayflags_t; - //------------------------------------------- // heads up font //------------------------------------------- @@ -180,7 +174,6 @@ static void Command_Sayto_f(void); static void Command_Sayteam_f(void); static void Command_CSay_f(void); static void Command_Shout(void); -static void Got_Saycmd(UINT8 **p, INT32 playernum); void HU_LoadGraphics(void) { @@ -226,7 +219,6 @@ void HU_Init(void) COM_AddCommand("sayteam", Command_Sayteam_f); COM_AddCommand("csay", Command_CSay_f); COM_AddCommand("shout", Command_Shout); - RegisterNetXCmd(XD_SAY, Got_Saycmd); // only allocate if not present, to save us a lot of headache if (missingpat == NULL) @@ -695,7 +687,7 @@ static tic_t stop_spamming[MAXPLAYERS]; * \sa DoSayCommand * \author Graue */ -static void Got_Saycmd(UINT8 **p, INT32 playernum) +void Got_Saycmd(UINT8 **p, INT32 playernum) { SINT8 target; UINT8 flags; @@ -707,57 +699,9 @@ static void Got_Saycmd(UINT8 **p, INT32 playernum) CONS_Debug(DBG_NETPLAY,"Received SAY cmd from Player %d (%s)\n", playernum+1, player_names[playernum]); - target = READSINT8(*p); - flags = READUINT8(*p); - msg = (char *)*p; - SKIPSTRINGL(*p, HU_MAXMSGLEN + 1); - - if ((cv_mute.value || flags & (HU_CSAY|HU_SHOUT)) && playernum != serverplayer && !(IsPlayerAdmin(playernum))) - { - CONS_Alert(CONS_WARNING, cv_mute.value ? - M_GetText("Illegal say command received from %s while muted\n") : M_GetText("Illegal csay command received from non-admin %s\n"), - player_names[playernum]); - if (server) - SendKick(playernum, KICK_MSG_CON_FAIL); - return; - } - - //check for invalid characters (0x80 or above) - { - size_t i; - const size_t j = strlen(msg); - for (i = 0; i < j; i++) - { - if (msg[i] & 0x80) - { - CONS_Alert(CONS_WARNING, M_GetText("Illegal say command received from %s containing invalid characters\n"), player_names[playernum]); - if (server) - SendKick(playernum, KICK_MSG_CON_FAIL); - return; - } - } - } - - // before we do anything, let's verify the guy isn't spamming, get this easier on us. - - //if (stop_spamming[playernum] != 0 && cv_chatspamprotection.value && !(flags & HU_CSAY)) - if (stop_spamming[playernum] != 0 && consoleplayer != playernum && cv_chatspamprotection.value && !(flags & (HU_CSAY|HU_SHOUT))) - { - CONS_Debug(DBG_NETPLAY,"Received SAY cmd too quickly from Player %d (%s), assuming as spam and blocking message.\n", playernum+1, player_names[playernum]); - stop_spamming[playernum] = 4; - spam_eatmsg = 1; - } - else - stop_spamming[playernum] = 4; // you can hold off for 4 tics, can you? - - // run the lua hook even if we were supposed to eat the msg, netgame consistency goes first. - if (LUA_HookPlayerMsg(playernum, target, flags, msg, spam_eatmsg)) return; - if (spam_eatmsg) - return; // don't proceed if we were supposed to eat the message. - // If it's a CSAY, just CECHO and be done with it. if (flags & HU_CSAY) { @@ -1161,7 +1105,7 @@ static void HU_sendChatMessage(void) buf[0] = target; buf[1] = ((server || IsPlayerAdmin(consoleplayer)) && cv_autoshout.value) ? HU_SHOUT : 0; // flags - SendNetXCmd(XD_SAY, buf, 2 + strlen(&buf[2]) + 1); + SendNetXCmd(XD_REQSAY, buf, 2 + strlen(&buf[2]) + 1); } } diff --git a/src/hu_stuff.h b/src/hu_stuff.h index 08b997bdc..ca0b265d7 100644 --- a/src/hu_stuff.h +++ b/src/hu_stuff.h @@ -162,6 +162,14 @@ void HU_ClearTitlecardCEcho(void); extern UINT32 hu_demotime; extern UINT32 hu_demolap; +void Got_Saycmd(UINT8 **p, INT32 playernum); + +typedef enum +{ + HU_SHOUT = 1, // Shout message + HU_CSAY = 1<<1, // Middle-of-screen server message +} sayflags_t; + #ifdef __cplusplus } // extern "C" #endif From 07eb7dea19f3747de8de3e1f6634fa05d1f93d74 Mon Sep 17 00:00:00 2001 From: AJ Martinez Date: Fri, 28 Apr 2023 17:14:32 -0700 Subject: [PATCH 04/16] Revert "WIP: XD_REQSAY" This reverts commit 00e25ee593c9ca462b1faa325607ce410dbb6229. --- src/d_clisrv.c | 3 --- src/d_netcmd.c | 73 +------------------------------------------------- src/d_netcmd.h | 3 --- src/hu_stuff.c | 60 +++++++++++++++++++++++++++++++++++++++-- src/hu_stuff.h | 8 ------ 5 files changed, 59 insertions(+), 88 deletions(-) diff --git a/src/d_clisrv.c b/src/d_clisrv.c index cc87ff24b..b6a46a8fd 100644 --- a/src/d_clisrv.c +++ b/src/d_clisrv.c @@ -3625,9 +3625,6 @@ void D_ClientServerInit(void) RegisterNetXCmd(XD_ADDPLAYER, Got_AddPlayer); RegisterNetXCmd(XD_REMOVEPLAYER, Got_RemovePlayer); RegisterNetXCmd(XD_ADDBOT, Got_AddBot); - RegisterNetXCmd(XD_SAY, Got_Saycmd); - RegisterNetXCmd(XD_REQSAY, Got_RequestSaycmd); - #ifdef DUMPCONSISTENCY CV_RegisterVar(&cv_dumpconsistency); #endif diff --git a/src/d_netcmd.c b/src/d_netcmd.c index 44fc30368..0f351c201 100644 --- a/src/d_netcmd.c +++ b/src/d_netcmd.c @@ -65,7 +65,6 @@ #include "k_race.h" #include "g_party.h" #include "k_vote.h" -#include "hu_stuff.h" // HU_ flags #ifdef SRB2_CONFIG_ENABLE_WEBM_MOVIES #include "m_avrecorder.h" @@ -580,6 +579,7 @@ size_t schedule_len = 0; // Automation commands char *automate_commands[AEV__MAX]; + const char *automate_names[AEV__MAX] = { "RoundStart", // AEV_ROUNDSTART @@ -589,8 +589,6 @@ const char *automate_names[AEV__MAX] = UINT32 livestudioaudience_timer = 90; -static tic_t stop_spamming[MAXPLAYERS]; - /// \warning Keep this up-to-date if you add/remove/rename net text commands const char *netxcmdnames[MAXNETXCMD - 1] = { @@ -634,7 +632,6 @@ const char *netxcmdnames[MAXNETXCMD - 1] = "AUTOMATE", // XD_AUTOMATE "REQMAPQUEUE", // XD_REQMAPQUEUE "MAPQUEUE", // XD_MAPQUEUE - "REQSAY", // XD_REQSAY }; // ========================================================================= @@ -3491,74 +3488,6 @@ static void Got_MapQueuecmd(UINT8 **cp, INT32 playernum) CONS_Printf("queuemap: A map was added to the round queue (pos. %u)\n", queueposition+1); } -void Got_RequestSaycmd(UINT8 **p, INT32 playernum) -{ - SINT8 target; - UINT8 flags; - const char *dispname; - char *msg; - boolean action = false; - char *ptr; - INT32 spam_eatmsg = 0; - - UINT8 *original = *p; - - // Only the server processes this message. - if (client) - return; - - CONS_Debug(DBG_NETPLAY,"Received REQSAY cmd from Player %d (%s)\n", playernum+1, player_names[playernum]); - - target = READSINT8(*p); - flags = READUINT8(*p); - msg = (char *)*p; - SKIPSTRINGL(*p, HU_MAXMSGLEN + 1); - - if ((cv_mute.value || flags & (HU_CSAY|HU_SHOUT)) && playernum != serverplayer && !(IsPlayerAdmin(playernum))) - { - CONS_Alert(CONS_WARNING, cv_mute.value ? - M_GetText("Illegal reqsay command received from %s while muted\n") : M_GetText("Illegal csay command received from non-admin %s\n"), - player_names[playernum]); - if (server) - SendKick(playernum, KICK_MSG_CON_FAIL); - return; - } - - //check for invalid characters (0x80 or above) - { - size_t i; - const size_t j = strlen(msg); - for (i = 0; i < j; i++) - { - if (msg[i] & 0x80) - { - CONS_Alert(CONS_WARNING, M_GetText("Illegal reqsay command received from %s containing invalid characters\n"), player_names[playernum]); - if (server) - SendKick(playernum, KICK_MSG_CON_FAIL); - return; - } - } - } - - // before we do anything, let's verify the guy isn't spamming, get this easier on us. - - //if (stop_spamming[playernum] != 0 && cv_chatspamprotection.value && !(flags & HU_CSAY)) - if (stop_spamming[playernum] != 0 && consoleplayer != playernum && cv_chatspamprotection.value && !(flags & (HU_CSAY|HU_SHOUT))) - { - CONS_Debug(DBG_NETPLAY,"Received REQSAY cmd too quickly from Player %d (%s), assuming as spam and blocking message.\n", playernum+1, player_names[playernum]); - stop_spamming[playernum] = 4; - spam_eatmsg = 1; - } - else - stop_spamming[playernum] = 4; // you can hold off for 4 tics, can you? - - if (spam_eatmsg) - return; // don't proceed if we were supposed to eat the message. - - SendNetXCmd(XD_SAY, original, *p - original); -} - - static void Command_Pause(void) { UINT8 buf[2]; diff --git a/src/d_netcmd.h b/src/d_netcmd.h index 8e9d11d9f..94883ce92 100644 --- a/src/d_netcmd.h +++ b/src/d_netcmd.h @@ -181,7 +181,6 @@ typedef enum XD_AUTOMATE, // 36 XD_REQMAPQUEUE, // 37 XD_MAPQUEUE, // 38 - XD_REQSAY, // 39 MAXNETXCMD } netxcmd_t; @@ -289,8 +288,6 @@ void D_Cheat(INT32 playernum, INT32 cheat, ...); // used for the player setup menu boolean CanChangeSkin(INT32 playernum); -void Got_RequestSaycmd(UINT8 **cp, INT32 playernum); - #ifdef __cplusplus } // extern "C" #endif diff --git a/src/hu_stuff.c b/src/hu_stuff.c index 33c824e79..5b028662d 100644 --- a/src/hu_stuff.c +++ b/src/hu_stuff.c @@ -65,6 +65,12 @@ #define HU_INPUTX 0 #define HU_INPUTY 0 +typedef enum +{ + HU_SHOUT = 1, // Shout message + HU_CSAY = 1<<1, // Middle-of-screen server message +} sayflags_t; + //------------------------------------------- // heads up font //------------------------------------------- @@ -174,6 +180,7 @@ static void Command_Sayto_f(void); static void Command_Sayteam_f(void); static void Command_CSay_f(void); static void Command_Shout(void); +static void Got_Saycmd(UINT8 **p, INT32 playernum); void HU_LoadGraphics(void) { @@ -219,6 +226,7 @@ void HU_Init(void) COM_AddCommand("sayteam", Command_Sayteam_f); COM_AddCommand("csay", Command_CSay_f); COM_AddCommand("shout", Command_Shout); + RegisterNetXCmd(XD_SAY, Got_Saycmd); // only allocate if not present, to save us a lot of headache if (missingpat == NULL) @@ -687,7 +695,7 @@ static tic_t stop_spamming[MAXPLAYERS]; * \sa DoSayCommand * \author Graue */ -void Got_Saycmd(UINT8 **p, INT32 playernum) +static void Got_Saycmd(UINT8 **p, INT32 playernum) { SINT8 target; UINT8 flags; @@ -699,9 +707,57 @@ void Got_Saycmd(UINT8 **p, INT32 playernum) CONS_Debug(DBG_NETPLAY,"Received SAY cmd from Player %d (%s)\n", playernum+1, player_names[playernum]); + target = READSINT8(*p); + flags = READUINT8(*p); + msg = (char *)*p; + SKIPSTRINGL(*p, HU_MAXMSGLEN + 1); + + if ((cv_mute.value || flags & (HU_CSAY|HU_SHOUT)) && playernum != serverplayer && !(IsPlayerAdmin(playernum))) + { + CONS_Alert(CONS_WARNING, cv_mute.value ? + M_GetText("Illegal say command received from %s while muted\n") : M_GetText("Illegal csay command received from non-admin %s\n"), + player_names[playernum]); + if (server) + SendKick(playernum, KICK_MSG_CON_FAIL); + return; + } + + //check for invalid characters (0x80 or above) + { + size_t i; + const size_t j = strlen(msg); + for (i = 0; i < j; i++) + { + if (msg[i] & 0x80) + { + CONS_Alert(CONS_WARNING, M_GetText("Illegal say command received from %s containing invalid characters\n"), player_names[playernum]); + if (server) + SendKick(playernum, KICK_MSG_CON_FAIL); + return; + } + } + } + + // before we do anything, let's verify the guy isn't spamming, get this easier on us. + + //if (stop_spamming[playernum] != 0 && cv_chatspamprotection.value && !(flags & HU_CSAY)) + if (stop_spamming[playernum] != 0 && consoleplayer != playernum && cv_chatspamprotection.value && !(flags & (HU_CSAY|HU_SHOUT))) + { + CONS_Debug(DBG_NETPLAY,"Received SAY cmd too quickly from Player %d (%s), assuming as spam and blocking message.\n", playernum+1, player_names[playernum]); + stop_spamming[playernum] = 4; + spam_eatmsg = 1; + } + else + stop_spamming[playernum] = 4; // you can hold off for 4 tics, can you? + + // run the lua hook even if we were supposed to eat the msg, netgame consistency goes first. + if (LUA_HookPlayerMsg(playernum, target, flags, msg, spam_eatmsg)) return; + if (spam_eatmsg) + return; // don't proceed if we were supposed to eat the message. + // If it's a CSAY, just CECHO and be done with it. if (flags & HU_CSAY) { @@ -1105,7 +1161,7 @@ static void HU_sendChatMessage(void) buf[0] = target; buf[1] = ((server || IsPlayerAdmin(consoleplayer)) && cv_autoshout.value) ? HU_SHOUT : 0; // flags - SendNetXCmd(XD_REQSAY, buf, 2 + strlen(&buf[2]) + 1); + SendNetXCmd(XD_SAY, buf, 2 + strlen(&buf[2]) + 1); } } diff --git a/src/hu_stuff.h b/src/hu_stuff.h index ca0b265d7..08b997bdc 100644 --- a/src/hu_stuff.h +++ b/src/hu_stuff.h @@ -162,14 +162,6 @@ void HU_ClearTitlecardCEcho(void); extern UINT32 hu_demotime; extern UINT32 hu_demolap; -void Got_Saycmd(UINT8 **p, INT32 playernum); - -typedef enum -{ - HU_SHOUT = 1, // Shout message - HU_CSAY = 1<<1, // Middle-of-screen server message -} sayflags_t; - #ifdef __cplusplus } // extern "C" #endif From cb8e48d8a7850c820462f8470033c0627f34af64 Mon Sep 17 00:00:00 2001 From: AJ Martinez Date: Sat, 29 Apr 2023 16:12:23 -0700 Subject: [PATCH 05/16] WIP: PT_SAY --- src/d_clisrv.c | 46 ++++++++++++++- src/d_clisrv.h | 16 ++++++ src/d_net.c | 1 + src/hu_stuff.c | 151 ++++++++----------------------------------------- src/hu_stuff.h | 3 +- src/typedef.h | 1 + 6 files changed, 88 insertions(+), 130 deletions(-) diff --git a/src/d_clisrv.c b/src/d_clisrv.c index b6a46a8fd..e40d9d7d6 100644 --- a/src/d_clisrv.c +++ b/src/d_clisrv.c @@ -5241,10 +5241,16 @@ static void HandlePacketFromPlayer(SINT8 node) textcmd[0] += (UINT8)netbuffer->u.textcmd[0]; } break; - case PT_LOGIN: + case PT_SAY: if (client) break; + say_pak say = netbuffer->u.say; + DoSayCommand(say.message, say.target, say.flags, say.source); + break; + case PT_LOGIN: + if (client) + break; #ifndef NOMD5 if (doomcom->datalength < 16)/* ignore partial sends */ break; @@ -6944,3 +6950,41 @@ void D_MD5PasswordPass(const UINT8 *buffer, size_t len, const char *salt, void * md5_buffer(tmpbuf, 256, dest); #endif } + +// Want to say something? XD_SAY is server only, gotta request that they send one on our behalf +void DoSayPacket(SINT8 target, UINT8 flags, UINT8 source, char *message) +{ + say_pak *packet = (void*)&netbuffer->u.say; + netbuffer->packettype = PT_SAY; + + memset(packet->message, 0, sizeof(packet->message)); + strcpy(packet->message, message); + + packet->source = source; + packet->flags = flags; + packet->target = target; + + HSendPacket(servernode, false, 0, sizeof(say_pak)); +} + +void DoSayPacketFromCommand(SINT8 target, size_t usedargs, UINT8 flags) +{ + char buf[2 + HU_MAXMSGLEN + 1]; + size_t numwords, ix; + char *msg = &buf[3]; + const size_t msgspace = sizeof buf - 2; + + numwords = COM_Argc() - usedargs; + I_Assert(numwords > 0); + + msg[0] = '\0'; + + for (ix = 0; ix < numwords; ix++) + { + if (ix > 0) + strlcat(msg, " ", msgspace); + strlcat(msg, COM_Argv(ix + usedargs), msgspace); + } + + DoSayPacket(target, flags, consoleplayer, msg); +} \ No newline at end of file diff --git a/src/d_clisrv.h b/src/d_clisrv.h index fee63b0ec..5f194498f 100644 --- a/src/d_clisrv.h +++ b/src/d_clisrv.h @@ -41,6 +41,8 @@ applications may follow different packet versions. // one that defines the actual packets to // be transmitted. +#define HU_MAXMSGLEN 223 + // Networking and tick handling related. #define BACKUPTICS 512 // more than enough for most timeouts.... #define CLIENTBACKUPTICS 32 @@ -127,6 +129,8 @@ typedef enum PT_RESPONSEALL, // OK, here is my signature on that random bullshit PT_RESULTSALL, // Here's what everyone responded to PT_CHALLENGEALL with, if this is wrong or you don't receive it disconnect + PT_SAY, // "Hey server, please send this chat message to everyone via XD_SAY" + NUMPACKETTYPE } packettype_t; @@ -385,6 +389,14 @@ struct resultsall_pak uint8_t signature[MAXPLAYERS][SIGNATURELENGTH]; } ATTRPACK; +struct say_pak +{ + char message[HU_MAXMSGLEN + 1]; + UINT8 target; + UINT8 flags; + UINT8 source; +} ATTRPACK; + // // Network packet data // @@ -427,6 +439,7 @@ struct doomdata_t challengeall_pak challengeall; // 256 bytes responseall_pak responseall; // 256 bytes resultsall_pak resultsall; // 1024 bytes. Also, you really shouldn't trust anything here. + say_pak say; // I don't care anymore. } u; // This is needed to pack diff packet types data together } ATTRPACK; @@ -630,6 +643,9 @@ rewind_t *CL_RewindToTime(tic_t time); void HandleSigfail(const char *string); +void DoSayPacket(SINT8 target, UINT8 flags, UINT8 source, char *message); +void DoSayPacketFromCommand(SINT8 target, size_t usedargs, UINT8 flags); + #ifdef __cplusplus } // extern "C" #endif diff --git a/src/d_net.c b/src/d_net.c index 917b181fe..c04ec400b 100644 --- a/src/d_net.c +++ b/src/d_net.c @@ -1023,6 +1023,7 @@ static boolean ShouldDropPacket(void) case PT_LOGIN: case PT_ASKLUAFILE: case PT_SENDINGLUAFILE: + case PT_SAY: return true; default: return false; diff --git a/src/hu_stuff.c b/src/hu_stuff.c index 5b028662d..a64c5548b 100644 --- a/src/hu_stuff.c +++ b/src/hu_stuff.c @@ -60,6 +60,7 @@ #include "k_color.h" #include "k_hud.h" #include "r_fps.h" +#include "d_clisrv.h" // coords are scaled #define HU_INPUTX 0 @@ -478,7 +479,6 @@ void HU_AddChatText(const char *text, boolean playsound) CON_LogMessage(va("%s\n", text)); } - /** Runs a say command, sending an ::XD_SAY message. * A say command consists of a signed 8-bit integer for the target, an * unsigned 8-bit flag variable, and then the message itself. @@ -498,27 +498,10 @@ void HU_AddChatText(const char *text, boolean playsound) * \author Graue */ -static void DoSayCommand(SINT8 target, size_t usedargs, UINT8 flags) +void DoSayCommand(char *message, SINT8 target, UINT8 flags, UINT8 source) { char buf[2 + HU_MAXMSGLEN + 1]; - size_t numwords, ix; - char *msg = &buf[2]; - const size_t msgspace = sizeof buf - 2; - - numwords = COM_Argc() - usedargs; - I_Assert(numwords > 0); - - if (CHAT_MUTE) // TODO: Per Player mute. - { - HU_AddChatText(va("%s>ERROR: The chat is muted. You can't say anything.", "\x85"), false); - return; - } - - // Only servers/admins can shout or CSAY. - if (!server && !IsPlayerAdmin(consoleplayer)) - { - flags &= ~(HU_SHOUT|HU_CSAY); - } + char *msg = &buf[3]; // Enforce shout for the dedicated server. if (dedicated && !(flags & HU_CSAY)) @@ -528,66 +511,16 @@ static void DoSayCommand(SINT8 target, size_t usedargs, UINT8 flags) buf[0] = target; buf[1] = flags; + buf[2] = source; msg[0] = '\0'; - for (ix = 0; ix < numwords; ix++) - { - if (ix > 0) - strlcat(msg, " ", msgspace); - strlcat(msg, COM_Argv(ix + usedargs), msgspace); - } - - if (strlen(msg) > 4 && strnicmp(msg, "/pm", 3) == 0) // used /pm - { - // what we're gonna do now is check if the player exists - // with that logic, characters 4 and 5 are our numbers: - const char *newmsg; - char playernum[3]; - INT32 spc = 1; // used if playernum[1] is a space. - - strncpy(playernum, msg+3, 3); - - // check for undesirable characters in our "number" - if (((playernum[0] < '0') || (playernum[0] > '9')) || ((playernum[1] < '0') || (playernum[1] > '9'))) - { - // check if playernum[1] is a space - if (playernum[1] == ' ') - spc = 0; - // let it slide - else - { - HU_AddChatText("\x82NOTICE: \x80Invalid command format. Correct format is \'/pm \'.", false); - return; - } - } - // I'm very bad at C, I swear I am, additional checks eww! - if (spc != 0 && msg[5] != ' ') - { - HU_AddChatText("\x82NOTICE: \x80Invalid command format. Correct format is \'/pm \'.", false); - return; - } - - target = atoi(playernum); // turn that into a number - //CONS_Printf("%d\n", target); - - // check for target player, if it doesn't exist then we can't send the message! - if (target < MAXPLAYERS && playeringame[target]) // player exists - target++; // even though playernums are from 0 to 31, target is 1 to 32, so up that by 1 to have it work! - else - { - HU_AddChatText(va("\x82NOTICE: \x80Player %d does not exist.", target), false); // same - return; - } - buf[0] = target; - newmsg = msg+5+spc; - strlcpy(msg, newmsg, HU_MAXMSGLEN + 1); - } + strcpy(msg, message); SendNetXCmd(XD_SAY, buf, strlen(msg) + 1 + msg-buf); } /** Send a message to everyone. - * \sa DoSayCommand, Command_Sayteam_f, Command_Sayto_f + * \sa DoSayPacket, Command_Sayteam_f, Command_Sayto_f * \author Graue */ static void Command_Say_f(void) @@ -600,11 +533,11 @@ static void Command_Say_f(void) // Autoshout is handled by HU_queueChatChar. // If you're using the say command, you can use the shout command, lol. - DoSayCommand(0, 1, 0); + DoSayPacketFromCommand(0, 1, 0); } /** Send a message to a particular person. - * \sa DoSayCommand, Command_Sayteam_f, Command_Say_f + * \sa DoSayPacket, Command_Sayteam_f, Command_Say_f * \author Graue */ static void Command_Sayto_f(void) @@ -625,11 +558,11 @@ static void Command_Sayto_f(void) } target++; // Internally we use 0 to 31, but say command uses 1 to 32. - DoSayCommand((SINT8)target, 2, 0); + DoSayPacketFromCommand((SINT8)target, 2, 0); } /** Send a message to members of the player's team. - * \sa DoSayCommand, Command_Say_f, Command_Sayto_f + * \sa DoSayPacket, Command_Say_f, Command_Sayto_f * \author Graue */ static void Command_Sayteam_f(void) @@ -647,9 +580,9 @@ static void Command_Sayteam_f(void) } if (G_GametypeHasTeams()) // revert to normal say if we don't have teams in this gametype. - DoSayCommand(-1, 1, 0); + DoSayPacketFromCommand(-1, 1, 0); else - DoSayCommand(0, 1, 0); + DoSayPacketFromCommand(0, 1, 0); } /** Send a message to everyone, to be displayed by CECHO. Only @@ -669,7 +602,7 @@ static void Command_CSay_f(void) return; } - DoSayCommand(0, 1, HU_CSAY); + DoSayPacketFromCommand(0, 1, HU_CSAY); } static void Command_Shout(void) @@ -686,13 +619,13 @@ static void Command_Shout(void) return; } - DoSayCommand(0, 1, HU_SHOUT); + DoSayPacketFromCommand(0, 1, HU_SHOUT); } static tic_t stop_spamming[MAXPLAYERS]; /** Receives a message, processing an ::XD_SAY command. - * \sa DoSayCommand + * \sa DoSayPacket * \author Graue */ static void Got_Saycmd(UINT8 **p, INT32 playernum) @@ -707,21 +640,16 @@ static void Got_Saycmd(UINT8 **p, INT32 playernum) CONS_Debug(DBG_NETPLAY,"Received SAY cmd from Player %d (%s)\n", playernum+1, player_names[playernum]); + // Only server can ever legitimately send this + if (playernum != serverplayer) + return; + target = READSINT8(*p); flags = READUINT8(*p); + playernum = READUINT8(*p); msg = (char *)*p; SKIPSTRINGL(*p, HU_MAXMSGLEN + 1); - if ((cv_mute.value || flags & (HU_CSAY|HU_SHOUT)) && playernum != serverplayer && !(IsPlayerAdmin(playernum))) - { - CONS_Alert(CONS_WARNING, cv_mute.value ? - M_GetText("Illegal say command received from %s while muted\n") : M_GetText("Illegal csay command received from non-admin %s\n"), - player_names[playernum]); - if (server) - SendKick(playernum, KICK_MSG_CON_FAIL); - return; - } - //check for invalid characters (0x80 or above) { size_t i; @@ -738,25 +666,8 @@ static void Got_Saycmd(UINT8 **p, INT32 playernum) } } - // before we do anything, let's verify the guy isn't spamming, get this easier on us. - - //if (stop_spamming[playernum] != 0 && cv_chatspamprotection.value && !(flags & HU_CSAY)) - if (stop_spamming[playernum] != 0 && consoleplayer != playernum && cv_chatspamprotection.value && !(flags & (HU_CSAY|HU_SHOUT))) - { - CONS_Debug(DBG_NETPLAY,"Received SAY cmd too quickly from Player %d (%s), assuming as spam and blocking message.\n", playernum+1, player_names[playernum]); - stop_spamming[playernum] = 4; - spam_eatmsg = 1; - } - else - stop_spamming[playernum] = 4; // you can hold off for 4 tics, can you? - - // run the lua hook even if we were supposed to eat the msg, netgame consistency goes first. - - if (LUA_HookPlayerMsg(playernum, target, flags, msg, spam_eatmsg)) - return; - if (spam_eatmsg) - return; // don't proceed if we were supposed to eat the message. + return; // If it's a CSAY, just CECHO and be done with it. if (flags & HU_CSAY) @@ -1094,29 +1005,12 @@ static void HU_sendChatMessage(void) memset(w_chat, '\0', sizeof(w_chat)); c_input = 0; - // last minute mute check - if (CHAT_MUTE) - { - HU_AddChatText(va("%s>ERROR: The chat is muted. You can't say anything.", "\x85"), false); - return; - } - if (strlen(msg) > 4 && strnicmp(msg, "/pm", 3) == 0) // used /pm { INT32 spc = 1; // used if playernum[1] is a space. char playernum[3]; const char *newmsg; - // what we're gonna do now is check if the player exists - // with that logic, characters 4 and 5 are our numbers: - - // teamtalk can't send PMs, just don't send it, else everyone would be able to see it, and no one wants to see your sex RP sicko. - if (teamtalk) - { - HU_AddChatText(va("%sCannot send sayto in Say-Team.", "\x85"), false); - return; - } - strncpy(playernum, msg+3, 3); // check for undesirable characters in our "number" if (!(isdigit(playernum[0]) && isdigit(playernum[1]))) @@ -1161,7 +1055,8 @@ static void HU_sendChatMessage(void) buf[0] = target; buf[1] = ((server || IsPlayerAdmin(consoleplayer)) && cv_autoshout.value) ? HU_SHOUT : 0; // flags - SendNetXCmd(XD_SAY, buf, 2 + strlen(&buf[2]) + 1); + + DoSayPacket(target, buf[1], consoleplayer, msg); } } diff --git a/src/hu_stuff.h b/src/hu_stuff.h index 08b997bdc..ff8dedf49 100644 --- a/src/hu_stuff.h +++ b/src/hu_stuff.h @@ -95,7 +95,6 @@ struct playersort_t //------------------------------------ // chat stuff //------------------------------------ -#define HU_MAXMSGLEN 223 #define CHAT_BUFSIZE 64 // that's enough messages, right? We'll delete the older ones when that gets out of hand. #define NETSPLITSCREEN // why the hell WOULDN'T we want this? #ifdef NETSPLITSCREEN @@ -158,6 +157,8 @@ void HU_DoCEcho(const char *msg); void HU_DoTitlecardCEcho(const char *msg); void HU_ClearTitlecardCEcho(void); +void DoSayCommand(char *message, SINT8 target, UINT8 flags, UINT8 source); + // Demo playback info extern UINT32 hu_demotime; extern UINT32 hu_demolap; diff --git a/src/typedef.h b/src/typedef.h index 47d5fcf45..62f87cb79 100644 --- a/src/typedef.h +++ b/src/typedef.h @@ -76,6 +76,7 @@ TYPEDEF (serverchallenge_pak); TYPEDEF (challengeall_pak); TYPEDEF (responseall_pak); TYPEDEF (resultsall_pak); +TYPEDEF (say_pak); // d_event.h TYPEDEF (event_t); From 42251419e0b524d9b89f48932acdb2436e80e474 Mon Sep 17 00:00:00 2001 From: AJ Martinez Date: Sat, 29 Apr 2023 17:21:44 -0700 Subject: [PATCH 06/16] PT_SAY: check that source matches packet source --- src/d_clisrv.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/d_clisrv.c b/src/d_clisrv.c index e40d9d7d6..be16c4254 100644 --- a/src/d_clisrv.c +++ b/src/d_clisrv.c @@ -5243,9 +5243,13 @@ static void HandlePacketFromPlayer(SINT8 node) break; case PT_SAY: if (client) - break; + break; // Only sent to servers, why are we receiving this? say_pak say = netbuffer->u.say; + + if (playernode[say.source] != node) + break; // Spoofed source! + DoSayCommand(say.message, say.target, say.flags, say.source); break; case PT_LOGIN: From 346a7783659f1083aa8943f0595168da44ac6187 Mon Sep 17 00:00:00 2001 From: AJ Martinez Date: Sat, 29 Apr 2023 17:45:04 -0700 Subject: [PATCH 07/16] PT_SAY: Restore cv_mute and stop_spamming --- src/d_clisrv.c | 41 +++++++++++++++++++++++++++++++++++++++++ src/hu_stuff.c | 15 --------------- src/hu_stuff.h | 6 ++++++ 3 files changed, 47 insertions(+), 15 deletions(-) diff --git a/src/d_clisrv.c b/src/d_clisrv.c index be16c4254..dce348edc 100644 --- a/src/d_clisrv.c +++ b/src/d_clisrv.c @@ -222,6 +222,8 @@ consvar_t cv_httpsource = CVAR_INIT ("http_source", "", CV_SAVE, NULL, NULL); consvar_t cv_kicktime = CVAR_INIT ("kicktime", "10", CV_SAVE, CV_Unsigned, NULL); +static tic_t stop_spamming[MAXPLAYERS]; + // Generate a message for an authenticating client to sign, with some guarantees about who we are. void GenerateChallenge(uint8_t *buf) { @@ -5250,6 +5252,36 @@ static void HandlePacketFromPlayer(SINT8 node) if (playernode[say.source] != node) break; // Spoofed source! + if ((cv_mute.value || say.flags & (HU_CSAY|HU_SHOUT)) && say.source != serverplayer && !(IsPlayerAdmin(say.source))) + { + CONS_Debug(DBG_NETPLAY,"Received SAY cmd from Player %d (%s), but cv_mute is on.\n", say.source+1, player_names[say.source]); + break; + } + + { + size_t i; + const size_t j = strlen(say.message); + for (i = 0; i < j; i++) + { + if (say.message[i] & 0x80) + { + CONS_Alert(CONS_WARNING, M_GetText("Illegal say command received from %s containing invalid characters\n"), player_names[say.source]); + if (server) + SendKick(say.source, KICK_MSG_CON_FAIL); + return; + } + } + } + + if (stop_spamming[say.source] != 0 && consoleplayer != say.source && cv_chatspamprotection.value && !(say.flags & (HU_CSAY|HU_SHOUT))) + { + CONS_Debug(DBG_NETPLAY,"Received SAY cmd too quickly from Player %d (%s), assuming as spam and blocking message.\n", say.source+1, player_names[say.source]); + stop_spamming[say.source] = 4; + break; + } + + stop_spamming[say.source] = 4; + DoSayCommand(say.message, say.target, say.flags, say.source); break; case PT_LOGIN: @@ -6806,6 +6838,15 @@ void NetUpdate(void) } } + if (server) + { + for(; (i 0) + stop_spamming[i]--; + } + } + Net_AckTicker(); HandleNodeTimeouts(); diff --git a/src/hu_stuff.c b/src/hu_stuff.c index a64c5548b..6c0311f51 100644 --- a/src/hu_stuff.c +++ b/src/hu_stuff.c @@ -66,12 +66,6 @@ #define HU_INPUTX 0 #define HU_INPUTY 0 -typedef enum -{ - HU_SHOUT = 1, // Shout message - HU_CSAY = 1<<1, // Middle-of-screen server message -} sayflags_t; - //------------------------------------------- // heads up font //------------------------------------------- @@ -622,8 +616,6 @@ static void Command_Shout(void) DoSayPacketFromCommand(0, 1, HU_SHOUT); } -static tic_t stop_spamming[MAXPLAYERS]; - /** Receives a message, processing an ::XD_SAY command. * \sa DoSayPacket * \author Graue @@ -930,13 +922,6 @@ void HU_Ticker(void) { size_t i = 0; - // handle spam while we're at it: - for(; (i 0) - stop_spamming[i]--; - } - // handle chat timers for (i=0; (i Date: Sat, 29 Apr 2023 17:46:45 -0700 Subject: [PATCH 08/16] PT_SAY: Fix index reuse in spam handler --- src/d_clisrv.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/d_clisrv.c b/src/d_clisrv.c index dce348edc..79d57fccd 100644 --- a/src/d_clisrv.c +++ b/src/d_clisrv.c @@ -6840,7 +6840,7 @@ void NetUpdate(void) if (server) { - for(; (i 0) stop_spamming[i]--; From 167f5f40edb3774798b97934320a2e134ae29cc0 Mon Sep 17 00:00:00 2001 From: AJ Martinez Date: Sat, 29 Apr 2023 20:16:20 -0700 Subject: [PATCH 09/16] PT_SAY and gamestochat: the rest of the owl --- src/d_clisrv.c | 33 +++++++++++++++++++++++++++++++-- src/d_clisrv.h | 3 +++ src/d_netcmd.c | 1 + src/hu_stuff.c | 13 +++++++++++++ src/hu_stuff.h | 1 + 5 files changed, 49 insertions(+), 2 deletions(-) diff --git a/src/d_clisrv.c b/src/d_clisrv.c index 79d57fccd..cd76ebfc1 100644 --- a/src/d_clisrv.c +++ b/src/d_clisrv.c @@ -222,6 +222,8 @@ consvar_t cv_httpsource = CVAR_INIT ("http_source", "", CV_SAVE, NULL, NULL); consvar_t cv_kicktime = CVAR_INIT ("kicktime", "10", CV_SAVE, CV_Unsigned, NULL); +consvar_t cv_gamestochat = CVAR_INIT ("gamestochat", "0", 0, CV_Unsigned, NULL); + static tic_t stop_spamming[MAXPLAYERS]; // Generate a message for an authenticating client to sign, with some guarantees about who we are. @@ -5258,6 +5260,13 @@ static void HandlePacketFromPlayer(SINT8 node) break; } + if ((say.flags & HU_PRIVNOTICE) && !(IsPlayerAdmin(say.source))) + { + CONS_Debug(DBG_NETPLAY,"Received SAY cmd from Player %d (%s) with an illegal HU_PRIVNOTICE flag.\n", say.source+1, player_names[say.source]); + SendKick(say.source, KICK_MSG_CON_FAIL); + return; + } + { size_t i; const size_t j = strlen(say.message); @@ -5266,8 +5275,7 @@ static void HandlePacketFromPlayer(SINT8 node) if (say.message[i] & 0x80) { CONS_Alert(CONS_WARNING, M_GetText("Illegal say command received from %s containing invalid characters\n"), player_names[say.source]); - if (server) - SendKick(say.source, KICK_MSG_CON_FAIL); + SendKick(say.source, KICK_MSG_CON_FAIL); return; } } @@ -5282,6 +5290,20 @@ static void HandlePacketFromPlayer(SINT8 node) stop_spamming[say.source] = 4; + serverplayer_t *stats = SV_GetStatsByPlayerIndex(say.source); + int remainingGames = cv_gamestochat.value - stats->finishedrounds; + + if (remainingGames > 0 && !(IsPlayerAdmin(say.source))) + { + CONS_Debug(DBG_NETPLAY,"Received SAY cmd from Player %d (%s), but they aren't permitted to chat yet.\n", say.source+1, player_names[say.source]); + + char rejectmsg[256]; + strlcpy(rejectmsg, va("Please play %d more games to use chat.", remainingGames), 256); + SendServerNotice(say.source, rejectmsg); + + break; + } + DoSayCommand(say.message, say.target, say.flags, say.source); break; case PT_LOGIN: @@ -7032,4 +7054,11 @@ void DoSayPacketFromCommand(SINT8 target, size_t usedargs, UINT8 flags) } DoSayPacket(target, flags, consoleplayer, msg); +} + +void SendServerNotice(SINT8 target, char *message) +{ + if (client) + return; + DoSayCommand(message, target + 1, HU_PRIVNOTICE, servernode); } \ No newline at end of file diff --git a/src/d_clisrv.h b/src/d_clisrv.h index 5f194498f..ae3811c4e 100644 --- a/src/d_clisrv.h +++ b/src/d_clisrv.h @@ -542,6 +542,8 @@ extern consvar_t cv_discordinvites; extern consvar_t cv_allowguests; +extern consvar_t cv_gamestochat; + #ifdef DEVELOP extern consvar_t cv_badjoin; extern consvar_t cv_badtraffic; @@ -645,6 +647,7 @@ void HandleSigfail(const char *string); void DoSayPacket(SINT8 target, UINT8 flags, UINT8 source, char *message); void DoSayPacketFromCommand(SINT8 target, size_t usedargs, UINT8 flags); +void SendServerNotice(SINT8 target, char *message); #ifdef __cplusplus } // extern "C" diff --git a/src/d_netcmd.c b/src/d_netcmd.c index 0f351c201..987262a06 100644 --- a/src/d_netcmd.c +++ b/src/d_netcmd.c @@ -953,6 +953,7 @@ void D_RegisterClientCommands(void) CV_RegisterVar(&cv_mindelay); CV_RegisterVar(&cv_allowguests); + CV_RegisterVar(&cv_gamestochat); #ifdef DEVELOP CV_RegisterVar(&cv_badjoin); diff --git a/src/hu_stuff.c b/src/hu_stuff.c index 6c0311f51..ce448f651 100644 --- a/src/hu_stuff.c +++ b/src/hu_stuff.c @@ -790,6 +790,13 @@ static void Got_Saycmd(UINT8 **p, INT32 playernum) cstart = "\x82"; textcolor = "\x82"; fmt2 = "%s<%s%s>%s\x80 %s%s"; + + if (flags & HU_PRIVNOTICE) + { + dispname = "SERVER"; + prefix = "\x82"; + fmt2 = "%s[%s%s]%s %s%s"; + } } else if (target > 0) // By you, to another player { @@ -799,6 +806,12 @@ static void Got_Saycmd(UINT8 **p, INT32 playernum) cstart = "\x82"; fmt2 = "%s<%s%s>%s\x80 %s%s"; + if (flags & HU_PRIVNOTICE) + { + if (tempchar) + Z_Free(tempchar); + return; // I pretend I do not see it + } } else // To everyone or sayteam, it doesn't change anything. fmt2 = "%s<%s%s%s>\x80 %s%s"; diff --git a/src/hu_stuff.h b/src/hu_stuff.h index 546061462..ca1b72c37 100644 --- a/src/hu_stuff.h +++ b/src/hu_stuff.h @@ -109,6 +109,7 @@ typedef enum { HU_SHOUT = 1, // Shout message HU_CSAY = 1<<1, // Middle-of-screen server message + HU_PRIVNOTICE = 1<<2, // Special server sayto, we don't want to see it as the sender. } sayflags_t; // some functions From c924228444d42c5c1e14daf337c8ca082f749452 Mon Sep 17 00:00:00 2001 From: AJ Martinez Date: Sat, 29 Apr 2023 20:24:17 -0700 Subject: [PATCH 10/16] Split PT_SAY handling into its own method --- src/d_clisrv.c | 123 +++++++++++++++++++++++++------------------------ 1 file changed, 64 insertions(+), 59 deletions(-) diff --git a/src/d_clisrv.c b/src/d_clisrv.c index cd76ebfc1..009d09ca5 100644 --- a/src/d_clisrv.c +++ b/src/d_clisrv.c @@ -4944,6 +4944,69 @@ static boolean CheckForSpeedHacks(UINT8 p) return false; } +static void PT_Say(int node) +{ + if (client) + return; // Only sent to servers, why are we receiving this? + + say_pak say = netbuffer->u.say; + + if (playernode[say.source] != node) + return; // Spoofed source! + + if ((cv_mute.value || say.flags & (HU_CSAY|HU_SHOUT)) && say.source != serverplayer && !(IsPlayerAdmin(say.source))) + { + CONS_Debug(DBG_NETPLAY,"Received SAY cmd from Player %d (%s), but cv_mute is on.\n", say.source+1, player_names[say.source]); + return; + } + + if ((say.flags & HU_PRIVNOTICE) && !(IsPlayerAdmin(say.source))) + { + CONS_Debug(DBG_NETPLAY,"Received SAY cmd from Player %d (%s) with an illegal HU_PRIVNOTICE flag.\n", say.source+1, player_names[say.source]); + SendKick(say.source, KICK_MSG_CON_FAIL); + return; + } + + { + size_t i; + const size_t j = strlen(say.message); + for (i = 0; i < j; i++) + { + if (say.message[i] & 0x80) + { + CONS_Alert(CONS_WARNING, M_GetText("Illegal say command received from %s containing invalid characters\n"), player_names[say.source]); + SendKick(say.source, KICK_MSG_CON_FAIL); + return; + } + } + } + + if (stop_spamming[say.source] != 0 && consoleplayer != say.source && cv_chatspamprotection.value && !(say.flags & (HU_CSAY|HU_SHOUT))) + { + CONS_Debug(DBG_NETPLAY,"Received SAY cmd too quickly from Player %d (%s), assuming as spam and blocking message.\n", say.source+1, player_names[say.source]); + stop_spamming[say.source] = 4; + return; + } + + stop_spamming[say.source] = 4; + + serverplayer_t *stats = SV_GetStatsByPlayerIndex(say.source); + int remainingGames = cv_gamestochat.value - stats->finishedrounds; + + if (remainingGames > 0 && !(IsPlayerAdmin(say.source))) + { + CONS_Debug(DBG_NETPLAY,"Received SAY cmd from Player %d (%s), but they aren't permitted to chat yet.\n", say.source+1, player_names[say.source]); + + char rejectmsg[256]; + strlcpy(rejectmsg, va("Please play %d more games to use chat.", remainingGames), 256); + SendServerNotice(say.source, rejectmsg); + + return; + } + + DoSayCommand(say.message, say.target, say.flags, say.source); +} + static char NodeToSplitPlayer(int node, int split) { if (split == 0) @@ -5246,65 +5309,7 @@ static void HandlePacketFromPlayer(SINT8 node) } break; case PT_SAY: - if (client) - break; // Only sent to servers, why are we receiving this? - - say_pak say = netbuffer->u.say; - - if (playernode[say.source] != node) - break; // Spoofed source! - - if ((cv_mute.value || say.flags & (HU_CSAY|HU_SHOUT)) && say.source != serverplayer && !(IsPlayerAdmin(say.source))) - { - CONS_Debug(DBG_NETPLAY,"Received SAY cmd from Player %d (%s), but cv_mute is on.\n", say.source+1, player_names[say.source]); - break; - } - - if ((say.flags & HU_PRIVNOTICE) && !(IsPlayerAdmin(say.source))) - { - CONS_Debug(DBG_NETPLAY,"Received SAY cmd from Player %d (%s) with an illegal HU_PRIVNOTICE flag.\n", say.source+1, player_names[say.source]); - SendKick(say.source, KICK_MSG_CON_FAIL); - return; - } - - { - size_t i; - const size_t j = strlen(say.message); - for (i = 0; i < j; i++) - { - if (say.message[i] & 0x80) - { - CONS_Alert(CONS_WARNING, M_GetText("Illegal say command received from %s containing invalid characters\n"), player_names[say.source]); - SendKick(say.source, KICK_MSG_CON_FAIL); - return; - } - } - } - - if (stop_spamming[say.source] != 0 && consoleplayer != say.source && cv_chatspamprotection.value && !(say.flags & (HU_CSAY|HU_SHOUT))) - { - CONS_Debug(DBG_NETPLAY,"Received SAY cmd too quickly from Player %d (%s), assuming as spam and blocking message.\n", say.source+1, player_names[say.source]); - stop_spamming[say.source] = 4; - break; - } - - stop_spamming[say.source] = 4; - - serverplayer_t *stats = SV_GetStatsByPlayerIndex(say.source); - int remainingGames = cv_gamestochat.value - stats->finishedrounds; - - if (remainingGames > 0 && !(IsPlayerAdmin(say.source))) - { - CONS_Debug(DBG_NETPLAY,"Received SAY cmd from Player %d (%s), but they aren't permitted to chat yet.\n", say.source+1, player_names[say.source]); - - char rejectmsg[256]; - strlcpy(rejectmsg, va("Please play %d more games to use chat.", remainingGames), 256); - SendServerNotice(say.source, rejectmsg); - - break; - } - - DoSayCommand(say.message, say.target, say.flags, say.source); + PT_Say(node); break; case PT_LOGIN: if (client) From 55f15f6c0e168e5b337dea90136516b411841c9d Mon Sep 17 00:00:00 2001 From: AJ Martinez Date: Sat, 29 Apr 2023 20:24:30 -0700 Subject: [PATCH 11/16] CV_SAVE gamestochat --- src/d_clisrv.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/d_clisrv.c b/src/d_clisrv.c index 009d09ca5..3a811397c 100644 --- a/src/d_clisrv.c +++ b/src/d_clisrv.c @@ -222,7 +222,7 @@ consvar_t cv_httpsource = CVAR_INIT ("http_source", "", CV_SAVE, NULL, NULL); consvar_t cv_kicktime = CVAR_INIT ("kicktime", "10", CV_SAVE, CV_Unsigned, NULL); -consvar_t cv_gamestochat = CVAR_INIT ("gamestochat", "0", 0, CV_Unsigned, NULL); +consvar_t cv_gamestochat = CVAR_INIT ("gamestochat", "0", CV_SAVE, CV_Unsigned, NULL); static tic_t stop_spamming[MAXPLAYERS]; From 833501f1915324419d041c4afad09edc8fda5816 Mon Sep 17 00:00:00 2001 From: AJ Martinez Date: Sat, 29 Apr 2023 20:26:51 -0700 Subject: [PATCH 12/16] Games-played chat reject message clarity --- src/d_clisrv.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/d_clisrv.c b/src/d_clisrv.c index 3a811397c..5768f1387 100644 --- a/src/d_clisrv.c +++ b/src/d_clisrv.c @@ -4998,7 +4998,7 @@ static void PT_Say(int node) CONS_Debug(DBG_NETPLAY,"Received SAY cmd from Player %d (%s), but they aren't permitted to chat yet.\n", say.source+1, player_names[say.source]); char rejectmsg[256]; - strlcpy(rejectmsg, va("Please play %d more games to use chat.", remainingGames), 256); + strlcpy(rejectmsg, va("Please finish in %d more games to use chat.", remainingGames), 256); SendServerNotice(say.source, rejectmsg); return; From eb8c568723414fb4f792b57cfdb381c395f85716 Mon Sep 17 00:00:00 2001 From: AJ Martinez Date: Sat, 29 Apr 2023 20:30:53 -0700 Subject: [PATCH 13/16] Index footgun warning --- src/d_clisrv.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/d_clisrv.c b/src/d_clisrv.c index 5768f1387..7bdc19693 100644 --- a/src/d_clisrv.c +++ b/src/d_clisrv.c @@ -7061,6 +7061,7 @@ void DoSayPacketFromCommand(SINT8 target, size_t usedargs, UINT8 flags) DoSayPacket(target, flags, consoleplayer, msg); } +// This is meant to be targeted at player indices, not whatever the hell XD_SAY is doing with 1-indexed players. void SendServerNotice(SINT8 target, char *message) { if (client) From 5854969fa9559431963bff64f07d396e7e34291d Mon Sep 17 00:00:00 2001 From: AJ Martinez Date: Sun, 30 Apr 2023 05:59:13 -0700 Subject: [PATCH 14/16] PT_SAY: Help GUESTs understand what's going on a little better --- src/d_clisrv.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/d_clisrv.c b/src/d_clisrv.c index 7bdc19693..aa0be37cb 100644 --- a/src/d_clisrv.c +++ b/src/d_clisrv.c @@ -4999,6 +4999,8 @@ static void PT_Say(int node) char rejectmsg[256]; strlcpy(rejectmsg, va("Please finish in %d more games to use chat.", remainingGames), 256); + if (IsPlayerGuest(say.source)) + strlcpy(rejectmsg, va("GUESTs can't chat on this server. Rejoin with a profile to track your playtime."), 256); SendServerNotice(say.source, rejectmsg); return; From 08ffc79b5c32fce9bf9db45a5f255f1d189252d2 Mon Sep 17 00:00:00 2001 From: AJ Martinez Date: Sun, 30 Apr 2023 19:05:29 -0700 Subject: [PATCH 15/16] PT_SAY: Review fixup --- src/d_clisrv.c | 5 ++--- src/hu_stuff.c | 4 ---- 2 files changed, 2 insertions(+), 7 deletions(-) diff --git a/src/d_clisrv.c b/src/d_clisrv.c index aa0be37cb..aa9ff8caa 100644 --- a/src/d_clisrv.c +++ b/src/d_clisrv.c @@ -4991,14 +4991,13 @@ static void PT_Say(int node) stop_spamming[say.source] = 4; serverplayer_t *stats = SV_GetStatsByPlayerIndex(say.source); - int remainingGames = cv_gamestochat.value - stats->finishedrounds; - if (remainingGames > 0 && !(IsPlayerAdmin(say.source))) + if (stats->finishedrounds < (uint32_t)cv_gamestochat.value && !(IsPlayerAdmin(say.source))) { CONS_Debug(DBG_NETPLAY,"Received SAY cmd from Player %d (%s), but they aren't permitted to chat yet.\n", say.source+1, player_names[say.source]); char rejectmsg[256]; - strlcpy(rejectmsg, va("Please finish in %d more games to use chat.", remainingGames), 256); + strlcpy(rejectmsg, va("Please finish in %d more games to use chat.", cv_gamestochat.value - stats->finishedrounds), 256); if (IsPlayerGuest(say.source)) strlcpy(rejectmsg, va("GUESTs can't chat on this server. Rejoin with a profile to track your playtime."), 256); SendServerNotice(say.source, rejectmsg); diff --git a/src/hu_stuff.c b/src/hu_stuff.c index ce448f651..9c1b213c5 100644 --- a/src/hu_stuff.c +++ b/src/hu_stuff.c @@ -628,7 +628,6 @@ static void Got_Saycmd(UINT8 **p, INT32 playernum) char *msg; boolean action = false; char *ptr; - INT32 spam_eatmsg = 0; CONS_Debug(DBG_NETPLAY,"Received SAY cmd from Player %d (%s)\n", playernum+1, player_names[playernum]); @@ -658,9 +657,6 @@ static void Got_Saycmd(UINT8 **p, INT32 playernum) } } - if (spam_eatmsg) - return; - // If it's a CSAY, just CECHO and be done with it. if (flags & HU_CSAY) { From 28bba54905ce473f6ca2e596601ebc45ab84e234 Mon Sep 17 00:00:00 2001 From: AJ Martinez Date: Wed, 10 May 2023 00:26:12 -0700 Subject: [PATCH 16/16] Allow host to skip gamestochat, not just admins (??????????????) --- src/d_clisrv.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/d_clisrv.c b/src/d_clisrv.c index aa9ff8caa..688418278 100644 --- a/src/d_clisrv.c +++ b/src/d_clisrv.c @@ -4992,7 +4992,7 @@ static void PT_Say(int node) serverplayer_t *stats = SV_GetStatsByPlayerIndex(say.source); - if (stats->finishedrounds < (uint32_t)cv_gamestochat.value && !(IsPlayerAdmin(say.source))) + if (stats->finishedrounds < (uint32_t)cv_gamestochat.value && !(consoleplayer == say.source || IsPlayerAdmin(say.source))) { CONS_Debug(DBG_NETPLAY,"Received SAY cmd from Player %d (%s), but they aren't permitted to chat yet.\n", say.source+1, player_names[say.source]);