From e8760fe5dd6ec67c21fc1659458bc0078819d730 Mon Sep 17 00:00:00 2001 From: Louis-Antoine Date: Wed, 22 Jan 2020 23:08:57 +0100 Subject: [PATCH 01/68] Update Lua I/O support to 2.2 --- src/CMakeLists.txt | 1 + src/blua/Makefile.cfg | 1 + src/blua/linit.c | 1 + src/blua/liolib.c | 720 ++++++++++++++++++++++++++++++++++++++++++ src/blua/lualib.h | 3 + src/d_clisrv.c | 26 ++ src/d_clisrv.h | 6 + src/d_main.c | 3 + src/d_net.c | 8 + src/d_netcmd.c | 4 +- src/d_netcmd.h | 1 + src/d_netfil.c | 252 ++++++++++++++- src/d_netfil.h | 40 +++ 13 files changed, 1058 insertions(+), 8 deletions(-) create mode 100644 src/blua/liolib.c diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index e8c9c3182..4971736ca 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -279,6 +279,7 @@ if(${SRB2_CONFIG_HAVE_BLUA}) blua/lfunc.c blua/lgc.c blua/linit.c + blua/liolib.c blua/llex.c blua/lmem.c blua/lobject.c diff --git a/src/blua/Makefile.cfg b/src/blua/Makefile.cfg index 8d2e73714..659faf3c8 100644 --- a/src/blua/Makefile.cfg +++ b/src/blua/Makefile.cfg @@ -18,6 +18,7 @@ OBJS:=$(OBJS) \ $(OBJDIR)/ldo.o \ $(OBJDIR)/lfunc.o \ $(OBJDIR)/linit.o \ + $(OBJDIR)/liolib.o \ $(OBJDIR)/llex.o \ $(OBJDIR)/lmem.o \ $(OBJDIR)/lobject.o \ diff --git a/src/blua/linit.c b/src/blua/linit.c index 52b02dbe7..d17390b20 100644 --- a/src/blua/linit.c +++ b/src/blua/linit.c @@ -17,6 +17,7 @@ static const luaL_Reg lualibs[] = { {"", luaopen_base}, {LUA_TABLIBNAME, luaopen_table}, + {LUA_IOLIBNAME, luaopen_io}, {LUA_STRLIBNAME, luaopen_string}, {NULL, NULL} }; diff --git a/src/blua/liolib.c b/src/blua/liolib.c new file mode 100644 index 000000000..6ebde08a3 --- /dev/null +++ b/src/blua/liolib.c @@ -0,0 +1,720 @@ +/* +** $Id: liolib.c,v 2.73.1.3 2008/01/18 17:47:43 roberto Exp $ +** Standard I/O (and system) library +** See Copyright Notice in lua.h +*/ + + +#include +#include +#include +#include + +#define liolib_c +#define LUA_LIB + +#include "lua.h" + +#include "lauxlib.h" +#include "lualib.h" +#include "../i_system.h" +#include "../g_game.h" +#include "../d_netfil.h" +#include "../lua_libs.h" +#include "../byteptr.h" +#include "../lua_script.h" +#include "../m_misc.h" + + +#define IO_INPUT 1 +#define IO_OUTPUT 2 + +#define FILELIMIT (1024 * 1024) // Size limit for reading/writing files + +#define FMT_FILECALLBACKID "file_callback_%d" + + +static const char *const fnames[] = {"input", "output"}; +static const char *whitelist[] = { // Allow scripters to write files of these types to SRB2's folder + ".txt", + ".sav2", + ".cfg", + ".png", + ".bmp" +}; + + +static int pushresult (lua_State *L, int i, const char *filename) { + int en = errno; /* calls to Lua API may change this value */ + if (i) { + lua_pushboolean(L, 1); + return 1; + } + else { + lua_pushnil(L); + if (filename) + lua_pushfstring(L, "%s: %s", filename, strerror(en)); + else + lua_pushfstring(L, "%s", strerror(en)); + lua_pushinteger(L, en); + return 3; + } +} + + +static void fileerror (lua_State *L, int arg, const char *filename) { + lua_pushfstring(L, "%s: %s", filename, strerror(errno)); + luaL_argerror(L, arg, lua_tostring(L, -1)); +} + + +#define tofilep(L) ((FILE **)luaL_checkudata(L, 1, LUA_FILEHANDLE)) + + +static int io_type (lua_State *L) { + void *ud; + luaL_checkany(L, 1); + ud = lua_touserdata(L, 1); + lua_getfield(L, LUA_REGISTRYINDEX, LUA_FILEHANDLE); + if (ud == NULL || !lua_getmetatable(L, 1) || !lua_rawequal(L, -2, -1)) + lua_pushnil(L); /* not a file */ + else if (*((FILE **)ud) == NULL) + lua_pushliteral(L, "closed file"); + else + lua_pushliteral(L, "file"); + return 1; +} + + +static FILE *tofile (lua_State *L) { + FILE **f = tofilep(L); + if (*f == NULL) + luaL_error(L, "attempt to use a closed file"); + return *f; +} + + + +/* +** When creating file handles, always creates a `closed' file handle +** before opening the actual file; so, if there is a memory error, the +** file is not left opened. +*/ +static FILE **newfile (lua_State *L) { + FILE **pf = (FILE **)lua_newuserdata(L, sizeof(FILE *)); + *pf = NULL; /* file handle is currently `closed' */ + luaL_getmetatable(L, LUA_FILEHANDLE); + lua_setmetatable(L, -2); + return pf; +} + + +/* +** function to (not) close the standard files stdin, stdout, and stderr +*/ +static int io_noclose (lua_State *L) { + lua_pushnil(L); + lua_pushliteral(L, "cannot close standard file"); + return 2; +} + + +/* +** function to close regular files +*/ +static int io_fclose (lua_State *L) { + FILE **p = tofilep(L); + int ok = (fclose(*p) == 0); + *p = NULL; + return pushresult(L, ok, NULL); +} + + +static int aux_close (lua_State *L) { + lua_getfenv(L, 1); + lua_getfield(L, -1, "__close"); + return (lua_tocfunction(L, -1))(L); +} + + +static int io_gc (lua_State *L) { + FILE *f = *tofilep(L); + /* ignore closed files */ + if (f != NULL) + aux_close(L); + return 0; +} + + +static int io_tostring (lua_State *L) { + FILE *f = *tofilep(L); + if (f == NULL) + lua_pushliteral(L, "file (closed)"); + else + lua_pushfstring(L, "file (%p)", f); + return 1; +} + + +// Create directories in the path +void MakePathDirs(char *path) +{ + char *c; + + for (c = path; *c; c++) + if (*c == '/' || *c == '\\') + { + char sep = *c; + *c = '\0'; + I_mkdir(path, 0755); + *c = sep; + } +} + + +static int io_open (lua_State *L) { + FILE **pf; + const char *filename = luaL_checkstring(L, 1); + boolean pass = false; + size_t i; + int length = strlen(filename); + const char *mode = luaL_optstring(L, 2, "r"); + luafiletransfer_t *filetransfer; + + if (strchr(filename, '\\')) + { + luaL_error(L, "access denied to %s: \\ is not allowed, use / instead", filename); + return pushresult(L,0,filename); + } + + for (i = 0; i < (sizeof (whitelist) / sizeof(const char *)); i++) + if (!stricmp(&filename[length - strlen(whitelist[i])], whitelist[i])) + { + pass = true; + break; + } + if (strstr(filename, "./") + || strstr(filename, "..") || strchr(filename, ':') + || filename[0] == '/' + || !pass) + { + luaL_error(L, "access denied to %s", filename); + return pushresult(L,0,filename); + } + + luaL_checktype(L, 4, LUA_TFUNCTION); + + if (lua_isnil(L, 3) && (strchr(mode, 'r') || strchr(mode, '+'))) // Synched reading + { + AddLuaFileTransfer(filename, mode); + + /*pf = newfile(L); + *pf = fopen(realfilename, mode); + return (*pf == NULL) ? pushresult(L, 0, filename) : 1;*/ + } + else // Local I/O + { + char *realfilename = va("%s" PATHSEP "%s", luafiledir, filename); + player_t *player = *((player_t **)luaL_checkudata(L, 3, META_PLAYER)); + + if (!player) + return LUA_ErrInvalid(L, "player_t"); + + if (player != &players[consoleplayer]) + return 0; + + if (client && strnicmp(filename, "shared/", strlen("shared/"))) + I_Error("Access denied to %s\n" + "Clients can only access files stored in luafiles/shared/\n", + filename); + + // Prevent access if the file is being downloaded + for (filetransfer = luafiletransfers; filetransfer; filetransfer = filetransfer->next) + if (!stricmp(filetransfer->filename, filename)) + I_Error("Access denied to %s\n" + "Files can't be opened while being downloaded\n", + filename); + + MakePathDirs(realfilename); + + // The callback is the last argument, no need to push it again + + // Push the first argument (file handle) on the stack + pf = newfile(gL); // Create and push the file handle + *pf = fopen(realfilename, mode); // Open the file + if (!*pf) + { + lua_pop(gL, 1); + lua_pushnil(gL); + } + + // Push the second argument (file name) on the stack + lua_pushstring(gL, filename); + + // Call the callback + LUA_Call(gL, 2); + + // Close the file + if (*pf) + { + fclose(*pf); + *pf = NULL; + } + } + + return 0; // !!! Todo: error handling? +} + + +void Got_LuaFile(UINT8 **cp, INT32 playernum) +{ + FILE **pf; + boolean success = READUINT8(*cp); // The first (and only) byte indicates whether the file could be opened + + if (playernum != serverplayer) + { + CONS_Alert(CONS_WARNING, M_GetText("Illegal luafile command received from %s\n"), player_names[playernum]); + if (server) + SendKick(playernum, KICK_MSG_CON_FAIL); + return; + } + + if (!luafiletransfers) + I_Error("No Lua file transfer\n"); + + // Retrieve the callback and push it on the stack + lua_pushfstring(gL, FMT_FILECALLBACKID, luafiletransfers->id); + lua_gettable(gL, LUA_REGISTRYINDEX); + + // Push the first argument (file handle or nil) on the stack + if (success) + { + pf = newfile(gL); // Create and push the file handle + *pf = fopen(luafiletransfers->realfilename, luafiletransfers->mode); // Open the file + if (!*pf) + I_Error("Can't open file \"%s\"\n", luafiletransfers->realfilename); // The file SHOULD exist + } + else + lua_pushnil(gL); + + // Push the second argument (file name) on the stack + lua_pushstring(gL, luafiletransfers->filename); + + // Call the callback + LUA_Call(gL, 2); + + if (success) + { + // Close the file + if (*pf) + { + fclose(*pf); + *pf = NULL; + } + + if (client) + remove(luafiletransfers->realfilename); + } + + RemoveLuaFileTransfer(); + + if (server && luafiletransfers) + { + if (FIL_FileOK(luafiletransfers->realfilename)) + SV_PrepareSendLuaFileToNextNode(); + else + { + // Send a net command with 0 as its first byte to indicate the file couldn't be opened + UINT8 success = 0; + SendNetXCmd(XD_LUAFILE, &success, 1); + } + } +} + + +void StoreLuaFileCallback(INT32 id) +{ + lua_pushfstring(gL, FMT_FILECALLBACKID, id); + lua_pushvalue(gL, 4); // Parameter 4 is the callback + lua_settable(gL, LUA_REGISTRYINDEX); // registry[callbackid] = callback +} + + +void RemoveLuaFileCallback(INT32 id) +{ + lua_pushfstring(gL, FMT_FILECALLBACKID, id); + lua_pushnil(gL); + lua_settable(gL, LUA_REGISTRYINDEX); // registry[callbackid] = nil +} + +static int io_tmpfile (lua_State *L) { + FILE **pf = newfile(L); + *pf = tmpfile(); + return (*pf == NULL) ? pushresult(L, 0, NULL) : 1; +} + + +static FILE *getiofile (lua_State *L, int findex) { + FILE *f; + lua_rawgeti(L, LUA_ENVIRONINDEX, findex); + f = *(FILE **)lua_touserdata(L, -1); + if (f == NULL) + luaL_error(L, "standard %s file is closed", fnames[findex - 1]); + return f; +} + + +static int g_iofile (lua_State *L, int f, const char *mode) { + if (!lua_isnoneornil(L, 1)) { + const char *filename = lua_tostring(L, 1); + if (filename) { + FILE **pf = newfile(L); + *pf = fopen(filename, mode); + if (*pf == NULL) + fileerror(L, 1, filename); + } + else { + tofile(L); /* check that it's a valid file handle */ + lua_pushvalue(L, 1); + } + lua_rawseti(L, LUA_ENVIRONINDEX, f); + } + /* return current value */ + lua_rawgeti(L, LUA_ENVIRONINDEX, f); + return 1; +} + + +static int io_input (lua_State *L) { + return g_iofile(L, IO_INPUT, "r"); +} + + +static int io_output (lua_State *L) { + return g_iofile(L, IO_OUTPUT, "w"); +} + + +static int io_readline (lua_State *L); + + +static void aux_lines (lua_State *L, int idx, int toclose) { + lua_pushvalue(L, idx); + lua_pushboolean(L, toclose); /* close/not close file when finished */ + lua_pushcclosure(L, io_readline, 2); +} + + +static int f_lines (lua_State *L) { + tofile(L); /* check that it's a valid file handle */ + aux_lines(L, 1, 0); + return 1; +} + + +static int io_lines (lua_State *L) { + if (lua_isnoneornil(L, 1)) { /* no arguments? */ + /* will iterate over default input */ + lua_rawgeti(L, LUA_ENVIRONINDEX, IO_INPUT); + return f_lines(L); + } + else { + const char *filename = luaL_checkstring(L, 1); + FILE **pf = newfile(L); + *pf = fopen(filename, "r"); + if (*pf == NULL) + fileerror(L, 1, filename); + aux_lines(L, lua_gettop(L), 1); + return 1; + } +} + + +/* +** {====================================================== +** READ +** ======================================================= +*/ + + +static int read_number (lua_State *L, FILE *f) { + lua_Number d; + if (fscanf(f, LUA_NUMBER_SCAN, &d) == 1) { + lua_pushnumber(L, d); + return 1; + } + else return 0; /* read fails */ +} + + +static int test_eof (lua_State *L, FILE *f) { + int c = getc(f); + ungetc(c, f); + lua_pushlstring(L, NULL, 0); + return (c != EOF); +} + + +static int read_line (lua_State *L, FILE *f) { + luaL_Buffer b; + luaL_buffinit(L, &b); + for (;;) { + size_t l; + char *p = luaL_prepbuffer(&b); + if (fgets(p, LUAL_BUFFERSIZE, f) == NULL) { /* eof? */ + luaL_pushresult(&b); /* close buffer */ + return (lua_objlen(L, -1) > 0); /* check whether read something */ + } + l = strlen(p); + if (l == 0 || p[l-1] != '\n') + luaL_addsize(&b, l); + else { + luaL_addsize(&b, l - 1); /* do not include `eol' */ + luaL_pushresult(&b); /* close buffer */ + return 1; /* read at least an `eol' */ + } + } +} + + +static int read_chars (lua_State *L, FILE *f, size_t n) { + size_t rlen; /* how much to read */ + size_t nr; /* number of chars actually read */ + luaL_Buffer b; + luaL_buffinit(L, &b); + rlen = LUAL_BUFFERSIZE; /* try to read that much each time */ + do { + char *p = luaL_prepbuffer(&b); + if (rlen > n) rlen = n; /* cannot read more than asked */ + nr = fread(p, sizeof(char), rlen, f); + luaL_addsize(&b, nr); + n -= nr; /* still have to read `n' chars */ + } while (n > 0 && nr == rlen); /* until end of count or eof */ + luaL_pushresult(&b); /* close buffer */ + return (n == 0 || lua_objlen(L, -1) > 0); +} + + +static int g_read (lua_State *L, FILE *f, int first) { + int nargs = lua_gettop(L) - 1; + int success; + int n; + clearerr(f); + if (nargs == 0) { /* no arguments? */ + success = read_line(L, f); + n = first+1; /* to return 1 result */ + } + else { /* ensure stack space for all results and for auxlib's buffer */ + luaL_checkstack(L, nargs+LUA_MINSTACK, "too many arguments"); + success = 1; + for (n = first; nargs-- && success; n++) { + if (lua_type(L, n) == LUA_TNUMBER) { + size_t l = (size_t)lua_tointeger(L, n); + success = (l == 0) ? test_eof(L, f) : read_chars(L, f, l); + } + else { + const char *p = lua_tostring(L, n); + luaL_argcheck(L, p && p[0] == '*', n, "invalid option"); + switch (p[1]) { + case 'n': /* number */ + success = read_number(L, f); + break; + case 'l': /* line */ + success = read_line(L, f); + break; + case 'a': /* file */ + read_chars(L, f, ~((size_t)0)); /* read MAX_SIZE_T chars */ + success = 1; /* always success */ + break; + default: + return luaL_argerror(L, n, "invalid format"); + } + } + } + } + if (ferror(f)) + return pushresult(L, 0, NULL); + if (!success) { + lua_pop(L, 1); /* remove last result */ + lua_pushnil(L); /* push nil instead */ + } + return n - first; +} + + +static int io_read (lua_State *L) { + return g_read(L, getiofile(L, IO_INPUT), 1); +} + + +static int f_read (lua_State *L) { + return g_read(L, tofile(L), 2); +} + + +static int io_readline (lua_State *L) { + FILE *f = *(FILE **)lua_touserdata(L, lua_upvalueindex(1)); + int sucess; + if (f == NULL) /* file is already closed? */ + luaL_error(L, "file is already closed"); + sucess = read_line(L, f); + if (ferror(f)) + return luaL_error(L, "%s", strerror(errno)); + if (sucess) return 1; + else { /* EOF */ + if (lua_toboolean(L, lua_upvalueindex(2))) { /* generator created file? */ + lua_settop(L, 0); + lua_pushvalue(L, lua_upvalueindex(1)); + aux_close(L); /* close it */ + } + return 0; + } +} + +/* }====================================================== */ + + +static int g_write (lua_State *L, FILE *f, int arg) { + int nargs = lua_gettop(L) - 1; + int status = 1; + size_t count; + for (; nargs--; arg++) { + if (lua_type(L, arg) == LUA_TNUMBER) { + /* optimization: could be done exactly as for strings */ + status = status && + fprintf(f, LUA_NUMBER_FMT, lua_tonumber(L, arg)) > 0; + } + else { + size_t l; + const char *s = luaL_checklstring(L, arg, &l); + count += l; + if (ftell(f) + l > FILELIMIT) + { + luaL_error(L,"write limit bypassed in file. Changes have been discarded."); + break; + } + status = status && (fwrite(s, sizeof(char), l, f) == l); + } + } + return pushresult(L, status, NULL); +} + + +static int io_write (lua_State *L) { + return g_write(L, getiofile(L, IO_OUTPUT), 1); +} + + +static int f_write (lua_State *L) { + return g_write(L, tofile(L), 2); +} + + +static int f_seek (lua_State *L) { + static const int mode[] = {SEEK_SET, SEEK_CUR, SEEK_END}; + static const char *const modenames[] = {"set", "cur", "end", NULL}; + FILE *f = tofile(L); + int op = luaL_checkoption(L, 2, "cur", modenames); + long offset = luaL_optlong(L, 3, 0); + op = fseek(f, offset, mode[op]); + if (op) + return pushresult(L, 0, NULL); /* error */ + else { + lua_pushinteger(L, ftell(f)); + return 1; + } +} + + +static int f_setvbuf (lua_State *L) { + static const int mode[] = {_IONBF, _IOFBF, _IOLBF}; + static const char *const modenames[] = {"no", "full", "line", NULL}; + FILE *f = tofile(L); + int op = luaL_checkoption(L, 2, NULL, modenames); + lua_Integer sz = luaL_optinteger(L, 3, LUAL_BUFFERSIZE); + int res = setvbuf(f, NULL, mode[op], sz); + return pushresult(L, res == 0, NULL); +} + + + +static int io_flush (lua_State *L) { + return pushresult(L, fflush(getiofile(L, IO_OUTPUT)) == 0, NULL); +} + + +static int f_flush (lua_State *L) { + return pushresult(L, fflush(tofile(L)) == 0, NULL); +} + + +static const luaL_Reg iolib[] = { + {"flush", io_flush}, + {"input", io_input}, + {"lines", io_lines}, + {"open", io_open}, + {"output", io_output}, + {"read", io_read}, + {"tmpfile", io_tmpfile}, + {"type", io_type}, + {"write", io_write}, + {NULL, NULL} +}; + + +static const luaL_Reg flib[] = { + {"flush", f_flush}, + {"lines", f_lines}, + {"read", f_read}, + {"seek", f_seek}, + {"setvbuf", f_setvbuf}, + {"write", f_write}, + {"__gc", io_gc}, + {"__tostring", io_tostring}, + {NULL, NULL} +}; + + +static void createmeta (lua_State *L) { + luaL_newmetatable(L, LUA_FILEHANDLE); /* create metatable for file handles */ + lua_pushvalue(L, -1); /* push metatable */ + lua_setfield(L, -2, "__index"); /* metatable.__index = metatable */ + luaL_register(L, NULL, flib); /* file methods */ +} + + +static void createstdfile (lua_State *L, FILE *f, int k, const char *fname) { + *newfile(L) = f; + if (k > 0) { + lua_pushvalue(L, -1); + lua_rawseti(L, LUA_ENVIRONINDEX, k); + } + lua_pushvalue(L, -2); /* copy environment */ + lua_setfenv(L, -2); /* set it */ + lua_setfield(L, -3, fname); +} + + +static void newfenv (lua_State *L, lua_CFunction cls) { + lua_createtable(L, 0, 1); + lua_pushcfunction(L, cls); + lua_setfield(L, -2, "__close"); +} + + +LUALIB_API int luaopen_io (lua_State *L) { + createmeta(L); + /* create (private) environment (with fields IO_INPUT, IO_OUTPUT, __close) */ + newfenv(L, io_fclose); + lua_replace(L, LUA_ENVIRONINDEX); + /* open library */ + luaL_register(L, LUA_IOLIBNAME, iolib); + /* create (and set) default files */ + newfenv(L, io_noclose); /* close function for default files */ + createstdfile(L, stdin, IO_INPUT, "stdin"); + createstdfile(L, stdout, IO_OUTPUT, "stdout"); + createstdfile(L, stderr, 0, "stderr"); + lua_pop(L, 1); /* pop environment for default files */ + return 1; +} + diff --git a/src/blua/lualib.h b/src/blua/lualib.h index 6ebe27287..4ea97edf3 100644 --- a/src/blua/lualib.h +++ b/src/blua/lualib.h @@ -21,6 +21,9 @@ LUALIB_API int (luaopen_base) (lua_State *L); #define LUA_TABLIBNAME "table" LUALIB_API int (luaopen_table) (lua_State *L); +#define LUA_IOLIBNAME "io" +LUALIB_API int (luaopen_io) (lua_State *L); + #define LUA_STRLIBNAME "string" LUALIB_API int (luaopen_string) (lua_State *L); diff --git a/src/d_clisrv.c b/src/d_clisrv.c index 8b87672bf..6b0f49544 100644 --- a/src/d_clisrv.c +++ b/src/d_clisrv.c @@ -3164,6 +3164,8 @@ void D_QuitNetGame(void) // abort send/receive of files CloseNetFile(); + RemoveLuaFileTransfers(); + waitingforluafiletransfer = false; if (server) { @@ -3534,6 +3536,10 @@ static void HandleConnect(SINT8 node) SV_SendRefuse(node, M_GetText("Too many players from\nthis node.")); else if (netgame && !netbuffer->u.clientcfg.localplayers) // Stealth join? SV_SendRefuse(node, M_GetText("No players from\nthis node.")); +#ifdef HAVE_BLUA + else if (luafiletransfers) + SV_SendRefuse(node, M_GetText("The server is broadcasting a file\nrequested by a Lua script.\nPlease wait a bit and then\ntry rejoining.")); +#endif else { #ifndef NONET @@ -4117,6 +4123,20 @@ static void HandlePacketFromPlayer(SINT8 node) Net_CloseConnection(node); nodeingame[node] = false; break; +#ifdef HAVE_BLUA + case PT_ASKLUAFILE: + if (server && luafiletransfers && luafiletransfers->nodestatus[node] == LFTNS_ASKED) + { + char *name = va("%s" PATHSEP "%s", luafiledir, luafiletransfers->filename); + boolean textmode = !strchr(luafiletransfers->mode, 'b'); + SV_SendLuaFile(node, name, textmode); + } + break; + case PT_HASLUAFILE: + if (server && luafiletransfers && luafiletransfers->nodestatus[node] == LFTNS_SENDING) + SV_HandleLuaFileSent(node); + break; +#endif // -------------------------------------------- CLIENT RECEIVE ---------- case PT_RESYNCHEND: // Only accept PT_RESYNCHEND from the server. @@ -4244,6 +4264,12 @@ static void HandlePacketFromPlayer(SINT8 node) if (client) Got_Filetxpak(); break; +#ifdef HAVE_BLUA + case PT_SENDINGLUAFILE: + if (client) + CL_PrepareDownloadLuaFile(); + break; +#endif default: DEBFILE(va("UNKNOWN PACKET TYPE RECEIVED %d from host %d\n", netbuffer->packettype, node)); diff --git a/src/d_clisrv.h b/src/d_clisrv.h index 408d0f8dd..4aaa9be6b 100644 --- a/src/d_clisrv.h +++ b/src/d_clisrv.h @@ -67,6 +67,12 @@ typedef enum PT_RESYNCHEND, // Player is now resynched and is being requested to remake the gametic PT_RESYNCHGET, // Player got resynch packet +#ifdef HAVE_BLUA + PT_SENDINGLUAFILE, // Server telling a client Lua needs to open a file + PT_ASKLUAFILE, // Client telling the server they don't have the file + PT_HASLUAFILE, // Client telling the server they have the file +#endif + // Add non-PT_CANFAIL packet types here to avoid breaking MS compatibility. PT_CANFAIL, // This is kind of a priority. Anything bigger than CANFAIL diff --git a/src/d_main.c b/src/d_main.c index 15d3c8041..b4eebb275 100644 --- a/src/d_main.c +++ b/src/d_main.c @@ -1113,6 +1113,7 @@ void D_SRB2Main(void) // can't use sprintf since there is %u in savegamename strcatbf(savegamename, srb2home, PATHSEP); + snprintf(luafiledir, sizeof luafiledir, "%s" PATHSEP "luafiles", srb2home); #else snprintf(srb2home, sizeof srb2home, "%s", userhome); snprintf(downloaddir, sizeof downloaddir, "%s", userhome); @@ -1123,6 +1124,8 @@ void D_SRB2Main(void) // can't use sprintf since there is %u in savegamename strcatbf(savegamename, userhome, PATHSEP); + + snprintf(luafiledir, sizeof luafiledir, "%s" PATHSEP "luafiles", userhome); #endif } diff --git a/src/d_net.c b/src/d_net.c index 573c9cfe9..ddb991235 100644 --- a/src/d_net.c +++ b/src/d_net.c @@ -715,6 +715,10 @@ void Net_CloseConnection(INT32 node) InitNode(&nodes[node]); SV_AbortSendFiles(node); +#ifdef HAVE_BLUA + if (server) + SV_AbortLuaFileTransfer(node); +#endif I_NetFreeNodenum(node); #endif } @@ -799,6 +803,10 @@ static const char *packettypename[NUMPACKETTYPE] = "RESYNCHEND", "RESYNCHGET", + "SENDINGLUAFILE", + "ASKLUAFILE", + "HASLUAFILE", + "FILEFRAGMENT", "TEXTCMD", "TEXTCMD2", diff --git a/src/d_netcmd.c b/src/d_netcmd.c index a597bad83..42b560a85 100644 --- a/src/d_netcmd.c +++ b/src/d_netcmd.c @@ -417,7 +417,8 @@ const char *netxcmdnames[MAXNETXCMD - 1] = "SUICIDE", #ifdef HAVE_BLUA "LUACMD", - "LUAVAR" + "LUAVAR", + "LUAFILE" #endif }; @@ -453,6 +454,7 @@ void D_RegisterServerCommands(void) RegisterNetXCmd(XD_RUNSOC, Got_RunSOCcmd); #ifdef HAVE_BLUA RegisterNetXCmd(XD_LUACMD, Got_Luacmd); + RegisterNetXCmd(XD_LUAFILE, Got_LuaFile); #endif // Remote Administration diff --git a/src/d_netcmd.h b/src/d_netcmd.h index 8f857c6db..5f3967a67 100644 --- a/src/d_netcmd.h +++ b/src/d_netcmd.h @@ -145,6 +145,7 @@ typedef enum #ifdef HAVE_BLUA XD_LUACMD, // 22 XD_LUAVAR, // 23 + XD_LUAFILE, // 24 #endif MAXNETXCMD } netxcmd_t; diff --git a/src/d_netfil.c b/src/d_netfil.c index 93b4b1990..5ceec2aaa 100644 --- a/src/d_netfil.c +++ b/src/d_netfil.c @@ -69,6 +69,7 @@ typedef struct filetx_s UINT32 size; // Size of the file UINT8 fileid; INT32 node; // Destination + boolean textmode; // For files requested by Lua without the "b" option struct filetx_s *next; // Next file in the list } filetx_t; @@ -94,6 +95,13 @@ char downloaddir[512] = "DOWNLOAD"; INT32 lastfilenum = -1; #endif +#ifdef HAVE_BLUA +luafiletransfer_t *luafiletransfers = NULL; +boolean waitingforluafiletransfer = false; +char luafiledir[256] = "luafiles"; +#endif + + /** Fills a serverinfo packet with information about wad files loaded. * * \todo Give this function a better name since it is in global scope. @@ -159,6 +167,7 @@ void D_ParseFileneeded(INT32 fileneedednum_parm, UINT8 *fileneededstr) fileneeded[i].file = NULL; // The file isn't open yet READSTRINGN(p, fileneeded[i].filename, MAX_WADPATH); // The next bytes are the file name READMEM(p, fileneeded[i].md5sum, 16); // The last 16 bytes are the file checksum + fileneeded[i].textmode = false; } } @@ -170,6 +179,7 @@ void CL_PrepareDownloadSaveGame(const char *tmpsave) fileneeded[0].file = NULL; memset(fileneeded[0].md5sum, 0, 16); strcpy(fileneeded[0].filename, tmpsave); + fileneeded[0].textmode = false; } /** Checks the server to see if we CAN download all the files, @@ -448,6 +458,164 @@ void CL_LoadServerFiles(void) } } +#ifdef HAVE_BLUA +void AddLuaFileTransfer(const char *filename, const char *mode) +{ + luafiletransfer_t **prevnext; // A pointer to the "next" field of the last transfer in the list + luafiletransfer_t *filetransfer; + static INT32 id; + + //CONS_Printf("AddLuaFileTransfer \"%s\"\n", filename); + + // Find the last transfer in the list and set a pointer to its "next" field + prevnext = &luafiletransfers; + while (*prevnext) + prevnext = &((*prevnext)->next); + + // Allocate file transfer information and append it to the transfer list + filetransfer = malloc(sizeof(luafiletransfer_t)); + if (!filetransfer) + I_Error("AddLuaFileTransfer: Out of memory\n"); + *prevnext = filetransfer; + filetransfer->next = NULL; + + // Allocate the file name + filetransfer->filename = strdup(filename); + if (!filetransfer->filename) + I_Error("AddLuaFileTransfer: Out of memory\n"); + + // Create and allocate the real file name + if (server) + filetransfer->realfilename = strdup(va("%s" PATHSEP "%s", + luafiledir, filename)); + else + filetransfer->realfilename = strdup(va("%s" PATHSEP "shared" PATHSEP "$$$%d%d.tmp", + luafiledir, rand(), rand())); + if (!filetransfer->realfilename) + I_Error("AddLuaFileTransfer: Out of memory\n"); + + strlcpy(filetransfer->mode, mode, sizeof(filetransfer->mode)); + + if (server) + { + INT32 i; + + // Set status to "waiting" for everyone + for (i = 0; i < MAXNETNODES; i++) + filetransfer->nodestatus[i] = LFTNS_WAITING; + + if (!luafiletransfers->next) // Only if there is no transfer already going on + { + if (FIL_FileOK(filetransfer->realfilename)) + SV_PrepareSendLuaFileToNextNode(); + else + { + // Send a net command with 0 as its first byte to indicate the file couldn't be opened + UINT8 success = 0; + SendNetXCmd(XD_LUAFILE, &success, 1); + } + } + } + + // Store the callback so it can be called once everyone has the file + filetransfer->id = id; + StoreLuaFileCallback(id); + id++; + + if (waitingforluafiletransfer) + { + waitingforluafiletransfer = false; + CL_PrepareDownloadLuaFile(); + } +} + +void SV_PrepareSendLuaFileToNextNode(void) +{ + INT32 i; + UINT8 success = 1; + + // Find a client to send the file to + for (i = 1; i < MAXNETNODES; i++) + if (nodeingame[i] && luafiletransfers->nodestatus[i] == LFTNS_WAITING) // Node waiting + { + // Tell the client we're about to send them the file + netbuffer->packettype = PT_SENDINGLUAFILE; + if (!HSendPacket(i, true, 0, 0)) + I_Error("Failed to send a PT_SENDINGLUAFILE packet\n"); // !!! Todo: Handle failure a bit better lol + + luafiletransfers->nodestatus[i] = LFTNS_ASKED; + + return; + } + + // No client found, everyone has the file + // Send a net command with 1 as its first byte to indicate the file could be opened + SendNetXCmd(XD_LUAFILE, &success, 1); +} + +void SV_HandleLuaFileSent(UINT8 node) +{ + luafiletransfers->nodestatus[node] = LFTNS_SENT; + SV_PrepareSendLuaFileToNextNode(); +} + +void RemoveLuaFileTransfer(void) +{ + luafiletransfer_t *filetransfer = luafiletransfers; + + RemoveLuaFileCallback(filetransfer->id); + + luafiletransfers = filetransfer->next; + + free(filetransfer->filename); + free(filetransfer->realfilename); + free(filetransfer); +} + +void RemoveLuaFileTransfers(void) +{ + while (luafiletransfers) + RemoveLuaFileTransfer(); +} + +void SV_AbortLuaFileTransfer(INT32 node) +{ + if (luafiletransfers + && (luafiletransfers->nodestatus[node] == LFTNS_ASKED + || luafiletransfers->nodestatus[node] == LFTNS_SENDING)) + { + luafiletransfers->nodestatus[node] = LFTNS_WAITING; + SV_PrepareSendLuaFileToNextNode(); + } +} + +void CL_PrepareDownloadLuaFile(void) +{ + // If there is no transfer in the list, this normally means the server + // called io.open before us, so we have to wait until we call it too + if (!luafiletransfers) + { + waitingforluafiletransfer = true; + return; + } + + // Tell the server we are ready to receive the file + netbuffer->packettype = PT_ASKLUAFILE; + HSendPacket(servernode, true, 0, 0); + + fileneedednum = 1; + fileneeded[0].status = FS_REQUESTED; + fileneeded[0].totalsize = UINT32_MAX; + fileneeded[0].file = NULL; + memset(fileneeded[0].md5sum, 0, 16); + strcpy(fileneeded[0].filename, luafiletransfers->realfilename); + fileneeded[0].textmode = !strchr(luafiletransfers->mode, 'b'); + + // Make sure all directories in the file path exist + MakePathDirs(fileneeded[0].filename); +} +#endif + // Number of files to send // Little optimization to quickly test if there is a file in the queue static INT32 filestosend = 0; @@ -458,6 +626,7 @@ static INT32 filestosend = 0; * \param filename The file to send * \param fileid ??? * \sa SV_SendRam + * \sa SV_SendLuaFile * */ static boolean SV_SendFile(INT32 node, const char *filename, UINT8 fileid) @@ -548,6 +717,7 @@ static boolean SV_SendFile(INT32 node, const char *filename, UINT8 fileid) * \param freemethod How to free the block after it has been sent * \param fileid ??? * \sa SV_SendFile + * \sa SV_SendLuaFile * */ void SV_SendRam(INT32 node, void *data, size_t size, freemethod_t freemethod, UINT8 fileid) @@ -579,6 +749,57 @@ void SV_SendRam(INT32 node, void *data, size_t size, freemethod_t freemethod, UI filestosend++; } +#ifdef HAVE_BLUA +/** Adds a file requested by Lua to the file list for a node + * + * \param node The node to send the file to + * \param filename The file to send + * \sa SV_SendFile + * \sa SV_SendRam + * + */ +boolean SV_SendLuaFile(INT32 node, const char *filename, boolean textmode) +{ + filetx_t **q; // A pointer to the "next" field of the last file in the list + filetx_t *p; // The new file request + //INT32 i; + //char wadfilename[MAX_WADPATH]; + + luafiletransfers->nodestatus[node] = LFTNS_SENDING; + + // Find the last file in the list and set a pointer to its "next" field + q = &transfer[node].txlist; + while (*q) + q = &((*q)->next); + + // Allocate a file request and append it to the file list + p = *q = (filetx_t *)malloc(sizeof (filetx_t)); + if (!p) + I_Error("SV_SendLuaFile: No more memory\n"); + + // Initialise with zeros + memset(p, 0, sizeof (filetx_t)); + + // Allocate the file name + p->id.filename = (char *)malloc(MAX_WADPATH); // !!! + if (!p->id.filename) + I_Error("SV_SendLuaFile: No more memory\n"); + + // Set the file name and get rid of the path + strlcpy(p->id.filename, filename, MAX_WADPATH); // !!! + //nameonly(p->id.filename); + + // Open in text mode if required by the Lua script + p->textmode = textmode; + + DEBFILE(va("Sending Lua file %s to %d\n", filename, node)); + p->ram = SF_FILE; // It's a file, we need to close it and free its name once we're done sending it + p->next = NULL; // End of list + filestosend++; + return true; +} +#endif + /** Stops sending a file for a node, and removes the file request from the list, * either because the file has been fully sent or because the node was disconnected * @@ -684,7 +905,7 @@ void SV_FileSendTicker(void) long filesize; transfer[i].currentfile = - fopen(f->id.filename, "rb"); + fopen(f->id.filename, f->textmode ? "r" : "rb"); if (!transfer[i].currentfile) I_Error("File %s does not exist", @@ -715,11 +936,20 @@ void SV_FileSendTicker(void) size = f->size-transfer[i].position; if (ram) M_Memcpy(p->data, &f->id.ram[transfer[i].position], size); - else if (fread(p->data, 1, size, transfer[i].currentfile) != size) - I_Error("SV_FileSendTicker: can't read %s byte on %s at %d because %s", sizeu1(size), f->id.filename, transfer[i].position, M_FileError(transfer[i].currentfile)); + else + { + size_t n = fread(p->data, 1, size, transfer[i].currentfile); + if (n != size) // Either an error or Windows turning CR-LF into LF + { + if (f->textmode && feof(transfer[i].currentfile)) + size = n; + else if (fread(p->data, 1, size, transfer[i].currentfile) != size) + I_Error("SV_FileSendTicker: can't read %s byte on %s at %d because %s", sizeu1(size), f->id.filename, transfer[i].position, M_FileError(transfer[i].currentfile)); + } + } p->position = LONG(transfer[i].position); // Put flag so receiver knows the total size - if (transfer[i].position + size == f->size) + if (transfer[i].position + size == f->size || (f->textmode && feof(transfer[i].currentfile))) p->position |= LONG(0x80000000); p->fileid = f->fileid; p->size = SHORT((UINT16)size); @@ -728,7 +958,7 @@ void SV_FileSendTicker(void) if (HSendPacket(i, true, 0, FILETXHEADER + size)) // Reliable SEND { // Success transfer[i].position = (UINT32)(transfer[i].position + size); - if (transfer[i].position == f->size) // Finish? + if (transfer[i].position == f->size || (f->textmode && feof(transfer[i].currentfile))) // Finish? SV_EndFileSend(i); } else @@ -772,7 +1002,7 @@ void Got_Filetxpak(void) { if (file->file) I_Error("Got_Filetxpak: already open file\n"); - file->file = fopen(filename, "wb"); + file->file = fopen(filename, file->textmode ? "w" : "wb"); if (!file->file) I_Error("Can't create file %s: %s", filename, strerror(errno)); CONS_Printf("\r%s...\n",filename); @@ -793,7 +1023,7 @@ void Got_Filetxpak(void) } // We can receive packet in the wrong order, anyway all os support gaped file fseek(file->file, pos, SEEK_SET); - if (fwrite(netbuffer->u.filetxpak.data,size,1,file->file) != 1) + if (size && fwrite(netbuffer->u.filetxpak.data,size,1,file->file) != 1) I_Error("Can't write to %s: %s\n",filename, M_FileError(file->file)); file->currentsize += size; @@ -805,6 +1035,14 @@ void Got_Filetxpak(void) file->status = FS_FOUND; CONS_Printf(M_GetText("Downloading %s...(done)\n"), filename); +#ifdef HAVE_BLUA + if (luafiletransfers) + { + // Tell the server we have received the file + netbuffer->packettype = PT_HASLUAFILE; + HSendPacket(servernode, true, 0, 0); + } +#endif } } else diff --git a/src/d_netfil.h b/src/d_netfil.h index 17aeb8b7e..0d0b16975 100644 --- a/src/d_netfil.h +++ b/src/d_netfil.h @@ -13,6 +13,7 @@ #ifndef __D_NETFIL__ #define __D_NETFIL__ +#include "d_net.h" #include "w_wad.h" typedef enum @@ -43,6 +44,7 @@ typedef struct UINT32 currentsize; UINT32 totalsize; filestatus_t status; // The value returned by recsearch + boolean textmode; // For files requested by Lua without the "b" option } fileneeded_t; extern INT32 fileneedednum; @@ -70,6 +72,44 @@ boolean CL_CheckDownloadable(void); boolean CL_SendRequestFile(void); boolean Got_RequestFilePak(INT32 node); +#ifdef HAVE_BLUA +typedef enum +{ + LFTNS_WAITING, // This node is waiting for the server to send the file + LFTNS_ASKED, // The server has told the node they're ready to send the file + LFTNS_SENDING, // The server is sending the file to this node + LFTNS_SENT // The node already has the file +} luafiletransfernodestatus_t; + +typedef struct luafiletransfer_s +{ + char *filename; + char *realfilename; + char mode[4]; // rb+/wb+/ab+ + null character + INT32 id; // Callback ID + luafiletransfernodestatus_t nodestatus[MAXNETNODES]; + struct luafiletransfer_s *next; +} luafiletransfer_t; + +extern luafiletransfer_t *luafiletransfers; +extern boolean waitingforluafiletransfer; +extern char luafiledir[256]; + +void AddLuaFileTransfer(const char *filename, const char *mode); +void SV_PrepareSendLuaFileToNextNode(void); +boolean SV_SendLuaFile(INT32 node, const char *filename, boolean textmode); +void SV_PrepareSendLuaFile(const char *filename); +void SV_HandleLuaFileSent(UINT8 node); +void RemoveLuaFileTransfer(void); +void RemoveLuaFileTransfers(void); +void SV_AbortLuaFileTransfer(INT32 node); +void CL_PrepareDownloadLuaFile(void); +void Got_LuaFile(UINT8 **cp, INT32 playernum); +void StoreLuaFileCallback(INT32 id); +void RemoveLuaFileCallback(INT32 id); +void MakePathDirs(char *path); +#endif + void SV_AbortSendFiles(INT32 node); void CloseNetFile(void); From 3b22a84296a239859adba85f6b27f4947631cff4 Mon Sep 17 00:00:00 2001 From: Louis-Antoine Date: Fri, 24 Jan 2020 02:47:51 +0100 Subject: [PATCH 02/68] Split open() into open() and openlocal() open() for normal, consistency-friendly reading, openlocal() for writing and local reading. --- src/blua/liolib.c | 122 +++++++++++++++++++++++----------------------- 1 file changed, 62 insertions(+), 60 deletions(-) diff --git a/src/blua/liolib.c b/src/blua/liolib.c index 6ebde08a3..dc425f3c0 100644 --- a/src/blua/liolib.c +++ b/src/blua/liolib.c @@ -137,6 +137,14 @@ static int aux_close (lua_State *L) { } +static int io_close (lua_State *L) { + if (lua_isnone(L, 1)) + lua_rawgeti(L, LUA_ENVIRONINDEX, IO_OUTPUT); + tofile(L); /* make sure argument is a file */ + return aux_close(L); +} + + static int io_gc (lua_State *L) { FILE *f = *tofilep(L); /* ignore closed files */ @@ -172,14 +180,11 @@ void MakePathDirs(char *path) } -static int io_open (lua_State *L) { - FILE **pf; - const char *filename = luaL_checkstring(L, 1); +static int CheckFileName(lua_State *L, const char *filename) +{ + int length = strlen(filename); boolean pass = false; size_t i; - int length = strlen(filename); - const char *mode = luaL_optstring(L, 2, "r"); - luafiletransfer_t *filetransfer; if (strchr(filename, '\\')) { @@ -202,67 +207,60 @@ static int io_open (lua_State *L) { return pushresult(L,0,filename); } - luaL_checktype(L, 4, LUA_TFUNCTION); + return 0; +} - if (lua_isnil(L, 3) && (strchr(mode, 'r') || strchr(mode, '+'))) // Synched reading - { - AddLuaFileTransfer(filename, mode); +static int io_open (lua_State *L) { + const char *filename = luaL_checkstring(L, 1); + const char *mode = luaL_optstring(L, 2, "r"); + int checkresult; - /*pf = newfile(L); - *pf = fopen(realfilename, mode); - return (*pf == NULL) ? pushresult(L, 0, filename) : 1;*/ - } - else // Local I/O - { - char *realfilename = va("%s" PATHSEP "%s", luafiledir, filename); - player_t *player = *((player_t **)luaL_checkudata(L, 3, META_PLAYER)); + checkresult = CheckFileName(L, filename); + if (checkresult) + return checkresult; - if (!player) - return LUA_ErrInvalid(L, "player_t"); + luaL_checktype(L, 3, LUA_TFUNCTION); - if (player != &players[consoleplayer]) - return 0; + if (!(strchr(mode, 'r') || strchr(mode, '+'))) + luaL_error(L, "open() is only for reading, use openlocal() for writing"); - if (client && strnicmp(filename, "shared/", strlen("shared/"))) + AddLuaFileTransfer(filename, mode); + + return 0; +} + + +static int io_openlocal (lua_State *L) { + FILE **pf; + const char *filename = luaL_checkstring(L, 1); + const char *mode = luaL_optstring(L, 2, "r"); + luafiletransfer_t *filetransfer; + int checkresult; + + checkresult = CheckFileName(L, filename); + if (checkresult) + return checkresult; + + char *realfilename = va("%s" PATHSEP "%s", luafiledir, filename); + + if (client && strnicmp(filename, "shared/", strlen("shared/"))) + I_Error("Access denied to %s\n" + "Clients can only access files stored in luafiles/shared/\n", + filename); + + // Prevent access if the file is being downloaded + for (filetransfer = luafiletransfers; filetransfer; filetransfer = filetransfer->next) + if (!stricmp(filetransfer->filename, filename)) I_Error("Access denied to %s\n" - "Clients can only access files stored in luafiles/shared/\n", - filename); + "Files can't be opened while being downloaded\n", + filename); - // Prevent access if the file is being downloaded - for (filetransfer = luafiletransfers; filetransfer; filetransfer = filetransfer->next) - if (!stricmp(filetransfer->filename, filename)) - I_Error("Access denied to %s\n" - "Files can't be opened while being downloaded\n", - filename); + MakePathDirs(realfilename); - MakePathDirs(realfilename); - - // The callback is the last argument, no need to push it again - - // Push the first argument (file handle) on the stack - pf = newfile(gL); // Create and push the file handle - *pf = fopen(realfilename, mode); // Open the file - if (!*pf) - { - lua_pop(gL, 1); - lua_pushnil(gL); - } - - // Push the second argument (file name) on the stack - lua_pushstring(gL, filename); - - // Call the callback - LUA_Call(gL, 2); - - // Close the file - if (*pf) - { - fclose(*pf); - *pf = NULL; - } - } - - return 0; // !!! Todo: error handling? + // Open and return the file + pf = newfile(L); + *pf = fopen(realfilename, mode); + return (*pf == NULL) ? pushresult(L, 0, filename) : 1; } @@ -335,7 +333,7 @@ void Got_LuaFile(UINT8 **cp, INT32 playernum) void StoreLuaFileCallback(INT32 id) { lua_pushfstring(gL, FMT_FILECALLBACKID, id); - lua_pushvalue(gL, 4); // Parameter 4 is the callback + lua_pushvalue(gL, 3); // Parameter 3 is the callback lua_settable(gL, LUA_REGISTRYINDEX); // registry[callbackid] = callback } @@ -347,6 +345,7 @@ void RemoveLuaFileCallback(INT32 id) lua_settable(gL, LUA_REGISTRYINDEX); // registry[callbackid] = nil } + static int io_tmpfile (lua_State *L) { FILE **pf = newfile(L); *pf = tmpfile(); @@ -649,10 +648,12 @@ static int f_flush (lua_State *L) { static const luaL_Reg iolib[] = { + {"close", io_close}, {"flush", io_flush}, {"input", io_input}, {"lines", io_lines}, {"open", io_open}, + {"openlocal", io_openlocal}, {"output", io_output}, {"read", io_read}, {"tmpfile", io_tmpfile}, @@ -663,6 +664,7 @@ static const luaL_Reg iolib[] = { static const luaL_Reg flib[] = { + {"close", io_close}, {"flush", f_flush}, {"lines", f_lines}, {"read", f_read}, From e0f0984fcc8e5d1f4e6881b1826662f24ff1b4e9 Mon Sep 17 00:00:00 2001 From: Louis-Antoine Date: Fri, 24 Jan 2020 17:11:00 +0100 Subject: [PATCH 03/68] Rename "luafiles/shared/" to "luafiles/client/" --- src/blua/liolib.c | 4 ++-- src/d_netfil.c | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/blua/liolib.c b/src/blua/liolib.c index dc425f3c0..244c13d15 100644 --- a/src/blua/liolib.c +++ b/src/blua/liolib.c @@ -243,9 +243,9 @@ static int io_openlocal (lua_State *L) { char *realfilename = va("%s" PATHSEP "%s", luafiledir, filename); - if (client && strnicmp(filename, "shared/", strlen("shared/"))) + if (client && strnicmp(filename, "client/", strlen("client/"))) I_Error("Access denied to %s\n" - "Clients can only access files stored in luafiles/shared/\n", + "Clients can only access files stored in luafiles/client/\n", filename); // Prevent access if the file is being downloaded diff --git a/src/d_netfil.c b/src/d_netfil.c index 5ceec2aaa..71449caca 100644 --- a/src/d_netfil.c +++ b/src/d_netfil.c @@ -489,7 +489,7 @@ void AddLuaFileTransfer(const char *filename, const char *mode) filetransfer->realfilename = strdup(va("%s" PATHSEP "%s", luafiledir, filename)); else - filetransfer->realfilename = strdup(va("%s" PATHSEP "shared" PATHSEP "$$$%d%d.tmp", + filetransfer->realfilename = strdup(va("%s" PATHSEP "client" PATHSEP "$$$%d%d.tmp", luafiledir, rand(), rand())); if (!filetransfer->realfilename) I_Error("AddLuaFileTransfer: Out of memory\n"); From 9658ef2c5bf4becdb8ab9473686d1726683510a1 Mon Sep 17 00:00:00 2001 From: Louis-Antoine Date: Fri, 24 Jan 2020 17:51:20 +0100 Subject: [PATCH 04/68] Add .dat and .csv to whitelisted file extensions --- src/blua/liolib.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/blua/liolib.c b/src/blua/liolib.c index 244c13d15..4ef3b4274 100644 --- a/src/blua/liolib.c +++ b/src/blua/liolib.c @@ -36,11 +36,13 @@ static const char *const fnames[] = {"input", "output"}; static const char *whitelist[] = { // Allow scripters to write files of these types to SRB2's folder - ".txt", - ".sav2", + ".bmp", ".cfg", + ".csv", + ".dat", ".png", - ".bmp" + ".sav2", + ".txt", }; From 51947319ee7ab0bed90128c41acf03e056468dd2 Mon Sep 17 00:00:00 2001 From: Louis-Antoine Date: Fri, 24 Jan 2020 23:24:54 +0100 Subject: [PATCH 05/68] Fix two compiler warnings --- src/blua/liolib.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/blua/liolib.c b/src/blua/liolib.c index 4ef3b4274..489a4ae16 100644 --- a/src/blua/liolib.c +++ b/src/blua/liolib.c @@ -268,8 +268,8 @@ static int io_openlocal (lua_State *L) { void Got_LuaFile(UINT8 **cp, INT32 playernum) { - FILE **pf; - boolean success = READUINT8(*cp); // The first (and only) byte indicates whether the file could be opened + FILE **pf = NULL; + UINT8 success = READUINT8(*cp); // The first (and only) byte indicates whether the file could be opened if (playernum != serverplayer) { From 16c7981bb8dc4af071e4dbbf8ad0bc4867ce31bf Mon Sep 17 00:00:00 2001 From: Louis-Antoine Date: Fri, 24 Jan 2020 23:34:39 +0100 Subject: [PATCH 06/68] Fix the compiler warning fix --- src/blua/liolib.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/blua/liolib.c b/src/blua/liolib.c index 489a4ae16..74bedc060 100644 --- a/src/blua/liolib.c +++ b/src/blua/liolib.c @@ -325,7 +325,7 @@ void Got_LuaFile(UINT8 **cp, INT32 playernum) else { // Send a net command with 0 as its first byte to indicate the file couldn't be opened - UINT8 success = 0; + success = 0; SendNetXCmd(XD_LUAFILE, &success, 1); } } From 2d142f734faadd67b07e9527dfabcdf80e7a85f3 Mon Sep 17 00:00:00 2001 From: Louis-Antoine Date: Sat, 25 Jan 2020 01:37:50 +0100 Subject: [PATCH 07/68] Remove all potentially dangerous functions Basically any function that calls fopen() is gone, except the ones created by me. And no access to stdin/stdout/stderr through io.read()/io.write(). Not really harmful but why would you want that in SRB2. --- src/blua/liolib.c | 87 ----------------------------------------------- 1 file changed, 87 deletions(-) diff --git a/src/blua/liolib.c b/src/blua/liolib.c index 74bedc060..e1a64c8d8 100644 --- a/src/blua/liolib.c +++ b/src/blua/liolib.c @@ -64,12 +64,6 @@ static int pushresult (lua_State *L, int i, const char *filename) { } -static void fileerror (lua_State *L, int arg, const char *filename) { - lua_pushfstring(L, "%s: %s", filename, strerror(errno)); - luaL_argerror(L, arg, lua_tostring(L, -1)); -} - - #define tofilep(L) ((FILE **)luaL_checkudata(L, 1, LUA_FILEHANDLE)) @@ -355,47 +349,6 @@ static int io_tmpfile (lua_State *L) { } -static FILE *getiofile (lua_State *L, int findex) { - FILE *f; - lua_rawgeti(L, LUA_ENVIRONINDEX, findex); - f = *(FILE **)lua_touserdata(L, -1); - if (f == NULL) - luaL_error(L, "standard %s file is closed", fnames[findex - 1]); - return f; -} - - -static int g_iofile (lua_State *L, int f, const char *mode) { - if (!lua_isnoneornil(L, 1)) { - const char *filename = lua_tostring(L, 1); - if (filename) { - FILE **pf = newfile(L); - *pf = fopen(filename, mode); - if (*pf == NULL) - fileerror(L, 1, filename); - } - else { - tofile(L); /* check that it's a valid file handle */ - lua_pushvalue(L, 1); - } - lua_rawseti(L, LUA_ENVIRONINDEX, f); - } - /* return current value */ - lua_rawgeti(L, LUA_ENVIRONINDEX, f); - return 1; -} - - -static int io_input (lua_State *L) { - return g_iofile(L, IO_INPUT, "r"); -} - - -static int io_output (lua_State *L) { - return g_iofile(L, IO_OUTPUT, "w"); -} - - static int io_readline (lua_State *L); @@ -413,24 +366,6 @@ static int f_lines (lua_State *L) { } -static int io_lines (lua_State *L) { - if (lua_isnoneornil(L, 1)) { /* no arguments? */ - /* will iterate over default input */ - lua_rawgeti(L, LUA_ENVIRONINDEX, IO_INPUT); - return f_lines(L); - } - else { - const char *filename = luaL_checkstring(L, 1); - FILE **pf = newfile(L); - *pf = fopen(filename, "r"); - if (*pf == NULL) - fileerror(L, 1, filename); - aux_lines(L, lua_gettop(L), 1); - return 1; - } -} - - /* ** {====================================================== ** READ @@ -543,11 +478,6 @@ static int g_read (lua_State *L, FILE *f, int first) { } -static int io_read (lua_State *L) { - return g_read(L, getiofile(L, IO_INPUT), 1); -} - - static int f_read (lua_State *L) { return g_read(L, tofile(L), 2); } @@ -601,11 +531,6 @@ static int g_write (lua_State *L, FILE *f, int arg) { } -static int io_write (lua_State *L) { - return g_write(L, getiofile(L, IO_OUTPUT), 1); -} - - static int f_write (lua_State *L) { return g_write(L, tofile(L), 2); } @@ -638,12 +563,6 @@ static int f_setvbuf (lua_State *L) { } - -static int io_flush (lua_State *L) { - return pushresult(L, fflush(getiofile(L, IO_OUTPUT)) == 0, NULL); -} - - static int f_flush (lua_State *L) { return pushresult(L, fflush(tofile(L)) == 0, NULL); } @@ -651,16 +570,10 @@ static int f_flush (lua_State *L) { static const luaL_Reg iolib[] = { {"close", io_close}, - {"flush", io_flush}, - {"input", io_input}, - {"lines", io_lines}, {"open", io_open}, {"openlocal", io_openlocal}, - {"output", io_output}, - {"read", io_read}, {"tmpfile", io_tmpfile}, {"type", io_type}, - {"write", io_write}, {NULL, NULL} }; From 2eaf02d234ab0287af9fbd41b50ca2015121cca7 Mon Sep 17 00:00:00 2001 From: Louis-Antoine Date: Mon, 3 Feb 2020 19:42:37 +0100 Subject: [PATCH 08/68] Let Lua access spectators mobjs --- src/lua_playerlib.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/lua_playerlib.c b/src/lua_playerlib.c index 1dd4c45b5..9ce1fed9b 100644 --- a/src/lua_playerlib.c +++ b/src/lua_playerlib.c @@ -97,6 +97,10 @@ static int player_get(lua_State *L) lua_pushboolean(L, true); else if (fastcmp(field,"name")) lua_pushstring(L, player_names[plr-players]); + else if (fastcmp(field,"realmo")) + LUA_PushUserdata(L, plr->mo, META_MOBJ); + // Kept for backward-compatibility + // Should be fixed to work like "realmo" later else if (fastcmp(field,"mo")) { if (plr->spectator) @@ -396,7 +400,7 @@ static int player_set(lua_State *L) if (hud_running) return luaL_error(L, "Do not alter player_t in HUD rendering code!"); - if (fastcmp(field,"mo")) { + if (fastcmp(field,"mo") || fastcmp(field,"realmo")) { mobj_t *newmo = *((mobj_t **)luaL_checkudata(L, 3, META_MOBJ)); plr->mo->player = NULL; // remove player pointer from old mobj (newmo->player = plr)->mo = newmo; // set player pointer for new mobj, and set new mobj as the player's mobj From b6089ccdaf9746446b229c14cdb34c0c3ad24544 Mon Sep 17 00:00:00 2001 From: Jaime Passos Date: Mon, 27 Jan 2020 01:57:55 -0300 Subject: [PATCH 09/68] Fix -OGLlib --- src/i_video.h | 13 +++++++++-- src/screen.c | 2 +- src/sdl/i_video.c | 58 +++++++++++++++++++++++++++++++++++++++-------- src/sdl/ogl_sdl.c | 8 +++---- 4 files changed, 65 insertions(+), 16 deletions(-) diff --git a/src/i_video.h b/src/i_video.h index 76b984d25..8f87a360e 100644 --- a/src/i_video.h +++ b/src/i_video.h @@ -32,10 +32,13 @@ typedef enum render_none = 3 // for dedicated server } rendermode_t; -/** \brief currect render mode +/** \brief current render mode */ extern rendermode_t rendermode; +/** \brief hardware renderer loaded +*/ +extern boolean hwrenderloaded; /** \brief use highcolor modes if true */ @@ -44,6 +47,9 @@ extern boolean highcolor; /** \brief setup video mode */ void I_StartupGraphics(void); + +/** \brief setup hardware mode +*/ void I_StartupHardwareGraphics(void); /** \brief restore old video mode @@ -82,9 +88,12 @@ INT32 VID_GetModeForSize(INT32 w, INT32 h); \param modenum video mode to set to - \return currect video mode + \return current video mode */ INT32 VID_SetMode(INT32 modenum); + +/** \brief Checks the render state +*/ void VID_CheckRenderer(void); /** \brief The VID_GetModeName function diff --git a/src/screen.c b/src/screen.c index fcf6c6b0b..b5faaef7e 100644 --- a/src/screen.c +++ b/src/screen.c @@ -464,7 +464,7 @@ void SCR_ChangeRenderer(void) { target_renderer = cv_renderer.value; #ifdef HWRENDER - if (M_CheckParm("-opengl")) + if (M_CheckParm("-opengl") && hwrenderloaded) target_renderer = rendermode = render_opengl; else #endif diff --git a/src/sdl/i_video.c b/src/sdl/i_video.c index 13e2423c4..a37e94fad 100644 --- a/src/sdl/i_video.c +++ b/src/sdl/i_video.c @@ -93,7 +93,8 @@ static INT32 numVidModes = -1; */ static char vidModeName[33][32]; // allow 33 different modes -rendermode_t rendermode=render_soft; +rendermode_t rendermode = render_soft; +static rendermode_t chosenrendermode = render_soft; // set by command line arguments boolean highcolor = false; @@ -103,6 +104,7 @@ static consvar_t cv_stretch = {"stretch", "Off", CV_SAVE|CV_NOSHOWHELP, CV_OnOff static consvar_t cv_alwaysgrabmouse = {"alwaysgrabmouse", "Off", CV_SAVE, CV_OnOff, NULL, 0, NULL, NULL, 0, 0, NULL}; UINT8 graphics_started = 0; // Is used in console.c and screen.c +boolean hwrenderloaded = false; // To disable fullscreen at startup; is set in VID_PrepareModeList boolean allow_fullscreen = false; @@ -1468,14 +1470,44 @@ static SDL_bool Impl_CreateContext(void) return SDL_TRUE; } +#ifdef HWRENDER +static void VID_CheckGLLoaded(rendermode_t oldrender) +{ + if (!hwrenderloaded) // Well, it didn't work the first time anyway. + { + CONS_Alert(CONS_ERROR, "OpenGL never loaded\n"); + rendermode = oldrender; + if (chosenrendermode == render_opengl) // fallback to software + rendermode = render_soft; + if (setrenderneeded) + { + CV_StealthSetValue(&cv_renderer, oldrender); + CV_StealthSetValue(&cv_newrenderer, oldrender); + setrenderneeded = 0; + } + } +} +#endif + void VID_CheckRenderer(void) { + rendermode_t oldrenderer = rendermode; + if (dedicated) return; +#ifdef HWRENDER + if (!graphics_started) + VID_CheckGLLoaded(oldrenderer); +#endif + if (setrenderneeded) { rendermode = setrenderneeded; +#ifdef HWRENDER + if (setrenderneeded == render_opengl) + VID_CheckGLLoaded(oldrenderer); +#endif Impl_CreateContext(); } @@ -1498,9 +1530,15 @@ void VID_CheckRenderer(void) else if (rendermode == render_opengl) { I_StartupHardwareGraphics(); - R_InitHardwareMode(); - HWR_Switch(); + // Needs to check if switching failed somehow, too. + if (rendermode == render_opengl) + { + R_InitHardwareMode(); + HWR_Switch(); + } } +#else + (void)oldrenderer; #endif } @@ -1665,10 +1703,10 @@ void I_StartupGraphics(void) #ifdef HWRENDER if (M_CheckParm("-opengl")) - rendermode = render_opengl; + chosenrendermode = rendermode = render_opengl; else if (M_CheckParm("-software")) #endif - rendermode = render_soft; + chosenrendermode = rendermode = render_soft; usesdl2soft = M_CheckParm("-softblit"); borderlesswindow = M_CheckParm("-borderless"); @@ -1764,13 +1802,15 @@ void I_StartupHardwareGraphics(void) HWD.pfnMakeScreenTexture= hwSym("MakeScreenTexture",NULL); HWD.pfnMakeScreenFinalTexture=hwSym("MakeScreenFinalTexture",NULL); HWD.pfnDrawScreenFinalTexture=hwSym("DrawScreenFinalTexture",NULL); - // check gl renderer lib - if (HWD.pfnGetRenderVersion() != VERSION) - I_Error("%s", M_GetText("The version of the renderer doesn't match the version of the executable\nBe sure you have installed SRB2 properly.\n")); + if (!HWD.pfnInit(I_Error)) // let load the OpenGL library + { rendermode = render_soft; + setrenderneeded = 0; + } else - glstartup = true; + hwrenderloaded = true; + glstartup = true; } #endif } diff --git a/src/sdl/ogl_sdl.c b/src/sdl/ogl_sdl.c index 6c0dd35a5..3e02e4ec3 100644 --- a/src/sdl/ogl_sdl.c +++ b/src/sdl/ogl_sdl.c @@ -128,15 +128,15 @@ boolean LoadGL(void) return SetupGLfunc(); else { - I_OutputMsg("Could not load GLU Library: %s\n", GLULibname); + CONS_Alert(CONS_ERROR, "Could not load GLU Library: %s\n", GLULibname); if (!M_CheckParm ("-GLUlib")) - I_OutputMsg("If you know what is the GLU library's name, use -GLUlib\n"); + CONS_Alert(CONS_ERROR, "If you know what is the GLU library's name, use -GLUlib\n"); } } else { - I_OutputMsg("Could not load GLU Library\n"); - I_OutputMsg("If you know what is the GLU library's name, use -GLUlib\n"); + CONS_Alert(CONS_ERROR, "Could not load GLU Library\n"); + CONS_Alert(CONS_ERROR, "If you know what is the GLU library's name, use -GLUlib\n"); } #endif return SetupGLfunc(); From cca366552519495e120ce798e1dd82930f4f16a2 Mon Sep 17 00:00:00 2001 From: Jaime Passos Date: Sat, 15 Feb 2020 13:51:32 -0300 Subject: [PATCH 10/68] Fallback to Software if the renderer version doesn't match --- src/sdl/i_video.c | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/src/sdl/i_video.c b/src/sdl/i_video.c index a37e94fad..4b30fc676 100644 --- a/src/sdl/i_video.c +++ b/src/sdl/i_video.c @@ -1803,13 +1803,19 @@ void I_StartupHardwareGraphics(void) HWD.pfnMakeScreenFinalTexture=hwSym("MakeScreenFinalTexture",NULL); HWD.pfnDrawScreenFinalTexture=hwSym("DrawScreenFinalTexture",NULL); - if (!HWD.pfnInit(I_Error)) // let load the OpenGL library + if (HWD.pfnGetRenderVersion() != VERSION) + { + CONS_Alert(CONS_ERROR, M_GetText("The version of the renderer doesn't match the version of the executable\nBe sure you have installed SRB2 properly.\n")); + hwrenderloaded = false; + } + else + hwrenderloaded = HWD.pfnInit(I_Error); // let load the OpenGL library + + if (!hwrenderloaded) { rendermode = render_soft; setrenderneeded = 0; } - else - hwrenderloaded = true; glstartup = true; } #endif From 6c5db5f4628f199206e1183414716839d563a5d5 Mon Sep 17 00:00:00 2001 From: Jaime Passos Date: Sat, 15 Feb 2020 14:25:28 -0300 Subject: [PATCH 11/68] Missing comment --- src/sdl/i_video.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/sdl/i_video.c b/src/sdl/i_video.c index 4b30fc676..afaaa2775 100644 --- a/src/sdl/i_video.c +++ b/src/sdl/i_video.c @@ -1803,6 +1803,7 @@ void I_StartupHardwareGraphics(void) HWD.pfnMakeScreenFinalTexture=hwSym("MakeScreenFinalTexture",NULL); HWD.pfnDrawScreenFinalTexture=hwSym("DrawScreenFinalTexture",NULL); + // check gl renderer lib if (HWD.pfnGetRenderVersion() != VERSION) { CONS_Alert(CONS_ERROR, M_GetText("The version of the renderer doesn't match the version of the executable\nBe sure you have installed SRB2 properly.\n")); From 07afd7520fb53bc110c5c97a890e82e5f8b67854 Mon Sep 17 00:00:00 2001 From: Jaime Passos Date: Sat, 15 Feb 2020 14:10:55 -0300 Subject: [PATCH 12/68] Only initialise OpenGL if you actually intend to use it. --- src/i_video.h | 3 ++- src/sdl/i_video.c | 24 +++++++++++++++--------- src/sdl/ogl_sdl.c | 4 ++-- 3 files changed, 19 insertions(+), 12 deletions(-) diff --git a/src/i_video.h b/src/i_video.h index 8f87a360e..294b7ef84 100644 --- a/src/i_video.h +++ b/src/i_video.h @@ -37,8 +37,9 @@ typedef enum extern rendermode_t rendermode; /** \brief hardware renderer loaded + 0 = never loaded, 1 = loaded successfully, -1 = failed loading */ -extern boolean hwrenderloaded; +extern INT32 hwrenderloaded; /** \brief use highcolor modes if true */ diff --git a/src/sdl/i_video.c b/src/sdl/i_video.c index afaaa2775..18cce3eab 100644 --- a/src/sdl/i_video.c +++ b/src/sdl/i_video.c @@ -104,7 +104,7 @@ static consvar_t cv_stretch = {"stretch", "Off", CV_SAVE|CV_NOSHOWHELP, CV_OnOff static consvar_t cv_alwaysgrabmouse = {"alwaysgrabmouse", "Off", CV_SAVE, CV_OnOff, NULL, 0, NULL, NULL, 0, 0, NULL}; UINT8 graphics_started = 0; // Is used in console.c and screen.c -boolean hwrenderloaded = false; +INT32 hwrenderloaded = 0; // To disable fullscreen at startup; is set in VID_PrepareModeList boolean allow_fullscreen = false; @@ -1473,7 +1473,7 @@ static SDL_bool Impl_CreateContext(void) #ifdef HWRENDER static void VID_CheckGLLoaded(rendermode_t oldrender) { - if (!hwrenderloaded) // Well, it didn't work the first time anyway. + if (hwrenderloaded == -1) // Well, it didn't work the first time anyway. { CONS_Alert(CONS_ERROR, "OpenGL never loaded\n"); rendermode = oldrender; @@ -1505,8 +1505,13 @@ void VID_CheckRenderer(void) { rendermode = setrenderneeded; #ifdef HWRENDER - if (setrenderneeded == render_opengl) + if (rendermode == render_opengl) + { VID_CheckGLLoaded(oldrenderer); + // Initialise OpenGL before calling SDLSetMode!!! + if (hwrenderloaded != 1) + I_StartupHardwareGraphics(); + } #endif Impl_CreateContext(); } @@ -1522,14 +1527,14 @@ void VID_CheckRenderer(void) bufSurface = NULL; } #ifdef HWRENDER - HWR_FreeTextureCache(); + if (hwrenderloaded == 1) // Only if OpenGL ever loaded! + HWR_FreeTextureCache(); #endif SCR_SetDrawFuncs(); } #ifdef HWRENDER else if (rendermode == render_opengl) { - I_StartupHardwareGraphics(); // Needs to check if switching failed somehow, too. if (rendermode == render_opengl) { @@ -1714,7 +1719,8 @@ void I_StartupGraphics(void) //SDL_EnableKeyRepeat(SDL_DEFAULT_REPEAT_DELAY>>1,SDL_DEFAULT_REPEAT_INTERVAL<<2); VID_Command_ModeList_f(); #ifdef HWRENDER - I_StartupHardwareGraphics(); + if (chosenrendermode == render_opengl) + I_StartupHardwareGraphics(); #endif // Fury: we do window initialization after GL setup to allow @@ -1807,12 +1813,12 @@ void I_StartupHardwareGraphics(void) if (HWD.pfnGetRenderVersion() != VERSION) { CONS_Alert(CONS_ERROR, M_GetText("The version of the renderer doesn't match the version of the executable\nBe sure you have installed SRB2 properly.\n")); - hwrenderloaded = false; + hwrenderloaded = -1; } else - hwrenderloaded = HWD.pfnInit(I_Error); // let load the OpenGL library + hwrenderloaded = HWD.pfnInit(I_Error) ? 1 : -1; // let load the OpenGL library - if (!hwrenderloaded) + if (hwrenderloaded == -1) { rendermode = render_soft; setrenderneeded = 0; diff --git a/src/sdl/ogl_sdl.c b/src/sdl/ogl_sdl.c index 3e02e4ec3..6654a75a0 100644 --- a/src/sdl/ogl_sdl.c +++ b/src/sdl/ogl_sdl.c @@ -95,10 +95,10 @@ boolean LoadGL(void) if (SDL_GL_LoadLibrary(OGLLibname) != 0) { - I_OutputMsg("Could not load OpenGL Library: %s\n" + CONS_Alert(CONS_ERROR, "Could not load OpenGL Library: %s\n" "Falling back to Software mode.\n", SDL_GetError()); if (!M_CheckParm ("-OGLlib")) - I_OutputMsg("If you know what is the OpenGL library's name, use -OGLlib\n"); + CONS_Alert(CONS_ERROR, "If you know what is the OpenGL library's name, use -OGLlib\n"); return 0; } From 3fa4db891f9ef693acf6c8e1668dc038e2fb310e Mon Sep 17 00:00:00 2001 From: Jaime Passos Date: Mon, 17 Feb 2020 01:13:13 -0300 Subject: [PATCH 13/68] Always load the GL library! --- src/sdl/i_video.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/sdl/i_video.c b/src/sdl/i_video.c index 18cce3eab..b50790f77 100644 --- a/src/sdl/i_video.c +++ b/src/sdl/i_video.c @@ -1719,8 +1719,7 @@ void I_StartupGraphics(void) //SDL_EnableKeyRepeat(SDL_DEFAULT_REPEAT_DELAY>>1,SDL_DEFAULT_REPEAT_INTERVAL<<2); VID_Command_ModeList_f(); #ifdef HWRENDER - if (chosenrendermode == render_opengl) - I_StartupHardwareGraphics(); + I_StartupHardwareGraphics(); #endif // Fury: we do window initialization after GL setup to allow From 9d9de3aff6e5272c62e1c243732ea5af3820a356 Mon Sep 17 00:00:00 2001 From: Jaime Passos Date: Sat, 15 Feb 2020 14:38:16 -0300 Subject: [PATCH 14/68] Only set OpenGL as the target renderer if it loaded successfully --- src/screen.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/screen.c b/src/screen.c index b5faaef7e..9c1af11a8 100644 --- a/src/screen.c +++ b/src/screen.c @@ -464,7 +464,7 @@ void SCR_ChangeRenderer(void) { target_renderer = cv_renderer.value; #ifdef HWRENDER - if (M_CheckParm("-opengl") && hwrenderloaded) + if (M_CheckParm("-opengl") && (hwrenderloaded == 1)) target_renderer = rendermode = render_opengl; else #endif From 7ddad9c9ec9fac004d9c19a5a8a97da7c07d3d74 Mon Sep 17 00:00:00 2001 From: Jaime Passos Date: Sat, 15 Feb 2020 21:08:06 -0300 Subject: [PATCH 15/68] Remove redundant check --- src/sdl/i_video.c | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/src/sdl/i_video.c b/src/sdl/i_video.c index b50790f77..fa1de19f2 100644 --- a/src/sdl/i_video.c +++ b/src/sdl/i_video.c @@ -1535,12 +1535,8 @@ void VID_CheckRenderer(void) #ifdef HWRENDER else if (rendermode == render_opengl) { - // Needs to check if switching failed somehow, too. - if (rendermode == render_opengl) - { - R_InitHardwareMode(); - HWR_Switch(); - } + R_InitHardwareMode(); + HWR_Switch(); } #else (void)oldrenderer; From 04ee6ca12c29fbb7a1a9ec2ca6288d1d60adda5e Mon Sep 17 00:00:00 2001 From: Jaime Passos Date: Sat, 15 Feb 2020 22:11:54 -0300 Subject: [PATCH 16/68] Don't call HWR_Switch twice --- src/sdl/i_video.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/sdl/i_video.c b/src/sdl/i_video.c index fa1de19f2..ca7b56592 100644 --- a/src/sdl/i_video.c +++ b/src/sdl/i_video.c @@ -1534,10 +1534,7 @@ void VID_CheckRenderer(void) } #ifdef HWRENDER else if (rendermode == render_opengl) - { R_InitHardwareMode(); - HWR_Switch(); - } #else (void)oldrenderer; #endif From 8da832986c31debdbba610997634febb880eac08 Mon Sep 17 00:00:00 2001 From: Jaime Passos Date: Sat, 15 Feb 2020 22:23:05 -0300 Subject: [PATCH 17/68] Don't center the window when changing renderers --- src/sdl/i_video.c | 21 +++++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) diff --git a/src/sdl/i_video.c b/src/sdl/i_video.c index ca7b56592..bacb4f771 100644 --- a/src/sdl/i_video.c +++ b/src/sdl/i_video.c @@ -176,7 +176,7 @@ static SDL_bool Impl_CreateWindow(SDL_bool fullscreen); //static void Impl_SetWindowName(const char *title); static void Impl_SetWindowIcon(void); -static void SDLSetMode(INT32 width, INT32 height, SDL_bool fullscreen) +static void SDLSetMode(INT32 width, INT32 height, SDL_bool fullscreen, SDL_bool reposition) { static SDL_bool wasfullscreen = SDL_FALSE; Uint32 rmask; @@ -205,10 +205,13 @@ static void SDLSetMode(INT32 width, INT32 height, SDL_bool fullscreen) } // Reposition window only in windowed mode SDL_SetWindowSize(window, width, height); - SDL_SetWindowPosition(window, - SDL_WINDOWPOS_CENTERED_DISPLAY(SDL_GetWindowDisplayIndex(window)), - SDL_WINDOWPOS_CENTERED_DISPLAY(SDL_GetWindowDisplayIndex(window)) - ); + if (reposition) + { + SDL_SetWindowPosition(window, + SDL_WINDOWPOS_CENTERED_DISPLAY(SDL_GetWindowDisplayIndex(window)), + SDL_WINDOWPOS_CENTERED_DISPLAY(SDL_GetWindowDisplayIndex(window)) + ); + } } } else @@ -1491,6 +1494,7 @@ static void VID_CheckGLLoaded(rendermode_t oldrender) void VID_CheckRenderer(void) { + SDL_bool rendererchanged = SDL_FALSE; rendermode_t oldrenderer = rendermode; if (dedicated) @@ -1504,6 +1508,8 @@ void VID_CheckRenderer(void) if (setrenderneeded) { rendermode = setrenderneeded; + rendererchanged = SDL_TRUE; + #ifdef HWRENDER if (rendermode == render_opengl) { @@ -1511,12 +1517,15 @@ void VID_CheckRenderer(void) // Initialise OpenGL before calling SDLSetMode!!! if (hwrenderloaded != 1) I_StartupHardwareGraphics(); + else if (hwrenderloaded == -1) + rendererchanged = SDL_FALSE; } #endif + Impl_CreateContext(); } - SDLSetMode(vid.width, vid.height, USE_FULLSCREEN); + SDLSetMode(vid.width, vid.height, USE_FULLSCREEN, (rendererchanged ? SDL_FALSE : SDL_TRUE)); Impl_VideoSetupBuffer(); if (rendermode == render_soft) From cac5f9d1714676f53fbe0f35531db17a3bc9ebfd Mon Sep 17 00:00:00 2001 From: Jaime Passos Date: Mon, 17 Feb 2020 12:14:51 -0300 Subject: [PATCH 18/68] add -nogl parm --- src/d_main.c | 9 ++++++++- src/m_menu.c | 28 ++++++++++++++++++++++++++-- src/screen.c | 14 ++++++++++++++ src/sdl/i_video.c | 11 +++++++---- 4 files changed, 55 insertions(+), 7 deletions(-) diff --git a/src/d_main.c b/src/d_main.c index 6616dfaa6..d9f67675c 100644 --- a/src/d_main.c +++ b/src/d_main.c @@ -1291,11 +1291,18 @@ void D_SRB2Main(void) // Lactozilla: Does the render mode need to change? if ((setrenderneeded != 0) && (setrenderneeded != rendermode)) { - CONS_Printf("Switching the renderer...\n"); + CONS_Printf(M_GetText("Switching the renderer...\n")); + Z_PreparePatchFlush(); + + // set needpatchflush / needpatchrecache true for D_CheckRendererState needpatchflush = true; needpatchrecache = true; + + // Set cv_renderer to the new render mode VID_CheckRenderer(); SCR_ChangeRendererCVars(setrenderneeded); + + // check the renderer's state, and then clear setrenderneeded D_CheckRendererState(); setrenderneeded = 0; } diff --git a/src/m_menu.c b/src/m_menu.c index 97c04ebd5..0349ed3bc 100644 --- a/src/m_menu.c +++ b/src/m_menu.c @@ -310,6 +310,7 @@ static void M_AssignJoystick(INT32 choice); static void M_ChangeControl(INT32 choice); // Video & Sound +static void M_VideoOptions(INT32 choice); menu_t OP_VideoOptionsDef, OP_VideoModeDef, OP_ColorOptionsDef; #ifdef HWRENDER static void M_OpenGLOptionsMenu(void); @@ -1031,7 +1032,7 @@ static menuitem_t OP_MainMenu[] = {IT_SUBMENU | IT_STRING, NULL, "Player 2 Controls...", &OP_P2ControlsDef, 20}, {IT_CVAR | IT_STRING, NULL, "Controls per key", &cv_controlperkey, 30}, - {IT_SUBMENU | IT_STRING, NULL, "Video Options...", &OP_VideoOptionsDef, 50}, + {IT_CALL | IT_STRING, NULL, "Video Options...", M_VideoOptions, 50}, {IT_SUBMENU | IT_STRING, NULL, "Sound Options...", &OP_SoundOptionsDef, 60}, {IT_CALL | IT_STRING, NULL, "Server Options...", M_ServerOptions, 80}, @@ -1282,6 +1283,16 @@ static menuitem_t OP_Camera2ExtendedOptionsMenu[] = {IT_STRING | IT_CVAR, NULL, "Crosshair", &cv_crosshair2, 126}, }; +enum +{ + op_video_resolution = 1, +#if (defined (__unix__) && !defined (MSDOS)) || defined (UNIXCOMMON) || defined (HAVE_SDL) + op_video_fullscreen, +#endif + op_video_vsync, + op_video_renderer, +}; + static menuitem_t OP_VideoOptionsMenu[] = { {IT_HEADER, NULL, "Screen", NULL, 0}, @@ -2078,6 +2089,20 @@ menu_t OP_PlaystyleDef = { 0, 0, 0, NULL }; +static void M_VideoOptions(INT32 choice) +{ + (void)choice; +#ifdef HWRENDER + if (hwrenderloaded == -1) + { + OP_VideoOptionsMenu[op_video_renderer].status = (IT_TRANSTEXT | IT_PAIR); + OP_VideoOptionsMenu[op_video_renderer].patch = "Renderer"; + OP_VideoOptionsMenu[op_video_renderer].text = "Software"; + } + +#endif + M_SetupNextMenu(&OP_VideoOptionsDef); +} menu_t OP_VideoOptionsDef = { @@ -12005,7 +12030,6 @@ static void M_VideoModeMenu(INT32 choice) static void M_DrawMainVideoMenu(void) { - M_DrawGenericScrollMenu(); if (itemOn < 8) // where it starts to go offscreen; change this number if you change the layout of the video menu { diff --git a/src/screen.c b/src/screen.c index 9c1af11a8..9c61f5689 100644 --- a/src/screen.c +++ b/src/screen.c @@ -450,6 +450,20 @@ static int target_renderer = 0; void SCR_ActuallyChangeRenderer(void) { setrenderneeded = target_renderer; + +#ifdef HWRENDER + // Well, it didn't even load anyway. + if ((hwrenderloaded == -1) && (setrenderneeded == render_opengl)) + { + if (M_CheckParm("-nogl")) + CONS_Alert(CONS_ERROR, "OpenGL rendering was disabled!\n"); + else + CONS_Alert(CONS_ERROR, "OpenGL never loaded\n"); + setrenderneeded = 0; + return; + } +#endif + // setting the same renderer twice WILL crash your game, so let's not, please if (rendermode == setrenderneeded) setrenderneeded = 0; diff --git a/src/sdl/i_video.c b/src/sdl/i_video.c index bacb4f771..1dbaf06bd 100644 --- a/src/sdl/i_video.c +++ b/src/sdl/i_video.c @@ -1440,7 +1440,7 @@ static SDL_bool Impl_CreateContext(void) { // Renderer-specific stuff #ifdef HWRENDER - if (rendermode == render_opengl) + if ((rendermode == render_opengl) && (hwrenderloaded != -1)) { if (!sdlglcontext) sdlglcontext = SDL_GL_CreateContext(window); @@ -1478,7 +1478,6 @@ static void VID_CheckGLLoaded(rendermode_t oldrender) { if (hwrenderloaded == -1) // Well, it didn't work the first time anyway. { - CONS_Alert(CONS_ERROR, "OpenGL never loaded\n"); rendermode = oldrender; if (chosenrendermode == render_opengl) // fallback to software rendermode = render_soft; @@ -1587,7 +1586,8 @@ static SDL_bool Impl_CreateWindow(SDL_bool fullscreen) flags |= SDL_WINDOW_BORDERLESS; #ifdef HWRENDER - flags |= SDL_WINDOW_OPENGL; + if (hwrenderloaded != -1) + flags |= SDL_WINDOW_OPENGL; #endif // Create a window @@ -1721,7 +1721,10 @@ void I_StartupGraphics(void) //SDL_EnableKeyRepeat(SDL_DEFAULT_REPEAT_DELAY>>1,SDL_DEFAULT_REPEAT_INTERVAL<<2); VID_Command_ModeList_f(); #ifdef HWRENDER - I_StartupHardwareGraphics(); + if (M_CheckParm("-nogl")) + hwrenderloaded = -1; // Don't call SDL_GL_LoadLibrary + else + I_StartupHardwareGraphics(); #endif // Fury: we do window initialization after GL setup to allow From a9a0601c84bcddbef577a458ced27e652b4df44c Mon Sep 17 00:00:00 2001 From: James R Date: Mon, 17 Feb 2020 16:37:13 -0800 Subject: [PATCH 19/68] Revert "Revert "Let the console open in menus"" This reverts commit 705cf9fd4078ce34857d98043023612e9205773c. --- src/console.c | 11 +---------- src/d_main.c | 15 +++++++-------- 2 files changed, 8 insertions(+), 18 deletions(-) diff --git a/src/console.c b/src/console.c index 59d2b3e6c..8746bf036 100644 --- a/src/console.c +++ b/src/console.c @@ -613,15 +613,6 @@ void CON_Ticker(void) con_tick++; con_tick &= 7; - // if the menu is open then close the console. - if (menuactive && con_destlines) - { - consoletoggle = false; - con_destlines = 0; - CON_ClearHUD(); - I_UpdateMouseGrab(); - } - // console key was pushed if (consoletoggle) { @@ -793,7 +784,7 @@ boolean CON_Responder(event_t *ev) // check other keys only if console prompt is active if (!consoleready && key < NUMINPUTS) // metzgermeister: boundary check!! { - if (bindtable[key]) + if (! menuactive && bindtable[key]) { COM_BufAddText(bindtable[key]); COM_BufAddText("\n"); diff --git a/src/d_main.c b/src/d_main.c index 2ff0042fd..149fb3071 100644 --- a/src/d_main.c +++ b/src/d_main.c @@ -188,14 +188,14 @@ void D_ProcessEvents(void) continue; } - // Menu input - if (M_Responder(ev)) - continue; // menu ate the event - // console input if (CON_Responder(ev)) continue; // ate the event + // Menu input + if (M_Responder(ev)) + continue; // menu ate the event + G_Responder(ev); } } @@ -502,13 +502,12 @@ static void D_Display(void) // vid size change is now finished if it was on... vid.recalc = 0; - // FIXME: draw either console or menu, not the two - if (gamestate != GS_TIMEATTACK) - CON_Drawer(); - M_Drawer(); // menu is drawn even on top of everything // focus lost moved to M_Drawer + if (gamestate != GS_TIMEATTACK) + CON_Drawer(); + // // wipe update // From b2733eba73146c9b87ebdcff26cc0e10d4107408 Mon Sep 17 00:00:00 2001 From: James R Date: Sat, 15 Feb 2020 17:08:07 -0800 Subject: [PATCH 20/68] Draw console in the Record/NiGHTS Attack menus (cherry picked from commit 4efd915d28d0f193528d141b04c5f88b84877f97) --- src/d_main.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/d_main.c b/src/d_main.c index 149fb3071..6b5164894 100644 --- a/src/d_main.c +++ b/src/d_main.c @@ -505,8 +505,7 @@ static void D_Display(void) M_Drawer(); // menu is drawn even on top of everything // focus lost moved to M_Drawer - if (gamestate != GS_TIMEATTACK) - CON_Drawer(); + CON_Drawer(); // // wipe update From b78fc670d0824e2054d102706813087d0086c331 Mon Sep 17 00:00:00 2001 From: James R Date: Mon, 17 Feb 2020 17:10:29 -0800 Subject: [PATCH 21/68] Don't let console open with menu keys while the menu is open --- src/d_main.c | 8 ++++---- src/m_menu.c | 4 ++++ 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/src/d_main.c b/src/d_main.c index 6b5164894..cff6b8805 100644 --- a/src/d_main.c +++ b/src/d_main.c @@ -188,14 +188,14 @@ void D_ProcessEvents(void) continue; } - // console input - if (CON_Responder(ev)) - continue; // ate the event - // Menu input if (M_Responder(ev)) continue; // menu ate the event + // console input + if (CON_Responder(ev)) + continue; // ate the event + G_Responder(ev); } } diff --git a/src/m_menu.c b/src/m_menu.c index dbe5d854f..687f87051 100644 --- a/src/m_menu.c +++ b/src/m_menu.c @@ -3150,6 +3150,9 @@ boolean M_Responder(event_t *ev) if (gamestate == GS_TITLESCREEN && finalecount < TICRATE) return false; + if (CON_Ready()) + return false; + if (noFurtherInput) { // Ignore input after enter/escape/other buttons @@ -3509,6 +3512,7 @@ boolean M_Responder(event_t *ev) return false; default: + CON_Responder(ev); break; } From ebc1809da987b47746a537ee5bdaa8824b6c7b3f Mon Sep 17 00:00:00 2001 From: Louis-Antoine Date: Wed, 19 Feb 2020 22:01:33 +0100 Subject: [PATCH 22/68] Fix bad tabulation --- src/d_netfil.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/d_netfil.c b/src/d_netfil.c index 71449caca..413eef1ef 100644 --- a/src/d_netfil.c +++ b/src/d_netfil.c @@ -477,21 +477,21 @@ void AddLuaFileTransfer(const char *filename, const char *mode) if (!filetransfer) I_Error("AddLuaFileTransfer: Out of memory\n"); *prevnext = filetransfer; - filetransfer->next = NULL; + filetransfer->next = NULL; - // Allocate the file name - filetransfer->filename = strdup(filename); - if (!filetransfer->filename) + // Allocate the file name + filetransfer->filename = strdup(filename); + if (!filetransfer->filename) I_Error("AddLuaFileTransfer: Out of memory\n"); - // Create and allocate the real file name + // Create and allocate the real file name if (server) filetransfer->realfilename = strdup(va("%s" PATHSEP "%s", luafiledir, filename)); else filetransfer->realfilename = strdup(va("%s" PATHSEP "client" PATHSEP "$$$%d%d.tmp", luafiledir, rand(), rand())); - if (!filetransfer->realfilename) + if (!filetransfer->realfilename) I_Error("AddLuaFileTransfer: Out of memory\n"); strlcpy(filetransfer->mode, mode, sizeof(filetransfer->mode)); @@ -556,7 +556,7 @@ void SV_PrepareSendLuaFileToNextNode(void) void SV_HandleLuaFileSent(UINT8 node) { luafiletransfers->nodestatus[node] = LFTNS_SENT; - SV_PrepareSendLuaFileToNextNode(); + SV_PrepareSendLuaFileToNextNode(); } void RemoveLuaFileTransfer(void) @@ -1036,7 +1036,7 @@ void Got_Filetxpak(void) CONS_Printf(M_GetText("Downloading %s...(done)\n"), filename); #ifdef HAVE_BLUA - if (luafiletransfers) + if (luafiletransfers) { // Tell the server we have received the file netbuffer->packettype = PT_HASLUAFILE; From 20d054a679acdf70971ad0e90d992dc942fe3679 Mon Sep 17 00:00:00 2001 From: Louis-Antoine Date: Wed, 19 Feb 2020 22:37:30 +0100 Subject: [PATCH 23/68] Minor code cleanup --- src/d_clisrv.c | 2 +- src/d_netfil.c | 4 ++-- src/d_netfil.h | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/d_clisrv.c b/src/d_clisrv.c index 6b0f49544..9f0d2439e 100644 --- a/src/d_clisrv.c +++ b/src/d_clisrv.c @@ -3164,7 +3164,7 @@ void D_QuitNetGame(void) // abort send/receive of files CloseNetFile(); - RemoveLuaFileTransfers(); + RemoveAllLuaFileTransfers(); waitingforluafiletransfer = false; if (server) diff --git a/src/d_netfil.c b/src/d_netfil.c index 413eef1ef..1d1e7d203 100644 --- a/src/d_netfil.c +++ b/src/d_netfil.c @@ -506,7 +506,7 @@ void AddLuaFileTransfer(const char *filename, const char *mode) if (!luafiletransfers->next) // Only if there is no transfer already going on { - if (FIL_FileOK(filetransfer->realfilename)) + if (FIL_ReadFileOK(filetransfer->realfilename)) SV_PrepareSendLuaFileToNextNode(); else { @@ -572,7 +572,7 @@ void RemoveLuaFileTransfer(void) free(filetransfer); } -void RemoveLuaFileTransfers(void) +void RemoveAllLuaFileTransfers(void) { while (luafiletransfers) RemoveLuaFileTransfer(); diff --git a/src/d_netfil.h b/src/d_netfil.h index 0d0b16975..0473b5f44 100644 --- a/src/d_netfil.h +++ b/src/d_netfil.h @@ -101,7 +101,7 @@ boolean SV_SendLuaFile(INT32 node, const char *filename, boolean textmode); void SV_PrepareSendLuaFile(const char *filename); void SV_HandleLuaFileSent(UINT8 node); void RemoveLuaFileTransfer(void); -void RemoveLuaFileTransfers(void); +void RemoveAllLuaFileTransfers(void); void SV_AbortLuaFileTransfer(INT32 node); void CL_PrepareDownloadLuaFile(void); void Got_LuaFile(UINT8 **cp, INT32 playernum); From 3502278f8a5a5232adadbeddd524a7bf3687798a Mon Sep 17 00:00:00 2001 From: Louis-Antoine Date: Thu, 20 Feb 2020 01:15:54 +0100 Subject: [PATCH 24/68] SpaceSpaceSpaceSpace --- src/d_netfil.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/d_netfil.c b/src/d_netfil.c index 1d1e7d203..ed81a51b1 100644 --- a/src/d_netfil.c +++ b/src/d_netfil.c @@ -1038,9 +1038,9 @@ void Got_Filetxpak(void) #ifdef HAVE_BLUA if (luafiletransfers) { - // Tell the server we have received the file - netbuffer->packettype = PT_HASLUAFILE; - HSendPacket(servernode, true, 0, 0); + // Tell the server we have received the file + netbuffer->packettype = PT_HASLUAFILE; + HSendPacket(servernode, true, 0, 0); } #endif } From 400366802c2d77a31b40da5ff57f366ca197383b Mon Sep 17 00:00:00 2001 From: Sally Cochenour Date: Thu, 20 Feb 2020 16:40:39 -0500 Subject: [PATCH 25/68] Make hook_FollowMobj a mobj hook instead of a player hook Makes more logical sense, as if you have a custom follow item for a custom skin, you probably want your new thinker to only run for your new MT_ and not the vanilla ones. --- src/lua_hooklib.c | 32 ++++++++++++++++++++++++++++++-- 1 file changed, 30 insertions(+), 2 deletions(-) diff --git a/src/lua_hooklib.c b/src/lua_hooklib.c index f9dad6cb7..99f41aef1 100644 --- a/src/lua_hooklib.c +++ b/src/lua_hooklib.c @@ -142,6 +142,7 @@ static int lib_addHook(lua_State *L) case hook_HurtMsg: case hook_MobjMoveBlocked: case hook_MapThingSpawn: + case hook_FollowMobj: hook.s.mt = MT_NULL; if (lua_isnumber(L, 2)) hook.s.mt = lua_tonumber(L, 2); @@ -203,6 +204,7 @@ static int lib_addHook(lua_State *L) case hook_MobjRemoved: case hook_MobjMoveBlocked: case hook_MapThingSpawn: + case hook_FollowMobj: lastp = &mobjhooks[hook.s.mt]; break; case hook_JumpSpecial: @@ -210,7 +212,6 @@ static int lib_addHook(lua_State *L) case hook_SpinSpecial: case hook_JumpSpinSpecial: case hook_PlayerSpawn: - case hook_FollowMobj: case hook_PlayerCanDamage: case hook_TeamSwitch: case hook_ViewpointSwitch: @@ -1364,7 +1365,34 @@ boolean LUAh_FollowMobj(player_t *player, mobj_t *mobj) lua_settop(gL, 0); - for (hookp = playerhooks; hookp; hookp = hookp->next) + // Look for all generic mobj follow item hooks + for (hookp = mobjhooks[MT_NULL]; hookp; hookp = hookp->next) + { + if (hookp->type != hook_FollowMobj) + continue; + + if (lua_gettop(gL) == 0) + { + LUA_PushUserdata(gL, player, META_PLAYER); + LUA_PushUserdata(gL, mobj, META_MOBJ); + } + lua_pushfstring(gL, FMT_HOOKID, hookp->id); + lua_gettable(gL, LUA_REGISTRYINDEX); + lua_pushvalue(gL, -3); + lua_pushvalue(gL, -3); + if (lua_pcall(gL, 2, 1, 0)) { + if (!hookp->error || cv_debug & DBG_LUA) + CONS_Alert(CONS_WARNING,"%s\n",lua_tostring(gL, -1)); + lua_pop(gL, 1); + hookp->error = true; + continue; + } + if (lua_toboolean(gL, -1)) + hooked = true; + lua_pop(gL, 1); + } + + for (hookp = mobjhooks[mobj->type]; hookp; hookp = hookp->next) { if (hookp->type != hook_FollowMobj) continue; From cce53081085b256165940e8ff38cc8d97880ae62 Mon Sep 17 00:00:00 2001 From: James R Date: Thu, 20 Feb 2020 17:18:26 -0800 Subject: [PATCH 26/68] Fix compiler warnings --- src/blua/liolib.c | 4 ++-- src/d_netfil.c | 2 +- src/d_netfil.h | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/blua/liolib.c b/src/blua/liolib.c index e1a64c8d8..378b8c86d 100644 --- a/src/blua/liolib.c +++ b/src/blua/liolib.c @@ -34,7 +34,6 @@ #define FMT_FILECALLBACKID "file_callback_%d" -static const char *const fnames[] = {"input", "output"}; static const char *whitelist[] = { // Allow scripters to write files of these types to SRB2's folder ".bmp", ".cfg", @@ -230,6 +229,7 @@ static int io_openlocal (lua_State *L) { FILE **pf; const char *filename = luaL_checkstring(L, 1); const char *mode = luaL_optstring(L, 2, "r"); + char *realfilename; luafiletransfer_t *filetransfer; int checkresult; @@ -237,7 +237,7 @@ static int io_openlocal (lua_State *L) { if (checkresult) return checkresult; - char *realfilename = va("%s" PATHSEP "%s", luafiledir, filename); + realfilename = va("%s" PATHSEP "%s", luafiledir, filename); if (client && strnicmp(filename, "client/", strlen("client/"))) I_Error("Access denied to %s\n" diff --git a/src/d_netfil.c b/src/d_netfil.c index ed81a51b1..a5d4b71e2 100644 --- a/src/d_netfil.c +++ b/src/d_netfil.c @@ -98,7 +98,7 @@ INT32 lastfilenum = -1; #ifdef HAVE_BLUA luafiletransfer_t *luafiletransfers = NULL; boolean waitingforluafiletransfer = false; -char luafiledir[256] = "luafiles"; +char luafiledir[MAX_WADPATH] = "luafiles"; #endif diff --git a/src/d_netfil.h b/src/d_netfil.h index 0473b5f44..13087dfb2 100644 --- a/src/d_netfil.h +++ b/src/d_netfil.h @@ -93,7 +93,7 @@ typedef struct luafiletransfer_s extern luafiletransfer_t *luafiletransfers; extern boolean waitingforluafiletransfer; -extern char luafiledir[256]; +extern char luafiledir[MAX_WADPATH]; void AddLuaFileTransfer(const char *filename, const char *mode); void SV_PrepareSendLuaFileToNextNode(void); From 2df4717f18475835c5924c84624d44d2674c6aa7 Mon Sep 17 00:00:00 2001 From: James R Date: Thu, 20 Feb 2020 17:20:50 -0800 Subject: [PATCH 27/68] Fix NO_LUA compiling --- src/d_clisrv.c | 2 ++ src/d_main.c | 6 +++++- src/d_net.c | 2 ++ 3 files changed, 9 insertions(+), 1 deletion(-) diff --git a/src/d_clisrv.c b/src/d_clisrv.c index f31c2bcd4..3ad8db3c7 100644 --- a/src/d_clisrv.c +++ b/src/d_clisrv.c @@ -3197,8 +3197,10 @@ void D_QuitNetGame(void) // abort send/receive of files CloseNetFile(); +#ifdef HAVE_BLUA RemoveAllLuaFileTransfers(); waitingforluafiletransfer = false; +#endif if (server) { diff --git a/src/d_main.c b/src/d_main.c index 715c90aa9..a54cdfc8d 100644 --- a/src/d_main.c +++ b/src/d_main.c @@ -1126,8 +1126,10 @@ void D_SRB2Main(void) // can't use sprintf since there is %u in savegamename strcatbf(savegamename, srb2home, PATHSEP); +#ifdef HAVE_BLUA snprintf(luafiledir, sizeof luafiledir, "%s" PATHSEP "luafiles", srb2home); -#else +#endif +#else/*DEFAULTDIR*/ snprintf(srb2home, sizeof srb2home, "%s", userhome); snprintf(downloaddir, sizeof downloaddir, "%s", userhome); if (dedicated) @@ -1138,8 +1140,10 @@ void D_SRB2Main(void) // can't use sprintf since there is %u in savegamename strcatbf(savegamename, userhome, PATHSEP); +#ifdef HAVE_BLUA snprintf(luafiledir, sizeof luafiledir, "%s" PATHSEP "luafiles", userhome); #endif +#endif/*DEFAULTDIR*/ } configfile[sizeof configfile - 1] = '\0'; diff --git a/src/d_net.c b/src/d_net.c index ddb991235..a83d6d0ff 100644 --- a/src/d_net.c +++ b/src/d_net.c @@ -803,9 +803,11 @@ static const char *packettypename[NUMPACKETTYPE] = "RESYNCHEND", "RESYNCHGET", +#ifdef HAVE_BLUA "SENDINGLUAFILE", "ASKLUAFILE", "HASLUAFILE", +#endif "FILEFRAGMENT", "TEXTCMD", From ec5b88e79fcf314fd4e4190ee343709ee74f7616 Mon Sep 17 00:00:00 2001 From: Louis-Antoine Date: Fri, 21 Feb 2020 17:17:39 +0100 Subject: [PATCH 28/68] Fix weird comment style --- src/d_main.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/d_main.c b/src/d_main.c index a54cdfc8d..2553dc8e6 100644 --- a/src/d_main.c +++ b/src/d_main.c @@ -1129,7 +1129,7 @@ void D_SRB2Main(void) #ifdef HAVE_BLUA snprintf(luafiledir, sizeof luafiledir, "%s" PATHSEP "luafiles", srb2home); #endif -#else/*DEFAULTDIR*/ +#else // DEFAULTDIR snprintf(srb2home, sizeof srb2home, "%s", userhome); snprintf(downloaddir, sizeof downloaddir, "%s", userhome); if (dedicated) @@ -1143,7 +1143,7 @@ void D_SRB2Main(void) #ifdef HAVE_BLUA snprintf(luafiledir, sizeof luafiledir, "%s" PATHSEP "luafiles", userhome); #endif -#endif/*DEFAULTDIR*/ +#endif // DEFAULTDIR } configfile[sizeof configfile - 1] = '\0'; From 472befd183143764218b466bbcda269aa3bb310a Mon Sep 17 00:00:00 2001 From: Louis-Antoine Date: Fri, 21 Feb 2020 17:31:32 +0100 Subject: [PATCH 29/68] Replace MAX_WADPATH with plain numbers This is not ideal either, but I would rather not use a constant for something unrelated. --- src/d_netfil.c | 2 +- src/d_netfil.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/d_netfil.c b/src/d_netfil.c index a5d4b71e2..ecb2229a6 100644 --- a/src/d_netfil.c +++ b/src/d_netfil.c @@ -98,7 +98,7 @@ INT32 lastfilenum = -1; #ifdef HAVE_BLUA luafiletransfer_t *luafiletransfers = NULL; boolean waitingforluafiletransfer = false; -char luafiledir[MAX_WADPATH] = "luafiles"; +char luafiledir[256 + 16] = "luafiles"; #endif diff --git a/src/d_netfil.h b/src/d_netfil.h index 13087dfb2..830293ca8 100644 --- a/src/d_netfil.h +++ b/src/d_netfil.h @@ -93,7 +93,7 @@ typedef struct luafiletransfer_s extern luafiletransfer_t *luafiletransfers; extern boolean waitingforluafiletransfer; -extern char luafiledir[MAX_WADPATH]; +extern char luafiledir[256 + 16]; void AddLuaFileTransfer(const char *filename, const char *mode); void SV_PrepareSendLuaFileToNextNode(void); From af9164f336a65634f6908e677b47773d88f583af Mon Sep 17 00:00:00 2001 From: Louis-Antoine Date: Sat, 22 Feb 2020 02:17:21 +0100 Subject: [PATCH 30/68] Improve join refusal handling --- src/d_clisrv.c | 81 ++++++++++++++++++++++++++++++-------------------- src/d_clisrv.h | 3 +- src/m_menu.c | 2 +- 3 files changed, 51 insertions(+), 35 deletions(-) diff --git a/src/d_clisrv.c b/src/d_clisrv.c index 77118110e..ef0c0c33e 100644 --- a/src/d_clisrv.c +++ b/src/d_clisrv.c @@ -1289,6 +1289,37 @@ static boolean CL_SendJoin(void) return HSendPacket(servernode, true, 0, sizeof (clientconfig_pak)); } +static INT32 FindRejoinerNum(SINT8 node) +{ + char strippednodeaddress[64]; + const char *nodeaddress; + char *port; + INT32 i; + + // Make sure there is no dead dress before proceeding to the stripping + if (!I_GetNodeAddress) + return -1; + nodeaddress = I_GetNodeAddress(node); + if (!nodeaddress) + return -1; + + // Strip the address of its port + strcpy(strippednodeaddress, nodeaddress); + port = strchr(strippednodeaddress, ':'); + if (port) + *port = '\0'; + + // Check if any player matches the stripped address + for (i = 0; i < MAXPLAYERS; i++) + { + if (playeringame[i] && playeraddress[i][0] && playernode[i] == UINT8_MAX + && !strcmp(playeraddress[i], strippednodeaddress)) + return i; + } + + return -1; +} + static void SV_SendServerInfo(INT32 node, tic_t servertime) { UINT8 *p; @@ -1306,6 +1337,16 @@ static void SV_SendServerInfo(INT32 node, tic_t servertime) netbuffer->u.serverinfo.numberofplayer = (UINT8)D_NumPlayers(); netbuffer->u.serverinfo.maxplayer = (UINT8)cv_maxplayers.value; + + if (FindRejoinerNum(node) != -1) + netbuffer->u.serverinfo.refusereason = 0; + else if (!cv_allownewplayer.value) + netbuffer->u.serverinfo.refusereason = 1; + else if (D_NumPlayers() >= cv_maxplayers.value) + netbuffer->u.serverinfo.refusereason = 2; + else + netbuffer->u.serverinfo.refusereason = 0; + strncpy(netbuffer->u.serverinfo.gametypename, Gametype_Names[gametype], sizeof netbuffer->u.serverinfo.gametypename); netbuffer->u.serverinfo.modifiedgame = (UINT8)modifiedgame; @@ -1863,12 +1904,17 @@ static boolean CL_ServerConnectionSearchTicker(boolean viams, tic_t *asksent) } // Quit here rather than downloading files and being refused later. - if (serverlist[i].info.numberofplayer >= serverlist[i].info.maxplayer) + if (serverlist[i].info.refusereason) { D_QuitNetGame(); CL_Reset(); D_StartTitle(); - M_StartMessage(va(M_GetText("Maximum players reached: %d\n\nPress ESC\n"), serverlist[i].info.maxplayer), NULL, MM_NOTHING); + if (serverlist[i].info.refusereason == 1) + M_StartMessage(M_GetText("The server is not accepting\njoins for the moment.\n\nPress ESC\n"), NULL, MM_NOTHING); + else if (serverlist[i].info.refusereason == 2) + M_StartMessage(va(M_GetText("Maximum players reached: %d\n\nPress ESC\n"), serverlist[i].info.maxplayer), NULL, MM_NOTHING); + else + M_StartMessage(M_GetText("You can't join.\nI don't know why,\nbut you can't join.\n\nPress ESC\n"), NULL, MM_NOTHING); return false; } @@ -3230,37 +3276,6 @@ void D_QuitNetGame(void) #endif } -static INT32 FindRejoinerNum(SINT8 node) -{ - char strippednodeaddress[64]; - const char *nodeaddress; - char *port; - INT32 i; - - // Make sure there is no dead dress before proceeding to the stripping - if (!I_GetNodeAddress) - return -1; - nodeaddress = I_GetNodeAddress(node); - if (!nodeaddress) - return -1; - - // Strip the address of its port - strcpy(strippednodeaddress, nodeaddress); - port = strchr(strippednodeaddress, ':'); - if (port) - *port = '\0'; - - // Check if any player matches the stripped address - for (i = 0; i < MAXPLAYERS; i++) - { - if (playeringame[i] && playeraddress[i][0] && playernode[i] == UINT8_MAX - && !strcmp(playeraddress[i], strippednodeaddress)) - return i; - } - - return -1; -} - // Adds a node to the game (player will follow at map change or at savegame....) static inline void SV_AddNode(INT32 node) { diff --git a/src/d_clisrv.h b/src/d_clisrv.h index df93fe31d..8cea2f00a 100644 --- a/src/d_clisrv.h +++ b/src/d_clisrv.h @@ -27,7 +27,7 @@ This version is independent of the mod name, and standard version and subversion. It should only account for the basic fields of the packet, and change infrequently. */ -#define PACKETVERSION 2 +#define PACKETVERSION 3 // Network play related stuff. // There is a data struct that stores network @@ -361,6 +361,7 @@ typedef struct UINT8 subversion; UINT8 numberofplayer; UINT8 maxplayer; + UINT8 refusereason; // 0: joinable, 1: joins disabled, 2: full char gametypename[24]; UINT8 modifiedgame; UINT8 cheatsenabled; diff --git a/src/m_menu.c b/src/m_menu.c index 8b564e068..d48714c35 100644 --- a/src/m_menu.c +++ b/src/m_menu.c @@ -10366,7 +10366,7 @@ static void M_DrawConnectMenu(void) for (i = 0; i < min(serverlistcount - serverlistpage * SERVERS_PER_PAGE, SERVERS_PER_PAGE); i++) { INT32 slindex = i + serverlistpage * SERVERS_PER_PAGE; - UINT32 globalflags = ((serverlist[slindex].info.numberofplayer >= serverlist[slindex].info.maxplayer) ? V_TRANSLUCENT : 0) + UINT32 globalflags = (serverlist[slindex].info.refusereason ? V_TRANSLUCENT : 0) |((itemOn == FIRSTSERVERLINE+i) ? V_YELLOWMAP : 0)|V_ALLOWLOWERCASE; V_DrawString(currentMenu->x, S_LINEY(i), globalflags, serverlist[slindex].info.servername); From 778715f6111a1bbf7d2ae874e5490e1b2ae0dbc1 Mon Sep 17 00:00:00 2001 From: Louis-Antoine Date: Sat, 22 Feb 2020 02:21:27 +0100 Subject: [PATCH 31/68] Cleanup code a little --- src/d_clisrv.c | 4 ++-- src/d_netcmd.c | 4 ---- 2 files changed, 2 insertions(+), 6 deletions(-) diff --git a/src/d_clisrv.c b/src/d_clisrv.c index ef0c0c33e..98eff1cd5 100644 --- a/src/d_clisrv.c +++ b/src/d_clisrv.c @@ -3606,7 +3606,7 @@ static void HandleConnect(SINT8 node) rejoinernum = FindRejoinerNum(node); if (bannednode && bannednode[node]) - SV_SendRefuse(node, M_GetText("You have been banned\nfrom the server")); + SV_SendRefuse(node, M_GetText("You have been banned\nfrom the server.")); else if (netbuffer->u.clientcfg._255 != 255 || netbuffer->u.clientcfg.packetversion != PACKETVERSION) SV_SendRefuse(node, "Incompatible packet formats."); @@ -3617,7 +3617,7 @@ static void HandleConnect(SINT8 node) || netbuffer->u.clientcfg.subversion != SUBVERSION) SV_SendRefuse(node, va(M_GetText("Different SRB2 versions cannot\nplay a netgame!\n(server version %d.%d.%d)"), VERSION/100, VERSION%100, SUBVERSION)); else if (!cv_allownewplayer.value && node && rejoinernum == -1) - SV_SendRefuse(node, M_GetText("The server is not accepting\njoins for the moment")); + SV_SendRefuse(node, M_GetText("The server is not accepting\njoins for the moment.")); else if (D_NumPlayers() >= cv_maxplayers.value && rejoinernum == -1) SV_SendRefuse(node, va(M_GetText("Maximum players reached: %d"), cv_maxplayers.value)); else if (netgame && netbuffer->u.clientcfg.localplayers > 1) // Hacked client? diff --git a/src/d_netcmd.c b/src/d_netcmd.c index 623a83c47..b619621c6 100644 --- a/src/d_netcmd.c +++ b/src/d_netcmd.c @@ -3342,10 +3342,6 @@ static void Got_RequestAddfilecmd(UINT8 **cp, INT32 playernum) boolean kick = false; boolean toomany = false; INT32 i,j; - serverinfo_pak *dummycheck = NULL; - - // Shut the compiler up. - (void)dummycheck; READSTRINGN(*cp, filename, 240); READMEM(*cp, md5sum, 16); From 0f2b8b8b2cfb431d46468c5a71fad9d4d51d98c6 Mon Sep 17 00:00:00 2001 From: James R Date: Fri, 21 Feb 2020 20:04:28 -0800 Subject: [PATCH 32/68] Register servername etc. under NOMD5 This fixes crashes in the menus. --- src/mserv.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/mserv.c b/src/mserv.c index 4ddab05b9..c02a58f99 100644 --- a/src/mserv.c +++ b/src/mserv.c @@ -18,7 +18,7 @@ #include -#if (defined (NOMD5) || defined (NOMSERV)) && !defined (NONET) +#if (defined (NOMSERV)) && !defined (NONET) #define NONET #endif From d19fe295e0556a49c3f5c37df1e2b379a961611e Mon Sep 17 00:00:00 2001 From: Louis-Antoine Date: Sat, 22 Feb 2020 15:08:17 +0100 Subject: [PATCH 33/68] Fix buffer overrun with nodeless players --- src/d_clisrv.c | 27 ++++++++++++--------------- 1 file changed, 12 insertions(+), 15 deletions(-) diff --git a/src/d_clisrv.c b/src/d_clisrv.c index 98eff1cd5..eda6f9c67 100644 --- a/src/d_clisrv.c +++ b/src/d_clisrv.c @@ -2485,14 +2485,14 @@ static void CL_RemovePlayer(INT32 playernum, kickreason_t reason) if (!playeringame[playernum]) return; - if (server && !demoplayback) + if (server && !demoplayback && playernode[playernum] != UINT8_MAX) { INT32 node = playernode[playernum]; playerpernode[node]--; if (playerpernode[node] <= 0) { - nodeingame[playernode[playernum]] = false; - Net_CloseConnection(playernode[playernum]); + nodeingame[node] = false; + Net_CloseConnection(node); ResetNode(node); } } @@ -2838,16 +2838,13 @@ static void Command_Kick(void) if (pn == -1 || pn == 0) return; - if (server) + // Special case if we are trying to kick a player who is downloading the game state: + // trigger a timeout instead of kicking them, because a kick would only + // take effect after they have finished downloading + if (server && playernode[pn] != UINT8_MAX && sendingsavegame[playernode[pn]]) { - // Special case if we are trying to kick a player who is downloading the game state: - // trigger a timeout instead of kicking them, because a kick would only - // take effect after they have finished downloading - if (sendingsavegame[playernode[pn]]) - { - Net_ConnectionTimeout(playernode[pn]); - return; - } + Net_ConnectionTimeout(playernode[pn]); + return; } WRITESINT8(p, pn); @@ -2905,7 +2902,7 @@ static void Got_KickCmd(UINT8 **p, INT32 playernum) // Is playernum authorized to make this kick? if (playernum != serverplayer && !IsPlayerAdmin(playernum) - && !(playerpernode[playernode[playernum]] == 2 + && !(playernode[playernum] != UINT8_MAX && playerpernode[playernode[playernum]] == 2 && nodetoplayer2[playernode[playernum]] == pnum)) { // We received a kick command from someone who isn't the @@ -3064,7 +3061,7 @@ static void Got_KickCmd(UINT8 **p, INT32 playernum) } else if (keepbody) { - if (server && !demoplayback) + if (server && !demoplayback && playernode[pnum] != UINT8_MAX) { INT32 node = playernode[pnum]; playerpernode[node]--; @@ -4956,7 +4953,7 @@ void NetUpdate(void) PingUpdate(); // update node latency values so we can take an average later. for (i = 0; i < MAXPLAYERS; i++) - if (playeringame[i]) + if (playeringame[i] && playernode[i] != UINT8_MAX) realpingtable[i] += G_TicsToMilliseconds(GetLag(playernode[i])); pingmeasurecount++; } From 4c00e3309647d6ee4a7f370b514f72ef1cc57027 Mon Sep 17 00:00:00 2001 From: Tatsuru <44866610+Ikkarin@users.noreply.github.com> Date: Sat, 22 Feb 2020 21:14:05 -0300 Subject: [PATCH 34/68] Oopsie --- src/p_user.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/p_user.c b/src/p_user.c index 880d932af..9417f752d 100644 --- a/src/p_user.c +++ b/src/p_user.c @@ -384,6 +384,9 @@ void P_GiveFinishFlags(player_t *player) if (!player->mo) return; + if !(netgame||multiplayer) + return; + for (i = 0; i < 3; i++) { angle_t fa = (angle >> ANGLETOFINESHIFT) & FINEMASK; From ff8a62819b6de93b32d746b6f4cb3c737b7041c2 Mon Sep 17 00:00:00 2001 From: Tatsuru <44866610+Ikkarin@users.noreply.github.com> Date: Sat, 22 Feb 2020 22:36:44 -0300 Subject: [PATCH 35/68] Oopsie 2 --- src/p_user.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/p_user.c b/src/p_user.c index 9417f752d..9167d5345 100644 --- a/src/p_user.c +++ b/src/p_user.c @@ -384,7 +384,7 @@ void P_GiveFinishFlags(player_t *player) if (!player->mo) return; - if !(netgame||multiplayer) + if (!(netgame||multiplayer)) return; for (i = 0; i < 3; i++) From 0362f53e9b9561a6cfb7408301ab80f88de9dcc8 Mon Sep 17 00:00:00 2001 From: Jaime Passos Date: Sun, 23 Feb 2020 12:14:52 -0300 Subject: [PATCH 36/68] Fix incorrect newdisplayplayer in forced ViewpointSwitch --- src/d_clisrv.c | 2 +- src/d_netcmd.c | 6 +++--- src/g_game.c | 2 +- src/p_user.c | 6 +++--- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/d_clisrv.c b/src/d_clisrv.c index f5fea366f..a4e1ba423 100644 --- a/src/d_clisrv.c +++ b/src/d_clisrv.c @@ -2513,7 +2513,7 @@ static void CL_RemovePlayer(INT32 playernum, kickreason_t reason) #ifdef HAVE_BLUA // Call ViewpointSwitch hooks here. // The viewpoint was forcibly changed. - LUAh_ViewpointSwitch(&players[consoleplayer], &players[displayplayer], true); + LUAh_ViewpointSwitch(&players[consoleplayer], &players[consoleplayer], true); #endif displayplayer = consoleplayer; } diff --git a/src/d_netcmd.c b/src/d_netcmd.c index c25929929..95cb9291f 100644 --- a/src/d_netcmd.c +++ b/src/d_netcmd.c @@ -2843,7 +2843,7 @@ static void Got_Teamchange(UINT8 **cp, INT32 playernum) // Call ViewpointSwitch hooks here. // The viewpoint was forcibly changed. if (displayplayer != consoleplayer) // You're already viewing yourself. No big deal. - LUAh_ViewpointSwitch(&players[playernum], &players[displayplayer], true); + LUAh_ViewpointSwitch(&players[consoleplayer], &players[consoleplayer], true); #endif displayplayer = consoleplayer; } @@ -3753,11 +3753,11 @@ static void ExitMove_OnChange(void) { if (players[i].mo->target && players[i].mo->target->type == MT_SIGN) P_SetTarget(&players[i].mo->target, NULL); - + if (players[i].pflags & PF_FINISHED) P_GiveFinishFlags(&players[i]); } - + CONS_Printf(M_GetText("Players can now move after completing the level.\n")); } else diff --git a/src/g_game.c b/src/g_game.c index 08192bfb8..3eeb2fa2b 100644 --- a/src/g_game.c +++ b/src/g_game.c @@ -1718,7 +1718,7 @@ void G_BuildTiccmd(ticcmd_t *cmd, INT32 realtics, UINT8 ssplayer) #ifdef HAVE_BLUA // Call ViewpointSwitch hooks here. // The viewpoint was forcibly changed. - LUAh_ViewpointSwitch(player, &players[displayplayer], true); + LUAh_ViewpointSwitch(player, &players[consoleplayer], true); #endif displayplayer = consoleplayer; } diff --git a/src/p_user.c b/src/p_user.c index 880d932af..9b0be6e53 100644 --- a/src/p_user.c +++ b/src/p_user.c @@ -392,7 +392,7 @@ void P_GiveFinishFlags(player_t *player) mobj_t* flag = P_SpawnMobjFromMobj(player->mo, xoffs, yoffs, 0, MT_FINISHFLAG); flag->angle = angle; angle += FixedAngle(120*FRACUNIT); - + P_SetTarget(&flag->target, player->mo); } } @@ -10650,7 +10650,7 @@ boolean P_SpectatorJoinGame(player_t *player) #ifdef HAVE_BLUA // Call ViewpointSwitch hooks here. // The viewpoint was forcibly changed. - LUAh_ViewpointSwitch(player, &players[displayplayer], true); + LUAh_ViewpointSwitch(player, &players[consoleplayer], true); #endif displayplayer = consoleplayer; } @@ -10699,7 +10699,7 @@ boolean P_SpectatorJoinGame(player_t *player) #ifdef HAVE_BLUA // Call ViewpointSwitch hooks here. // The viewpoint was forcibly changed. - LUAh_ViewpointSwitch(player, &players[displayplayer], true); + LUAh_ViewpointSwitch(player, &players[consoleplayer], true); #endif displayplayer = consoleplayer; } From 94738bfd2d18d79698f60b40b04c6ceaad846529 Mon Sep 17 00:00:00 2001 From: Jaime Passos Date: Sun, 23 Feb 2020 19:28:16 -0300 Subject: [PATCH 37/68] Don't get chosen as the tagger if you're a spectator --- src/p_setup.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/p_setup.c b/src/p_setup.c index 7b4c6773b..eee90e03d 100644 --- a/src/p_setup.c +++ b/src/p_setup.c @@ -3096,7 +3096,7 @@ static void P_InitTagGametype(void) //Also, you'd never have to loop through all 32 players slots to find anything ever again. for (i = 0; i < MAXPLAYERS; i++) { - if (playeringame[i] && !(players[i].spectator && players[i].quittime)) + if (playeringame[i] && !(players[i].spectator || players[i].quittime)) { playersactive[realnumplayers] = i; //stores the player's node in the array. realnumplayers++; From ce801e1076484ba85e9c8e5ae9c02a9c2b1df8b0 Mon Sep 17 00:00:00 2001 From: Steel Titanium Date: Mon, 24 Feb 2020 16:57:56 -0500 Subject: [PATCH 38/68] Fix new skin colors using default chat color --- src/hu_stuff.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/hu_stuff.c b/src/hu_stuff.c index 74b68ce69..98f3ca5a9 100644 --- a/src/hu_stuff.c +++ b/src/hu_stuff.c @@ -787,10 +787,12 @@ static void Got_Saycmd(UINT8 **p, INT32 playernum) case SKINCOLOR_RED: case SKINCOLOR_CRIMSON: case SKINCOLOR_FLAME: + case SKINCOLOR_KETCHUP: cstart = "\x85"; // V_REDMAP break; case SKINCOLOR_YOGURT: case SKINCOLOR_BROWN: + case SKINCOLOR_BRONZE: case SKINCOLOR_TAN: case SKINCOLOR_BEIGE: case SKINCOLOR_QUAIL: @@ -818,6 +820,7 @@ static void Got_Saycmd(UINT8 **p, INT32 playernum) cstart = "\x8e"; // V_ROSYMAP break; case SKINCOLOR_SUNSET: + case SKINCOLOR_COPPER: case SKINCOLOR_APRICOT: case SKINCOLOR_ORANGE: case SKINCOLOR_RUST: @@ -831,6 +834,7 @@ static void Got_Saycmd(UINT8 **p, INT32 playernum) break; case SKINCOLOR_LIME: case SKINCOLOR_PERIDOT: + case SKINCOLOR_APPLE: cstart = "\x8b"; // V_PERIDOTMAP break; case SKINCOLOR_SEAFOAM: @@ -851,12 +855,14 @@ static void Got_Saycmd(UINT8 **p, INT32 playernum) case SKINCOLOR_BLUE: case SKINCOLOR_COBALT: case SKINCOLOR_DUSK: + case SKINCOLOR_BLUEBELL: cstart = "\x84"; // V_BLUEMAP break; case SKINCOLOR_BUBBLEGUM: case SKINCOLOR_MAGENTA: case SKINCOLOR_NEON: case SKINCOLOR_VIOLET: + case SKINCOLOR_RASPBERRY: cstart = "\x81"; // V_MAGENTAMAP break; } From fe931555783c4a1d552a041faf0ae34ad0d7d532 Mon Sep 17 00:00:00 2001 From: Jaime Passos Date: Mon, 24 Feb 2020 20:03:08 -0300 Subject: [PATCH 39/68] Fix Amy cameo's love hearts crashing the Software renderer due to invalid translucency tables --- src/p_mobj.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/p_mobj.c b/src/p_mobj.c index 2bb2cc028..faee245d3 100644 --- a/src/p_mobj.c +++ b/src/p_mobj.c @@ -8001,8 +8001,9 @@ static void P_MobjSceneryThink(mobj_t *mobj) } if (mobj->fuse < 0) return; - if ((--mobj->fuse) < 6) + if (mobj->fuse < 6) mobj->frame = (mobj->frame & ~FF_TRANSMASK) | ((10 - (mobj->fuse*2)) << (FF_TRANSSHIFT)); + mobj->fuse--; } break; case MT_FINISHFLAG: @@ -11594,7 +11595,7 @@ void P_AfterPlayerSpawn(INT32 playernum) if (CheckForReverseGravity) P_CheckGravity(mobj, false); - + if (p->pflags & PF_FINISHED) P_GiveFinishFlags(p); } From 3d7cd29edaf34d7abf7a5d19f14d327fdd5c69bf Mon Sep 17 00:00:00 2001 From: Jaime Passos Date: Tue, 25 Feb 2020 22:30:34 -0300 Subject: [PATCH 40/68] Fix TYPEOFLEVEL options not having enough bits for TOL_ flags --- src/dehacked.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/dehacked.c b/src/dehacked.c index dea0289b9..329cf8788 100644 --- a/src/dehacked.c +++ b/src/dehacked.c @@ -1249,7 +1249,7 @@ static void readgametype(MYFILE *f, char *gtname) newgttol = (UINT32)i; else { - UINT16 tol = 0; + UINT32 tol = 0; tmp = strtok(word2,","); do { for (i = 0; TYPEOFLEVEL[i].name; i++) @@ -1591,7 +1591,7 @@ static void readlevelheader(MYFILE *f, INT32 num) mapheaderinfo[num-1]->typeoflevel = (UINT32)i; else { - UINT16 tol = 0; + UINT32 tol = 0; tmp = strtok(word2,","); do { for (i = 0; TYPEOFLEVEL[i].name; i++) @@ -7477,7 +7477,7 @@ static const char *const STATE_LIST[] = { // array length left dynamic for sanit // Got Flag Sign "S_GOTFLAG", - + // Finish flag "S_FINISHFLAG", From 25cf369f2261fc9b337524653332c45dfbb59b2d Mon Sep 17 00:00:00 2001 From: Jaime Passos Date: Tue, 25 Feb 2020 22:34:19 -0300 Subject: [PATCH 41/68] Fix (technically functional) string comparison counting an extra character in the comparison length --- src/dehacked.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/dehacked.c b/src/dehacked.c index 329cf8788..5d29ca280 100644 --- a/src/dehacked.c +++ b/src/dehacked.c @@ -10236,7 +10236,7 @@ static fixed_t find_const(const char **rword) free(word); return r; } - else if (fastncmp("GT_",word,4)) { + else if (fastncmp("GT_",word,3)) { r = get_gametype(word); free(word); return r; From 7f32a532332097f82ad348130b75bd8532d78c6a Mon Sep 17 00:00:00 2001 From: Jaime Passos Date: Fri, 28 Feb 2020 17:31:41 -0300 Subject: [PATCH 42/68] Fix HWR_DrawFlippedColumnInCache ignoring chroma key TF --- src/hardware/hw_cache.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/hardware/hw_cache.c b/src/hardware/hw_cache.c index ccffc8a49..059556f15 100644 --- a/src/hardware/hw_cache.c +++ b/src/hardware/hw_cache.c @@ -234,11 +234,11 @@ static void HWR_DrawFlippedColumnInCache(const column_t *patchcol, UINT8 *block, if (mipmap->colormap) texel = mipmap->colormap[texel]; - // transparent pixel - if (texel == HWR_PATCHES_CHROMAKEY_COLORINDEX) + // If the mipmap is chromakeyed, check if the texel's color + // is equivalent to the chroma key's color index. + alpha = 0xff; + if ((mipmap->flags & TF_CHROMAKEYED) && (texel == HWR_PATCHES_CHROMAKEY_COLORINDEX)) alpha = 0x00; - else - alpha = 0xff; // hope compiler will get this switch out of the loops (dreams...) // gcc do it ! but vcc not ! (why don't use cygwin gcc for win32 ?) From 4446b0d5630a9e06813e5895e1d26137e6173e12 Mon Sep 17 00:00:00 2001 From: Jaime Passos Date: Sat, 1 Feb 2020 22:25:48 -0300 Subject: [PATCH 43/68] Fix ACZ fence texture --- src/r_data.c | 36 +++++++++++++++++++++--------------- 1 file changed, 21 insertions(+), 15 deletions(-) diff --git a/src/r_data.c b/src/r_data.c index 87212c175..553e83bbc 100644 --- a/src/r_data.c +++ b/src/r_data.c @@ -230,25 +230,31 @@ static inline void R_DrawFlippedColumnInCache(column_t *patch, UINT8 *cache, tex UINT32 ASTBlendPixel(RGBA_t background, RGBA_t foreground, int style, UINT8 alpha) { RGBA_t output; + INT16 fullalpha = (alpha - (0xFF - foreground.s.alpha)); if (style == AST_TRANSLUCENT) { - if (alpha == 0) + if (fullalpha <= 0) output.rgba = background.rgba; - else if (alpha == 0xFF) - output.rgba = foreground.rgba; - else if (alpha < 0xFF) - { - UINT8 beta = (0xFF - alpha); - output.s.red = ((background.s.red * beta) + (foreground.s.red * alpha)) / 0xFF; - output.s.green = ((background.s.green * beta) + (foreground.s.green * alpha)) / 0xFF; - output.s.blue = ((background.s.blue * beta) + (foreground.s.blue * alpha)) / 0xFF; - } - // write foreground pixel alpha - // if there's no pixel in here - if (!background.rgba) - output.s.alpha = foreground.s.alpha; else - output.s.alpha = 0xFF; + { + // don't go too high + if (fullalpha >= 0xFF) + fullalpha = 0xFF; + alpha = (UINT8)fullalpha; + + // if the background pixel is empty, + // match software and don't blend anything + if (!background.s.alpha) + output.s.alpha = 0; + else + { + UINT8 beta = (0xFF - alpha); + output.s.red = ((background.s.red * beta) + (foreground.s.red * alpha)) / 0xFF; + output.s.green = ((background.s.green * beta) + (foreground.s.green * alpha)) / 0xFF; + output.s.blue = ((background.s.blue * beta) + (foreground.s.blue * alpha)) / 0xFF; + output.s.alpha = 0xFF; + } + } return output.rgba; } #define clamp(c) max(min(c, 0xFF), 0x00); From 48b352049fcf29ca7db94ff08aa0cb14c6971f8b Mon Sep 17 00:00:00 2001 From: Jaime Passos Date: Sat, 25 Jan 2020 21:37:33 -0300 Subject: [PATCH 44/68] Stop using NextTexAvail --- src/hardware/r_opengl/r_opengl.c | 45 +++++++++++--------------------- 1 file changed, 15 insertions(+), 30 deletions(-) diff --git a/src/hardware/r_opengl/r_opengl.c b/src/hardware/r_opengl/r_opengl.c index 03af059ad..97c887910 100644 --- a/src/hardware/r_opengl/r_opengl.c +++ b/src/hardware/r_opengl/r_opengl.c @@ -63,7 +63,6 @@ static float NEAR_CLIPPING_PLANE = NZCLIP_PLANE; // ************************************************************************** -static GLuint NextTexAvail = FIRST_TEX_AVAIL; static GLuint tex_downloaded = 0; static GLfloat fov = 90.0f; #if 0 @@ -72,8 +71,8 @@ static FRGBAFloat const_pal_col; #endif static FBITFIELD CurrentPolyFlags; -static FTextureInfo* gr_cachetail = NULL; -static FTextureInfo* gr_cachehead = NULL; +static FTextureInfo *gr_cachetail = NULL; +static FTextureInfo *gr_cachehead = NULL; RGBA_t myPaletteData[256]; GLint screen_width = 0; // used by Draw2DLine() @@ -243,6 +242,7 @@ FUNCPRINTF void DBG_Printf(const char *lpFmt, ...) /* 1.1 functions */ /* texture objects */ //GL_EXT_texture_object +#define pglGenTextures glGenTextures #define pglDeleteTextures glDeleteTextures #define pglBindTexture glBindTexture /* texture mapping */ //GL_EXT_copy_texture @@ -359,6 +359,8 @@ static PFNglFogfv pglFogfv; /* 1.1 functions */ /* texture objects */ //GL_EXT_texture_object +typedef void (APIENTRY * PFNglGenTextures) (GLsizei n, const GLuint *textures); +static PFNglGenTextures pglGenTextures; typedef void (APIENTRY * PFNglDeleteTextures) (GLsizei n, const GLuint *textures); static PFNglDeleteTextures pglDeleteTextures; typedef void (APIENTRY * PFNglBindTexture) (GLenum target, GLuint texture); @@ -487,6 +489,7 @@ boolean SetupGLfunc(void) GETOPENGLFUNC(pglFogf , glFogf) GETOPENGLFUNC(pglFogfv , glFogfv) + GETOPENGLFUNC(pglGenTextures , glGenTextures) GETOPENGLFUNC(pglDeleteTextures , glDeleteTextures) GETOPENGLFUNC(pglBindTexture , glBindTexture) @@ -723,33 +726,12 @@ void Flush(void) while (gr_cachehead) { - // this is not necessary at all, because you have loaded them normally, - // and so they already are in your list! -#if 0 - //Hurdler: 25/04/2000: now support colormap in hardware mode - FTextureInfo *tmp = gr_cachehead->nextskin; - - // The memory should be freed in the main code - while (tmp) - { - pglDeleteTextures(1, &tmp->downloaded); - tmp->downloaded = 0; - tmp = tmp->nextcolormap; - } -#endif - pglDeleteTextures(1, (GLuint *)&gr_cachehead->downloaded); + if (gr_cachehead->downloaded) + pglDeleteTextures(1, (GLuint *)&gr_cachehead->downloaded); gr_cachehead->downloaded = 0; gr_cachehead = gr_cachehead->nextmipmap; } gr_cachetail = gr_cachehead = NULL; //Hurdler: well, gr_cachehead is already NULL - NextTexAvail = FIRST_TEX_AVAIL; -#if 0 - if (screentexture != FIRST_TEX_AVAIL) - { - pglDeleteTextures(1, &screentexture); - screentexture = FIRST_TEX_AVAIL; - } -#endif tex_downloaded = 0; } @@ -1128,8 +1110,10 @@ EXPORT void HWRAPI(SetTexture) (FTextureInfo *pTexInfo) static RGBA_t tex[2048*2048]; const GLvoid *ptex = tex; INT32 w, h; + GLuint texnum = 0; - //DBG_Printf ("DownloadMipmap %d %x\n",NextTexAvail,pTexInfo->grInfo.data); + pglGenTextures(1, &texnum); + //DBG_Printf ("DownloadMipmap %d %x\n",(INT32)texnum,pTexInfo->grInfo.data); w = pTexInfo->width; h = pTexInfo->height; @@ -1217,9 +1201,10 @@ EXPORT void HWRAPI(SetTexture) (FTextureInfo *pTexInfo) else DBG_Printf ("SetTexture(bad format) %ld\n", pTexInfo->grInfo.format); - pTexInfo->downloaded = NextTexAvail++; - tex_downloaded = pTexInfo->downloaded; - pglBindTexture(GL_TEXTURE_2D, pTexInfo->downloaded); + // the texture number was already generated by pglGenTextures + pglBindTexture(GL_TEXTURE_2D, texnum); + pTexInfo->downloaded = texnum; + tex_downloaded = texnum; // disable texture filtering on any texture that has holes so there's no dumb borders or blending issues if (pTexInfo->flags & TF_TRANSPARENT) From 9b88edc458a5630a116b3b2bde02d20ba6c2efe6 Mon Sep 17 00:00:00 2001 From: Jaime Passos Date: Sat, 25 Jan 2020 21:46:52 -0300 Subject: [PATCH 45/68] Same deal for screen textures --- src/hardware/r_opengl/r_opengl.c | 14 ++++---------- 1 file changed, 4 insertions(+), 10 deletions(-) diff --git a/src/hardware/r_opengl/r_opengl.c b/src/hardware/r_opengl/r_opengl.c index 97c887910..9a3607e3a 100644 --- a/src/hardware/r_opengl/r_opengl.c +++ b/src/hardware/r_opengl/r_opengl.c @@ -93,16 +93,10 @@ static GLfloat modelMatrix[16]; static GLfloat projMatrix[16]; static GLint viewport[4]; -// Yay for arbitrary numbers! NextTexAvail is buggy for some reason. // Sryder: NextTexAvail is broken for these because palette changes or changes to the texture filter or antialiasing // flush all of the stored textures, leaving them unavailable at times such as between levels // These need to start at 0 and be set to their number, and be reset to 0 when deleted so that intel GPUs // can know when the textures aren't there, as textures are always considered resident in their virtual memory -// TODO: Store them in a more normal way -#define SCRTEX_SCREENTEXTURE 4294967295U -#define SCRTEX_STARTSCREENWIPE 4294967294U -#define SCRTEX_ENDSCREENWIPE 4294967293U -#define SCRTEX_FINALSCREENTEXTURE 4294967292U static GLuint screentexture = 0; static GLuint startScreenWipe = 0; static GLuint endScreenWipe = 0; @@ -2404,7 +2398,7 @@ EXPORT void HWRAPI(StartScreenWipe) (void) // Create screen texture if (firstTime) - startScreenWipe = SCRTEX_STARTSCREENWIPE; + pglGenTextures(1, &startScreenWipe); pglBindTexture(GL_TEXTURE_2D, startScreenWipe); if (firstTime) @@ -2435,7 +2429,7 @@ EXPORT void HWRAPI(EndScreenWipe)(void) // Create screen texture if (firstTime) - endScreenWipe = SCRTEX_ENDSCREENWIPE; + pglGenTextures(1, &endScreenWipe); pglBindTexture(GL_TEXTURE_2D, endScreenWipe); if (firstTime) @@ -2606,7 +2600,7 @@ EXPORT void HWRAPI(MakeScreenTexture) (void) // Create screen texture if (firstTime) - screentexture = SCRTEX_SCREENTEXTURE; + pglGenTextures(1, &screentexture); pglBindTexture(GL_TEXTURE_2D, screentexture); if (firstTime) @@ -2636,7 +2630,7 @@ EXPORT void HWRAPI(MakeScreenFinalTexture) (void) // Create screen texture if (firstTime) - finalScreenTexture = SCRTEX_FINALSCREENTEXTURE; + pglGenTextures(1, &finalScreenTexture); pglBindTexture(GL_TEXTURE_2D, finalScreenTexture); if (firstTime) From 4a489c4d95d01ead0e192b05bdd48eeb4bc6b7d3 Mon Sep 17 00:00:00 2001 From: Jaime Passos Date: Sat, 25 Jan 2020 22:20:27 -0300 Subject: [PATCH 46/68] Fix NOTEXTURE_NUM --- src/hardware/r_opengl/r_opengl.c | 20 ++++---------------- 1 file changed, 4 insertions(+), 16 deletions(-) diff --git a/src/hardware/r_opengl/r_opengl.c b/src/hardware/r_opengl/r_opengl.c index 9a3607e3a..ed60f2175 100644 --- a/src/hardware/r_opengl/r_opengl.c +++ b/src/hardware/r_opengl/r_opengl.c @@ -49,8 +49,7 @@ static const GLubyte white[4] = { 255, 255, 255, 255 }; // ========================================================================== // With OpenGL 1.1+, the first texture should be 1 -#define NOTEXTURE_NUM 1 // small white texture -#define FIRST_TEX_AVAIL (NOTEXTURE_NUM + 1) +static GLuint NOTEXTURE_NUM = 0; #define N_PI_DEMI (M_PIl/2.0f) //(1.5707963268f) @@ -524,6 +523,8 @@ static void SetNoTexture(void) // Set small white texture. if (tex_downloaded != NOTEXTURE_NUM) { + if (NOTEXTURE_NUM == 0) + pglGenTextures(1, &NOTEXTURE_NUM); pglBindTexture(GL_TEXTURE_2D, NOTEXTURE_NUM); tex_downloaded = NOTEXTURE_NUM; } @@ -638,11 +639,6 @@ void SetModelView(GLint w, GLint h) // -----------------+ void SetStates(void) { - // Bind little white RGBA texture to ID NOTEXTURE_NUM. - /* - FUINT Data[8*8]; - INT32 i; - */ #ifdef GL_LIGHT_MODEL_AMBIENT GLfloat LightDiffuse[] = {1.0f, 1.0f, 1.0f, 1.0f}; #endif @@ -676,16 +672,8 @@ void SetStates(void) CurrentPolyFlags = 0xffffffff; SetBlend(0); - /* - for (i = 0; i < 64; i++) - Data[i] = 0xffFFffFF; // white pixel - */ - - tex_downloaded = (GLuint)-1; + tex_downloaded = 0; SetNoTexture(); - //pglBindTexture(GL_TEXTURE_2D, NOTEXTURE_NUM); - //tex_downloaded = NOTEXTURE_NUM; - //pglTexImage2D(GL_TEXTURE_2D, 0, 4, 8, 8, 0, GL_RGBA, GL_UNSIGNED_BYTE, Data); pglPolygonOffset(-1.0f, -1.0f); From 94107a5320944784ab0717ce5e3dc5337e72fd10 Mon Sep 17 00:00:00 2001 From: Jaime Passos Date: Sat, 29 Feb 2020 19:36:50 -0300 Subject: [PATCH 47/68] Set pixel RGBA to nothing instead of the alpha only --- src/r_data.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/r_data.c b/src/r_data.c index 553e83bbc..3d80bbda3 100644 --- a/src/r_data.c +++ b/src/r_data.c @@ -242,10 +242,9 @@ UINT32 ASTBlendPixel(RGBA_t background, RGBA_t foreground, int style, UINT8 alph fullalpha = 0xFF; alpha = (UINT8)fullalpha; - // if the background pixel is empty, - // match software and don't blend anything + // if the background pixel is empty, match software and don't blend anything if (!background.s.alpha) - output.s.alpha = 0; + output.rgba = 0; else { UINT8 beta = (0xFF - alpha); From 70a103b9afb143141449d99fbce0a59a29e358c4 Mon Sep 17 00:00:00 2001 From: Jaime Passos Date: Mon, 2 Mar 2020 00:20:53 -0300 Subject: [PATCH 48/68] Fix G_TOLFlag returning the wrong type --- src/g_game.c | 4 ++-- src/g_game.h | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/g_game.c b/src/g_game.c index 08192bfb8..6d8826f1c 100644 --- a/src/g_game.c +++ b/src/g_game.c @@ -3592,7 +3592,7 @@ boolean G_CompetitionGametype(void) * \return The typeoflevel flag to check for that gametype. * \author Graue */ -INT16 G_TOLFlag(INT32 pgametype) +UINT32 G_TOLFlag(INT32 pgametype) { if (!multiplayer) return TOL_SP; @@ -3723,7 +3723,7 @@ static void G_DoCompleted(void) && (nextmap >= 0 && nextmap < NUMMAPS)) { register INT16 cm = nextmap; - INT16 tolflag = G_TOLFlag(gametype); + UINT32 tolflag = G_TOLFlag(gametype); UINT8 visitedmap[(NUMMAPS+7)/8]; memset(visitedmap, 0, sizeof (visitedmap)); diff --git a/src/g_game.h b/src/g_game.h index c4c40d84b..8a2c5b0ae 100644 --- a/src/g_game.h +++ b/src/g_game.h @@ -309,6 +309,6 @@ FUNCMATH INT32 G_TicsToCentiseconds(tic_t tics); FUNCMATH INT32 G_TicsToMilliseconds(tic_t tics); // Don't split up TOL handling -INT16 G_TOLFlag(INT32 pgametype); +UINT32 G_TOLFlag(INT32 pgametype); #endif From c671097c04e784d4a0e1009ea766cde8af492c51 Mon Sep 17 00:00:00 2001 From: sphere Date: Mon, 2 Mar 2020 13:23:54 +0100 Subject: [PATCH 49/68] Actually use the "SCREENSHOT OPTIONS" header for its menu. --- 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 403d3a036..fd710ff10 100644 --- a/src/m_menu.c +++ b/src/m_menu.c @@ -2179,7 +2179,7 @@ menu_t OP_DataOptionsDef = DEFAULTMENUSTYLE( menu_t OP_ScreenshotOptionsDef = { MN_OP_MAIN + (MN_OP_DATA << 6) + (MN_OP_SCREENSHOTS << 12), - "M_DATA", + "M_SCREEN", sizeof (OP_ScreenshotOptionsMenu)/sizeof (menuitem_t), &OP_DataOptionsDef, OP_ScreenshotOptionsMenu, From 7ba93c184f15ef1212dbc45c69354dbc278293a2 Mon Sep 17 00:00:00 2001 From: Louis-Antoine Date: Mon, 2 Mar 2020 13:33:12 +0100 Subject: [PATCH 50/68] Fix warning and formatting --- src/blua/liolib.c | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/src/blua/liolib.c b/src/blua/liolib.c index 378b8c86d..b43052194 100644 --- a/src/blua/liolib.c +++ b/src/blua/liolib.c @@ -34,7 +34,8 @@ #define FMT_FILECALLBACKID "file_callback_%d" -static const char *whitelist[] = { // Allow scripters to write files of these types to SRB2's folder +// Allow scripters to write files of these types to SRB2's folder +static const char *whitelist[] = { ".bmp", ".cfg", ".csv", @@ -508,7 +509,6 @@ static int io_readline (lua_State *L) { static int g_write (lua_State *L, FILE *f, int arg) { int nargs = lua_gettop(L) - 1; int status = 1; - size_t count; for (; nargs--; arg++) { if (lua_type(L, arg) == LUA_TNUMBER) { /* optimization: could be done exactly as for strings */ @@ -518,12 +518,10 @@ static int g_write (lua_State *L, FILE *f, int arg) { else { size_t l; const char *s = luaL_checklstring(L, arg, &l); - count += l; - if (ftell(f) + l > FILELIMIT) - { - luaL_error(L,"write limit bypassed in file. Changes have been discarded."); - break; - } + if (ftell(f) + l > FILELIMIT) { + luaL_error(L,"write limit bypassed in file. Changes have been discarded."); + break; + } status = status && (fwrite(s, sizeof(char), l, f) == l); } } From 04a1f479be653e8def68ead20e489ab735bd0ac6 Mon Sep 17 00:00:00 2001 From: Louis-Antoine Date: Mon, 2 Mar 2020 15:36:28 +0100 Subject: [PATCH 51/68] Increase the length of the player input buffer --- src/d_clisrv.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/d_clisrv.h b/src/d_clisrv.h index 42da89670..7cdff8ef0 100644 --- a/src/d_clisrv.h +++ b/src/d_clisrv.h @@ -36,7 +36,7 @@ basic fields of the packet, and change infrequently. // be transmitted. // Networking and tick handling related. -#define BACKUPTICS 32 +#define BACKUPTICS 96 #define MAXTEXTCMD 256 // // Packet structure From b8db3e2216df8379344300e5979945e5c6e4d37e Mon Sep 17 00:00:00 2001 From: Jaime Passos Date: Wed, 4 Mar 2020 09:42:34 -0300 Subject: [PATCH 52/68] Fix NiGHTS Attack crash after render mode change --- src/m_menu.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/m_menu.c b/src/m_menu.c index 72e2d57e0..e0becdff1 100644 --- a/src/m_menu.c +++ b/src/m_menu.c @@ -5740,6 +5740,8 @@ static void M_DrawNightsAttackSuperSonic(void) const UINT8 *colormap = R_GetTranslationColormap(TC_DEFAULT, SKINCOLOR_YELLOW, GTC_CACHE); INT32 timer = (ntsatkdrawtimer/4) % 2; angle_t fa = (FixedAngle(((ntsatkdrawtimer * 4) % 360)<>ANGLETOFINESHIFT) & FINEMASK; + ntssupersonic[0] = W_CachePatchName("NTSSONC1", PU_PATCH); + ntssupersonic[1] = W_CachePatchName("NTSSONC2", PU_PATCH); V_DrawFixedPatch(235< Date: Wed, 4 Mar 2020 17:31:52 +0100 Subject: [PATCH 53/68] Fix division-by-0 crash with gamepad deadzones Fix division-by-0 crash with gamepad deadzones The problem was that it checked if A was more than B, then lowered A to a max value, then subtracted B from A, then divided something by that, without checking if A minus B was 0, allowing division by 0 if B was the same as that max value This fixes that by making sure that A is less than the max value --- src/g_game.c | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/src/g_game.c b/src/g_game.c index 08192bfb8..4db55329e 100644 --- a/src/g_game.c +++ b/src/g_game.c @@ -1045,17 +1045,14 @@ static INT32 G_BasicDeadZoneCalculation(INT32 magnitude, fixed_t deadZone) const INT32 jdeadzone = (JOYAXISRANGE * deadZone) / FRACUNIT; INT32 deadzoneAppliedValue = 0; - if (jdeadzone > 0) + if (jdeadzone > 0 && magnitude > jdeadzone && deadZone < FRACUNIT) { - if (magnitude > jdeadzone) - { - INT32 adjustedMagnitude = abs(magnitude); - adjustedMagnitude = min(adjustedMagnitude, JOYAXISRANGE); + INT32 adjustedMagnitude = abs(magnitude); + adjustedMagnitude = min(adjustedMagnitude, JOYAXISRANGE); - adjustedMagnitude -= jdeadzone; + adjustedMagnitude -= jdeadzone; - deadzoneAppliedValue = (adjustedMagnitude * JOYAXISRANGE) / (JOYAXISRANGE - jdeadzone); - } + deadzoneAppliedValue = (adjustedMagnitude * JOYAXISRANGE) / (JOYAXISRANGE - jdeadzone); } return deadzoneAppliedValue; From df220aa2c2d209f7b253e958abc2cf0d63a8bca9 Mon Sep 17 00:00:00 2001 From: Zwip-Zwap Zapony Date: Wed, 4 Mar 2020 21:11:55 +0100 Subject: [PATCH 54/68] Hotfix for 100% deadzone returning 0 input It makes more sense for 100% deadzone to just make it so that you have to push the axis all the way to trigger it, rather than 100% deadzone resulting in no axis input ever happening... So, let's make it be the former way instead --- src/g_game.c | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/src/g_game.c b/src/g_game.c index 4db55329e..2cddaf8b8 100644 --- a/src/g_game.c +++ b/src/g_game.c @@ -1045,14 +1045,19 @@ static INT32 G_BasicDeadZoneCalculation(INT32 magnitude, fixed_t deadZone) const INT32 jdeadzone = (JOYAXISRANGE * deadZone) / FRACUNIT; INT32 deadzoneAppliedValue = 0; - if (jdeadzone > 0 && magnitude > jdeadzone && deadZone < FRACUNIT) + if (jdeadzone > 0 && magnitude > jdeadzone) { - INT32 adjustedMagnitude = abs(magnitude); - adjustedMagnitude = min(adjustedMagnitude, JOYAXISRANGE); + if (deadZone >= FRACUNIT) // If the deadzone value is at 100%... + return JOYAXISRANGE; // ...return 100% input directly, to avoid dividing by 0 + else + { + INT32 adjustedMagnitude = abs(magnitude); + adjustedMagnitude = min(adjustedMagnitude, JOYAXISRANGE); - adjustedMagnitude -= jdeadzone; + adjustedMagnitude -= jdeadzone; - deadzoneAppliedValue = (adjustedMagnitude * JOYAXISRANGE) / (JOYAXISRANGE - jdeadzone); + deadzoneAppliedValue = (adjustedMagnitude * JOYAXISRANGE) / (JOYAXISRANGE - jdeadzone); + } } return deadzoneAppliedValue; From bce6349b1d09e24fd6a4deada395bc628452d38f Mon Sep 17 00:00:00 2001 From: Zwip-Zwap Zapony Date: Wed, 4 Mar 2020 21:44:57 +0100 Subject: [PATCH 55/68] More gamepad deadzone tweakage --- src/g_game.c | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-) diff --git a/src/g_game.c b/src/g_game.c index 2cddaf8b8..818cb38c2 100644 --- a/src/g_game.c +++ b/src/g_game.c @@ -1044,20 +1044,17 @@ static INT32 G_BasicDeadZoneCalculation(INT32 magnitude, fixed_t deadZone) { const INT32 jdeadzone = (JOYAXISRANGE * deadZone) / FRACUNIT; INT32 deadzoneAppliedValue = 0; + INT32 adjustedMagnitude = abs(magnitude); - if (jdeadzone > 0 && magnitude > jdeadzone) + if (jdeadzone >= JOYAXISRANGE && adjustedMagnitude >= JOYAXISRANGE) // If the deadzone and magnitude are both 100%... + return JOYAXISRANGE; // ...return 100% input directly, to avoid dividing by 0 + else if (adjustedMagnitude > jdeadzone) // Otherwise, calculate how much the magnitude exceeds the deadzone { - if (deadZone >= FRACUNIT) // If the deadzone value is at 100%... - return JOYAXISRANGE; // ...return 100% input directly, to avoid dividing by 0 - else - { - INT32 adjustedMagnitude = abs(magnitude); - adjustedMagnitude = min(adjustedMagnitude, JOYAXISRANGE); + adjustedMagnitude = min(adjustedMagnitude, JOYAXISRANGE); - adjustedMagnitude -= jdeadzone; + adjustedMagnitude -= jdeadzone; - deadzoneAppliedValue = (adjustedMagnitude * JOYAXISRANGE) / (JOYAXISRANGE - jdeadzone); - } + deadzoneAppliedValue = (adjustedMagnitude * JOYAXISRANGE) / (JOYAXISRANGE - jdeadzone); } return deadzoneAppliedValue; From da9bf34175f6eb9bc0affaf5ad1a314f58f23abd Mon Sep 17 00:00:00 2001 From: ZipperQR Date: Thu, 5 Mar 2020 12:24:45 +0300 Subject: [PATCH 56/68] fix source player being nil --- src/p_inter.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/p_inter.c b/src/p_inter.c index a24f9bf03..c2d4c695e 100644 --- a/src/p_inter.c +++ b/src/p_inter.c @@ -3441,7 +3441,7 @@ void P_SpecialStageDamage(player_t *player, mobj_t *inflictor, mobj_t *source) if (player->powers[pw_invulnerability] || player->powers[pw_flashing] || player->powers[pw_super]) return; - if (!cv_friendlyfire.value) + if (!cv_friendlyfire.value && source->player) { if (inflictor->type == MT_LHRT && !(player->powers[pw_shield] & SH_NOSTACK)) { From 4d8a523da92654f80d40898efa6e81c34e2f2110 Mon Sep 17 00:00:00 2001 From: Zipper Date: Thu, 5 Mar 2020 18:30:27 -0500 Subject: [PATCH 57/68] Update p_inter.c --- src/p_inter.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/p_inter.c b/src/p_inter.c index c2d4c695e..858718f84 100644 --- a/src/p_inter.c +++ b/src/p_inter.c @@ -3441,7 +3441,7 @@ void P_SpecialStageDamage(player_t *player, mobj_t *inflictor, mobj_t *source) if (player->powers[pw_invulnerability] || player->powers[pw_flashing] || player->powers[pw_super]) return; - if (!cv_friendlyfire.value && source->player) + if (!cv_friendlyfire.value && source && source->player) { if (inflictor->type == MT_LHRT && !(player->powers[pw_shield] & SH_NOSTACK)) { From 4b28e19d16db1ece7bdeffef981513ef0b30d360 Mon Sep 17 00:00:00 2001 From: Zipper Date: Thu, 5 Mar 2020 18:48:20 -0500 Subject: [PATCH 58/68] Update p_inter.c --- src/p_inter.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/p_inter.c b/src/p_inter.c index 858718f84..95f8fba41 100644 --- a/src/p_inter.c +++ b/src/p_inter.c @@ -3456,7 +3456,7 @@ void P_SpecialStageDamage(player_t *player, mobj_t *inflictor, mobj_t *source) return; } - if (inflictor->type == MT_LHRT) + if (inflictor && inflictor->type == MT_LHRT) return; if (player->powers[pw_shield] || player->bot) //If One-Hit Shield From a3f3dd649f5a177d4e42bc4f443a3a127574dd45 Mon Sep 17 00:00:00 2001 From: Steel Titanium Date: Sun, 8 Mar 2020 00:02:43 -0500 Subject: [PATCH 59/68] Fix guest deletion confirmation returning to Record Attack menu from Nights Mode menu --- src/m_menu.c | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/m_menu.c b/src/m_menu.c index 72e2d57e0..712fd2cd7 100644 --- a/src/m_menu.c +++ b/src/m_menu.c @@ -10103,10 +10103,7 @@ static void M_EraseGuest(INT32 choice) (void)choice; if (FIL_FileExists(rguest)) remove(rguest); - if (currentMenu == &SP_NightsGuestReplayDef) - M_SetupNextMenu(&SP_NightsAttackDef); - else - M_SetupNextMenu(&SP_TimeAttackDef); + M_SetupNextMenu(currentMenu->prevMenu->prevMenu); Nextmap_OnChange(); M_StartMessage(M_GetText("Guest replay data erased.\n"),NULL,MM_NOTHING); } From e5270ad9c71a79b0bc95561b2d8cd55306d3d5f7 Mon Sep 17 00:00:00 2001 From: Steel Titanium Date: Sun, 8 Mar 2020 00:34:15 -0500 Subject: [PATCH 60/68] Fix guest data being deleted regardless of choice --- src/m_menu.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/m_menu.c b/src/m_menu.c index 712fd2cd7..75774c74c 100644 --- a/src/m_menu.c +++ b/src/m_menu.c @@ -10100,9 +10100,12 @@ static void M_ReplayTimeAttack(INT32 choice) static void M_EraseGuest(INT32 choice) { const char *rguest = va("%s"PATHSEP"replay"PATHSEP"%s"PATHSEP"%s-guest.lmp", srb2home, timeattackfolder, G_BuildMapName(cv_nextmap.value)); - (void)choice; - if (FIL_FileExists(rguest)) - remove(rguest); + + if (choice == 'y' || choice == KEY_ENTER) + { + if (FIL_FileExists(rguest)) + remove(rguest); + } M_SetupNextMenu(currentMenu->prevMenu->prevMenu); Nextmap_OnChange(); M_StartMessage(M_GetText("Guest replay data erased.\n"),NULL,MM_NOTHING); From ce29129f3a67eab7c88af8e2360be48333ce0d16 Mon Sep 17 00:00:00 2001 From: colette Date: Sun, 8 Mar 2020 13:09:39 -0400 Subject: [PATCH 61/68] Fix resyncs never recovering if node and player mismatch --- 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 f5fea366f..71e32977a 100644 --- a/src/d_clisrv.c +++ b/src/d_clisrv.c @@ -3966,7 +3966,7 @@ static void HandlePacketFromPlayer(SINT8 node) case PT_RESYNCHGET: if (client) break; - SV_AcknowledgeResynchAck(netconsole, netbuffer->u.resynchgot); + SV_AcknowledgeResynchAck(node, netbuffer->u.resynchgot); break; case PT_CLIENTCMD: case PT_CLIENT2CMD: From a67dbfff549ec6d9e330d9c83b4df8cce2d327f9 Mon Sep 17 00:00:00 2001 From: Monster Iestyn Date: Sun, 8 Mar 2020 19:32:07 +0000 Subject: [PATCH 62/68] Create r_skins.c/.h, to contain skins code formerly in r_things.c/h --- src/r_skins.c | 825 +++++++++++++++++++++++++++++++++++++++++++++++++ src/r_skins.h | 102 ++++++ src/r_things.c | 799 +---------------------------------------------- src/r_things.h | 81 +---- 4 files changed, 936 insertions(+), 871 deletions(-) create mode 100644 src/r_skins.c create mode 100644 src/r_skins.h diff --git a/src/r_skins.c b/src/r_skins.c new file mode 100644 index 000000000..48764ff75 --- /dev/null +++ b/src/r_skins.c @@ -0,0 +1,825 @@ +// SONIC ROBO BLAST 2 +//----------------------------------------------------------------------------- +// Copyright (C) 1993-1996 by id Software, Inc. +// Copyright (C) 1998-2000 by DooM Legacy Team. +// Copyright (C) 1999-2020 by Sonic Team Junior. +// +// This program is free software distributed under the +// terms of the GNU General Public License, version 2. +// See the 'LICENSE' file for more details. +//----------------------------------------------------------------------------- +/// \file r_skins.c +/// \brief Loading skins + +#include "doomdef.h" +#include "console.h" +#include "g_game.h" +#include "r_local.h" +#include "st_stuff.h" +#include "w_wad.h" +#include "z_zone.h" +#include "m_misc.h" +#include "info.h" // spr2names +#include "i_video.h" // rendermode +#include "i_system.h" +#include "r_things.h" +#include "r_skins.h" +#include "p_local.h" +#include "dehacked.h" // get_number (for thok) +#include "m_cond.h" +#ifdef HWRENDER +#include "hardware/hw_md2.h" +#endif + +#ifdef PC_DOS +#include // for snprintf +int snprintf(char *str, size_t n, const char *fmt, ...); +//int vsnprintf(char *str, size_t n, const char *fmt, va_list ap); +#endif + +INT32 numskins = 0; +skin_t skins[MAXSKINS]; + +// FIXTHIS: don't work because it must be inistilised before the config load +//#define SKINVALUES +#ifdef SKINVALUES +CV_PossibleValue_t skin_cons_t[MAXSKINS+1]; +#endif + +// +// P_GetSkinSprite2 +// For non-super players, tries each sprite2's immediate predecessor until it finds one with a number of frames or ends up at standing. +// For super players, does the same as above - but tries the super equivalent for each sprite2 before the non-super version. +// + +UINT8 P_GetSkinSprite2(skin_t *skin, UINT8 spr2, player_t *player) +{ + UINT8 super = 0, i = 0; + + if (!skin) + return 0; + + if ((playersprite_t)(spr2 & ~FF_SPR2SUPER) >= free_spr2) + return 0; + + while (!skin->sprites[spr2].numframes + && spr2 != SPR2_STND + && ++i < 32) // recursion limiter + { + if (spr2 & FF_SPR2SUPER) + { + super = FF_SPR2SUPER; + spr2 &= ~FF_SPR2SUPER; + continue; + } + + switch(spr2) + { + // Normal special cases. + case SPR2_JUMP: + spr2 = ((player + ? player->charflags + : skin->flags) + & SF_NOJUMPSPIN) ? SPR2_SPNG : SPR2_ROLL; + break; + case SPR2_TIRE: + spr2 = ((player + ? player->charability + : skin->ability) + == CA_SWIM) ? SPR2_SWIM : SPR2_FLY; + break; + // Use the handy list, that's what it's there for! + default: + spr2 = spr2defaults[spr2]; + break; + } + + spr2 |= super; + } + + if (i >= 32) // probably an infinite loop... + return 0; + + return spr2; +} + +static void Sk_SetDefaultValue(skin_t *skin) +{ + INT32 i; + // + // set default skin values + // + memset(skin, 0, sizeof (skin_t)); + snprintf(skin->name, + sizeof skin->name, "skin %u", (UINT32)(skin-skins)); + skin->name[sizeof skin->name - 1] = '\0'; + skin->wadnum = INT16_MAX; + + skin->flags = 0; + + strcpy(skin->realname, "Someone"); + strcpy(skin->hudname, "???"); + + skin->starttranscolor = 96; + skin->prefcolor = SKINCOLOR_GREEN; + skin->supercolor = SKINCOLOR_SUPERGOLD1; + skin->prefoppositecolor = 0; // use tables + + skin->normalspeed = 36<runspeed = 28<thrustfactor = 5; + skin->accelstart = 96; + skin->acceleration = 40; + + skin->ability = CA_NONE; + skin->ability2 = CA2_SPINDASH; + skin->jumpfactor = FRACUNIT; + skin->actionspd = 30<mindash = 15<maxdash = 70<radius = mobjinfo[MT_PLAYER].radius; + skin->height = mobjinfo[MT_PLAYER].height; + skin->spinheight = FixedMul(skin->height, 2*FRACUNIT/3); + + skin->shieldscale = FRACUNIT; + skin->camerascale = FRACUNIT; + + skin->thokitem = -1; + skin->spinitem = -1; + skin->revitem = -1; + skin->followitem = 0; + + skin->highresscale = FRACUNIT; + skin->contspeed = 17; + skin->contangle = 0; + + skin->availability = 0; + + for (i = 0; i < sfx_skinsoundslot0; i++) + if (S_sfx[i].skinsound != -1) + skin->soundsid[S_sfx[i].skinsound] = i; +} + +// +// Initialize the basic skins +// +void R_InitSkins(void) +{ +#ifdef SKINVALUES + INT32 i; + + for (i = 0; i <= MAXSKINS; i++) + { + skin_cons_t[i].value = 0; + skin_cons_t[i].strvalue = NULL; + } +#endif + + // no default skin! + numskins = 0; +} + +UINT32 R_GetSkinAvailabilities(void) +{ + INT32 s; + UINT32 response = 0; + + for (s = 0; s < MAXSKINS; s++) + { + if (skins[s].availability && unlockables[skins[s].availability - 1].unlocked) + response |= (1 << s); + } + return response; +} + +// returns true if available in circumstances, otherwise nope +// warning don't use with an invalid skinnum other than -1 which always returns true +boolean R_SkinUsable(INT32 playernum, INT32 skinnum) +{ + return ((skinnum == -1) // Simplifies things elsewhere, since there's already plenty of checks for less-than-0... + || (!skins[skinnum].availability) + || (((netgame || multiplayer) && playernum != -1) ? (players[playernum].availabilities & (1 << skinnum)) : (unlockables[skins[skinnum].availability - 1].unlocked)) + || (modeattacking) // If you have someone else's run you might as well take a look + || (Playing() && (R_SkinAvailable(mapheaderinfo[gamemap-1]->forcecharacter) == skinnum)) // Force 1. + || (netgame && (cv_forceskin.value == skinnum)) // Force 2. + || (metalrecording && skinnum == 5) // Force 3. + ); +} + +// returns true if the skin name is found (loaded from pwad) +// warning return -1 if not found +INT32 R_SkinAvailable(const char *name) +{ + INT32 i; + + for (i = 0; i < numskins; i++) + { + // search in the skin list + if (stricmp(skins[i].name,name)==0) + return i; + } + return -1; +} + +// network code calls this when a 'skin change' is received +void SetPlayerSkin(INT32 playernum, const char *skinname) +{ + INT32 i = R_SkinAvailable(skinname); + player_t *player = &players[playernum]; + + if ((i != -1) && R_SkinUsable(playernum, i)) + { + SetPlayerSkinByNum(playernum, i); + return; + } + + if (P_IsLocalPlayer(player)) + CONS_Alert(CONS_WARNING, M_GetText("Skin '%s' not found.\n"), skinname); + else if(server || IsPlayerAdmin(consoleplayer)) + CONS_Alert(CONS_WARNING, M_GetText("Player %d (%s) skin '%s' not found\n"), playernum, player_names[playernum], skinname); + + SetPlayerSkinByNum(playernum, 0); +} + +// Same as SetPlayerSkin, but uses the skin #. +// network code calls this when a 'skin change' is received +void SetPlayerSkinByNum(INT32 playernum, INT32 skinnum) +{ + player_t *player = &players[playernum]; + skin_t *skin = &skins[skinnum]; + UINT8 newcolor = 0; + + if (skinnum >= 0 && skinnum < numskins && R_SkinUsable(playernum, skinnum)) // Make sure it exists! + { + player->skin = skinnum; + + player->camerascale = skin->camerascale; + player->shieldscale = skin->shieldscale; + + player->charability = (UINT8)skin->ability; + player->charability2 = (UINT8)skin->ability2; + + player->charflags = (UINT32)skin->flags; + + player->thokitem = skin->thokitem < 0 ? (UINT32)mobjinfo[MT_PLAYER].painchance : (UINT32)skin->thokitem; + player->spinitem = skin->spinitem < 0 ? (UINT32)mobjinfo[MT_PLAYER].damage : (UINT32)skin->spinitem; + player->revitem = skin->revitem < 0 ? (mobjtype_t)mobjinfo[MT_PLAYER].raisestate : (UINT32)skin->revitem; + player->followitem = skin->followitem; + + if (((player->powers[pw_shield] & SH_NOSTACK) == SH_PINK) && (player->revitem == MT_LHRT || player->spinitem == MT_LHRT || player->thokitem == MT_LHRT)) // Healers can't keep their buff. + player->powers[pw_shield] &= SH_STACK; + + player->actionspd = skin->actionspd; + player->mindash = skin->mindash; + player->maxdash = skin->maxdash; + + player->normalspeed = skin->normalspeed; + player->runspeed = skin->runspeed; + player->thrustfactor = skin->thrustfactor; + player->accelstart = skin->accelstart; + player->acceleration = skin->acceleration; + + player->jumpfactor = skin->jumpfactor; + + player->height = skin->height; + player->spinheight = skin->spinheight; + + if (!(cv_debug || devparm) && !(netgame || multiplayer || demoplayback)) + { + if (playernum == consoleplayer) + CV_StealthSetValue(&cv_playercolor, skin->prefcolor); + else if (playernum == secondarydisplayplayer) + CV_StealthSetValue(&cv_playercolor2, skin->prefcolor); + player->skincolor = newcolor = skin->prefcolor; + } + + if (player->followmobj) + { + P_RemoveMobj(player->followmobj); + P_SetTarget(&player->followmobj, NULL); + } + + if (player->mo) + { + fixed_t radius = FixedMul(skin->radius, player->mo->scale); + if ((player->powers[pw_carry] == CR_NIGHTSMODE) && (skin->sprites[SPR2_NFLY].numframes == 0)) // If you don't have a sprite for flying horizontally, use the default NiGHTS skin. + { + skin = &skins[DEFAULTNIGHTSSKIN]; + player->followitem = skin->followitem; + if (!(cv_debug || devparm) && !(netgame || multiplayer || demoplayback)) + newcolor = skin->prefcolor; // will be updated in thinker to flashing + } + player->mo->skin = skin; + if (newcolor) + player->mo->color = newcolor; + P_SetScale(player->mo, player->mo->scale); + player->mo->radius = radius; + + P_SetPlayerMobjState(player->mo, player->mo->state-states); // Prevent visual errors when switching between skins with differing number of frames + } + return; + } + + if (P_IsLocalPlayer(player)) + CONS_Alert(CONS_WARNING, M_GetText("Requested skin %d not found\n"), skinnum); + else if(server || IsPlayerAdmin(consoleplayer)) + CONS_Alert(CONS_WARNING, "Player %d (%s) skin %d not found\n", playernum, player_names[playernum], skinnum); + SetPlayerSkinByNum(playernum, 0); // not found put the sonic skin +} + +// +// Add skins from a pwad, each skin preceded by 'S_SKIN' marker +// + +// Does the same is in w_wad, but check only for +// the first 6 characters (this is so we can have S_SKIN1, S_SKIN2.. +// for wad editors that don't like multiple resources of the same name) +// +static UINT16 W_CheckForSkinMarkerInPwad(UINT16 wadid, UINT16 startlump) +{ + UINT16 i; + const char *S_SKIN = "S_SKIN"; + lumpinfo_t *lump_p; + + // scan forward, start at + if (startlump < wadfiles[wadid]->numlumps) + { + lump_p = wadfiles[wadid]->lumpinfo + startlump; + for (i = startlump; i < wadfiles[wadid]->numlumps; i++, lump_p++) + if (memcmp(lump_p->name,S_SKIN,6)==0) + return i; + } + return INT16_MAX; // not found +} + +#define HUDNAMEWRITE(value) STRBUFCPY(skin->hudname, value) + +// turn _ into spaces and . into katana dot +#define SYMBOLCONVERT(name) for (value = name; *value; value++)\ + {\ + if (*value == '_') *value = ' ';\ + else if (*value == '.') *value = '\x1E';\ + } + +// +// Patch skins from a pwad, each skin preceded by 'P_SKIN' marker +// + +// Does the same is in w_wad, but check only for +// the first 6 characters (this is so we can have P_SKIN1, P_SKIN2.. +// for wad editors that don't like multiple resources of the same name) +// +static UINT16 W_CheckForPatchSkinMarkerInPwad(UINT16 wadid, UINT16 startlump) +{ + UINT16 i; + const char *P_SKIN = "P_SKIN"; + lumpinfo_t *lump_p; + + // scan forward, start at + if (startlump < wadfiles[wadid]->numlumps) + { + lump_p = wadfiles[wadid]->lumpinfo + startlump; + for (i = startlump; i < wadfiles[wadid]->numlumps; i++, lump_p++) + if (memcmp(lump_p->name,P_SKIN,6)==0) + return i; + } + return INT16_MAX; // not found +} + +static void R_LoadSkinSprites(UINT16 wadnum, UINT16 *lump, UINT16 *lastlump, skin_t *skin) +{ + UINT16 newlastlump; + UINT8 sprite2; + + *lump += 1; // start after S_SKIN + *lastlump = W_CheckNumForNamePwad("S_END",wadnum,*lump); // stop at S_END + + // old wadding practices die hard -- stop at S_SKIN (or P_SKIN) or S_START if they come before S_END. + newlastlump = W_CheckForSkinMarkerInPwad(wadnum,*lump); + if (newlastlump < *lastlump) *lastlump = newlastlump; + newlastlump = W_CheckForPatchSkinMarkerInPwad(wadnum,*lump); + if (newlastlump < *lastlump) *lastlump = newlastlump; + newlastlump = W_CheckNumForNamePwad("S_START",wadnum,*lump); + if (newlastlump < *lastlump) *lastlump = newlastlump; + + // ...and let's handle super, too + newlastlump = W_CheckNumForNamePwad("S_SUPER",wadnum,*lump); + if (newlastlump < *lastlump) + { + newlastlump++; + // load all sprite sets we are aware of... for super! + for (sprite2 = 0; sprite2 < free_spr2; sprite2++) + R_AddSingleSpriteDef((spritename = spr2names[sprite2]), &skin->sprites[FF_SPR2SUPER|sprite2], wadnum, newlastlump, *lastlump); + + newlastlump--; + *lastlump = newlastlump; // okay, make the normal sprite set loading end there + } + + // load all sprite sets we are aware of... for normal stuff. + for (sprite2 = 0; sprite2 < free_spr2; sprite2++) + R_AddSingleSpriteDef((spritename = spr2names[sprite2]), &skin->sprites[sprite2], wadnum, *lump, *lastlump); + + if (skin->sprites[0].numframes == 0) + I_Error("R_LoadSkinSprites: no frames found for sprite SPR2_%s\n", spr2names[0]); +} + +// returns whether found appropriate property +static boolean R_ProcessPatchableFields(skin_t *skin, char *stoken, char *value) +{ + // custom translation table + if (!stricmp(stoken, "startcolor")) + skin->starttranscolor = atoi(value); + +#define FULLPROCESS(field) else if (!stricmp(stoken, #field)) skin->field = get_number(value); + // character type identification + FULLPROCESS(flags) + FULLPROCESS(ability) + FULLPROCESS(ability2) + + FULLPROCESS(thokitem) + FULLPROCESS(spinitem) + FULLPROCESS(revitem) + FULLPROCESS(followitem) +#undef FULLPROCESS + +#define GETFRACBITS(field) else if (!stricmp(stoken, #field)) skin->field = atoi(value)<field = atoi(value); + GETINT(thrustfactor) + GETINT(accelstart) + GETINT(acceleration) + GETINT(contspeed) + GETINT(contangle) +#undef GETINT + +#define GETSKINCOLOR(field) else if (!stricmp(stoken, #field)) skin->field = R_GetColorByName(value); + GETSKINCOLOR(prefcolor) + GETSKINCOLOR(prefoppositecolor) +#undef GETSKINCOLOR + else if (!stricmp(stoken, "supercolor")) + skin->supercolor = R_GetSuperColorByName(value); + +#define GETFLOAT(field) else if (!stricmp(stoken, #field)) skin->field = FLOAT_TO_FIXED(atof(value)); + GETFLOAT(jumpfactor) + GETFLOAT(highresscale) + GETFLOAT(shieldscale) + GETFLOAT(camerascale) +#undef GETFLOAT + +#define GETFLAG(field) else if (!stricmp(stoken, #field)) { \ + strupr(value); \ + if (atoi(value) || value[0] == 'T' || value[0] == 'Y') \ + skin->flags |= (SF_##field); \ + else \ + skin->flags &= ~(SF_##field); \ +} + // parameters for individual character flags + // these are uppercase so they can be concatenated with SF_ + // 1, true, yes are all valid values + GETFLAG(SUPER) + GETFLAG(NOSUPERSPIN) + GETFLAG(NOSPINDASHDUST) + GETFLAG(HIRES) + GETFLAG(NOSKID) + GETFLAG(NOSPEEDADJUST) + GETFLAG(RUNONWATER) + GETFLAG(NOJUMPSPIN) + GETFLAG(NOJUMPDAMAGE) + GETFLAG(STOMPDAMAGE) + GETFLAG(MARIODAMAGE) + GETFLAG(MACHINE) + GETFLAG(DASHMODE) + GETFLAG(FASTEDGE) + GETFLAG(MULTIABILITY) + GETFLAG(NONIGHTSROTATION) +#undef GETFLAG + + else // let's check if it's a sound, otherwise error out + { + boolean found = false; + sfxenum_t i; + size_t stokenadjust; + + // Remove the prefix. (We need to affect an adjusting variable so that we can print error messages if it's not actually a sound.) + if ((stoken[0] == 'D' || stoken[0] == 'd') && (stoken[1] == 'S' || stoken[1] == 's')) // DS* + stokenadjust = 2; + else // sfx_* + stokenadjust = 4; + + // Remove the prefix. (We can affect this directly since we're not going to use it again.) + if ((value[0] == 'D' || value[0] == 'd') && (value[1] == 'S' || value[1] == 's')) // DS* + value += 2; + else // sfx_* + value += 4; + + // copy name of sounds that are remapped + // for this skin + for (i = 0; i < sfx_skinsoundslot0; i++) + { + if (!S_sfx[i].name) + continue; + if (S_sfx[i].skinsound != -1 + && !stricmp(S_sfx[i].name, + stoken + stokenadjust)) + { + skin->soundsid[S_sfx[i].skinsound] = + S_AddSoundFx(value, S_sfx[i].singularity, S_sfx[i].pitch, true); + found = true; + } + } + return found; + } + return true; +} + +// +// Find skin sprites, sounds & optional status bar face, & add them +// +void R_AddSkins(UINT16 wadnum) +{ + UINT16 lump, lastlump = 0; + char *buf; + char *buf2; + char *stoken; + char *value; + size_t size; + skin_t *skin; + boolean hudname, realname; + + // + // search for all skin markers in pwad + // + + while ((lump = W_CheckForSkinMarkerInPwad(wadnum, lastlump)) != INT16_MAX) + { + // advance by default + lastlump = lump + 1; + + if (numskins >= MAXSKINS) + { + CONS_Debug(DBG_RENDER, "ignored skin (%d skins maximum)\n", MAXSKINS); + continue; // so we know how many skins couldn't be added + } + buf = W_CacheLumpNumPwad(wadnum, lump, PU_CACHE); + size = W_LumpLengthPwad(wadnum, lump); + + // for strtok + buf2 = malloc(size+1); + if (!buf2) + I_Error("R_AddSkins: No more free memory\n"); + M_Memcpy(buf2,buf,size); + buf2[size] = '\0'; + + // set defaults + skin = &skins[numskins]; + Sk_SetDefaultValue(skin); + skin->wadnum = wadnum; + hudname = realname = false; + // parse + stoken = strtok (buf2, "\r\n= "); + while (stoken) + { + if ((stoken[0] == '/' && stoken[1] == '/') + || (stoken[0] == '#'))// skip comments + { + stoken = strtok(NULL, "\r\n"); // skip end of line + goto next_token; // find the real next token + } + + value = strtok(NULL, "\r\n= "); + + if (!value) + I_Error("R_AddSkins: syntax error in S_SKIN lump# %d(%s) in WAD %s\n", lump, W_CheckNameForNumPwad(wadnum,lump), wadfiles[wadnum]->filename); + + // Some of these can't go in R_ProcessPatchableFields because they have side effects for future lines. + // Others can't go in there because we don't want them to be patchable. + if (!stricmp(stoken, "name")) + { + INT32 skinnum = R_SkinAvailable(value); + strlwr(value); + if (skinnum == -1) + STRBUFCPY(skin->name, value); + // the skin name must uniquely identify a single skin + // if the name is already used I make the name 'namex' + // using the default skin name's number set above + else + { + const size_t stringspace = + strlen(value) + sizeof (numskins) + 1; + char *value2 = Z_Malloc(stringspace, PU_STATIC, NULL); + snprintf(value2, stringspace, + "%s%d", value, numskins); + value2[stringspace - 1] = '\0'; + if (R_SkinAvailable(value2) == -1) + // I'm lazy so if NEW name is already used I leave the 'skin x' + // default skin name set in Sk_SetDefaultValue + STRBUFCPY(skin->name, value2); + Z_Free(value2); + } + + // copy to hudname and fullname as a default. + if (!realname) + { + STRBUFCPY(skin->realname, skin->name); + for (value = skin->realname; *value; value++) + { + if (*value == '_') *value = ' '; // turn _ into spaces. + else if (*value == '.') *value = '\x1E'; // turn . into katana dot. + } + } + if (!hudname) + { + HUDNAMEWRITE(skin->name); + strupr(skin->hudname); + SYMBOLCONVERT(skin->hudname) + } + } + else if (!stricmp(stoken, "realname")) + { // Display name (eg. "Knuckles") + realname = true; + STRBUFCPY(skin->realname, value); + SYMBOLCONVERT(skin->realname) + if (!hudname) + HUDNAMEWRITE(skin->realname); + } + else if (!stricmp(stoken, "hudname")) + { // Life icon name (eg. "K.T.E") + hudname = true; + HUDNAMEWRITE(value); + SYMBOLCONVERT(skin->hudname) + if (!realname) + STRBUFCPY(skin->realname, skin->hudname); + } + else if (!stricmp(stoken, "availability")) + { + skin->availability = atoi(value); + if (skin->availability >= MAXUNLOCKABLES) + skin->availability = 0; + } + else if (!R_ProcessPatchableFields(skin, stoken, value)) + CONS_Debug(DBG_SETUP, "R_AddSkins: Unknown keyword '%s' in S_SKIN lump #%d (WAD %s)\n", stoken, lump, wadfiles[wadnum]->filename); + +next_token: + stoken = strtok(NULL, "\r\n= "); + } + free(buf2); + + // Add sprites + R_LoadSkinSprites(wadnum, &lump, &lastlump, skin); + //ST_LoadFaceGraphics(numskins); -- nah let's do this elsewhere + + R_FlushTranslationColormapCache(); + + if (!skin->availability) // Safe to print... + CONS_Printf(M_GetText("Added skin '%s'\n"), skin->name); +#ifdef SKINVALUES + skin_cons_t[numskins].value = numskins; + skin_cons_t[numskins].strvalue = skin->name; +#endif + +#ifdef HWRENDER + if (rendermode == render_opengl) + HWR_AddPlayerModel(numskins); +#endif + + numskins++; + } + return; +} + +// +// Patch skin sprites +// +void R_PatchSkins(UINT16 wadnum) +{ + UINT16 lump, lastlump = 0; + char *buf; + char *buf2; + char *stoken; + char *value; + size_t size; + skin_t *skin; + boolean noskincomplain, realname, hudname; + + // + // search for all skin patch markers in pwad + // + + while ((lump = W_CheckForPatchSkinMarkerInPwad(wadnum, lastlump)) != INT16_MAX) + { + INT32 skinnum = 0; + + // advance by default + lastlump = lump + 1; + + buf = W_CacheLumpNumPwad(wadnum, lump, PU_CACHE); + size = W_LumpLengthPwad(wadnum, lump); + + // for strtok + buf2 = malloc(size+1); + if (!buf2) + I_Error("R_PatchSkins: No more free memory\n"); + M_Memcpy(buf2,buf,size); + buf2[size] = '\0'; + + skin = NULL; + noskincomplain = realname = hudname = false; + + /* + Parse. Has more phases than the parser in R_AddSkins because it needs to have the patching name first (no default skin name is acceptible for patching, unlike skin creation) + */ + + stoken = strtok(buf2, "\r\n= "); + while (stoken) + { + if ((stoken[0] == '/' && stoken[1] == '/') + || (stoken[0] == '#'))// skip comments + { + stoken = strtok(NULL, "\r\n"); // skip end of line + goto next_token; // find the real next token + } + + value = strtok(NULL, "\r\n= "); + + if (!value) + I_Error("R_PatchSkins: syntax error in P_SKIN lump# %d(%s) in WAD %s\n", lump, W_CheckNameForNumPwad(wadnum,lump), wadfiles[wadnum]->filename); + + if (!skin) // Get the name! + { + if (!stricmp(stoken, "name")) + { + strlwr(value); + skinnum = R_SkinAvailable(value); + if (skinnum != -1) + skin = &skins[skinnum]; + else + { + CONS_Debug(DBG_SETUP, "R_PatchSkins: unknown skin name in P_SKIN lump# %d(%s) in WAD %s\n", lump, W_CheckNameForNumPwad(wadnum,lump), wadfiles[wadnum]->filename); + noskincomplain = true; + } + } + } + else // Get the properties! + { + // Some of these can't go in R_ProcessPatchableFields because they have side effects for future lines. + if (!stricmp(stoken, "realname")) + { // Display name (eg. "Knuckles") + realname = true; + STRBUFCPY(skin->realname, value); + SYMBOLCONVERT(skin->realname) + if (!hudname) + HUDNAMEWRITE(skin->realname); + } + else if (!stricmp(stoken, "hudname")) + { // Life icon name (eg. "K.T.E") + hudname = true; + HUDNAMEWRITE(value); + SYMBOLCONVERT(skin->hudname) + if (!realname) + STRBUFCPY(skin->realname, skin->hudname); + } + else if (!R_ProcessPatchableFields(skin, stoken, value)) + CONS_Debug(DBG_SETUP, "R_PatchSkins: Unknown keyword '%s' in P_SKIN lump #%d (WAD %s)\n", stoken, lump, wadfiles[wadnum]->filename); + } + + if (!skin) + break; + +next_token: + stoken = strtok(NULL, "\r\n= "); + } + free(buf2); + + if (!skin) // Didn't include a name parameter? What a waste. + { + if (!noskincomplain) + CONS_Debug(DBG_SETUP, "R_PatchSkins: no skin name given in P_SKIN lump #%d (WAD %s)\n", lump, wadfiles[wadnum]->filename); + continue; + } + + // Patch sprites + R_LoadSkinSprites(wadnum, &lump, &lastlump, skin); + //ST_LoadFaceGraphics(skinnum); -- nah let's do this elsewhere + + R_FlushTranslationColormapCache(); + + if (!skin->availability) // Safe to print... + CONS_Printf(M_GetText("Patched skin '%s'\n"), skin->name); + } + return; +} + +#undef HUDNAMEWRITE +#undef SYMBOLCONVERT diff --git a/src/r_skins.h b/src/r_skins.h new file mode 100644 index 000000000..4b83966ac --- /dev/null +++ b/src/r_skins.h @@ -0,0 +1,102 @@ +// SONIC ROBO BLAST 2 +//----------------------------------------------------------------------------- +// Copyright (C) 1993-1996 by id Software, Inc. +// Copyright (C) 1998-2000 by DooM Legacy Team. +// Copyright (C) 1999-2020 by Sonic Team Junior. +// +// This program is free software distributed under the +// terms of the GNU General Public License, version 2. +// See the 'LICENSE' file for more details. +//----------------------------------------------------------------------------- +/// \file r_skins.h +/// \brief Skins stuff + +#ifndef __R_SKINS__ +#define __R_SKINS__ + +#include "info.h" +#include "sounds.h" +#include "r_patch.h" // spriteinfo_t +#include "r_defs.h" // spritedef_t + +/// Defaults +#define SKINNAMESIZE 16 +// should be all lowercase!! S_SKIN processing does a strlwr +#define DEFAULTSKIN "sonic" +#define DEFAULTSKIN2 "tails" // secondary player +#define DEFAULTNIGHTSSKIN 0 + +/// The skin_t struct +typedef struct +{ + char name[SKINNAMESIZE+1]; // INT16 descriptive name of the skin + UINT16 wadnum; + skinflags_t flags; + + char realname[SKINNAMESIZE+1]; // Display name for level completion. + char hudname[SKINNAMESIZE+1]; // HUD name to display (officially exactly 5 characters long) + + UINT8 ability; // ability definition + UINT8 ability2; // secondary ability definition + INT32 thokitem; + INT32 spinitem; + INT32 revitem; + INT32 followitem; + fixed_t actionspd; + fixed_t mindash; + fixed_t maxdash; + + fixed_t normalspeed; // Normal ground + fixed_t runspeed; // Speed that you break into your run animation + + UINT8 thrustfactor; // Thrust = thrustfactor * acceleration + UINT8 accelstart; // Acceleration if speed = 0 + UINT8 acceleration; // Acceleration + + fixed_t jumpfactor; // multiple of standard jump height + + fixed_t radius; // Bounding box changes. + fixed_t height; + fixed_t spinheight; + + fixed_t shieldscale; // no change to bounding box, but helps set the shield's sprite size + fixed_t camerascale; + + // Definable color translation table + UINT8 starttranscolor; + UINT8 prefcolor; + UINT8 supercolor; + UINT8 prefoppositecolor; // if 0 use tables instead + + fixed_t highresscale; // scale of highres, default is 0.5 + UINT8 contspeed; // continue screen animation speed + UINT8 contangle; // initial angle on continue screen + + // specific sounds per skin + sfxenum_t soundsid[NUMSKINSOUNDS]; // sound # in S_sfx table + + // contains super versions too + spritedef_t sprites[NUMPLAYERSPRITES*2]; + spriteinfo_t sprinfo[NUMPLAYERSPRITES*2]; + + UINT8 availability; // lock? +} skin_t; + +/// Externs +extern INT32 numskins; +extern skin_t skins[MAXSKINS]; + +/// Function prototypes +void R_InitSkins(void); + +void SetPlayerSkin(INT32 playernum,const char *skinname); +void SetPlayerSkinByNum(INT32 playernum,INT32 skinnum); // Tails 03-16-2002 +boolean R_SkinUsable(INT32 playernum, INT32 skinnum); +UINT32 R_GetSkinAvailabilities(void); +INT32 R_SkinAvailable(const char *name); +void R_PatchSkins(UINT16 wadnum); +void R_AddSkins(UINT16 wadnum); + +UINT8 P_GetSkinSprite2(skin_t *skin, UINT8 spr2, player_t *player); + +#endif //__R_SKINS__ diff --git a/src/r_things.c b/src/r_things.c index 953825d0f..17a5f0809 100644 --- a/src/r_things.c +++ b/src/r_things.c @@ -30,11 +30,8 @@ #include "p_tick.h" #include "p_local.h" #include "p_slopes.h" -#include "dehacked.h" // get_number (for thok) #include "d_netfil.h" // blargh. for nameonly(). #include "m_cheat.h" // objectplace -#include "m_cond.h" -#include "fastcmp.h" #ifdef HWRENDER #include "hardware/hw_md2.h" #include "hardware/hw_glob.h" @@ -42,14 +39,6 @@ #include "hardware/hw_drv.h" #endif -#ifdef PC_DOS -#include // for snprintf -int snprintf(char *str, size_t n, const char *fmt, ...); -//int vsnprintf(char *str, size_t n, const char *fmt, va_list ap); -#endif - -static void R_InitSkins(void); - #define MINZ (FRACUNIT*4) #define BASEYCENTER (BASEVIDHEIGHT/2) @@ -233,7 +222,7 @@ static void R_InstallSpriteLump(UINT16 wad, // graphics patch // // Returns true if the sprite was succesfully added // -static boolean R_AddSingleSpriteDef(const char *sprname, spritedef_t *spritedef, UINT16 wadnum, UINT16 startlump, UINT16 endlump) +boolean R_AddSingleSpriteDef(const char *sprname, spritedef_t *spritedef, UINT16 wadnum, UINT16 startlump, UINT16 endlump) { UINT16 l; UINT8 frame; @@ -2971,788 +2960,4 @@ void R_DrawMasked(maskcount_t* masks, UINT8 nummasks) // // ========================================================================== -INT32 numskins = 0; -skin_t skins[MAXSKINS]; -// FIXTHIS: don't work because it must be inistilised before the config load -//#define SKINVALUES -#ifdef SKINVALUES -CV_PossibleValue_t skin_cons_t[MAXSKINS+1]; -#endif - -// -// P_GetSkinSprite2 -// For non-super players, tries each sprite2's immediate predecessor until it finds one with a number of frames or ends up at standing. -// For super players, does the same as above - but tries the super equivalent for each sprite2 before the non-super version. -// - -UINT8 P_GetSkinSprite2(skin_t *skin, UINT8 spr2, player_t *player) -{ - UINT8 super = 0, i = 0; - - if (!skin) - return 0; - - if ((playersprite_t)(spr2 & ~FF_SPR2SUPER) >= free_spr2) - return 0; - - while (!skin->sprites[spr2].numframes - && spr2 != SPR2_STND - && ++i < 32) // recursion limiter - { - if (spr2 & FF_SPR2SUPER) - { - super = FF_SPR2SUPER; - spr2 &= ~FF_SPR2SUPER; - continue; - } - - switch(spr2) - { - // Normal special cases. - case SPR2_JUMP: - spr2 = ((player - ? player->charflags - : skin->flags) - & SF_NOJUMPSPIN) ? SPR2_SPNG : SPR2_ROLL; - break; - case SPR2_TIRE: - spr2 = ((player - ? player->charability - : skin->ability) - == CA_SWIM) ? SPR2_SWIM : SPR2_FLY; - break; - // Use the handy list, that's what it's there for! - default: - spr2 = spr2defaults[spr2]; - break; - } - - spr2 |= super; - } - - if (i >= 32) // probably an infinite loop... - return 0; - - return spr2; -} - -static void Sk_SetDefaultValue(skin_t *skin) -{ - INT32 i; - // - // set default skin values - // - memset(skin, 0, sizeof (skin_t)); - snprintf(skin->name, - sizeof skin->name, "skin %u", (UINT32)(skin-skins)); - skin->name[sizeof skin->name - 1] = '\0'; - skin->wadnum = INT16_MAX; - - skin->flags = 0; - - strcpy(skin->realname, "Someone"); - strcpy(skin->hudname, "???"); - - skin->starttranscolor = 96; - skin->prefcolor = SKINCOLOR_GREEN; - skin->supercolor = SKINCOLOR_SUPERGOLD1; - skin->prefoppositecolor = 0; // use tables - - skin->normalspeed = 36<runspeed = 28<thrustfactor = 5; - skin->accelstart = 96; - skin->acceleration = 40; - - skin->ability = CA_NONE; - skin->ability2 = CA2_SPINDASH; - skin->jumpfactor = FRACUNIT; - skin->actionspd = 30<mindash = 15<maxdash = 70<radius = mobjinfo[MT_PLAYER].radius; - skin->height = mobjinfo[MT_PLAYER].height; - skin->spinheight = FixedMul(skin->height, 2*FRACUNIT/3); - - skin->shieldscale = FRACUNIT; - skin->camerascale = FRACUNIT; - - skin->thokitem = -1; - skin->spinitem = -1; - skin->revitem = -1; - skin->followitem = 0; - - skin->highresscale = FRACUNIT; - skin->contspeed = 17; - skin->contangle = 0; - - skin->availability = 0; - - for (i = 0; i < sfx_skinsoundslot0; i++) - if (S_sfx[i].skinsound != -1) - skin->soundsid[S_sfx[i].skinsound] = i; -} - -// -// Initialize the basic skins -// -void R_InitSkins(void) -{ -#ifdef SKINVALUES - INT32 i; - - for (i = 0; i <= MAXSKINS; i++) - { - skin_cons_t[i].value = 0; - skin_cons_t[i].strvalue = NULL; - } -#endif - - // no default skin! - numskins = 0; -} - -UINT32 R_GetSkinAvailabilities(void) -{ - INT32 s; - UINT32 response = 0; - - for (s = 0; s < MAXSKINS; s++) - { - if (skins[s].availability && unlockables[skins[s].availability - 1].unlocked) - response |= (1 << s); - } - return response; -} - -// returns true if available in circumstances, otherwise nope -// warning don't use with an invalid skinnum other than -1 which always returns true -boolean R_SkinUsable(INT32 playernum, INT32 skinnum) -{ - return ((skinnum == -1) // Simplifies things elsewhere, since there's already plenty of checks for less-than-0... - || (!skins[skinnum].availability) - || (((netgame || multiplayer) && playernum != -1) ? (players[playernum].availabilities & (1 << skinnum)) : (unlockables[skins[skinnum].availability - 1].unlocked)) - || (modeattacking) // If you have someone else's run you might as well take a look - || (Playing() && (R_SkinAvailable(mapheaderinfo[gamemap-1]->forcecharacter) == skinnum)) // Force 1. - || (netgame && (cv_forceskin.value == skinnum)) // Force 2. - || (metalrecording && skinnum == 5) // Force 3. - ); -} - -// returns true if the skin name is found (loaded from pwad) -// warning return -1 if not found -INT32 R_SkinAvailable(const char *name) -{ - INT32 i; - - for (i = 0; i < numskins; i++) - { - // search in the skin list - if (stricmp(skins[i].name,name)==0) - return i; - } - return -1; -} - -// network code calls this when a 'skin change' is received -void SetPlayerSkin(INT32 playernum, const char *skinname) -{ - INT32 i = R_SkinAvailable(skinname); - player_t *player = &players[playernum]; - - if ((i != -1) && R_SkinUsable(playernum, i)) - { - SetPlayerSkinByNum(playernum, i); - return; - } - - if (P_IsLocalPlayer(player)) - CONS_Alert(CONS_WARNING, M_GetText("Skin '%s' not found.\n"), skinname); - else if(server || IsPlayerAdmin(consoleplayer)) - CONS_Alert(CONS_WARNING, M_GetText("Player %d (%s) skin '%s' not found\n"), playernum, player_names[playernum], skinname); - - SetPlayerSkinByNum(playernum, 0); -} - -// Same as SetPlayerSkin, but uses the skin #. -// network code calls this when a 'skin change' is received -void SetPlayerSkinByNum(INT32 playernum, INT32 skinnum) -{ - player_t *player = &players[playernum]; - skin_t *skin = &skins[skinnum]; - UINT8 newcolor = 0; - - if (skinnum >= 0 && skinnum < numskins && R_SkinUsable(playernum, skinnum)) // Make sure it exists! - { - player->skin = skinnum; - - player->camerascale = skin->camerascale; - player->shieldscale = skin->shieldscale; - - player->charability = (UINT8)skin->ability; - player->charability2 = (UINT8)skin->ability2; - - player->charflags = (UINT32)skin->flags; - - player->thokitem = skin->thokitem < 0 ? (UINT32)mobjinfo[MT_PLAYER].painchance : (UINT32)skin->thokitem; - player->spinitem = skin->spinitem < 0 ? (UINT32)mobjinfo[MT_PLAYER].damage : (UINT32)skin->spinitem; - player->revitem = skin->revitem < 0 ? (mobjtype_t)mobjinfo[MT_PLAYER].raisestate : (UINT32)skin->revitem; - player->followitem = skin->followitem; - - if (((player->powers[pw_shield] & SH_NOSTACK) == SH_PINK) && (player->revitem == MT_LHRT || player->spinitem == MT_LHRT || player->thokitem == MT_LHRT)) // Healers can't keep their buff. - player->powers[pw_shield] &= SH_STACK; - - player->actionspd = skin->actionspd; - player->mindash = skin->mindash; - player->maxdash = skin->maxdash; - - player->normalspeed = skin->normalspeed; - player->runspeed = skin->runspeed; - player->thrustfactor = skin->thrustfactor; - player->accelstart = skin->accelstart; - player->acceleration = skin->acceleration; - - player->jumpfactor = skin->jumpfactor; - - player->height = skin->height; - player->spinheight = skin->spinheight; - - if (!(cv_debug || devparm) && !(netgame || multiplayer || demoplayback)) - { - if (playernum == consoleplayer) - CV_StealthSetValue(&cv_playercolor, skin->prefcolor); - else if (playernum == secondarydisplayplayer) - CV_StealthSetValue(&cv_playercolor2, skin->prefcolor); - player->skincolor = newcolor = skin->prefcolor; - } - - if (player->followmobj) - { - P_RemoveMobj(player->followmobj); - P_SetTarget(&player->followmobj, NULL); - } - - if (player->mo) - { - fixed_t radius = FixedMul(skin->radius, player->mo->scale); - if ((player->powers[pw_carry] == CR_NIGHTSMODE) && (skin->sprites[SPR2_NFLY].numframes == 0)) // If you don't have a sprite for flying horizontally, use the default NiGHTS skin. - { - skin = &skins[DEFAULTNIGHTSSKIN]; - player->followitem = skin->followitem; - if (!(cv_debug || devparm) && !(netgame || multiplayer || demoplayback)) - newcolor = skin->prefcolor; // will be updated in thinker to flashing - } - player->mo->skin = skin; - if (newcolor) - player->mo->color = newcolor; - P_SetScale(player->mo, player->mo->scale); - player->mo->radius = radius; - - P_SetPlayerMobjState(player->mo, player->mo->state-states); // Prevent visual errors when switching between skins with differing number of frames - } - return; - } - - if (P_IsLocalPlayer(player)) - CONS_Alert(CONS_WARNING, M_GetText("Requested skin %d not found\n"), skinnum); - else if(server || IsPlayerAdmin(consoleplayer)) - CONS_Alert(CONS_WARNING, "Player %d (%s) skin %d not found\n", playernum, player_names[playernum], skinnum); - SetPlayerSkinByNum(playernum, 0); // not found put the sonic skin -} - -// -// Add skins from a pwad, each skin preceded by 'S_SKIN' marker -// - -// Does the same is in w_wad, but check only for -// the first 6 characters (this is so we can have S_SKIN1, S_SKIN2.. -// for wad editors that don't like multiple resources of the same name) -// -static UINT16 W_CheckForSkinMarkerInPwad(UINT16 wadid, UINT16 startlump) -{ - UINT16 i; - const char *S_SKIN = "S_SKIN"; - lumpinfo_t *lump_p; - - // scan forward, start at - if (startlump < wadfiles[wadid]->numlumps) - { - lump_p = wadfiles[wadid]->lumpinfo + startlump; - for (i = startlump; i < wadfiles[wadid]->numlumps; i++, lump_p++) - if (memcmp(lump_p->name,S_SKIN,6)==0) - return i; - } - return INT16_MAX; // not found -} - -#define HUDNAMEWRITE(value) STRBUFCPY(skin->hudname, value) - -// turn _ into spaces and . into katana dot -#define SYMBOLCONVERT(name) for (value = name; *value; value++)\ - {\ - if (*value == '_') *value = ' ';\ - else if (*value == '.') *value = '\x1E';\ - } - -// -// Patch skins from a pwad, each skin preceded by 'P_SKIN' marker -// - -// Does the same is in w_wad, but check only for -// the first 6 characters (this is so we can have P_SKIN1, P_SKIN2.. -// for wad editors that don't like multiple resources of the same name) -// -static UINT16 W_CheckForPatchSkinMarkerInPwad(UINT16 wadid, UINT16 startlump) -{ - UINT16 i; - const char *P_SKIN = "P_SKIN"; - lumpinfo_t *lump_p; - - // scan forward, start at - if (startlump < wadfiles[wadid]->numlumps) - { - lump_p = wadfiles[wadid]->lumpinfo + startlump; - for (i = startlump; i < wadfiles[wadid]->numlumps; i++, lump_p++) - if (memcmp(lump_p->name,P_SKIN,6)==0) - return i; - } - return INT16_MAX; // not found -} - -static void R_LoadSkinSprites(UINT16 wadnum, UINT16 *lump, UINT16 *lastlump, skin_t *skin) -{ - UINT16 newlastlump; - UINT8 sprite2; - - *lump += 1; // start after S_SKIN - *lastlump = W_CheckNumForNamePwad("S_END",wadnum,*lump); // stop at S_END - - // old wadding practices die hard -- stop at S_SKIN (or P_SKIN) or S_START if they come before S_END. - newlastlump = W_CheckForSkinMarkerInPwad(wadnum,*lump); - if (newlastlump < *lastlump) *lastlump = newlastlump; - newlastlump = W_CheckForPatchSkinMarkerInPwad(wadnum,*lump); - if (newlastlump < *lastlump) *lastlump = newlastlump; - newlastlump = W_CheckNumForNamePwad("S_START",wadnum,*lump); - if (newlastlump < *lastlump) *lastlump = newlastlump; - - // ...and let's handle super, too - newlastlump = W_CheckNumForNamePwad("S_SUPER",wadnum,*lump); - if (newlastlump < *lastlump) - { - newlastlump++; - // load all sprite sets we are aware of... for super! - for (sprite2 = 0; sprite2 < free_spr2; sprite2++) - R_AddSingleSpriteDef((spritename = spr2names[sprite2]), &skin->sprites[FF_SPR2SUPER|sprite2], wadnum, newlastlump, *lastlump); - - newlastlump--; - *lastlump = newlastlump; // okay, make the normal sprite set loading end there - } - - // load all sprite sets we are aware of... for normal stuff. - for (sprite2 = 0; sprite2 < free_spr2; sprite2++) - R_AddSingleSpriteDef((spritename = spr2names[sprite2]), &skin->sprites[sprite2], wadnum, *lump, *lastlump); - - if (skin->sprites[0].numframes == 0) - I_Error("R_LoadSkinSprites: no frames found for sprite SPR2_%s\n", spr2names[0]); -} - -// returns whether found appropriate property -static boolean R_ProcessPatchableFields(skin_t *skin, char *stoken, char *value) -{ - // custom translation table - if (!stricmp(stoken, "startcolor")) - skin->starttranscolor = atoi(value); - -#define FULLPROCESS(field) else if (!stricmp(stoken, #field)) skin->field = get_number(value); - // character type identification - FULLPROCESS(flags) - FULLPROCESS(ability) - FULLPROCESS(ability2) - - FULLPROCESS(thokitem) - FULLPROCESS(spinitem) - FULLPROCESS(revitem) - FULLPROCESS(followitem) -#undef FULLPROCESS - -#define GETFRACBITS(field) else if (!stricmp(stoken, #field)) skin->field = atoi(value)<field = atoi(value); - GETINT(thrustfactor) - GETINT(accelstart) - GETINT(acceleration) - GETINT(contspeed) - GETINT(contangle) -#undef GETINT - -#define GETSKINCOLOR(field) else if (!stricmp(stoken, #field)) skin->field = R_GetColorByName(value); - GETSKINCOLOR(prefcolor) - GETSKINCOLOR(prefoppositecolor) -#undef GETSKINCOLOR - else if (!stricmp(stoken, "supercolor")) - skin->supercolor = R_GetSuperColorByName(value); - -#define GETFLOAT(field) else if (!stricmp(stoken, #field)) skin->field = FLOAT_TO_FIXED(atof(value)); - GETFLOAT(jumpfactor) - GETFLOAT(highresscale) - GETFLOAT(shieldscale) - GETFLOAT(camerascale) -#undef GETFLOAT - -#define GETFLAG(field) else if (!stricmp(stoken, #field)) { \ - strupr(value); \ - if (atoi(value) || value[0] == 'T' || value[0] == 'Y') \ - skin->flags |= (SF_##field); \ - else \ - skin->flags &= ~(SF_##field); \ -} - // parameters for individual character flags - // these are uppercase so they can be concatenated with SF_ - // 1, true, yes are all valid values - GETFLAG(SUPER) - GETFLAG(NOSUPERSPIN) - GETFLAG(NOSPINDASHDUST) - GETFLAG(HIRES) - GETFLAG(NOSKID) - GETFLAG(NOSPEEDADJUST) - GETFLAG(RUNONWATER) - GETFLAG(NOJUMPSPIN) - GETFLAG(NOJUMPDAMAGE) - GETFLAG(STOMPDAMAGE) - GETFLAG(MARIODAMAGE) - GETFLAG(MACHINE) - GETFLAG(DASHMODE) - GETFLAG(FASTEDGE) - GETFLAG(MULTIABILITY) - GETFLAG(NONIGHTSROTATION) -#undef GETFLAG - - else // let's check if it's a sound, otherwise error out - { - boolean found = false; - sfxenum_t i; - size_t stokenadjust; - - // Remove the prefix. (We need to affect an adjusting variable so that we can print error messages if it's not actually a sound.) - if ((stoken[0] == 'D' || stoken[0] == 'd') && (stoken[1] == 'S' || stoken[1] == 's')) // DS* - stokenadjust = 2; - else // sfx_* - stokenadjust = 4; - - // Remove the prefix. (We can affect this directly since we're not going to use it again.) - if ((value[0] == 'D' || value[0] == 'd') && (value[1] == 'S' || value[1] == 's')) // DS* - value += 2; - else // sfx_* - value += 4; - - // copy name of sounds that are remapped - // for this skin - for (i = 0; i < sfx_skinsoundslot0; i++) - { - if (!S_sfx[i].name) - continue; - if (S_sfx[i].skinsound != -1 - && !stricmp(S_sfx[i].name, - stoken + stokenadjust)) - { - skin->soundsid[S_sfx[i].skinsound] = - S_AddSoundFx(value, S_sfx[i].singularity, S_sfx[i].pitch, true); - found = true; - } - } - return found; - } - return true; -} - -// -// Find skin sprites, sounds & optional status bar face, & add them -// -void R_AddSkins(UINT16 wadnum) -{ - UINT16 lump, lastlump = 0; - char *buf; - char *buf2; - char *stoken; - char *value; - size_t size; - skin_t *skin; - boolean hudname, realname; - - // - // search for all skin markers in pwad - // - - while ((lump = W_CheckForSkinMarkerInPwad(wadnum, lastlump)) != INT16_MAX) - { - // advance by default - lastlump = lump + 1; - - if (numskins >= MAXSKINS) - { - CONS_Debug(DBG_RENDER, "ignored skin (%d skins maximum)\n", MAXSKINS); - continue; // so we know how many skins couldn't be added - } - buf = W_CacheLumpNumPwad(wadnum, lump, PU_CACHE); - size = W_LumpLengthPwad(wadnum, lump); - - // for strtok - buf2 = malloc(size+1); - if (!buf2) - I_Error("R_AddSkins: No more free memory\n"); - M_Memcpy(buf2,buf,size); - buf2[size] = '\0'; - - // set defaults - skin = &skins[numskins]; - Sk_SetDefaultValue(skin); - skin->wadnum = wadnum; - hudname = realname = false; - // parse - stoken = strtok (buf2, "\r\n= "); - while (stoken) - { - if ((stoken[0] == '/' && stoken[1] == '/') - || (stoken[0] == '#'))// skip comments - { - stoken = strtok(NULL, "\r\n"); // skip end of line - goto next_token; // find the real next token - } - - value = strtok(NULL, "\r\n= "); - - if (!value) - I_Error("R_AddSkins: syntax error in S_SKIN lump# %d(%s) in WAD %s\n", lump, W_CheckNameForNumPwad(wadnum,lump), wadfiles[wadnum]->filename); - - // Some of these can't go in R_ProcessPatchableFields because they have side effects for future lines. - // Others can't go in there because we don't want them to be patchable. - if (!stricmp(stoken, "name")) - { - INT32 skinnum = R_SkinAvailable(value); - strlwr(value); - if (skinnum == -1) - STRBUFCPY(skin->name, value); - // the skin name must uniquely identify a single skin - // if the name is already used I make the name 'namex' - // using the default skin name's number set above - else - { - const size_t stringspace = - strlen(value) + sizeof (numskins) + 1; - char *value2 = Z_Malloc(stringspace, PU_STATIC, NULL); - snprintf(value2, stringspace, - "%s%d", value, numskins); - value2[stringspace - 1] = '\0'; - if (R_SkinAvailable(value2) == -1) - // I'm lazy so if NEW name is already used I leave the 'skin x' - // default skin name set in Sk_SetDefaultValue - STRBUFCPY(skin->name, value2); - Z_Free(value2); - } - - // copy to hudname and fullname as a default. - if (!realname) - { - STRBUFCPY(skin->realname, skin->name); - for (value = skin->realname; *value; value++) - { - if (*value == '_') *value = ' '; // turn _ into spaces. - else if (*value == '.') *value = '\x1E'; // turn . into katana dot. - } - } - if (!hudname) - { - HUDNAMEWRITE(skin->name); - strupr(skin->hudname); - SYMBOLCONVERT(skin->hudname) - } - } - else if (!stricmp(stoken, "realname")) - { // Display name (eg. "Knuckles") - realname = true; - STRBUFCPY(skin->realname, value); - SYMBOLCONVERT(skin->realname) - if (!hudname) - HUDNAMEWRITE(skin->realname); - } - else if (!stricmp(stoken, "hudname")) - { // Life icon name (eg. "K.T.E") - hudname = true; - HUDNAMEWRITE(value); - SYMBOLCONVERT(skin->hudname) - if (!realname) - STRBUFCPY(skin->realname, skin->hudname); - } - else if (!stricmp(stoken, "availability")) - { - skin->availability = atoi(value); - if (skin->availability >= MAXUNLOCKABLES) - skin->availability = 0; - } - else if (!R_ProcessPatchableFields(skin, stoken, value)) - CONS_Debug(DBG_SETUP, "R_AddSkins: Unknown keyword '%s' in S_SKIN lump #%d (WAD %s)\n", stoken, lump, wadfiles[wadnum]->filename); - -next_token: - stoken = strtok(NULL, "\r\n= "); - } - free(buf2); - - // Add sprites - R_LoadSkinSprites(wadnum, &lump, &lastlump, skin); - //ST_LoadFaceGraphics(numskins); -- nah let's do this elsewhere - - R_FlushTranslationColormapCache(); - - if (!skin->availability) // Safe to print... - CONS_Printf(M_GetText("Added skin '%s'\n"), skin->name); -#ifdef SKINVALUES - skin_cons_t[numskins].value = numskins; - skin_cons_t[numskins].strvalue = skin->name; -#endif - -#ifdef HWRENDER - if (rendermode == render_opengl) - HWR_AddPlayerModel(numskins); -#endif - - numskins++; - } - return; -} - -// -// Patch skin sprites -// -void R_PatchSkins(UINT16 wadnum) -{ - UINT16 lump, lastlump = 0; - char *buf; - char *buf2; - char *stoken; - char *value; - size_t size; - skin_t *skin; - boolean noskincomplain, realname, hudname; - - // - // search for all skin patch markers in pwad - // - - while ((lump = W_CheckForPatchSkinMarkerInPwad(wadnum, lastlump)) != INT16_MAX) - { - INT32 skinnum = 0; - - // advance by default - lastlump = lump + 1; - - buf = W_CacheLumpNumPwad(wadnum, lump, PU_CACHE); - size = W_LumpLengthPwad(wadnum, lump); - - // for strtok - buf2 = malloc(size+1); - if (!buf2) - I_Error("R_PatchSkins: No more free memory\n"); - M_Memcpy(buf2,buf,size); - buf2[size] = '\0'; - - skin = NULL; - noskincomplain = realname = hudname = false; - - /* - Parse. Has more phases than the parser in R_AddSkins because it needs to have the patching name first (no default skin name is acceptible for patching, unlike skin creation) - */ - - stoken = strtok(buf2, "\r\n= "); - while (stoken) - { - if ((stoken[0] == '/' && stoken[1] == '/') - || (stoken[0] == '#'))// skip comments - { - stoken = strtok(NULL, "\r\n"); // skip end of line - goto next_token; // find the real next token - } - - value = strtok(NULL, "\r\n= "); - - if (!value) - I_Error("R_PatchSkins: syntax error in P_SKIN lump# %d(%s) in WAD %s\n", lump, W_CheckNameForNumPwad(wadnum,lump), wadfiles[wadnum]->filename); - - if (!skin) // Get the name! - { - if (!stricmp(stoken, "name")) - { - strlwr(value); - skinnum = R_SkinAvailable(value); - if (skinnum != -1) - skin = &skins[skinnum]; - else - { - CONS_Debug(DBG_SETUP, "R_PatchSkins: unknown skin name in P_SKIN lump# %d(%s) in WAD %s\n", lump, W_CheckNameForNumPwad(wadnum,lump), wadfiles[wadnum]->filename); - noskincomplain = true; - } - } - } - else // Get the properties! - { - // Some of these can't go in R_ProcessPatchableFields because they have side effects for future lines. - if (!stricmp(stoken, "realname")) - { // Display name (eg. "Knuckles") - realname = true; - STRBUFCPY(skin->realname, value); - SYMBOLCONVERT(skin->realname) - if (!hudname) - HUDNAMEWRITE(skin->realname); - } - else if (!stricmp(stoken, "hudname")) - { // Life icon name (eg. "K.T.E") - hudname = true; - HUDNAMEWRITE(value); - SYMBOLCONVERT(skin->hudname) - if (!realname) - STRBUFCPY(skin->realname, skin->hudname); - } - else if (!R_ProcessPatchableFields(skin, stoken, value)) - CONS_Debug(DBG_SETUP, "R_PatchSkins: Unknown keyword '%s' in P_SKIN lump #%d (WAD %s)\n", stoken, lump, wadfiles[wadnum]->filename); - } - - if (!skin) - break; - -next_token: - stoken = strtok(NULL, "\r\n= "); - } - free(buf2); - - if (!skin) // Didn't include a name parameter? What a waste. - { - if (!noskincomplain) - CONS_Debug(DBG_SETUP, "R_PatchSkins: no skin name given in P_SKIN lump #%d (WAD %s)\n", lump, wadfiles[wadnum]->filename); - continue; - } - - // Patch sprites - R_LoadSkinSprites(wadnum, &lump, &lastlump, skin); - //ST_LoadFaceGraphics(skinnum); -- nah let's do this elsewhere - - R_FlushTranslationColormapCache(); - - if (!skin->availability) // Safe to print... - CONS_Printf(M_GetText("Patched skin '%s'\n"), skin->name); - } - return; -} - -#undef HUDNAMEWRITE -#undef SYMBOLCONVERT +#include "r_skins.c" diff --git a/src/r_things.h b/src/r_things.h index bd6271b60..dd9b227f8 100644 --- a/src/r_things.h +++ b/src/r_things.h @@ -14,7 +14,6 @@ #ifndef __R_THINGS__ #define __R_THINGS__ -#include "sounds.h" #include "r_plane.h" #include "r_patch.h" #include "r_portal.h" @@ -82,69 +81,17 @@ typedef struct void R_DrawMasked(maskcount_t* masks, UINT8 nummasks); +// -------------- +// SPRITE LOADING +// -------------- + +boolean R_AddSingleSpriteDef(const char *sprname, spritedef_t *spritedef, UINT16 wadnum, UINT16 startlump, UINT16 endlump); + // ----------- // SKINS STUFF // ----------- -#define SKINNAMESIZE 16 -// should be all lowercase!! S_SKIN processing does a strlwr -#define DEFAULTSKIN "sonic" -#define DEFAULTSKIN2 "tails" // secondary player -#define DEFAULTNIGHTSSKIN 0 -typedef struct -{ - char name[SKINNAMESIZE+1]; // INT16 descriptive name of the skin - UINT16 wadnum; - skinflags_t flags; - - char realname[SKINNAMESIZE+1]; // Display name for level completion. - char hudname[SKINNAMESIZE+1]; // HUD name to display (officially exactly 5 characters long) - - UINT8 ability; // ability definition - UINT8 ability2; // secondary ability definition - INT32 thokitem; - INT32 spinitem; - INT32 revitem; - INT32 followitem; - fixed_t actionspd; - fixed_t mindash; - fixed_t maxdash; - - fixed_t normalspeed; // Normal ground - fixed_t runspeed; // Speed that you break into your run animation - - UINT8 thrustfactor; // Thrust = thrustfactor * acceleration - UINT8 accelstart; // Acceleration if speed = 0 - UINT8 acceleration; // Acceleration - - fixed_t jumpfactor; // multiple of standard jump height - - fixed_t radius; // Bounding box changes. - fixed_t height; - fixed_t spinheight; - - fixed_t shieldscale; // no change to bounding box, but helps set the shield's sprite size - fixed_t camerascale; - - // Definable color translation table - UINT8 starttranscolor; - UINT8 prefcolor; - UINT8 supercolor; - UINT8 prefoppositecolor; // if 0 use tables instead - - fixed_t highresscale; // scale of highres, default is 0.5 - UINT8 contspeed; // continue screen animation speed - UINT8 contangle; // initial angle on continue screen - - // specific sounds per skin - sfxenum_t soundsid[NUMSKINSOUNDS]; // sound # in S_sfx table - - // contains super versions too - spritedef_t sprites[NUMPLAYERSPRITES*2]; - spriteinfo_t sprinfo[NUMPLAYERSPRITES*2]; - - UINT8 availability; // lock? -} skin_t; +#include "r_skins.h" // ----------- // NOT SKINS STUFF ! @@ -241,24 +188,10 @@ typedef struct drawnode_s struct drawnode_s *prev; } drawnode_t; -extern INT32 numskins; -extern skin_t skins[MAXSKINS]; extern UINT32 visspritecount; -void SetPlayerSkin(INT32 playernum,const char *skinname); -void SetPlayerSkinByNum(INT32 playernum,INT32 skinnum); // Tails 03-16-2002 -boolean R_SkinUsable(INT32 playernum, INT32 skinnum); -UINT32 R_GetSkinAvailabilities(void); -INT32 R_SkinAvailable(const char *name); -void R_PatchSkins(UINT16 wadnum); -void R_AddSkins(UINT16 wadnum); - -UINT8 P_GetSkinSprite2(skin_t *skin, UINT8 spr2, player_t *player); - void R_InitDrawNodes(void); -char *GetPlayerFacePic(INT32 skinnum); - // Functions to go from sprite character ID to frame number // for 2.1 compatibility this still uses the old 'A' + frame code // The use of symbols tends to be painful for wad editors though From b2d9697e070c8d7742fcb9b4a49592517141d256 Mon Sep 17 00:00:00 2001 From: Monster Iestyn Date: Sun, 8 Mar 2020 19:36:56 +0000 Subject: [PATCH 63/68] remove r_skins.c include in r_things.c, on second thoughts --- src/r_things.c | 8 -------- 1 file changed, 8 deletions(-) diff --git a/src/r_things.c b/src/r_things.c index 17a5f0809..77418e066 100644 --- a/src/r_things.c +++ b/src/r_things.c @@ -2953,11 +2953,3 @@ void R_DrawMasked(maskcount_t* masks, UINT8 nummasks) free(heads); } - -// ========================================================================== -// -// SKINS CODE -// -// ========================================================================== - -#include "r_skins.c" From 37ecb47436854ece31d049da326bd8e622cd4586 Mon Sep 17 00:00:00 2001 From: Monster Iestyn Date: Sun, 8 Mar 2020 19:48:23 +0000 Subject: [PATCH 64/68] update Makefile, CMakeLists.txt and MSVC project files --- src/CMakeLists.txt | 2 ++ src/Makefile | 1 + src/sdl/Srb2SDL-vc10.vcxproj | 2 ++ src/sdl/Srb2SDL-vc10.vcxproj.filters | 6 ++++++ 4 files changed, 11 insertions(+) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index e8c9c3182..bc4a6114e 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -120,6 +120,7 @@ set(SRB2_CORE_RENDER_SOURCES r_main.c r_plane.c r_segs.c + r_skins.c r_sky.c r_splats.c r_things.c @@ -134,6 +135,7 @@ set(SRB2_CORE_RENDER_SOURCES r_main.h r_plane.h r_segs.h + r_skins.h r_sky.h r_splats.h r_state.h diff --git a/src/Makefile b/src/Makefile index 9f368217c..701f2cfda 100644 --- a/src/Makefile +++ b/src/Makefile @@ -468,6 +468,7 @@ OBJS:=$(i_main_o) \ $(OBJDIR)/r_main.o \ $(OBJDIR)/r_plane.o \ $(OBJDIR)/r_segs.o \ + $(OBJDIR)/r_skins.o \ $(OBJDIR)/r_sky.o \ $(OBJDIR)/r_splats.o \ $(OBJDIR)/r_things.o \ diff --git a/src/sdl/Srb2SDL-vc10.vcxproj b/src/sdl/Srb2SDL-vc10.vcxproj index b334f6313..7e260f4c0 100644 --- a/src/sdl/Srb2SDL-vc10.vcxproj +++ b/src/sdl/Srb2SDL-vc10.vcxproj @@ -284,6 +284,7 @@ + @@ -446,6 +447,7 @@ + diff --git a/src/sdl/Srb2SDL-vc10.vcxproj.filters b/src/sdl/Srb2SDL-vc10.vcxproj.filters index 3f61e8709..21820551a 100644 --- a/src/sdl/Srb2SDL-vc10.vcxproj.filters +++ b/src/sdl/Srb2SDL-vc10.vcxproj.filters @@ -417,6 +417,9 @@ R_Rend + + R_Rend + R_Rend @@ -849,6 +852,9 @@ R_Rend + + R_Rend + R_Rend From e81ef824d6db33e06a83cd2deedd2ed6547f441a Mon Sep 17 00:00:00 2001 From: Monster Iestyn Date: Sun, 8 Mar 2020 19:51:18 +0000 Subject: [PATCH 65/68] Set the "spritename" variable within R_AddSingleSpriteDef rather than externally to it, since R_InstallSpriteLump is the only place it gets used anyway This way we don't have to make it some extern in order for r_skins.c to use it --- src/r_skins.c | 4 ++-- src/r_things.c | 9 +++++---- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/src/r_skins.c b/src/r_skins.c index 48764ff75..2e9548bd7 100644 --- a/src/r_skins.c +++ b/src/r_skins.c @@ -410,7 +410,7 @@ static void R_LoadSkinSprites(UINT16 wadnum, UINT16 *lump, UINT16 *lastlump, ski newlastlump++; // load all sprite sets we are aware of... for super! for (sprite2 = 0; sprite2 < free_spr2; sprite2++) - R_AddSingleSpriteDef((spritename = spr2names[sprite2]), &skin->sprites[FF_SPR2SUPER|sprite2], wadnum, newlastlump, *lastlump); + R_AddSingleSpriteDef(spr2names[sprite2], &skin->sprites[FF_SPR2SUPER|sprite2], wadnum, newlastlump, *lastlump); newlastlump--; *lastlump = newlastlump; // okay, make the normal sprite set loading end there @@ -418,7 +418,7 @@ static void R_LoadSkinSprites(UINT16 wadnum, UINT16 *lump, UINT16 *lastlump, ski // load all sprite sets we are aware of... for normal stuff. for (sprite2 = 0; sprite2 < free_spr2; sprite2++) - R_AddSingleSpriteDef((spritename = spr2names[sprite2]), &skin->sprites[sprite2], wadnum, *lump, *lastlump); + R_AddSingleSpriteDef(spr2names[sprite2], &skin->sprites[sprite2], wadnum, *lump, *lastlump); if (skin->sprites[0].numframes == 0) I_Error("R_LoadSkinSprites: no frames found for sprite SPR2_%s\n", spr2names[0]); diff --git a/src/r_things.c b/src/r_things.c index 77418e066..2fc44faf8 100644 --- a/src/r_things.c +++ b/src/r_things.c @@ -234,6 +234,8 @@ boolean R_AddSingleSpriteDef(const char *sprname, spritedef_t *spritedef, UINT16 memset(sprtemp,0xFF, sizeof (sprtemp)); maxframe = (size_t)-1; + spritename = sprname; + // are we 'patching' a sprite already loaded ? // if so, it might patch only certain frames, not all if (spritedef->numframes) // (then spriteframes is not null) @@ -465,11 +467,10 @@ void R_AddSpriteDefs(UINT16 wadnum) // for (i = 0; i < numsprites; i++) { - spritename = sprnames[i]; - if (spritename[4] && wadnum >= (UINT16)spritename[4]) + if (sprnames[i][4] && wadnum >= (UINT16)sprnames[i][4]) continue; - if (R_AddSingleSpriteDef(spritename, &sprites[i], wadnum, start, end)) + if (R_AddSingleSpriteDef(sprnames[i], &sprites[i], wadnum, start, end)) { #ifdef HWRENDER if (rendermode == render_opengl) @@ -478,7 +479,7 @@ void R_AddSpriteDefs(UINT16 wadnum) // if a new sprite was added (not just replaced) addsprites++; #ifndef ZDEBUG - CONS_Debug(DBG_SETUP, "sprite %s set in pwad %d\n", spritename, wadnum); + CONS_Debug(DBG_SETUP, "sprite %s set in pwad %d\n", sprnames[i], wadnum); #endif } } From 1dd4ed4afaa71401fad875e7e702ee8c7535eaa8 Mon Sep 17 00:00:00 2001 From: Monster Iestyn Date: Sun, 8 Mar 2020 21:17:30 +0000 Subject: [PATCH 66/68] Reorganise r_things.h --- src/r_things.h | 71 ++++++++++++++++++++++++++++++-------------------- 1 file changed, 43 insertions(+), 28 deletions(-) diff --git a/src/r_things.h b/src/r_things.h index dd9b227f8..05d6fb27b 100644 --- a/src/r_things.h +++ b/src/r_things.h @@ -18,21 +18,23 @@ #include "r_patch.h" #include "r_portal.h" #include "r_defs.h" +#include "r_skins.h" -// number of sprite lumps for spritewidth,offset,topoffset lookup tables -// Fab: this is a hack : should allocate the lookup tables per sprite -#define MAXVISSPRITES 2048 // added 2-2-98 was 128 - -#define VISSPRITECHUNKBITS 6 // 2^6 = 64 sprites per chunk -#define VISSPRITESPERCHUNK (1 << VISSPRITECHUNKBITS) -#define VISSPRITEINDEXMASK (VISSPRITESPERCHUNK - 1) +// -------------- +// SPRITE LOADING +// -------------- #define FEETADJUST (4<dispoffset, affects ordering but not drawing } vissprite_t; +extern UINT32 visspritecount; + +// ---------- +// DRAW NODES +// ---------- + // A drawnode is something that points to a 3D floor, 3D side, or masked // middle texture. This is used for sorting with sprites. typedef struct drawnode_s @@ -188,10 +201,12 @@ typedef struct drawnode_s struct drawnode_s *prev; } drawnode_t; -extern UINT32 visspritecount; - void R_InitDrawNodes(void); +// ----------------------- +// SPRITE FRAME CHARACTERS +// ----------------------- + // Functions to go from sprite character ID to frame number // for 2.1 compatibility this still uses the old 'A' + frame code // The use of symbols tends to be painful for wad editors though From f4cb6e1f5ed149bd6484960d2329f24a50bd7e65 Mon Sep 17 00:00:00 2001 From: Monster Iestyn Date: Mon, 9 Mar 2020 13:54:56 +0000 Subject: [PATCH 67/68] Include r_skins.h instead of r_things.h in many files where all they wanted from it was skins stuff (oddly enough, this actually revealed some secret file dependencies previously included via r_things.h! I also needed to include d_player.h in r_skins.h itself it seems) --- src/d_netcmd.c | 2 +- src/dehacked.c | 1 + src/f_wipe.c | 1 + src/g_game.c | 2 +- src/hardware/hw3sound.c | 2 +- src/lua_baselib.c | 2 +- src/lua_hooklib.c | 2 +- src/lua_mobjlib.c | 2 +- src/lua_skinlib.c | 2 +- src/m_cond.c | 2 +- src/m_menu.h | 3 ++- src/p_enemy.c | 2 +- src/p_mobj.c | 2 +- src/p_saveg.c | 2 +- src/p_setup.c | 2 +- src/p_spec.c | 2 +- src/p_user.c | 2 +- src/r_skins.h | 1 + src/s_sound.c | 2 +- src/sounds.c | 2 +- src/y_inter.c | 2 +- 21 files changed, 22 insertions(+), 18 deletions(-) diff --git a/src/d_netcmd.c b/src/d_netcmd.c index c25929929..a55e70287 100644 --- a/src/d_netcmd.c +++ b/src/d_netcmd.c @@ -22,7 +22,7 @@ #include "g_input.h" #include "m_menu.h" #include "r_local.h" -#include "r_things.h" +#include "r_skins.h" #include "p_local.h" #include "p_setup.h" #include "s_sound.h" diff --git a/src/dehacked.c b/src/dehacked.c index dea0289b9..e6466f792 100644 --- a/src/dehacked.c +++ b/src/dehacked.c @@ -31,6 +31,7 @@ #include "r_data.h" #include "r_draw.h" #include "r_patch.h" +#include "r_things.h" // R_Char2Frame #include "r_sky.h" #include "fastcmp.h" #include "lua_script.h" diff --git a/src/f_wipe.c b/src/f_wipe.c index 8d12262ef..0fd6c1431 100644 --- a/src/f_wipe.c +++ b/src/f_wipe.c @@ -16,6 +16,7 @@ #include "i_video.h" #include "v_video.h" +#include "r_state.h" // fadecolormap #include "r_draw.h" // transtable #include "p_pspr.h" // tr_transxxx #include "p_local.h" diff --git a/src/g_game.c b/src/g_game.c index 08192bfb8..1bf76f33f 100644 --- a/src/g_game.c +++ b/src/g_game.c @@ -38,7 +38,7 @@ #include "byteptr.h" #include "i_joy.h" #include "r_local.h" -#include "r_things.h" +#include "r_skins.h" #include "y_inter.h" #include "v_video.h" #include "dehacked.h" // get_number (for ghost thok) diff --git a/src/hardware/hw3sound.c b/src/hardware/hw3sound.c index f7c6e1da0..7858640c0 100644 --- a/src/hardware/hw3sound.c +++ b/src/hardware/hw3sound.c @@ -28,7 +28,7 @@ #include "../tables.h" #include "../sounds.h" #include "../r_main.h" -#include "../r_things.h" +#include "../r_skins.h" #include "../m_random.h" #include "../p_local.h" #include "hw3dsdrv.h" diff --git a/src/lua_baselib.c b/src/lua_baselib.c index 03f142446..d001408be 100644 --- a/src/lua_baselib.c +++ b/src/lua_baselib.c @@ -21,7 +21,7 @@ #include "z_zone.h" #include "r_main.h" #include "r_draw.h" -#include "r_things.h" +#include "r_things.h" // R_Frame2Char etc #include "m_random.h" #include "s_sound.h" #include "g_game.h" diff --git a/src/lua_hooklib.c b/src/lua_hooklib.c index efed9adb7..92080e8ed 100644 --- a/src/lua_hooklib.c +++ b/src/lua_hooklib.c @@ -15,7 +15,7 @@ #include "doomstat.h" #include "p_mobj.h" #include "g_game.h" -#include "r_things.h" +#include "r_skins.h" #include "b_bot.h" #include "z_zone.h" diff --git a/src/lua_mobjlib.c b/src/lua_mobjlib.c index a7bd8da94..9e46344cf 100644 --- a/src/lua_mobjlib.c +++ b/src/lua_mobjlib.c @@ -13,7 +13,7 @@ #include "doomdef.h" #ifdef HAVE_BLUA #include "fastcmp.h" -#include "r_things.h" +#include "r_skins.h" #include "p_local.h" #include "g_game.h" #include "p_setup.h" diff --git a/src/lua_skinlib.c b/src/lua_skinlib.c index 3ade06042..f26aed2f0 100644 --- a/src/lua_skinlib.c +++ b/src/lua_skinlib.c @@ -13,7 +13,7 @@ #include "doomdef.h" #ifdef HAVE_BLUA #include "fastcmp.h" -#include "r_things.h" +#include "r_skins.h" #include "sounds.h" #include "lua_script.h" diff --git a/src/m_cond.c b/src/m_cond.c index 89058a488..0abc7adf8 100644 --- a/src/m_cond.c +++ b/src/m_cond.c @@ -18,7 +18,7 @@ #include "v_video.h" // video flags #include "g_game.h" // record info -#include "r_things.h" // numskins +#include "r_skins.h" // numskins #include "r_draw.h" // R_GetColorByName // Map triggers for linedef executors diff --git a/src/m_menu.h b/src/m_menu.h index 18b681ff0..e7270380d 100644 --- a/src/m_menu.h +++ b/src/m_menu.h @@ -15,9 +15,10 @@ #ifndef __X_MENU__ #define __X_MENU__ +#include "doomstat.h" // for NUMGAMETYPES #include "d_event.h" #include "command.h" -#include "r_things.h" // for SKINNAMESIZE +#include "r_skins.h" // for SKINNAMESIZE #include "f_finale.h" // for ttmode_enum // diff --git a/src/p_enemy.c b/src/p_enemy.c index 5d067ebc3..14e5c4d3e 100644 --- a/src/p_enemy.c +++ b/src/p_enemy.c @@ -21,7 +21,7 @@ #include "s_sound.h" #include "m_random.h" #include "m_misc.h" -#include "r_things.h" +#include "r_skins.h" #include "i_video.h" #include "z_zone.h" #include "lua_hook.h" diff --git a/src/p_mobj.c b/src/p_mobj.c index faee245d3..9194cf9f7 100644 --- a/src/p_mobj.c +++ b/src/p_mobj.c @@ -19,7 +19,7 @@ #include "p_local.h" #include "p_setup.h" #include "r_main.h" -#include "r_things.h" +#include "r_skins.h" #include "r_sky.h" #include "r_splats.h" #include "s_sound.h" diff --git a/src/p_saveg.c b/src/p_saveg.c index e8c6c7a84..2cd4bbbef 100644 --- a/src/p_saveg.c +++ b/src/p_saveg.c @@ -22,7 +22,7 @@ #include "p_setup.h" #include "p_saveg.h" #include "r_data.h" -#include "r_things.h" +#include "r_skins.h" #include "r_state.h" #include "w_wad.h" #include "y_inter.h" diff --git a/src/p_setup.c b/src/p_setup.c index 7b4c6773b..857bb1d43 100644 --- a/src/p_setup.c +++ b/src/p_setup.c @@ -27,7 +27,7 @@ #include "i_system.h" #include "r_data.h" -#include "r_things.h" +#include "r_things.h" // for R_AddSpriteDefs #include "r_patch.h" #include "r_sky.h" #include "r_draw.h" diff --git a/src/p_spec.c b/src/p_spec.c index d9bbab246..aecdb3b98 100644 --- a/src/p_spec.c +++ b/src/p_spec.c @@ -36,7 +36,7 @@ #include "m_cond.h" //unlock triggers #include "lua_hook.h" // LUAh_LinedefExecute #include "f_finale.h" // control text prompt -#include "r_things.h" // skins +#include "r_skins.h" // skins #ifdef HW3SOUND #include "hardware/hw3sound.h" diff --git a/src/p_user.c b/src/p_user.c index 9167d5345..c34d37264 100644 --- a/src/p_user.c +++ b/src/p_user.c @@ -22,7 +22,7 @@ #include "p_local.h" #include "r_main.h" #include "s_sound.h" -#include "r_things.h" +#include "r_skins.h" #include "d_think.h" #include "r_sky.h" #include "p_setup.h" diff --git a/src/r_skins.h b/src/r_skins.h index 4b83966ac..96697b422 100644 --- a/src/r_skins.h +++ b/src/r_skins.h @@ -16,6 +16,7 @@ #include "info.h" #include "sounds.h" +#include "d_player.h" // skinflags #include "r_patch.h" // spriteinfo_t #include "r_defs.h" // spritedef_t diff --git a/src/s_sound.c b/src/s_sound.c index 8193fdb9b..8e9461d78 100644 --- a/src/s_sound.c +++ b/src/s_sound.c @@ -27,7 +27,7 @@ extern INT32 msg_id; #include "g_game.h" #include "m_argv.h" #include "r_main.h" // R_PointToAngle2() used to calc stereo sep. -#include "r_things.h" // for skins +#include "r_skins.h" // for skins #include "i_system.h" #include "i_sound.h" #include "s_sound.h" diff --git a/src/sounds.c b/src/sounds.c index a9d720d5c..ca943c2d0 100644 --- a/src/sounds.c +++ b/src/sounds.c @@ -15,7 +15,7 @@ #include "i_sound.h" #include "sounds.h" #include "r_defs.h" -#include "r_things.h" +#include "r_skins.h" #include "z_zone.h" #include "w_wad.h" #include "lua_script.h" diff --git a/src/y_inter.c b/src/y_inter.c index 36cb64d06..6f23ef1b1 100644 --- a/src/y_inter.c +++ b/src/y_inter.c @@ -19,7 +19,7 @@ #include "i_video.h" #include "p_tick.h" #include "r_defs.h" -#include "r_things.h" +#include "r_skins.h" #include "s_sound.h" #include "st_stuff.h" #include "v_video.h" From 40e728fdb5d428532ecd5f9fa73a2ea0a4a83b73 Mon Sep 17 00:00:00 2001 From: Monster Iestyn Date: Mon, 9 Mar 2020 15:04:22 +0000 Subject: [PATCH 68/68] One line fix: don't assume 0 (aka SPR2_STND) is the default value for sprite2, but rather what the state sets for it This fixes some issues with a custom character tested during netplay, which did not have SPR2_WAIT sprites and therefore fell back to SPR2_STND sprites. Unfortunately, the fact they used SPR2_STND instead meant the sprite2 was not synced at all! --- src/p_saveg.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/p_saveg.c b/src/p_saveg.c index e8c6c7a84..fc8d9d4ee 100644 --- a/src/p_saveg.c +++ b/src/p_saveg.c @@ -1406,7 +1406,7 @@ static void SaveMobjThinker(const thinker_t *th, const UINT8 type) diff |= MD_TICS; if (mobj->sprite != mobj->state->sprite) diff |= MD_SPRITE; - if (mobj->sprite == SPR_PLAY && mobj->sprite2 != 0) + if (mobj->sprite == SPR_PLAY && mobj->sprite2 != (mobj->state->frame&FF_FRAMEMASK)) diff |= MD_SPRITE; if (mobj->frame != mobj->state->frame) diff |= MD_FRAME;