diff --git a/src/d_clisrv.c b/src/d_clisrv.c index 8b41ce616..9888f261e 100644 --- a/src/d_clisrv.c +++ b/src/d_clisrv.c @@ -221,6 +221,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]; + static inline void *G_DcpyTiccmd(void* dest, const ticcmd_t* src, const size_t n) { const size_t d = n / sizeof(ticcmd_t); @@ -4799,6 +4801,56 @@ 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? + + doomdata_t *netbuffer = DOOMCOM_DATA(doomcom); + 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; + + DoSayCommand(say.message, say.target, say.flags, say.source); +} + static void PT_ClientCmd(SINT8 node, INT32 netconsole) { doomdata_t *netbuffer = DOOMCOM_DATA(doomcom); @@ -5441,6 +5493,7 @@ static void HandlePacketFromPlayer(SINT8 node) case PT_TEXTCMD2 : PT_TextCmd (node, netconsole); break; case PT_TEXTCMD3 : PT_TextCmd (node, netconsole); break; case PT_TEXTCMD4 : PT_TextCmd (node, netconsole); break; + case PT_SAY : PT_Say (node ); break; case PT_LOGIN : PT_Login (node ); break; case PT_LOGINAUTH : PT_LoginAuth (node, netconsole); break; case PT_NODETIMEOUT : PT_ClientQuit (node, netconsole); break; @@ -6687,6 +6740,15 @@ void NetUpdate(void) } } + if (server) + { + for(i = 0; i < MAXPLAYERS; i++) + { + if (stop_spamming[i] > 0) + stop_spamming[i]--; + } + } + Net_AckTicker(); HandleNodeTimeouts(); @@ -6825,3 +6887,51 @@ void D_SHA256PasswordPass(const UINT8 *buffer, size_t len, const char *salt, UIN lonesha256(dest, tmpbuf, len+sl); Z_Free(tmpbuf); } + +// 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) +{ + doomdata_t *netbuffer = DOOMCOM_DATA(doomcom); + + 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); +} + +void SendServerNotice(SINT8 target, char *message) +{ + if (client) + return; + DoSayCommand(message, target + 1, HU_PRIVNOTICE, servernode); +} + diff --git a/src/d_clisrv.h b/src/d_clisrv.h index 841f7f883..c3be9de11 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 + #define MAXSERVERNAME 32 #define MAXSERVERCONTACT 350 #define MAXSERVERDESCRIPTION 1024 @@ -132,6 +134,9 @@ typedef enum PT_PING, // Packet sent to tell clients the other client's latency to server. PT_SERVERINFOUPDATE, // Update server info for clients. + + PT_SAY, // "Hey server, please send this chat message to everyone via XD_SAY" + NUMPACKETTYPE } packettype_t; @@ -378,6 +383,14 @@ struct netinfo_pak UINT32 delay[MAXPLAYERS+1]; } ATTRPACK; +struct say_pak +{ + char message[HU_MAXMSGLEN + 1]; + UINT8 target; + UINT8 flags; + UINT8 source; +} ATTRPACK; + // // Network packet data // @@ -413,6 +426,7 @@ struct doomdata_t INT32 filesneedednum; filesneededconfig_pak filesneededcfg; netinfo_pak netinfo; + say_pak say; } u; // This is needed to pack diff packet types data together } ATTRPACK; @@ -649,6 +663,10 @@ void CL_ClearRewinds(void); rewind_t *CL_SaveRewindPoint(size_t demopos); rewind_t *CL_RewindToTime(tic_t time); +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" #endif diff --git a/src/hu_stuff.c b/src/hu_stuff.c index d8180e319..ab800efaf 100644 --- a/src/hu_stuff.c +++ b/src/hu_stuff.c @@ -67,12 +67,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 //------------------------------------------- @@ -491,27 +485,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)) @@ -521,66 +498,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) @@ -593,11 +520,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) @@ -618,11 +545,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) @@ -640,9 +567,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 @@ -662,7 +589,7 @@ static void Command_CSay_f(void) return; } - DoSayCommand(0, 1, HU_CSAY); + DoSayPacketFromCommand(0, 1, HU_CSAY); } static void Command_Shout(void) @@ -679,13 +606,11 @@ 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) @@ -696,25 +621,19 @@ 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]); + // 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; @@ -731,26 +650,6 @@ 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. - // If it's a CSAY, just CECHO and be done with it. if (flags & HU_CSAY) { @@ -890,6 +789,13 @@ static void Got_Saycmd(UINT8 **p, INT32 playernum) cstart = "\x82"; textcolor = "\x82"; fmt2 = "%s<%s%s> %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 { @@ -899,6 +805,13 @@ static void Got_Saycmd(UINT8 **p, INT32 playernum) cstart = "\x82"; fmt2 = "%s<%s%s> %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%s"; @@ -1011,13 +924,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; (iERROR: 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]))) @@ -1148,7 +1037,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); + DoSayPacket(target, buf[1], consoleplayer, msg); } } diff --git a/src/hu_stuff.h b/src/hu_stuff.h index 7cbbb2063..266f4747f 100644 --- a/src/hu_stuff.h +++ b/src/hu_stuff.h @@ -85,7 +85,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 @@ -96,6 +95,13 @@ struct playersort_t #define CHAT_MUTE (cv_mute.value && !(server || IsPlayerAdmin(consoleplayer))) // this still allows to open the chat but not to type. That's used for scrolling and whatnot. #define OLD_MUTE (OLDCHAT && cv_mute.value && !(server || IsPlayerAdmin(consoleplayer))) // this is used to prevent oldchat from opening when muted. +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 void HU_AddChatText(const char *text, boolean playsound); @@ -144,6 +150,8 @@ void HU_SetCEchoDuration(INT32 seconds); void HU_SetCEchoFlags(INT32 flags); void HU_DoCEcho(const char *msg); +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 80d1d76b7..16d587a01 100644 --- a/src/typedef.h +++ b/src/typedef.h @@ -71,6 +71,7 @@ TYPEDEF (doomdata_t); TYPEDEF (serverelem_t); TYPEDEF (rewind_t); TYPEDEF (netinfo_pak); +TYPEDEF (say_pak); // d_event.h TYPEDEF (event_t);