From 512342bf7c4f3f1362f6b44bcf6b1b3193bc2c0d Mon Sep 17 00:00:00 2001 From: SteelT Date: Thu, 9 Dec 2021 01:32:08 -0500 Subject: [PATCH 001/114] SDL: Support setting vsync at runtime Allows vid_wait to work under software without having to switch to OpenGL and then back Yeah, support on windows will require a newer version of SDL. I'm just pushing this so the code is already there. --- src/sdl/i_video.c | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/src/sdl/i_video.c b/src/sdl/i_video.c index c04eab8bc..4ce2993e8 100644 --- a/src/sdl/i_video.c +++ b/src/sdl/i_video.c @@ -106,8 +106,10 @@ rendermode_t chosenrendermode = render_none; // set by command line arguments boolean highcolor = false; +static void Impl_SetVsync(void); + // synchronize page flipping with screen refresh -consvar_t cv_vidwait = CVAR_INIT ("vid_wait", "On", CV_SAVE, CV_OnOff, NULL); +consvar_t cv_vidwait = CVAR_INIT ("vid_wait", "On", CV_SAVE|CV_CALL|CV_NOINIT, CV_OnOff, Impl_SetVsync); static consvar_t cv_stretch = CVAR_INIT ("stretch", "Off", CV_SAVE|CV_NOSHOWHELP, CV_OnOff, NULL); static consvar_t cv_alwaysgrabmouse = CVAR_INIT ("alwaysgrabmouse", "Off", CV_SAVE, CV_OnOff, NULL); @@ -2061,3 +2063,11 @@ void I_ShutdownGraphics(void) framebuffer = SDL_FALSE; } #endif + +static void Impl_SetVsync(void) +{ +#if SDL_VERSION_ATLEAST(2,0,18) + if (renderer) + SDL_RenderSetVSync(renderer, cv_vidwait.value); +#endif +} \ No newline at end of file From 6252bb113d1b2f335ce4e492a3cca3c0941fa730 Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Sun, 5 Jun 2022 07:50:36 -0400 Subject: [PATCH 002/114] 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 003/114] 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 004/114] 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 005/114] 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 006/114] 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 007/114] 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 008/114] 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 009/114] - 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 010/114] 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 011/114] 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 012/114] 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 013/114] 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 014/114] 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 015/114] 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 016/114] 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 017/114] 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 018/114] 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 019/114] 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 020/114] 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 021/114] 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 022/114] 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 023/114] 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 024/114] 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 025/114] - 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 026/114] 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 027/114] 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 028/114] 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 029/114] 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 030/114] 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 031/114] 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 032/114] 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 033/114] 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 034/114] 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 035/114] 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 036/114] 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 037/114] 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 038/114] 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 039/114] 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 040/114] 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 041/114] 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 042/114] 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 043/114] 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 044/114] 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 045/114] 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 046/114] 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 7edec43d726f7fa96cdba5c0e6957f314cce9c3f Mon Sep 17 00:00:00 2001 From: toaster Date: Sun, 28 Aug 2022 23:15:45 +0100 Subject: [PATCH 047/114] Port Gametype Preference and associated changes Notably, makes the PWR-based Encore scrambling component of G_SometimesGetDifferentGametype WAY less spaghettified. --- src/d_clisrv.c | 5 ++- src/d_netcmd.c | 41 ++++++++++++++++++------ src/d_netcmd.h | 1 + src/g_game.c | 86 ++++++++++++++++++++++++++++---------------------- src/g_game.h | 3 +- src/k_kart.c | 1 + src/y_inter.c | 9 +++--- 7 files changed, 93 insertions(+), 53 deletions(-) diff --git a/src/d_clisrv.c b/src/d_clisrv.c index 19c45d82b..4507652d5 100644 --- a/src/d_clisrv.c +++ b/src/d_clisrv.c @@ -890,6 +890,9 @@ static void SV_SendServerInfo(INT32 node, tic_t servertime) UINT8 *p; size_t mirror_length; const char *httpurl = cv_httpsource.string; + UINT8 prefgametype = (cv_kartgametypepreference.value == -1) + ? gametype + : cv_kartgametypepreference.value; netbuffer->packettype = PT_SERVERINFO; netbuffer->u.serverinfo._255 = 255; @@ -914,7 +917,7 @@ static void SV_SendServerInfo(INT32 node, tic_t servertime) else netbuffer->u.serverinfo.refusereason = 0; - strncpy(netbuffer->u.serverinfo.gametypename, Gametype_Names[gametype], + strncpy(netbuffer->u.serverinfo.gametypename, Gametype_Names[prefgametype], sizeof netbuffer->u.serverinfo.gametypename); netbuffer->u.serverinfo.modifiedgame = (UINT8)modifiedgame; netbuffer->u.serverinfo.cheatsenabled = CV_CheatsEnabled(); diff --git a/src/d_netcmd.c b/src/d_netcmd.c index 9b310eb66..e5f57c6ec 100644 --- a/src/d_netcmd.c +++ b/src/d_netcmd.c @@ -383,6 +383,8 @@ static CV_PossibleValue_t kartencore_cons_t[] = {{-1, "Auto"}, {0, "Off"}, {1, " consvar_t cv_kartencore = CVAR_INIT ("kartencore", "Auto", CV_NETVAR|CV_CALL|CV_NOINIT, kartencore_cons_t, KartEncore_OnChange); static CV_PossibleValue_t kartvoterulechanges_cons_t[] = {{0, "Never"}, {1, "Sometimes"}, {2, "Frequent"}, {3, "Always"}, {0, NULL}}; consvar_t cv_kartvoterulechanges = CVAR_INIT ("kartvoterulechanges", "Frequent", CV_NETVAR, kartvoterulechanges_cons_t, NULL); +static CV_PossibleValue_t kartgametypepreference_cons_t[] = {{-1, "None"}, {GT_RACE, "Race"}, {GT_BATTLE, "Battle"}, {0, NULL}}; +consvar_t cv_kartgametypepreference = CVAR_INIT ("kartgametypepreference", "None", CV_NETVAR, kartgametypepreference_cons_t, NULL); static CV_PossibleValue_t kartspeedometer_cons_t[] = {{0, "Off"}, {1, "Percentage"}, {2, "Kilometers"}, {3, "Miles"}, {4, "Fracunits"}, {0, NULL}}; consvar_t cv_kartspeedometer = CVAR_INIT ("kartdisplayspeed", "Percentage", CV_SAVE, kartspeedometer_cons_t, NULL); // use tics in display static CV_PossibleValue_t kartvoices_cons_t[] = {{0, "Never"}, {1, "Tasteful"}, {2, "Meme"}, {0, NULL}}; @@ -2436,27 +2438,28 @@ void D_SetupVote(void) UINT8 buf[5*2]; // four UINT16 maps (at twice the width of a UINT8), and two gametypes UINT8 *p = buf; INT32 i; - UINT8 secondgt = G_SometimesGetDifferentGametype(); + UINT8 gt = (cv_kartgametypepreference.value == -1) ? gametype : cv_kartgametypepreference.value; + UINT8 secondgt = G_SometimesGetDifferentGametype(gt); INT16 votebuffer[4] = {-1,-1,-1,0}; - if ((cv_kartencore.value == 1) && (gametyperules & GTR_CIRCUIT)) - WRITEUINT8(p, (gametype|0x80)); + if ((cv_kartencore.value == 1) && (gametypedefaultrules[gt] & GTR_CIRCUIT)) + WRITEUINT8(p, (gt|VOTEMODIFIER_ENCORE)); else - WRITEUINT8(p, gametype); + WRITEUINT8(p, gt); WRITEUINT8(p, secondgt); - secondgt &= ~0x80; + secondgt &= ~VOTEMODIFIER_ENCORE; for (i = 0; i < 4; i++) { UINT16 m; if (i == 2) // sometimes a different gametype m = G_RandMap(G_TOLFlag(secondgt), prevmap, ((secondgt != gametype) ? 2 : 0), 0, true, votebuffer); - else if (i >= 3) // unknown-random and force-unknown MAP HELL - m = G_RandMap(G_TOLFlag(gametype), prevmap, 0, (i-2), (i < 4), votebuffer); + else if (i >= 3) // unknown-random and formerly force-unknown MAP HELL + m = G_RandMap(G_TOLFlag(gt), prevmap, 0, (i-2), (i < 4), votebuffer); else - m = G_RandMap(G_TOLFlag(gametype), prevmap, 0, 0, true, votebuffer); + m = G_RandMap(G_TOLFlag(gt), prevmap, 0, 0, true, votebuffer); if (i < 3) - votebuffer[i] = m; // min() is a dumb workaround for gcc 4.4 array-bounds error + votebuffer[i] = m; WRITEUINT16(p, m); } @@ -4877,6 +4880,13 @@ static void Got_SetupVotecmd(UINT8 **cp, INT32 playernum) gt = (UINT8)READUINT8(*cp); secondgt = (UINT8)READUINT8(*cp); + // Strip illegal Encore flag. + if ((gt & VOTEMODIFIER_ENCORE) + && !(gametypedefaultrules[(gt & ~VOTEMODIFIER_ENCORE)] & GTR_CIRCUIT)) + { + gt &= ~VOTEMODIFIER_ENCORE; + } + for (i = 0; i < 4; i++) { votelevels[i][0] = (UINT16)READUINT16(*cp); @@ -4885,6 +4895,19 @@ static void Got_SetupVotecmd(UINT8 **cp, INT32 playernum) P_AllocMapHeader(votelevels[i][0]); } + // If third entry has an illelegal Encore flag... (illelegal!?) + if ((secondgt & VOTEMODIFIER_ENCORE) + && !(gametypedefaultrules[(secondgt & ~VOTEMODIFIER_ENCORE)] & GTR_CIRCUIT)) + { + secondgt &= ~VOTEMODIFIER_ENCORE; + // Apply it to the second entry instead, gametype permitting! + if (gametypedefaultrules[gt] & GTR_CIRCUIT) + { + votelevels[1][1] |= VOTEMODIFIER_ENCORE; + } + } + + // Finally, set third entry's gametype/Encore status. votelevels[2][1] = secondgt; G_SetGamestate(GS_VOTING); diff --git a/src/d_netcmd.h b/src/d_netcmd.h index e84342795..44e3aedcc 100644 --- a/src/d_netcmd.h +++ b/src/d_netcmd.h @@ -77,6 +77,7 @@ extern consvar_t cv_kartfrantic; extern consvar_t cv_kartcomeback; extern consvar_t cv_kartencore; extern consvar_t cv_kartvoterulechanges; +extern consvar_t cv_kartgametypepreference; extern consvar_t cv_kartspeedometer; extern consvar_t cv_kartvoices; extern consvar_t cv_kartbot; diff --git a/src/g_game.c b/src/g_game.c index 60261e368..6bdd5e028 100644 --- a/src/g_game.c +++ b/src/g_game.c @@ -3260,51 +3260,53 @@ boolean G_GametypeHasSpectators(void) // // Oh, yeah, and we sometimes flip encore mode on here too. // -INT16 G_SometimesGetDifferentGametype(void) +INT16 G_SometimesGetDifferentGametype(UINT8 prefgametype) { - boolean encorepossible = ((M_SecretUnlocked(SECRET_ENCORE) || encorescramble == 1) && (gametyperules & GTR_CIRCUIT)); + // Most of the gametype references in this condition are intentionally not prefgametype. + // This is so a server CAN continue playing a gametype if they like the taste of it. + // The encore check needs prefgametype so can't use G_RaceGametype... + boolean encorepossible = ((M_SecretUnlocked(SECRET_ENCORE) || encorescramble == 1) + && ((gametyperules|gametypedefaultrules[prefgametype]) & GTR_CIRCUIT)); + UINT8 encoremodifier = 0; - if (!cv_kartvoterulechanges.value // never - && encorescramble != 1) // destroying the code for this one instance - return gametype; - - if (randmapbuffer[NUMMAPS] > 0 && (encorepossible || cv_kartvoterulechanges.value != 3)) + if (encorepossible) { - randmapbuffer[NUMMAPS]--; - if (encorepossible) + if (encorescramble != -1) { - if (encorescramble != -1) - encorepossible = (boolean)encorescramble; // FORCE to what was scrambled on intermission - else - { - switch (cv_kartvoterulechanges.value) - { - case 3: // always - randmapbuffer[NUMMAPS] = 0; // gotta prep this in case it isn't already set - break; - case 2: // frequent - encorepossible = M_RandomChance(FRACUNIT>>1); - break; - case 1: // sometimes - default: - encorepossible = M_RandomChance(FRACUNIT>>2); - break; - } - } - if (encorepossible != (cv_kartencore.value == 1)) - return (gametype|0x80); + encorepossible = (boolean)encorescramble; // FORCE to what was scrambled on intermission } - return gametype; + else + { + switch (cv_kartvoterulechanges.value) + { + case 3: // always + encorepossible = true; + break; + case 2: // frequent + encorepossible = M_RandomChance(FRACUNIT>>1); + break; + case 1: // sometimes + encorepossible = M_RandomChance(FRACUNIT>>2); + break; + default: + break; + } + } + if (encorepossible != (cv_kartencore.value == 1)) + encoremodifier = VOTEMODIFIER_ENCORE; } - if (!cv_kartvoterulechanges.value) // never (again) - return gametype; + if (!cv_kartvoterulechanges.value) // never + return (gametype|encoremodifier); + + if (randmapbuffer[NUMMAPS] > 0 && (cv_kartvoterulechanges.value != 3)) + { + randmapbuffer[NUMMAPS]--; + return (gametype|encoremodifier); + } switch (cv_kartvoterulechanges.value) // okay, we're having a gametype change! when's the next one, luv? { - case 3: // always - randmapbuffer[NUMMAPS] = 1; // every other vote (or always if !encorepossible) - break; case 1: // sometimes randmapbuffer[NUMMAPS] = 5; // per "cup" break; @@ -3315,9 +3317,17 @@ INT16 G_SometimesGetDifferentGametype(void) break; } - if (gametype == GT_BATTLE) - return GT_RACE; - return GT_BATTLE; + // Only this response is prefgametype-based. + // todo custom gametypes + if (prefgametype == GT_BATTLE) + { + // Intentionally does not use encoremodifier! + if (cv_kartencore.value == 1) + return (GT_RACE|VOTEMODIFIER_ENCORE); + return (GT_RACE); + } + // This might appear wrong HERE, but the game will display the Encore possibility on the second voting choice instead. + return (GT_BATTLE|encoremodifier); } // diff --git a/src/g_game.h b/src/g_game.h index 52c7c7a0d..9fbbd5861 100644 --- a/src/g_game.h +++ b/src/g_game.h @@ -199,7 +199,8 @@ boolean G_IsSpecialStage(INT32 mapnum); boolean G_GametypeUsesLives(void); boolean G_GametypeHasTeams(void); boolean G_GametypeHasSpectators(void); -INT16 G_SometimesGetDifferentGametype(void); +#define VOTEMODIFIER_ENCORE 0x80 +INT16 G_SometimesGetDifferentGametype(UINT8 prefgametype); UINT8 G_GetGametypeColor(INT16 gt); void G_ExitLevel(void); void G_NextLevel(void); diff --git a/src/k_kart.c b/src/k_kart.c index f47bf3b17..11f4fc68a 100644 --- a/src/k_kart.c +++ b/src/k_kart.c @@ -248,6 +248,7 @@ void K_RegisterKartStuff(void) CV_RegisterVar(&cv_kartcomeback); CV_RegisterVar(&cv_kartencore); CV_RegisterVar(&cv_kartvoterulechanges); + CV_RegisterVar(&cv_kartgametypepreference); CV_RegisterVar(&cv_kartspeedometer); CV_RegisterVar(&cv_kartvoices); CV_RegisterVar(&cv_kartbot); diff --git a/src/y_inter.c b/src/y_inter.c index 2959853e0..807f10b4a 100644 --- a/src/y_inter.c +++ b/src/y_inter.c @@ -1588,6 +1588,7 @@ void Y_VoteTicker(void) void Y_StartVote(void) { INT32 i = 0; + boolean battlemode = ((votelevels[0][1] & ~VOTEMODIFIER_ENCORE) == GT_BATTLE); // todo gametyperules votetic = -1; @@ -1596,8 +1597,8 @@ void Y_StartVote(void) I_Error("voteendtic is dirty"); #endif - widebgpatch = W_CachePatchName(((gametype == GT_BATTLE) ? "BATTLSCW" : "INTERSCW"), PU_STATIC); - bgpatch = W_CachePatchName(((gametype == GT_BATTLE) ? "BATTLSCR" : "INTERSCR"), PU_STATIC); + widebgpatch = W_CachePatchName((battlemode ? "BATTLSCW" : "INTERSCW"), PU_STATIC); + bgpatch = W_CachePatchName((battlemode ? "BATTLSCR" : "INTERSCR"), PU_STATIC); cursor = W_CachePatchName("M_CURSOR", PU_STATIC); cursor1 = W_CachePatchName("P1CURSOR", PU_STATIC); cursor2 = W_CachePatchName("P2CURSOR", PU_STATIC); @@ -1631,8 +1632,8 @@ void Y_StartVote(void) lumpnum_t lumpnum; // set up the encore - levelinfo[i].encore = (votelevels[i][1] & 0x80); - votelevels[i][1] &= ~0x80; + levelinfo[i].encore = (votelevels[i][1] & VOTEMODIFIER_ENCORE); + votelevels[i][1] &= ~VOTEMODIFIER_ENCORE; // set up the levelstring if (mapheaderinfo[votelevels[i][0]]->levelflags & LF_NOZONE || !mapheaderinfo[votelevels[i][0]]->zonttl[0]) From ceeda5c826594c93e5c5634b0763324e82c17a71 Mon Sep 17 00:00:00 2001 From: James R Date: Mon, 5 Sep 2022 09:26:56 -0700 Subject: [PATCH 048/114] Lua hooklib refactor squash commit 8d382e49fb3411cad1a3ef5ee1e546030c3a9d93 Author: James R Date: Tue Nov 17 04:14:45 2020 -0800 Big Large Lua Hooklib Refactor * Hooks are no longer a mess of lua boiler plate. Helper functions reduce hooks to, at the most basic level, only two calls. * Lua tables (the array part) are used to index hooks. Such tables contain only hooks of the same type. * Hook types are defined in one place so you no longer need to sync up the enum and name array. commit 3f7c2ae0b0c450cb8993ffe7664ead05fd9f5000 Author: James R Date: Thu Dec 10 05:39:53 2020 -0800 Avoid using multiple tables to fetch hook String hooks still use a table to fetch the id, but the id indexes a C array. Also I fixed a missing call in the MusicChange hook. commit dbd8903a538e7b87061795ce27ec5c72c26743af Author: James R Date: Thu Dec 10 08:50:23 2020 -0800 Use ref for pushing error handler commit 9ddeb5f5896de0407b2b6fce8c949295a9e6d5e4 Author: James R Date: Sat Dec 12 02:05:21 2020 -0800 Resolve GameQuit hook conflicts commit 93e4f43e4b3a24fc2d8032e8730cbf427e75b297 Author: James R Date: Sat Dec 12 03:06:57 2020 -0800 Hooklib macros names -> uppercase + documentation commit 80fe39bbd1433c91131d2cdb36ba709f37d3b5f3 Author: Steel Titanium Date: Mon May 3 01:40:02 2021 -0400 Fix MusicChange hook not returning some values correctly commit 46ca9613c68422a26910cbb034a9f3e004a967c3 Author: James R Date: Thu Jun 10 18:09:39 2021 -0700 Pop hook id fetched from table commit a75d4a1c360874a3c301a494e38ea49cd89b6616 Author: James R Date: Tue Jul 6 18:42:08 2021 -0700 Automatically count hook values commit 331329306cad257d52f84e47a92d9214d9eaa8d3 Author: James R Date: Tue Jul 6 19:12:47 2021 -0700 Refactor hook ref allocation commit ae57b6ca8664e00ff4d9544339dbf29a41138040 Author: James R Date: Tue Jul 6 20:23:38 2021 -0700 MORE MACROS I just can't stop myself! commit b4fa98d2fbab180f487ce3efedb8ab715e5f3390 Author: James R Date: Wed Jul 7 00:23:51 2021 -0700 Refactor hudlib hooks to hooklib HUD hooks now meet the standard of hooklib. HUD registry magic numbers are gone. HUD hooks may also be added using addHook. addHook('HUD', fn[, type]) hud.add still exists, but the intention is to remove it eventually. commit cb619fad5d762ba8d8dd20e9a979a04ced7c943f Author: SMS Alfredo <65426124+SMS-Alfredo@users.noreply.github.com> Date: Wed Jul 7 19:57:28 2021 -0500 Rebase on !1307 commit f271f88c7f3084523a03ca7f47060d6527724796 Author: LJ Sonic Date: Sun Dec 5 17:59:33 2021 +0100 Fix MusicChange hook crashing when called BRUH MOMENT BRUH MOMENT PREPARE THE HALL OF SHAME commit 0a0c17da7c793ec29e80c5bdaa66b02da0884078 Author: James R Date: Tue Feb 1 02:27:27 2022 -0800 PARANOIA: I_Error if mobj hook is called with MT_NULL commit 86336d6bed80bee6f8168078aa8856109091e50f Author: katsy Date: Mon Mar 7 18:33:15 2022 -0600 remove MODID check from hooklib to fix compile issue --- src/lua_hook.h | 234 ++-- src/lua_hooklib.c | 2869 ++++++++++++++++----------------------------- src/s_sound.h | 10 + 3 files changed, 1179 insertions(+), 1934 deletions(-) diff --git a/src/lua_hook.h b/src/lua_hook.h index 7760c188b..fc6a5f4ee 100644 --- a/src/lua_hook.h +++ b/src/lua_hook.h @@ -1,7 +1,7 @@ // SONIC ROBO BLAST 2 //----------------------------------------------------------------------------- // Copyright (C) 2012-2016 by John "JTE" Muniz. -// Copyright (C) 2012-2020 by Sonic Team Junior. +// Copyright (C) 2012-2022 by Sonic Team Junior. // // This program is free software distributed under the // terms of the GNU General Public License, version 2. @@ -12,115 +12,139 @@ #include "r_defs.h" #include "d_player.h" -#include "lua_hudlib_drawlist.h" +#include "s_sound.h" +#include "d_event.h" -enum hook { - hook_NetVars=0, - hook_MapChange, - hook_MapLoad, - hook_PlayerJoin, - hook_PreThinkFrame, - hook_ThinkFrame, - hook_PostThinkFrame, - hook_MobjSpawn, - hook_MobjCollide, - hook_MobjLineCollide, - hook_MobjMoveCollide, - hook_TouchSpecial, - hook_MobjFuse, - hook_MobjThinker, - hook_BossThinker, - hook_ShouldDamage, - hook_MobjDamage, - hook_MobjDeath, - hook_BossDeath, - hook_MobjRemoved, - hook_JumpSpecial, - hook_AbilitySpecial, - hook_SpinSpecial, - hook_JumpSpinSpecial, - hook_BotTiccmd, - hook_BotAI, - hook_BotRespawn, - hook_LinedefExecute, - hook_PlayerMsg, - hook_HurtMsg, - hook_PlayerSpawn, - hook_ShieldSpawn, - hook_ShieldSpecial, - hook_MobjMoveBlocked, - hook_MapThingSpawn, - hook_FollowMobj, - hook_PlayerCanDamage, - hook_PlayerQuit, - hook_MusicChange, - hook_TeamSwitch, - hook_ViewpointSwitch, - hook_PlayerThink, - hook_ShouldJingleContinue, - hook_GameQuit, - hook_PlayerCmd, +/* +Do you know what an 'X Macro' is? Such a macro is called over each element of +a list and expands the input. I use it for the hook lists because both an enum +and array of hook names need to be kept in order. The X Macro handles this +automatically. +*/ - // SRB2Kart - hook_IntermissionThinker, - hook_VoteThinker, +#define MOBJ_HOOK_LIST(X) \ + X (MobjSpawn),/* P_SpawnMobj */\ + X (MobjCollide),/* PIT_CheckThing */\ + X (MobjLineCollide),/* ditto */\ + X (MobjMoveCollide),/* tritto */\ + X (TouchSpecial),/* P_TouchSpecialThing */\ + X (MobjFuse),/* when mobj->fuse runs out */\ + X (MobjThinker),/* P_MobjThinker, P_SceneryThinker */\ + X (BossThinker),/* P_GenericBossThinker */\ + X (ShouldDamage),/* P_DamageMobj (Should mobj take damage?) */\ + X (MobjDamage),/* P_DamageMobj (Mobj actually takes damage!) */\ + X (MobjDeath),/* P_KillMobj */\ + X (BossDeath),/* A_BossDeath */\ + X (MobjRemoved),/* P_RemoveMobj */\ + X (BotRespawn),/* B_CheckRespawn */\ + X (MobjMoveBlocked),/* P_XYMovement (when movement is blocked) */\ + X (MapThingSpawn),/* P_SpawnMapThing */\ + X (FollowMobj),/* P_PlayerAfterThink Smiles mobj-following */\ - hook_MAX // last hook -}; -extern const char *const hookNames[]; +#define HOOK_LIST(X) \ + X (NetVars),/* add to archive table (netsave) */\ + X (MapChange),/* (before map load) */\ + X (MapLoad),\ + X (PlayerJoin),/* Got_AddPlayer */\ + X (PreThinkFrame)/* frame (before mobj and player thinkers) */,\ + X (ThinkFrame),/* frame (after mobj and player thinkers) */\ + X (PostThinkFrame),/* frame (at end of tick, ie after overlays, precipitation, specials) */\ + X (JumpSpecial),/* P_DoJumpStuff (Any-jumping) */\ + X (AbilitySpecial),/* P_DoJumpStuff (Double-jumping) */\ + X (SpinSpecial),/* P_DoSpinAbility (Spin button effect) */\ + X (JumpSpinSpecial),/* P_DoJumpStuff (Spin button effect (mid-air)) */\ + X (BotTiccmd),/* B_BuildTiccmd */\ + X (PlayerMsg),/* chat messages */\ + X (HurtMsg),/* imhurttin */\ + X (PlayerSpawn),/* G_SpawnPlayer */\ + X (ShieldSpawn),/* P_SpawnShieldOrb */\ + X (ShieldSpecial),/* shield abilities */\ + X (PlayerCanDamage),/* P_PlayerCanDamage */\ + X (PlayerQuit),\ + X (IntermissionThinker),/* Y_Ticker */\ + X (TeamSwitch),/* team switching in... uh... *what* speak, spit it the fuck out */\ + X (ViewpointSwitch),/* spy mode (no trickstabs) */\ + X (SeenPlayer),/* MT_NAMECHECK */\ + X (PlayerThink),/* P_PlayerThink */\ + X (GameQuit),\ + X (PlayerCmd),/* building the player's ticcmd struct (Ported from SRB2Kart) */\ + X (MusicChange),\ + X (PlayerHeight),/* override player height */\ + X (PlayerCanEnterSpinGaps),\ + X (KeyDown),\ + X (KeyUp),\ + +#define STRING_HOOK_LIST(X) \ + X (BotAI),/* B_BuildTailsTiccmd by skin name */\ + X (LinedefExecute),\ + X (ShouldJingleContinue),/* should jingle of the given music continue playing */\ + +#define HUD_HOOK_LIST(X) \ + X (game),\ + X (scores),/* emblems/multiplayer list */\ + X (title),/* titlescreen */\ + X (titlecard),\ + X (intermission),\ + +/* +I chose to access the hook enums through a macro as well. This could provide +a hint to lookup the macro's definition instead of the enum's definition. +(Since each enumeration is not defined in the source code, but by the list +macros above, it is not greppable.) The name passed to the macro can also be +grepped and found in the lists above. +*/ + +#define MOBJ_HOOK(name) mobjhook_ ## name +#define HOOK(name) hook_ ## name +#define HUD_HOOK(name) hudhook_ ## name +#define STRING_HOOK(name) stringhook_ ## name + +#define ENUM(X) enum { X ## _LIST (X) X(MAX) } + +ENUM (MOBJ_HOOK); +ENUM (HOOK); +ENUM (HUD_HOOK); +ENUM (STRING_HOOK); + +#undef ENUM + +/* dead simple, LUA_HOOK(GameQuit) */ +#define LUA_HOOK(type) LUA_HookVoid(HOOK(type)) +#define LUA_HUDHOOK(type) LUA_HookHUD(HUD_HOOK(type)) extern boolean hook_cmd_running; -extern int hook_defrosting; -void LUAh_MapChange(INT16 mapnumber); // Hook for map change (before load) -void LUAh_MapLoad(void); // Hook for map load -void LUAh_PlayerJoin(int playernum); // Hook for Got_AddPlayer -void LUAh_PreThinkFrame(void); // Hook for frame (before mobj and player thinkers) -void LUAh_ThinkFrame(void); // Hook for frame (after mobj and player thinkers) -void LUAh_PostThinkFrame(void); // Hook for frame (at end of tick, ie after overlays, precipitation, specials) -boolean LUAh_MobjHook(mobj_t *mo, enum hook which); -boolean LUAh_PlayerHook(player_t *plr, enum hook which); -#define LUAh_MobjSpawn(mo) LUAh_MobjHook(mo, hook_MobjSpawn) // Hook for P_SpawnMobj by mobj type -UINT8 LUAh_MobjCollideHook(mobj_t *thing1, mobj_t *thing2, enum hook which); -UINT8 LUAh_MobjLineCollideHook(mobj_t *thing, line_t *line, enum hook which); -#define LUAh_MobjCollide(thing1, thing2) LUAh_MobjCollideHook(thing1, thing2, hook_MobjCollide) // Hook for PIT_CheckThing by (thing) mobj type -#define LUAh_MobjLineCollide(thing, line) LUAh_MobjLineCollideHook(thing, line, hook_MobjLineCollide) // Hook for PIT_CheckThing by (thing) mobj type -#define LUAh_MobjMoveCollide(thing1, thing2) LUAh_MobjCollideHook(thing1, thing2, hook_MobjMoveCollide) // Hook for PIT_CheckThing by (tmthing) mobj type -boolean LUAh_TouchSpecial(mobj_t *special, mobj_t *toucher); // Hook for P_TouchSpecialThing by mobj type -#define LUAh_MobjFuse(mo) LUAh_MobjHook(mo, hook_MobjFuse) // Hook for mobj->fuse == 0 by mobj type -boolean LUAh_MobjThinker(mobj_t *mo); // Hook for P_MobjThinker or P_SceneryThinker by mobj type -#define LUAh_BossThinker(mo) LUAh_MobjHook(mo, hook_BossThinker) // Hook for P_GenericBossThinker by mobj type -UINT8 LUAh_ShouldDamage(mobj_t *target, mobj_t *inflictor, mobj_t *source, INT32 damage, UINT8 damagetype); // Hook for P_DamageMobj by mobj type (Should mobj take damage?) -boolean LUAh_MobjDamage(mobj_t *target, mobj_t *inflictor, mobj_t *source, INT32 damage, UINT8 damagetype); // Hook for P_DamageMobj by mobj type (Mobj actually takes damage!) -boolean LUAh_MobjDeath(mobj_t *target, mobj_t *inflictor, mobj_t *source, UINT8 damagetype); // Hook for P_KillMobj by mobj type -#define LUAh_BossDeath(mo) LUAh_MobjHook(mo, hook_BossDeath) // Hook for A_BossDeath by mobj type -#define LUAh_MobjRemoved(mo) LUAh_MobjHook(mo, hook_MobjRemoved) // Hook for P_RemoveMobj by mobj type -#define LUAh_JumpSpecial(player) LUAh_PlayerHook(player, hook_JumpSpecial) // Hook for P_DoJumpStuff (Any-jumping) -#define LUAh_AbilitySpecial(player) LUAh_PlayerHook(player, hook_AbilitySpecial) // Hook for P_DoJumpStuff (Double-jumping) -#define LUAh_SpinSpecial(player) LUAh_PlayerHook(player, hook_SpinSpecial) // Hook for P_DoSpinAbility (Spin button effect) -#define LUAh_JumpSpinSpecial(player) LUAh_PlayerHook(player, hook_JumpSpinSpecial) // Hook for P_DoJumpStuff (Spin button effect (mid-air)) -boolean LUAh_BotTiccmd(player_t *bot, ticcmd_t *cmd); // Hook for B_BuildTiccmd -boolean LUAh_BotAI(mobj_t *sonic, mobj_t *tails, ticcmd_t *cmd); // Hook for B_BuildTailsTiccmd by skin name -boolean LUAh_BotRespawn(mobj_t *sonic, mobj_t *tails); // Hook for B_CheckRespawn -boolean LUAh_LinedefExecute(line_t *line, mobj_t *mo, sector_t *sector); // Hook for linedef executors -boolean LUAh_PlayerMsg(int source, int target, int flags, char *msg, int mute); // Hook for chat messages -boolean LUAh_HurtMsg(player_t *player, mobj_t *inflictor, mobj_t *source, UINT8 damagetype); // Hook for hurt messages -#define LUAh_PlayerSpawn(player) LUAh_PlayerHook(player, hook_PlayerSpawn) // Hook for G_SpawnPlayer -void LUAh_PlayerQuit(player_t *plr, kickreason_t reason); // Hook for player quitting -boolean LUAh_MusicChange(const char *oldname, char *newname, UINT16 *mflags, boolean *looping, - UINT32 *position, UINT32 *prefadems, UINT32 *fadeinms); // Hook for music changes +void LUA_HookVoid(int hook); +void LUA_HookHUD(int hook); -boolean LUAh_PlayerCmd(player_t *player, ticcmd_t *cmd); // Allows to write to player cmd before the game does anything with them. +int LUA_HookMobj(mobj_t *, int hook); +int LUA_Hook2Mobj(mobj_t *, mobj_t *, int hook); +void LUA_HookInt(INT32 integer, int hook); +void LUA_HookBool(boolean value, int hook); +int LUA_HookPlayer(player_t *, int hook); +int LUA_HookTiccmd(player_t *, ticcmd_t *, int hook); +int LUA_HookKey(event_t *event, int hook); // Hooks for key events -void LUAh_IntermissionThinker(void); // Hook for Y_Ticker -void LUAh_VoteThinker(void); // Hook for Y_VoteTicker - -#define LUAh_MobjMoveBlocked(mo) LUAh_MobjHook(mo, hook_MobjMoveBlocked) // Hook for P_XYMovement (when movement is blocked) -boolean LUAh_MapThingSpawn(mobj_t *mo, mapthing_t *mthing); // Hook for P_SpawnMapThing by mobj type -boolean LUAh_FollowMobj(player_t *player, mobj_t *mobj); // Hook for P_PlayerAfterThink Smiles mobj-following -UINT8 LUAh_PlayerCanDamage(player_t *player, mobj_t *mobj); // Hook for P_PlayerCanDamage -boolean LUAh_TeamSwitch(player_t *player, int newteam, boolean fromspectators, boolean tryingautobalance, boolean tryingscramble); // Hook for team switching in... uh.... -UINT8 LUAh_ViewpointSwitch(player_t *player, player_t *newdisplayplayer, boolean forced); // Hook for spy mode -#define LUAh_PlayerThink(player) LUAh_PlayerHook(player, hook_PlayerThink) // Hook for P_PlayerThink -boolean LUAh_ShouldJingleContinue(player_t *player, const char *musname); // Hook for whether a jingle of the given music should continue playing -void LUAh_GameQuit(boolean quitting); // Hook for game quitting +void LUA_HookThinkFrame(void); +int LUA_HookMobjLineCollide(mobj_t *, line_t *); +int LUA_HookTouchSpecial(mobj_t *special, mobj_t *toucher); +int LUA_HookShouldDamage(mobj_t *target, mobj_t *inflictor, mobj_t *source, INT32 damage, UINT8 damagetype); +int LUA_HookMobjDamage(mobj_t *target, mobj_t *inflictor, mobj_t *source, INT32 damage, UINT8 damagetype); +int LUA_HookMobjDeath(mobj_t *target, mobj_t *inflictor, mobj_t *source, UINT8 damagetype); +int LUA_HookMobjMoveBlocked(mobj_t *, mobj_t *, line_t *); +int LUA_HookBotAI(mobj_t *sonic, mobj_t *tails, ticcmd_t *cmd); +void LUA_HookLinedefExecute(line_t *, mobj_t *, sector_t *); +int LUA_HookPlayerMsg(int source, int target, int flags, char *msg); +int LUA_HookHurtMsg(player_t *, mobj_t *inflictor, mobj_t *source, UINT8 damagetype); +int LUA_HookMapThingSpawn(mobj_t *, mapthing_t *); +int LUA_HookFollowMobj(player_t *, mobj_t *); +int LUA_HookPlayerCanDamage(player_t *, mobj_t *); +void LUA_HookPlayerQuit(player_t *, kickreason_t); +int LUA_HookTeamSwitch(player_t *, int newteam, boolean fromspectators, boolean tryingautobalance, boolean tryingscramble); +int LUA_HookViewpointSwitch(player_t *player, player_t *newdisplayplayer, boolean forced); +int LUA_HookSeenPlayer(player_t *player, player_t *seenfriend); +int LUA_HookShouldJingleContinue(player_t *, const char *musname); +int LUA_HookPlayerCmd(player_t *, ticcmd_t *); +int LUA_HookMusicChange(const char *oldname, struct MusicChange *); +fixed_t LUA_HookPlayerHeight(player_t *player); +int LUA_HookPlayerCanEnterSpinGaps(player_t *player); diff --git a/src/lua_hooklib.c b/src/lua_hooklib.c index 771182a59..dc5069319 100644 --- a/src/lua_hooklib.c +++ b/src/lua_hooklib.c @@ -1,7 +1,7 @@ // SONIC ROBO BLAST 2 //----------------------------------------------------------------------------- // Copyright (C) 2012-2016 by John "JTE" Muniz. -// Copyright (C) 2012-2020 by Sonic Team Junior. +// Copyright (C) 2012-2022 by Sonic Team Junior. // // This program is free software distributed under the // terms of the GNU General Public License, version 2. @@ -27,1939 +27,1150 @@ #include "d_netcmd.h" // for cv_perfstats #include "i_system.h" // I_GetPreciseTime -static UINT8 hooksAvailable[(hook_MAX/8)+1]; +/* ========================================================================= + ABSTRACTION + ========================================================================= */ -const char *const hookNames[hook_MAX+1] = { - "NetVars", - "MapChange", - "MapLoad", - "PlayerJoin", - "PreThinkFrame", - "ThinkFrame", - "PostThinkFrame", - "MobjSpawn", - "MobjCollide", - "MobjLineCollide", - "MobjMoveCollide", - "TouchSpecial", - "MobjFuse", - "MobjThinker", - "BossThinker", - "ShouldDamage", - "MobjDamage", - "MobjDeath", - "BossDeath", - "MobjRemoved", - "JumpSpecial", - "AbilitySpecial", - "SpinSpecial", - "JumpSpinSpecial", - "BotTiccmd", - "BotAI", - "BotRespawn", - "LinedefExecute", - "PlayerMsg", - "HurtMsg", - "PlayerSpawn", - "ShieldSpawn", - "ShieldSpecial", - "MobjMoveBlocked", - "MapThingSpawn", - "FollowMobj", - "PlayerCanDamage", - "PlayerQuit", - "MusicChange", - "TeamSwitch", - "ViewpointSwitch", - "PlayerThink", - "ShouldJingleContinue", - "GameQuit", - "PlayerCmd", +#define LIST(id, M) \ + static const char * const id [] = { M (TOSTR) NULL } - // SRB2Kart - "IntermissionThinker", - "VoteThinker", +LIST (mobjHookNames, MOBJ_HOOK_LIST); +LIST (hookNames, HOOK_LIST); +LIST (hudHookNames, HUD_HOOK_LIST); +LIST (stringHookNames, STRING_HOOK_LIST); - NULL -}; +#undef LIST -// Hook metadata -struct hook_s +typedef struct { + int numHooks; + int *ids; +} hook_t; + +typedef struct { + int numGeneric; + int ref; +} stringhook_t; + +static hook_t hookIds[HOOK(MAX)]; +static hook_t hudHookIds[HUD_HOOK(MAX)]; +static hook_t mobjHookIds[NUMMOBJTYPES][MOBJ_HOOK(MAX)]; + +// Lua tables are used to lookup string hook ids. +static stringhook_t stringHooks[STRING_HOOK(MAX)]; + +// This will be indexed by hook id, the value of which fetches the registry. +static int * hookRefs; +static int nextid; + +// After a hook errors once, don't print the error again. +static UINT8 * hooksErrored; + +static int errorRef; + +static boolean mobj_hook_available(int hook_type, mobjtype_t mobj_type) { - struct hook_s *next; - enum hook type; - UINT16 id; - union { - mobjtype_t mt; - char *str; - } s; - boolean error; -}; -typedef struct hook_s* hook_p; + return + ( + mobjHookIds [MT_NULL] [hook_type].numHooks > 0 || + mobjHookIds[mobj_type][hook_type].numHooks > 0 + ); +} -#define FMT_HOOKID "hook_%d" +static int hook_in_list +( + const char * const name, + const char * const * const list +){ + int type; -// For each mobj type, a linked list to its thinker and collision hooks. -// That way, we don't have to iterate through all the hooks. -// We could do that with all other mobj hooks, but it would probably just be -// a waste of memory since they are only called occasionally. Probably... -static hook_p mobjthinkerhooks[NUMMOBJTYPES]; -static hook_p mobjcollidehooks[NUMMOBJTYPES]; + for (type = 0; list[type] != NULL; ++type) + { + if (strcmp(name, list[type]) == 0) + break; + } -// For each mobj type, a linked list for other mobj hooks -static hook_p mobjhooks[NUMMOBJTYPES]; + return type; +} -// A linked list for player hooks -static hook_p playerhooks; - -// A linked list for linedef executor hooks -static hook_p linedefexecutorhooks; - -// For other hooks, a unique linked list -hook_p roothook; - -static void PushHook(lua_State *L, hook_p hookp) +static void get_table(lua_State *L) { - lua_pushfstring(L, FMT_HOOKID, hookp->id); - lua_gettable(L, LUA_REGISTRYINDEX); + lua_pushvalue(L, -1); + lua_rawget(L, -3); + + if (lua_isnil(L, -1)) + { + lua_pop(L, 1); + lua_createtable(L, 1, 0); + lua_pushvalue(L, -2); + lua_pushvalue(L, -2); + lua_rawset(L, -5); + } + + lua_remove(L, -2); +} + +static void add_hook_to_table(lua_State *L, int n) +{ + lua_pushnumber(L, nextid); + lua_rawseti(L, -2, n); +} + +static void add_string_hook(lua_State *L, int type) +{ + stringhook_t * hook = &stringHooks[type]; + + char * string = NULL; + + switch (type) + { + case STRING_HOOK(BotAI): + case STRING_HOOK(ShouldJingleContinue): + if (lua_isstring(L, 3)) + { // lowercase copy + string = Z_StrDup(lua_tostring(L, 3)); + strlwr(string); + } + break; + + case STRING_HOOK(LinedefExecute): + string = Z_StrDup(luaL_checkstring(L, 3)); + strupr(string); + break; + } + + if (hook->ref > 0) + lua_getref(L, hook->ref); + else + { + lua_newtable(L); + lua_pushvalue(L, -1); + hook->ref = luaL_ref(L, LUA_REGISTRYINDEX); + } + + if (string) + { + lua_pushstring(L, string); + get_table(L); + add_hook_to_table(L, 1 + lua_objlen(L, -1)); + } + else + add_hook_to_table(L, ++hook->numGeneric); +} + +static void add_hook(hook_t *map) +{ + Z_Realloc(map->ids, (map->numHooks + 1) * sizeof *map->ids, + PU_STATIC, &map->ids); + map->ids[map->numHooks++] = nextid; +} + +static void add_mobj_hook(lua_State *L, int hook_type) +{ + mobjtype_t mobj_type = luaL_optnumber(L, 3, MT_NULL); + + luaL_argcheck(L, mobj_type < NUMMOBJTYPES, 3, "invalid mobjtype_t"); + + add_hook(&mobjHookIds[mobj_type][hook_type]); +} + +static void add_hud_hook(lua_State *L, int idx) +{ + add_hook(&hudHookIds[luaL_checkoption(L, + idx, "game", hudHookNames)]); +} + +static void add_hook_ref(lua_State *L, int idx) +{ + if (!(nextid & 7)) + { + Z_Realloc(hooksErrored, + BIT_ARRAY_SIZE (nextid + 1) * sizeof *hooksErrored, + PU_STATIC, &hooksErrored); + hooksErrored[nextid >> 3] = 0; + } + + Z_Realloc(hookRefs, (nextid + 1) * sizeof *hookRefs, PU_STATIC, &hookRefs); + + // set the hook function in the registry. + lua_pushvalue(L, idx); + hookRefs[nextid++] = luaL_ref(L, LUA_REGISTRYINDEX); } // Takes hook, function, and additional arguments (mobj type to act on, etc.) static int lib_addHook(lua_State *L) { - static struct hook_s hook = {NULL, 0, 0, {0}, false}; - static UINT32 nextid; - hook_p hookp, *lastp; - - hook.type = luaL_checkoption(L, 1, NULL, hookNames); - lua_remove(L, 1); - - luaL_checktype(L, 1, LUA_TFUNCTION); + const char * name; + int type; if (!lua_lumploading) return luaL_error(L, "This function cannot be called from within a hook or coroutine!"); - switch(hook.type) + name = luaL_checkstring(L, 1); + luaL_checktype(L, 2, LUA_TFUNCTION); + + /* this is a very special case */ + if (( type = hook_in_list(name, stringHookNames) ) < STRING_HOOK(MAX)) { - // Take a mobjtype enum which this hook is specifically for. - case hook_MobjSpawn: - case hook_MobjCollide: - case hook_MobjLineCollide: - case hook_MobjMoveCollide: - case hook_TouchSpecial: - case hook_MobjFuse: - case hook_MobjThinker: - case hook_BossThinker: - case hook_ShouldDamage: - case hook_MobjDamage: - case hook_MobjDeath: - case hook_BossDeath: - case hook_MobjRemoved: - case hook_HurtMsg: - case hook_MobjMoveBlocked: - case hook_MapThingSpawn: - case hook_FollowMobj: - hook.s.mt = MT_NULL; - if (lua_isnumber(L, 2)) - hook.s.mt = lua_tonumber(L, 2); - luaL_argcheck(L, hook.s.mt < NUMMOBJTYPES, 2, "invalid mobjtype_t"); - break; - case hook_BotAI: - case hook_ShouldJingleContinue: - hook.s.str = NULL; - if (lua_isstring(L, 2)) - { // lowercase copy - hook.s.str = Z_StrDup(lua_tostring(L, 2)); - strlwr(hook.s.str); - } - break; - case hook_LinedefExecute: // Linedef executor functions - hook.s.str = Z_StrDup(luaL_checkstring(L, 2)); - strupr(hook.s.str); - break; - default: - break; + add_string_hook(L, type); } - lua_settop(L, 1); // lua stack contains only the function now. - - hooksAvailable[hook.type/8] |= 1<<(hook.type%8); - - // set hook.id to the highest id + 1 - hook.id = nextid++; - - // Special cases for some hook types (see the comments above mobjthinkerhooks declaration) - switch(hook.type) + else if (( type = hook_in_list(name, mobjHookNames) ) < MOBJ_HOOK(MAX)) { - case hook_MobjThinker: - lastp = &mobjthinkerhooks[hook.s.mt]; - break; - case hook_MobjCollide: - case hook_MobjLineCollide: - case hook_MobjMoveCollide: - lastp = &mobjcollidehooks[hook.s.mt]; - break; - case hook_MobjSpawn: - case hook_TouchSpecial: - case hook_MobjFuse: - case hook_BossThinker: - case hook_ShouldDamage: - case hook_MobjDamage: - case hook_MobjDeath: - case hook_BossDeath: - case hook_MobjRemoved: - case hook_MobjMoveBlocked: - case hook_MapThingSpawn: - case hook_FollowMobj: - lastp = &mobjhooks[hook.s.mt]; - break; - case hook_JumpSpecial: - case hook_AbilitySpecial: - case hook_SpinSpecial: - case hook_JumpSpinSpecial: - case hook_PlayerSpawn: - case hook_PlayerCanDamage: - case hook_TeamSwitch: - case hook_ViewpointSwitch: - case hook_ShieldSpawn: - case hook_ShieldSpecial: - case hook_PlayerThink: - lastp = &playerhooks; - break; - case hook_LinedefExecute: - lastp = &linedefexecutorhooks; - break; - default: - lastp = &roothook; - break; + add_mobj_hook(L, type); + } + else if (( type = hook_in_list(name, hookNames) ) < HOOK(MAX)) + { + add_hook(&hookIds[type]); + } + else if (strcmp(name, "HUD") == 0) + { + add_hud_hook(L, 3); + } + else + { + return luaL_argerror(L, 1, lua_pushfstring(L, "invalid hook " LUA_QS, name)); } - // iterate the hook metadata structs - // set lastp to the last hook struct's "next" pointer. - for (hookp = *lastp; hookp; hookp = hookp->next) - lastp = &hookp->next; - // allocate a permanent memory struct to stuff hook. - hookp = ZZ_Alloc(sizeof(struct hook_s)); - memcpy(hookp, &hook, sizeof(struct hook_s)); - // tack it onto the end of the linked list. - *lastp = hookp; + add_hook_ref(L, 2);/* the function */ - // set the hook function in the registry. - lua_pushfstring(L, FMT_HOOKID, hook.id); - lua_pushvalue(L, 1); - lua_settable(L, LUA_REGISTRYINDEX); return 0; } int LUA_HookLib(lua_State *L) { - memset(hooksAvailable,0,sizeof(UINT8[(hook_MAX/8)+1])); - roothook = NULL; + lua_pushcfunction(L, LUA_GetErrorMessage); + errorRef = luaL_ref(L, LUA_REGISTRYINDEX); + lua_register(L, "addHook", lib_addHook); + return 0; } -boolean LUAh_MobjHook(mobj_t *mo, enum hook which) +/* TODO: remove in next backwards incompatible release */ +int lib_hudadd(lua_State *L);/* yeah compiler */ +int lib_hudadd(lua_State *L) { - hook_p hookp; - boolean hooked = false; - if (!gL || !(hooksAvailable[which/8] & (1<<(which%8)))) - return false; + if (!lua_lumploading) + return luaL_error(L, "This function cannot be called from within a hook or coroutine!"); - I_Assert(mo->type < NUMMOBJTYPES); + luaL_checktype(L, 1, LUA_TFUNCTION); - if (!(mobjhooks[MT_NULL] || mobjhooks[mo->type])) - return false; + add_hud_hook(L, 2); + add_hook_ref(L, 1); + return 0; +} + +typedef struct Hook_State Hook_State; +typedef void (*Hook_Callback)(Hook_State *); + +struct Hook_State { + INT32 status;/* return status to calling function */ + void * userdata; + int hook_type; + mobjtype_t mobj_type;/* >0 if mobj hook */ + const char * string;/* used to fetch table, ran first if set */ + int top;/* index of last argument passed to hook */ + int id;/* id to fetch ref */ + int values;/* num arguments passed to hook */ + int results;/* num values returned by hook */ + Hook_Callback results_handler;/* callback when hook successfully returns */ +}; + +enum { + EINDEX = 1,/* error handler */ + SINDEX = 2,/* string itself is pushed in case of string hook */ +}; + +static void push_error_handler(void) +{ + lua_getref(gL, errorRef); +} + +/* repush hook string */ +static void push_string(void) +{ + lua_pushvalue(gL, SINDEX); +} + +static boolean begin_hook_values(Hook_State *hook) +{ + hook->top = lua_gettop(gL); + return true; +} + +static void start_hook_stack(void) +{ lua_settop(gL, 0); - lua_pushcfunction(gL, LUA_GetErrorMessage); + push_error_handler(); +} - // Look for all generic mobj hooks - for (hookp = mobjhooks[MT_NULL]; hookp; hookp = hookp->next) +static boolean init_hook_type +( + Hook_State * hook, + int status, + int hook_type, + mobjtype_t mobj_type, + const char * string, + int nonzero +){ + hook->status = status; + + if (nonzero) { - if (hookp->type != which) - continue; + start_hook_stack(); + hook->hook_type = hook_type; + hook->mobj_type = mobj_type; + hook->string = string; + return begin_hook_values(hook); + } + else + return false; +} - ps_lua_mobjhooks++; - if (lua_gettop(gL) == 1) - LUA_PushUserdata(gL, mo, META_MOBJ); - PushHook(gL, hookp); - lua_pushvalue(gL, -2); - if (lua_pcall(gL, 1, 1, 1)) { - if (!hookp->error || cv_debug & DBG_LUA) - CONS_Alert(CONS_WARNING,"%s\n",lua_tostring(gL, -1)); - lua_pop(gL, 1); - hookp->error = true; - continue; +static boolean prepare_hook +( + Hook_State * hook, + int default_status, + int hook_type +){ + return init_hook_type(hook, default_status, + hook_type, 0, NULL, + hookIds[hook_type].numHooks); +} + +static boolean prepare_mobj_hook +( + Hook_State * hook, + int default_status, + int hook_type, + mobjtype_t mobj_type +){ +#ifdef PARANOIA + if (mobj_type == MT_NULL) + I_Error("MT_NULL has been passed to a mobj hook\n"); +#endif + return init_hook_type(hook, default_status, + hook_type, mobj_type, NULL, + mobj_hook_available(hook_type, mobj_type)); +} + +static boolean prepare_string_hook +( + Hook_State * hook, + int default_status, + int hook_type, + const char * string +){ + if (init_hook_type(hook, default_status, + hook_type, 0, string, + stringHooks[hook_type].ref)) + { + lua_pushstring(gL, string); + return begin_hook_values(hook); + } + else + return false; +} + +static void init_hook_call +( + Hook_State * hook, + int results, + Hook_Callback results_handler +){ + const int top = lua_gettop(gL); + hook->values = (top - hook->top); + hook->top = top; + hook->results = results; + hook->results_handler = results_handler; +} + +static void get_hook(Hook_State *hook, const int *ids, int n) +{ + hook->id = ids[n]; + lua_getref(gL, hookRefs[hook->id]); +} + +static void get_hook_from_table(Hook_State *hook, int n) +{ + lua_rawgeti(gL, -1, n); + hook->id = lua_tonumber(gL, -1); + lua_pop(gL, 1); + lua_getref(gL, hookRefs[hook->id]); +} + +static int call_single_hook_no_copy(Hook_State *hook) +{ + if (lua_pcall(gL, hook->values, hook->results, EINDEX) == 0) + { + if (hook->results > 0) + { + (*hook->results_handler)(hook); + lua_pop(gL, hook->results); + } + } + else + { + /* print the error message once */ + if (cv_debug & DBG_LUA || !in_bit_array(hooksErrored, hook->id)) + { + CONS_Alert(CONS_WARNING, "%s\n", lua_tostring(gL, -1)); + set_bit_array(hooksErrored, hook->id); } - if (lua_toboolean(gL, -1)) - hooked = true; lua_pop(gL, 1); } - for (hookp = mobjhooks[mo->type]; hookp; hookp = hookp->next) - { - if (hookp->type != which) - continue; + return 1; +} - ps_lua_mobjhooks++; - if (lua_gettop(gL) == 1) - LUA_PushUserdata(gL, mo, META_MOBJ); - PushHook(gL, hookp); - lua_pushvalue(gL, -2); - if (lua_pcall(gL, 1, 1, 1)) { - if (!hookp->error || cv_debug & DBG_LUA) - CONS_Alert(CONS_WARNING,"%s\n",lua_tostring(gL, -1)); - lua_pop(gL, 1); - hookp->error = true; - continue; - } +static int call_single_hook(Hook_State *hook) +{ + int i; + + for (i = -(hook->values) + 1; i <= 0; ++i) + lua_pushvalue(gL, hook->top + i); + + return call_single_hook_no_copy(hook); +} + +static int call_hook_table_for(Hook_State *hook, int n) +{ + int k; + + for (k = 1; k <= n; ++k) + { + get_hook_from_table(hook, k); + call_single_hook(hook); + } + + return n; +} + +static int call_hook_table(Hook_State *hook) +{ + return call_hook_table_for(hook, lua_objlen(gL, -1)); +} + +static int call_mapped(Hook_State *hook, const hook_t *map) +{ + int k; + + for (k = 0; k < map->numHooks; ++k) + { + get_hook(hook, map->ids, k); + call_single_hook(hook); + } + + return map->numHooks; +} + +static int call_string_hooks(Hook_State *hook) +{ + const stringhook_t *map = &stringHooks[hook->hook_type]; + + int calls = 0; + + lua_getref(gL, map->ref); + + /* call generic string hooks first */ + calls += call_hook_table_for(hook, map->numGeneric); + + push_string(); + lua_rawget(gL, -2); + calls += call_hook_table(hook); + + return calls; +} + +static int call_mobj_type_hooks(Hook_State *hook, mobjtype_t mobj_type) +{ + return call_mapped(hook, &mobjHookIds[mobj_type][hook->hook_type]); +} + +static int call_hooks +( + Hook_State * hook, + int results, + Hook_Callback results_handler +){ + int calls = 0; + + init_hook_call(hook, results, results_handler); + + if (hook->string) + { + calls += call_string_hooks(hook); + } + else if (hook->mobj_type > 0) + { + /* call generic mobj hooks first */ + calls += call_mobj_type_hooks(hook, MT_NULL); + calls += call_mobj_type_hooks(hook, hook->mobj_type); + + ps_lua_mobjhooks.value.i += calls; + } + else + calls += call_mapped(hook, &hookIds[hook->hook_type]); + + lua_settop(gL, 0); + + return calls; +} + +/* ========================================================================= + COMMON RESULT HANDLERS + ========================================================================= */ + +#define res_none NULL + +static void res_true(Hook_State *hook) +{ + if (lua_toboolean(gL, -1)) + hook->status = true; +} + +static void res_false(Hook_State *hook) +{ + if (!lua_isnil(gL, -1) && !lua_toboolean(gL, -1)) + hook->status = false; +} + +static void res_force(Hook_State *hook) +{ + if (!lua_isnil(gL, -1)) + { if (lua_toboolean(gL, -1)) - hooked = true; - lua_pop(gL, 1); + hook->status = 1; // Force yes + else + hook->status = 2; // Force no } - - lua_settop(gL, 0); - return hooked; } -boolean LUAh_PlayerHook(player_t *plr, enum hook which) +/* ========================================================================= + GENERALISED HOOKS + ========================================================================= */ + +int LUA_HookMobj(mobj_t *mobj, int hook_type) { - hook_p hookp; - boolean hooked = false; - if (!gL || !(hooksAvailable[which/8] & (1<<(which%8)))) - return false; - - lua_settop(gL, 0); - lua_pushcfunction(gL, LUA_GetErrorMessage); - - for (hookp = playerhooks; hookp; hookp = hookp->next) + Hook_State hook; + if (prepare_mobj_hook(&hook, false, hook_type, mobj->type)) { - if (hookp->type != which) - continue; - - ps_lua_mobjhooks++; - if (lua_gettop(gL) == 1) - LUA_PushUserdata(gL, plr, META_PLAYER); - PushHook(gL, hookp); - lua_pushvalue(gL, -2); - if (lua_pcall(gL, 1, 1, 1)) { - if (!hookp->error || cv_debug & DBG_LUA) - CONS_Alert(CONS_WARNING,"%s\n",lua_tostring(gL, -1)); - lua_pop(gL, 1); - hookp->error = true; - continue; - } - if (lua_toboolean(gL, -1)) - hooked = true; - lua_pop(gL, 1); + LUA_PushUserdata(gL, mobj, META_MOBJ); + call_hooks(&hook, 1, res_true); } - - lua_settop(gL, 0); - return hooked; + return hook.status; } -// Hook for map change (before load) -void LUAh_MapChange(INT16 mapnumber) +int LUA_Hook2Mobj(mobj_t *t1, mobj_t *t2, int hook_type) { - hook_p hookp; - if (!gL || !(hooksAvailable[hook_MapChange/8] & (1<<(hook_MapChange%8)))) - return; - - lua_settop(gL, 0); - lua_pushcfunction(gL, LUA_GetErrorMessage); - lua_pushinteger(gL, mapnumber); - - for (hookp = roothook; hookp; hookp = hookp->next) + Hook_State hook; + if (prepare_mobj_hook(&hook, 0, hook_type, t1->type)) { - if (hookp->type != hook_MapChange) - continue; - - PushHook(gL, hookp); - lua_pushvalue(gL, -2); - if (lua_pcall(gL, 1, 0, 1)) { - CONS_Alert(CONS_WARNING,"%s\n",lua_tostring(gL, -1)); - lua_pop(gL, 1); - } + LUA_PushUserdata(gL, t1, META_MOBJ); + LUA_PushUserdata(gL, t2, META_MOBJ); + call_hooks(&hook, 1, res_force); } - - lua_settop(gL, 0); + return hook.status; } -// Hook for map load -void LUAh_MapLoad(void) +void LUA_HookVoid(int type) { - hook_p hookp; - if (!gL || !(hooksAvailable[hook_MapLoad/8] & (1<<(hook_MapLoad%8)))) - return; + Hook_State hook; + if (prepare_hook(&hook, 0, type)) + call_hooks(&hook, 0, res_none); +} - lua_settop(gL, 0); - lua_pushcfunction(gL, LUA_GetErrorMessage); - lua_pushinteger(gL, gamemap); - - for (hookp = roothook; hookp; hookp = hookp->next) +void LUA_HookInt(INT32 number, int hook_type) +{ + Hook_State hook; + if (prepare_hook(&hook, 0, hook_type)) { - if (hookp->type != hook_MapLoad) - continue; - - PushHook(gL, hookp); - lua_pushvalue(gL, -2); - if (lua_pcall(gL, 1, 0, 1)) { - CONS_Alert(CONS_WARNING,"%s\n",lua_tostring(gL, -1)); - lua_pop(gL, 1); - } + lua_pushinteger(gL, number); + call_hooks(&hook, 0, res_none); } - - lua_settop(gL, 0); } -// Hook for Got_AddPlayer -void LUAh_PlayerJoin(int playernum) +void LUA_HookBool(boolean value, int hook_type) { - hook_p hookp; - if (!gL || !(hooksAvailable[hook_PlayerJoin/8] & (1<<(hook_PlayerJoin%8)))) - return; - - lua_settop(gL, 0); - lua_pushcfunction(gL, LUA_GetErrorMessage); - lua_pushinteger(gL, playernum); - - for (hookp = roothook; hookp; hookp = hookp->next) + Hook_State hook; + if (prepare_hook(&hook, 0, hook_type)) { - if (hookp->type != hook_PlayerJoin) - continue; - - PushHook(gL, hookp); - lua_pushvalue(gL, -2); - if (lua_pcall(gL, 1, 0, 1)) { - CONS_Alert(CONS_WARNING,"%s\n",lua_tostring(gL, -1)); - lua_pop(gL, 1); - } + lua_pushboolean(gL, value); + call_hooks(&hook, 0, res_none); } - - lua_settop(gL, 0); } -// Hook for frame (before mobj and player thinkers) -void LUAh_PreThinkFrame(void) +int LUA_HookPlayer(player_t *player, int hook_type) { - hook_p hookp; - if (!gL || !(hooksAvailable[hook_PreThinkFrame/8] & (1<<(hook_PreThinkFrame%8)))) - return; - - lua_pushcfunction(gL, LUA_GetErrorMessage); - - for (hookp = roothook; hookp; hookp = hookp->next) + Hook_State hook; + if (prepare_hook(&hook, false, hook_type)) { - if (hookp->type != hook_PreThinkFrame) - continue; - - PushHook(gL, hookp); - if (lua_pcall(gL, 0, 0, 1)) { - if (!hookp->error || cv_debug & DBG_LUA) - CONS_Alert(CONS_WARNING,"%s\n",lua_tostring(gL, -1)); - lua_pop(gL, 1); - hookp->error = true; - } + LUA_PushUserdata(gL, player, META_PLAYER); + call_hooks(&hook, 1, res_true); } - - lua_pop(gL, 1); // Pop error handler + return hook.status; } -// Hook for frame (after mobj and player thinkers) -void LUAh_ThinkFrame(void) +int LUA_HookTiccmd(player_t *player, ticcmd_t *cmd, int hook_type) { - hook_p hookp; + Hook_State hook; + if (prepare_hook(&hook, false, hook_type)) + { + LUA_PushUserdata(gL, player, META_PLAYER); + LUA_PushUserdata(gL, cmd, META_TICCMD); + + if (hook_type == HOOK(PlayerCmd)) + hook_cmd_running = true; + + call_hooks(&hook, 1, res_true); + + if (hook_type == HOOK(PlayerCmd)) + hook_cmd_running = false; + } + return hook.status; +} + +int LUA_HookKey(event_t *event, int hook_type) +{ + Hook_State hook; + if (prepare_hook(&hook, false, hook_type)) + { + LUA_PushUserdata(gL, event, META_KEYEVENT); + call_hooks(&hook, 1, res_true); + } + return hook.status; +} + +void LUA_HookHUD(int hook_type) +{ + const hook_t * map = &hudHookIds[hook_type]; + Hook_State hook; + if (map->numHooks > 0) + { + start_hook_stack(); + begin_hook_values(&hook); + + LUA_SetHudHook(hook_type); + + hud_running = true; // local hook + init_hook_call(&hook, 0, res_none); + call_mapped(&hook, map); + hud_running = false; + } +} + +/* ========================================================================= + SPECIALIZED HOOKS + ========================================================================= */ + +void LUA_HookThinkFrame(void) +{ + const int type = HOOK(ThinkFrame); + // variables used by perf stats int hook_index = 0; precise_t time_taken = 0; - if (!gL || !(hooksAvailable[hook_ThinkFrame/8] & (1<<(hook_ThinkFrame%8)))) - return; - lua_pushcfunction(gL, LUA_GetErrorMessage); + Hook_State hook; - for (hookp = roothook; hookp; hookp = hookp->next) + const hook_t * map = &hookIds[type]; + int k; + + if (prepare_hook(&hook, 0, type)) { - if (hookp->type != hook_ThinkFrame) - continue; + init_hook_call(&hook, 0, res_none); - if (cv_perfstats.value == PS_THINKFRAME) - time_taken = I_GetPreciseTime(); - PushHook(gL, hookp); - if (lua_pcall(gL, 0, 0, 1)) { - if (!hookp->error || cv_debug & DBG_LUA) - CONS_Alert(CONS_WARNING,"%s\n",lua_tostring(gL, -1)); - lua_pop(gL, 1); - hookp->error = true; - } - if (cv_perfstats.value == PS_THINKFRAME) + for (k = 0; k < map->numHooks; ++k) { - lua_Debug ar; - time_taken = I_GetPreciseTime() - time_taken; - // we need the function, let's just retrieve it again - PushHook(gL, hookp); - lua_getinfo(gL, ">S", &ar); - PS_SetThinkFrameHookInfo(hook_index, time_taken, ar.short_src); - hook_index++; - } - } + get_hook(&hook, map->ids, k); - lua_pop(gL, 1); // Pop error handler -} - -// Hook for frame (at end of tick, ie after overlays, precipitation, specials) -void LUAh_PostThinkFrame(void) -{ - hook_p hookp; - if (!gL || !(hooksAvailable[hook_PostThinkFrame/8] & (1<<(hook_PostThinkFrame%8)))) - return; - - lua_pushcfunction(gL, LUA_GetErrorMessage); - - for (hookp = roothook; hookp; hookp = hookp->next) - { - if (hookp->type != hook_PostThinkFrame) - continue; - - PushHook(gL, hookp); - if (lua_pcall(gL, 0, 0, 1)) { - if (!hookp->error || cv_debug & DBG_LUA) - CONS_Alert(CONS_WARNING,"%s\n",lua_tostring(gL, -1)); - lua_pop(gL, 1); - hookp->error = true; - } - } - - lua_pop(gL, 1); // Pop error handler -} - -// Hook for mobj collisions -UINT8 LUAh_MobjCollideHook(mobj_t *thing1, mobj_t *thing2, enum hook which) -{ - hook_p hookp; - UINT8 shouldCollide = 0; // 0 = default, 1 = force yes, 2 = force no. - if (!gL || !(hooksAvailable[which/8] & (1<<(which%8)))) - return 0; - - I_Assert(thing1->type < NUMMOBJTYPES); - - if (!(mobjcollidehooks[MT_NULL] || mobjcollidehooks[thing1->type])) - return 0; - - lua_settop(gL, 0); - lua_pushcfunction(gL, LUA_GetErrorMessage); - - // Look for all generic mobj collision hooks - for (hookp = mobjcollidehooks[MT_NULL]; hookp; hookp = hookp->next) - { - if (hookp->type != which) - continue; - - ps_lua_mobjhooks++; - if (lua_gettop(gL) == 1) - { - LUA_PushUserdata(gL, thing1, META_MOBJ); - LUA_PushUserdata(gL, thing2, META_MOBJ); - } - PushHook(gL, hookp); - lua_pushvalue(gL, -3); - lua_pushvalue(gL, -3); - if (lua_pcall(gL, 2, 1, 1)) { - if (!hookp->error || cv_debug & DBG_LUA) - CONS_Alert(CONS_WARNING,"%s\n",lua_tostring(gL, -1)); - lua_pop(gL, 1); - hookp->error = true; - continue; - } - if (!lua_isnil(gL, -1)) - { // if nil, leave shouldCollide = 0. - if (lua_toboolean(gL, -1)) - shouldCollide = 1; // Force yes - else - shouldCollide = 2; // Force no - } - lua_pop(gL, 1); - } - - for (hookp = mobjcollidehooks[thing1->type]; hookp; hookp = hookp->next) - { - if (hookp->type != which) - continue; - - ps_lua_mobjhooks++; - if (lua_gettop(gL) == 1) - { - LUA_PushUserdata(gL, thing1, META_MOBJ); - LUA_PushUserdata(gL, thing2, META_MOBJ); - } - PushHook(gL, hookp); - lua_pushvalue(gL, -3); - lua_pushvalue(gL, -3); - if (lua_pcall(gL, 2, 1, 1)) { - if (!hookp->error || cv_debug & DBG_LUA) - CONS_Alert(CONS_WARNING,"%s\n",lua_tostring(gL, -1)); - lua_pop(gL, 1); - hookp->error = true; - continue; - } - if (!lua_isnil(gL, -1)) - { // if nil, leave shouldCollide = 0. - if (lua_toboolean(gL, -1)) - shouldCollide = 1; // Force yes - else - shouldCollide = 2; // Force no - } - lua_pop(gL, 1); - } - - lua_settop(gL, 0); - return shouldCollide; -} - -UINT8 LUAh_MobjLineCollideHook(mobj_t *thing, line_t *line, enum hook which) -{ - hook_p hookp; - UINT8 shouldCollide = 0; // 0 = default, 1 = force yes, 2 = force no. - if (!gL || !(hooksAvailable[which/8] & (1<<(which%8)))) - return 0; - - I_Assert(thing->type < NUMMOBJTYPES); - - if (!(mobjcollidehooks[MT_NULL] || mobjcollidehooks[thing->type])) - return 0; - - lua_settop(gL, 0); - lua_pushcfunction(gL, LUA_GetErrorMessage); - - // Look for all generic mobj collision hooks - for (hookp = mobjcollidehooks[MT_NULL]; hookp; hookp = hookp->next) - { - if (hookp->type != which) - continue; - - ps_lua_mobjhooks++; - if (lua_gettop(gL) == 1) - { - LUA_PushUserdata(gL, thing, META_MOBJ); - LUA_PushUserdata(gL, line, META_LINE); - } - PushHook(gL, hookp); - lua_pushvalue(gL, -3); - lua_pushvalue(gL, -3); - if (lua_pcall(gL, 2, 1, 1)) { - if (!hookp->error || cv_debug & DBG_LUA) - CONS_Alert(CONS_WARNING,"%s\n",lua_tostring(gL, -1)); - lua_pop(gL, 1); - hookp->error = true; - continue; - } - if (!lua_isnil(gL, -1)) - { // if nil, leave shouldCollide = 0. - if (lua_toboolean(gL, -1)) - shouldCollide = 1; // Force yes - else - shouldCollide = 2; // Force no - } - lua_pop(gL, 1); - } - - for (hookp = mobjcollidehooks[thing->type]; hookp; hookp = hookp->next) - { - if (hookp->type != which) - continue; - - ps_lua_mobjhooks++; - if (lua_gettop(gL) == 1) - { - LUA_PushUserdata(gL, thing, META_MOBJ); - LUA_PushUserdata(gL, line, META_LINE); - } - PushHook(gL, hookp); - lua_pushvalue(gL, -3); - lua_pushvalue(gL, -3); - if (lua_pcall(gL, 2, 1, 1)) { - if (!hookp->error || cv_debug & DBG_LUA) - CONS_Alert(CONS_WARNING,"%s\n",lua_tostring(gL, -1)); - lua_pop(gL, 1); - hookp->error = true; - continue; - } - if (!lua_isnil(gL, -1)) - { // if nil, leave shouldCollide = 0. - if (lua_toboolean(gL, -1)) - shouldCollide = 1; // Force yes - else - shouldCollide = 2; // Force no - } - lua_pop(gL, 1); - } - - lua_settop(gL, 0); - return shouldCollide; -} - -// Hook for mobj thinkers -boolean LUAh_MobjThinker(mobj_t *mo) -{ - hook_p hookp; - boolean hooked = false; - if (!gL || !(hooksAvailable[hook_MobjThinker/8] & (1<<(hook_MobjThinker%8)))) - return false; - - I_Assert(mo->type < NUMMOBJTYPES); - - if (!(mobjthinkerhooks[MT_NULL] || mobjthinkerhooks[mo->type])) - return false; - - lua_settop(gL, 0); - lua_pushcfunction(gL, LUA_GetErrorMessage); - - // Look for all generic mobj thinker hooks - for (hookp = mobjthinkerhooks[MT_NULL]; hookp; hookp = hookp->next) - { - ps_lua_mobjhooks++; - if (lua_gettop(gL) == 1) - LUA_PushUserdata(gL, mo, META_MOBJ); - PushHook(gL, hookp); - lua_pushvalue(gL, -2); - if (lua_pcall(gL, 1, 1, 1)) { - if (!hookp->error || cv_debug & DBG_LUA) - CONS_Alert(CONS_WARNING,"%s\n",lua_tostring(gL, -1)); - lua_pop(gL, 1); - hookp->error = true; - continue; - } - if (lua_toboolean(gL, -1)) - hooked = true; - lua_pop(gL, 1); - } - - for (hookp = mobjthinkerhooks[mo->type]; hookp; hookp = hookp->next) - { - ps_lua_mobjhooks++; - if (lua_gettop(gL) == 1) - LUA_PushUserdata(gL, mo, META_MOBJ); - PushHook(gL, hookp); - lua_pushvalue(gL, -2); - if (lua_pcall(gL, 1, 1, 1)) { - if (!hookp->error || cv_debug & DBG_LUA) - CONS_Alert(CONS_WARNING,"%s\n",lua_tostring(gL, -1)); - lua_pop(gL, 1); - hookp->error = true; - continue; - } - if (lua_toboolean(gL, -1)) - hooked = true; - lua_pop(gL, 1); - } - - lua_settop(gL, 0); - return hooked; -} - -// Hook for P_TouchSpecialThing by mobj type -boolean LUAh_TouchSpecial(mobj_t *special, mobj_t *toucher) -{ - hook_p hookp; - boolean hooked = false; - if (!gL || !(hooksAvailable[hook_TouchSpecial/8] & (1<<(hook_TouchSpecial%8)))) - return false; - - I_Assert(special->type < NUMMOBJTYPES); - - if (!(mobjhooks[MT_NULL] || mobjhooks[special->type])) - return false; - - lua_settop(gL, 0); - lua_pushcfunction(gL, LUA_GetErrorMessage); - - // Look for all generic touch special hooks - for (hookp = mobjhooks[MT_NULL]; hookp; hookp = hookp->next) - { - if (hookp->type != hook_TouchSpecial) - continue; - - ps_lua_mobjhooks++; - if (lua_gettop(gL) == 1) - { - LUA_PushUserdata(gL, special, META_MOBJ); - LUA_PushUserdata(gL, toucher, META_MOBJ); - } - PushHook(gL, hookp); - lua_pushvalue(gL, -3); - lua_pushvalue(gL, -3); - if (lua_pcall(gL, 2, 1, 1)) { - if (!hookp->error || cv_debug & DBG_LUA) - CONS_Alert(CONS_WARNING,"%s\n",lua_tostring(gL, -1)); - lua_pop(gL, 1); - hookp->error = true; - continue; - } - if (lua_toboolean(gL, -1)) - hooked = true; - lua_pop(gL, 1); - } - - for (hookp = mobjhooks[special->type]; hookp; hookp = hookp->next) - { - if (hookp->type != hook_TouchSpecial) - continue; - - ps_lua_mobjhooks++; - if (lua_gettop(gL) == 1) - { - LUA_PushUserdata(gL, special, META_MOBJ); - LUA_PushUserdata(gL, toucher, META_MOBJ); - } - PushHook(gL, hookp); - lua_pushvalue(gL, -3); - lua_pushvalue(gL, -3); - if (lua_pcall(gL, 2, 1, 1)) { - if (!hookp->error || cv_debug & DBG_LUA) - CONS_Alert(CONS_WARNING,"%s\n",lua_tostring(gL, -1)); - lua_pop(gL, 1); - hookp->error = true; - continue; - } - if (lua_toboolean(gL, -1)) - hooked = true; - lua_pop(gL, 1); - } - - lua_settop(gL, 0); - return hooked; -} - -// Hook for P_DamageMobj by mobj type (Should mobj take damage?) -UINT8 LUAh_ShouldDamage(mobj_t *target, mobj_t *inflictor, mobj_t *source, INT32 damage, UINT8 damagetype) -{ - hook_p hookp; - UINT8 shouldDamage = 0; // 0 = default, 1 = force yes, 2 = force no. - if (!gL || !(hooksAvailable[hook_ShouldDamage/8] & (1<<(hook_ShouldDamage%8)))) - return 0; - - I_Assert(target->type < NUMMOBJTYPES); - - if (!(mobjhooks[MT_NULL] || mobjhooks[target->type])) - return 0; - - lua_settop(gL, 0); - lua_pushcfunction(gL, LUA_GetErrorMessage); - - // Look for all generic should damage hooks - for (hookp = mobjhooks[MT_NULL]; hookp; hookp = hookp->next) - { - if (hookp->type != hook_ShouldDamage) - continue; - - ps_lua_mobjhooks++; - if (lua_gettop(gL) == 1) - { - LUA_PushUserdata(gL, target, META_MOBJ); - LUA_PushUserdata(gL, inflictor, META_MOBJ); - LUA_PushUserdata(gL, source, META_MOBJ); - lua_pushinteger(gL, damage); - lua_pushinteger(gL, damagetype); - } - PushHook(gL, hookp); - lua_pushvalue(gL, -6); - lua_pushvalue(gL, -6); - lua_pushvalue(gL, -6); - lua_pushvalue(gL, -6); - lua_pushvalue(gL, -6); - if (lua_pcall(gL, 5, 1, 1)) { - if (!hookp->error || cv_debug & DBG_LUA) - CONS_Alert(CONS_WARNING,"%s\n",lua_tostring(gL, -1)); - lua_pop(gL, 1); - hookp->error = true; - continue; - } - if (!lua_isnil(gL, -1)) - { - if (lua_toboolean(gL, -1)) - shouldDamage = 1; // Force yes - else - shouldDamage = 2; // Force no - } - lua_pop(gL, 1); - } - - for (hookp = mobjhooks[target->type]; hookp; hookp = hookp->next) - { - if (hookp->type != hook_ShouldDamage) - continue; - ps_lua_mobjhooks++; - if (lua_gettop(gL) == 1) - { - LUA_PushUserdata(gL, target, META_MOBJ); - LUA_PushUserdata(gL, inflictor, META_MOBJ); - LUA_PushUserdata(gL, source, META_MOBJ); - lua_pushinteger(gL, damage); - lua_pushinteger(gL, damagetype); - } - PushHook(gL, hookp); - lua_pushvalue(gL, -6); - lua_pushvalue(gL, -6); - lua_pushvalue(gL, -6); - lua_pushvalue(gL, -6); - lua_pushvalue(gL, -6); - if (lua_pcall(gL, 5, 1, 1)) { - if (!hookp->error || cv_debug & DBG_LUA) - CONS_Alert(CONS_WARNING,"%s\n",lua_tostring(gL, -1)); - lua_pop(gL, 1); - hookp->error = true; - continue; - } - if (!lua_isnil(gL, -1)) - { - if (lua_toboolean(gL, -1)) - shouldDamage = 1; // Force yes - else - shouldDamage = 2; // Force no - } - lua_pop(gL, 1); - } - - lua_settop(gL, 0); - return shouldDamage; -} - -// Hook for P_DamageMobj by mobj type (Mobj actually takes damage!) -boolean LUAh_MobjDamage(mobj_t *target, mobj_t *inflictor, mobj_t *source, INT32 damage, UINT8 damagetype) -{ - hook_p hookp; - boolean hooked = false; - if (!gL || !(hooksAvailable[hook_MobjDamage/8] & (1<<(hook_MobjDamage%8)))) - return false; - - I_Assert(target->type < NUMMOBJTYPES); - - if (!(mobjhooks[MT_NULL] || mobjhooks[target->type])) - return false; - - lua_settop(gL, 0); - lua_pushcfunction(gL, LUA_GetErrorMessage); - - // Look for all generic mobj damage hooks - for (hookp = mobjhooks[MT_NULL]; hookp; hookp = hookp->next) - { - if (hookp->type != hook_MobjDamage) - continue; - - ps_lua_mobjhooks++; - if (lua_gettop(gL) == 1) - { - LUA_PushUserdata(gL, target, META_MOBJ); - LUA_PushUserdata(gL, inflictor, META_MOBJ); - LUA_PushUserdata(gL, source, META_MOBJ); - lua_pushinteger(gL, damage); - lua_pushinteger(gL, damagetype); - } - PushHook(gL, hookp); - lua_pushvalue(gL, -6); - lua_pushvalue(gL, -6); - lua_pushvalue(gL, -6); - lua_pushvalue(gL, -6); - lua_pushvalue(gL, -6); - if (lua_pcall(gL, 5, 1, 1)) { - if (!hookp->error || cv_debug & DBG_LUA) - CONS_Alert(CONS_WARNING,"%s\n",lua_tostring(gL, -1)); - lua_pop(gL, 1); - hookp->error = true; - continue; - } - if (lua_toboolean(gL, -1)) - hooked = true; - lua_pop(gL, 1); - } - - for (hookp = mobjhooks[target->type]; hookp; hookp = hookp->next) - { - if (hookp->type != hook_MobjDamage) - continue; - - ps_lua_mobjhooks++; - if (lua_gettop(gL) == 1) - { - LUA_PushUserdata(gL, target, META_MOBJ); - LUA_PushUserdata(gL, inflictor, META_MOBJ); - LUA_PushUserdata(gL, source, META_MOBJ); - lua_pushinteger(gL, damage); - lua_pushinteger(gL, damagetype); - } - PushHook(gL, hookp); - lua_pushvalue(gL, -6); - lua_pushvalue(gL, -6); - lua_pushvalue(gL, -6); - lua_pushvalue(gL, -6); - lua_pushvalue(gL, -6); - if (lua_pcall(gL, 5, 1, 1)) { - if (!hookp->error || cv_debug & DBG_LUA) - CONS_Alert(CONS_WARNING,"%s\n",lua_tostring(gL, -1)); - lua_pop(gL, 1); - hookp->error = true; - continue; - } - if (lua_toboolean(gL, -1)) - hooked = true; - lua_pop(gL, 1); - } - - lua_settop(gL, 0); - return hooked; -} - -// Hook for P_KillMobj by mobj type -boolean LUAh_MobjDeath(mobj_t *target, mobj_t *inflictor, mobj_t *source, UINT8 damagetype) -{ - hook_p hookp; - boolean hooked = false; - if (!gL || !(hooksAvailable[hook_MobjDeath/8] & (1<<(hook_MobjDeath%8)))) - return false; - - I_Assert(target->type < NUMMOBJTYPES); - - if (!(mobjhooks[MT_NULL] || mobjhooks[target->type])) - return false; - - lua_settop(gL, 0); - lua_pushcfunction(gL, LUA_GetErrorMessage); - - // Look for all generic mobj death hooks - for (hookp = mobjhooks[MT_NULL]; hookp; hookp = hookp->next) - { - if (hookp->type != hook_MobjDeath) - continue; - - ps_lua_mobjhooks++; - if (lua_gettop(gL) == 1) - { - LUA_PushUserdata(gL, target, META_MOBJ); - LUA_PushUserdata(gL, inflictor, META_MOBJ); - LUA_PushUserdata(gL, source, META_MOBJ); - lua_pushinteger(gL, damagetype); - } - PushHook(gL, hookp); - lua_pushvalue(gL, -5); - lua_pushvalue(gL, -5); - lua_pushvalue(gL, -5); - lua_pushvalue(gL, -5); - if (lua_pcall(gL, 4, 1, 1)) { - if (!hookp->error || cv_debug & DBG_LUA) - CONS_Alert(CONS_WARNING,"%s\n",lua_tostring(gL, -1)); - lua_pop(gL, 1); - hookp->error = true; - continue; - } - if (lua_toboolean(gL, -1)) - hooked = true; - lua_pop(gL, 1); - } - - for (hookp = mobjhooks[target->type]; hookp; hookp = hookp->next) - { - if (hookp->type != hook_MobjDeath) - continue; - - ps_lua_mobjhooks++; - if (lua_gettop(gL) == 1) - { - LUA_PushUserdata(gL, target, META_MOBJ); - LUA_PushUserdata(gL, inflictor, META_MOBJ); - LUA_PushUserdata(gL, source, META_MOBJ); - lua_pushinteger(gL, damagetype); - } - PushHook(gL, hookp); - lua_pushvalue(gL, -5); - lua_pushvalue(gL, -5); - lua_pushvalue(gL, -5); - lua_pushvalue(gL, -5); - if (lua_pcall(gL, 4, 1, 1)) { - if (!hookp->error || cv_debug & DBG_LUA) - CONS_Alert(CONS_WARNING,"%s\n",lua_tostring(gL, -1)); - lua_pop(gL, 1); - hookp->error = true; - continue; - } - if (lua_toboolean(gL, -1)) - hooked = true; - lua_pop(gL, 1); - } - - lua_settop(gL, 0); - return hooked; -} - -// Hook for B_BuildTiccmd -boolean LUAh_BotTiccmd(player_t *bot, ticcmd_t *cmd) -{ - hook_p hookp; - boolean hooked = false; - if (!gL || !(hooksAvailable[hook_BotTiccmd/8] & (1<<(hook_BotTiccmd%8)))) - return false; - - lua_settop(gL, 0); - lua_pushcfunction(gL, LUA_GetErrorMessage); - - for (hookp = roothook; hookp; hookp = hookp->next) - { - if (hookp->type != hook_BotTiccmd) - continue; - - if (lua_gettop(gL) == 1) - { - LUA_PushUserdata(gL, bot, META_PLAYER); - LUA_PushUserdata(gL, cmd, META_TICCMD); - } - PushHook(gL, hookp); - lua_pushvalue(gL, -3); - lua_pushvalue(gL, -3); - if (lua_pcall(gL, 2, 1, 1)) { - if (!hookp->error || cv_debug & DBG_LUA) - CONS_Alert(CONS_WARNING,"%s\n",lua_tostring(gL, -1)); - lua_pop(gL, 1); - hookp->error = true; - continue; - } - if (lua_toboolean(gL, -1)) - hooked = true; - lua_pop(gL, 1); - } - - lua_settop(gL, 0); - return hooked; -} - -// Hook for B_BuildTailsTiccmd by skin name -boolean LUAh_BotAI(mobj_t *sonic, mobj_t *tails, ticcmd_t *cmd) -{ -#if 1 - (void)sonic; - (void)tails; - (void)cmd; - return false; -#else - hook_p hookp; - boolean hooked = false; - if (!gL || !(hooksAvailable[hook_BotAI/8] & (1<<(hook_BotAI%8)))) - return false; - - lua_settop(gL, 0); - lua_pushcfunction(gL, LUA_GetErrorMessage); - - for (hookp = roothook; hookp; hookp = hookp->next) - { - if (hookp->type != hook_BotAI - || (hookp->s.str && strcmp(hookp->s.str, ((skin_t*)tails->skin)->name))) - continue; - - if (lua_gettop(gL) == 1) - { - LUA_PushUserdata(gL, sonic, META_MOBJ); - LUA_PushUserdata(gL, tails, META_MOBJ); - } - PushHook(gL, hookp); - lua_pushvalue(gL, -3); - lua_pushvalue(gL, -3); - if (lua_pcall(gL, 2, 8, 1)) { - if (!hookp->error || cv_debug & DBG_LUA) - CONS_Alert(CONS_WARNING,"%s\n",lua_tostring(gL, -1)); - lua_pop(gL, 1); - hookp->error = true; - continue; - } - - // This turns forward, backward, left, right, jump, and spin into a proper ticcmd for tails. - if (lua_istable(gL, 2+1)) { - boolean forward=false, backward=false, left=false, right=false, strafeleft=false, straferight=false, jump=false, spin=false; -#define CHECKFIELD(field) \ - lua_getfield(gL, 2+1, #field);\ - if (lua_toboolean(gL, -1))\ - field = true;\ - lua_pop(gL, 1); - - CHECKFIELD(forward) - CHECKFIELD(backward) - CHECKFIELD(left) - CHECKFIELD(right) - CHECKFIELD(strafeleft) - CHECKFIELD(straferight) - CHECKFIELD(jump) - CHECKFIELD(spin) -#undef CHECKFIELD - B_KeysToTiccmd(tails, cmd, forward, backward, left, right, strafeleft, straferight, jump, spin); - } else - B_KeysToTiccmd(tails, cmd, lua_toboolean(gL, 2+1), lua_toboolean(gL, 2+2), lua_toboolean(gL, 2+3), lua_toboolean(gL, 2+4), lua_toboolean(gL, 2+5), lua_toboolean(gL, 2+6), lua_toboolean(gL, 2+7), lua_toboolean(gL, 2+8)); - - lua_pop(gL, 8); - hooked = true; - } - - lua_settop(gL, 0); - return hooked; -#endif -} - -// Hook for B_CheckRespawn -boolean LUAh_BotRespawn(mobj_t *sonic, mobj_t *tails) -{ - hook_p hookp; - UINT8 shouldRespawn = 0; // 0 = default, 1 = force yes, 2 = force no. - if (!gL || !(hooksAvailable[hook_BotRespawn/8] & (1<<(hook_BotRespawn%8)))) - return false; - - lua_settop(gL, 0); - lua_pushcfunction(gL, LUA_GetErrorMessage); - - for (hookp = roothook; hookp; hookp = hookp->next) - { - if (hookp->type != hook_BotRespawn) - continue; - - if (lua_gettop(gL) == 1) - { - LUA_PushUserdata(gL, sonic, META_MOBJ); - LUA_PushUserdata(gL, tails, META_MOBJ); - } - PushHook(gL, hookp); - lua_pushvalue(gL, -3); - lua_pushvalue(gL, -3); - if (lua_pcall(gL, 2, 1, 1)) { - if (!hookp->error || cv_debug & DBG_LUA) - CONS_Alert(CONS_WARNING,"%s\n",lua_tostring(gL, -1)); - lua_pop(gL, 1); - hookp->error = true; - continue; - } - if (!lua_isnil(gL, -1)) - { - if (lua_toboolean(gL, -1)) - shouldRespawn = 1; // Force yes - else - shouldRespawn = 2; // Force no - } - lua_pop(gL, 1); - } - - lua_settop(gL, 0); - return shouldRespawn; -} - -// Hook for linedef executors -boolean LUAh_LinedefExecute(line_t *line, mobj_t *mo, sector_t *sector) -{ - hook_p hookp; - boolean hooked = false; - if (!gL || !(hooksAvailable[hook_LinedefExecute/8] & (1<<(hook_LinedefExecute%8)))) - return 0; - - lua_settop(gL, 0); - lua_pushcfunction(gL, LUA_GetErrorMessage); - - for (hookp = linedefexecutorhooks; hookp; hookp = hookp->next) - { - if (strcmp(hookp->s.str, line->stringargs[0])) - continue; - - ps_lua_mobjhooks++; - if (lua_gettop(gL) == 1) - { - LUA_PushUserdata(gL, line, META_LINE); - LUA_PushUserdata(gL, mo, META_MOBJ); - LUA_PushUserdata(gL, sector, META_SECTOR); - } - PushHook(gL, hookp); - lua_pushvalue(gL, -4); - lua_pushvalue(gL, -4); - lua_pushvalue(gL, -4); - if (lua_pcall(gL, 3, 0, 1)) { - CONS_Alert(CONS_WARNING,"%s\n",lua_tostring(gL, -1)); - lua_pop(gL, 1); - } - hooked = true; - } - - lua_settop(gL, 0); - return hooked; -} - -// Hook for player chat -// Added the "mute" field. It's set to true if the message was supposed to be eaten by spam protection. -// But for netgame consistency purposes, this hook is ran first reguardless, so this boolean allows for modders to adapt if they so desire. -boolean LUAh_PlayerMsg(int source, int target, int flags, char *msg, int mute) -{ - hook_p hookp; - boolean hooked = false; - if (!gL || !(hooksAvailable[hook_PlayerMsg/8] & (1<<(hook_PlayerMsg%8)))) - return false; - - lua_settop(gL, 0); - lua_pushcfunction(gL, LUA_GetErrorMessage); - - for (hookp = roothook; hookp; hookp = hookp->next) - { - if (hookp->type != hook_PlayerMsg) - continue; - - if (lua_gettop(gL) == 1) - { - if (lua_gettop(gL) == 0) + if (cv_perfstats.value == 3) { - LUA_PushUserdata(gL, &players[source], META_PLAYER); // Source player - if (flags & 2 /*HU_CSAY*/) { // csay TODO: make HU_CSAY accessible outside hu_stuff.c - lua_pushinteger(gL, 3); // type - lua_pushnil(gL); // target - } else if (target == -1) { // sayteam - lua_pushinteger(gL, 1); // type - lua_pushnil(gL); // target - } else if (target == 0) { // say - lua_pushinteger(gL, 0); // type - lua_pushnil(gL); // target - } else { // sayto - lua_pushinteger(gL, 2); // type - LUA_PushUserdata(gL, &players[target-1], META_PLAYER); // target + lua_pushvalue(gL, -1);/* need the function again */ + time_taken = I_GetPreciseTime(); + } + + call_single_hook(&hook); + + if (cv_perfstats.value == 3) + { + lua_Debug ar; + time_taken = I_GetPreciseTime() - time_taken; + lua_getinfo(gL, ">S", &ar); + PS_SetThinkFrameHookInfo(hook_index, time_taken, ar.short_src); + hook_index++; + } + } + + lua_settop(gL, 0); + } +} + +int LUA_HookMobjLineCollide(mobj_t *mobj, line_t *line) +{ + Hook_State hook; + if (prepare_mobj_hook(&hook, 0, MOBJ_HOOK(MobjLineCollide), mobj->type)) + { + LUA_PushUserdata(gL, mobj, META_MOBJ); + LUA_PushUserdata(gL, line, META_LINE); + call_hooks(&hook, 1, res_force); + } + return hook.status; +} + +int LUA_HookTouchSpecial(mobj_t *special, mobj_t *toucher) +{ + Hook_State hook; + if (prepare_mobj_hook(&hook, false, MOBJ_HOOK(TouchSpecial), special->type)) + { + LUA_PushUserdata(gL, special, META_MOBJ); + LUA_PushUserdata(gL, toucher, META_MOBJ); + call_hooks(&hook, 1, res_true); + } + return hook.status; +} + +static int damage_hook +( + mobj_t *target, + mobj_t *inflictor, + mobj_t *source, + INT32 damage, + UINT8 damagetype, + int hook_type, + Hook_Callback results_handler +){ + Hook_State hook; + if (prepare_mobj_hook(&hook, 0, hook_type, target->type)) + { + LUA_PushUserdata(gL, target, META_MOBJ); + LUA_PushUserdata(gL, inflictor, META_MOBJ); + LUA_PushUserdata(gL, source, META_MOBJ); + if (hook_type != MOBJ_HOOK(MobjDeath)) + lua_pushinteger(gL, damage); + lua_pushinteger(gL, damagetype); + call_hooks(&hook, 1, results_handler); + } + return hook.status; +} + +int LUA_HookShouldDamage(mobj_t *target, mobj_t *inflictor, mobj_t *source, INT32 damage, UINT8 damagetype) +{ + return damage_hook(target, inflictor, source, damage, damagetype, + MOBJ_HOOK(ShouldDamage), res_force); +} + +int LUA_HookMobjDamage(mobj_t *target, mobj_t *inflictor, mobj_t *source, INT32 damage, UINT8 damagetype) +{ + return damage_hook(target, inflictor, source, damage, damagetype, + MOBJ_HOOK(MobjDamage), res_true); +} + +int LUA_HookMobjDeath(mobj_t *target, mobj_t *inflictor, mobj_t *source, UINT8 damagetype) +{ + return damage_hook(target, inflictor, source, 0, damagetype, + MOBJ_HOOK(MobjDeath), res_true); +} + +int LUA_HookMobjMoveBlocked(mobj_t *t1, mobj_t *t2, line_t *line) +{ + Hook_State hook; + if (prepare_mobj_hook(&hook, 0, MOBJ_HOOK(MobjMoveBlocked), t1->type)) + { + LUA_PushUserdata(gL, t1, META_MOBJ); + LUA_PushUserdata(gL, t2, META_MOBJ); + LUA_PushUserdata(gL, line, META_LINE); + call_hooks(&hook, 1, res_true); + } + return hook.status; +} + +typedef struct { + mobj_t * tails; + ticcmd_t * cmd; +} BotAI_State; + +static boolean checkbotkey(const char *field) +{ + return lua_toboolean(gL, -1) && strcmp(lua_tostring(gL, -2), field) == 0; +} + +static void res_botai(Hook_State *hook) +{ + BotAI_State *botai = hook->userdata; + + int k[8]; + + int fields = 0; + + // This turns forward, backward, left, right, jump, and spin into a proper ticcmd for tails. + if (lua_istable(gL, -8)) { + lua_pushnil(gL); // key + while (lua_next(gL, -9)) { +#define CHECK(n, f) (checkbotkey(f) ? (k[(n)-1] = 1) : 0) + if ( + CHECK(1, "forward") || CHECK(2, "backward") || + CHECK(3, "left") || CHECK(4, "right") || + CHECK(5, "strafeleft") || CHECK(6, "straferight") || + CHECK(7, "jump") || CHECK(8, "spin") + ){ + if (8 <= ++fields) + { + lua_pop(gL, 2); // pop key and value + break; } - lua_pushstring(gL, msg); // msg - if (mute) - lua_pushboolean(gL, true); // the message was supposed to be eaten by spamprotecc. - else - lua_pushboolean(gL, false); - } - PushHook(gL, hookp); - lua_pushvalue(gL, -6); - lua_pushvalue(gL, -6); - lua_pushvalue(gL, -6); - lua_pushvalue(gL, -6); - lua_pushvalue(gL, -6); - if (lua_pcall(gL, 4, 1, 1)) { - if (!hookp->error || cv_debug & DBG_LUA) - CONS_Alert(CONS_WARNING,"%s\n",lua_tostring(gL, -1)); - lua_pop(gL, 1); - hookp->error = true; - continue; - } - if (lua_toboolean(gL, -1)) - hooked = true; - lua_pop(gL, 1); - } - } - - lua_settop(gL, 0); - return hooked; -} - -// Hook for hurt messages -boolean LUAh_HurtMsg(player_t *player, mobj_t *inflictor, mobj_t *source, UINT8 damagetype) -{ - hook_p hookp; - boolean hooked = false; - if (!gL || !(hooksAvailable[hook_HurtMsg/8] & (1<<(hook_HurtMsg%8)))) - return false; - - lua_settop(gL, 0); - lua_pushcfunction(gL, LUA_GetErrorMessage); - - for (hookp = roothook; hookp; hookp = hookp->next) - { - if (hookp->type != hook_HurtMsg - || (hookp->s.mt && !(inflictor && hookp->s.mt == inflictor->type))) - continue; - - if (lua_gettop(gL) == 1) - { - LUA_PushUserdata(gL, player, META_PLAYER); - LUA_PushUserdata(gL, inflictor, META_MOBJ); - LUA_PushUserdata(gL, source, META_MOBJ); - lua_pushinteger(gL, damagetype); - } - PushHook(gL, hookp); - lua_pushvalue(gL, -5); - lua_pushvalue(gL, -5); - lua_pushvalue(gL, -5); - lua_pushvalue(gL, -5); - if (lua_pcall(gL, 4, 1, 1)) { - if (!hookp->error || cv_debug & DBG_LUA) - CONS_Alert(CONS_WARNING,"%s\n",lua_tostring(gL, -1)); - lua_pop(gL, 1); - hookp->error = true; - continue; - } - if (lua_toboolean(gL, -1)) - hooked = true; - lua_pop(gL, 1); - } - - lua_settop(gL, 0); - return hooked; -} - -void LUAh_NetArchiveHook(lua_CFunction archFunc) -{ - hook_p hookp; - int errorhandlerindex; - if (!gL || !(hooksAvailable[hook_NetVars/8] & (1<<(hook_NetVars%8)))) - return; - - // stack: tables - I_Assert(lua_gettop(gL) > 0); - I_Assert(lua_istable(gL, -1)); - - lua_pushcfunction(gL, LUA_GetErrorMessage); - errorhandlerindex = lua_gettop(gL); - - // tables becomes an upvalue of archFunc - lua_pushvalue(gL, -2); - lua_pushcclosure(gL, archFunc, 1); - // stack: tables, archFunc - - for (hookp = roothook; hookp; hookp = hookp->next) - { - if (hookp->type != hook_NetVars) - continue; - - PushHook(gL, hookp); - lua_pushvalue(gL, -2); // archFunc - if (lua_pcall(gL, 1, 0, errorhandlerindex)) { - CONS_Alert(CONS_WARNING,"%s\n",lua_tostring(gL, -1)); - lua_pop(gL, 1); - } - } - - lua_pop(gL, 2); // Pop archFunc and error handler - // stack: tables -} - -boolean LUAh_MapThingSpawn(mobj_t *mo, mapthing_t *mthing) -{ - hook_p hookp; - boolean hooked = false; - if (!gL || !(hooksAvailable[hook_MapThingSpawn/8] & (1<<(hook_MapThingSpawn%8)))) - return false; - - if (!(mobjhooks[MT_NULL] || mobjhooks[mo->type])) - return false; - - lua_settop(gL, 0); - lua_pushcfunction(gL, LUA_GetErrorMessage); - - // Look for all generic mobj map thing spawn hooks - for (hookp = mobjhooks[MT_NULL]; hookp; hookp = hookp->next) - { - if (hookp->type != hook_MapThingSpawn) - continue; - - ps_lua_mobjhooks++; - if (lua_gettop(gL) == 1) - { - LUA_PushUserdata(gL, mo, META_MOBJ); - LUA_PushUserdata(gL, mthing, META_MAPTHING); - } - PushHook(gL, hookp); - lua_pushvalue(gL, -3); - lua_pushvalue(gL, -3); - if (lua_pcall(gL, 2, 1, 1)) { - if (!hookp->error || cv_debug & DBG_LUA) - CONS_Alert(CONS_WARNING,"%s\n",lua_tostring(gL, -1)); - lua_pop(gL, 1); - hookp->error = true; - continue; - } - if (lua_toboolean(gL, -1)) - hooked = true; - lua_pop(gL, 1); - } - - for (hookp = mobjhooks[mo->type]; hookp; hookp = hookp->next) - { - if (hookp->type != hook_MapThingSpawn) - continue; - - ps_lua_mobjhooks++; - if (lua_gettop(gL) == 1) - { - LUA_PushUserdata(gL, mo, META_MOBJ); - LUA_PushUserdata(gL, mthing, META_MAPTHING); - } - PushHook(gL, hookp); - lua_pushvalue(gL, -3); - lua_pushvalue(gL, -3); - if (lua_pcall(gL, 2, 1, 1)) { - if (!hookp->error || cv_debug & DBG_LUA) - CONS_Alert(CONS_WARNING,"%s\n",lua_tostring(gL, -1)); - lua_pop(gL, 1); - hookp->error = true; - continue; - } - if (lua_toboolean(gL, -1)) - hooked = true; - lua_pop(gL, 1); - } - - lua_settop(gL, 0); - return hooked; -} - -// Hook for P_PlayerAfterThink Smiles mobj-following -boolean LUAh_FollowMobj(player_t *player, mobj_t *mobj) -{ - hook_p hookp; - boolean hooked = false; - if (!gL || !(hooksAvailable[hook_FollowMobj/8] & (1<<(hook_FollowMobj%8)))) - return 0; - - if (!(mobjhooks[MT_NULL] || mobjhooks[mobj->type])) - return 0; - - lua_settop(gL, 0); - lua_pushcfunction(gL, LUA_GetErrorMessage); - - // Look for all generic mobj follow item hooks - for (hookp = mobjhooks[MT_NULL]; hookp; hookp = hookp->next) - { - if (hookp->type != hook_FollowMobj) - continue; - - ps_lua_mobjhooks++; - if (lua_gettop(gL) == 1) - { - LUA_PushUserdata(gL, player, META_PLAYER); - LUA_PushUserdata(gL, mobj, META_MOBJ); - } - PushHook(gL, hookp); - lua_pushvalue(gL, -3); - lua_pushvalue(gL, -3); - if (lua_pcall(gL, 2, 1, 1)) { - if (!hookp->error || cv_debug & DBG_LUA) - CONS_Alert(CONS_WARNING,"%s\n",lua_tostring(gL, -1)); - lua_pop(gL, 1); - hookp->error = true; - continue; - } - if (lua_toboolean(gL, -1)) - hooked = true; - lua_pop(gL, 1); - } - - for (hookp = mobjhooks[mobj->type]; hookp; hookp = hookp->next) - { - if (hookp->type != hook_FollowMobj) - continue; - - ps_lua_mobjhooks++; - if (lua_gettop(gL) == 1) - { - LUA_PushUserdata(gL, player, META_PLAYER); - LUA_PushUserdata(gL, mobj, META_MOBJ); - } - PushHook(gL, hookp); - lua_pushvalue(gL, -3); - lua_pushvalue(gL, -3); - if (lua_pcall(gL, 2, 1, 1)) { - if (!hookp->error || cv_debug & DBG_LUA) - CONS_Alert(CONS_WARNING,"%s\n",lua_tostring(gL, -1)); - lua_pop(gL, 1); - hookp->error = true; - continue; - } - if (lua_toboolean(gL, -1)) - hooked = true; - lua_pop(gL, 1); - } - - lua_settop(gL, 0); - return hooked; -} - -// Hook for P_PlayerCanDamage -UINT8 LUAh_PlayerCanDamage(player_t *player, mobj_t *mobj) -{ - hook_p hookp; - UINT8 shouldCollide = 0; // 0 = default, 1 = force yes, 2 = force no. - if (!gL || !(hooksAvailable[hook_PlayerCanDamage/8] & (1<<(hook_PlayerCanDamage%8)))) - return 0; - - lua_settop(gL, 0); - lua_pushcfunction(gL, LUA_GetErrorMessage); - - for (hookp = playerhooks; hookp; hookp = hookp->next) - { - if (hookp->type != hook_PlayerCanDamage) - continue; - - ps_lua_mobjhooks++; - if (lua_gettop(gL) == 1) - { - LUA_PushUserdata(gL, player, META_PLAYER); - LUA_PushUserdata(gL, mobj, META_MOBJ); - } - PushHook(gL, hookp); - lua_pushvalue(gL, -3); - lua_pushvalue(gL, -3); - if (lua_pcall(gL, 2, 1, 1)) { - if (!hookp->error || cv_debug & DBG_LUA) - CONS_Alert(CONS_WARNING,"%s\n",lua_tostring(gL, -1)); - lua_pop(gL, 1); - hookp->error = true; - continue; - } - if (!lua_isnil(gL, -1)) - { // if nil, leave shouldCollide = 0. - if (lua_toboolean(gL, -1)) - shouldCollide = 1; // Force yes - else - shouldCollide = 2; // Force no - } - lua_pop(gL, 1); - } - - lua_settop(gL, 0); - return shouldCollide; -} - -void LUAh_PlayerQuit(player_t *plr, kickreason_t reason) -{ - hook_p hookp; - if (!gL || !(hooksAvailable[hook_PlayerQuit/8] & (1<<(hook_PlayerQuit%8)))) - return; - - lua_settop(gL, 0); - lua_pushcfunction(gL, LUA_GetErrorMessage); - - for (hookp = roothook; hookp; hookp = hookp->next) - { - if (hookp->type != hook_PlayerQuit) - continue; - - if (lua_gettop(gL) == 1) - { - LUA_PushUserdata(gL, plr, META_PLAYER); // Player that quit - lua_pushinteger(gL, reason); // Reason for quitting - } - PushHook(gL, hookp); - lua_pushvalue(gL, -3); - lua_pushvalue(gL, -3); - if (lua_pcall(gL, 2, 0, 1)) { - CONS_Alert(CONS_WARNING,"%s\n",lua_tostring(gL, -1)); - lua_pop(gL, 1); - } - } - - lua_settop(gL, 0); -} - -// Hook for Y_Ticker -void LUAh_IntermissionThinker(void) -{ - hook_p hookp; - if (!gL || !(hooksAvailable[hook_IntermissionThinker/8] & (1<<(hook_IntermissionThinker%8)))) - return; - - lua_pushcfunction(gL, LUA_GetErrorMessage); - - for (hookp = roothook; hookp; hookp = hookp->next) - { - if (hookp->type != hook_IntermissionThinker) - continue; - - PushHook(gL, hookp); - if (lua_pcall(gL, 0, 0, 1)) { - if (!hookp->error || cv_debug & DBG_LUA) - CONS_Alert(CONS_WARNING,"%s\n",lua_tostring(gL, -1)); - lua_pop(gL, 1); - hookp->error = true; - } - } - - lua_pop(gL, 1); // Pop error handler -} - -// Hook for team switching -// It's just an edit of LUAh_ViewpointSwitch. -boolean LUAh_TeamSwitch(player_t *player, int newteam, boolean fromspectators, boolean tryingautobalance, boolean tryingscramble) -{ - hook_p hookp; - boolean canSwitchTeam = true; - if (!gL || !(hooksAvailable[hook_TeamSwitch/8] & (1<<(hook_TeamSwitch%8)))) - return true; - - lua_settop(gL, 0); - lua_pushcfunction(gL, LUA_GetErrorMessage); - - for (hookp = playerhooks; hookp; hookp = hookp->next) - { - if (hookp->type != hook_TeamSwitch) - continue; - - if (lua_gettop(gL) == 1) - { - LUA_PushUserdata(gL, player, META_PLAYER); - lua_pushinteger(gL, newteam); - lua_pushboolean(gL, fromspectators); - lua_pushboolean(gL, tryingautobalance); - lua_pushboolean(gL, tryingscramble); - } - PushHook(gL, hookp); - lua_pushvalue(gL, -6); - lua_pushvalue(gL, -6); - lua_pushvalue(gL, -6); - lua_pushvalue(gL, -6); - lua_pushvalue(gL, -6); - if (lua_pcall(gL, 5, 1, 1)) { - if (!hookp->error || cv_debug & DBG_LUA) - CONS_Alert(CONS_WARNING,"%s\n",lua_tostring(gL, -1)); - lua_pop(gL, 1); - hookp->error = true; - continue; - } - if (!lua_isnil(gL, -1) && !lua_toboolean(gL, -1)) - canSwitchTeam = false; // Can't switch team - lua_pop(gL, 1); - } - - lua_settop(gL, 0); - return canSwitchTeam; -} - -// Hook for spy mode -UINT8 LUAh_ViewpointSwitch(player_t *player, player_t *newdisplayplayer, boolean forced) -{ - hook_p hookp; - UINT8 canSwitchView = 0; // 0 = default, 1 = force yes, 2 = force no. - if (!gL || !(hooksAvailable[hook_ViewpointSwitch/8] & (1<<(hook_ViewpointSwitch%8)))) - return 0; - - lua_settop(gL, 0); - lua_pushcfunction(gL, LUA_GetErrorMessage); - - hud_running = true; // local hook - - for (hookp = playerhooks; hookp; hookp = hookp->next) - { - if (hookp->type != hook_ViewpointSwitch) - continue; - - if (lua_gettop(gL) == 1) - { - LUA_PushUserdata(gL, player, META_PLAYER); - LUA_PushUserdata(gL, newdisplayplayer, META_PLAYER); - lua_pushboolean(gL, forced); - } - PushHook(gL, hookp); - lua_pushvalue(gL, -4); - lua_pushvalue(gL, -4); - lua_pushvalue(gL, -4); - if (lua_pcall(gL, 3, 1, 1)) { - if (!hookp->error || cv_debug & DBG_LUA) - CONS_Alert(CONS_WARNING,"%s\n",lua_tostring(gL, -1)); - lua_pop(gL, 1); - hookp->error = true; - continue; - } - if (!lua_isnil(gL, -1)) - { // if nil, leave canSwitchView = 0. - if (lua_toboolean(gL, -1)) - canSwitchView = 1; // Force viewpoint switch - else - canSwitchView = 2; // Skip viewpoint switch - } - lua_pop(gL, 1); - } - - lua_settop(gL, 0); - - hud_running = false; - - return canSwitchView; -} - -boolean LUAh_ShouldJingleContinue(player_t *player, const char *musname) -{ - hook_p hookp; - boolean keepplaying = false; - if (!gL || !(hooksAvailable[hook_ShouldJingleContinue/8] & (1<<(hook_ShouldJingleContinue%8)))) - return true; - - lua_settop(gL, 0); - lua_pushcfunction(gL, LUA_GetErrorMessage); - - hud_running = true; // local hook - - for (hookp = roothook; hookp; hookp = hookp->next) - { - if (hookp->type != hook_ShouldJingleContinue - || (hookp->s.str && strcmp(hookp->s.str, musname))) - continue; - - if (lua_gettop(gL) == 1) - { - LUA_PushUserdata(gL, player, META_PLAYER); - lua_pushstring(gL, musname); - } - PushHook(gL, hookp); - lua_pushvalue(gL, -3); - lua_pushvalue(gL, -3); - if (lua_pcall(gL, 2, 1, 1)) { - if (!hookp->error || cv_debug & DBG_LUA) - CONS_Alert(CONS_WARNING,"%s\n",lua_tostring(gL, -1)); - lua_pop(gL, 1); - hookp->error = true; - continue; - } - if (!lua_isnil(gL, -1) && lua_toboolean(gL, -1)) - keepplaying = true; // Keep playing this boolean - lua_pop(gL, 1); - } - - lua_settop(gL, 0); - - hud_running = false; - - return keepplaying; -} - -// Hook for music changes -boolean LUAh_MusicChange(const char *oldname, char *newname, UINT16 *mflags, boolean *looping, - UINT32 *position, UINT32 *prefadems, UINT32 *fadeinms) -{ - hook_p hookp; - boolean hooked = false; - - if (!gL || !(hooksAvailable[hook_MusicChange/8] & (1<<(hook_MusicChange%8)))) - return false; - - lua_settop(gL, 0); - lua_pushcfunction(gL, LUA_GetErrorMessage); - - for (hookp = roothook; hookp; hookp = hookp->next) - if (hookp->type == hook_MusicChange) - { - PushHook(gL, hookp); - lua_pushstring(gL, oldname); - lua_pushstring(gL, newname); - lua_pushinteger(gL, *mflags); - lua_pushboolean(gL, *looping); - lua_pushinteger(gL, *position); - lua_pushinteger(gL, *prefadems); - lua_pushinteger(gL, *fadeinms); - if (lua_pcall(gL, 7, 6, 1)) { - CONS_Alert(CONS_WARNING,"%s\n",lua_tostring(gL,-1)); - lua_pop(gL, 1); - continue; } - // output 1: true, false, or string musicname override - if (lua_isboolean(gL, -6) && lua_toboolean(gL, -6)) - hooked = true; - else if (lua_isstring(gL, -6)) - strncpy(newname, lua_tostring(gL, -6), 7); - // output 2: mflags override - if (lua_isnumber(gL, -5)) - *mflags = lua_tonumber(gL, -5); - // output 3: looping override - if (lua_isboolean(gL, -4)) - *looping = lua_toboolean(gL, -4); - // output 4: position override - if (lua_isnumber(gL, -3)) - *position = lua_tonumber(gL, -3); - // output 5: prefadems override - if (lua_isnumber(gL, -2)) - *prefadems = lua_tonumber(gL, -2); - // output 6: fadeinms override - if (lua_isnumber(gL, -1)) - *fadeinms = lua_tonumber(gL, -1); - - lua_pop(gL, 7); // Pop returned values and error handler + lua_pop(gL, 1); // pop value +#undef CHECK } - - lua_settop(gL, 0); - newname[6] = 0; - return hooked; -} - -// Hook for Y_VoteTicker -void LUAh_VoteThinker(void) -{ - hook_p hookp; - if (!gL || !(hooksAvailable[hook_VoteThinker/8] & (1<<(hook_VoteThinker%8)))) - return; - - for (hookp = roothook; hookp; hookp = hookp->next) - if (hookp->type == hook_VoteThinker) + } else { + while (fields < 8) { - lua_pushfstring(gL, FMT_HOOKID, hookp->id); - lua_gettable(gL, LUA_REGISTRYINDEX); - if (lua_pcall(gL, 0, 0, 0)) { - if (!hookp->error || cv_debug & DBG_LUA) - CONS_Alert(CONS_WARNING,"%s\n",lua_tostring(gL, -1)); - lua_pop(gL, 1); - hookp->error = true; - } - } -} - -// Hook for game quitting -void LUAh_GameQuit(boolean quitting) -{ - hook_p hookp; - if (!gL || !(hooksAvailable[hook_GameQuit/8] & (1<<(hook_GameQuit%8)))) - return; - - lua_pushcfunction(gL, LUA_GetErrorMessage); - - for (hookp = roothook; hookp; hookp = hookp->next) - { - if (hookp->type != hook_GameQuit) - continue; - - PushHook(gL, hookp); - lua_pushboolean(gL, quitting); - if (lua_pcall(gL, 1, 0, 1)) { - if (!hookp->error || cv_debug & DBG_LUA) - CONS_Alert(CONS_WARNING,"%s\n",lua_tostring(gL, -1)); - lua_pop(gL, 1); - hookp->error = true; + k[fields] = lua_toboolean(gL, -8 + fields); + fields++; } } - lua_pop(gL, 1); // Pop error handler + B_KeysToTiccmd(botai->tails, botai->cmd, + k[0],k[1],k[2],k[3],k[4],k[5],k[6],k[7]); + + hook->status = true; +} + +int LUA_HookBotAI(mobj_t *sonic, mobj_t *tails, ticcmd_t *cmd) +{ + const char *skin = ((skin_t *)tails->skin)->name; + + Hook_State hook; + BotAI_State botai; + + if (prepare_string_hook(&hook, false, STRING_HOOK(BotAI), skin)) + { + LUA_PushUserdata(gL, sonic, META_MOBJ); + LUA_PushUserdata(gL, tails, META_MOBJ); + + botai.tails = tails; + botai.cmd = cmd; + + hook.userdata = &botai; + + call_hooks(&hook, 8, res_botai); + } + + return hook.status; +} + +void LUA_HookLinedefExecute(line_t *line, mobj_t *mo, sector_t *sector) +{ + Hook_State hook; + if (prepare_string_hook + (&hook, 0, STRING_HOOK(LinedefExecute), line->stringargs[0])) + { + LUA_PushUserdata(gL, line, META_LINE); + LUA_PushUserdata(gL, mo, META_MOBJ); + LUA_PushUserdata(gL, sector, META_SECTOR); + ps_lua_mobjhooks.value.i += call_hooks(&hook, 0, res_none); + } +} + +int LUA_HookPlayerMsg(int source, int target, int flags, char *msg) +{ + Hook_State hook; + if (prepare_hook(&hook, false, HOOK(PlayerMsg))) + { + LUA_PushUserdata(gL, &players[source], META_PLAYER); // Source player + if (flags & 2 /*HU_CSAY*/) { // csay TODO: make HU_CSAY accessible outside hu_stuff.c + lua_pushinteger(gL, 3); // type + lua_pushnil(gL); // target + } else if (target == -1) { // sayteam + lua_pushinteger(gL, 1); // type + lua_pushnil(gL); // target + } else if (target == 0) { // say + lua_pushinteger(gL, 0); // type + lua_pushnil(gL); // target + } else { // sayto + lua_pushinteger(gL, 2); // type + LUA_PushUserdata(gL, &players[target-1], META_PLAYER); // target + } + lua_pushstring(gL, msg); // msg + call_hooks(&hook, 1, res_true); + } + return hook.status; +} + +int LUA_HookHurtMsg(player_t *player, mobj_t *inflictor, mobj_t *source, UINT8 damagetype) +{ + Hook_State hook; + if (prepare_hook(&hook, false, HOOK(HurtMsg))) + { + LUA_PushUserdata(gL, player, META_PLAYER); + LUA_PushUserdata(gL, inflictor, META_MOBJ); + LUA_PushUserdata(gL, source, META_MOBJ); + lua_pushinteger(gL, damagetype); + call_hooks(&hook, 1, res_true); + } + return hook.status; +} + +void LUA_HookNetArchive(lua_CFunction archFunc) +{ + const hook_t * map = &hookIds[HOOK(NetVars)]; + Hook_State hook; + /* this is a remarkable case where the stack isn't reset */ + if (map->numHooks > 0) + { + // stack: tables + I_Assert(lua_gettop(gL) > 0); + I_Assert(lua_istable(gL, -1)); + + push_error_handler(); + lua_insert(gL, EINDEX); + + begin_hook_values(&hook); + + // tables becomes an upvalue of archFunc + lua_pushvalue(gL, -1); + lua_pushcclosure(gL, archFunc, 1); + // stack: tables, archFunc + + init_hook_call(&hook, 0, res_none); + call_mapped(&hook, map); + + lua_pop(gL, 1); // pop archFunc + lua_remove(gL, EINDEX); // pop error handler + // stack: tables + } +} + +int LUA_HookMapThingSpawn(mobj_t *mobj, mapthing_t *mthing) +{ + Hook_State hook; + if (prepare_mobj_hook(&hook, false, MOBJ_HOOK(MapThingSpawn), mobj->type)) + { + LUA_PushUserdata(gL, mobj, META_MOBJ); + LUA_PushUserdata(gL, mthing, META_MAPTHING); + call_hooks(&hook, 1, res_true); + } + return hook.status; +} + +int LUA_HookFollowMobj(player_t *player, mobj_t *mobj) +{ + Hook_State hook; + if (prepare_mobj_hook(&hook, false, MOBJ_HOOK(FollowMobj), mobj->type)) + { + LUA_PushUserdata(gL, player, META_PLAYER); + LUA_PushUserdata(gL, mobj, META_MOBJ); + call_hooks(&hook, 1, res_true); + } + return hook.status; +} + +int LUA_HookPlayerCanDamage(player_t *player, mobj_t *mobj) +{ + Hook_State hook; + if (prepare_hook(&hook, 0, HOOK(PlayerCanDamage))) + { + LUA_PushUserdata(gL, player, META_PLAYER); + LUA_PushUserdata(gL, mobj, META_MOBJ); + call_hooks(&hook, 1, res_force); + } + return hook.status; +} + +void LUA_HookPlayerQuit(player_t *plr, kickreason_t reason) +{ + Hook_State hook; + if (prepare_hook(&hook, 0, HOOK(PlayerQuit))) + { + LUA_PushUserdata(gL, plr, META_PLAYER); // Player that quit + lua_pushinteger(gL, reason); // Reason for quitting + call_hooks(&hook, 0, res_none); + } +} + +int LUA_HookTeamSwitch(player_t *player, int newteam, boolean fromspectators, boolean tryingautobalance, boolean tryingscramble) +{ + Hook_State hook; + if (prepare_hook(&hook, true, HOOK(TeamSwitch))) + { + LUA_PushUserdata(gL, player, META_PLAYER); + lua_pushinteger(gL, newteam); + lua_pushboolean(gL, fromspectators); + lua_pushboolean(gL, tryingautobalance); + lua_pushboolean(gL, tryingscramble); + call_hooks(&hook, 1, res_false); + } + return hook.status; +} + +int LUA_HookViewpointSwitch(player_t *player, player_t *newdisplayplayer, boolean forced) +{ + Hook_State hook; + if (prepare_hook(&hook, 0, HOOK(ViewpointSwitch))) + { + LUA_PushUserdata(gL, player, META_PLAYER); + LUA_PushUserdata(gL, newdisplayplayer, META_PLAYER); + lua_pushboolean(gL, forced); + + hud_running = true; // local hook + call_hooks(&hook, 1, res_force); + hud_running = false; + } + return hook.status; +} + +int LUA_HookSeenPlayer(player_t *player, player_t *seenfriend) +{ + Hook_State hook; + if (prepare_hook(&hook, true, HOOK(SeenPlayer))) + { + LUA_PushUserdata(gL, player, META_PLAYER); + LUA_PushUserdata(gL, seenfriend, META_PLAYER); + + hud_running = true; // local hook + call_hooks(&hook, 1, res_false); + hud_running = false; + } + return hook.status; +} + +int LUA_HookShouldJingleContinue(player_t *player, const char *musname) +{ + Hook_State hook; + if (prepare_string_hook + (&hook, false, STRING_HOOK(ShouldJingleContinue), musname)) + { + LUA_PushUserdata(gL, player, META_PLAYER); + push_string(); + + hud_running = true; // local hook + call_hooks(&hook, 1, res_true); + hud_running = false; + } + return hook.status; } -// Hook for G_BuildTicCmd boolean hook_cmd_running = false; -boolean LUAh_PlayerCmd(player_t *player, ticcmd_t *cmd) + +static void update_music_name(struct MusicChange *musicchange) { - hook_p hookp; - boolean hooked = false; - if (!gL || !(hooksAvailable[hook_PlayerCmd/8] & (1<<(hook_PlayerCmd%8)))) - return false; + size_t length; + const char * new = lua_tolstring(gL, -6, &length); - lua_settop(gL, 0); + if (length < 7) + { + strcpy(musicchange->newname, new); + lua_pushvalue(gL, -6);/* may as well keep it for next call */ + } + else + { + memcpy(musicchange->newname, new, 6); + musicchange->newname[6] = '\0'; + lua_pushlstring(gL, new, 6); + } - hook_cmd_running = true; - for (hookp = roothook; hookp; hookp = hookp->next) - if (hookp->type == hook_PlayerCmd) + lua_replace(gL, -7); +} + +static void res_musicchange(Hook_State *hook) +{ + struct MusicChange *musicchange = hook->userdata; + + // output 1: true, false, or string musicname override + if (lua_isstring(gL, -6)) + update_music_name(musicchange); + else if (lua_isboolean(gL, -6) && lua_toboolean(gL, -6)) + hook->status = true; + + // output 2: mflags override + if (lua_isnumber(gL, -5)) + *musicchange->mflags = lua_tonumber(gL, -5); + // output 3: looping override + if (lua_isboolean(gL, -4)) + *musicchange->looping = lua_toboolean(gL, -4); + // output 4: position override + if (lua_isnumber(gL, -3)) + *musicchange->position = lua_tonumber(gL, -3); + // output 5: prefadems override + if (lua_isnumber(gL, -2)) + *musicchange->prefadems = lua_tonumber(gL, -2); + // output 6: fadeinms override + if (lua_isnumber(gL, -1)) + *musicchange->fadeinms = lua_tonumber(gL, -1); +} + +int LUA_HookMusicChange(const char *oldname, struct MusicChange *param) +{ + const int type = HOOK(MusicChange); + const hook_t * map = &hookIds[type]; + + Hook_State hook; + + int k; + + if (prepare_hook(&hook, false, type)) + { + init_hook_call(&hook, 6, res_musicchange); + hook.values = 7;/* values pushed later */ + hook.userdata = param; + + lua_pushstring(gL, oldname);/* the only constant value */ + lua_pushstring(gL, param->newname);/* semi constant */ + + for (k = 0; k < map->numHooks; ++k) { - if (lua_gettop(gL) == 0) - { - LUA_PushUserdata(gL, player, META_PLAYER); - LUA_PushUserdata(gL, cmd, META_TICCMD); - } - lua_pushfstring(gL, FMT_HOOKID, hookp->id); - lua_gettable(gL, LUA_REGISTRYINDEX); + get_hook(&hook, map->ids, k); + lua_pushvalue(gL, -3); lua_pushvalue(gL, -3); - if (lua_pcall(gL, 2, 1, 0)) { - if (!hookp->error || cv_debug & DBG_LUA) - CONS_Alert(CONS_WARNING,"%s\n",lua_tostring(gL, -1)); - lua_pop(gL, 1); - hookp->error = true; - continue; - } - if (lua_toboolean(gL, -1)) - hooked = true; - lua_pop(gL, 1); + lua_pushinteger(gL, *param->mflags); + lua_pushboolean(gL, *param->looping); + lua_pushinteger(gL, *param->position); + lua_pushinteger(gL, *param->prefadems); + lua_pushinteger(gL, *param->fadeinms); + + call_single_hook_no_copy(&hook); } - hook_cmd_running = false; - lua_settop(gL, 0); - return hooked; + lua_settop(gL, 0); + } + + return hook.status; +} + +static void res_playerheight(Hook_State *hook) +{ + if (!lua_isnil(gL, -1)) + { + fixed_t returnedheight = lua_tonumber(gL, -1); + // 0 height has... strange results, but it's not problematic like negative heights are. + // when an object's height is set to a negative number directly with lua, it's forced to 0 instead. + // here, I think it's better to ignore negatives so that they don't replace any results of previous hooks! + if (returnedheight >= 0) + hook->status = returnedheight; + } +} + +fixed_t LUA_HookPlayerHeight(player_t *player) +{ + Hook_State hook; + if (prepare_hook(&hook, -1, HOOK(PlayerHeight))) + { + LUA_PushUserdata(gL, player, META_PLAYER); + call_hooks(&hook, 1, res_playerheight); + } + return hook.status; +} + +int LUA_HookPlayerCanEnterSpinGaps(player_t *player) +{ + Hook_State hook; + if (prepare_hook(&hook, 0, HOOK(PlayerCanEnterSpinGaps))) + { + LUA_PushUserdata(gL, player, META_PLAYER); + call_hooks(&hook, 1, res_force); + } + return hook.status; } diff --git a/src/s_sound.h b/src/s_sound.h index 515fcd4f4..461f376af 100644 --- a/src/s_sound.h +++ b/src/s_sound.h @@ -243,6 +243,16 @@ boolean S_RecallMusic(UINT16 status, boolean fromfirst); // Music Playback // +/* this is for the sake of the hook */ +struct MusicChange { + char * newname; + UINT16 * mflags; + boolean * looping; + UINT32 * position; + UINT32 * prefadems; + UINT32 * fadeinms; +}; + enum { MUS_SPECIAL = 1,/* powerups--invincibility, grow */ From b4d1fade5fa6fc9ba9501388f92f0dd8394f10ad Mon Sep 17 00:00:00 2001 From: James R Date: Mon, 5 Sep 2022 10:03:10 -0700 Subject: [PATCH 049/114] Apply b4fa98d2f to lua_hudlib.c Continued from 544812a45 --- src/lua_hook.h | 8 +- src/lua_hooklib.c | 8 +- src/lua_hud.h | 6 +- src/lua_hudlib.c | 238 +++++----------------------------------------- 4 files changed, 34 insertions(+), 226 deletions(-) diff --git a/src/lua_hook.h b/src/lua_hook.h index fc6a5f4ee..2c69e6b03 100644 --- a/src/lua_hook.h +++ b/src/lua_hook.h @@ -1,4 +1,4 @@ -// SONIC ROBO BLAST 2 + //----------------------------------------------------------------------------- // Copyright (C) 2012-2016 by John "JTE" Muniz. // Copyright (C) 2012-2022 by Sonic Team Junior. @@ -15,6 +15,8 @@ #include "s_sound.h" #include "d_event.h" +#include "lua_hudlib_drawlist.h" + /* Do you know what an 'X Macro' is? Such a macro is called over each element of a list and expands the input. I use it for the hook lists because both an enum @@ -110,12 +112,12 @@ ENUM (STRING_HOOK); /* dead simple, LUA_HOOK(GameQuit) */ #define LUA_HOOK(type) LUA_HookVoid(HOOK(type)) -#define LUA_HUDHOOK(type) LUA_HookHUD(HUD_HOOK(type)) +//#define LUA_HUDHOOK(type) LUA_HookHUD(HUD_HOOK(type)) extern boolean hook_cmd_running; void LUA_HookVoid(int hook); -void LUA_HookHUD(int hook); +void LUA_HookHUD(huddrawlist_h, int hook); int LUA_HookMobj(mobj_t *, int hook); int LUA_Hook2Mobj(mobj_t *, mobj_t *, int hook); diff --git a/src/lua_hooklib.c b/src/lua_hooklib.c index dc5069319..b2029cc95 100644 --- a/src/lua_hooklib.c +++ b/src/lua_hooklib.c @@ -630,6 +630,7 @@ int LUA_HookTiccmd(player_t *player, ticcmd_t *cmd, int hook_type) return hook.status; } +<<<<<<< Updated upstream int LUA_HookKey(event_t *event, int hook_type) { Hook_State hook; @@ -641,7 +642,10 @@ int LUA_HookKey(event_t *event, int hook_type) return hook.status; } -void LUA_HookHUD(int hook_type) +void LUA_HookHUD(int hook_type, huddrawlist_h list) +======= +void LUA_HookHUD(huddrawlist_h list, int hook_type) +>>>>>>> Stashed changes { const hook_t * map = &hudHookIds[hook_type]; Hook_State hook; @@ -650,7 +654,7 @@ void LUA_HookHUD(int hook_type) start_hook_stack(); begin_hook_values(&hook); - LUA_SetHudHook(hook_type); + LUA_SetHudHook(hook_type, list); hud_running = true; // local hook init_hook_call(&hook, 0, res_none); diff --git a/src/lua_hud.h b/src/lua_hud.h index 35db4ef87..fc91811f0 100644 --- a/src/lua_hud.h +++ b/src/lua_hud.h @@ -47,10 +47,6 @@ extern boolean hud_running; boolean LUA_HudEnabled(enum hud option); -void LUAh_GameHUD(player_t *stplyr, huddrawlist_h list); -void LUAh_ScoresHUD(huddrawlist_h list); -void LUAh_TitleHUD(huddrawlist_h list); -void LUAh_TitleCardHUD(player_t *stplayr, huddrawlist_h list); -void LUAh_IntermissionHUD(huddrawlist_h list); +void LUA_SetHudHook(int hook, huddrawlist_h list); #endif // __LUA_HUD_H__ diff --git a/src/lua_hudlib.c b/src/lua_hudlib.c index ad6555541..c8d0d3b10 100644 --- a/src/lua_hudlib.c +++ b/src/lua_hudlib.c @@ -29,14 +29,13 @@ #include "lua_script.h" #include "lua_libs.h" #include "lua_hud.h" +#include "lua_hook.h" #define HUDONLY if (!hud_running) return luaL_error(L, "HUD rendering code should not be called outside of rendering hooks!"); boolean hud_running = false; static UINT8 hud_enabled[(hud_MAX/8)+1]; -static UINT8 hudAvailable; // hud hooks field - static UINT8 camnum = 1; // must match enum hud in lua_hud.h @@ -79,21 +78,6 @@ static const char *const patch_opt[] = { "topoffset", NULL}; -enum hudhook { - hudhook_game = 0, - hudhook_scores, - hudhook_intermission, - hudhook_title, - hudhook_titlecard -}; -static const char *const hudhook_opt[] = { - "game", - "scores", - "intermission", - "title", - "titlecard", - NULL}; - // alignment types for v.drawString enum align { align_left = 0, @@ -1249,6 +1233,8 @@ static luaL_Reg lib_draw[] = { {NULL, NULL} }; +static int lib_draw_ref; + // // lib_hud // @@ -1282,28 +1268,7 @@ static int lib_hudenabled(lua_State *L) } // add a HUD element for rendering -static int lib_hudadd(lua_State *L) -{ - enum hudhook field; - - luaL_checktype(L, 1, LUA_TFUNCTION); - field = luaL_checkoption(L, 2, "game", hudhook_opt); - - if (!lua_lumploading) - return luaL_error(L, "This function cannot be called from within a hook or coroutine!"); - - lua_getfield(L, LUA_REGISTRYINDEX, "HUD"); - I_Assert(lua_istable(L, -1)); - lua_rawgeti(L, -1, field+2); // HUD[2+] - I_Assert(lua_istable(L, -1)); - lua_remove(L, -2); - - lua_pushvalue(L, 1); - lua_rawseti(L, -2, (int)(lua_objlen(L, -2) + 1)); - - hudAvailable |= 1< Date: Mon, 5 Sep 2022 10:54:38 -0700 Subject: [PATCH 050/114] Fix conflicts 522467a88 --- src/d_clisrv.c | 14 ++--- src/d_netcmd.c | 8 +-- src/doomtype.h | 2 + src/f_finale.c | 3 +- src/g_game.c | 8 +-- src/hu_stuff.c | 4 +- src/k_bot.c | 2 +- src/lua_hook.h | 15 ++---- src/lua_hooklib.c | 131 ++-------------------------------------------- src/lua_script.c | 4 +- src/lua_script.h | 2 +- src/p_enemy.c | 2 +- src/p_inter.c | 10 ++-- src/p_map.c | 6 +-- src/p_mobj.c | 22 ++++---- src/p_setup.c | 4 +- src/p_spec.c | 4 +- src/p_tick.c | 12 ++--- src/p_user.c | 14 ++--- src/s_sound.c | 11 +++- src/sdl/i_video.c | 2 +- src/st_stuff.c | 5 +- src/y_inter.c | 6 +-- 23 files changed, 86 insertions(+), 205 deletions(-) diff --git a/src/d_clisrv.c b/src/d_clisrv.c index f26ec7274..d56d8399c 100644 --- a/src/d_clisrv.c +++ b/src/d_clisrv.c @@ -2348,14 +2348,14 @@ void CL_RemovePlayer(INT32 playernum, kickreason_t reason) K_CalculateBattleWanted(); - LUAh_PlayerQuit(&players[playernum], reason); // Lua hook for player quitting + LUA_HookPlayerQuit(&players[playernum], reason); // Lua hook for player quitting // don't look through someone's view who isn't there if (playernum == displayplayers[0] && !demo.playback) { // Call ViewpointSwitch hooks here. // The viewpoint was forcibly changed. - LUAh_ViewpointSwitch(&players[consoleplayer], &players[consoleplayer], true); + LUA_HookViewpointSwitch(&players[consoleplayer], &players[consoleplayer], true); displayplayers[0] = consoleplayer; } @@ -2859,7 +2859,7 @@ static void Got_KickCmd(UINT8 **p, INT32 playernum) if (playernode[pnum] == playernode[consoleplayer]) { - LUAh_GameQuit(false); + LUA_HookBool(false, HOOK(GameQuit)); #ifdef DUMPCONSISTENCY if (msg == KICK_MSG_CON_FAIL) SV_SavedGame(); #endif @@ -3377,7 +3377,7 @@ static void Got_AddPlayer(UINT8 **p, INT32 playernum) if (server && multiplayer && motd[0] != '\0') COM_BufAddText(va("sayto %d %s\n", newplayernum, motd)); - LUAh_PlayerJoin(newplayernum); + LUA_HookInt(newplayernum, HOOK(PlayerJoin)); #ifdef HAVE_DISCORDRPC DRPC_UpdatePresence(); @@ -3458,7 +3458,7 @@ static void Got_AddBot(UINT8 **p, INT32 playernum) HU_AddChatText(va("\x82*Bot %d has been added to the game", newplayernum+1), false); } - LUAh_PlayerJoin(newplayernum); + LUA_HookInt(newplayernum, HOOK(PlayerJoin)); } static boolean SV_AddWaitingPlayers(SINT8 node, const char *name, const char *name2, const char *name3, const char *name4) @@ -3803,7 +3803,7 @@ static void HandleConnect(SINT8 node) static void HandleShutdown(SINT8 node) { (void)node; - LUAh_GameQuit(false); + LUA_HookBool(false, HOOK(GameQuit)); D_QuitNetGame(); CL_Reset(); D_StartTitle(); @@ -3818,7 +3818,7 @@ static void HandleShutdown(SINT8 node) static void HandleTimeout(SINT8 node) { (void)node; - LUAh_GameQuit(false); + LUA_HookBool(false, HOOK(GameQuit)); D_QuitNetGame(); CL_Reset(); D_StartTitle(); diff --git a/src/d_netcmd.c b/src/d_netcmd.c index 1fef5acd1..92e3e01f1 100644 --- a/src/d_netcmd.c +++ b/src/d_netcmd.c @@ -3412,7 +3412,7 @@ static void Got_Teamchange(UINT8 **cp, INT32 playernum) } // Don't switch team, just go away, please, go awaayyyy, aaauuauugghhhghgh - if (!LUAh_TeamSwitch(&players[playernum], NetPacket.packet.newteam, players[playernum].spectator, NetPacket.packet.autobalance, NetPacket.packet.scrambled)) + if (!LUA_HookTeamSwitch(&players[playernum], NetPacket.packet.newteam, players[playernum].spectator, NetPacket.packet.autobalance, NetPacket.packet.scrambled)) return; //Make sure that the right team number is sent. Keep in mind that normal clients cannot change to certain teams in certain gametypes. @@ -3508,7 +3508,7 @@ static void Got_Teamchange(UINT8 **cp, INT32 playernum) { if (localplayertable[i] == playernum) { - LUAh_ViewpointSwitch(players+playernum, players+playernum, true); + LUA_HookViewpointSwitch(players+playernum, players+playernum, true); displayplayers[i] = playernum; break; } @@ -4428,7 +4428,7 @@ static void Command_Playintro_f(void) */ FUNCNORETURN static ATTRNORETURN void Command_Quit_f(void) { - LUAh_GameQuit(true); + LUA_HookBool(true, HOOK(GameQuit)); I_Quit(); } @@ -4992,7 +4992,7 @@ void Command_ExitGame_f(void) { INT32 i; - LUAh_GameQuit(false); + LUA_HookBool(false, HOOK(GameQuit)); D_QuitNetGame(); CL_Reset(); diff --git a/src/doomtype.h b/src/doomtype.h index 7037c41cc..5d14fceae 100644 --- a/src/doomtype.h +++ b/src/doomtype.h @@ -368,6 +368,8 @@ typedef UINT32 tic_t; #define UINT2RGBA(a) (UINT32)((a&0xff)<<24)|((a&0xff00)<<8)|((a&0xff0000)>>8)|(((UINT32)a&0xff000000)>>24) #endif +#define TOSTR(x) #x + /* preprocessor dumb and needs second macro to expand input */ #define WSTRING2(s) L ## s #define WSTRING(s) WSTRING2 (s) diff --git a/src/f_finale.c b/src/f_finale.c index 52f2556f4..871913cd1 100644 --- a/src/f_finale.c +++ b/src/f_finale.c @@ -41,6 +41,7 @@ #include "fastcmp.h" #include "lua_hud.h" +#include "lua_hook.h" // Stage of animation: // 0 = text, 1 = art screen @@ -2118,7 +2119,7 @@ luahook: if (renderisnewtic) { LUA_HUD_ClearDrawList(luahuddrawlist_title); - LUAh_TitleHUD(luahuddrawlist_title); + LUA_HookHUD(luahuddrawlist_title, HUD_HOOK(title)); } LUA_HUD_DrawList(luahuddrawlist_title); } diff --git a/src/g_game.c b/src/g_game.c index 155a62a0f..02d1c3636 100644 --- a/src/g_game.c +++ b/src/g_game.c @@ -1223,7 +1223,7 @@ void G_BuildTiccmd(ticcmd_t *cmd, INT32 realtics, UINT8 ssplayer) */ if (addedtogame && gamestate == GS_LEVEL) { - LUAh_PlayerCmd(player, cmd); + LUA_HookTiccmd(player, cmd, HOOK(PlayerCmd)); // Send leveltime when this tic was generated to the server for control lag calculations. // Only do this when in a level. Also do this after the hook, so that it can't overwrite this. @@ -1253,7 +1253,7 @@ void G_BuildTiccmd(ticcmd_t *cmd, INT32 realtics, UINT8 ssplayer) { // Call ViewpointSwitch hooks here. // The viewpoint was forcibly changed. - LUAh_ViewpointSwitch(player, &players[consoleplayer], true); + LUA_HookViewpointSwitch(player, &players[consoleplayer], true); displayplayers[0] = consoleplayer; } } @@ -2532,7 +2532,7 @@ void G_SpawnPlayer(INT32 playernum) P_SpawnPlayer(playernum); G_MovePlayerToSpawnOrStarpost(playernum); - LUAh_PlayerSpawn(&players[playernum]); // Lua hook for player spawning :) + LUA_HookPlayer(&players[playernum], HOOK(PlayerSpawn)); // Lua hook for player spawning :) } void G_MovePlayerToSpawnOrStarpost(INT32 playernum) @@ -4666,7 +4666,7 @@ void G_InitNew(UINT8 pencoremode, const char *mapname, boolean resetplayer, bool F_StartCustomCutscene(mapheaderinfo[gamemap-1]->precutscenenum-1, true, resetplayer); else { - LUAh_MapChange(gamemap); + LUA_HookInt(HOOK(MapChange), gamemap); G_DoLoadLevel(resetplayer); } diff --git a/src/hu_stuff.c b/src/hu_stuff.c index f4f52f5cf..492a2b45a 100644 --- a/src/hu_stuff.c +++ b/src/hu_stuff.c @@ -694,7 +694,7 @@ static void Got_Saycmd(UINT8 **p, INT32 playernum) // run the lua hook even if we were supposed to eat the msg, netgame consistency goes first. - if (LUAh_PlayerMsg(playernum, target, flags, msg, spam_eatmsg)) + if (LUA_HookPlayerMsg(playernum, target, flags, msg, spam_eatmsg)) return; if (spam_eatmsg) @@ -2123,7 +2123,7 @@ void HU_Drawer(void) if (renderisnewtic) { LUA_HUD_ClearDrawList(luahuddrawlist_scores); - LUAh_ScoresHUD(luahuddrawlist_scores); + LUA_HookHUD(luahuddrawlist_scores, HUD_HOOK(scores)); } LUA_HUD_DrawList(luahuddrawlist_scores); } diff --git a/src/k_bot.c b/src/k_bot.c index e64089aef..d7955435e 100644 --- a/src/k_bot.c +++ b/src/k_bot.c @@ -1247,7 +1247,7 @@ void K_BuildBotTiccmd(player_t *player, ticcmd_t *cmd) } // Complete override of all ticcmd functionality - if (LUAh_BotTiccmd(player, cmd) == true) + if (LUA_HookTiccmd(player, cmd, HOOK(BotTiccmd)) == true) { return; } diff --git a/src/lua_hook.h b/src/lua_hook.h index 2c69e6b03..ce98a5ec7 100644 --- a/src/lua_hook.h +++ b/src/lua_hook.h @@ -69,15 +69,11 @@ automatically. X (SeenPlayer),/* MT_NAMECHECK */\ X (PlayerThink),/* P_PlayerThink */\ X (GameQuit),\ - X (PlayerCmd),/* building the player's ticcmd struct (Ported from SRB2Kart) */\ + X (PlayerCmd),/* building the player's ticcmd struct */\ X (MusicChange),\ - X (PlayerHeight),/* override player height */\ - X (PlayerCanEnterSpinGaps),\ - X (KeyDown),\ - X (KeyUp),\ + X (VoteThinker),/* Y_VoteTicker */\ #define STRING_HOOK_LIST(X) \ - X (BotAI),/* B_BuildTailsTiccmd by skin name */\ X (LinedefExecute),\ X (ShouldJingleContinue),/* should jingle of the given music continue playing */\ @@ -115,6 +111,7 @@ ENUM (STRING_HOOK); //#define LUA_HUDHOOK(type) LUA_HookHUD(HUD_HOOK(type)) extern boolean hook_cmd_running; +extern int hook_defrosting; void LUA_HookVoid(int hook); void LUA_HookHUD(huddrawlist_h, int hook); @@ -134,9 +131,8 @@ int LUA_HookShouldDamage(mobj_t *target, mobj_t *inflictor, mobj_t *source, INT int LUA_HookMobjDamage(mobj_t *target, mobj_t *inflictor, mobj_t *source, INT32 damage, UINT8 damagetype); int LUA_HookMobjDeath(mobj_t *target, mobj_t *inflictor, mobj_t *source, UINT8 damagetype); int LUA_HookMobjMoveBlocked(mobj_t *, mobj_t *, line_t *); -int LUA_HookBotAI(mobj_t *sonic, mobj_t *tails, ticcmd_t *cmd); void LUA_HookLinedefExecute(line_t *, mobj_t *, sector_t *); -int LUA_HookPlayerMsg(int source, int target, int flags, char *msg); +int LUA_HookPlayerMsg(int source, int target, int flags, char *msg, int mute); int LUA_HookHurtMsg(player_t *, mobj_t *inflictor, mobj_t *source, UINT8 damagetype); int LUA_HookMapThingSpawn(mobj_t *, mapthing_t *); int LUA_HookFollowMobj(player_t *, mobj_t *); @@ -146,7 +142,4 @@ int LUA_HookTeamSwitch(player_t *, int newteam, boolean fromspectators, boolean int LUA_HookViewpointSwitch(player_t *player, player_t *newdisplayplayer, boolean forced); int LUA_HookSeenPlayer(player_t *player, player_t *seenfriend); int LUA_HookShouldJingleContinue(player_t *, const char *musname); -int LUA_HookPlayerCmd(player_t *, ticcmd_t *); int LUA_HookMusicChange(const char *oldname, struct MusicChange *); -fixed_t LUA_HookPlayerHeight(player_t *player); -int LUA_HookPlayerCanEnterSpinGaps(player_t *player); diff --git a/src/lua_hooklib.c b/src/lua_hooklib.c index b2029cc95..efb0522df 100644 --- a/src/lua_hooklib.c +++ b/src/lua_hooklib.c @@ -123,7 +123,6 @@ static void add_string_hook(lua_State *L, int type) switch (type) { - case STRING_HOOK(BotAI): case STRING_HOOK(ShouldJingleContinue): if (lua_isstring(L, 3)) { // lowercase copy @@ -630,22 +629,7 @@ int LUA_HookTiccmd(player_t *player, ticcmd_t *cmd, int hook_type) return hook.status; } -<<<<<<< Updated upstream -int LUA_HookKey(event_t *event, int hook_type) -{ - Hook_State hook; - if (prepare_hook(&hook, false, hook_type)) - { - LUA_PushUserdata(gL, event, META_KEYEVENT); - call_hooks(&hook, 1, res_true); - } - return hook.status; -} - -void LUA_HookHUD(int hook_type, huddrawlist_h list) -======= void LUA_HookHUD(huddrawlist_h list, int hook_type) ->>>>>>> Stashed changes { const hook_t * map = &hudHookIds[hook_type]; Hook_State hook; @@ -789,82 +773,6 @@ int LUA_HookMobjMoveBlocked(mobj_t *t1, mobj_t *t2, line_t *line) return hook.status; } -typedef struct { - mobj_t * tails; - ticcmd_t * cmd; -} BotAI_State; - -static boolean checkbotkey(const char *field) -{ - return lua_toboolean(gL, -1) && strcmp(lua_tostring(gL, -2), field) == 0; -} - -static void res_botai(Hook_State *hook) -{ - BotAI_State *botai = hook->userdata; - - int k[8]; - - int fields = 0; - - // This turns forward, backward, left, right, jump, and spin into a proper ticcmd for tails. - if (lua_istable(gL, -8)) { - lua_pushnil(gL); // key - while (lua_next(gL, -9)) { -#define CHECK(n, f) (checkbotkey(f) ? (k[(n)-1] = 1) : 0) - if ( - CHECK(1, "forward") || CHECK(2, "backward") || - CHECK(3, "left") || CHECK(4, "right") || - CHECK(5, "strafeleft") || CHECK(6, "straferight") || - CHECK(7, "jump") || CHECK(8, "spin") - ){ - if (8 <= ++fields) - { - lua_pop(gL, 2); // pop key and value - break; - } - } - - lua_pop(gL, 1); // pop value -#undef CHECK - } - } else { - while (fields < 8) - { - k[fields] = lua_toboolean(gL, -8 + fields); - fields++; - } - } - - B_KeysToTiccmd(botai->tails, botai->cmd, - k[0],k[1],k[2],k[3],k[4],k[5],k[6],k[7]); - - hook->status = true; -} - -int LUA_HookBotAI(mobj_t *sonic, mobj_t *tails, ticcmd_t *cmd) -{ - const char *skin = ((skin_t *)tails->skin)->name; - - Hook_State hook; - BotAI_State botai; - - if (prepare_string_hook(&hook, false, STRING_HOOK(BotAI), skin)) - { - LUA_PushUserdata(gL, sonic, META_MOBJ); - LUA_PushUserdata(gL, tails, META_MOBJ); - - botai.tails = tails; - botai.cmd = cmd; - - hook.userdata = &botai; - - call_hooks(&hook, 8, res_botai); - } - - return hook.status; -} - void LUA_HookLinedefExecute(line_t *line, mobj_t *mo, sector_t *sector) { Hook_State hook; @@ -878,7 +786,7 @@ void LUA_HookLinedefExecute(line_t *line, mobj_t *mo, sector_t *sector) } } -int LUA_HookPlayerMsg(int source, int target, int flags, char *msg) +int LUA_HookPlayerMsg(int source, int target, int flags, char *msg, int mute) { Hook_State hook; if (prepare_hook(&hook, false, HOOK(PlayerMsg))) @@ -898,6 +806,8 @@ int LUA_HookPlayerMsg(int source, int target, int flags, char *msg) LUA_PushUserdata(gL, &players[target-1], META_PLAYER); // target } lua_pushstring(gL, msg); // msg + lua_pushboolean(gL, mute); // the message was supposed to be eaten by spamprotecc. + call_hooks(&hook, 1, res_true); } return hook.status; @@ -1143,38 +1053,3 @@ int LUA_HookMusicChange(const char *oldname, struct MusicChange *param) return hook.status; } - -static void res_playerheight(Hook_State *hook) -{ - if (!lua_isnil(gL, -1)) - { - fixed_t returnedheight = lua_tonumber(gL, -1); - // 0 height has... strange results, but it's not problematic like negative heights are. - // when an object's height is set to a negative number directly with lua, it's forced to 0 instead. - // here, I think it's better to ignore negatives so that they don't replace any results of previous hooks! - if (returnedheight >= 0) - hook->status = returnedheight; - } -} - -fixed_t LUA_HookPlayerHeight(player_t *player) -{ - Hook_State hook; - if (prepare_hook(&hook, -1, HOOK(PlayerHeight))) - { - LUA_PushUserdata(gL, player, META_PLAYER); - call_hooks(&hook, 1, res_playerheight); - } - return hook.status; -} - -int LUA_HookPlayerCanEnterSpinGaps(player_t *player) -{ - Hook_State hook; - if (prepare_hook(&hook, 0, HOOK(PlayerCanEnterSpinGaps))) - { - LUA_PushUserdata(gL, player, META_PLAYER); - call_hooks(&hook, 1, res_force); - } - return hook.status; -} diff --git a/src/lua_script.c b/src/lua_script.c index 1511fdf0d..8a1e307bb 100644 --- a/src/lua_script.c +++ b/src/lua_script.c @@ -1687,7 +1687,7 @@ void LUA_Archive(UINT8 **p) WRITEUINT32(*p, UINT32_MAX); // end of mobjs marker, replaces mobjnum. - LUAh_NetArchiveHook(NetArchive); // call the NetArchive hook in archive mode + LUA_HookNetArchive(NetArchive); // call the NetArchive hook in archive mode } ArchiveTables(p); @@ -1726,7 +1726,7 @@ void LUA_UnArchive(UINT8 **p) } } while(mobjnum != UINT32_MAX); // repeat until end of mobjs marker. - LUAh_NetArchiveHook(NetUnArchive); // call the NetArchive hook in unarchive mode + LUA_HookNetArchive(NetUnArchive); // call the NetArchive hook in unarchive mode } UnArchiveTables(p); diff --git a/src/lua_script.h b/src/lua_script.h index 1c6d4587d..6268407f3 100644 --- a/src/lua_script.h +++ b/src/lua_script.h @@ -63,7 +63,7 @@ void Got_Luacmd(UINT8 **cp, INT32 playernum); // lua_consolelib.c void LUA_CVarChanged(void *cvar); // lua_consolelib.c int Lua_optoption(lua_State *L, int narg, const char *def, const char *const lst[]); -void LUAh_NetArchiveHook(lua_CFunction archFunc); +void LUA_HookNetArchive(lua_CFunction archFunc); void LUA_PushTaggableObjectArray ( lua_State *L, diff --git a/src/p_enemy.c b/src/p_enemy.c index 2c69e7bfe..d78089e06 100644 --- a/src/p_enemy.c +++ b/src/p_enemy.c @@ -3495,7 +3495,7 @@ void A_BossDeath(mobj_t *mo) } bossjustdie: - if (LUAh_BossDeath(mo)) + if (LUA_HookMobj(mo, MOBJ_HOOK(BossDeath))) return; else if (P_MobjWasRemoved(mo)) return; diff --git a/src/p_inter.c b/src/p_inter.c index 0974e11d7..c7007bd7f 100644 --- a/src/p_inter.c +++ b/src/p_inter.c @@ -219,7 +219,7 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck) if (special->flags & (MF_ENEMY|MF_BOSS) && special->flags2 & MF2_FRET) return; - if (LUAh_TouchSpecial(special, toucher) || P_MobjWasRemoved(special)) + if (LUA_HookTouchSpecial(special, toucher) || P_MobjWasRemoved(special)) return; if ((special->flags & (MF_ENEMY|MF_BOSS)) && !(special->flags & MF_MISSILE)) @@ -969,7 +969,7 @@ void P_KillMobj(mobj_t *target, mobj_t *inflictor, mobj_t *source, UINT8 damaget target->shadowscale = 0; } - if (LUAh_MobjDeath(target, inflictor, source, damagetype) || P_MobjWasRemoved(target)) + if (LUA_HookMobjDeath(target, inflictor, source, damagetype) || P_MobjWasRemoved(target)) return; //K_SetHitLagForObjects(target, inflictor, MAXHITLAGTICS, true); @@ -1855,7 +1855,7 @@ boolean P_DamageMobj(mobj_t *target, mobj_t *inflictor, mobj_t *source, INT32 da // Everything above here can't be forced. if (!metalrecording) { - UINT8 shouldForce = LUAh_ShouldDamage(target, inflictor, source, damage, damagetype); + UINT8 shouldForce = LUA_HookShouldDamage(target, inflictor, source, damage, damagetype); if (P_MobjWasRemoved(target)) return (shouldForce == 1); // mobj was removed if (shouldForce == 1) @@ -1881,7 +1881,7 @@ boolean P_DamageMobj(mobj_t *target, mobj_t *inflictor, mobj_t *source, INT32 da if (!force && target->flags2 & MF2_FRET) // Currently flashing from being hit return false; - if (LUAh_MobjDamage(target, inflictor, source, damage, damagetype) || P_MobjWasRemoved(target)) + if (LUA_HookMobjDamage(target, inflictor, source, damage, damagetype) || P_MobjWasRemoved(target)) return true; if (target->health > 1) @@ -1911,7 +1911,7 @@ boolean P_DamageMobj(mobj_t *target, mobj_t *inflictor, mobj_t *source, INT32 da if (!P_KillPlayer(player, inflictor, source, damagetype)) return false; } - else if (LUAh_MobjDamage(target, inflictor, source, damage, damagetype)) + else if (LUA_HookMobjDamage(target, inflictor, source, damage, damagetype)) { return true; } diff --git a/src/p_map.c b/src/p_map.c index d667b0346..4df2d17c0 100644 --- a/src/p_map.c +++ b/src/p_map.c @@ -656,7 +656,7 @@ static BlockItReturn_t PIT_CheckThing(mobj_t *thing) } { - UINT8 shouldCollide = LUAh_MobjCollide(thing, tmthing); // checks hook for thing's type + UINT8 shouldCollide = LUA_Hook2Mobj(thing, tmthing, MOBJ_HOOK(MobjCollide)); // checks hook for thing's type if (P_MobjWasRemoved(tmthing) || P_MobjWasRemoved(thing)) return BMIT_CONTINUE; // one of them was removed??? if (shouldCollide == 1) @@ -664,7 +664,7 @@ static BlockItReturn_t PIT_CheckThing(mobj_t *thing) else if (shouldCollide == 2) return BMIT_CONTINUE; // force no collide - shouldCollide = LUAh_MobjMoveCollide(tmthing, thing); // checks hook for tmthing's type + shouldCollide = LUA_Hook2Mobj(tmthing, thing, MOBJ_HOOK(MobjMoveCollide)); // checks hook for tmthing's type if (P_MobjWasRemoved(tmthing) || P_MobjWasRemoved(thing)) return BMIT_CONTINUE; // one of them was removed??? if (shouldCollide == 1) @@ -1640,7 +1640,7 @@ static BlockItReturn_t PIT_CheckLine(line_t *ld) blockingline = ld; { - UINT8 shouldCollide = LUAh_MobjLineCollide(tmthing, blockingline); // checks hook for thing's type + UINT8 shouldCollide = LUA_HookMobjLineCollide(tmthing, blockingline); // checks hook for thing's type if (P_MobjWasRemoved(tmthing)) return BMIT_CONTINUE; // one of them was removed??? if (shouldCollide == 1) diff --git a/src/p_mobj.c b/src/p_mobj.c index 8e6e0eb2a..f85fc3454 100644 --- a/src/p_mobj.c +++ b/src/p_mobj.c @@ -1568,7 +1568,7 @@ void P_XYMovement(mobj_t *mo) // blocked move moved = false; - if (LUAh_MobjMoveBlocked(mo)) + if (LUA_HookMobjMoveBlocked(mo, tmhitthing, blockingline)) { if (P_MobjWasRemoved(mo)) return; @@ -3963,7 +3963,7 @@ static void P_RingThinker(mobj_t *mobj) if (!mobj->fuse) { - if (!LUAh_MobjFuse(mobj)) + if (!LUA_HookMobj(mobj, MOBJ_HOOK(MobjFuse))) { mobj->renderflags &= ~RF_DONTDRAW; spark = P_SpawnMobj(mobj->x, mobj->y, mobj->z, MT_SIGNSPARKLE); // Spawn a fancy sparkle @@ -5343,7 +5343,7 @@ static boolean P_ParticleGenSceneryThink(mobj_t *mobj) static void P_MobjSceneryThink(mobj_t *mobj) { - if (LUAh_MobjThinker(mobj)) + if (LUA_HookMobj(mobj, MOBJ_HOOK(MobjThinker))) return; if (P_MobjWasRemoved(mobj)) return; @@ -6205,7 +6205,7 @@ static void P_MobjSceneryThink(mobj_t *mobj) mobj->fuse--; if (!mobj->fuse) { - if (!LUAh_MobjFuse(mobj)) + if (!LUA_HookMobj(mobj, MOBJ_HOOK(MobjFuse))) P_RemoveMobj(mobj); return; } @@ -6225,7 +6225,7 @@ static boolean P_MobjPushableThink(mobj_t *mobj) static boolean P_MobjBossThink(mobj_t *mobj) { - if (LUAh_BossThinker(mobj)) + if (LUA_HookMobj(mobj, MOBJ_HOOK(BossThinker))) { if (P_MobjWasRemoved(mobj)) return false; @@ -9147,7 +9147,7 @@ static boolean P_FuseThink(mobj_t *mobj) if (mobj->fuse) return true; - if (LUAh_MobjFuse(mobj) || P_MobjWasRemoved(mobj)) + if (LUA_HookMobj(mobj, MOBJ_HOOK(MobjFuse)) || P_MobjWasRemoved(mobj)) ; else if (mobj->info->flags & MF_MONITOR) { @@ -9379,13 +9379,13 @@ void P_MobjThinker(mobj_t *mobj) // Check for a Lua thinker first if (!mobj->player) { - if (LUAh_MobjThinker(mobj) || P_MobjWasRemoved(mobj)) + if (LUA_HookMobj(mobj, MOBJ_HOOK(MobjThinker)) || P_MobjWasRemoved(mobj)) return; } else if (!mobj->player->spectator) { // You cannot short-circuit the player thinker like you can other thinkers. - LUAh_MobjThinker(mobj); + LUA_HookMobj(mobj, MOBJ_HOOK(MobjThinker)); if (P_MobjWasRemoved(mobj)) return; } @@ -9977,7 +9977,7 @@ mobj_t *P_SpawnMobj(fixed_t x, fixed_t y, fixed_t z, mobjtype_t type) // DANGER! This can cause P_SpawnMobj to return NULL! // Avoid using P_RemoveMobj on the newly created mobj in "MobjSpawn" Lua hooks! - if (LUAh_MobjSpawn(mobj)) + if (LUA_HookMobj(mobj, MOBJ_HOOK(MobjSpawn))) { if (P_MobjWasRemoved(mobj)) return NULL; @@ -10577,7 +10577,7 @@ void P_RemoveMobj(mobj_t *mobj) return; // something already removing this mobj. mobj->thinker.function.acp1 = (actionf_p1)P_RemoveThinkerDelayed; // shh. no recursing. - LUAh_MobjRemoved(mobj); + LUA_HookMobj(mobj, MOBJ_HOOK(MobjRemoved)); mobj->thinker.function.acp1 = (actionf_p1)P_MobjThinker; // needed for P_UnsetThingPosition, etc. to work. // Rings only, please! @@ -12174,7 +12174,7 @@ static void P_SnapToFinishLine(mobj_t *mobj) static boolean P_SetupSpawnedMapThing(mapthing_t *mthing, mobj_t *mobj, boolean *doangle) { - boolean override = LUAh_MapThingSpawn(mobj, mthing); + boolean override = LUA_HookMapThingSpawn(mobj, mthing); if (P_MobjWasRemoved(mobj)) return false; diff --git a/src/p_setup.c b/src/p_setup.c index 43f97e666..6893210a5 100644 --- a/src/p_setup.c +++ b/src/p_setup.c @@ -66,7 +66,7 @@ #include "md5.h" // map MD5 -// for LUAh_MapLoad +// for MapLoad hook #include "lua_script.h" #include "lua_hook.h" @@ -4485,7 +4485,7 @@ boolean P_LoadLevel(boolean fromnetsave, boolean reloadinggamestate) } P_PreTicker(2); P_MapStart(); // just in case MapLoad modifies tmthing - LUAh_MapLoad(); + LUA_HookInt(gamemap, HOOK(MapLoad)); P_MapEnd(); // just in case MapLoad modifies tmthing } diff --git a/src/p_spec.c b/src/p_spec.c index 942e72fb6..681e5e8e9 100644 --- a/src/p_spec.c +++ b/src/p_spec.c @@ -36,7 +36,7 @@ #include "v_video.h" // V_ALLOWLOWERCASE #include "m_misc.h" #include "m_cond.h" //unlock triggers -#include "lua_hook.h" // LUAh_LinedefExecute +#include "lua_hook.h" // LUA_HookLinedefExecute #include "f_finale.h" // control text prompt #include "r_skins.h" // skins @@ -3028,7 +3028,7 @@ static void P_ProcessLineSpecial(line_t *line, mobj_t *mo, sector_t *callsec) case 443: // Calls a named Lua function if (line->stringargs[0]) - LUAh_LinedefExecute(line, mo, callsec); + LUA_HookLinedefExecute(line, mo, callsec); else CONS_Alert(CONS_WARNING, "Linedef %s is missing the hook name of the Lua function to call! (This should be given in arg0str)\n", sizeu1(line-lines)); break; diff --git a/src/p_tick.c b/src/p_tick.c index 843461db6..98f97a45a 100644 --- a/src/p_tick.c +++ b/src/p_tick.c @@ -581,7 +581,7 @@ void P_Ticker(boolean run) ps_lua_mobjhooks = 0; ps_checkposition_calls = 0; - LUAh_PreThinkFrame(); + LUA_HOOK(PreThinkFrame); ps_playerthink_time = I_GetPreciseTime(); @@ -654,7 +654,7 @@ void P_Ticker(boolean run) } ps_lua_thinkframe_time = I_GetPreciseTime(); - LUAh_ThinkFrame(); + LUA_HOOK(ThinkFrame); ps_lua_thinkframe_time = I_GetPreciseTime() - ps_lua_thinkframe_time; } @@ -748,7 +748,7 @@ void P_Ticker(boolean run) // Always move the camera. P_RunChaseCameras(); - LUAh_PostThinkFrame(); + LUA_HOOK(PostThinkFrame); if (run) { @@ -802,7 +802,7 @@ void P_PreTicker(INT32 frames) K_KartUpdatePosition(&players[i]); // OK! Now that we got all of that sorted, players can think! - LUAh_PreThinkFrame(); + LUA_HOOK(PreThinkFrame); for (i = 0; i < MAXPLAYERS; i++) if (playeringame[i] && players[i].mo && !P_MobjWasRemoved(players[i].mo)) @@ -825,7 +825,7 @@ void P_PreTicker(INT32 frames) if (playeringame[i] && players[i].mo && !P_MobjWasRemoved(players[i].mo)) P_PlayerAfterThink(&players[i]); - LUAh_ThinkFrame(); + LUA_HOOK(ThinkFrame); // Run shield positioning P_RunOverlays(); @@ -833,7 +833,7 @@ void P_PreTicker(INT32 frames) P_UpdateSpecials(); P_RespawnSpecials(); - LUAh_PostThinkFrame(); + LUA_HOOK(PostThinkFrame); R_UpdateLevelInterpolators(); R_UpdateViewInterpolation(); diff --git a/src/p_user.c b/src/p_user.c index 4030232f7..903e874d8 100644 --- a/src/p_user.c +++ b/src/p_user.c @@ -664,7 +664,7 @@ boolean P_EvaluateMusicStatus(UINT16 status, const char *musname) break; case JT_OTHER: // Other state - result = LUAh_ShouldJingleContinue(&players[i], musname); + result = LUA_HookShouldJingleContinue(&players[i], musname); break; case JT_NONE: // Null state @@ -3671,7 +3671,7 @@ boolean P_SpectatorJoinGame(player_t *player) else changeto = (P_RandomFixed() & 1) + 1; - if (!LUAh_TeamSwitch(player, changeto, true, false, false)) + if (!LUA_HookTeamSwitch(player, changeto, true, false, false)) return false; } @@ -3697,7 +3697,7 @@ boolean P_SpectatorJoinGame(player_t *player) { if (localplayertable[i] == (player-players)) { - LUAh_ViewpointSwitch(player, player, true); + LUA_HookViewpointSwitch(player, player, true); displayplayers[i] = (player-players); break; } @@ -4207,7 +4207,7 @@ void P_PlayerThink(player_t *player) if (player->playerstate == PST_DEAD) { - LUAh_PlayerThink(player); + LUA_HookPlayer(player, HOOK(PlayerThink)); return; } } @@ -4256,7 +4256,7 @@ void P_PlayerThink(player_t *player) else player->mo->renderflags &= ~RF_GHOSTLYMASK; P_DeathThink(player); - LUAh_PlayerThink(player); + LUA_HookPlayer(player, HOOK(PlayerThink)); return; } @@ -4461,7 +4461,7 @@ void P_PlayerThink(player_t *player) if (player->carry == CR_SLIDING) player->carry = CR_NONE; - LUAh_PlayerThink(player); + LUA_HookPlayer(player, HOOK(PlayerThink)); } // @@ -4568,7 +4568,7 @@ void P_PlayerAfterThink(player_t *player) if (player->followmobj) { - if (LUAh_FollowMobj(player, player->followmobj) || P_MobjWasRemoved(player->followmobj)) + if (LUA_HookFollowMobj(player, player->followmobj) || P_MobjWasRemoved(player->followmobj)) {;} else { diff --git a/src/s_sound.c b/src/s_sound.c index 9d3456f59..9bddda377 100644 --- a/src/s_sound.c +++ b/src/s_sound.c @@ -2121,6 +2121,15 @@ void S_ChangeMusicEx(const char *mmusic, UINT16 mflags, boolean looping, UINT32 { char newmusic[7]; + struct MusicChange hook_param = { + newmusic, + &mflags, + &looping, + &position, + &prefadems, + &fadeinms + }; + if (S_MusicDisabled() || demo.rewinding // Don't mess with music while rewinding! || demo.title) // SRB2Kart: Demos don't interrupt title screen music @@ -2128,7 +2137,7 @@ void S_ChangeMusicEx(const char *mmusic, UINT16 mflags, boolean looping, UINT32 strncpy(newmusic, mmusic, 7); - if (LUAh_MusicChange(music_name, newmusic, &mflags, &looping, &position, &prefadems, &fadeinms)) + if (LUA_HookMusicChange(music_name, &hook_param)) return; newmusic[6] = 0; diff --git a/src/sdl/i_video.c b/src/sdl/i_video.c index ac23eb926..36e769918 100644 --- a/src/sdl/i_video.c +++ b/src/sdl/i_video.c @@ -1149,7 +1149,7 @@ void I_GetEvent(void) M_SetupJoystickMenu(0); break; case SDL_QUIT: - LUAh_GameQuit(true); + LUA_HookBool(true, HOOK(GameQuit)); I_Quit(); break; } diff --git a/src/st_stuff.c b/src/st_stuff.c index be5e3cc97..c0e91c34f 100644 --- a/src/st_stuff.c +++ b/src/st_stuff.c @@ -47,6 +47,7 @@ #include "lua_hudlib_drawlist.h" #include "lua_hud.h" +#include "lua_hook.h" // SRB2Kart #include "k_hud.h" // SRB2kart @@ -962,7 +963,7 @@ luahook: if (renderisnewtic) { LUA_HUD_ClearDrawList(luahuddrawlist_titlecard); - LUAh_TitleCardHUD(stplyr, luahuddrawlist_titlecard); + LUA_HookHUD(luahuddrawlist_titlecard, HUD_HOOK(titlecard)); } LUA_HUD_DrawList(luahuddrawlist_titlecard); } @@ -1009,7 +1010,7 @@ static void ST_overlayDrawer(void) if (renderisnewtic) { LUA_HUD_ClearDrawList(luahuddrawlist_game); - LUAh_GameHUD(stplyr, luahuddrawlist_game); + LUA_HookHUD(luahuddrawlist_game, HUD_HOOK(game)); } LUA_HUD_DrawList(luahuddrawlist_game); } diff --git a/src/y_inter.c b/src/y_inter.c index d66a82dcb..69df1ba8a 100644 --- a/src/y_inter.c +++ b/src/y_inter.c @@ -478,7 +478,7 @@ void Y_IntermissionDrawer(void) if (renderisnewtic) { LUA_HUD_ClearDrawList(luahuddrawlist_intermission); - LUAh_IntermissionHUD(luahuddrawlist_intermission); + LUA_HookHUD(luahuddrawlist_intermission, HUD_HOOK(intermission)); } LUA_HUD_DrawList(luahuddrawlist_intermission); @@ -777,7 +777,7 @@ void Y_Ticker(void) if (paused || P_AutoPause()) return; - LUAh_IntermissionThinker(); + LUA_HOOK(IntermissionThinker); intertic++; @@ -1371,7 +1371,7 @@ void Y_VoteTicker(void) if (paused || P_AutoPause() || !voteclient.loaded) return; - LUAh_VoteThinker(); + LUA_HOOK(VoteThinker); votetic++; From aa5063668e6049e20b47e9178fb612d85c35ef08 Mon Sep 17 00:00:00 2001 From: James R Date: Mon, 5 Sep 2022 10:56:48 -0700 Subject: [PATCH 051/114] hooklib: revert ps_lua_mobjhooks to pre perfstats averaging refactor (77ecfb9cdc) Revert later --- src/lua_hooklib.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/lua_hooklib.c b/src/lua_hooklib.c index efb0522df..8d47b7389 100644 --- a/src/lua_hooklib.c +++ b/src/lua_hooklib.c @@ -506,7 +506,7 @@ static int call_hooks calls += call_mobj_type_hooks(hook, MT_NULL); calls += call_mobj_type_hooks(hook, hook->mobj_type); - ps_lua_mobjhooks.value.i += calls; + ps_lua_mobjhooks += calls; } else calls += call_mapped(hook, &hookIds[hook->hook_type]); @@ -782,7 +782,7 @@ void LUA_HookLinedefExecute(line_t *line, mobj_t *mo, sector_t *sector) LUA_PushUserdata(gL, line, META_LINE); LUA_PushUserdata(gL, mo, META_MOBJ); LUA_PushUserdata(gL, sector, META_SECTOR); - ps_lua_mobjhooks.value.i += call_hooks(&hook, 0, res_none); + ps_lua_mobjhooks += call_hooks(&hook, 0, res_none); } } From b17717babe2a2c2f89a37d5de5fb7eebef0ba15e Mon Sep 17 00:00:00 2001 From: James R Date: Tue, 1 Feb 2022 02:42:33 -0800 Subject: [PATCH 052/114] A_LobShot: remove ??? MT_NULL spawning, not cool bro. (cherry picked from commit f6f002e70b645982dddd83d6f831a601faa0fdae) --- src/p_enemy.c | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/src/p_enemy.c b/src/p_enemy.c index d78089e06..ffac4524b 100644 --- a/src/p_enemy.c +++ b/src/p_enemy.c @@ -2474,7 +2474,7 @@ void A_LobShot(mobj_t *actor) { INT32 locvar1 = var1; INT32 locvar2 = var2 >> 16; - mobj_t *shot, *hitspot; + mobj_t *shot; angle_t an; fixed_t z; fixed_t dist; @@ -2502,11 +2502,6 @@ void A_LobShot(mobj_t *actor) shot->destscale = actor->scale; P_SetScale(shot, actor->scale); - // Keep track of where it's going to land - hitspot = P_SpawnMobj(actor->target->x&(64*FRACUNIT-1), actor->target->y&(64*FRACUNIT-1), actor->target->subsector->sector->floorheight, MT_NULL); - hitspot->tics = airtime; - P_SetTarget(&shot->tracer, hitspot); - P_SetTarget(&shot->target, actor); // where it came from P_InitAngle(shot, actor->angle); From ba1b6bb253d20cdddba96fdab8821a9ef36644af Mon Sep 17 00:00:00 2001 From: James R Date: Tue, 1 Feb 2022 03:29:38 -0800 Subject: [PATCH 053/114] Add P_CheckMove Checks if P_TryMove would succeed without actually moving. (cherry picked from commit 518de0ce104166c3eacd10ebe6ade422307ce671) --- src/deh_tables.c | 1 + src/info.c | 27 ++++++++++++++++++++++++ src/info.h | 1 + src/p_local.h | 1 + src/p_map.c | 54 ++++++++++++++++++++++++++++++++++++++++-------- 5 files changed, 75 insertions(+), 9 deletions(-) diff --git a/src/deh_tables.c b/src/deh_tables.c index 47a6ca515..f6d206d00 100644 --- a/src/deh_tables.c +++ b/src/deh_tables.c @@ -4506,6 +4506,7 @@ const char *const STATE_LIST[] = { // array length left dynamic for sanity testi // because sadly no one remembers this place while searching for full state names. const char *const MOBJTYPE_LIST[] = { // array length left dynamic for sanity testing later. "MT_NULL", + "MT_RAY", "MT_UNKNOWN", "MT_THOK", // Thok! mobj diff --git a/src/info.c b/src/info.c index 8fd5f929c..28880ac82 100644 --- a/src/info.c +++ b/src/info.c @@ -5120,6 +5120,33 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] = S_NULL // raisestate }, + { // MT_RAY + -1, // doomednum + S_NULL, // spawnstate + 0, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 0, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 0, // radius + 0, // height + 0, // display offset + 0, // mass + 0, // damage + sfx_None, // activesound + MF_NOBLOCKMAP|MF_NOSECTOR|MF_NOCLIP|MF_NOCLIPHEIGHT|MF_NOGRAVITY|MF_SCENERY, // flags + S_NULL // raisestate + }, + { // MT_UNKNOWN -1, // doomednum S_UNKNOWN, // spawnstate diff --git a/src/info.h b/src/info.h index 175de9cab..79c746849 100644 --- a/src/info.h +++ b/src/info.h @@ -5532,6 +5532,7 @@ extern playersprite_t free_spr2; typedef enum mobj_type { MT_NULL, + MT_RAY, // General purpose mobj MT_UNKNOWN, MT_THOK, // Thok! mobj diff --git a/src/p_local.h b/src/p_local.h index 0e03b8482..6c885191c 100644 --- a/src/p_local.h +++ b/src/p_local.h @@ -410,6 +410,7 @@ boolean P_IsLineBlocking(const line_t *ld, const mobj_t *thing); boolean P_IsLineTripWire(const line_t *ld); boolean P_CheckPosition(mobj_t *thing, fixed_t x, fixed_t y); boolean P_CheckCameraPosition(fixed_t x, fixed_t y, camera_t *thiscam); +boolean P_CheckMove(mobj_t *thing, fixed_t x, fixed_t y, boolean allowdropoff); fixed_t P_BaseStepUp(void); fixed_t P_GetThingStepUp(mobj_t *thing); boolean P_TryMove(mobj_t *thing, fixed_t x, fixed_t y, boolean allowdropoff); diff --git a/src/p_map.c b/src/p_map.c index 4df2d17c0..53fa01f69 100644 --- a/src/p_map.c +++ b/src/p_map.c @@ -2489,21 +2489,19 @@ fixed_t P_GetThingStepUp(mobj_t *thing) return maxstep; } -// -// P_TryMove -// Attempt to move to a new position. -// -boolean P_TryMove(mobj_t *thing, fixed_t x, fixed_t y, boolean allowdropoff) +static boolean +increment_move +( mobj_t * thing, + fixed_t x, + fixed_t y, + boolean allowdropoff, + fixed_t * return_stairjank) { fixed_t tryx = thing->x; fixed_t tryy = thing->y; - fixed_t oldx = tryx; - fixed_t oldy = tryy; fixed_t radius = thing->radius; fixed_t thingtop; - fixed_t startingonground = P_IsObjectOnGround(thing); fixed_t stairjank = 0; - pslope_t *oldslope = thing->standingslope; floatok = false; // reset this to 0 at the start of each trymove call as it's only used here @@ -2644,7 +2642,45 @@ boolean P_TryMove(mobj_t *thing, fixed_t x, fixed_t y, boolean allowdropoff) } } while (tryx != x || tryy != y); + if (return_stairjank) + *return_stairjank = stairjank; + + return true; +} + +// +// P_CheckMove +// Check if a P_TryMove would be successful. +// +boolean P_CheckMove(mobj_t *thing, fixed_t x, fixed_t y, boolean allowdropoff) +{ + boolean moveok; + mobj_t *hack = P_SpawnMobjFromMobj(thing, 0, 0, 0, MT_RAY); + + hack->radius = thing->radius; + hack->height = thing->height; + + moveok = increment_move(hack, x, y, allowdropoff, NULL); + P_RemoveMobj(hack); + + return moveok; +} + +// +// P_TryMove +// Attempt to move to a new position. +// +boolean P_TryMove(mobj_t *thing, fixed_t x, fixed_t y, boolean allowdropoff) +{ + fixed_t oldx = thing->x; + fixed_t oldy = thing->y; + fixed_t startingonground = P_IsObjectOnGround(thing); + fixed_t stairjank = 0; + pslope_t *oldslope = thing->standingslope; + // The move is ok! + if (!increment_move(thing, x, y, allowdropoff, &stairjank)) + return false; // If it's a pushable object, check if anything is // standing on top and move it, too. From 72342338b1aa78ecd6aa05c382d01fbf39839e2f Mon Sep 17 00:00:00 2001 From: James R Date: Tue, 1 Feb 2022 03:40:26 -0800 Subject: [PATCH 054/114] Use P_CheckMove (cherry picked from commit 9dfa153e7497403d874e980868b7aa6d15595286) --- src/p_enemy.c | 15 +++++---------- 1 file changed, 5 insertions(+), 10 deletions(-) diff --git a/src/p_enemy.c b/src/p_enemy.c index ffac4524b..1467f66a0 100644 --- a/src/p_enemy.c +++ b/src/p_enemy.c @@ -3141,10 +3141,8 @@ void A_SkullAttack(mobj_t *actor) UINT32 oldflags = mobjinfo[MT_NULL].flags; fixed_t oldradius = mobjinfo[MT_NULL].radius; fixed_t oldheight = mobjinfo[MT_NULL].height; - mobj_t *check; INT32 i, j; static INT32 k;/* static for (at least) GCC 9.1 weirdness */ - boolean allow; angle_t testang = 0; mobjinfo[MT_NULL].spawnstate = S_INVISIBLE; @@ -3163,15 +3161,12 @@ void A_SkullAttack(mobj_t *actor) j = 9; } -#define dostuff(q) check = P_SpawnMobjFromMobj(actor, 0, 0, 0, MT_NULL);\ +#define dostuff(q) \ testang = actor->angle + ((i+(q))*ANG10);\ - allow = (P_TryMove(check,\ - P_ReturnThrustX(check, testang, dist + 2*actor->radius),\ - P_ReturnThrustY(check, testang, dist + 2*actor->radius),\ - true));\ - P_RemoveMobj(check);\ - if (allow)\ - break; + if (P_CheckMove(actor,\ + P_ReturnThrustX(actor, testang, dist + 2*actor->radius),\ + P_ReturnThrustY(actor, testang, dist + 2*actor->radius),\ + true)) break; if (P_RandomChance(FRACUNIT/2)) // port priority 2? { From 707f487f92e2e50a956e149bb05acee71bd3bd93 Mon Sep 17 00:00:00 2001 From: James R Date: Tue, 1 Feb 2022 04:04:53 -0800 Subject: [PATCH 055/114] Never spawn MT_NULL (cherry picked from commit a8c658b545e3c7709212a43489163f33ffbe91f0) --- src/p_mobj.c | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/src/p_mobj.c b/src/p_mobj.c index f85fc3454..e6749e3d8 100644 --- a/src/p_mobj.c +++ b/src/p_mobj.c @@ -9876,7 +9876,17 @@ mobj_t *P_SpawnMobj(fixed_t x, fixed_t y, fixed_t z, mobjtype_t type) const mobjinfo_t *info = &mobjinfo[type]; SINT8 sc = -1; state_t *st; - mobj_t *mobj = Z_Calloc(sizeof (*mobj), PU_LEVEL, NULL); + mobj_t *mobj; + + if (type == MT_NULL) + { +#ifdef PARANOIA + I_Error("Tried to spawn MT_NULL\n"); +#endif + return NULL; + } + + mobj = Z_Calloc(sizeof (*mobj), PU_LEVEL, NULL); // this is officially a mobj, declared as soon as possible. mobj->thinker.function.acp1 = (actionf_p1)P_MobjThinker; From 3a2fe2248588b6549c8f56b91351f126955e7f0e Mon Sep 17 00:00:00 2001 From: spherallic Date: Fri, 4 Feb 2022 20:09:37 +0100 Subject: [PATCH 056/114] Don't read or set MT_NULL's properties in A_SkullAttack (cherry picked from commit 878b4dc5b6025e1deef775ccb72b8a871833598a) --- src/p_enemy.c | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/src/p_enemy.c b/src/p_enemy.c index 1467f66a0..d5f999624 100644 --- a/src/p_enemy.c +++ b/src/p_enemy.c @@ -3137,18 +3137,18 @@ void A_SkullAttack(mobj_t *actor) actor->angle += (P_RandomChance(FRACUNIT/2)) ? ANGLE_90 : -ANGLE_90; else if (locvar1 == 3) { - statenum_t oldspawnstate = mobjinfo[MT_NULL].spawnstate; - UINT32 oldflags = mobjinfo[MT_NULL].flags; - fixed_t oldradius = mobjinfo[MT_NULL].radius; - fixed_t oldheight = mobjinfo[MT_NULL].height; + statenum_t oldspawnstate = mobjinfo[MT_RAY].spawnstate; + UINT32 oldflags = mobjinfo[MT_RAY].flags; + fixed_t oldradius = mobjinfo[MT_RAY].radius; + fixed_t oldheight = mobjinfo[MT_RAY].height; INT32 i, j; static INT32 k;/* static for (at least) GCC 9.1 weirdness */ angle_t testang = 0; - mobjinfo[MT_NULL].spawnstate = S_INVISIBLE; - mobjinfo[MT_NULL].flags = MF_NOGRAVITY|MF_NOTHINK|MF_NOCLIPTHING|MF_NOBLOCKMAP; - mobjinfo[MT_NULL].radius = mobjinfo[actor->type].radius; - mobjinfo[MT_NULL].height = mobjinfo[actor->type].height; + mobjinfo[MT_RAY].spawnstate = S_INVISIBLE; + mobjinfo[MT_RAY].flags = MF_NOGRAVITY|MF_NOTHINK|MF_NOCLIPTHING|MF_NOBLOCKMAP; + mobjinfo[MT_RAY].radius = mobjinfo[actor->type].radius; + mobjinfo[MT_RAY].height = mobjinfo[actor->type].height; if (P_RandomChance(FRACUNIT/2)) // port priority 1? { @@ -3192,10 +3192,10 @@ void A_SkullAttack(mobj_t *actor) #undef dostuff - mobjinfo[MT_NULL].spawnstate = oldspawnstate; - mobjinfo[MT_NULL].flags = oldflags; - mobjinfo[MT_NULL].radius = oldradius; - mobjinfo[MT_NULL].height = oldheight; + mobjinfo[MT_RAY].spawnstate = oldspawnstate; + mobjinfo[MT_RAY].flags = oldflags; + mobjinfo[MT_RAY].radius = oldradius; + mobjinfo[MT_RAY].height = oldheight; } an = actor->angle >> ANGLETOFINESHIFT; From c73d3b424496e2f73d047a348758389b9fcee2ea Mon Sep 17 00:00:00 2001 From: SteelT Date: Wed, 2 Mar 2022 12:35:03 -0500 Subject: [PATCH 057/114] Spawn MT_RAY when attempting to spawn MT_NULL Some code assumes that P_SpawnMobj can never return NULL So spawn MT_RAY in it's place when attempting to spawn MT_NULL and show a console warning (cherry picked from commit 34f8464cbfc01adf1650da1311a0751fce5b0678) --- src/p_mobj.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/p_mobj.c b/src/p_mobj.c index e6749e3d8..69c87975e 100644 --- a/src/p_mobj.c +++ b/src/p_mobj.c @@ -9880,10 +9880,17 @@ mobj_t *P_SpawnMobj(fixed_t x, fixed_t y, fixed_t z, mobjtype_t type) if (type == MT_NULL) { +#if 0 #ifdef PARANOIA I_Error("Tried to spawn MT_NULL\n"); #endif return NULL; +#endif + // Hack: Some code assumes that P_SpawnMobj can never return NULL + // So replace MT_NULL with MT_RAY in the meantime + // Remove when dealt properly + CONS_Alert(CONS_WARNING, "Tried to spawn MT_NULL, using MT_RAY\n"); + type = MT_RAY; } mobj = Z_Calloc(sizeof (*mobj), PU_LEVEL, NULL); From cb46f8de39be7f367b1a65db704bcf3a6e53fa78 Mon Sep 17 00:00:00 2001 From: SteelT Date: Wed, 2 Mar 2022 12:46:24 -0500 Subject: [PATCH 058/114] Turn the console warning into a devmode print because turns out it happens more often than I thought (cherry picked from commit 893ea10a677274b8cb3aa0987f22f162521b6311) --- src/p_mobj.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/p_mobj.c b/src/p_mobj.c index 69c87975e..aff165618 100644 --- a/src/p_mobj.c +++ b/src/p_mobj.c @@ -9889,7 +9889,7 @@ mobj_t *P_SpawnMobj(fixed_t x, fixed_t y, fixed_t z, mobjtype_t type) // Hack: Some code assumes that P_SpawnMobj can never return NULL // So replace MT_NULL with MT_RAY in the meantime // Remove when dealt properly - CONS_Alert(CONS_WARNING, "Tried to spawn MT_NULL, using MT_RAY\n"); + CONS_Debug(DBG_GAMELOGIC, "Tried to spawn MT_NULL, using MT_RAY\n"); type = MT_RAY; } From 787b5283fc3a0833acae4a507138c7e7acb7a8d0 Mon Sep 17 00:00:00 2001 From: James R Date: Mon, 5 Sep 2022 12:16:59 -0700 Subject: [PATCH 059/114] Fix big blunder by MEEEEEEE Blame 37c3a55dd --- src/g_game.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/g_game.c b/src/g_game.c index 02d1c3636..4bdafb46b 100644 --- a/src/g_game.c +++ b/src/g_game.c @@ -4666,7 +4666,7 @@ void G_InitNew(UINT8 pencoremode, const char *mapname, boolean resetplayer, bool F_StartCustomCutscene(mapheaderinfo[gamemap-1]->precutscenenum-1, true, resetplayer); else { - LUA_HookInt(HOOK(MapChange), gamemap); + LUA_HookInt(gamemap, HOOK(MapChange)); G_DoLoadLevel(resetplayer); } From c145d12db3b9182305aa8406a231bab353301959 Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Sat, 22 Jan 2022 07:51:05 -0500 Subject: [PATCH 060/114] Use hashes for terrain loading --- src/k_terrain.c | 92 ++++++++++++++++++++++++++++++------------------- src/k_terrain.h | 14 ++++---- 2 files changed, 64 insertions(+), 42 deletions(-) diff --git a/src/k_terrain.c b/src/k_terrain.c index 44b8b3163..5b12295df 100644 --- a/src/k_terrain.c +++ b/src/k_terrain.c @@ -91,6 +91,7 @@ t_splash_t *K_GetSplashByIndex(size_t checkIndex) --------------------------------------------------*/ t_splash_t *K_GetSplashByName(const char *checkName) { + UINT32 checkHash = quickncasehash(checkName, TERRAIN_NAME_LEN); size_t i; if (numSplashDefs == 0) @@ -102,7 +103,7 @@ t_splash_t *K_GetSplashByName(const char *checkName) { t_splash_t *s = &splashDefs[i]; - if (stricmp(checkName, s->name) == 0) + if (checkHash == s->hash && !strncmp(checkName, s->name, TERRAIN_NAME_LEN)) { // Name matches. return s; @@ -159,6 +160,7 @@ t_footstep_t *K_GetFootstepByIndex(size_t checkIndex) --------------------------------------------------*/ t_footstep_t *K_GetFootstepByName(const char *checkName) { + UINT32 checkHash = quickncasehash(checkName, TERRAIN_NAME_LEN); size_t i; if (numFootstepDefs == 0) @@ -170,7 +172,7 @@ t_footstep_t *K_GetFootstepByName(const char *checkName) { t_footstep_t *fs = &footstepDefs[i]; - if (stricmp(checkName, fs->name) == 0) + if (checkHash == fs->hash && !strncmp(checkName, fs->name, TERRAIN_NAME_LEN)) { // Name matches. return fs; @@ -227,21 +229,20 @@ terrain_t *K_GetTerrainByIndex(size_t checkIndex) --------------------------------------------------*/ terrain_t *K_GetTerrainByName(const char *checkName) { + UINT32 checkHash = quickncasehash(checkName, TERRAIN_NAME_LEN); size_t i; - if (numTerrainDefs == 0) + if (numTerrainDefs > 0) { - return NULL; - } - - for (i = 0; i < numTerrainDefs; i++) - { - terrain_t *t = &terrainDefs[i]; - - if (stricmp(checkName, t->name) == 0) + for (i = 0; i < numTerrainDefs; i++) { - // Name matches. - return t; + terrain_t *t = &terrainDefs[i]; + + if (checkHash == t->hash && !strncmp(checkName, t->name, TERRAIN_NAME_LEN)) + { + // Name matches. + return t; + } } } @@ -265,20 +266,19 @@ terrain_t *K_GetDefaultTerrain(void) --------------------------------------------------*/ terrain_t *K_GetTerrainForTextureName(const char *checkName) { + UINT32 checkHash = quickncasehash(checkName, 8); size_t i; - if (numTerrainFloorDefs == 0) + if (numTerrainFloorDefs > 0) { - return NULL; - } - - for (i = 0; i < numTerrainFloorDefs; i++) - { - t_floor_t *f = &terrainFloorDefs[i]; - - if (strncasecmp(checkName, f->textureName, 8) == 0) + for (i = 0; i < numTerrainFloorDefs; i++) { - return K_GetTerrainByIndex(f->terrainID); + t_floor_t *f = &terrainFloorDefs[i]; + + if (checkHash == f->textureHash && !strncmp(checkName, f->textureName, 8)) + { + return K_GetTerrainByIndex(f->terrainID); + } } } @@ -294,15 +294,15 @@ terrain_t *K_GetTerrainForTextureName(const char *checkName) --------------------------------------------------*/ terrain_t *K_GetTerrainForTextureNum(INT32 textureNum) { - texture_t *tex = NULL; - - if (textureNum < 0 || textureNum >= numtextures) + if (textureNum >= 0 && textureNum < numtextures) { - return NULL; + texture_t *tex = textures[textureNum]; + return K_GetTerrainForTextureName(tex->name); } - tex = textures[textureNum]; - return K_GetTerrainForTextureName(tex->name); + // This texture doesn't have a terrain directly applied to it, + // so we fallback to the default terrain. + return K_GetDefaultTerrain(); } /*-------------------------------------------------- @@ -1187,6 +1187,7 @@ static boolean K_DoTERRAINLumpParse(size_t num, void (*parser)(size_t, char *, c static boolean K_TERRAINLumpParser(UINT8 *data, size_t size) { char *tkn = M_GetToken((char *)data); + UINT32 tknHash = 0; size_t pos = 0; size_t i; @@ -1211,11 +1212,13 @@ static boolean K_TERRAINLumpParser(UINT8 *data, size_t size) { t_splash_t *s = NULL; + tknHash = quickncasehash(tkn, TERRAIN_NAME_LEN); + for (i = 0; i < numSplashDefs; i++) { s = &splashDefs[i]; - if (stricmp(tkn, s->name) == 0) + if (tknHash == s->hash && !strncmp(tkn, s->name, TERRAIN_NAME_LEN)) { break; } @@ -1227,6 +1230,8 @@ static boolean K_TERRAINLumpParser(UINT8 *data, size_t size) s = &splashDefs[i]; strncpy(s->name, tkn, TERRAIN_NAME_LEN); + s->hash = tknHash; + CONS_Printf("Created new Splash type '%s'\n", s->name); } @@ -1248,11 +1253,13 @@ static boolean K_TERRAINLumpParser(UINT8 *data, size_t size) { t_footstep_t *fs = NULL; + tknHash = quickncasehash(tkn, TERRAIN_NAME_LEN); + for (i = 0; i < numFootstepDefs; i++) { fs = &footstepDefs[i]; - if (stricmp(tkn, fs->name) == 0) + if (tknHash == fs->hash && !strncmp(tkn, fs->name, TERRAIN_NAME_LEN)) { break; } @@ -1264,6 +1271,8 @@ static boolean K_TERRAINLumpParser(UINT8 *data, size_t size) fs = &footstepDefs[i]; strncpy(fs->name, tkn, TERRAIN_NAME_LEN); + fs->hash = tknHash; + CONS_Printf("Created new Footstep type '%s'\n", fs->name); } @@ -1285,11 +1294,13 @@ static boolean K_TERRAINLumpParser(UINT8 *data, size_t size) { terrain_t *t = NULL; + tknHash = quickncasehash(tkn, TERRAIN_NAME_LEN); + for (i = 0; i < numTerrainDefs; i++) { t = &terrainDefs[i]; - if (stricmp(tkn, t->name) == 0) + if (tknHash == t->hash && !strncmp(tkn, t->name, TERRAIN_NAME_LEN)) { break; } @@ -1301,6 +1312,8 @@ static boolean K_TERRAINLumpParser(UINT8 *data, size_t size) t = &terrainDefs[i]; strncpy(t->name, tkn, TERRAIN_NAME_LEN); + t->hash = tknHash; + CONS_Printf("Created new Terrain type '%s'\n", t->name); } @@ -1333,11 +1346,13 @@ static boolean K_TERRAINLumpParser(UINT8 *data, size_t size) { t_floor_t *f = NULL; + tknHash = quickncasehash(tkn, 8); + for (i = 0; i < numTerrainFloorDefs; i++) { f = &terrainFloorDefs[i]; - if (stricmp(tkn, f->textureName) == 0) + if (f->textureHash == tknHash && !strncmp(tkn, f->textureName, 8)) { break; } @@ -1348,7 +1363,8 @@ static boolean K_TERRAINLumpParser(UINT8 *data, size_t size) K_NewTerrainFloorDefs(); f = &terrainFloorDefs[i]; - strncpy(f->textureName, tkn, 9); + strncpy(f->textureName, tkn, 8); + f->textureHash = tknHash; } Z_Free(tkn); @@ -1398,11 +1414,13 @@ static boolean K_TERRAINLumpParser(UINT8 *data, size_t size) { terrain_t *t = NULL; + tknHash = quickncasehash(tkn, TERRAIN_NAME_LEN); + for (i = 0; i < numTerrainDefs; i++) { t = &terrainDefs[i]; - if (stricmp(tkn, t->name) == 0) + if (tknHash == t->hash && !strncmp(tkn, t->name, TERRAIN_NAME_LEN)) { break; } @@ -1435,11 +1453,13 @@ static boolean K_TERRAINLumpParser(UINT8 *data, size_t size) { t_footstep_t *fs = NULL; + tknHash = quickncasehash(tkn, TERRAIN_NAME_LEN); + for (i = 0; i < numFootstepDefs; i++) { fs = &footstepDefs[i]; - if (stricmp(tkn, fs->name) == 0) + if (tknHash == fs->hash && !strncmp(tkn, fs->name, TERRAIN_NAME_LEN)) { break; } diff --git a/src/k_terrain.h b/src/k_terrain.h index 924f7a1b1..c83f199d2 100644 --- a/src/k_terrain.h +++ b/src/k_terrain.h @@ -28,6 +28,7 @@ typedef struct t_splash_s // These are particles spawned when hitting the floor. char name[TERRAIN_NAME_LEN]; // Lookup name. + UINT32 hash; // Lookup name's hash. UINT16 mobjType; // Thing type. MT_NULL to not spawn anything. UINT16 sfx; // Sound to play. @@ -48,6 +49,7 @@ typedef struct t_footstep_s // These are particles spawned when moving fast enough on a floor. char name[TERRAIN_NAME_LEN]; // Lookup name. + UINT32 hash; // Lookup name's hash. UINT16 mobjType; // Thing type. MT_NULL to not spawn anything. UINT16 sfx; // Sound to play. @@ -79,6 +81,7 @@ typedef struct terrain_s // These are all of the properties that the floor gets. char name[TERRAIN_NAME_LEN]; // Lookup name. + UINT32 hash; // Lookup name's hash. size_t splashID; // Splash defintion ID. size_t footstepID; // Footstep defintion ID. @@ -93,16 +96,14 @@ typedef struct terrain_s typedef struct t_floor_s { // Terrain floor definition. - // Ties texture names to a . + // Ties a texture name to a terrain definition. - // (Could be optimized by using texture IDs instead of names, - // but was concerned because I recall sooomething about those not being netsafe? - // Someone confirm if I just hallucinated that. :V) - - char textureName[9]; // Floor texture name. + char textureName[8]; // Floor texture name. + UINT32 textureHash; // Floor texture hash. size_t terrainID; // Terrain definition ID. } t_floor_t; + /*-------------------------------------------------- size_t K_GetSplashHeapIndex(t_splash_t *splash); @@ -285,6 +286,7 @@ terrain_t *K_GetTerrainByIndex(size_t checkIndex); terrain_t *K_GetTerrainByName(const char *checkName); + /*-------------------------------------------------- terrain_t *K_GetDefaultTerrain(void); From abeec10d3243414c89755782d9f2776c3624d079 Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Sat, 22 Jan 2022 08:00:09 -0500 Subject: [PATCH 061/114] Brightmap loading uses hashes # Conflicts: # src/k_brightmap.c # src/k_brightmap.h --- src/k_brightmap.c | 10 +++++----- src/k_brightmap.h | 8 ++++---- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/src/k_brightmap.c b/src/k_brightmap.c index c62d645e6..a70d7a955 100644 --- a/src/k_brightmap.c +++ b/src/k_brightmap.c @@ -75,7 +75,7 @@ static brightmapStorage_t *K_GetBrightmapStorageByTextureName(const char *checkN { brightmapStorage_t *bms = &brightmapStorage[i]; - if (checkHash == bms->textureHash) + if (checkHash == bms->textureHash && !strncmp(checkName, bms->textureName, 8)) { // Name matches. return bms; @@ -119,8 +119,8 @@ static boolean K_BRIGHTLumpParser(UINT8 *data, size_t size) if (bms == NULL) { bms = K_NewBrightmap(); - strncpy(bms->textureName, tkn, 9); - bms->textureHash = quickncasehash(bms->textureName, 8); + strncpy(bms->textureName, tkn, 8); + bms->textureHash = quickncasehash(tkn, 8); } Z_Free(tkn); @@ -129,8 +129,8 @@ static boolean K_BRIGHTLumpParser(UINT8 *data, size_t size) if (tkn && pos < size) { - strncpy(bms->brightmapName, tkn, 9); - bms->brightmapHash = quickncasehash(bms->brightmapName, 8); + strncpy(bms->brightmapName, tkn, 8); + bms->brightmapHash = quickncasehash(tkn, 8); } else { diff --git a/src/k_brightmap.h b/src/k_brightmap.h index 72bc7e9df..33a1faec2 100644 --- a/src/k_brightmap.h +++ b/src/k_brightmap.h @@ -23,11 +23,11 @@ typedef struct brightmapStorage_s // Stores data for brightmap definitions, // before putting them into texturebrightmaps. - char textureName[9]; // The texture's name. - UINT32 textureHash; // The texture name's hash. + char textureName[8]; // The texture's name. + UINT32 textureHash; // The texture name's hash. - char brightmapName[9]; // The brightmap's name. - UINT32 brightmapHash; // The brightmap name's hash. + char brightmapName[8]; // The brightmap's name. + UINT32 brightmapHash; // The brightmap name's hash. } brightmapStorage_t; /*-------------------------------------------------- From 3dc2e3230d5a1170c2aef686f73608b3df9e8636 Mon Sep 17 00:00:00 2001 From: toaster Date: Wed, 7 Sep 2022 12:33:04 +0100 Subject: [PATCH 062/114] Manual zip-together of Ashnal's cvar suggestions and LJSonic's download speed cap raise - Lowered resynchattempts - Increased maxsend and downloadspeed - Increased max downloadspeed - Doubled nettimeout and jointimeout --- src/d_clisrv.c | 8 ++++---- src/d_netcmd.c | 4 ++-- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/d_clisrv.c b/src/d_clisrv.c index 00207d1bb..6e08137ce 100644 --- a/src/d_clisrv.c +++ b/src/d_clisrv.c @@ -3053,17 +3053,17 @@ consvar_t cv_discordinvites = CVAR_INIT ("discordinvites", "Everyone", CV_SAVE|C static CV_PossibleValue_t resynchattempts_cons_t[] = {{1, "MIN"}, {20, "MAX"}, {0, "No"}, {0, NULL}}; -consvar_t cv_resynchattempts = CVAR_INIT ("resynchattempts", "10", CV_SAVE|CV_NETVAR, resynchattempts_cons_t, NULL); +consvar_t cv_resynchattempts = CVAR_INIT ("resynchattempts", "2", CV_SAVE|CV_NETVAR, resynchattempts_cons_t, NULL); consvar_t cv_blamecfail = CVAR_INIT ("blamecfail", "Off", CV_SAVE|CV_NETVAR, CV_OnOff, NULL); // max file size to send to a player (in kilobytes) static CV_PossibleValue_t maxsend_cons_t[] = {{0, "MIN"}, {51200, "MAX"}, {0, NULL}}; -consvar_t cv_maxsend = CVAR_INIT ("maxsend", "4096", CV_SAVE|CV_NETVAR, maxsend_cons_t, NULL); +consvar_t cv_maxsend = CVAR_INIT ("maxsend", "51200", CV_SAVE|CV_NETVAR, maxsend_cons_t, NULL); consvar_t cv_noticedownload = CVAR_INIT ("noticedownload", "Off", CV_SAVE|CV_NETVAR, CV_OnOff, NULL); // Speed of file downloading (in packets per tic) -static CV_PossibleValue_t downloadspeed_cons_t[] = {{0, "MIN"}, {32, "MAX"}, {0, NULL}}; -consvar_t cv_downloadspeed = CVAR_INIT ("downloadspeed", "16", CV_SAVE|CV_NETVAR, downloadspeed_cons_t, NULL); +static CV_PossibleValue_t downloadspeed_cons_t[] = {{0, "MIN"}, {300, "MAX"}, {0, NULL}}; +consvar_t cv_downloadspeed = CVAR_INIT ("downloadspeed", "32", CV_SAVE|CV_NETVAR, downloadspeed_cons_t, NULL); static void Got_AddPlayer(UINT8 **p, INT32 playernum); static void Got_RemovePlayer(UINT8 **p, INT32 playernum); diff --git a/src/d_netcmd.c b/src/d_netcmd.c index c90129081..b7f41e2a7 100644 --- a/src/d_netcmd.c +++ b/src/d_netcmd.c @@ -481,9 +481,9 @@ consvar_t cv_allowexitlevel = CVAR_INIT ("allowexitlevel", "No", CV_NETVAR, CV_Y consvar_t cv_netstat = CVAR_INIT ("netstat", "Off", 0, CV_OnOff, NULL); // show bandwidth statistics static CV_PossibleValue_t nettimeout_cons_t[] = {{TICRATE/7, "MIN"}, {60*TICRATE, "MAX"}, {0, NULL}}; -consvar_t cv_nettimeout = CVAR_INIT ("nettimeout", "105", CV_CALL|CV_SAVE, nettimeout_cons_t, NetTimeout_OnChange); +consvar_t cv_nettimeout = CVAR_INIT ("nettimeout", "210", 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_jointimeout = CVAR_INIT ("jointimeout", "210", 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_lagless = CVAR_INIT ("lagless", "Off", CV_SAVE|CV_NETVAR|CV_CALL, CV_OnOff, Lagless_OnChange); From 88802852224bf52cccf897fddaf7a1948aa45dbc Mon Sep 17 00:00:00 2001 From: LJ Sonic Date: Thu, 2 Dec 2021 23:34:28 +0100 Subject: [PATCH 063/114] Remove downloadspeed cruft # Conflicts: # src/d_clisrv.c --- src/d_clisrv.c | 2 +- src/d_netfil.c | 10 +--------- 2 files changed, 2 insertions(+), 10 deletions(-) diff --git a/src/d_clisrv.c b/src/d_clisrv.c index 6e08137ce..c0ab93f4c 100644 --- a/src/d_clisrv.c +++ b/src/d_clisrv.c @@ -3062,7 +3062,7 @@ consvar_t cv_maxsend = CVAR_INIT ("maxsend", "51200", CV_SAVE|CV_NETVAR, maxsend consvar_t cv_noticedownload = CVAR_INIT ("noticedownload", "Off", CV_SAVE|CV_NETVAR, CV_OnOff, NULL); // Speed of file downloading (in packets per tic) -static CV_PossibleValue_t downloadspeed_cons_t[] = {{0, "MIN"}, {300, "MAX"}, {0, NULL}}; +static CV_PossibleValue_t downloadspeed_cons_t[] = {{1, "MIN"}, {300, "MAX"}, {0, NULL}}; consvar_t cv_downloadspeed = CVAR_INIT ("downloadspeed", "32", CV_SAVE|CV_NETVAR, downloadspeed_cons_t, NULL); static void Got_AddPlayer(UINT8 **p, INT32 playernum); diff --git a/src/d_netfil.c b/src/d_netfil.c index 84ead407a..5336d598b 100644 --- a/src/d_netfil.c +++ b/src/d_netfil.c @@ -1007,7 +1007,6 @@ static void SV_EndFileSend(INT32 node) filestosend--; } -#define PACKETPERTIC net_bandwidth/(TICRATE*software_MAXPACKETLENGTH) #define FILEFRAGMENTSIZE (software_MAXPACKETLENGTH - (FILETXHEADER + BASEPACKETSIZE)) /** Handles file transmission @@ -1040,14 +1039,7 @@ void FileSendTicker(void) if (!filestosend) // No file to send return; - if (cv_downloadspeed.value) // New behavior - packetsent = cv_downloadspeed.value; - else // Old behavior - { - packetsent = PACKETPERTIC; - if (!packetsent) - packetsent = 1; - } + packetsent = cv_downloadspeed.value; netbuffer->packettype = PT_FILEFRAGMENT; From 7adca2ef1990e07a130b4d438ba386a784289cc8 Mon Sep 17 00:00:00 2001 From: toaster Date: Wed, 7 Sep 2022 13:05:40 +0100 Subject: [PATCH 064/114] Fix papersprites being rotated incorrectly in Software when viewing them on the flipped side Rewritten version of STJr/SRB2!1770 --- src/r_things.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/r_things.c b/src/r_things.c index cc5694dff..97e164a5b 100644 --- a/src/r_things.c +++ b/src/r_things.c @@ -1690,8 +1690,14 @@ static void R_ProjectSprite(mobj_t *thing) if (spriterotangle && !(splat && !(thing->renderflags & RF_NOSPLATROLLANGLE))) { - rollangle = R_GetRollAngle(vflip - ? InvAngle(spriterotangle) : spriterotangle); + if ((papersprite && ang >= ANGLE_180) != vflip) + { + rollangle = R_GetRollAngle(InvAngle(spriterotangle)); + } + else + { + rollangle = R_GetRollAngle(spriterotangle); + } rotsprite = Patch_GetRotatedSprite(sprframe, (thing->frame & FF_FRAMEMASK), rot, flip, false, sprinfo, rollangle); if (rotsprite != NULL) From 4d52ca53e709d08660840ef47affc9324eccaf1f Mon Sep 17 00:00:00 2001 From: toaster Date: Wed, 7 Sep 2022 13:24:31 +0100 Subject: [PATCH 065/114] Make P_SpawnGhostMobj ghosts properly copy spritestuff2 variables - spritexscale - spriteyscale - spritexoffset - spriteyoffset - renderflags --- src/p_user.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/p_user.c b/src/p_user.c index 215f67ffd..c95decdd9 100644 --- a/src/p_user.c +++ b/src/p_user.c @@ -1197,7 +1197,7 @@ mobj_t *P_SpawnGhostMobj(mobj_t *mobj) ghost->sprite2 = mobj->sprite2; ghost->frame = mobj->frame; ghost->tics = -1; - ghost->renderflags |= tr_trans50 << RF_TRANSSHIFT; + ghost->renderflags = (mobj->renderflags & ~RF_TRANSMASK)|RF_TRANS50; ghost->fuse = ghost->info->damage; ghost->skin = mobj->skin; ghost->standingslope = mobj->standingslope; @@ -1207,6 +1207,11 @@ mobj_t *P_SpawnGhostMobj(mobj_t *mobj) ghost->sprzoff = mobj->sprzoff; ghost->rollangle = mobj->rollangle; + ghost->spritexscale = mobj->spritexscale; + ghost->spriteyscale = mobj->spriteyscale; + ghost->spritexoffset = mobj->spritexoffset; + ghost->spriteyoffset = mobj->spriteyoffset; + if (mobj->flags2 & MF2_OBJECTFLIP) ghost->flags |= MF2_OBJECTFLIP; From 925e05a8a0537af1760e1da730a9a475bd03e836 Mon Sep 17 00:00:00 2001 From: toaster Date: Wed, 7 Sep 2022 13:33:40 +0100 Subject: [PATCH 066/114] Sink painstate crash fix --- src/info.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/info.c b/src/info.c index 72665927e..794689a6f 100644 --- a/src/info.c +++ b/src/info.c @@ -24017,7 +24017,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] = sfx_tossed, // seesound 8, // reactiontime sfx_None, // attacksound - 256*FRACUNIT, // painstate + S_NULL, // painstate 100, // painchance sfx_None, // painsound S_NULL, // meleestate From 54e03194ea9625ad29dc65c96fb4053a851c0aa4 Mon Sep 17 00:00:00 2001 From: toaster Date: Wed, 7 Sep 2022 13:50:26 +0100 Subject: [PATCH 067/114] Ignore client joins while loading a level, to prevent a myriad of join bugs. Slightly rewritten from Jugador's, to prevent spurious DEBFILE prints of unknown packet type --- src/d_clisrv.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/d_clisrv.c b/src/d_clisrv.c index 00207d1bb..a3b8cf7fe 100644 --- a/src/d_clisrv.c +++ b/src/d_clisrv.c @@ -4646,7 +4646,10 @@ static void GetPackets(void) if (netbuffer->packettype == PT_CLIENTJOIN && server) { - HandleConnect(node); + if (!levelloading) // Otherwise just ignore + { + HandleConnect(node); + } continue; } if (node == servernode && client && cl_mode != CL_SEARCHING) From f901ce0f00da722ef168cc5ae1cb2bf22ef20267 Mon Sep 17 00:00:00 2001 From: toaster Date: Wed, 7 Sep 2022 14:04:22 +0100 Subject: [PATCH 068/114] Only calculate wipeout slowdown if the player's speed > 0 --- src/k_kart.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/k_kart.c b/src/k_kart.c index 9d92e8118..60d9ee48a 100644 --- a/src/k_kart.c +++ b/src/k_kart.c @@ -9483,7 +9483,7 @@ void K_AdjustPlayerFriction(player_t *player) } // Wipeout slowdown - if (player->spinouttimer && player->wipeoutslow) + if (player->speed > 0 && player->spinouttimer && player->wipeoutslow) { if (player->offroad) player->mo->friction -= 4912; From 082d9206e89113533f4f6f7a8798f872e9a1ea39 Mon Sep 17 00:00:00 2001 From: James R Date: Tue, 6 Sep 2022 12:36:59 -0700 Subject: [PATCH 069/114] Add a hitbox renderer to Software mode renderhitbox - Tangible - collision activating objects, minus rings - All - every object - Intangible - the opposite of Tangible, also no rings - Rings - rings --- src/Sourcefile | 1 + src/d_netcmd.c | 1 + src/p_mobj.h | 2 +- src/r_bbox.c | 303 +++++++++++++++++++++++++++++++++++++++++++++++++ src/r_things.c | 8 ++ src/r_things.h | 6 + src/screen.h | 2 +- 7 files changed, 321 insertions(+), 2 deletions(-) create mode 100644 src/r_bbox.c diff --git a/src/Sourcefile b/src/Sourcefile index 986b75f74..ea5f2d0d5 100644 --- a/src/Sourcefile +++ b/src/Sourcefile @@ -65,6 +65,7 @@ r_skins.c r_sky.c r_splats.c r_things.c +r_bbox.c r_textures.c r_patch.c r_patchrotation.c diff --git a/src/d_netcmd.c b/src/d_netcmd.c index 76034f927..df31f52f6 100644 --- a/src/d_netcmd.c +++ b/src/d_netcmd.c @@ -1008,6 +1008,7 @@ void D_RegisterClientCommands(void) // screen.c CV_RegisterVar(&cv_fullscreen); CV_RegisterVar(&cv_renderview); + CV_RegisterVar(&cv_renderhitbox); CV_RegisterVar(&cv_vhseffect); CV_RegisterVar(&cv_shittyscreen); CV_RegisterVar(&cv_renderer); diff --git a/src/p_mobj.h b/src/p_mobj.h index 3b947bec6..d6b176828 100644 --- a/src/p_mobj.h +++ b/src/p_mobj.h @@ -122,7 +122,7 @@ typedef enum MF_AMBIENT = 1<<10, // Slide this object when it hits a wall. MF_SLIDEME = 1<<11, - // Player cheat. + // Don't collide with walls or solid objects. Two MF_NOCLIP objects can't touch each other at all! MF_NOCLIP = 1<<12, // Allow moves to any height, no gravity. For active floaters. MF_FLOAT = 1<<13, diff --git a/src/r_bbox.c b/src/r_bbox.c new file mode 100644 index 000000000..ef72488fb --- /dev/null +++ b/src/r_bbox.c @@ -0,0 +1,303 @@ +// SONIC ROBO BLAST 2 +//----------------------------------------------------------------------------- +// Copyright (C) 1993-1996 by id Software, Inc. +// Copyright (C) 1998-2000 by DooM Legacy Team. +// Copyright (C) 1999-2021 by Sonic Team Junior. +// Copyright (C) 2022 by Kart Krew. +// +// This program is free software distributed under the +// terms of the GNU General Public License, version 2. +// See the 'LICENSE' file for more details. +//----------------------------------------------------------------------------- +/// \file r_bbox.c +/// \brief Boundary box (cube) renderer + +#include "doomdef.h" +#include "command.h" +#include "r_fps.h" +#include "r_local.h" +#include "screen.h" // cv_renderhitbox +#include "v_video.h" // V_DrawFill + +enum { + RENDERHITBOX_OFF, + RENDERHITBOX_TANGIBLE, + RENDERHITBOX_ALL, + RENDERHITBOX_INTANGIBLE, + RENDERHITBOX_RINGS, +}; + +static CV_PossibleValue_t renderhitbox_cons_t[] = { + {RENDERHITBOX_OFF, "Off"}, + {RENDERHITBOX_TANGIBLE, "Tangible"}, + {RENDERHITBOX_ALL, "All"}, + {RENDERHITBOX_INTANGIBLE, "Intangible"}, + {RENDERHITBOX_RINGS, "Rings"}, + {0}}; + +consvar_t cv_renderhitbox = CVAR_INIT ("renderhitbox", "Off", 0, renderhitbox_cons_t, NULL); + +struct bbox_col { + INT32 x; + INT32 y; + INT32 h; +}; + +struct bbox_config { + fixed_t height; + fixed_t tz; + struct bbox_col col[4]; + UINT8 color; +}; + +static inline void +raster_bbox_seg +( INT32 x, + fixed_t y, + fixed_t h, + UINT8 pixel) +{ + y /= FRACUNIT; + + if (y < 0) + y = 0; + + h = y + (FixedCeil(abs(h)) / FRACUNIT); + + if (h >= viewheight) + h = viewheight; + + while (y < h) + { + topleft[x + y * vid.width] = pixel; + y++; + } +} + +static void +draw_bbox_col +( struct bbox_config * bb, + int p, + fixed_t tx, + fixed_t ty) +{ + struct bbox_col *col = &bb->col[p]; + + fixed_t xscale = FixedDiv(projection[viewssnum], ty); + fixed_t yscale = FixedDiv(projectiony[viewssnum], ty); + + col->x = (centerxfrac + FixedMul(tx, xscale)) / FRACUNIT; + col->y = (centeryfrac - FixedMul(bb->tz, yscale)); + col->h = FixedMul(bb->height, yscale); + + // Using this function is TOO EASY! + V_DrawFill( + viewwindowx + col->x, + viewwindowy + col->y / FRACUNIT, 1, + col->h / FRACUNIT, V_NOSCALESTART | bb->color); +} + +static void +draw_bbox_row +( struct bbox_config * bb, + int p1, + int p2) +{ + struct bbox_col + *a = &bb->col[p1], + *b = &bb->col[p2]; + + INT32 x1, x2; // left, right + INT32 dx; // width + + fixed_t y1, y2; // top, bottom + fixed_t s1, s2; // top and bottom increment + + if (a->x > b->x) + { + struct bbox_col *c = a; + a = b; + b = c; + } + + x1 = a->x; + x2 = b->x; + + if (x2 >= viewwidth) + x2 = viewwidth - 1; + + if (x1 == x2 || x1 >= viewwidth || x2 < 0) + return; + + dx = x2 - x1; + + y1 = a->y; + y2 = b->y; + s1 = (y2 - y1) / dx; + + y2 = y1 + a->h; + s2 = ((b->y + b->h) - y2) / dx; + + // FixedCeil needs a minimum!!! :D :D + + if (s1 == 0) + s1 = 1; + + if (s2 == 0) + s2 = 1; + + if (x1 < 0) + { + y1 -= x1 * s1; + y2 -= x1 * s2; + x1 = 0; + } + + while (x1 < x2) + { + raster_bbox_seg(x1, y1, s1, bb->color); + raster_bbox_seg(x1, y2, s2, bb->color); + + y1 += s1; + y2 += s2; + + x1++; + } +} + +static UINT8 +get_bbox_color (mobj_t *thing) +{ + UINT32 flags = thing->flags; + + if (thing->player) + return 255; // 0FF + + if (flags & (MF_NOCLIPTHING)) + return 7; // BFBFBF + + if (flags & (MF_SPECIAL)) + return 73; // FF0 + + if (flags & (MF_BOSS|MF_MISSILE|MF_ENEMY|MF_PAIN)) + return 35; // F00 + + if (flags & (MF_NOCLIP)) + return 152; // 00F + + return 0; // FFF +} + +void R_DrawThingBoundingBox(mobj_t *thing) +{ + fixed_t rs, rc; // radius offsets + fixed_t gx, gy; // origin + fixed_t tx, ty; // translated coordinates + + // uncapped/interpolation + interpmobjstate_t interp = {0}; + + struct bbox_config bb = {0}; + + // do interpolation + if (R_UsingFrameInterpolation() && !paused) + { + R_InterpolateMobjState(thing, rendertimefrac, &interp); + } + else + { + R_InterpolateMobjState(thing, FRACUNIT, &interp); + } + + rs = FixedMul(thing->radius, viewsin); + rc = FixedMul(thing->radius, viewcos); + + gx = interp.x - viewx; + gy = interp.y - viewy; + + tx = FixedMul(gx, viewsin) - FixedMul(gy, viewcos); + ty = FixedMul(gx, viewcos) + FixedMul(gy, viewsin); + + bb.height = thing->height; + bb.tz = (interp.z + bb.height) - viewz; + bb.color = get_bbox_color(thing); + + // 1--3 + // | | + // 0--2 + + // left + + tx -= rs; + ty -= rc; + + draw_bbox_col(&bb, 0, tx + rc, ty - rs); // bottom + draw_bbox_col(&bb, 1, tx - rc, ty + rs); // top + + // right + + tx += rs + rs; + ty += rc + rc; + + draw_bbox_col(&bb, 2, tx + rc, ty - rs); // bottom + draw_bbox_col(&bb, 3, tx - rc, ty + rs); // top + + // connect all four columns + + draw_bbox_row(&bb, 0, 1); + draw_bbox_row(&bb, 1, 3); + draw_bbox_row(&bb, 3, 2); + draw_bbox_row(&bb, 2, 0); +} + +static boolean is_tangible (mobj_t *thing) +{ + // These objects can never touch another + if (thing->flags & (MF_NOCLIPTHING)) + { + return false; + } + + // These objects probably do nothing! :D + if ((thing->flags & (MF_SPECIAL|MF_SOLID|MF_SHOOTABLE + |MF_PUSHABLE|MF_BOSS|MF_MISSILE|MF_SPRING + |MF_MONITOR|MF_ENEMY|MF_PAIN|MF_STICKY + |MF_PICKUPFROMBELOW)) == 0U) + { + return false; + } + + return true; +} + +boolean R_ThingBoundingBoxVisible(mobj_t *thing) +{ + INT32 cvmode = cv_renderhitbox.value; + + switch (cvmode) + { + case RENDERHITBOX_OFF: + return false; + + case RENDERHITBOX_ALL: + return true; + + case RENDERHITBOX_INTANGIBLE: + return !is_tangible(thing); + + case RENDERHITBOX_TANGIBLE: + // Exclude rings from here, lots of them! + if (thing->type == MT_RING) + { + return false; + } + + return is_tangible(thing); + + case RENDERHITBOX_RINGS: + return (thing->type == MT_RING); + + default: + return false; + } +} diff --git a/src/r_things.c b/src/r_things.c index d0fd57600..ca4e6ecf7 100644 --- a/src/r_things.c +++ b/src/r_things.c @@ -2950,6 +2950,14 @@ static void R_DrawSprite(vissprite_t *spr) R_DrawFloorSplat(spr); else R_DrawVisSprite(spr); + + if (R_ThingBoundingBoxVisible(spr->mobj)) + { + // fuck you fuck you fuck you FUCK YOU + // (shadows are linked to their mobj) + if (!(spr->cut & SC_SHADOW)) + R_DrawThingBoundingBox(spr->mobj); + } } // Special drawer for precipitation sprites Tails 08-18-2002 diff --git a/src/r_things.h b/src/r_things.h index bd7449d3e..ef5ceedd0 100644 --- a/src/r_things.h +++ b/src/r_things.h @@ -66,8 +66,14 @@ void R_AddSprites(sector_t *sec, INT32 lightlevel); void R_InitSprites(void); void R_ClearSprites(void); +boolean R_ThingBoundingBoxVisible(mobj_t *thing); +void R_DrawThingBoundingBox(mobj_t *thing); + boolean R_ThingVisible (mobj_t *thing); +boolean R_ThingWithinDist (mobj_t *thing, + fixed_t draw_dist); + boolean R_ThingVisibleWithinDist (mobj_t *thing, fixed_t draw_dist); diff --git a/src/screen.h b/src/screen.h index 268b11e95..8abf77224 100644 --- a/src/screen.h +++ b/src/screen.h @@ -201,7 +201,7 @@ extern CV_PossibleValue_t cv_renderer_t[]; extern INT32 scr_bpp; extern UINT8 *scr_borderpatch; // patch used to fill the view borders -extern consvar_t cv_scr_width, cv_scr_height, cv_scr_depth, cv_renderview, cv_renderer, cv_fullscreen; +extern consvar_t cv_scr_width, cv_scr_height, cv_scr_depth, cv_renderview, cv_renderer, cv_renderhitbox, cv_fullscreen; extern consvar_t cv_vhseffect, cv_shittyscreen; // wait for page flipping to end or not From 5107160a27c26bb277563bfb0d553f9ac112f85d Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Sat, 10 Sep 2022 03:52:31 -0400 Subject: [PATCH 070/114] Circuit pathfinding An alternative to the standard pathfind function. Instead of pathfinding to a specific waypoint, it always goes towards the finish line waypoint, but also won't stop when it reaches it. It only stops when it travels a far enough distance. This is basically a cleaner, less hacky, and optimized version of the pathfinding I gave to the bots; instead of doing 1-2 full pathfinds to do this (depending on if they are near the finish line or not), it will instead always do a single small pathfind. I also need it for shrink laser behavior. --- src/k_bot.c | 34 +--- src/k_pathfind.c | 410 ++++++++++++++++++++++++----------------------- src/k_pathfind.h | 5 + src/k_waypoint.c | 153 +++++++++++++++++- src/k_waypoint.h | 31 ++++ 5 files changed, 397 insertions(+), 236 deletions(-) diff --git a/src/k_bot.c b/src/k_bot.c index 3fbb5278d..a2c9f0a11 100644 --- a/src/k_bot.c +++ b/src/k_bot.c @@ -660,7 +660,6 @@ static botprediction_t *K_CreateBotPrediction(player_t *player) angle_t angletonext = ANGLE_MAX; INT32 disttonext = INT32_MAX; - waypoint_t *finishLine = K_GetFinishLineWaypoint(); waypoint_t *wp = player->nextwaypoint; mobj_t *prevwpmobj = player->mo; @@ -676,8 +675,8 @@ static botprediction_t *K_CreateBotPrediction(player_t *player) angletonext = R_PointToAngle2(prevwpmobj->x, prevwpmobj->y, wp->mobj->x, wp->mobj->y); disttonext = P_AproxDistance(prevwpmobj->x - wp->mobj->x, prevwpmobj->y - wp->mobj->y) / FRACUNIT; - pathfindsuccess = K_PathfindToWaypoint( - player->nextwaypoint, finishLine, + pathfindsuccess = K_PathfindThruCircuit( + player->nextwaypoint, (unsigned)distanceleft, &pathtofinish, useshortcuts, huntbackwards ); @@ -720,35 +719,6 @@ static botprediction_t *K_CreateBotPrediction(player_t *player) // We're done!! break; } - - if (i == pathtofinish.numnodes-1 && disttonext > 0) - { - // We were pathfinding to the finish, but we want to go past it. - // Set up a new pathfind. - - waypoint_t *next = NULL; - - if (finishLine->numnextwaypoints == 0) - { - distanceleft = 0; - break; - } - - // default to first one - next = wp->nextwaypoints[0]; - - pathfindsuccess = K_PathfindToWaypoint( - next, finishLine, - &pathtofinish, - useshortcuts, huntbackwards - ); - - if (pathfindsuccess == false) - { - distanceleft = 0; - break; - } - } } Z_Free(pathtofinish.array); diff --git a/src/k_pathfind.c b/src/k_pathfind.c index 857f0343a..bd11ecdeb 100644 --- a/src/k_pathfind.c +++ b/src/k_pathfind.c @@ -190,6 +190,10 @@ static boolean K_PathfindSetupValid(const pathfindsetup_t *const pathfindsetup) { CONS_Debug(DBG_GAMELOGIC, "Pathfindsetup has NULL gettraversable function.\n"); } + else if (pathfindsetup->getfinished == NULL) + { + CONS_Debug(DBG_GAMELOGIC, "Pathfindsetup has NULL getfinished function.\n"); + } else if (pathfindsetup->getconnectednodes(pathfindsetup->startnodedata, &sourcenodenumconnectednodes) == NULL) { CONS_Debug(DBG_GAMELOGIC, "K_PathfindSetupValid: Source node returned NULL connecting nodes.\n"); @@ -295,242 +299,244 @@ boolean K_PathfindAStar(path_t *const path, pathfindsetup_t *const pathfindsetup { CONS_Debug(DBG_GAMELOGIC, "K_PathfindAStar: Pathfinding setup is not valid.\n"); } - else if (pathfindsetup->startnodedata == pathfindsetup->endnodedata) + else { - // At the destination, return a simple 1 node path pathfindnode_t singlenode = {0}; singlenode.camefrom = NULL; - singlenode.nodedata = pathfindsetup->endnodedata; + singlenode.nodedata = pathfindsetup->startnodedata; singlenode.heapindex = SIZE_MAX; singlenode.hscore = 0U; singlenode.gscore = 0U; - K_ReconstructPath(path, &singlenode); - - pathfindsuccess = true; - } - else - { - bheap_t openset = {0}; - bheapitem_t poppedbheapitem = {0}; - pathfindnode_t *nodesarray = NULL; - pathfindnode_t **closedset = NULL; - pathfindnode_t *newnode = NULL; - pathfindnode_t *currentnode = NULL; - pathfindnode_t *connectingnode = NULL; - void **connectingnodesdata = NULL; - void *checknodedata = NULL; - UINT32 *connectingnodecosts = NULL; - size_t numconnectingnodes = 0U; - size_t connectingnodeheapindex = 0U; - size_t nodesarraycount = 0U; - size_t closedsetcount = 0U; - size_t i = 0U; - UINT32 tentativegscore = 0U; - - // Set the dynamic structure capacites to defaults if they are 0 - if (pathfindsetup->nodesarraycapacity == 0U) + if (pathfindsetup->getfinished(&singlenode, pathfindsetup) == true) { - pathfindsetup->nodesarraycapacity = DEFAULT_NODEARRAY_CAPACITY; + // At the destination, return a simple 1 node path + K_ReconstructPath(path, &singlenode); + pathfindsuccess = true; } - if (pathfindsetup->opensetcapacity == 0U) + else { - pathfindsetup->opensetcapacity = DEFAULT_OPENSET_CAPACITY; - } - if (pathfindsetup->closedsetcapacity == 0U) - { - pathfindsetup->closedsetcapacity = DEFAULT_CLOSEDSET_CAPACITY; - } + bheap_t openset = {0}; + bheapitem_t poppedbheapitem = {0}; + pathfindnode_t *nodesarray = NULL; + pathfindnode_t **closedset = NULL; + pathfindnode_t *newnode = NULL; + pathfindnode_t *currentnode = NULL; + pathfindnode_t *connectingnode = NULL; + void **connectingnodesdata = NULL; + void *checknodedata = NULL; + UINT32 *connectingnodecosts = NULL; + size_t numconnectingnodes = 0U; + size_t connectingnodeheapindex = 0U; + size_t nodesarraycount = 0U; + size_t closedsetcount = 0U; + size_t i = 0U; + UINT32 tentativegscore = 0U; - // Allocate the necessary memory - nodesarray = Z_Calloc(pathfindsetup->nodesarraycapacity * sizeof(pathfindnode_t), PU_STATIC, NULL); - if (nodesarray == NULL) - { - I_Error("K_PathfindAStar: Out of memory allocating nodes array."); - } - closedset = Z_Calloc(pathfindsetup->closedsetcapacity * sizeof(pathfindnode_t*), PU_STATIC, NULL); - if (closedset == NULL) - { - I_Error("K_PathfindAStar: Out of memory allocating closed set."); - } - K_BHeapInit(&openset, pathfindsetup->opensetcapacity); - - // Create the first node and add it to the open set - newnode = &nodesarray[nodesarraycount]; - newnode->heapindex = SIZE_MAX; - newnode->nodedata = pathfindsetup->startnodedata; - newnode->camefrom = NULL; - newnode->gscore = 0U; - newnode->hscore = pathfindsetup->getheuristic(newnode->nodedata, pathfindsetup->endnodedata); - nodesarraycount++; - K_BHeapPush(&openset, newnode, K_NodeGetFScore(newnode), K_NodeUpdateHeapIndex); - - // update openset capacity if it changed - if (openset.capacity != pathfindsetup->opensetcapacity) - { - pathfindsetup->opensetcapacity = openset.capacity; - } - - // Go through each node in the openset, adding new ones from each node to it - // this continues until a path is found or there are no more nodes to check - while (openset.count > 0U) - { - // pop the best node off of the openset - K_BHeapPop(&openset, &poppedbheapitem); - currentnode = (pathfindnode_t*)poppedbheapitem.data; - - if (currentnode->nodedata == pathfindsetup->endnodedata) + // Set the dynamic structure capacites to defaults if they are 0 + if (pathfindsetup->nodesarraycapacity == 0U) { - pathfindsuccess = K_ReconstructPath(path, currentnode); - break; + pathfindsetup->nodesarraycapacity = DEFAULT_NODEARRAY_CAPACITY; + } + if (pathfindsetup->opensetcapacity == 0U) + { + pathfindsetup->opensetcapacity = DEFAULT_OPENSET_CAPACITY; + } + if (pathfindsetup->closedsetcapacity == 0U) + { + pathfindsetup->closedsetcapacity = DEFAULT_CLOSEDSET_CAPACITY; } - // Place the node we just popped into the closed set, as we are now evaluating it - if (closedsetcount >= pathfindsetup->closedsetcapacity) + // Allocate the necessary memory + nodesarray = Z_Calloc(pathfindsetup->nodesarraycapacity * sizeof(pathfindnode_t), PU_STATIC, NULL); + if (nodesarray == NULL) { - // Need to reallocate closedset to fit another node - pathfindsetup->closedsetcapacity = pathfindsetup->closedsetcapacity * 2; - closedset = - Z_Realloc(closedset, pathfindsetup->closedsetcapacity * sizeof(pathfindnode_t*), PU_STATIC, NULL); - if (closedset == NULL) + I_Error("K_PathfindAStar: Out of memory allocating nodes array."); + } + closedset = Z_Calloc(pathfindsetup->closedsetcapacity * sizeof(pathfindnode_t*), PU_STATIC, NULL); + if (closedset == NULL) + { + I_Error("K_PathfindAStar: Out of memory allocating closed set."); + } + K_BHeapInit(&openset, pathfindsetup->opensetcapacity); + + // Create the first node and add it to the open set + newnode = &nodesarray[nodesarraycount]; + newnode->heapindex = SIZE_MAX; + newnode->nodedata = pathfindsetup->startnodedata; + newnode->camefrom = NULL; + newnode->gscore = 0U; + newnode->hscore = pathfindsetup->getheuristic(newnode->nodedata, pathfindsetup->endnodedata); + nodesarraycount++; + K_BHeapPush(&openset, newnode, K_NodeGetFScore(newnode), K_NodeUpdateHeapIndex); + + // update openset capacity if it changed + if (openset.capacity != pathfindsetup->opensetcapacity) + { + pathfindsetup->opensetcapacity = openset.capacity; + } + + // Go through each node in the openset, adding new ones from each node to it + // this continues until a path is found or there are no more nodes to check + while (openset.count > 0U) + { + // pop the best node off of the openset + K_BHeapPop(&openset, &poppedbheapitem); + currentnode = (pathfindnode_t*)poppedbheapitem.data; + + if (pathfindsetup->getfinished(currentnode, pathfindsetup) == true) { - I_Error("K_PathfindAStar: Out of memory reallocating closed set."); + pathfindsuccess = K_ReconstructPath(path, currentnode); + break; } - } - closedset[closedsetcount] = currentnode; - closedsetcount++; - // Get the needed data for the next nodes from the current node - connectingnodesdata = pathfindsetup->getconnectednodes(currentnode->nodedata, &numconnectingnodes); - connectingnodecosts = pathfindsetup->getconnectioncosts(currentnode->nodedata); - - if (connectingnodesdata == NULL) - { - CONS_Debug(DBG_GAMELOGIC, "K_PathfindAStar: A Node returned NULL connecting node data.\n"); - } - else if (connectingnodecosts == NULL) - { - CONS_Debug(DBG_GAMELOGIC, "K_PathfindAStar: A Node returned NULL connecting node costs.\n"); - } - else - { - // For each connecting node add it to the openset if it's unevaluated and not there, - // skip it if it's in the closedset or not traversable - for (i = 0; i < numconnectingnodes; i++) + // Place the node we just popped into the closed set, as we are now evaluating it + if (closedsetcount >= pathfindsetup->closedsetcapacity) { - checknodedata = connectingnodesdata[i]; - - if (checknodedata == NULL) + // Need to reallocate closedset to fit another node + pathfindsetup->closedsetcapacity = pathfindsetup->closedsetcapacity * 2; + closedset = + Z_Realloc(closedset, pathfindsetup->closedsetcapacity * sizeof(pathfindnode_t*), PU_STATIC, NULL); + if (closedset == NULL) { - CONS_Debug(DBG_GAMELOGIC, "K_PathfindAStar: A Node has a NULL connecting node.\n"); + I_Error("K_PathfindAStar: Out of memory reallocating closed set."); } - else + } + closedset[closedsetcount] = currentnode; + closedsetcount++; + + // Get the needed data for the next nodes from the current node + connectingnodesdata = pathfindsetup->getconnectednodes(currentnode->nodedata, &numconnectingnodes); + connectingnodecosts = pathfindsetup->getconnectioncosts(currentnode->nodedata); + + if (connectingnodesdata == NULL) + { + CONS_Debug(DBG_GAMELOGIC, "K_PathfindAStar: A Node returned NULL connecting node data.\n"); + } + else if (connectingnodecosts == NULL) + { + CONS_Debug(DBG_GAMELOGIC, "K_PathfindAStar: A Node returned NULL connecting node costs.\n"); + } + else + { + // For each connecting node add it to the openset if it's unevaluated and not there, + // skip it if it's in the closedset or not traversable + for (i = 0; i < numconnectingnodes; i++) { - // skip this node if it isn't traversable - if (pathfindsetup->gettraversable(checknodedata, currentnode->nodedata) == false) + checknodedata = connectingnodesdata[i]; + + if (checknodedata == NULL) { - continue; - } - - // Figure out what the gscore of this route for the connecting node is - tentativegscore = currentnode->gscore + connectingnodecosts[i]; - - // find this data in the nodes array if it's been generated before - connectingnode = K_NodesArrayContainsNodeData(nodesarray, checknodedata, nodesarraycount); - - if (connectingnode != NULL) - { - // The connecting node has been seen before, so it must be in either the closedset (skip it) - // or the openset (re-evaluate it's gscore) - if (K_ClosedsetContainsNode(closedset, connectingnode, closedsetcount) == true) - { - continue; - } - else if (tentativegscore < connectingnode->gscore) - { - // The node is not in the closedset, update it's gscore if this path to it is faster - connectingnode->gscore = tentativegscore; - connectingnode->camefrom = currentnode; - - connectingnodeheapindex = - K_BHeapContains(&openset, connectingnode, connectingnode->heapindex); - if (connectingnodeheapindex != SIZE_MAX) - { - K_UpdateBHeapItemValue( - &openset.array[connectingnodeheapindex], K_NodeGetFScore(connectingnode)); - } - else - { - // SOMEHOW the node is not in either the closed set OR the open set - CONS_Debug(DBG_GAMELOGIC, "K_PathfindAStar: A Node is not in either set.\n"); - } - } + CONS_Debug(DBG_GAMELOGIC, "K_PathfindAStar: A Node has a NULL connecting node.\n"); } else { - // Node is not created yet, so it hasn't been seen so far - // Reallocate nodesarray if it's full - if (nodesarraycount >= pathfindsetup->nodesarraycapacity) + // skip this node if it isn't traversable + if (pathfindsetup->gettraversable(checknodedata, currentnode->nodedata) == false) { - pathfindnode_t *nodesarrayrealloc = NULL; - pathfindsetup->nodesarraycapacity = pathfindsetup->nodesarraycapacity * 2; - nodesarrayrealloc = Z_Realloc(nodesarray, pathfindsetup->nodesarraycapacity * sizeof(pathfindnode_t), PU_STATIC, NULL); - - if (nodesarrayrealloc == NULL) - { - I_Error("K_PathfindAStar: Out of memory reallocating nodes array."); - } - - // Need to update pointers in closedset, openset, and node "camefrom" if nodesarray moved. - if (nodesarray != nodesarrayrealloc) - { - size_t j = 0U; - size_t arrayindex = 0U; - for (j = 0U; j < closedsetcount; j++) - { - arrayindex = closedset[j] - nodesarray; - closedset[j] = &nodesarrayrealloc[arrayindex]; - } - for (j = 0U; j < openset.count; j++) - { - arrayindex = ((pathfindnode_t *)(openset.array[j].data)) - nodesarray; - openset.array[j].data = &nodesarrayrealloc[arrayindex]; - } - for (j = 0U; j < nodesarraycount; j++) - { - if (nodesarrayrealloc[j].camefrom != NULL) - { - arrayindex = nodesarrayrealloc[j].camefrom - nodesarray; - nodesarrayrealloc[j].camefrom = &nodesarrayrealloc[arrayindex]; - } - } - - arrayindex = currentnode - nodesarray; - currentnode = &nodesarrayrealloc[arrayindex]; - } - - nodesarray = nodesarrayrealloc; + continue; } - // Create the new node and add it to the nodes array and open set - newnode = &nodesarray[nodesarraycount]; - newnode->heapindex = SIZE_MAX; - newnode->nodedata = checknodedata; - newnode->camefrom = currentnode; - newnode->gscore = tentativegscore; - newnode->hscore = pathfindsetup->getheuristic(newnode->nodedata, pathfindsetup->endnodedata); - nodesarraycount++; - K_BHeapPush(&openset, newnode, K_NodeGetFScore(newnode), K_NodeUpdateHeapIndex); + // Figure out what the gscore of this route for the connecting node is + tentativegscore = currentnode->gscore + connectingnodecosts[i]; + + // find this data in the nodes array if it's been generated before + connectingnode = K_NodesArrayContainsNodeData(nodesarray, checknodedata, nodesarraycount); + + if (connectingnode != NULL) + { + // The connecting node has been seen before, so it must be in either the closedset (skip it) + // or the openset (re-evaluate it's gscore) + if (K_ClosedsetContainsNode(closedset, connectingnode, closedsetcount) == true) + { + continue; + } + else if (tentativegscore < connectingnode->gscore) + { + // The node is not in the closedset, update it's gscore if this path to it is faster + connectingnode->gscore = tentativegscore; + connectingnode->camefrom = currentnode; + + connectingnodeheapindex = + K_BHeapContains(&openset, connectingnode, connectingnode->heapindex); + if (connectingnodeheapindex != SIZE_MAX) + { + K_UpdateBHeapItemValue( + &openset.array[connectingnodeheapindex], K_NodeGetFScore(connectingnode)); + } + else + { + // SOMEHOW the node is not in either the closed set OR the open set + CONS_Debug(DBG_GAMELOGIC, "K_PathfindAStar: A Node is not in either set.\n"); + } + } + } + else + { + // Node is not created yet, so it hasn't been seen so far + // Reallocate nodesarray if it's full + if (nodesarraycount >= pathfindsetup->nodesarraycapacity) + { + pathfindnode_t *nodesarrayrealloc = NULL; + pathfindsetup->nodesarraycapacity = pathfindsetup->nodesarraycapacity * 2; + nodesarrayrealloc = Z_Realloc(nodesarray, pathfindsetup->nodesarraycapacity * sizeof(pathfindnode_t), PU_STATIC, NULL); + + if (nodesarrayrealloc == NULL) + { + I_Error("K_PathfindAStar: Out of memory reallocating nodes array."); + } + + // Need to update pointers in closedset, openset, and node "camefrom" if nodesarray moved. + if (nodesarray != nodesarrayrealloc) + { + size_t j = 0U; + size_t arrayindex = 0U; + for (j = 0U; j < closedsetcount; j++) + { + arrayindex = closedset[j] - nodesarray; + closedset[j] = &nodesarrayrealloc[arrayindex]; + } + for (j = 0U; j < openset.count; j++) + { + arrayindex = ((pathfindnode_t *)(openset.array[j].data)) - nodesarray; + openset.array[j].data = &nodesarrayrealloc[arrayindex]; + } + for (j = 0U; j < nodesarraycount; j++) + { + if (nodesarrayrealloc[j].camefrom != NULL) + { + arrayindex = nodesarrayrealloc[j].camefrom - nodesarray; + nodesarrayrealloc[j].camefrom = &nodesarrayrealloc[arrayindex]; + } + } + + arrayindex = currentnode - nodesarray; + currentnode = &nodesarrayrealloc[arrayindex]; + } + + nodesarray = nodesarrayrealloc; + } + + // Create the new node and add it to the nodes array and open set + newnode = &nodesarray[nodesarraycount]; + newnode->heapindex = SIZE_MAX; + newnode->nodedata = checknodedata; + newnode->camefrom = currentnode; + newnode->gscore = tentativegscore; + newnode->hscore = pathfindsetup->getheuristic(newnode->nodedata, pathfindsetup->endnodedata); + nodesarraycount++; + K_BHeapPush(&openset, newnode, K_NodeGetFScore(newnode), K_NodeUpdateHeapIndex); + } } } } } - } - // Clean up the memory - K_BHeapFree(&openset); - Z_Free(closedset); - Z_Free(nodesarray); + // Clean up the memory + K_BHeapFree(&openset); + Z_Free(closedset); + Z_Free(nodesarray); + } } return pathfindsuccess; diff --git a/src/k_pathfind.h b/src/k_pathfind.h index 7b7f1475f..3d08d87e7 100644 --- a/src/k_pathfind.h +++ b/src/k_pathfind.h @@ -29,6 +29,9 @@ typedef UINT32(*getnodeheuristicfunc)(void*, void*); // function pointer for getting if a node is traversable from its base data typedef boolean(*getnodetraversablefunc)(void*, void*); +// function pointer for getting if a node is our pathfinding end point +typedef boolean(*getpathfindfinishedfunc)(void*, void*); + // A pathfindnode contains information about a node from the pathfinding // heapindex is only used within the pathfinding algorithm itself, and is always 0 after it is completed @@ -58,10 +61,12 @@ typedef struct pathfindsetup_s { size_t nodesarraycapacity; void *startnodedata; void *endnodedata; + UINT32 endgscore; getconnectednodesfunc getconnectednodes; getnodeconnectioncostsfunc getconnectioncosts; getnodeheuristicfunc getheuristic; getnodetraversablefunc gettraversable; + getpathfindfinishedfunc getfinished; } pathfindsetup_t; diff --git a/src/k_waypoint.c b/src/k_waypoint.c index 357731bd9..a6a592a6b 100644 --- a/src/k_waypoint.c +++ b/src/k_waypoint.c @@ -1039,6 +1039,68 @@ static boolean K_WaypointPathfindTraversableNoShortcuts(void *data, void *prevda return traversable; } +/*-------------------------------------------------- + static boolean K_WaypointPathfindReachedEnd(void *data, void *setupData) + + Returns if the current waypoint data is our end point of our pathfinding. + + Input Arguments:- + data - Should point to a pathfindnode_t to compare + setupData - Should point to the pathfindsetup_t to compare + + Return:- + True if the waypoint is the pathfind end point, false otherwise. +--------------------------------------------------*/ +static boolean K_WaypointPathfindReachedEnd(void *data, void *setupData) +{ + boolean isEnd = false; + + if (data == NULL || setupData == NULL) + { + CONS_Debug(DBG_GAMELOGIC, "K_WaypointPathfindReachedEnd received NULL data.\n"); + } + else + { + pathfindnode_t *node = (pathfindnode_t *)data; + pathfindsetup_t *setup = (pathfindsetup_t *)setupData; + + isEnd = (node->nodedata == setup->endnodedata); + } + + return isEnd; +} + +/*-------------------------------------------------- + static boolean K_WaypointPathfindReachedGScore(void *data, void *setupData) + + Returns if the current waypoint data reaches our end G score. + + Input Arguments:- + data - Should point to a pathfindnode_t to compare + setupData - Should point to the pathfindsetup_t to compare + + Return:- + True if the waypoint reached the G score, false otherwise. +--------------------------------------------------*/ +static boolean K_WaypointPathfindReachedGScore(void *data, void *setupData) +{ + boolean scoreReached = false; + + if (data == NULL || setupData == NULL) + { + CONS_Debug(DBG_GAMELOGIC, "K_WaypointPathfindReachedGScore received NULL data.\n"); + } + else + { + pathfindnode_t *node = (pathfindnode_t *)data; + pathfindsetup_t *setup = (pathfindsetup_t *)setupData; + + scoreReached = (node->gscore >= setup->endgscore); + } + + return scoreReached; +} + /*-------------------------------------------------- boolean K_PathfindToWaypoint( waypoint_t *const sourcewaypoint, @@ -1087,18 +1149,19 @@ boolean K_PathfindToWaypoint( getnodeconnectioncostsfunc nodecostsfunc = K_WaypointPathfindGetNextCosts; getnodeheuristicfunc heuristicfunc = K_WaypointPathfindGetHeuristic; getnodetraversablefunc traversablefunc = K_WaypointPathfindTraversableNoShortcuts; + getpathfindfinishedfunc finishedfunc = K_WaypointPathfindReachedEnd; if (huntbackwards) { nextnodesfunc = K_WaypointPathfindGetPrev; nodecostsfunc = K_WaypointPathfindGetPrevCosts; } + if (useshortcuts) { traversablefunc = K_WaypointPathfindTraversableAllEnabled; } - pathfindsetup.opensetcapacity = K_GetOpensetBaseSize(); pathfindsetup.closedsetcapacity = K_GetClosedsetBaseSize(); pathfindsetup.nodesarraycapacity = K_GetNodesArrayBaseSize(); @@ -1108,6 +1171,90 @@ boolean K_PathfindToWaypoint( pathfindsetup.getconnectioncosts = nodecostsfunc; pathfindsetup.getheuristic = heuristicfunc; pathfindsetup.gettraversable = traversablefunc; + pathfindsetup.getfinished = finishedfunc; + + pathfound = K_PathfindAStar(returnpath, &pathfindsetup); + + K_UpdateOpensetBaseSize(pathfindsetup.opensetcapacity); + K_UpdateClosedsetBaseSize(pathfindsetup.closedsetcapacity); + K_UpdateNodesArrayBaseSize(pathfindsetup.nodesarraycapacity); + } + + return pathfound; +} + +/*-------------------------------------------------- + boolean K_PathfindThruCircuit( + waypoint_t *const sourcewaypoint, + const UINT32 traveldistance, + path_t *const returnpath, + const boolean useshortcuts, + const boolean huntbackwards) + + See header file for description. +--------------------------------------------------*/ +boolean K_PathfindThruCircuit( + waypoint_t *const sourcewaypoint, + const UINT32 traveldistance, + path_t *const returnpath, + const boolean useshortcuts, + const boolean huntbackwards) +{ + boolean pathfound = false; + + if (sourcewaypoint == NULL) + { + CONS_Debug(DBG_GAMELOGIC, "NULL sourcewaypoint in K_PathfindThruCircuit.\n"); + } + else if (finishline == NULL) + { + CONS_Debug(DBG_GAMELOGIC, "NULL finishline in K_PathfindThruCircuit.\n"); + } + else if (((huntbackwards == false) && (sourcewaypoint->numnextwaypoints == 0)) + || ((huntbackwards == true) && (sourcewaypoint->numprevwaypoints == 0))) + { + CONS_Debug(DBG_GAMELOGIC, + "K_PathfindThruCircuit: sourcewaypoint with ID %d has no next waypoint\n", + K_GetWaypointID(sourcewaypoint)); + } + else if (((huntbackwards == false) && (finishline->numprevwaypoints == 0)) + || ((huntbackwards == true) && (finishline->numnextwaypoints == 0))) + { + CONS_Debug(DBG_GAMELOGIC, + "K_PathfindThruCircuit: finishline with ID %d has no previous waypoint\n", + K_GetWaypointID(finishline)); + } + else + { + pathfindsetup_t pathfindsetup = {0}; + getconnectednodesfunc nextnodesfunc = K_WaypointPathfindGetNext; + getnodeconnectioncostsfunc nodecostsfunc = K_WaypointPathfindGetNextCosts; + getnodeheuristicfunc heuristicfunc = K_WaypointPathfindGetHeuristic; + getnodetraversablefunc traversablefunc = K_WaypointPathfindTraversableNoShortcuts; + getpathfindfinishedfunc finishedfunc = K_WaypointPathfindReachedGScore; + + if (huntbackwards) + { + nextnodesfunc = K_WaypointPathfindGetPrev; + nodecostsfunc = K_WaypointPathfindGetPrevCosts; + } + + if (useshortcuts) + { + traversablefunc = K_WaypointPathfindTraversableAllEnabled; + } + + pathfindsetup.opensetcapacity = K_GetOpensetBaseSize(); + pathfindsetup.closedsetcapacity = K_GetClosedsetBaseSize(); + pathfindsetup.nodesarraycapacity = K_GetNodesArrayBaseSize(); + pathfindsetup.startnodedata = sourcewaypoint; + pathfindsetup.endnodedata = finishline; + pathfindsetup.endgscore = traveldistance; + pathfindsetup.getconnectednodes = nextnodesfunc; + pathfindsetup.getconnectioncosts = nodecostsfunc; + pathfindsetup.getheuristic = heuristicfunc; + pathfindsetup.gettraversable = traversablefunc; + pathfindsetup.getfinished = finishedfunc; pathfound = K_PathfindAStar(returnpath, &pathfindsetup); @@ -1183,18 +1330,19 @@ waypoint_t *K_GetNextWaypointToDestination( getnodeconnectioncostsfunc nodecostsfunc = K_WaypointPathfindGetNextCosts; getnodeheuristicfunc heuristicfunc = K_WaypointPathfindGetHeuristic; getnodetraversablefunc traversablefunc = K_WaypointPathfindTraversableNoShortcuts; + getpathfindfinishedfunc finishedfunc = K_WaypointPathfindReachedEnd; if (huntbackwards) { nextnodesfunc = K_WaypointPathfindGetPrev; nodecostsfunc = K_WaypointPathfindGetPrevCosts; } + if (useshortcuts) { traversablefunc = K_WaypointPathfindTraversableAllEnabled; } - pathfindsetup.opensetcapacity = K_GetOpensetBaseSize(); pathfindsetup.closedsetcapacity = K_GetClosedsetBaseSize(); pathfindsetup.nodesarraycapacity = K_GetNodesArrayBaseSize(); @@ -1204,6 +1352,7 @@ waypoint_t *K_GetNextWaypointToDestination( pathfindsetup.getconnectioncosts = nodecostsfunc; pathfindsetup.getheuristic = heuristicfunc; pathfindsetup.gettraversable = traversablefunc; + pathfindsetup.getfinished = finishedfunc; pathfindsuccess = K_PathfindAStar(&pathtowaypoint, &pathfindsetup); diff --git a/src/k_waypoint.h b/src/k_waypoint.h index f49e162e8..a2201ff26 100644 --- a/src/k_waypoint.h +++ b/src/k_waypoint.h @@ -214,6 +214,37 @@ boolean K_PathfindToWaypoint( const boolean huntbackwards); +/*-------------------------------------------------- + boolean K_PathfindThruCircuit( + waypoint_t *const sourcewaypoint, + const UINT32 traveldistance, + path_t *const returnpath, + const boolean useshortcuts, + const boolean huntbackwards) + + Tries a pathfind to the finish line waypoint, similar to K_PathfindToWaypoint, but it will continue + until it reaches the specified distance. The final path returned will only have the waypoints up to the + specified distance. + + Input Arguments:- + sourcewaypoint - The waypoint to start searching from + traveldistance - How far along the circuit it will try to pathfind. + returnpath - The path_t that will contain the final found path + useshortcuts - Whether to use waypoints that are marked as being shortcuts in the search + huntbackwards - Goes through the waypoints backwards if true + + Return:- + True if a circuit path could be constructed, false if it couldn't. +--------------------------------------------------*/ + +boolean K_PathfindThruCircuit( + waypoint_t *const sourcewaypoint, + const UINT32 traveldistance, + path_t *const returnpath, + const boolean useshortcuts, + const boolean huntbackwards); + + /*-------------------------------------------------- waypoint_t *K_GetNextWaypointToDestination( waypoint_t *const sourcewaypoint, From 0c5b42a07e2456e7902785d07cbabb722dce18c3 Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Sat, 10 Sep 2022 03:59:04 -0400 Subject: [PATCH 071/114] Stop lag when bots respawn They try to predict their direction while they respawn, which makes them go a bit nuts when air time compensation kicks in. Cap it so that this can't happen. --- src/k_bot.c | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/src/k_bot.c b/src/k_bot.c index a2c9f0a11..360bc006d 100644 --- a/src/k_bot.c +++ b/src/k_bot.c @@ -298,7 +298,7 @@ boolean K_BotCanTakeCut(player_t *player) /*-------------------------------------------------- static fixed_t K_BotSpeedScaled(player_t *player, fixed_t speed) - Gets the bot's speed value, adjusted for predictions. + What the bot "thinks" their speed is, for predictions. Mainly to make bots brake earlier when on friction sectors. Input Arguments:- @@ -312,6 +312,12 @@ static fixed_t K_BotSpeedScaled(player_t *player, fixed_t speed) { fixed_t result = speed; + if (P_IsObjectOnGround(player->mo) == false) + { + // You have no air control, so don't predict too far ahead. + return 0; + } + if (player->mo->movefactor != FRACUNIT) { fixed_t moveFactor = player->mo->movefactor; @@ -650,7 +656,8 @@ static botprediction_t *K_CreateBotPrediction(player_t *player) const fixed_t speed = K_BotSpeedScaled(player, P_AproxDistance(player->mo->momx, player->mo->momy)); const INT32 startDist = (DEFAULT_WAYPOINT_RADIUS * 2 * mapobjectscale) / FRACUNIT; - const INT32 distance = ((speed / FRACUNIT) * futuresight) + startDist; + const INT32 maxDist = startDist * 4; // This function gets very laggy when it goes far distances, and going too far isn't very helpful anyway. + const INT32 distance = min(((speed / FRACUNIT) * futuresight) + startDist, maxDist); // Halves radius when encountering a wall on your way to your destination. fixed_t radreduce = FRACUNIT; From cf9bfcae028b7a7d20d9c6c6708d4b4ae59a613e Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Sat, 10 Sep 2022 21:44:40 -0400 Subject: [PATCH 072/114] Start on new Shrink Currently just spawns an object that goes along the waypoint path --- src/deh_tables.c | 2 + src/info.c | 27 ++++ src/info.h | 2 + src/k_kart.c | 36 +---- src/k_objects.h | 4 + src/k_waypoint.c | 1 + src/objects/Sourcefile | 1 + src/objects/shrink.c | 347 +++++++++++++++++++++++++++++++++++++++++ src/p_mobj.c | 5 + 9 files changed, 390 insertions(+), 35 deletions(-) create mode 100644 src/objects/shrink.c diff --git a/src/deh_tables.c b/src/deh_tables.c index d58550367..dd97d1825 100644 --- a/src/deh_tables.c +++ b/src/deh_tables.c @@ -5336,6 +5336,8 @@ const char *const MOBJTYPE_LIST[] = { // array length left dynamic for sanity t "MT_HYUDORO", "MT_HYUDORO_CENTER", + "MT_SHRINK_POHBEE", + "MT_SINK", // Kitchen Sink Stuff "MT_SINK_SHIELD", "MT_SINKTRAIL", diff --git a/src/info.c b/src/info.c index 72665927e..f7135472a 100644 --- a/src/info.c +++ b/src/info.c @@ -24009,6 +24009,33 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] = S_NULL // raisestate }, + { // MT_SHRINK_POHBEE + -1, // doomednum + S_HYUDORO, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 32*FRACUNIT, // radius + 24*FRACUNIT, // height + 0, // display offset + 0, // mass + 0, // damage + sfx_None, // activesound + MF_NOCLIP|MF_NOCLIPTHING|MF_NOCLIPHEIGHT|MF_NOGRAVITY|MF_DONTENCOREMAP, // flags + S_NULL // raisestate + }, + { // MT_SINK -1, // doomednum S_SINK, // spawnstate diff --git a/src/info.h b/src/info.h index ab1867973..5d96a4317 100644 --- a/src/info.h +++ b/src/info.h @@ -6362,6 +6362,8 @@ typedef enum mobj_type MT_HYUDORO, MT_HYUDORO_CENTER, + MT_SHRINK_POHBEE, + MT_SINK, // Kitchen Sink Stuff MT_SINK_SHIELD, MT_SINKTRAIL, diff --git a/src/k_kart.c b/src/k_kart.c index ab8f04da9..08d3fc1a9 100644 --- a/src/k_kart.c +++ b/src/k_kart.c @@ -5581,46 +5581,12 @@ void K_DoSneaker(player_t *player, INT32 type) static void K_DoShrink(player_t *user) { - INT32 i; mobj_t *mobj, *next; S_StartSound(user->mo, sfx_kc46); // Sound the BANG! user->pflags |= PF_ATTACKDOWN; - for (i = 0; i < MAXPLAYERS; i++) - { - if (!playeringame[i] || players[i].spectator || !players[i].mo) - continue; - if (&players[i] == user) - continue; - if (players[i].position < user->position) - { - //P_FlashPal(&players[i], PAL_NUKE, 10); - - // Grow should get taken away. - if (players[i].growshrinktimer > 0) - K_RemoveGrowShrink(&players[i]); - else - { - // Start shrinking! - K_DropItems(&players[i]); - players[i].growshrinktimer = -(15*TICRATE); - - if (players[i].mo && !P_MobjWasRemoved(players[i].mo)) - { - players[i].mo->scalespeed = mapobjectscale/TICRATE; - players[i].mo->destscale = FixedMul(mapobjectscale, SHRINK_SCALE); - - if (K_PlayerShrinkCheat(&players[i]) == true) - { - players[i].mo->destscale = FixedMul(players[i].mo->destscale, SHRINK_SCALE); - } - - S_StartSound(players[i].mo, sfx_kc59); - } - } - } - } + Obj_CreateShrinkPohbees(user); // kill everything in the kitem list while we're at it: for (mobj = kitemcap; mobj; mobj = next) diff --git a/src/k_objects.h b/src/k_objects.h index 45f9df78c..d0b6f6b8b 100644 --- a/src/k_objects.h +++ b/src/k_objects.h @@ -8,4 +8,8 @@ void Obj_HyudoroThink(mobj_t *actor); void Obj_HyudoroCenterThink(mobj_t *actor); void Obj_HyudoroCollide(mobj_t *special, mobj_t *toucher); +/* Shrink */ +void Obj_PohbeeThinker(mobj_t *pohbee); +void Obj_CreateShrinkPohbees(player_t *owner); + #endif/*k_objects_H*/ diff --git a/src/k_waypoint.c b/src/k_waypoint.c index a6a592a6b..0a8dfd0f0 100644 --- a/src/k_waypoint.c +++ b/src/k_waypoint.c @@ -2044,6 +2044,7 @@ boolean K_SetupWaypointList(void) // Loop through the waypointcap here so that all waypoints are added to the heap, and allow easier debugging for (waypointmobj = waypointcap; waypointmobj; waypointmobj = waypointmobj->tracer) { + waypointmobj->cusval = (INT32)numwaypoints; K_SetupWaypoint(waypointmobj); } diff --git a/src/objects/Sourcefile b/src/objects/Sourcefile index f7e4f2491..94f7dd25b 100644 --- a/src/objects/Sourcefile +++ b/src/objects/Sourcefile @@ -1 +1,2 @@ hyudoro.c +shrink.c diff --git a/src/objects/shrink.c b/src/objects/shrink.c new file mode 100644 index 000000000..b6e41111b --- /dev/null +++ b/src/objects/shrink.c @@ -0,0 +1,347 @@ +// DR. ROBOTNIK'S RING RACERS +//----------------------------------------------------------------------------- +// Copyright (C) 2022 by Sally "TehRealSalt" Cochenour +// Copyright (C) 2022 by Kart Krew +// +// This program is free software distributed under the +// terms of the GNU General Public License, version 2. +// See the 'LICENSE' file for more details. +//----------------------------------------------------------------------------- +/// \file shrink.c +/// \brief Shrink laser item code. + +#include "../doomdef.h" +#include "../doomstat.h" +#include "../info.h" +#include "../k_kart.h" +#include "../k_objects.h" +#include "../m_random.h" +#include "../p_local.h" +#include "../r_main.h" +#include "../s_sound.h" +#include "../g_game.h" +#include "../z_zone.h" +#include "../k_waypoint.h" + +#define POHBEE_HOVER (300 << FRACBITS) +#define POHBEE_SPEED (128 << FRACBITS) +#define POHBEE_TIME (15 * TICRATE) +#define POHBEE_DIST (2048 << FRACBITS) +#define POHBEE_CHAIN (16) + +#define LASER_SPEED (20 << FRACBITS) + +enum +{ + POHBEE_MODE_SPAWN, + POHBEE_MODE_ACT, + POHBEE_MODE_DESPAWN, +}; + +#define pohbee_mode(o) ((o)->cusval) + +#define pohbee_waypoint_cur(o) ((o)->extravalue1) +#define pohbee_waypoint_dest(o) ((o)->extravalue2) + +#define pohbee_owner(o) ((o)->target) +#define pohbee_laser(o) ((o)->tracer) +#define pohbee_chain(o) ((o)->hnext) + +#define laser_owner(o) ((o)->target) +#define laser_pohbee(o) ((o)->tracer) + +static void PohbeeMoveTo(mobj_t *pohbee, fixed_t destx, fixed_t desty, fixed_t destz) +{ + pohbee->momx = destx - pohbee->x; + pohbee->momy = desty - pohbee->y; + pohbee->momz = destz - pohbee->z; +} + +static fixed_t GenericDistance( + fixed_t curx, fixed_t cury, fixed_t curz, + fixed_t destx, fixed_t desty, fixed_t destz) +{ + return P_AproxDistance(P_AproxDistance(destx - curx, desty - cury), destz - curz); +} + +static fixed_t PohbeeWaypointZ(mobj_t *dest) +{ + return dest->z + (FixedMul(POHBEE_HOVER, mapobjectscale) * P_MobjFlip(dest)); +} + +static void PohbeeSpawn(mobj_t *pohbee) +{ + waypoint_t *curWaypoint = NULL; + waypoint_t *destWaypoint = NULL; + + fixed_t distLeft = INT32_MAX; + fixed_t newX = pohbee->x; + fixed_t newY = pohbee->y; + fixed_t newZ = pohbee->z; + + boolean finalize = false; + + const boolean useshortcuts = false; + const boolean huntbackwards = false; + boolean pathfindsuccess = false; + path_t pathtofinish = {0}; + size_t pathIndex = 0; + + curWaypoint = K_GetWaypointFromIndex((size_t)pohbee_waypoint_cur(pohbee)); + destWaypoint = K_GetWaypointFromIndex((size_t)pohbee_waypoint_dest(pohbee)); + + if (curWaypoint == NULL || destWaypoint == NULL) + { + // Waypoints aren't valid. + // Just transition into the next state. + pohbee_mode(pohbee) = POHBEE_MODE_ACT; + return; + } + + distLeft = FixedMul(POHBEE_SPEED, mapobjectscale); + + while (distLeft > 0) + { + fixed_t wpX = curWaypoint->mobj->x; + fixed_t wpY = curWaypoint->mobj->y; + fixed_t wpZ = PohbeeWaypointZ(curWaypoint->mobj); + + fixed_t distToNext = GenericDistance( + newX, newY, newZ, + wpX, wpY, wpZ + ); + + if (distToNext > distLeft) + { + // Only made it partially there. + newX += FixedMul(FixedDiv(wpX - newX, distToNext), distLeft); + newY += FixedMul(FixedDiv(wpY - newY, distToNext), distLeft); + newZ += FixedMul(FixedDiv(wpZ - newZ, distToNext), distLeft); + + distLeft = 0; + } + else + { + // Close enough to the next waypoint, + // move there and remove the distance. + newX = wpX; + newY = wpY; + newZ = wpZ; + + distLeft -= distToNext; + + if (curWaypoint == destWaypoint) + { + // Reached the end. + finalize = true; + break; + } + + // Create waypoint path to our destination. + // Crazy over-engineered, just to catch when + // waypoints are insanely close to each other :P + if (pathfindsuccess == false) + { + pathfindsuccess = K_PathfindToWaypoint( + curWaypoint, destWaypoint, + &pathtofinish, + useshortcuts, huntbackwards + ); + + if (pathfindsuccess == false) + { + // Path isn't valid. + // Just transition into the next state. + finalize = true; + break; + } + } + + pathIndex++; + + if (pathIndex >= pathtofinish.numnodes) + { + // Successfully reached the end of the path. + finalize = true; + break; + } + + // Now moving to the next waypoint. + curWaypoint = (waypoint_t *)pathtofinish.array[pathIndex].nodedata; + pohbee_waypoint_cur(pohbee) = (INT32)K_GetWaypointHeapIndex(curWaypoint); + } + } + + PohbeeMoveTo(pohbee, newX, newY, newZ); + + if (finalize == true) + { + pohbee_mode(pohbee) = POHBEE_MODE_ACT; + } + + if (pathfindsuccess == true) + { + Z_Free(pathtofinish.array); + } +} + +void Obj_PohbeeThinker(mobj_t *pohbee) +{ + pohbee->momx = 0; + pohbee->momy = 0; + pohbee->momz = 0; + + switch (pohbee_mode(pohbee)) + { + case POHBEE_MODE_SPAWN: + PohbeeSpawn(pohbee); + break; + + case POHBEE_MODE_ACT: + //PohbeeSpawn(pohbee); + break; + + case POHBEE_MODE_DESPAWN: + //PohbeeSpawn(pohbee); + break; + + default: + // failsafe + pohbee_mode(pohbee) = POHBEE_MODE_SPAWN; + break; + } +} + +static void CreatePohbeeForPlayer(player_t *target, player_t *owner) +{ + const UINT32 traveldist = FixedMul(POHBEE_DIST, mapobjectscale) / FRACUNIT; + + mobj_t *pohbee = NULL; + //mobj_t *laser = NULL; + + waypoint_t *startWaypoint = NULL; + waypoint_t *endWaypoint = NULL; + + I_Assert(target != NULL); + I_Assert(owner != NULL); + + if (target->mo == NULL || P_MobjWasRemoved(target->mo) == true) + { + // No object for the target. + return; + } + + // First, go backwards to find our starting point. + { + const boolean useshortcuts = false; + const boolean huntbackwards = true; + boolean pathfindsuccess = false; + path_t pathtofinish = {0}; + + pathfindsuccess = K_PathfindThruCircuit( + target->nextwaypoint, traveldist, + &pathtofinish, + useshortcuts, huntbackwards + ); + + if (pathfindsuccess == true) + { + startWaypoint = (waypoint_t *)pathtofinish.array[ pathtofinish.numnodes - 1 ].nodedata; + Z_Free(pathtofinish.array); + } + else + { + startWaypoint = target->nextwaypoint; + } + } + + // Now, go forwards to get our ending point. + { + const boolean useshortcuts = false; + const boolean huntbackwards = false; + boolean pathfindsuccess = false; + path_t pathtofinish = {0}; + + pathfindsuccess = K_PathfindThruCircuit( + target->nextwaypoint, traveldist * 2, + &pathtofinish, + useshortcuts, huntbackwards + ); + + if (pathfindsuccess == true) + { + endWaypoint = (waypoint_t *)pathtofinish.array[ pathtofinish.numnodes - 1 ].nodedata; + Z_Free(pathtofinish.array); + } + else + { + endWaypoint = target->nextwaypoint; + } + } + + // Try to repair an incomplete pair, just in case. + if (startWaypoint == NULL && endWaypoint != NULL) + { + startWaypoint = endWaypoint; + } + + if (endWaypoint == NULL && startWaypoint != NULL) + { + endWaypoint = startWaypoint; + } + + if (startWaypoint == NULL || endWaypoint == NULL) + { + // Unable to create shrink lasers + // due to invalid waypoint structure... + return; + } + + // Valid spawning conditions, + // we can start creating each individual part. + pohbee = P_SpawnMobjFromMobj(startWaypoint->mobj, 0, 0, POHBEE_HOVER, MT_SHRINK_POHBEE); + + P_SetTarget(&pohbee_owner(pohbee), owner->mo); + + pohbee_mode(pohbee) = POHBEE_MODE_SPAWN; + + pohbee_waypoint_cur(pohbee) = (INT32)K_GetWaypointHeapIndex(startWaypoint); + pohbee_waypoint_dest(pohbee) = (INT32)K_GetWaypointHeapIndex(endWaypoint); +} + +void Obj_CreateShrinkPohbees(player_t *owner) +{ + UINT8 ownerPos = 1; + UINT8 i; + + I_Assert(owner != NULL); + + ownerPos = owner->position; + + for (i = 0; i < MAXPLAYERS; i++) + { + player_t *player = NULL; + + if (playeringame[i] == false) + { + // Not valid. + continue; + } + + player = &players[i]; + + if (player->spectator == true) + { + // Not playing. + continue; + } + + if (player->position > ownerPos) + { + // Too far behind. + continue; + } + + CreatePohbeeForPlayer(player, owner); + } +} diff --git a/src/p_mobj.c b/src/p_mobj.c index 621da9d9f..f2aebb6d7 100644 --- a/src/p_mobj.c +++ b/src/p_mobj.c @@ -7841,6 +7841,11 @@ static boolean P_MobjRegularThink(mobj_t *mobj) Obj_HyudoroCenterThink(mobj); break; } + case MT_SHRINK_POHBEE: + { + Obj_PohbeeThinker(mobj); + break; + } case MT_ROCKETSNEAKER: if (!mobj->target || !mobj->target->health) { From a74506690fa9e9a1218e3a60b87007f287418c27 Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Sun, 11 Sep 2022 02:15:44 -0400 Subject: [PATCH 073/114] More shrink foundation - Keeps track of ties. If multiple Poh-Bees would go to the same waypoint, then it will combine them into a single one, with multiple lasers attached to it. - Added the laser shooters. Still purely visual. - Added despawn behavior. --- src/deh_tables.c | 1 + src/info.c | 27 ++++ src/info.h | 1 + src/objects/shrink.c | 356 ++++++++++++++++++++++++++++++++----------- 4 files changed, 295 insertions(+), 90 deletions(-) diff --git a/src/deh_tables.c b/src/deh_tables.c index dd97d1825..9e7718333 100644 --- a/src/deh_tables.c +++ b/src/deh_tables.c @@ -5337,6 +5337,7 @@ const char *const MOBJTYPE_LIST[] = { // array length left dynamic for sanity t "MT_HYUDORO_CENTER", "MT_SHRINK_POHBEE", + "MT_SHRINK_LASER", "MT_SINK", // Kitchen Sink Stuff "MT_SINK_SHIELD", diff --git a/src/info.c b/src/info.c index f7135472a..e1ea01a05 100644 --- a/src/info.c +++ b/src/info.c @@ -24036,6 +24036,33 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] = S_NULL // raisestate }, + { // MT_SHRINK_LASER + -1, // doomednum + S_HYUDORO, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 32*FRACUNIT, // radius + 24*FRACUNIT, // height + 0, // display offset + 0, // mass + 0, // damage + sfx_None, // activesound + MF_NOCLIP|MF_NOCLIPTHING|MF_NOCLIPHEIGHT|MF_NOGRAVITY|MF_DONTENCOREMAP, // flags + S_NULL // raisestate + }, + { // MT_SINK -1, // doomednum S_SINK, // spawnstate diff --git a/src/info.h b/src/info.h index 5d96a4317..668485d69 100644 --- a/src/info.h +++ b/src/info.h @@ -6363,6 +6363,7 @@ typedef enum mobj_type MT_HYUDORO_CENTER, MT_SHRINK_POHBEE, + MT_SHRINK_LASER, MT_SINK, // Kitchen Sink Stuff MT_SINK_SHIELD, diff --git a/src/objects/shrink.c b/src/objects/shrink.c index b6e41111b..bde2fdb35 100644 --- a/src/objects/shrink.c +++ b/src/objects/shrink.c @@ -23,13 +23,15 @@ #include "../z_zone.h" #include "../k_waypoint.h" -#define POHBEE_HOVER (300 << FRACBITS) +#define POHBEE_HOVER (384 << FRACBITS) #define POHBEE_SPEED (128 << FRACBITS) #define POHBEE_TIME (15 * TICRATE) -#define POHBEE_DIST (2048 << FRACBITS) -#define POHBEE_CHAIN (16) +#define POHBEE_DIST (4096 << FRACBITS) #define LASER_SPEED (20 << FRACBITS) +#define LASER_SWINGTIME (3 * TICRATE) + +#define CHAIN_SIZE (16) enum { @@ -39,16 +41,20 @@ enum }; #define pohbee_mode(o) ((o)->cusval) - +#define pohbee_timer(o) ((o)->reactiontime) #define pohbee_waypoint_cur(o) ((o)->extravalue1) #define pohbee_waypoint_dest(o) ((o)->extravalue2) #define pohbee_owner(o) ((o)->target) -#define pohbee_laser(o) ((o)->tracer) -#define pohbee_chain(o) ((o)->hnext) +#define pohbee_lasers(o) ((o)->hnext) -#define laser_owner(o) ((o)->target) -#define laser_pohbee(o) ((o)->tracer) +#define laser_offset(o) ((o)->movecount) +#define laser_swing(o) ((o)->movedir) +#define laser_numsegs(o) ((o)->extravalue1) + +#define laser_pohbee(o) ((o)->target) +#define laser_collider(o) ((o)->tracer) +#define laser_chains(o) ((o)->hprev) static void PohbeeMoveTo(mobj_t *pohbee, fixed_t destx, fixed_t desty, fixed_t destz) { @@ -173,9 +179,11 @@ static void PohbeeSpawn(mobj_t *pohbee) } PohbeeMoveTo(pohbee, newX, newY, newZ); + pohbee->angle = K_MomentumAngle(pohbee); if (finalize == true) { + // Move to next state pohbee_mode(pohbee) = POHBEE_MODE_ACT; } @@ -185,11 +193,77 @@ static void PohbeeSpawn(mobj_t *pohbee) } } +static void PohbeeAct(mobj_t *pohbee) +{ + pohbee_timer(pohbee)--; + + if (pohbee_timer(pohbee) <= 0) + { + // Move to next state + pohbee_mode(pohbee) = POHBEE_MODE_DESPAWN; + pohbee->fuse = 5*TICRATE; + } +} + +static void PohbeeDespawn(mobj_t *pohbee) +{ + pohbee->momz = 16 * pohbee->scale * P_MobjFlip(pohbee); +} + +static void DoLaserSwing(mobj_t *laser, mobj_t *pohbee) +{ + const angle_t angle = laser->angle + ANGLE_90; + const tic_t swingTimer = leveltime + laser_offset(laser); + + const angle_t swingAmt = swingTimer * (ANGLE_MAX / LASER_SWINGTIME); + const fixed_t swingCos = FINECOSINE(swingAmt >> ANGLETOFINESHIFT); + const fixed_t swing = FixedMul(laser_swing(laser), 9 * swingCos); + + const angle_t pitch = -ANGLE_90 + swing; + const fixed_t dist = laser_numsegs(laser) * CHAIN_SIZE * laser->scale; + + fixed_t offsetX = FixedMul( + dist, FixedMul( + FINECOSINE(angle >> ANGLETOFINESHIFT), + FINECOSINE(pitch >> ANGLETOFINESHIFT) + ) + ); + + fixed_t offsetY = FixedMul( + dist, FixedMul( + FINESINE(angle >> ANGLETOFINESHIFT), + FINECOSINE(pitch >> ANGLETOFINESHIFT) + ) + ); + + fixed_t offsetZ = FixedMul( + dist, FINESINE(pitch >> ANGLETOFINESHIFT) + ); + + PohbeeMoveTo(laser, pohbee->x + offsetX, pohbee->y + offsetY, pohbee->z + offsetZ); +} + +static void ShrinkLaserThinker(mobj_t *laser) +{ + mobj_t *pohbee = laser_pohbee(laser); + + if (pohbee == NULL || P_MobjWasRemoved(pohbee) == true) + { + P_RemoveMobj(laser); + return; + } + + laser->angle = pohbee->angle; + DoLaserSwing(laser, pohbee); + + //PohbeeMoveTo(laser_collider(laser), laser->x, laser->y, laser->z); +} + void Obj_PohbeeThinker(mobj_t *pohbee) { - pohbee->momx = 0; - pohbee->momy = 0; - pohbee->momz = 0; + mobj_t *laser = NULL; + + pohbee->momx = pohbee->momy = pohbee->momz = 0; switch (pohbee_mode(pohbee)) { @@ -198,11 +272,11 @@ void Obj_PohbeeThinker(mobj_t *pohbee) break; case POHBEE_MODE_ACT: - //PohbeeSpawn(pohbee); + PohbeeAct(pohbee); break; case POHBEE_MODE_DESPAWN: - //PohbeeSpawn(pohbee); + PohbeeDespawn(pohbee); break; default: @@ -210,117 +284,188 @@ void Obj_PohbeeThinker(mobj_t *pohbee) pohbee_mode(pohbee) = POHBEE_MODE_SPAWN; break; } + + laser = pohbee_lasers(pohbee); + while (laser != NULL && P_MobjWasRemoved(laser) == false) + { + ShrinkLaserThinker(laser); + laser = pohbee_lasers(laser); + } } -static void CreatePohbeeForPlayer(player_t *target, player_t *owner) +/* +void Obj_PohbeeRemoved(mobj_t *pohbee) +{ + mobj_t *chain = NULL; + + if (pohbee_laser(pohbee) != NULL) + { + P_RemoveMobj(pohbee_laser(pohbee)); + } + + chain = pohbee_chain(pohbee); + while (chain != NULL) + { + mobj_t *temp = chain; + chain = pohbee_chain(temp); + P_RemoveMobj(temp); + } +} +*/ + +static waypoint_t *GetPohbeeStart(waypoint_t *anchor) +{ + const UINT32 traveldist = FixedMul(POHBEE_DIST >> 1, mapobjectscale) / FRACUNIT; + const boolean useshortcuts = false; + const boolean huntbackwards = true; + boolean pathfindsuccess = false; + path_t pathtofinish = {0}; + waypoint_t *ret = NULL; + + pathfindsuccess = K_PathfindThruCircuit( + anchor, traveldist, + &pathtofinish, + useshortcuts, huntbackwards + ); + + if (pathfindsuccess == true) + { + ret = (waypoint_t *)pathtofinish.array[ pathtofinish.numnodes - 1 ].nodedata; + Z_Free(pathtofinish.array); + } + else + { + ret = anchor; + } + + return ret; +} + +static waypoint_t *GetPohbeeEnd(waypoint_t *anchor) { const UINT32 traveldist = FixedMul(POHBEE_DIST, mapobjectscale) / FRACUNIT; + const boolean useshortcuts = false; + const boolean huntbackwards = false; + boolean pathfindsuccess = false; + path_t pathtofinish = {0}; + waypoint_t *ret = NULL; + pathfindsuccess = K_PathfindThruCircuit( + anchor, traveldist, + &pathtofinish, + useshortcuts, huntbackwards + ); + + if (pathfindsuccess == true) + { + ret = (waypoint_t *)pathtofinish.array[ pathtofinish.numnodes - 1 ].nodedata; + Z_Free(pathtofinish.array); + } + else + { + ret = anchor; + } + + return ret; +} + +static void CreatePohbee(player_t *owner, waypoint_t *start, waypoint_t *end, UINT8 numLasers) +{ mobj_t *pohbee = NULL; - //mobj_t *laser = NULL; - waypoint_t *startWaypoint = NULL; - waypoint_t *endWaypoint = NULL; + fixed_t size = INT32_MAX; + INT32 baseSegs = INT32_MAX; + INT32 segVal = INT32_MAX; + mobj_t *prevLaser = NULL; - I_Assert(target != NULL); - I_Assert(owner != NULL); + size_t i, j; - if (target->mo == NULL || P_MobjWasRemoved(target->mo) == true) + if (owner == NULL || owner->mo == NULL || P_MobjWasRemoved(owner->mo) == true + || start == NULL || end == NULL + || numLasers == 0) { - // No object for the target. + // Invalid inputs return; } - // First, go backwards to find our starting point. + // Calculate number of chain segments added per laser. + size = end->mobj->radius / mapobjectscale; + baseSegs = 1 + (size / CHAIN_SIZE); + + if (baseSegs < MAXPLAYERS) { - const boolean useshortcuts = false; - const boolean huntbackwards = true; - boolean pathfindsuccess = false; - path_t pathtofinish = {0}; - - pathfindsuccess = K_PathfindThruCircuit( - target->nextwaypoint, traveldist, - &pathtofinish, - useshortcuts, huntbackwards - ); - - if (pathfindsuccess == true) - { - startWaypoint = (waypoint_t *)pathtofinish.array[ pathtofinish.numnodes - 1 ].nodedata; - Z_Free(pathtofinish.array); - } - else - { - startWaypoint = target->nextwaypoint; - } + baseSegs = MAXPLAYERS; } - // Now, go forwards to get our ending point. - { - const boolean useshortcuts = false; - const boolean huntbackwards = false; - boolean pathfindsuccess = false; - path_t pathtofinish = {0}; - - pathfindsuccess = K_PathfindThruCircuit( - target->nextwaypoint, traveldist * 2, - &pathtofinish, - useshortcuts, huntbackwards - ); - - if (pathfindsuccess == true) - { - endWaypoint = (waypoint_t *)pathtofinish.array[ pathtofinish.numnodes - 1 ].nodedata; - Z_Free(pathtofinish.array); - } - else - { - endWaypoint = target->nextwaypoint; - } - } - - // Try to repair an incomplete pair, just in case. - if (startWaypoint == NULL && endWaypoint != NULL) - { - startWaypoint = endWaypoint; - } - - if (endWaypoint == NULL && startWaypoint != NULL) - { - endWaypoint = startWaypoint; - } - - if (startWaypoint == NULL || endWaypoint == NULL) - { - // Unable to create shrink lasers - // due to invalid waypoint structure... - return; - } + segVal = baseSegs / numLasers; // Valid spawning conditions, // we can start creating each individual part. - pohbee = P_SpawnMobjFromMobj(startWaypoint->mobj, 0, 0, POHBEE_HOVER, MT_SHRINK_POHBEE); - + pohbee = P_SpawnMobjFromMobj(start->mobj, 0, 0, POHBEE_HOVER * 3, MT_SHRINK_POHBEE); P_SetTarget(&pohbee_owner(pohbee), owner->mo); pohbee_mode(pohbee) = POHBEE_MODE_SPAWN; + pohbee_timer(pohbee) = POHBEE_TIME; - pohbee_waypoint_cur(pohbee) = (INT32)K_GetWaypointHeapIndex(startWaypoint); - pohbee_waypoint_dest(pohbee) = (INT32)K_GetWaypointHeapIndex(endWaypoint); + pohbee_waypoint_cur(pohbee) = (INT32)K_GetWaypointHeapIndex(start); + pohbee_waypoint_dest(pohbee) = (INT32)K_GetWaypointHeapIndex(end); + + prevLaser = pohbee; + + for (i = 0; i < numLasers; i++) + { + const UINT8 numSegs = segVal * (i + 1); + + mobj_t *laser = P_SpawnMobjFromMobj(pohbee, 0, 0, 0, MT_SHRINK_LASER); + //mobj_t *collider = NULL; + //mobj_t *prevChain = NULL; + + P_SetTarget(&laser_pohbee(laser), pohbee); + P_SetTarget(&pohbee_lasers(prevLaser), laser); + + laser_numsegs(laser) = numSegs; + laser_swing(laser) = (ANGLE_45 * baseSeg) / numSegs; + laser_offset(laser) = P_RandomKey(LASER_SWINGTIME); + + /* + prevChain = laser; + for (j = 0; j < numSegs; j++) + { + mobj_t *chain = P_SpawnMobjFromMobj(laser, 0, 0, 0, MT_SHRINK_LASER); + P_SetTarget(&laser_chains(prevChain), chain); + prevChain = chain; + } + */ + (void)j; + + prevLaser = laser; + } } void Obj_CreateShrinkPohbees(player_t *owner) { UINT8 ownerPos = 1; - UINT8 i; - I_Assert(owner != NULL); + struct { + waypoint_t *start; + waypoint_t *end; + UINT8 lasers; + } pohbees[MAXPLAYERS]; + size_t numPohbees = 0; + + size_t i, j; + + if (owner == NULL || owner->mo == NULL || P_MobjWasRemoved(owner->mo) == true) + { + return; + } ownerPos = owner->position; for (i = 0; i < MAXPLAYERS; i++) { player_t *player = NULL; + waypoint_t *endWaypoint = NULL; if (playeringame[i] == false) { @@ -342,6 +487,37 @@ void Obj_CreateShrinkPohbees(player_t *owner) continue; } - CreatePohbeeForPlayer(player, owner); + if (player->nextwaypoint == NULL) + { + // No waypoint? + continue; + } + + endWaypoint = GetPohbeeEnd(player->nextwaypoint); + + for (j = 0; j < numPohbees; j++) + { + if (pohbees[j].end == endWaypoint) + { + // Increment laser count for the already existing poh-bee, + // if another one would occupy the same space. + pohbees[j].lasers++; + break; + } + } + + if (j == numPohbees) + { + // Push a new poh-bee + pohbees[j].start = GetPohbeeStart(player->nextwaypoint); + pohbees[j].end = endWaypoint; + pohbees[j].lasers = 4; + numPohbees++; + } + } + + for (i = 0; i < numPohbees; i++) + { + CreatePohbee(owner, pohbees[i].start, pohbees[i].end, pohbees[i].lasers); } } From 5a58b3ca3add9d9964e00a7bf9a84987e76b1767 Mon Sep 17 00:00:00 2001 From: James R Date: Sat, 10 Sep 2022 21:27:37 -0700 Subject: [PATCH 074/114] Refactor hitbox renderer to project vissprites Properly accounts for portals (skyboxes). --- src/r_bbox.c | 60 ++++++------------ src/r_things.c | 166 +++++++++++++++++++++++++++++++++++++++++++------ src/r_things.h | 4 +- 3 files changed, 169 insertions(+), 61 deletions(-) diff --git a/src/r_bbox.c b/src/r_bbox.c index ef72488fb..2812429a7 100644 --- a/src/r_bbox.c +++ b/src/r_bbox.c @@ -14,7 +14,6 @@ #include "doomdef.h" #include "command.h" -#include "r_fps.h" #include "r_local.h" #include "screen.h" // cv_renderhitbox #include "v_video.h" // V_DrawFill @@ -166,11 +165,11 @@ draw_bbox_row } static UINT8 -get_bbox_color (mobj_t *thing) +get_bbox_color (vissprite_t *vis) { - UINT32 flags = thing->flags; + UINT32 flags = vis->mobjflags; - if (thing->player) + if (vis->mobj->player) return 255; // 0FF if (flags & (MF_NOCLIPTHING)) @@ -188,39 +187,21 @@ get_bbox_color (mobj_t *thing) return 0; // FFF } -void R_DrawThingBoundingBox(mobj_t *thing) +void R_DrawThingBoundingBox(vissprite_t *vis) { - fixed_t rs, rc; // radius offsets - fixed_t gx, gy; // origin - fixed_t tx, ty; // translated coordinates + // radius offsets + fixed_t rs = vis->scale; + fixed_t rc = vis->xscale; - // uncapped/interpolation - interpmobjstate_t interp = {0}; + // translated coordinates + fixed_t tx = vis->gx; + fixed_t ty = vis->gy; - struct bbox_config bb = {0}; - - // do interpolation - if (R_UsingFrameInterpolation() && !paused) - { - R_InterpolateMobjState(thing, rendertimefrac, &interp); - } - else - { - R_InterpolateMobjState(thing, FRACUNIT, &interp); - } - - rs = FixedMul(thing->radius, viewsin); - rc = FixedMul(thing->radius, viewcos); - - gx = interp.x - viewx; - gy = interp.y - viewy; - - tx = FixedMul(gx, viewsin) - FixedMul(gy, viewcos); - ty = FixedMul(gx, viewcos) + FixedMul(gy, viewsin); - - bb.height = thing->height; - bb.tz = (interp.z + bb.height) - viewz; - bb.color = get_bbox_color(thing); + struct bbox_config bb = { + .height = vis->thingheight, + .tz = vis->texturemid, + .color = get_bbox_color(vis), + }; // 1--3 // | | @@ -228,18 +209,15 @@ void R_DrawThingBoundingBox(mobj_t *thing) // left - tx -= rs; - ty -= rc; - - draw_bbox_col(&bb, 0, tx + rc, ty - rs); // bottom + draw_bbox_col(&bb, 0, tx, ty); // bottom draw_bbox_col(&bb, 1, tx - rc, ty + rs); // top // right - tx += rs + rs; - ty += rc + rc; + tx += rs; + ty += rc; - draw_bbox_col(&bb, 2, tx + rc, ty - rs); // bottom + draw_bbox_col(&bb, 2, tx, ty); // bottom draw_bbox_col(&bb, 3, tx - rc, ty + rs); // top // connect all four columns diff --git a/src/r_things.c b/src/r_things.c index ca4e6ecf7..d7d2a12b5 100644 --- a/src/r_things.c +++ b/src/r_things.c @@ -1449,6 +1449,104 @@ static void R_ProjectDropShadow( objectsdrawn++; } +static void R_ProjectBoundingBox(mobj_t *thing, vissprite_t *vis) +{ + fixed_t gx, gy; + fixed_t tx, tz; + + vissprite_t *box; + + // uncapped/interpolation + interpmobjstate_t interp = {0}; + + if (!R_ThingBoundingBoxVisible(thing)) + { + return; + } + + // do interpolation + if (R_UsingFrameInterpolation() && !paused) + { + R_InterpolateMobjState(thing, rendertimefrac, &interp); + } + else + { + R_InterpolateMobjState(thing, FRACUNIT, &interp); + } + + // 1--3 + // | | + // 0--2 + + // start in the (0) corner + gx = interp.x - thing->radius - viewx; + gy = interp.y - thing->radius - viewy; + + tz = FixedMul(gx, viewcos) + FixedMul(gy, viewsin); + + // thing is behind view plane? + // if parent vis is visible, ignore this + if (!vis && (tz < FixedMul(MINZ, interp.scale))) + { + return; + } + + tx = FixedMul(gx, viewsin) - FixedMul(gy, viewcos); + + // too far off the side? + if (!vis && abs(tx) > FixedMul(tz, fovtan[viewssnum])<<2) + { + return; + } + + box = R_NewVisSprite(); + box->mobj = thing; + box->mobjflags = thing->flags; + box->thingheight = thing->height; + box->cut = SC_BBOX; + + box->gx = tx; + box->gy = tz; + + box->scale = 2 * FixedMul(thing->radius, viewsin); + box->xscale = 2 * FixedMul(thing->radius, viewcos); + + box->pz = interp.z; + box->pzt = box->pz + box->thingheight; + + box->gzt = box->pzt; + box->gz = box->pz; + box->texturemid = box->gzt - viewz; + + if (vis) + { + box->x1 = vis->x1; + box->x2 = vis->x2; + box->szt = vis->szt; + box->sz = vis->sz; + + box->sortscale = vis->sortscale; // link sorting to sprite + box->dispoffset = vis->dispoffset + 5; + + box->cut |= SC_LINKDRAW; + } + else + { + fixed_t xscale = FixedDiv(projection[viewssnum], tz); + fixed_t yscale = FixedDiv(projectiony[viewssnum], tz); + fixed_t top = (centeryfrac - FixedMul(box->texturemid, yscale)); + + box->x1 = (centerxfrac + FixedMul(box->gx, xscale)) / FRACUNIT; + box->x2 = box->x1; + + box->szt = top / FRACUNIT; + box->sz = (top + FixedMul(box->thingheight, yscale)) / FRACUNIT; + + box->sortscale = yscale; + box->dispoffset = 0; + } +} + // // R_ProjectSprite // Generates a vissprite for a thing @@ -2195,6 +2293,8 @@ static void R_ProjectSprite(mobj_t *thing) R_ProjectDropShadow(oldthing, vis, oldthing->shadowscale, basetx, basetz); } + R_ProjectBoundingBox(oldthing, vis); + // Debug ++objectsdrawn; } @@ -2429,8 +2529,26 @@ void R_AddSprites(sector_t *sec, INT32 lightlevel) limit_dist = (fixed_t)(cv_drawdist.value) * mapobjectscale; for (thing = sec->thinglist; thing; thing = thing->snext) { - if (R_ThingVisibleWithinDist(thing, limit_dist)) - R_ProjectSprite(thing); + if (R_ThingWithinDist(thing, limit_dist)) + { + const INT32 oldobjectsdrawn = objectsdrawn; + + if (R_ThingVisible(thing)) + { + R_ProjectSprite(thing); + } + + // I'm so smart :^) + if (objectsdrawn == oldobjectsdrawn) + { + /* + Object is invisible OR is off screen but + render its bbox even if the latter because + radius could be bigger than sprite. + */ + R_ProjectBoundingBox(thing, NULL); + } + } } // no, no infinite draw distance for precipitation. this option at zero is supposed to turn it off @@ -2503,6 +2621,10 @@ static void R_SortVisSprites(vissprite_t* vsprsortedhead, UINT32 start, UINT32 e if (dsfirst->cut & SC_SHADOW) continue; + // don't connect to your bounding box! + if (dsfirst->cut & SC_BBOX) + continue; + // don't connect if it's not the tracer if (dsfirst->mobj != ds->mobj) continue; @@ -2946,18 +3068,12 @@ static void R_DrawSprite(vissprite_t *spr) mfloorclip = spr->clipbot; mceilingclip = spr->cliptop; - if (spr->cut & SC_SPLAT) + if (spr->cut & SC_BBOX) + R_DrawThingBoundingBox(spr); + else if (spr->cut & SC_SPLAT) R_DrawFloorSplat(spr); else R_DrawVisSprite(spr); - - if (R_ThingBoundingBoxVisible(spr->mobj)) - { - // fuck you fuck you fuck you FUCK YOU - // (shadows are linked to their mobj) - if (!(spr->cut & SC_SHADOW)) - R_DrawThingBoundingBox(spr->mobj); - } } // Special drawer for precipitation sprites Tails 08-18-2002 @@ -3253,6 +3369,12 @@ void R_ClipSprites(drawseg_t* dsstart, portal_t* portal) INT32 x1 = (spr->cut & SC_SPLAT) ? 0 : spr->x1; INT32 x2 = (spr->cut & SC_SPLAT) ? viewwidth : spr->x2; + if (spr->cut & SC_BBOX) + { + // Do not clip bounding boxes + continue; + } + if (x2 < cx) { drawsegs_xrange = drawsegs_xranges[1].items; @@ -3291,20 +3413,26 @@ boolean R_ThingVisible (mobj_t *thing) return true; } +boolean R_ThingWithinDist (mobj_t *thing, fixed_t limit_dist) +{ + const fixed_t dist = R_PointToDist(thing->x, thing->y); + + if (limit_dist && dist > limit_dist) + { + return false; + } + + return true; +} + +// For OpenGL, TODO: REMOVE!! boolean R_ThingVisibleWithinDist (mobj_t *thing, fixed_t limit_dist) { - fixed_t approx_dist; - if (! R_ThingVisible(thing)) return false; - approx_dist = P_AproxDistance(viewx-thing->x, viewy-thing->y); - - if (limit_dist && approx_dist > limit_dist) - return false; - - return true; + return R_ThingWithinDist(thing, limit_dist); } /* Check if precipitation may be drawn from our current view. */ diff --git a/src/r_things.h b/src/r_things.h index ef5ceedd0..085d15512 100644 --- a/src/r_things.h +++ b/src/r_things.h @@ -67,7 +67,6 @@ void R_InitSprites(void); void R_ClearSprites(void); boolean R_ThingBoundingBoxVisible(mobj_t *thing); -void R_DrawThingBoundingBox(mobj_t *thing); boolean R_ThingVisible (mobj_t *thing); @@ -140,6 +139,7 @@ typedef enum SC_SPLAT = 1<<11, // srb2kart SC_SEMIBRIGHT = 1<<12, + SC_BBOX = 1<<13, // masks SC_CUTMASK = SC_TOP|SC_BOTTOM, SC_FLAGMASK = ~SC_CUTMASK @@ -226,6 +226,8 @@ extern UINT32 visspritecount; void R_ClipSprites(drawseg_t* dsstart, portal_t* portal); void R_ClipVisSprite(vissprite_t *spr, INT32 x1, INT32 x2, portal_t* portal); +void R_DrawThingBoundingBox(vissprite_t *spr); + UINT8 *R_GetSpriteTranslation(vissprite_t *vis); // ---------- From 7094a064dada13691d3d9d0499dc928a6bb2fce9 Mon Sep 17 00:00:00 2001 From: James R Date: Sun, 11 Sep 2022 06:25:08 -0700 Subject: [PATCH 075/114] Do not render waypoint or spectator self hitbox Visual clutter and waypoint debugger exists. :) If you are a spectator, the hitbox exists right ontop of you and hitboxes don't render correctly if they are too close to the viewpoint. --- src/r_bbox.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/r_bbox.c b/src/r_bbox.c index 2812429a7..d5d2bf77c 100644 --- a/src/r_bbox.c +++ b/src/r_bbox.c @@ -252,6 +252,18 @@ boolean R_ThingBoundingBoxVisible(mobj_t *thing) { INT32 cvmode = cv_renderhitbox.value; + if (thing->type == MT_WAYPOINT) + { + // Waypoints debugger serves this purpose + return false; + } + + if (thing == r_viewmobj) + { + // Rendering bbox right on top causes anomalies + return false; + } + switch (cvmode) { case RENDERHITBOX_OFF: From 7719cf27fa154749e617aebeafa49b2698692bba Mon Sep 17 00:00:00 2001 From: James R Date: Sun, 11 Sep 2022 07:08:10 -0700 Subject: [PATCH 076/114] Remedy some quirky rendering of hitboxes if your viewpoint is too close It's not correct but it's better than before. --- src/r_bbox.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/r_bbox.c b/src/r_bbox.c index d5d2bf77c..969aeacff 100644 --- a/src/r_bbox.c +++ b/src/r_bbox.c @@ -82,8 +82,13 @@ draw_bbox_col { struct bbox_col *col = &bb->col[p]; - fixed_t xscale = FixedDiv(projection[viewssnum], ty); - fixed_t yscale = FixedDiv(projectiony[viewssnum], ty); + fixed_t xscale, yscale; + + if (ty < FRACUNIT) // projection breaks down here + ty = FRACUNIT; + + xscale = FixedDiv(projection[viewssnum], ty); + yscale = FixedDiv(projectiony[viewssnum], ty); col->x = (centerxfrac + FixedMul(tx, xscale)) / FRACUNIT; col->y = (centeryfrac - FixedMul(bb->tz, yscale)); From 0a7ad42c53adaaa630c06803bd13166f0a742f5d Mon Sep 17 00:00:00 2001 From: James R Date: Sun, 11 Sep 2022 16:54:15 -0700 Subject: [PATCH 077/114] r_opengl: add PF_WireFrame and SHADER_NONE Draw lines instead of tris and disable shader entirely. --- src/hardware/hw_defs.h | 4 +++- src/hardware/r_opengl/r_opengl.c | 8 +++++++- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/src/hardware/hw_defs.h b/src/hardware/hw_defs.h index 5ad7de244..1ba8164a3 100644 --- a/src/hardware/hw_defs.h +++ b/src/hardware/hw_defs.h @@ -133,6 +133,7 @@ typedef struct // Predefined shader types enum { + SHADER_NONE = -1, SHADER_DEFAULT = 0, SHADER_FLOOR, @@ -235,7 +236,8 @@ enum EPolyFlags PF_RemoveYWrap = 0x00010000, // Forces clamp texture on Y PF_ForceWrapX = 0x00020000, // Forces repeat texture on X PF_ForceWrapY = 0x00040000, // Forces repeat texture on Y - PF_Ripple = 0x00100000 // Water ripple effect. The current backend doesn't use it for anything. + PF_Ripple = 0x00100000, // Water ripple effect. The current backend doesn't use it for anything. + PF_WireFrame = 0x00200000, // Draws vertices as lines instead of triangles }; diff --git a/src/hardware/r_opengl/r_opengl.c b/src/hardware/r_opengl/r_opengl.c index 84ddc2492..8ab74b493 100644 --- a/src/hardware/r_opengl/r_opengl.c +++ b/src/hardware/r_opengl/r_opengl.c @@ -1061,6 +1061,12 @@ EXPORT void HWRAPI(LoadCustomShader) (int number, char *code, size_t size, boole EXPORT void HWRAPI(SetShader) (int type) { #ifdef GL_SHADERS + if (type == SHADER_NONE) + { + HWRAPI(UnSetShader)(); + return; + } + if (gl_allowshaders != HWD_SHADEROPTION_OFF) { gl_shader_t *shader = gl_shaderstate.current; @@ -2319,7 +2325,7 @@ EXPORT void HWRAPI(DrawPolygon) (FSurfaceInfo *pSurf, FOutVector *pOutVerts, FUI pglVertexPointer(3, GL_FLOAT, sizeof(FOutVector), &pOutVerts[0].x); pglTexCoordPointer(2, GL_FLOAT, sizeof(FOutVector), &pOutVerts[0].s); - pglDrawArrays(GL_TRIANGLE_FAN, 0, iNumPts); + pglDrawArrays(PolyFlags & PF_WireFrame ? GL_LINES : GL_TRIANGLE_FAN, 0, iNumPts); if (PolyFlags & PF_RemoveYWrap) pglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); From ac2a13fe574238c8fbf931a08b8d6e48a9171af7 Mon Sep 17 00:00:00 2001 From: James R Date: Sun, 11 Sep 2022 16:57:58 -0700 Subject: [PATCH 078/114] OpenGL hitbox renderer I apologize for that vertex array. --- src/hardware/hw_glob.h | 1 + src/hardware/hw_main.c | 130 +++++++++++++++++++++++++++++++++++++++-- src/r_bbox.c | 9 ++- src/r_things.h | 1 + 4 files changed, 132 insertions(+), 9 deletions(-) diff --git a/src/hardware/hw_glob.h b/src/hardware/hw_glob.h index 5b3f4654a..c13da6889 100644 --- a/src/hardware/hw_glob.h +++ b/src/hardware/hw_glob.h @@ -83,6 +83,7 @@ typedef struct gl_vissprite_s boolean flip, vflip; boolean precip; // Tails 08-25-2002 + boolean bbox; boolean rotated; UINT8 translucency; //alpha level 0-255 diff --git a/src/hardware/hw_main.c b/src/hardware/hw_main.c index a819171b4..3a4a0687f 100644 --- a/src/hardware/hw_main.c +++ b/src/hardware/hw_main.c @@ -70,6 +70,7 @@ static void HWR_ProjectSprite(mobj_t *thing); #ifdef HWPRECIP static void HWR_ProjectPrecipitationSprite(precipmobj_t *thing); #endif +static void HWR_ProjectBoundingBox(mobj_t *thing); static void HWR_RollTransform(FTransform *tr, angle_t roll); void HWR_AddTransparentFloor(levelflat_t *levelflat, extrasubsector_t *xsub, boolean isceiling, fixed_t fixedheight, INT32 lightlevel, INT32 alpha, sector_t *FOFSector, FBITFIELD blend, boolean fogplane, extracolormap_t *planecolormap); @@ -4109,6 +4110,54 @@ static void HWR_SplitSprite(gl_vissprite_t *spr) HWR_LinkDrawHackAdd(wallVerts, spr); } +static void HWR_DrawBoundingBox(gl_vissprite_t *vis) +{ + FOutVector v[24]; + FSurfaceInfo Surf = {0}; + + // + // create a cube (side view) + // + // 5--4 3 + // | + // | + // 0--1 2 + // + // repeat this 4 times (overhead) + // + // + // 17 20 21 11 + // 16 15 14 10 + // 27 22 *--* 07 12 + // | | + // 26 23 *--* 06 13 + // 24 00 01 02 + // 25 05 04 03 + // + + v[000].x = v[005].x = v[015].x = v[016].x = v[017].x = v[020].x = + v[022].x = v[023].x = v[024].x = v[025].x = v[026].x = v[027].x = vis->x1; // west + + v[001].x = v[002].x = v[003].x = v[004].x = v[006].x = v[007].x = + v[010].x = v[011].x = v[012].x = v[013].x = v[014].x = v[021].x = vis->x2; // east + + v[000].z = v[001].z = v[002].z = v[003].z = v[004].z = v[005].z = + v[006].z = v[013].z = v[023].z = v[024].z = v[025].z = v[026].z = vis->z1; // south + + v[007].z = v[010].z = v[011].z = v[012].z = v[014].z = v[015].z = + v[016].z = v[017].z = v[020].z = v[021].z = v[022].z = v[027].z = vis->z2; // north + + v[000].y = v[001].y = v[002].y = v[006].y = v[007].y = v[010].y = + v[014].y = v[015].y = v[016].y = v[022].y = v[023].y = v[024].y = vis->gz; // bottom + + v[003].y = v[004].y = v[005].y = v[011].y = v[012].y = v[013].y = + v[017].y = v[020].y = v[021].y = v[025].y = v[026].y = v[027].y = vis->gzt; // top + + Surf.PolyColor = V_GetColor(R_GetBoundingBoxColor(vis->mobj)); + + HWR_ProcessPolygon(&Surf, v, 24, PF_Modulated|PF_NoTexture|PF_WireFrame, SHADER_NONE, false); +} + // -----------------+ // HWR_DrawSprite : Draw flat sprites // : (monsters, bonuses, weapons, lights, ...) @@ -4562,9 +4611,16 @@ static int CompareVisSprites(const void *p1, const void *p2) int frame1; int frame2; + int linkdraw1; + int linkdraw2; + + // bbox doesn't need to be sorted + if (spr1->bbox || spr2->bbox) + return 0; + // check for precip first, because then sprX->mobj is actually a precipmobj_t and does not have flags2 or tracer - int linkdraw1 = !spr1->precip && (spr1->mobj->flags2 & MF2_LINKDRAW) && spr1->mobj->tracer; - int linkdraw2 = !spr2->precip && (spr2->mobj->flags2 & MF2_LINKDRAW) && spr2->mobj->tracer; + linkdraw1 = !spr1->precip && (spr1->mobj->flags2 & MF2_LINKDRAW) && spr1->mobj->tracer; + linkdraw2 = !spr2->precip && (spr2->mobj->flags2 & MF2_LINKDRAW) && spr2->mobj->tracer; // ^ is the XOR operation // if comparing a linkdraw and non-linkdraw sprite or 2 linkdraw sprites with different tracers, then use @@ -4954,6 +5010,9 @@ static void HWR_DrawSprites(void) for (i = 0; i < gl_visspritecount; i++) { gl_vissprite_t *spr = gl_vsprorder[i]; + if (spr->bbox) + HWR_DrawBoundingBox(spr); + else #ifdef HWPRECIP if (spr->precip) HWR_DrawPrecipitationSprite(spr); @@ -5052,8 +5111,15 @@ static void HWR_AddSprites(sector_t *sec) limit_dist = (fixed_t)(cv_drawdist.value) * mapobjectscale; for (thing = sec->thinglist; thing; thing = thing->snext) { - if (R_ThingVisibleWithinDist(thing, limit_dist)) - HWR_ProjectSprite(thing); + if (R_ThingWithinDist(thing, limit_dist)) + { + if (R_ThingVisible(thing)) + { + HWR_ProjectSprite(thing); + } + + HWR_ProjectBoundingBox(thing); + } } #ifdef HWPRECIP @@ -5552,6 +5618,7 @@ static void HWR_ProjectSprite(mobj_t *thing) vis->vflip = vflip; vis->precip = false; + vis->bbox = false; } #ifdef HWPRECIP @@ -5675,6 +5742,7 @@ static void HWR_ProjectPrecipitationSprite(precipmobj_t *thing) vis->gz = vis->gzt - (FIXED_TO_FLOAT(spritecachedinfo[lumpoff].height) * this_scale); vis->precip = true; + vis->bbox = false; // okay... this is a hack, but weather isn't networked, so it should be ok if (!(thing->precipflags & PCF_THUNK)) @@ -5685,6 +5753,60 @@ static void HWR_ProjectPrecipitationSprite(precipmobj_t *thing) } #endif +static void HWR_ProjectBoundingBox(mobj_t *thing) +{ + gl_vissprite_t *vis; + float tr_x, tr_y; + float tz; + float rad; + + // uncapped/interpolation + interpmobjstate_t interp = {0}; + + if (!thing) + return; + + if (!R_ThingBoundingBoxVisible(thing)) + return; + + if (R_UsingFrameInterpolation() && !paused) + { + R_InterpolateMobjState(thing, rendertimefrac, &interp); + } + else + { + R_InterpolateMobjState(thing, FRACUNIT, &interp); + } + + // transform the origin point + tr_x = FIXED_TO_FLOAT(interp.x) - gl_viewx; + tr_y = FIXED_TO_FLOAT(interp.y) - gl_viewy; + + // rotation around vertical axis + tz = (tr_x * gl_viewcos) + (tr_y * gl_viewsin); + + // thing is behind view plane? + if (tz < ZCLIP_PLANE) + return; + + tr_x += gl_viewx; + tr_y += gl_viewy; + + rad = FIXED_TO_FLOAT(thing->radius); + + vis = HWR_NewVisSprite(); + vis->x1 = tr_x - rad; + vis->x2 = tr_x + rad; + vis->z1 = tr_y - rad; + vis->z2 = tr_y + rad; + vis->gz = FIXED_TO_FLOAT(interp.z); + vis->gzt = vis->gz + FIXED_TO_FLOAT(thing->height); + vis->mobj = thing; + + vis->precip = false; + vis->bbox = true; +} + // ========================================================================== // Sky dome rendering, ported from PrBoom+ // ========================================================================== diff --git a/src/r_bbox.c b/src/r_bbox.c index 969aeacff..7c8887398 100644 --- a/src/r_bbox.c +++ b/src/r_bbox.c @@ -169,12 +169,11 @@ draw_bbox_row } } -static UINT8 -get_bbox_color (vissprite_t *vis) +UINT8 R_GetBoundingBoxColor(mobj_t *thing) { - UINT32 flags = vis->mobjflags; + UINT32 flags = thing->flags; - if (vis->mobj->player) + if (thing->player) return 255; // 0FF if (flags & (MF_NOCLIPTHING)) @@ -205,7 +204,7 @@ void R_DrawThingBoundingBox(vissprite_t *vis) struct bbox_config bb = { .height = vis->thingheight, .tz = vis->texturemid, - .color = get_bbox_color(vis), + .color = R_GetBoundingBoxColor(vis->mobj), }; // 1--3 diff --git a/src/r_things.h b/src/r_things.h index 085d15512..c6ad2a0e0 100644 --- a/src/r_things.h +++ b/src/r_things.h @@ -66,6 +66,7 @@ void R_AddSprites(sector_t *sec, INT32 lightlevel); void R_InitSprites(void); void R_ClearSprites(void); +UINT8 R_GetBoundingBoxColor(mobj_t *thing); boolean R_ThingBoundingBoxVisible(mobj_t *thing); boolean R_ThingVisible (mobj_t *thing); From d60d4b05fc6ec67db47a7db3307e71f2e3c07821 Mon Sep 17 00:00:00 2001 From: James R Date: Sun, 11 Sep 2022 16:59:25 -0700 Subject: [PATCH 079/114] Remove R_ThingVisibleWithinDist It's no longer used! --- src/r_things.c | 10 ---------- src/r_things.h | 3 --- 2 files changed, 13 deletions(-) diff --git a/src/r_things.c b/src/r_things.c index d7d2a12b5..c0e727fd9 100644 --- a/src/r_things.c +++ b/src/r_things.c @@ -3425,16 +3425,6 @@ boolean R_ThingWithinDist (mobj_t *thing, fixed_t limit_dist) return true; } -// For OpenGL, TODO: REMOVE!! -boolean R_ThingVisibleWithinDist (mobj_t *thing, - fixed_t limit_dist) -{ - if (! R_ThingVisible(thing)) - return false; - - return R_ThingWithinDist(thing, limit_dist); -} - /* Check if precipitation may be drawn from our current view. */ boolean R_PrecipThingVisible (precipmobj_t *precipthing, fixed_t limit_dist) diff --git a/src/r_things.h b/src/r_things.h index c6ad2a0e0..b89645fff 100644 --- a/src/r_things.h +++ b/src/r_things.h @@ -74,9 +74,6 @@ boolean R_ThingVisible (mobj_t *thing); boolean R_ThingWithinDist (mobj_t *thing, fixed_t draw_dist); -boolean R_ThingVisibleWithinDist (mobj_t *thing, - fixed_t draw_dist); - boolean R_PrecipThingVisible (precipmobj_t *precipthing, fixed_t precip_draw_dist); From 6aaa6082999e52c18fd937164516cee6c2bf6348 Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Sun, 11 Sep 2022 21:40:24 -0400 Subject: [PATCH 080/114] Playable Shrink --- src/deh_tables.c | 8 ++ src/info.c | 96 ++++++++++++++- src/info.h | 9 ++ src/k_kart.c | 2 +- src/k_kart.h | 1 + src/k_objects.h | 1 + src/objects/shrink.c | 272 +++++++++++++++++++++++++++++++++++-------- src/p_map.c | 46 ++++++++ 8 files changed, 379 insertions(+), 56 deletions(-) diff --git a/src/deh_tables.c b/src/deh_tables.c index 3c4e7fe1e..8b25029b6 100644 --- a/src/deh_tables.c +++ b/src/deh_tables.c @@ -3755,6 +3755,11 @@ const char *const STATE_LIST[] = { // array length left dynamic for sanity testi // Caked-Up Booty-Sheet Ghost "S_HYUDORO", + // Shrink + "S_SHRINK_GUN", + "S_SHRINK_LASER", + "S_SHRINK_PARTICLE", + // The legend "S_SINK", "S_SINK_SHIELD", @@ -5338,7 +5343,10 @@ const char *const MOBJTYPE_LIST[] = { // array length left dynamic for sanity t "MT_HYUDORO_CENTER", "MT_SHRINK_POHBEE", + "MT_SHRINK_GUN", + "MT_SHRINK_CHAIN", "MT_SHRINK_LASER", + "MT_SHRINK_PARTICLE", "MT_SINK", // Kitchen Sink Stuff "MT_SINK_SHIELD", diff --git a/src/info.c b/src/info.c index f067ee1f1..c2bd3c964 100644 --- a/src/info.c +++ b/src/info.c @@ -573,6 +573,7 @@ char sprnames[NUMSPRITES + 1][5] = "FLML", // Flame Shield speed lines "FLMF", // Flame Shield flash "HYUU", // Hyudoro + "SHRG", // Shrink gun / laser "SINK", // Kitchen Sink "SITR", // Kitchen Sink Trail "KBLN", // Battle Mode Bumper @@ -4314,6 +4315,10 @@ state_t states[NUMSTATES] = {SPR_HYUU, FF_FULLBRIGHT, -1, {NULL}, 0, 0, S_NULL}, // S_HYUDORO + {SPR_SHRG, 0, -1, {NULL}, 0, 0, S_NULL}, // S_SHRINK_GUN + {SPR_SHRG, FF_FULLBRIGHT|1, -1, {NULL}, 0, 0, S_NULL}, // S_SHRINK_LASER + {SPR_SHRG, FF_FULLBRIGHT|2, -1, {NULL}, 0, 0, S_NULL}, // S_SHRINK_PARTICLE + {SPR_SINK, 0, 1, {A_SmokeTrailer}, MT_SINKTRAIL, 0, S_SINK}, // S_SINK {SPR_SINK, 0|FF_TRANS80|FF_FULLBRIGHT, -1, {NULL}, 0, 0, S_SINK_SHIELD}, // S_SINK_SHIELD {SPR_SITR, 0, 1, {NULL}, 0, 0, S_SINKTRAIL2}, // S_SINKTRAIL1 @@ -24063,9 +24068,9 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] = S_NULL // raisestate }, - { // MT_SHRINK_LASER + { // MT_SHRINK_GUN -1, // doomednum - S_HYUDORO, // spawnstate + S_SHRINK_GUN, // spawnstate 1000, // spawnhealth S_NULL, // seestate sfx_None, // seesound @@ -24080,13 +24085,94 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] = S_NULL, // xdeathstate sfx_None, // deathsound 0, // speed - 32*FRACUNIT, // radius - 24*FRACUNIT, // height + 48*FRACUNIT, // radius + 120*FRACUNIT, // height 0, // display offset 0, // mass 0, // damage sfx_None, // activesound - MF_NOCLIP|MF_NOCLIPTHING|MF_NOCLIPHEIGHT|MF_NOGRAVITY|MF_DONTENCOREMAP, // flags + MF_SCENERY|MF_NOCLIPHEIGHT|MF_NOGRAVITY|MF_DONTENCOREMAP, // flags + S_NULL // raisestate + }, + + { // MT_SHRINK_CHAIN + -1, // doomednum + S_SHRINK_GUN, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 16*FRACUNIT, // radius + 120*FRACUNIT, // height + 0, // display offset + 0, // mass + 0, // damage + sfx_None, // activesound + MF_NOTHINK|MF_NOCLIP|MF_NOCLIPTHING|MF_NOCLIPHEIGHT|MF_NOGRAVITY|MF_DONTENCOREMAP, // flags + S_NULL // raisestate + }, + + { // MT_SHRINK_LASER + -1, // doomednum + S_SHRINK_LASER, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 16*FRACUNIT, // radius + 33*FRACUNIT, // height + 0, // display offset + 0, // mass + 0, // damage + sfx_None, // activesound + MF_SCENERY|MF_NOCLIP|MF_NOCLIPTHING|MF_NOCLIPHEIGHT|MF_NOGRAVITY|MF_DONTENCOREMAP, // flags + S_NULL // raisestate + }, + + { // MT_SHRINK_PARTICLE + -1, // doomednum + S_SHRINK_PARTICLE, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 16*FRACUNIT, // radius + 16*FRACUNIT, // height + 0, // display offset + 0, // mass + 0, // damage + sfx_None, // activesound + MF_SCENERY|MF_NOCLIP|MF_NOCLIPTHING|MF_NOCLIPHEIGHT|MF_NOGRAVITY|MF_DONTENCOREMAP, // flags S_NULL // raisestate }, diff --git a/src/info.h b/src/info.h index 74771110e..0ff8f7f93 100644 --- a/src/info.h +++ b/src/info.h @@ -1119,6 +1119,7 @@ typedef enum sprite SPR_FLML, // Flame Shield speed lines SPR_FLMF, // Flame Shield flash SPR_HYUU, // Hyudoro + SPR_SHRG, // Shrink gun / laser SPR_SINK, // Kitchen Sink SPR_SITR, // Kitchen Sink Trail SPR_KBLN, // Battle Mode Bumper @@ -4745,6 +4746,11 @@ typedef enum state // Caked-Up Booty-Sheet Ghost S_HYUDORO, + // Shrink + S_SHRINK_GUN, + S_SHRINK_LASER, + S_SHRINK_PARTICLE, + // The legend S_SINK, S_SINK_SHIELD, @@ -6364,7 +6370,10 @@ typedef enum mobj_type MT_HYUDORO_CENTER, MT_SHRINK_POHBEE, + MT_SHRINK_GUN, + MT_SHRINK_CHAIN, MT_SHRINK_LASER, + MT_SHRINK_PARTICLE, MT_SINK, // Kitchen Sink Stuff MT_SINK_SHIELD, diff --git a/src/k_kart.c b/src/k_kart.c index b36009f68..f96f50182 100644 --- a/src/k_kart.c +++ b/src/k_kart.c @@ -3643,7 +3643,7 @@ void K_SpinPlayer(player_t *player, mobj_t *inflictor, mobj_t *source, INT32 typ P_SetPlayerMobjState(player->mo, S_KART_SPINOUT); } -static void K_RemoveGrowShrink(player_t *player) +void K_RemoveGrowShrink(player_t *player) { if (player->mo && !P_MobjWasRemoved(player->mo)) { diff --git a/src/k_kart.h b/src/k_kart.h index bab501200..4529b2611 100644 --- a/src/k_kart.h +++ b/src/k_kart.h @@ -71,6 +71,7 @@ void K_AwardPlayerRings(player_t *player, INT32 rings, boolean overload); void K_DoInstashield(player_t *player); void K_DoPowerClash(player_t *t1, player_t *t2); void K_BattleAwardHit(player_t *player, player_t *victim, mobj_t *inflictor, UINT8 bumpersRemoved); +void K_RemoveGrowShrink(player_t *player); void K_SpinPlayer(player_t *player, mobj_t *inflictor, mobj_t *source, INT32 type); void K_TumblePlayer(player_t *player, mobj_t *inflictor, mobj_t *source); void K_TumbleInterrupt(player_t *player); diff --git a/src/k_objects.h b/src/k_objects.h index d0b6f6b8b..45a487c5a 100644 --- a/src/k_objects.h +++ b/src/k_objects.h @@ -10,6 +10,7 @@ void Obj_HyudoroCollide(mobj_t *special, mobj_t *toucher); /* Shrink */ void Obj_PohbeeThinker(mobj_t *pohbee); +boolean Obj_ShrinkLaserCollide(mobj_t *gun, mobj_t *victim); void Obj_CreateShrinkPohbees(player_t *owner); #endif/*k_objects_H*/ diff --git a/src/objects/shrink.c b/src/objects/shrink.c index bde2fdb35..2ea94245f 100644 --- a/src/objects/shrink.c +++ b/src/objects/shrink.c @@ -23,13 +23,13 @@ #include "../z_zone.h" #include "../k_waypoint.h" -#define POHBEE_HOVER (384 << FRACBITS) +#define POHBEE_HOVER (256 << FRACBITS) #define POHBEE_SPEED (128 << FRACBITS) #define POHBEE_TIME (15 * TICRATE) #define POHBEE_DIST (4096 << FRACBITS) -#define LASER_SPEED (20 << FRACBITS) -#define LASER_SWINGTIME (3 * TICRATE) +#define GUN_SWING (ANGLE_90 - ANG10) +#define GUN_SWINGTIME (3 * TICRATE) #define CHAIN_SIZE (16) @@ -44,17 +44,61 @@ enum #define pohbee_timer(o) ((o)->reactiontime) #define pohbee_waypoint_cur(o) ((o)->extravalue1) #define pohbee_waypoint_dest(o) ((o)->extravalue2) +#define pohbee_height(o) ((o)->movefactor) #define pohbee_owner(o) ((o)->target) -#define pohbee_lasers(o) ((o)->hnext) +#define pohbee_guns(o) ((o)->hnext) -#define laser_offset(o) ((o)->movecount) -#define laser_swing(o) ((o)->movedir) -#define laser_numsegs(o) ((o)->extravalue1) +#define gun_offset(o) ((o)->movecount) +#define gun_numsegs(o) ((o)->extravalue1) -#define laser_pohbee(o) ((o)->target) -#define laser_collider(o) ((o)->tracer) -#define laser_chains(o) ((o)->hprev) +#define gun_pohbee(o) ((o)->target) +#define gun_laser(o) ((o)->tracer) +#define gun_chains(o) ((o)->hprev) + +enum +{ + LASER_SHRINK, + LASER_GROW, +}; + +static skincolornum_t ShrinkLaserColor(mobj_t *pohbee) +{ + UINT8 laserState = LASER_SHRINK; + player_t *owner = NULL; + + if (pohbee_owner(pohbee) != NULL && P_MobjWasRemoved(pohbee_owner(pohbee)) == false) + { + owner = pohbee_owner(pohbee)->player; + } + + if (owner != NULL && P_IsDisplayPlayer(owner) == true) + { + laserState = LASER_GROW; + + if (r_splitscreen > 0 && (leveltime & 1)) + { + // TODO: make this properly screen dependent, + // instead of flashing. + laserState = LASER_SHRINK; + } + } + + switch (laserState) + { + default: + case LASER_SHRINK: + return SKINCOLOR_KETCHUP; + + case LASER_GROW: + return SKINCOLOR_SAPPHIRE; + } +} + +static boolean ShrinkLaserActive(mobj_t *pohbee) +{ + return (pohbee_mode(pohbee) == POHBEE_MODE_ACT); +} static void PohbeeMoveTo(mobj_t *pohbee, fixed_t destx, fixed_t desty, fixed_t destz) { @@ -70,9 +114,9 @@ static fixed_t GenericDistance( return P_AproxDistance(P_AproxDistance(destx - curx, desty - cury), destz - curz); } -static fixed_t PohbeeWaypointZ(mobj_t *dest) +static fixed_t PohbeeWaypointZ(mobj_t *pohbee, mobj_t *dest) { - return dest->z + (FixedMul(POHBEE_HOVER, mapobjectscale) * P_MobjFlip(dest)); + return dest->z + (pohbee_height(pohbee) + FixedMul(POHBEE_HOVER, mapobjectscale) * P_MobjFlip(dest)); } static void PohbeeSpawn(mobj_t *pohbee) @@ -110,7 +154,7 @@ static void PohbeeSpawn(mobj_t *pohbee) { fixed_t wpX = curWaypoint->mobj->x; fixed_t wpY = curWaypoint->mobj->y; - fixed_t wpZ = PohbeeWaypointZ(curWaypoint->mobj); + fixed_t wpZ = PohbeeWaypointZ(pohbee, curWaypoint->mobj); fixed_t distToNext = GenericDistance( newX, newY, newZ, @@ -210,17 +254,16 @@ static void PohbeeDespawn(mobj_t *pohbee) pohbee->momz = 16 * pohbee->scale * P_MobjFlip(pohbee); } -static void DoLaserSwing(mobj_t *laser, mobj_t *pohbee) +static void DoGunSwing(mobj_t *gun, mobj_t *pohbee) { - const angle_t angle = laser->angle + ANGLE_90; - const tic_t swingTimer = leveltime + laser_offset(laser); + const angle_t angle = gun->angle + ANGLE_90; + const tic_t swingTimer = leveltime + gun_offset(gun); - const angle_t swingAmt = swingTimer * (ANGLE_MAX / LASER_SWINGTIME); + const angle_t swingAmt = swingTimer * (ANGLE_MAX / GUN_SWINGTIME); const fixed_t swingCos = FINECOSINE(swingAmt >> ANGLETOFINESHIFT); - const fixed_t swing = FixedMul(laser_swing(laser), 9 * swingCos); - const angle_t pitch = -ANGLE_90 + swing; - const fixed_t dist = laser_numsegs(laser) * CHAIN_SIZE * laser->scale; + const angle_t pitch = -ANGLE_90 + FixedMul(swingCos, GUN_SWING); + const fixed_t dist = gun_numsegs(gun) * CHAIN_SIZE * gun->scale; fixed_t offsetX = FixedMul( dist, FixedMul( @@ -240,28 +283,76 @@ static void DoLaserSwing(mobj_t *laser, mobj_t *pohbee) dist, FINESINE(pitch >> ANGLETOFINESHIFT) ); - PohbeeMoveTo(laser, pohbee->x + offsetX, pohbee->y + offsetY, pohbee->z + offsetZ); + PohbeeMoveTo(gun, pohbee->x + offsetX, pohbee->y + offsetY, pohbee->z + offsetZ); } -static void ShrinkLaserThinker(mobj_t *laser) +static void ShrinkLaserThinker(mobj_t *pohbee, mobj_t *gun, mobj_t *laser) { - mobj_t *pohbee = laser_pohbee(laser); + PohbeeMoveTo(laser, gun->x, gun->y, gun->floorz); + + if (ShrinkLaserActive(pohbee) == true) + { + mobj_t *particle = NULL; + + laser->renderflags &= ~RF_DONTDRAW; + laser->color = gun->color; + + if (leveltime & 1) + { + laser->spritexscale = 3*FRACUNIT/2; + } + else + { + laser->spritexscale = FRACUNIT; + } + + laser->spriteyscale = FixedDiv(FixedDiv(gun->z - gun->floorz, mapobjectscale), laser->info->height); + + particle = P_SpawnMobjFromMobj( + laser, + P_RandomRange(-16, 16) * FRACUNIT, + P_RandomRange(-16, 16) * FRACUNIT, + 0, + MT_SHRINK_PARTICLE + ); + + particle->color = laser->color; + + P_SetScale(particle, particle->scale * 2); + particle->destscale = 0; + + particle->momz = 2 * particle->scale * P_MobjFlip(particle); + } + else + { + laser->renderflags |= RF_DONTDRAW; + } +} + +static void ShrinkGunThinker(mobj_t *gun) +{ + mobj_t *pohbee = gun_pohbee(gun); if (pohbee == NULL || P_MobjWasRemoved(pohbee) == true) { - P_RemoveMobj(laser); + P_RemoveMobj(gun); return; } - laser->angle = pohbee->angle; - DoLaserSwing(laser, pohbee); + gun->angle = pohbee->angle; + gun->color = ShrinkLaserColor(pohbee); - //PohbeeMoveTo(laser_collider(laser), laser->x, laser->y, laser->z); + DoGunSwing(gun, pohbee); + + if (gun_laser(gun) != NULL && P_MobjWasRemoved(gun_laser(gun)) == false) + { + ShrinkLaserThinker(pohbee, gun, gun_laser(gun)); + } } void Obj_PohbeeThinker(mobj_t *pohbee) { - mobj_t *laser = NULL; + mobj_t *gun = NULL; pohbee->momx = pohbee->momy = pohbee->momz = 0; @@ -285,11 +376,11 @@ void Obj_PohbeeThinker(mobj_t *pohbee) break; } - laser = pohbee_lasers(pohbee); - while (laser != NULL && P_MobjWasRemoved(laser) == false) + gun = pohbee_guns(pohbee); + while (gun != NULL && P_MobjWasRemoved(gun) == false) { - ShrinkLaserThinker(laser); - laser = pohbee_lasers(laser); + ShrinkGunThinker(gun); + gun = pohbee_guns(gun); } } @@ -298,7 +389,7 @@ void Obj_PohbeeRemoved(mobj_t *pohbee) { mobj_t *chain = NULL; - if (pohbee_laser(pohbee) != NULL) + if (pohbee_guns(pohbee) != NULL) { P_RemoveMobj(pohbee_laser(pohbee)); } @@ -313,6 +404,84 @@ void Obj_PohbeeRemoved(mobj_t *pohbee) } */ +boolean Obj_ShrinkLaserCollide(mobj_t *gun, mobj_t *victim) +{ + mobj_t *pohbee = gun_pohbee(gun); + mobj_t *owner = NULL; + + if (pohbee == NULL || P_MobjWasRemoved(pohbee) == true) + { + return true; + } + + if (ShrinkLaserActive(pohbee) == false) + { + return true; + } + + owner = pohbee_owner(pohbee); + + if (owner != NULL && victim == owner) + { + // Belongs to us. Give us Grow! + if (victim->player->growshrinktimer <= 0) + { + victim->scalespeed = mapobjectscale/TICRATE; + victim->destscale = FixedMul(mapobjectscale, GROW_SCALE); + + if (K_PlayerShrinkCheat(victim->player) == true) + { + victim->destscale = FixedMul(victim->destscale, SHRINK_SCALE); + } + + // TODO: gametyperules + victim->player->growshrinktimer = (gametype == GT_BATTLE ? 8 : 12) * TICRATE; + + if (victim->player->invincibilitytimer > 0) + { + ; // invincibility has priority in P_RestoreMusic, no point in starting here + } + else if (P_IsLocalPlayer(victim->player) == true) + { + S_ChangeMusicSpecial("kgrow"); + } + else //used to be "if (P_IsDisplayPlayer(victim->player) == false)" + { + S_StartSound(victim, (cv_kartinvinsfx.value ? sfx_alarmg : sfx_kgrow)); + } + + P_RestoreMusic(victim->player); + S_StartSound(victim, sfx_kc5a); + } + } + else + { + if (victim->player->growshrinktimer > 0) + { + // Take away Grow. + K_RemoveGrowShrink(victim->player); + } + else + { + // Start shrinking! + K_DropItems(victim->player); + victim->player->growshrinktimer = -(15*TICRATE); + + victim->scalespeed = mapobjectscale/TICRATE; + victim->destscale = FixedMul(mapobjectscale, SHRINK_SCALE); + + if (K_PlayerShrinkCheat(victim->player) == true) + { + victim->destscale = FixedMul(victim->destscale, SHRINK_SCALE); + } + + S_StartSound(victim, sfx_kc59); + } + } + + return true; +} + static waypoint_t *GetPohbeeStart(waypoint_t *anchor) { const UINT32 traveldist = FixedMul(POHBEE_DIST >> 1, mapobjectscale) / FRACUNIT; @@ -373,10 +542,10 @@ static void CreatePohbee(player_t *owner, waypoint_t *start, waypoint_t *end, UI { mobj_t *pohbee = NULL; - fixed_t size = INT32_MAX; + fixed_t size = 0; INT32 baseSegs = INT32_MAX; INT32 segVal = INT32_MAX; - mobj_t *prevLaser = NULL; + mobj_t *prevGun = NULL; size_t i, j; @@ -389,8 +558,8 @@ static void CreatePohbee(player_t *owner, waypoint_t *start, waypoint_t *end, UI } // Calculate number of chain segments added per laser. - size = end->mobj->radius / mapobjectscale; - baseSegs = 1 + (size / CHAIN_SIZE); + size = FixedMul(end->mobj->radius, 3*FRACUNIT/2); + baseSegs = 1 + ((size / start->mobj->scale) / CHAIN_SIZE); if (baseSegs < MAXPLAYERS) { @@ -406,39 +575,42 @@ static void CreatePohbee(player_t *owner, waypoint_t *start, waypoint_t *end, UI pohbee_mode(pohbee) = POHBEE_MODE_SPAWN; pohbee_timer(pohbee) = POHBEE_TIME; + pohbee_height(pohbee) = size; pohbee_waypoint_cur(pohbee) = (INT32)K_GetWaypointHeapIndex(start); pohbee_waypoint_dest(pohbee) = (INT32)K_GetWaypointHeapIndex(end); - prevLaser = pohbee; + prevGun = pohbee; for (i = 0; i < numLasers; i++) { const UINT8 numSegs = segVal * (i + 1); - mobj_t *laser = P_SpawnMobjFromMobj(pohbee, 0, 0, 0, MT_SHRINK_LASER); - //mobj_t *collider = NULL; + mobj_t *gun = P_SpawnMobjFromMobj(pohbee, 0, 0, 0, MT_SHRINK_GUN); + mobj_t *laser = NULL; //mobj_t *prevChain = NULL; - P_SetTarget(&laser_pohbee(laser), pohbee); - P_SetTarget(&pohbee_lasers(prevLaser), laser); + P_SetTarget(&gun_pohbee(gun), pohbee); + P_SetTarget(&pohbee_guns(prevGun), gun); - laser_numsegs(laser) = numSegs; - laser_swing(laser) = (ANGLE_45 * baseSeg) / numSegs; - laser_offset(laser) = P_RandomKey(LASER_SWINGTIME); + gun_numsegs(gun) = numSegs; + gun_offset(gun) = P_RandomKey(GUN_SWINGTIME); + + laser = P_SpawnMobjFromMobj(gun, 0, 0, 0, MT_SHRINK_LASER); + P_SetTarget(&gun_laser(gun), laser); /* - prevChain = laser; + prevChain = gun; for (j = 0; j < numSegs; j++) { - mobj_t *chain = P_SpawnMobjFromMobj(laser, 0, 0, 0, MT_SHRINK_LASER); - P_SetTarget(&laser_chains(prevChain), chain); + mobj_t *chain = P_SpawnMobjFromMobj(gun, 0, 0, 0, MT_SHRINK_CHAIN); + P_SetTarget(&gun_chains(prevChain), chain); prevChain = chain; } */ (void)j; - prevLaser = laser; + prevGun = gun; } } @@ -511,7 +683,7 @@ void Obj_CreateShrinkPohbees(player_t *owner) // Push a new poh-bee pohbees[j].start = GetPohbeeStart(player->nextwaypoint); pohbees[j].end = endWaypoint; - pohbees[j].lasers = 4; + pohbees[j].lasers = 1; numPohbees++; } } diff --git a/src/p_map.c b/src/p_map.c index ad970ee45..5c9b98ef2 100644 --- a/src/p_map.c +++ b/src/p_map.c @@ -32,6 +32,7 @@ #include "hu_stuff.h" // SRB2kart #include "i_system.h" // SRB2kart #include "k_terrain.h" +#include "k_objects.h" #include "r_splats.h" @@ -739,6 +740,51 @@ static BlockItReturn_t PIT_CheckThing(mobj_t *thing) // SRB2kart 011617 - Colission[sic] code for kart items //{ + if (thing->type == MT_SHRINK_GUN) + { + if (tmthing->type != MT_PLAYER) + { + return BMIT_CONTINUE; + } + + // Use special collision for the laser gun. + // The laser sprite itself is just a visual, + // the gun itself does the colliding for us. + if (tmthing->z > thing->z) + { + return BMIT_CONTINUE; // overhead + } + + if (tmthing->z + tmthing->height < thing->floorz) + { + return BMIT_CONTINUE; // underneath + } + + return Obj_ShrinkLaserCollide(thing, tmthing) ? BMIT_CONTINUE : BMIT_ABORT; + } + else if (tmthing->type == MT_SHRINK_GUN) + { + if (thing->type != MT_PLAYER) + { + return BMIT_CONTINUE; + } + + // Use special collision for the laser gun. + // The laser sprite itself is just a visual, + // the gun itself does the colliding for us. + if (thing->z > tmthing->z) + { + return BMIT_CONTINUE; // overhead + } + + if (thing->z + thing->height < tmthing->floorz) + { + return BMIT_CONTINUE; // underneath + } + + return Obj_ShrinkLaserCollide(tmthing, thing) ? BMIT_CONTINUE : BMIT_ABORT; + } + if (tmthing->type == MT_SMK_ICEBLOCK) { // see if it went over / under From e1cf42dcbdcfeaf5f7e31ec3f307c8c3d17042f6 Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Sun, 11 Sep 2022 21:48:30 -0400 Subject: [PATCH 081/114] Increase Shrink odds --- src/k_kart.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/k_kart.c b/src/k_kart.c index f96f50182..6c1241a7d 100644 --- a/src/k_kart.c +++ b/src/k_kart.c @@ -355,7 +355,7 @@ static INT32 K_KartItemOddsRace[NUMKARTRESULTS-1][8] = //P-Odds 0 1 2 3 4 5 6 7 /*Sneaker*/ { 0, 0, 2, 4, 6, 0, 0, 0 }, // Sneaker /*Rocket Sneaker*/ { 0, 0, 0, 0, 0, 2, 4, 6 }, // Rocket Sneaker - /*Invincibility*/ { 0, 0, 0, 0, 3, 4, 6, 9 }, // Invincibility + /*Invincibility*/ { 0, 0, 0, 0, 3, 4, 5, 7 }, // Invincibility /*Banana*/ { 2, 3, 1, 0, 0, 0, 0, 0 }, // Banana /*Eggman Monitor*/ { 1, 2, 0, 0, 0, 0, 0, 0 }, // Eggman Monitor /*Orbinaut*/ { 5, 5, 2, 2, 0, 0, 0, 0 }, // Orbinaut @@ -365,7 +365,7 @@ static INT32 K_KartItemOddsRace[NUMKARTRESULTS-1][8] = /*Ballhog*/ { 0, 0, 2, 2, 0, 0, 0, 0 }, // Ballhog /*Self-Propelled Bomb*/ { 0, 0, 0, 0, 0, 2, 4, 0 }, // Self-Propelled Bomb /*Grow*/ { 0, 0, 0, 1, 2, 3, 0, 0 }, // Grow - /*Shrink*/ { 0, 0, 0, 0, 0, 0, 2, 0 }, // Shrink + /*Shrink*/ { 0, 0, 0, 0, 0, 0, 4, 2 }, // Shrink /*Lightning Shield*/ { 1, 0, 0, 0, 0, 0, 0, 0 }, // Lightning Shield /*Bubble Shield*/ { 0, 1, 2, 1, 0, 0, 0, 0 }, // Bubble Shield /*Flame Shield*/ { 0, 0, 0, 0, 0, 1, 3, 5 }, // Flame Shield @@ -375,7 +375,7 @@ static INT32 K_KartItemOddsRace[NUMKARTRESULTS-1][8] = /*Kitchen Sink*/ { 0, 0, 0, 0, 0, 0, 0, 0 }, // Kitchen Sink /*Drop Target*/ { 3, 0, 0, 0, 0, 0, 0, 0 }, // Drop Target /*Sneaker x2*/ { 0, 0, 2, 2, 2, 0, 0, 0 }, // Sneaker x2 - /*Sneaker x3*/ { 0, 0, 0, 1, 6,10, 5, 0 }, // Sneaker x3 + /*Sneaker x3*/ { 0, 0, 0, 1, 6,10, 4, 0 }, // Sneaker x3 /*Banana x3*/ { 0, 1, 1, 0, 0, 0, 0, 0 }, // Banana x3 /*Banana x10*/ { 0, 0, 0, 1, 0, 0, 0, 0 }, // Banana x10 /*Orbinaut x3*/ { 0, 0, 1, 0, 0, 0, 0, 0 }, // Orbinaut x3 From 892863db05d2822f4fa9cc13cc0e6cfb85fbc59f Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Sun, 11 Sep 2022 21:52:53 -0400 Subject: [PATCH 082/114] Add bot support to new Shrink They move towards lasers that belong to them, and steer away from ones that don't. --- src/k_botsearch.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/k_botsearch.c b/src/k_botsearch.c index af7464c30..74a458bce 100644 --- a/src/k_botsearch.c +++ b/src/k_botsearch.c @@ -425,6 +425,16 @@ static BlockItReturn_t K_FindObjectsForNudging(mobj_t *thing) case MT_BUBBLESHIELDTRAP: K_AddDodgeObject(thing, side, 20); break; + case MT_SHRINK_GUN: + if (thing->target == globalsmuggle.botmo) + { + K_AddAttackObject(thing, side, 20); + } + else + { + K_AddDodgeObject(thing, side, 20); + } + break; case MT_RANDOMITEM: if (anglediff >= 45) { From b8bbdda1f350001e1d8d46d5de9038aa95ed6281 Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Sun, 11 Sep 2022 23:31:53 -0400 Subject: [PATCH 083/114] Improvements from the netgame - Double active time - Increase height they spawn from - Slow down swing speed the tiniest bit - Add an extra 7 for first place - Increase hitbox - Make particles into hitbox - Tweak item odds --- src/info.c | 8 +++--- src/k_kart.c | 4 +-- src/objects/shrink.c | 30 +++++++++++++++++--- src/p_map.c | 66 ++++++++++++++++++++++++++++++++------------ 4 files changed, 80 insertions(+), 28 deletions(-) diff --git a/src/info.c b/src/info.c index c2bd3c964..56892d9c9 100644 --- a/src/info.c +++ b/src/info.c @@ -24085,7 +24085,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] = S_NULL, // xdeathstate sfx_None, // deathsound 0, // speed - 48*FRACUNIT, // radius + 52*FRACUNIT, // radius 120*FRACUNIT, // height 0, // display offset 0, // mass @@ -24166,13 +24166,13 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] = S_NULL, // xdeathstate sfx_None, // deathsound 0, // speed - 16*FRACUNIT, // radius - 16*FRACUNIT, // height + 26*FRACUNIT, // radius + 26*FRACUNIT, // height 0, // display offset 0, // mass 0, // damage sfx_None, // activesound - MF_SCENERY|MF_NOCLIP|MF_NOCLIPTHING|MF_NOCLIPHEIGHT|MF_NOGRAVITY|MF_DONTENCOREMAP, // flags + MF_NOCLIPHEIGHT|MF_NOGRAVITY|MF_DONTENCOREMAP, // flags S_NULL // raisestate }, diff --git a/src/k_kart.c b/src/k_kart.c index 6c1241a7d..61a32bd76 100644 --- a/src/k_kart.c +++ b/src/k_kart.c @@ -365,7 +365,7 @@ static INT32 K_KartItemOddsRace[NUMKARTRESULTS-1][8] = /*Ballhog*/ { 0, 0, 2, 2, 0, 0, 0, 0 }, // Ballhog /*Self-Propelled Bomb*/ { 0, 0, 0, 0, 0, 2, 4, 0 }, // Self-Propelled Bomb /*Grow*/ { 0, 0, 0, 1, 2, 3, 0, 0 }, // Grow - /*Shrink*/ { 0, 0, 0, 0, 0, 0, 4, 2 }, // Shrink + /*Shrink*/ { 0, 0, 0, 0, 0, 2, 4, 2 }, // Shrink /*Lightning Shield*/ { 1, 0, 0, 0, 0, 0, 0, 0 }, // Lightning Shield /*Bubble Shield*/ { 0, 1, 2, 1, 0, 0, 0, 0 }, // Bubble Shield /*Flame Shield*/ { 0, 0, 0, 0, 0, 1, 3, 5 }, // Flame Shield @@ -375,7 +375,7 @@ static INT32 K_KartItemOddsRace[NUMKARTRESULTS-1][8] = /*Kitchen Sink*/ { 0, 0, 0, 0, 0, 0, 0, 0 }, // Kitchen Sink /*Drop Target*/ { 3, 0, 0, 0, 0, 0, 0, 0 }, // Drop Target /*Sneaker x2*/ { 0, 0, 2, 2, 2, 0, 0, 0 }, // Sneaker x2 - /*Sneaker x3*/ { 0, 0, 0, 1, 6,10, 4, 0 }, // Sneaker x3 + /*Sneaker x3*/ { 0, 0, 0, 1, 6, 8, 4, 0 }, // Sneaker x3 /*Banana x3*/ { 0, 1, 1, 0, 0, 0, 0, 0 }, // Banana x3 /*Banana x10*/ { 0, 0, 0, 1, 0, 0, 0, 0 }, // Banana x10 /*Orbinaut x3*/ { 0, 0, 1, 0, 0, 0, 0, 0 }, // Orbinaut x3 diff --git a/src/objects/shrink.c b/src/objects/shrink.c index 2ea94245f..6f8cb92a1 100644 --- a/src/objects/shrink.c +++ b/src/objects/shrink.c @@ -25,14 +25,16 @@ #define POHBEE_HOVER (256 << FRACBITS) #define POHBEE_SPEED (128 << FRACBITS) -#define POHBEE_TIME (15 * TICRATE) +#define POHBEE_TIME (30 * TICRATE) #define POHBEE_DIST (4096 << FRACBITS) #define GUN_SWING (ANGLE_90 - ANG10) -#define GUN_SWINGTIME (3 * TICRATE) +#define GUN_SWINGTIME (4 * TICRATE) #define CHAIN_SIZE (16) +#define EXTRA_FOR_FIRST (7) + enum { POHBEE_MODE_SPAWN, @@ -321,7 +323,7 @@ static void ShrinkLaserThinker(mobj_t *pohbee, mobj_t *gun, mobj_t *laser) P_SetScale(particle, particle->scale * 2); particle->destscale = 0; - particle->momz = 2 * particle->scale * P_MobjFlip(particle); + //particle->momz = 2 * particle->scale * P_MobjFlip(particle); } else { @@ -570,7 +572,7 @@ static void CreatePohbee(player_t *owner, waypoint_t *start, waypoint_t *end, UI // Valid spawning conditions, // we can start creating each individual part. - pohbee = P_SpawnMobjFromMobj(start->mobj, 0, 0, POHBEE_HOVER * 3, MT_SHRINK_POHBEE); + pohbee = P_SpawnMobjFromMobj(start->mobj, 0, 0, FixedDiv(size, mapobjectscale) + POHBEE_HOVER * 3, MT_SHRINK_POHBEE); P_SetTarget(&pohbee_owner(pohbee), owner->mo); pohbee_mode(pohbee) = POHBEE_MODE_SPAWN; @@ -622,6 +624,7 @@ void Obj_CreateShrinkPohbees(player_t *owner) waypoint_t *start; waypoint_t *end; UINT8 lasers; + boolean first; } pohbees[MAXPLAYERS]; size_t numPohbees = 0; @@ -684,6 +687,12 @@ void Obj_CreateShrinkPohbees(player_t *owner) pohbees[j].start = GetPohbeeStart(player->nextwaypoint); pohbees[j].end = endWaypoint; pohbees[j].lasers = 1; + + if (player->position == 1) + { + pohbees[j].first = true; + } + numPohbees++; } } @@ -691,5 +700,18 @@ void Obj_CreateShrinkPohbees(player_t *owner) for (i = 0; i < numPohbees; i++) { CreatePohbee(owner, pohbees[i].start, pohbees[i].end, pohbees[i].lasers); + + if (pohbees[i].first == true) + { + // Add a chain of extra ones for 1st place. + waypoint_t *prev = pohbees[i].end; + + for (j = 0; j < EXTRA_FOR_FIRST; j++) + { + waypoint_t *new = GetPohbeeEnd(pohbees[i].end); + CreatePohbee(owner, prev, new, 1); + prev = new; + } + } } } diff --git a/src/p_map.c b/src/p_map.c index 5c9b98ef2..0c490ce41 100644 --- a/src/p_map.c +++ b/src/p_map.c @@ -740,46 +740,76 @@ static BlockItReturn_t PIT_CheckThing(mobj_t *thing) // SRB2kart 011617 - Colission[sic] code for kart items //{ - if (thing->type == MT_SHRINK_GUN) + if (thing->type == MT_SHRINK_GUN || thing->type == MT_SHRINK_PARTICLE) { if (tmthing->type != MT_PLAYER) { return BMIT_CONTINUE; } - // Use special collision for the laser gun. - // The laser sprite itself is just a visual, - // the gun itself does the colliding for us. - if (tmthing->z > thing->z) + if (thing->type == MT_SHRINK_GUN) { - return BMIT_CONTINUE; // overhead - } + // Use special collision for the laser gun. + // The laser sprite itself is just a visual, + // the gun itself does the colliding for us. + if (tmthing->z > thing->z) + { + return BMIT_CONTINUE; // overhead + } - if (tmthing->z + tmthing->height < thing->floorz) + if (tmthing->z + tmthing->height < thing->floorz) + { + return BMIT_CONTINUE; // underneath + } + } + else { - return BMIT_CONTINUE; // underneath + if (tmthing->z > thing->z + thing->height) + { + return BMIT_CONTINUE; // overhead + } + + if (tmthing->z + tmthing->height < thing->z) + { + return BMIT_CONTINUE; // underneath + } } return Obj_ShrinkLaserCollide(thing, tmthing) ? BMIT_CONTINUE : BMIT_ABORT; } - else if (tmthing->type == MT_SHRINK_GUN) + else if (tmthing->type == MT_SHRINK_GUN || tmthing->type == MT_SHRINK_PARTICLE) { if (thing->type != MT_PLAYER) { return BMIT_CONTINUE; } - // Use special collision for the laser gun. - // The laser sprite itself is just a visual, - // the gun itself does the colliding for us. - if (thing->z > tmthing->z) + if (thing->type == MT_SHRINK_GUN) { - return BMIT_CONTINUE; // overhead - } + // Use special collision for the laser gun. + // The laser sprite itself is just a visual, + // the gun itself does the colliding for us. + if (thing->z > tmthing->z) + { + return BMIT_CONTINUE; // overhead + } - if (thing->z + thing->height < tmthing->floorz) + if (thing->z + thing->height < tmthing->floorz) + { + return BMIT_CONTINUE; // underneath + } + } + else { - return BMIT_CONTINUE; // underneath + if (tmthing->z > thing->z + thing->height) + { + return BMIT_CONTINUE; // overhead + } + + if (tmthing->z + tmthing->height < thing->z) + { + return BMIT_CONTINUE; // underneath + } } return Obj_ShrinkLaserCollide(tmthing, thing) ? BMIT_CONTINUE : BMIT_ABORT; From 135b444b5eddf1123b0e7c6740eabe9876ceb4bc Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Mon, 12 Sep 2022 00:06:04 -0400 Subject: [PATCH 084/114] Fix bad shrink position --- src/objects/shrink.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/objects/shrink.c b/src/objects/shrink.c index 6f8cb92a1..4053d8248 100644 --- a/src/objects/shrink.c +++ b/src/objects/shrink.c @@ -708,7 +708,7 @@ void Obj_CreateShrinkPohbees(player_t *owner) for (j = 0; j < EXTRA_FOR_FIRST; j++) { - waypoint_t *new = GetPohbeeEnd(pohbees[i].end); + waypoint_t *new = GetPohbeeEnd(prev); CreatePohbee(owner, prev, new, 1); prev = new; } From 27fdb815b93d28b751f143658ea1058a8035eaac Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Mon, 12 Sep 2022 00:06:48 -0400 Subject: [PATCH 085/114] Give solid to gun & particles Fixes hitboxes for the gun, IDK why particle isn't working. Also fix typo error --- src/info.c | 4 ++-- src/p_map.c | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/info.c b/src/info.c index 56892d9c9..e011e7874 100644 --- a/src/info.c +++ b/src/info.c @@ -24091,7 +24091,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] = 0, // mass 0, // damage sfx_None, // activesound - MF_SCENERY|MF_NOCLIPHEIGHT|MF_NOGRAVITY|MF_DONTENCOREMAP, // flags + MF_SOLID|MF_NOCLIPHEIGHT|MF_NOGRAVITY|MF_DONTENCOREMAP, // flags S_NULL // raisestate }, @@ -24172,7 +24172,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] = 0, // mass 0, // damage sfx_None, // activesound - MF_NOCLIPHEIGHT|MF_NOGRAVITY|MF_DONTENCOREMAP, // flags + MF_SOLID|MF_NOCLIPHEIGHT|MF_NOGRAVITY|MF_DONTENCOREMAP, // flags S_NULL // raisestate }, diff --git a/src/p_map.c b/src/p_map.c index 0c490ce41..e00cbea1c 100644 --- a/src/p_map.c +++ b/src/p_map.c @@ -784,7 +784,7 @@ static BlockItReturn_t PIT_CheckThing(mobj_t *thing) return BMIT_CONTINUE; } - if (thing->type == MT_SHRINK_GUN) + if (tmthing->type == MT_SHRINK_GUN) { // Use special collision for the laser gun. // The laser sprite itself is just a visual, From 07f2932d3ac681a0063a533895d82c77beac9fd3 Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Mon, 12 Sep 2022 00:18:35 -0400 Subject: [PATCH 086/114] Set owner of particle --- src/info.c | 4 ++-- src/objects/shrink.c | 2 ++ 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/info.c b/src/info.c index e011e7874..0b390cf0a 100644 --- a/src/info.c +++ b/src/info.c @@ -24091,7 +24091,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] = 0, // mass 0, // damage sfx_None, // activesound - MF_SOLID|MF_NOCLIPHEIGHT|MF_NOGRAVITY|MF_DONTENCOREMAP, // flags + MF_SPECIAL|MF_NOCLIPHEIGHT|MF_NOGRAVITY|MF_DONTENCOREMAP, // flags S_NULL // raisestate }, @@ -24172,7 +24172,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] = 0, // mass 0, // damage sfx_None, // activesound - MF_SOLID|MF_NOCLIPHEIGHT|MF_NOGRAVITY|MF_DONTENCOREMAP, // flags + MF_SPECIAL|MF_NOCLIPHEIGHT|MF_NOGRAVITY|MF_DONTENCOREMAP, // flags S_NULL // raisestate }, diff --git a/src/objects/shrink.c b/src/objects/shrink.c index 4053d8248..019f2db2a 100644 --- a/src/objects/shrink.c +++ b/src/objects/shrink.c @@ -318,6 +318,8 @@ static void ShrinkLaserThinker(mobj_t *pohbee, mobj_t *gun, mobj_t *laser) MT_SHRINK_PARTICLE ); + P_SetTarget(&gun_pohbee(particle), pohbee); + particle->color = laser->color; P_SetScale(particle, particle->scale * 2); From fe0aa349a7621e5d71bc6bf8d84b3b1185180729 Mon Sep 17 00:00:00 2001 From: toaster Date: Mon, 12 Sep 2022 12:58:39 +0100 Subject: [PATCH 087/114] Fix compilation issue with nested defines for r_opengl.c UnSetShader --- src/hardware/r_opengl/r_opengl.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/hardware/r_opengl/r_opengl.c b/src/hardware/r_opengl/r_opengl.c index 8ab74b493..00125221c 100644 --- a/src/hardware/r_opengl/r_opengl.c +++ b/src/hardware/r_opengl/r_opengl.c @@ -1063,7 +1063,7 @@ EXPORT void HWRAPI(SetShader) (int type) #ifdef GL_SHADERS if (type == SHADER_NONE) { - HWRAPI(UnSetShader)(); + UnSetShader(); return; } From 3064b2cb7a23da02de926b21bc12a5c0989c0d54 Mon Sep 17 00:00:00 2001 From: toaster Date: Mon, 12 Sep 2022 16:01:37 +0100 Subject: [PATCH 088/114] Fixes for the replay post-game save interface (NOT THE ACTUAL REPLAYS OR HUT, THAT'LL BE LATER) - `(B) or (X)` for replay saving instead of Press Lookback (HUD) or `(Y)` (input) - Don't activate while closing pause menu if either of those buttons is shared for menu open/close --- src/p_tick.c | 2 +- src/st_stuff.c | 2 +- src/y_inter.c | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/p_tick.c b/src/p_tick.c index 5e60ba711..58b11406f 100644 --- a/src/p_tick.c +++ b/src/p_tick.c @@ -721,7 +721,7 @@ void P_Ticker(boolean run) G_WriteAllGhostTics(); if (cv_recordmultiplayerdemos.value && (demo.savemode == DSM_NOTSAVING || demo.savemode == DSM_WILLAUTOSAVE)) - if (demo.savebutton && demo.savebutton + 3*TICRATE < leveltime && G_PlayerInputDown(0, gc_y, 0)) + if (demo.savebutton && demo.savebutton + 3*TICRATE < leveltime && !menuactive && (G_PlayerInputDown(0, gc_b, 0) || G_PlayerInputDown(0, gc_x, 0))) demo.savemode = DSM_TITLEENTRY; } else if (demo.playback) // Use Ghost data for consistency checks. diff --git a/src/st_stuff.c b/src/st_stuff.c index 08e1305c1..af18b1441 100644 --- a/src/st_stuff.c +++ b/src/st_stuff.c @@ -1218,7 +1218,7 @@ void ST_Drawer(void) switch (demo.savemode) { case DSM_NOTSAVING: - V_DrawRightAlignedThinString(BASEVIDWIDTH - 2, 2, V_HUDTRANS|V_SNAPTOTOP|V_SNAPTORIGHT|V_ALLOWLOWERCASE|((gametyperules & GTR_BUMPERS) ? V_REDMAP : V_SKYMAP), "Look Backward: Save replay"); + V_DrawRightAlignedThinString(BASEVIDWIDTH - 2, 2, V_HUDTRANS|V_SNAPTOTOP|V_SNAPTORIGHT|V_ALLOWLOWERCASE|((gametyperules & GTR_BUMPERS) ? V_REDMAP : V_SKYMAP), "(B) or (X): Save replay"); break; case DSM_WILLAUTOSAVE: diff --git a/src/y_inter.c b/src/y_inter.c index 89eb4010e..798ff19e9 100644 --- a/src/y_inter.c +++ b/src/y_inter.c @@ -592,7 +592,7 @@ skiptallydrawer: switch (demo.savemode) { case DSM_NOTSAVING: - V_DrawRightAlignedThinString(BASEVIDWIDTH - 2, 2, V_SNAPTOTOP|V_SNAPTORIGHT|V_ALLOWLOWERCASE|hilicol, "(B): Save replay"); + V_DrawRightAlignedThinString(BASEVIDWIDTH - 2, 2, V_SNAPTOTOP|V_SNAPTORIGHT|V_ALLOWLOWERCASE|hilicol, "(B) or (X): Save replay"); break; case DSM_SAVED: @@ -633,7 +633,7 @@ void Y_Ticker(void) if (demo.recording) { - if (demo.savemode == DSM_NOTSAVING && G_PlayerInputDown(0, gc_y, 0)) + if (demo.savemode == DSM_NOTSAVING && !menuactive && (G_PlayerInputDown(0, gc_b, 0) || G_PlayerInputDown(0, gc_x, 0))) demo.savemode = DSM_TITLEENTRY; if (demo.savemode == DSM_WILLSAVE || demo.savemode == DSM_WILLAUTOSAVE) From 902fcfa569bbc1dbed18c64fb4864df7b1078e86 Mon Sep 17 00:00:00 2001 From: "X.organic" Date: Fri, 2 Sep 2022 13:20:31 +0000 Subject: [PATCH 089/114] Miscellaneous memory bug fixes to make AddressSanitizer happy # Conflicts: # src/d_clisrv.c # src/dehacked.c # src/hardware/hw_cache.c # src/hardware/hw_md2.c # src/http-mserv.c # src/lua_hudlib_drawlist.c --- src/d_clisrv.c | 2 +- src/d_net.c | 2 +- src/g_input.c | 3 +++ src/hardware/hw_md2.c | 16 +++++++++---- src/http-mserv.c | 8 ++++--- src/i_net.h | 2 +- src/i_tcp.c | 4 ++-- src/lua_hudlib_drawlist.c | 48 +++++++++++++++++++++++---------------- 8 files changed, 53 insertions(+), 32 deletions(-) diff --git a/src/d_clisrv.c b/src/d_clisrv.c index f8934c2a8..99a5e5fe6 100644 --- a/src/d_clisrv.c +++ b/src/d_clisrv.c @@ -1351,7 +1351,7 @@ static void SendAskInfo(INT32 node) if (node != 0 && node != BROADCASTADDR && cv_rendezvousserver.string[0]) { - I_NetRequestHolePunch(); + I_NetRequestHolePunch(node); } asktime = I_GetTime(); diff --git a/src/d_net.c b/src/d_net.c index 1c81fe3f3..48e97aa31 100644 --- a/src/d_net.c +++ b/src/d_net.c @@ -75,7 +75,7 @@ boolean (*I_NetCanGet)(void) = NULL; void (*I_NetCloseSocket)(void) = NULL; void (*I_NetFreeNodenum)(INT32 nodenum) = NULL; SINT8 (*I_NetMakeNodewPort)(const char *address, const char* port) = NULL; -void (*I_NetRequestHolePunch)(void) = NULL; +void (*I_NetRequestHolePunch)(INT32 node) = NULL; void (*I_NetRegisterHolePunch)(void) = NULL; boolean (*I_NetOpenSocket)(void) = NULL; boolean (*I_Ban) (INT32 node) = NULL; diff --git a/src/g_input.c b/src/g_input.c index ead8cab27..9fbf89bf0 100644 --- a/src/g_input.c +++ b/src/g_input.c @@ -499,6 +499,9 @@ INT32 G_KeyStringtoNum(const char *keystr) { UINT32 j; + if (!keystr[0]) + return 0; + if (!keystr[1] && keystr[0] > ' ' && keystr[0] <= 'z') return keystr[0]; diff --git a/src/hardware/hw_md2.c b/src/hardware/hw_md2.c index 32b7ff7f4..746f094e2 100644 --- a/src/hardware/hw_md2.c +++ b/src/hardware/hw_md2.c @@ -713,11 +713,11 @@ static void HWR_CreateBlendedTexture(patch_t *gpatch, patch_t *blendgpatch, GLMi UINT16 w = gpatch->width, h = gpatch->height; UINT32 size = w*h; RGBA_t *image, *blendimage, *cur, blendcolor; - UINT8 translation[16]; // First the color index - UINT8 cutoff[16]; // Brightness cutoff before using the next color + UINT8 translation[17]; // First the color index + UINT8 cutoff[17]; // Brightness cutoff before using the next color UINT8 translen = 0; UINT8 i; - UINT8 colorbrightnesses[16]; + UINT8 colorbrightnesses[17]; UINT8 color_match_lookup[256]; // optimization attempt blendcolor = V_GetColor(0); // initialize @@ -788,6 +788,11 @@ static void HWR_CreateBlendedTexture(patch_t *gpatch, patch_t *blendgpatch, GLMi translen++; } + if (translen > 0) + translation[translen] = translation[translen-1]; // extended to accomodate secondi if firsti equal to translen-1 + if (translen > 1) + cutoff[translen] = cutoff[translen-1] = 0; // as above + if (skinnum == TC_RAINBOW && translen > 0) { UINT16 b; @@ -803,7 +808,7 @@ static void HWR_CreateBlendedTexture(patch_t *gpatch, patch_t *blendgpatch, GLMi { UINT16 brightdif = 256; - color_match_lookup[i] = 0; + color_match_lookup[b] = 0; for (i = 0; i < translen; i++) { if (b > colorbrightnesses[i]) // don't allow greater matches (because calculating a makeshift gradient for this is already a huge mess as is) @@ -820,6 +825,9 @@ static void HWR_CreateBlendedTexture(patch_t *gpatch, patch_t *blendgpatch, GLMi } } + if (translen > 0) + colorbrightnesses[translen] = colorbrightnesses[translen-1]; + while (size--) { if (skinnum == TC_HITLAG) diff --git a/src/http-mserv.c b/src/http-mserv.c index b5e3ba6c7..d7d65d871 100644 --- a/src/http-mserv.c +++ b/src/http-mserv.c @@ -55,6 +55,8 @@ consvar_t cv_masterserver_token = CVAR_INIT NULL ); +#define HMS_QUERY_VERSION "?v=2.2" + #ifdef MASTERSERVER static int hms_started; @@ -174,7 +176,7 @@ HMS_connect (const char *format, ...) va_start (ap, format); url = malloc(seek + vsnprintf(0, 0, format, ap) + - sizeof "?v=2" - 1 + + sizeof HMS_QUERY_VERSION - 1 + token_length + 1); va_end (ap); @@ -188,8 +190,8 @@ HMS_connect (const char *format, ...) seek += vsprintf(&url[seek], format, ap); va_end (ap); - strcpy(&url[seek], "?v=2"); - seek += sizeof "?v=2" - 1; + strcpy(&url[seek], HMS_QUERY_VERSION); + seek += sizeof HMS_QUERY_VERSION - 1; if (quack_token) sprintf(&url[seek], "&token=%s", quack_token); diff --git a/src/i_net.h b/src/i_net.h index 8caa0edcc..6efa22c69 100644 --- a/src/i_net.h +++ b/src/i_net.h @@ -150,7 +150,7 @@ extern void (*I_NetCloseSocket)(void); /** \brief send a hole punching request */ -extern void (*I_NetRequestHolePunch)(void); +extern void (*I_NetRequestHolePunch)(INT32 node); /** \brief register this machine on the hole punching server */ diff --git a/src/i_tcp.c b/src/i_tcp.c index fef81d05c..55667d252 100644 --- a/src/i_tcp.c +++ b/src/i_tcp.c @@ -1382,9 +1382,9 @@ static void rendezvous(int size) free(addrs); } -static void SOCK_RequestHolePunch(void) +static void SOCK_RequestHolePunch(INT32 node) { - mysockaddr_t * addr = &clientaddress[doomcom->remotenode]; + mysockaddr_t * addr = &clientaddress[node]; holepunchpacket->addr = addr->ip4.sin_addr.s_addr; holepunchpacket->port = addr->ip4.sin_port; diff --git a/src/lua_hudlib_drawlist.c b/src/lua_hudlib_drawlist.c index 9b318ee91..ce5b9d49f 100644 --- a/src/lua_hudlib_drawlist.c +++ b/src/lua_hudlib_drawlist.c @@ -54,7 +54,7 @@ typedef struct drawitem_s { fixed_t sy; INT32 num; INT32 digits; - const char *str; + size_t stroffset; // offset into strbuf to get str UINT16 color; UINT8 strength; INT32 align; @@ -129,6 +129,10 @@ void LUA_HUD_DestroyDrawList(huddrawlist_h list) { Z_Free(list->items); } + if (list->strbuf) + { + Z_Free(list->strbuf); + } Z_Free(list); } @@ -156,7 +160,7 @@ static size_t AllocateDrawItem(huddrawlist_h list) // copy string to list's internal string buffer // lua can deallocate the string before we get to use it, so it's important to // keep our own copy -static const char *CopyString(huddrawlist_h list, const char* str) +static size_t CopyString(huddrawlist_h list, const char* str) { size_t lenstr; @@ -168,10 +172,13 @@ static const char *CopyString(huddrawlist_h list, const char* str) else list->strbuf_capacity *= 2; list->strbuf = (char*) Z_ReallocAlign(list->strbuf, sizeof(char) * list->strbuf_capacity, PU_STATIC, NULL, 8); } - const char *result = (const char *) &list->strbuf[list->strbuf_len]; - strncpy(&list->strbuf[list->strbuf_len], str, lenstr + 1); - list->strbuf_len += lenstr + 1; - return result; + + { + size_t old_len = list->strbuf_len; + strncpy(&list->strbuf[old_len], str, lenstr + 1); + list->strbuf_len += lenstr + 1; + return old_len; + } } void LUA_HUD_AddDraw( @@ -325,7 +332,7 @@ void LUA_HUD_AddDrawString( item->type = DI_DrawString; item->x = x; item->y = y; - item->str = CopyString(list, str); + item->stroffset = CopyString(list, str); item->flags = flags; item->align = align; } @@ -360,7 +367,7 @@ void LUA_HUD_AddDrawTitleCardString( item->x = x; item->y = y; item->flags = flags; - item->str = CopyString(list, str); + item->stroffset = CopyString(list, str); item->bossmode = bossmode; item->timer = timer; item->threshold = threshold; @@ -379,8 +386,8 @@ void LUA_HUD_AddDrawKartString( item->type = DI_DrawKartString; item->x = x; item->y = y; + item->stroffset = CopyString(list, str); item->flags = flags; - item->str = CopyString(list, str); } void LUA_HUD_DrawList(huddrawlist_h list) @@ -394,6 +401,7 @@ void LUA_HUD_DrawList(huddrawlist_h list) for (i = 0; i < list->items_len; i++) { drawitem_t *item = &list->items[i]; + const char *itemstr = &list->strbuf[item->stroffset]; switch (item->type) { @@ -423,33 +431,33 @@ void LUA_HUD_DrawList(huddrawlist_h list) { // hu_font case align_left: - V_DrawString(item->x, item->y, item->flags, item->str); + V_DrawString(item->x, item->y, item->flags, itemstr); break; case align_center: - V_DrawCenteredString(item->x, item->y, item->flags, item->str); + V_DrawCenteredString(item->x, item->y, item->flags, itemstr); break; case align_right: - V_DrawRightAlignedString(item->x, item->y, item->flags, item->str); + V_DrawRightAlignedString(item->x, item->y, item->flags, itemstr); break; // hu_font, 0.5x scale case align_small: - V_DrawSmallString(item->x, item->y, item->flags, item->str); + V_DrawSmallString(item->x, item->y, item->flags, itemstr); break; case align_smallcenter: - V_DrawCenteredSmallString(item->x, item->y, item->flags, item->str); + V_DrawCenteredSmallString(item->x, item->y, item->flags, itemstr); break; case align_smallright: - V_DrawRightAlignedSmallString(item->x, item->y, item->flags, item->str); + V_DrawRightAlignedSmallString(item->x, item->y, item->flags, itemstr); break; // tny_font case align_thin: - V_DrawThinString(item->x, item->y, item->flags, item->str); + V_DrawThinString(item->x, item->y, item->flags, itemstr); break; case align_thincenter: - V_DrawCenteredThinString(item->x, item->y, item->flags, item->str); + V_DrawCenteredThinString(item->x, item->y, item->flags, itemstr); break; case align_thinright: - V_DrawRightAlignedThinString(item->x, item->y, item->flags, item->str); + V_DrawRightAlignedThinString(item->x, item->y, item->flags, itemstr); break; } break; @@ -457,10 +465,10 @@ void LUA_HUD_DrawList(huddrawlist_h list) V_DrawFadeScreen(item->color, item->strength); break; case DI_DrawTitleCardString: - V_DrawTitleCardString(item->x, item->y, item->str, item->flags, item->bossmode, item->timer, item->threshold); + V_DrawTitleCardString(item->x, item->y, itemstr, item->flags, item->bossmode, item->timer, item->threshold); break; case DI_DrawKartString: - V_DrawKartString(item->x, item->y, item->flags, item->str); + V_DrawKartString(item->x, item->y, item->flags, itemstr); break; default: I_Error("can't draw draw list item: invalid draw list item type"); From 6dfd49a3b96161c18c95cf29c6d03ae8f607a592 Mon Sep 17 00:00:00 2001 From: "X.organic" Date: Sat, 3 Sep 2022 02:58:47 +0000 Subject: [PATCH 090/114] Fix use-after-frees around mobjs # Conflicts: # src/p_enemy.c # src/p_mobj.c # src/p_saveg.c # src/p_tick.c --- src/k_kart.c | 2 +- src/p_mobj.c | 3 ++- src/p_saveg.c | 6 +++--- 3 files changed, 6 insertions(+), 5 deletions(-) diff --git a/src/k_kart.c b/src/k_kart.c index 269d45d06..5683baa6f 100644 --- a/src/k_kart.c +++ b/src/k_kart.c @@ -6701,7 +6701,7 @@ static void K_MoveHeldObjects(player_t *player) targz += (player->mo->height/2 - 32*player->mo->scale)*6; } - if (cur->tracer) + if (cur->tracer && !P_MobjWasRemoved(cur->tracer)) { fixed_t diffx, diffy, diffz; diff --git a/src/p_mobj.c b/src/p_mobj.c index c4c9b7962..b541744b3 100644 --- a/src/p_mobj.c +++ b/src/p_mobj.c @@ -10345,7 +10345,7 @@ mobj_t *P_SpawnMobj(fixed_t x, fixed_t y, fixed_t z, mobjtype_t type) mobj_t *side = P_SpawnMobj(mobj->x + FINECOSINE((ang>>ANGLETOFINESHIFT) & FINEMASK), mobj->y + FINESINE((ang>>ANGLETOFINESHIFT) & FINEMASK), mobj->z, MT_DAYTONAPINETREE_SIDE); P_InitAngle(side, ang); - side->target = mobj; + P_SetTarget(&side->target, mobj); side->threshold = i; } break; @@ -10762,6 +10762,7 @@ void P_RemoveSavegameMobj(mobj_t *mobj) // free block P_RemoveThinker((thinker_t *)mobj); + R_RemoveMobjInterpolator(mobj); } static CV_PossibleValue_t respawnitemtime_cons_t[] = {{1, "MIN"}, {300, "MAX"}, {0, NULL}}; diff --git a/src/p_saveg.c b/src/p_saveg.c index 72101fc9c..e657da154 100644 --- a/src/p_saveg.c +++ b/src/p_saveg.c @@ -4193,21 +4193,21 @@ static void P_RelinkPointers(void) { temp = (UINT32)(size_t)mobj->hnext; mobj->hnext = NULL; - if (!(mobj->hnext = P_FindNewPosition(temp))) + if (!P_SetTarget(&mobj->hnext, P_FindNewPosition(temp))) CONS_Debug(DBG_GAMELOGIC, "hnext not found on %d\n", mobj->type); } if (mobj->hprev) { temp = (UINT32)(size_t)mobj->hprev; mobj->hprev = NULL; - if (!(mobj->hprev = P_FindNewPosition(temp))) + if (!P_SetTarget(&mobj->hprev, P_FindNewPosition(temp))) CONS_Debug(DBG_GAMELOGIC, "hprev not found on %d\n", mobj->type); } if (mobj->itnext) { temp = (UINT32)(size_t)mobj->itnext; mobj->itnext = NULL; - if (!(mobj->itnext = P_FindNewPosition(temp))) + if (!P_SetTarget(&mobj->itnext, P_FindNewPosition(temp))) CONS_Debug(DBG_GAMELOGIC, "itnext not found on %d\n", mobj->type); } if (mobj->terrain) From 92cfb5a677346b8b88a3fa1b9a03fe8c2f4dc583 Mon Sep 17 00:00:00 2001 From: VelocitOni Date: Tue, 13 Sep 2022 19:17:39 -0400 Subject: [PATCH 091/114] 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 092/114] 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 093/114] 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); From 970add8b91f43f2ebb21ccd14b090cbdac9d56d1 Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Wed, 14 Sep 2022 02:47:42 -0400 Subject: [PATCH 094/114] Add grow/shrink particles --- src/deh_tables.c | 5 +++++ src/info.c | 30 ++++++++++++++++++++++++++ src/info.h | 6 ++++++ src/k_kart.c | 56 ++++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 97 insertions(+) diff --git a/src/deh_tables.c b/src/deh_tables.c index 8b25029b6..8a2713bad 100644 --- a/src/deh_tables.c +++ b/src/deh_tables.c @@ -3755,6 +3755,9 @@ const char *const STATE_LIST[] = { // array length left dynamic for sanity testi // Caked-Up Booty-Sheet Ghost "S_HYUDORO", + // Grow + "S_GROW_PARTICLE", + // Shrink "S_SHRINK_GUN", "S_SHRINK_LASER", @@ -5342,6 +5345,8 @@ const char *const MOBJTYPE_LIST[] = { // array length left dynamic for sanity t "MT_HYUDORO", "MT_HYUDORO_CENTER", + "MT_GROW_PARTICLE", + "MT_SHRINK_POHBEE", "MT_SHRINK_GUN", "MT_SHRINK_CHAIN", diff --git a/src/info.c b/src/info.c index 0b390cf0a..7f1840092 100644 --- a/src/info.c +++ b/src/info.c @@ -573,6 +573,7 @@ char sprnames[NUMSPRITES + 1][5] = "FLML", // Flame Shield speed lines "FLMF", // Flame Shield flash "HYUU", // Hyudoro + "GRWP", // Grow "SHRG", // Shrink gun / laser "SINK", // Kitchen Sink "SITR", // Kitchen Sink Trail @@ -4315,6 +4316,8 @@ state_t states[NUMSTATES] = {SPR_HYUU, FF_FULLBRIGHT, -1, {NULL}, 0, 0, S_NULL}, // S_HYUDORO + {SPR_GRWP, FF_ADD|FF_FULLBRIGHT|FF_ANIMATE, 13, {NULL}, 7, 1, S_NULL}, // S_GROW_PARTICLE + {SPR_SHRG, 0, -1, {NULL}, 0, 0, S_NULL}, // S_SHRINK_GUN {SPR_SHRG, FF_FULLBRIGHT|1, -1, {NULL}, 0, 0, S_NULL}, // S_SHRINK_LASER {SPR_SHRG, FF_FULLBRIGHT|2, -1, {NULL}, 0, 0, S_NULL}, // S_SHRINK_PARTICLE @@ -24041,6 +24044,33 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] = S_NULL // raisestate }, + { // MT_GROW_PARTICLE + -1, // doomednum + S_GROW_PARTICLE, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 4*FRACUNIT, // radius + 8*FRACUNIT, // height + 0, // display offset + 0, // mass + 0, // damage + sfx_None, // activesound + MF_SCENERY|MF_NOCLIP|MF_NOCLIPTHING|MF_NOCLIPHEIGHT|MF_NOGRAVITY|MF_DONTENCOREMAP, // flags + S_NULL // raisestate + }, + { // MT_SHRINK_POHBEE -1, // doomednum S_HYUDORO, // spawnstate diff --git a/src/info.h b/src/info.h index 0ff8f7f93..ca7264dba 100644 --- a/src/info.h +++ b/src/info.h @@ -1119,6 +1119,7 @@ typedef enum sprite SPR_FLML, // Flame Shield speed lines SPR_FLMF, // Flame Shield flash SPR_HYUU, // Hyudoro + SPR_GRWP, // Grow SPR_SHRG, // Shrink gun / laser SPR_SINK, // Kitchen Sink SPR_SITR, // Kitchen Sink Trail @@ -4746,6 +4747,9 @@ typedef enum state // Caked-Up Booty-Sheet Ghost S_HYUDORO, + // Grow + S_GROW_PARTICLE, + // Shrink S_SHRINK_GUN, S_SHRINK_LASER, @@ -6369,6 +6373,8 @@ typedef enum mobj_type MT_HYUDORO, MT_HYUDORO_CENTER, + MT_GROW_PARTICLE, + MT_SHRINK_POHBEE, MT_SHRINK_GUN, MT_SHRINK_CHAIN, diff --git a/src/k_kart.c b/src/k_kart.c index 3b9de17da..19a2dc3e6 100644 --- a/src/k_kart.c +++ b/src/k_kart.c @@ -2284,6 +2284,57 @@ void K_SpawnInvincibilitySpeedLines(mobj_t *mo) fast->destscale = 6*((mo->player->invincibilitytimer/TICRATE)*FRACUNIT)/8; } +static void K_SpawnGrowShrinkParticles(mobj_t *mo, INT32 timer) +{ + const boolean shrink = (timer < 0); + const INT32 maxTime = (10*TICRATE); + INT32 spawnFreq = 1; + + mobj_t *particle = NULL; + fixed_t particleSpeed = 0; + + spawnFreq = (maxTime - min(maxTime, abs(timer))) / TICRATE / 2; + if (spawnFreq == 0) + { + spawnFreq++; + } + + if (leveltime % spawnFreq != 0) + { + return; + } + + particle = P_SpawnMobjFromMobj( + mo, + P_RandomRange(-32, 32) * FRACUNIT, + P_RandomRange(-32, 32) * FRACUNIT, + (shrink ? P_RandomRange(24, 48) : P_RandomRange(0, 24)) * FRACUNIT, + MT_GROW_PARTICLE + ); + + P_SetTarget(&particle->target, mo); + + particle->momx = mo->momx; + particle->momy = mo->momy; + particle->momz = P_GetMobjZMovement(mo); + + K_MatchGenericExtraFlags(particle, mo); + + particleSpeed = particle->scale * 4 * P_MobjFlip(mo); + + if (shrink == true) + { + particle->color = SKINCOLOR_KETCHUP; + particle->momz -= particleSpeed; + particle->renderflags |= RF_VERTICALFLIP; + } + else + { + particle->color = SKINCOLOR_SAPPHIRE; + particle->momz += particleSpeed; + } +} + void K_SpawnBumpEffect(mobj_t *mo) { mobj_t *fx = P_SpawnMobj(mo->x, mo->y, mo->z, MT_BUMP); @@ -7325,6 +7376,11 @@ void K_KartPlayerThink(player_t *player, ticcmd_t *cmd) } } + if (player->growshrinktimer != 0) + { + K_SpawnGrowShrinkParticles(player->mo, player->growshrinktimer); + } + if (gametype == GT_RACE && player->rings <= 0) // spawn ring debt indicator { mobj_t *debtflag = P_SpawnMobj(player->mo->x + player->mo->momx, player->mo->y + player->mo->momy, From a4b63f3ffae6be99429f8250249d88db8dc4f9e2 Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Wed, 14 Sep 2022 03:15:03 -0400 Subject: [PATCH 095/114] Make shrink lasers add timer instead of resetting --- src/d_player.h | 2 + src/k_kart.c | 5 ++- src/objects/shrink.c | 96 ++++++++++++++++++++++++++------------------ src/p_saveg.c | 4 ++ 4 files changed, 68 insertions(+), 39 deletions(-) diff --git a/src/d_player.h b/src/d_player.h index 91989279c..5675dfb5e 100644 --- a/src/d_player.h +++ b/src/d_player.h @@ -584,6 +584,8 @@ typedef struct player_s UINT8 stairjank; + UINT8 shrinkLaserDelay; + #ifdef HWRENDER fixed_t fovadd; // adjust FOV for hw rendering #endif diff --git a/src/k_kart.c b/src/k_kart.c index 19a2dc3e6..cef0a644e 100644 --- a/src/k_kart.c +++ b/src/k_kart.c @@ -7545,6 +7545,9 @@ void K_KartPlayerThink(player_t *player, ticcmd_t *cmd) comebackshowninfo = true; // client has already seen the message } + if (player->shrinkLaserDelay) + player->shrinkLaserDelay--; + if (player->ringdelay) player->ringdelay--; @@ -10041,7 +10044,7 @@ void K_MoveKartPlayer(player_t *player, boolean onground) } // TODO: gametyperules - player->growshrinktimer = (gametype == GT_BATTLE ? 8 : 12) * TICRATE; + player->growshrinktimer = max(player->growshrinktimer, (gametype == GT_BATTLE ? 8 : 12) * TICRATE); if (player->invincibilitytimer > 0) { diff --git a/src/objects/shrink.c b/src/objects/shrink.c index 019f2db2a..a4717fb17 100644 --- a/src/objects/shrink.c +++ b/src/objects/shrink.c @@ -412,6 +412,7 @@ boolean Obj_ShrinkLaserCollide(mobj_t *gun, mobj_t *victim) { mobj_t *pohbee = gun_pohbee(gun); mobj_t *owner = NULL; + INT32 prevTimer = 0; if (pohbee == NULL || P_MobjWasRemoved(pohbee) == true) { @@ -423,44 +424,60 @@ boolean Obj_ShrinkLaserCollide(mobj_t *gun, mobj_t *victim) return true; } + if (victim->player->shrinkLaserDelay > 0) + { + victim->player->shrinkLaserDelay = TICRATE; + return true; + } + + victim->player->shrinkLaserDelay = TICRATE; + owner = pohbee_owner(pohbee); + prevTimer = victim->player->growshrinktimer; if (owner != NULL && victim == owner) { // Belongs to us. Give us Grow! - if (victim->player->growshrinktimer <= 0) + if (prevTimer < 0) { - victim->scalespeed = mapobjectscale/TICRATE; - victim->destscale = FixedMul(mapobjectscale, GROW_SCALE); - - if (K_PlayerShrinkCheat(victim->player) == true) - { - victim->destscale = FixedMul(victim->destscale, SHRINK_SCALE); - } - - // TODO: gametyperules - victim->player->growshrinktimer = (gametype == GT_BATTLE ? 8 : 12) * TICRATE; - - if (victim->player->invincibilitytimer > 0) - { - ; // invincibility has priority in P_RestoreMusic, no point in starting here - } - else if (P_IsLocalPlayer(victim->player) == true) - { - S_ChangeMusicSpecial("kgrow"); - } - else //used to be "if (P_IsDisplayPlayer(victim->player) == false)" - { - S_StartSound(victim, (cv_kartinvinsfx.value ? sfx_alarmg : sfx_kgrow)); - } - - P_RestoreMusic(victim->player); + // Take away Shrink. + K_RemoveGrowShrink(victim->player); + } + else + { + victim->player->growshrinktimer += 5*TICRATE; S_StartSound(victim, sfx_kc5a); + + if (prevTimer <= 0) + { + victim->scalespeed = mapobjectscale/TICRATE; + victim->destscale = FixedMul(mapobjectscale, GROW_SCALE); + + if (K_PlayerShrinkCheat(victim->player) == true) + { + victim->destscale = FixedMul(victim->destscale, SHRINK_SCALE); + } + + if (victim->player->invincibilitytimer > 0) + { + ; // invincibility has priority in P_RestoreMusic, no point in starting here + } + else if (P_IsLocalPlayer(victim->player) == true) + { + S_ChangeMusicSpecial("kgrow"); + } + else //used to be "if (P_IsDisplayPlayer(victim->player) == false)" + { + S_StartSound(victim, (cv_kartinvinsfx.value ? sfx_alarmg : sfx_kgrow)); + } + + P_RestoreMusic(victim->player); + } } } else { - if (victim->player->growshrinktimer > 0) + if (prevTimer > 0) { // Take away Grow. K_RemoveGrowShrink(victim->player); @@ -468,18 +485,21 @@ boolean Obj_ShrinkLaserCollide(mobj_t *gun, mobj_t *victim) else { // Start shrinking! - K_DropItems(victim->player); - victim->player->growshrinktimer = -(15*TICRATE); - - victim->scalespeed = mapobjectscale/TICRATE; - victim->destscale = FixedMul(mapobjectscale, SHRINK_SCALE); - - if (K_PlayerShrinkCheat(victim->player) == true) - { - victim->destscale = FixedMul(victim->destscale, SHRINK_SCALE); - } - + victim->player->growshrinktimer -= 5*TICRATE; S_StartSound(victim, sfx_kc59); + + if (prevTimer >= 0) + { + K_DropItems(victim->player); + + victim->scalespeed = mapobjectscale/TICRATE; + victim->destscale = FixedMul(mapobjectscale, SHRINK_SCALE); + + if (K_PlayerShrinkCheat(victim->player) == true) + { + victim->destscale = FixedMul(victim->destscale, SHRINK_SCALE); + } + } } } diff --git a/src/p_saveg.c b/src/p_saveg.c index f35ddc305..e1a75248d 100644 --- a/src/p_saveg.c +++ b/src/p_saveg.c @@ -367,6 +367,8 @@ static void P_NetArchivePlayers(void) WRITEUINT8(save_p, players[i].stairjank); + WRITEUINT8(save_p, players[i].shrinkLaserDelay); + // respawnvars_t WRITEUINT8(save_p, players[i].respawn.state); WRITEUINT32(save_p, K_GetWaypointHeapIndex(players[i].respawn.wp)); @@ -654,6 +656,8 @@ static void P_NetUnArchivePlayers(void) players[i].stairjank = READUINT8(save_p); + players[i].shrinkLaserDelay = READUINT8(save_p); + // respawnvars_t players[i].respawn.state = READUINT8(save_p); players[i].respawn.wp = (waypoint_t *)(size_t)READUINT32(save_p); From ed61140504bde19a804899137ee0c6e70d4ab1bb Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Wed, 14 Sep 2022 05:13:26 -0400 Subject: [PATCH 096/114] Funny mode fixes --- src/d_clisrv.c | 2 +- src/d_netcmd.c | 2 +- src/d_netcmd.h | 1 + src/p_saveg.c | 4 ++++ 4 files changed, 7 insertions(+), 2 deletions(-) diff --git a/src/d_clisrv.c b/src/d_clisrv.c index a7193327d..f89fccf20 100644 --- a/src/d_clisrv.c +++ b/src/d_clisrv.c @@ -5531,7 +5531,7 @@ boolean TryRunTics(tic_t realtics) ps_tictime = I_GetPreciseTime(); G_Ticker((gametic % NEWTICRATERATIO) == 0); - if (gametic % TICRATE == 0) + if (Playing() && (gametic % TICRATE == 0)) { Schedule_Run(); diff --git a/src/d_netcmd.c b/src/d_netcmd.c index 62bc4ee29..eb6e630c0 100644 --- a/src/d_netcmd.c +++ b/src/d_netcmd.c @@ -604,7 +604,7 @@ const char *automate_names[AEV__MAX] = "VoteStart" // AEV_VOTESTART }; -static UINT32 livestudioaudience_timer = 90; +UINT32 livestudioaudience_timer = 90; /// \warning Keep this up-to-date if you add/remove/rename net text commands const char *netxcmdnames[MAXNETXCMD - 1] = diff --git a/src/d_netcmd.h b/src/d_netcmd.h index 7125e3b27..3a67fc458 100644 --- a/src/d_netcmd.h +++ b/src/d_netcmd.h @@ -276,6 +276,7 @@ void Automate_Run(automateEvents_t type); void Automate_Set(automateEvents_t type, const char *command); void Automate_Clear(void); +extern UINT32 livestudioaudience_timer; void LiveStudioAudience(void); // used for the player setup menu diff --git a/src/p_saveg.c b/src/p_saveg.c index f35ddc305..be02b13c6 100644 --- a/src/p_saveg.c +++ b/src/p_saveg.c @@ -4553,6 +4553,8 @@ static void P_NetArchiveMisc(boolean resending) else WRITEUINT8(save_p, 0x2e); + WRITEUINT32(save_p, livestudioaudience_timer); + // Only the server uses this, but it // needs synched for remote admins anyway. WRITEUINT32(save_p, schedule_len); @@ -4712,6 +4714,8 @@ static inline boolean P_NetUnArchiveMisc(boolean reloading) if (READUINT8(save_p) == 0x2f) paused = true; + livestudioaudience_timer = READUINT32(save_p); + // Only the server uses this, but it // needs synched for remote admins anyway. Schedule_Clear(); From 81a4cb17c0f66c8d835279adc68d68b8ccd5b4d3 Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Wed, 14 Sep 2022 05:14:54 -0400 Subject: [PATCH 097/114] Only do it in netgames --- src/d_clisrv.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/d_clisrv.c b/src/d_clisrv.c index f89fccf20..0f1f39fa7 100644 --- a/src/d_clisrv.c +++ b/src/d_clisrv.c @@ -5531,7 +5531,7 @@ boolean TryRunTics(tic_t realtics) ps_tictime = I_GetPreciseTime(); G_Ticker((gametic % NEWTICRATERATIO) == 0); - if (Playing() && (gametic % TICRATE == 0)) + if (Playing() && netgame && (gametic % TICRATE == 0)) { Schedule_Run(); From c65e5af718d8901da58c55adfc356634627a4af2 Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Wed, 14 Sep 2022 19:15:42 -0400 Subject: [PATCH 098/114] Reduce grow add from lasers --- src/objects/shrink.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/objects/shrink.c b/src/objects/shrink.c index a4717fb17..4595417a7 100644 --- a/src/objects/shrink.c +++ b/src/objects/shrink.c @@ -445,7 +445,7 @@ boolean Obj_ShrinkLaserCollide(mobj_t *gun, mobj_t *victim) } else { - victim->player->growshrinktimer += 5*TICRATE; + victim->player->growshrinktimer += 3*TICRATE; S_StartSound(victim, sfx_kc5a); if (prevTimer <= 0) From cee2c25e274a5eceb12b63a51e948e84a069f486 Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Wed, 14 Sep 2022 19:17:45 -0400 Subject: [PATCH 099/114] Don't drop items from Shrink --- src/objects/shrink.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/objects/shrink.c b/src/objects/shrink.c index 4595417a7..c76f3b0d3 100644 --- a/src/objects/shrink.c +++ b/src/objects/shrink.c @@ -490,7 +490,7 @@ boolean Obj_ShrinkLaserCollide(mobj_t *gun, mobj_t *victim) if (prevTimer >= 0) { - K_DropItems(victim->player); + //K_DropItems(victim->player); victim->scalespeed = mapobjectscale/TICRATE; victim->destscale = FixedMul(mapobjectscale, SHRINK_SCALE); From 72948ad49d37bbbedc6de7eaad8004fb772f2665 Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Wed, 14 Sep 2022 23:50:48 -0400 Subject: [PATCH 100/114] Make timer more obvious on grow particles --- src/info.c | 6 +++--- src/k_kart.c | 17 ++++++++++++++++- 2 files changed, 19 insertions(+), 4 deletions(-) diff --git a/src/info.c b/src/info.c index 7f1840092..4da0d27e1 100644 --- a/src/info.c +++ b/src/info.c @@ -4316,7 +4316,7 @@ state_t states[NUMSTATES] = {SPR_HYUU, FF_FULLBRIGHT, -1, {NULL}, 0, 0, S_NULL}, // S_HYUDORO - {SPR_GRWP, FF_ADD|FF_FULLBRIGHT|FF_ANIMATE, 13, {NULL}, 7, 1, S_NULL}, // S_GROW_PARTICLE + {SPR_GRWP, FF_FULLBRIGHT|FF_ANIMATE, 13, {NULL}, 7, 1, S_NULL}, // S_GROW_PARTICLE {SPR_SHRG, 0, -1, {NULL}, 0, 0, S_NULL}, // S_SHRINK_GUN {SPR_SHRG, FF_FULLBRIGHT|1, -1, {NULL}, 0, 0, S_NULL}, // S_SHRINK_LASER @@ -24121,7 +24121,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] = 0, // mass 0, // damage sfx_None, // activesound - MF_SPECIAL|MF_NOCLIPHEIGHT|MF_NOGRAVITY|MF_DONTENCOREMAP, // flags + MF_SPECIAL|MF_NOCLIP|MF_NOCLIPHEIGHT|MF_NOGRAVITY|MF_DONTENCOREMAP, // flags S_NULL // raisestate }, @@ -24202,7 +24202,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] = 0, // mass 0, // damage sfx_None, // activesound - MF_SPECIAL|MF_NOCLIPHEIGHT|MF_NOGRAVITY|MF_DONTENCOREMAP, // flags + MF_SPECIAL|MF_NOCLIP|MF_NOCLIPHEIGHT|MF_NOGRAVITY|MF_DONTENCOREMAP, // flags S_NULL // raisestate }, diff --git a/src/k_kart.c b/src/k_kart.c index cef0a644e..de17e614a 100644 --- a/src/k_kart.c +++ b/src/k_kart.c @@ -2288,12 +2288,27 @@ static void K_SpawnGrowShrinkParticles(mobj_t *mo, INT32 timer) { const boolean shrink = (timer < 0); const INT32 maxTime = (10*TICRATE); + const INT32 noTime = (2*TICRATE); INT32 spawnFreq = 1; mobj_t *particle = NULL; fixed_t particleSpeed = 0; - spawnFreq = (maxTime - min(maxTime, abs(timer))) / TICRATE / 2; + spawnFreq = abs(timer); + + if (spawnFreq < noTime) + { + return; + } + + spawnFreq -= noTime; + + if (spawnFreq > maxTime) + { + spawnFreq = maxTime; + } + + spawnFreq = (maxTime - spawnFreq) / TICRATE / 4; if (spawnFreq == 0) { spawnFreq++; From 71723a803db90c9c723ff65dd73206350a0176e5 Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Thu, 15 Sep 2022 01:03:08 -0400 Subject: [PATCH 101/114] Implement pohbee chain Crashes when drawing the sprites after looking at it for like half a second and idk why tf --- src/deh_tables.c | 1 + src/info.c | 10 ++++++---- src/info.h | 2 ++ src/objects/shrink.c | 42 +++++++++++++++++++++++++++++++++++++----- 4 files changed, 46 insertions(+), 9 deletions(-) diff --git a/src/deh_tables.c b/src/deh_tables.c index 8a2713bad..5fe950e9e 100644 --- a/src/deh_tables.c +++ b/src/deh_tables.c @@ -3760,6 +3760,7 @@ const char *const STATE_LIST[] = { // array length left dynamic for sanity testi // Shrink "S_SHRINK_GUN", + "S_SHRINK_CHAIN", "S_SHRINK_LASER", "S_SHRINK_PARTICLE", diff --git a/src/info.c b/src/info.c index 4da0d27e1..186f04022 100644 --- a/src/info.c +++ b/src/info.c @@ -574,6 +574,7 @@ char sprnames[NUMSPRITES + 1][5] = "FLMF", // Flame Shield flash "HYUU", // Hyudoro "GRWP", // Grow + "POHB", // Shrink Poh-Bee "SHRG", // Shrink gun / laser "SINK", // Kitchen Sink "SITR", // Kitchen Sink Trail @@ -4319,6 +4320,7 @@ state_t states[NUMSTATES] = {SPR_GRWP, FF_FULLBRIGHT|FF_ANIMATE, 13, {NULL}, 7, 1, S_NULL}, // S_GROW_PARTICLE {SPR_SHRG, 0, -1, {NULL}, 0, 0, S_NULL}, // S_SHRINK_GUN + {SPR_POHB, 0, -1, {NULL}, 0, 0, S_NULL}, // S_SHRINK_CHAIN {SPR_SHRG, FF_FULLBRIGHT|1, -1, {NULL}, 0, 0, S_NULL}, // S_SHRINK_LASER {SPR_SHRG, FF_FULLBRIGHT|2, -1, {NULL}, 0, 0, S_NULL}, // S_SHRINK_PARTICLE @@ -24127,7 +24129,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] = { // MT_SHRINK_CHAIN -1, // doomednum - S_SHRINK_GUN, // spawnstate + S_SHRINK_CHAIN, // spawnstate 1000, // spawnhealth S_NULL, // seestate sfx_None, // seesound @@ -24142,13 +24144,13 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] = S_NULL, // xdeathstate sfx_None, // deathsound 0, // speed - 16*FRACUNIT, // radius - 120*FRACUNIT, // height + 26*FRACUNIT, // radius + 26*FRACUNIT, // height 0, // display offset 0, // mass 0, // damage sfx_None, // activesound - MF_NOTHINK|MF_NOCLIP|MF_NOCLIPTHING|MF_NOCLIPHEIGHT|MF_NOGRAVITY|MF_DONTENCOREMAP, // flags + MF_SCENERY|MF_NOCLIP|MF_NOCLIPTHING|MF_NOCLIPHEIGHT|MF_NOGRAVITY|MF_DONTENCOREMAP, // flags S_NULL // raisestate }, diff --git a/src/info.h b/src/info.h index ca7264dba..8acf73ba8 100644 --- a/src/info.h +++ b/src/info.h @@ -1120,6 +1120,7 @@ typedef enum sprite SPR_FLMF, // Flame Shield flash SPR_HYUU, // Hyudoro SPR_GRWP, // Grow + SPR_POHB, // Shrink Poh-Bee SPR_SHRG, // Shrink gun / laser SPR_SINK, // Kitchen Sink SPR_SITR, // Kitchen Sink Trail @@ -4752,6 +4753,7 @@ typedef enum state // Shrink S_SHRINK_GUN, + S_SHRINK_CHAIN, S_SHRINK_LASER, S_SHRINK_PARTICLE, diff --git a/src/objects/shrink.c b/src/objects/shrink.c index c76f3b0d3..2272c2909 100644 --- a/src/objects/shrink.c +++ b/src/objects/shrink.c @@ -31,7 +31,7 @@ #define GUN_SWING (ANGLE_90 - ANG10) #define GUN_SWINGTIME (4 * TICRATE) -#define CHAIN_SIZE (16) +#define CHAIN_SIZE (52) #define EXTRA_FOR_FIRST (7) @@ -58,6 +58,8 @@ enum #define gun_laser(o) ((o)->tracer) #define gun_chains(o) ((o)->hprev) +#define chain_index(o) ((o)->extravalue1) + enum { LASER_SHRINK, @@ -333,6 +335,34 @@ static void ShrinkLaserThinker(mobj_t *pohbee, mobj_t *gun, mobj_t *laser) } } +static void DoGunChains(mobj_t *gun, mobj_t *pohbee) +{ + const fixed_t gunZ = P_GetMobjHead(gun); + const fixed_t beeZ = P_GetMobjFeet(pohbee); + + const fixed_t offsetX = (pohbee->x - gun->x) / gun_numsegs(gun); + const fixed_t offsetY = (pohbee->y - gun->y) / gun_numsegs(gun); + const fixed_t offsetZ = (beeZ - gunZ) / gun_numsegs(gun); + + mobj_t *chain = NULL; + + fixed_t curX = gun->x + (offsetX / 2); + fixed_t curY = gun->y + (offsetY / 2); + fixed_t curZ = gunZ + (offsetZ / 2); + + chain = gun_chains(gun); + while (chain != NULL && P_MobjWasRemoved(chain) == false) + { + PohbeeMoveTo(chain, curX, curY, curZ); + + curX += offsetX; + curY += offsetY; + curZ += offsetZ; + + chain = gun_chains(chain); + } +} + static void ShrinkGunThinker(mobj_t *gun) { mobj_t *pohbee = gun_pohbee(gun); @@ -352,6 +382,8 @@ static void ShrinkGunThinker(mobj_t *gun) { ShrinkLaserThinker(pohbee, gun, gun_laser(gun)); } + + DoGunChains(gun, pohbee); } void Obj_PohbeeThinker(mobj_t *pohbee) @@ -612,7 +644,7 @@ static void CreatePohbee(player_t *owner, waypoint_t *start, waypoint_t *end, UI mobj_t *gun = P_SpawnMobjFromMobj(pohbee, 0, 0, 0, MT_SHRINK_GUN); mobj_t *laser = NULL; - //mobj_t *prevChain = NULL; + mobj_t *prevChain = NULL; P_SetTarget(&gun_pohbee(gun), pohbee); P_SetTarget(&pohbee_guns(prevGun), gun); @@ -623,16 +655,16 @@ static void CreatePohbee(player_t *owner, waypoint_t *start, waypoint_t *end, UI laser = P_SpawnMobjFromMobj(gun, 0, 0, 0, MT_SHRINK_LASER); P_SetTarget(&gun_laser(gun), laser); - /* prevChain = gun; for (j = 0; j < numSegs; j++) { mobj_t *chain = P_SpawnMobjFromMobj(gun, 0, 0, 0, MT_SHRINK_CHAIN); + P_SetTarget(&gun_chains(prevChain), chain); + chain_index(chain) = j; + prevChain = chain; } - */ - (void)j; prevGun = gun; } From ef0ff01ef133740fa025146172a0fe48cb54eb1d Mon Sep 17 00:00:00 2001 From: James R Date: Wed, 14 Sep 2022 22:38:12 -0700 Subject: [PATCH 102/114] FixedDiv2: divide numerator before taking absolute value to avoid INT32_MIN overflow to 0 --- src/m_fixed.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/m_fixed.h b/src/m_fixed.h index 9f3bb2910..021f84d89 100644 --- a/src/m_fixed.h +++ b/src/m_fixed.h @@ -204,7 +204,7 @@ FUNCMATH FUNCINLINE static ATTRINLINE fixed_t FixedInt(fixed_t a) */ FUNCMATH FUNCINLINE static ATTRINLINE fixed_t FixedDiv(fixed_t a, fixed_t b) { - if ((abs(a) >> (FRACBITS-2)) >= abs(b)) + if ((abs(a / (FRACUNIT/4))) >= abs(b)) return (a^b) < 0 ? INT32_MIN : INT32_MAX; return FixedDiv2(a, b); From e2e984ec145031c3998d9ec787efde626d95bced Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Thu, 15 Sep 2022 01:54:19 -0400 Subject: [PATCH 103/114] More consistent chain length --- src/objects/shrink.c | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/src/objects/shrink.c b/src/objects/shrink.c index 2272c2909..3120b1540 100644 --- a/src/objects/shrink.c +++ b/src/objects/shrink.c @@ -615,18 +615,12 @@ static void CreatePohbee(player_t *owner, waypoint_t *start, waypoint_t *end, UI // Calculate number of chain segments added per laser. size = FixedMul(end->mobj->radius, 3*FRACUNIT/2); - baseSegs = 1 + ((size / start->mobj->scale) / CHAIN_SIZE); - - if (baseSegs < MAXPLAYERS) - { - baseSegs = MAXPLAYERS; - } - - segVal = baseSegs / numLasers; + segVal = max(1, 1 + ((size / start->mobj->scale) / CHAIN_SIZE) / numLasers); + baseSegs = segVal * numLasers; // Valid spawning conditions, // we can start creating each individual part. - pohbee = P_SpawnMobjFromMobj(start->mobj, 0, 0, FixedDiv(size, mapobjectscale) + POHBEE_HOVER * 3, MT_SHRINK_POHBEE); + pohbee = P_SpawnMobjFromMobj(start->mobj, 0, 0, (baseSegs * CHAIN_SIZE * FRACUNIT) + POHBEE_HOVER * 3, MT_SHRINK_POHBEE); P_SetTarget(&pohbee_owner(pohbee), owner->mo); pohbee_mode(pohbee) = POHBEE_MODE_SPAWN; From 6af7df8d4022ff80a34258c754de0151f96707e3 Mon Sep 17 00:00:00 2001 From: James R Date: Wed, 14 Sep 2022 23:02:55 -0700 Subject: [PATCH 104/114] Fix -Wsign-compare --- src/k_bot.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/k_bot.c b/src/k_bot.c index ca3aaa2c3..968ba8632 100644 --- a/src/k_bot.c +++ b/src/k_bot.c @@ -657,7 +657,7 @@ static botprediction_t *K_CreateBotPrediction(player_t *player) const INT32 startDist = (DEFAULT_WAYPOINT_RADIUS * 2 * mapobjectscale) / FRACUNIT; const INT32 maxDist = startDist * 4; // This function gets very laggy when it goes far distances, and going too far isn't very helpful anyway. - const INT32 distance = min(((speed / FRACUNIT) * futuresight) + startDist, maxDist); + const INT32 distance = min(((speed / FRACUNIT) * (INT32)futuresight) + startDist, maxDist); // Halves radius when encountering a wall on your way to your destination. fixed_t radreduce = FRACUNIT; From dc44196c49ffa59845ac8f711b331cfc4c8ab423 Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Thu, 15 Sep 2022 02:27:20 -0400 Subject: [PATCH 105/114] Bigger laser shimmer --- src/objects/shrink.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/objects/shrink.c b/src/objects/shrink.c index 3120b1540..841875fc8 100644 --- a/src/objects/shrink.c +++ b/src/objects/shrink.c @@ -303,7 +303,7 @@ static void ShrinkLaserThinker(mobj_t *pohbee, mobj_t *gun, mobj_t *laser) if (leveltime & 1) { - laser->spritexscale = 3*FRACUNIT/2; + laser->spritexscale = 5*FRACUNIT/2; } else { From b1f9dd8264bf69679101c212e4987f8c3983065c Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Thu, 15 Sep 2022 02:42:16 -0400 Subject: [PATCH 106/114] Line up the laser visual better --- src/objects/shrink.c | 25 +++++++++++++++++-------- 1 file changed, 17 insertions(+), 8 deletions(-) diff --git a/src/objects/shrink.c b/src/objects/shrink.c index 841875fc8..8fcf1eb58 100644 --- a/src/objects/shrink.c +++ b/src/objects/shrink.c @@ -292,7 +292,11 @@ static void DoGunSwing(mobj_t *gun, mobj_t *pohbee) static void ShrinkLaserThinker(mobj_t *pohbee, mobj_t *gun, mobj_t *laser) { - PohbeeMoveTo(laser, gun->x, gun->y, gun->floorz); + const fixed_t gunX = gun->x + gun->momx; + const fixed_t gunY = gun->y + gun->momy; + const fixed_t gunZ = P_GetMobjFeet(gun) + gun->momz; + + PohbeeMoveTo(laser, gunX, gunY, gun->floorz); if (ShrinkLaserActive(pohbee) == true) { @@ -310,7 +314,7 @@ static void ShrinkLaserThinker(mobj_t *pohbee, mobj_t *gun, mobj_t *laser) laser->spritexscale = FRACUNIT; } - laser->spriteyscale = FixedDiv(FixedDiv(gun->z - gun->floorz, mapobjectscale), laser->info->height); + laser->spriteyscale = FixedDiv(FixedDiv(gunZ - gun->floorz, mapobjectscale), laser->info->height); particle = P_SpawnMobjFromMobj( laser, @@ -337,17 +341,22 @@ static void ShrinkLaserThinker(mobj_t *pohbee, mobj_t *gun, mobj_t *laser) static void DoGunChains(mobj_t *gun, mobj_t *pohbee) { - const fixed_t gunZ = P_GetMobjHead(gun); - const fixed_t beeZ = P_GetMobjFeet(pohbee); + const fixed_t gunX = gun->x + gun->momx; + const fixed_t gunY = gun->y + gun->momy; + const fixed_t gunZ = P_GetMobjHead(gun) + gun->momz; - const fixed_t offsetX = (pohbee->x - gun->x) / gun_numsegs(gun); - const fixed_t offsetY = (pohbee->y - gun->y) / gun_numsegs(gun); + const fixed_t beeX = pohbee->x + pohbee->momx; + const fixed_t beeY = pohbee->y + pohbee->momy; + const fixed_t beeZ = P_GetMobjFeet(pohbee) + pohbee->momz; + + const fixed_t offsetX = (beeX - gunX) / gun_numsegs(gun); + const fixed_t offsetY = (beeY - gunY) / gun_numsegs(gun); const fixed_t offsetZ = (beeZ - gunZ) / gun_numsegs(gun); mobj_t *chain = NULL; - fixed_t curX = gun->x + (offsetX / 2); - fixed_t curY = gun->y + (offsetY / 2); + fixed_t curX = gunX + (offsetX / 2); + fixed_t curY = gunY + (offsetY / 2); fixed_t curZ = gunZ + (offsetZ / 2); chain = gun_chains(gun); From 3950a228c0998bd6d279c242adb2cb9c29b8b4d6 Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Thu, 15 Sep 2022 02:51:53 -0400 Subject: [PATCH 107/114] Lower shrink odds slightly --- src/k_kart.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/k_kart.c b/src/k_kart.c index de17e614a..3978fe3cd 100644 --- a/src/k_kart.c +++ b/src/k_kart.c @@ -365,7 +365,7 @@ static INT32 K_KartItemOddsRace[NUMKARTRESULTS-1][8] = /*Ballhog*/ { 0, 0, 2, 2, 0, 0, 0, 0 }, // Ballhog /*Self-Propelled Bomb*/ { 0, 0, 0, 0, 0, 2, 4, 0 }, // Self-Propelled Bomb /*Grow*/ { 0, 0, 0, 1, 2, 3, 0, 0 }, // Grow - /*Shrink*/ { 0, 0, 0, 0, 0, 2, 4, 2 }, // Shrink + /*Shrink*/ { 0, 0, 0, 0, 0, 1, 3, 2 }, // Shrink /*Lightning Shield*/ { 1, 0, 0, 0, 0, 0, 0, 0 }, // Lightning Shield /*Bubble Shield*/ { 0, 1, 2, 1, 0, 0, 0, 0 }, // Bubble Shield /*Flame Shield*/ { 0, 0, 0, 0, 0, 1, 3, 5 }, // Flame Shield @@ -375,7 +375,7 @@ static INT32 K_KartItemOddsRace[NUMKARTRESULTS-1][8] = /*Kitchen Sink*/ { 0, 0, 0, 0, 0, 0, 0, 0 }, // Kitchen Sink /*Drop Target*/ { 3, 0, 0, 0, 0, 0, 0, 0 }, // Drop Target /*Sneaker x2*/ { 0, 0, 2, 2, 2, 0, 0, 0 }, // Sneaker x2 - /*Sneaker x3*/ { 0, 0, 0, 1, 6, 8, 4, 0 }, // Sneaker x3 + /*Sneaker x3*/ { 0, 0, 0, 1, 6, 9, 5, 0 }, // Sneaker x3 /*Banana x3*/ { 0, 1, 1, 0, 0, 0, 0, 0 }, // Banana x3 /*Banana x10*/ { 0, 0, 0, 1, 0, 0, 0, 0 }, // Banana x10 /*Orbinaut x3*/ { 0, 0, 1, 0, 0, 0, 0, 0 }, // Orbinaut x3 From 04323fc39e61bcb58c60891574c14c585faa7770 Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Thu, 15 Sep 2022 03:19:57 -0400 Subject: [PATCH 108/114] Adjust Grow/Shrink camera with scale again --- src/p_user.c | 23 +++++++++++++++-------- 1 file changed, 15 insertions(+), 8 deletions(-) diff --git a/src/p_user.c b/src/p_user.c index 78d20a794..299d42a4b 100644 --- a/src/p_user.c +++ b/src/p_user.c @@ -3044,6 +3044,10 @@ boolean P_MoveChaseCamera(player_t *player, camera_t *thiscam, boolean resetcall subsector_t *newsubsec; #endif + fixed_t playerScale = FixedDiv(player->mo->scale, mapobjectscale); + fixed_t scaleDiff = playerScale - FRACUNIT; + fixed_t cameraScale = mapobjectscale; + thiscam->old_x = thiscam->x; thiscam->old_y = thiscam->y; thiscam->old_z = thiscam->z; @@ -3132,8 +3136,11 @@ boolean P_MoveChaseCamera(player_t *player, camera_t *thiscam, boolean resetcall return true; } - thiscam->radius = 20*mapobjectscale; - thiscam->height = 16*mapobjectscale; + // Adjust camera to match Grow/Shrink + cameraScale = FixedMul(cameraScale, FRACUNIT + (scaleDiff / 3)); + + thiscam->radius = 20*cameraScale; + thiscam->height = 16*cameraScale; // Don't run while respawning from a starpost // Inu 4/8/13 Why not?! @@ -3159,8 +3166,8 @@ boolean P_MoveChaseCamera(player_t *player, camera_t *thiscam, boolean resetcall camspeed = cv_cam_speed[num].value; camstill = cv_cam_still[num].value; camrotate = cv_cam_rotate[num].value; - camdist = FixedMul(cv_cam_dist[num].value, mapobjectscale); - camheight = FixedMul(cv_cam_height[num].value, mapobjectscale); + camdist = FixedMul(cv_cam_dist[num].value, cameraScale); + camheight = FixedMul(cv_cam_height[num].value, cameraScale); if (timeover) { @@ -3171,8 +3178,8 @@ boolean P_MoveChaseCamera(player_t *player, camera_t *thiscam, boolean resetcall { const INT32 introcam = (introtime - leveltime); camrotate += introcam*5; - camdist += (introcam * mapobjectscale)*3; - camheight += (introcam * mapobjectscale)*2; + camdist += (introcam * cameraScale)*3; + camheight += (introcam * cameraScale)*2; } else if (player->exiting) // SRB2Kart: Leave the camera behind while exiting, for dramatic effect! camstill = true; @@ -3236,7 +3243,7 @@ boolean P_MoveChaseCamera(player_t *player, camera_t *thiscam, boolean resetcall // sets ideal cam pos { - const fixed_t speedthreshold = 48*mapobjectscale; + const fixed_t speedthreshold = 48*cameraScale; const fixed_t olddist = P_AproxDistance(mo->x - thiscam->x, mo->y - thiscam->y); fixed_t lag, distoffset; @@ -3541,7 +3548,7 @@ boolean P_MoveChaseCamera(player_t *player, camera_t *thiscam, boolean resetcall // point viewed by the camera // this point is just 64 unit forward the player - dist = 64*mapobjectscale; + dist = 64*cameraScale; viewpointx = mo->x + FixedMul(FINECOSINE((angle>>ANGLETOFINESHIFT) & FINEMASK), dist) + xpan; viewpointy = mo->y + FixedMul(FINESINE((angle>>ANGLETOFINESHIFT) & FINEMASK), dist) + ypan; From c80f46f7c79a9c390e08f18531b53a045a68e6be Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Thu, 15 Sep 2022 19:52:35 -0400 Subject: [PATCH 109/114] Reduce code duplication --- src/objects/shrink.c | 35 +++++++++++------------------------ 1 file changed, 11 insertions(+), 24 deletions(-) diff --git a/src/objects/shrink.c b/src/objects/shrink.c index 8fcf1eb58..bc99420be 100644 --- a/src/objects/shrink.c +++ b/src/objects/shrink.c @@ -547,11 +547,9 @@ boolean Obj_ShrinkLaserCollide(mobj_t *gun, mobj_t *victim) return true; } -static waypoint_t *GetPohbeeStart(waypoint_t *anchor) +static waypoint_t *GetPohbeeWaypoint(waypoint_t *anchor, const UINT32 traveldist, const boolean huntbackwards) { - const UINT32 traveldist = FixedMul(POHBEE_DIST >> 1, mapobjectscale) / FRACUNIT; const boolean useshortcuts = false; - const boolean huntbackwards = true; boolean pathfindsuccess = false; path_t pathtofinish = {0}; waypoint_t *ret = NULL; @@ -573,34 +571,23 @@ static waypoint_t *GetPohbeeStart(waypoint_t *anchor) } return ret; + +} + +static waypoint_t *GetPohbeeStart(waypoint_t *anchor) +{ + const UINT32 traveldist = FixedMul(POHBEE_DIST >> 1, mapobjectscale) / FRACUNIT; + const boolean huntbackwards = true; + + return GetPohbeeWaypoint(anchor, traveldist, huntbackwards); } static waypoint_t *GetPohbeeEnd(waypoint_t *anchor) { const UINT32 traveldist = FixedMul(POHBEE_DIST, mapobjectscale) / FRACUNIT; - const boolean useshortcuts = false; const boolean huntbackwards = false; - boolean pathfindsuccess = false; - path_t pathtofinish = {0}; - waypoint_t *ret = NULL; - pathfindsuccess = K_PathfindThruCircuit( - anchor, traveldist, - &pathtofinish, - useshortcuts, huntbackwards - ); - - if (pathfindsuccess == true) - { - ret = (waypoint_t *)pathtofinish.array[ pathtofinish.numnodes - 1 ].nodedata; - Z_Free(pathtofinish.array); - } - else - { - ret = anchor; - } - - return ret; + return GetPohbeeWaypoint(anchor, traveldist, huntbackwards); } static void CreatePohbee(player_t *owner, waypoint_t *start, waypoint_t *end, UINT8 numLasers) From eda9e0cd9d9845ce00b8b922f476f2b6763e8792 Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Thu, 15 Sep 2022 19:54:50 -0400 Subject: [PATCH 110/114] Offset for shrink particles --- src/k_kart.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/k_kart.c b/src/k_kart.c index 3978fe3cd..fb068f4f2 100644 --- a/src/k_kart.c +++ b/src/k_kart.c @@ -2323,7 +2323,7 @@ static void K_SpawnGrowShrinkParticles(mobj_t *mo, INT32 timer) mo, P_RandomRange(-32, 32) * FRACUNIT, P_RandomRange(-32, 32) * FRACUNIT, - (shrink ? P_RandomRange(24, 48) : P_RandomRange(0, 24)) * FRACUNIT, + P_RandomRange(0, 24) + (shrink ? 24 : 0) * FRACUNIT, MT_GROW_PARTICLE ); From cb2d2cb2ba27b60f20e4455bb9a94fedc612a90d Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Thu, 15 Sep 2022 20:03:32 -0400 Subject: [PATCH 111/114] Cleanup objects properly --- src/k_objects.h | 2 ++ src/objects/shrink.c | 28 +++++++++++++++++++--------- src/p_mobj.c | 10 ++++++++++ 3 files changed, 31 insertions(+), 9 deletions(-) diff --git a/src/k_objects.h b/src/k_objects.h index 45a487c5a..cc80d2555 100644 --- a/src/k_objects.h +++ b/src/k_objects.h @@ -10,6 +10,8 @@ void Obj_HyudoroCollide(mobj_t *special, mobj_t *toucher); /* Shrink */ void Obj_PohbeeThinker(mobj_t *pohbee); +void Obj_PohbeeRemoved(mobj_t *pohbee); +void Obj_ShrinkGunRemoved(mobj_t *gun); boolean Obj_ShrinkLaserCollide(mobj_t *gun, mobj_t *victim); void Obj_CreateShrinkPohbees(player_t *owner); diff --git a/src/objects/shrink.c b/src/objects/shrink.c index bc99420be..a37c93ac3 100644 --- a/src/objects/shrink.c +++ b/src/objects/shrink.c @@ -429,25 +429,35 @@ void Obj_PohbeeThinker(mobj_t *pohbee) } } -/* void Obj_PohbeeRemoved(mobj_t *pohbee) +{ + mobj_t *gun = pohbee_guns(pohbee); + + while (gun != NULL && P_MobjWasRemoved(gun) == false) + { + mobj_t *nextGun = pohbee_guns(gun); + P_RemoveMobj(gun); + gun = nextGun; + } +} + +void Obj_ShrinkGunRemoved(mobj_t *gun) { mobj_t *chain = NULL; - if (pohbee_guns(pohbee) != NULL) + if (gun_laser(gun) != NULL && P_MobjWasRemoved(gun_laser(gun)) == false) { - P_RemoveMobj(pohbee_laser(pohbee)); + P_RemoveMobj(gun_laser(gun)); } - chain = pohbee_chain(pohbee); - while (chain != NULL) + chain = gun_chains(gun); + while (chain != NULL && P_MobjWasRemoved(chain) == false) { - mobj_t *temp = chain; - chain = pohbee_chain(temp); - P_RemoveMobj(temp); + mobj_t *nextChain = gun_chains(chain); + P_RemoveMobj(chain); + chain = nextChain; } } -*/ boolean Obj_ShrinkLaserCollide(mobj_t *gun, mobj_t *victim) { diff --git a/src/p_mobj.c b/src/p_mobj.c index a86d1af4e..0505d6bda 100644 --- a/src/p_mobj.c +++ b/src/p_mobj.c @@ -10656,6 +10656,16 @@ void P_RemoveMobj(mobj_t *mobj) P_SetTarget(&mobj->player->followmobj, NULL); } + if (mobj->type == MT_SHRINK_POHBEE) + { + Obj_PohbeeRemoved(mobj); + } + + if (mobj->type == MT_SHRINK_GUN) + { + Obj_ShrinkGunRemoved(mobj); + } + mobj->health = 0; // Just because // unlink from sector and block lists From 94c64519d4eb319589c5a0a5c51d10bcc0804a2c Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Thu, 15 Sep 2022 21:44:08 -0400 Subject: [PATCH 112/114] Make grow/shrink particles closer to base scale --- src/k_kart.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/k_kart.c b/src/k_kart.c index fb068f4f2..edefc6895 100644 --- a/src/k_kart.c +++ b/src/k_kart.c @@ -2292,6 +2292,7 @@ static void K_SpawnGrowShrinkParticles(mobj_t *mo, INT32 timer) INT32 spawnFreq = 1; mobj_t *particle = NULL; + fixed_t particleScale = FRACUNIT; fixed_t particleSpeed = 0; spawnFreq = abs(timer); @@ -2323,7 +2324,7 @@ static void K_SpawnGrowShrinkParticles(mobj_t *mo, INT32 timer) mo, P_RandomRange(-32, 32) * FRACUNIT, P_RandomRange(-32, 32) * FRACUNIT, - P_RandomRange(0, 24) + (shrink ? 24 : 0) * FRACUNIT, + (P_RandomRange(0, 24) + (shrink ? 48 : 0)) * FRACUNIT, MT_GROW_PARTICLE ); @@ -2335,7 +2336,11 @@ static void K_SpawnGrowShrinkParticles(mobj_t *mo, INT32 timer) K_MatchGenericExtraFlags(particle, mo); - particleSpeed = particle->scale * 4 * P_MobjFlip(mo); + particleScale = FixedMul((shrink ? SHRINK_PHYSICS_SCALE : GROW_PHYSICS_SCALE), mapobjectscale); + particleSpeed = mo->scale * 4 * P_MobjFlip(mo); // NOT particleScale + + particle->destscale = particleScale; + P_SetScale(particle, particle->destscale); if (shrink == true) { From 01b1a9d3a734385723a67aa536d527e39e222a1a Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Thu, 15 Sep 2022 21:44:45 -0400 Subject: [PATCH 113/114] Prevent poh-bees on no-respawn waypoints --- src/k_waypoint.c | 117 +++++++++++++++++++++++++++++++++++++++++++ src/k_waypoint.h | 30 +++++++++++ src/objects/shrink.c | 2 +- 3 files changed, 148 insertions(+), 1 deletion(-) diff --git a/src/k_waypoint.c b/src/k_waypoint.c index 0a8dfd0f0..2c9ebec9a 100644 --- a/src/k_waypoint.c +++ b/src/k_waypoint.c @@ -1101,6 +1101,40 @@ static boolean K_WaypointPathfindReachedGScore(void *data, void *setupData) return scoreReached; } +/*-------------------------------------------------- + static boolean K_WaypointPathfindReachedGScoreSpawnable(void *data, void *setupData) + + Returns if the current waypoint data reaches our end G score. + + Input Arguments:- + data - Should point to a pathfindnode_t to compare + setupData - Should point to the pathfindsetup_t to compare + + Return:- + True if the waypoint reached the G score, false otherwise. +--------------------------------------------------*/ +static boolean K_WaypointPathfindReachedGScoreSpawnable(void *data, void *setupData) +{ + boolean scoreReached = false; + boolean spawnable = false; + + if (data == NULL || setupData == NULL) + { + CONS_Debug(DBG_GAMELOGIC, "K_WaypointPathfindReachedGScoreSpawnable received NULL data.\n"); + } + else + { + pathfindnode_t *node = (pathfindnode_t *)data; + pathfindsetup_t *setup = (pathfindsetup_t *)setupData; + waypoint_t *wp = (waypoint_t *)node->nodedata; + + scoreReached = (node->gscore >= setup->endgscore); + spawnable = K_GetWaypointIsSpawnpoint(wp); + } + + return (scoreReached && spawnable); +} + /*-------------------------------------------------- boolean K_PathfindToWaypoint( waypoint_t *const sourcewaypoint, @@ -1266,6 +1300,89 @@ boolean K_PathfindThruCircuit( return pathfound; } +/*-------------------------------------------------- + boolean K_PathfindThruCircuitSpawnable( + waypoint_t *const sourcewaypoint, + const UINT32 traveldistance, + path_t *const returnpath, + const boolean useshortcuts, + const boolean huntbackwards) + + See header file for description. +--------------------------------------------------*/ +boolean K_PathfindThruCircuitSpawnable( + waypoint_t *const sourcewaypoint, + const UINT32 traveldistance, + path_t *const returnpath, + const boolean useshortcuts, + const boolean huntbackwards) +{ + boolean pathfound = false; + + if (sourcewaypoint == NULL) + { + CONS_Debug(DBG_GAMELOGIC, "NULL sourcewaypoint in K_PathfindThruCircuitSpawnable.\n"); + } + else if (finishline == NULL) + { + CONS_Debug(DBG_GAMELOGIC, "NULL finishline in K_PathfindThruCircuitSpawnable.\n"); + } + else if (((huntbackwards == false) && (sourcewaypoint->numnextwaypoints == 0)) + || ((huntbackwards == true) && (sourcewaypoint->numprevwaypoints == 0))) + { + CONS_Debug(DBG_GAMELOGIC, + "K_PathfindThruCircuitSpawnable: sourcewaypoint with ID %d has no next waypoint\n", + K_GetWaypointID(sourcewaypoint)); + } + else if (((huntbackwards == false) && (finishline->numprevwaypoints == 0)) + || ((huntbackwards == true) && (finishline->numnextwaypoints == 0))) + { + CONS_Debug(DBG_GAMELOGIC, + "K_PathfindThruCircuitSpawnable: finishline with ID %d has no previous waypoint\n", + K_GetWaypointID(finishline)); + } + else + { + pathfindsetup_t pathfindsetup = {0}; + getconnectednodesfunc nextnodesfunc = K_WaypointPathfindGetNext; + getnodeconnectioncostsfunc nodecostsfunc = K_WaypointPathfindGetNextCosts; + getnodeheuristicfunc heuristicfunc = K_WaypointPathfindGetHeuristic; + getnodetraversablefunc traversablefunc = K_WaypointPathfindTraversableNoShortcuts; + getpathfindfinishedfunc finishedfunc = K_WaypointPathfindReachedGScoreSpawnable; + + if (huntbackwards) + { + nextnodesfunc = K_WaypointPathfindGetPrev; + nodecostsfunc = K_WaypointPathfindGetPrevCosts; + } + + if (useshortcuts) + { + traversablefunc = K_WaypointPathfindTraversableAllEnabled; + } + + pathfindsetup.opensetcapacity = K_GetOpensetBaseSize(); + pathfindsetup.closedsetcapacity = K_GetClosedsetBaseSize(); + pathfindsetup.nodesarraycapacity = K_GetNodesArrayBaseSize(); + pathfindsetup.startnodedata = sourcewaypoint; + pathfindsetup.endnodedata = finishline; + pathfindsetup.endgscore = traveldistance; + pathfindsetup.getconnectednodes = nextnodesfunc; + pathfindsetup.getconnectioncosts = nodecostsfunc; + pathfindsetup.getheuristic = heuristicfunc; + pathfindsetup.gettraversable = traversablefunc; + pathfindsetup.getfinished = finishedfunc; + + pathfound = K_PathfindAStar(returnpath, &pathfindsetup); + + K_UpdateOpensetBaseSize(pathfindsetup.opensetcapacity); + K_UpdateClosedsetBaseSize(pathfindsetup.closedsetcapacity); + K_UpdateNodesArrayBaseSize(pathfindsetup.nodesarraycapacity); + } + + return pathfound; +} + /*-------------------------------------------------- waypoint_t *K_GetNextWaypointToDestination( waypoint_t *const sourcewaypoint, diff --git a/src/k_waypoint.h b/src/k_waypoint.h index a2201ff26..1cb659dbe 100644 --- a/src/k_waypoint.h +++ b/src/k_waypoint.h @@ -245,6 +245,36 @@ boolean K_PathfindThruCircuit( const boolean huntbackwards); +/*-------------------------------------------------- + boolean K_PathfindThruCircuitSpawnable( + waypoint_t *const sourcewaypoint, + const UINT32 traveldistance, + path_t *const returnpath, + const boolean useshortcuts, + const boolean huntbackwards) + + The same as K_PathfindThruCircuit, but continues until hitting a waypoint that + can be respawned at. + + Input Arguments:- + sourcewaypoint - The waypoint to start searching from + traveldistance - How far along the circuit it will try to pathfind. + returnpath - The path_t that will contain the final found path + useshortcuts - Whether to use waypoints that are marked as being shortcuts in the search + huntbackwards - Goes through the waypoints backwards if true + + Return:- + True if a circuit path could be constructed, false if it couldn't. +--------------------------------------------------*/ + +boolean K_PathfindThruCircuitSpawnable( + waypoint_t *const sourcewaypoint, + const UINT32 traveldistance, + path_t *const returnpath, + const boolean useshortcuts, + const boolean huntbackwards); + + /*-------------------------------------------------- waypoint_t *K_GetNextWaypointToDestination( waypoint_t *const sourcewaypoint, diff --git a/src/objects/shrink.c b/src/objects/shrink.c index a37c93ac3..0728385cf 100644 --- a/src/objects/shrink.c +++ b/src/objects/shrink.c @@ -564,7 +564,7 @@ static waypoint_t *GetPohbeeWaypoint(waypoint_t *anchor, const UINT32 traveldist path_t pathtofinish = {0}; waypoint_t *ret = NULL; - pathfindsuccess = K_PathfindThruCircuit( + pathfindsuccess = K_PathfindThruCircuitSpawnable( anchor, traveldist, &pathtofinish, useshortcuts, huntbackwards From 8f38555a86dae93ec52b847f2e6a190c3121c32c Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Fri, 16 Sep 2022 00:06:02 -0400 Subject: [PATCH 114/114] fixme for james :) --- src/objects/shrink.c | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/objects/shrink.c b/src/objects/shrink.c index 0728385cf..017c19829 100644 --- a/src/objects/shrink.c +++ b/src/objects/shrink.c @@ -23,6 +23,17 @@ #include "../z_zone.h" #include "../k_waypoint.h" +// +// ███████╗██╗██╗░░██╗███╗░░░███╗███████╗ +// ██╔════╝██║╚██╗██╔╝████╗░████║██╔════╝ +// █████╗░░██║░╚███╔╝░██╔████╔██║█████╗░░ +// ██╔══╝░░██║░██╔██╗░██║╚██╔╝██║██╔══╝░░ +// ██║░░░░░██║██╔╝╚██╗██║░╚═╝░██║███████╗ +// ╚═╝░░░░░╚═╝╚═╝░░╚═╝╚═╝░░░░░╚═╝╚══════╝ +// +// vertical flip +// + #define POHBEE_HOVER (256 << FRACBITS) #define POHBEE_SPEED (128 << FRACBITS) #define POHBEE_TIME (30 * TICRATE)