From 0a9ba013fece245d0ff660e4b91f40da4fea5ee4 Mon Sep 17 00:00:00 2001 From: James R Date: Fri, 22 Feb 2019 20:18:33 -0800 Subject: [PATCH 01/92] Overhaul the map command Added support for map names, matched by substring and keywords too! Added support for two digit MAP codes without the MAP part. Added support for decimal map number. (But who cares.) Gave a better description of command. Supported abbreviated optional parameters. And now REALLY detects incorrect parameters. --- src/d_netcmd.c | 266 +++++++++++++++++++++++++++++++++++++++---------- src/doomdef.h | 1 + src/doomtype.h | 3 + src/m_misc.c | 67 +++++++++++++ 4 files changed, 285 insertions(+), 52 deletions(-) diff --git a/src/d_netcmd.c b/src/d_netcmd.c index 438cdcd5..5ed78165 100644 --- a/src/d_netcmd.c +++ b/src/d_netcmd.c @@ -2180,25 +2180,54 @@ void D_PickVote(void) SendNetXCmd(XD_PICKVOTE, &buf, 2); } +/* +Easy macro; declare parm_*id* and define acceptableargc; put in the parameter +to match as a string as *name*. Set *argn* to the number of extra arguments +following the parameter. parm_*id* is filled with the index of the parameter +found and acceptableargc is incremented to match the macro parameters. +Returned is whether the parameter was found. +*/ +#define CHECKPARM( id, name, argn ) \ +( (( parm_ ## id = COM_CheckParm(name) )) &&\ + ( acceptableargc += 1 + argn ) ) +// // Warp to map code. // Called either from map console command, or idclev cheat. // +// Largely rewritten by James. +// static void Command_Map_f(void) { - const char *mapname; - size_t i; - INT32 j, newmapnum; + size_t acceptableargc; + size_t parm_force; + size_t parm_gametype; + const char *arg_gametype; + /* debug? */ + size_t parm_noresetplayers; boolean newresetplayers; + + boolean mustmodifygame; + boolean usemapcode = false; + + INT32 newmapnum; + INT32 apromapnum = 0; + + const char *mapname; + size_t mapnamelen; + char *realmapname = NULL; + char *apromapname = NULL; + + /* Keyword matching */ + char *query; + char *key; + UINT8 *freq; + UINT8 freqc; + INT32 newgametype = gametype; - // max length of command: map map03 -gametype coop -noresetplayers -force - // 1 2 3 4 5 6 - // = 8 arg max - if (COM_Argc() < 2 || COM_Argc() > 8) - { - CONS_Printf(M_GetText("map [-gametype [-force]: warp to map\n")); - return; - } + INT32 i; + INT32 d; + char *p; if (client && !IsPlayerAdmin(consoleplayer)) { @@ -2206,91 +2235,220 @@ static void Command_Map_f(void) return; } - // internal wad lump always: map command doesn't support external files as in doom legacy - if (W_CheckNumForName(COM_Argv(1)) == LUMPERROR) + acceptableargc = 2;/* map name */ + + (void) + ( + CHECKPARM (force, "-force", 0) || + CHECKPARM (force, "-f", 0) + ); + (void) + ( + CHECKPARM (gametype, "-gametype", 1) || + CHECKPARM (gametype, "-g", 1) || + CHECKPARM (gametype, "-gt", 1) + ); + + (void)CHECKPARM (noresetplayers, "-noresetplayers", 0); + + newresetplayers = !parm_noresetplayers; + + mustmodifygame = !( netgame || multiplayer || majormods ); + + if (mustmodifygame && !parm_force) { - CONS_Alert(CONS_ERROR, M_GetText("Internal game level '%s' not found\n"), COM_Argv(1)); + /* May want to be more descriptive? */ + CONS_Printf(M_GetText("Sorry, level change disabled in single player.\n")); return; } - if (!(netgame || multiplayer) && !majormods) - { - if (COM_CheckParm("-force")) - { - G_SetGameModified(false, true); - } - else - { - CONS_Printf(M_GetText("Sorry, level change disabled in single player.\n")); - return; - } - } - - newresetplayers = !COM_CheckParm("-noresetplayers"); - if (!newresetplayers && !cv_debug) { CONS_Printf(M_GetText("DEVMODE must be enabled.\n")); return; } - mapname = COM_Argv(1); - if (strlen(mapname) != 5 - || (newmapnum = M_MapNumber(mapname[3], mapname[4])) == 0) + if (parm_gametype && !multiplayer) { - CONS_Alert(CONS_ERROR, M_GetText("Invalid level name %s\n"), mapname); + CONS_Printf(M_GetText("You can't switch gametypes in single player!\n")); return; } - // new gametype value - // use current one by default - i = COM_CheckParm("-gametype"); - if (i) + if (COM_Argc() != acceptableargc) { - if (!multiplayer) + /* I'm going over the fucking lines and I DON'T CAREEEEE */ + CONS_Printf("map [-gametype ] [-force]:\n"); + CONS_Printf(M_GetText( + "Warp to a map, by its name, two character code, with optional \"MAP\" prefix, or by its number (though why would you).\n" + "All parameters are case-insensitive.\n" + "* \"-force\" may be shortened to \"-f\".\n" + "* \"-gametype\" may be shortened to \"-g\" or \"-gt\".\n")); + return; + } + + mapname = COM_Argv(1); + mapnamelen = strlen(mapname); + + if (mapnamelen == 2)/* maybe two digit code */ + { + if (( newmapnum = M_MapNumber(mapname[0], mapname[1]) )) + usemapcode = true; + } + else if (mapnamelen == 5 && strnicmp(mapname, "MAP", 3) == 0) + { + if (( newmapnum = M_MapNumber(mapname[3], mapname[4]) ) == 0) { - CONS_Printf(M_GetText("You can't switch gametypes in single player!\n")); + CONS_Alert(CONS_ERROR, M_GetText("Invalid map code '%s'.\n"), mapname); return; } + usemapcode = true; + } - for (j = 0; gametype_cons_t[j].strvalue; j++) - if (!strcasecmp(gametype_cons_t[j].strvalue, COM_Argv(i+1))) + if (!usemapcode) + { + /* Now detect map number in base 10, which no one asked for. */ + newmapnum = strtol(mapname, &p, 10); + if (*p == '\0')/* we got it */ + { + if (newmapnum < 1 || newmapnum > NUMMAPS) + { + CONS_Alert(CONS_ERROR, M_GetText("Invalid map number %d.\n"), newmapnum); + return; + } + usemapcode = true; + } + else + { + query = ZZ_Alloc(strlen(mapname)+1); + freq = ZZ_Calloc(NUMMAPS * sizeof (UINT8)); + + for (i = 0, newmapnum = 1; i < NUMMAPS; ++i, ++newmapnum) + if (mapheaderinfo[i]) + { + realmapname = G_BuildMapTitle(newmapnum); + + /* Now that we found a perfect match no need to fucking guess. */ + if (strnicmp(realmapname, mapname, mapnamelen) == 0) + { + Z_Free(apromapname); + break; + } + + if (apromapnum == 0) + { + /* LEVEL 1--match keywords verbatim */ + if (strcasestr(realmapname, mapname)) + { + apromapnum = newmapnum; + apromapname = realmapname; + realmapname = 0; + } + else/* ...match individual keywords */ + { + strcpy(query, mapname); + for (key = strtok(query, " "); + key; + key = strtok(0, " ")) + { + if (strcasestr(realmapname, key)) + { + freq[i]++; + } + } + } + } + + Z_Free(realmapname);/* leftover old name */ + } + + if (newmapnum == NUMMAPS+1)/* no perfect match--try a substring */ + { + newmapnum = apromapnum; + realmapname = apromapname; + } + + if (newmapnum == 0)/* calculate most queries met! */ + { + freqc = 0; + for (i = 0; i < NUMMAPS; ++i) + { + if (freq[i] > freqc) + { + freqc = freq[i]; + newmapnum = i + 1; + } + } + if (newmapnum) + { + realmapname = G_BuildMapTitle(newmapnum); + } + } + + Z_Free(freq); + Z_Free(query); + } + } + + if (newmapnum == 0 || !mapheaderinfo[newmapnum-1]) + { + CONS_Alert(CONS_ERROR, M_GetText("Could not find any map described as '%s'.\n"), mapname); + return; + } + + if (usemapcode) + { + realmapname = G_BuildMapTitle(newmapnum); + } + + if (mustmodifygame && parm_force) + { + G_SetGameModified(false, true); + } + + arg_gametype = COM_Argv(parm_gametype + 1); + + // new gametype value + // use current one by default + if (parm_gametype) + { + for (i = 0; gametype_cons_t[i].strvalue; i++) + if (!strcasecmp(gametype_cons_t[i].strvalue, arg_gametype)) { // Don't do any variable setting here. Wait until you get your // map packet first to avoid sending the same info twice! - newgametype = gametype_cons_t[j].value; + newgametype = gametype_cons_t[i].value; break; } - if (!gametype_cons_t[j].strvalue) // reached end of the list with no match + if (!gametype_cons_t[i].strvalue) // reached end of the list with no match { + d = atoi(arg_gametype); // assume they gave us a gametype number, which is okay too - for (j = 0; gametype_cons_t[j].strvalue != NULL; j++) + for (i = 0; gametype_cons_t[i].strvalue != NULL; i++) { - if (atoi(COM_Argv(i+1)) == gametype_cons_t[j].value) + if (d == gametype_cons_t[i].value) { - newgametype = gametype_cons_t[j].value; + newgametype = gametype_cons_t[i].value; break; } } } } - if (!(i = COM_CheckParm("-force")) && newgametype == gametype) // SRB2Kart + if (!parm_force && newgametype == gametype) // SRB2Kart newresetplayers = false; // if not forcing and gametypes is the same // don't use a gametype the map doesn't support - if (cv_debug || i || cv_skipmapcheck.value) + if (cv_debug || parm_force || cv_skipmapcheck.value) ; // The player wants us to trek on anyway. Do so. // G_TOLFlag handles both multiplayer gametype and ignores it for !multiplayer - // Alternatively, bail if the map header is completely missing anyway. else { - if (!mapheaderinfo[newmapnum-1] - || !(mapheaderinfo[newmapnum-1]->typeoflevel & G_TOLFlag(newgametype))) + if (!(mapheaderinfo[newmapnum-1]->typeoflevel & G_TOLFlag(newgametype))) { - CONS_Alert(CONS_WARNING, M_GetText("%s doesn't support %s mode!\n(Use -force to override)\n"), mapname, + CONS_Alert(CONS_WARNING, M_GetText("Course %s (%s) doesn't support %s mode!\n(Use -force to override)\n"), realmapname, G_BuildMapName(newmapnum), (multiplayer ? gametype_cons_t[newgametype].strvalue : "Single Player")); + Z_Free(realmapname); return; } } @@ -2302,12 +2460,16 @@ static void Command_Map_f(void) if (!dedicated && M_MapLocked(newmapnum)) { CONS_Alert(CONS_NOTICE, M_GetText("You need to unlock this level before you can warp to it!\n")); + Z_Free(realmapname); return; } fromlevelselect = false; D_MapChange(newmapnum, newgametype, (boolean)cv_kartencore.value, newresetplayers, 0, false, false); + + Z_Free(realmapname); } +#undef CHECKPARM /** Receives a map command and changes the map. * diff --git a/src/doomdef.h b/src/doomdef.h index ab863c6f..c1af6e59 100644 --- a/src/doomdef.h +++ b/src/doomdef.h @@ -460,6 +460,7 @@ extern boolean capslock; // if we ever make our alloc stuff... #define ZZ_Alloc(x) Z_Malloc(x, PU_STATIC, NULL) +#define ZZ_Calloc(x) Z_Calloc(x, PU_STATIC, NULL) // i_system.c, replace getchar() once the keyboard has been appropriated INT32 I_GetKey(void); diff --git a/src/doomtype.h b/src/doomtype.h index 5d643484..89032ae1 100644 --- a/src/doomtype.h +++ b/src/doomtype.h @@ -139,6 +139,9 @@ typedef long ssize_t; #define strlwr _strlwr #endif +char *strcasestr(const char *in, const char *what); +#define stristr strcasestr + #if defined (macintosh) //|| defined (__APPLE__) //skip all boolean/Boolean crap #define true 1 #define false 0 diff --git a/src/m_misc.c b/src/m_misc.c index c95aa392..d28577c0 100644 --- a/src/m_misc.c +++ b/src/m_misc.c @@ -1607,6 +1607,73 @@ void strcatbf(char *s1, const char *s2, const char *s3) strcat(s1, tmp); } +/** Locate a substring, case-insensitively. + * Know that I hate this style. -James + * + * \param s The string to search within. + * \param q The substring to find. + * \return a pointer to the located substring, or NULL if it could be found. +*/ +char *strcasestr(const char *s, const char *q) +{ + void **vpp;/* a hack! */ + + size_t qz; + + const char *up; + const char *lp; + + int uc; + int lc; + + qz = strlen(q); + + uc = toupper(*q); + lc = tolower(*q); + + up = s; + lp = s; + + do + { + if (uc > 0) + { + up = strchr(up, uc); + if (!up || ( lc == 0 && lp < up )) + uc = -1; + else + if (strnicmp(q, up, qz) == 0) + uc = 0; + else + up++; + } + if (lc > 0) + { + lp = strchr(lp, lc); + if (!lp || ( uc == 0 && up < lp )) + lc = -1; + else + if (strnicmp(q, lp, qz) == 0) + lc = 0; + else + lp++; + } + } + while (( uc > 0 ) || ( lc > 0 )) ; + + if (uc == 0) + vpp = (void **)&up; + else + vpp = (void **)&lp; + + /* + We can dereference a double void pointer and cast it to remove const. + This works because the original variable (the pointer) is writeable, + but its value is not. + */ + return (char *)*vpp; +} + /** Converts an ASCII Hex string into an integer. Thanks, Borland! * I don't know if this belongs here specifically, but it sure * doesn't belong in p_spec.c, that's for sure From 3cdfb6327a713ac4d58576dba6a554481d8e34ca Mon Sep 17 00:00:00 2001 From: James R Date: Fri, 22 Feb 2019 22:25:05 -0800 Subject: [PATCH 02/92] Add support for custom keywords For now, 32 characters separated by spaces. Also fixed a SIGSEGV from an empty level title. --- src/d_netcmd.c | 39 ++++++++++++++++++++++++--------------- src/dehacked.c | 5 +++++ src/doomstat.h | 1 + src/lua_maplib.c | 2 ++ src/p_setup.c | 2 ++ 5 files changed, 34 insertions(+), 15 deletions(-) diff --git a/src/d_netcmd.c b/src/d_netcmd.c index 5ed78165..d81e9d6f 100644 --- a/src/d_netcmd.c +++ b/src/d_netcmd.c @@ -2180,6 +2180,25 @@ void D_PickVote(void) SendNetXCmd(XD_PICKVOTE, &buf, 2); } +/* +Return the number of times a series of keywords, delimited by spaces, matched. +*/ +static int measurekeywords(const char *s, const char *q) +{ + int r = 0; + char *qp; + for (qp = strtok(va("%s", q), " "); + qp; + qp = strtok(0, " ")) + { + if (strcasestr(s, qp)) + { + r++; + } + } + return r; +} + /* Easy macro; declare parm_*id* and define acceptableargc; put in the parameter to match as a string as *name*. Set *argn* to the number of extra arguments @@ -2218,8 +2237,6 @@ static void Command_Map_f(void) char *apromapname = NULL; /* Keyword matching */ - char *query; - char *key; UINT8 *freq; UINT8 freqc; @@ -2319,13 +2336,13 @@ static void Command_Map_f(void) } else { - query = ZZ_Alloc(strlen(mapname)+1); freq = ZZ_Calloc(NUMMAPS * sizeof (UINT8)); for (i = 0, newmapnum = 1; i < NUMMAPS; ++i, ++newmapnum) if (mapheaderinfo[i]) { - realmapname = G_BuildMapTitle(newmapnum); + if (!( realmapname = G_BuildMapTitle(newmapnum) )) + continue; /* Now that we found a perfect match no need to fucking guess. */ if (strnicmp(realmapname, mapname, mapnamelen) == 0) @@ -2345,16 +2362,9 @@ static void Command_Map_f(void) } else/* ...match individual keywords */ { - strcpy(query, mapname); - for (key = strtok(query, " "); - key; - key = strtok(0, " ")) - { - if (strcasestr(realmapname, key)) - { - freq[i]++; - } - } + freq[i] += measurekeywords(realmapname, mapname); + freq[i] += measurekeywords(mapheaderinfo[i]->keyword, + mapname); } } @@ -2385,7 +2395,6 @@ static void Command_Map_f(void) } Z_Free(freq); - Z_Free(query); } } diff --git a/src/dehacked.c b/src/dehacked.c index 11aed24d..7dfedde5 100644 --- a/src/dehacked.c +++ b/src/dehacked.c @@ -1191,6 +1191,11 @@ static void readlevelheader(MYFILE *f, INT32 num) mapheaderinfo[num-1]->typeoflevel = tol; } } + else if (fastcmp(word, "KEYWORD")) + { + deh_strlcpy(mapheaderinfo[num-1]->keyword, word2, + sizeof(mapheaderinfo[num-1]->keyword), va("Level header %d: keyword", num)); + } else if (fastcmp(word, "MUSIC")) { if (fastcmp(word2, "NONE")) diff --git a/src/doomstat.h b/src/doomstat.h index 9ae2726d..43ebaf08 100644 --- a/src/doomstat.h +++ b/src/doomstat.h @@ -226,6 +226,7 @@ typedef struct char actnum[3]; ///< SRB2Kart: Now a 2 character long string. UINT16 typeoflevel; ///< Combination of typeoflevel flags. INT16 nextlevel; ///< Map number of next level, or 1100-1102 to end. + char keyword[33]; ///< Keywords separated by space to search for. 32 characters. char musname[7]; ///< Music track to play. "" for no music. UINT16 mustrack; ///< Subsong to play. Only really relevant for music modules and specific formats supported by GME. 0 to ignore. char forcecharacter[17]; ///< (SKINNAMESIZE+1) Skin to switch to or "" to disable. diff --git a/src/lua_maplib.c b/src/lua_maplib.c index 0bb9a99d..d7a4fdaf 100644 --- a/src/lua_maplib.c +++ b/src/lua_maplib.c @@ -1473,6 +1473,8 @@ static int mapheaderinfo_get(lua_State *L) lua_pushinteger(L, header->typeoflevel); else if (fastcmp(field,"nextlevel")) lua_pushinteger(L, header->nextlevel); + else if (fastcmp(field,"keyword")) + lua_pushstring(L, header->keyword); else if (fastcmp(field,"musname")) lua_pushstring(L, header->musname); else if (fastcmp(field,"mustrack")) diff --git a/src/p_setup.c b/src/p_setup.c index 58e13c2e..0187788d 100644 --- a/src/p_setup.c +++ b/src/p_setup.c @@ -188,6 +188,8 @@ static void P_ClearSingleMapHeaderInfo(INT16 i) mapheaderinfo[num]->typeoflevel = 0; DEH_WriteUndoline("NEXTLEVEL", va("%d", mapheaderinfo[num]->nextlevel), UNDO_NONE); mapheaderinfo[num]->nextlevel = (INT16)(i + 1); + DEH_WriteUndoline("KEYWORD", mapheaderinfo[num]->keyword, UNDO_NONE); + mapheaderinfo[num]->keyword[0] = '\0'; DEH_WriteUndoline("MUSIC", mapheaderinfo[num]->musname, UNDO_NONE); snprintf(mapheaderinfo[num]->musname, 7, "%sM", G_BuildMapName(i)); mapheaderinfo[num]->musname[6] = 0; From 7e389f40626445e86994c114302d944a663e3348 Mon Sep 17 00:00:00 2001 From: James R Date: Sat, 23 Feb 2019 21:25:53 -0800 Subject: [PATCH 03/92] Extend map name text matching code into a function --- src/d_netcmd.c | 85 +---------------------- src/g_game.c | 184 +++++++++++++++++++++++++++++++++++++++++++++++++ src/g_game.h | 21 ++++++ 3 files changed, 206 insertions(+), 84 deletions(-) diff --git a/src/d_netcmd.c b/src/d_netcmd.c index d81e9d6f..7f22b2fe 100644 --- a/src/d_netcmd.c +++ b/src/d_netcmd.c @@ -2180,25 +2180,6 @@ void D_PickVote(void) SendNetXCmd(XD_PICKVOTE, &buf, 2); } -/* -Return the number of times a series of keywords, delimited by spaces, matched. -*/ -static int measurekeywords(const char *s, const char *q) -{ - int r = 0; - char *qp; - for (qp = strtok(va("%s", q), " "); - qp; - qp = strtok(0, " ")) - { - if (strcasestr(s, qp)) - { - r++; - } - } - return r; -} - /* Easy macro; declare parm_*id* and define acceptableargc; put in the parameter to match as a string as *name*. Set *argn* to the number of extra arguments @@ -2229,16 +2210,10 @@ static void Command_Map_f(void) boolean usemapcode = false; INT32 newmapnum; - INT32 apromapnum = 0; const char *mapname; size_t mapnamelen; char *realmapname = NULL; - char *apromapname = NULL; - - /* Keyword matching */ - UINT8 *freq; - UINT8 freqc; INT32 newgametype = gametype; @@ -2336,65 +2311,7 @@ static void Command_Map_f(void) } else { - freq = ZZ_Calloc(NUMMAPS * sizeof (UINT8)); - - for (i = 0, newmapnum = 1; i < NUMMAPS; ++i, ++newmapnum) - if (mapheaderinfo[i]) - { - if (!( realmapname = G_BuildMapTitle(newmapnum) )) - continue; - - /* Now that we found a perfect match no need to fucking guess. */ - if (strnicmp(realmapname, mapname, mapnamelen) == 0) - { - Z_Free(apromapname); - break; - } - - if (apromapnum == 0) - { - /* LEVEL 1--match keywords verbatim */ - if (strcasestr(realmapname, mapname)) - { - apromapnum = newmapnum; - apromapname = realmapname; - realmapname = 0; - } - else/* ...match individual keywords */ - { - freq[i] += measurekeywords(realmapname, mapname); - freq[i] += measurekeywords(mapheaderinfo[i]->keyword, - mapname); - } - } - - Z_Free(realmapname);/* leftover old name */ - } - - if (newmapnum == NUMMAPS+1)/* no perfect match--try a substring */ - { - newmapnum = apromapnum; - realmapname = apromapname; - } - - if (newmapnum == 0)/* calculate most queries met! */ - { - freqc = 0; - for (i = 0; i < NUMMAPS; ++i) - { - if (freq[i] > freqc) - { - freqc = freq[i]; - newmapnum = i + 1; - } - } - if (newmapnum) - { - realmapname = G_BuildMapTitle(newmapnum); - } - } - - Z_Free(freq); + newmapnum = G_FindMap(mapname, &realmapname, NULL, NULL); } } diff --git a/src/g_game.c b/src/g_game.c index f0d221ff..dbd1f87e 100644 --- a/src/g_game.c +++ b/src/g_game.c @@ -4486,6 +4486,190 @@ char *G_BuildMapTitle(INT32 mapnum) return title; } +static void measurekeywords(mapsearchfreq_t *fr, + struct searchdim **dimp, UINT8 *cuntp, + const char *s, const char *q, boolean wanttable) +{ + char *qp; + char *sp; + if (wanttable) + (*dimp) = Z_Realloc((*dimp), 255 * sizeof (struct searchdim), + PU_STATIC, NULL); + for (qp = strtok(va("%s", q), " "); + qp && fr->total < 255; + qp = strtok(0, " ")) + { + if (( sp = strcasestr(s, qp) )) + { + if (wanttable) + { + (*dimp)[(*cuntp)].pos = sp - s; + (*dimp)[(*cuntp)].siz = strlen(qp); + } + (*cuntp)++; + fr->total++; + } + } + if (wanttable) + (*dimp) = Z_Realloc((*dimp), (*cuntp) * sizeof (struct searchdim), + PU_STATIC, NULL); +} + +void writesimplefreq(mapsearchfreq_t *fr, INT32 *frc, + INT32 mapnum, UINT8 pos, UINT8 siz) +{ + fr[(*frc)].mapnum = mapnum; + fr[(*frc)].matchd = ZZ_Alloc(sizeof (struct searchdim)); + fr[(*frc)].matchd[0].pos = pos; + fr[(*frc)].matchd[0].siz = siz; + fr[(*frc)].matchc = 1; + fr[(*frc)].total = 1; + (*frc)++; +} + +INT32 G_FindMap(const char *mapname, char **foundmapnamep, + mapsearchfreq_t **freqp, INT32 *freqcp) +{ + INT32 newmapnum = 0; + INT32 mapnum; + INT32 apromapnum = 0; + + size_t mapnamelen; + char *realmapname = NULL; + char *newmapname = NULL; + char *apromapname = NULL; + char *aprop = NULL; + + mapsearchfreq_t *freq; + boolean wanttable; + INT32 freqc; + UINT8 frequ; + + INT32 i; + + mapnamelen = strlen(mapname); + + /* Count available maps; how ugly. */ + for (i = 0, freqc = 0; i < NUMMAPS; ++i) + { + if (mapheaderinfo[i]) + freqc++; + } + + freq = ZZ_Calloc(freqc * sizeof (mapsearchfreq_t)); + + wanttable = !!( freqp ); + + freqc = 0; + for (i = 0, mapnum = 1; i < NUMMAPS; ++i, ++mapnum) + if (mapheaderinfo[i]) + { + if (!( realmapname = G_BuildMapTitle(mapnum) )) + continue; + + aprop = realmapname; + + /* Now that we found a perfect match no need to fucking guess. */ + if (strnicmp(realmapname, mapname, mapnamelen) == 0) + { + if (wanttable) + { + writesimplefreq(freq, &freqc, mapnum, 0, mapnamelen); + } + if (newmapnum == 0) + { + newmapnum = mapnum; + newmapname = realmapname; + realmapname = 0; + Z_Free(apromapname); + if (!wanttable) + break; + } + } + else + if (apromapnum == 0 || wanttable) + { + /* LEVEL 1--match keywords verbatim */ + if (( aprop = strcasestr(realmapname, mapname) )) + { + if (wanttable) + { + writesimplefreq(freq, &freqc, + mapnum, aprop - realmapname, mapnamelen); + } + if (apromapnum == 0) + { + apromapnum = mapnum; + apromapname = realmapname; + realmapname = 0; + } + } + else/* ...match individual keywords */ + { + freq[freqc].mapnum = mapnum; + measurekeywords(&freq[freqc], + &freq[freqc].matchd, &freq[freqc].matchc, + realmapname, mapname, wanttable); + measurekeywords(&freq[freqc], + &freq[freqc].keywhd, &freq[freqc].keywhc, + mapheaderinfo[i]->keyword, mapname, wanttable); + if (freq[freqc].total) + freqc++; + } + } + + Z_Free(realmapname);/* leftover old name */ + } + + if (newmapnum == 0)/* no perfect match--try a substring */ + { + newmapnum = apromapnum; + newmapname = apromapname; + } + + if (newmapnum == 0)/* calculate most queries met! */ + { + frequ = 0; + for (i = 0; i < freqc; ++i) + { + if (freq[i].total > frequ) + { + frequ = freq[i].total; + newmapnum = freq[i].mapnum; + } + } + if (newmapnum) + { + newmapname = G_BuildMapTitle(newmapnum); + } + } + + if (freqp) + (*freqp) = freq; + else + Z_Free(freq); + + if (freqcp) + (*freqcp) = freqc; + + if (foundmapnamep) + (*foundmapnamep) = newmapname; + else + Z_Free(newmapname); + + return newmapnum; +} + +void G_FreeMapSearch(mapsearchfreq_t *freq, INT32 freqc) +{ + INT32 i; + for (i = 0; i < freqc; ++i) + { + Z_Free(freq[i].matchd); + } + Z_Free(freq); +} + // // DEMO RECORDING // diff --git a/src/g_game.h b/src/g_game.h index fc7a4a4f..eace3fcd 100644 --- a/src/g_game.h +++ b/src/g_game.h @@ -115,6 +115,27 @@ void G_InitNew(UINT8 pencoremode, const char *mapname, boolean resetplayer, boolean skipprecutscene); char *G_BuildMapTitle(INT32 mapnum); +struct searchdim +{ + UINT8 pos; + UINT8 siz; +}; + +typedef struct +{ + INT16 mapnum; + UINT8 matchc; + struct searchdim *matchd;/* offset that a pattern was matched */ + UINT8 keywhc; + struct searchdim *keywhd;/* ...in KEYWORD */ + UINT8 total;/* total hits */ +} +mapsearchfreq_t; + +INT32 G_FindMap(const char *query, char **foundmapnamep, + mapsearchfreq_t **freqp, INT32 *freqc); +void G_FreeMapSearch(mapsearchfreq_t *freq, INT32 freqc); + // XMOD spawning mapthing_t *G_FindCTFStart(INT32 playernum); mapthing_t *G_FindMatchStart(INT32 playernum); From b4e900be1f91c1fff2738a2fae1ed9ac23dd7acc Mon Sep 17 00:00:00 2001 From: James R Date: Sun, 21 Apr 2019 23:18:40 -0700 Subject: [PATCH 04/92] Use a superior strcasestr --- src/m_misc.c | 67 ----------------------------- src/strcasestr.c | 110 +++++++++++++++++++++++++++++++++++++++++++++++ src/string.c | 3 ++ 3 files changed, 113 insertions(+), 67 deletions(-) create mode 100644 src/strcasestr.c diff --git a/src/m_misc.c b/src/m_misc.c index d28577c0..c95aa392 100644 --- a/src/m_misc.c +++ b/src/m_misc.c @@ -1607,73 +1607,6 @@ void strcatbf(char *s1, const char *s2, const char *s3) strcat(s1, tmp); } -/** Locate a substring, case-insensitively. - * Know that I hate this style. -James - * - * \param s The string to search within. - * \param q The substring to find. - * \return a pointer to the located substring, or NULL if it could be found. -*/ -char *strcasestr(const char *s, const char *q) -{ - void **vpp;/* a hack! */ - - size_t qz; - - const char *up; - const char *lp; - - int uc; - int lc; - - qz = strlen(q); - - uc = toupper(*q); - lc = tolower(*q); - - up = s; - lp = s; - - do - { - if (uc > 0) - { - up = strchr(up, uc); - if (!up || ( lc == 0 && lp < up )) - uc = -1; - else - if (strnicmp(q, up, qz) == 0) - uc = 0; - else - up++; - } - if (lc > 0) - { - lp = strchr(lp, lc); - if (!lp || ( uc == 0 && up < lp )) - lc = -1; - else - if (strnicmp(q, lp, qz) == 0) - lc = 0; - else - lp++; - } - } - while (( uc > 0 ) || ( lc > 0 )) ; - - if (uc == 0) - vpp = (void **)&up; - else - vpp = (void **)&lp; - - /* - We can dereference a double void pointer and cast it to remove const. - This works because the original variable (the pointer) is writeable, - but its value is not. - */ - return (char *)*vpp; -} - /** Converts an ASCII Hex string into an integer. Thanks, Borland! * I don't know if this belongs here specifically, but it sure * doesn't belong in p_spec.c, that's for sure diff --git a/src/strcasestr.c b/src/strcasestr.c new file mode 100644 index 00000000..2077dc3f --- /dev/null +++ b/src/strcasestr.c @@ -0,0 +1,110 @@ +/* +strcasestr -- case insensitive substring searching function. +*/ +/* +Copyright 2019 James R. +All rights reserved. + +Redistribution and use in source forms, with or without modification, is +permitted provided that the following condition is met: + +1. Redistributions of source code must retain the above copyright notice, this + condition and the following disclaimer. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR +ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#define SWAP( a, b ) \ +(\ + (a) ^= (b),\ + (b) ^= (a),\ + (a) ^= (b)\ +) + +static inline int +trycmp (char **pp, char *cp, + const char *q, size_t qn) +{ + char *p; + p = (*pp); + if (strncasecmp(p, q, qn) == 0) + return 0; + (*pp) = strchr(&p[1], (*cp)); + return 1; +} + +static inline void +swapp (char ***ppap, char ***ppbp, char **cpap, char **cpbp) +{ + SWAP(*(intptr_t *)ppap, *(intptr_t *)ppbp); + SWAP(*(intptr_t *)cpap, *(intptr_t *)cpbp); +} + +char * +strcasestr (const char *s, const char *q) +{ + size_t qn; + + char uc; + char lc; + + char *up; + char *lp; + + char **ppa; + char **ppb; + + char *cpa; + char *cpb; + + uc = toupper(*q); + lc = tolower(*q); + + up = strchr(s, uc); + lp = strchr(s, lc); + + if (!( (intptr_t)up|(intptr_t)lp )) + return 0; + + if (!lp || up < lp) + { + ppa = &up; + ppb = &lp; + + cpa = &uc; + cpb = &lc; + } + else + { + ppa = &lp; + ppb = &up; + + cpa = &lc; + cpb = &uc; + } + + qn = strlen(q); + + for (;;) + { + if (trycmp(ppa, cpa, q, qn) == 0) + return (*ppa); + + if (!( (intptr_t)up|(intptr_t)lp )) + break; + + if (!(*ppa) || ( (*ppb) && (*ppb) < (*ppa) )) + swapp(&ppa, &ppb, &cpa, &cpb); + } + + return 0; +} diff --git a/src/string.c b/src/string.c index 2a03e872..c415e524 100644 --- a/src/string.c +++ b/src/string.c @@ -2,6 +2,7 @@ //----------------------------------------------------------------------------- // Copyright (C) 2006 by Graue. // Copyright (C) 2006-2018 by Sonic Team Junior. +// Copyright (C) 2019 by James R. // // This program is free software distributed under the // terms of the GNU General Public License, version 2. @@ -50,3 +51,5 @@ size_t strlcpy(char *dst, const char *src, size_t siz) } #endif + +#include "strcasestr.c" From e79ec14dc3bbfa7b1d7817d472e42d07aca63126 Mon Sep 17 00:00:00 2001 From: James R Date: Sun, 21 Apr 2019 23:19:20 -0700 Subject: [PATCH 05/92] Fix compiler warnings --- 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 d89e557f..69781da2 100644 --- a/src/g_game.c +++ b/src/g_game.c @@ -4524,7 +4524,7 @@ static void measurekeywords(mapsearchfreq_t *fr, PU_STATIC, NULL); } -void writesimplefreq(mapsearchfreq_t *fr, INT32 *frc, +static void writesimplefreq(mapsearchfreq_t *fr, INT32 *frc, INT32 mapnum, UINT8 pos, UINT8 siz) { fr[(*frc)].mapnum = mapnum; From e1898336977c282a5e0443c01b42ba1114ba015e Mon Sep 17 00:00:00 2001 From: James R Date: Thu, 10 Oct 2019 11:07:34 -0700 Subject: [PATCH 06/92] Fix minor unnecessary call --- src/d_netcmd.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/d_netcmd.c b/src/d_netcmd.c index 577dd403..d8c335e8 100644 --- a/src/d_netcmd.c +++ b/src/d_netcmd.c @@ -2347,12 +2347,12 @@ static void Command_Map_f(void) G_SetGameModified(false, true); } - arg_gametype = COM_Argv(parm_gametype + 1); - // new gametype value // use current one by default if (parm_gametype) { + arg_gametype = COM_Argv(parm_gametype + 1); + for (i = 0; gametype_cons_t[i].strvalue; i++) if (!strcasecmp(gametype_cons_t[i].strvalue, arg_gametype)) { From 6dc44c50ff334631c5649ee0c735ecad4be288d6 Mon Sep 17 00:00:00 2001 From: James R Date: Thu, 10 Oct 2019 11:43:21 -0700 Subject: [PATCH 07/92] Support -encore parameter in new map command --- src/d_netcmd.c | 27 +++++++++++++++++++++++---- 1 file changed, 23 insertions(+), 4 deletions(-) diff --git a/src/d_netcmd.c b/src/d_netcmd.c index 09c58be0..c49eb081 100644 --- a/src/d_netcmd.c +++ b/src/d_netcmd.c @@ -2455,6 +2455,7 @@ static void Command_Map_f(void) size_t acceptableargc; size_t parm_force; size_t parm_gametype; + size_t parm_encore; const char *arg_gametype; /* debug? */ size_t parm_noresetplayers; @@ -2469,7 +2470,8 @@ static void Command_Map_f(void) size_t mapnamelen; char *realmapname = NULL; - INT32 newgametype = gametype; + INT32 newgametype = gametype; + boolean newencoremode = cv_kartencore.value; INT32 i; INT32 d; @@ -2494,6 +2496,12 @@ static void Command_Map_f(void) CHECKPARM (gametype, "-g", 1) || CHECKPARM (gametype, "-gt", 1) ); + (void) + ( + CHECKPARM (encore, "-encore", 0) || + CHECKPARM (encore, "-en", 0) || + CHECKPARM (encore, "-e", 0) + ); (void)CHECKPARM (noresetplayers, "-noresetplayers", 0); @@ -2523,12 +2531,13 @@ static void Command_Map_f(void) if (COM_Argc() != acceptableargc) { /* I'm going over the fucking lines and I DON'T CAREEEEE */ - CONS_Printf("map [-gametype ] [-force]:\n"); + CONS_Printf("map [-gametype ] [-encore] [-force]:\n"); CONS_Printf(M_GetText( "Warp to a map, by its name, two character code, with optional \"MAP\" prefix, or by its number (though why would you).\n" "All parameters are case-insensitive.\n" "* \"-force\" may be shortened to \"-f\".\n" - "* \"-gametype\" may be shortened to \"-g\" or \"-gt\".\n")); + "* \"-gametype\" may be shortened to \"-g\" or \"-gt\".\n" + "* \"-encore\" may be shortened to \"-e\" or \"-en\".\n")); return; } @@ -2644,8 +2653,18 @@ static void Command_Map_f(void) return; } + if (parm_encore) + { + newencoremode = ! newencoremode; + if (! M_SecretUnlocked(SECRET_ENCORE) && newencoremode) + { + CONS_Alert(CONS_NOTICE, M_GetText("You haven't unlocked Encore Mode yet!\n")); + return; + } + } + fromlevelselect = false; - D_MapChange(newmapnum, newgametype, (boolean)cv_kartencore.value, newresetplayers, 0, false, false); + D_MapChange(newmapnum, newgametype, newencoremode, newresetplayers, 0, false, false); Z_Free(realmapname); } From c7a31ea5de32802fb6c0a1bdd50befdf203b2db8 Mon Sep 17 00:00:00 2001 From: James R Date: Fri, 25 Oct 2019 21:18:36 -0700 Subject: [PATCH 08/92] Oh right, the keywords... (cherry picked from commit a922de8242143a55eb0c19cc5a65bb9126213f29) --- src/dehacked.c | 5 ----- src/doomstat.h | 1 - src/g_game.c | 3 --- src/lua_maplib.c | 2 -- src/p_setup.c | 2 -- 5 files changed, 13 deletions(-) diff --git a/src/dehacked.c b/src/dehacked.c index afc8b798..00f4fa96 100644 --- a/src/dehacked.c +++ b/src/dehacked.c @@ -1052,11 +1052,6 @@ static void readlevelheader(MYFILE *f, INT32 num) mapheaderinfo[num-1]->typeoflevel = tol; } } - else if (fastcmp(word, "KEYWORD")) - { - deh_strlcpy(mapheaderinfo[num-1]->keyword, word2, - sizeof(mapheaderinfo[num-1]->keyword), va("Level header %d: keyword", num)); - } else if (fastcmp(word, "MUSIC")) { if (fastcmp(word2, "NONE")) diff --git a/src/doomstat.h b/src/doomstat.h index 66c6df4e..232a5af8 100644 --- a/src/doomstat.h +++ b/src/doomstat.h @@ -222,7 +222,6 @@ typedef struct char actnum[3]; ///< SRB2Kart: Now a 2 character long string. UINT16 typeoflevel; ///< Combination of typeoflevel flags. INT16 nextlevel; ///< Map number of next level, or 1100-1102 to end. - char keyword[33]; ///< Keywords separated by space to search for. 32 characters. char musname[7]; ///< Music track to play. "" for no music. UINT16 mustrack; ///< Subsong to play. Only really relevant for music modules and specific formats supported by GME. 0 to ignore. UINT32 muspos; ///< Music position to jump to. diff --git a/src/g_game.c b/src/g_game.c index c2a176ff..06699c6b 100644 --- a/src/g_game.c +++ b/src/g_game.c @@ -4871,9 +4871,6 @@ INT32 G_FindMap(const char *mapname, char **foundmapnamep, measurekeywords(&freq[freqc], &freq[freqc].matchd, &freq[freqc].matchc, realmapname, mapname, wanttable); - measurekeywords(&freq[freqc], - &freq[freqc].keywhd, &freq[freqc].keywhc, - mapheaderinfo[i]->keyword, mapname, wanttable); if (freq[freqc].total) freqc++; } diff --git a/src/lua_maplib.c b/src/lua_maplib.c index 76a4662b..41875ea9 100644 --- a/src/lua_maplib.c +++ b/src/lua_maplib.c @@ -1449,8 +1449,6 @@ static int mapheaderinfo_get(lua_State *L) lua_pushinteger(L, header->typeoflevel); else if (fastcmp(field,"nextlevel")) lua_pushinteger(L, header->nextlevel); - else if (fastcmp(field,"keyword")) - lua_pushstring(L, header->keyword); else if (fastcmp(field,"musname")) lua_pushstring(L, header->musname); else if (fastcmp(field,"mustrack")) diff --git a/src/p_setup.c b/src/p_setup.c index 6cb96692..5956a5f9 100644 --- a/src/p_setup.c +++ b/src/p_setup.c @@ -190,8 +190,6 @@ static void P_ClearSingleMapHeaderInfo(INT16 i) mapheaderinfo[num]->typeoflevel = 0; DEH_WriteUndoline("NEXTLEVEL", va("%d", mapheaderinfo[num]->nextlevel), UNDO_NONE); mapheaderinfo[num]->nextlevel = (INT16)(i + 1); - DEH_WriteUndoline("KEYWORD", mapheaderinfo[num]->keyword, UNDO_NONE); - mapheaderinfo[num]->keyword[0] = '\0'; DEH_WriteUndoline("MUSIC", mapheaderinfo[num]->musname, UNDO_NONE); snprintf(mapheaderinfo[num]->musname, 7, "%sM", G_BuildMapName(i)); mapheaderinfo[num]->musname[6] = 0; From 3506ae6d1dc178b7771b4161a7a724b2fecb9197 Mon Sep 17 00:00:00 2001 From: James R Date: Sun, 27 Oct 2019 14:29:13 -0700 Subject: [PATCH 09/92] :oh: (cherry picked from commit fb9421893e555629f0fe42705f034e0b639c6905) --- src/d_netcmd.c | 1 - 1 file changed, 1 deletion(-) diff --git a/src/d_netcmd.c b/src/d_netcmd.c index c5f94dcc..b7dc097e 100644 --- a/src/d_netcmd.c +++ b/src/d_netcmd.c @@ -2518,7 +2518,6 @@ static void Command_Map_f(void) INT32 newgametype = gametype; boolean newencoremode = cv_kartencore.value; - INT32 i; INT32 d; char *p; From fb858dd64bafeab8773f309662afd6a4cd735a47 Mon Sep 17 00:00:00 2001 From: James R Date: Tue, 12 Nov 2019 16:08:41 -0800 Subject: [PATCH 10/92] So you don't like macros? (cherry picked from commit 5fd6561d464d53521343dd4abae6b08e42719885) --- src/d_netcmd.c | 134 ++++++++++++++++++++++++++++++++----------------- 1 file changed, 89 insertions(+), 45 deletions(-) diff --git a/src/d_netcmd.c b/src/d_netcmd.c index b7dc097e..3de734cc 100644 --- a/src/d_netcmd.c +++ b/src/d_netcmd.c @@ -2479,16 +2479,49 @@ void D_PickVote(void) SendNetXCmd(XD_PICKVOTE, &buf, 2); } -/* -Easy macro; declare parm_*id* and define acceptableargc; put in the parameter -to match as a string as *name*. Set *argn* to the number of extra arguments -following the parameter. parm_*id* is filled with the index of the parameter -found and acceptableargc is incremented to match the macro parameters. -Returned is whether the parameter was found. -*/ -#define CHECKPARM( id, name, argn ) \ -( (( parm_ ## id = COM_CheckParm(name) )) &&\ - ( acceptableargc += 1 + argn ) ) +enum +{ + MAP_COMMAND_FORCE_OPTION, + MAP_COMMAND_GAMETYPE_OPTION, + MAP_COMMAND_NORESETPLAYERS_OPTION, + + NUM_MAP_COMMAND_OPTIONS +}; + +static size_t CheckOptions( + int num_options, + size_t *user_options, + const char ***option_names, + int *option_num_arguments +) +{ + int arguments_used; + + int i; + const char **pp; + const char *name; + size_t n; + + arguments_used = 0; + + for (i = 0; i < num_options; ++i) + { + pp = option_names[i]; + name = *pp; + do + { + if (( n = COM_CheckParm(name) )) + { + user_options[i] = n; + arguments_used += 1 + option_num_arguments[i]; + } + } + while (( name = *++pp )) ; + } + + return arguments_used; +} + // // Warp to map code. // Called either from map console command, or idclev cheat. @@ -2497,13 +2530,42 @@ Returned is whether the parameter was found. // static void Command_Map_f(void) { - size_t acceptableargc; - size_t parm_force; - size_t parm_gametype; - size_t parm_encore; + const char *force_option_names[] = + { + "-force", + "-f", + NULL + }; + const char *gametype_option_names[] = + { + "-gametype", + "-g", + "-gt", + NULL + }; + const char *noresetplayers_option_names[] = + { + "-noresetplayers", + NULL + }; + const char **option_names[] = + { + force_option_names, + gametype_option_names, + noresetplayers_option_names, + }; + int option_num_arguments[] = + { + 0,/* -force */ + 1,/* -gametype */ + 0,/* -noresetplayers */ + }; + + size_t acceptableargc;/* (this includes the command name itself!) */ + + size_t user_options [NUM_MAP_COMMAND_OPTIONS] = {0}; + const char *arg_gametype; - /* debug? */ - size_t parm_noresetplayers; boolean newresetplayers; boolean mustmodifygame; @@ -2527,33 +2589,15 @@ static void Command_Map_f(void) return; } - acceptableargc = 2;/* map name */ + /* map name + options */ + acceptableargc = 2 + CheckOptions(NUM_MAP_COMMAND_OPTIONS, + user_options, option_names, option_num_arguments); - (void) - ( - CHECKPARM (force, "-force", 0) || - CHECKPARM (force, "-f", 0) - ); - (void) - ( - CHECKPARM (gametype, "-gametype", 1) || - CHECKPARM (gametype, "-g", 1) || - CHECKPARM (gametype, "-gt", 1) - ); - (void) - ( - CHECKPARM (encore, "-encore", 0) || - CHECKPARM (encore, "-en", 0) || - CHECKPARM (encore, "-e", 0) - ); - - (void)CHECKPARM (noresetplayers, "-noresetplayers", 0); - - newresetplayers = !parm_noresetplayers; + newresetplayers = !user_options[MAP_COMMAND_NORESETPLAYERS_OPTION]; mustmodifygame = !( netgame || multiplayer || majormods ); - if (mustmodifygame && !parm_force) + if (mustmodifygame && !user_options[MAP_COMMAND_FORCE_OPTION]) { /* May want to be more descriptive? */ CONS_Printf(M_GetText("Sorry, level change disabled in single player.\n")); @@ -2566,7 +2610,7 @@ static void Command_Map_f(void) return; } - if (parm_gametype && !multiplayer) + if (user_options[MAP_COMMAND_GAMETYPE_OPTION] && !multiplayer) { CONS_Printf(M_GetText("You can't switch gametypes in single player!\n")); return; @@ -2633,16 +2677,16 @@ static void Command_Map_f(void) realmapname = G_BuildMapTitle(newmapnum); } - if (mustmodifygame && parm_force) + if (mustmodifygame && user_options[MAP_COMMAND_FORCE_OPTION]) { G_SetGameModified(false, true); } // new gametype value // use current one by default - if (parm_gametype) + if (user_options[MAP_COMMAND_GAMETYPE_OPTION]) { - arg_gametype = COM_Argv(parm_gametype + 1); + arg_gametype = COM_Argv(user_options[MAP_COMMAND_GAMETYPE_OPTION] + 1); for (i = 0; gametype_cons_t[i].strvalue; i++) if (!strcasecmp(gametype_cons_t[i].strvalue, arg_gametype)) @@ -2672,8 +2716,8 @@ static void Command_Map_f(void) newresetplayers = false; // if not forcing and gametypes is the same // don't use a gametype the map doesn't support - if (cv_debug || parm_force || cv_skipmapcheck.value) - ; // The player wants us to trek on anyway. Do so. + if (cv_debug || user_options[MAP_COMMAND_FORCE_OPTION] || cv_skipmapcheck.value) + fromlevelselect = false; // The player wants us to trek on anyway. Do so. // G_TOLFlag handles both multiplayer gametype and ignores it for !multiplayer else { From 93c351a38e7282ccf052384da05819773cd66a14 Mon Sep 17 00:00:00 2001 From: James R Date: Tue, 12 Nov 2019 17:42:54 -0800 Subject: [PATCH 11/92] Get map name from multiple arguments (This means you don't need quoting.) (cherry picked from commit 46fbed8b716f96a7e87ac4c68ca3ee98c8c6a25c) --- src/d_netcmd.c | 57 ++++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 53 insertions(+), 4 deletions(-) diff --git a/src/d_netcmd.c b/src/d_netcmd.c index 3de734cc..b794f606 100644 --- a/src/d_netcmd.c +++ b/src/d_netcmd.c @@ -2490,12 +2490,14 @@ enum static size_t CheckOptions( int num_options, + size_t *first_argumentp, size_t *user_options, const char ***option_names, int *option_num_arguments ) { - int arguments_used; + int arguments_used; + size_t first_argument; int i; const char **pp; @@ -2503,6 +2505,7 @@ static size_t CheckOptions( size_t n; arguments_used = 0; + first_argument = COM_Argc(); for (i = 0; i < num_options; ++i) { @@ -2514,14 +2517,53 @@ static size_t CheckOptions( { user_options[i] = n; arguments_used += 1 + option_num_arguments[i]; + if (n < first_argument) + first_argument = n; } } while (( name = *++pp )) ; } + (*first_argumentp) = first_argument; + return arguments_used; } +static char * +ConcatCommandArgv (int start, int end) +{ + char *final; + + size_t size; + + int i; + char *p; + + size = 0; + + for (i = start; i < end; ++i) + { + /* + one space after each argument, but terminating + character on final argument + */ + size += strlen(COM_Argv(i)) + 1; + } + + final = ZZ_Alloc(size); + p = final; + + --end;/* handle the final argument separately */ + for (i = start; i < end; ++i) + { + p += sprintf(p, "%s ", COM_Argv(i)); + } + /* at this point "end" is actually the last argument's position */ + strcpy(p, COM_Argv(end)); + + return final; +} + // // Warp to map code. // Called either from map console command, or idclev cheat. @@ -2562,6 +2604,7 @@ static void Command_Map_f(void) }; size_t acceptableargc;/* (this includes the command name itself!) */ + size_t first_argument; size_t user_options [NUM_MAP_COMMAND_OPTIONS] = {0}; @@ -2573,7 +2616,7 @@ static void Command_Map_f(void) INT32 newmapnum; - const char *mapname; + char * mapname; size_t mapnamelen; char *realmapname = NULL; @@ -2591,6 +2634,7 @@ static void Command_Map_f(void) /* map name + options */ acceptableargc = 2 + CheckOptions(NUM_MAP_COMMAND_OPTIONS, + &first_argument, user_options, option_names, option_num_arguments); newresetplayers = !user_options[MAP_COMMAND_NORESETPLAYERS_OPTION]; @@ -2616,7 +2660,7 @@ static void Command_Map_f(void) return; } - if (COM_Argc() != acceptableargc) + if (COM_Argc() < acceptableargc) { /* I'm going over the fucking lines and I DON'T CAREEEEE */ CONS_Printf("map [-gametype ] [-encore] [-force]:\n"); @@ -2629,7 +2673,7 @@ static void Command_Map_f(void) return; } - mapname = COM_Argv(1); + mapname = ConcatCommandArgv(1, first_argument); mapnamelen = strlen(mapname); if (mapnamelen == 2)/* maybe two digit code */ @@ -2642,6 +2686,7 @@ static void Command_Map_f(void) if (( newmapnum = M_MapNumber(mapname[3], mapname[4]) ) == 0) { CONS_Alert(CONS_ERROR, M_GetText("Invalid map code '%s'.\n"), mapname); + Z_Free(mapname); return; } usemapcode = true; @@ -2656,6 +2701,7 @@ static void Command_Map_f(void) if (newmapnum < 1 || newmapnum > NUMMAPS) { CONS_Alert(CONS_ERROR, M_GetText("Invalid map number %d.\n"), newmapnum); + Z_Free(mapname); return; } usemapcode = true; @@ -2669,6 +2715,7 @@ static void Command_Map_f(void) if (newmapnum == 0 || !mapheaderinfo[newmapnum-1]) { CONS_Alert(CONS_ERROR, M_GetText("Could not find any map described as '%s'.\n"), mapname); + Z_Free(mapname); return; } @@ -2726,6 +2773,7 @@ static void Command_Map_f(void) CONS_Alert(CONS_WARNING, M_GetText("Course %s (%s) doesn't support %s mode!\n(Use -force to override)\n"), realmapname, G_BuildMapName(newmapnum), (multiplayer ? gametype_cons_t[newgametype].strvalue : "Single Player")); Z_Free(realmapname); + Z_Free(mapname); return; } } @@ -2738,6 +2786,7 @@ static void Command_Map_f(void) { CONS_Alert(CONS_NOTICE, M_GetText("You need to unlock this level before you can warp to it!\n")); Z_Free(realmapname); + Z_Free(mapname); return; } From 9a054ab9893fea85ceb8974a4d742cb971d0f76f Mon Sep 17 00:00:00 2001 From: James R Date: Tue, 12 Nov 2019 18:29:42 -0800 Subject: [PATCH 12/92] Warn if the first argument is an option (cherry picked from commit c9aad2d1862e92fc78d8dfbd5d1f2c5969d90d84) --- src/d_netcmd.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/d_netcmd.c b/src/d_netcmd.c index b794f606..b92bbc42 100644 --- a/src/d_netcmd.c +++ b/src/d_netcmd.c @@ -2660,7 +2660,8 @@ static void Command_Map_f(void) return; } - if (COM_Argc() < acceptableargc) + /* If the first argument is an option, you fucked up. */ + if (COM_Argc() < acceptableargc || first_argument == 1) { /* I'm going over the fucking lines and I DON'T CAREEEEE */ CONS_Printf("map [-gametype ] [-encore] [-force]:\n"); From 7c3aedc48e31f81902b9bc5ba7cafc0af1c9403e Mon Sep 17 00:00:00 2001 From: James R Date: Wed, 13 Nov 2019 14:26:28 -0800 Subject: [PATCH 13/92] Optimizations from LJ and generic functions (cherry picked from commit 359d8a2c254222d4e44934ec71d56b9511dd9379) --- src/command.c | 34 ++++++++++++ src/command.h | 2 + src/d_netcmd.c | 143 ++++++++++++------------------------------------- 3 files changed, 69 insertions(+), 110 deletions(-) diff --git a/src/command.c b/src/command.c index 95fd5ef4..fba1d4dc 100644 --- a/src/command.c +++ b/src/command.c @@ -353,6 +353,40 @@ size_t COM_CheckParm(const char *check) return 0; } +/** \brief COM_CheckParm, but checks only the start of each argument. + * E.g. checking for "-no" would match "-noerror" too. + */ +size_t COM_CheckPartialParm(const char *check) +{ + int len; + size_t i; + + len = strlen(check); + + for (i = 1; i < com_argc; i++) + { + if (strncasecmp(check, com_argv[i], len) == 0) + return i; + } + return 0; +} + +/** Find the first argument that starts with a hyphen (-). + * \return The index of the argument, or 0 + * if there are no such arguments. + */ +size_t COM_FirstOption(void) +{ + size_t i; + + for (i = 1; i < com_argc; i++) + { + if (com_argv[i][0] == '-')/* options start with a hyphen */ + return i; + } + return 0; +} + /** Parses a string into command-line tokens. * * \param ptext A null-terminated string. Does not need to be diff --git a/src/command.h b/src/command.h index 0880065b..c1b2d591 100644 --- a/src/command.h +++ b/src/command.h @@ -29,6 +29,8 @@ size_t COM_Argc(void); const char *COM_Argv(size_t arg); // if argv > argc, returns empty string char *COM_Args(void); size_t COM_CheckParm(const char *check); // like M_CheckParm :) +size_t COM_CheckPartialParm(const char *check); +size_t COM_FirstOption(void); // match existing command or NULL const char *COM_CompleteCommand(const char *partial, INT32 skips); diff --git a/src/d_netcmd.c b/src/d_netcmd.c index b92bbc42..c46fdc47 100644 --- a/src/d_netcmd.c +++ b/src/d_netcmd.c @@ -2479,56 +2479,6 @@ void D_PickVote(void) SendNetXCmd(XD_PICKVOTE, &buf, 2); } -enum -{ - MAP_COMMAND_FORCE_OPTION, - MAP_COMMAND_GAMETYPE_OPTION, - MAP_COMMAND_NORESETPLAYERS_OPTION, - - NUM_MAP_COMMAND_OPTIONS -}; - -static size_t CheckOptions( - int num_options, - size_t *first_argumentp, - size_t *user_options, - const char ***option_names, - int *option_num_arguments -) -{ - int arguments_used; - size_t first_argument; - - int i; - const char **pp; - const char *name; - size_t n; - - arguments_used = 0; - first_argument = COM_Argc(); - - for (i = 0; i < num_options; ++i) - { - pp = option_names[i]; - name = *pp; - do - { - if (( n = COM_CheckParm(name) )) - { - user_options[i] = n; - arguments_used += 1 + option_num_arguments[i]; - if (n < first_argument) - first_argument = n; - } - } - while (( name = *++pp )) ; - } - - (*first_argumentp) = first_argument; - - return arguments_used; -} - static char * ConcatCommandArgv (int start, int end) { @@ -2572,43 +2522,10 @@ ConcatCommandArgv (int start, int end) // static void Command_Map_f(void) { - const char *force_option_names[] = - { - "-force", - "-f", - NULL - }; - const char *gametype_option_names[] = - { - "-gametype", - "-g", - "-gt", - NULL - }; - const char *noresetplayers_option_names[] = - { - "-noresetplayers", - NULL - }; - const char **option_names[] = - { - force_option_names, - gametype_option_names, - noresetplayers_option_names, - }; - int option_num_arguments[] = - { - 0,/* -force */ - 1,/* -gametype */ - 0,/* -noresetplayers */ - }; - - size_t acceptableargc;/* (this includes the command name itself!) */ - size_t first_argument; - - size_t user_options [NUM_MAP_COMMAND_OPTIONS] = {0}; - - const char *arg_gametype; + size_t first_option; + size_t option_force; + size_t option_gametype; + const char *gametypename; boolean newresetplayers; boolean mustmodifygame; @@ -2632,16 +2549,13 @@ static void Command_Map_f(void) return; } - /* map name + options */ - acceptableargc = 2 + CheckOptions(NUM_MAP_COMMAND_OPTIONS, - &first_argument, - user_options, option_names, option_num_arguments); - - newresetplayers = !user_options[MAP_COMMAND_NORESETPLAYERS_OPTION]; + option_force = COM_CheckPartialParm("-f"); + option_gametype = COM_CheckPartialParm("-g"); + newresetplayers = ! COM_CheckParm("-noresetplayers"); mustmodifygame = !( netgame || multiplayer || majormods ); - if (mustmodifygame && !user_options[MAP_COMMAND_FORCE_OPTION]) + if (mustmodifygame && !option_force) { /* May want to be more descriptive? */ CONS_Printf(M_GetText("Sorry, level change disabled in single player.\n")); @@ -2654,27 +2568,37 @@ static void Command_Map_f(void) return; } - if (user_options[MAP_COMMAND_GAMETYPE_OPTION] && !multiplayer) + if (option_gametype) { - CONS_Printf(M_GetText("You can't switch gametypes in single player!\n")); - return; + if (!multiplayer) + { + CONS_Printf(M_GetText( + "You can't switch gametypes in single player!\n")); + return; + } + else if (COM_Argc() < option_gametype + 2)/* no argument after? */ + { + CONS_Alert(CONS_ERROR, + "No gametype name follows parameter '%s'.\n", + COM_Argv(option_gametype)); + return; + } } - /* If the first argument is an option, you fucked up. */ - if (COM_Argc() < acceptableargc || first_argument == 1) + if (!( first_option = COM_FirstOption() )) + first_option = COM_Argc(); + + if (first_option < 2) { /* I'm going over the fucking lines and I DON'T CAREEEEE */ CONS_Printf("map [-gametype ] [-encore] [-force]:\n"); CONS_Printf(M_GetText( "Warp to a map, by its name, two character code, with optional \"MAP\" prefix, or by its number (though why would you).\n" - "All parameters are case-insensitive.\n" - "* \"-force\" may be shortened to \"-f\".\n" - "* \"-gametype\" may be shortened to \"-g\" or \"-gt\".\n" - "* \"-encore\" may be shortened to \"-e\" or \"-en\".\n")); + "All parameters are case-insensitive and may be abbreviated.\n")); return; } - mapname = ConcatCommandArgv(1, first_argument); + mapname = ConcatCommandArgv(1, first_option); mapnamelen = strlen(mapname); if (mapnamelen == 2)/* maybe two digit code */ @@ -2725,16 +2649,16 @@ static void Command_Map_f(void) realmapname = G_BuildMapTitle(newmapnum); } - if (mustmodifygame && user_options[MAP_COMMAND_FORCE_OPTION]) + if (mustmodifygame && option_force) { G_SetGameModified(false, true); } // new gametype value // use current one by default - if (user_options[MAP_COMMAND_GAMETYPE_OPTION]) + if (option_gametype) { - arg_gametype = COM_Argv(user_options[MAP_COMMAND_GAMETYPE_OPTION] + 1); + gametypename = COM_Argv(option_gametype + 1); for (i = 0; gametype_cons_t[i].strvalue; i++) if (!strcasecmp(gametype_cons_t[i].strvalue, arg_gametype)) @@ -2747,7 +2671,7 @@ static void Command_Map_f(void) if (!gametype_cons_t[i].strvalue) // reached end of the list with no match { - d = atoi(arg_gametype); + d = atoi(gametypename); // assume they gave us a gametype number, which is okay too for (i = 0; gametype_cons_t[i].strvalue != NULL; i++) { @@ -2764,7 +2688,7 @@ static void Command_Map_f(void) newresetplayers = false; // if not forcing and gametypes is the same // don't use a gametype the map doesn't support - if (cv_debug || user_options[MAP_COMMAND_FORCE_OPTION] || cv_skipmapcheck.value) + if (cv_debug || option_force || cv_skipmapcheck.value) fromlevelselect = false; // The player wants us to trek on anyway. Do so. // G_TOLFlag handles both multiplayer gametype and ignores it for !multiplayer else @@ -2806,7 +2730,6 @@ static void Command_Map_f(void) Z_Free(realmapname); } -#undef CHECKPARM /** Receives a map command and changes the map. * From a5bf7719e78ee72f4b8ea4404cccb906733126af Mon Sep 17 00:00:00 2001 From: James R Date: Wed, 13 Nov 2019 14:31:44 -0800 Subject: [PATCH 14/92] Warn if the gametype is not valid at all! (cherry picked from commit 7776c59cddaa4cc09dfdb2f15192a194fda36eb7) --- src/d_netcmd.c | 22 ++++++++++++++-------- 1 file changed, 14 insertions(+), 8 deletions(-) diff --git a/src/d_netcmd.c b/src/d_netcmd.c index c46fdc47..a758256d 100644 --- a/src/d_netcmd.c +++ b/src/d_netcmd.c @@ -2671,15 +2671,21 @@ static void Command_Map_f(void) if (!gametype_cons_t[i].strvalue) // reached end of the list with no match { - d = atoi(gametypename); - // assume they gave us a gametype number, which is okay too - for (i = 0; gametype_cons_t[i].strvalue != NULL; i++) + /* Did they give us a gametype number? That's okay too! */ + if (isdigit(gametypename[0])) { - if (d == gametype_cons_t[i].value) - { - newgametype = gametype_cons_t[i].value; - break; - } + d = atoi(gametypename); + if (d >= 0 && d < NUMGAMETYPES) + newgametype = d; + } + else + { + CONS_Alert(CONS_ERROR, + "'%s' is not a gametype.\n", + gametypename); + Z_Free(realmapname); + Z_Free(mapname); + return; } } } From 19443fec4c89d829dd5d698e4c03c69f41aa7d43 Mon Sep 17 00:00:00 2001 From: James R Date: Tue, 19 Nov 2019 12:06:15 -0800 Subject: [PATCH 15/92] Merge descrepancies --- src/d_netcmd.c | 13 +++---------- 1 file changed, 3 insertions(+), 10 deletions(-) diff --git a/src/d_netcmd.c b/src/d_netcmd.c index a758256d..78b4c907 100644 --- a/src/d_netcmd.c +++ b/src/d_netcmd.c @@ -2660,16 +2660,9 @@ static void Command_Map_f(void) { gametypename = COM_Argv(option_gametype + 1); - for (i = 0; gametype_cons_t[i].strvalue; i++) - if (!strcasecmp(gametype_cons_t[i].strvalue, arg_gametype)) - { - // Don't do any variable setting here. Wait until you get your - // map packet first to avoid sending the same info twice! - newgametype = gametype_cons_t[i].value; - break; - } + newgametype = G_GetGametypeByName(gametypename); - if (!gametype_cons_t[i].strvalue) // reached end of the list with no match + if (newgametype == -1) // reached end of the list with no match { /* Did they give us a gametype number? That's okay too! */ if (isdigit(gametypename[0])) @@ -2690,7 +2683,7 @@ static void Command_Map_f(void) } } - if (!parm_force && newgametype == gametype) // SRB2Kart + if (!option_force && newgametype == gametype) // SRB2Kart newresetplayers = false; // if not forcing and gametypes is the same // don't use a gametype the map doesn't support From dc23895875c40035da3a417869978050a8bdbdf3 Mon Sep 17 00:00:00 2001 From: James R Date: Tue, 19 Nov 2019 12:43:25 -0800 Subject: [PATCH 16/92] Add back the encore parameter --- src/d_netcmd.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/d_netcmd.c b/src/d_netcmd.c index 78b4c907..493eb18e 100644 --- a/src/d_netcmd.c +++ b/src/d_netcmd.c @@ -2525,6 +2525,7 @@ static void Command_Map_f(void) size_t first_option; size_t option_force; size_t option_gametype; + size_t option_encore; const char *gametypename; boolean newresetplayers; @@ -2551,6 +2552,7 @@ static void Command_Map_f(void) option_force = COM_CheckPartialParm("-f"); option_gametype = COM_CheckPartialParm("-g"); + option_encore = COM_CheckPartialParm("-e"); newresetplayers = ! COM_CheckParm("-noresetplayers"); mustmodifygame = !( netgame || multiplayer || majormods ); @@ -2714,7 +2716,7 @@ static void Command_Map_f(void) return; } - if (parm_encore) + if (option_encore) { newencoremode = ! newencoremode; if (! M_SecretUnlocked(SECRET_ENCORE) && newencoremode) From 05246f36a61e37584b58bd7b7bb2864563fea537 Mon Sep 17 00:00:00 2001 From: James R Date: Tue, 19 Nov 2019 11:16:17 -0800 Subject: [PATCH 17/92] Fix strcasestr SIGSEGV in the case of only upper case strchr returning NULL (cherry picked from commit c5d73e37bbc0c9fa8fd1dd874ac782b655068576) --- src/strcasestr.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/strcasestr.c b/src/strcasestr.c index 2077dc3f..86c7ec5b 100644 --- a/src/strcasestr.c +++ b/src/strcasestr.c @@ -75,7 +75,7 @@ strcasestr (const char *s, const char *q) if (!( (intptr_t)up|(intptr_t)lp )) return 0; - if (!lp || up < lp) + if (!lp || ( up && up < lp )) { ppa = &up; ppb = &lp; From 401f30f27d0c8732b18259b047f78f8faea400c6 Mon Sep 17 00:00:00 2001 From: James R Date: Tue, 19 Nov 2019 12:25:50 -0800 Subject: [PATCH 18/92] Check for out of range gametype on map change (cherry picked from commit 9b96964cbbfc20565a8cfdb741f4927f36df7899) --- src/d_netcmd.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/d_netcmd.c b/src/d_netcmd.c index 493eb18e..c21e5c28 100644 --- a/src/d_netcmd.c +++ b/src/d_netcmd.c @@ -2778,7 +2778,9 @@ static void Got_Mapcmd(UINT8 **cp, INT32 playernum) lastgametype = gametype; gametype = READUINT8(*cp); - if (gametype != lastgametype) + if (gametype < 0 || gametype >= NUMGAMETYPES) + gametype = lastgametype; + else if (gametype != lastgametype) D_GameTypeChanged(lastgametype); // emulate consvar_t behavior for gametype if (!G_RaceGametype()) From e5fa66a2a9cb8929dd056f8c61ebc9e070efaf15 Mon Sep 17 00:00:00 2001 From: James R Date: Tue, 19 Nov 2019 12:32:02 -0800 Subject: [PATCH 19/92] Warn if gametype number is out of range to map command (cherry picked from commit 6ffb18c4b157f9efecec6a104ef930dd71f8b986) --- src/d_netcmd.c | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/d_netcmd.c b/src/d_netcmd.c index c21e5c28..1906f964 100644 --- a/src/d_netcmd.c +++ b/src/d_netcmd.c @@ -2672,6 +2672,17 @@ static void Command_Map_f(void) d = atoi(gametypename); if (d >= 0 && d < NUMGAMETYPES) newgametype = d; + else + { + CONS_Alert(CONS_ERROR, + "Gametype number %d is out of range. Use a number between" + " 0 and %d inclusive. ...Or just use the name. :v\n", + d, + NUMGAMETYPES-1); + Z_Free(realmapname); + Z_Free(mapname); + return; + } } else { From bd253c0e7ea946da4cb1beb6c44360330edbcdc6 Mon Sep 17 00:00:00 2001 From: James R Date: Sat, 7 Dec 2019 17:03:46 -0800 Subject: [PATCH 20/92] Use a third variable of XOR nonsense (cherry picked from commit d5816d44f337c6eb82d937959f971fe45a5eec2a) --- src/strcasestr.c | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/src/strcasestr.c b/src/strcasestr.c index 86c7ec5b..4ff778bf 100644 --- a/src/strcasestr.c +++ b/src/strcasestr.c @@ -23,13 +23,6 @@ ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#define SWAP( a, b ) \ -(\ - (a) ^= (b),\ - (b) ^= (a),\ - (a) ^= (b)\ -) - static inline int trycmp (char **pp, char *cp, const char *q, size_t qn) @@ -45,8 +38,16 @@ trycmp (char **pp, char *cp, static inline void swapp (char ***ppap, char ***ppbp, char **cpap, char **cpbp) { - SWAP(*(intptr_t *)ppap, *(intptr_t *)ppbp); - SWAP(*(intptr_t *)cpap, *(intptr_t *)cpbp); + char **pp; + char *p; + + pp = *ppap; + *ppap = *ppbp; + *ppbp = pp; + + p = *cpap; + *cpap = *cpbp; + *cpbp = p; } char * From 1adae3deea3e4868dc2040d061ca849a326603b4 Mon Sep 17 00:00:00 2001 From: James R Date: Mon, 9 Dec 2019 20:04:09 -0800 Subject: [PATCH 21/92] Maybe I'm mistaken but this probably isn't needed (cherry picked from commit 6464df9876e472d1210aabce4237d02af38377e1) --- src/d_netcmd.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/d_netcmd.c b/src/d_netcmd.c index 1906f964..8953e209 100644 --- a/src/d_netcmd.c +++ b/src/d_netcmd.c @@ -2639,7 +2639,7 @@ static void Command_Map_f(void) } } - if (newmapnum == 0 || !mapheaderinfo[newmapnum-1]) + if (newmapnum == 0) { CONS_Alert(CONS_ERROR, M_GetText("Could not find any map described as '%s'.\n"), mapname); Z_Free(mapname); From 52a863d1fde8d4d5ca7a3f385dd210e0a6398bfe Mon Sep 17 00:00:00 2001 From: James R Date: Sun, 29 Dec 2019 02:14:02 -0800 Subject: [PATCH 22/92] Split map code checking from Command_Map_f (cherry picked from commit 19aafbfd0b9bde40809ca91195d4636899504708) --- src/d_netcmd.c | 45 +---------------------------------------- src/g_game.c | 54 ++++++++++++++++++++++++++++++++++++++++++++++++++ src/g_game.h | 3 +++ 3 files changed, 58 insertions(+), 44 deletions(-) diff --git a/src/d_netcmd.c b/src/d_netcmd.c index 8953e209..85938522 100644 --- a/src/d_netcmd.c +++ b/src/d_netcmd.c @@ -2530,19 +2530,16 @@ static void Command_Map_f(void) boolean newresetplayers; boolean mustmodifygame; - boolean usemapcode = false; INT32 newmapnum; char * mapname; - size_t mapnamelen; char *realmapname = NULL; INT32 newgametype = gametype; boolean newencoremode = cv_kartencore.value; INT32 d; - char *p; if (client && !IsPlayerAdmin(consoleplayer)) { @@ -2601,43 +2598,8 @@ static void Command_Map_f(void) } mapname = ConcatCommandArgv(1, first_option); - mapnamelen = strlen(mapname); - if (mapnamelen == 2)/* maybe two digit code */ - { - if (( newmapnum = M_MapNumber(mapname[0], mapname[1]) )) - usemapcode = true; - } - else if (mapnamelen == 5 && strnicmp(mapname, "MAP", 3) == 0) - { - if (( newmapnum = M_MapNumber(mapname[3], mapname[4]) ) == 0) - { - CONS_Alert(CONS_ERROR, M_GetText("Invalid map code '%s'.\n"), mapname); - Z_Free(mapname); - return; - } - usemapcode = true; - } - - if (!usemapcode) - { - /* Now detect map number in base 10, which no one asked for. */ - newmapnum = strtol(mapname, &p, 10); - if (*p == '\0')/* we got it */ - { - if (newmapnum < 1 || newmapnum > NUMMAPS) - { - CONS_Alert(CONS_ERROR, M_GetText("Invalid map number %d.\n"), newmapnum); - Z_Free(mapname); - return; - } - usemapcode = true; - } - else - { - newmapnum = G_FindMap(mapname, &realmapname, NULL, NULL); - } - } + newmapnum = G_FindMapByNameOrCode(mapname, &realmapname); if (newmapnum == 0) { @@ -2646,11 +2608,6 @@ static void Command_Map_f(void) return; } - if (usemapcode) - { - realmapname = G_BuildMapTitle(newmapnum); - } - if (mustmodifygame && option_force) { G_SetGameModified(false, true); diff --git a/src/g_game.c b/src/g_game.c index 06699c6b..da5dac94 100644 --- a/src/g_game.c +++ b/src/g_game.c @@ -4928,6 +4928,60 @@ void G_FreeMapSearch(mapsearchfreq_t *freq, INT32 freqc) Z_Free(freq); } +INT32 G_FindMapByNameOrCode(const char *mapname, char **realmapnamep) +{ + boolean usemapcode = false; + + INT32 newmapnum; + + size_t mapnamelen; + + char *p; + + mapnamelen = strlen(mapname); + + if (mapnamelen == 2)/* maybe two digit code */ + { + if (( newmapnum = M_MapNumber(mapname[0], mapname[1]) )) + usemapcode = true; + } + else if (mapnamelen == 5 && strnicmp(mapname, "MAP", 3) == 0) + { + if (( newmapnum = M_MapNumber(mapname[3], mapname[4]) ) == 0) + { + CONS_Alert(CONS_ERROR, M_GetText("Invalid map code '%s'.\n"), mapname); + return 0; + } + usemapcode = true; + } + + if (!usemapcode) + { + /* Now detect map number in base 10, which no one asked for. */ + newmapnum = strtol(mapname, &p, 10); + if (*p == '\0')/* we got it */ + { + if (newmapnum < 1 || newmapnum > NUMMAPS) + { + CONS_Alert(CONS_ERROR, M_GetText("Invalid map number %d.\n"), newmapnum); + return 0; + } + usemapcode = true; + } + else + { + newmapnum = G_FindMap(mapname, realmapnamep, NULL, NULL); + } + } + + if (usemapcode) + { + (*realmapnamep) = G_BuildMapTitle(newmapnum); + } + + return newmapnum; +} + // // DEMO RECORDING // diff --git a/src/g_game.h b/src/g_game.h index 1febcac7..2b2f7e2c 100644 --- a/src/g_game.h +++ b/src/g_game.h @@ -193,6 +193,9 @@ INT32 G_FindMap(const char *query, char **foundmapnamep, mapsearchfreq_t **freqp, INT32 *freqc); void G_FreeMapSearch(mapsearchfreq_t *freq, INT32 freqc); +/* Match map name by search + 2 digit map code or map number. */ +INT32 G_FindMapByNameOrCode(const char *query, char **foundmapnamep); + // XMOD spawning mapthing_t *G_FindCTFStart(INT32 playernum); mapthing_t *G_FindMatchStart(INT32 playernum); From 9a2ff50f5a57bedac7104c36517f2d50e366bcda Mon Sep 17 00:00:00 2001 From: James R Date: Sun, 29 Dec 2019 02:15:48 -0800 Subject: [PATCH 23/92] Who cares? (cherry picked from commit 2ffff56b391235257c65eb23e539926c4da6ac62) --- src/g_game.c | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/src/g_game.c b/src/g_game.c index da5dac94..46a0cc5a 100644 --- a/src/g_game.c +++ b/src/g_game.c @@ -4947,12 +4947,8 @@ INT32 G_FindMapByNameOrCode(const char *mapname, char **realmapnamep) } else if (mapnamelen == 5 && strnicmp(mapname, "MAP", 3) == 0) { - if (( newmapnum = M_MapNumber(mapname[3], mapname[4]) ) == 0) - { - CONS_Alert(CONS_ERROR, M_GetText("Invalid map code '%s'.\n"), mapname); - return 0; - } - usemapcode = true; + if (( newmapnum = M_MapNumber(mapname[3], mapname[4]) )) + usemapcode = true; } if (!usemapcode) From 9885ab164a4a95c120e392a6502088cd5bd84111 Mon Sep 17 00:00:00 2001 From: James R Date: Sun, 29 Dec 2019 02:16:37 -0800 Subject: [PATCH 24/92] I'm still an idiot (cherry picked from commit 0a014755b98fb909aed4a12feba00744bc2af225) --- src/g_game.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/g_game.c b/src/g_game.c index 46a0cc5a..ecc57fa1 100644 --- a/src/g_game.c +++ b/src/g_game.c @@ -4972,7 +4972,8 @@ INT32 G_FindMapByNameOrCode(const char *mapname, char **realmapnamep) if (usemapcode) { - (*realmapnamep) = G_BuildMapTitle(newmapnum); + if (realmapnamep) + (*realmapnamep) = G_BuildMapTitle(newmapnum); } return newmapnum; From 9e33e16d269278cf01e086bbde9de37b16fb4a84 Mon Sep 17 00:00:00 2001 From: James R Date: Sun, 29 Dec 2019 02:31:14 -0800 Subject: [PATCH 25/92] If we move the -warp code down, map searching can be used (cherry picked from commit 404f3c13e4ed1d9e1a375bba1c2ae59b4f694e0e) --- src/d_main.c | 34 ++++++++++++++-------------------- 1 file changed, 14 insertions(+), 20 deletions(-) diff --git a/src/d_main.c b/src/d_main.c index 2c02565d..340165cd 100644 --- a/src/d_main.c +++ b/src/d_main.c @@ -1160,26 +1160,6 @@ void D_SRB2Main(void) if (M_CheckParm("-server") || dedicated) netgame = server = true; - if (M_CheckParm("-warp") && M_IsNextParm()) - { - const char *word = M_GetNextParm(); - char ch; // use this with sscanf to catch non-digits with - if (fastncmp(word, "MAP", 3)) // MAPxx name - pstartmap = M_MapNumber(word[3], word[4]); - else if (sscanf(word, "%d%c", &pstartmap, &ch) != 1) // a plain number - I_Error("Cannot warp to map %s (invalid map name)\n", word); - // Don't check if lump exists just yet because the wads haven't been loaded! - // Just do a basic range check here. - if (pstartmap < 1 || pstartmap > NUMMAPS) - I_Error("Cannot warp to map %d (out of range)\n", pstartmap); - else - { - if (!M_CheckParm("-server")) - G_SetGameModified(true, true); - autostart = true; - } - } - CONS_Printf("Z_Init(): Init zone memory allocation daemon. \n"); Z_Init(); @@ -1302,6 +1282,20 @@ void D_SRB2Main(void) } } + if (M_CheckParm("-warp") && M_IsNextParm()) + { + const char *word = M_GetNextParm(); + pstartmap = G_FindMapByNameOrCode(word, 0); + if (! pstartmap) + I_Error("Cannot find a map remotely named '%s'\n", word); + else + { + if (!M_CheckParm("-server")) + G_SetGameModified(true, true); + autostart = true; + } + } + cht_Init(); //---------------------------------------------------- READY SCREEN From 93a76c10de5d24c8717ac98d1dd39a957fa295f2 Mon Sep 17 00:00:00 2001 From: James R Date: Sun, 29 Dec 2019 02:44:27 -0800 Subject: [PATCH 26/92] Don't let us warp to a map that doesn't exist (really!) Okay so 6464df9876e472d1210aabce4237d02af38377e1, I WAS mistaken! Except that's not how you check for a map's existence, at least not how the old map command did it. (cherry picked from commit 566b4a1626399b1c1621bf8b4ab1f8426c789c36) --- src/g_game.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/g_game.c b/src/g_game.c index ecc57fa1..0c850e6a 100644 --- a/src/g_game.c +++ b/src/g_game.c @@ -4972,6 +4972,10 @@ INT32 G_FindMapByNameOrCode(const char *mapname, char **realmapnamep) if (usemapcode) { + /* we can't check mapheaderinfo for this hahahaha */ + if (W_CheckNumForName(G_BuildMapName(newmapnum)) == LUMPERROR) + return 0; + if (realmapnamep) (*realmapnamep) = G_BuildMapTitle(newmapnum); } From 31237935da80205fac62915240b41cd78119358d Mon Sep 17 00:00:00 2001 From: Monster Iestyn Date: Thu, 20 Feb 2020 20:31:11 +0000 Subject: [PATCH 27/92] move -warp code to a later part of D_SRB2Main so G_LoadGameData isn't upset by G_SetGameModified (cherry picked from commit 903cc311a711d80436215e7f7c897a447daf2539) --- src/d_main.c | 30 ++++++++++++++++-------------- 1 file changed, 16 insertions(+), 14 deletions(-) diff --git a/src/d_main.c b/src/d_main.c index 340165cd..6d3cc72b 100644 --- a/src/d_main.c +++ b/src/d_main.c @@ -1282,20 +1282,6 @@ void D_SRB2Main(void) } } - if (M_CheckParm("-warp") && M_IsNextParm()) - { - const char *word = M_GetNextParm(); - pstartmap = G_FindMapByNameOrCode(word, 0); - if (! pstartmap) - I_Error("Cannot find a map remotely named '%s'\n", word); - else - { - if (!M_CheckParm("-server")) - G_SetGameModified(true, true); - autostart = true; - } - } - cht_Init(); //---------------------------------------------------- READY SCREEN @@ -1353,6 +1339,22 @@ void D_SRB2Main(void) //------------------------------------------------ COMMAND LINE PARAMS + // this must be done after loading gamedata, to avoid setting off the corrupted gamedata flag in G_LoadGameData + // -- Monster Iestyn 20/02/20 + if (M_CheckParm("-warp") && M_IsNextParm()) + { + const char *word = M_GetNextParm(); + pstartmap = G_FindMapByNameOrCode(word, 0); + if (! pstartmap) + I_Error("Cannot find a map remotely named '%s'\n", word); + else + { + if (!M_CheckParm("-server")) + G_SetGameModified(true, true); + autostart = true; + } + } + // Initialize CD-Audio if (M_CheckParm("-usecd") && !dedicated) I_InitCD(); From 8a5fd38bca118670f438a4ead45754572cf39125 Mon Sep 17 00:00:00 2001 From: Monster Iestyn Date: Thu, 20 Feb 2020 20:38:01 +0000 Subject: [PATCH 28/92] clarify the situation a bit more in the comments (cherry picked from commit 5c33ff04581abe44e1c8ad03e680ff918b9dc0d2) --- src/d_main.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/d_main.c b/src/d_main.c index 6d3cc72b..2851a968 100644 --- a/src/d_main.c +++ b/src/d_main.c @@ -1339,7 +1339,8 @@ void D_SRB2Main(void) //------------------------------------------------ COMMAND LINE PARAMS - // this must be done after loading gamedata, to avoid setting off the corrupted gamedata flag in G_LoadGameData + // this must be done after loading gamedata, + // to avoid setting off the corrupted gamedata code in G_LoadGameData if a SOC with custom gamedata is added // -- Monster Iestyn 20/02/20 if (M_CheckParm("-warp") && M_IsNextParm()) { From 59fb3ed900dd4e90b39b75cd7ba247180f94a662 Mon Sep 17 00:00:00 2001 From: Ashnal Date: Sat, 10 Sep 2022 13:29:18 -0400 Subject: [PATCH 29/92] Attempt to fix use after free bug with precipitation mobjs on netgame load --- src/p_mobj.c | 28 +++++++++++++++++++++------- src/p_saveg.c | 8 +++++++- 2 files changed, 28 insertions(+), 8 deletions(-) diff --git a/src/p_mobj.c b/src/p_mobj.c index 530dd37d..befcf04f 100644 --- a/src/p_mobj.c +++ b/src/p_mobj.c @@ -10298,20 +10298,34 @@ void P_RemovePrecipMobj(precipmobj_t *mobj) void P_RemoveSavegameMobj(mobj_t *mobj) { // unlink from sector and block lists - P_UnsetThingPosition(mobj); - - // Remove touching_sectorlist from mobj. - if (sector_list) + if (((thinker_t *)mobj)->function.acp1 == (actionf_p1)P_NullPrecipThinker) { - P_DelSeclist(sector_list); - sector_list = NULL; + P_UnsetPrecipThingPosition((precipmobj_t *)mobj); + + if (precipsector_list) + { + P_DelPrecipSeclist(precipsector_list); + precipsector_list = NULL; + } + } + else + { + // unlink from sector and block lists + P_UnsetThingPosition(mobj); + + // Remove touching_sectorlist from mobj. + if (sector_list) + { + P_DelSeclist(sector_list); + sector_list = NULL; + } } // stop any playing sound S_StopSound(mobj); // free block - P_RemoveThinker((thinker_t *)mobj); + P_RemoveThinkerDelayed((thinker_t *)mobj); // Call directly here since we are calling P_InitThinkers R_RemoveMobjInterpolator(mobj); } diff --git a/src/p_saveg.c b/src/p_saveg.c index f79b3f57..c3455498 100644 --- a/src/p_saveg.c +++ b/src/p_saveg.c @@ -2166,6 +2166,12 @@ static void LoadMobjThinker(actionf_p1 thinker) mobj->player->viewz = mobj->player->mo->z + mobj->player->viewheight; } + if (mobj->type == MT_SKYBOX) + if (mobj->spawnpoint->options & MTF_OBJECTSPECIAL) + skyboxmo[1] = mobj; + else + skyboxmo[0] = mobj; + P_AddThinker(&mobj->thinker); if (diff2 & MD2_WAYPOINTCAP) @@ -2666,7 +2672,7 @@ static void P_NetUnArchiveThinkers(void) { next = currentthinker->next; - if (currentthinker->function.acp1 == (actionf_p1)P_MobjThinker) + if (currentthinker->function.acp1 == (actionf_p1)P_MobjThinker || currentthinker->function.acp1 == (actionf_p1)P_NullPrecipThinker) P_RemoveSavegameMobj((mobj_t *)currentthinker); // item isn't saved, don't remove it else Z_Free(currentthinker); From 3d311eeae3f3404bf4911d68f9aefe9d4da81544 Mon Sep 17 00:00:00 2001 From: Ashnal Date: Sat, 10 Sep 2022 13:30:24 -0400 Subject: [PATCH 30/92] Fix compiler warning --- src/p_saveg.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/p_saveg.c b/src/p_saveg.c index c3455498..a2656a46 100644 --- a/src/p_saveg.c +++ b/src/p_saveg.c @@ -2167,10 +2167,12 @@ static void LoadMobjThinker(actionf_p1 thinker) } if (mobj->type == MT_SKYBOX) + { if (mobj->spawnpoint->options & MTF_OBJECTSPECIAL) skyboxmo[1] = mobj; else skyboxmo[0] = mobj; + } P_AddThinker(&mobj->thinker); From 078e249d557615bb4fd42719cf8887a1c760785b Mon Sep 17 00:00:00 2001 From: Alam Ed Arias Date: Sat, 24 Sep 2022 15:58:24 +0000 Subject: [PATCH 31/92] CircleCI: Disable NONET builds --- .circleci/config.yml | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index e7916074..8e2dcbe5 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -42,15 +42,15 @@ jobs: paths: - /var/cache/apt/archives - checkout - - run: - name: Compile without network support and BLUA - command: make -C src LINUX=1 ERRORMODE=1 -k NONET=1 NO_LUA=1 - - run: - name: wipe build - command: make -C src LINUX=1 cleandep - - run: - name: rebuild depend - command: make -C src LINUX=1 clean + #- run: + # name: Compile without network support and BLUA + # command: make -C src LINUX=1 ERRORMODE=1 -k NONET=1 NO_LUA=1 + #- run: + # name: wipe build + # command: make -C src LINUX=1 cleandep + #- run: + # name: rebuild depend + # command: make -C src LINUX=1 clean - restore_cache: keys: - v1-SRB2-{{ .Branch }}-{{ checksum "objs/Linux/SDL/Release/depend.dep" }} From 984d44bb57a567a6c6e080e446e4ba02313c5fd7 Mon Sep 17 00:00:00 2001 From: Alam Ed Arias Date: Sat, 24 Sep 2022 16:13:27 +0000 Subject: [PATCH 32/92] CircleCI: we need the depend.dep file for the cache --- .circleci/config.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 8e2dcbe5..10ab56a1 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -45,9 +45,9 @@ jobs: #- run: # name: Compile without network support and BLUA # command: make -C src LINUX=1 ERRORMODE=1 -k NONET=1 NO_LUA=1 - #- run: - # name: wipe build - # command: make -C src LINUX=1 cleandep + - run: + name: wipe build + command: make -C src LINUX=1 cleandep #- run: # name: rebuild depend # command: make -C src LINUX=1 clean From 2db57c72e53c29db74743ea44809d62498ccfc81 Mon Sep 17 00:00:00 2001 From: Alam Ed Arias Date: Sat, 24 Sep 2022 16:28:11 +0000 Subject: [PATCH 33/92] CircleCI: run the clean step to build the 32-bit LINUX depend.dep --- .circleci/config.yml | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 10ab56a1..c08a7f6d 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -45,12 +45,12 @@ jobs: #- run: # name: Compile without network support and BLUA # command: make -C src LINUX=1 ERRORMODE=1 -k NONET=1 NO_LUA=1 - - run: - name: wipe build - command: make -C src LINUX=1 cleandep #- run: - # name: rebuild depend - # command: make -C src LINUX=1 clean + # name: wipe build + # command: make -C src LINUX=1 cleandep + - run: + name: rebuild depend + command: make -C src LINUX=1 clean - restore_cache: keys: - v1-SRB2-{{ .Branch }}-{{ checksum "objs/Linux/SDL/Release/depend.dep" }} From c24a0d0a47c92670fe51fcb08fab7b50a55e0966 Mon Sep 17 00:00:00 2001 From: Ashnal Date: Thu, 29 Sep 2022 15:43:50 -0400 Subject: [PATCH 34/92] Remove usage of currentthinker from direct removal It's designed to be referenced from P_RunTHinkers, whjich we aren't doing --- src/p_mobj.c | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/src/p_mobj.c b/src/p_mobj.c index befcf04f..b2a9b09b 100644 --- a/src/p_mobj.c +++ b/src/p_mobj.c @@ -10323,10 +10323,17 @@ void P_RemoveSavegameMobj(mobj_t *mobj) // stop any playing sound S_StopSound(mobj); + R_RemoveMobjInterpolator(mobj); // free block - P_RemoveThinkerDelayed((thinker_t *)mobj); // Call directly here since we are calling P_InitThinkers - R_RemoveMobjInterpolator(mobj); + // Here we use the same code as R_RemoveThinkerDelayed, but without reference counting (we're removing everything so it shouldn't matter) and without touching currentthinker since we aren't in P_RunThinkers + { + thinker_t *thinker = (thinker_t *)mobj; + thinker_t *next = thinker->next; + (next->prev = thinker->prev)->next = next; + R_DestroyLevelInterpolators(thinker); + Z_Free(thinker); + } } static CV_PossibleValue_t respawnitemtime_cons_t[] = {{1, "MIN"}, {300, "MAX"}, {0, NULL}}; From f8d71450e5e6649e36f82448e92626b3bed713fb Mon Sep 17 00:00:00 2001 From: Ashnal Date: Thu, 29 Sep 2022 19:22:53 -0400 Subject: [PATCH 35/92] Unlink non-mobj and non-precip thinkers when loading and freeing Move globalweather to before P_SpawnSpecials so that specials can properly change weather and have it communicated in savegames --- src/p_mobj.c | 1 - src/p_saveg.c | 6 ++++++ src/p_setup.c | 4 ++-- 3 files changed, 8 insertions(+), 3 deletions(-) diff --git a/src/p_mobj.c b/src/p_mobj.c index b2a9b09b..14800481 100644 --- a/src/p_mobj.c +++ b/src/p_mobj.c @@ -10331,7 +10331,6 @@ void P_RemoveSavegameMobj(mobj_t *mobj) thinker_t *thinker = (thinker_t *)mobj; thinker_t *next = thinker->next; (next->prev = thinker->prev)->next = next; - R_DestroyLevelInterpolators(thinker); Z_Free(thinker); } } diff --git a/src/p_saveg.c b/src/p_saveg.c index a2656a46..75806003 100644 --- a/src/p_saveg.c +++ b/src/p_saveg.c @@ -2677,7 +2677,11 @@ static void P_NetUnArchiveThinkers(void) if (currentthinker->function.acp1 == (actionf_p1)P_MobjThinker || currentthinker->function.acp1 == (actionf_p1)P_NullPrecipThinker) P_RemoveSavegameMobj((mobj_t *)currentthinker); // item isn't saved, don't remove it else + { + (next->prev = currentthinker->prev)->next = next; + R_DestroyLevelInterpolators(currentthinker); Z_Free(currentthinker); + } } // we don't want the removed mobjs to come back @@ -3090,6 +3094,7 @@ static inline void P_NetArchiveSpecials(void) // Sky number WRITEINT32(save_p, globallevelskynum); + CONS_Printf("globalweather write %d", globalweather); // Current global weather type WRITEUINT8(save_p, globalweather); @@ -3126,6 +3131,7 @@ static void P_NetUnArchiveSpecials(void) P_SetupLevelSky(j, true); globalweather = READUINT8(save_p); + CONS_Printf("globalweather read %d", globalweather); if (globalweather) { diff --git a/src/p_setup.c b/src/p_setup.c index 328a3ff3..4f4140df 100644 --- a/src/p_setup.c +++ b/src/p_setup.c @@ -3124,14 +3124,14 @@ boolean P_SetupLevel(boolean skipprecip) if (!playerstarts[numcoopstarts]) break; + globalweather = mapheaderinfo[gamemap-1]->weather; + // set up world state P_SpawnSpecials(fromnetsave); if (loadprecip) // ugly hack for P_NetUnArchiveMisc (and P_LoadNetGame) P_SpawnPrecipitation(); - globalweather = mapheaderinfo[gamemap-1]->weather; - #ifdef HWRENDER // not win32 only 19990829 by Kin if (rendermode != render_soft && rendermode != render_none) { From 78ad817cf1afc6b230927805c300d249c785ce78 Mon Sep 17 00:00:00 2001 From: Ashnal Date: Thu, 29 Sep 2022 19:26:42 -0400 Subject: [PATCH 36/92] Forgot to remove my debug prints --- src/p_saveg.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/p_saveg.c b/src/p_saveg.c index 75806003..e3bc51d0 100644 --- a/src/p_saveg.c +++ b/src/p_saveg.c @@ -3094,7 +3094,6 @@ static inline void P_NetArchiveSpecials(void) // Sky number WRITEINT32(save_p, globallevelskynum); - CONS_Printf("globalweather write %d", globalweather); // Current global weather type WRITEUINT8(save_p, globalweather); @@ -3131,7 +3130,6 @@ static void P_NetUnArchiveSpecials(void) P_SetupLevelSky(j, true); globalweather = READUINT8(save_p); - CONS_Printf("globalweather read %d", globalweather); if (globalweather) { From bd4150b90ee358c2bacbe53fe46781d3b7ed9d86 Mon Sep 17 00:00:00 2001 From: Ashnal Date: Mon, 3 Oct 2022 02:07:36 -0400 Subject: [PATCH 37/92] Fixes an issue where mobjs with shadows would never get freed, due to their reference count getting reset after having their shadows spawned, resulting in a reference count of -1 and the mobj never being freed, or a use-after-free during the shadow's thinker. Also adds some P_SetTargets to P_BlockThingsIterator to fix an inconsistency I noticed while investigating this. --- src/p_maputl.c | 4 ++++ src/p_mobj.c | 6 +++--- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/src/p_maputl.c b/src/p_maputl.c index 760e45c4..17df33a9 100644 --- a/src/p_maputl.c +++ b/src/p_maputl.c @@ -1034,7 +1034,10 @@ boolean P_BlockThingsIterator(INT32 x, INT32 y, boolean (*func)(mobj_t *)) { P_SetTarget(&bnext, mobj->bnext); // We want to note our reference to bnext here incase it is MF_NOTHINK and gets removed! if (!func(mobj)) + { + P_SetTarget(&bnext, NULL); return false; + } if (P_MobjWasRemoved(tmthing) // func just popped our tmthing, cannot continue. || (bnext && P_MobjWasRemoved(bnext))) // func just broke blockmap chain, cannot continue. { @@ -1042,6 +1045,7 @@ boolean P_BlockThingsIterator(INT32 x, INT32 y, boolean (*func)(mobj_t *)) return true; } } + P_SetTarget(&bnext, NULL); return true; } diff --git a/src/p_mobj.c b/src/p_mobj.c index 530dd37d..bfdb5a7f 100644 --- a/src/p_mobj.c +++ b/src/p_mobj.c @@ -9893,6 +9893,9 @@ mobj_t *P_SpawnMobj(fixed_t x, fixed_t y, fixed_t z, mobjtype_t type) break; } + if (!(mobj->flags & MF_NOTHINK)) + P_AddThinker(&mobj->thinker); // Needs to come before the shadow spawn, or else the shadow's reference gets forgotten + switch (mobj->type) { case MT_PLAYER: @@ -9916,9 +9919,6 @@ mobj_t *P_SpawnMobj(fixed_t x, fixed_t y, fixed_t z, mobjtype_t type) break; } - if (!(mobj->flags & MF_NOTHINK)) - P_AddThinker(&mobj->thinker); - // Call action functions when the state is set if (st->action.acp1 && (mobj->flags & MF_RUNSPAWNFUNC)) { From 2473c7d7cc3e1ad32587bce613b30a1a36782eff Mon Sep 17 00:00:00 2001 From: Eidolon Date: Wed, 26 Oct 2022 00:59:35 +0000 Subject: [PATCH 38/92] Merge branch 'mobj-jitter' into 'next' Ensure view interpolates between T-1 to T See merge request KartKrew/Kart-Public!317 --- src/p_tick.c | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/src/p_tick.c b/src/p_tick.c index 5386b1db..d70c491d 100644 --- a/src/p_tick.c +++ b/src/p_tick.c @@ -23,6 +23,7 @@ #include "lua_script.h" #include "lua_hook.h" #include "k_kart.h" +#include "r_main.h" #include "r_fps.h" // Object place @@ -811,6 +812,20 @@ void P_Ticker(boolean run) { R_UpdateLevelInterpolators(); R_UpdateViewInterpolation(); + + // Hack: ensure newview is assigned every tic. + // Ensures view interpolation is T-1 to T in poor network conditions + // We need a better way to assign view state decoupled from game logic + for (i = 0; i <= splitscreen; i++) + { + player_t *player = &players[displayplayers[i]]; + BOOL skyVisible = skyVisiblePerPlayer[i]; + if (skyVisible && skyboxmo[0] && cv_skybox.value) + { + R_SkyboxFrame(player); + } + R_SetupFrame(player, (skyboxmo[0] && cv_skybox.value)); + } } P_MapEnd(); From e7c16a13b70d4847b840b3ce00aeb3729de3d44f Mon Sep 17 00:00:00 2001 From: Eidolon Date: Wed, 26 Oct 2022 03:04:51 +0000 Subject: [PATCH 39/92] Merge branch 'splitscreen-hudhook-fix' into 'next' Clear and draw all game hud hook calls to 1 list See merge request KartKrew/Kart-Public!318 (cherry picked from commit 7e17eb8591afa9487fd311a6f111bd2ed42b67e0) 2cffc9b4 Clear and draw all game hud hook calls to 1 list --- src/st_stuff.c | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/src/st_stuff.c b/src/st_stuff.c index 4a9accf0..0c91ad71 100644 --- a/src/st_stuff.c +++ b/src/st_stuff.c @@ -1967,12 +1967,10 @@ static void ST_overlayDrawer(void) { if (renderisnewtic) { - LUA_HUD_ClearDrawList(luahuddrawlist_game); LUAh_GameHUD(stplyr, luahuddrawlist_game); } - LUA_HUD_DrawList(luahuddrawlist_game); } -#endif +#endif // HAVE_BLUA // draw level title Tails if (*mapheaderinfo[gamemap-1]->lvlttl != '\0' && !(hu_showscores && (netgame || multiplayer) && !mapreset) @@ -2166,6 +2164,12 @@ void ST_Drawer(void) if (st_overlay) { +#ifdef HAVE_BLUA + if (renderisnewtic) + { + LUA_HUD_ClearDrawList(luahuddrawlist_game); + } +#endif // HAVE_BLUA // No deadview! for (i = 0; i <= splitscreen; i++) { @@ -2173,6 +2177,10 @@ void ST_Drawer(void) ST_overlayDrawer(); } +#ifdef HAVE_BLUA + LUA_HUD_DrawList(luahuddrawlist_game); +#endif // HAVE_BLUA + // draw Midnight Channel's overlay ontop if (mapheaderinfo[gamemap-1]->typeoflevel & TOL_TV) // Very specific Midnight Channel stuff. ST_MayonakaStatic(); From 33e35b7e13e6eb66e6ee0bf87a360b61c2926adf Mon Sep 17 00:00:00 2001 From: toaster Date: Wed, 26 Oct 2022 13:50:35 +0100 Subject: [PATCH 40/92] Fix ping measurement off-by-one that slightly underestimates ping measurements --- 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 bb2364c5..aa34c5a7 100644 --- a/src/d_clisrv.c +++ b/src/d_clisrv.c @@ -6054,7 +6054,7 @@ static inline void PingUpdate(void) if (nodeingame[i]) HSendPacket(i, true, 0, sizeof(INT32) * (MAXPLAYERS+1)); - pingmeasurecount = 1; //Reset count + pingmeasurecount = 0; //Reset count } static tic_t gametime = 0; From ab68f0dadd203dce539a85024b6fb72d7e809e47 Mon Sep 17 00:00:00 2001 From: toaster Date: Wed, 26 Oct 2022 20:27:32 +0100 Subject: [PATCH 41/92] Rewrite ping timeout - ALWAYS kick someone who's about to stop the server because they're about to overrun TICQUEUE, even if they're in the joiner grace period - Reduce the joiner grace period for normal ping limit to 10 seconds (from 30) - Properly account for ignoring all local players when the host is splitscreen --- src/d_clisrv.c | 80 ++++++++++++++++++++++++++++++++++---------------- 1 file changed, 54 insertions(+), 26 deletions(-) diff --git a/src/d_clisrv.c b/src/d_clisrv.c index aa34c5a7..a2ccd6c0 100644 --- a/src/d_clisrv.c +++ b/src/d_clisrv.c @@ -5983,53 +5983,77 @@ boolean TryRunTics(tic_t realtics) static INT32 pingtimeout[MAXPLAYERS]; +#define PINGKICK_TICQUEUE 2 +#define PINGKICK_LIMIT 1 + static inline void PingUpdate(void) { INT32 i; - boolean laggers[MAXPLAYERS]; - UINT8 numlaggers = 0; - memset(laggers, 0, sizeof(boolean) * MAXPLAYERS); + UINT8 pingkick[MAXPLAYERS]; + UINT8 nonlaggers = 0; + memset(pingkick, 0, sizeof(pingkick)); netbuffer->packettype = PT_PING; //check for ping limit breakage. - if (cv_maxping.value) + //if (cv_maxping.value) -- always check for TICQUEUE overrun { - for (i = 1; i < MAXPLAYERS; i++) + for (i = 0; i < MAXPLAYERS; i++) { - if (playeringame[i] && (realpingtable[i] / pingmeasurecount > (unsigned)cv_maxping.value)) + if (!playeringame[i] || P_IsLocalPlayer(&players[i])) // should be P_IsMachineLocalPlayer for DRRR { - if (players[i].jointime > 30 * TICRATE) - laggers[i] = true; - numlaggers++; + pingtimeout[i] = 0; + continue; + } + + if ((maketic + 5) >= nettics[playernode[i]] + (TICQUEUE-(2*TICRATE))) + { + // Anyone who's gobbled most of the TICQUEUE and is likely to halt the server the next few times this runs has to die *right now*. (See also NetUpdate) + pingkick[i] = PINGKICK_TICQUEUE; + } + else if ((cv_maxping.value) + && (realpingtable[i] / pingmeasurecount > (unsigned)cv_maxping.value)) + { + if (players[i].jointime > 10 * TICRATE) + { + pingkick[i] = PINGKICK_LIMIT; + } } else - pingtimeout[i] = 0; + { + nonlaggers++; + + // you aren't lagging, but you aren't free yet. In case you'll keep spiking, we just make the timer go back down. (Very unstable net must still get kicked). + if (pingtimeout[i] > 0) + pingtimeout[i]--; + } } //kick lagging players... unless everyone but the server's ping sucks. //in that case, it is probably the server's fault. - if (numlaggers < D_NumPlayers() - 1) + // Always kick TICQUEUE-overrunners, too. { - for (i = 1; i < MAXPLAYERS; i++) + UINT8 minimumkicklevel = (nonlaggers > 0) ? PINGKICK_LIMIT : PINGKICK_TICQUEUE; + for (i = 0; i < MAXPLAYERS; i++) { - if (playeringame[i] && laggers[i]) + XBOXSTATIC char buf[2]; + + if (!playeringame[i] || pingkick[i] < minimumkicklevel) + continue; + + if (pingkick[i] == PINGKICK_LIMIT) { - pingtimeout[i]++; - if (pingtimeout[i] > cv_pingtimeout.value) // ok your net has been bad for too long, you deserve to die. - { - XBOXSTATIC char buf[2]; - - pingtimeout[i] = 0; - - buf[0] = (char)i; - buf[1] = KICK_MSG_PING_HIGH; - SendNetXCmd(XD_KICK, &buf, 2); - } + // Don't kick on ping alone if we haven't reached our threshold yet. + if (++pingtimeout[i] < cv_pingtimeout.value) + continue; } - else // you aren't lagging, but you aren't free yet. In case you'll keep spiking, we just make the timer go back down. (Very unstable net must still get kicked). - pingtimeout[i] = (pingtimeout[i] == 0 ? 0 : pingtimeout[i]-1); + + pingtimeout[i] = 0; + + buf[0] = (char)i; + buf[1] = KICK_MSG_PING_HIGH; + SendNetXCmd(XD_KICK, &buf, 2); } } } @@ -6057,6 +6081,9 @@ static inline void PingUpdate(void) pingmeasurecount = 0; //Reset count } +#undef PINGKICK_DANGER +#undef PINGKICK_LIMIT + static tic_t gametime = 0; static void UpdatePingTable(void) @@ -6225,6 +6252,7 @@ FILESTAMP // Do not make tics while resynching if (counts != -666) { + // See also PingUpdate if (maketic + counts >= firstticstosend + TICQUEUE) counts = firstticstosend+TICQUEUE-maketic-1; From 427944f6338f6ad9620abd8042ead17016934c2d Mon Sep 17 00:00:00 2001 From: toaster Date: Wed, 26 Oct 2022 20:40:19 +0100 Subject: [PATCH 42/92] Remove advancedemo Just a weird, meaningless footgun waiting in the wings for someone to catch themselves on. D_StartTitle works plenty fine here! --- src/d_clisrv.c | 27 ++++++++++++--------------- src/d_main.c | 11 ----------- src/d_main.h | 4 ---- src/g_game.c | 4 ++-- 4 files changed, 14 insertions(+), 32 deletions(-) diff --git a/src/d_clisrv.c b/src/d_clisrv.c index a2ccd6c0..15ab7f88 100644 --- a/src/d_clisrv.c +++ b/src/d_clisrv.c @@ -5945,23 +5945,20 @@ boolean TryRunTics(tic_t realtics) if (ticking) { - if (advancedemo) - D_StartTitle(); - else - // run the count * tics - while (neededtic > gametic) - { - DEBFILE(va("============ Running tic %d (local %d)\n", gametic, localgametic)); + // run the count * tics + while (neededtic > gametic) + { + DEBFILE(va("============ Running tic %d (local %d)\n", gametic, localgametic)); - G_Ticker((gametic % NEWTICRATERATIO) == 0); - ExtraDataTicker(); - gametic++; - consistancy[gametic%TICQUEUE] = Consistancy(); + G_Ticker((gametic % NEWTICRATERATIO) == 0); + ExtraDataTicker(); + gametic++; + consistancy[gametic%TICQUEUE] = Consistancy(); - // Leave a certain amount of tics present in the net buffer as long as we've ran at least one tic this frame. - if (client && gamestate == GS_LEVEL && leveltime > 3 && neededtic <= gametic + cv_netticbuffer.value) - break; - } + // Leave a certain amount of tics present in the net buffer as long as we've ran at least one tic this frame. + if (client && gamestate == GS_LEVEL && leveltime > 3 && neededtic <= gametic + cv_netticbuffer.value) + break; + } } else { diff --git a/src/d_main.c b/src/d_main.c index a08ed77d..517b3b98 100644 --- a/src/d_main.c +++ b/src/d_main.c @@ -144,7 +144,6 @@ boolean sound_disabled = false; boolean digital_disabled = false; #endif -boolean advancedemo; #ifdef DEBUGFILE INT32 debugload = 0; #endif @@ -815,15 +814,6 @@ void D_SRB2Loop(void) } } -// -// D_AdvanceDemo -// Called after each demo or intro demosequence finishes -// -void D_AdvanceDemo(void) -{ - advancedemo = true; -} - // ========================================================================= // D_SRB2Main // ========================================================================= @@ -883,7 +873,6 @@ void D_StartTitle(void) //demosequence = -1; gametype = GT_RACE; // SRB2kart paused = false; - advancedemo = false; F_StartTitleScreen(); // Reset the palette -- SRB2Kart: actually never mind let's do this in the middle of every fade diff --git a/src/d_main.h b/src/d_main.h index d67a5bb4..af74e7a6 100644 --- a/src/d_main.h +++ b/src/d_main.h @@ -17,8 +17,6 @@ #include "d_event.h" #include "w_wad.h" // for MAX_WADFILES -extern boolean advancedemo; - // make sure not to write back the config until it's been correctly loaded extern tic_t rendergametic; @@ -34,7 +32,6 @@ void D_SRB2Loop(void) FUNCNORETURN; // D_SRB2Main() // Not a globally visible function, just included for source reference, // calls all startup code, parses command line options. -// If not overrided by user input, calls D_AdvanceDemo. // void D_SRB2Main(void); @@ -51,7 +48,6 @@ const char *D_Home(void); // // BASE LEVEL // -void D_AdvanceDemo(void); void D_StartTitle(void); #endif //__D_MAIN__ diff --git a/src/g_game.c b/src/g_game.c index 972d3c18..68dc393a 100644 --- a/src/g_game.c +++ b/src/g_game.c @@ -8358,7 +8358,7 @@ boolean G_CheckDemoStatus(void) CONS_Printf(M_GetText("timed %u gametics in %d realtics\n%f seconds, %f avg fps\n"), leveltime,demotime,f1/TICRATE,f2/f1); if (restorecv_vidwait != cv_vidwait.value) CV_SetValue(&cv_vidwait, restorecv_vidwait); - D_AdvanceDemo(); + D_StartTitle(); return true; } @@ -8376,7 +8376,7 @@ boolean G_CheckDemoStatus(void) if (modeattacking) M_EndModeAttackRun(); else - D_AdvanceDemo(); + D_StartTitle(); } return true; From 0a98f9c69a5659b5a8f523c2f6d2a5eca37973ec Mon Sep 17 00:00:00 2001 From: toaster Date: Wed, 26 Oct 2022 20:45:41 +0100 Subject: [PATCH 43/92] SDL: Support setting vsync at runtime Allows vid_wait to work under software without having to switch to OpenGL and then back --- src/sdl/i_video.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/sdl/i_video.c b/src/sdl/i_video.c index 05cb385e..f0a8a719 100644 --- a/src/sdl/i_video.c +++ b/src/sdl/i_video.c @@ -102,7 +102,7 @@ rendermode_t rendermode = render_none; boolean highcolor = false; // synchronize page flipping with screen refresh -consvar_t cv_vidwait = {"vid_wait", "Off", CV_SAVE, CV_OnOff, NULL, 0, NULL, NULL, 0, 0, NULL}; +consvar_t cv_vidwait = {"vid_wait", "Off", CV_SAVE|CV_CALL|CV_NOINIT, CV_OnOff, Impl_SetVsync, 0, NULL, NULL, 0, 0, NULL}; static consvar_t cv_stretch = {"stretch", "Off", CV_SAVE|CV_NOSHOWHELP, CV_OnOff, NULL, 0, NULL, NULL, 0, 0, NULL}; UINT8 graphics_started = 0; // Is used in console.c and screen.c @@ -2131,4 +2131,9 @@ UINT32 I_GetRefreshRate(void) return refresh_rate; } +static void Impl_SetVsync(void) +{ +#if SDL_VERSION_ATLEAST(2,0,18) + if (renderer) + SDL_RenderSetVSync(renderer, cv_vidwait.value); #endif From d13c1f83e8e7d40c97f2b9e7b4c6ed141ef6897c Mon Sep 17 00:00:00 2001 From: toaster Date: Wed, 26 Oct 2022 20:47:08 +0100 Subject: [PATCH 44/92] SDL: OpenGL instead of Direct3D11 for Software blitter Repaired performance regression under certain play conditions --- src/sdl/i_video.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sdl/i_video.c b/src/sdl/i_video.c index f0a8a719..7122c4df 100644 --- a/src/sdl/i_video.c +++ b/src/sdl/i_video.c @@ -1782,7 +1782,7 @@ static SDL_bool Impl_CreateWindow(SDL_bool fullscreen) // "direct3d" driver (D3D9) causes Drmingw exchndl // to not write RPT files. Every other driver // seems fine. - SDL_SetHint(SDL_HINT_RENDER_DRIVER, "direct3d11"); + SDL_SetHint(SDL_HINT_RENDER_DRIVER, "opengl"); renderer = SDL_CreateRenderer(window, -1, flags); if (renderer == NULL) From 33c6dba5af170ae47c52fe433efdffc394d608f4 Mon Sep 17 00:00:00 2001 From: toaster Date: Wed, 26 Oct 2022 20:53:16 +0100 Subject: [PATCH 45/92] Repair bad manual cherrypick of vsync repair --- src/sdl/i_video.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/sdl/i_video.c b/src/sdl/i_video.c index 7122c4df..30a88737 100644 --- a/src/sdl/i_video.c +++ b/src/sdl/i_video.c @@ -101,6 +101,8 @@ rendermode_t rendermode = render_none; boolean highcolor = false; +static void Impl_SetVsync(void); + // synchronize page flipping with screen refresh consvar_t cv_vidwait = {"vid_wait", "Off", CV_SAVE|CV_CALL|CV_NOINIT, CV_OnOff, Impl_SetVsync, 0, NULL, NULL, 0, 0, NULL}; static consvar_t cv_stretch = {"stretch", "Off", CV_SAVE|CV_NOSHOWHELP, CV_OnOff, NULL, 0, NULL, NULL, 0, 0, NULL}; @@ -2137,3 +2139,5 @@ static void Impl_SetVsync(void) if (renderer) SDL_RenderSetVSync(renderer, cv_vidwait.value); #endif +} +#endif From c2f9a4059310d67805e530bd0b37fe109bbe8f0a Mon Sep 17 00:00:00 2001 From: toaster Date: Wed, 26 Oct 2022 21:09:44 +0100 Subject: [PATCH 46/92] New asset hashes for gfx.kart and maps.kart --- src/config.h.in | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/config.h.in b/src/config.h.in index 0ead749a..063a13c1 100644 --- a/src/config.h.in +++ b/src/config.h.in @@ -40,6 +40,7 @@ * Last updated 2020 / 08 / 30 - Kart v1.3 - patch.kart * Last updated 2022 / 08 / 16 - Kart v1.4 - Main assets * Last updated 2022 / 08 / 19 - Kart v1.5 - gfx.kart + * Last updated 2022 / 10 / 26 - Kart v1.6 - gfx.kart, maps.kart */ // Base SRB2 hashes @@ -49,10 +50,10 @@ #endif // SRB2Kart-specific hashes -#define ASSET_HASH_GFX_KART "30b2d9fb5009f1b3a3d7216a0fe28e51" +#define ASSET_HASH_GFX_KART "06f86ee16136eb8a7043b15001797034" #define ASSET_HASH_TEXTURES_KART "abb53d56aba47c3a8cb0f764da1c8b80" #define ASSET_HASH_CHARS_KART "e2c428347dde52858a3dacd29fc5b964" -#define ASSET_HASH_MAPS_KART "13e273292576b71af0cdb3a98ca066eb" +#define ASSET_HASH_MAPS_KART "ce353777feba6f8c2d7b1f59151a4126" #ifdef USE_PATCH_KART #define ASSET_HASH_PATCH_KART "00000000000000000000000000000000" #endif From 63a5bed7d8940c6c07daced31321ef99fddb42bd Mon Sep 17 00:00:00 2001 From: toaster Date: Thu, 27 Oct 2022 11:54:52 +0100 Subject: [PATCH 47/92] Adjust antigrief Instead of not running for server or admin, run for ALL players, but only avoid *kicking* local or admin players. Fixes a bug where having a non-interacting splitscreen player could take down your server. --- src/p_user.c | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/src/p_user.c b/src/p_user.c index 82e5fa3f..5842aca8 100644 --- a/src/p_user.c +++ b/src/p_user.c @@ -8523,11 +8523,6 @@ void P_PlayerThink(player_t *player) const tic_t griefval = cv_antigrief.value * TICRATE; const UINT8 n = player - players; - if (n != serverplayer -#ifndef DEVELOP - && !IsPlayerAdmin(n) -#endif - ) { if (player->grieftime > griefval) { @@ -8536,7 +8531,11 @@ void P_PlayerThink(player_t *player) if (server) { - if (player->griefstrikes > 2) + if ((player->griefstrikes > 2) +#ifndef DEVELOP + && !IsPlayerAdmin(n) +#endif + && !P_IsLocalPlayer(player)) // P_IsMachineLocalPlayer for DRRR { // Send kick XBOXSTATIC UINT8 buf[2]; From 7e668f99b3a03d843ba6dc43155df37f931c5778 Mon Sep 17 00:00:00 2001 From: toaster Date: Thu, 27 Oct 2022 12:13:06 +0100 Subject: [PATCH 48/92] Fix spectators spawning at world origin on sectionrace maps. --- src/g_game.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/g_game.c b/src/g_game.c index 68dc393a..089b6ff4 100644 --- a/src/g_game.c +++ b/src/g_game.c @@ -3339,7 +3339,9 @@ void G_DoReborn(INT32 playernum) // respawn at the start mobj_t *oldmo = NULL; - if (player->starpostnum || ((mapheaderinfo[gamemap - 1]->levelflags & LF_SECTIONRACE) && player->laps)) // SRB2kart + if (player->spectator) + ; + else if (player->starpostnum || ((mapheaderinfo[gamemap - 1]->levelflags & LF_SECTIONRACE) && player->laps)) // SRB2kart starpost = true; // first dissasociate the corpse From b6eb21c5e21a42e4735df7004c62211d1f068ea9 Mon Sep 17 00:00:00 2001 From: toaster Date: Thu, 27 Oct 2022 12:38:53 +0100 Subject: [PATCH 49/92] Make all the join/leave sound effects have a `singularity` of `true` so that they don't stack super loud during major net interruption or multiple splitscreen player joins. --- src/sounds.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/sounds.c b/src/sounds.c index 90b1ae30..d949c27a 100644 --- a/src/sounds.c +++ b/src/sounds.c @@ -818,10 +818,10 @@ sfxinfo_t S_sfx[NUMSFX] = {"gemhit", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR}, // Opulence gem/coin tumbling {"wrink", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR}, // Some sort of ghoulie? {"bsnipe", false, 96, 8, -1, NULL, 0, -1, -1, LUMPERROR}, // Banana sniping - {"join", false, 96, 8, -1, NULL, 0, -1, -1, LUMPERROR}, // Player joined server - {"leave", false, 96, 8, -1, NULL, 0, -1, -1, LUMPERROR}, // Player left server - {"requst", false, 96, 8, -1, NULL, 0, -1, -1, LUMPERROR}, // Got a Discord join request - {"syfail", false, 96, 8, -1, NULL, 0, -1, -1, LUMPERROR}, // Funny sync failure + {"join", true, 96, 8, -1, NULL, 0, -1, -1, LUMPERROR}, // Player joined server + {"leave", true, 96, 8, -1, NULL, 0, -1, -1, LUMPERROR}, // Player left server + {"requst", true, 96, 8, -1, NULL, 0, -1, -1, LUMPERROR}, // Got a Discord join request + {"syfail", true, 96, 8, -1, NULL, 0, -1, -1, LUMPERROR}, // Funny sync failure {"itfree", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR}, // :shitsfree: {"dbgsal", false, 255, 8, -1, NULL, 0, -1, -1, LUMPERROR}, // Debug notification From 054bbd7745a17e94f81500c42ab12a612f595b82 Mon Sep 17 00:00:00 2001 From: toaster Date: Thu, 27 Oct 2022 13:36:14 +0100 Subject: [PATCH 50/92] Fix undesired interpolation for regular teleport --- src/p_telept.c | 25 +++++++++---------------- 1 file changed, 9 insertions(+), 16 deletions(-) diff --git a/src/p_telept.c b/src/p_telept.c index 41896562..7c9da226 100644 --- a/src/p_telept.c +++ b/src/p_telept.c @@ -17,6 +17,7 @@ #include "r_state.h" #include "s_sound.h" #include "r_main.h" +#include "r_fps.h" /** \brief The P_MixUp function @@ -149,25 +150,17 @@ boolean P_Teleport(mobj_t *thing, fixed_t x, fixed_t y, fixed_t z, angle_t angle thing->reactiontime = TICRATE/2; // don't move for about half a second // absolute angle position - if (thing == players[consoleplayer].mo) - localangle[0] = angle; - else if (splitscreen) - { - for (i = 1; i <= splitscreen; i++) - { - if (thing == players[displayplayers[i]].mo) - { - localangle[i] = angle; - break; - } - } - } - // move chasecam at new player location for (i = 0; i <= splitscreen; i++) { - if (thing->player == &players[displayplayers[i]] && camera[i].chase) - P_ResetCamera(thing->player, &camera[i]); + if (thing->player == &players[displayplayers[i]]) + { + localangle[i] = angle; + if (camera[i].chase) + P_ResetCamera(thing->player, &camera[i]); + R_ResetViewInterpolation(i + 1); + break; + } } // don't run in place after a teleport From 2bab452268db1aa538b684a9983e4dedb99ed7b7 Mon Sep 17 00:00:00 2001 From: toaster Date: Thu, 27 Oct 2022 13:38:13 +0100 Subject: [PATCH 51/92] Add R_RelativeTeleportViewInterpolation Adjusts pview_old for relative teleport to attempt a contigious motion --- src/p_spec.c | 1 + src/r_fps.c | 8 ++++++++ src/r_fps.h | 2 ++ 3 files changed, 11 insertions(+) diff --git a/src/p_spec.c b/src/p_spec.c index fd9ef4b7..61ae9c63 100644 --- a/src/p_spec.c +++ b/src/p_spec.c @@ -2395,6 +2395,7 @@ static void P_ProcessLineSpecial(line_t *line, mobj_t *mo, sector_t *callsec) camera[i].y += y; camera[i].z += z; camera[i].subsector = R_PointInSubsector(camera[i].x, camera[i].y); + R_RelativeTeleportViewInterpolation(i, x, y, z, 0); break; } } diff --git a/src/r_fps.c b/src/r_fps.c index 2ed03979..9cbee9c0 100644 --- a/src/r_fps.c +++ b/src/r_fps.c @@ -210,6 +210,14 @@ void R_ResetViewInterpolation(UINT8 p) } } +void R_RelativeTeleportViewInterpolation(UINT8 p, fixed_t xdiff, fixed_t ydiff, fixed_t zdiff, angle_t angdiff) +{ + pview_old[p].x += xdiff; + pview_old[p].y += ydiff; + pview_old[p].z += zdiff; + pview_old[p].angle += angdiff; +} + void R_SetViewContext(enum viewcontext_e _viewcontext) { UINT8 i = 0; diff --git a/src/r_fps.h b/src/r_fps.h index 7e64683f..2f3f4430 100644 --- a/src/r_fps.h +++ b/src/r_fps.h @@ -115,6 +115,8 @@ void R_InterpolateView(fixed_t frac); void R_UpdateViewInterpolation(void); // Reset the view states (e.g. after level load) so R_InterpolateView doesn't interpolate invalid data void R_ResetViewInterpolation(UINT8 p); +// Update old view for seamless relative teleport +void R_RelativeTeleportViewInterpolation(UINT8 p, fixed_t xdiff, fixed_t ydiff, fixed_t zdiff, angle_t angdiff); // Set the current view context (the viewvars pointed to by newview) void R_SetViewContext(enum viewcontext_e _viewcontext); From 33989f56e5ed431fc3283da2c7b2d5d7a42fa735 Mon Sep 17 00:00:00 2001 From: toaster Date: Thu, 27 Oct 2022 15:32:43 +0100 Subject: [PATCH 52/92] Version is now 1.6 If you're looking to this commit to figure out what a hypothetical 1.7 should change, also increment MODVERSION - in this commit's case it was already incremented by another branch. --- CMakeLists.txt | 2 +- src/doomdef.h | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 8bd76843..ceaf65f0 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -2,7 +2,7 @@ cmake_minimum_required(VERSION 3.0) # DO NOT CHANGE THIS SRB2 STRING! Some variable names depend on this string. # Version change is fine. project(SRB2 - VERSION 1.4 + VERSION 1.6 LANGUAGES C) if(${PROJECT_SOURCE_DIR} MATCHES ${PROJECT_BINARY_DIR}) diff --git a/src/doomdef.h b/src/doomdef.h index ae02939e..5ed1e9ed 100644 --- a/src/doomdef.h +++ b/src/doomdef.h @@ -148,9 +148,9 @@ extern char logfilename[1024]; // we use comprevision and compbranch instead. #else #define VERSION 1 // Game version -#define SUBVERSION 4 // more precise version number -#define VERSIONSTRING "v1.5" -#define VERSIONSTRINGW L"v1.5" +#define SUBVERSION 6 // more precise version number +#define VERSIONSTRING "v1.6" +#define VERSIONSTRINGW L"v1.6" // Hey! If you change this, add 1 to the MODVERSION below! Otherwise we can't force updates! // And change CMakeLists.txt (not src/, but in root), for CMake users! // AND appveyor.yml, for the build bots! From 1b96c8b40f77f726354b2ddbc4bbfb0cf1e379e8 Mon Sep 17 00:00:00 2001 From: Eidolon Date: Thu, 27 Oct 2022 18:12:05 -0500 Subject: [PATCH 53/92] Net-save/load turnobjs field on rotating polyobjs Fixes desyncs with rotating polyobjs --- src/p_saveg.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/p_saveg.c b/src/p_saveg.c index e3bc51d0..f553ddf6 100644 --- a/src/p_saveg.c +++ b/src/p_saveg.c @@ -1564,6 +1564,7 @@ static inline void SavePolyrotatetThinker(const thinker_t *th, const UINT8 type) WRITEINT32(save_p, ht->polyObjNum); WRITEINT32(save_p, ht->speed); WRITEINT32(save_p, ht->distance); + WRITEUINT8(save_p, ht->turnobjs); } // @@ -2533,6 +2534,7 @@ static inline void LoadPolyrotatetThinker(actionf_p1 thinker) ht->polyObjNum = READINT32(save_p); ht->speed = READINT32(save_p); ht->distance = READINT32(save_p); + ht->turnobjs = READUINT8(save_p); P_AddThinker(&ht->thinker); } From 19403d28890ba1180cc0e233e8f35fbd270653dd Mon Sep 17 00:00:00 2001 From: Ashnal Date: Thu, 27 Oct 2022 20:28:14 -0400 Subject: [PATCH 54/92] Ensure skin index read from replay file is within numskins before drawing face --- src/m_menu.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/m_menu.c b/src/m_menu.c index b657d1c9..367df794 100644 --- a/src/m_menu.c +++ b/src/m_menu.c @@ -5580,7 +5580,7 @@ static void DrawReplayHutReplayInfo(void) } // Character face! - if (W_CheckNumForName(skins[demolist[dir_on[menudepthleft]].standings[0].skin].facewant) != LUMPERROR) + if (demolist[dir_on[menudepthleft]].standings[0].skin < numskins && W_CheckNumForName(skins[demolist[dir_on[menudepthleft]].standings[0].skin].facewant) != LUMPERROR) { patch = facewantprefix[demolist[dir_on[menudepthleft]].standings[0].skin]; colormap = R_GetTranslationColormap( From 661f06e1ef0a262de75962f8e049250ffffce6bf Mon Sep 17 00:00:00 2001 From: Eidolon Date: Thu, 27 Oct 2022 20:07:56 -0500 Subject: [PATCH 55/92] Correct local variable boolean type --- src/p_tick.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/p_tick.c b/src/p_tick.c index fcc09ea6..62acdc16 100644 --- a/src/p_tick.c +++ b/src/p_tick.c @@ -821,7 +821,7 @@ void P_Ticker(boolean run) for (i = 0; i <= splitscreen; i++) { player_t *player = &players[displayplayers[i]]; - BOOL skyVisible = skyVisiblePerPlayer[i]; + boolean skyVisible = skyVisiblePerPlayer[i]; if (skyVisible && skyboxmo[0] && cv_skybox.value) { R_SkyboxFrame(player); From 4b61bcdb819cbc69151bae2f05921b087832b8a2 Mon Sep 17 00:00:00 2001 From: Eidolon Date: Thu, 27 Oct 2022 20:07:56 -0500 Subject: [PATCH 56/92] Correct local variable boolean type --- src/p_tick.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/p_tick.c b/src/p_tick.c index d70c491d..74eeab59 100644 --- a/src/p_tick.c +++ b/src/p_tick.c @@ -819,7 +819,7 @@ void P_Ticker(boolean run) for (i = 0; i <= splitscreen; i++) { player_t *player = &players[displayplayers[i]]; - BOOL skyVisible = skyVisiblePerPlayer[i]; + boolean skyVisible = skyVisiblePerPlayer[i]; if (skyVisible && skyboxmo[0] && cv_skybox.value) { R_SkyboxFrame(player); From 4680094dd5201fd589509eeea5f88ed9377b1b30 Mon Sep 17 00:00:00 2001 From: toaster Date: Fri, 28 Oct 2022 15:50:50 +0100 Subject: [PATCH 57/92] Update credits again --- src/f_finale.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/f_finale.c b/src/f_finale.c index 76152027..a765cdd1 100644 --- a/src/f_finale.c +++ b/src/f_finale.c @@ -512,6 +512,7 @@ static const char *credits[] = { "", "\1Testing", "RKH License holders", + "The KCS", "\"CyberIF\"", "\"Dani\"", "Karol \"Fooruman\" D""\x1E""browski", // DÄ…browski, accents in srb2 :ytho: @@ -559,7 +560,7 @@ static struct { // This Tyler52 gag is troublesome // Alignment should be ((spaces+1 * 100) + (headers+1 * 38) + (lines * 15)) // Current max image spacing: (216*17) - {112, (16*100)+(19*38)+(100*15), "TYLER52", SKINCOLOR_NONE}, + {112, (16*100)+(19*38)+(101*15), "TYLER52", SKINCOLOR_NONE}, {0, 0, NULL, SKINCOLOR_NONE} }; From cb575bcde99ac49c3b22f37df408e1f6f83d1bcc Mon Sep 17 00:00:00 2001 From: toaster Date: Fri, 28 Oct 2022 18:02:50 +0100 Subject: [PATCH 58/92] Add additional external contributors. --- src/f_finale.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/f_finale.c b/src/f_finale.c index a765cdd1..1b5310e0 100644 --- a/src/f_finale.c +++ b/src/f_finale.c @@ -431,10 +431,12 @@ static const char *credits[] = { "\"JugadorXEI\"", "\"Kimberly\"", "\"Lighto97\"", + "\"Lonsfor\"", "\"mazmazz\"", "\"minenice\"", "\"Shuffle\"", "\"Snu\"", + "\"X.organic\"", "", "\1Lead Artists", "Desmond \"Blade\" DesJardins", @@ -560,7 +562,7 @@ static struct { // This Tyler52 gag is troublesome // Alignment should be ((spaces+1 * 100) + (headers+1 * 38) + (lines * 15)) // Current max image spacing: (216*17) - {112, (16*100)+(19*38)+(101*15), "TYLER52", SKINCOLOR_NONE}, + {112, (16*100)+(19*38)+(103*15), "TYLER52", SKINCOLOR_NONE}, {0, 0, NULL, SKINCOLOR_NONE} }; From 6db1496aaa0f331808591fed11c16d1230d1bbd6 Mon Sep 17 00:00:00 2001 From: Eidolon Date: Fri, 28 Oct 2022 15:19:20 -0500 Subject: [PATCH 59/92] Disambiguate skyVisible from global variable --- src/p_tick.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/p_tick.c b/src/p_tick.c index 74eeab59..7d723b90 100644 --- a/src/p_tick.c +++ b/src/p_tick.c @@ -819,8 +819,8 @@ void P_Ticker(boolean run) for (i = 0; i <= splitscreen; i++) { player_t *player = &players[displayplayers[i]]; - boolean skyVisible = skyVisiblePerPlayer[i]; - if (skyVisible && skyboxmo[0] && cv_skybox.value) + boolean isSkyVisibleForPlayer = skyVisiblePerPlayer[i]; + if (isSkyVisibleForPlayer && skyboxmo[0] && cv_skybox.value) { R_SkyboxFrame(player); } From 8d2d91a3bf9280cffe9156ef6eaad83079d273db Mon Sep 17 00:00:00 2001 From: toaster Date: Fri, 28 Oct 2022 21:26:31 +0100 Subject: [PATCH 60/92] Fix mixed declarations and code warning --- src/d_main.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/d_main.c b/src/d_main.c index 1948ad99..b2591b94 100644 --- a/src/d_main.c +++ b/src/d_main.c @@ -1143,6 +1143,8 @@ void D_SRB2Main(void) { const char *userhome = D_Home(); //Alam: path to home + FILE *tmpfile; + char testfile[MAX_WADPATH]; if (!userhome) { @@ -1192,9 +1194,6 @@ void D_SRB2Main(void) // If config isn't writable, tons of behavior will be broken. // Fail loudly before things get confusing! - FILE *tmpfile; - char testfile[MAX_WADPATH]; - snprintf(testfile, sizeof testfile, "%s" PATHSEP "file.tmp", srb2home); testfile[sizeof testfile - 1] = '\0'; From 8b0ed1769024e944e28b995eb8aefff5f3ef9029 Mon Sep 17 00:00:00 2001 From: Ashnal Date: Fri, 28 Oct 2022 17:15:50 -0400 Subject: [PATCH 61/92] Another spot that needs guarding --- src/m_menu.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/m_menu.c b/src/m_menu.c index 367df794..c8ba37e4 100644 --- a/src/m_menu.c +++ b/src/m_menu.c @@ -5780,7 +5780,7 @@ static void M_DrawReplayStartMenu(void) V_DrawString(BASEVIDWIDTH-92, STARTY + i*20 + 9, V_SNAPTOTOP, va("%d", demolist[dir_on[menudepthleft]].standings[i].timeorscore)); // Character face! - if (W_CheckNumForName(skins[demolist[dir_on[menudepthleft]].standings[i].skin].facerank) != LUMPERROR) + if (demolist[dir_on[menudepthleft]].standings[i].skin < numskins && W_CheckNumForName(skins[demolist[dir_on[menudepthleft]].standings[i].skin].facerank) != LUMPERROR) { patch = facerankprefix[demolist[dir_on[menudepthleft]].standings[i].skin]; colormap = R_GetTranslationColormap( From de51d065b0bc6a9172c00ce174dc838a4367deaf Mon Sep 17 00:00:00 2001 From: toaster Date: Sat, 29 Oct 2022 12:51:00 +0100 Subject: [PATCH 62/92] Fix an issue with reading invalid memory when loading a map inside a pk3 that has no REJECT table. --- src/p_setup.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/p_setup.c b/src/p_setup.c index 8d73c12b..5263d1b3 100644 --- a/src/p_setup.c +++ b/src/p_setup.c @@ -3071,6 +3071,10 @@ boolean P_SetupLevel(boolean skipprecip) (fileinfo + ML_REJECT)->size, (fileinfo + ML_REJECT)->name); } + else + { + rejectmatrix = NULL; + } // Important: take care of the ordering of the next functions. if (!loadedbm) From 17bde07bda3f5ecac6fc6e41337351c02727c9d7 Mon Sep 17 00:00:00 2001 From: toaster Date: Sat, 29 Oct 2022 13:06:25 +0100 Subject: [PATCH 63/92] Only do view interpolation hack if renderer exists --- src/p_tick.c | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/src/p_tick.c b/src/p_tick.c index b7cd035a..b8334f11 100644 --- a/src/p_tick.c +++ b/src/p_tick.c @@ -25,6 +25,7 @@ #include "k_kart.h" #include "r_main.h" #include "r_fps.h" +#include "i_video.h" // rendermode // Object place #include "m_cheat.h" @@ -818,15 +819,18 @@ void P_Ticker(boolean run) // Hack: ensure newview is assigned every tic. // Ensures view interpolation is T-1 to T in poor network conditions // We need a better way to assign view state decoupled from game logic - for (i = 0; i <= splitscreen; i++) + if (rendermode != render_none) { - player_t *player = &players[displayplayers[i]]; - boolean isSkyVisibleForPlayer = skyVisiblePerPlayer[i]; - if (isSkyVisibleForPlayer && skyboxmo[0] && cv_skybox.value) + for (i = 0; i <= splitscreen; i++) { - R_SkyboxFrame(player); + player_t *player = &players[displayplayers[i]]; + boolean isSkyVisibleForPlayer = skyVisiblePerPlayer[i]; + if (isSkyVisibleForPlayer && skyboxmo[0] && cv_skybox.value) + { + R_SkyboxFrame(player); + } + R_SetupFrame(player, (skyboxmo[0] && cv_skybox.value)); } - R_SetupFrame(player, (skyboxmo[0] && cv_skybox.value)); } } From 144795c22c63f7fbaf9327febbe59c7656b13318 Mon Sep 17 00:00:00 2001 From: AJ Martinez Date: Sat, 29 Oct 2022 15:37:45 -0700 Subject: [PATCH 64/92] Don't antigrief players alone in local freeplay --- src/p_user.c | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/src/p_user.c b/src/p_user.c index 5842aca8..94169754 100644 --- a/src/p_user.c +++ b/src/p_user.c @@ -8518,7 +8518,17 @@ void P_PlayerThink(player_t *player) if (netgame && cv_antigrief.value != 0 && G_RaceGametype()) { - if (!player->spectator && !player->exiting && !(player->pflags & PF_TIMEOVER)) + INT32 i; + for (i = 0; i < MAXPLAYERS; i++) + { + if (!playeringame[i] || players[i].spectator) + continue; + if (&players[i] == player) + continue; + break; + } + + if (i < MAXPLAYERS && !player->spectator && !player->exiting && !(player->pflags & PF_TIMEOVER)) { const tic_t griefval = cv_antigrief.value * TICRATE; const UINT8 n = player - players; From fcdb098b9cd9912a4554c4c5f224f96ec733ece3 Mon Sep 17 00:00:00 2001 From: Eidolon Date: Sat, 29 Oct 2022 22:56:47 -0500 Subject: [PATCH 65/92] Ensure player mo exists in T-1 hack skybox update --- src/p_tick.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/p_tick.c b/src/p_tick.c index 7d723b90..57a7df12 100644 --- a/src/p_tick.c +++ b/src/p_tick.c @@ -820,7 +820,7 @@ void P_Ticker(boolean run) { player_t *player = &players[displayplayers[i]]; boolean isSkyVisibleForPlayer = skyVisiblePerPlayer[i]; - if (isSkyVisibleForPlayer && skyboxmo[0] && cv_skybox.value) + if (isSkyVisibleForPlayer && player->mo && skyboxmo[0] && cv_skybox.value) { R_SkyboxFrame(player); } From a6c825dd16efc294e1dc9f0e6df264dde7e478cb Mon Sep 17 00:00:00 2001 From: toaster Date: Sun, 30 Oct 2022 13:13:42 +0000 Subject: [PATCH 66/92] More consistent netreplay wadfile list writing Fixes crashes for newly-written demos that have files with absurdly long names attached --- 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 32d6ef1d..2faf1cf5 100644 --- a/src/g_game.c +++ b/src/g_game.c @@ -6769,7 +6769,7 @@ void G_BeginRecording(void) if (wadfiles[i]->important) { nameonly(( filename = va("%s", wadfiles[i]->filename) )); - WRITESTRINGN(demo_p, filename, 64); + WRITESTRINGL(demo_p, filename, MAX_WADPATH); WRITEMEM(demo_p, wadfiles[i]->md5sum, 16); totalfiles++; From 21bfb9753f1bbbaa24def53f1ed094427799679a Mon Sep 17 00:00:00 2001 From: toaster Date: Sun, 30 Oct 2022 13:29:27 +0000 Subject: [PATCH 67/92] Only read/write luavars from demos if Lua state has been initialised. --- src/g_game.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/g_game.c b/src/g_game.c index 2faf1cf5..cc5a1044 100644 --- a/src/g_game.c +++ b/src/g_game.c @@ -6734,7 +6734,7 @@ void G_BeginRecording(void) demoflags |= DF_ENCORE; #ifdef HAVE_BLUA - if (!modeattacking) // Ghosts don't read luavars, and you shouldn't ever need to save Lua in replays, you doof! + if (!modeattacking && gL) // Ghosts don't read luavars, and you shouldn't ever need to save Lua in replays, you doof! // SERIOUSLY THOUGH WHY WOULD YOU LOAD HOSTMOD AND RECORD A GHOST WITH IT !???? demoflags |= DF_LUAVARS; #endif @@ -6843,7 +6843,7 @@ void G_BeginRecording(void) #ifdef HAVE_BLUA // player lua vars, always saved even if empty... Unless it's record attack. - if (!modeattacking) + if (demoflags & DF_LUAVARS) LUA_ArchiveDemo(); #endif From 461a80d357ce6bd8d654ce77b993ef16303d319e Mon Sep 17 00:00:00 2001 From: toaster Date: Sat, 29 Oct 2022 13:06:25 +0100 Subject: [PATCH 68/92] Only do view interpolation hack if renderer exists --- src/p_tick.c | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/src/p_tick.c b/src/p_tick.c index 57a7df12..5d36d410 100644 --- a/src/p_tick.c +++ b/src/p_tick.c @@ -25,6 +25,7 @@ #include "k_kart.h" #include "r_main.h" #include "r_fps.h" +#include "i_video.h" // rendermode // Object place #include "m_cheat.h" @@ -816,15 +817,18 @@ void P_Ticker(boolean run) // Hack: ensure newview is assigned every tic. // Ensures view interpolation is T-1 to T in poor network conditions // We need a better way to assign view state decoupled from game logic - for (i = 0; i <= splitscreen; i++) + if (rendermode != render_none) { - player_t *player = &players[displayplayers[i]]; - boolean isSkyVisibleForPlayer = skyVisiblePerPlayer[i]; - if (isSkyVisibleForPlayer && player->mo && skyboxmo[0] && cv_skybox.value) + for (i = 0; i <= splitscreen; i++) { - R_SkyboxFrame(player); + player_t *player = &players[displayplayers[i]]; + boolean isSkyVisibleForPlayer = skyVisiblePerPlayer[i]; + if (isSkyVisibleForPlayer && player->mo && skyboxmo[0] && cv_skybox.value) + { + R_SkyboxFrame(player); + } + R_SetupFrame(player, (skyboxmo[0] && cv_skybox.value)); } - R_SetupFrame(player, (skyboxmo[0] && cv_skybox.value)); } } From 65737cd4bde4cc1806825d073fc9fa737732566a Mon Sep 17 00:00:00 2001 From: Eidolon Date: Sun, 30 Oct 2022 21:03:00 -0500 Subject: [PATCH 69/92] Only call SetupFrame in T-1 hack with player->mo --- src/p_tick.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/p_tick.c b/src/p_tick.c index 5d36d410..87624d2a 100644 --- a/src/p_tick.c +++ b/src/p_tick.c @@ -827,7 +827,10 @@ void P_Ticker(boolean run) { R_SkyboxFrame(player); } - R_SetupFrame(player, (skyboxmo[0] && cv_skybox.value)); + if (player->mo) + { + R_SetupFrame(player, (skyboxmo[0] && cv_skybox.value)); + } } } } From d814674d81cdb929039a0e2cd48b904dd5586f8a Mon Sep 17 00:00:00 2001 From: Eidolon Date: Sun, 30 Oct 2022 23:03:47 -0500 Subject: [PATCH 70/92] Use interp position in GL billboarding --- src/hardware/hw_main.c | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/src/hardware/hw_main.c b/src/hardware/hw_main.c index 397ef637..15a30764 100644 --- a/src/hardware/hw_main.c +++ b/src/hardware/hw_main.c @@ -3019,11 +3019,24 @@ static void HWR_RotateSpritePolyToAim(gr_vissprite_t *spr, FOutVector *wallVerts { if (cv_grspritebillboarding.value && spr && spr->mobj && !(spr->mobj->frame & FF_PAPERSPRITE) && wallVerts) { - float basey = FIXED_TO_FLOAT(spr->mobj->z); + // uncapped/interpolation + interpmobjstate_t interp = {0}; + + // do interpolation + if (R_UsingFrameInterpolation() && !paused) + { + R_InterpolateMobjState(spr->mobj, rendertimefrac, &interp); + } + else + { + R_InterpolateMobjState(spr->mobj, FRACUNIT, &interp); + } + + float basey = FIXED_TO_FLOAT(interp.z); float lowy = wallVerts[0].y; if (P_MobjFlip(spr->mobj) == -1) { - basey = FIXED_TO_FLOAT(spr->mobj->z + spr->mobj->height); + basey = FIXED_TO_FLOAT(interp.z + spr->mobj->height); } // Rotate sprites to fully billboard with the camera // X, Y, AND Z need to be manipulated for the polys to rotate around the From 163bf9e491e7fb1523020fccc262adfc930c96b6 Mon Sep 17 00:00:00 2001 From: toaster Date: Mon, 31 Oct 2022 10:51:20 +0000 Subject: [PATCH 71/92] Resolve mixed declarations It's the collective opinion of Kart Krew's coders that sticking to C90 spec isn't healthy... but while the compiler still warns for it in the v1 buildflags, avoid undesired reports. --- src/hardware/hw_main.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/hardware/hw_main.c b/src/hardware/hw_main.c index 15a30764..f01bc5cd 100644 --- a/src/hardware/hw_main.c +++ b/src/hardware/hw_main.c @@ -3021,6 +3021,7 @@ static void HWR_RotateSpritePolyToAim(gr_vissprite_t *spr, FOutVector *wallVerts { // uncapped/interpolation interpmobjstate_t interp = {0}; + float basey, lowy; // do interpolation if (R_UsingFrameInterpolation() && !paused) @@ -3032,12 +3033,16 @@ static void HWR_RotateSpritePolyToAim(gr_vissprite_t *spr, FOutVector *wallVerts R_InterpolateMobjState(spr->mobj, FRACUNIT, &interp); } - float basey = FIXED_TO_FLOAT(interp.z); - float lowy = wallVerts[0].y; if (P_MobjFlip(spr->mobj) == -1) { basey = FIXED_TO_FLOAT(interp.z + spr->mobj->height); } + else + { + basey = FIXED_TO_FLOAT(interp.z); + } + lowy = wallVerts[0].y; + // Rotate sprites to fully billboard with the camera // X, Y, AND Z need to be manipulated for the polys to rotate around the // origin, because of how the origin setting works I believe that should From fbf696a38a341337b7ec0b465ba4cf3daf00cfb5 Mon Sep 17 00:00:00 2001 From: toaster Date: Mon, 31 Oct 2022 11:03:01 +0000 Subject: [PATCH 72/92] Update maps.kart hash --- src/config.h.in | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/config.h.in b/src/config.h.in index 063a13c1..eccef2a3 100644 --- a/src/config.h.in +++ b/src/config.h.in @@ -40,7 +40,7 @@ * Last updated 2020 / 08 / 30 - Kart v1.3 - patch.kart * Last updated 2022 / 08 / 16 - Kart v1.4 - Main assets * Last updated 2022 / 08 / 19 - Kart v1.5 - gfx.kart - * Last updated 2022 / 10 / 26 - Kart v1.6 - gfx.kart, maps.kart + * Last updated 2022 / 10 / 31 - Kart v1.6 - gfx.kart, maps.kart */ // Base SRB2 hashes @@ -53,7 +53,7 @@ #define ASSET_HASH_GFX_KART "06f86ee16136eb8a7043b15001797034" #define ASSET_HASH_TEXTURES_KART "abb53d56aba47c3a8cb0f764da1c8b80" #define ASSET_HASH_CHARS_KART "e2c428347dde52858a3dacd29fc5b964" -#define ASSET_HASH_MAPS_KART "ce353777feba6f8c2d7b1f59151a4126" +#define ASSET_HASH_MAPS_KART "59b75e162b4d29332b3c3f6b9c4e806a" #ifdef USE_PATCH_KART #define ASSET_HASH_PATCH_KART "00000000000000000000000000000000" #endif From 5ab988dc3ea422fd7417c76e3f3f380c709c4e8c Mon Sep 17 00:00:00 2001 From: toaster Date: Mon, 31 Oct 2022 12:43:25 +0000 Subject: [PATCH 73/92] Legacy downloader requests have recieved a little TLC. - Catch buffer overrun opportunities and fail early. - Add #define MORELEGACYDOWNLOADER for the equivalent of MOREFILENEEDED, but disabled for now because honestly we really shouldn't be encouraging people to use this thing by making it support 255 WADs at once, but also because that'd be MISERABLE to test - Add a menu report for when legacy downloader attempts fail --- src/d_clisrv.c | 26 +++++++++++++ src/d_netfil.c | 99 +++++++++++++++++++++++++++++++++++++++++--------- 2 files changed, 107 insertions(+), 18 deletions(-) diff --git a/src/d_clisrv.c b/src/d_clisrv.c index 15ab7f88..55276e69 100644 --- a/src/d_clisrv.c +++ b/src/d_clisrv.c @@ -1130,6 +1130,7 @@ typedef enum CL_PREPAREHTTPFILES, CL_DOWNLOADHTTPFILES, #endif + CL_LEGACYREQUESTFAILED, } cl_mode_t; static void GetPackets(void); @@ -1227,6 +1228,7 @@ static inline void CL_DrawConnectionStatus(void) #endif case CL_ASKFULLFILELIST: case CL_CONFIRMCONNECT: + case CL_LEGACYREQUESTFAILED: cltext = ""; break; case CL_SETUPFILES: @@ -2124,6 +2126,10 @@ static void M_ConfirmConnect(event_t *ev) { cl_mode = CL_DOWNLOADFILES; } + else + { + cl_mode = CL_LEGACYREQUESTFAILED; + } } #ifdef HAVE_CURL else @@ -2279,6 +2285,10 @@ static boolean CL_FinishedFileList(void) { cl_mode = CL_DOWNLOADFILES; } + else + { + cl_mode = CL_LEGACYREQUESTFAILED; + } } #endif } @@ -2465,6 +2475,22 @@ static boolean CL_ServerConnectionTicker(const char *tmpsave, tic_t *oldtic, tic cl_mode = CL_LOADFILES; break; + case CL_LEGACYREQUESTFAILED: + { + CONS_Printf(M_GetText("Legacy downloader request packet failed.\n")); + CONS_Printf(M_GetText("Network game synchronization aborted.\n")); + D_QuitNetGame(); + CL_Reset(); + D_StartTitle(); + M_StartMessage(M_GetText( + "The legacy file downloader could not handle that many files.\n" + "Ask the server host to set up a http source, or\n" + "locate and download the necessary files yourself.\n" + "\n" + "Press ESC\n" + ), NULL, MM_NOTHING); + return false; + } case CL_LOADFILES: if (CL_LoadServerFiles()) cl_mode = CL_SETUPFILES; diff --git a/src/d_netfil.c b/src/d_netfil.c index 8f46c045..adf68206 100644 --- a/src/d_netfil.c +++ b/src/d_netfil.c @@ -299,6 +299,9 @@ boolean CL_CheckDownloadable(void) return false; } +// The following was written and then quickly deemed too fragile on paper to be worth testing. +//#DEFINE MORELEGACYDOWNLOADER + /** Sends requests for files in the ::fileneeded table with a status of * ::FS_NOTFOUND. * @@ -311,42 +314,102 @@ boolean CL_SendRequestFile(void) char *p; INT32 i; INT64 totalfreespaceneeded = 0, availablefreespace; + INT32 skippedafile = -1; +#ifdef MORELEGACYDOWNLOADER + boolean firstloop = true; +#endif #ifdef PARANOIA if (M_CheckParm("-nodownload")) - I_Error("Attempted to download files in -nodownload mode"); + I_Error("CL_SendRequestFile: Attempted to download files in -nodownload mode"); +#endif for (i = 0; i < fileneedednum; i++) + { +#ifdef PARANOIA if (fileneeded[i].status != FS_FOUND && fileneeded[i].status != FS_OPEN && (fileneeded[i].willsend == 0 || fileneeded[i].willsend == 2)) { - I_Error("Attempted to download files that were not sendable"); + I_Error("CL_SendRequestFile: Attempted to download files that were not sendable"); } #endif - - netbuffer->packettype = PT_REQUESTFILE; - p = (char *)netbuffer->u.textcmd; - for (i = 0; i < fileneedednum; i++) if ((fileneeded[i].status == FS_NOTFOUND || fileneeded[i].status == FS_MD5SUMBAD || fileneeded[i].status == FS_FALLBACK)) { + // Error check for the first time around. totalfreespaceneeded += fileneeded[i].totalsize; - nameonly(fileneeded[i].filename); - WRITEUINT8(p, i); // fileid - WRITESTRINGN(p, fileneeded[i].filename, MAX_WADPATH); - // put it in download dir - strcatbf(fileneeded[i].filename, downloaddir, "/"); - fileneeded[i].status = FS_REQUESTED; } - WRITEUINT8(p, 0xFF); + } + I_GetDiskFreeSpace(&availablefreespace); if (totalfreespaceneeded > availablefreespace) I_Error("To play on this server you must download %s KB,\n" "but you have only %s KB free space on this drive\n", sizeu1((size_t)(totalfreespaceneeded>>10)), sizeu2((size_t)(availablefreespace>>10))); - // prepare to download - I_mkdir(downloaddir, 0755); - return HSendPacket(servernode, true, 0, p - (char *)netbuffer->u.textcmd); +#ifdef MORELEGACYDOWNLOADER +tryagain: + skippedafile = -1; +#endif + + netbuffer->packettype = PT_REQUESTFILE; + p = (char *)netbuffer->u.textcmd; + + for (i = 0; i < fileneedednum; i++) + { + if ((fileneeded[i].status == FS_NOTFOUND || fileneeded[i].status == FS_MD5SUMBAD || fileneeded[i].status == FS_FALLBACK)) + { + // Pre-prepare. + size_t checklen; + nameonly(fileneeded[i].filename); + + // Figure out if we'd overrun our buffer. + checklen = strlen(fileneeded[i].filename)+2; // plus the fileid (and terminator, in case this is last) + if ((UINT8 *)(p + checklen) > netbuffer->u.textcmd + MAXTEXTCMD-1) + { + skippedafile = i; + // we might have a shorter file that can fit in the remaining space, and file ID permits out-of-order data + continue; + } + + // Now write. + WRITEUINT8(p, i); // fileid + WRITESTRINGN(p, fileneeded[i].filename, MAX_WADPATH); + + // put it in download dir + strcatbf(fileneeded[i].filename, downloaddir, "/"); + fileneeded[i].status = FS_REQUESTED; + } + } + +#ifdef MORELEGACYDOWNLOADER + if (firstloop) +#else + // If we're not trying extralong legacy download requests, gotta bail. + if (skippedafile != -1) + return false; + else +#endif + I_mkdir(downloaddir, 0755); + +#ifdef PARANOIA + // Couldn't fit a single one in? + if (p == (char *)netbuffer->u.textcmd) + I_Error("CL_SendRequestFile: Fileneeded name for %s (fileneeded[%d]) too long??", (p > 0 ? fileneeded[p].filename : NULL), p); +#endif + + WRITEUINT8(p, 0xFF); // terminator + if (!HSendPacket(servernode, true, 0, p - (char *)netbuffer->u.textcmd)) + return false; + +#ifdef MORELEGACYDOWNLOADER + if (skippedafile != -1) + { + firstloop = false; + goto tryagain; + } +#endif + + return true; } // get request filepak and put it on the send queue @@ -362,10 +425,10 @@ boolean Got_RequestFilePak(INT32 node) if (id == 0xFF) break; READSTRINGN(p, wad, MAX_WADPATH); - if (!SV_SendFile(node, wad, id)) + if (p >= netbuffer->u.textcmd + MAXTEXTCMD-1 || !SV_SendFile(node, wad, id)) { SV_AbortSendFiles(node); - return false; // don't read the rest of the files + return false; // don't read any more } } return true; // no problems with any files From a679e7a9e1f7106483e5a310358530a3ac13de5d Mon Sep 17 00:00:00 2001 From: toaster Date: Mon, 31 Oct 2022 17:49:52 +0000 Subject: [PATCH 74/92] Legacy download code recieves more attention. - Make all the I_Errors return false and print to the console instead. - New prints for missing files if you can't fit it all into one packet. - Make the startmessage warning less specific and direct you to the logfile, to accomodate all the different ways legacy downloads can fail. --- src/d_clisrv.c | 9 +++++---- src/d_netfil.c | 42 +++++++++++++++++++++++++++++++----------- 2 files changed, 36 insertions(+), 15 deletions(-) diff --git a/src/d_clisrv.c b/src/d_clisrv.c index 55276e69..9e1749b3 100644 --- a/src/d_clisrv.c +++ b/src/d_clisrv.c @@ -1335,8 +1335,10 @@ static inline void CL_DrawConnectionStatus(void) strncpy(tempname, filename, sizeof(tempname)-1); } + V_DrawCenteredString(BASEVIDWIDTH/2, BASEVIDHEIGHT-58-30, 0, + va(M_GetText("%s downloading"), ((cl_mode == CL_DOWNLOADHTTPFILES) ? "\x82""HTTP" : "\x85""Direct"))); V_DrawCenteredString(BASEVIDWIDTH/2, BASEVIDHEIGHT-58-22, V_YELLOWMAP, - va(M_GetText("Downloading \"%s\""), tempname)); + va(M_GetText("\"%s\""), tempname)); V_DrawString(BASEVIDWIDTH/2-128, BASEVIDHEIGHT-58, V_20TRANS|V_MONOSPACE, va(" %4uK/%4uK",fileneeded[lastfilenum].currentsize>>10,file->totalsize>>10)); V_DrawRightAlignedString(BASEVIDWIDTH/2+128, BASEVIDHEIGHT-58, V_20TRANS|V_MONOSPACE, @@ -2483,9 +2485,8 @@ static boolean CL_ServerConnectionTicker(const char *tmpsave, tic_t *oldtic, tic CL_Reset(); D_StartTitle(); M_StartMessage(M_GetText( - "The legacy file downloader could not handle that many files.\n" - "Ask the server host to set up a http source, or\n" - "locate and download the necessary files yourself.\n" + "The direct download encountered an error.\n" + "See the logfile for more info.\n" "\n" "Press ESC\n" ), NULL, MM_NOTHING); diff --git a/src/d_netfil.c b/src/d_netfil.c index adf68206..d3114dfa 100644 --- a/src/d_netfil.c +++ b/src/d_netfil.c @@ -300,7 +300,7 @@ boolean CL_CheckDownloadable(void) } // The following was written and then quickly deemed too fragile on paper to be worth testing. -//#DEFINE MORELEGACYDOWNLOADER +//#define MORELEGACYDOWNLOADER /** Sends requests for files in the ::fileneeded table with a status of * ::FS_NOTFOUND. @@ -321,18 +321,21 @@ boolean CL_SendRequestFile(void) #ifdef PARANOIA if (M_CheckParm("-nodownload")) - I_Error("CL_SendRequestFile: Attempted to download files in -nodownload mode"); + { + CONS_Printf("Direct download - Attempted to download files in -nodownload mode"); + return false; + } #endif for (i = 0; i < fileneedednum; i++) { -#ifdef PARANOIA if (fileneeded[i].status != FS_FOUND && fileneeded[i].status != FS_OPEN && (fileneeded[i].willsend == 0 || fileneeded[i].willsend == 2)) { - I_Error("CL_SendRequestFile: Attempted to download files that were not sendable"); + CONS_Printf("Direct download - attempted to download files that were not sendable\n"); + return false; } -#endif + if ((fileneeded[i].status == FS_NOTFOUND || fileneeded[i].status == FS_MD5SUMBAD || fileneeded[i].status == FS_FALLBACK)) { // Error check for the first time around. @@ -342,9 +345,13 @@ boolean CL_SendRequestFile(void) I_GetDiskFreeSpace(&availablefreespace); if (totalfreespaceneeded > availablefreespace) - I_Error("To play on this server you must download %s KB,\n" - "but you have only %s KB free space on this drive\n", + { + CONS_Printf("Direct download -\n" + " To play on this server you must download %s KB,\n" + " but you have only %s KB free space on this drive\n", sizeu1((size_t)(totalfreespaceneeded>>10)), sizeu2((size_t)(availablefreespace>>10))); + return false; + } #ifdef MORELEGACYDOWNLOADER tryagain: @@ -386,20 +393,33 @@ tryagain: #else // If we're not trying extralong legacy download requests, gotta bail. if (skippedafile != -1) + { +#ifndef MORELEGACYDOWNLOADER + CONS_Printf("Direct download - missing files are as follows:\n"); + for (i = 0; i < fileneedednum; i++) + { + if ((fileneeded[i].status == FS_NOTFOUND || fileneeded[i].status == FS_MD5SUMBAD || fileneeded[i].status == FS_FALLBACK || fileneeded[i].status == FS_REQUESTED)) // FS_REQUESTED added + CONS_Printf(" %s\n", fileneeded[i].filename); + } +#endif return false; - else + } #endif I_mkdir(downloaddir, 0755); -#ifdef PARANOIA // Couldn't fit a single one in? if (p == (char *)netbuffer->u.textcmd) - I_Error("CL_SendRequestFile: Fileneeded name for %s (fileneeded[%d]) too long??", (p > 0 ? fileneeded[p].filename : NULL), p); -#endif + { + CONS_Printf("Direct download - fileneeded name for %s (fileneeded[%d]) too long??\n", (skippedafile != -1 ? fileneeded[skippedafile].filename : NULL), skippedafile); + return false; + } WRITEUINT8(p, 0xFF); // terminator if (!HSendPacket(servernode, true, 0, p - (char *)netbuffer->u.textcmd)) + { + CONS_Printf("Direct download - unable to send packet.\n"); return false; + } #ifdef MORELEGACYDOWNLOADER if (skippedafile != -1) From b19004ae3bc12e3563cbbef92f162ea51537fda4 Mon Sep 17 00:00:00 2001 From: toaster Date: Mon, 31 Oct 2022 17:51:25 +0000 Subject: [PATCH 75/92] Enable extended legacy downloader Tested with a full 255 WAD server, it's a LITTLE silly but I think it's inexplicably safe to ship!? --- src/d_netfil.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/d_netfil.c b/src/d_netfil.c index d3114dfa..fac60d28 100644 --- a/src/d_netfil.c +++ b/src/d_netfil.c @@ -299,8 +299,8 @@ boolean CL_CheckDownloadable(void) return false; } -// The following was written and then quickly deemed too fragile on paper to be worth testing. -//#define MORELEGACYDOWNLOADER +// The following was written and, against all odds, works. +#define MORELEGACYDOWNLOADER /** Sends requests for files in the ::fileneeded table with a status of * ::FS_NOTFOUND. From 3cce3ec2ce607bef480a5583f7d6a9b02e2791c3 Mon Sep 17 00:00:00 2001 From: toaster Date: Mon, 31 Oct 2022 18:06:34 +0000 Subject: [PATCH 76/92] Use Partial Addfile for G_LoadDemoExtraFiles as well Still cope compared to the MP addfile codepath, but not the n^2 time cope of before. --- src/g_game.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/g_game.c b/src/g_game.c index cc5a1044..c6b633ce 100644 --- a/src/g_game.c +++ b/src/g_game.c @@ -7014,10 +7014,13 @@ static void G_LoadDemoExtraFiles(UINT8 **pp) } else { - P_AddWadFile(filename); + P_PartialAddWadFile(filename); } } } + + if (P_PartialAddGetStage() >= 0) + P_MultiSetupWadFiles(true); // in case any partial adds were done } static void G_SkipDemoExtraFiles(UINT8 **pp) From a7ae0e8677c796661531a6ffe7d69d2556204131 Mon Sep 17 00:00:00 2001 From: toaster Date: Mon, 31 Oct 2022 18:20:21 +0000 Subject: [PATCH 77/92] Fixes interpolated z for portals (x and y were fixed already) --- src/r_main.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/r_main.c b/src/r_main.c index b6643f06..11de07e3 100644 --- a/src/r_main.c +++ b/src/r_main.c @@ -1220,7 +1220,7 @@ static void R_PortalFrame(line_t *start, line_t *dest, portal_pair *portal) dest_c.y = (dest->v1->y + dest->v2->y) / 2; // Heights! - newview->z += dest->frontsector->floorheight - start->frontsector->floorheight; + viewz += dest->frontsector->floorheight - start->frontsector->floorheight; // calculate the difference in position and rotation! #ifdef ANGLED_PORTALS From 2d8794a8b5edac81ec5ac4eefba33ed8802e634d Mon Sep 17 00:00:00 2001 From: James R Date: Thu, 13 Aug 2020 21:57:36 -0700 Subject: [PATCH 78/92] Use AsciiChar to get the input from windows console window --- src/sdl/i_system.c | 18 ++---------------- 1 file changed, 2 insertions(+), 16 deletions(-) diff --git a/src/sdl/i_system.c b/src/sdl/i_system.c index cc3a37e6..50204f3f 100644 --- a/src/sdl/i_system.c +++ b/src/sdl/i_system.c @@ -567,14 +567,12 @@ static void Impl_HandleKeyboardConsoleEvent(KEY_EVENT_RECORD evt, HANDLE co) case VK_TAB: event.data1 = KEY_NULL; break; - case VK_SHIFT: - event.data1 = KEY_LSHIFT; - break; case VK_RETURN: entering_con_command = false; // Fall through. default: - event.data1 = MapVirtualKey(evt.wVirtualKeyCode,2); // convert in to char + //event.data1 = MapVirtualKey(evt.wVirtualKeyCode,2); // convert in to char + event.data1 = evt.uChar.AsciiChar; } if (co != INVALID_HANDLE_VALUE && GetFileType(co) == FILE_TYPE_CHAR && GetConsoleMode(co, &t)) { @@ -593,18 +591,6 @@ static void Impl_HandleKeyboardConsoleEvent(KEY_EVENT_RECORD evt, HANDLE co) } } } - else - { - event.type = ev_keyup; - switch (evt.wVirtualKeyCode) - { - case VK_SHIFT: - event.data1 = KEY_LSHIFT; - break; - default: - break; - } - } if (event.data1) D_PostEvent(&event); } From cbe5479712e5b9b5606de337cb681ac7b99fe532 Mon Sep 17 00:00:00 2001 From: toaster Date: Mon, 31 Oct 2022 20:55:52 +0000 Subject: [PATCH 79/92] EXPERIMENTAL: Dedicated server idling system - If no clients at server start or after 10 seconds of GS_LEVEL, and no Netxcmd waiting to be digested, halt all SV_MakeTic. - Currently #define'd out, but if we don't get to test it before 1.6 release, I fully encourage community build developers to enable this codepath and trial it on their servers. - It's absolutely netsafe to only have enabled on the host's end, the only risk is that a dedicated server might not re-awaken when presented with certain stimuli. --- src/d_clisrv.c | 52 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 52 insertions(+) diff --git a/src/d_clisrv.c b/src/d_clisrv.c index 9e1749b3..53a24056 100644 --- a/src/d_clisrv.c +++ b/src/d_clisrv.c @@ -6198,6 +6198,9 @@ FILESTAMP SV_FileSendTicker(); } +// If a tree falls in the forest but nobody is around to hear it, does it make a tic? +//#define DEDICATEDIDLETIME (10*TICRATE) + void NetUpdate(void) { static tic_t resptime = 0; @@ -6210,6 +6213,55 @@ void NetUpdate(void) if (realtics <= 0) // nothing new to update return; + +#ifdef DEDICATEDIDLETIME + if (server && dedicated && gamestate == GS_LEVEL) + { + static tic_t dedicatedidle = 0; + + for (i = 1; i < MAXNETNODES; ++i) + if (nodeingame[i]) + { + if (dedicatedidle == DEDICATEDIDLETIME) + { + CONS_Printf("DEDICATED: Awakening from idle (Node %d detected...)\n", i); + dedicatedidle = 0; + } + break; + } + + if (i == MAXNETNODES) + { + if (leveltime == 2) + { + // On next tick... + dedicatedidle = DEDICATEDIDLETIME-1; + } + else if (dedicatedidle == DEDICATEDIDLETIME) + { + if (D_GetExistingTextcmd(gametic, 0) || D_GetExistingTextcmd(gametic+1, 0)) + { + CONS_Printf("DEDICATED: Awakening from idle (Netxcmd detected...)\n"); + dedicatedidle = 0; + } + else + { + realtics = 0; + } + } + else if (++dedicatedidle == DEDICATEDIDLETIME) + { + char *idlereason = "at round start"; + if (leveltime > 3) + idlereason = va("for %d seconds", dedicatedidle/TICRATE); + + CONS_Printf("DEDICATED: No nodes %s, idling...\n", idlereason); + realtics = 0; + } + } + } +#endif + if (realtics > 5) { if (server) From 2dca300891453ebeea62306c7e2b99e5c00de1cf Mon Sep 17 00:00:00 2001 From: toaster Date: Mon, 31 Oct 2022 21:23:16 +0000 Subject: [PATCH 80/92] Update maps.kart hash again --- src/config.h.in | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/config.h.in b/src/config.h.in index eccef2a3..44608500 100644 --- a/src/config.h.in +++ b/src/config.h.in @@ -53,7 +53,7 @@ #define ASSET_HASH_GFX_KART "06f86ee16136eb8a7043b15001797034" #define ASSET_HASH_TEXTURES_KART "abb53d56aba47c3a8cb0f764da1c8b80" #define ASSET_HASH_CHARS_KART "e2c428347dde52858a3dacd29fc5b964" -#define ASSET_HASH_MAPS_KART "59b75e162b4d29332b3c3f6b9c4e806a" +#define ASSET_HASH_MAPS_KART "3be48978af1807bdbd6cea1eb9eecb49" #ifdef USE_PATCH_KART #define ASSET_HASH_PATCH_KART "00000000000000000000000000000000" #endif From 8d9f684708264676e2426c5cc937b7c92d56eaf7 Mon Sep 17 00:00:00 2001 From: toaster Date: Mon, 31 Oct 2022 23:14:26 +0000 Subject: [PATCH 81/92] Fix "NO CONTEST" support for exitlevel-replay hut interaction --- src/m_menu.c | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/src/m_menu.c b/src/m_menu.c index de927783..4bdc6ee0 100644 --- a/src/m_menu.c +++ b/src/m_menu.c @@ -5603,6 +5603,18 @@ static void DrawReplayHutReplayInfo(void) if (demolist[dir_on[menudepthleft]].gametype == GT_RACE) { V_DrawThinString(x, y+39, V_SNAPTOTOP|highlightflags, "TIME"); + } + else + { + V_DrawThinString(x, y+39, V_SNAPTOTOP|highlightflags, "SCORE"); + } + + if (demolist[dir_on[menudepthleft]].standings[0].timeorscore == (UINT32_MAX-1)) + { + V_DrawThinString(x+32, y+40-1, V_SNAPTOTOP, "NO CONTEST"); + } + else if (demolist[dir_on[menudepthleft]].gametype == GT_RACE) + { V_DrawRightAlignedString(x+84, y+40, V_SNAPTOTOP, va("%d'%02d\"%02d", G_TicsToMinutes(demolist[dir_on[menudepthleft]].standings[0].timeorscore, true), G_TicsToSeconds(demolist[dir_on[menudepthleft]].standings[0].timeorscore), @@ -5611,7 +5623,6 @@ static void DrawReplayHutReplayInfo(void) } else { - V_DrawThinString(x, y+39, V_SNAPTOTOP|highlightflags, "SCORE"); V_DrawString(x+32, y+40, V_SNAPTOTOP, va("%d", demolist[dir_on[menudepthleft]].standings[0].timeorscore)); } From 9974a582522f8f27636774acab79c3470c760e18 Mon Sep 17 00:00:00 2001 From: toaster Date: Mon, 31 Oct 2022 23:18:06 +0000 Subject: [PATCH 82/92] Catch the player in 4k if they try to use the SPB-eggbox combo trick Same rules as thunder shield - the SPBis coming out again ASAP. --- src/k_kart.c | 37 +++++++++++++++++++++++++------------ 1 file changed, 25 insertions(+), 12 deletions(-) diff --git a/src/k_kart.c b/src/k_kart.c index d040b8d2..7cafa491 100644 --- a/src/k_kart.c +++ b/src/k_kart.c @@ -2166,7 +2166,7 @@ void K_SpinPlayer(player_t *player, mobj_t *source, INT32 type, mobj_t *inflicto player->kartstuff[k_spinouttype] = type; - if (player->kartstuff[k_spinouttype] <= 0) // type 0 is spinout, type 1 is wipeout + if (player->kartstuff[k_spinouttype] <= 0) // type 0 is spinout, type 1 is wipeout, type 2 is spb { // At spinout, player speed is increased to 1/4 their regular speed, moving them forward if (player->speed < K_GetKartSpeed(player, true)/4) @@ -2332,6 +2332,7 @@ void K_SquishPlayer(player_t *player, mobj_t *source, mobj_t *inflictor) void K_ExplodePlayer(player_t *player, mobj_t *source, mobj_t *inflictor) // A bit of a hack, we just throw the player up higher here and extend their spinout timer { + fixed_t upgoer; UINT8 scoremultiply = 1; #ifdef HAVE_BLUA boolean force = false; // Used to check if Lua ShouldExplode should get us damaged reguardless of flashtics or heck knows what. @@ -2378,9 +2379,6 @@ void K_ExplodePlayer(player_t *player, mobj_t *source, mobj_t *inflictor) // A b if (source && source != player->mo && source->player) K_PlayHitEmSound(source); - player->mo->momz = 18*mapobjectscale*P_MobjFlip(player->mo); // please stop forgetting mobjflip checks!!!! - player->mo->momx = player->mo->momy = 0; - player->kartstuff[k_sneakertimer] = 0; player->kartstuff[k_driftboost] = 0; @@ -2428,19 +2426,34 @@ void K_ExplodePlayer(player_t *player, mobj_t *source, mobj_t *inflictor) // A b K_CheckBumpers(); } - player->kartstuff[k_spinouttype] = 1; - player->kartstuff[k_spinouttimer] = (3*TICRATE/2)+2; - player->powers[pw_flashing] = K_GetKartFlashing(player); + upgoer = (18*mapobjectscale*P_MobjFlip(player->mo)); + if (player->mo->eflags & MFE_UNDERWATER) + upgoer = (117 * upgoer) / 200; + +#define EXPLODESPINTIME ((3*TICRATE/2)+2) + if (inflictor && inflictor->type == MT_SPBEXPLOSION && inflictor->extravalue1) { - player->kartstuff[k_spinouttimer] = ((5*player->kartstuff[k_spinouttimer])/2)+1; - player->mo->momz *= 2; + player->kartstuff[k_spinouttype] = 2; + player->kartstuff[k_spinouttimer] = (5*EXPLODESPINTIME/2)+1; + player->mo->momz = upgoer*2; } + else + { + if (player->kartstuff[k_spinouttype] == 2) + { + // We're on to your tricks. But let's not STOP the tech - let's make you have to work extra hard for it to pay off. + indirectitemcooldown = 0; + } + player->kartstuff[k_spinouttype] = 1; + player->kartstuff[k_spinouttimer] = EXPLODESPINTIME; + player->mo->momz = upgoer; + } + player->mo->momx = player->mo->momy = 0; - if (player->mo->eflags & MFE_UNDERWATER) - player->mo->momz = (117 * player->mo->momz) / 200; +#undef SPINTIME if (player->mo->state != &states[S_KART_SPIN]) P_SetPlayerMobjState(player->mo, S_KART_SPIN); @@ -4748,7 +4761,7 @@ void K_KartPlayerThink(player_t *player, ticcmd_t *cmd) if (player->kartstuff[k_spinouttimer]) { - if ((P_IsObjectOnGround(player->mo) || player->kartstuff[k_spinouttype] == 1) + if ((P_IsObjectOnGround(player->mo) || ((player->kartstuff[k_spinouttype]+1)/2 == 1)) // spinouttype 1 and 2 - explosion and spb && (player->kartstuff[k_sneakertimer] == 0)) { player->kartstuff[k_spinouttimer]--; From d52c77dba46f7bf09ea304d36f794dbdcc1609e5 Mon Sep 17 00:00:00 2001 From: toaster Date: Mon, 31 Oct 2022 23:21:56 +0000 Subject: [PATCH 83/92] Enable experimental dedicated idle system There's enough confidence among krew that there's zero apparent downside to this, but made sure to do it as a seperate commit if we have to hit the emergency switch and revert --- 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 53a24056..b4c39ccb 100644 --- a/src/d_clisrv.c +++ b/src/d_clisrv.c @@ -6199,7 +6199,7 @@ FILESTAMP } // If a tree falls in the forest but nobody is around to hear it, does it make a tic? -//#define DEDICATEDIDLETIME (10*TICRATE) +#define DEDICATEDIDLETIME (10*TICRATE) void NetUpdate(void) { From 8d2d926e707a4cb048b2064ba0c7c8ea24db86f3 Mon Sep 17 00:00:00 2001 From: toaster Date: Mon, 31 Oct 2022 23:24:23 +0000 Subject: [PATCH 84/92] Const qualifier warning --- 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 b4c39ccb..9350b485 100644 --- a/src/d_clisrv.c +++ b/src/d_clisrv.c @@ -6251,7 +6251,7 @@ void NetUpdate(void) } else if (++dedicatedidle == DEDICATEDIDLETIME) { - char *idlereason = "at round start"; + const char *idlereason = "at round start"; if (leveltime > 3) idlereason = va("for %d seconds", dedicatedidle/TICRATE); From 76b719ae9088298d34d28ad324be74e3a75faf34 Mon Sep 17 00:00:00 2001 From: Eidolon Date: Mon, 31 Oct 2022 20:23:49 -0500 Subject: [PATCH 85/92] Use precip interp if gl sprite is for precip --- src/hardware/hw_main.c | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/src/hardware/hw_main.c b/src/hardware/hw_main.c index f01bc5cd..b169c3d4 100644 --- a/src/hardware/hw_main.c +++ b/src/hardware/hw_main.c @@ -3026,11 +3026,25 @@ static void HWR_RotateSpritePolyToAim(gr_vissprite_t *spr, FOutVector *wallVerts // do interpolation if (R_UsingFrameInterpolation() && !paused) { - R_InterpolateMobjState(spr->mobj, rendertimefrac, &interp); + if (spr->precip) + { + R_InterpolatePrecipMobjState(spr->mobj, rendertimefrac, &interp); + } + else + { + R_InterpolateMobjState(spr->mobj, rendertimefrac, &interp); + } } else { - R_InterpolateMobjState(spr->mobj, FRACUNIT, &interp); + if (spr->precip) + { + R_InterpolatePrecipMobjState(spr->mobj, FRACUNIT, &interp); + } + else + { + R_InterpolateMobjState(spr->mobj, FRACUNIT, &interp); + } } if (P_MobjFlip(spr->mobj) == -1) From 4bc482bdf5b63968cededf9b6d2d04afe1fb55a9 Mon Sep 17 00:00:00 2001 From: toaster Date: Tue, 1 Nov 2022 13:41:04 +0000 Subject: [PATCH 86/92] Explicit pointer cast for R_InterpolatePrecipMobjState --- src/hardware/hw_main.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/hardware/hw_main.c b/src/hardware/hw_main.c index b169c3d4..86dd7c34 100644 --- a/src/hardware/hw_main.c +++ b/src/hardware/hw_main.c @@ -3028,7 +3028,7 @@ static void HWR_RotateSpritePolyToAim(gr_vissprite_t *spr, FOutVector *wallVerts { if (spr->precip) { - R_InterpolatePrecipMobjState(spr->mobj, rendertimefrac, &interp); + R_InterpolatePrecipMobjState((precipmobj_t *)spr->mobj, rendertimefrac, &interp); } else { @@ -3039,7 +3039,7 @@ static void HWR_RotateSpritePolyToAim(gr_vissprite_t *spr, FOutVector *wallVerts { if (spr->precip) { - R_InterpolatePrecipMobjState(spr->mobj, FRACUNIT, &interp); + R_InterpolatePrecipMobjState((precipmobj_t *)spr->mobj, FRACUNIT, &interp); } else { From 06040733515f5f77cda35b2e4c2896c9725a1666 Mon Sep 17 00:00:00 2001 From: toaster Date: Tue, 1 Nov 2022 13:41:19 +0000 Subject: [PATCH 87/92] Update maps.kart hash again again --- src/config.h.in | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/config.h.in b/src/config.h.in index 44608500..5225c8b0 100644 --- a/src/config.h.in +++ b/src/config.h.in @@ -40,7 +40,7 @@ * Last updated 2020 / 08 / 30 - Kart v1.3 - patch.kart * Last updated 2022 / 08 / 16 - Kart v1.4 - Main assets * Last updated 2022 / 08 / 19 - Kart v1.5 - gfx.kart - * Last updated 2022 / 10 / 31 - Kart v1.6 - gfx.kart, maps.kart + * Last updated 2022 / 11 / 01 - Kart v1.6 - gfx.kart, maps.kart */ // Base SRB2 hashes @@ -53,7 +53,7 @@ #define ASSET_HASH_GFX_KART "06f86ee16136eb8a7043b15001797034" #define ASSET_HASH_TEXTURES_KART "abb53d56aba47c3a8cb0f764da1c8b80" #define ASSET_HASH_CHARS_KART "e2c428347dde52858a3dacd29fc5b964" -#define ASSET_HASH_MAPS_KART "3be48978af1807bdbd6cea1eb9eecb49" +#define ASSET_HASH_MAPS_KART "d051e55141ba736582228c456953cd98" #ifdef USE_PATCH_KART #define ASSET_HASH_PATCH_KART "00000000000000000000000000000000" #endif From f30f1bf1636d724aa0049193c3731186f82e4583 Mon Sep 17 00:00:00 2001 From: toaster Date: Tue, 1 Nov 2022 13:45:31 +0000 Subject: [PATCH 88/92] Do a little more short circuiting if dedicated idle time is occouring Skips resync, clearticcmd, and sendtics, since all they're doing with no nodes in game is writing the same value to the same address again and again --- 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 9350b485..727eba4a 100644 --- a/src/d_clisrv.c +++ b/src/d_clisrv.c @@ -6304,7 +6304,7 @@ FILESTAMP } else { - if (!demo.playback) + if (!demo.playback && realtics > 0) { INT32 counts; From 234bdc90bfda1168e9afa396e4f683523682a849 Mon Sep 17 00:00:00 2001 From: toaster Date: Tue, 1 Nov 2022 21:30:10 +0000 Subject: [PATCH 89/92] Legacy downloader adjustments - Fixed off by one in Got_RequestFilePak that could cause correct, maximised-space-usage packets to be rejected - More verbose printing for aborting send files - More verbose printing for client request files (behind a define for troubleshooting) --- src/d_netfil.c | 28 +++++++++++++++++++++------- 1 file changed, 21 insertions(+), 7 deletions(-) diff --git a/src/d_netfil.c b/src/d_netfil.c index fac60d28..b34ca351 100644 --- a/src/d_netfil.c +++ b/src/d_netfil.c @@ -358,6 +358,10 @@ tryagain: skippedafile = -1; #endif +#ifdef VERBOSEREQUESTFILE + CONS_Printf("Preparing packet\n"); +#endif + netbuffer->packettype = PT_REQUESTFILE; p = (char *)netbuffer->u.textcmd; @@ -371,7 +375,7 @@ tryagain: // Figure out if we'd overrun our buffer. checklen = strlen(fileneeded[i].filename)+2; // plus the fileid (and terminator, in case this is last) - if ((UINT8 *)(p + checklen) > netbuffer->u.textcmd + MAXTEXTCMD-1) + if ((UINT8 *)(p + checklen) >= netbuffer->u.textcmd + MAXTEXTCMD) { skippedafile = i; // we might have a shorter file that can fit in the remaining space, and file ID permits out-of-order data @@ -382,6 +386,10 @@ tryagain: WRITEUINT8(p, i); // fileid WRITESTRINGN(p, fileneeded[i].filename, MAX_WADPATH); +#ifdef VERBOSEREQUESTFILE + CONS_Printf(" file \"%s\" (id %d)\n", i, fileneeded[i].filename); +#endif + // put it in download dir strcatbf(fileneeded[i].filename, downloaddir, "/"); fileneeded[i].status = FS_REQUESTED; @@ -394,14 +402,12 @@ tryagain: // If we're not trying extralong legacy download requests, gotta bail. if (skippedafile != -1) { -#ifndef MORELEGACYDOWNLOADER CONS_Printf("Direct download - missing files are as follows:\n"); for (i = 0; i < fileneedednum; i++) { if ((fileneeded[i].status == FS_NOTFOUND || fileneeded[i].status == FS_MD5SUMBAD || fileneeded[i].status == FS_FALLBACK || fileneeded[i].status == FS_REQUESTED)) // FS_REQUESTED added CONS_Printf(" %s\n", fileneeded[i].filename); } -#endif return false; } #endif @@ -429,6 +435,10 @@ tryagain: } #endif +#ifdef VERBOSEREQUESTFILE + CONS_Printf("Returning true\n"); +#endif + return true; } @@ -439,14 +449,16 @@ boolean Got_RequestFilePak(INT32 node) char wad[MAX_WADPATH+1]; UINT8 *p = netbuffer->u.textcmd; UINT8 id; - while (p < netbuffer->u.textcmd + MAXTEXTCMD-1) // Don't allow hacked client to overflow + while (p < netbuffer->u.textcmd + MAXTEXTCMD) // Don't allow hacked client to overflow { id = READUINT8(p); if (id == 0xFF) break; READSTRINGN(p, wad, MAX_WADPATH); - if (p >= netbuffer->u.textcmd + MAXTEXTCMD-1 || !SV_SendFile(node, wad, id)) + if (p >= netbuffer->u.textcmd + MAXTEXTCMD || !SV_SendFile(node, wad, id)) { + if (cv_noticedownload.value) + CONS_Printf("Bad PT_REQUESTFILE from node %d!\n", node); SV_AbortSendFiles(node); return false; // don't read any more } @@ -621,7 +633,7 @@ static boolean SV_SendFile(INT32 node, const char *filename, UINT8 fileid) char wadfilename[MAX_WADPATH]; if (cv_noticedownload.value) - CONS_Printf("Sending file \"%s\" to node %d (%s)\n", filename, node, I_GetNodeAddress(node)); + CONS_Printf("Sending file \"%s\" (id %d) to node %d (%s)\n", filename, fileid, node, I_GetNodeAddress(node)); // Find the last file in the list and set a pointer to its "next" field q = &transfer[node].txlist; @@ -747,7 +759,7 @@ static void SV_EndFileSend(INT32 node) { case SF_FILE: // It's a file, close it and free its filename if (cv_noticedownload.value) - CONS_Printf("Ending file transfer for node %d\n", node); + CONS_Printf("Ending file transfer (id %d) for node %d\n", p->fileid, node); if (transfer[node].currentfile) fclose(transfer[node].currentfile); free(p->id.filename); @@ -1019,6 +1031,8 @@ boolean SV_SendingFile(INT32 node) */ void SV_AbortSendFiles(INT32 node) { + if (cv_noticedownload.value) + CONS_Printf("Aborting send files for node %d...\n", node); while (transfer[node].txlist) SV_EndFileSend(node); } From 3a720a61cd225a3b1d40fca566396a59ac26e898 Mon Sep 17 00:00:00 2001 From: SteelT Date: Tue, 1 Nov 2022 18:15:13 -0400 Subject: [PATCH 90/92] Add MD5 checking to HTTP downloading In cases of where the file mismatches from what the server expects, it will fall back to direct downloading of the file. --- src/d_netfil.c | 29 ++++++++++++++++++++--------- 1 file changed, 20 insertions(+), 9 deletions(-) diff --git a/src/d_netfil.c b/src/d_netfil.c index fac60d28..6f8ad4f9 100644 --- a/src/d_netfil.c +++ b/src/d_netfil.c @@ -1239,6 +1239,7 @@ void CURLGetFile(void) int msgs_left; /* how many messages are left */ const char *easy_handle_error; long response_code = 0; + static char *filename; if (curl_runninghandles) { @@ -1263,6 +1264,8 @@ void CURLGetFile(void) { e = m->easy_handle; easyres = m->data.result; + filename = Z_StrDup(curl_realname); + nameonly(filename); if (easyres != CURLE_OK) { if (easyres == CURLE_HTTP_RETURNED_ERROR) @@ -1275,21 +1278,29 @@ void CURLGetFile(void) curl_failedwebdownload = true; fclose(curl_curfile->file); remove(curl_curfile->filename); - curl_curfile->file = NULL; - //nameonly(curl_curfile->filename); - nameonly(curl_realname); - CONS_Printf(M_GetText("Failed to download %s (%s)\n"), curl_realname, easy_handle_error); + CONS_Printf(M_GetText("Failed to download %s (%s)\n"), filename, easy_handle_error); } else { - nameonly(curl_realname); - CONS_Printf(M_GetText("Finished downloading %s\n"), curl_realname); - downloadcompletednum++; - downloadcompletedsize += curl_curfile->totalsize; - curl_curfile->status = FS_FOUND; fclose(curl_curfile->file); + + if (checkfilemd5(curl_curfile->filename, curl_curfile->md5sum) == FS_MD5SUMBAD) + { + CONS_Alert(CONS_ERROR, M_GetText("HTTP Download of %s finished but is corrupt or has been modified\n"), filename); + curl_curfile->status = FS_FALLBACK; + } + else + { + CONS_Printf(M_GetText("Finished HTTP download of %s\n"), filename); + downloadcompletednum++; + downloadcompletedsize += curl_curfile->totalsize; + curl_curfile->status = FS_FOUND; + } } + + Z_Free(filename); + curl_curfile->file = NULL; curl_running = false; curl_transfers--; curl_multi_remove_handle(multi_handle, e); From 74421b7700ab6adb0837cc0d915a3598084cae88 Mon Sep 17 00:00:00 2001 From: SteelT Date: Tue, 1 Nov 2022 18:49:42 -0400 Subject: [PATCH 91/92] Set curl_failedwebdownload to true for corrupt or modified HTTP downloads. So that it actually falls back to direct downloading --- src/d_netfil.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/d_netfil.c b/src/d_netfil.c index 7072037e..80d9fdc4 100644 --- a/src/d_netfil.c +++ b/src/d_netfil.c @@ -1302,6 +1302,7 @@ void CURLGetFile(void) { CONS_Alert(CONS_ERROR, M_GetText("HTTP Download of %s finished but is corrupt or has been modified\n"), filename); curl_curfile->status = FS_FALLBACK; + curl_failedwebdownload = true; } else { From 024a140e8d8a1f86ccf16ef3dd93e3bccbe3bd73 Mon Sep 17 00:00:00 2001 From: toaster Date: Tue, 1 Nov 2022 22:57:04 +0000 Subject: [PATCH 92/92] Do not include spurious noticedownload aborting sendfile reports --- src/d_netfil.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/d_netfil.c b/src/d_netfil.c index 80d9fdc4..663bcc86 100644 --- a/src/d_netfil.c +++ b/src/d_netfil.c @@ -1031,8 +1031,6 @@ boolean SV_SendingFile(INT32 node) */ void SV_AbortSendFiles(INT32 node) { - if (cv_noticedownload.value) - CONS_Printf("Aborting send files for node %d...\n", node); while (transfer[node].txlist) SV_EndFileSend(node); }