From 6252bb113d1b2f335ce4e492a3cca3c0941fa730 Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Sun, 5 Jun 2022 07:50:36 -0400 Subject: [PATCH 01/48] Map helper commands from HOSTMOD --- src/d_netcmd.c | 65 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 65 insertions(+) diff --git a/src/d_netcmd.c b/src/d_netcmd.c index cd03e1e22..50da295c3 100644 --- a/src/d_netcmd.c +++ b/src/d_netcmd.c @@ -158,6 +158,8 @@ static void Command_Stopdemo_f(void); static void Command_StartMovie_f(void); static void Command_StopMovie_f(void); static void Command_Map_f(void); +static void Command_RandomMap(void); +static void Command_RestartLevel(void); static void Command_ResetCamera_f(void); static void Command_View_f (void); @@ -643,6 +645,8 @@ void D_RegisterServerCommands(void) RegisterNetXCmd(XD_CLEARSCORES, Got_Clearscores); COM_AddCommand("clearscores", Command_Clearscores_f); COM_AddCommand("map", Command_Map_f); + COM_AddCommand("randommap", Command_RandomMap); + COM_AddCommand("restartlevel", Command_RestartLevel); COM_AddCommand("exitgame", Command_ExitGame_f); COM_AddCommand("retry", Command_Retry_f); @@ -2943,6 +2947,67 @@ static void Got_Mapcmd(UINT8 **cp, INT32 playernum) #endif } +static void Command_RandomMap(void) +{ + INT32 oldmapnum; + INT32 newmapnum; + INT32 newgametype; + boolean newencoremode; + boolean newresetplayers; + + if (client && !IsPlayerAdmin(consoleplayer)) + { + CONS_Printf(M_GetText("Only the server or a remote admin can use this.\n")); + return; + } + + // TODO: Handle singleplayer conditions. + // The existing ones are way too annoyingly complicated and "anti-cheat" for my tastes. + + if (Playing()) + { + newgametype = gametype; + newencoremode = encoremode; + newresetplayers = false; + + if (gamestate == GS_LEVEL) + { + oldmapnum = gamemap-1; + } + else + { + oldmapnum = prevmap; + } + } + else + { + newgametype = cv_newgametype.value; + newencoremode = false; + newresetplayers = true; + oldmapnum = -1; + } + + newmapnum = G_RandMap(G_TOLFlag(newgametype), oldmapnum, 0, 0, false, NULL) + 1; + D_MapChange(newmapnum, newgametype, newencoremode, newresetplayers, 0, false, false); +} + +static void Command_RestartLevel(void) +{ + if (client && !IsPlayerAdmin(consoleplayer)) + { + CONS_Printf(M_GetText("Only the server or a remote admin can use this.\n")); + return; + } + + if (!Playing()) + { + CONS_Printf(M_GetText("You must be in a game to use this.\n")); + return; + } + + D_MapChange(gamemap, gametype, encoremode, false, 0, false, false); +} + static void Command_Pause(void) { UINT8 buf[2]; From 66c42cc31e2764676528e544779a1038b3adace4 Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Sun, 5 Jun 2022 09:35:31 -0400 Subject: [PATCH 02/48] Ping measured in frame delay instead of milliseconds The part of HOSTMOD ministats that I wanted. Can go back to ms using the pingmeasurement cvar. If Tyron wants to bring ministats fully in I think it'd be better to bring its ideas to replace the current HUD instead, ideally using the existing ping gfx, so they should bring it up with Oni --- src/d_clisrv.c | 12 ++++++------ src/d_net.c | 41 +++++++++++++++++++++++++++++------------ src/d_netcmd.c | 8 ++++++-- src/d_netcmd.h | 1 + src/doomstat.h | 1 - src/hu_stuff.c | 47 +++++++++++++++++++++++++++++++---------------- src/m_menu.c | 6 ++---- 7 files changed, 75 insertions(+), 41 deletions(-) diff --git a/src/d_clisrv.c b/src/d_clisrv.c index b03eb478e..f34c243e6 100644 --- a/src/d_clisrv.c +++ b/src/d_clisrv.c @@ -122,7 +122,7 @@ SINT8 nodetoplayer4[MAXNETNODES]; // say the numplayer for this node if any (spl UINT8 playerpernode[MAXNETNODES]; // used specialy for splitscreen boolean nodeingame[MAXNETNODES]; // set false as nodes leave game -tic_t servermaxping = 800; // server's max ping. Defaults to 800 +tic_t servermaxping = 20; // server's max delay, in frames. Defaults to 20 static tic_t nettics[MAXNETNODES]; // what tic the client have received static tic_t supposedtics[MAXNETNODES]; // nettics prevision for smaller packet static UINT8 nodewaiting[MAXNETNODES]; @@ -2777,7 +2777,7 @@ static void Got_KickCmd(UINT8 **p, INT32 playernum) kickreason = KR_KICK; break; case KICK_MSG_PING_HIGH: - HU_AddChatText(va("\x82*%s left the game (Broke ping limit)", player_names[pnum]), false); + HU_AddChatText(va("\x82*%s left the game (Broke delay limit)", player_names[pnum]), false); kickreason = KR_PINGLIMIT; break; case KICK_MSG_CON_FAIL: @@ -2869,7 +2869,7 @@ static void Got_KickCmd(UINT8 **p, INT32 playernum) if (msg == KICK_MSG_CON_FAIL) M_StartMessage(M_GetText("Server closed connection\n(Synch failure)\nPress ESC\n"), NULL, MM_NOTHING); else if (msg == KICK_MSG_PING_HIGH) - M_StartMessage(M_GetText("Server closed connection\n(Broke ping limit)\nPress ESC\n"), NULL, MM_NOTHING); + M_StartMessage(M_GetText("Server closed connection\n(Broke delay limit)\nPress ESC\n"), NULL, MM_NOTHING); else if (msg == KICK_MSG_BANNED) M_StartMessage(M_GetText("You have been banned by the server\n\nPress ESC\n"), NULL, MM_NOTHING); else if (msg == KICK_MSG_CUSTOM_KICK) @@ -5337,7 +5337,7 @@ static void UpdatePingTable(void) if (! server_lagless && playernode[i] > 0 && !players[i].spectator) { lag = GetLag(playernode[i]); - realpingtable[i] += (INT32)(lag * (1000.00f/TICRATE)); + realpingtable[i] += lag; if (! fastest || lag < fastest) fastest = lag; @@ -5345,7 +5345,7 @@ static void UpdatePingTable(void) else { // TicsToMilliseconds can't handle pings over 1000ms lol - realpingtable[i] += (INT32)(GetLag(playernode[i]) * (1000.00f/TICRATE)); + realpingtable[i] += GetLag(playernode[i]); } } } @@ -5363,7 +5363,7 @@ static void UpdatePingTable(void) else lag = GetLag(0); - lag = ( realpingtable[0] + G_TicsToMilliseconds(lag) ); + lag = ( realpingtable[0] + lag ); switch (playerpernode[0]) { diff --git a/src/d_net.c b/src/d_net.c index 1c81fe3f3..558310e70 100644 --- a/src/d_net.c +++ b/src/d_net.c @@ -1389,6 +1389,7 @@ struct pingcell { INT32 num; INT32 ms; + INT32 f; }; static int pingcellcmp(const void *va, const void *vb) @@ -1412,6 +1413,7 @@ void Command_Ping_f(void) INT32 pingc; int name_width = 0; + int f_width = 0; int ms_width = 0; int n; @@ -1419,21 +1421,35 @@ void Command_Ping_f(void) pingc = 0; for (i = 1; i < MAXPLAYERS; ++i) - if (playeringame[i]) { - n = strlen(player_names[i]); - if (n > name_width) - name_width = n; + if (playeringame[i]) + { + INT32 ms; - n = playerpingtable[i]; - if (n > ms_width) - ms_width = n; + n = strlen(player_names[i]); + if (n > name_width) + name_width = n; - pingv[pingc].num = i; - pingv[pingc].ms = playerpingtable[i]; - pingc++; + n = playerpingtable[i]; + if (n > f_width) + f_width = n; + + ms = (INT32)(playerpingtable[i] * (1000.00f / TICRATE)); + n = ms; + if (n > ms_width) + ms_width = n; + + pingv[pingc].num = i; + pingv[pingc].f = playerpingtable[i]; + pingv[pingc].ms = ms; + pingc++; + } } + if (f_width < 10) f_width = 1; + else if (f_width < 100) f_width = 2; + else f_width = 3; + if (ms_width < 10) ms_width = 1; else if (ms_width < 100) ms_width = 2; else ms_width = 3; @@ -1442,15 +1458,16 @@ void Command_Ping_f(void) for (i = 0; i < pingc; ++i) { - CONS_Printf("%02d : %-*s %*d ms\n", + CONS_Printf("%02d : %-*s %*d frames (%*d ms)\n", pingv[i].num, name_width, player_names[pingv[i].num], + f_width, pingv[i].f, ms_width, pingv[i].ms); } if (!server && playeringame[consoleplayer]) { - CONS_Printf("\nYour ping is %d ms\n", playerpingtable[consoleplayer]); + CONS_Printf("\nYour ping is %d frames (%d ms)\n", playerpingtable[consoleplayer], (INT32)(playerpingtable[i] * (1000.00f / TICRATE))); } } diff --git a/src/d_netcmd.c b/src/d_netcmd.c index 50da295c3..4b8fce517 100644 --- a/src/d_netcmd.c +++ b/src/d_netcmd.c @@ -488,17 +488,20 @@ static CV_PossibleValue_t nettimeout_cons_t[] = {{TICRATE/7, "MIN"}, {60*TICRATE consvar_t cv_nettimeout = CVAR_INIT ("nettimeout", "105", CV_CALL|CV_SAVE, nettimeout_cons_t, NetTimeout_OnChange); //static CV_PossibleValue_t jointimeout_cons_t[] = {{5*TICRATE, "MIN"}, {60*TICRATE, "MAX"}, {0, NULL}}; consvar_t cv_jointimeout = CVAR_INIT ("jointimeout", "105", CV_CALL|CV_SAVE, nettimeout_cons_t, JoinTimeout_OnChange); -consvar_t cv_maxping = CVAR_INIT ("maxping", "800", CV_SAVE, CV_Unsigned, NULL); +consvar_t cv_maxping = CVAR_INIT ("maxdelay", "20", CV_SAVE, CV_Unsigned, NULL); consvar_t cv_lagless = CVAR_INIT ("lagless", "Off", CV_SAVE|CV_NETVAR|CV_CALL, CV_OnOff, Lagless_OnChange); static CV_PossibleValue_t pingtimeout_cons_t[] = {{8, "MIN"}, {120, "MAX"}, {0, NULL}}; -consvar_t cv_pingtimeout = CVAR_INIT ("pingtimeout", "10", CV_SAVE|CV_NETVAR, pingtimeout_cons_t, NULL); +consvar_t cv_pingtimeout = CVAR_INIT ("maxdelaytimeout", "10", CV_SAVE|CV_NETVAR, pingtimeout_cons_t, NULL); // show your ping on the HUD next to framerate. Defaults to warning only (shows up if your ping is > maxping) static CV_PossibleValue_t showping_cons_t[] = {{0, "Off"}, {1, "Always"}, {2, "Warning"}, {0, NULL}}; consvar_t cv_showping = CVAR_INIT ("showping", "Always", CV_SAVE, showping_cons_t, NULL); +static CV_PossibleValue_t pingmeasurement_cons_t[] = {{0, "Frames"}, {1, "Milliseconds"}, {0, NULL}}; +consvar_t cv_pingmeasurement = CVAR_INIT ("pingmeasurement", "Frames", CV_SAVE, pingmeasurement_cons_t, NULL); + consvar_t cv_showviewpointtext = CVAR_INIT ("showviewpointtext", "On", CV_SAVE, CV_OnOff, NULL); // Intermission time Tails 04-19-2002 @@ -752,6 +755,7 @@ void D_RegisterServerCommands(void) CV_RegisterVar(&cv_lagless); CV_RegisterVar(&cv_pingtimeout); CV_RegisterVar(&cv_showping); + CV_RegisterVar(&cv_pingmeasurement); CV_RegisterVar(&cv_showviewpointtext); CV_RegisterVar(&cv_director); diff --git a/src/d_netcmd.h b/src/d_netcmd.h index f6d172bcc..9abfae18f 100644 --- a/src/d_netcmd.h +++ b/src/d_netcmd.h @@ -105,6 +105,7 @@ extern consvar_t cv_maxping; extern consvar_t cv_lagless; extern consvar_t cv_pingtimeout; extern consvar_t cv_showping; +extern consvar_t cv_pingmeasurement; extern consvar_t cv_showviewpointtext; extern consvar_t cv_skipmapcheck; diff --git a/src/doomstat.h b/src/doomstat.h index 4b3f75d13..6bb09c9f0 100644 --- a/src/doomstat.h +++ b/src/doomstat.h @@ -784,7 +784,6 @@ extern consvar_t cv_forceskin; // force clients to use the server's skin extern consvar_t cv_downloading; // allow clients to downloading WADs. extern consvar_t cv_nettimeout; // SRB2Kart: Advanced server options menu extern consvar_t cv_jointimeout; -extern consvar_t cv_maxping; extern ticcmd_t netcmds[BACKUPTICS][MAXPLAYERS]; extern INT32 serverplayer; extern INT32 adminplayers[MAXPLAYERS]; diff --git a/src/hu_stuff.c b/src/hu_stuff.c index 194f1de0b..933cccbb4 100644 --- a/src/hu_stuff.c +++ b/src/hu_stuff.c @@ -75,6 +75,7 @@ // Note: I'd like to adress that at this point we might *REALLY* want to work towards a common drawString function that can take any font we want because this is really turning into a MESS. :V -Lat' patch_t *pinggfx[5]; // small ping graphic patch_t *mping[5]; // smaller ping graphic +patch_t *pingmeasure[2]; // ping measurement graphic patch_t *framecounter; patch_t *frameslash; // framerate stuff. Used in screen.c @@ -189,6 +190,9 @@ void HU_LoadGraphics(void) HU_UpdatePatch(&mping[i], "MPING%d", i+1); } + HU_UpdatePatch(&pingmeasure[0], "PINGF"); + HU_UpdatePatch(&pingmeasure[1], "PINGMS"); + // fps stuff HU_UpdatePatch(&framecounter, "FRAMER"); HU_UpdatePatch(&frameslash, "FRAMESL"); @@ -2246,15 +2250,15 @@ void HU_Erase(void) //====================================================================== static int -Ping_gfx_num (int ping) +Ping_gfx_num (int lag) { - if (ping < 76) + if (lag < 2) return 0; - else if (ping < 137) + else if (lag < 4) return 1; - else if (ping < 256) + else if (lag < 7) return 2; - else if (ping < 500) + else if (lag < 10) return 3; else return 4; @@ -2263,22 +2267,33 @@ Ping_gfx_num (int ping) // // HU_drawPing // -void HU_drawPing(INT32 x, INT32 y, UINT32 ping, INT32 flags) +void HU_drawPing(INT32 x, INT32 y, UINT32 lag, INT32 flags) { - INT32 gfxnum; // gfx to draw - UINT8 const *colormap = R_GetTranslationColormap(TC_RAINBOW, SKINCOLOR_RASPBERRY, GTC_CACHE); + UINT8 *colormap = NULL; + INT32 measureid = cv_pingmeasurement.value ? 1 : 0; + INT32 gfxnum; // gfx to draw - gfxnum = Ping_gfx_num(ping); + gfxnum = Ping_gfx_num(lag); - V_DrawScaledPatch(x, y, flags, pinggfx[gfxnum]); - if (servermaxping && ping > servermaxping && hu_tick < 4) // flash ping red if too high - V_DrawPingNum(x, y+9, flags, ping, colormap); - else - V_DrawPingNum(x, y+9, flags, ping, NULL); + V_DrawScaledPatch(x+11 - pingmeasure[measureid]->width, y+9, flags, pingmeasure[measureid]); + V_DrawScaledPatch(x+2, y, flags, pinggfx[gfxnum]); + + if (servermaxping && lag > servermaxping && hu_tick < 4) + { + // flash ping red if too high + colormap = R_GetTranslationColormap(TC_RAINBOW, SKINCOLOR_RASPBERRY, GTC_CACHE); + } + + if (cv_pingmeasurement.value) + { + lag = (INT32)(lag * (1000.00f / TICRATE)); + } + + V_DrawPingNum(x+11 - pingmeasure[measureid]->width, y+9, flags, lag, colormap); } void -HU_drawMiniPing (INT32 x, INT32 y, UINT32 ping, INT32 flags) +HU_drawMiniPing (INT32 x, INT32 y, UINT32 lag, INT32 flags) { patch_t *patch; INT32 w = BASEVIDWIDTH; @@ -2288,7 +2303,7 @@ HU_drawMiniPing (INT32 x, INT32 y, UINT32 ping, INT32 flags) w /= 2; } - patch = mping[Ping_gfx_num(ping)]; + patch = mping[Ping_gfx_num(lag)]; if (( flags & V_SNAPTORIGHT )) x += ( w - SHORT (patch->width) ); diff --git a/src/m_menu.c b/src/m_menu.c index dc753db68..30d383aee 100644 --- a/src/m_menu.c +++ b/src/m_menu.c @@ -1521,8 +1521,8 @@ static menuitem_t OP_AdvServerOptionsMenu[] = NULL, "Server Browser Address", {.cvar = &cv_masterserver}, 10}, {IT_STRING | IT_CVAR, NULL, "Attempts to resynchronise", {.cvar = &cv_resynchattempts}, 40}, - {IT_STRING | IT_CVAR, NULL, "Ping limit (ms)", {.cvar = &cv_maxping}, 50}, - {IT_STRING | IT_CVAR, NULL, "Ping timeout (s)", {.cvar = &cv_pingtimeout}, 60}, + {IT_STRING | IT_CVAR, NULL, "Delay limit (frames)", {.cvar = &cv_maxping}, 50}, + {IT_STRING | IT_CVAR, NULL, "Delay timeout (s)", {.cvar = &cv_pingtimeout}, 60}, {IT_STRING | IT_CVAR, NULL, "Connection timeout (tics)", {.cvar = &cv_nettimeout}, 70}, {IT_STRING | IT_CVAR, NULL, "Join timeout (tics)", {.cvar = &cv_jointimeout}, 80}, @@ -2423,8 +2423,6 @@ static void M_ChangeCvar(INT32 choice) choice *= (TICRATE/7); else if (cv == &cv_maxsend) choice *= 512; - else if (cv == &cv_maxping) - choice *= 50; #endif CV_AddValue(cv,choice); } From 82a1dd5243461240f86dd37a71914c79e641c218 Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Sun, 5 Jun 2022 10:25:41 -0400 Subject: [PATCH 03/48] Playsound command For scripted global sound cues for the entire server. --- src/d_netcmd.c | 68 ++++++++++++++++++++++++------------------------ src/d_netcmd.h | 1 + src/s_sound.c | 70 ++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 106 insertions(+), 33 deletions(-) diff --git a/src/d_netcmd.c b/src/d_netcmd.c index 4b8fce517..a784ad8e2 100644 --- a/src/d_netcmd.c +++ b/src/d_netcmd.c @@ -542,41 +542,43 @@ INT32 adminplayers[MAXPLAYERS]; /// \warning Keep this up-to-date if you add/remove/rename net text commands const char *netxcmdnames[MAXNETXCMD - 1] = { - "NAMEANDCOLOR", - "WEAPONPREF", - "KICK", - "NETVAR", - "SAY", - "MAP", - "EXITLEVEL", - "ADDFILE", - "PAUSE", - "ADDPLAYER", - "TEAMCHANGE", - "CLEARSCORES", - "VERIFIED", - "RANDOMSEED", - "RUNSOC", - "REQADDFILE", - "SETMOTD", - "RESPAWN", - "DEMOTED", - "LUACMD", - "LUAVAR", - "LUAFILE", + "NAMEANDCOLOR", // XD_NAMEANDCOLOR + "WEAPONPREF", // XD_WEAPONPREF + "KICK", // XD_KICK + "NETVAR", // XD_NETVAR + "SAY", // XD_SAY + "MAP", // XD_MAP + "EXITLEVEL", // XD_EXITLEVEL + "ADDFILE", // XD_ADDFILE + "PAUSE", // XD_PAUSE + "ADDPLAYER", // XD_ADDPLAYER + "TEAMCHANGE", // XD_TEAMCHANGE + "CLEARSCORES", // XD_CLEARSCORES + "VERIFIED", // XD_VERIFIED + "RANDOMSEED", // XD_RANDOMSEED + "RUNSOC", // XD_RUNSOC + "REQADDFILE", // XD_REQADDFILE + "SETMOTD", // XD_SETMOTD + "RESPAWN", // XD_RESPAWN + "DEMOTED", // XD_DEMOTED + "LUACMD", // XD_LUACMD + "LUAVAR", // XD_LUAVAR + "LUAFILE", // XD_LUAFILE // SRB2Kart - "SETUPVOTE", - "MODIFYVOTE", - "PICKVOTE", - "REMOVEPLAYER", - "POWERLEVEL", - "PARTYINVITE", - "ACCEPTPARTYINVITE", - "LEAVEPARTY", - "CANCELPARTYINVITE", - "GIVEITEM", - "ADDBOT" + "SETUPVOTE", // XD_SETUPVOTE + "MODIFYVOTE", // XD_MODIFYVOTE + "PICKVOTE", // XD_PICKVOTE + "REMOVEPLAYER", // XD_REMOVEPLAYER + "POWERLEVEL", // XD_POWERLEVEL + "PARTYINVITE", // XD_PARTYINVITE + "ACCEPTPARTYINVITE", // XD_ACCEPTPARTYINVITE + "LEAVEPARTY", // XD_LEAVEPARTY + "CANCELPARTYINVITE", // XD_CANCELPARTYINVITE + "GIVEITEM", // XD_GIVEITEM + "ADDBOT", // XD_ADDBOT + "DISCORD", // XD_DISCORD + "PLAYSOUND" // XD_PLAYSOUND }; // ========================================================================= diff --git a/src/d_netcmd.h b/src/d_netcmd.h index 9abfae18f..8acb2ed9a 100644 --- a/src/d_netcmd.h +++ b/src/d_netcmd.h @@ -159,6 +159,7 @@ typedef enum XD_GIVEITEM, // 32 XD_ADDBOT, // 33 XD_DISCORD, // 34 + XD_PLAYSOUND, // 35 MAXNETXCMD } netxcmd_t; diff --git a/src/s_sound.c b/src/s_sound.c index 1214472d7..44f57459c 100644 --- a/src/s_sound.c +++ b/src/s_sound.c @@ -31,6 +31,7 @@ #include "m_cond.h" // for conditionsets #include "lua_hook.h" // MusicChange hook #include "k_boss.h" // bossinfo +#include "byteptr.h" #ifdef HW3SOUND // 3D Sound Interface @@ -43,6 +44,8 @@ CV_PossibleValue_t soundvolume_cons_t[] = {{0, "MIN"}, {MAX_VOLUME, "MAX"}, {0, static void SetChannelsNum(void); static void Command_Tunes_f(void); static void Command_RestartAudio_f(void); +static void Command_PlaySound(void); +static void Got_PlaySound(UINT8 **p, INT32 playernum); // Sound system toggles static void GameSounds_OnChange(void); @@ -268,6 +271,8 @@ void S_RegisterSoundStuff(void) COM_AddCommand("tunes", Command_Tunes_f); COM_AddCommand("restartaudio", Command_RestartAudio_f); + COM_AddCommand("playsound", Command_PlaySound); + RegisterNetXCmd(XD_PLAYSOUND, Got_PlaySound); } static void SetChannelsNum(void) @@ -2461,6 +2466,71 @@ static void Command_RestartAudio_f(void) S_ChangeMusicInternal("titles", looptitle); } +static void Command_PlaySound(void) +{ + const char *sound; + const size_t argc = COM_Argc(); + sfxenum_t sfx = NUMSFX; + UINT8 buf[4]; + UINT8 *buf_p = buf; + + if (argc < 2) + { + CONS_Printf("playsound : Plays a sound effect for the entire server.\n"); + return; + } + + if (client && !IsPlayerAdmin(consoleplayer)) + { + CONS_Printf("This can only be used by the server host.\n"); + return; + } + + sound = COM_Argv(1); + if (*sound >= '0' && *sound <= '9') + { + sfx = atoi(sound); + } + else + { + for (sfx = 0; sfx < NUMSFX; sfx++) + { + if (S_sfx[sfx].name && fasticmp(sound, S_sfx[sfx].name)) + break; + } + } + + if (sfx < 0 || sfx >= NUMSFX) + { + CONS_Printf("Could not find sound effect named \"sfx_%s\".\n", sound); + return; + } + + WRITEINT32(buf_p, sfx); + SendNetXCmd(XD_PLAYSOUND, buf, buf_p - buf); +} + +static void Got_PlaySound(UINT8 **cp, INT32 playernum) +{ + INT32 sound_id = READINT32(*cp); + + if (playernum != serverplayer && !IsPlayerAdmin(playernum)) // hacked client, or disasterous bug + { + CONS_Alert(CONS_WARNING, M_GetText("Illegal playsound received from %s (serverplayer is %s)\n"), player_names[playernum], player_names[serverplayer]); + if (server) + SendKick(playernum, KICK_MSG_CON_FAIL); + return; + } + + if (sound_id < 0 || sound_id >= NUMSFX) + { + // bad sound effect, ignore + return; + } + + S_StartSound(NULL, sound_id); +} + void GameSounds_OnChange(void) { if (M_CheckParm("-nosound") || M_CheckParm("-noaudio")) From e7f159113ad8a40b615e6400203faa9556d6f597 Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Mon, 6 Jun 2022 16:28:50 -0400 Subject: [PATCH 04/48] Remove MAXBANS Needs proper stress testing but seems to work. --- src/d_clisrv.c | 4 +-- src/i_tcp.c | 88 ++++++++++++++++++++++++++++++++++++-------------- 2 files changed, 65 insertions(+), 27 deletions(-) diff --git a/src/d_clisrv.c b/src/d_clisrv.c index f34c243e6..09de423a1 100644 --- a/src/d_clisrv.c +++ b/src/d_clisrv.c @@ -2524,7 +2524,7 @@ static void Command_Ban(void) { if (COM_Argc() < 2) { - CONS_Printf(M_GetText("Ban : ban and kick a player\n")); + CONS_Printf(M_GetText("ban : ban and kick a player\n")); return; } @@ -2548,7 +2548,7 @@ static void Command_Ban(void) if (server && I_Ban && !I_Ban(node)) // only the server is allowed to do this right now { - CONS_Alert(CONS_WARNING, M_GetText("Too many bans! Geez, that's a lot of people you're excluding...\n")); + CONS_Alert(CONS_WARNING, M_GetText("Ban failed. Invalid node?\n")); WRITEUINT8(p, KICK_MSG_GO_AWAY); SendNetXCmd(XD_KICK, &buf, 2); } diff --git a/src/i_tcp.c b/src/i_tcp.c index 1ca1b7f22..3a2e8f3dc 100644 --- a/src/i_tcp.c +++ b/src/i_tcp.c @@ -138,8 +138,6 @@ #endif // !NONET -#define MAXBANS 100 - #include "i_system.h" #include "i_net.h" #include "d_net.h" @@ -147,6 +145,7 @@ #include "i_tcp.h" #include "m_argv.h" #include "stun.h" +#include "z_zone.h" #include "doomstat.h" @@ -196,12 +195,14 @@ static mysockaddr_t broadcastaddress[MAXNETNODES+1]; static size_t broadcastaddresses = 0; static boolean nodeconnected[MAXNETNODES+1]; - static mysockaddr_t banned[MAXBANS]; - static UINT8 bannedmask[MAXBANS]; + static mysockaddr_t *banned; + static UINT8 *bannedmask; static const INT32 hole_punch_magic = MSBF_LONG (0x52eb11); #endif static size_t numbans = 0; +static size_t banned_size = 0; + static boolean SOCK_bannednode[MAXNETNODES+1]; /// \note do we really need the +1? static boolean init_tcp_driver = false; @@ -1423,6 +1424,39 @@ static boolean SOCK_OpenSocket(void) #endif } +static void AddBannedIndex(void) +{ + if (numbans >= banned_size) + { + if (banned_size == 0) + { + banned_size = 128; + } + else + { + banned_size *= 2; + } + + banned = Z_ReallocAlign( + (void*) banned, + sizeof(mysockaddr_t) * banned_size, + PU_STATIC, + NULL, + sizeof(mysockaddr_t) * 8 + ); + + bannedmask = Z_ReallocAlign( + (void*) banned, + sizeof(UINT8) * banned_size, + PU_STATIC, + NULL, + sizeof(UINT8) * 8 + ); + } + + numbans++; +} + static boolean SOCK_Ban(INT32 node) { if (node > MAXNETNODES) @@ -1430,23 +1464,23 @@ static boolean SOCK_Ban(INT32 node) #ifdef NONET return false; #else - if (numbans == MAXBANS) - return false; - M_Memcpy(&banned[numbans], &clientaddress[node], sizeof (mysockaddr_t)); - if (banned[numbans].any.sa_family == AF_INET) + AddBannedIndex(); + + M_Memcpy(&banned[numbans-1], &clientaddress[node], sizeof (mysockaddr_t)); + if (banned[numbans-1].any.sa_family == AF_INET) { - banned[numbans].ip4.sin_port = 0; - bannedmask[numbans] = 32; + banned[numbans-1].ip4.sin_port = 0; + bannedmask[numbans-1] = 32; } #ifdef HAVE_IPV6 - else if (banned[numbans].any.sa_family == AF_INET6) + else if (banned[numbans-1].any.sa_family == AF_INET6) { - banned[numbans].ip6.sin6_port = 0; - bannedmask[numbans] = 128; + banned[numbans-1].ip6.sin6_port = 0; + bannedmask[numbans-1] = 128; } #endif - numbans++; + return true; #endif } @@ -1461,7 +1495,7 @@ static boolean SOCK_SetBanAddress(const char *address, const char *mask) struct my_addrinfo *ai, *runp, hints; int gaie; - if (numbans == MAXBANS || !address) + if (!address) return false; memset(&hints, 0x00, sizeof(hints)); @@ -1476,26 +1510,27 @@ static boolean SOCK_SetBanAddress(const char *address, const char *mask) runp = ai; - while(runp != NULL && numbans != MAXBANS) + while (runp != NULL) { - memcpy(&banned[numbans], runp->ai_addr, runp->ai_addrlen); + AddBannedIndex(); + + memcpy(&banned[numbans-1], runp->ai_addr, runp->ai_addrlen); if (mask) - bannedmask[numbans] = (UINT8)atoi(mask); + bannedmask[numbans-1] = (UINT8)atoi(mask); #ifdef HAVE_IPV6 else if (runp->ai_family == AF_INET6) - bannedmask[numbans] = 128; + bannedmask[numbans-1] = 128; #endif else - bannedmask[numbans] = 32; + bannedmask[numbans-1] = 32; - if (bannedmask[numbans] > 32 && runp->ai_family == AF_INET) - bannedmask[numbans] = 32; + if (bannedmask[numbans-1] > 32 && runp->ai_family == AF_INET) + bannedmask[numbans-1] = 32; #ifdef HAVE_IPV6 - else if (bannedmask[numbans] > 128 && runp->ai_family == AF_INET6) - bannedmask[numbans] = 128; + else if (bannedmask[numbans-1] > 128 && runp->ai_family == AF_INET6) + bannedmask[numbans-1] = 128; #endif - numbans++; runp = runp->ai_next; } @@ -1508,6 +1543,9 @@ static boolean SOCK_SetBanAddress(const char *address, const char *mask) static void SOCK_ClearBans(void) { numbans = 0; + banned_size = 0; + banned = NULL; + bannedmask = NULL; } boolean I_InitTcpNetwork(void) From 6391afd1481522bff86a1387466bd425d36d0518 Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Mon, 6 Jun 2022 17:05:44 -0400 Subject: [PATCH 05/48] Random choice commands --- src/console.c | 78 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/console.c b/src/console.c index c0e5d1877..cfca61b38 100644 --- a/src/console.c +++ b/src/console.c @@ -34,6 +34,7 @@ #include "m_menu.h" #include "filesrch.h" #include "m_misc.h" +#include "m_random.h" #ifdef _WINDOWS #include "win32/win_main.h" @@ -265,6 +266,81 @@ static void CONS_Bind_f(void) bindtable[key] = Z_StrDup(COM_Argv(2)); } +static void CONS_Choose_f(void) +{ + size_t na = COM_Argc(); + + if (na < 2) + { + CONS_Printf(M_GetText("choose [] [] [...]: Picks a command at random\n")); + return; + } + + COM_BufAddText(COM_Argv(M_RandomKey(na - 1) + 1)); + COM_BufAddText("\n"); +} + +static void CONS_ChooseWeighted_f(void) +{ + size_t na = COM_Argc(); + size_t i, cmd; + const char *commands[40]; + INT32 weights[40]; + INT32 totalWeight = 0; + INT32 roll; + + if (na < 3) + { + CONS_Printf(M_GetText("chooseweighted [ ] [ ] [...]: Picks a command with weighted randomization\n")); + return; + } + + memset(weights, 0, sizeof(weights)); + + i = 1; + cmd = 0; + while (i < na) + { + commands[cmd] = COM_Argv(i); + + i++; + if (i >= na) + { + break; + } + + weights[cmd] = atoi(COM_Argv(i)); + totalWeight += weights[cmd]; + + i++; + cmd++; + } + + if (cmd == 0 || totalWeight <= 0) + { + return; + } + + roll = M_RandomRange(1, totalWeight); + + for (i = 0; i < cmd; i++) + { + if (roll <= weights[i]) + { + if (commands[i] == NULL) + { + break; + } + + COM_BufAddText(commands[i]); + COM_BufAddText("\n"); + break; + } + + roll -= weights[i]; + } +} + //====================================================================== // CONSOLE SETUP //====================================================================== @@ -468,6 +544,8 @@ void CON_Init(void) CV_RegisterVar(&cons_backcolor); CV_RegisterVar(&cons_menuhighlight); COM_AddCommand("bind", CONS_Bind_f); + COM_AddCommand("choose", CONS_Choose_f); + COM_AddCommand("chooseweighted", CONS_ChooseWeighted_f); } else { From f2244e80465a9731a0ed0718b41f2d655fbafd4e Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Mon, 6 Jun 2022 17:14:07 -0400 Subject: [PATCH 06/48] Start banned_size much smaller --- src/i_tcp.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/i_tcp.c b/src/i_tcp.c index 3a2e8f3dc..e8066dd58 100644 --- a/src/i_tcp.c +++ b/src/i_tcp.c @@ -1430,7 +1430,7 @@ static void AddBannedIndex(void) { if (banned_size == 0) { - banned_size = 128; + banned_size = 8; } else { From 14508d42b7c7829ec27f3c87a6c0f5feb10680d4 Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Mon, 6 Jun 2022 17:54:27 -0400 Subject: [PATCH 07/48] Combine banned & bannedmask into banned_t Better code cleanliness, also makes it easier to add more data to bans later (such as a timestamp for temporary bans) --- src/i_tcp.c | 70 +++++++++++++++++++++++++++++------------------------ 1 file changed, 39 insertions(+), 31 deletions(-) diff --git a/src/i_tcp.c b/src/i_tcp.c index e8066dd58..b37584503 100644 --- a/src/i_tcp.c +++ b/src/i_tcp.c @@ -187,6 +187,14 @@ #if (defined (WATTCP) && !defined (__libsocket_socklen_t)) || defined (USE_WINSOCK1) typedef int socklen_t; #endif + + typedef struct + { + mysockaddr_t address; + UINT8 mask; + // TODO: timestamp, for tempbans! + } banned_t; + static SOCKET_TYPE mysockets[MAXNETNODES+1] = {ERRSOCKET}; static size_t mysocketses = 0; static int myfamily[MAXNETNODES+1] = {0}; @@ -195,8 +203,7 @@ static mysockaddr_t broadcastaddress[MAXNETNODES+1]; static size_t broadcastaddresses = 0; static boolean nodeconnected[MAXNETNODES+1]; - static mysockaddr_t *banned; - static UINT8 *bannedmask; + static banned_t *banned; static const INT32 hole_punch_magic = MSBF_LONG (0x52eb11); #endif @@ -431,7 +438,7 @@ static const char *SOCK_GetBanAddress(size_t ban) #ifdef NONET return NULL; #else - return SOCK_AddrToStr(&banned[ban]); + return SOCK_AddrToStr(&banned[ban].address); #endif } @@ -443,7 +450,7 @@ static const char *SOCK_GetBanMask(size_t ban) static char s[16]; //255.255.255.255 netmask? no, just CDIR for only if (ban >= numbans) return NULL; - if (sprintf(s,"%d",bannedmask[ban]) > 0) + if (sprintf(s,"%d",banned[ban].mask) > 0) return s; #endif return NULL; @@ -632,7 +639,7 @@ static boolean SOCK_Get(void) // check if it's a banned dude so we can send a refusal later for (i = 0; i < numbans; i++) { - if (SOCK_cmpaddr(&fromaddress, &banned[i], bannedmask[i])) + if (SOCK_cmpaddr(&fromaddress, &banned[i].address, banned[i].mask)) { SOCK_bannednode[j] = true; DEBFILE("This dude has been banned\n"); @@ -1439,18 +1446,10 @@ static void AddBannedIndex(void) banned = Z_ReallocAlign( (void*) banned, - sizeof(mysockaddr_t) * banned_size, + sizeof(banned_t) * banned_size, PU_STATIC, NULL, - sizeof(mysockaddr_t) * 8 - ); - - bannedmask = Z_ReallocAlign( - (void*) banned, - sizeof(UINT8) * banned_size, - PU_STATIC, - NULL, - sizeof(UINT8) * 8 + sizeof(banned_t) * 8 ); } @@ -1459,25 +1458,31 @@ static void AddBannedIndex(void) static boolean SOCK_Ban(INT32 node) { + INT32 ban; + if (node > MAXNETNODES) return false; + #ifdef NONET + (void)ban; return false; #else + ban = numbans; AddBannedIndex(); - M_Memcpy(&banned[numbans-1], &clientaddress[node], sizeof (mysockaddr_t)); - if (banned[numbans-1].any.sa_family == AF_INET) + M_Memcpy(&banned[ban].address, &clientaddress[node], sizeof (mysockaddr_t)); + + if (banned[ban].address.any.sa_family == AF_INET) { - banned[numbans-1].ip4.sin_port = 0; - bannedmask[numbans-1] = 32; + banned[ban].address.ip4.sin_port = 0; + banned[ban].mask = 32; } #ifdef HAVE_IPV6 - else if (banned[numbans-1].any.sa_family == AF_INET6) + else if (banned[ban].address.any.sa_family == AF_INET6) { - banned[numbans-1].ip6.sin6_port = 0; - bannedmask[numbans-1] = 128; + banned[ban].address.ip6.sin6_port = 0; + banned[ban].mask = 128; } #endif @@ -1512,25 +1517,29 @@ static boolean SOCK_SetBanAddress(const char *address, const char *mask) while (runp != NULL) { + INT32 ban; + + ban = numbans; AddBannedIndex(); - memcpy(&banned[numbans-1], runp->ai_addr, runp->ai_addrlen); + memcpy(&banned[ban].address, runp->ai_addr, runp->ai_addrlen); if (mask) - bannedmask[numbans-1] = (UINT8)atoi(mask); + banned[ban].mask = (UINT8)atoi(mask); #ifdef HAVE_IPV6 else if (runp->ai_family == AF_INET6) - bannedmask[numbans-1] = 128; + banned[ban].mask = 128; #endif else - bannedmask[numbans-1] = 32; + banned[ban].mask = 32; - if (bannedmask[numbans-1] > 32 && runp->ai_family == AF_INET) - bannedmask[numbans-1] = 32; + if (banned[ban].mask > 32 && runp->ai_family == AF_INET) + banned[ban].mask = 32; #ifdef HAVE_IPV6 - else if (bannedmask[numbans-1] > 128 && runp->ai_family == AF_INET6) - bannedmask[numbans-1] = 128; + else if (banned[ban].mask > 128 && runp->ai_family == AF_INET6) + banned[ban].mask = 128; #endif + runp = runp->ai_next; } @@ -1545,7 +1554,6 @@ static void SOCK_ClearBans(void) numbans = 0; banned_size = 0; banned = NULL; - bannedmask = NULL; } boolean I_InitTcpNetwork(void) From de718568ef79ea0c5cf797279f11103cf574a8d3 Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Tue, 7 Jun 2022 11:44:03 -0400 Subject: [PATCH 08/48] - Attach ban reasons to banned_t - Properly call D_SaveBan after remote bans. Bans are no longer saved in the ban command and instead wait for the actual kick to process, since before they were split between the two, which is what caused the discrepancy. --- src/d_clisrv.c | 152 ++++++++++++++----------------------------------- src/d_net.c | 2 + src/i_net.h | 2 + src/i_tcp.c | 30 ++++++++++ 4 files changed, 76 insertions(+), 110 deletions(-) diff --git a/src/d_clisrv.c b/src/d_clisrv.c index 09de423a1..f0cb7697e 100644 --- a/src/d_clisrv.c +++ b/src/d_clisrv.c @@ -2050,40 +2050,27 @@ static void CL_ConnectToServer(void) } #ifndef NONET -typedef struct banreason_s -{ - char *reason; - struct banreason_s *prev; //-1 - struct banreason_s *next; //+1 -} banreason_t; - -static banreason_t *reasontail = NULL; //last entry, use prev -static banreason_t *reasonhead = NULL; //1st entry, use next - static void Command_ShowBan(void) //Print out ban list { size_t i; - const char *address, *mask; - banreason_t *reasonlist = reasonhead; + const char *address, *mask, *reason; if (I_GetBanAddress) CONS_Printf(M_GetText("Ban List:\n")); else return; - for (i = 0;(address = I_GetBanAddress(i)) != NULL;i++) + for (i = 0; (address = I_GetBanAddress(i)) != NULL; i++) { if (!I_GetBanMask || (mask = I_GetBanMask(i)) == NULL) CONS_Printf("%s: %s ", sizeu1(i+1), address); else CONS_Printf("%s: %s/%s ", sizeu1(i+1), address, mask); - if (reasonlist && reasonlist->reason) - CONS_Printf("(%s)\n", reasonlist->reason); + if (I_GetBanReason && (reason = I_GetBanReason(i)) != NULL) + CONS_Printf("(%s)\n", reason); else CONS_Printf("\n"); - - if (reasonlist) reasonlist = reasonlist->next; } if (i == 0 && !address) @@ -2094,16 +2081,9 @@ void D_SaveBan(void) { FILE *f; size_t i; - banreason_t *reasonlist = reasonhead; - const char *address, *mask; + const char *address, *mask, *reason; const char *path = va("%s"PATHSEP"%s", srb2home, "ban.txt"); - if (!reasonhead) - { - remove(path); - return; - } - f = fopen(path, "w"); if (!f) @@ -2112,65 +2092,28 @@ void D_SaveBan(void) return; } - for (i = 0;(address = I_GetBanAddress(i)) != NULL;i++) + for (i = 0; (address = I_GetBanAddress(i)) != NULL; i++) { if (!I_GetBanMask || (mask = I_GetBanMask(i)) == NULL) fprintf(f, "%s 0", address); else fprintf(f, "%s %s", address, mask); - if (reasonlist && reasonlist->reason) - fprintf(f, " %s\n", reasonlist->reason); + if (I_GetBanReason && (reason = I_GetBanReason(i)) != NULL) + fprintf(f, " %s\n", reason); else - fprintf(f, " %s\n", "NA"); - - if (reasonlist) reasonlist = reasonlist->next; + fprintf(f, " %s\n", "No reason given"); } fclose(f); } -static void Ban_Add(const char *reason) -{ - banreason_t *reasonlist = malloc(sizeof(*reasonlist)); - - if (!reasonlist) - return; - if (!reason) - reason = "NA"; - - reasonlist->next = NULL; - reasonlist->reason = Z_StrDup(reason); - if ((reasonlist->prev = reasontail) == NULL) - reasonhead = reasonlist; - else - reasontail->next = reasonlist; - reasontail = reasonlist; -} - -static void Ban_Clear(void) -{ - banreason_t *temp; - - I_ClearBans(); - - reasontail = NULL; - - while (reasonhead) - { - temp = reasonhead->next; - Z_Free(reasonhead->reason); - free(reasonhead); - reasonhead = temp; - } -} - static void Command_ClearBans(void) { if (!I_ClearBans) return; - Ban_Clear(); + I_ClearBans(); D_SaveBan(); } @@ -2178,7 +2121,7 @@ static void Ban_Load_File(boolean warning) { FILE *f; size_t i; - const char *address, *mask; + const char *address, *mask, *reason; char buffer[MAX_WADPATH]; if (!I_ClearBans) @@ -2193,16 +2136,17 @@ static void Ban_Load_File(boolean warning) return; } - Ban_Clear(); + I_ClearBans(); for (i=0; fgets(buffer, (int)sizeof(buffer), f); i++) { address = strtok(buffer, " \t\r\n"); mask = strtok(NULL, " \t\r\n"); + reason = strtok(NULL, "\r\n"); I_SetBanAddress(address, mask); - - Ban_Add(strtok(NULL, "\r\n")); + if (I_SetBanReason) + I_SetBanReason(reason); } fclose(f); @@ -2539,54 +2483,37 @@ static void Command_Ban(void) UINT8 buf[3 + MAX_REASONLENGTH]; UINT8 *p = buf; const SINT8 pn = nametonum(COM_Argv(1)); - const INT32 node = playernode[(INT32)pn]; if (pn == -1 || pn == 0) return; WRITEUINT8(p, pn); - if (server && I_Ban && !I_Ban(node)) // only the server is allowed to do this right now + if (COM_Argc() == 2) { - CONS_Alert(CONS_WARNING, M_GetText("Ban failed. Invalid node?\n")); - WRITEUINT8(p, KICK_MSG_GO_AWAY); + WRITEUINT8(p, KICK_MSG_BANNED); SendNetXCmd(XD_KICK, &buf, 2); } else { - if (server) // only the server is allowed to do this right now + size_t i, j = COM_Argc(); + char message[MAX_REASONLENGTH]; + + //Steal from the motd code so you don't have to put the reason in quotes. + strlcpy(message, COM_Argv(2), sizeof message); + for (i = 3; i < j; i++) { - Ban_Add(COM_Argv(2)); - D_SaveBan(); // save the ban list + strlcat(message, " ", sizeof message); + strlcat(message, COM_Argv(i), sizeof message); } - if (COM_Argc() == 2) - { - WRITEUINT8(p, KICK_MSG_BANNED); - SendNetXCmd(XD_KICK, &buf, 2); - } - else - { - size_t i, j = COM_Argc(); - char message[MAX_REASONLENGTH]; - - //Steal from the motd code so you don't have to put the reason in quotes. - strlcpy(message, COM_Argv(2), sizeof message); - for (i = 3; i < j; i++) - { - strlcat(message, " ", sizeof message); - strlcat(message, COM_Argv(i), sizeof message); - } - - WRITEUINT8(p, KICK_MSG_CUSTOM_BAN); - WRITESTRINGN(p, message, MAX_REASONLENGTH); - SendNetXCmd(XD_KICK, &buf, p - buf); - } + WRITEUINT8(p, KICK_MSG_CUSTOM_BAN); + WRITESTRINGN(p, message, MAX_REASONLENGTH); + SendNetXCmd(XD_KICK, &buf, p - buf); } } else CONS_Printf(M_GetText("Only the server or a remote admin can use this.\n")); - } static void Command_BanIP(void) @@ -2607,7 +2534,6 @@ static void Command_BanIP(void) else reason = COM_Argv(2); - if (I_SetBanAddress && I_SetBanAddress(address, NULL)) { if (reason) @@ -2615,7 +2541,8 @@ static void Command_BanIP(void) else CONS_Printf("Banned IP address %s\n", address); - Ban_Add(reason); + if (I_SetBanReason) + I_SetBanReason(reason); D_SaveBan(); } else @@ -2753,16 +2680,21 @@ static void Got_KickCmd(UINT8 **p, INT32 playernum) //CONS_Printf("\x82%s ", player_names[pnum]); - // If a verified admin banned someone, the server needs to know about it. - // If the playernum isn't zero (the server) then the server needs to record the ban. - if (server && playernum && (msg == KICK_MSG_BANNED || msg == KICK_MSG_CUSTOM_BAN)) + // Save bans here. Used to be split between here and the actual command, depending on + // whenever the server did it or a remote admin did it, but it's simply more convenient + // to keep it all in one place. + if (server && (msg == KICK_MSG_BANNED || msg == KICK_MSG_CUSTOM_BAN)) { if (I_Ban && !I_Ban(playernode[(INT32)pnum])) - CONS_Alert(CONS_WARNING, M_GetText("Too many bans! Geez, that's a lot of people you're excluding...\n")); -#ifndef NONET + { + CONS_Alert(CONS_WARNING, M_GetText("Ban failed. Invalid node?\n")); + } else - Ban_Add(reason); -#endif + { + if (I_SetBanReason) + I_SetBanReason(reason); + D_SaveBan(); + } } if (msg == KICK_MSG_PLAYER_QUIT) diff --git a/src/d_net.c b/src/d_net.c index 558310e70..bdfd06767 100644 --- a/src/d_net.c +++ b/src/d_net.c @@ -83,7 +83,9 @@ void (*I_ClearBans)(void) = NULL; const char *(*I_GetNodeAddress) (INT32 node) = NULL; const char *(*I_GetBanAddress) (size_t ban) = NULL; const char *(*I_GetBanMask) (size_t ban) = NULL; +const char *(*I_GetBanReason) (size_t ban) = NULL; boolean (*I_SetBanAddress) (const char *address, const char *mask) = NULL; +boolean (*I_SetBanReason) (const char *reason) = NULL; boolean *bannednode = NULL; diff --git a/src/i_net.h b/src/i_net.h index 8caa0edcc..f3d55a61a 100644 --- a/src/i_net.h +++ b/src/i_net.h @@ -162,7 +162,9 @@ extern void (*I_ClearBans)(void); extern const char *(*I_GetNodeAddress) (INT32 node); extern const char *(*I_GetBanAddress) (size_t ban); extern const char *(*I_GetBanMask) (size_t ban); +extern const char *(*I_GetBanReason) (size_t ban); extern boolean (*I_SetBanAddress) (const char *address,const char *mask); +extern boolean (*I_SetBanReason) (const char *reason); extern boolean *bannednode; /// \brief Called by D_SRB2Main to be defined by extern network driver diff --git a/src/i_tcp.c b/src/i_tcp.c index b37584503..382df176e 100644 --- a/src/i_tcp.c +++ b/src/i_tcp.c @@ -192,6 +192,7 @@ { mysockaddr_t address; UINT8 mask; + char *reason; // TODO: timestamp, for tempbans! } banned_t; @@ -456,6 +457,16 @@ static const char *SOCK_GetBanMask(size_t ban) return NULL; } +static const char *SOCK_GetBanReason(size_t ban) +{ +#ifdef NONET + (void)ban; + return NULL; +#else + return banned[ban].reason; +#endif +} + #ifndef NONET static boolean SOCK_cmpaddr(mysockaddr_t *a, mysockaddr_t *b, UINT8 mask) { @@ -1549,6 +1560,23 @@ static boolean SOCK_SetBanAddress(const char *address, const char *mask) #endif } +static boolean SOCK_SetBanReason(const char *reason) +{ +#ifdef NONET + (void)reason; + return false; +#else + + if (!reason) + { + reason = "No reason given"; + } + + banned[numbans - 1].reason = Z_StrDup(reason); + return true; +#endif +} + static void SOCK_ClearBans(void) { numbans = 0; @@ -1648,7 +1676,9 @@ boolean I_InitTcpNetwork(void) I_GetNodeAddress = SOCK_GetNodeAddress; I_GetBanAddress = SOCK_GetBanAddress; I_GetBanMask = SOCK_GetBanMask; + I_GetBanReason = SOCK_GetBanReason; I_SetBanAddress = SOCK_SetBanAddress; + I_SetBanReason = SOCK_SetBanReason; bannednode = SOCK_bannednode; return ret; From cc44588c9c8a2556862492def4aefa1cb2f59325 Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Tue, 7 Jun 2022 14:08:02 -0400 Subject: [PATCH 09/48] Kicks are now temp bans Length is determined by the "kicktime" cvar, in minutes. By default, this is set to 10, but I'm willing to adjust this. Only applies to manual kicks (in the future, maybe also name filter kicks). The timestamp for the unban time is even saved in ban.txt, so long-term temporary bans are completely possible. (I checked, you can attempt to ban someone for up to 1902 years if you really want to.) --- src/d_clisrv.c | 118 ++++++++++++++++++++++++++++++++++++++++++++----- src/d_clisrv.h | 1 + src/d_net.c | 3 ++ src/i_net.h | 5 +++ src/i_tcp.c | 64 ++++++++++++++++++++++++--- 5 files changed, 174 insertions(+), 17 deletions(-) diff --git a/src/d_clisrv.c b/src/d_clisrv.c index f0cb7697e..3ba0b31aa 100644 --- a/src/d_clisrv.c +++ b/src/d_clisrv.c @@ -189,6 +189,8 @@ consvar_t cv_playbackspeed = CVAR_INIT ("playbackspeed", "1", 0, playbackspeed_c 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 inline void *G_DcpyTiccmd(void* dest, const ticcmd_t* src, const size_t n) { const size_t d = n / sizeof(ticcmd_t); @@ -2067,6 +2069,13 @@ static void Command_ShowBan(void) //Print out ban list else CONS_Printf("%s: %s/%s ", sizeu1(i+1), address, mask); + if (I_GetUnbanTime && I_GetUnbanTime(i) != NO_BAN_TIME) + { + // todo: maybe try to actually print out the time remaining, + // and/or the datetime of the unbanning? + CONS_Printf("(temporary) "); + } + if (I_GetBanReason && (reason = I_GetBanReason(i)) != NULL) CONS_Printf("(%s)\n", reason); else @@ -2082,6 +2091,8 @@ void D_SaveBan(void) FILE *f; size_t i; const char *address, *mask, *reason; + const time_t curTime = time(NULL); + time_t unbanTime = NO_BAN_TIME; const char *path = va("%s"PATHSEP"%s", srb2home, "ban.txt"); f = fopen(path, "w"); @@ -2094,11 +2105,24 @@ void D_SaveBan(void) for (i = 0; (address = I_GetBanAddress(i)) != NULL; i++) { + if (I_GetUnbanTime) + { + unbanTime = I_GetUnbanTime(i); + } + + if (unbanTime != NO_BAN_TIME && curTime >= unbanTime) + { + // Don't need to save this one anymore. + continue; + } + if (!I_GetBanMask || (mask = I_GetBanMask(i)) == NULL) fprintf(f, "%s 0", address); else fprintf(f, "%s %s", address, mask); + fprintf(f, " %ld", (long)unbanTime); + if (I_GetBanReason && (reason = I_GetBanReason(i)) != NULL) fprintf(f, " %s\n", reason); else @@ -2122,6 +2146,7 @@ static void Ban_Load_File(boolean warning) FILE *f; size_t i; const char *address, *mask, *reason; + time_t unbanTime = NO_BAN_TIME; char buffer[MAX_WADPATH]; if (!I_ClearBans) @@ -2142,9 +2167,12 @@ static void Ban_Load_File(boolean warning) { address = strtok(buffer, " \t\r\n"); mask = strtok(NULL, " \t\r\n"); + unbanTime = atoi(strtok(NULL, " \t\r\n")); reason = strtok(NULL, "\r\n"); I_SetBanAddress(address, mask); + if (I_SetUnbanTime) + I_SetUnbanTime(unbanTime); if (I_SetBanReason) I_SetBanReason(reason); } @@ -2620,6 +2648,7 @@ static void Got_KickCmd(UINT8 **p, INT32 playernum) char buf[3 + MAX_REASONLENGTH]; char *reason = buf; kickreason_t kickreason = KR_KICK; + UINT32 banMinutes = 0; pnum = READUINT8(*p); msg = READUINT8(*p); @@ -2683,17 +2712,35 @@ static void Got_KickCmd(UINT8 **p, INT32 playernum) // Save bans here. Used to be split between here and the actual command, depending on // whenever the server did it or a remote admin did it, but it's simply more convenient // to keep it all in one place. - if (server && (msg == KICK_MSG_BANNED || msg == KICK_MSG_CUSTOM_BAN)) + if (server) { - if (I_Ban && !I_Ban(playernode[(INT32)pnum])) + if (msg == KICK_MSG_GO_AWAY || msg == KICK_MSG_CUSTOM_KICK) { - CONS_Alert(CONS_WARNING, M_GetText("Ban failed. Invalid node?\n")); + // Kick as a temporary ban. + banMinutes = cv_kicktime.value; } - else + + if (msg == KICK_MSG_BANNED || msg == KICK_MSG_CUSTOM_BAN || banMinutes) { - if (I_SetBanReason) - I_SetBanReason(reason); - D_SaveBan(); + if (I_Ban && !I_Ban(playernode[(INT32)pnum])) + { + CONS_Alert(CONS_WARNING, M_GetText("Ban failed. Invalid node?\n")); + } + else + { + if (I_SetBanReason) + I_SetBanReason(reason); + + if (I_SetUnbanTime) + { + if (banMinutes) + I_SetUnbanTime(time(NULL) + (banMinutes * 60)); + else + I_SetUnbanTime(NO_BAN_TIME); + } + + D_SaveBan(); + } } } @@ -2791,13 +2838,16 @@ static void Got_KickCmd(UINT8 **p, INT32 playernum) if (playernode[pnum] == playernode[consoleplayer]) { - LUAh_GameQuit(false); #ifdef DUMPCONSISTENCY if (msg == KICK_MSG_CON_FAIL) SV_SavedGame(); #endif + + LUAh_GameQuit(false); + D_QuitNetGame(); CL_Reset(); D_StartTitle(); + if (msg == KICK_MSG_CON_FAIL) M_StartMessage(M_GetText("Server closed connection\n(Synch failure)\nPress ESC\n"), NULL, MM_NOTHING); else if (msg == KICK_MSG_PING_HIGH) @@ -3006,6 +3056,7 @@ void D_ClientServerInit(void) #ifndef NONET COM_AddCommand("getplayernum", Command_GetPlayerNum); COM_AddCommand("kick", Command_Kick); + CV_RegisterVar(&cv_kicktime); COM_AddCommand("ban", Command_Ban); COM_AddCommand("banip", Command_BanIP); COM_AddCommand("clearbans", Command_ClearBans); @@ -3641,31 +3692,74 @@ static void HandleConnect(SINT8 node) UINT8 maxplayers = min((dedicated ? MAXPLAYERS-1 : MAXPLAYERS), cv_maxplayers.value); if (bannednode && bannednode[node]) - SV_SendRefuse(node, M_GetText("You have been banned\nfrom the server.")); + { + if (bannednodetimeleft && bannednodetimeleft[node] != NO_BAN_TIME) + { + int minutes = bannednodetimeleft[node] / 60; + int hours = minutes / 60; + + if (hours) + { + SV_SendRefuse(node, va(M_GetText("You have been temporarily\nkicked from the server.\n(Time remaining: %d hour%s)"), hours, hours > 1 ? "s" : "")); + } + else if (minutes) + { + SV_SendRefuse(node, va(M_GetText("You have been temporarily\nkicked from the server.\n(Time remaining: %d minute%s)"), minutes, minutes > 1 ? "s" : "")); + } + else + { + SV_SendRefuse(node, M_GetText("You have been temporarily\nkicked from the server.\n(Time remaining: <1 minute)")); + } + } + else + { + SV_SendRefuse(node, M_GetText("You have been banned\nfrom the server.")); + } + } else if (netbuffer->u.clientcfg._255 != 255 || netbuffer->u.clientcfg.packetversion != PACKETVERSION) + { SV_SendRefuse(node, "Incompatible packet formats."); + } else if (strncmp(netbuffer->u.clientcfg.application, SRB2APPLICATION, sizeof netbuffer->u.clientcfg.application)) + { SV_SendRefuse(node, "Different Ring Racers modifications\nare not compatible."); + } else if (netbuffer->u.clientcfg.version != VERSION || netbuffer->u.clientcfg.subversion != SUBVERSION) + { SV_SendRefuse(node, va(M_GetText("Different Ring Racers versions cannot\nplay a netgame!\n(server version %d.%d)"), VERSION, SUBVERSION)); + } else if (!cv_allownewplayer.value && node) + { SV_SendRefuse(node, M_GetText("The server is not accepting\njoins for the moment.")); + } else if (D_NumPlayers() >= maxplayers) + { SV_SendRefuse(node, va(M_GetText("Maximum players reached: %d"), maxplayers)); + } else if (netgame && netbuffer->u.clientcfg.localplayers > MAXSPLITSCREENPLAYERS) // Hacked client? + { SV_SendRefuse(node, M_GetText("Too many players from\nthis node.")); + } else if (netgame && D_NumPlayers() + netbuffer->u.clientcfg.localplayers > maxplayers) + { SV_SendRefuse(node, va(M_GetText("Number of local players\nwould exceed maximum: %d"), maxplayers)); + } else if (netgame && !netbuffer->u.clientcfg.localplayers) // Stealth join? + { SV_SendRefuse(node, M_GetText("No players from\nthis node.")); + } else if (luafiletransfers) + { SV_SendRefuse(node, M_GetText("The server is broadcasting a file\nrequested by a Lua script.\nPlease wait a bit and then\ntry rejoining.")); + } else if (netgame && joindelay > 2 * (tic_t)cv_joindelay.value * TICRATE) + { SV_SendRefuse(node, va(M_GetText("Too many people are connecting.\nPlease wait %d seconds and then\ntry rejoining."), (joindelay - 2 * cv_joindelay.value * TICRATE) / TICRATE)); + } else { #ifndef NONET @@ -3939,13 +4033,13 @@ static void HandlePacketFromAwayNode(SINT8 node) break; } - M_StartMessage(va(M_GetText("Server refuses connection\n\nReason:\n%s"), - reason), NULL, MM_NOTHING); - D_QuitNetGame(); CL_Reset(); D_StartTitle(); + M_StartMessage(va(M_GetText("Server refuses connection\n\nReason:\n%s"), + reason), NULL, MM_NOTHING); + free(reason); // Will be reset by caller. Signals refusal. diff --git a/src/d_clisrv.h b/src/d_clisrv.h index 9fa7ac37d..48e1b0d0d 100644 --- a/src/d_clisrv.h +++ b/src/d_clisrv.h @@ -394,6 +394,7 @@ extern INT32 mapchangepending; extern doomdata_t *netbuffer; extern consvar_t cv_stunserver; extern consvar_t cv_httpsource; +extern consvar_t cv_kicktime; extern consvar_t cv_showjoinaddress; extern consvar_t cv_playbackspeed; diff --git a/src/d_net.c b/src/d_net.c index bdfd06767..cf465a1f0 100644 --- a/src/d_net.c +++ b/src/d_net.c @@ -84,9 +84,12 @@ const char *(*I_GetNodeAddress) (INT32 node) = NULL; const char *(*I_GetBanAddress) (size_t ban) = NULL; const char *(*I_GetBanMask) (size_t ban) = NULL; const char *(*I_GetBanReason) (size_t ban) = NULL; +time_t (*I_GetUnbanTime) (size_t ban) = NULL; boolean (*I_SetBanAddress) (const char *address, const char *mask) = NULL; boolean (*I_SetBanReason) (const char *reason) = NULL; +boolean (*I_SetUnbanTime) (time_t timestamp) = NULL; boolean *bannednode = NULL; +time_t *bannednodetimeleft = NULL; // network stats diff --git a/src/i_net.h b/src/i_net.h index f3d55a61a..5b40001eb 100644 --- a/src/i_net.h +++ b/src/i_net.h @@ -31,6 +31,8 @@ /// For use on the internet #define INETPACKETLENGTH 1024 +#define NO_BAN_TIME (time_t)(-1) + extern INT16 hardware_MAXPACKETLENGTH; extern INT32 net_bandwidth; // in byte/s @@ -163,9 +165,12 @@ extern const char *(*I_GetNodeAddress) (INT32 node); extern const char *(*I_GetBanAddress) (size_t ban); extern const char *(*I_GetBanMask) (size_t ban); extern const char *(*I_GetBanReason) (size_t ban); +extern time_t (*I_GetUnbanTime) (size_t ban); extern boolean (*I_SetBanAddress) (const char *address,const char *mask); extern boolean (*I_SetBanReason) (const char *reason); +extern boolean (*I_SetUnbanTime) (time_t timestamp); extern boolean *bannednode; +extern time_t *bannednodetimeleft; /// \brief Called by D_SRB2Main to be defined by extern network driver boolean I_InitNetwork(void); diff --git a/src/i_tcp.c b/src/i_tcp.c index 382df176e..f5d5c610e 100644 --- a/src/i_tcp.c +++ b/src/i_tcp.c @@ -193,7 +193,7 @@ mysockaddr_t address; UINT8 mask; char *reason; - // TODO: timestamp, for tempbans! + time_t timestamp; } banned_t; static SOCKET_TYPE mysockets[MAXNETNODES+1] = {ERRSOCKET}; @@ -212,6 +212,7 @@ static size_t numbans = 0; static size_t banned_size = 0; static boolean SOCK_bannednode[MAXNETNODES+1]; /// \note do we really need the +1? +static time_t SOCK_bannednodetimeleft[MAXNETNODES+1]; static boolean init_tcp_driver = false; static const char *serverport_name = DEFAULTPORT; @@ -463,10 +464,24 @@ static const char *SOCK_GetBanReason(size_t ban) (void)ban; return NULL; #else + if (ban >= numbans) + return NULL; return banned[ban].reason; #endif } +static time_t SOCK_GetUnbanTime(size_t ban) +{ +#ifdef NONET + (void)ban; + return NO_BAN_TIME; +#else + if (ban >= numbans) + return NO_BAN_TIME; + return banned[ban].timestamp; +#endif +} + #ifndef NONET static boolean SOCK_cmpaddr(mysockaddr_t *a, mysockaddr_t *b, UINT8 mask) { @@ -640,6 +655,8 @@ static boolean SOCK_Get(void) j = getfreenode(); if (j > 0) { + const time_t curTime = time(NULL); + M_Memcpy(&clientaddress[j], &fromaddress, fromlen); nodesocket[j] = mysockets[n]; DEBFILE(va("New node detected: node:%d address:%s\n", j, @@ -652,13 +669,37 @@ static boolean SOCK_Get(void) { if (SOCK_cmpaddr(&fromaddress, &banned[i].address, banned[i].mask)) { - SOCK_bannednode[j] = true; - DEBFILE("This dude has been banned\n"); - break; + if (banned[i].timestamp != NO_BAN_TIME) + { + if (curTime >= banned[i].timestamp) + { + SOCK_bannednodetimeleft[j] = NO_BAN_TIME; + SOCK_bannednode[j] = false; + DEBFILE("This dude was banned, but enough time has passed\n"); + break; + } + + SOCK_bannednodetimeleft[j] = banned[i].timestamp - curTime; + SOCK_bannednode[j] = true; + DEBFILE("This dude has been temporarily banned\n"); + break; + } + else + { + SOCK_bannednodetimeleft[j] = NO_BAN_TIME; + SOCK_bannednode[j] = true; + DEBFILE("This dude has been banned\n"); + break; + } } } + if (i == numbans) + { + SOCK_bannednodetimeleft[j] = NO_BAN_TIME; SOCK_bannednode[j] = false; + } + return true; } else @@ -1566,7 +1607,6 @@ static boolean SOCK_SetBanReason(const char *reason) (void)reason; return false; #else - if (!reason) { reason = "No reason given"; @@ -1577,6 +1617,17 @@ static boolean SOCK_SetBanReason(const char *reason) #endif } +static boolean SOCK_SetUnbanTime(time_t timestamp) +{ +#ifdef NONET + (void)reason; + return false; +#else + banned[numbans - 1].timestamp = timestamp; + return true; +#endif +} + static void SOCK_ClearBans(void) { numbans = 0; @@ -1677,9 +1728,12 @@ boolean I_InitTcpNetwork(void) I_GetBanAddress = SOCK_GetBanAddress; I_GetBanMask = SOCK_GetBanMask; I_GetBanReason = SOCK_GetBanReason; + I_GetUnbanTime = SOCK_GetUnbanTime; I_SetBanAddress = SOCK_SetBanAddress; I_SetBanReason = SOCK_SetBanReason; + I_SetUnbanTime = SOCK_SetUnbanTime; bannednode = SOCK_bannednode; + bannednodetimeleft = SOCK_bannednodetimeleft; return ret; } From 04ec0b1ffd8fc537c434b65c5ef5586e65619955 Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Tue, 7 Jun 2022 19:09:02 -0400 Subject: [PATCH 10/48] Implement shout commands - shout command to create a server message, with its own special sound cue & color - shoutname to control the nametag, by default this is "SERVER" - shoutcolor controls the color of the message. By default it's red, but there's also a option for player colored. - autoshout makes any message sent by a admin/server automatically turn into a shout - Unlike HOSTMOD shout, integrated it with the dedicated server say behavior -- using say on dedicated server is always a shout. --- src/d_netcmd.c | 5 +++ src/g_game.c | 30 +++++++++++++++++ src/g_game.h | 1 + src/hu_stuff.c | 90 ++++++++++++++++++++++++++++++++++++++++---------- src/sounds.c | 3 ++ src/sounds.h | 3 ++ 6 files changed, 114 insertions(+), 18 deletions(-) diff --git a/src/d_netcmd.c b/src/d_netcmd.c index a784ad8e2..8a3a0066e 100644 --- a/src/d_netcmd.c +++ b/src/d_netcmd.c @@ -947,6 +947,11 @@ void D_RegisterClientCommands(void) CV_RegisterVar(&cv_consolechat); CV_RegisterVar(&cv_chatnotifications); CV_RegisterVar(&cv_chatbacktint); + + CV_RegisterVar(&cv_shoutname); + CV_RegisterVar(&cv_shoutcolor); + CV_RegisterVar(&cv_autoshout); + CV_RegisterVar(&cv_songcredits); CV_RegisterVar(&cv_tutorialprompt); CV_RegisterVar(&cv_showfocuslost); diff --git a/src/g_game.c b/src/g_game.c index 896ea00ea..bfc66c4e1 100644 --- a/src/g_game.c +++ b/src/g_game.c @@ -393,6 +393,36 @@ consvar_t cv_chatbacktint = CVAR_INIT ("chatbacktint", "On", CV_SAVE, CV_OnOff, static CV_PossibleValue_t consolechat_cons_t[] = {{0, "Window"}, {1, "Console"}, {2, "Window (Hidden)"}, {0, NULL}}; consvar_t cv_consolechat = CVAR_INIT ("chatmode", "Window", CV_SAVE, consolechat_cons_t, NULL); +// Shout settings +// The relevant ones are CV_NETVAR because too lazy to send them any other way +consvar_t cv_shoutname = CVAR_INIT ("shout_name", "SERVER", CV_NETVAR, NULL, NULL); + +static CV_PossibleValue_t shoutcolor_cons_t[] = +{ + {-1, "Player color"}, + {0, "White"}, + {1, "Yellow"}, + {2, "Purple"}, + {3, "Green"}, + {4, "Blue"}, + {5, "Red"}, + {6, "Gray"}, + {7, "Orange"}, + {8, "Sky-blue"}, + {9, "Gold"}, + {10, "Lavender"}, + {11, "Aqua-green"}, + {12, "Magenta"}, + {13, "Pink"}, + {14, "Brown"}, + {15, "Tan"}, + {0, NULL} +}; +consvar_t cv_shoutcolor = CVAR_INIT ("shout_color", "Red", CV_NETVAR, shoutcolor_cons_t, NULL); + +// If on and you're an admin, your messages will automatically become shouts. +consvar_t cv_autoshout = CVAR_INIT ("autoshout", "Off", CV_NETVAR, CV_OnOff, NULL); + // Pause game upon window losing focus consvar_t cv_pauseifunfocused = CVAR_INIT ("pauseifunfocused", "Yes", CV_SAVE, CV_YesNo, NULL); diff --git a/src/g_game.h b/src/g_game.h index 52c7c7a0d..40495b5c3 100644 --- a/src/g_game.h +++ b/src/g_game.h @@ -48,6 +48,7 @@ extern boolean promptactive; extern consvar_t cv_tutorialprompt; extern consvar_t cv_chatwidth, cv_chatnotifications, cv_chatheight, cv_chattime, cv_consolechat, cv_chatbacktint, cv_chatspamprotection; +extern consvar_t cv_shoutname, cv_shoutcolor, cv_autoshout; extern consvar_t cv_songcredits; extern consvar_t cv_pauseifunfocused; diff --git a/src/hu_stuff.c b/src/hu_stuff.c index 933cccbb4..ed0727fc1 100644 --- a/src/hu_stuff.c +++ b/src/hu_stuff.c @@ -64,8 +64,11 @@ #define HU_INPUTX 0 #define HU_INPUTY 0 -#define HU_SERVER_SAY 1 // Server message (dedicated). -#define HU_CSAY 2 // Server CECHOes to everyone. +typedef enum +{ + HU_SHOUT = 1, // Shout message + HU_CSAY = 1<<1, // Middle-of-screen server message +} sayflags_t; //------------------------------------------- // heads up font @@ -169,6 +172,7 @@ static void Command_Say_f(void); 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); #endif @@ -210,6 +214,7 @@ void HU_Init(void) COM_AddCommand("sayto", Command_Sayto_f); COM_AddCommand("sayteam", Command_Sayteam_f); COM_AddCommand("csay", Command_CSay_f); + COM_AddCommand("shout", Command_Shout); RegisterNetXCmd(XD_SAY, Got_Saycmd); #endif @@ -456,7 +461,7 @@ void HU_AddChatText(const char *text, boolean playsound) * to -32 to say to everyone on that player's team. Note: This means you * have to add 1 to the player number, since they are 0 to 31 internally. * - * The flag HU_SERVER_SAY will be set if it is the dedicated server speaking. + * The flag HU_SHOUT will be set if it is the dedicated server speaking. * * This function obtains the message using COM_Argc() and COM_Argv(). * @@ -483,14 +488,17 @@ static void DoSayCommand(SINT8 target, size_t usedargs, UINT8 flags) return; } - // Only servers/admins can CSAY. - if(!server && !(IsPlayerAdmin(consoleplayer))) - flags &= ~HU_CSAY; + // Only servers/admins can shout or CSAY. + if (!server && !IsPlayerAdmin(consoleplayer)) + { + flags &= ~(HU_SHOUT|HU_CSAY); + } - // We handle HU_SERVER_SAY, not the caller. - flags &= ~HU_SERVER_SAY; - if(dedicated && !(flags & HU_CSAY)) - flags |= HU_SERVER_SAY; + // Enforce shout for the dedicated server. + if (dedicated && !(flags & HU_CSAY)) + { + flags |= HU_SHOUT; + } buf[0] = target; buf[1] = flags; @@ -564,6 +572,8 @@ static void Command_Say_f(void) return; } + // Autoshout is handled by HU_queueChatChar. + // If you're using the say command, you can use the shout command, lol. DoSayCommand(0, 1, 0); } @@ -627,7 +637,7 @@ static void Command_CSay_f(void) return; } - if(!server && !IsPlayerAdmin(consoleplayer)) + if (!server && !IsPlayerAdmin(consoleplayer)) { CONS_Alert(CONS_NOTICE, M_GetText("Only servers and admins can use csay.\n")); return; @@ -635,6 +645,24 @@ static void Command_CSay_f(void) DoSayCommand(0, 1, HU_CSAY); } + +static void Command_Shout(void) +{ + if (COM_Argc() < 2) + { + CONS_Printf(M_GetText("shout : send a message with special alert sound, name, and color\n")); + return; + } + + if (!server && !IsPlayerAdmin(consoleplayer)) + { + CONS_Alert(CONS_NOTICE, M_GetText("Only servers and admins can use shout.\n")); + return; + } + + DoSayCommand(0, 1, HU_SHOUT); +} + static tic_t stop_spamming[MAXPLAYERS]; /** Receives a message, processing an ::XD_SAY command. @@ -658,7 +686,7 @@ static void Got_Saycmd(UINT8 **p, INT32 playernum) msg = (char *)*p; SKIPSTRING(*p); - if ((cv_mute.value || flags & (HU_CSAY|HU_SERVER_SAY)) && playernum != serverplayer && !(IsPlayerAdmin(playernum))) + 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"), @@ -687,7 +715,7 @@ 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)) + 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; @@ -720,8 +748,8 @@ static void Got_Saycmd(UINT8 **p, INT32 playernum) action = true; } - if (flags & HU_SERVER_SAY) - dispname = "SERVER"; + if (flags & HU_SHOUT) + dispname = cv_shoutname.zstring; else dispname = player_names[playernum]; @@ -747,7 +775,30 @@ static void Got_Saycmd(UINT8 **p, INT32 playernum) char *tempchar = NULL; char color_prefix[2]; - if (players[playernum].spectator) + if (flags & HU_SHOUT) + { + if (cv_shoutcolor.value == -1) + { + UINT16 chatcolor = skincolors[players[playernum].skincolor].chatcolor; + + if (chatcolor > V_TANMAP) + { + sprintf(color_prefix, "%c", '\x80'); + } + else + { + sprintf(color_prefix, "%c", '\x80' + (chatcolor >> V_CHARCOLORSHIFT)); + } + } + else + { + sprintf(color_prefix, "%c", '\x80' + cv_shoutcolor.value); + } + + // Colorize full text + cstart = textcolor = color_prefix; + } + else if (players[playernum].spectator) { // grey text cstart = textcolor = "\x86"; @@ -834,7 +885,10 @@ static void Got_Saycmd(UINT8 **p, INT32 playernum) fmt2 = "%s<%s%s>\x80%s %s%s"; }*/ - HU_AddChatText(va(fmt2, prefix, cstart, dispname, cend, textcolor, msg), cv_chatnotifications.value); // add to chat + HU_AddChatText(va(fmt2, prefix, cstart, dispname, cend, textcolor, msg), (cv_chatnotifications.value) && !(flags & HU_SHOUT)); // add to chat + + if ((cv_chatnotifications.value) && (flags & HU_SHOUT)) + S_StartSound(NULL, sfx_sysmsg); if (tempchar) Z_Free(tempchar); @@ -1156,7 +1210,7 @@ static void HU_queueChatChar(INT32 c) else buf[0] = target; - buf[1] = 0; // flags + buf[1] = ((server || IsPlayerAdmin(consoleplayer)) && cv_autoshout.value) ? HU_SHOUT : 0; // flags SendNetXCmd(XD_SAY, buf, 2 + strlen(&buf[2]) + 1); } return; diff --git a/src/sounds.c b/src/sounds.c index 761fa109c..121e49a6b 100644 --- a/src/sounds.c +++ b/src/sounds.c @@ -1111,6 +1111,9 @@ sfxinfo_t S_sfx[NUMSFX] = // SRB2kart - Grow/invinc clash {"parry", false, 64, 16, -1, NULL, 0, -1, -1, LUMPERROR, ""}, // SF_X8AWAYSOUND + // Shout message sound effect + {"sysmsg", false, 60, 0, -1, NULL, 0, -1, -1, LUMPERROR, "Server notification"}, + // SRB2Kart - Engine sounds // Engine class A {"krta00", false, 48, 65, -1, NULL, 0, -1, -1, LUMPERROR, ""}, diff --git a/src/sounds.h b/src/sounds.h index 336cd8b74..d3468971a 100644 --- a/src/sounds.h +++ b/src/sounds.h @@ -1175,6 +1175,9 @@ typedef enum // SRB2Kart - Powerup clash SFX sfx_parry, + // Shout message sound effect + sfx_sysmsg, + // Next up: UNIQUE ENGINE SOUNDS! Hoooooo boy... // Engine class A - Low Speed, Low Weight sfx_krta00, From 55cbc410179a1d6f454b1178d99c9718ae8b5966 Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Tue, 7 Jun 2022 19:22:44 -0400 Subject: [PATCH 11/48] Attempt fix for players being able to get admin Tyron said there is an "outstanding base game bug where an authenticated player can ghost and a new player will log in on their node, inheriting admin" ... wow that's terrifying! Let's reset admin for every instance of CL_ClearPlayer instead of only in CL_RemovePlayer. --- src/d_clisrv.c | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/src/d_clisrv.c b/src/d_clisrv.c index 3ba0b31aa..6ac5ab551 100644 --- a/src/d_clisrv.c +++ b/src/d_clisrv.c @@ -2291,6 +2291,8 @@ void CL_ClearPlayer(INT32 playernum) splitscreen_original_party_size[playernum] = 0; memset(&players[playernum], 0, sizeof (player_t)); + + RemoveAdminPlayer(playernum); // don't stay admin after you're gone } // @@ -2348,11 +2350,6 @@ void CL_RemovePlayer(INT32 playernum, kickreason_t reason) player_name_changes[playernum] = 0; - if (IsPlayerAdmin(playernum)) - { - RemoveAdminPlayer(playernum); // don't stay admin after you're gone - } - LUA_InvalidatePlayer(&players[playernum]); K_CheckBumpers(); From 6b6d88641f7b98c3f524e5523060863b629686ed Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Fri, 10 Jun 2022 09:24:08 -0400 Subject: [PATCH 12/48] Scheduling commands - `schedule_add ` to add a command that runs on a recurring timer. - `schedule_list` to print out all of the scheduled tasks. - NEW: `schedule_clear` to revert the schedule to a blank slate. - `schedule` cvar determines whenever or not to run the scheduled tasks. Unlike HOSTMOD, turning this off will reset the timers of the tasks, instead of freezing them. - I did not implement HOSTMOD's ability to pick from several random command per scheduled task. Would drastically increase the code complexity when you can just use a choose command in your schedule_add for the exact same effect. --- src/d_clisrv.c | 22 +++- src/d_netcmd.c | 297 ++++++++++++++++++++++++++++++++++++++++++++++++- src/d_netcmd.h | 20 ++++ src/k_pwrlv.c | 5 +- src/p_saveg.c | 34 +++++- 5 files changed, 363 insertions(+), 15 deletions(-) diff --git a/src/d_clisrv.c b/src/d_clisrv.c index 6ac5ab551..e60a9a549 100644 --- a/src/d_clisrv.c +++ b/src/d_clisrv.c @@ -2001,7 +2001,9 @@ static void CL_ConnectToServer(void) wipegamestate = GS_WAITINGPLAYERS; ClearAdminPlayers(); + Schedule_Clear(); K_ClearClientPowerLevels(); + pnumnodes = 1; oldtic = 0; #ifndef NONET @@ -3135,14 +3137,17 @@ void SV_ResetServer(void) for (i = 0; i < MAXPLAYERS; i++) { LUA_InvalidatePlayer(&players[i]); - playeringame[i] = false; - playernode[i] = UINT8_MAX; sprintf(player_names[i], "Player %c", 'A' + i); - adminplayers[i] = -1; // Populate the entire adminplayers array with -1. - K_ClearClientPowerLevels(); - splitscreen_invitations[i] = -1; } + memset(playeringame, false, sizeof playeringame); + memset(playernode, UINT8_MAX, sizeof playernode); + + ClearAdminPlayers(); + Schedule_Clear(); + K_ClearClientPowerLevels(); + + memset(splitscreen_invitations, -1, sizeof splitscreen_invitations); memset(splitscreen_partied, 0, sizeof splitscreen_partied); memset(player_name_changes, 0, sizeof player_name_changes); @@ -3227,6 +3232,7 @@ void D_QuitNetGame(void) D_CloseConnection(); ClearAdminPlayers(); + Schedule_Clear(); K_ClearClientPowerLevels(); DEBFILE("===========================================================================\n" @@ -5230,7 +5236,13 @@ boolean TryRunTics(tic_t realtics) ps_tictime = I_GetPreciseTime(); G_Ticker((gametic % NEWTICRATERATIO) == 0); + if (gametic % TICRATE == 0) + { + Schedule_Run(); + } + ExtraDataTicker(); + gametic++; consistancy[gametic%BACKUPTICS] = Consistancy(); diff --git a/src/d_netcmd.c b/src/d_netcmd.c index 8a3a0066e..f518939b7 100644 --- a/src/d_netcmd.c +++ b/src/d_netcmd.c @@ -98,6 +98,8 @@ static void Got_RunSOCcmd(UINT8 **cp, INT32 playernum); static void Got_Teamchange(UINT8 **cp, INT32 playernum); static void Got_Clearscores(UINT8 **cp, INT32 playernum); static void Got_DiscordInfo(UINT8 **cp, INT32 playernum); +static void Got_ScheduleTaskcmd(UINT8 **cp, INT32 playernum); +static void Got_ScheduleClearcmd(UINT8 **cp, INT32 playernum); static void PointLimit_OnChange(void); static void TimeLimit_OnChange(void); @@ -148,6 +150,8 @@ static void KartEncore_OnChange(void); static void KartComeback_OnChange(void); static void KartEliminateLast_OnChange(void); +static void Schedule_OnChange(void); + #ifdef NETGAME_DEVMODE static void Fishcake_OnChange(void); #endif @@ -223,6 +227,10 @@ static void Command_Archivetest_f(void); static void Command_KartGiveItem_f(void); +static void Command_Schedule_Add(void); +static void Command_Schedule_Clear(void); +static void Command_Schedule_List(void); + // ========================================================================= // CLIENT VARIABLES // ========================================================================= @@ -524,6 +532,8 @@ consvar_t cv_perfstats = CVAR_INIT ("perfstats", "Off", 0, perfstats_cons_t, NUL consvar_t cv_director = CVAR_INIT ("director", "Off", 0, CV_OnOff, NULL); +consvar_t cv_schedule = CVAR_INIT ("schedule", "On", CV_NETVAR|CV_CALL, CV_OnOff, Schedule_OnChange); + char timedemo_name[256]; boolean timedemo_csv; char timedemo_csv_id[256]; @@ -539,6 +549,11 @@ UINT8 splitscreen = 0; boolean circuitmap = false; INT32 adminplayers[MAXPLAYERS]; +// Scheduled cvars. +scheduleTask_t **schedule = NULL; +size_t schedule_size = 0; +size_t schedule_len = 0; + /// \warning Keep this up-to-date if you add/remove/rename net text commands const char *netxcmdnames[MAXNETXCMD - 1] = { @@ -578,7 +593,9 @@ const char *netxcmdnames[MAXNETXCMD - 1] = "GIVEITEM", // XD_GIVEITEM "ADDBOT", // XD_ADDBOT "DISCORD", // XD_DISCORD - "PLAYSOUND" // XD_PLAYSOUND + "PLAYSOUND", // XD_PLAYSOUND + "SCHEDULETASK", // XD_SCHEDULETASK + "SCHEDULECLEAR" // XD_SCHEDULECLEAR }; // ========================================================================= @@ -633,6 +650,9 @@ void D_RegisterServerCommands(void) RegisterNetXCmd(XD_GIVEITEM, Got_GiveItemcmd); + RegisterNetXCmd(XD_SCHEDULETASK, Got_ScheduleTaskcmd); + RegisterNetXCmd(XD_SCHEDULECLEAR, Got_ScheduleClearcmd); + // Remote Administration COM_AddCommand("password", Command_Changepassword_f); COM_AddCommand("login", Command_Login_f); // useful in dedicated to kick off remote admin @@ -690,6 +710,10 @@ void D_RegisterServerCommands(void) COM_AddCommand("kartgiveitem", Command_KartGiveItem_f); + COM_AddCommand("schedule_add", Command_Schedule_Add); + COM_AddCommand("schedule_clear", Command_Schedule_Clear); + COM_AddCommand("schedule_list", Command_Schedule_List); + // for master server connection AddMServCommands(); @@ -762,6 +786,8 @@ void D_RegisterServerCommands(void) CV_RegisterVar(&cv_director); + CV_RegisterVar(&cv_schedule); + CV_RegisterVar(&cv_dummyconsvar); #ifdef USE_STUN @@ -3733,9 +3759,7 @@ void SetAdminPlayer(INT32 playernum) void ClearAdminPlayers(void) { - INT32 i; - for (i = 0; i < MAXPLAYERS; i++) - adminplayers[i] = -1; + memset(adminplayers, -1, sizeof(adminplayers)); } void RemoveAdminPlayer(INT32 playernum) @@ -3852,6 +3876,117 @@ static void Got_Removal(UINT8 **cp, INT32 playernum) CONS_Printf(M_GetText("You are no longer a server administrator.\n")); } +void Schedule_Run(void) +{ + size_t i; + + if (schedule_len == 0) + { + // No scheduled tasks to run. + return; + } + + if (!cv_schedule.value) + { + // We don't WANT to run tasks. + return; + } + + for (i = 0; i < schedule_len; i++) + { + scheduleTask_t *task = schedule[i]; + + if (task == NULL) + { + // Shouldn't happen. + break; + } + + if (task->timer > 0) + { + task->timer--; + } + + if (task->timer == 0) + { + // Reset timer + task->timer = task->basetime; + + // Run command for server + if (server) + { + CONS_Printf( + "%d seconds elapsed, running \"" "\x82" "%s" "\x80" "\".\n", + task->basetime, + task->command + ); + + COM_BufAddText(task->command); + COM_BufAddText("\n"); + } + } + } +} + +void Schedule_Insert(scheduleTask_t *addTask) +{ + if (schedule_len >= schedule_size) + { + if (schedule_size == 0) + { + schedule_size = 8; + } + else + { + schedule_size *= 2; + } + + schedule = Z_ReallocAlign( + (void*) schedule, + sizeof(scheduleTask_t*) * schedule_size, + PU_STATIC, + NULL, + sizeof(scheduleTask_t*) * 8 + ); + } + + schedule[schedule_len] = addTask; + schedule_len++; +} + +void Schedule_Add(INT16 basetime, INT16 timeleft, const char *command) +{ + scheduleTask_t *task = (scheduleTask_t*) Z_CallocAlign( + sizeof(scheduleTask_t), + PU_STATIC, + NULL, + sizeof(scheduleTask_t) * 8 + ); + + task->basetime = basetime; + task->timer = timeleft; + task->command = Z_StrDup(command); + + Schedule_Insert(task); +} + +void Schedule_Clear(void) +{ + size_t i; + + for (i = 0; i < schedule_len; i++) + { + scheduleTask_t *task = schedule[i]; + + if (task->command) + Z_Free(task->command); + } + + schedule_len = 0; + schedule_size = 0; + schedule = NULL; +} + static void Command_MotD_f(void) { size_t i, j; @@ -5043,6 +5178,58 @@ static void Got_GiveItemcmd(UINT8 **cp, INT32 playernum) players[playernum].itemamount = amt; } +static void Got_ScheduleTaskcmd(UINT8 **cp, INT32 playernum) +{ + char command[MAXTEXTCMD]; + INT16 seconds; + + seconds = READINT16(*cp); + READSTRING(*cp, command); + + if (playernum != serverplayer && !IsPlayerAdmin(playernum)) + { + CONS_Alert(CONS_WARNING, + M_GetText ("Illegal schedule task received from %s\n"), + player_names[playernum]); + if (server) + SendKick(playernum, KICK_MSG_CON_FAIL); + return; + } + + Schedule_Add(seconds, seconds, (const char *)command); + + if (server || consoleplayer == playernum) + { + CONS_Printf( + "OK! Running \"" "\x82" "%s" "\x80" "\" every " "\x82" "%d" "\x80" " seconds.\n", + command, + seconds + ); + } +} + +static void Got_ScheduleClearcmd(UINT8 **cp, INT32 playernum) +{ + (void)cp; + + if (playernum != serverplayer && !IsPlayerAdmin(playernum)) + { + CONS_Alert(CONS_WARNING, + M_GetText ("Illegal schedule clear received from %s\n"), + player_names[playernum]); + if (server) + SendKick(playernum, KICK_MSG_CON_FAIL); + return; + } + + Schedule_Clear(); + + if (server || consoleplayer == playernum) + { + CONS_Printf("All scheduled tasks have been cleared.\n"); + } +} + /** Prints the number of displayplayers[0]. * * \todo Possibly remove this; it was useful for debugging at one point. @@ -5307,6 +5494,86 @@ static void Command_KartGiveItem_f(void) } } +static void Command_Schedule_Add(void) +{ + UINT8 buf[MAXTEXTCMD]; + UINT8 *buf_p = buf; + + size_t ac; + INT16 seconds; + const char *command; + + if (!(server || IsPlayerAdmin(consoleplayer))) + { + CONS_Printf("Only the server or a remote admin can use this.\n"); + return; + } + + ac = COM_Argc(); + if (ac < 3) + { + CONS_Printf("schedule <...>: runs the specified commands on a recurring timer\n"); + return; + } + + seconds = atoi(COM_Argv(1)); + + if (seconds <= 0) + { + CONS_Printf("Timer must be at least 1 second.\n"); + return; + } + + command = COM_Argv(2); + + WRITEINT16(buf_p, seconds); + WRITESTRING(buf_p, command); + + SendNetXCmd(XD_SCHEDULETASK, buf, buf_p - buf); +} + +static void Command_Schedule_Clear(void) +{ + if (!(server || IsPlayerAdmin(consoleplayer))) + { + CONS_Printf("Only the server or a remote admin can use this.\n"); + return; + } + + SendNetXCmd(XD_SCHEDULECLEAR, NULL, 0); +} + +static void Command_Schedule_List(void) +{ + size_t i; + + if (!(server || IsPlayerAdmin(consoleplayer))) + { + // I set it up in a way that this information could be available + // to everyone, but HOSTMOD has it server/admin-only too, so eh? + CONS_Printf("Only the server or a remote admin can use this.\n"); + return; + } + + if (schedule_len == 0) + { + CONS_Printf("No tasks are scheduled.\n"); + return; + } + + for (i = 0; i < schedule_len; i++) + { + scheduleTask_t *task = schedule[i]; + + CONS_Printf( + "In " "\x82" "%d" "\x80" " second%s: " "\x82" "%s" "\x80" "\n", + task->timer, + (task->timer > 1) ? "s" : "", + task->command + ); + } +} + /** Makes a change to ::cv_forceskin take effect immediately. * * \sa Command_SetForcedSkin_f, cv_forceskin, forcedskin @@ -5934,6 +6201,28 @@ static void KartEliminateLast_OnChange(void) P_CheckRacers(); } +static void Schedule_OnChange(void) +{ + size_t i; + + if (cv_schedule.value) + { + return; + } + + if (schedule_len == 0) + { + return; + } + + // Reset timers when turning off. + for (i = 0; i < schedule_len; i++) + { + scheduleTask_t *task = schedule[i]; + task->timer = task->basetime; + } +} + void Got_DiscordInfo(UINT8 **p, INT32 playernum) { if (playernum != serverplayer /*&& !IsPlayerAdmin(playernum)*/) diff --git a/src/d_netcmd.h b/src/d_netcmd.h index 8acb2ed9a..f8e9369fb 100644 --- a/src/d_netcmd.h +++ b/src/d_netcmd.h @@ -116,6 +116,8 @@ extern consvar_t cv_perfstats; extern consvar_t cv_director; +extern consvar_t cv_schedule; + extern char timedemo_name[256]; extern boolean timedemo_csv; extern char timedemo_csv_id[256]; @@ -160,6 +162,8 @@ typedef enum XD_ADDBOT, // 33 XD_DISCORD, // 34 XD_PLAYSOUND, // 35 + XD_SCHEDULETASK, // 36 + XD_SCHEDULECLEAR, // 37 MAXNETXCMD } netxcmd_t; @@ -228,6 +232,22 @@ void RemoveAdminPlayer(INT32 playernum); void ItemFinder_OnChange(void); void D_SetPassword(const char *pw); +typedef struct +{ + UINT16 basetime; + UINT16 timer; + char *command; +} scheduleTask_t; + +extern scheduleTask_t **schedule; +extern size_t schedule_size; +extern size_t schedule_len; + +void Schedule_Run(void); +void Schedule_Insert(scheduleTask_t *addTask); +void Schedule_Add(INT16 basetime, INT16 timeleft, const char *command); +void Schedule_Clear(void); + // used for the player setup menu UINT8 CanChangeSkin(INT32 playernum); diff --git a/src/k_pwrlv.c b/src/k_pwrlv.c index 22c386191..e9b3664ee 100644 --- a/src/k_pwrlv.c +++ b/src/k_pwrlv.c @@ -50,10 +50,7 @@ SINT8 K_UsingPowerLevels(void) void K_ClearClientPowerLevels(void) { - UINT8 i, j; - for (i = 0; i < MAXPLAYERS; i++) - for (j = 0; j < PWRLV_NUMTYPES; j++) - clientpowerlevels[i][j] = 0; + memset(clientpowerlevels, 0, sizeof clientpowerlevels); } // Adapted from this: http://wiki.tockdom.com/wiki/Player_Rating diff --git a/src/p_saveg.c b/src/p_saveg.c index 0ae0ee72a..b2dfec620 100644 --- a/src/p_saveg.c +++ b/src/p_saveg.c @@ -4401,7 +4401,7 @@ static inline void P_UnArchiveSPGame(INT16 mapoverride) static void P_NetArchiveMisc(boolean resending) { - INT32 i; + size_t i; WRITEUINT32(save_p, ARCHIVEBLOCK_MISC); @@ -4530,11 +4530,23 @@ static void P_NetArchiveMisc(boolean resending) WRITEUINT8(save_p, 0x2f); else WRITEUINT8(save_p, 0x2e); + + // Only the server uses this, but it + // needs synched for remote admins anyway. + WRITEUINT32(save_p, schedule_len); + for (i = 0; i < schedule_len; i++) + { + scheduleTask_t *task = schedule[i]; + WRITEINT16(save_p, task->basetime); + WRITEINT16(save_p, task->timer); + WRITESTRING(save_p, task->command); + } } static inline boolean P_NetUnArchiveMisc(boolean reloading) { - INT32 i; + size_t i; + size_t numTasks; if (READUINT32(save_p) != ARCHIVEBLOCK_MISC) I_Error("Bad $$$.sav at archive block Misc"); @@ -4678,6 +4690,24 @@ static inline boolean P_NetUnArchiveMisc(boolean reloading) if (READUINT8(save_p) == 0x2f) paused = true; + // Only the server uses this, but it + // needs synched for remote admins anyway. + Schedule_Clear(); + + numTasks = READUINT32(save_p); + for (i = 0; i < numTasks; i++) + { + INT16 basetime; + INT16 timer; + char command[MAXTEXTCMD]; + + basetime = READINT16(save_p); + timer = READINT16(save_p); + READSTRING(save_p, command); + + Schedule_Add(basetime, timer, command); + } + return true; } From 79101e56e44b6cfd0e8c05628c6a6bdd983d425b Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Fri, 10 Jun 2022 11:19:17 -0400 Subject: [PATCH 13/48] Automate commands - `automate_set ` to set a command to run each time an event triggers. - Currently implemented events are "roundstart", "intermissionstart", and "votestart", all of the ones from HOSTMOD. - Turn `automate` off to disable this feature entirely. Because of the new safer way this is implemented (in HOSTMOD, this just calls some console aliases), this is turned on by default instead of off. - This is set up in a way to facilitate adding more automation events very easily, if desired. --- src/d_clisrv.c | 3 + src/d_netcmd.c | 192 ++++++++++++++++++++++++++++++++++++++++++++++++- src/d_netcmd.h | 13 ++++ src/g_game.c | 10 +++ src/y_inter.c | 3 + 5 files changed, 219 insertions(+), 2 deletions(-) diff --git a/src/d_clisrv.c b/src/d_clisrv.c index e60a9a549..2a6cf1fde 100644 --- a/src/d_clisrv.c +++ b/src/d_clisrv.c @@ -2002,6 +2002,7 @@ static void CL_ConnectToServer(void) ClearAdminPlayers(); Schedule_Clear(); + Automate_Clear(); K_ClearClientPowerLevels(); pnumnodes = 1; @@ -3145,6 +3146,7 @@ void SV_ResetServer(void) ClearAdminPlayers(); Schedule_Clear(); + Automate_Clear(); K_ClearClientPowerLevels(); memset(splitscreen_invitations, -1, sizeof splitscreen_invitations); @@ -3233,6 +3235,7 @@ void D_QuitNetGame(void) D_CloseConnection(); ClearAdminPlayers(); Schedule_Clear(); + Automate_Clear(); K_ClearClientPowerLevels(); DEBFILE("===========================================================================\n" diff --git a/src/d_netcmd.c b/src/d_netcmd.c index f518939b7..ec35da083 100644 --- a/src/d_netcmd.c +++ b/src/d_netcmd.c @@ -100,6 +100,7 @@ static void Got_Clearscores(UINT8 **cp, INT32 playernum); static void Got_DiscordInfo(UINT8 **cp, INT32 playernum); static void Got_ScheduleTaskcmd(UINT8 **cp, INT32 playernum); static void Got_ScheduleClearcmd(UINT8 **cp, INT32 playernum); +static void Got_Automatecmd(UINT8 **cp, INT32 playernum); static void PointLimit_OnChange(void); static void TimeLimit_OnChange(void); @@ -231,6 +232,8 @@ static void Command_Schedule_Add(void); static void Command_Schedule_Clear(void); static void Command_Schedule_List(void); +static void Command_Automate_Set(void); + // ========================================================================= // CLIENT VARIABLES // ========================================================================= @@ -534,6 +537,8 @@ consvar_t cv_director = CVAR_INIT ("director", "Off", 0, CV_OnOff, NULL); consvar_t cv_schedule = CVAR_INIT ("schedule", "On", CV_NETVAR|CV_CALL, CV_OnOff, Schedule_OnChange); +consvar_t cv_automate = CVAR_INIT ("automate", "On", CV_NETVAR, CV_OnOff, NULL); + char timedemo_name[256]; boolean timedemo_csv; char timedemo_csv_id[256]; @@ -549,11 +554,21 @@ UINT8 splitscreen = 0; boolean circuitmap = false; INT32 adminplayers[MAXPLAYERS]; -// Scheduled cvars. +// Scheduled commands. scheduleTask_t **schedule = NULL; size_t schedule_size = 0; size_t schedule_len = 0; +// Automation commands +char *automate_commands[AEV__MAX]; + +const char *automate_names[AEV__MAX] = +{ + "RoundStart", // AEV_ROUNDSTART + "IntermissionStart", // AEV_INTERMISSIONSTART + "VoteStart" // AEV_VOTESTART +}; + /// \warning Keep this up-to-date if you add/remove/rename net text commands const char *netxcmdnames[MAXNETXCMD - 1] = { @@ -595,7 +610,8 @@ const char *netxcmdnames[MAXNETXCMD - 1] = "DISCORD", // XD_DISCORD "PLAYSOUND", // XD_PLAYSOUND "SCHEDULETASK", // XD_SCHEDULETASK - "SCHEDULECLEAR" // XD_SCHEDULECLEAR + "SCHEDULECLEAR", // XD_SCHEDULECLEAR + "AUTOMATE" // XD_AUTOMATE }; // ========================================================================= @@ -652,6 +668,7 @@ void D_RegisterServerCommands(void) RegisterNetXCmd(XD_SCHEDULETASK, Got_ScheduleTaskcmd); RegisterNetXCmd(XD_SCHEDULECLEAR, Got_ScheduleClearcmd); + RegisterNetXCmd(XD_AUTOMATE, Got_Automatecmd); // Remote Administration COM_AddCommand("password", Command_Changepassword_f); @@ -714,6 +731,8 @@ void D_RegisterServerCommands(void) COM_AddCommand("schedule_clear", Command_Schedule_Clear); COM_AddCommand("schedule_list", Command_Schedule_List); + COM_AddCommand("automate_set", Command_Automate_Set); + // for master server connection AddMServCommands(); @@ -787,6 +806,7 @@ void D_RegisterServerCommands(void) CV_RegisterVar(&cv_director); CV_RegisterVar(&cv_schedule); + CV_RegisterVar(&cv_automate); CV_RegisterVar(&cv_dummyconsvar); @@ -3987,6 +4007,82 @@ void Schedule_Clear(void) schedule = NULL; } +void Automate_Set(automateEvents_t type, const char *command) +{ + if (!server) + { + // Since there's no list command or anything for this, + // we don't need this code to run for anyone but the server. + return; + } + + if (automate_commands[type] != NULL) + { + // Free the old command. + Z_Free(automate_commands[type]); + } + + if (command == NULL || strlen(command) == 0) + { + // Remove the command. + automate_commands[type] = NULL; + } + else + { + // New command. + automate_commands[type] = Z_StrDup(command); + } +} + +void Automate_Run(automateEvents_t type) +{ + if (!server) + { + // Only the server should be doing this. + return; + } + +#ifdef PARANOIA + if (type >= AEV__MAX) + { + // Shouldn't happen. + I_Error("Attempted to run invalid automation type."); + return; + } +#endif + + if (!cv_automate.value) + { + // We don't want to run automation. + return; + } + + if (automate_commands[type] == NULL) + { + // No command to run. + return; + } + + CONS_Printf( + "Running %s automate command \"" "\x82" "%s" "\x80" "\"...\n", + automate_names[type], + automate_commands[type] + ); + + COM_BufAddText(automate_commands[type]); + COM_BufAddText("\n"); +} + +void Automate_Clear(void) +{ + size_t i; + + for (i = 0; i < AEV__MAX; i++) + { + Automate_Set(i, NULL); + } +} + static void Command_MotD_f(void) { size_t i, j; @@ -5230,6 +5326,49 @@ static void Got_ScheduleClearcmd(UINT8 **cp, INT32 playernum) } } +static void Got_Automatecmd(UINT8 **cp, INT32 playernum) +{ + UINT8 eventID; + char command[MAXTEXTCMD]; + + eventID = READUINT8(*cp); + READSTRING(*cp, command); + + if ( + (playernum != serverplayer && !IsPlayerAdmin(playernum)) + || (eventID >= AEV__MAX) + ) + { + CONS_Alert(CONS_WARNING, + M_GetText ("Illegal automate received from %s\n"), + player_names[playernum]); + if (server) + SendKick(playernum, KICK_MSG_CON_FAIL); + return; + } + + Automate_Set(eventID, command); + + if (server || consoleplayer == playernum) + { + if (command == NULL || strlen(command) == 0) + { + CONS_Printf( + "Removed the %s automate command.\n", + automate_names[eventID] + ); + } + else + { + CONS_Printf( + "Set the %s automate command to \"" "\x82" "%s" "\x80" "\".\n", + automate_names[eventID], + command + ); + } + } +} + /** Prints the number of displayplayers[0]. * * \todo Possibly remove this; it was useful for debugging at one point. @@ -5574,6 +5713,55 @@ static void Command_Schedule_List(void) } } +static void Command_Automate_Set(void) +{ + UINT8 buf[MAXTEXTCMD]; + UINT8 *buf_p = buf; + + size_t ac; + + const char *event; + size_t eventID; + + const char *command; + + if (!(server || IsPlayerAdmin(consoleplayer))) + { + CONS_Printf("Only the server or a remote admin can use this.\n"); + return; + } + + ac = COM_Argc(); + if (ac < 3) + { + CONS_Printf("automate_set : sets the command to run each time a event triggers\n"); + return; + } + + event = COM_Argv(1); + + for (eventID = 0; eventID < AEV__MAX; eventID++) + { + if (strcasecmp(event, automate_names[eventID]) == 0) + { + break; + } + } + + if (eventID == AEV__MAX) + { + CONS_Printf("Unknown event type \"%s\".\n", event); + return; + } + + command = COM_Argv(2); + + WRITEUINT8(buf_p, eventID); + WRITESTRING(buf_p, command); + + SendNetXCmd(XD_AUTOMATE, buf, buf_p - buf); +} + /** Makes a change to ::cv_forceskin take effect immediately. * * \sa Command_SetForcedSkin_f, cv_forceskin, forcedskin diff --git a/src/d_netcmd.h b/src/d_netcmd.h index f8e9369fb..b17b53a22 100644 --- a/src/d_netcmd.h +++ b/src/d_netcmd.h @@ -164,6 +164,7 @@ typedef enum XD_PLAYSOUND, // 35 XD_SCHEDULETASK, // 36 XD_SCHEDULECLEAR, // 37 + XD_AUTOMATE, // 38 MAXNETXCMD } netxcmd_t; @@ -248,6 +249,18 @@ void Schedule_Insert(scheduleTask_t *addTask); void Schedule_Add(INT16 basetime, INT16 timeleft, const char *command); void Schedule_Clear(void); +typedef enum +{ + AEV_ROUNDSTART, + AEV_INTERMISSIONSTART, + AEV_VOTESTART, + AEV__MAX +} automateEvents_t; + +void Automate_Run(automateEvents_t type); +void Automate_Set(automateEvents_t type, const char *command); +void Automate_Clear(void); + // used for the player setup menu UINT8 CanChangeSkin(INT32 playernum); diff --git a/src/g_game.c b/src/g_game.c index bfc66c4e1..14325977b 100644 --- a/src/g_game.c +++ b/src/g_game.c @@ -1332,6 +1332,7 @@ static void weaponPrefChange4(void) void G_DoLoadLevel(boolean resetplayer) { INT32 i, j; + boolean doAutomate = false; // Make sure objectplace is OFF when you first start the level! OP_ResetObjectplace(); @@ -1361,6 +1362,10 @@ void G_DoLoadLevel(boolean resetplayer) else titlemapinaction = TITLEMAP_OFF; + // Doing this matches HOSTMOD behavior. + // Is that desired? IDK + doAutomate = (gamestate != GS_LEVEL); + G_SetGamestate(GS_LEVEL); I_UpdateMouseGrab(); @@ -1402,6 +1407,11 @@ void G_DoLoadLevel(boolean resetplayer) CON_ClearHUD(); server_lagless = cv_lagless.value; + + if (doAutomate == true) + { + Automate_Run(AEV_ROUNDSTART); + } } // diff --git a/src/y_inter.c b/src/y_inter.c index d612f0e75..d59855c28 100644 --- a/src/y_inter.c +++ b/src/y_inter.c @@ -1166,6 +1166,8 @@ void Y_StartIntermission(void) usetile = useinterpic = false; usebuffer = true; } + + Automate_Run(AEV_INTERMISSIONSTART); } // ====== @@ -1828,6 +1830,7 @@ void Y_StartVote(void) } voteclient.loaded = true; + Automate_Run(AEV_VOTESTART); } // From f19f8bc706e036866f4ff67628e963b7a4316cf1 Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Fri, 10 Jun 2022 18:27:43 -0400 Subject: [PATCH 14/48] Ban improvements - Save a note of the username, not just the reason. - Allow setting a mask with the `banip` command. - Make ban.txt's formatting a lot more sane. Username and reason are stored in quotes. The mask uses the same formatting as actual CDIR. - Keep track of if we tried to load ban.txt. If it wasn't, then don't save over it with a blank file. - Disallow quotes in player names, as it makes player name detection in console more annoying, and saving username in files scary. --- src/d_clisrv.c | 119 ++++++++++++++++++++++++++++++++++++++----------- src/d_net.c | 2 + src/d_net.h | 1 + src/d_netcmd.c | 14 ++++-- src/i_net.h | 2 + src/i_tcp.c | 50 ++++++++++++++++++++- 6 files changed, 159 insertions(+), 29 deletions(-) diff --git a/src/d_clisrv.c b/src/d_clisrv.c index 2a6cf1fde..1803e5367 100644 --- a/src/d_clisrv.c +++ b/src/d_clisrv.c @@ -2089,15 +2089,25 @@ static void Command_ShowBan(void) //Print out ban list CONS_Printf(M_GetText("(empty)\n")); } +static boolean bansLoaded = false; + void D_SaveBan(void) { FILE *f; size_t i; - const char *address, *mask, *reason; + const char *address, *mask; + const char *username, *reason; const time_t curTime = time(NULL); time_t unbanTime = NO_BAN_TIME; const char *path = va("%s"PATHSEP"%s", srb2home, "ban.txt"); + if (bansLoaded != true) + { + // You didn't even get to ATTEMPT to load bans.txt. + // Don't immediately save nothing over it. + return; + } + f = fopen(path, "w"); if (!f) @@ -2112,24 +2122,37 @@ void D_SaveBan(void) { unbanTime = I_GetUnbanTime(i); } + else + { + unbanTime = NO_BAN_TIME; + } if (unbanTime != NO_BAN_TIME && curTime >= unbanTime) { - // Don't need to save this one anymore. + // This one has served their sentence. + // We don't need to save them in the file anymore. continue; } + mask = NULL; if (!I_GetBanMask || (mask = I_GetBanMask(i)) == NULL) - fprintf(f, "%s 0", address); + fprintf(f, "%s/0", address); else - fprintf(f, "%s %s", address, mask); + fprintf(f, "%s/%s", address, mask); fprintf(f, " %ld", (long)unbanTime); - if (I_GetBanReason && (reason = I_GetBanReason(i)) != NULL) - fprintf(f, " %s\n", reason); + username = NULL; + if (I_GetBanUsername && (username = I_GetBanUsername(i)) != NULL) + fprintf(f, " \"%s\"", username); else - fprintf(f, " %s\n", "No reason given"); + fprintf(f, " \"%s\"", "Direct IP ban"); + + reason = NULL; + if (I_GetBanReason && (reason = I_GetBanReason(i)) != NULL) + fprintf(f, " \"%s\"\n", reason); + else + fprintf(f, " \"%s\"\n", "No reason given"); } fclose(f); @@ -2144,17 +2167,21 @@ static void Command_ClearBans(void) D_SaveBan(); } -static void Ban_Load_File(boolean warning) +void D_LoadBan(boolean warning) { FILE *f; size_t i; - const char *address, *mask, *reason; + const char *address, *mask; + const char *username, *reason; time_t unbanTime = NO_BAN_TIME; char buffer[MAX_WADPATH]; if (!I_ClearBans) return; + // We at least attempted loading bans.txt + bansLoaded = true; + f = fopen(va("%s"PATHSEP"%s", srb2home, "ban.txt"), "r"); if (!f) @@ -2166,16 +2193,26 @@ static void Ban_Load_File(boolean warning) I_ClearBans(); - for (i=0; fgets(buffer, (int)sizeof(buffer), f); i++) + for (i = 0; fgets(buffer, (int)sizeof(buffer), f); i++) { - address = strtok(buffer, " \t\r\n"); + address = strtok(buffer, "/\t\r\n"); mask = strtok(NULL, " \t\r\n"); - unbanTime = atoi(strtok(NULL, " \t\r\n")); - reason = strtok(NULL, "\r\n"); + + unbanTime = atoi(strtok(NULL, " \"\t\r\n")); + + username = strtok(NULL, "\"\t\r\n"); // go until next " + + strtok(NULL, "\"\t\r\n"); // remove first " + reason = strtok(NULL, "\"\r\n"); // go until next " I_SetBanAddress(address, mask); + if (I_SetUnbanTime) I_SetUnbanTime(unbanTime); + + if (I_SetBanUsername) + I_SetBanUsername(username); + if (I_SetBanReason) I_SetBanReason(reason); } @@ -2185,7 +2222,7 @@ static void Ban_Load_File(boolean warning) static void Command_ReloadBan(void) //recheck ban.txt { - Ban_Load_File(true); + D_LoadBan(true); } static void Command_connect(void) @@ -2546,31 +2583,60 @@ static void Command_Ban(void) static void Command_BanIP(void) { - if (COM_Argc() < 2) + size_t ac = COM_Argc(); + + if (ac < 2) { - CONS_Printf(M_GetText("banip : ban an ip address\n")); + CONS_Printf(M_GetText("banip []: ban an ip address\n")); return; } if (server) // Only the server can use this, otherwise does nothing. { - const char *address = (COM_Argv(1)); - const char *reason; + char *addressInput = Z_StrDup(COM_Argv(1)); - if (COM_Argc() == 2) - reason = NULL; - else + const char *address = NULL; + const char *mask = NULL; + + const char *reason = NULL; + + address = strtok(addressInput, "/"); + mask = strtok(NULL, ""); + + if (ac > 2) + { reason = COM_Argv(2); + } - if (I_SetBanAddress && I_SetBanAddress(address, NULL)) + if (I_SetBanAddress && I_SetBanAddress(address, mask)) { if (reason) - CONS_Printf("Banned IP address %s for: %s\n", address, reason); + { + CONS_Printf( + "Banned IP address %s%s for: %s\n", + address, + (mask && (strlen(mask) > 0)) ? va("/%s", mask) : "", + reason + ); + } else - CONS_Printf("Banned IP address %s\n", address); + { + CONS_Printf( + "Banned IP address %s%s\n", + address, + (mask && (strlen(mask) > 0)) ? va("/%s", mask) : "" + ); + } + + if (I_SetUnbanTime) + I_SetUnbanTime(NO_BAN_TIME); + + if (I_SetBanUsername) + I_SetBanUsername(NULL); if (I_SetBanReason) I_SetBanReason(reason); + D_SaveBan(); } else @@ -2728,6 +2794,9 @@ static void Got_KickCmd(UINT8 **p, INT32 playernum) } else { + if (I_SetBanUsername) + I_SetBanUsername(player_names[pnum]); + if (I_SetBanReason) I_SetBanReason(reason); @@ -3086,7 +3155,7 @@ void D_ClientServerInit(void) #ifdef DUMPCONSISTENCY CV_RegisterVar(&cv_dumpconsistency); #endif - Ban_Load_File(false); + D_LoadBan(false); #endif gametic = 0; diff --git a/src/d_net.c b/src/d_net.c index cf465a1f0..618bd002f 100644 --- a/src/d_net.c +++ b/src/d_net.c @@ -83,9 +83,11 @@ void (*I_ClearBans)(void) = NULL; const char *(*I_GetNodeAddress) (INT32 node) = NULL; const char *(*I_GetBanAddress) (size_t ban) = NULL; const char *(*I_GetBanMask) (size_t ban) = NULL; +const char *(*I_GetBanUsername) (size_t ban) = NULL; const char *(*I_GetBanReason) (size_t ban) = NULL; time_t (*I_GetUnbanTime) (size_t ban) = NULL; boolean (*I_SetBanAddress) (const char *address, const char *mask) = NULL; +boolean (*I_SetBanUsername) (const char *username) = NULL; boolean (*I_SetBanReason) (const char *reason) = NULL; boolean (*I_SetUnbanTime) (time_t timestamp) = NULL; boolean *bannednode = NULL; diff --git a/src/d_net.h b/src/d_net.h index 7f50706ee..9586cecb9 100644 --- a/src/d_net.h +++ b/src/d_net.h @@ -55,6 +55,7 @@ boolean HGetPacket(void); void D_SetDoomcom(void); #ifndef NONET void D_SaveBan(void); +void D_LoadBan(boolean warning); #endif boolean D_CheckNetGame(void); void D_CloseConnection(void); diff --git a/src/d_netcmd.c b/src/d_netcmd.c index ec35da083..4f0d98641 100644 --- a/src/d_netcmd.c +++ b/src/d_netcmd.c @@ -1121,6 +1121,15 @@ void D_RegisterClientCommands(void) * \sa CleanupPlayerName, SetPlayerName, Got_NameAndColor * \author Graue */ + +static boolean AllowedPlayerNameChar(char ch) +{ + if (!isprint(ch) || ch == ';' || ch == '"' || (UINT8)(ch) >= 0x80) + return false; + + return true; +} + boolean EnsurePlayerNameIsGood(char *name, INT32 playernum) { INT32 ix; @@ -1142,7 +1151,7 @@ boolean EnsurePlayerNameIsGood(char *name, INT32 playernum) // Also, anything over 0x80 is disallowed too, since compilers love to // differ on whether they're printable characters or not. for (ix = 0; name[ix] != '\0'; ix++) - if (!isprint(name[ix]) || name[ix] == ';' || (UINT8)(name[ix]) >= 0x80) + if (!AllowedPlayerNameChar(name[ix])) return false; // Check if a player is currently using the name, case-insensitively. @@ -1233,8 +1242,7 @@ void CleanupPlayerName(INT32 playernum, const char *newname) do { - /* from EnsurePlayerNameIsGood */ - if (!isprint(*p) || *p == ';' || (UINT8)*p >= 0x80) + if (!AllowedPlayerNameChar(*p)) break; } while (*++p) ; diff --git a/src/i_net.h b/src/i_net.h index 5b40001eb..92fe1bf2d 100644 --- a/src/i_net.h +++ b/src/i_net.h @@ -164,9 +164,11 @@ extern void (*I_ClearBans)(void); extern const char *(*I_GetNodeAddress) (INT32 node); extern const char *(*I_GetBanAddress) (size_t ban); extern const char *(*I_GetBanMask) (size_t ban); +extern const char *(*I_GetBanUsername) (size_t ban); extern const char *(*I_GetBanReason) (size_t ban); extern time_t (*I_GetUnbanTime) (size_t ban); extern boolean (*I_SetBanAddress) (const char *address,const char *mask); +extern boolean (*I_SetBanUsername) (const char *username); extern boolean (*I_SetBanReason) (const char *reason); extern boolean (*I_SetUnbanTime) (time_t timestamp); extern boolean *bannednode; diff --git a/src/i_tcp.c b/src/i_tcp.c index f5d5c610e..7b3db6f9d 100644 --- a/src/i_tcp.c +++ b/src/i_tcp.c @@ -192,6 +192,7 @@ { mysockaddr_t address; UINT8 mask; + char *username; char *reason; time_t timestamp; } banned_t; @@ -458,6 +459,18 @@ static const char *SOCK_GetBanMask(size_t ban) return NULL; } +static const char *SOCK_GetBanUsername(size_t ban) +{ +#ifdef NONET + (void)ban; + return NULL; +#else + if (ban >= numbans) + return NULL; + return banned[ban].username; +#endif +} + static const char *SOCK_GetBanReason(size_t ban) { #ifdef NONET @@ -1592,6 +1605,11 @@ static boolean SOCK_SetBanAddress(const char *address, const char *mask) banned[ban].mask = 128; #endif + // Set defaults, in case anything funny happens. + SOCK_SetBanUsername(NULL); + SOCK_SetBanReason(NULL); + SOCK_SetUnbanTime(NO_BAN_TIME); + runp = runp->ai_next; } @@ -1601,17 +1619,45 @@ static boolean SOCK_SetBanAddress(const char *address, const char *mask) #endif } +static boolean SOCK_SetBanUsername(const char *username) +{ +#ifdef NONET + (void)username; + return false; +#else + if (username == NULL || strlen(username) == 0) + { + username = "Direct IP ban"; + } + + if (banned[numbans - 1].username) + { + Z_Free(banned[numbans - 1].username); + banned[numbans - 1].username = NULL; + } + + banned[numbans - 1].username = Z_StrDup(username); + return true; +#endif +} + static boolean SOCK_SetBanReason(const char *reason) { #ifdef NONET (void)reason; return false; #else - if (!reason) + if (reason == NULL || strlen(reason) == 0) { reason = "No reason given"; } + if (banned[numbans - 1].reason) + { + Z_Free(banned[numbans - 1].reason); + banned[numbans - 1].reason = NULL; + } + banned[numbans - 1].reason = Z_StrDup(reason); return true; #endif @@ -1727,9 +1773,11 @@ boolean I_InitTcpNetwork(void) I_GetNodeAddress = SOCK_GetNodeAddress; I_GetBanAddress = SOCK_GetBanAddress; I_GetBanMask = SOCK_GetBanMask; + I_GetBanUsername = SOCK_GetBanUsername; I_GetBanReason = SOCK_GetBanReason; I_GetUnbanTime = SOCK_GetUnbanTime; I_SetBanAddress = SOCK_SetBanAddress; + I_SetBanUsername = SOCK_SetBanUsername; I_SetBanReason = SOCK_SetBanReason; I_SetUnbanTime = SOCK_SetUnbanTime; bannednode = SOCK_bannednode; From 9a49d96c765574dfed0b87c6ae5fa060d5dcde8e Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Fri, 10 Jun 2022 18:34:17 -0400 Subject: [PATCH 15/48] date-time todo comment --- src/d_clisrv.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/d_clisrv.c b/src/d_clisrv.c index 1803e5367..2bfad1b4b 100644 --- a/src/d_clisrv.c +++ b/src/d_clisrv.c @@ -2140,6 +2140,8 @@ void D_SaveBan(void) else fprintf(f, "%s/%s", address, mask); + // TODO: it'd be nice to convert this to an actual date-time, + // so it'd be easier to edit outside of the game. fprintf(f, " %ld", (long)unbanTime); username = NULL; From 260ab10728ff22b3729aa7430a644c2e05bb9621 Mon Sep 17 00:00:00 2001 From: James R Date: Mon, 1 Aug 2022 07:46:35 -0700 Subject: [PATCH 16/48] Fix compiler warnings --- src/d_netcmd.c | 2 +- src/i_tcp.c | 110 ++++++++++++++++++++++++------------------------- 2 files changed, 56 insertions(+), 56 deletions(-) diff --git a/src/d_netcmd.c b/src/d_netcmd.c index 4f0d98641..b89711e91 100644 --- a/src/d_netcmd.c +++ b/src/d_netcmd.c @@ -5359,7 +5359,7 @@ static void Got_Automatecmd(UINT8 **cp, INT32 playernum) if (server || consoleplayer == playernum) { - if (command == NULL || strlen(command) == 0) + if (command[0] == '\0') { CONS_Printf( "Removed the %s automate command.\n", diff --git a/src/i_tcp.c b/src/i_tcp.c index 7b3db6f9d..46af0fdf1 100644 --- a/src/i_tcp.c +++ b/src/i_tcp.c @@ -1555,6 +1555,61 @@ static boolean SOCK_Ban(INT32 node) #endif } +static boolean SOCK_SetBanUsername(const char *username) +{ +#ifdef NONET + (void)username; + return false; +#else + if (username == NULL || strlen(username) == 0) + { + username = "Direct IP ban"; + } + + if (banned[numbans - 1].username) + { + Z_Free(banned[numbans - 1].username); + banned[numbans - 1].username = NULL; + } + + banned[numbans - 1].username = Z_StrDup(username); + return true; +#endif +} + +static boolean SOCK_SetBanReason(const char *reason) +{ +#ifdef NONET + (void)reason; + return false; +#else + if (reason == NULL || strlen(reason) == 0) + { + reason = "No reason given"; + } + + if (banned[numbans - 1].reason) + { + Z_Free(banned[numbans - 1].reason); + banned[numbans - 1].reason = NULL; + } + + banned[numbans - 1].reason = Z_StrDup(reason); + return true; +#endif +} + +static boolean SOCK_SetUnbanTime(time_t timestamp) +{ +#ifdef NONET + (void)reason; + return false; +#else + banned[numbans - 1].timestamp = timestamp; + return true; +#endif +} + static boolean SOCK_SetBanAddress(const char *address, const char *mask) { #ifdef NONET @@ -1619,61 +1674,6 @@ static boolean SOCK_SetBanAddress(const char *address, const char *mask) #endif } -static boolean SOCK_SetBanUsername(const char *username) -{ -#ifdef NONET - (void)username; - return false; -#else - if (username == NULL || strlen(username) == 0) - { - username = "Direct IP ban"; - } - - if (banned[numbans - 1].username) - { - Z_Free(banned[numbans - 1].username); - banned[numbans - 1].username = NULL; - } - - banned[numbans - 1].username = Z_StrDup(username); - return true; -#endif -} - -static boolean SOCK_SetBanReason(const char *reason) -{ -#ifdef NONET - (void)reason; - return false; -#else - if (reason == NULL || strlen(reason) == 0) - { - reason = "No reason given"; - } - - if (banned[numbans - 1].reason) - { - Z_Free(banned[numbans - 1].reason); - banned[numbans - 1].reason = NULL; - } - - banned[numbans - 1].reason = Z_StrDup(reason); - return true; -#endif -} - -static boolean SOCK_SetUnbanTime(time_t timestamp) -{ -#ifdef NONET - (void)reason; - return false; -#else - banned[numbans - 1].timestamp = timestamp; - return true; -#endif -} - static void SOCK_ClearBans(void) { numbans = 0; From 113dd6a6009fc525aff31187fc7cb5ba15fca757 Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Sun, 5 Jun 2022 07:50:36 -0400 Subject: [PATCH 17/48] Map helper commands from HOSTMOD --- src/d_netcmd.c | 65 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 65 insertions(+) diff --git a/src/d_netcmd.c b/src/d_netcmd.c index 7e8143427..1eb6ce0a4 100644 --- a/src/d_netcmd.c +++ b/src/d_netcmd.c @@ -156,6 +156,8 @@ static void Command_Stopdemo_f(void); static void Command_StartMovie_f(void); static void Command_StopMovie_f(void); static void Command_Map_f(void); +static void Command_RandomMap(void); +static void Command_RestartLevel(void); static void Command_ResetCamera_f(void); static void Command_View_f (void); @@ -640,6 +642,8 @@ void D_RegisterServerCommands(void) RegisterNetXCmd(XD_CLEARSCORES, Got_Clearscores); COM_AddCommand("clearscores", Command_Clearscores_f); COM_AddCommand("map", Command_Map_f); + COM_AddCommand("randommap", Command_RandomMap); + COM_AddCommand("restartlevel", Command_RestartLevel); COM_AddCommand("exitgame", Command_ExitGame_f); COM_AddCommand("retry", Command_Retry_f); @@ -2939,6 +2943,67 @@ static void Got_Mapcmd(UINT8 **cp, INT32 playernum) #endif } +static void Command_RandomMap(void) +{ + INT32 oldmapnum; + INT32 newmapnum; + INT32 newgametype; + boolean newencoremode; + boolean newresetplayers; + + if (client && !IsPlayerAdmin(consoleplayer)) + { + CONS_Printf(M_GetText("Only the server or a remote admin can use this.\n")); + return; + } + + // TODO: Handle singleplayer conditions. + // The existing ones are way too annoyingly complicated and "anti-cheat" for my tastes. + + if (Playing()) + { + newgametype = gametype; + newencoremode = encoremode; + newresetplayers = false; + + if (gamestate == GS_LEVEL) + { + oldmapnum = gamemap-1; + } + else + { + oldmapnum = prevmap; + } + } + else + { + newgametype = cv_newgametype.value; + newencoremode = false; + newresetplayers = true; + oldmapnum = -1; + } + + newmapnum = G_RandMap(G_TOLFlag(newgametype), oldmapnum, 0, 0, false, NULL) + 1; + D_MapChange(newmapnum, newgametype, newencoremode, newresetplayers, 0, false, false); +} + +static void Command_RestartLevel(void) +{ + if (client && !IsPlayerAdmin(consoleplayer)) + { + CONS_Printf(M_GetText("Only the server or a remote admin can use this.\n")); + return; + } + + if (!Playing()) + { + CONS_Printf(M_GetText("You must be in a game to use this.\n")); + return; + } + + D_MapChange(gamemap, gametype, encoremode, false, 0, false, false); +} + static void Command_Pause(void) { UINT8 buf[2]; From 70c48fc0e9c50403d87a86d87bf8e7c08a4d1f8e Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Sun, 5 Jun 2022 09:35:31 -0400 Subject: [PATCH 18/48] Ping measured in frame delay instead of milliseconds The part of HOSTMOD ministats that I wanted. Can go back to ms using the pingmeasurement cvar. If Tyron wants to bring ministats fully in I think it'd be better to bring its ideas to replace the current HUD instead, ideally using the existing ping gfx, so they should bring it up with Oni --- src/d_clisrv.c | 12 ++++++------ src/d_net.c | 41 +++++++++++++++++++++++++++++------------ src/d_netcmd.c | 8 ++++++-- src/d_netcmd.h | 1 + src/doomstat.h | 1 - src/hu_stuff.c | 47 +++++++++++++++++++++++++++++++---------------- src/m_menu.c | 6 ++---- 7 files changed, 75 insertions(+), 41 deletions(-) diff --git a/src/d_clisrv.c b/src/d_clisrv.c index b03eb478e..f34c243e6 100644 --- a/src/d_clisrv.c +++ b/src/d_clisrv.c @@ -122,7 +122,7 @@ SINT8 nodetoplayer4[MAXNETNODES]; // say the numplayer for this node if any (spl UINT8 playerpernode[MAXNETNODES]; // used specialy for splitscreen boolean nodeingame[MAXNETNODES]; // set false as nodes leave game -tic_t servermaxping = 800; // server's max ping. Defaults to 800 +tic_t servermaxping = 20; // server's max delay, in frames. Defaults to 20 static tic_t nettics[MAXNETNODES]; // what tic the client have received static tic_t supposedtics[MAXNETNODES]; // nettics prevision for smaller packet static UINT8 nodewaiting[MAXNETNODES]; @@ -2777,7 +2777,7 @@ static void Got_KickCmd(UINT8 **p, INT32 playernum) kickreason = KR_KICK; break; case KICK_MSG_PING_HIGH: - HU_AddChatText(va("\x82*%s left the game (Broke ping limit)", player_names[pnum]), false); + HU_AddChatText(va("\x82*%s left the game (Broke delay limit)", player_names[pnum]), false); kickreason = KR_PINGLIMIT; break; case KICK_MSG_CON_FAIL: @@ -2869,7 +2869,7 @@ static void Got_KickCmd(UINT8 **p, INT32 playernum) if (msg == KICK_MSG_CON_FAIL) M_StartMessage(M_GetText("Server closed connection\n(Synch failure)\nPress ESC\n"), NULL, MM_NOTHING); else if (msg == KICK_MSG_PING_HIGH) - M_StartMessage(M_GetText("Server closed connection\n(Broke ping limit)\nPress ESC\n"), NULL, MM_NOTHING); + M_StartMessage(M_GetText("Server closed connection\n(Broke delay limit)\nPress ESC\n"), NULL, MM_NOTHING); else if (msg == KICK_MSG_BANNED) M_StartMessage(M_GetText("You have been banned by the server\n\nPress ESC\n"), NULL, MM_NOTHING); else if (msg == KICK_MSG_CUSTOM_KICK) @@ -5337,7 +5337,7 @@ static void UpdatePingTable(void) if (! server_lagless && playernode[i] > 0 && !players[i].spectator) { lag = GetLag(playernode[i]); - realpingtable[i] += (INT32)(lag * (1000.00f/TICRATE)); + realpingtable[i] += lag; if (! fastest || lag < fastest) fastest = lag; @@ -5345,7 +5345,7 @@ static void UpdatePingTable(void) else { // TicsToMilliseconds can't handle pings over 1000ms lol - realpingtable[i] += (INT32)(GetLag(playernode[i]) * (1000.00f/TICRATE)); + realpingtable[i] += GetLag(playernode[i]); } } } @@ -5363,7 +5363,7 @@ static void UpdatePingTable(void) else lag = GetLag(0); - lag = ( realpingtable[0] + G_TicsToMilliseconds(lag) ); + lag = ( realpingtable[0] + lag ); switch (playerpernode[0]) { diff --git a/src/d_net.c b/src/d_net.c index 1c81fe3f3..558310e70 100644 --- a/src/d_net.c +++ b/src/d_net.c @@ -1389,6 +1389,7 @@ struct pingcell { INT32 num; INT32 ms; + INT32 f; }; static int pingcellcmp(const void *va, const void *vb) @@ -1412,6 +1413,7 @@ void Command_Ping_f(void) INT32 pingc; int name_width = 0; + int f_width = 0; int ms_width = 0; int n; @@ -1419,21 +1421,35 @@ void Command_Ping_f(void) pingc = 0; for (i = 1; i < MAXPLAYERS; ++i) - if (playeringame[i]) { - n = strlen(player_names[i]); - if (n > name_width) - name_width = n; + if (playeringame[i]) + { + INT32 ms; - n = playerpingtable[i]; - if (n > ms_width) - ms_width = n; + n = strlen(player_names[i]); + if (n > name_width) + name_width = n; - pingv[pingc].num = i; - pingv[pingc].ms = playerpingtable[i]; - pingc++; + n = playerpingtable[i]; + if (n > f_width) + f_width = n; + + ms = (INT32)(playerpingtable[i] * (1000.00f / TICRATE)); + n = ms; + if (n > ms_width) + ms_width = n; + + pingv[pingc].num = i; + pingv[pingc].f = playerpingtable[i]; + pingv[pingc].ms = ms; + pingc++; + } } + if (f_width < 10) f_width = 1; + else if (f_width < 100) f_width = 2; + else f_width = 3; + if (ms_width < 10) ms_width = 1; else if (ms_width < 100) ms_width = 2; else ms_width = 3; @@ -1442,15 +1458,16 @@ void Command_Ping_f(void) for (i = 0; i < pingc; ++i) { - CONS_Printf("%02d : %-*s %*d ms\n", + CONS_Printf("%02d : %-*s %*d frames (%*d ms)\n", pingv[i].num, name_width, player_names[pingv[i].num], + f_width, pingv[i].f, ms_width, pingv[i].ms); } if (!server && playeringame[consoleplayer]) { - CONS_Printf("\nYour ping is %d ms\n", playerpingtable[consoleplayer]); + CONS_Printf("\nYour ping is %d frames (%d ms)\n", playerpingtable[consoleplayer], (INT32)(playerpingtable[i] * (1000.00f / TICRATE))); } } diff --git a/src/d_netcmd.c b/src/d_netcmd.c index 1eb6ce0a4..4e7af8712 100644 --- a/src/d_netcmd.c +++ b/src/d_netcmd.c @@ -485,17 +485,20 @@ static CV_PossibleValue_t nettimeout_cons_t[] = {{TICRATE/7, "MIN"}, {60*TICRATE consvar_t cv_nettimeout = CVAR_INIT ("nettimeout", "105", CV_CALL|CV_SAVE, nettimeout_cons_t, NetTimeout_OnChange); //static CV_PossibleValue_t jointimeout_cons_t[] = {{5*TICRATE, "MIN"}, {60*TICRATE, "MAX"}, {0, NULL}}; consvar_t cv_jointimeout = CVAR_INIT ("jointimeout", "105", CV_CALL|CV_SAVE, nettimeout_cons_t, JoinTimeout_OnChange); -consvar_t cv_maxping = CVAR_INIT ("maxping", "800", CV_SAVE, CV_Unsigned, NULL); +consvar_t cv_maxping = CVAR_INIT ("maxdelay", "20", CV_SAVE, CV_Unsigned, NULL); consvar_t cv_lagless = CVAR_INIT ("lagless", "Off", CV_SAVE|CV_NETVAR|CV_CALL, CV_OnOff, Lagless_OnChange); static CV_PossibleValue_t pingtimeout_cons_t[] = {{8, "MIN"}, {120, "MAX"}, {0, NULL}}; -consvar_t cv_pingtimeout = CVAR_INIT ("pingtimeout", "10", CV_SAVE|CV_NETVAR, pingtimeout_cons_t, NULL); +consvar_t cv_pingtimeout = CVAR_INIT ("maxdelaytimeout", "10", CV_SAVE|CV_NETVAR, pingtimeout_cons_t, NULL); // show your ping on the HUD next to framerate. Defaults to warning only (shows up if your ping is > maxping) static CV_PossibleValue_t showping_cons_t[] = {{0, "Off"}, {1, "Always"}, {2, "Warning"}, {0, NULL}}; consvar_t cv_showping = CVAR_INIT ("showping", "Always", CV_SAVE, showping_cons_t, NULL); +static CV_PossibleValue_t pingmeasurement_cons_t[] = {{0, "Frames"}, {1, "Milliseconds"}, {0, NULL}}; +consvar_t cv_pingmeasurement = CVAR_INIT ("pingmeasurement", "Frames", CV_SAVE, pingmeasurement_cons_t, NULL); + consvar_t cv_showviewpointtext = CVAR_INIT ("showviewpointtext", "On", CV_SAVE, CV_OnOff, NULL); // Intermission time Tails 04-19-2002 @@ -748,6 +751,7 @@ void D_RegisterServerCommands(void) CV_RegisterVar(&cv_lagless); CV_RegisterVar(&cv_pingtimeout); CV_RegisterVar(&cv_showping); + CV_RegisterVar(&cv_pingmeasurement); CV_RegisterVar(&cv_showviewpointtext); CV_RegisterVar(&cv_director); diff --git a/src/d_netcmd.h b/src/d_netcmd.h index e84342795..bc20ff50f 100644 --- a/src/d_netcmd.h +++ b/src/d_netcmd.h @@ -104,6 +104,7 @@ extern consvar_t cv_maxping; extern consvar_t cv_lagless; extern consvar_t cv_pingtimeout; extern consvar_t cv_showping; +extern consvar_t cv_pingmeasurement; extern consvar_t cv_showviewpointtext; extern consvar_t cv_skipmapcheck; diff --git a/src/doomstat.h b/src/doomstat.h index 791504815..c9d5d7e5c 100644 --- a/src/doomstat.h +++ b/src/doomstat.h @@ -797,7 +797,6 @@ extern consvar_t cv_forceskin; // force clients to use the server's skin extern consvar_t cv_downloading; // allow clients to downloading WADs. extern consvar_t cv_nettimeout; // SRB2Kart: Advanced server options menu extern consvar_t cv_jointimeout; -extern consvar_t cv_maxping; extern ticcmd_t netcmds[BACKUPTICS][MAXPLAYERS]; extern INT32 serverplayer; extern INT32 adminplayers[MAXPLAYERS]; diff --git a/src/hu_stuff.c b/src/hu_stuff.c index f4f52f5cf..b378e33af 100644 --- a/src/hu_stuff.c +++ b/src/hu_stuff.c @@ -75,6 +75,7 @@ // Note: I'd like to adress that at this point we might *REALLY* want to work towards a common drawString function that can take any font we want because this is really turning into a MESS. :V -Lat' patch_t *pinggfx[5]; // small ping graphic patch_t *mping[5]; // smaller ping graphic +patch_t *pingmeasure[2]; // ping measurement graphic patch_t *framecounter; patch_t *frameslash; // framerate stuff. Used in screen.c @@ -189,6 +190,9 @@ void HU_LoadGraphics(void) HU_UpdatePatch(&mping[i], "MPING%d", i+1); } + HU_UpdatePatch(&pingmeasure[0], "PINGF"); + HU_UpdatePatch(&pingmeasure[1], "PINGMS"); + // fps stuff HU_UpdatePatch(&framecounter, "FRAMER"); HU_UpdatePatch(&frameslash, "FRAMESL"); @@ -2246,15 +2250,15 @@ void HU_Erase(void) //====================================================================== static int -Ping_gfx_num (int ping) +Ping_gfx_num (int lag) { - if (ping < 76) + if (lag < 2) return 0; - else if (ping < 137) + else if (lag < 4) return 1; - else if (ping < 256) + else if (lag < 7) return 2; - else if (ping < 500) + else if (lag < 10) return 3; else return 4; @@ -2263,22 +2267,33 @@ Ping_gfx_num (int ping) // // HU_drawPing // -void HU_drawPing(INT32 x, INT32 y, UINT32 ping, INT32 flags) +void HU_drawPing(INT32 x, INT32 y, UINT32 lag, INT32 flags) { - INT32 gfxnum; // gfx to draw - UINT8 const *colormap = R_GetTranslationColormap(TC_RAINBOW, SKINCOLOR_RASPBERRY, GTC_CACHE); + UINT8 *colormap = NULL; + INT32 measureid = cv_pingmeasurement.value ? 1 : 0; + INT32 gfxnum; // gfx to draw - gfxnum = Ping_gfx_num(ping); + gfxnum = Ping_gfx_num(lag); - V_DrawScaledPatch(x, y, flags, pinggfx[gfxnum]); - if (servermaxping && ping > servermaxping && hu_tick < 4) // flash ping red if too high - V_DrawPingNum(x, y+9, flags, ping, colormap); - else - V_DrawPingNum(x, y+9, flags, ping, NULL); + V_DrawScaledPatch(x+11 - pingmeasure[measureid]->width, y+9, flags, pingmeasure[measureid]); + V_DrawScaledPatch(x+2, y, flags, pinggfx[gfxnum]); + + if (servermaxping && lag > servermaxping && hu_tick < 4) + { + // flash ping red if too high + colormap = R_GetTranslationColormap(TC_RAINBOW, SKINCOLOR_RASPBERRY, GTC_CACHE); + } + + if (cv_pingmeasurement.value) + { + lag = (INT32)(lag * (1000.00f / TICRATE)); + } + + V_DrawPingNum(x+11 - pingmeasure[measureid]->width, y+9, flags, lag, colormap); } void -HU_drawMiniPing (INT32 x, INT32 y, UINT32 ping, INT32 flags) +HU_drawMiniPing (INT32 x, INT32 y, UINT32 lag, INT32 flags) { patch_t *patch; INT32 w = BASEVIDWIDTH; @@ -2288,7 +2303,7 @@ HU_drawMiniPing (INT32 x, INT32 y, UINT32 ping, INT32 flags) w /= 2; } - patch = mping[Ping_gfx_num(ping)]; + patch = mping[Ping_gfx_num(lag)]; if (( flags & V_SNAPTORIGHT )) x += ( w - SHORT (patch->width) ); diff --git a/src/m_menu.c b/src/m_menu.c index 58a40176e..d88435680 100644 --- a/src/m_menu.c +++ b/src/m_menu.c @@ -1521,8 +1521,8 @@ static menuitem_t OP_AdvServerOptionsMenu[] = NULL, "Server Browser Address", {.cvar = &cv_masterserver}, 10}, {IT_STRING | IT_CVAR, NULL, "Attempts to resynchronise", {.cvar = &cv_resynchattempts}, 40}, - {IT_STRING | IT_CVAR, NULL, "Ping limit (ms)", {.cvar = &cv_maxping}, 50}, - {IT_STRING | IT_CVAR, NULL, "Ping timeout (s)", {.cvar = &cv_pingtimeout}, 60}, + {IT_STRING | IT_CVAR, NULL, "Delay limit (frames)", {.cvar = &cv_maxping}, 50}, + {IT_STRING | IT_CVAR, NULL, "Delay timeout (s)", {.cvar = &cv_pingtimeout}, 60}, {IT_STRING | IT_CVAR, NULL, "Connection timeout (tics)", {.cvar = &cv_nettimeout}, 70}, {IT_STRING | IT_CVAR, NULL, "Join timeout (tics)", {.cvar = &cv_jointimeout}, 80}, @@ -2423,8 +2423,6 @@ static void M_ChangeCvar(INT32 choice) choice *= (TICRATE/7); else if (cv == &cv_maxsend) choice *= 512; - else if (cv == &cv_maxping) - choice *= 50; #endif CV_AddValue(cv,choice); } From f571e559d4ad7c268c16e5261c0043b5c7ce2519 Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Sun, 5 Jun 2022 10:25:41 -0400 Subject: [PATCH 19/48] Playsound command For scripted global sound cues for the entire server. --- src/d_netcmd.c | 68 ++++++++++++++++++++++++------------------------ src/d_netcmd.h | 1 + src/s_sound.c | 70 ++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 106 insertions(+), 33 deletions(-) diff --git a/src/d_netcmd.c b/src/d_netcmd.c index 4e7af8712..33dcbe27c 100644 --- a/src/d_netcmd.c +++ b/src/d_netcmd.c @@ -539,41 +539,43 @@ INT32 adminplayers[MAXPLAYERS]; /// \warning Keep this up-to-date if you add/remove/rename net text commands const char *netxcmdnames[MAXNETXCMD - 1] = { - "NAMEANDCOLOR", - "WEAPONPREF", - "KICK", - "NETVAR", - "SAY", - "MAP", - "EXITLEVEL", - "ADDFILE", - "PAUSE", - "ADDPLAYER", - "TEAMCHANGE", - "CLEARSCORES", - "VERIFIED", - "RANDOMSEED", - "RUNSOC", - "REQADDFILE", - "SETMOTD", - "RESPAWN", - "DEMOTED", - "LUACMD", - "LUAVAR", - "LUAFILE", + "NAMEANDCOLOR", // XD_NAMEANDCOLOR + "WEAPONPREF", // XD_WEAPONPREF + "KICK", // XD_KICK + "NETVAR", // XD_NETVAR + "SAY", // XD_SAY + "MAP", // XD_MAP + "EXITLEVEL", // XD_EXITLEVEL + "ADDFILE", // XD_ADDFILE + "PAUSE", // XD_PAUSE + "ADDPLAYER", // XD_ADDPLAYER + "TEAMCHANGE", // XD_TEAMCHANGE + "CLEARSCORES", // XD_CLEARSCORES + "VERIFIED", // XD_VERIFIED + "RANDOMSEED", // XD_RANDOMSEED + "RUNSOC", // XD_RUNSOC + "REQADDFILE", // XD_REQADDFILE + "SETMOTD", // XD_SETMOTD + "RESPAWN", // XD_RESPAWN + "DEMOTED", // XD_DEMOTED + "LUACMD", // XD_LUACMD + "LUAVAR", // XD_LUAVAR + "LUAFILE", // XD_LUAFILE // SRB2Kart - "SETUPVOTE", - "MODIFYVOTE", - "PICKVOTE", - "REMOVEPLAYER", - "POWERLEVEL", - "PARTYINVITE", - "ACCEPTPARTYINVITE", - "LEAVEPARTY", - "CANCELPARTYINVITE", - "GIVEITEM", - "ADDBOT" + "SETUPVOTE", // XD_SETUPVOTE + "MODIFYVOTE", // XD_MODIFYVOTE + "PICKVOTE", // XD_PICKVOTE + "REMOVEPLAYER", // XD_REMOVEPLAYER + "POWERLEVEL", // XD_POWERLEVEL + "PARTYINVITE", // XD_PARTYINVITE + "ACCEPTPARTYINVITE", // XD_ACCEPTPARTYINVITE + "LEAVEPARTY", // XD_LEAVEPARTY + "CANCELPARTYINVITE", // XD_CANCELPARTYINVITE + "GIVEITEM", // XD_GIVEITEM + "ADDBOT", // XD_ADDBOT + "DISCORD", // XD_DISCORD + "PLAYSOUND" // XD_PLAYSOUND }; // ========================================================================= diff --git a/src/d_netcmd.h b/src/d_netcmd.h index bc20ff50f..bf8109be5 100644 --- a/src/d_netcmd.h +++ b/src/d_netcmd.h @@ -158,6 +158,7 @@ typedef enum XD_GIVEITEM, // 32 XD_ADDBOT, // 33 XD_DISCORD, // 34 + XD_PLAYSOUND, // 35 MAXNETXCMD } netxcmd_t; diff --git a/src/s_sound.c b/src/s_sound.c index 9d3456f59..73607cdbc 100644 --- a/src/s_sound.c +++ b/src/s_sound.c @@ -31,6 +31,7 @@ #include "m_cond.h" // for conditionsets #include "lua_hook.h" // MusicChange hook #include "k_boss.h" // bossinfo +#include "byteptr.h" #ifdef HW3SOUND // 3D Sound Interface @@ -43,6 +44,8 @@ CV_PossibleValue_t soundvolume_cons_t[] = {{0, "MIN"}, {MAX_VOLUME, "MAX"}, {0, static void SetChannelsNum(void); static void Command_Tunes_f(void); static void Command_RestartAudio_f(void); +static void Command_PlaySound(void); +static void Got_PlaySound(UINT8 **p, INT32 playernum); // Sound system toggles static void GameSounds_OnChange(void); @@ -268,6 +271,8 @@ void S_RegisterSoundStuff(void) COM_AddCommand("tunes", Command_Tunes_f); COM_AddCommand("restartaudio", Command_RestartAudio_f); + COM_AddCommand("playsound", Command_PlaySound); + RegisterNetXCmd(XD_PLAYSOUND, Got_PlaySound); } static void SetChannelsNum(void) @@ -2469,6 +2474,71 @@ static void Command_RestartAudio_f(void) S_ChangeMusicInternal("titles", looptitle); } +static void Command_PlaySound(void) +{ + const char *sound; + const size_t argc = COM_Argc(); + sfxenum_t sfx = NUMSFX; + UINT8 buf[4]; + UINT8 *buf_p = buf; + + if (argc < 2) + { + CONS_Printf("playsound : Plays a sound effect for the entire server.\n"); + return; + } + + if (client && !IsPlayerAdmin(consoleplayer)) + { + CONS_Printf("This can only be used by the server host.\n"); + return; + } + + sound = COM_Argv(1); + if (*sound >= '0' && *sound <= '9') + { + sfx = atoi(sound); + } + else + { + for (sfx = 0; sfx < NUMSFX; sfx++) + { + if (S_sfx[sfx].name && fasticmp(sound, S_sfx[sfx].name)) + break; + } + } + + if (sfx < 0 || sfx >= NUMSFX) + { + CONS_Printf("Could not find sound effect named \"sfx_%s\".\n", sound); + return; + } + + WRITEINT32(buf_p, sfx); + SendNetXCmd(XD_PLAYSOUND, buf, buf_p - buf); +} + +static void Got_PlaySound(UINT8 **cp, INT32 playernum) +{ + INT32 sound_id = READINT32(*cp); + + if (playernum != serverplayer && !IsPlayerAdmin(playernum)) // hacked client, or disasterous bug + { + CONS_Alert(CONS_WARNING, M_GetText("Illegal playsound received from %s (serverplayer is %s)\n"), player_names[playernum], player_names[serverplayer]); + if (server) + SendKick(playernum, KICK_MSG_CON_FAIL); + return; + } + + if (sound_id < 0 || sound_id >= NUMSFX) + { + // bad sound effect, ignore + return; + } + + S_StartSound(NULL, sound_id); +} + void GameSounds_OnChange(void) { if (M_CheckParm("-nosound") || M_CheckParm("-noaudio")) From 118eb0b3dd305142220aa646b879b6922117f85c Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Mon, 6 Jun 2022 16:28:50 -0400 Subject: [PATCH 20/48] Remove MAXBANS Needs proper stress testing but seems to work. --- src/d_clisrv.c | 4 +-- src/i_tcp.c | 88 ++++++++++++++++++++++++++++++++++++-------------- 2 files changed, 65 insertions(+), 27 deletions(-) diff --git a/src/d_clisrv.c b/src/d_clisrv.c index f34c243e6..09de423a1 100644 --- a/src/d_clisrv.c +++ b/src/d_clisrv.c @@ -2524,7 +2524,7 @@ static void Command_Ban(void) { if (COM_Argc() < 2) { - CONS_Printf(M_GetText("Ban : ban and kick a player\n")); + CONS_Printf(M_GetText("ban : ban and kick a player\n")); return; } @@ -2548,7 +2548,7 @@ static void Command_Ban(void) if (server && I_Ban && !I_Ban(node)) // only the server is allowed to do this right now { - CONS_Alert(CONS_WARNING, M_GetText("Too many bans! Geez, that's a lot of people you're excluding...\n")); + CONS_Alert(CONS_WARNING, M_GetText("Ban failed. Invalid node?\n")); WRITEUINT8(p, KICK_MSG_GO_AWAY); SendNetXCmd(XD_KICK, &buf, 2); } diff --git a/src/i_tcp.c b/src/i_tcp.c index 1ca1b7f22..3a2e8f3dc 100644 --- a/src/i_tcp.c +++ b/src/i_tcp.c @@ -138,8 +138,6 @@ #endif // !NONET -#define MAXBANS 100 - #include "i_system.h" #include "i_net.h" #include "d_net.h" @@ -147,6 +145,7 @@ #include "i_tcp.h" #include "m_argv.h" #include "stun.h" +#include "z_zone.h" #include "doomstat.h" @@ -196,12 +195,14 @@ static mysockaddr_t broadcastaddress[MAXNETNODES+1]; static size_t broadcastaddresses = 0; static boolean nodeconnected[MAXNETNODES+1]; - static mysockaddr_t banned[MAXBANS]; - static UINT8 bannedmask[MAXBANS]; + static mysockaddr_t *banned; + static UINT8 *bannedmask; static const INT32 hole_punch_magic = MSBF_LONG (0x52eb11); #endif static size_t numbans = 0; +static size_t banned_size = 0; + static boolean SOCK_bannednode[MAXNETNODES+1]; /// \note do we really need the +1? static boolean init_tcp_driver = false; @@ -1423,6 +1424,39 @@ static boolean SOCK_OpenSocket(void) #endif } +static void AddBannedIndex(void) +{ + if (numbans >= banned_size) + { + if (banned_size == 0) + { + banned_size = 128; + } + else + { + banned_size *= 2; + } + + banned = Z_ReallocAlign( + (void*) banned, + sizeof(mysockaddr_t) * banned_size, + PU_STATIC, + NULL, + sizeof(mysockaddr_t) * 8 + ); + + bannedmask = Z_ReallocAlign( + (void*) banned, + sizeof(UINT8) * banned_size, + PU_STATIC, + NULL, + sizeof(UINT8) * 8 + ); + } + + numbans++; +} + static boolean SOCK_Ban(INT32 node) { if (node > MAXNETNODES) @@ -1430,23 +1464,23 @@ static boolean SOCK_Ban(INT32 node) #ifdef NONET return false; #else - if (numbans == MAXBANS) - return false; - M_Memcpy(&banned[numbans], &clientaddress[node], sizeof (mysockaddr_t)); - if (banned[numbans].any.sa_family == AF_INET) + AddBannedIndex(); + + M_Memcpy(&banned[numbans-1], &clientaddress[node], sizeof (mysockaddr_t)); + if (banned[numbans-1].any.sa_family == AF_INET) { - banned[numbans].ip4.sin_port = 0; - bannedmask[numbans] = 32; + banned[numbans-1].ip4.sin_port = 0; + bannedmask[numbans-1] = 32; } #ifdef HAVE_IPV6 - else if (banned[numbans].any.sa_family == AF_INET6) + else if (banned[numbans-1].any.sa_family == AF_INET6) { - banned[numbans].ip6.sin6_port = 0; - bannedmask[numbans] = 128; + banned[numbans-1].ip6.sin6_port = 0; + bannedmask[numbans-1] = 128; } #endif - numbans++; + return true; #endif } @@ -1461,7 +1495,7 @@ static boolean SOCK_SetBanAddress(const char *address, const char *mask) struct my_addrinfo *ai, *runp, hints; int gaie; - if (numbans == MAXBANS || !address) + if (!address) return false; memset(&hints, 0x00, sizeof(hints)); @@ -1476,26 +1510,27 @@ static boolean SOCK_SetBanAddress(const char *address, const char *mask) runp = ai; - while(runp != NULL && numbans != MAXBANS) + while (runp != NULL) { - memcpy(&banned[numbans], runp->ai_addr, runp->ai_addrlen); + AddBannedIndex(); + + memcpy(&banned[numbans-1], runp->ai_addr, runp->ai_addrlen); if (mask) - bannedmask[numbans] = (UINT8)atoi(mask); + bannedmask[numbans-1] = (UINT8)atoi(mask); #ifdef HAVE_IPV6 else if (runp->ai_family == AF_INET6) - bannedmask[numbans] = 128; + bannedmask[numbans-1] = 128; #endif else - bannedmask[numbans] = 32; + bannedmask[numbans-1] = 32; - if (bannedmask[numbans] > 32 && runp->ai_family == AF_INET) - bannedmask[numbans] = 32; + if (bannedmask[numbans-1] > 32 && runp->ai_family == AF_INET) + bannedmask[numbans-1] = 32; #ifdef HAVE_IPV6 - else if (bannedmask[numbans] > 128 && runp->ai_family == AF_INET6) - bannedmask[numbans] = 128; + else if (bannedmask[numbans-1] > 128 && runp->ai_family == AF_INET6) + bannedmask[numbans-1] = 128; #endif - numbans++; runp = runp->ai_next; } @@ -1508,6 +1543,9 @@ static boolean SOCK_SetBanAddress(const char *address, const char *mask) static void SOCK_ClearBans(void) { numbans = 0; + banned_size = 0; + banned = NULL; + bannedmask = NULL; } boolean I_InitTcpNetwork(void) From 35b82b6dd99ffde8f9d4a3b1d48df2f55c9511a1 Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Mon, 6 Jun 2022 17:05:44 -0400 Subject: [PATCH 21/48] Random choice commands --- src/console.c | 78 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/console.c b/src/console.c index c0e5d1877..cfca61b38 100644 --- a/src/console.c +++ b/src/console.c @@ -34,6 +34,7 @@ #include "m_menu.h" #include "filesrch.h" #include "m_misc.h" +#include "m_random.h" #ifdef _WINDOWS #include "win32/win_main.h" @@ -265,6 +266,81 @@ static void CONS_Bind_f(void) bindtable[key] = Z_StrDup(COM_Argv(2)); } +static void CONS_Choose_f(void) +{ + size_t na = COM_Argc(); + + if (na < 2) + { + CONS_Printf(M_GetText("choose [] [] [...]: Picks a command at random\n")); + return; + } + + COM_BufAddText(COM_Argv(M_RandomKey(na - 1) + 1)); + COM_BufAddText("\n"); +} + +static void CONS_ChooseWeighted_f(void) +{ + size_t na = COM_Argc(); + size_t i, cmd; + const char *commands[40]; + INT32 weights[40]; + INT32 totalWeight = 0; + INT32 roll; + + if (na < 3) + { + CONS_Printf(M_GetText("chooseweighted [ ] [ ] [...]: Picks a command with weighted randomization\n")); + return; + } + + memset(weights, 0, sizeof(weights)); + + i = 1; + cmd = 0; + while (i < na) + { + commands[cmd] = COM_Argv(i); + + i++; + if (i >= na) + { + break; + } + + weights[cmd] = atoi(COM_Argv(i)); + totalWeight += weights[cmd]; + + i++; + cmd++; + } + + if (cmd == 0 || totalWeight <= 0) + { + return; + } + + roll = M_RandomRange(1, totalWeight); + + for (i = 0; i < cmd; i++) + { + if (roll <= weights[i]) + { + if (commands[i] == NULL) + { + break; + } + + COM_BufAddText(commands[i]); + COM_BufAddText("\n"); + break; + } + + roll -= weights[i]; + } +} + //====================================================================== // CONSOLE SETUP //====================================================================== @@ -468,6 +544,8 @@ void CON_Init(void) CV_RegisterVar(&cons_backcolor); CV_RegisterVar(&cons_menuhighlight); COM_AddCommand("bind", CONS_Bind_f); + COM_AddCommand("choose", CONS_Choose_f); + COM_AddCommand("chooseweighted", CONS_ChooseWeighted_f); } else { From e9ae1e39447beba9cbdc90c50b08af5cbd0347eb Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Mon, 6 Jun 2022 17:14:07 -0400 Subject: [PATCH 22/48] Start banned_size much smaller --- src/i_tcp.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/i_tcp.c b/src/i_tcp.c index 3a2e8f3dc..e8066dd58 100644 --- a/src/i_tcp.c +++ b/src/i_tcp.c @@ -1430,7 +1430,7 @@ static void AddBannedIndex(void) { if (banned_size == 0) { - banned_size = 128; + banned_size = 8; } else { From 2c604e2487035a429506d7073b6df203ee0aebf2 Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Mon, 6 Jun 2022 17:54:27 -0400 Subject: [PATCH 23/48] Combine banned & bannedmask into banned_t Better code cleanliness, also makes it easier to add more data to bans later (such as a timestamp for temporary bans) --- src/i_tcp.c | 70 +++++++++++++++++++++++++++++------------------------ 1 file changed, 39 insertions(+), 31 deletions(-) diff --git a/src/i_tcp.c b/src/i_tcp.c index e8066dd58..b37584503 100644 --- a/src/i_tcp.c +++ b/src/i_tcp.c @@ -187,6 +187,14 @@ #if (defined (WATTCP) && !defined (__libsocket_socklen_t)) || defined (USE_WINSOCK1) typedef int socklen_t; #endif + + typedef struct + { + mysockaddr_t address; + UINT8 mask; + // TODO: timestamp, for tempbans! + } banned_t; + static SOCKET_TYPE mysockets[MAXNETNODES+1] = {ERRSOCKET}; static size_t mysocketses = 0; static int myfamily[MAXNETNODES+1] = {0}; @@ -195,8 +203,7 @@ static mysockaddr_t broadcastaddress[MAXNETNODES+1]; static size_t broadcastaddresses = 0; static boolean nodeconnected[MAXNETNODES+1]; - static mysockaddr_t *banned; - static UINT8 *bannedmask; + static banned_t *banned; static const INT32 hole_punch_magic = MSBF_LONG (0x52eb11); #endif @@ -431,7 +438,7 @@ static const char *SOCK_GetBanAddress(size_t ban) #ifdef NONET return NULL; #else - return SOCK_AddrToStr(&banned[ban]); + return SOCK_AddrToStr(&banned[ban].address); #endif } @@ -443,7 +450,7 @@ static const char *SOCK_GetBanMask(size_t ban) static char s[16]; //255.255.255.255 netmask? no, just CDIR for only if (ban >= numbans) return NULL; - if (sprintf(s,"%d",bannedmask[ban]) > 0) + if (sprintf(s,"%d",banned[ban].mask) > 0) return s; #endif return NULL; @@ -632,7 +639,7 @@ static boolean SOCK_Get(void) // check if it's a banned dude so we can send a refusal later for (i = 0; i < numbans; i++) { - if (SOCK_cmpaddr(&fromaddress, &banned[i], bannedmask[i])) + if (SOCK_cmpaddr(&fromaddress, &banned[i].address, banned[i].mask)) { SOCK_bannednode[j] = true; DEBFILE("This dude has been banned\n"); @@ -1439,18 +1446,10 @@ static void AddBannedIndex(void) banned = Z_ReallocAlign( (void*) banned, - sizeof(mysockaddr_t) * banned_size, + sizeof(banned_t) * banned_size, PU_STATIC, NULL, - sizeof(mysockaddr_t) * 8 - ); - - bannedmask = Z_ReallocAlign( - (void*) banned, - sizeof(UINT8) * banned_size, - PU_STATIC, - NULL, - sizeof(UINT8) * 8 + sizeof(banned_t) * 8 ); } @@ -1459,25 +1458,31 @@ static void AddBannedIndex(void) static boolean SOCK_Ban(INT32 node) { + INT32 ban; + if (node > MAXNETNODES) return false; + #ifdef NONET + (void)ban; return false; #else + ban = numbans; AddBannedIndex(); - M_Memcpy(&banned[numbans-1], &clientaddress[node], sizeof (mysockaddr_t)); - if (banned[numbans-1].any.sa_family == AF_INET) + M_Memcpy(&banned[ban].address, &clientaddress[node], sizeof (mysockaddr_t)); + + if (banned[ban].address.any.sa_family == AF_INET) { - banned[numbans-1].ip4.sin_port = 0; - bannedmask[numbans-1] = 32; + banned[ban].address.ip4.sin_port = 0; + banned[ban].mask = 32; } #ifdef HAVE_IPV6 - else if (banned[numbans-1].any.sa_family == AF_INET6) + else if (banned[ban].address.any.sa_family == AF_INET6) { - banned[numbans-1].ip6.sin6_port = 0; - bannedmask[numbans-1] = 128; + banned[ban].address.ip6.sin6_port = 0; + banned[ban].mask = 128; } #endif @@ -1512,25 +1517,29 @@ static boolean SOCK_SetBanAddress(const char *address, const char *mask) while (runp != NULL) { + INT32 ban; + + ban = numbans; AddBannedIndex(); - memcpy(&banned[numbans-1], runp->ai_addr, runp->ai_addrlen); + memcpy(&banned[ban].address, runp->ai_addr, runp->ai_addrlen); if (mask) - bannedmask[numbans-1] = (UINT8)atoi(mask); + banned[ban].mask = (UINT8)atoi(mask); #ifdef HAVE_IPV6 else if (runp->ai_family == AF_INET6) - bannedmask[numbans-1] = 128; + banned[ban].mask = 128; #endif else - bannedmask[numbans-1] = 32; + banned[ban].mask = 32; - if (bannedmask[numbans-1] > 32 && runp->ai_family == AF_INET) - bannedmask[numbans-1] = 32; + if (banned[ban].mask > 32 && runp->ai_family == AF_INET) + banned[ban].mask = 32; #ifdef HAVE_IPV6 - else if (bannedmask[numbans-1] > 128 && runp->ai_family == AF_INET6) - bannedmask[numbans-1] = 128; + else if (banned[ban].mask > 128 && runp->ai_family == AF_INET6) + banned[ban].mask = 128; #endif + runp = runp->ai_next; } @@ -1545,7 +1554,6 @@ static void SOCK_ClearBans(void) numbans = 0; banned_size = 0; banned = NULL; - bannedmask = NULL; } boolean I_InitTcpNetwork(void) From 5ea0f69a8386048cdb741ccae5c8cc8d99a9df59 Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Tue, 7 Jun 2022 11:44:03 -0400 Subject: [PATCH 24/48] - Attach ban reasons to banned_t - Properly call D_SaveBan after remote bans. Bans are no longer saved in the ban command and instead wait for the actual kick to process, since before they were split between the two, which is what caused the discrepancy. --- src/d_clisrv.c | 152 ++++++++++++++----------------------------------- src/d_net.c | 2 + src/i_net.h | 2 + src/i_tcp.c | 30 ++++++++++ 4 files changed, 76 insertions(+), 110 deletions(-) diff --git a/src/d_clisrv.c b/src/d_clisrv.c index 09de423a1..f0cb7697e 100644 --- a/src/d_clisrv.c +++ b/src/d_clisrv.c @@ -2050,40 +2050,27 @@ static void CL_ConnectToServer(void) } #ifndef NONET -typedef struct banreason_s -{ - char *reason; - struct banreason_s *prev; //-1 - struct banreason_s *next; //+1 -} banreason_t; - -static banreason_t *reasontail = NULL; //last entry, use prev -static banreason_t *reasonhead = NULL; //1st entry, use next - static void Command_ShowBan(void) //Print out ban list { size_t i; - const char *address, *mask; - banreason_t *reasonlist = reasonhead; + const char *address, *mask, *reason; if (I_GetBanAddress) CONS_Printf(M_GetText("Ban List:\n")); else return; - for (i = 0;(address = I_GetBanAddress(i)) != NULL;i++) + for (i = 0; (address = I_GetBanAddress(i)) != NULL; i++) { if (!I_GetBanMask || (mask = I_GetBanMask(i)) == NULL) CONS_Printf("%s: %s ", sizeu1(i+1), address); else CONS_Printf("%s: %s/%s ", sizeu1(i+1), address, mask); - if (reasonlist && reasonlist->reason) - CONS_Printf("(%s)\n", reasonlist->reason); + if (I_GetBanReason && (reason = I_GetBanReason(i)) != NULL) + CONS_Printf("(%s)\n", reason); else CONS_Printf("\n"); - - if (reasonlist) reasonlist = reasonlist->next; } if (i == 0 && !address) @@ -2094,16 +2081,9 @@ void D_SaveBan(void) { FILE *f; size_t i; - banreason_t *reasonlist = reasonhead; - const char *address, *mask; + const char *address, *mask, *reason; const char *path = va("%s"PATHSEP"%s", srb2home, "ban.txt"); - if (!reasonhead) - { - remove(path); - return; - } - f = fopen(path, "w"); if (!f) @@ -2112,65 +2092,28 @@ void D_SaveBan(void) return; } - for (i = 0;(address = I_GetBanAddress(i)) != NULL;i++) + for (i = 0; (address = I_GetBanAddress(i)) != NULL; i++) { if (!I_GetBanMask || (mask = I_GetBanMask(i)) == NULL) fprintf(f, "%s 0", address); else fprintf(f, "%s %s", address, mask); - if (reasonlist && reasonlist->reason) - fprintf(f, " %s\n", reasonlist->reason); + if (I_GetBanReason && (reason = I_GetBanReason(i)) != NULL) + fprintf(f, " %s\n", reason); else - fprintf(f, " %s\n", "NA"); - - if (reasonlist) reasonlist = reasonlist->next; + fprintf(f, " %s\n", "No reason given"); } fclose(f); } -static void Ban_Add(const char *reason) -{ - banreason_t *reasonlist = malloc(sizeof(*reasonlist)); - - if (!reasonlist) - return; - if (!reason) - reason = "NA"; - - reasonlist->next = NULL; - reasonlist->reason = Z_StrDup(reason); - if ((reasonlist->prev = reasontail) == NULL) - reasonhead = reasonlist; - else - reasontail->next = reasonlist; - reasontail = reasonlist; -} - -static void Ban_Clear(void) -{ - banreason_t *temp; - - I_ClearBans(); - - reasontail = NULL; - - while (reasonhead) - { - temp = reasonhead->next; - Z_Free(reasonhead->reason); - free(reasonhead); - reasonhead = temp; - } -} - static void Command_ClearBans(void) { if (!I_ClearBans) return; - Ban_Clear(); + I_ClearBans(); D_SaveBan(); } @@ -2178,7 +2121,7 @@ static void Ban_Load_File(boolean warning) { FILE *f; size_t i; - const char *address, *mask; + const char *address, *mask, *reason; char buffer[MAX_WADPATH]; if (!I_ClearBans) @@ -2193,16 +2136,17 @@ static void Ban_Load_File(boolean warning) return; } - Ban_Clear(); + I_ClearBans(); for (i=0; fgets(buffer, (int)sizeof(buffer), f); i++) { address = strtok(buffer, " \t\r\n"); mask = strtok(NULL, " \t\r\n"); + reason = strtok(NULL, "\r\n"); I_SetBanAddress(address, mask); - - Ban_Add(strtok(NULL, "\r\n")); + if (I_SetBanReason) + I_SetBanReason(reason); } fclose(f); @@ -2539,54 +2483,37 @@ static void Command_Ban(void) UINT8 buf[3 + MAX_REASONLENGTH]; UINT8 *p = buf; const SINT8 pn = nametonum(COM_Argv(1)); - const INT32 node = playernode[(INT32)pn]; if (pn == -1 || pn == 0) return; WRITEUINT8(p, pn); - if (server && I_Ban && !I_Ban(node)) // only the server is allowed to do this right now + if (COM_Argc() == 2) { - CONS_Alert(CONS_WARNING, M_GetText("Ban failed. Invalid node?\n")); - WRITEUINT8(p, KICK_MSG_GO_AWAY); + WRITEUINT8(p, KICK_MSG_BANNED); SendNetXCmd(XD_KICK, &buf, 2); } else { - if (server) // only the server is allowed to do this right now + size_t i, j = COM_Argc(); + char message[MAX_REASONLENGTH]; + + //Steal from the motd code so you don't have to put the reason in quotes. + strlcpy(message, COM_Argv(2), sizeof message); + for (i = 3; i < j; i++) { - Ban_Add(COM_Argv(2)); - D_SaveBan(); // save the ban list + strlcat(message, " ", sizeof message); + strlcat(message, COM_Argv(i), sizeof message); } - if (COM_Argc() == 2) - { - WRITEUINT8(p, KICK_MSG_BANNED); - SendNetXCmd(XD_KICK, &buf, 2); - } - else - { - size_t i, j = COM_Argc(); - char message[MAX_REASONLENGTH]; - - //Steal from the motd code so you don't have to put the reason in quotes. - strlcpy(message, COM_Argv(2), sizeof message); - for (i = 3; i < j; i++) - { - strlcat(message, " ", sizeof message); - strlcat(message, COM_Argv(i), sizeof message); - } - - WRITEUINT8(p, KICK_MSG_CUSTOM_BAN); - WRITESTRINGN(p, message, MAX_REASONLENGTH); - SendNetXCmd(XD_KICK, &buf, p - buf); - } + WRITEUINT8(p, KICK_MSG_CUSTOM_BAN); + WRITESTRINGN(p, message, MAX_REASONLENGTH); + SendNetXCmd(XD_KICK, &buf, p - buf); } } else CONS_Printf(M_GetText("Only the server or a remote admin can use this.\n")); - } static void Command_BanIP(void) @@ -2607,7 +2534,6 @@ static void Command_BanIP(void) else reason = COM_Argv(2); - if (I_SetBanAddress && I_SetBanAddress(address, NULL)) { if (reason) @@ -2615,7 +2541,8 @@ static void Command_BanIP(void) else CONS_Printf("Banned IP address %s\n", address); - Ban_Add(reason); + if (I_SetBanReason) + I_SetBanReason(reason); D_SaveBan(); } else @@ -2753,16 +2680,21 @@ static void Got_KickCmd(UINT8 **p, INT32 playernum) //CONS_Printf("\x82%s ", player_names[pnum]); - // If a verified admin banned someone, the server needs to know about it. - // If the playernum isn't zero (the server) then the server needs to record the ban. - if (server && playernum && (msg == KICK_MSG_BANNED || msg == KICK_MSG_CUSTOM_BAN)) + // Save bans here. Used to be split between here and the actual command, depending on + // whenever the server did it or a remote admin did it, but it's simply more convenient + // to keep it all in one place. + if (server && (msg == KICK_MSG_BANNED || msg == KICK_MSG_CUSTOM_BAN)) { if (I_Ban && !I_Ban(playernode[(INT32)pnum])) - CONS_Alert(CONS_WARNING, M_GetText("Too many bans! Geez, that's a lot of people you're excluding...\n")); -#ifndef NONET + { + CONS_Alert(CONS_WARNING, M_GetText("Ban failed. Invalid node?\n")); + } else - Ban_Add(reason); -#endif + { + if (I_SetBanReason) + I_SetBanReason(reason); + D_SaveBan(); + } } if (msg == KICK_MSG_PLAYER_QUIT) diff --git a/src/d_net.c b/src/d_net.c index 558310e70..bdfd06767 100644 --- a/src/d_net.c +++ b/src/d_net.c @@ -83,7 +83,9 @@ void (*I_ClearBans)(void) = NULL; const char *(*I_GetNodeAddress) (INT32 node) = NULL; const char *(*I_GetBanAddress) (size_t ban) = NULL; const char *(*I_GetBanMask) (size_t ban) = NULL; +const char *(*I_GetBanReason) (size_t ban) = NULL; boolean (*I_SetBanAddress) (const char *address, const char *mask) = NULL; +boolean (*I_SetBanReason) (const char *reason) = NULL; boolean *bannednode = NULL; diff --git a/src/i_net.h b/src/i_net.h index 8caa0edcc..f3d55a61a 100644 --- a/src/i_net.h +++ b/src/i_net.h @@ -162,7 +162,9 @@ extern void (*I_ClearBans)(void); extern const char *(*I_GetNodeAddress) (INT32 node); extern const char *(*I_GetBanAddress) (size_t ban); extern const char *(*I_GetBanMask) (size_t ban); +extern const char *(*I_GetBanReason) (size_t ban); extern boolean (*I_SetBanAddress) (const char *address,const char *mask); +extern boolean (*I_SetBanReason) (const char *reason); extern boolean *bannednode; /// \brief Called by D_SRB2Main to be defined by extern network driver diff --git a/src/i_tcp.c b/src/i_tcp.c index b37584503..382df176e 100644 --- a/src/i_tcp.c +++ b/src/i_tcp.c @@ -192,6 +192,7 @@ { mysockaddr_t address; UINT8 mask; + char *reason; // TODO: timestamp, for tempbans! } banned_t; @@ -456,6 +457,16 @@ static const char *SOCK_GetBanMask(size_t ban) return NULL; } +static const char *SOCK_GetBanReason(size_t ban) +{ +#ifdef NONET + (void)ban; + return NULL; +#else + return banned[ban].reason; +#endif +} + #ifndef NONET static boolean SOCK_cmpaddr(mysockaddr_t *a, mysockaddr_t *b, UINT8 mask) { @@ -1549,6 +1560,23 @@ static boolean SOCK_SetBanAddress(const char *address, const char *mask) #endif } +static boolean SOCK_SetBanReason(const char *reason) +{ +#ifdef NONET + (void)reason; + return false; +#else + + if (!reason) + { + reason = "No reason given"; + } + + banned[numbans - 1].reason = Z_StrDup(reason); + return true; +#endif +} + static void SOCK_ClearBans(void) { numbans = 0; @@ -1648,7 +1676,9 @@ boolean I_InitTcpNetwork(void) I_GetNodeAddress = SOCK_GetNodeAddress; I_GetBanAddress = SOCK_GetBanAddress; I_GetBanMask = SOCK_GetBanMask; + I_GetBanReason = SOCK_GetBanReason; I_SetBanAddress = SOCK_SetBanAddress; + I_SetBanReason = SOCK_SetBanReason; bannednode = SOCK_bannednode; return ret; From 3096806f04b5e753912b640488038951814ad1f6 Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Tue, 7 Jun 2022 14:08:02 -0400 Subject: [PATCH 25/48] Kicks are now temp bans Length is determined by the "kicktime" cvar, in minutes. By default, this is set to 10, but I'm willing to adjust this. Only applies to manual kicks (in the future, maybe also name filter kicks). The timestamp for the unban time is even saved in ban.txt, so long-term temporary bans are completely possible. (I checked, you can attempt to ban someone for up to 1902 years if you really want to.) --- src/d_clisrv.c | 118 ++++++++++++++++++++++++++++++++++++++++++++----- src/d_clisrv.h | 1 + src/d_net.c | 3 ++ src/i_net.h | 5 +++ src/i_tcp.c | 64 ++++++++++++++++++++++++--- 5 files changed, 174 insertions(+), 17 deletions(-) diff --git a/src/d_clisrv.c b/src/d_clisrv.c index f0cb7697e..3ba0b31aa 100644 --- a/src/d_clisrv.c +++ b/src/d_clisrv.c @@ -189,6 +189,8 @@ consvar_t cv_playbackspeed = CVAR_INIT ("playbackspeed", "1", 0, playbackspeed_c 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 inline void *G_DcpyTiccmd(void* dest, const ticcmd_t* src, const size_t n) { const size_t d = n / sizeof(ticcmd_t); @@ -2067,6 +2069,13 @@ static void Command_ShowBan(void) //Print out ban list else CONS_Printf("%s: %s/%s ", sizeu1(i+1), address, mask); + if (I_GetUnbanTime && I_GetUnbanTime(i) != NO_BAN_TIME) + { + // todo: maybe try to actually print out the time remaining, + // and/or the datetime of the unbanning? + CONS_Printf("(temporary) "); + } + if (I_GetBanReason && (reason = I_GetBanReason(i)) != NULL) CONS_Printf("(%s)\n", reason); else @@ -2082,6 +2091,8 @@ void D_SaveBan(void) FILE *f; size_t i; const char *address, *mask, *reason; + const time_t curTime = time(NULL); + time_t unbanTime = NO_BAN_TIME; const char *path = va("%s"PATHSEP"%s", srb2home, "ban.txt"); f = fopen(path, "w"); @@ -2094,11 +2105,24 @@ void D_SaveBan(void) for (i = 0; (address = I_GetBanAddress(i)) != NULL; i++) { + if (I_GetUnbanTime) + { + unbanTime = I_GetUnbanTime(i); + } + + if (unbanTime != NO_BAN_TIME && curTime >= unbanTime) + { + // Don't need to save this one anymore. + continue; + } + if (!I_GetBanMask || (mask = I_GetBanMask(i)) == NULL) fprintf(f, "%s 0", address); else fprintf(f, "%s %s", address, mask); + fprintf(f, " %ld", (long)unbanTime); + if (I_GetBanReason && (reason = I_GetBanReason(i)) != NULL) fprintf(f, " %s\n", reason); else @@ -2122,6 +2146,7 @@ static void Ban_Load_File(boolean warning) FILE *f; size_t i; const char *address, *mask, *reason; + time_t unbanTime = NO_BAN_TIME; char buffer[MAX_WADPATH]; if (!I_ClearBans) @@ -2142,9 +2167,12 @@ static void Ban_Load_File(boolean warning) { address = strtok(buffer, " \t\r\n"); mask = strtok(NULL, " \t\r\n"); + unbanTime = atoi(strtok(NULL, " \t\r\n")); reason = strtok(NULL, "\r\n"); I_SetBanAddress(address, mask); + if (I_SetUnbanTime) + I_SetUnbanTime(unbanTime); if (I_SetBanReason) I_SetBanReason(reason); } @@ -2620,6 +2648,7 @@ static void Got_KickCmd(UINT8 **p, INT32 playernum) char buf[3 + MAX_REASONLENGTH]; char *reason = buf; kickreason_t kickreason = KR_KICK; + UINT32 banMinutes = 0; pnum = READUINT8(*p); msg = READUINT8(*p); @@ -2683,17 +2712,35 @@ static void Got_KickCmd(UINT8 **p, INT32 playernum) // Save bans here. Used to be split between here and the actual command, depending on // whenever the server did it or a remote admin did it, but it's simply more convenient // to keep it all in one place. - if (server && (msg == KICK_MSG_BANNED || msg == KICK_MSG_CUSTOM_BAN)) + if (server) { - if (I_Ban && !I_Ban(playernode[(INT32)pnum])) + if (msg == KICK_MSG_GO_AWAY || msg == KICK_MSG_CUSTOM_KICK) { - CONS_Alert(CONS_WARNING, M_GetText("Ban failed. Invalid node?\n")); + // Kick as a temporary ban. + banMinutes = cv_kicktime.value; } - else + + if (msg == KICK_MSG_BANNED || msg == KICK_MSG_CUSTOM_BAN || banMinutes) { - if (I_SetBanReason) - I_SetBanReason(reason); - D_SaveBan(); + if (I_Ban && !I_Ban(playernode[(INT32)pnum])) + { + CONS_Alert(CONS_WARNING, M_GetText("Ban failed. Invalid node?\n")); + } + else + { + if (I_SetBanReason) + I_SetBanReason(reason); + + if (I_SetUnbanTime) + { + if (banMinutes) + I_SetUnbanTime(time(NULL) + (banMinutes * 60)); + else + I_SetUnbanTime(NO_BAN_TIME); + } + + D_SaveBan(); + } } } @@ -2791,13 +2838,16 @@ static void Got_KickCmd(UINT8 **p, INT32 playernum) if (playernode[pnum] == playernode[consoleplayer]) { - LUAh_GameQuit(false); #ifdef DUMPCONSISTENCY if (msg == KICK_MSG_CON_FAIL) SV_SavedGame(); #endif + + LUAh_GameQuit(false); + D_QuitNetGame(); CL_Reset(); D_StartTitle(); + if (msg == KICK_MSG_CON_FAIL) M_StartMessage(M_GetText("Server closed connection\n(Synch failure)\nPress ESC\n"), NULL, MM_NOTHING); else if (msg == KICK_MSG_PING_HIGH) @@ -3006,6 +3056,7 @@ void D_ClientServerInit(void) #ifndef NONET COM_AddCommand("getplayernum", Command_GetPlayerNum); COM_AddCommand("kick", Command_Kick); + CV_RegisterVar(&cv_kicktime); COM_AddCommand("ban", Command_Ban); COM_AddCommand("banip", Command_BanIP); COM_AddCommand("clearbans", Command_ClearBans); @@ -3641,31 +3692,74 @@ static void HandleConnect(SINT8 node) UINT8 maxplayers = min((dedicated ? MAXPLAYERS-1 : MAXPLAYERS), cv_maxplayers.value); if (bannednode && bannednode[node]) - SV_SendRefuse(node, M_GetText("You have been banned\nfrom the server.")); + { + if (bannednodetimeleft && bannednodetimeleft[node] != NO_BAN_TIME) + { + int minutes = bannednodetimeleft[node] / 60; + int hours = minutes / 60; + + if (hours) + { + SV_SendRefuse(node, va(M_GetText("You have been temporarily\nkicked from the server.\n(Time remaining: %d hour%s)"), hours, hours > 1 ? "s" : "")); + } + else if (minutes) + { + SV_SendRefuse(node, va(M_GetText("You have been temporarily\nkicked from the server.\n(Time remaining: %d minute%s)"), minutes, minutes > 1 ? "s" : "")); + } + else + { + SV_SendRefuse(node, M_GetText("You have been temporarily\nkicked from the server.\n(Time remaining: <1 minute)")); + } + } + else + { + SV_SendRefuse(node, M_GetText("You have been banned\nfrom the server.")); + } + } else if (netbuffer->u.clientcfg._255 != 255 || netbuffer->u.clientcfg.packetversion != PACKETVERSION) + { SV_SendRefuse(node, "Incompatible packet formats."); + } else if (strncmp(netbuffer->u.clientcfg.application, SRB2APPLICATION, sizeof netbuffer->u.clientcfg.application)) + { SV_SendRefuse(node, "Different Ring Racers modifications\nare not compatible."); + } else if (netbuffer->u.clientcfg.version != VERSION || netbuffer->u.clientcfg.subversion != SUBVERSION) + { SV_SendRefuse(node, va(M_GetText("Different Ring Racers versions cannot\nplay a netgame!\n(server version %d.%d)"), VERSION, SUBVERSION)); + } else if (!cv_allownewplayer.value && node) + { SV_SendRefuse(node, M_GetText("The server is not accepting\njoins for the moment.")); + } else if (D_NumPlayers() >= maxplayers) + { SV_SendRefuse(node, va(M_GetText("Maximum players reached: %d"), maxplayers)); + } else if (netgame && netbuffer->u.clientcfg.localplayers > MAXSPLITSCREENPLAYERS) // Hacked client? + { SV_SendRefuse(node, M_GetText("Too many players from\nthis node.")); + } else if (netgame && D_NumPlayers() + netbuffer->u.clientcfg.localplayers > maxplayers) + { SV_SendRefuse(node, va(M_GetText("Number of local players\nwould exceed maximum: %d"), maxplayers)); + } else if (netgame && !netbuffer->u.clientcfg.localplayers) // Stealth join? + { SV_SendRefuse(node, M_GetText("No players from\nthis node.")); + } else if (luafiletransfers) + { SV_SendRefuse(node, M_GetText("The server is broadcasting a file\nrequested by a Lua script.\nPlease wait a bit and then\ntry rejoining.")); + } else if (netgame && joindelay > 2 * (tic_t)cv_joindelay.value * TICRATE) + { SV_SendRefuse(node, va(M_GetText("Too many people are connecting.\nPlease wait %d seconds and then\ntry rejoining."), (joindelay - 2 * cv_joindelay.value * TICRATE) / TICRATE)); + } else { #ifndef NONET @@ -3939,13 +4033,13 @@ static void HandlePacketFromAwayNode(SINT8 node) break; } - M_StartMessage(va(M_GetText("Server refuses connection\n\nReason:\n%s"), - reason), NULL, MM_NOTHING); - D_QuitNetGame(); CL_Reset(); D_StartTitle(); + M_StartMessage(va(M_GetText("Server refuses connection\n\nReason:\n%s"), + reason), NULL, MM_NOTHING); + free(reason); // Will be reset by caller. Signals refusal. diff --git a/src/d_clisrv.h b/src/d_clisrv.h index 9fa7ac37d..48e1b0d0d 100644 --- a/src/d_clisrv.h +++ b/src/d_clisrv.h @@ -394,6 +394,7 @@ extern INT32 mapchangepending; extern doomdata_t *netbuffer; extern consvar_t cv_stunserver; extern consvar_t cv_httpsource; +extern consvar_t cv_kicktime; extern consvar_t cv_showjoinaddress; extern consvar_t cv_playbackspeed; diff --git a/src/d_net.c b/src/d_net.c index bdfd06767..cf465a1f0 100644 --- a/src/d_net.c +++ b/src/d_net.c @@ -84,9 +84,12 @@ const char *(*I_GetNodeAddress) (INT32 node) = NULL; const char *(*I_GetBanAddress) (size_t ban) = NULL; const char *(*I_GetBanMask) (size_t ban) = NULL; const char *(*I_GetBanReason) (size_t ban) = NULL; +time_t (*I_GetUnbanTime) (size_t ban) = NULL; boolean (*I_SetBanAddress) (const char *address, const char *mask) = NULL; boolean (*I_SetBanReason) (const char *reason) = NULL; +boolean (*I_SetUnbanTime) (time_t timestamp) = NULL; boolean *bannednode = NULL; +time_t *bannednodetimeleft = NULL; // network stats diff --git a/src/i_net.h b/src/i_net.h index f3d55a61a..5b40001eb 100644 --- a/src/i_net.h +++ b/src/i_net.h @@ -31,6 +31,8 @@ /// For use on the internet #define INETPACKETLENGTH 1024 +#define NO_BAN_TIME (time_t)(-1) + extern INT16 hardware_MAXPACKETLENGTH; extern INT32 net_bandwidth; // in byte/s @@ -163,9 +165,12 @@ extern const char *(*I_GetNodeAddress) (INT32 node); extern const char *(*I_GetBanAddress) (size_t ban); extern const char *(*I_GetBanMask) (size_t ban); extern const char *(*I_GetBanReason) (size_t ban); +extern time_t (*I_GetUnbanTime) (size_t ban); extern boolean (*I_SetBanAddress) (const char *address,const char *mask); extern boolean (*I_SetBanReason) (const char *reason); +extern boolean (*I_SetUnbanTime) (time_t timestamp); extern boolean *bannednode; +extern time_t *bannednodetimeleft; /// \brief Called by D_SRB2Main to be defined by extern network driver boolean I_InitNetwork(void); diff --git a/src/i_tcp.c b/src/i_tcp.c index 382df176e..f5d5c610e 100644 --- a/src/i_tcp.c +++ b/src/i_tcp.c @@ -193,7 +193,7 @@ mysockaddr_t address; UINT8 mask; char *reason; - // TODO: timestamp, for tempbans! + time_t timestamp; } banned_t; static SOCKET_TYPE mysockets[MAXNETNODES+1] = {ERRSOCKET}; @@ -212,6 +212,7 @@ static size_t numbans = 0; static size_t banned_size = 0; static boolean SOCK_bannednode[MAXNETNODES+1]; /// \note do we really need the +1? +static time_t SOCK_bannednodetimeleft[MAXNETNODES+1]; static boolean init_tcp_driver = false; static const char *serverport_name = DEFAULTPORT; @@ -463,10 +464,24 @@ static const char *SOCK_GetBanReason(size_t ban) (void)ban; return NULL; #else + if (ban >= numbans) + return NULL; return banned[ban].reason; #endif } +static time_t SOCK_GetUnbanTime(size_t ban) +{ +#ifdef NONET + (void)ban; + return NO_BAN_TIME; +#else + if (ban >= numbans) + return NO_BAN_TIME; + return banned[ban].timestamp; +#endif +} + #ifndef NONET static boolean SOCK_cmpaddr(mysockaddr_t *a, mysockaddr_t *b, UINT8 mask) { @@ -640,6 +655,8 @@ static boolean SOCK_Get(void) j = getfreenode(); if (j > 0) { + const time_t curTime = time(NULL); + M_Memcpy(&clientaddress[j], &fromaddress, fromlen); nodesocket[j] = mysockets[n]; DEBFILE(va("New node detected: node:%d address:%s\n", j, @@ -652,13 +669,37 @@ static boolean SOCK_Get(void) { if (SOCK_cmpaddr(&fromaddress, &banned[i].address, banned[i].mask)) { - SOCK_bannednode[j] = true; - DEBFILE("This dude has been banned\n"); - break; + if (banned[i].timestamp != NO_BAN_TIME) + { + if (curTime >= banned[i].timestamp) + { + SOCK_bannednodetimeleft[j] = NO_BAN_TIME; + SOCK_bannednode[j] = false; + DEBFILE("This dude was banned, but enough time has passed\n"); + break; + } + + SOCK_bannednodetimeleft[j] = banned[i].timestamp - curTime; + SOCK_bannednode[j] = true; + DEBFILE("This dude has been temporarily banned\n"); + break; + } + else + { + SOCK_bannednodetimeleft[j] = NO_BAN_TIME; + SOCK_bannednode[j] = true; + DEBFILE("This dude has been banned\n"); + break; + } } } + if (i == numbans) + { + SOCK_bannednodetimeleft[j] = NO_BAN_TIME; SOCK_bannednode[j] = false; + } + return true; } else @@ -1566,7 +1607,6 @@ static boolean SOCK_SetBanReason(const char *reason) (void)reason; return false; #else - if (!reason) { reason = "No reason given"; @@ -1577,6 +1617,17 @@ static boolean SOCK_SetBanReason(const char *reason) #endif } +static boolean SOCK_SetUnbanTime(time_t timestamp) +{ +#ifdef NONET + (void)reason; + return false; +#else + banned[numbans - 1].timestamp = timestamp; + return true; +#endif +} + static void SOCK_ClearBans(void) { numbans = 0; @@ -1677,9 +1728,12 @@ boolean I_InitTcpNetwork(void) I_GetBanAddress = SOCK_GetBanAddress; I_GetBanMask = SOCK_GetBanMask; I_GetBanReason = SOCK_GetBanReason; + I_GetUnbanTime = SOCK_GetUnbanTime; I_SetBanAddress = SOCK_SetBanAddress; I_SetBanReason = SOCK_SetBanReason; + I_SetUnbanTime = SOCK_SetUnbanTime; bannednode = SOCK_bannednode; + bannednodetimeleft = SOCK_bannednodetimeleft; return ret; } From 2fdd8c1e138c705b1657b4a91dd14780aa8376b1 Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Tue, 7 Jun 2022 19:09:02 -0400 Subject: [PATCH 26/48] Implement shout commands - shout command to create a server message, with its own special sound cue & color - shoutname to control the nametag, by default this is "SERVER" - shoutcolor controls the color of the message. By default it's red, but there's also a option for player colored. - autoshout makes any message sent by a admin/server automatically turn into a shout - Unlike HOSTMOD shout, integrated it with the dedicated server say behavior -- using say on dedicated server is always a shout. --- src/d_netcmd.c | 5 +++ src/g_game.c | 30 +++++++++++++++++ src/g_game.h | 1 + src/hu_stuff.c | 90 ++++++++++++++++++++++++++++++++++++++++---------- src/sounds.c | 3 ++ src/sounds.h | 3 ++ 6 files changed, 114 insertions(+), 18 deletions(-) diff --git a/src/d_netcmd.c b/src/d_netcmd.c index 33dcbe27c..da1bbfbe7 100644 --- a/src/d_netcmd.c +++ b/src/d_netcmd.c @@ -943,6 +943,11 @@ void D_RegisterClientCommands(void) CV_RegisterVar(&cv_consolechat); CV_RegisterVar(&cv_chatnotifications); CV_RegisterVar(&cv_chatbacktint); + + CV_RegisterVar(&cv_shoutname); + CV_RegisterVar(&cv_shoutcolor); + CV_RegisterVar(&cv_autoshout); + CV_RegisterVar(&cv_songcredits); CV_RegisterVar(&cv_tutorialprompt); CV_RegisterVar(&cv_showfocuslost); diff --git a/src/g_game.c b/src/g_game.c index 68286da99..f3e0e4f1d 100644 --- a/src/g_game.c +++ b/src/g_game.c @@ -396,6 +396,36 @@ consvar_t cv_chatbacktint = CVAR_INIT ("chatbacktint", "On", CV_SAVE, CV_OnOff, static CV_PossibleValue_t consolechat_cons_t[] = {{0, "Window"}, {1, "Console"}, {2, "Window (Hidden)"}, {0, NULL}}; consvar_t cv_consolechat = CVAR_INIT ("chatmode", "Window", CV_SAVE, consolechat_cons_t, NULL); +// Shout settings +// The relevant ones are CV_NETVAR because too lazy to send them any other way +consvar_t cv_shoutname = CVAR_INIT ("shout_name", "SERVER", CV_NETVAR, NULL, NULL); + +static CV_PossibleValue_t shoutcolor_cons_t[] = +{ + {-1, "Player color"}, + {0, "White"}, + {1, "Yellow"}, + {2, "Purple"}, + {3, "Green"}, + {4, "Blue"}, + {5, "Red"}, + {6, "Gray"}, + {7, "Orange"}, + {8, "Sky-blue"}, + {9, "Gold"}, + {10, "Lavender"}, + {11, "Aqua-green"}, + {12, "Magenta"}, + {13, "Pink"}, + {14, "Brown"}, + {15, "Tan"}, + {0, NULL} +}; +consvar_t cv_shoutcolor = CVAR_INIT ("shout_color", "Red", CV_NETVAR, shoutcolor_cons_t, NULL); + +// If on and you're an admin, your messages will automatically become shouts. +consvar_t cv_autoshout = CVAR_INIT ("autoshout", "Off", CV_NETVAR, CV_OnOff, NULL); + // Pause game upon window losing focus consvar_t cv_pauseifunfocused = CVAR_INIT ("pauseifunfocused", "Yes", CV_SAVE, CV_YesNo, NULL); diff --git a/src/g_game.h b/src/g_game.h index 52c7c7a0d..40495b5c3 100644 --- a/src/g_game.h +++ b/src/g_game.h @@ -48,6 +48,7 @@ extern boolean promptactive; extern consvar_t cv_tutorialprompt; extern consvar_t cv_chatwidth, cv_chatnotifications, cv_chatheight, cv_chattime, cv_consolechat, cv_chatbacktint, cv_chatspamprotection; +extern consvar_t cv_shoutname, cv_shoutcolor, cv_autoshout; extern consvar_t cv_songcredits; extern consvar_t cv_pauseifunfocused; diff --git a/src/hu_stuff.c b/src/hu_stuff.c index b378e33af..48179281a 100644 --- a/src/hu_stuff.c +++ b/src/hu_stuff.c @@ -64,8 +64,11 @@ #define HU_INPUTX 0 #define HU_INPUTY 0 -#define HU_SERVER_SAY 1 // Server message (dedicated). -#define HU_CSAY 2 // Server CECHOes to everyone. +typedef enum +{ + HU_SHOUT = 1, // Shout message + HU_CSAY = 1<<1, // Middle-of-screen server message +} sayflags_t; //------------------------------------------- // heads up font @@ -169,6 +172,7 @@ static void Command_Say_f(void); 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); #endif @@ -210,6 +214,7 @@ void HU_Init(void) COM_AddCommand("sayto", Command_Sayto_f); COM_AddCommand("sayteam", Command_Sayteam_f); COM_AddCommand("csay", Command_CSay_f); + COM_AddCommand("shout", Command_Shout); RegisterNetXCmd(XD_SAY, Got_Saycmd); #endif @@ -456,7 +461,7 @@ void HU_AddChatText(const char *text, boolean playsound) * to -32 to say to everyone on that player's team. Note: This means you * have to add 1 to the player number, since they are 0 to 31 internally. * - * The flag HU_SERVER_SAY will be set if it is the dedicated server speaking. + * The flag HU_SHOUT will be set if it is the dedicated server speaking. * * This function obtains the message using COM_Argc() and COM_Argv(). * @@ -483,14 +488,17 @@ static void DoSayCommand(SINT8 target, size_t usedargs, UINT8 flags) return; } - // Only servers/admins can CSAY. - if(!server && !(IsPlayerAdmin(consoleplayer))) - flags &= ~HU_CSAY; + // Only servers/admins can shout or CSAY. + if (!server && !IsPlayerAdmin(consoleplayer)) + { + flags &= ~(HU_SHOUT|HU_CSAY); + } - // We handle HU_SERVER_SAY, not the caller. - flags &= ~HU_SERVER_SAY; - if(dedicated && !(flags & HU_CSAY)) - flags |= HU_SERVER_SAY; + // Enforce shout for the dedicated server. + if (dedicated && !(flags & HU_CSAY)) + { + flags |= HU_SHOUT; + } buf[0] = target; buf[1] = flags; @@ -564,6 +572,8 @@ static void Command_Say_f(void) return; } + // Autoshout is handled by HU_queueChatChar. + // If you're using the say command, you can use the shout command, lol. DoSayCommand(0, 1, 0); } @@ -627,7 +637,7 @@ static void Command_CSay_f(void) return; } - if(!server && !IsPlayerAdmin(consoleplayer)) + if (!server && !IsPlayerAdmin(consoleplayer)) { CONS_Alert(CONS_NOTICE, M_GetText("Only servers and admins can use csay.\n")); return; @@ -635,6 +645,24 @@ static void Command_CSay_f(void) DoSayCommand(0, 1, HU_CSAY); } + +static void Command_Shout(void) +{ + if (COM_Argc() < 2) + { + CONS_Printf(M_GetText("shout : send a message with special alert sound, name, and color\n")); + return; + } + + if (!server && !IsPlayerAdmin(consoleplayer)) + { + CONS_Alert(CONS_NOTICE, M_GetText("Only servers and admins can use shout.\n")); + return; + } + + DoSayCommand(0, 1, HU_SHOUT); +} + static tic_t stop_spamming[MAXPLAYERS]; /** Receives a message, processing an ::XD_SAY command. @@ -658,7 +686,7 @@ static void Got_Saycmd(UINT8 **p, INT32 playernum) msg = (char *)*p; SKIPSTRING(*p); - if ((cv_mute.value || flags & (HU_CSAY|HU_SERVER_SAY)) && playernum != serverplayer && !(IsPlayerAdmin(playernum))) + 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"), @@ -687,7 +715,7 @@ 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)) + 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; @@ -720,8 +748,8 @@ static void Got_Saycmd(UINT8 **p, INT32 playernum) action = true; } - if (flags & HU_SERVER_SAY) - dispname = "SERVER"; + if (flags & HU_SHOUT) + dispname = cv_shoutname.zstring; else dispname = player_names[playernum]; @@ -747,7 +775,30 @@ static void Got_Saycmd(UINT8 **p, INT32 playernum) char *tempchar = NULL; char color_prefix[2]; - if (players[playernum].spectator) + if (flags & HU_SHOUT) + { + if (cv_shoutcolor.value == -1) + { + UINT16 chatcolor = skincolors[players[playernum].skincolor].chatcolor; + + if (chatcolor > V_TANMAP) + { + sprintf(color_prefix, "%c", '\x80'); + } + else + { + sprintf(color_prefix, "%c", '\x80' + (chatcolor >> V_CHARCOLORSHIFT)); + } + } + else + { + sprintf(color_prefix, "%c", '\x80' + cv_shoutcolor.value); + } + + // Colorize full text + cstart = textcolor = color_prefix; + } + else if (players[playernum].spectator) { // grey text cstart = textcolor = "\x86"; @@ -834,7 +885,10 @@ static void Got_Saycmd(UINT8 **p, INT32 playernum) fmt2 = "%s<%s%s>\x80%s %s%s"; }*/ - HU_AddChatText(va(fmt2, prefix, cstart, dispname, cend, textcolor, msg), cv_chatnotifications.value); // add to chat + HU_AddChatText(va(fmt2, prefix, cstart, dispname, cend, textcolor, msg), (cv_chatnotifications.value) && !(flags & HU_SHOUT)); // add to chat + + if ((cv_chatnotifications.value) && (flags & HU_SHOUT)) + S_StartSound(NULL, sfx_sysmsg); if (tempchar) Z_Free(tempchar); @@ -1156,7 +1210,7 @@ static void HU_queueChatChar(INT32 c) else buf[0] = target; - buf[1] = 0; // flags + buf[1] = ((server || IsPlayerAdmin(consoleplayer)) && cv_autoshout.value) ? HU_SHOUT : 0; // flags SendNetXCmd(XD_SAY, buf, 2 + strlen(&buf[2]) + 1); } return; diff --git a/src/sounds.c b/src/sounds.c index 7cbc0e8c0..99a262899 100644 --- a/src/sounds.c +++ b/src/sounds.c @@ -1112,6 +1112,9 @@ sfxinfo_t S_sfx[NUMSFX] = // SRB2kart - Grow/invinc clash {"parry", false, 64, 16, -1, NULL, 0, -1, -1, LUMPERROR, ""}, // SF_X8AWAYSOUND + // Shout message sound effect + {"sysmsg", false, 60, 0, -1, NULL, 0, -1, -1, LUMPERROR, "Server notification"}, + // SRB2Kart - Engine sounds // Engine class A {"krta00", false, 48, 65, -1, NULL, 0, -1, -1, LUMPERROR, ""}, diff --git a/src/sounds.h b/src/sounds.h index 6d1c51f57..f9fcb564c 100644 --- a/src/sounds.h +++ b/src/sounds.h @@ -1176,6 +1176,9 @@ typedef enum // SRB2Kart - Powerup clash SFX sfx_parry, + // Shout message sound effect + sfx_sysmsg, + // Next up: UNIQUE ENGINE SOUNDS! Hoooooo boy... // Engine class A - Low Speed, Low Weight sfx_krta00, From d2a0bdb04473296730ab7ad62fa83fba9d41fcf2 Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Tue, 7 Jun 2022 19:22:44 -0400 Subject: [PATCH 27/48] Attempt fix for players being able to get admin Tyron said there is an "outstanding base game bug where an authenticated player can ghost and a new player will log in on their node, inheriting admin" ... wow that's terrifying! Let's reset admin for every instance of CL_ClearPlayer instead of only in CL_RemovePlayer. --- src/d_clisrv.c | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/src/d_clisrv.c b/src/d_clisrv.c index 3ba0b31aa..6ac5ab551 100644 --- a/src/d_clisrv.c +++ b/src/d_clisrv.c @@ -2291,6 +2291,8 @@ void CL_ClearPlayer(INT32 playernum) splitscreen_original_party_size[playernum] = 0; memset(&players[playernum], 0, sizeof (player_t)); + + RemoveAdminPlayer(playernum); // don't stay admin after you're gone } // @@ -2348,11 +2350,6 @@ void CL_RemovePlayer(INT32 playernum, kickreason_t reason) player_name_changes[playernum] = 0; - if (IsPlayerAdmin(playernum)) - { - RemoveAdminPlayer(playernum); // don't stay admin after you're gone - } - LUA_InvalidatePlayer(&players[playernum]); K_CheckBumpers(); From 0308ab6bd46a10fe721bed2455698f21f7cf5b13 Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Fri, 10 Jun 2022 09:24:08 -0400 Subject: [PATCH 28/48] Scheduling commands - `schedule_add ` to add a command that runs on a recurring timer. - `schedule_list` to print out all of the scheduled tasks. - NEW: `schedule_clear` to revert the schedule to a blank slate. - `schedule` cvar determines whenever or not to run the scheduled tasks. Unlike HOSTMOD, turning this off will reset the timers of the tasks, instead of freezing them. - I did not implement HOSTMOD's ability to pick from several random command per scheduled task. Would drastically increase the code complexity when you can just use a choose command in your schedule_add for the exact same effect. --- src/d_clisrv.c | 22 +++- src/d_netcmd.c | 297 ++++++++++++++++++++++++++++++++++++++++++++++++- src/d_netcmd.h | 20 ++++ src/k_pwrlv.c | 12 +- src/p_saveg.c | 34 +++++- 5 files changed, 364 insertions(+), 21 deletions(-) diff --git a/src/d_clisrv.c b/src/d_clisrv.c index 6ac5ab551..e60a9a549 100644 --- a/src/d_clisrv.c +++ b/src/d_clisrv.c @@ -2001,7 +2001,9 @@ static void CL_ConnectToServer(void) wipegamestate = GS_WAITINGPLAYERS; ClearAdminPlayers(); + Schedule_Clear(); K_ClearClientPowerLevels(); + pnumnodes = 1; oldtic = 0; #ifndef NONET @@ -3135,14 +3137,17 @@ void SV_ResetServer(void) for (i = 0; i < MAXPLAYERS; i++) { LUA_InvalidatePlayer(&players[i]); - playeringame[i] = false; - playernode[i] = UINT8_MAX; sprintf(player_names[i], "Player %c", 'A' + i); - adminplayers[i] = -1; // Populate the entire adminplayers array with -1. - K_ClearClientPowerLevels(); - splitscreen_invitations[i] = -1; } + memset(playeringame, false, sizeof playeringame); + memset(playernode, UINT8_MAX, sizeof playernode); + + ClearAdminPlayers(); + Schedule_Clear(); + K_ClearClientPowerLevels(); + + memset(splitscreen_invitations, -1, sizeof splitscreen_invitations); memset(splitscreen_partied, 0, sizeof splitscreen_partied); memset(player_name_changes, 0, sizeof player_name_changes); @@ -3227,6 +3232,7 @@ void D_QuitNetGame(void) D_CloseConnection(); ClearAdminPlayers(); + Schedule_Clear(); K_ClearClientPowerLevels(); DEBFILE("===========================================================================\n" @@ -5230,7 +5236,13 @@ boolean TryRunTics(tic_t realtics) ps_tictime = I_GetPreciseTime(); G_Ticker((gametic % NEWTICRATERATIO) == 0); + if (gametic % TICRATE == 0) + { + Schedule_Run(); + } + ExtraDataTicker(); + gametic++; consistancy[gametic%BACKUPTICS] = Consistancy(); diff --git a/src/d_netcmd.c b/src/d_netcmd.c index da1bbfbe7..f5976997b 100644 --- a/src/d_netcmd.c +++ b/src/d_netcmd.c @@ -98,6 +98,8 @@ static void Got_RunSOCcmd(UINT8 **cp, INT32 playernum); static void Got_Teamchange(UINT8 **cp, INT32 playernum); static void Got_Clearscores(UINT8 **cp, INT32 playernum); static void Got_DiscordInfo(UINT8 **cp, INT32 playernum); +static void Got_ScheduleTaskcmd(UINT8 **cp, INT32 playernum); +static void Got_ScheduleClearcmd(UINT8 **cp, INT32 playernum); static void PointLimit_OnChange(void); static void TimeLimit_OnChange(void); @@ -146,6 +148,8 @@ static void KartEncore_OnChange(void); static void KartComeback_OnChange(void); static void KartEliminateLast_OnChange(void); +static void Schedule_OnChange(void); + #ifdef NETGAME_DEVMODE static void Fishcake_OnChange(void); #endif @@ -221,6 +225,10 @@ static void Command_Archivetest_f(void); static void Command_KartGiveItem_f(void); +static void Command_Schedule_Add(void); +static void Command_Schedule_Clear(void); +static void Command_Schedule_List(void); + // ========================================================================= // CLIENT VARIABLES // ========================================================================= @@ -521,6 +529,8 @@ consvar_t cv_perfstats = CVAR_INIT ("perfstats", "Off", 0, perfstats_cons_t, NUL consvar_t cv_director = CVAR_INIT ("director", "Off", 0, CV_OnOff, NULL); +consvar_t cv_schedule = CVAR_INIT ("schedule", "On", CV_NETVAR|CV_CALL, CV_OnOff, Schedule_OnChange); + char timedemo_name[256]; boolean timedemo_csv; char timedemo_csv_id[256]; @@ -536,6 +546,11 @@ UINT8 splitscreen = 0; boolean circuitmap = false; INT32 adminplayers[MAXPLAYERS]; +// Scheduled cvars. +scheduleTask_t **schedule = NULL; +size_t schedule_size = 0; +size_t schedule_len = 0; + /// \warning Keep this up-to-date if you add/remove/rename net text commands const char *netxcmdnames[MAXNETXCMD - 1] = { @@ -575,7 +590,9 @@ const char *netxcmdnames[MAXNETXCMD - 1] = "GIVEITEM", // XD_GIVEITEM "ADDBOT", // XD_ADDBOT "DISCORD", // XD_DISCORD - "PLAYSOUND" // XD_PLAYSOUND + "PLAYSOUND", // XD_PLAYSOUND + "SCHEDULETASK", // XD_SCHEDULETASK + "SCHEDULECLEAR" // XD_SCHEDULECLEAR }; // ========================================================================= @@ -630,6 +647,9 @@ void D_RegisterServerCommands(void) RegisterNetXCmd(XD_GIVEITEM, Got_GiveItemcmd); + RegisterNetXCmd(XD_SCHEDULETASK, Got_ScheduleTaskcmd); + RegisterNetXCmd(XD_SCHEDULECLEAR, Got_ScheduleClearcmd); + // Remote Administration COM_AddCommand("password", Command_Changepassword_f); COM_AddCommand("login", Command_Login_f); // useful in dedicated to kick off remote admin @@ -687,6 +707,10 @@ void D_RegisterServerCommands(void) COM_AddCommand("kartgiveitem", Command_KartGiveItem_f); + COM_AddCommand("schedule_add", Command_Schedule_Add); + COM_AddCommand("schedule_clear", Command_Schedule_Clear); + COM_AddCommand("schedule_list", Command_Schedule_List); + // for master server connection AddMServCommands(); @@ -758,6 +782,8 @@ void D_RegisterServerCommands(void) CV_RegisterVar(&cv_director); + CV_RegisterVar(&cv_schedule); + CV_RegisterVar(&cv_dummyconsvar); #ifdef USE_STUN @@ -3731,9 +3757,7 @@ void SetAdminPlayer(INT32 playernum) void ClearAdminPlayers(void) { - INT32 i; - for (i = 0; i < MAXPLAYERS; i++) - adminplayers[i] = -1; + memset(adminplayers, -1, sizeof(adminplayers)); } void RemoveAdminPlayer(INT32 playernum) @@ -3850,6 +3874,117 @@ static void Got_Removal(UINT8 **cp, INT32 playernum) CONS_Printf(M_GetText("You are no longer a server administrator.\n")); } +void Schedule_Run(void) +{ + size_t i; + + if (schedule_len == 0) + { + // No scheduled tasks to run. + return; + } + + if (!cv_schedule.value) + { + // We don't WANT to run tasks. + return; + } + + for (i = 0; i < schedule_len; i++) + { + scheduleTask_t *task = schedule[i]; + + if (task == NULL) + { + // Shouldn't happen. + break; + } + + if (task->timer > 0) + { + task->timer--; + } + + if (task->timer == 0) + { + // Reset timer + task->timer = task->basetime; + + // Run command for server + if (server) + { + CONS_Printf( + "%d seconds elapsed, running \"" "\x82" "%s" "\x80" "\".\n", + task->basetime, + task->command + ); + + COM_BufAddText(task->command); + COM_BufAddText("\n"); + } + } + } +} + +void Schedule_Insert(scheduleTask_t *addTask) +{ + if (schedule_len >= schedule_size) + { + if (schedule_size == 0) + { + schedule_size = 8; + } + else + { + schedule_size *= 2; + } + + schedule = Z_ReallocAlign( + (void*) schedule, + sizeof(scheduleTask_t*) * schedule_size, + PU_STATIC, + NULL, + sizeof(scheduleTask_t*) * 8 + ); + } + + schedule[schedule_len] = addTask; + schedule_len++; +} + +void Schedule_Add(INT16 basetime, INT16 timeleft, const char *command) +{ + scheduleTask_t *task = (scheduleTask_t*) Z_CallocAlign( + sizeof(scheduleTask_t), + PU_STATIC, + NULL, + sizeof(scheduleTask_t) * 8 + ); + + task->basetime = basetime; + task->timer = timeleft; + task->command = Z_StrDup(command); + + Schedule_Insert(task); +} + +void Schedule_Clear(void) +{ + size_t i; + + for (i = 0; i < schedule_len; i++) + { + scheduleTask_t *task = schedule[i]; + + if (task->command) + Z_Free(task->command); + } + + schedule_len = 0; + schedule_size = 0; + schedule = NULL; +} + static void Command_MotD_f(void) { size_t i, j; @@ -5023,6 +5158,58 @@ static void Got_GiveItemcmd(UINT8 **cp, INT32 playernum) players[playernum].itemamount = amt; } +static void Got_ScheduleTaskcmd(UINT8 **cp, INT32 playernum) +{ + char command[MAXTEXTCMD]; + INT16 seconds; + + seconds = READINT16(*cp); + READSTRING(*cp, command); + + if (playernum != serverplayer && !IsPlayerAdmin(playernum)) + { + CONS_Alert(CONS_WARNING, + M_GetText ("Illegal schedule task received from %s\n"), + player_names[playernum]); + if (server) + SendKick(playernum, KICK_MSG_CON_FAIL); + return; + } + + Schedule_Add(seconds, seconds, (const char *)command); + + if (server || consoleplayer == playernum) + { + CONS_Printf( + "OK! Running \"" "\x82" "%s" "\x80" "\" every " "\x82" "%d" "\x80" " seconds.\n", + command, + seconds + ); + } +} + +static void Got_ScheduleClearcmd(UINT8 **cp, INT32 playernum) +{ + (void)cp; + + if (playernum != serverplayer && !IsPlayerAdmin(playernum)) + { + CONS_Alert(CONS_WARNING, + M_GetText ("Illegal schedule clear received from %s\n"), + player_names[playernum]); + if (server) + SendKick(playernum, KICK_MSG_CON_FAIL); + return; + } + + Schedule_Clear(); + + if (server || consoleplayer == playernum) + { + CONS_Printf("All scheduled tasks have been cleared.\n"); + } +} + /** Prints the number of displayplayers[0]. * * \todo Possibly remove this; it was useful for debugging at one point. @@ -5287,6 +5474,86 @@ static void Command_KartGiveItem_f(void) } } +static void Command_Schedule_Add(void) +{ + UINT8 buf[MAXTEXTCMD]; + UINT8 *buf_p = buf; + + size_t ac; + INT16 seconds; + const char *command; + + if (!(server || IsPlayerAdmin(consoleplayer))) + { + CONS_Printf("Only the server or a remote admin can use this.\n"); + return; + } + + ac = COM_Argc(); + if (ac < 3) + { + CONS_Printf("schedule <...>: runs the specified commands on a recurring timer\n"); + return; + } + + seconds = atoi(COM_Argv(1)); + + if (seconds <= 0) + { + CONS_Printf("Timer must be at least 1 second.\n"); + return; + } + + command = COM_Argv(2); + + WRITEINT16(buf_p, seconds); + WRITESTRING(buf_p, command); + + SendNetXCmd(XD_SCHEDULETASK, buf, buf_p - buf); +} + +static void Command_Schedule_Clear(void) +{ + if (!(server || IsPlayerAdmin(consoleplayer))) + { + CONS_Printf("Only the server or a remote admin can use this.\n"); + return; + } + + SendNetXCmd(XD_SCHEDULECLEAR, NULL, 0); +} + +static void Command_Schedule_List(void) +{ + size_t i; + + if (!(server || IsPlayerAdmin(consoleplayer))) + { + // I set it up in a way that this information could be available + // to everyone, but HOSTMOD has it server/admin-only too, so eh? + CONS_Printf("Only the server or a remote admin can use this.\n"); + return; + } + + if (schedule_len == 0) + { + CONS_Printf("No tasks are scheduled.\n"); + return; + } + + for (i = 0; i < schedule_len; i++) + { + scheduleTask_t *task = schedule[i]; + + CONS_Printf( + "In " "\x82" "%d" "\x80" " second%s: " "\x82" "%s" "\x80" "\n", + task->timer, + (task->timer > 1) ? "s" : "", + task->command + ); + } +} + /** Makes a change to ::cv_forceskin take effect immediately. * * \sa Command_SetForcedSkin_f, cv_forceskin, forcedskin @@ -5912,6 +6179,28 @@ static void KartEliminateLast_OnChange(void) P_CheckRacers(); } +static void Schedule_OnChange(void) +{ + size_t i; + + if (cv_schedule.value) + { + return; + } + + if (schedule_len == 0) + { + return; + } + + // Reset timers when turning off. + for (i = 0; i < schedule_len; i++) + { + scheduleTask_t *task = schedule[i]; + task->timer = task->basetime; + } +} + void Got_DiscordInfo(UINT8 **p, INT32 playernum) { if (playernum != serverplayer /*&& !IsPlayerAdmin(playernum)*/) diff --git a/src/d_netcmd.h b/src/d_netcmd.h index bf8109be5..ff3b0be9c 100644 --- a/src/d_netcmd.h +++ b/src/d_netcmd.h @@ -115,6 +115,8 @@ extern consvar_t cv_perfstats; extern consvar_t cv_director; +extern consvar_t cv_schedule; + extern char timedemo_name[256]; extern boolean timedemo_csv; extern char timedemo_csv_id[256]; @@ -159,6 +161,8 @@ typedef enum XD_ADDBOT, // 33 XD_DISCORD, // 34 XD_PLAYSOUND, // 35 + XD_SCHEDULETASK, // 36 + XD_SCHEDULECLEAR, // 37 MAXNETXCMD } netxcmd_t; @@ -227,6 +231,22 @@ void RemoveAdminPlayer(INT32 playernum); void ItemFinder_OnChange(void); void D_SetPassword(const char *pw); +typedef struct +{ + UINT16 basetime; + UINT16 timer; + char *command; +} scheduleTask_t; + +extern scheduleTask_t **schedule; +extern size_t schedule_size; +extern size_t schedule_len; + +void Schedule_Run(void); +void Schedule_Insert(scheduleTask_t *addTask); +void Schedule_Add(INT16 basetime, INT16 timeleft, const char *command); +void Schedule_Clear(void); + // used for the player setup menu UINT8 CanChangeSkin(INT32 playernum); diff --git a/src/k_pwrlv.c b/src/k_pwrlv.c index cd3ee1827..b25d26cda 100644 --- a/src/k_pwrlv.c +++ b/src/k_pwrlv.c @@ -60,16 +60,8 @@ SINT8 K_UsingPowerLevels(void) void K_ClearClientPowerLevels(void) { - UINT8 i, j; - for (i = 0; i < MAXPLAYERS; i++) - { - clientPowerAdd[i] = 0; - - for (j = 0; j < PWRLV_NUMTYPES; j++) - { - clientpowerlevels[i][j] = 0; - } - } + memset(clientpowerlevels, 0, sizeof clientpowerlevels); + memset(clientPowerAdd, 0, sizeof clientPowerAdd); } // Adapted from this: http://wiki.tockdom.com/wiki/Player_Rating diff --git a/src/p_saveg.c b/src/p_saveg.c index 81adeca60..1abb6d598 100644 --- a/src/p_saveg.c +++ b/src/p_saveg.c @@ -4407,7 +4407,7 @@ static inline void P_UnArchiveSPGame(INT16 mapoverride) static void P_NetArchiveMisc(boolean resending) { - INT32 i; + size_t i; WRITEUINT32(save_p, ARCHIVEBLOCK_MISC); @@ -4536,11 +4536,23 @@ static void P_NetArchiveMisc(boolean resending) WRITEUINT8(save_p, 0x2f); else WRITEUINT8(save_p, 0x2e); + + // Only the server uses this, but it + // needs synched for remote admins anyway. + WRITEUINT32(save_p, schedule_len); + for (i = 0; i < schedule_len; i++) + { + scheduleTask_t *task = schedule[i]; + WRITEINT16(save_p, task->basetime); + WRITEINT16(save_p, task->timer); + WRITESTRING(save_p, task->command); + } } static inline boolean P_NetUnArchiveMisc(boolean reloading) { - INT32 i; + size_t i; + size_t numTasks; if (READUINT32(save_p) != ARCHIVEBLOCK_MISC) I_Error("Bad $$$.sav at archive block Misc"); @@ -4684,6 +4696,24 @@ static inline boolean P_NetUnArchiveMisc(boolean reloading) if (READUINT8(save_p) == 0x2f) paused = true; + // Only the server uses this, but it + // needs synched for remote admins anyway. + Schedule_Clear(); + + numTasks = READUINT32(save_p); + for (i = 0; i < numTasks; i++) + { + INT16 basetime; + INT16 timer; + char command[MAXTEXTCMD]; + + basetime = READINT16(save_p); + timer = READINT16(save_p); + READSTRING(save_p, command); + + Schedule_Add(basetime, timer, command); + } + return true; } From eb7e84b96142995929b65512c2e2f469a87dbf0c Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Fri, 10 Jun 2022 11:19:17 -0400 Subject: [PATCH 29/48] Automate commands - `automate_set ` to set a command to run each time an event triggers. - Currently implemented events are "roundstart", "intermissionstart", and "votestart", all of the ones from HOSTMOD. - Turn `automate` off to disable this feature entirely. Because of the new safer way this is implemented (in HOSTMOD, this just calls some console aliases), this is turned on by default instead of off. - This is set up in a way to facilitate adding more automation events very easily, if desired. --- src/d_clisrv.c | 3 + src/d_netcmd.c | 192 ++++++++++++++++++++++++++++++++++++++++++++++++- src/d_netcmd.h | 13 ++++ src/g_game.c | 10 +++ src/y_inter.c | 3 + 5 files changed, 219 insertions(+), 2 deletions(-) diff --git a/src/d_clisrv.c b/src/d_clisrv.c index e60a9a549..2a6cf1fde 100644 --- a/src/d_clisrv.c +++ b/src/d_clisrv.c @@ -2002,6 +2002,7 @@ static void CL_ConnectToServer(void) ClearAdminPlayers(); Schedule_Clear(); + Automate_Clear(); K_ClearClientPowerLevels(); pnumnodes = 1; @@ -3145,6 +3146,7 @@ void SV_ResetServer(void) ClearAdminPlayers(); Schedule_Clear(); + Automate_Clear(); K_ClearClientPowerLevels(); memset(splitscreen_invitations, -1, sizeof splitscreen_invitations); @@ -3233,6 +3235,7 @@ void D_QuitNetGame(void) D_CloseConnection(); ClearAdminPlayers(); Schedule_Clear(); + Automate_Clear(); K_ClearClientPowerLevels(); DEBFILE("===========================================================================\n" diff --git a/src/d_netcmd.c b/src/d_netcmd.c index f5976997b..416f94ace 100644 --- a/src/d_netcmd.c +++ b/src/d_netcmd.c @@ -100,6 +100,7 @@ static void Got_Clearscores(UINT8 **cp, INT32 playernum); static void Got_DiscordInfo(UINT8 **cp, INT32 playernum); static void Got_ScheduleTaskcmd(UINT8 **cp, INT32 playernum); static void Got_ScheduleClearcmd(UINT8 **cp, INT32 playernum); +static void Got_Automatecmd(UINT8 **cp, INT32 playernum); static void PointLimit_OnChange(void); static void TimeLimit_OnChange(void); @@ -229,6 +230,8 @@ static void Command_Schedule_Add(void); static void Command_Schedule_Clear(void); static void Command_Schedule_List(void); +static void Command_Automate_Set(void); + // ========================================================================= // CLIENT VARIABLES // ========================================================================= @@ -531,6 +534,8 @@ consvar_t cv_director = CVAR_INIT ("director", "Off", 0, CV_OnOff, NULL); consvar_t cv_schedule = CVAR_INIT ("schedule", "On", CV_NETVAR|CV_CALL, CV_OnOff, Schedule_OnChange); +consvar_t cv_automate = CVAR_INIT ("automate", "On", CV_NETVAR, CV_OnOff, NULL); + char timedemo_name[256]; boolean timedemo_csv; char timedemo_csv_id[256]; @@ -546,11 +551,21 @@ UINT8 splitscreen = 0; boolean circuitmap = false; INT32 adminplayers[MAXPLAYERS]; -// Scheduled cvars. +// Scheduled commands. scheduleTask_t **schedule = NULL; size_t schedule_size = 0; size_t schedule_len = 0; +// Automation commands +char *automate_commands[AEV__MAX]; + +const char *automate_names[AEV__MAX] = +{ + "RoundStart", // AEV_ROUNDSTART + "IntermissionStart", // AEV_INTERMISSIONSTART + "VoteStart" // AEV_VOTESTART +}; + /// \warning Keep this up-to-date if you add/remove/rename net text commands const char *netxcmdnames[MAXNETXCMD - 1] = { @@ -592,7 +607,8 @@ const char *netxcmdnames[MAXNETXCMD - 1] = "DISCORD", // XD_DISCORD "PLAYSOUND", // XD_PLAYSOUND "SCHEDULETASK", // XD_SCHEDULETASK - "SCHEDULECLEAR" // XD_SCHEDULECLEAR + "SCHEDULECLEAR", // XD_SCHEDULECLEAR + "AUTOMATE" // XD_AUTOMATE }; // ========================================================================= @@ -649,6 +665,7 @@ void D_RegisterServerCommands(void) RegisterNetXCmd(XD_SCHEDULETASK, Got_ScheduleTaskcmd); RegisterNetXCmd(XD_SCHEDULECLEAR, Got_ScheduleClearcmd); + RegisterNetXCmd(XD_AUTOMATE, Got_Automatecmd); // Remote Administration COM_AddCommand("password", Command_Changepassword_f); @@ -711,6 +728,8 @@ void D_RegisterServerCommands(void) COM_AddCommand("schedule_clear", Command_Schedule_Clear); COM_AddCommand("schedule_list", Command_Schedule_List); + COM_AddCommand("automate_set", Command_Automate_Set); + // for master server connection AddMServCommands(); @@ -783,6 +802,7 @@ void D_RegisterServerCommands(void) CV_RegisterVar(&cv_director); CV_RegisterVar(&cv_schedule); + CV_RegisterVar(&cv_automate); CV_RegisterVar(&cv_dummyconsvar); @@ -3985,6 +4005,82 @@ void Schedule_Clear(void) schedule = NULL; } +void Automate_Set(automateEvents_t type, const char *command) +{ + if (!server) + { + // Since there's no list command or anything for this, + // we don't need this code to run for anyone but the server. + return; + } + + if (automate_commands[type] != NULL) + { + // Free the old command. + Z_Free(automate_commands[type]); + } + + if (command == NULL || strlen(command) == 0) + { + // Remove the command. + automate_commands[type] = NULL; + } + else + { + // New command. + automate_commands[type] = Z_StrDup(command); + } +} + +void Automate_Run(automateEvents_t type) +{ + if (!server) + { + // Only the server should be doing this. + return; + } + +#ifdef PARANOIA + if (type >= AEV__MAX) + { + // Shouldn't happen. + I_Error("Attempted to run invalid automation type."); + return; + } +#endif + + if (!cv_automate.value) + { + // We don't want to run automation. + return; + } + + if (automate_commands[type] == NULL) + { + // No command to run. + return; + } + + CONS_Printf( + "Running %s automate command \"" "\x82" "%s" "\x80" "\"...\n", + automate_names[type], + automate_commands[type] + ); + + COM_BufAddText(automate_commands[type]); + COM_BufAddText("\n"); +} + +void Automate_Clear(void) +{ + size_t i; + + for (i = 0; i < AEV__MAX; i++) + { + Automate_Set(i, NULL); + } +} + static void Command_MotD_f(void) { size_t i, j; @@ -5210,6 +5306,49 @@ static void Got_ScheduleClearcmd(UINT8 **cp, INT32 playernum) } } +static void Got_Automatecmd(UINT8 **cp, INT32 playernum) +{ + UINT8 eventID; + char command[MAXTEXTCMD]; + + eventID = READUINT8(*cp); + READSTRING(*cp, command); + + if ( + (playernum != serverplayer && !IsPlayerAdmin(playernum)) + || (eventID >= AEV__MAX) + ) + { + CONS_Alert(CONS_WARNING, + M_GetText ("Illegal automate received from %s\n"), + player_names[playernum]); + if (server) + SendKick(playernum, KICK_MSG_CON_FAIL); + return; + } + + Automate_Set(eventID, command); + + if (server || consoleplayer == playernum) + { + if (command == NULL || strlen(command) == 0) + { + CONS_Printf( + "Removed the %s automate command.\n", + automate_names[eventID] + ); + } + else + { + CONS_Printf( + "Set the %s automate command to \"" "\x82" "%s" "\x80" "\".\n", + automate_names[eventID], + command + ); + } + } +} + /** Prints the number of displayplayers[0]. * * \todo Possibly remove this; it was useful for debugging at one point. @@ -5554,6 +5693,55 @@ static void Command_Schedule_List(void) } } +static void Command_Automate_Set(void) +{ + UINT8 buf[MAXTEXTCMD]; + UINT8 *buf_p = buf; + + size_t ac; + + const char *event; + size_t eventID; + + const char *command; + + if (!(server || IsPlayerAdmin(consoleplayer))) + { + CONS_Printf("Only the server or a remote admin can use this.\n"); + return; + } + + ac = COM_Argc(); + if (ac < 3) + { + CONS_Printf("automate_set : sets the command to run each time a event triggers\n"); + return; + } + + event = COM_Argv(1); + + for (eventID = 0; eventID < AEV__MAX; eventID++) + { + if (strcasecmp(event, automate_names[eventID]) == 0) + { + break; + } + } + + if (eventID == AEV__MAX) + { + CONS_Printf("Unknown event type \"%s\".\n", event); + return; + } + + command = COM_Argv(2); + + WRITEUINT8(buf_p, eventID); + WRITESTRING(buf_p, command); + + SendNetXCmd(XD_AUTOMATE, buf, buf_p - buf); +} + /** Makes a change to ::cv_forceskin take effect immediately. * * \sa Command_SetForcedSkin_f, cv_forceskin, forcedskin diff --git a/src/d_netcmd.h b/src/d_netcmd.h index ff3b0be9c..ddcba960e 100644 --- a/src/d_netcmd.h +++ b/src/d_netcmd.h @@ -163,6 +163,7 @@ typedef enum XD_PLAYSOUND, // 35 XD_SCHEDULETASK, // 36 XD_SCHEDULECLEAR, // 37 + XD_AUTOMATE, // 38 MAXNETXCMD } netxcmd_t; @@ -247,6 +248,18 @@ void Schedule_Insert(scheduleTask_t *addTask); void Schedule_Add(INT16 basetime, INT16 timeleft, const char *command); void Schedule_Clear(void); +typedef enum +{ + AEV_ROUNDSTART, + AEV_INTERMISSIONSTART, + AEV_VOTESTART, + AEV__MAX +} automateEvents_t; + +void Automate_Run(automateEvents_t type); +void Automate_Set(automateEvents_t type, const char *command); +void Automate_Clear(void); + // used for the player setup menu UINT8 CanChangeSkin(INT32 playernum); diff --git a/src/g_game.c b/src/g_game.c index f3e0e4f1d..31fe9876e 100644 --- a/src/g_game.c +++ b/src/g_game.c @@ -1335,6 +1335,7 @@ static void weaponPrefChange4(void) void G_DoLoadLevel(boolean resetplayer) { INT32 i, j; + boolean doAutomate = false; // Make sure objectplace is OFF when you first start the level! OP_ResetObjectplace(); @@ -1364,6 +1365,10 @@ void G_DoLoadLevel(boolean resetplayer) else titlemapinaction = TITLEMAP_OFF; + // Doing this matches HOSTMOD behavior. + // Is that desired? IDK + doAutomate = (gamestate != GS_LEVEL); + G_SetGamestate(GS_LEVEL); I_UpdateMouseGrab(); @@ -1405,6 +1410,11 @@ void G_DoLoadLevel(boolean resetplayer) CON_ClearHUD(); server_lagless = cv_lagless.value; + + if (doAutomate == true) + { + Automate_Run(AEV_ROUNDSTART); + } } // diff --git a/src/y_inter.c b/src/y_inter.c index d66a82dcb..3a19360a9 100644 --- a/src/y_inter.c +++ b/src/y_inter.c @@ -1023,6 +1023,8 @@ void Y_StartIntermission(void) usetile = useinterpic = false; usebuffer = true; } + + Automate_Run(AEV_INTERMISSIONSTART); } // ====== @@ -1685,6 +1687,7 @@ void Y_StartVote(void) } voteclient.loaded = true; + Automate_Run(AEV_VOTESTART); } // From d6e2410cbbf9f4c40db93e28837a906a6777ca2e Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Fri, 10 Jun 2022 18:27:43 -0400 Subject: [PATCH 30/48] Ban improvements - Save a note of the username, not just the reason. - Allow setting a mask with the `banip` command. - Make ban.txt's formatting a lot more sane. Username and reason are stored in quotes. The mask uses the same formatting as actual CDIR. - Keep track of if we tried to load ban.txt. If it wasn't, then don't save over it with a blank file. - Disallow quotes in player names, as it makes player name detection in console more annoying, and saving username in files scary. --- src/d_clisrv.c | 119 ++++++++++++++++++++++++++++++++++++++----------- src/d_net.c | 2 + src/d_net.h | 1 + src/d_netcmd.c | 14 ++++-- src/i_net.h | 2 + src/i_tcp.c | 50 ++++++++++++++++++++- 6 files changed, 159 insertions(+), 29 deletions(-) diff --git a/src/d_clisrv.c b/src/d_clisrv.c index 2a6cf1fde..1803e5367 100644 --- a/src/d_clisrv.c +++ b/src/d_clisrv.c @@ -2089,15 +2089,25 @@ static void Command_ShowBan(void) //Print out ban list CONS_Printf(M_GetText("(empty)\n")); } +static boolean bansLoaded = false; + void D_SaveBan(void) { FILE *f; size_t i; - const char *address, *mask, *reason; + const char *address, *mask; + const char *username, *reason; const time_t curTime = time(NULL); time_t unbanTime = NO_BAN_TIME; const char *path = va("%s"PATHSEP"%s", srb2home, "ban.txt"); + if (bansLoaded != true) + { + // You didn't even get to ATTEMPT to load bans.txt. + // Don't immediately save nothing over it. + return; + } + f = fopen(path, "w"); if (!f) @@ -2112,24 +2122,37 @@ void D_SaveBan(void) { unbanTime = I_GetUnbanTime(i); } + else + { + unbanTime = NO_BAN_TIME; + } if (unbanTime != NO_BAN_TIME && curTime >= unbanTime) { - // Don't need to save this one anymore. + // This one has served their sentence. + // We don't need to save them in the file anymore. continue; } + mask = NULL; if (!I_GetBanMask || (mask = I_GetBanMask(i)) == NULL) - fprintf(f, "%s 0", address); + fprintf(f, "%s/0", address); else - fprintf(f, "%s %s", address, mask); + fprintf(f, "%s/%s", address, mask); fprintf(f, " %ld", (long)unbanTime); - if (I_GetBanReason && (reason = I_GetBanReason(i)) != NULL) - fprintf(f, " %s\n", reason); + username = NULL; + if (I_GetBanUsername && (username = I_GetBanUsername(i)) != NULL) + fprintf(f, " \"%s\"", username); else - fprintf(f, " %s\n", "No reason given"); + fprintf(f, " \"%s\"", "Direct IP ban"); + + reason = NULL; + if (I_GetBanReason && (reason = I_GetBanReason(i)) != NULL) + fprintf(f, " \"%s\"\n", reason); + else + fprintf(f, " \"%s\"\n", "No reason given"); } fclose(f); @@ -2144,17 +2167,21 @@ static void Command_ClearBans(void) D_SaveBan(); } -static void Ban_Load_File(boolean warning) +void D_LoadBan(boolean warning) { FILE *f; size_t i; - const char *address, *mask, *reason; + const char *address, *mask; + const char *username, *reason; time_t unbanTime = NO_BAN_TIME; char buffer[MAX_WADPATH]; if (!I_ClearBans) return; + // We at least attempted loading bans.txt + bansLoaded = true; + f = fopen(va("%s"PATHSEP"%s", srb2home, "ban.txt"), "r"); if (!f) @@ -2166,16 +2193,26 @@ static void Ban_Load_File(boolean warning) I_ClearBans(); - for (i=0; fgets(buffer, (int)sizeof(buffer), f); i++) + for (i = 0; fgets(buffer, (int)sizeof(buffer), f); i++) { - address = strtok(buffer, " \t\r\n"); + address = strtok(buffer, "/\t\r\n"); mask = strtok(NULL, " \t\r\n"); - unbanTime = atoi(strtok(NULL, " \t\r\n")); - reason = strtok(NULL, "\r\n"); + + unbanTime = atoi(strtok(NULL, " \"\t\r\n")); + + username = strtok(NULL, "\"\t\r\n"); // go until next " + + strtok(NULL, "\"\t\r\n"); // remove first " + reason = strtok(NULL, "\"\r\n"); // go until next " I_SetBanAddress(address, mask); + if (I_SetUnbanTime) I_SetUnbanTime(unbanTime); + + if (I_SetBanUsername) + I_SetBanUsername(username); + if (I_SetBanReason) I_SetBanReason(reason); } @@ -2185,7 +2222,7 @@ static void Ban_Load_File(boolean warning) static void Command_ReloadBan(void) //recheck ban.txt { - Ban_Load_File(true); + D_LoadBan(true); } static void Command_connect(void) @@ -2546,31 +2583,60 @@ static void Command_Ban(void) static void Command_BanIP(void) { - if (COM_Argc() < 2) + size_t ac = COM_Argc(); + + if (ac < 2) { - CONS_Printf(M_GetText("banip : ban an ip address\n")); + CONS_Printf(M_GetText("banip []: ban an ip address\n")); return; } if (server) // Only the server can use this, otherwise does nothing. { - const char *address = (COM_Argv(1)); - const char *reason; + char *addressInput = Z_StrDup(COM_Argv(1)); - if (COM_Argc() == 2) - reason = NULL; - else + const char *address = NULL; + const char *mask = NULL; + + const char *reason = NULL; + + address = strtok(addressInput, "/"); + mask = strtok(NULL, ""); + + if (ac > 2) + { reason = COM_Argv(2); + } - if (I_SetBanAddress && I_SetBanAddress(address, NULL)) + if (I_SetBanAddress && I_SetBanAddress(address, mask)) { if (reason) - CONS_Printf("Banned IP address %s for: %s\n", address, reason); + { + CONS_Printf( + "Banned IP address %s%s for: %s\n", + address, + (mask && (strlen(mask) > 0)) ? va("/%s", mask) : "", + reason + ); + } else - CONS_Printf("Banned IP address %s\n", address); + { + CONS_Printf( + "Banned IP address %s%s\n", + address, + (mask && (strlen(mask) > 0)) ? va("/%s", mask) : "" + ); + } + + if (I_SetUnbanTime) + I_SetUnbanTime(NO_BAN_TIME); + + if (I_SetBanUsername) + I_SetBanUsername(NULL); if (I_SetBanReason) I_SetBanReason(reason); + D_SaveBan(); } else @@ -2728,6 +2794,9 @@ static void Got_KickCmd(UINT8 **p, INT32 playernum) } else { + if (I_SetBanUsername) + I_SetBanUsername(player_names[pnum]); + if (I_SetBanReason) I_SetBanReason(reason); @@ -3086,7 +3155,7 @@ void D_ClientServerInit(void) #ifdef DUMPCONSISTENCY CV_RegisterVar(&cv_dumpconsistency); #endif - Ban_Load_File(false); + D_LoadBan(false); #endif gametic = 0; diff --git a/src/d_net.c b/src/d_net.c index cf465a1f0..618bd002f 100644 --- a/src/d_net.c +++ b/src/d_net.c @@ -83,9 +83,11 @@ void (*I_ClearBans)(void) = NULL; const char *(*I_GetNodeAddress) (INT32 node) = NULL; const char *(*I_GetBanAddress) (size_t ban) = NULL; const char *(*I_GetBanMask) (size_t ban) = NULL; +const char *(*I_GetBanUsername) (size_t ban) = NULL; const char *(*I_GetBanReason) (size_t ban) = NULL; time_t (*I_GetUnbanTime) (size_t ban) = NULL; boolean (*I_SetBanAddress) (const char *address, const char *mask) = NULL; +boolean (*I_SetBanUsername) (const char *username) = NULL; boolean (*I_SetBanReason) (const char *reason) = NULL; boolean (*I_SetUnbanTime) (time_t timestamp) = NULL; boolean *bannednode = NULL; diff --git a/src/d_net.h b/src/d_net.h index 7f50706ee..9586cecb9 100644 --- a/src/d_net.h +++ b/src/d_net.h @@ -55,6 +55,7 @@ boolean HGetPacket(void); void D_SetDoomcom(void); #ifndef NONET void D_SaveBan(void); +void D_LoadBan(boolean warning); #endif boolean D_CheckNetGame(void); void D_CloseConnection(void); diff --git a/src/d_netcmd.c b/src/d_netcmd.c index 416f94ace..1675b29de 100644 --- a/src/d_netcmd.c +++ b/src/d_netcmd.c @@ -1117,6 +1117,15 @@ void D_RegisterClientCommands(void) * \sa CleanupPlayerName, SetPlayerName, Got_NameAndColor * \author Graue */ + +static boolean AllowedPlayerNameChar(char ch) +{ + if (!isprint(ch) || ch == ';' || ch == '"' || (UINT8)(ch) >= 0x80) + return false; + + return true; +} + boolean EnsurePlayerNameIsGood(char *name, INT32 playernum) { INT32 ix; @@ -1138,7 +1147,7 @@ boolean EnsurePlayerNameIsGood(char *name, INT32 playernum) // Also, anything over 0x80 is disallowed too, since compilers love to // differ on whether they're printable characters or not. for (ix = 0; name[ix] != '\0'; ix++) - if (!isprint(name[ix]) || name[ix] == ';' || (UINT8)(name[ix]) >= 0x80) + if (!AllowedPlayerNameChar(name[ix])) return false; // Check if a player is currently using the name, case-insensitively. @@ -1229,8 +1238,7 @@ void CleanupPlayerName(INT32 playernum, const char *newname) do { - /* from EnsurePlayerNameIsGood */ - if (!isprint(*p) || *p == ';' || (UINT8)*p >= 0x80) + if (!AllowedPlayerNameChar(*p)) break; } while (*++p) ; diff --git a/src/i_net.h b/src/i_net.h index 5b40001eb..92fe1bf2d 100644 --- a/src/i_net.h +++ b/src/i_net.h @@ -164,9 +164,11 @@ extern void (*I_ClearBans)(void); extern const char *(*I_GetNodeAddress) (INT32 node); extern const char *(*I_GetBanAddress) (size_t ban); extern const char *(*I_GetBanMask) (size_t ban); +extern const char *(*I_GetBanUsername) (size_t ban); extern const char *(*I_GetBanReason) (size_t ban); extern time_t (*I_GetUnbanTime) (size_t ban); extern boolean (*I_SetBanAddress) (const char *address,const char *mask); +extern boolean (*I_SetBanUsername) (const char *username); extern boolean (*I_SetBanReason) (const char *reason); extern boolean (*I_SetUnbanTime) (time_t timestamp); extern boolean *bannednode; diff --git a/src/i_tcp.c b/src/i_tcp.c index f5d5c610e..7b3db6f9d 100644 --- a/src/i_tcp.c +++ b/src/i_tcp.c @@ -192,6 +192,7 @@ { mysockaddr_t address; UINT8 mask; + char *username; char *reason; time_t timestamp; } banned_t; @@ -458,6 +459,18 @@ static const char *SOCK_GetBanMask(size_t ban) return NULL; } +static const char *SOCK_GetBanUsername(size_t ban) +{ +#ifdef NONET + (void)ban; + return NULL; +#else + if (ban >= numbans) + return NULL; + return banned[ban].username; +#endif +} + static const char *SOCK_GetBanReason(size_t ban) { #ifdef NONET @@ -1592,6 +1605,11 @@ static boolean SOCK_SetBanAddress(const char *address, const char *mask) banned[ban].mask = 128; #endif + // Set defaults, in case anything funny happens. + SOCK_SetBanUsername(NULL); + SOCK_SetBanReason(NULL); + SOCK_SetUnbanTime(NO_BAN_TIME); + runp = runp->ai_next; } @@ -1601,17 +1619,45 @@ static boolean SOCK_SetBanAddress(const char *address, const char *mask) #endif } +static boolean SOCK_SetBanUsername(const char *username) +{ +#ifdef NONET + (void)username; + return false; +#else + if (username == NULL || strlen(username) == 0) + { + username = "Direct IP ban"; + } + + if (banned[numbans - 1].username) + { + Z_Free(banned[numbans - 1].username); + banned[numbans - 1].username = NULL; + } + + banned[numbans - 1].username = Z_StrDup(username); + return true; +#endif +} + static boolean SOCK_SetBanReason(const char *reason) { #ifdef NONET (void)reason; return false; #else - if (!reason) + if (reason == NULL || strlen(reason) == 0) { reason = "No reason given"; } + if (banned[numbans - 1].reason) + { + Z_Free(banned[numbans - 1].reason); + banned[numbans - 1].reason = NULL; + } + banned[numbans - 1].reason = Z_StrDup(reason); return true; #endif @@ -1727,9 +1773,11 @@ boolean I_InitTcpNetwork(void) I_GetNodeAddress = SOCK_GetNodeAddress; I_GetBanAddress = SOCK_GetBanAddress; I_GetBanMask = SOCK_GetBanMask; + I_GetBanUsername = SOCK_GetBanUsername; I_GetBanReason = SOCK_GetBanReason; I_GetUnbanTime = SOCK_GetUnbanTime; I_SetBanAddress = SOCK_SetBanAddress; + I_SetBanUsername = SOCK_SetBanUsername; I_SetBanReason = SOCK_SetBanReason; I_SetUnbanTime = SOCK_SetUnbanTime; bannednode = SOCK_bannednode; From 281004bb85703cacbbe77422b263ba5d08f54d63 Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Fri, 10 Jun 2022 18:34:17 -0400 Subject: [PATCH 31/48] date-time todo comment --- src/d_clisrv.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/d_clisrv.c b/src/d_clisrv.c index 1803e5367..2bfad1b4b 100644 --- a/src/d_clisrv.c +++ b/src/d_clisrv.c @@ -2140,6 +2140,8 @@ void D_SaveBan(void) else fprintf(f, "%s/%s", address, mask); + // TODO: it'd be nice to convert this to an actual date-time, + // so it'd be easier to edit outside of the game. fprintf(f, " %ld", (long)unbanTime); username = NULL; From 2ec2873b3ed1aff4f27481593093cb6501c3da26 Mon Sep 17 00:00:00 2001 From: James R Date: Mon, 1 Aug 2022 07:46:35 -0700 Subject: [PATCH 32/48] Fix compiler warnings --- src/d_netcmd.c | 2 +- src/i_tcp.c | 110 ++++++++++++++++++++++++------------------------- 2 files changed, 56 insertions(+), 56 deletions(-) diff --git a/src/d_netcmd.c b/src/d_netcmd.c index 1675b29de..2a28a454b 100644 --- a/src/d_netcmd.c +++ b/src/d_netcmd.c @@ -5339,7 +5339,7 @@ static void Got_Automatecmd(UINT8 **cp, INT32 playernum) if (server || consoleplayer == playernum) { - if (command == NULL || strlen(command) == 0) + if (command[0] == '\0') { CONS_Printf( "Removed the %s automate command.\n", diff --git a/src/i_tcp.c b/src/i_tcp.c index 7b3db6f9d..46af0fdf1 100644 --- a/src/i_tcp.c +++ b/src/i_tcp.c @@ -1555,6 +1555,61 @@ static boolean SOCK_Ban(INT32 node) #endif } +static boolean SOCK_SetBanUsername(const char *username) +{ +#ifdef NONET + (void)username; + return false; +#else + if (username == NULL || strlen(username) == 0) + { + username = "Direct IP ban"; + } + + if (banned[numbans - 1].username) + { + Z_Free(banned[numbans - 1].username); + banned[numbans - 1].username = NULL; + } + + banned[numbans - 1].username = Z_StrDup(username); + return true; +#endif +} + +static boolean SOCK_SetBanReason(const char *reason) +{ +#ifdef NONET + (void)reason; + return false; +#else + if (reason == NULL || strlen(reason) == 0) + { + reason = "No reason given"; + } + + if (banned[numbans - 1].reason) + { + Z_Free(banned[numbans - 1].reason); + banned[numbans - 1].reason = NULL; + } + + banned[numbans - 1].reason = Z_StrDup(reason); + return true; +#endif +} + +static boolean SOCK_SetUnbanTime(time_t timestamp) +{ +#ifdef NONET + (void)reason; + return false; +#else + banned[numbans - 1].timestamp = timestamp; + return true; +#endif +} + static boolean SOCK_SetBanAddress(const char *address, const char *mask) { #ifdef NONET @@ -1619,61 +1674,6 @@ static boolean SOCK_SetBanAddress(const char *address, const char *mask) #endif } -static boolean SOCK_SetBanUsername(const char *username) -{ -#ifdef NONET - (void)username; - return false; -#else - if (username == NULL || strlen(username) == 0) - { - username = "Direct IP ban"; - } - - if (banned[numbans - 1].username) - { - Z_Free(banned[numbans - 1].username); - banned[numbans - 1].username = NULL; - } - - banned[numbans - 1].username = Z_StrDup(username); - return true; -#endif -} - -static boolean SOCK_SetBanReason(const char *reason) -{ -#ifdef NONET - (void)reason; - return false; -#else - if (reason == NULL || strlen(reason) == 0) - { - reason = "No reason given"; - } - - if (banned[numbans - 1].reason) - { - Z_Free(banned[numbans - 1].reason); - banned[numbans - 1].reason = NULL; - } - - banned[numbans - 1].reason = Z_StrDup(reason); - return true; -#endif -} - -static boolean SOCK_SetUnbanTime(time_t timestamp) -{ -#ifdef NONET - (void)reason; - return false; -#else - banned[numbans - 1].timestamp = timestamp; - return true; -#endif -} - static void SOCK_ClearBans(void) { numbans = 0; From 795aa4b73fd8c0027e3d0b3041adeedbac801276 Mon Sep 17 00:00:00 2001 From: James R Date: Fri, 19 Aug 2022 20:45:16 -0700 Subject: [PATCH 33/48] Ping counter: change patch name PINGF to PINGD # Conflicts: # src/config.h.in # src/hu_stuff.c --- src/hu_stuff.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/hu_stuff.c b/src/hu_stuff.c index 48179281a..ef45b0eae 100644 --- a/src/hu_stuff.c +++ b/src/hu_stuff.c @@ -194,7 +194,7 @@ void HU_LoadGraphics(void) HU_UpdatePatch(&mping[i], "MPING%d", i+1); } - HU_UpdatePatch(&pingmeasure[0], "PINGF"); + HU_UpdatePatch(&pingmeasure[0], "PINGD"); HU_UpdatePatch(&pingmeasure[1], "PINGMS"); // fps stuff From a477182ea7a2b9318e7b33f52f9f2c014ef705c7 Mon Sep 17 00:00:00 2001 From: James R Date: Fri, 19 Aug 2022 21:12:43 -0700 Subject: [PATCH 34/48] Draw PINGD before delay number --- src/hu_stuff.c | 8 ++++++-- src/v_video.c | 4 +++- src/v_video.h | 2 +- 3 files changed, 10 insertions(+), 4 deletions(-) diff --git a/src/hu_stuff.c b/src/hu_stuff.c index ef45b0eae..2dd718b46 100644 --- a/src/hu_stuff.c +++ b/src/hu_stuff.c @@ -2329,7 +2329,8 @@ void HU_drawPing(INT32 x, INT32 y, UINT32 lag, INT32 flags) gfxnum = Ping_gfx_num(lag); - V_DrawScaledPatch(x+11 - pingmeasure[measureid]->width, y+9, flags, pingmeasure[measureid]); + if (measureid == 1) + V_DrawScaledPatch(x+11 - pingmeasure[measureid]->width, y+9, flags, pingmeasure[measureid]); V_DrawScaledPatch(x+2, y, flags, pinggfx[gfxnum]); if (servermaxping && lag > servermaxping && hu_tick < 4) @@ -2343,7 +2344,10 @@ void HU_drawPing(INT32 x, INT32 y, UINT32 lag, INT32 flags) lag = (INT32)(lag * (1000.00f / TICRATE)); } - V_DrawPingNum(x+11 - pingmeasure[measureid]->width, y+9, flags, lag, colormap); + x = V_DrawPingNum(x + (measureid == 1 ? 11 - pingmeasure[measureid]->width : 10), y+9, flags, lag, colormap); + + if (measureid == 0) + V_DrawScaledPatch(x+1 - pingmeasure[measureid]->width, y+9, flags, pingmeasure[measureid]); } void diff --git a/src/v_video.c b/src/v_video.c index 7e01f3393..750b831a9 100644 --- a/src/v_video.c +++ b/src/v_video.c @@ -2247,7 +2247,7 @@ void V_DrawRightAlignedThinStringAtFixed(fixed_t x, fixed_t y, INT32 option, con // Draws a number using the PING font thingy. // TODO: Merge number drawing functions into one with "font name" selection. -void V_DrawPingNum(INT32 x, INT32 y, INT32 flags, INT32 num, const UINT8 *colormap) +INT32 V_DrawPingNum(INT32 x, INT32 y, INT32 flags, INT32 num, const UINT8 *colormap) { INT32 w = SHORT(fontv[PINGNUM_FONT].font[0]->width); // this SHOULD always be 5 but I guess custom graphics exist. @@ -2264,6 +2264,8 @@ void V_DrawPingNum(INT32 x, INT32 y, INT32 flags, INT32 num, const UINT8 *colorm V_DrawFixedPatch(x< Date: Tue, 14 Jun 2022 20:23:59 +0100 Subject: [PATCH 35/48] Core 1.4 specific changes. * Ensure it can compile. * Removing vanilla 2.2 properties that slipped in to previous commits. * Rearranging i_tcp.c to avoid implicit declaration. * Complete rename of `IsNameGood` to `EnsurePlayerNameIsGood`. * Add "BANFORMAT" header, for versioning support. * Add conversion from 1.3-and-earlier format to new system. * Don't ban the entire internet - convert zero-masks to the most specific ones. # Conflicts: # src/d_netcmd.c # src/i_tcp.c --- src/d_clisrv.c | 45 ++++++++++++++++++++++++++++++++++++--------- src/i_tcp.c | 26 ++++++++++++++++---------- 2 files changed, 52 insertions(+), 19 deletions(-) diff --git a/src/d_clisrv.c b/src/d_clisrv.c index 2bfad1b4b..8dc388350 100644 --- a/src/d_clisrv.c +++ b/src/d_clisrv.c @@ -189,7 +189,7 @@ consvar_t cv_playbackspeed = CVAR_INIT ("playbackspeed", "1", 0, playbackspeed_c 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_kicktime = {"kicktime", "10", CV_SAVE, CV_Unsigned, NULL, 0, NULL, NULL, 0, 0, NULL}; static inline void *G_DcpyTiccmd(void* dest, const ticcmd_t* src, const size_t n) { @@ -2090,6 +2090,10 @@ static void Command_ShowBan(void) //Print out ban list } static boolean bansLoaded = false; +// If you're a community contributor looking to improve how bans are written, please +// offer your changes back to our Git repository. Kart Krew reserve the right to +// utilise format numbers in use by community builds for different layouts. +#define BANFORMAT 1 void D_SaveBan(void) { @@ -2116,6 +2120,9 @@ void D_SaveBan(void) return; } + // Add header. + fprintf(f, "BANFORMAT %d\n", BANFORMAT); + for (i = 0; (address = I_GetBanAddress(i)) != NULL; i++) { if (I_GetUnbanTime) @@ -2177,6 +2184,7 @@ void D_LoadBan(boolean warning) const char *username, *reason; time_t unbanTime = NO_BAN_TIME; char buffer[MAX_WADPATH]; + boolean banmode = 0; if (!I_ClearBans) return; @@ -2197,15 +2205,35 @@ void D_LoadBan(boolean warning) for (i = 0; fgets(buffer, (int)sizeof(buffer), f); i++) { - address = strtok(buffer, "/\t\r\n"); + address = strtok(buffer, " /\t\r\n"); mask = strtok(NULL, " \t\r\n"); - unbanTime = atoi(strtok(NULL, " \"\t\r\n")); + if (i == 0 && !strncmp(address, "BANFORMAT", 9)) + { + banmode = atoi(mask); + continue; + } - username = strtok(NULL, "\"\t\r\n"); // go until next " + // One-way legacy format conversion -- the game will crash otherwise + if (banmode == 0) + { + unbanTime = NO_BAN_TIME; + username = NULL; // not guaranteed to be accurate, but only sane substitute + reason = strtok(NULL, "\r\n"); + if (reason[0] == 'N' && reason[1] == 'A' && reason[2] == '\0') + { + reason = NULL; + } + } + else + { + unbanTime = atoi(strtok(NULL, " \"\t\r\n")); - strtok(NULL, "\"\t\r\n"); // remove first " - reason = strtok(NULL, "\"\r\n"); // go until next " + username = strtok(NULL, "\"\t\r\n"); // go until next " + + strtok(NULL, "\"\t\r\n"); // remove first " + reason = strtok(NULL, "\"\r\n"); // go until next " + } I_SetBanAddress(address, mask); @@ -2222,6 +2250,8 @@ void D_LoadBan(boolean warning) fclose(f); } +#undef BANFORMAT + static void Command_ReloadBan(void) //recheck ban.txt { D_LoadBan(true); @@ -2912,9 +2942,6 @@ static void Got_KickCmd(UINT8 **p, INT32 playernum) #ifdef DUMPCONSISTENCY if (msg == KICK_MSG_CON_FAIL) SV_SavedGame(); #endif - - LUAh_GameQuit(false); - D_QuitNetGame(); CL_Reset(); D_StartTitle(); diff --git a/src/i_tcp.c b/src/i_tcp.c index 46af0fdf1..9d61c2c42 100644 --- a/src/i_tcp.c +++ b/src/i_tcp.c @@ -1638,27 +1638,33 @@ static boolean SOCK_SetBanAddress(const char *address, const char *mask) while (runp != NULL) { INT32 ban; + UINT8 numericalmask; ban = numbans; AddBannedIndex(); memcpy(&banned[ban].address, runp->ai_addr, runp->ai_addrlen); - if (mask) - banned[ban].mask = (UINT8)atoi(mask); #ifdef HAVE_IPV6 - else if (runp->ai_family == AF_INET6) + if (runp->ai_family == AF_INET6) banned[ban].mask = 128; -#endif else +#endif banned[ban].mask = 32; - if (banned[ban].mask > 32 && runp->ai_family == AF_INET) - banned[ban].mask = 32; -#ifdef HAVE_IPV6 - else if (banned[ban].mask > 128 && runp->ai_family == AF_INET6) - banned[ban].mask = 128; -#endif + if (mask) + { + numericalmask = (UINT8)atoi(mask); + } + else + { + numericalmask = 0; + } + + if (numericalmask > 0 && numericalmask < banned[ban].mask) + { + banned[ban].mask = numericalmask; + } // Set defaults, in case anything funny happens. SOCK_SetBanUsername(NULL); From e4e2550592856054e1144a972a95d036d5694120 Mon Sep 17 00:00:00 2001 From: toaster Date: Tue, 14 Jun 2022 21:42:41 +0100 Subject: [PATCH 36/48] Enforce MAX_REASONLENGTH when reading ban.txt. --- src/d_clisrv.c | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/src/d_clisrv.c b/src/d_clisrv.c index 8dc388350..0281e97b4 100644 --- a/src/d_clisrv.c +++ b/src/d_clisrv.c @@ -2179,9 +2179,9 @@ static void Command_ClearBans(void) void D_LoadBan(boolean warning) { FILE *f; - size_t i; - const char *address, *mask; - const char *username, *reason; + size_t i, j; + char *address, *mask; + char *username, *reason; time_t unbanTime = NO_BAN_TIME; char buffer[MAX_WADPATH]; boolean banmode = 0; @@ -2235,6 +2235,19 @@ void D_LoadBan(boolean warning) reason = strtok(NULL, "\"\r\n"); // go until next " } + // Enforce MAX_REASONLENGTH. + if (reason) + { + j = 0; + while (reason[j] != '\0') + { + if ((j++) < MAX_REASONLENGTH) + continue; + reason[j] = '\0'; + break; + } + } + I_SetBanAddress(address, mask); if (I_SetUnbanTime) From f728f164f5972e355498569bc948669f0c312f59 Mon Sep 17 00:00:00 2001 From: toaster Date: Tue, 14 Jun 2022 22:09:27 +0100 Subject: [PATCH 37/48] Create a single struct for bannednode and bannednodetimelft, and use the matching ban ID inside that struct. While this commit does not increase the visibility of ban reasons, it makes this possible later. --- src/d_clisrv.c | 6 +++--- src/d_net.c | 3 +-- src/i_net.h | 9 +++++++-- src/i_tcp.c | 20 +++++++++----------- 4 files changed, 20 insertions(+), 18 deletions(-) diff --git a/src/d_clisrv.c b/src/d_clisrv.c index 0281e97b4..a3a3b7cdd 100644 --- a/src/d_clisrv.c +++ b/src/d_clisrv.c @@ -3808,11 +3808,11 @@ static void HandleConnect(SINT8 node) // It's too much effort to legimately fix right now. Just prevent it from reaching that state. UINT8 maxplayers = min((dedicated ? MAXPLAYERS-1 : MAXPLAYERS), cv_maxplayers.value); - if (bannednode && bannednode[node]) + if (bannednode && bannednode[node].banid != SIZE_MAX) { - if (bannednodetimeleft && bannednodetimeleft[node] != NO_BAN_TIME) + if (bannednode[node].timeleft != NO_BAN_TIME) { - int minutes = bannednodetimeleft[node] / 60; + int minutes = bannednode[node].timeleft / 60; int hours = minutes / 60; if (hours) diff --git a/src/d_net.c b/src/d_net.c index 618bd002f..3c442c2c6 100644 --- a/src/d_net.c +++ b/src/d_net.c @@ -90,8 +90,7 @@ boolean (*I_SetBanAddress) (const char *address, const char *mask) = NULL; boolean (*I_SetBanUsername) (const char *username) = NULL; boolean (*I_SetBanReason) (const char *reason) = NULL; boolean (*I_SetUnbanTime) (time_t timestamp) = NULL; -boolean *bannednode = NULL; -time_t *bannednodetimeleft = NULL; +bannednode_t *bannednode = NULL; // network stats diff --git a/src/i_net.h b/src/i_net.h index 92fe1bf2d..b39b8d999 100644 --- a/src/i_net.h +++ b/src/i_net.h @@ -171,8 +171,13 @@ extern boolean (*I_SetBanAddress) (const char *address,const char *mask); extern boolean (*I_SetBanUsername) (const char *username); extern boolean (*I_SetBanReason) (const char *reason); extern boolean (*I_SetUnbanTime) (time_t timestamp); -extern boolean *bannednode; -extern time_t *bannednodetimeleft; + +typedef struct +{ + size_t banid; + time_t timeleft; +} bannednode_t; +extern bannednode_t *bannednode; /// \brief Called by D_SRB2Main to be defined by extern network driver boolean I_InitNetwork(void); diff --git a/src/i_tcp.c b/src/i_tcp.c index 9d61c2c42..2501cc5b9 100644 --- a/src/i_tcp.c +++ b/src/i_tcp.c @@ -212,8 +212,7 @@ static size_t numbans = 0; static size_t banned_size = 0; -static boolean SOCK_bannednode[MAXNETNODES+1]; /// \note do we really need the +1? -static time_t SOCK_bannednodetimeleft[MAXNETNODES+1]; +static bannednode_t SOCK_bannednode[MAXNETNODES+1]; /// \note do we really need the +1? static boolean init_tcp_driver = false; static const char *serverport_name = DEFAULTPORT; @@ -686,21 +685,21 @@ static boolean SOCK_Get(void) { if (curTime >= banned[i].timestamp) { - SOCK_bannednodetimeleft[j] = NO_BAN_TIME; - SOCK_bannednode[j] = false; + SOCK_bannednode[j].timeleft = NO_BAN_TIME; + SOCK_bannednode[j].banid = SIZE_MAX; DEBFILE("This dude was banned, but enough time has passed\n"); break; } - SOCK_bannednodetimeleft[j] = banned[i].timestamp - curTime; - SOCK_bannednode[j] = true; + SOCK_bannednode[j].timeleft = banned[i].timestamp - curTime; + SOCK_bannednode[j].banid = i; DEBFILE("This dude has been temporarily banned\n"); break; } else { - SOCK_bannednodetimeleft[j] = NO_BAN_TIME; - SOCK_bannednode[j] = true; + SOCK_bannednode[j].timeleft = NO_BAN_TIME; + SOCK_bannednode[j].banid = i; DEBFILE("This dude has been banned\n"); break; } @@ -709,8 +708,8 @@ static boolean SOCK_Get(void) if (i == numbans) { - SOCK_bannednodetimeleft[j] = NO_BAN_TIME; - SOCK_bannednode[j] = false; + SOCK_bannednode[j].timeleft = NO_BAN_TIME; + SOCK_bannednode[j].banid = SIZE_MAX; } return true; @@ -1787,7 +1786,6 @@ boolean I_InitTcpNetwork(void) I_SetBanReason = SOCK_SetBanReason; I_SetUnbanTime = SOCK_SetUnbanTime; bannednode = SOCK_bannednode; - bannednodetimeleft = SOCK_bannednodetimeleft; return ret; } From a798c43028690a444f34b74657f6061807be76e2 Mon Sep 17 00:00:00 2001 From: toaster Date: Wed, 15 Jun 2022 16:56:22 +0100 Subject: [PATCH 38/48] First commit with actual human testing involved. * Fix some bugs. * Reset bannode information properly, fixing being unable to join your own server. * Write to the buffer before saving the kick/ban reason, rather than after. * Improve the print output for the `showbanlist` command. * Includes username. * Includes remaining time as seen by a kicked joiner. * Hides expired bans. * Improve the messages for ban/kick related refused joins. * Replace the Reason with the actual admin-provided reason for refused connection. * Replace the "Server refuses connection" header with "You have been [banned/temporarily kicked] from the server", the previous given Reason. * Fudge the time reported for temporary kicks so that a user is encouraged to return slightly after their tempkick ends, rather than before. * Add an extra newline to the M_StartMessage for being kicked/banned with a reason provided. # Conflicts: # src/d_clisrv.c --- src/d_clisrv.c | 104 ++++++++++++++++++++++++++++++++++++------------- 1 file changed, 77 insertions(+), 27 deletions(-) diff --git a/src/d_clisrv.c b/src/d_clisrv.c index a3a3b7cdd..68cf427e6 100644 --- a/src/d_clisrv.c +++ b/src/d_clisrv.c @@ -2058,7 +2058,9 @@ static void CL_ConnectToServer(void) static void Command_ShowBan(void) //Print out ban list { size_t i; - const char *address, *mask, *reason; + const char *address, *mask, *reason, *username; + time_t unbanTime = NO_BAN_TIME; + const time_t curTime = time(NULL); if (I_GetBanAddress) CONS_Printf(M_GetText("Ban List:\n")); @@ -2067,22 +2069,43 @@ static void Command_ShowBan(void) //Print out ban list for (i = 0; (address = I_GetBanAddress(i)) != NULL; i++) { - if (!I_GetBanMask || (mask = I_GetBanMask(i)) == NULL) - CONS_Printf("%s: %s ", sizeu1(i+1), address); - else - CONS_Printf("%s: %s/%s ", sizeu1(i+1), address, mask); + unbanTime = NO_BAN_TIME; + if (I_GetUnbanTime) + unbanTime = I_GetUnbanTime(i); - if (I_GetUnbanTime && I_GetUnbanTime(i) != NO_BAN_TIME) - { - // todo: maybe try to actually print out the time remaining, - // and/or the datetime of the unbanning? - CONS_Printf("(temporary) "); - } + if (unbanTime != NO_BAN_TIME && curTime >= unbanTime) + continue; + + CONS_Printf("%s: ", sizeu1(i+1)); + + if (I_GetBanUsername && (username = I_GetBanUsername(i)) != NULL) + CONS_Printf("%s - ", username); + + if (!I_GetBanMask || (mask = I_GetBanMask(i)) == NULL) + CONS_Printf("%s", address); + else + CONS_Printf("%s/%s", address, mask); if (I_GetBanReason && (reason = I_GetBanReason(i)) != NULL) - CONS_Printf("(%s)\n", reason); - else - CONS_Printf("\n"); + CONS_Printf(" - %s", reason); + + if (unbanTime != NO_BAN_TIME) + { + // these are fudged a little to match what a joiner sees + int minutes = ((unbanTime - curTime) + 30) / 60; + int hours = (minutes + 1) / 60; + int days = (hours + 1) / 24; + if (days) + CONS_Printf(" (%d day%s)", days, days > 1 ? "s" : ""); + else if (hours) + CONS_Printf(" (%d hour%s)", hours, hours > 1 ? "s" : ""); + else if (minutes) + CONS_Printf(" (%d minute%s)", minutes, minutes > 1 ? "s" : ""); + else + CONS_Printf(" (<1 minute)"); + } + + CONS_Printf("\n"); } if (i == 0 && !address) @@ -2818,6 +2841,11 @@ static void Got_KickCmd(UINT8 **p, INT32 playernum) msg = KICK_MSG_CON_FAIL; } + if (msg == KICK_MSG_CUSTOM_BAN || msg == KICK_MSG_CUSTOM_KICK) + { + READSTRINGN(*p, reason, MAX_REASONLENGTH+1); + } + //CONS_Printf("\x82%s ", player_names[pnum]); // Save bans here. Used to be split between here and the actual command, depending on @@ -2924,12 +2952,10 @@ static void Got_KickCmd(UINT8 **p, INT32 playernum) kickreason = KR_BAN; break; case KICK_MSG_CUSTOM_KICK: - READSTRINGN(*p, reason, MAX_REASONLENGTH+1); HU_AddChatText(va("\x82*%s has been kicked (%s)", player_names[pnum], reason), false); kickreason = KR_KICK; break; case KICK_MSG_CUSTOM_BAN: - READSTRINGN(*p, reason, MAX_REASONLENGTH+1); HU_AddChatText(va("\x82*%s has been banned (%s)", player_names[pnum], reason), false); kickreason = KR_BAN; break; @@ -2966,9 +2992,9 @@ static void Got_KickCmd(UINT8 **p, INT32 playernum) else if (msg == KICK_MSG_BANNED) M_StartMessage(M_GetText("You have been banned by the server\n\nPress ESC\n"), NULL, MM_NOTHING); else if (msg == KICK_MSG_CUSTOM_KICK) - M_StartMessage(va(M_GetText("You have been kicked\n(%s)\nPress ESC\n"), reason), NULL, MM_NOTHING); + M_StartMessage(va(M_GetText("You have been kicked\n(%s)\n\nPress ESC\n"), reason), NULL, MM_NOTHING); else if (msg == KICK_MSG_CUSTOM_BAN) - M_StartMessage(va(M_GetText("You have been banned\n(%s)\nPress ESC\n"), reason), NULL, MM_NOTHING); + M_StartMessage(va(M_GetText("You have been banned\n(%s)\n\nPress ESC\n"), reason), NULL, MM_NOTHING); else M_StartMessage(M_GetText("You have been kicked by the server\n\nPress ESC\n"), NULL, MM_NOTHING); } @@ -3227,6 +3253,9 @@ static void ResetNode(INT32 node) sendingsavegame[node] = false; resendingsavegame[node] = false; savegameresendcooldown[node] = 0; + + bannednode[node].banid = SIZE_MAX; + bannednode[node].timeleft = NO_BAN_TIME; } void SV_ResetServer(void) @@ -3810,27 +3839,39 @@ static void HandleConnect(SINT8 node) if (bannednode && bannednode[node].banid != SIZE_MAX) { + const char *reason = NULL; + + // Get the reason... + if (!I_GetBanReason || (reason = I_GetBanReason(bannednode[node].banid)) == NULL) + reason = "No reason given"; + if (bannednode[node].timeleft != NO_BAN_TIME) { - int minutes = bannednode[node].timeleft / 60; - int hours = minutes / 60; + // these are fudged a little to allow it to sink in for impatient rejoiners + int minutes = (bannednode[node].timeleft + 30) / 60; + int hours = (minutes + 1) / 60; + int days = (hours + 1) / 24; - if (hours) + if (days) { - SV_SendRefuse(node, va(M_GetText("You have been temporarily\nkicked from the server.\n(Time remaining: %d hour%s)"), hours, hours > 1 ? "s" : "")); + SV_SendRefuse(node, va("K|%s\n(Time remaining: %d day%s)", reason, days, days > 1 ? "s" : "")); + } + else if (hours) + { + SV_SendRefuse(node, va("K|%s\n(Time remaining: %d hour%s)", reason, hours, hours > 1 ? "s" : "")); } else if (minutes) { - SV_SendRefuse(node, va(M_GetText("You have been temporarily\nkicked from the server.\n(Time remaining: %d minute%s)"), minutes, minutes > 1 ? "s" : "")); + SV_SendRefuse(node, va("K|%s\n(Time remaining: %d minute%s)", reason, minutes, minutes > 1 ? "s" : "")); } else { - SV_SendRefuse(node, M_GetText("You have been temporarily\nkicked from the server.\n(Time remaining: <1 minute)")); + SV_SendRefuse(node, va("K|%s\n(Time remaining: <1 minute)", reason)); } } else { - SV_SendRefuse(node, M_GetText("You have been banned\nfrom the server.")); + SV_SendRefuse(node, va("B|%s", reason)); } } else if (netbuffer->u.clientcfg._255 != 255 || @@ -4154,8 +4195,17 @@ static void HandlePacketFromAwayNode(SINT8 node) CL_Reset(); D_StartTitle(); - M_StartMessage(va(M_GetText("Server refuses connection\n\nReason:\n%s"), - reason), NULL, MM_NOTHING); + if (reason[1] == '|') + { + M_StartMessage(va("You have been %sfrom the server\n\nReason:\n%s", + (reason[0] == 'B') ? "banned\n" : "temporarily\nkicked ", + reason+2), NULL, MM_NOTHING); + } + else + { + M_StartMessage(va(M_GetText("Server refuses connection\n\nReason:\n%s"), + reason), NULL, MM_NOTHING); + } free(reason); From 34cb5192a738f0d0ecf2efc902b068890bd6db14 Mon Sep 17 00:00:00 2001 From: toaster Date: Wed, 15 Jun 2022 17:00:09 +0100 Subject: [PATCH 39/48] Fix an issue where if the last line of an M_StartMessage was the longest, the box width wouldn't account for it. --- src/m_menu.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/m_menu.c b/src/m_menu.c index d88435680..ea456a301 100644 --- a/src/m_menu.c +++ b/src/m_menu.c @@ -4591,7 +4591,11 @@ void M_StartMessage(const char *string, void *routine, } if (i == strlen(message+start)) + { start += i; + if (i > max) + max = i; + } } MessageDef.x = (INT16)((BASEVIDWIDTH - 8*max-16)/2); From 1667b050b20b1f8fb6623b9ac53bf3da0de57a2b Mon Sep 17 00:00:00 2001 From: toaster Date: Wed, 15 Jun 2022 22:34:05 +0100 Subject: [PATCH 40/48] Complete BANFORMAT header implementation. * Warn the user when an incompatible ban.txt is being loaded, and stop early. * Don't inexplicably assign as a boolean, you bafooligan! --- src/d_clisrv.c | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/src/d_clisrv.c b/src/d_clisrv.c index 68cf427e6..4c7e29c46 100644 --- a/src/d_clisrv.c +++ b/src/d_clisrv.c @@ -2207,7 +2207,7 @@ void D_LoadBan(boolean warning) char *username, *reason; time_t unbanTime = NO_BAN_TIME; char buffer[MAX_WADPATH]; - boolean banmode = 0; + UINT8 banmode = 0; if (!I_ClearBans) return; @@ -2234,6 +2234,18 @@ void D_LoadBan(boolean warning) if (i == 0 && !strncmp(address, "BANFORMAT", 9)) { banmode = atoi(mask); + switch (banmode) + { + case BANFORMAT: // currently supported format + //case 0: -- permitted only when BANFORMAT string not present + break; + default: + { + fclose(f); + CONS_Alert(CONS_WARNING, "Could not load unknown ban.txt for ban list (BANFORMAT %d, expected %d)\n", banmode, BANFORMAT); + return; + } + } continue; } From 6e26aff1e3ab91a329d4d6fc473f9f67563cb943 Mon Sep 17 00:00:00 2001 From: toaster Date: Sun, 3 Jul 2022 22:01:51 +0100 Subject: [PATCH 41/48] Free the banned struct on SOCK_ClearBans. --- src/i_tcp.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/i_tcp.c b/src/i_tcp.c index 2501cc5b9..e7286686f 100644 --- a/src/i_tcp.c +++ b/src/i_tcp.c @@ -1683,6 +1683,7 @@ static void SOCK_ClearBans(void) { numbans = 0; banned_size = 0; + Z_Free(banned); banned = NULL; } From a59e28f33988f0f6b0513919c5d0bd5a6a2d4248 Mon Sep 17 00:00:00 2001 From: toaster Date: Mon, 4 Jul 2022 14:00:12 +0100 Subject: [PATCH 42/48] Catch several ways ban.txt could be malformed by a well-meaning server host, and report it back via the log. --- src/d_clisrv.c | 43 +++++++++++++++++++++++++++++++++++++------ 1 file changed, 37 insertions(+), 6 deletions(-) diff --git a/src/d_clisrv.c b/src/d_clisrv.c index 4c7e29c46..0cc586ede 100644 --- a/src/d_clisrv.c +++ b/src/d_clisrv.c @@ -2208,6 +2208,7 @@ void D_LoadBan(boolean warning) time_t unbanTime = NO_BAN_TIME; char buffer[MAX_WADPATH]; UINT8 banmode = 0; + boolean malformed = false; if (!I_ClearBans) return; @@ -2233,7 +2234,10 @@ void D_LoadBan(boolean warning) if (i == 0 && !strncmp(address, "BANFORMAT", 9)) { - banmode = atoi(mask); + if (mask) + { + banmode = atoi(mask); + } switch (banmode) { case BANFORMAT: // currently supported format @@ -2242,32 +2246,56 @@ void D_LoadBan(boolean warning) default: { fclose(f); - CONS_Alert(CONS_WARNING, "Could not load unknown ban.txt for ban list (BANFORMAT %d, expected %d)\n", banmode, BANFORMAT); + CONS_Alert(CONS_WARNING, "Could not load unknown ban.txt for ban list (BANFORMAT %s, expected %d)\n", mask, BANFORMAT); return; } } continue; } + if (I_SetBanAddress(address, mask) == false) // invalid IP input? + { + CONS_Alert(CONS_WARNING, "\"%s/%s\" is not a valid IP address, discarding...\n", address, mask); + continue; + } + // One-way legacy format conversion -- the game will crash otherwise if (banmode == 0) { unbanTime = NO_BAN_TIME; username = NULL; // not guaranteed to be accurate, but only sane substitute reason = strtok(NULL, "\r\n"); - if (reason[0] == 'N' && reason[1] == 'A' && reason[2] == '\0') + if (reason && reason[0] == 'N' && reason[1] == 'A' && reason[2] == '\0') { reason = NULL; } } else { - unbanTime = atoi(strtok(NULL, " \"\t\r\n")); + reason = strtok(NULL, " \"\t\r\n"); + if (reason) + { + unbanTime = atoi(reason); + reason = NULL; + } + else + { + unbanTime = NO_BAN_TIME; + malformed = true; + } username = strtok(NULL, "\"\t\r\n"); // go until next " + if (!username) + { + malformed = true; + } strtok(NULL, "\"\t\r\n"); // remove first " reason = strtok(NULL, "\"\r\n"); // go until next " + if (!reason) + { + malformed = true; + } } // Enforce MAX_REASONLENGTH. @@ -2283,8 +2311,6 @@ void D_LoadBan(boolean warning) } } - I_SetBanAddress(address, mask); - if (I_SetUnbanTime) I_SetUnbanTime(unbanTime); @@ -2295,6 +2321,11 @@ void D_LoadBan(boolean warning) I_SetBanReason(reason); } + if (malformed) + { + CONS_Alert(CONS_WARNING, "One or more lines of ban.txt are malformed. The game can correct for this, but some data may be lost.\n"); + } + fclose(f); } From 48a1f7f94dda82004c5f7ef181788d511921872f Mon Sep 17 00:00:00 2001 From: toaster Date: Wed, 24 Aug 2022 22:17:29 +0100 Subject: [PATCH 43/48] Revert the reversion of 2.2 information. - Corrects the definition for cv_kicktime. - Moves its registering to the correct place for being saved to the config. - Restore LUAh_GameQuit. --- src/d_clisrv.c | 6 ++++-- src/d_netcmd.c | 2 +- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/src/d_clisrv.c b/src/d_clisrv.c index 0cc586ede..cffd2e25e 100644 --- a/src/d_clisrv.c +++ b/src/d_clisrv.c @@ -189,7 +189,7 @@ consvar_t cv_playbackspeed = CVAR_INIT ("playbackspeed", "1", 0, playbackspeed_c consvar_t cv_httpsource = CVAR_INIT ("http_source", "", CV_SAVE, NULL, NULL); -consvar_t cv_kicktime = {"kicktime", "10", CV_SAVE, CV_Unsigned, NULL, 0, NULL, NULL, 0, 0, NULL}; +consvar_t cv_kicktime = CVAR_INIT ("kicktime", "10", CV_SAVE, CV_Unsigned, NULL); static inline void *G_DcpyTiccmd(void* dest, const ticcmd_t* src, const size_t n) { @@ -3024,6 +3024,9 @@ static void Got_KickCmd(UINT8 **p, INT32 playernum) #ifdef DUMPCONSISTENCY if (msg == KICK_MSG_CON_FAIL) SV_SavedGame(); #endif + + LUAh_GameQuit(false); + D_QuitNetGame(); CL_Reset(); D_StartTitle(); @@ -3236,7 +3239,6 @@ void D_ClientServerInit(void) #ifndef NONET COM_AddCommand("getplayernum", Command_GetPlayerNum); COM_AddCommand("kick", Command_Kick); - CV_RegisterVar(&cv_kicktime); COM_AddCommand("ban", Command_Ban); COM_AddCommand("banip", Command_BanIP); COM_AddCommand("clearbans", Command_ClearBans); diff --git a/src/d_netcmd.c b/src/d_netcmd.c index 2a28a454b..c36922275 100644 --- a/src/d_netcmd.c +++ b/src/d_netcmd.c @@ -789,7 +789,7 @@ void D_RegisterServerCommands(void) COM_AddCommand("ping", Command_Ping_f); CV_RegisterVar(&cv_nettimeout); CV_RegisterVar(&cv_jointimeout); - + CV_RegisterVar(&cv_kicktime); CV_RegisterVar(&cv_skipmapcheck); CV_RegisterVar(&cv_sleep); CV_RegisterVar(&cv_maxping); From 37f8b0e7175c6c92e695a362d43a8a09bfc69d0f Mon Sep 17 00:00:00 2001 From: toaster Date: Wed, 24 Aug 2022 22:28:02 +0100 Subject: [PATCH 44/48] NUMSFX > sfxfree for PlaySound --- src/s_sound.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/s_sound.c b/src/s_sound.c index 73607cdbc..a76d7c302 100644 --- a/src/s_sound.c +++ b/src/s_sound.c @@ -2501,7 +2501,7 @@ static void Command_PlaySound(void) } else { - for (sfx = 0; sfx < NUMSFX; sfx++) + for (sfx = 0; sfx < sfxfree; sfx++) { if (S_sfx[sfx].name && fasticmp(sound, S_sfx[sfx].name)) break; From 7a7cab67b1967ba5ea8ce506b44dacc21c710030 Mon Sep 17 00:00:00 2001 From: toaster Date: Wed, 24 Aug 2022 22:38:02 +0100 Subject: [PATCH 45/48] Don't engage in server automation in a restricted context. --- src/d_netcmd.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/d_netcmd.c b/src/d_netcmd.c index c36922275..0a38ed2b6 100644 --- a/src/d_netcmd.c +++ b/src/d_netcmd.c @@ -3918,6 +3918,12 @@ void Schedule_Run(void) return; } + if (K_CanChangeRules() == false) + { + // Don't engage in automation while in a restricted context. + return; + } + for (i = 0; i < schedule_len; i++) { scheduleTask_t *task = schedule[i]; @@ -4048,6 +4054,12 @@ void Automate_Run(automateEvents_t type) return; } + if (K_CanChangeRules() == false) + { + // Don't engage in automation while in a restricted context. + return; + } + #ifdef PARANOIA if (type >= AEV__MAX) { From 92cfb5a677346b8b88a3fa1b9a03fe8c2f4dc583 Mon Sep 17 00:00:00 2001 From: VelocitOni Date: Tue, 13 Sep 2022 19:17:39 -0400 Subject: [PATCH 46/48] Just bringing it up to date w/ master Got it to compile after some cleaning. --- src/d_netcmd.c | 2 +- src/g_game.c | 1 - src/v_video.c | 24 ++---------------------- src/y_inter.c | 7 ------- 4 files changed, 3 insertions(+), 31 deletions(-) diff --git a/src/d_netcmd.c b/src/d_netcmd.c index 0e58998fa..eb79b950f 100644 --- a/src/d_netcmd.c +++ b/src/d_netcmd.c @@ -3076,7 +3076,7 @@ static void Command_RandomMap(void) } else { - newgametype = cv_newgametype.value; + newgametype = cv_dummygametype.value; // Changed from cv_newgametype to match newmenus newencoremode = false; newresetplayers = true; oldmapnum = -1; diff --git a/src/g_game.c b/src/g_game.c index 3121ab1d6..4d98256f1 100644 --- a/src/g_game.c +++ b/src/g_game.c @@ -1285,7 +1285,6 @@ static void weaponPrefChange4(void) // void G_DoLoadLevel(boolean resetplayer) { - INT32 i, j; boolean doAutomate = false; INT32 i; diff --git a/src/v_video.c b/src/v_video.c index bf93996bc..193339574 100644 --- a/src/v_video.c +++ b/src/v_video.c @@ -2582,6 +2582,8 @@ INT32 V_DrawPingNum(INT32 x, INT32 y, INT32 flags, INT32 num, const UINT8 *color } while (num); return x; +} + void V_DrawCenteredKartString(INT32 x, INT32 y, INT32 option, const char *string) { x -= V_KartStringWidth(string, option)/2; @@ -2705,28 +2707,6 @@ void V_DrawProfileNum(INT32 x, INT32 y, INT32 flags, UINT8 num) } while (--digits); } -// Draws a number using the PING font thingy. -// TODO: Merge number drawing functions into one with "font name" selection. - -void V_DrawPingNum(INT32 x, INT32 y, INT32 flags, INT32 num, const UINT8 *colormap) -{ - INT32 w = SHORT(fontv[PINGNUM_FONT].font[0]->width); // this SHOULD always be 5 but I guess custom graphics exist. - - if (flags & V_NOSCALESTART) - w *= vid.dupx; - - if (num < 0) - num = -num; - - // draw the number - do - { - x -= (w-1); // Oni wanted their outline to intersect. - V_DrawFixedPatch(x< Date: Tue, 13 Sep 2022 20:57:54 -0400 Subject: [PATCH 47/48] Fix sound order --- src/sounds.h | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/sounds.h b/src/sounds.h index 1a27e4a59..e7248a369 100644 --- a/src/sounds.h +++ b/src/sounds.h @@ -1176,11 +1176,12 @@ typedef enum // SRB2Kart - Powerup clash SFX sfx_parry, - // Shout message sound effect - sfx_sysmsg, // Fast fall bounce sfx_ffbonc, + // Shout message sound effect + sfx_sysmsg, + // Next up: UNIQUE ENGINE SOUNDS! Hoooooo boy... // Engine class A - Low Speed, Low Weight sfx_krta00, From ec29a978ae4fe6d904ce7c44faeaeb2685dbc043 Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Tue, 13 Sep 2022 21:23:04 -0400 Subject: [PATCH 48/48] Funny --- src/d_clisrv.c | 5 +++++ src/d_netcmd.c | 28 ++++++++++++++++++++++++++++ src/d_netcmd.h | 4 ++++ 3 files changed, 37 insertions(+) diff --git a/src/d_clisrv.c b/src/d_clisrv.c index ebbd97dac..a7193327d 100644 --- a/src/d_clisrv.c +++ b/src/d_clisrv.c @@ -5534,6 +5534,11 @@ boolean TryRunTics(tic_t realtics) if (gametic % TICRATE == 0) { Schedule_Run(); + + if (cv_livestudioaudience.value) + { + LiveStudioAudience(); + } } ExtraDataTicker(); diff --git a/src/d_netcmd.c b/src/d_netcmd.c index eb79b950f..62bc4ee29 100644 --- a/src/d_netcmd.c +++ b/src/d_netcmd.c @@ -151,6 +151,7 @@ static void KartComeback_OnChange(void); static void KartEliminateLast_OnChange(void); static void Schedule_OnChange(void); +static void LiveStudioAudience_OnChange(void); #ifdef NETGAME_DEVMODE static void Fishcake_OnChange(void); @@ -567,6 +568,12 @@ consvar_t cv_schedule = CVAR_INIT ("schedule", "On", CV_NETVAR|CV_CALL, CV_OnOff consvar_t cv_automate = CVAR_INIT ("automate", "On", CV_NETVAR, CV_OnOff, NULL); +#ifdef DEVELOP +consvar_t cv_livestudioaudience = CVAR_INIT ("livestudioaudience", "On", CV_NETVAR|CV_CALL, CV_OnOff, LiveStudioAudience_OnChange); +#else +consvar_t cv_livestudioaudience = CVAR_INIT ("livestudioaudience", "Off", CV_NETVAR|CV_CALL, CV_OnOff, LiveStudioAudience_OnChange); +#endif + char timedemo_name[256]; boolean timedemo_csv; char timedemo_csv_id[256]; @@ -597,6 +604,8 @@ const char *automate_names[AEV__MAX] = "VoteStart" // AEV_VOTESTART }; +static UINT32 livestudioaudience_timer = 90; + /// \warning Keep this up-to-date if you add/remove/rename net text commands const char *netxcmdnames[MAXNETXCMD - 1] = { @@ -828,6 +837,7 @@ void D_RegisterServerCommands(void) CV_RegisterVar(&cv_schedule); CV_RegisterVar(&cv_automate); + CV_RegisterVar(&cv_livestudioaudience); CV_RegisterVar(&cv_dummyconsvar); @@ -4135,6 +4145,19 @@ void Automate_Clear(void) } } +void LiveStudioAudience(void) +{ + if (livestudioaudience_timer == 0) + { + S_StartSound(NULL, sfx_mbv91); + livestudioaudience_timer = 90; + } + else + { + livestudioaudience_timer--; + } +} + static void Command_MotD_f(void) { size_t i, j; @@ -6457,6 +6480,11 @@ static void Schedule_OnChange(void) } } +static void LiveStudioAudience_OnChange(void) +{ + livestudioaudience_timer = 90; +} + void Got_DiscordInfo(UINT8 **p, INT32 playernum) { if (playernum != serverplayer /*&& !IsPlayerAdmin(playernum)*/) diff --git a/src/d_netcmd.h b/src/d_netcmd.h index 9914380ff..7125e3b27 100644 --- a/src/d_netcmd.h +++ b/src/d_netcmd.h @@ -131,6 +131,8 @@ extern consvar_t cv_director; extern consvar_t cv_schedule; +extern consvar_t cv_livestudioaudience; + extern char timedemo_name[256]; extern boolean timedemo_csv; extern char timedemo_csv_id[256]; @@ -274,6 +276,8 @@ void Automate_Run(automateEvents_t type); void Automate_Set(automateEvents_t type, const char *command); void Automate_Clear(void); +void LiveStudioAudience(void); + // used for the player setup menu UINT8 CanChangeSkin(INT32 playernum);