Port PT_SAY from RR

Based on the various commits by tyron
This commit is contained in:
NepDisk 2026-03-27 23:18:00 -04:00
parent 2166bfa7c2
commit 90026bc683
5 changed files with 172 additions and 146 deletions

View file

@ -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);
}

View file

@ -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

View file

@ -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 <graue@oceanbase.org>
*/
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<playernum> \'.", 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<playernum> \'.", 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 <graue@oceanbase.org>
*/
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 <graue@oceanbase.org>
*/
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 <graue@oceanbase.org>
*/
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 <graue@oceanbase.org>
*/
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<MAXPLAYERS); i++)
{
if (stop_spamming[i] > 0)
stop_spamming[i]--;
}
// handle chat timers
for (i=0; (i<chat_nummsg_min); i++)
{
@ -1081,29 +987,12 @@ static void HU_sendChatMessage(void)
M_TextInputClear(&w_chat);
// 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])))
@ -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);
}
}

View file

@ -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;

View file

@ -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);