From 029e79024ba33f5b9ebfb08bba2be328df64fe14 Mon Sep 17 00:00:00 2001 From: Zwip-Zwap Zapony Date: Fri, 11 Dec 2020 23:43:38 +0100 Subject: [PATCH 001/126] V_DrawCroppedPatch Lua exposure and improvements Separated X and Y scale, and added colormap argument Added V_*SCALEPATCH and V_PERPLAYER flags support Made sx,sy,w,h into fixed-point values Exposed to Lua as "v.drawCropped(...)" (Also fix HWR_DrawStretchyFixedPatch ignoring vscale without pscale) --- src/console.c | 4 +- src/hardware/hw_draw.c | 147 ++++++++++++++++++++++++++++++----------- src/hardware/hw_main.h | 2 +- src/lua_hudlib.c | 40 +++++++++++ src/m_menu.c | 2 +- src/v_video.c | 71 +++++++++++++------- src/v_video.h | 2 +- src/y_inter.c | 2 +- 8 files changed, 202 insertions(+), 68 deletions(-) diff --git a/src/console.c b/src/console.c index b19b8818d..bcf01c989 100644 --- a/src/console.c +++ b/src/console.c @@ -1736,8 +1736,8 @@ static void CON_DrawBackpic(void) } // Draw the patch. - V_DrawCroppedPatch(x << FRACBITS, 0, FRACUNIT, V_NOSCALESTART, con_backpic, - 0, ( BASEVIDHEIGHT - h ), BASEVIDWIDTH, h); + V_DrawCroppedPatch(x << FRACBITS, 0, FRACUNIT, FRACUNIT, V_NOSCALESTART, con_backpic, NULL, + 0, (BASEVIDHEIGHT - h) << FRACBITS, BASEVIDWIDTH << FRACBITS, h << FRACBITS); // Unlock the cached patch. W_UnlockCachedPatch(con_backpic); diff --git a/src/hardware/hw_draw.c b/src/hardware/hw_draw.c index c5d362520..0322e9d27 100644 --- a/src/hardware/hw_draw.c +++ b/src/hardware/hw_draw.c @@ -317,7 +317,7 @@ void HWR_DrawStretchyFixedPatch(patch_t *gpatch, fixed_t x, fixed_t y, fixed_t p } } - if (pscale != FRACUNIT || (splitscreen && option & V_PERPLAYER)) + if (pscale != FRACUNIT || vscale != FRACUNIT || (splitscreen && option & V_PERPLAYER)) { fwidth = (float)(gpatch->width) * fscalew * dupx; fheight = (float)(gpatch->height) * fscaleh * dupy; @@ -382,7 +382,7 @@ void HWR_DrawStretchyFixedPatch(patch_t *gpatch, fixed_t x, fixed_t y, fixed_t p HWD.pfnDrawPolygon(NULL, v, 4, flags); } -void HWR_DrawCroppedPatch(patch_t *gpatch, fixed_t x, fixed_t y, fixed_t pscale, INT32 option, fixed_t sx, fixed_t sy, fixed_t w, fixed_t h) +void HWR_DrawCroppedPatch(patch_t *gpatch, fixed_t x, fixed_t y, fixed_t pscale, fixed_t vscale, INT32 option, const UINT8 *colormap, fixed_t sx, fixed_t sy, fixed_t w, fixed_t h) { FOutVector v[4]; FBITFIELD flags; @@ -395,13 +395,19 @@ void HWR_DrawCroppedPatch(patch_t *gpatch, fixed_t x, fixed_t y, fixed_t pscale, // | /| // |/ | // 0--1 - float dupx, dupy, fscale, fwidth, fheight; + float dupx, dupy, fscalew, fscaleh, fwidth, fheight; + + UINT8 perplayershuffle = 0; if (alphalevel >= 10 && alphalevel < 13) return; // make patch ready in hardware cache - HWR_GetPatch(gpatch); + if (!colormap) + HWR_GetPatch(gpatch); + else + HWR_GetMappedPatch(gpatch, colormap); + hwrPatch = ((GLPatch_t *)gpatch->hardware); dupx = (float)vid.dupx; @@ -423,12 +429,80 @@ void HWR_DrawCroppedPatch(patch_t *gpatch, fixed_t x, fixed_t y, fixed_t pscale, } dupx = dupy = (dupx < dupy ? dupx : dupy); - fscale = FIXED_TO_FLOAT(pscale); + fscalew = fscaleh = FIXED_TO_FLOAT(pscale); + if (vscale != pscale) + fscaleh = FIXED_TO_FLOAT(vscale); - // fuck it, no GL support for croppedpatch v_perplayer right now. it's not like it's accessible to Lua or anything, and we only use it for menus... + cx -= (float)(gpatch->leftoffset) * fscalew; + cy -= (float)(gpatch->topoffset) * fscaleh; - cy -= (float)(gpatch->topoffset) * fscale; - cx -= (float)(gpatch->leftoffset) * fscale; + if (splitscreen && (option & V_PERPLAYER)) + { + float adjusty = ((option & V_NOSCALESTART) ? vid.height : BASEVIDHEIGHT)/2.0f; + fscaleh /= 2; + cy /= 2; +#ifdef QUADS + if (splitscreen > 1) // 3 or 4 players + { + float adjustx = ((option & V_NOSCALESTART) ? vid.width : BASEVIDWIDTH)/2.0f; + fscalew /= 2; + cx /= 2; + if (stplyr == &players[displayplayer]) + { + if (!(option & (V_SNAPTOTOP|V_SNAPTOBOTTOM))) + perplayershuffle |= 1; + if (!(option & (V_SNAPTOLEFT|V_SNAPTORIGHT))) + perplayershuffle |= 4; + option &= ~V_SNAPTOBOTTOM|V_SNAPTORIGHT; + } + else if (stplyr == &players[secondarydisplayplayer]) + { + if (!(option & (V_SNAPTOTOP|V_SNAPTOBOTTOM))) + perplayershuffle |= 1; + if (!(option & (V_SNAPTOLEFT|V_SNAPTORIGHT))) + perplayershuffle |= 8; + cx += adjustx; + option &= ~V_SNAPTOBOTTOM|V_SNAPTOLEFT; + } + else if (stplyr == &players[thirddisplayplayer]) + { + if (!(option & (V_SNAPTOTOP|V_SNAPTOBOTTOM))) + perplayershuffle |= 2; + if (!(option & (V_SNAPTOLEFT|V_SNAPTORIGHT))) + perplayershuffle |= 4; + cy += adjusty; + option &= ~V_SNAPTOTOP|V_SNAPTORIGHT; + } + else if (stplyr == &players[fourthdisplayplayer]) + { + if (!(option & (V_SNAPTOTOP|V_SNAPTOBOTTOM))) + perplayershuffle |= 2; + if (!(option & (V_SNAPTOLEFT|V_SNAPTORIGHT))) + perplayershuffle |= 8; + cx += adjustx; + cy += adjusty; + option &= ~V_SNAPTOTOP|V_SNAPTOLEFT; + } + } + else +#endif + // 2 players + { + if (stplyr == &players[displayplayer]) + { + if (!(option & (V_SNAPTOTOP|V_SNAPTOBOTTOM))) + perplayershuffle = 1; + option &= ~V_SNAPTOBOTTOM; + } + else //if (stplyr == &players[secondarydisplayplayer]) + { + if (!(option & (V_SNAPTOTOP|V_SNAPTOBOTTOM))) + perplayershuffle = 2; + cy += adjusty; + option &= ~V_SNAPTOTOP; + } + } + } if (!(option & V_NOSCALESTART)) { @@ -437,18 +511,9 @@ void HWR_DrawCroppedPatch(patch_t *gpatch, fixed_t x, fixed_t y, fixed_t pscale, if (!(option & V_SCALEPATCHMASK)) { - // if it's meant to cover the whole screen, black out the rest (ONLY IF TOP LEFT ISN'T TRANSPARENT) - // cx and cy are possibly *slightly* off from float maths - // This is done before here compared to software because we directly alter cx and cy to centre - if (cx >= -0.1f && cx <= 0.1f && gpatch->width == BASEVIDWIDTH && cy >= -0.1f && cy <= 0.1f && gpatch->height == BASEVIDHEIGHT) - { - const column_t *column = (const column_t *)((const UINT8 *)(gpatch->columns) + (gpatch->columnofs[0])); - if (!column->topdelta) - { - const UINT8 *source = (const UINT8 *)(column) + 3; - HWR_DrawFill(0, 0, BASEVIDWIDTH, BASEVIDHEIGHT, (column->topdelta == 0xff ? 31 : source[0])); - } - } + // if it's meant to cover the whole screen, black out the rest + // no the patch is cropped do not do this ever + // centre screen if (fabsf((float)vid.width - (float)BASEVIDWIDTH * dupx) > 1.0E-36f) { @@ -456,6 +521,10 @@ void HWR_DrawCroppedPatch(patch_t *gpatch, fixed_t x, fixed_t y, fixed_t pscale, cx += ((float)vid.width - ((float)BASEVIDWIDTH * dupx)); else if (!(option & V_SNAPTOLEFT)) cx += ((float)vid.width - ((float)BASEVIDWIDTH * dupx))/2; + if (perplayershuffle & 4) + cx -= ((float)vid.width - ((float)BASEVIDWIDTH * dupx))/4; + else if (perplayershuffle & 8) + cx += ((float)vid.width - ((float)BASEVIDWIDTH * dupx))/4; } if (fabsf((float)vid.height - (float)BASEVIDHEIGHT * dupy) > 1.0E-36f) { @@ -463,23 +532,27 @@ void HWR_DrawCroppedPatch(patch_t *gpatch, fixed_t x, fixed_t y, fixed_t pscale, cy += ((float)vid.height - ((float)BASEVIDHEIGHT * dupy)); else if (!(option & V_SNAPTOTOP)) cy += ((float)vid.height - ((float)BASEVIDHEIGHT * dupy))/2; + if (perplayershuffle & 1) + cy -= ((float)vid.height - ((float)BASEVIDHEIGHT * dupy))/4; + else if (perplayershuffle & 2) + cy += ((float)vid.height - ((float)BASEVIDHEIGHT * dupy))/4; } } } - fwidth = w; - fheight = h; + fwidth = FIXED_TO_FLOAT(w); + fheight = FIXED_TO_FLOAT(h); - if (fwidth > gpatch->width) - fwidth = gpatch->width; + if (sx + w > gpatch->width<width< gpatch->height) - fheight = gpatch->height; + if (sy + h > gpatch->height<height<width))*hwrPatch->max_s; - if (sx + w > gpatch->width) - v[2].s = v[1].s = hwrPatch->max_s - ((sx+w)/(float)(gpatch->width))*hwrPatch->max_s; + v[0].s = v[3].s = (FIXED_TO_FLOAT(sx)/(float)(gpatch->width))*hwrPatch->max_s; + if (sx + w > gpatch->width<max_s; else - v[2].s = v[1].s = ((sx+w)/(float)(gpatch->width))*hwrPatch->max_s; + v[2].s = v[1].s = (FIXED_TO_FLOAT(sx+w)/(float)(gpatch->width))*hwrPatch->max_s; - v[0].t = v[1].t = ((sy)/(float)(gpatch->height))*hwrPatch->max_t; - if (sy + h > gpatch->height) - v[2].t = v[3].t = hwrPatch->max_t - ((sy+h)/(float)(gpatch->height))*hwrPatch->max_t; + v[0].t = v[1].t = (FIXED_TO_FLOAT(sy)/(float)(gpatch->height))*hwrPatch->max_t; + if (sy + h > gpatch->height<max_t; else - v[2].t = v[3].t = ((sy+h)/(float)(gpatch->height))*hwrPatch->max_t; + v[2].t = v[3].t = (FIXED_TO_FLOAT(sy+h)/(float)(gpatch->height))*hwrPatch->max_t; flags = PF_Translucent|PF_NoDepthTest; diff --git a/src/hardware/hw_main.h b/src/hardware/hw_main.h index 4ad09aa3d..708227585 100644 --- a/src/hardware/hw_main.h +++ b/src/hardware/hw_main.h @@ -39,7 +39,7 @@ void HWR_InitTextureMapping(void); void HWR_SetViewSize(void); void HWR_DrawPatch(patch_t *gpatch, INT32 x, INT32 y, INT32 option); void HWR_DrawStretchyFixedPatch(patch_t *gpatch, fixed_t x, fixed_t y, fixed_t pscale, fixed_t vscale, INT32 option, const UINT8 *colormap); -void HWR_DrawCroppedPatch(patch_t *gpatch, fixed_t x, fixed_t y, fixed_t scale, INT32 option, fixed_t sx, fixed_t sy, fixed_t w, fixed_t h); +void HWR_DrawCroppedPatch(patch_t *gpatch, fixed_t x, fixed_t y, fixed_t pscale, fixed_t vscale, INT32 option, const UINT8 *colormap, fixed_t sx, fixed_t sy, fixed_t w, fixed_t h); void HWR_MakePatch(const patch_t *patch, GLPatch_t *grPatch, GLMipmap_t *grMipmap, boolean makebitmap); void HWR_CreatePlanePolygons(INT32 bspnum); void HWR_CreateStaticLightmaps(INT32 bspnum); diff --git a/src/lua_hudlib.c b/src/lua_hudlib.c index e58cd4a58..20eb33f37 100644 --- a/src/lua_hudlib.c +++ b/src/lua_hudlib.c @@ -660,6 +660,45 @@ static int libd_drawStretched(lua_State *L) return 0; } +static int libd_drawCropped(lua_State *L) +{ + fixed_t x, y, hscale, vscale, sx, sy, w, h; + INT32 flags; + patch_t *patch; + const UINT8 *colormap = NULL; + + HUDONLY + x = luaL_checkinteger(L, 1); + y = luaL_checkinteger(L, 2); + hscale = luaL_checkinteger(L, 3); + if (hscale < 0) + return luaL_error(L, "negative horizontal scale"); + vscale = luaL_checkinteger(L, 4); + if (vscale < 0) + return luaL_error(L, "negative vertical scale"); + patch = *((patch_t **)luaL_checkudata(L, 5, META_PATCH)); + flags = luaL_checkinteger(L, 6); + if (!lua_isnoneornil(L, 7)) + colormap = *((UINT8 **)luaL_checkudata(L, 7, META_COLORMAP)); + sx = luaL_checkinteger(L, 8); + if (sx < 0) // Don't crash. Now, we could do "x-=sx*FRACUNIT; sx=0;" here... + return luaL_error(L, "negative crop sx"); + sy = luaL_checkinteger(L, 9); + if (sy < 0) // ...but it's more truthful to just deny it, as negative values would crash + return luaL_error(L, "negative crop sy"); + w = luaL_checkinteger(L, 10); + if (w < 0) // Again, don't crash + return luaL_error(L, "negative crop w"); + h = luaL_checkinteger(L, 11); + if (h < 0) + return luaL_error(L, "negative crop h"); + + flags &= ~V_PARAMMASK; // Don't let crashes happen. + + V_DrawCroppedPatch(x, y, hscale, vscale, flags, patch, colormap, sx, sy, w, h); + return 0; +} + static int libd_drawNum(lua_State *L) { INT32 x, y, flags, num; @@ -1084,6 +1123,7 @@ static luaL_Reg lib_draw[] = { {"draw", libd_draw}, {"drawScaled", libd_drawScaled}, {"drawStretched", libd_drawStretched}, + {"drawCropped", libd_drawCropped}, {"drawNum", libd_drawNum}, {"drawPaddedNum", libd_drawPaddedNum}, {"drawFill", libd_drawFill}, diff --git a/src/m_menu.c b/src/m_menu.c index 5ec9132f7..003e308b0 100644 --- a/src/m_menu.c +++ b/src/m_menu.c @@ -4183,7 +4183,7 @@ static void M_DrawStaticBox(fixed_t x, fixed_t y, INT32 flags, fixed_t w, fixed_ if (staticalong > pw) // simplified for base LSSTATIC staticalong -= pw; - V_DrawCroppedPatch(x<> V_SCALEPATCHSHIFT) + { + case 1: // V_NOSCALEPATCH + dupx = dupy = 1; + break; + case 2: // V_SMALLSCALEPATCH + dupx = vid.smalldupx; + dupy = vid.smalldupy; + break; + case 3: // V_MEDSCALEPATCH + dupx = vid.meddupx; + dupy = vid.meddupy; + break; + default: + break; + } + + // only use one dup, to avoid stretching (har har) + dupx = dupy = (dupx < dupy ? dupx : dupy); + fdup = vdup = FixedMul(dupx<topoffset<leftoffset<topoffset<>= 1; + vdup >>= 1; rowfrac <<= 1; y >>= 1; - sy >>= 1; - h >>= 1; #ifdef QUADS if (splitscreen > 1) // 3 or 4 players { fixed_t adjustx = ((scrn & V_NOSCALESTART) ? vid.height : BASEVIDHEIGHT)<<(FRACBITS-1)); + fdup >>= 1; colfrac <<= 1; x >>= 1; - sx >>= 1; - w >>= 1; if (stplyr == &players[displayplayer]) { if (!(scrn & (V_SNAPTOTOP|V_SNAPTOBOTTOM))) @@ -896,7 +921,6 @@ void V_DrawCroppedPatch(fixed_t x, fixed_t y, fixed_t pscale, INT32 scrn, patch_ if (!(scrn & (V_SNAPTOLEFT|V_SNAPTORIGHT))) perplayershuffle |= 8; x += adjustx; - sx += adjustx; scrn &= ~V_SNAPTOBOTTOM|V_SNAPTOLEFT; } else if (stplyr == &players[thirddisplayplayer]) @@ -906,7 +930,6 @@ void V_DrawCroppedPatch(fixed_t x, fixed_t y, fixed_t pscale, INT32 scrn, patch_ if (!(scrn & (V_SNAPTOLEFT|V_SNAPTORIGHT))) perplayershuffle |= 4; y += adjusty; - sy += adjusty; scrn &= ~V_SNAPTOTOP|V_SNAPTORIGHT; } else //if (stplyr == &players[fourthdisplayplayer]) @@ -916,9 +939,7 @@ void V_DrawCroppedPatch(fixed_t x, fixed_t y, fixed_t pscale, INT32 scrn, patch_ if (!(scrn & (V_SNAPTOLEFT|V_SNAPTORIGHT))) perplayershuffle |= 8; x += adjustx; - sx += adjustx; y += adjusty; - sy += adjusty; scrn &= ~V_SNAPTOTOP|V_SNAPTOLEFT; } } @@ -937,7 +958,6 @@ void V_DrawCroppedPatch(fixed_t x, fixed_t y, fixed_t pscale, INT32 scrn, patch_ if (!(scrn & (V_SNAPTOTOP|V_SNAPTOBOTTOM))) perplayershuffle |= 2; y += adjusty; - sy += adjusty; scrn &= ~V_SNAPTOTOP; } } @@ -950,7 +970,8 @@ void V_DrawCroppedPatch(fixed_t x, fixed_t y, fixed_t pscale, INT32 scrn, patch_ deststop = desttop + vid.rowbytes * vid.height; - if (scrn & V_NOSCALESTART) { + if (scrn & V_NOSCALESTART) + { x >>= FRACBITS; y >>= FRACBITS; desttop += (y*vid.width) + x; @@ -998,7 +1019,7 @@ void V_DrawCroppedPatch(fixed_t x, fixed_t y, fixed_t pscale, INT32 scrn, patch_ desttop += (y*vid.width) + x; } - for (col = sx<>FRACBITS) < patch->width && ((col>>FRACBITS) - sx) < w; col += colfrac, ++x, desttop++) + for (col = sx; (col>>FRACBITS) < patch->width && (col - sx) < w; col += colfrac, ++x, desttop++) { INT32 topdelta, prevdelta = -1; if (x < 0) // don't draw off the left of the screen (WRAP PREVENTION) @@ -1015,15 +1036,15 @@ void V_DrawCroppedPatch(fixed_t x, fixed_t y, fixed_t pscale, INT32 scrn, patch_ prevdelta = topdelta; source = (const UINT8 *)(column) + 3; dest = desttop; - if (topdelta-sy > 0) + if ((topdelta< 0) { - dest += FixedInt(FixedMul((topdelta-sy)<>FRACBITS) < column->length && (((ofs>>FRACBITS) - sy) + topdelta) < h; ofs += rowfrac) + for (; dest < deststop && (ofs>>FRACBITS) < column->length && ((ofs - sy) + (topdelta<= screens[scrn&V_PARAMMASK]) // don't draw off the top of the screen (CRASH PREVENTION) *dest = patchdrawfunc(dest, source, ofs); diff --git a/src/v_video.h b/src/v_video.h index 8a18f82ad..f3f169079 100644 --- a/src/v_video.h +++ b/src/v_video.h @@ -165,7 +165,7 @@ void V_CubeApply(UINT8 *red, UINT8 *green, UINT8 *blue); #define V_DrawSciencePatch(x,y,s,p,sc) V_DrawFixedPatch(x,y,sc,s,p,NULL) #define V_DrawFixedPatch(x,y,sc,s,p,c) V_DrawStretchyFixedPatch(x,y,sc,sc,s,p,c) void V_DrawStretchyFixedPatch(fixed_t x, fixed_t y, fixed_t pscale, fixed_t vscale, INT32 scrn, patch_t *patch, const UINT8 *colormap); -void V_DrawCroppedPatch(fixed_t x, fixed_t y, fixed_t pscale, INT32 scrn, patch_t *patch, fixed_t sx, fixed_t sy, fixed_t w, fixed_t h); +void V_DrawCroppedPatch(fixed_t x, fixed_t y, fixed_t pscale, fixed_t vscale, INT32 scrn, patch_t *patch, const UINT8 *colormap, fixed_t sx, fixed_t sy, fixed_t w, fixed_t h); void V_DrawContinueIcon(INT32 x, INT32 y, INT32 flags, INT32 skinnum, UINT16 skincolor); diff --git a/src/y_inter.c b/src/y_inter.c index 061cbb5e1..f2f676919 100644 --- a/src/y_inter.c +++ b/src/y_inter.c @@ -212,7 +212,7 @@ static void Y_IntermissionTokenDrawer(void) calc = (lowy - y)*2; if (calc > 0) - V_DrawCroppedPatch(32<width, calc); + V_DrawCroppedPatch(32<width< Date: Sat, 23 May 2020 17:06:10 -0500 Subject: [PATCH 002/126] Make the colormap returned by v.getColormap() writable. I mean it was already readable anyway... --- src/lua_hudlib.c | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/src/lua_hudlib.c b/src/lua_hudlib.c index e58cd4a58..0da0cb662 100644 --- a/src/lua_hudlib.c +++ b/src/lua_hudlib.c @@ -277,7 +277,7 @@ static int hudinfo_num(lua_State *L) static int colormap_get(lua_State *L) { - const UINT8 *colormap = *((UINT8 **)luaL_checkudata(L, 1, META_COLORMAP)); + UINT8 *colormap = *((UINT8 **)luaL_checkudata(L, 1, META_COLORMAP)); UINT32 i = luaL_checkinteger(L, 2); if (i >= 256) return luaL_error(L, "colormap index %d out of range (0 - %d)", i, 255); @@ -285,6 +285,16 @@ static int colormap_get(lua_State *L) return 1; } +static int colormap_set(lua_State *L) +{ + UINT8 *colormap = *((UINT8 **)luaL_checkudata(L, 1, META_COLORMAP)); + UINT32 i = luaL_checkinteger(L, 2); + if (i >= 256) + return luaL_error(L, "colormap index %d out of range (0 - %d)", i, 255); + colormap[i] = (UINT8)luaL_checkinteger(L, 3); + return 0; +} + static int patch_get(lua_State *L) { patch_t *patch = *((patch_t **)luaL_checkudata(L, 1, META_PATCH)); @@ -1230,6 +1240,9 @@ int LUA_HudLib(lua_State *L) luaL_newmetatable(L, META_COLORMAP); lua_pushcfunction(L, colormap_get); lua_setfield(L, -2, "__index"); + + lua_pushcfunction(L, colormap_set); + lua_setfield(L, -2, "__newindex"); lua_pop(L,1); luaL_newmetatable(L, META_PATCH); From 81ee4a75e3186a4936941c33a06f97b74f863f22 Mon Sep 17 00:00:00 2001 From: GoldenTails Date: Sat, 28 Nov 2020 00:09:12 -0600 Subject: [PATCH 003/126] Copy colormaps so Lua cannot modify cached colormaps! (And Z_Free them on garbage collection.) --- src/lua_hudlib.c | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/src/lua_hudlib.c b/src/lua_hudlib.c index 0da0cb662..ab091eb2f 100644 --- a/src/lua_hudlib.c +++ b/src/lua_hudlib.c @@ -295,6 +295,13 @@ static int colormap_set(lua_State *L) return 0; } +static int colormap_free(lua_State *L) +{ + UINT8 *colormap = *((UINT8 **)luaL_checkudata(L, 1, META_COLORMAP)); + Z_Free(colormap); + return 0; +} + static int patch_get(lua_State *L) { patch_t *patch = *((patch_t **)luaL_checkudata(L, 1, META_PATCH)); @@ -921,7 +928,7 @@ static int libd_getColormap(lua_State *L) // all was successful above, now we generate the colormap at last! - colormap = R_GetTranslationColormap(skinnum, color, GTC_CACHE); + colormap = R_GetTranslationColormap(skinnum, color, 0); LUA_PushUserdata(L, colormap, META_COLORMAP); // push as META_COLORMAP userdata, specifically for patches to use! return 1; } @@ -930,10 +937,14 @@ static int libd_getStringColormap(lua_State *L) { INT32 flags = luaL_checkinteger(L, 1); UINT8* colormap = NULL; + UINT8* lua_colormap = NULL; HUDONLY colormap = V_GetStringColormap(flags & V_CHARCOLORMASK); if (colormap) { - LUA_PushUserdata(L, colormap, META_COLORMAP); // push as META_COLORMAP userdata, specifically for patches to use! + lua_colormap = Z_Malloc(256 * sizeof(UINT8), PU_LUA, NULL); + memcpy(lua_colormap, colormap, 256 * sizeof(UINT8)); + + LUA_PushUserdata(L, lua_colormap, META_COLORMAP); // push as META_COLORMAP userdata, specifically for patches to use! return 1; } return 0; @@ -1243,6 +1254,9 @@ int LUA_HudLib(lua_State *L) lua_pushcfunction(L, colormap_set); lua_setfield(L, -2, "__newindex"); + + lua_pushcfunction(L, colormap_free); + lua_setfield(L, -2, "__gc"); lua_pop(L,1); luaL_newmetatable(L, META_PATCH); From 794d92767032f718e6c31aa62f3125cd21b2511f Mon Sep 17 00:00:00 2001 From: Shane Ellis Date: Fri, 15 Jan 2021 16:44:26 -0500 Subject: [PATCH 004/126] Add conditions for new player bot type --- src/p_inter.c | 64 ++++++++++++++++++++++++--------------------------- 1 file changed, 30 insertions(+), 34 deletions(-) diff --git a/src/p_inter.c b/src/p_inter.c index e9a16a3dd..778ec703b 100644 --- a/src/p_inter.c +++ b/src/p_inter.c @@ -151,7 +151,7 @@ boolean P_CanPickupItem(player_t *player, boolean weapon) if (!player->mo || player->mo->health <= 0) return false; - if (player->bot) + if (player->bot && player->bot != 3) { if (weapon) return false; @@ -178,7 +178,7 @@ void P_DoNightsScore(player_t *player) return; // Don't do any fancy shit for failures. dummymo = P_SpawnMobj(player->mo->x, player->mo->y, player->mo->z+player->mo->height/2, MT_NIGHTSCORE); - if (player->bot) + if (player->bot && player->bot != 3) player = &players[consoleplayer]; if (G_IsSpecialStage(gamemap)) // Global link count? Maybe not a good idea... @@ -470,14 +470,14 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck) if (!(player->charability2 == CA2_MELEE && player->panim == PA_ABILITY2)) { fixed_t setmomz = -toucher->momz; // Store this, momz get changed by P_DoJump within P_DoBubbleBounce - + if (elementalpierce == 2) // Reset bubblewrap, part 1 P_DoBubbleBounce(player); toucher->momz = setmomz; if (elementalpierce == 2) // Reset bubblewrap, part 2 { boolean underwater = toucher->eflags & MFE_UNDERWATER; - + if (underwater) toucher->momz /= 2; toucher->momz -= (toucher->momz/(underwater ? 8 : 4)); // Cap the height! @@ -630,7 +630,7 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck) // ***************************** // // Special Stage Token case MT_TOKEN: - if (player->bot) + if (player->bot && player->bot != 3) return; P_AddPlayerScore(player, 1000); @@ -670,7 +670,7 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck) // Emerald Hunt case MT_EMERHUNT: - if (player->bot) + if (player->bot && player->bot != 3) return; if (hunt1 == special) @@ -701,7 +701,7 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck) case MT_EMERALD5: case MT_EMERALD6: case MT_EMERALD7: - if (player->bot) + if (player->bot && player->bot != 3) return; if (special->threshold) @@ -738,7 +738,7 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck) // Secret emblem thingy case MT_EMBLEM: { - if (demoplayback || player->bot) + if (demoplayback || (player->bot && player->bot != 3)) return; emblemlocations[special->health-1].collected = true; @@ -751,7 +751,7 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck) // CTF Flags case MT_REDFLAG: case MT_BLUEFLAG: - if (player->bot) + if (player->bot && player->bot != 3) return; if (player->powers[pw_flashing] || player->tossdelay) return; @@ -826,7 +826,7 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck) { boolean spec = G_IsSpecialStage(gamemap); boolean cangiveemmy = false; - if (player->bot) + if (player->bot && player->bot != 3) return; if (player->exiting) return; @@ -1072,7 +1072,7 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck) } return; case MT_EGGCAPSULE: - if (player->bot) + if (player->bot && player->bot != 3) return; // make sure everything is as it should be, THEN take rings from players in special stages @@ -1164,7 +1164,7 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck) } return; case MT_NIGHTSSUPERLOOP: - if (player->bot || !(player->powers[pw_carry] == CR_NIGHTSMODE)) + if ((player->bot && player->bot != 3) || !(player->powers[pw_carry] == CR_NIGHTSMODE)) return; if (!G_IsSpecialStage(gamemap)) player->powers[pw_nights_superloop] = (UINT16)special->info->speed; @@ -1186,7 +1186,7 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck) } break; case MT_NIGHTSDRILLREFILL: - if (player->bot || !(player->powers[pw_carry] == CR_NIGHTSMODE)) + if ((player->bot && player->bot != 3) || !(player->powers[pw_carry] == CR_NIGHTSMODE)) return; if (!G_IsSpecialStage(gamemap)) player->drillmeter = special->info->speed; @@ -1208,7 +1208,7 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck) } break; case MT_NIGHTSHELPER: - if (player->bot || !(player->powers[pw_carry] == CR_NIGHTSMODE)) + if ((player->bot && player->bot != 3) || !(player->powers[pw_carry] == CR_NIGHTSMODE)) return; if (!G_IsSpecialStage(gamemap)) { @@ -1240,7 +1240,7 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck) } break; case MT_NIGHTSEXTRATIME: - if (player->bot || !(player->powers[pw_carry] == CR_NIGHTSMODE)) + if ((player->bot && player->bot != 3) || !(player->powers[pw_carry] == CR_NIGHTSMODE)) return; if (!G_IsSpecialStage(gamemap)) { @@ -1272,7 +1272,7 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck) } break; case MT_NIGHTSLINKFREEZE: - if (player->bot || !(player->powers[pw_carry] == CR_NIGHTSMODE)) + if ((player->bot && player->bot != 3) || !(player->powers[pw_carry] == CR_NIGHTSMODE)) return; if (!G_IsSpecialStage(gamemap)) { @@ -1332,7 +1332,7 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck) if (playeringame[i] && players[i].powers[pw_carry] == CR_NIGHTSMODE) players[i].drillmeter += TICRATE/2; } - else if (player->bot) + else if (player->bot && player->bot != 3) players[consoleplayer].drillmeter += TICRATE/2; else player->drillmeter += TICRATE/2; @@ -1385,13 +1385,9 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck) thinker_t *th; mobj_t *mo2; - if (player->bot) + if (player->bot && player->bot != 3) return; - // Initialize my junk - junk.tags.tags = NULL; - junk.tags.count = 0; - Tag_FSet(&junk.tags, LE_AXE); EV_DoElevator(&junk, bridgeFall, false); @@ -1423,7 +1419,7 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck) return; } case MT_FIREFLOWER: - if (player->bot) + if (player->bot && player->bot != 3) return; S_StartSound(toucher, sfx_mario3); @@ -1617,7 +1613,7 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck) if (special->tracer && !(special->tracer->flags2 & MF2_STRONGBOX)) macespin = true; - + if (macespin ? (player->powers[pw_ignorelatch] & (1<<15)) : (player->powers[pw_ignorelatch])) return; @@ -1685,7 +1681,7 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck) return; // Only go in the mouth // Eaten by player! - if ((!player->bot) && (player->powers[pw_underwater] && player->powers[pw_underwater] <= 12*TICRATE + 1)) + if ((!player->bot || player->bot == 3) && (player->powers[pw_underwater] && player->powers[pw_underwater] <= 12*TICRATE + 1)) { player->powers[pw_underwater] = underwatertics + 1; P_RestoreMusic(player); @@ -1696,7 +1692,7 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck) if (!player->climbing) { - if (player->bot && toucher->state-states != S_PLAY_GASP) + if (player->bot && player->bot != 3 && toucher->state-states != S_PLAY_GASP) S_StartSound(toucher, special->info->deathsound); // Force it to play a sound for bots P_SetPlayerMobjState(toucher, S_PLAY_GASP); P_ResetPlayer(player); @@ -1704,7 +1700,7 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck) toucher->momx = toucher->momy = toucher->momz = 0; - if (player->bot) + if (player->bot && player->bot != 3) return; else break; @@ -1736,7 +1732,7 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck) return; case MT_MINECARTSPAWNER: - if (!player->bot && special->fuse <= TICRATE && player->powers[pw_carry] != CR_MINECART && !(player->powers[pw_ignorelatch] & (1<<15))) + if (!player->bot && player->bot != 3 && special->fuse <= TICRATE && player->powers[pw_carry] != CR_MINECART && !(player->powers[pw_ignorelatch] & (1<<15))) { mobj_t *mcart = P_SpawnMobj(special->x, special->y, special->z, MT_MINECART); P_SetTarget(&mcart->target, toucher); @@ -1789,7 +1785,7 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck) } return; default: // SOC or script pickup - if (player->bot) + if (player->bot && player->bot != 3) return; P_SetTarget(&special->target, toucher); break; @@ -1813,7 +1809,7 @@ void P_TouchStarPost(mobj_t *post, player_t *player, boolean snaptopost) mobj_t *toucher = player->mo; mobj_t *checkbase = snaptopost ? post : toucher; - if (player->bot) + if (player->bot && player->bot != 3) return; // In circuit, player must have touched all previous starposts if (circuitmap @@ -2555,7 +2551,7 @@ void P_KillMobj(mobj_t *target, mobj_t *inflictor, mobj_t *source, UINT8 damaget if ((target->player->lives <= 1) && (netgame || multiplayer) && G_GametypeUsesCoopLives() && (cv_cooplives.value == 0)) ; - else if (!target->player->bot && !target->player->spectator && (target->player->lives != INFLIVES) + else if ((!target->player->bot || target->player->bot == 3) && !target->player->spectator && (target->player->lives != INFLIVES) && G_GametypeUsesLives()) { if (!(target->player->pflags & PF_FINISHED)) @@ -3475,7 +3471,7 @@ void P_SpecialStageDamage(player_t *player, mobj_t *inflictor, mobj_t *source) if (inflictor && inflictor->type == MT_LHRT) return; - if (player->powers[pw_shield] || player->bot) //If One-Hit Shield + if (player->powers[pw_shield] || (player->bot && player->bot != 3)) //If One-Hit Shield { P_RemoveShield(player); S_StartSound(player->mo, sfx_shldls); // Ba-Dum! Shield loss. @@ -3566,7 +3562,7 @@ boolean P_DamageMobj(mobj_t *target, mobj_t *inflictor, mobj_t *source, INT32 da return false; // Make sure that boxes cannot be popped by enemies, red rings, etc. - if (target->flags & MF_MONITOR && ((!source || !source->player || source->player->bot) + if (target->flags & MF_MONITOR && ((!source || !source->player || (source->player->bot && source->player->bot != 3)) || (inflictor && (inflictor->type == MT_REDRING || (inflictor->type >= MT_THROWNBOUNCE && inflictor->type <= MT_THROWNGRENADE))))) return false; } @@ -3701,7 +3697,7 @@ boolean P_DamageMobj(mobj_t *target, mobj_t *inflictor, mobj_t *source, INT32 da } else if (LUAh_MobjDamage(target, inflictor, source, damage, damagetype)) return true; - else if (player->powers[pw_shield] || (player->bot && !ultimatemode)) //If One-Hit Shield + else if (player->powers[pw_shield] || (player->bot && player->bot != 3 && !ultimatemode)) //If One-Hit Shield { P_ShieldDamage(player, inflictor, source, damage, damagetype); damage = 0; From 4b9a95a53837166a080b200b34982ce867a31ffd Mon Sep 17 00:00:00 2001 From: Shane Ellis Date: Fri, 15 Jan 2021 16:46:15 -0500 Subject: [PATCH 005/126] Add conditions for new player bot type --- src/p_enemy.c | 12 ++---------- 1 file changed, 2 insertions(+), 10 deletions(-) diff --git a/src/p_enemy.c b/src/p_enemy.c index 203e04af1..38df59855 100644 --- a/src/p_enemy.c +++ b/src/p_enemy.c @@ -743,7 +743,7 @@ boolean P_LookForPlayers(mobj_t *actor, boolean allaround, boolean tracer, fixed if (player->mo->health <= 0) continue; // dead - if (player->bot) + if (player->bot && player->bot != 3) continue; // ignore bots if (player->quittime) @@ -1834,7 +1834,7 @@ void A_SnailerThink(mobj_t *actor) fixed_t dist; fixed_t dx, dy; - dist = R_PointToDist2(0, 0, actor->x - actor->target->x, actor->y - actor->target->y); + dist = P_AproxDistance(actor->x - actor->target->x, actor->y - actor->target->y); if (an > ANGLE_45 && an <= ANGLE_90) // fire at 45 degrees to the left { @@ -3924,10 +3924,6 @@ void A_BossDeath(mobj_t *mo) } else { - // Initialize my junk - junk.tags.tags = NULL; - junk.tags.count = 0; - // Bring the egg trap up to the surface // Incredibly shitty code ahead Tag_FSet(&junk.tags, LE_CAPSULE0); @@ -4057,10 +4053,6 @@ bossjustdie: } case MT_KOOPA: { - // Initialize my junk - junk.tags.tags = NULL; - junk.tags.count = 0; - Tag_FSet(&junk.tags, LE_KOOPA); EV_DoCeiling(&junk, raiseToHighest); return; From b995e3cb75b67116d51583c1ae85debf25013621 Mon Sep 17 00:00:00 2001 From: Shane Ellis Date: Fri, 15 Jan 2021 16:48:19 -0500 Subject: [PATCH 006/126] Add conditions for new player bot type --- src/p_user.c | 125 ++++++++++++++++++++------------------------------- 1 file changed, 48 insertions(+), 77 deletions(-) diff --git a/src/p_user.c b/src/p_user.c index a70dceb8b..2466310bb 100644 --- a/src/p_user.c +++ b/src/p_user.c @@ -1189,7 +1189,7 @@ void P_GivePlayerRings(player_t *player, INT32 num_rings) if (!player) return; - if (player->bot) + if (player->bot && player->bot != 3) player = &players[consoleplayer]; if (!player->mo) @@ -1234,7 +1234,7 @@ void P_GivePlayerSpheres(player_t *player, INT32 num_spheres) if (!player) return; - if (player->bot) + if (player->bot && player->bot != 3) player = &players[consoleplayer]; if (!player->mo) @@ -1261,7 +1261,7 @@ void P_GivePlayerLives(player_t *player, INT32 numlives) if (!player) return; - if (player->bot) + if (player->bot && player->bot != 3) player = &players[consoleplayer]; if (gamestate == GS_LEVEL) @@ -1341,7 +1341,7 @@ void P_DoSuperTransformation(player_t *player, boolean giverings) // Transformation animation P_SetPlayerMobjState(player->mo, S_PLAY_SUPER_TRANS1); - if (giverings && player->rings < 50) + if (giverings) player->rings = 50; // Just in case. @@ -1367,7 +1367,7 @@ void P_AddPlayerScore(player_t *player, UINT32 amount) { UINT32 oldscore; - if (player->bot) + if (player->bot && player->bot != 3) player = &players[consoleplayer]; // NiGHTS does it different! @@ -1491,10 +1491,10 @@ void P_PlayLivesJingle(player_t *player) if (player && !P_IsLocalPlayer(player)) return; - if (mariomode) - S_StartSound(NULL, sfx_marioa); - else if (use1upSound || cv_1upsound.value) + if (use1upSound || cv_1upsound.value) S_StartSound(NULL, sfx_oneup); + else if (mariomode) + S_StartSound(NULL, sfx_marioa); else { P_PlayJingle(player, JT_1UP); @@ -2329,8 +2329,7 @@ boolean P_PlayerHitFloor(player_t *player, boolean dorollstuff) P_MobjCheckWater(player->mo); if (player->pflags & PF_SPINNING) { - if (!(player->pflags & PF_STARTDASH) && player->panim != PA_ROLL && player->panim != PA_ETC - && player->panim != PA_ABILITY && player->panim != PA_ABILITY2) + if (player->mo->state-states != S_PLAY_ROLL && !(player->pflags & PF_STARTDASH)) { P_SetPlayerMobjState(player->mo, S_PLAY_ROLL); S_StartSound(player->mo, sfx_spin); @@ -2613,10 +2612,10 @@ static void P_CheckBustableBlocks(player_t *player) if ((netgame || multiplayer) && player->spectator) return; - + oldx = player->mo->x; oldy = player->mo->y; - + if (!(player->pflags & PF_BOUNCING)) // Bouncers only get to break downwards, not sideways { P_UnsetThingPosition(player->mo); @@ -2635,7 +2634,7 @@ static void P_CheckBustableBlocks(player_t *player) if (!node->m_sector->ffloors) continue; - + for (rover = node->m_sector->ffloors; rover; rover = rover->next) { if (!P_PlayerCanBust(player, rover)) @@ -2993,7 +2992,7 @@ static void P_CheckUnderwaterAndSpaceTimer(player_t *player) player->powers[pw_spacetime] = 0; // Underwater audio cues - if (P_IsLocalPlayer(player) && !player->bot) + if (P_IsLocalPlayer(player) && !player->bot && player->bot != 3) { if ((player->powers[pw_underwater] == 25*TICRATE + 1) || (player->powers[pw_underwater] == 20*TICRATE + 1) @@ -4526,9 +4525,6 @@ void P_DoJump(player_t *player, boolean soundandstate) player->pflags |= P_GetJumpFlags(player);; - if (player->charflags & SF_NOJUMPDAMAGE) - player->pflags &= ~PF_SPINNING; - if (soundandstate) { if (!player->spectator) @@ -5024,7 +5020,7 @@ static boolean P_PlayerShieldThink(player_t *player, ticcmd_t *cmd, mobj_t *lock if ((player->powers[pw_shield] & SH_NOSTACK) && !player->powers[pw_super] && !(player->pflags & PF_SPINDOWN) && ((!(player->pflags & PF_THOKKED) || (((player->powers[pw_shield] & SH_NOSTACK) == SH_BUBBLEWRAP || (player->powers[pw_shield] & SH_NOSTACK) == SH_ATTRACT) && player->secondjump == UINT8_MAX) ))) // thokked is optional if you're bubblewrapped / 3dblasted { - if ((player->powers[pw_shield] & SH_NOSTACK) == SH_ATTRACT && !(player->charflags & SF_NOSHIELDABILITY)) + if ((player->powers[pw_shield] & SH_NOSTACK) == SH_ATTRACT) { if ((lockonshield = P_LookForEnemies(player, false, false))) { @@ -5047,7 +5043,7 @@ static boolean P_PlayerShieldThink(player_t *player, ticcmd_t *cmd, mobj_t *lock } } } - if ((!(player->charflags & SF_NOSHIELDABILITY)) && (cmd->buttons & BT_SPIN && !LUAh_ShieldSpecial(player))) // Spin button effects + if (cmd->buttons & BT_SPIN && !LUAh_ShieldSpecial(player)) // Spin button effects { // Force stop if ((player->powers[pw_shield] & ~(SH_FORCEHP|SH_STACK)) == SH_FORCE) @@ -5495,7 +5491,7 @@ static void P_DoJumpStuff(player_t *player, ticcmd_t *cmd) break; } } - else if ((!(player->charflags & SF_NOSHIELDABILITY)) && ((player->powers[pw_shield] & SH_NOSTACK) == SH_WHIRLWIND && !player->powers[pw_super] && !LUAh_ShieldSpecial(player))) + else if ((player->powers[pw_shield] & SH_NOSTACK) == SH_WHIRLWIND && !player->powers[pw_super]) P_DoJumpShield(player); } @@ -5924,7 +5920,7 @@ static void P_3dMovement(player_t *player) player->rmomy = player->mo->momy - player->cmomy; // Calculates player's speed based on distance-of-a-line formula - player->speed = R_PointToDist2(0, 0, player->rmomx, player->rmomy); + player->speed = P_AproxDistance(player->rmomx, player->rmomy); // Monster Iestyn - 04-11-13 // Quadrants are stupid, excessive and broken, let's do this a much simpler way! @@ -5957,22 +5953,6 @@ static void P_3dMovement(player_t *player) acceleration = 96 + (FixedDiv(player->speed, player->mo->scale)>>FRACBITS) * 40; topspeed = normalspd; } - else if (player->bot) - { // Bot steals player 1's stats - normalspd = FixedMul(players[consoleplayer].normalspeed, player->mo->scale); - thrustfactor = players[consoleplayer].thrustfactor; - acceleration = players[consoleplayer].accelstart + (FixedDiv(player->speed, player->mo->scale)>>FRACBITS) * players[consoleplayer].acceleration; - - if (player->powers[pw_tailsfly]) - topspeed = normalspd/2; - else if (player->mo->eflags & (MFE_UNDERWATER|MFE_GOOWATER)) - { - topspeed = normalspd/2; - acceleration = 2*acceleration/3; - } - else - topspeed = normalspd; - } else { if (player->powers[pw_super] || player->powers[pw_sneakers]) @@ -7756,11 +7736,6 @@ void P_ElementalFire(player_t *player, boolean cropcircle) flame->eflags = (flame->eflags & ~MFE_VERTICALFLIP)|(player->mo->eflags & MFE_VERTICALFLIP); P_InstaThrust(flame, flame->angle, FixedMul(3*FRACUNIT, flame->scale)); P_SetObjectMomZ(flame, 3*FRACUNIT, false); - if (!(gametyperules & GTR_FRIENDLY)) - { - P_SetMobjState(flame, S_TEAM_SPINFIRE1); - flame->color = player->mo->color; - } } #undef limitangle #undef numangles @@ -7788,11 +7763,6 @@ void P_ElementalFire(player_t *player, boolean cropcircle) flame->destscale = player->mo->scale; P_SetScale(flame, player->mo->scale); flame->eflags = (flame->eflags & ~MFE_VERTICALFLIP)|(player->mo->eflags & MFE_VERTICALFLIP); - if (!(gametyperules & GTR_FRIENDLY)) - { - P_SetMobjState(flame, S_TEAM_SPINFIRE1); - flame->color = player->mo->color; - } flame->momx = 8; // this is a hack which is used to ensure it still behaves as a missile and can damage others P_XYMovement(flame); @@ -8619,6 +8589,12 @@ void P_MovePlayer(player_t *player) player->climbing--; } + if (!player->climbing) + { + player->lastsidehit = -1; + player->lastlinehit = -1; + } + // Make sure you're not teetering when you shouldn't be. if (player->panim == PA_EDGE && (player->mo->momx || player->mo->momy || player->mo->momz)) @@ -8643,7 +8619,6 @@ void P_MovePlayer(player_t *player) P_DoFiring(player, cmd); { - boolean atspinheight = false; fixed_t oldheight = player->mo->height; // Less height while spinning. Good for spinning under things...? @@ -8653,35 +8628,32 @@ void P_MovePlayer(player_t *player) || player->powers[pw_tailsfly] || player->pflags & PF_GLIDING || (player->charability == CA_GLIDEANDCLIMB && player->mo->state-states == S_PLAY_GLIDE_LANDING) || (player->charability == CA_FLY && player->mo->state-states == S_PLAY_FLY_TIRED)) - { player->mo->height = P_GetPlayerSpinHeight(player); - atspinheight = true; - } else player->mo->height = P_GetPlayerHeight(player); if (player->mo->eflags & MFE_VERTICALFLIP && player->mo->height != oldheight) // adjust z height for reverse gravity, similar to how it's done for scaling player->mo->z -= player->mo->height - oldheight; + } - // Crush test... - if ((player->mo->ceilingz - player->mo->floorz < player->mo->height) - && !(player->mo->flags & MF_NOCLIP)) + // Crush test... + if ((player->mo->ceilingz - player->mo->floorz < player->mo->height) + && !(player->mo->flags & MF_NOCLIP)) + { + if ((player->charability2 == CA2_SPINDASH) && !(player->pflags & PF_SPINNING)) { - if (!atspinheight) - { - player->pflags |= PF_SPINNING; - P_SetPlayerMobjState(player->mo, S_PLAY_ROLL); - } - else if (player->mo->ceilingz - player->mo->floorz < player->mo->height) - { - if ((netgame || multiplayer) && player->spectator) - P_DamageMobj(player->mo, NULL, NULL, 1, DMG_SPECTATOR); // Respawn crushed spectators - else - P_DamageMobj(player->mo, NULL, NULL, 1, DMG_CRUSHED); + player->pflags |= PF_SPINNING; + P_SetPlayerMobjState(player->mo, S_PLAY_ROLL); + } + else if (player->mo->ceilingz - player->mo->floorz < player->mo->height) + { + if ((netgame || multiplayer) && player->spectator) + P_DamageMobj(player->mo, NULL, NULL, 1, DMG_SPECTATOR); // Respawn crushed spectators + else + P_DamageMobj(player->mo, NULL, NULL, 1, DMG_CRUSHED); - if (player->playerstate == PST_DEAD) - return; - } + if (player->playerstate == PST_DEAD) + return; } } @@ -9497,11 +9469,11 @@ static void P_DeathThink(player_t *player) if (player->deadtimer < INT32_MAX) player->deadtimer++; - if (player->bot) // don't allow bots to do any of the below, B_CheckRespawn does all they need for respawning already + if (player->bot && player->bot != 3) // don't allow bots to do any of the below, B_CheckRespawn does all they need for respawning already goto notrealplayer; // continue logic - if (!(netgame || multiplayer) && player->lives <= 0) + if (!(netgame || multiplayer) && player->lives <= 0 && player ==&players[consoleplayer]) //Extra players in SP can't be allowed to continue or end game { if (player->deadtimer > (3*TICRATE) && (cmd->buttons & BT_SPIN || cmd->buttons & BT_JUMP) && (!continuesInSession || player->continues > 0)) G_UseContinue(); @@ -11480,7 +11452,7 @@ void P_PlayerThink(player_t *player) player->playerstate = PST_DEAD; } - if (player->bot) + if (player->bot && player->bot != 3) { if (player->playerstate == PST_LIVE || player->playerstate == PST_DEAD) { @@ -11494,6 +11466,7 @@ void P_PlayerThink(player_t *player) } } +#ifdef SEENAMES if (netgame && player == &players[displayplayer] && !(leveltime % (TICRATE/5))) { seenplayer = NULL; @@ -11518,6 +11491,7 @@ void P_PlayerThink(player_t *player) } } } +#endif if (player->awayviewmobj && P_MobjWasRemoved(player->awayviewmobj)) { @@ -12588,16 +12562,13 @@ void P_PlayerAfterThink(player_t *player) player->powers[pw_carry] = CR_NONE; else { - if (tails->player) - P_TryMove(player->mo, tails->x + P_ReturnThrustX(tails, tails->player->drawangle, 4*FRACUNIT), tails->y + P_ReturnThrustY(tails, tails->player->drawangle, 4*FRACUNIT), true); - else - P_TryMove(player->mo, tails->x + P_ReturnThrustX(tails, tails->angle, 4*FRACUNIT), tails->y + P_ReturnThrustY(tails, tails->angle, 4*FRACUNIT), true); + P_TryMove(player->mo, tails->x + P_ReturnThrustX(tails, tails->player->drawangle, 4*FRACUNIT), tails->y + P_ReturnThrustY(tails, tails->player->drawangle, 4*FRACUNIT), true); player->mo->momx = tails->momx; player->mo->momy = tails->momy; player->mo->momz = tails->momz; } - if (G_CoopGametype() && tails->player && tails->player->bot != 1) + if (G_CoopGametype() && (!tails->player || tails->player->bot != 1)) { player->mo->angle = tails->angle; @@ -12612,7 +12583,7 @@ void P_PlayerAfterThink(player_t *player) { if (player->mo->state-states != S_PLAY_RIDE) P_SetPlayerMobjState(player->mo, S_PLAY_RIDE); - if (tails->player && (tails->skin && ((skin_t *)(tails->skin))->sprites[SPR2_SWIM].numframes) && (tails->eflags & MFE_UNDERWATER)) + if ((tails->skin && ((skin_t *)(tails->skin))->sprites[SPR2_SWIM].numframes) && (tails->eflags & MFE_UNDERWATER)) tails->player->powers[pw_tailsfly] = 0; } else From 5e8313e157ab0bef4f2e2a346906aa1a6efdd58b Mon Sep 17 00:00:00 2001 From: Shane Ellis Date: Fri, 15 Jan 2021 16:50:01 -0500 Subject: [PATCH 007/126] Implementation of lua function P_AddPlayer() --- src/lua_baselib.c | 89 ++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 81 insertions(+), 8 deletions(-) diff --git a/src/lua_baselib.c b/src/lua_baselib.c index c5f847be6..9bc8813a2 100644 --- a/src/lua_baselib.c +++ b/src/lua_baselib.c @@ -155,8 +155,6 @@ static const struct { {META_PIVOTLIST, "spriteframepivot_t[]"}, {META_FRAMEPIVOT, "spriteframepivot_t"}, - {META_TAGLIST, "taglist"}, - {META_MOBJ, "mobj_t"}, {META_MAPTHING, "mapthing_t"}, @@ -165,8 +163,6 @@ static const struct { {META_SKIN, "skin_t"}, {META_POWERS, "player_t.powers"}, {META_SOUNDSID, "skin_t.soundsid"}, - {META_SKINSPRITES, "skin_t.sprites"}, - {META_SKINSPRITESLIST, "skin_t.sprites[]"}, {META_VERTEX, "vertex_t"}, {META_LINE, "line_t"}, @@ -188,9 +184,6 @@ static const struct { {META_CVAR, "consvar_t"}, {META_SECTORLINES, "sector_t.lines"}, -#ifdef MUTABLE_TAGS - {META_SECTORTAGLIST, "sector_t.taglist"}, -#endif {META_SIDENUM, "line_t.sidenum"}, {META_LINEARGS, "line_t.args"}, {META_LINESTRINGARGS, "line_t.stringargs"}, @@ -2639,7 +2632,7 @@ static int lib_rSkinUsable(lua_State *L) else // skin name { const char *skinname = luaL_checkstring(L, 2); - i = R_SkinAvailable(skinname); + if (R_SkinAvailable(skinname) >= 0) if (i == -1) return luaL_error(L, "skin %s (argument 2) is not loaded", skinname); } @@ -3407,6 +3400,85 @@ static int lib_gAddGametype(lua_State *L) return 0; } +// Bot adding function! +// Partly lifted from Got_AddPlayer +static int lib_gAddPlayer(lua_State *L) +{ + INT16 i, newplayernum, botcount = 1; + player_t *newplayer; + INT8 skinnum = 0, bot; + + for (i = 0; i < MAXPLAYERS; i++) + { + if (!playeringame[i]) + break; + + if (players[i].bot) + botcount++; // How many of us are there already? + } + if (i >= MAXPLAYERS) + { + lua_pushnil(L); + return 1; + } + + + newplayernum = i; + + //if (!splitscreen && !botingame) + CL_ClearPlayer(newplayernum); + + playeringame[newplayernum] = true; + G_AddPlayer(newplayernum); + newplayer = &players[newplayernum]; + + newplayer->jointime = 0; + newplayer->quittime = 0; + + // I hereby name you Bot X + strcpy(player_names[newplayernum], va("Bot %d", botcount)); + + // Read the skin string! + if (!lua_isnoneornil(L, 1)) + { + skinnum = R_SkinAvailable(luaL_checkstring(L, 1)); + skinnum = skinnum < 0 ? 0 : skinnum; + + // Bots can be whatever they want + if (!R_SkinUsable(newplayernum, skinnum)) + newplayer->availabilities |= 1 << skinnum; + } + + // Read the color! + if (!lua_isnoneornil(L, 2)) + newplayer->skincolor = R_GetColorByName(luaL_checkstring(L, 2)); + else + newplayer->skincolor = skins[newplayer->skin].prefcolor; + + // Read the bot name, if given! + if (!lua_isnoneornil(L, 3)) + strcpy(player_names[newplayernum], luaL_checkstring(L, 3)); + + bot = luaL_optinteger(L, 4, 3); + newplayer->bot = (bot >= 0 && bot <= 3) ? bot : 3; + + // Set the skin + SetPlayerSkinByNum(newplayernum, skinnum); + + + if (netgame) + { + char joinmsg[256]; + + strcpy(joinmsg, M_GetText("\x82*Bot %s has joined the game (player %d)")); + strcpy(joinmsg, va(joinmsg, player_names[newplayernum], newplayernum)); + HU_AddChatText(joinmsg, false); + } + + LUA_PushUserdata(L, newplayer, META_PLAYER); + return 0; +} + static int Lcheckmapnumber (lua_State *L, int idx, const char *fun) { if (ISINLEVEL) @@ -3987,6 +4059,7 @@ static luaL_Reg lib[] = { // g_game {"G_AddGametype", lib_gAddGametype}, + {"G_AddPlayer", lib_gAddPlayer}, {"G_BuildMapName",lib_gBuildMapName}, {"G_BuildMapTitle",lib_gBuildMapTitle}, {"G_FindMap",lib_gFindMap}, From 9eeaef2e32c90d2c23a314c2cd99a0b72dde0f0a Mon Sep 17 00:00:00 2001 From: Shane Ellis Date: Fri, 15 Jan 2021 16:55:49 -0500 Subject: [PATCH 008/126] Exception made in R_SkinUsable() for player bot types --- src/r_skins.c | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/r_skins.c b/src/r_skins.c index 6f150f234..155a64700 100644 --- a/src/r_skins.c +++ b/src/r_skins.c @@ -198,6 +198,7 @@ boolean R_SkinUsable(INT32 playernum, INT32 skinnum) || (Playing() && (R_SkinAvailable(mapheaderinfo[gamemap-1]->forcecharacter) == skinnum)) // Force 1. || (netgame && (cv_forceskin.value == skinnum)) // Force 2. || (metalrecording && skinnum == 5) // Force 3. + || players[playernum].bot //Force (player is a bot) ); } @@ -511,10 +512,6 @@ static boolean R_ProcessPatchableFields(skin_t *skin, char *stoken, char *value) GETFLAG(MULTIABILITY) GETFLAG(NONIGHTSROTATION) GETFLAG(NONIGHTSSUPER) - GETFLAG(NOSUPERSPRITES) - GETFLAG(NOSUPERJUMPBOOST) - GETFLAG(CANBUSTWALLS) - GETFLAG(NOSHIELDABILITY) #undef GETFLAG else // let's check if it's a sound, otherwise error out From f166af4d8b7805e1ce5688cbef386f6ddc6e3ec1 Mon Sep 17 00:00:00 2001 From: Shane Ellis Date: Fri, 15 Jan 2021 17:00:21 -0500 Subject: [PATCH 009/126] CL_RemovePlayer() - Allow for removal of non-consoleplayer and non-secondaryviewplayer player instances (e.g. bot players) --- src/r_skins.c | 5748 ++++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 5019 insertions(+), 729 deletions(-) diff --git a/src/r_skins.c b/src/r_skins.c index 155a64700..88b4d9387 100644 --- a/src/r_skins.c +++ b/src/r_skins.c @@ -1,6 +1,5 @@ // 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. // @@ -8,826 +7,5117 @@ // terms of the GNU General Public License, version 2. // See the 'LICENSE' file for more details. //----------------------------------------------------------------------------- -/// \file r_skins.c -/// \brief Loading skins +/// \file d_clisrv.c +/// \brief SRB2 Network game communication and protocol, all OS independent parts. -#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 +#ifdef __GNUC__ +#include //for unlink +#endif + +#include "i_net.h" #include "i_system.h" -#include "r_things.h" -#include "r_skins.h" +#include "i_video.h" +#include "d_net.h" +#include "d_main.h" +#include "g_game.h" +#include "st_stuff.h" +#include "hu_stuff.h" +#include "keys.h" +#include "g_input.h" // JOY1 +#include "m_menu.h" +#include "console.h" +#include "d_netfil.h" +#include "byteptr.h" +#include "p_saveg.h" +#include "z_zone.h" #include "p_local.h" -#include "dehacked.h" // get_number (for thok) -#include "m_cond.h" -#ifdef HWRENDER -#include "hardware/hw_md2.h" -#endif +#include "m_misc.h" +#include "am_map.h" +#include "m_random.h" +#include "mserv.h" +#include "y_inter.h" +#include "r_local.h" +#include "m_argv.h" +#include "p_setup.h" +#include "lzf.h" +#include "lua_script.h" +#include "lua_hook.h" +#include "md5.h" +#include "m_perfstats.h" -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]; +#ifndef NONET +// cl loading screen +#include "v_video.h" +#include "f_finale.h" #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. +// NETWORKING // +// gametic is the tic about to (or currently being) run +// Server: +// maketic is the tic that hasn't had control made for it yet +// nettics is the tic for each node +// firstticstosend is the lowest value of nettics +// Client: +// neededtic is the tic needed by the client to run the game +// firstticstosend is used to optimize a condition +// Normally maketic >= gametic > 0 -UINT8 P_GetSkinSprite2(skin_t *skin, UINT8 spr2, player_t *player) +#define PREDICTIONQUEUE BACKUPTICS +#define PREDICTIONMASK (PREDICTIONQUEUE-1) +#define MAX_REASONLENGTH 30 + +boolean server = true; // true or false but !server == client +#define client (!server) +boolean nodownload = false; +boolean serverrunning = false; +INT32 serverplayer = 0; +char motd[254], server_context[8]; // Message of the Day, Unique Context (even without Mumble support) + +// Server specific vars +UINT8 playernode[MAXPLAYERS]; +char playeraddress[MAXPLAYERS][64]; + +// Minimum timeout for sending the savegame +// The actual timeout will be longer depending on the savegame length +tic_t jointimeout = (10*TICRATE); +static boolean sendingsavegame[MAXNETNODES]; // Are we sending the savegame? +static boolean resendingsavegame[MAXNETNODES]; // Are we resending the savegame? +static tic_t savegameresendcooldown[MAXNETNODES]; // How long before we can resend again? +static tic_t freezetimeout[MAXNETNODES]; // Until when can this node freeze the server before getting a timeout? + +// Incremented by cv_joindelay when a client joins, decremented each tic. +// If higher than cv_joindelay * 2 (3 joins in a short timespan), joins are temporarily disabled. +static tic_t joindelay = 0; + +UINT16 pingmeasurecount = 1; +UINT32 realpingtable[MAXPLAYERS]; //the base table of ping where an average will be sent to everyone. +UINT32 playerpingtable[MAXPLAYERS]; //table of player latency values. +SINT8 nodetoplayer[MAXNETNODES]; +SINT8 nodetoplayer2[MAXNETNODES]; // say the numplayer for this node if any (splitscreen) +UINT8 playerpernode[MAXNETNODES]; // used specialy for scplitscreen +boolean nodeingame[MAXNETNODES]; // set false as nodes leave game +tic_t servermaxping = 800; // server's max ping. Defaults to 800 +static tic_t nettics[MAXNETNODES]; // what tic the client have received +static tic_t supposedtics[MAXNETNODES]; // nettics prevision for smaller packet +static UINT8 nodewaiting[MAXNETNODES]; +static tic_t firstticstosend; // min of the nettics +static tic_t tictoclear = 0; // optimize d_clearticcmd +static tic_t maketic; + +static INT16 consistancy[BACKUPTICS]; + +static UINT8 player_joining = false; +UINT8 hu_redownloadinggamestate = 0; + +UINT8 adminpassmd5[16]; +boolean adminpasswordset = false; + +// Client specific +static ticcmd_t localcmds; +static ticcmd_t localcmds2; +static boolean cl_packetmissed; +// here it is for the secondary local player (splitscreen) +static UINT8 mynode; // my address pointofview server +static boolean cl_redownloadinggamestate = false; + +static UINT8 localtextcmd[MAXTEXTCMD]; +static UINT8 localtextcmd2[MAXTEXTCMD]; // splitscreen +static tic_t neededtic; +SINT8 servernode = 0; // the number of the server node +/// \brief do we accept new players? +/// \todo WORK! +boolean acceptnewnode = true; + +// engine + +// Must be a power of two +#define TEXTCMD_HASH_SIZE 4 + +typedef struct textcmdplayer_s { - UINT8 super = 0, i = 0; + INT32 playernum; + UINT8 cmd[MAXTEXTCMD]; + struct textcmdplayer_s *next; +} textcmdplayer_t; - if (!skin) - return 0; +typedef struct textcmdtic_s +{ + tic_t tic; + textcmdplayer_t *playercmds[TEXTCMD_HASH_SIZE]; + struct textcmdtic_s *next; +} textcmdtic_t; - if ((playersprite_t)(spr2 & ~FF_SPR2SUPER) >= free_spr2) - return 0; +ticcmd_t netcmds[BACKUPTICS][MAXPLAYERS]; +static textcmdtic_t *textcmds[TEXTCMD_HASH_SIZE] = {NULL}; - 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; - } +consvar_t cv_showjoinaddress = CVAR_INIT ("showjoinaddress", "Off", CV_SAVE|CV_NETVAR, CV_OnOff, NULL); - spr2 |= super; - } +static CV_PossibleValue_t playbackspeed_cons_t[] = {{1, "MIN"}, {10, "MAX"}, {0, NULL}}; +consvar_t cv_playbackspeed = CVAR_INIT ("playbackspeed", "1", 0, playbackspeed_cons_t, NULL); - if (i >= 32) // probably an infinite loop... - return 0; +static inline void *G_DcpyTiccmd(void* dest, const ticcmd_t* src, const size_t n) +{ + const size_t d = n / sizeof(ticcmd_t); + const size_t r = n % sizeof(ticcmd_t); + UINT8 *ret = dest; - return spr2; + if (r) + M_Memcpy(dest, src, n); + else if (d) + G_MoveTiccmd(dest, src, d); + return ret+n; } -static void Sk_SetDefaultValue(skin_t *skin) +static inline void *G_ScpyTiccmd(ticcmd_t* dest, void* src, const size_t n) { - 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; + const size_t d = n / sizeof(ticcmd_t); + const size_t r = n % sizeof(ticcmd_t); + UINT8 *ret = src; - 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; + if (r) + M_Memcpy(dest, src, n); + else if (d) + G_MoveTiccmd(dest, src, d); + return ret+n; } -// -// 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; - } + +// Some software don't support largest packet +// (original sersetup, not exactely, but the probability of sending a packet +// of 512 bytes is like 0.1) +UINT16 software_MAXPACKETLENGTH; + +/** Guesses the full value of a tic from its lowest byte, for a specific node + * + * \param low The lowest byte of the tic value + * \param node The node to deduce the tic for + * \return The full tic value + * + */ +tic_t ExpandTics(INT32 low, INT32 node) +{ + INT32 delta; + + delta = low - (nettics[node] & UINT8_MAX); + + if (delta >= -64 && delta <= 64) + return (nettics[node] & ~UINT8_MAX) + low; + else if (delta > 64) + return (nettics[node] & ~UINT8_MAX) - 256 + low; + else //if (delta < -64) + return (nettics[node] & ~UINT8_MAX) + 256 + low; +} + +// ----------------------------------------------------------------- +// Some extra data function for handle textcmd buffer +// ----------------------------------------------------------------- + +static void (*listnetxcmd[MAXNETXCMD])(UINT8 **p, INT32 playernum); + +void RegisterNetXCmd(netxcmd_t id, void (*cmd_f)(UINT8 **p, INT32 playernum)) +{ +#ifdef PARANOIA + if (id >= MAXNETXCMD) + I_Error("Command id %d too big", id); + if (listnetxcmd[id] != 0) + I_Error("Command id %d already used", id); #endif - - // no default skin! - numskins = 0; + listnetxcmd[id] = cmd_f; } -UINT32 R_GetSkinAvailabilities(void) +void SendNetXCmd(netxcmd_t id, const void *param, size_t nparam) { - INT32 s; - UINT32 response = 0; - - for (s = 0; s < MAXSKINS; s++) + if (localtextcmd[0]+2+nparam > MAXTEXTCMD) { - 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. - || players[playernum].bot //Force (player is a bot) - ); -} - -// 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); + // for future reference: if (cv_debug) != debug disabled. + CONS_Alert(CONS_ERROR, M_GetText("NetXCmd buffer full, cannot add netcmd %d! (size: %d, needed: %s)\n"), id, localtextcmd[0], sizeu1(nparam)); 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); + localtextcmd[0]++; + localtextcmd[localtextcmd[0]] = (UINT8)id; + if (param && nparam) + { + M_Memcpy(&localtextcmd[localtextcmd[0]+1], param, nparam); + localtextcmd[0] = (UINT8)(localtextcmd[0] + (UINT8)nparam); + } } -// Same as SetPlayerSkin, but uses the skin #. -// network code calls this when a 'skin change' is received -void SetPlayerSkinByNum(INT32 playernum, INT32 skinnum) +// splitscreen player +void SendNetXCmd2(netxcmd_t id, const void *param, size_t nparam) { - player_t *player = &players[playernum]; - skin_t *skin = &skins[skinnum]; - UINT16 newcolor = 0; - - if (skinnum >= 0 && skinnum < numskins && R_SkinUsable(playernum, skinnum)) // Make sure it exists! + if (localtextcmd2[0]+2+nparam > MAXTEXTCMD) { - player->skin = skinnum; + I_Error("No more place in the buffer for netcmd %d\n",id); + return; + } + localtextcmd2[0]++; + localtextcmd2[localtextcmd2[0]] = (UINT8)id; + if (param && nparam) + { + M_Memcpy(&localtextcmd2[localtextcmd2[0]+1], param, nparam); + localtextcmd2[0] = (UINT8)(localtextcmd2[0] + (UINT8)nparam); + } +} - player->camerascale = skin->camerascale; - player->shieldscale = skin->shieldscale; +UINT8 GetFreeXCmdSize(void) +{ + // -1 for the size and another -1 for the ID. + return (UINT8)(localtextcmd[0] - 2); +} - player->charability = (UINT8)skin->ability; - player->charability2 = (UINT8)skin->ability2; +// Frees all textcmd memory for the specified tic +static void D_FreeTextcmd(tic_t tic) +{ + textcmdtic_t **tctprev = &textcmds[tic & (TEXTCMD_HASH_SIZE - 1)]; + textcmdtic_t *textcmdtic = *tctprev; - player->charflags = (UINT32)skin->flags; + while (textcmdtic && textcmdtic->tic != tic) + { + tctprev = &textcmdtic->next; + textcmdtic = textcmdtic->next; + } - 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 (textcmdtic) + { + INT32 i; - 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; + // Remove this tic from the list. + *tctprev = textcmdtic->next; - 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)) + // Free all players. + for (i = 0; i < TEXTCMD_HASH_SIZE; i++) { - 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->bot && botingame) + textcmdplayer_t *textcmdplayer = textcmdtic->playercmds[i]; + + while (textcmdplayer) { - botskin = (UINT8)(skinnum + 1); - botcolor = skin->prefcolor; + textcmdplayer_t *tcpnext = textcmdplayer->next; + Z_Free(textcmdplayer); + textcmdplayer = tcpnext; } } - 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; + // Free this tic's own memory. + Z_Free(textcmdtic); } - - 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) +// Gets the buffer for the specified ticcmd, or NULL if there isn't one +static UINT8* D_GetExistingTextcmd(tic_t tic, INT32 playernum) { - UINT16 i; - const char *S_SKIN = "S_SKIN"; - lumpinfo_t *lump_p; + textcmdtic_t *textcmdtic = textcmds[tic & (TEXTCMD_HASH_SIZE - 1)]; + while (textcmdtic && textcmdtic->tic != tic) textcmdtic = textcmdtic->next; - // scan forward, start at - if (startlump < wadfiles[wadid]->numlumps) + // Do we have an entry for the tic? If so, look for player. + if (textcmdtic) { - 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; + textcmdplayer_t *textcmdplayer = textcmdtic->playercmds[playernum & (TEXTCMD_HASH_SIZE - 1)]; + while (textcmdplayer && textcmdplayer->playernum != playernum) textcmdplayer = textcmdplayer->next; + + if (textcmdplayer) return textcmdplayer->cmd; } - return INT16_MAX; // not found + + return NULL; } -#define HUDNAMEWRITE(value) STRBUFCPY(skin->hudname, value) +// Gets the buffer for the specified ticcmd, creating one if necessary +static UINT8* D_GetTextcmd(tic_t tic, INT32 playernum) +{ + textcmdtic_t *textcmdtic = textcmds[tic & (TEXTCMD_HASH_SIZE - 1)]; + textcmdtic_t **tctprev = &textcmds[tic & (TEXTCMD_HASH_SIZE - 1)]; + textcmdplayer_t *textcmdplayer, **tcpprev; -// turn _ into spaces and . into katana dot -#define SYMBOLCONVERT(name) for (value = name; *value; value++)\ - {\ - if (*value == '_') *value = ' ';\ - else if (*value == '.') *value = '\x1E';\ + // Look for the tic. + while (textcmdtic && textcmdtic->tic != tic) + { + tctprev = &textcmdtic->next; + textcmdtic = textcmdtic->next; + } + + // If we don't have an entry for the tic, make it. + if (!textcmdtic) + { + textcmdtic = *tctprev = Z_Calloc(sizeof (textcmdtic_t), PU_STATIC, NULL); + textcmdtic->tic = tic; + } + + tcpprev = &textcmdtic->playercmds[playernum & (TEXTCMD_HASH_SIZE - 1)]; + textcmdplayer = *tcpprev; + + // Look for the player. + while (textcmdplayer && textcmdplayer->playernum != playernum) + { + tcpprev = &textcmdplayer->next; + textcmdplayer = textcmdplayer->next; + } + + // If we don't have an entry for the player, make it. + if (!textcmdplayer) + { + textcmdplayer = *tcpprev = Z_Calloc(sizeof (textcmdplayer_t), PU_STATIC, NULL); + textcmdplayer->playernum = playernum; + } + + return textcmdplayer->cmd; +} + +static void ExtraDataTicker(void) +{ + INT32 i; + + for (i = 0; i < MAXPLAYERS; i++) + if (playeringame[i] || i == 0) + { + UINT8 *bufferstart = D_GetExistingTextcmd(gametic, i); + + if (bufferstart) + { + UINT8 *curpos = bufferstart; + UINT8 *bufferend = &curpos[curpos[0]+1]; + + curpos++; + while (curpos < bufferend) + { + if (*curpos < MAXNETXCMD && listnetxcmd[*curpos]) + { + const UINT8 id = *curpos; + curpos++; + DEBFILE(va("executing x_cmd %s ply %u ", netxcmdnames[id - 1], i)); + (listnetxcmd[id])(&curpos, i); + DEBFILE("done\n"); } - -// -// 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(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(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)) \ -{ \ - UINT16 color = R_GetColorByName(value); \ - skin->field = (color ? color : SKINCOLOR_GREEN); \ -} - GETSKINCOLOR(prefcolor) - GETSKINCOLOR(prefoppositecolor) -#undef GETSKINCOLOR - else if (!stricmp(stoken, "supercolor")) - { - UINT16 color = R_GetSuperColorByName(value); - skin->supercolor = (color ? color : SKINCOLOR_SUPERGOLD1); - } - -#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) - GETFLAG(NONIGHTSSUPER) -#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; + else + { + if (server) + { + SendKick(i, KICK_MSG_CON_FAIL | KICK_MSG_KEEP_BODY); + DEBFILE(va("player %d kicked [gametic=%u] reason as follows:\n", i, gametic)); + } + CONS_Alert(CONS_WARNING, M_GetText("Got unknown net command [%s]=%d (max %d)\n"), sizeu1(curpos - bufferstart), *curpos, bufferstart[0]); + break; + } + } } } - return found; - } + + // If you are a client, you can safely forget the net commands for this tic + // If you are the server, you need to remember them until every client has been acknowledged, + // because if you need to resend a PT_SERVERTICS packet, you will need to put the commands in it + if (client) + D_FreeTextcmd(gametic); +} + +static void D_Clearticcmd(tic_t tic) +{ + INT32 i; + + D_FreeTextcmd(tic); + + for (i = 0; i < MAXPLAYERS; i++) + netcmds[tic%BACKUPTICS][i].angleturn = 0; + + DEBFILE(va("clear tic %5u (%2u)\n", tic, tic%BACKUPTICS)); +} + +void D_ResetTiccmds(void) +{ + INT32 i; + + memset(&localcmds, 0, sizeof(ticcmd_t)); + memset(&localcmds2, 0, sizeof(ticcmd_t)); + + // Reset the net command list + for (i = 0; i < TEXTCMD_HASH_SIZE; i++) + while (textcmds[i]) + D_Clearticcmd(textcmds[i]->tic); +} + +void SendKick(UINT8 playernum, UINT8 msg) +{ + UINT8 buf[2]; + + if (!(server && cv_rejointimeout.value)) + msg &= ~KICK_MSG_KEEP_BODY; + + buf[0] = playernum; + buf[1] = msg; + SendNetXCmd(XD_KICK, &buf, 2); +} + +// ----------------------------------------------------------------- +// end of extra data function +// ----------------------------------------------------------------- + +// ----------------------------------------------------------------- +// extra data function for lmps +// ----------------------------------------------------------------- + +// if extradatabit is set, after the ziped tic you find this: +// +// type | description +// ---------+-------------- +// byte | size of the extradata +// byte | the extradata (xd) bits: see XD_... +// with this byte you know what parameter folow +// if (xd & XDNAMEANDCOLOR) +// byte | color +// char[MAXPLAYERNAME] | name of the player +// endif +// if (xd & XD_WEAPON_PREF) +// byte | original weapon switch: boolean, true if use the old +// | weapon switch methode +// char[NUMWEAPONS] | the weapon switch priority +// byte | autoaim: true if use the old autoaim system +// endif +/*boolean AddLmpExtradata(UINT8 **demo_point, INT32 playernum) +{ + UINT8 *textcmd = D_GetExistingTextcmd(gametic, playernum); + + if (!textcmd) + return false; + + M_Memcpy(*demo_point, textcmd, textcmd[0]+1); + *demo_point += textcmd[0]+1; return true; } -// -// Find skin sprites, sounds & optional status bar face, & add them -// -void R_AddSkins(UINT16 wadnum) +void ReadLmpExtraData(UINT8 **demo_pointer, INT32 playernum) { - UINT16 lump, lastlump = 0; - char *buf; - char *buf2; - char *stoken; - char *value; - size_t size; - skin_t *skin; - boolean hudname, realname; + UINT8 nextra; + UINT8 *textcmd; - // - // search for all skin markers in pwad - // + if (!demo_pointer) + return; - while ((lump = W_CheckForSkinMarkerInPwad(wadnum, lastlump)) != INT16_MAX) + textcmd = D_GetTextcmd(gametic, playernum); + nextra = **demo_pointer; + M_Memcpy(textcmd, *demo_pointer, nextra + 1); + // increment demo pointer + *demo_pointer += nextra + 1; +}*/ + +// ----------------------------------------------------------------- +// end extra data function for lmps +// ----------------------------------------------------------------- + +static INT16 Consistancy(void); + +typedef enum +{ + CL_SEARCHING, + CL_DOWNLOADFILES, + CL_ASKJOIN, + CL_WAITJOINRESPONSE, + CL_DOWNLOADSAVEGAME, + CL_CONNECTED, + CL_ABORTED +} cl_mode_t; + +static void GetPackets(void); + +static cl_mode_t cl_mode = CL_SEARCHING; + +#ifndef NONET +#define SNAKE_SPEED 5 + +#define SNAKE_NUM_BLOCKS_X 20 +#define SNAKE_NUM_BLOCKS_Y 10 +#define SNAKE_BLOCK_SIZE 12 +#define SNAKE_BORDER_SIZE 12 + +#define SNAKE_MAP_WIDTH (SNAKE_NUM_BLOCKS_X * SNAKE_BLOCK_SIZE) +#define SNAKE_MAP_HEIGHT (SNAKE_NUM_BLOCKS_Y * SNAKE_BLOCK_SIZE) + +#define SNAKE_LEFT_X ((BASEVIDWIDTH - SNAKE_MAP_WIDTH) / 2 - SNAKE_BORDER_SIZE) +#define SNAKE_RIGHT_X (SNAKE_LEFT_X + SNAKE_MAP_WIDTH + SNAKE_BORDER_SIZE * 2 - 1) +#define SNAKE_BOTTOM_Y (BASEVIDHEIGHT - 48) +#define SNAKE_TOP_Y (SNAKE_BOTTOM_Y - SNAKE_MAP_HEIGHT - SNAKE_BORDER_SIZE * 2 + 1) + +enum snake_bonustype_s { + SNAKE_BONUS_NONE = 0, + SNAKE_BONUS_SLOW, + SNAKE_BONUS_FAST, + SNAKE_BONUS_GHOST, + SNAKE_BONUS_NUKE, + SNAKE_BONUS_SCISSORS, + SNAKE_BONUS_REVERSE, + SNAKE_BONUS_EGGMAN, + SNAKE_NUM_BONUSES, +}; + +static const char *snake_bonuspatches[] = { + NULL, + "DL_SLOW", + "TVSSC0", + "TVIVC0", + "TVARC0", + "DL_SCISSORS", + "TVRCC0", + "TVEGC0", +}; + +static const char *snake_backgrounds[] = { + "RVPUMICF", + "FRSTRCKF", + "TAR", + "MMFLRB4", + "RVDARKF1", + "RVZWALF1", + "RVZWALF4", + "RVZWALF5", + "RVZGRS02", + "RVZGRS04", +}; + +typedef struct snake_s +{ + boolean paused; + boolean pausepressed; + tic_t time; + tic_t nextupdate; + boolean gameover; + UINT8 background; + + UINT16 snakelength; + enum snake_bonustype_s snakebonus; + tic_t snakebonustime; + UINT8 snakex[SNAKE_NUM_BLOCKS_X * SNAKE_NUM_BLOCKS_Y]; + UINT8 snakey[SNAKE_NUM_BLOCKS_X * SNAKE_NUM_BLOCKS_Y]; + UINT8 snakedir[SNAKE_NUM_BLOCKS_X * SNAKE_NUM_BLOCKS_Y]; + + UINT8 applex; + UINT8 appley; + + enum snake_bonustype_s bonustype; + UINT8 bonusx; + UINT8 bonusy; +} snake_t; + +static snake_t *snake = NULL; + +static void Snake_Initialise(void) +{ + if (!snake) + snake = malloc(sizeof(snake_t)); + + snake->paused = false; + snake->pausepressed = false; + snake->time = 0; + snake->nextupdate = SNAKE_SPEED; + snake->gameover = false; + snake->background = M_RandomKey(sizeof(snake_backgrounds) / sizeof(*snake_backgrounds)); + + snake->snakelength = 1; + snake->snakebonus = SNAKE_BONUS_NONE; + snake->snakex[0] = M_RandomKey(SNAKE_NUM_BLOCKS_X); + snake->snakey[0] = M_RandomKey(SNAKE_NUM_BLOCKS_Y); + snake->snakedir[0] = 0; + snake->snakedir[1] = 0; + + snake->applex = M_RandomKey(SNAKE_NUM_BLOCKS_X); + snake->appley = M_RandomKey(SNAKE_NUM_BLOCKS_Y); + + snake->bonustype = SNAKE_BONUS_NONE; +} + +static UINT8 Snake_GetOppositeDir(UINT8 dir) +{ + if (dir == 1 || dir == 3) + return dir + 1; + else if (dir == 2 || dir == 4) + return dir - 1; + else + return 12 + 5 - dir; +} + +static void Snake_FindFreeSlot(UINT8 *freex, UINT8 *freey, UINT8 headx, UINT8 heady) +{ + UINT8 x, y; + UINT16 i; + + do { - // advance by default - lastlump = lump + 1; + x = M_RandomKey(SNAKE_NUM_BLOCKS_X); + y = M_RandomKey(SNAKE_NUM_BLOCKS_Y); - 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 (i = 0; i < snake->snakelength; i++) + if (x == snake->snakex[i] && y == snake->snakey[i]) + break; + } while (i < snake->snakelength || (x == headx && y == heady) + || (x == snake->applex && y == snake->appley) + || (snake->bonustype != SNAKE_BONUS_NONE && x == snake->bonusx && y == snake->bonusy)); - // 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'; + *freex = x; + *freey = y; +} - // 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 - } +static void Snake_Handle(void) +{ + UINT8 x, y; + UINT8 oldx, oldy; + UINT16 i; - 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++; + // Handle retry + if (snake->gameover && (PLAYER1INPUTDOWN(gc_jump) || gamekeydown[KEY_ENTER])) + { + Snake_Initialise(); + snake->pausepressed = true; // Avoid accidental pause on respawn } - return; + + // Handle pause + if (PLAYER1INPUTDOWN(gc_pause) || gamekeydown[KEY_ENTER]) + { + if (!snake->pausepressed) + snake->paused = !snake->paused; + snake->pausepressed = true; + } + else + snake->pausepressed = false; + + if (snake->paused) + return; + + snake->time++; + + x = snake->snakex[0]; + y = snake->snakey[0]; + oldx = snake->snakex[1]; + oldy = snake->snakey[1]; + + // Update direction + if (gamekeydown[KEY_LEFTARROW]) + { + if (snake->snakelength < 2 || x <= oldx) + snake->snakedir[0] = 1; + } + else if (gamekeydown[KEY_RIGHTARROW]) + { + if (snake->snakelength < 2 || x >= oldx) + snake->snakedir[0] = 2; + } + else if (gamekeydown[KEY_UPARROW]) + { + if (snake->snakelength < 2 || y <= oldy) + snake->snakedir[0] = 3; + } + else if (gamekeydown[KEY_DOWNARROW]) + { + if (snake->snakelength < 2 || y >= oldy) + snake->snakedir[0] = 4; + } + + if (snake->snakebonustime) + { + snake->snakebonustime--; + if (!snake->snakebonustime) + snake->snakebonus = SNAKE_BONUS_NONE; + } + + snake->nextupdate--; + if (snake->nextupdate) + return; + if (snake->snakebonus == SNAKE_BONUS_SLOW) + snake->nextupdate = SNAKE_SPEED * 2; + else if (snake->snakebonus == SNAKE_BONUS_FAST) + snake->nextupdate = SNAKE_SPEED * 2 / 3; + else + snake->nextupdate = SNAKE_SPEED; + + if (snake->gameover) + return; + + // Find new position + switch (snake->snakedir[0]) + { + case 1: + if (x > 0) + x--; + else + snake->gameover = true; + break; + case 2: + if (x < SNAKE_NUM_BLOCKS_X - 1) + x++; + else + snake->gameover = true; + break; + case 3: + if (y > 0) + y--; + else + snake->gameover = true; + break; + case 4: + if (y < SNAKE_NUM_BLOCKS_Y - 1) + y++; + else + snake->gameover = true; + break; + } + + // Check collision with snake + if (snake->snakebonus != SNAKE_BONUS_GHOST) + for (i = 1; i < snake->snakelength - 1; i++) + if (x == snake->snakex[i] && y == snake->snakey[i]) + { + if (snake->snakebonus == SNAKE_BONUS_SCISSORS) + { + snake->snakebonus = SNAKE_BONUS_NONE; + snake->snakelength = i; + S_StartSound(NULL, sfx_adderr); + } + else + snake->gameover = true; + } + + if (snake->gameover) + { + S_StartSound(NULL, sfx_lose); + return; + } + + // Check collision with apple + if (x == snake->applex && y == snake->appley) + { + if (snake->snakelength + 3 < SNAKE_NUM_BLOCKS_X * SNAKE_NUM_BLOCKS_Y) + { + snake->snakelength++; + snake->snakex [snake->snakelength - 1] = snake->snakex [snake->snakelength - 2]; + snake->snakey [snake->snakelength - 1] = snake->snakey [snake->snakelength - 2]; + snake->snakedir[snake->snakelength - 1] = snake->snakedir[snake->snakelength - 2]; + } + + // Spawn new apple + Snake_FindFreeSlot(&snake->applex, &snake->appley, x, y); + + // Spawn new bonus + if (!(snake->snakelength % 5)) + { + do + { + snake->bonustype = M_RandomKey(SNAKE_NUM_BONUSES - 1) + 1; + } while (snake->snakelength > SNAKE_NUM_BLOCKS_X * SNAKE_NUM_BLOCKS_Y * 3 / 4 + && (snake->bonustype == SNAKE_BONUS_EGGMAN || snake->bonustype == SNAKE_BONUS_FAST || snake->bonustype == SNAKE_BONUS_REVERSE)); + + Snake_FindFreeSlot(&snake->bonusx, &snake->bonusy, x, y); + } + + S_StartSound(NULL, sfx_s3k6b); + } + + if (snake->snakelength > 1 && snake->snakedir[0]) + { + UINT8 dir = snake->snakedir[0]; + + oldx = snake->snakex[1]; + oldy = snake->snakey[1]; + + // Move + for (i = snake->snakelength - 1; i > 0; i--) + { + snake->snakex[i] = snake->snakex[i - 1]; + snake->snakey[i] = snake->snakey[i - 1]; + snake->snakedir[i] = snake->snakedir[i - 1]; + } + + // Handle corners + if (x < oldx && dir == 3) + dir = 5; + else if (x > oldx && dir == 3) + dir = 6; + else if (x < oldx && dir == 4) + dir = 7; + else if (x > oldx && dir == 4) + dir = 8; + else if (y < oldy && dir == 1) + dir = 9; + else if (y < oldy && dir == 2) + dir = 10; + else if (y > oldy && dir == 1) + dir = 11; + else if (y > oldy && dir == 2) + dir = 12; + snake->snakedir[1] = dir; + } + + snake->snakex[0] = x; + snake->snakey[0] = y; + + // Check collision with bonus + if (snake->bonustype != SNAKE_BONUS_NONE && x == snake->bonusx && y == snake->bonusy) + { + S_StartSound(NULL, sfx_ncchip); + + switch (snake->bonustype) + { + case SNAKE_BONUS_SLOW: + snake->snakebonus = SNAKE_BONUS_SLOW; + snake->snakebonustime = 20 * TICRATE; + break; + case SNAKE_BONUS_FAST: + snake->snakebonus = SNAKE_BONUS_FAST; + snake->snakebonustime = 20 * TICRATE; + break; + case SNAKE_BONUS_GHOST: + snake->snakebonus = SNAKE_BONUS_GHOST; + snake->snakebonustime = 10 * TICRATE; + break; + case SNAKE_BONUS_NUKE: + for (i = 0; i < snake->snakelength; i++) + { + snake->snakex [i] = snake->snakex [0]; + snake->snakey [i] = snake->snakey [0]; + snake->snakedir[i] = snake->snakedir[0]; + } + + S_StartSound(NULL, sfx_bkpoof); + break; + case SNAKE_BONUS_SCISSORS: + snake->snakebonus = SNAKE_BONUS_SCISSORS; + snake->snakebonustime = 60 * TICRATE; + break; + case SNAKE_BONUS_REVERSE: + for (i = 0; i < (snake->snakelength + 1) / 2; i++) + { + UINT16 i2 = snake->snakelength - 1 - i; + UINT8 tmpx = snake->snakex [i]; + UINT8 tmpy = snake->snakey [i]; + UINT8 tmpdir = snake->snakedir[i]; + + // Swap first segment with last segment + snake->snakex [i] = snake->snakex [i2]; + snake->snakey [i] = snake->snakey [i2]; + snake->snakedir[i] = Snake_GetOppositeDir(snake->snakedir[i2]); + snake->snakex [i2] = tmpx; + snake->snakey [i2] = tmpy; + snake->snakedir[i2] = Snake_GetOppositeDir(tmpdir); + } + + snake->snakedir[0] = 0; + + S_StartSound(NULL, sfx_gravch); + break; + default: + if (snake->snakebonus != SNAKE_BONUS_GHOST) + { + snake->gameover = true; + S_StartSound(NULL, sfx_lose); + } + } + + snake->bonustype = SNAKE_BONUS_NONE; + } +} + +static void Snake_Draw(void) +{ + INT16 i; + + // Background + V_DrawFlatFill( + SNAKE_LEFT_X + SNAKE_BORDER_SIZE, + SNAKE_TOP_Y + SNAKE_BORDER_SIZE, + SNAKE_MAP_WIDTH, + SNAKE_MAP_HEIGHT, + W_GetNumForName(snake_backgrounds[snake->background]) + ); + + // Borders + V_DrawFill(SNAKE_LEFT_X, SNAKE_TOP_Y, SNAKE_BORDER_SIZE + SNAKE_MAP_WIDTH, SNAKE_BORDER_SIZE, 242); // Top + V_DrawFill(SNAKE_LEFT_X + SNAKE_BORDER_SIZE + SNAKE_MAP_WIDTH, SNAKE_TOP_Y, SNAKE_BORDER_SIZE, SNAKE_BORDER_SIZE + SNAKE_MAP_HEIGHT, 242); // Right + V_DrawFill(SNAKE_LEFT_X + SNAKE_BORDER_SIZE, SNAKE_TOP_Y + SNAKE_BORDER_SIZE + SNAKE_MAP_HEIGHT, SNAKE_BORDER_SIZE + SNAKE_MAP_WIDTH, SNAKE_BORDER_SIZE, 242); // Bottom + V_DrawFill(SNAKE_LEFT_X, SNAKE_TOP_Y + SNAKE_BORDER_SIZE, SNAKE_BORDER_SIZE, SNAKE_BORDER_SIZE + SNAKE_MAP_HEIGHT, 242); // Left + + // Apple + V_DrawFixedPatch( + (SNAKE_LEFT_X + SNAKE_BORDER_SIZE + snake->applex * SNAKE_BLOCK_SIZE + SNAKE_BLOCK_SIZE / 2) * FRACUNIT, + (SNAKE_TOP_Y + SNAKE_BORDER_SIZE + snake->appley * SNAKE_BLOCK_SIZE + SNAKE_BLOCK_SIZE / 2) * FRACUNIT, + FRACUNIT / 4, + 0, + W_CachePatchLongName("DL_APPLE", PU_HUDGFX), + NULL + ); + + // Bonus + if (snake->bonustype != SNAKE_BONUS_NONE) + V_DrawFixedPatch( + (SNAKE_LEFT_X + SNAKE_BORDER_SIZE + snake->bonusx * SNAKE_BLOCK_SIZE + SNAKE_BLOCK_SIZE / 2 ) * FRACUNIT, + (SNAKE_TOP_Y + SNAKE_BORDER_SIZE + snake->bonusy * SNAKE_BLOCK_SIZE + SNAKE_BLOCK_SIZE / 2 + 4) * FRACUNIT, + FRACUNIT / 2, + 0, + W_CachePatchLongName(snake_bonuspatches[snake->bonustype], PU_HUDGFX), + NULL + ); + + // Snake + if (!snake->gameover || snake->time % 8 < 8 / 2) // Blink if game over + { + for (i = snake->snakelength - 1; i >= 0; i--) + { + const char *patchname; + UINT8 dir = snake->snakedir[i]; + + if (i == 0) // Head + { + switch (dir) + { + case 1: patchname = "DL_SNAKEHEAD_L"; break; + case 2: patchname = "DL_SNAKEHEAD_R"; break; + case 3: patchname = "DL_SNAKEHEAD_T"; break; + case 4: patchname = "DL_SNAKEHEAD_B"; break; + default: patchname = "DL_SNAKEHEAD_M"; + } + } + else // Body + { + switch (dir) + { + case 1: patchname = "DL_SNAKEBODY_L"; break; + case 2: patchname = "DL_SNAKEBODY_R"; break; + case 3: patchname = "DL_SNAKEBODY_T"; break; + case 4: patchname = "DL_SNAKEBODY_B"; break; + case 5: patchname = "DL_SNAKEBODY_LT"; break; + case 6: patchname = "DL_SNAKEBODY_RT"; break; + case 7: patchname = "DL_SNAKEBODY_LB"; break; + case 8: patchname = "DL_SNAKEBODY_RB"; break; + case 9: patchname = "DL_SNAKEBODY_TL"; break; + case 10: patchname = "DL_SNAKEBODY_TR"; break; + case 11: patchname = "DL_SNAKEBODY_BL"; break; + case 12: patchname = "DL_SNAKEBODY_BR"; break; + default: patchname = "DL_SNAKEBODY_B"; + } + } + + V_DrawFixedPatch( + (SNAKE_LEFT_X + SNAKE_BORDER_SIZE + snake->snakex[i] * SNAKE_BLOCK_SIZE + SNAKE_BLOCK_SIZE / 2) * FRACUNIT, + (SNAKE_TOP_Y + SNAKE_BORDER_SIZE + snake->snakey[i] * SNAKE_BLOCK_SIZE + SNAKE_BLOCK_SIZE / 2) * FRACUNIT, + i == 0 && dir == 0 ? FRACUNIT / 5 : FRACUNIT / 2, + snake->snakebonus == SNAKE_BONUS_GHOST ? V_TRANSLUCENT : 0, + W_CachePatchLongName(patchname, PU_HUDGFX), + NULL + ); + } + } + + // Length + V_DrawString(SNAKE_RIGHT_X + 4, SNAKE_TOP_Y, V_MONOSPACE, va("%u", snake->snakelength)); + + // Bonus + if (snake->snakebonus != SNAKE_BONUS_NONE + && (snake->snakebonustime >= 3 * TICRATE || snake->time % 4 < 4 / 2)) + V_DrawFixedPatch( + (SNAKE_RIGHT_X + 10) * FRACUNIT, + (SNAKE_TOP_Y + 24) * FRACUNIT, + FRACUNIT / 2, + 0, + W_CachePatchLongName(snake_bonuspatches[snake->snakebonus], PU_HUDGFX), + NULL + ); } // -// Patch skin sprites +// CL_DrawConnectionStatus // -void R_PatchSkins(UINT16 wadnum) +// Keep the local client informed of our status. +// +static inline void CL_DrawConnectionStatus(void) { - UINT16 lump, lastlump = 0; - char *buf; - char *buf2; - char *stoken; - char *value; - size_t size; - skin_t *skin; - boolean noskincomplain, realname, hudname; + INT32 ccstime = I_GetTime(); - // - // search for all skin patch markers in pwad - // + // Draw background fade + if (!menuactive) // menu already draws its own fade + V_DrawFadeScreen(0xFF00, 16); // force default - while ((lump = W_CheckForPatchSkinMarkerInPwad(wadnum, lastlump)) != INT16_MAX) + // Draw the bottom box. + M_DrawTextBox(BASEVIDWIDTH/2-128-8, BASEVIDHEIGHT-16-8, 32, 1); + V_DrawCenteredString(BASEVIDWIDTH/2, BASEVIDHEIGHT-16-16, V_YELLOWMAP, "Press ESC to abort"); + + if (cl_mode != CL_DOWNLOADFILES) { - INT32 skinnum = 0; + INT32 i, animtime = ((ccstime / 4) & 15) + 16; + UINT8 palstart = (cl_mode == CL_SEARCHING) ? 32 : 96; + // 15 pal entries total. + const char *cltext; - // advance by default - lastlump = lump + 1; + if (!(cl_mode == CL_DOWNLOADSAVEGAME && lastfilenum != -1)) + for (i = 0; i < 16; ++i) + V_DrawFill((BASEVIDWIDTH/2-128) + (i * 16), BASEVIDHEIGHT-16, 16, 8, palstart + ((animtime - i) & 15)); - 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) + switch (cl_mode) { - 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")) + case CL_DOWNLOADSAVEGAME: + if (lastfilenum != -1) { - 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); - } + UINT32 currentsize = fileneeded[lastfilenum].currentsize; + UINT32 totalsize = fileneeded[lastfilenum].totalsize; + INT32 dldlength; - if (!skin) + cltext = M_GetText("Downloading game state..."); + Net_GetNetStat(); + + dldlength = (INT32)((currentsize/(double)totalsize) * 256); + if (dldlength > 256) + dldlength = 256; + V_DrawFill(BASEVIDWIDTH/2-128, BASEVIDHEIGHT-16, 256, 8, 111); + V_DrawFill(BASEVIDWIDTH/2-128, BASEVIDHEIGHT-16, dldlength, 8, 96); + + V_DrawString(BASEVIDWIDTH/2-128, BASEVIDHEIGHT-16, V_20TRANS|V_MONOSPACE, + va(" %4uK/%4uK",currentsize>>10,totalsize>>10)); + + V_DrawRightAlignedString(BASEVIDWIDTH/2+128, BASEVIDHEIGHT-16, V_20TRANS|V_MONOSPACE, + va("%3.1fK/s ", ((double)getbps)/1024)); + } + else + cltext = M_GetText("Waiting to download game state..."); + break; + case CL_ASKJOIN: + case CL_WAITJOINRESPONSE: + cltext = M_GetText("Requesting to join..."); + break; + default: + cltext = M_GetText("Connecting to server..."); break; - -next_token: - stoken = strtok(NULL, "\r\n= "); } - free(buf2); - - if (!skin) // Didn't include a name parameter? What a waste. + V_DrawCenteredString(BASEVIDWIDTH/2, BASEVIDHEIGHT-16-24, V_YELLOWMAP, cltext); + } + else + { + if (lastfilenum != -1) { - if (!noskincomplain) - CONS_Debug(DBG_SETUP, "R_PatchSkins: no skin name given in P_SKIN lump #%d (WAD %s)\n", lump, wadfiles[wadnum]->filename); + INT32 dldlength; + static char tempname[28]; + fileneeded_t *file = &fileneeded[lastfilenum]; + char *filename = file->filename; + + Snake_Draw(); + + Net_GetNetStat(); + dldlength = (INT32)((file->currentsize/(double)file->totalsize) * 256); + if (dldlength > 256) + dldlength = 256; + V_DrawFill(BASEVIDWIDTH/2-128, BASEVIDHEIGHT-16, 256, 8, 111); + V_DrawFill(BASEVIDWIDTH/2-128, BASEVIDHEIGHT-16, dldlength, 8, 96); + + memset(tempname, 0, sizeof(tempname)); + // offset filename to just the name only part + filename += strlen(filename) - nameonlylength(filename); + + if (strlen(filename) > sizeof(tempname)-1) // too long to display fully + { + size_t endhalfpos = strlen(filename)-10; + // display as first 14 chars + ... + last 10 chars + // which should add up to 27 if our math(s) is correct + snprintf(tempname, sizeof(tempname), "%.14s...%.10s", filename, filename+endhalfpos); + } + else // we can copy the whole thing in safely + { + strncpy(tempname, filename, sizeof(tempname)-1); + } + + V_DrawCenteredString(BASEVIDWIDTH/2, BASEVIDHEIGHT-16-24, V_YELLOWMAP, + va(M_GetText("Downloading \"%s\""), tempname)); + V_DrawString(BASEVIDWIDTH/2-128, BASEVIDHEIGHT-16, V_20TRANS|V_MONOSPACE, + va(" %4uK/%4uK",fileneeded[lastfilenum].currentsize>>10,file->totalsize>>10)); + V_DrawRightAlignedString(BASEVIDWIDTH/2+128, BASEVIDHEIGHT-16, V_20TRANS|V_MONOSPACE, + va("%3.1fK/s ", ((double)getbps)/1024)); + } + else + V_DrawCenteredString(BASEVIDWIDTH/2, BASEVIDHEIGHT-16-24, V_YELLOWMAP, + M_GetText("Waiting to download files...")); + } +} +#endif + +/** Sends a special packet to declare how many players in local + * Used only in arbitratrenetstart() + * Sends a PT_CLIENTJOIN packet to the server + * + * \return True if the packet was successfully sent + * \todo Improve the description... + * Because to be honest, I have no idea what arbitratrenetstart is... + * Is it even used...? + * + */ +static boolean CL_SendJoin(void) +{ + UINT8 localplayers = 1; + if (netgame) + CONS_Printf(M_GetText("Sending join request...\n")); + netbuffer->packettype = PT_CLIENTJOIN; + + if (splitscreen || botingame) + localplayers++; + netbuffer->u.clientcfg.localplayers = localplayers; + netbuffer->u.clientcfg._255 = 255; + netbuffer->u.clientcfg.packetversion = PACKETVERSION; + netbuffer->u.clientcfg.version = VERSION; + netbuffer->u.clientcfg.subversion = SUBVERSION; + strncpy(netbuffer->u.clientcfg.application, SRB2APPLICATION, + sizeof netbuffer->u.clientcfg.application); + + CleanupPlayerName(consoleplayer, cv_playername.zstring); + if (splitscreen) + CleanupPlayerName(1, cv_playername2.zstring);/* 1 is a HACK? oh no */ + + strncpy(netbuffer->u.clientcfg.names[0], cv_playername.zstring, MAXPLAYERNAME); + strncpy(netbuffer->u.clientcfg.names[1], cv_playername2.zstring, MAXPLAYERNAME); + + 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; + + netbuffer->packettype = PT_SERVERINFO; + netbuffer->u.serverinfo._255 = 255; + netbuffer->u.serverinfo.packetversion = PACKETVERSION; + netbuffer->u.serverinfo.version = VERSION; + netbuffer->u.serverinfo.subversion = SUBVERSION; + strncpy(netbuffer->u.serverinfo.application, SRB2APPLICATION, + sizeof netbuffer->u.serverinfo.application); + // return back the time value so client can compute their ping + netbuffer->u.serverinfo.time = (tic_t)LONG(servertime); + netbuffer->u.serverinfo.leveltime = (tic_t)LONG(leveltime); + + netbuffer->u.serverinfo.numberofplayer = (UINT8)D_NumPlayers(); + netbuffer->u.serverinfo.maxplayer = (UINT8)cv_maxplayers.value; + + if (!node || 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; + netbuffer->u.serverinfo.cheatsenabled = CV_CheatsEnabled(); + netbuffer->u.serverinfo.isdedicated = (UINT8)dedicated; + strncpy(netbuffer->u.serverinfo.servername, cv_servername.string, + MAXSERVERNAME); + strncpy(netbuffer->u.serverinfo.mapname, G_BuildMapName(gamemap), 7); + + M_Memcpy(netbuffer->u.serverinfo.mapmd5, mapmd5, 16); + + memset(netbuffer->u.serverinfo.maptitle, 0, sizeof netbuffer->u.serverinfo.maptitle); + + if (mapheaderinfo[gamemap-1] && *mapheaderinfo[gamemap-1]->lvlttl) + { + char *read = mapheaderinfo[gamemap-1]->lvlttl, *writ = netbuffer->u.serverinfo.maptitle; + while (writ < (netbuffer->u.serverinfo.maptitle+32) && *read != '\0') + { + if (!(*read & 0x80)) + { + *writ = toupper(*read); + writ++; + } + read++; + } + *writ = '\0'; + //strncpy(netbuffer->u.serverinfo.maptitle, (char *)mapheaderinfo[gamemap-1]->lvlttl, 33); + } + else + strncpy(netbuffer->u.serverinfo.maptitle, "UNKNOWN", 32); + + if (mapheaderinfo[gamemap-1] && !(mapheaderinfo[gamemap-1]->levelflags & LF_NOZONE)) + netbuffer->u.serverinfo.iszone = 1; + else + netbuffer->u.serverinfo.iszone = 0; + + if (mapheaderinfo[gamemap-1]) + netbuffer->u.serverinfo.actnum = mapheaderinfo[gamemap-1]->actnum; + + p = PutFileNeeded(); + + HSendPacket(node, false, 0, p - ((UINT8 *)&netbuffer->u)); +} + +static void SV_SendPlayerInfo(INT32 node) +{ + UINT8 i; + netbuffer->packettype = PT_PLAYERINFO; + + for (i = 0; i < MAXPLAYERS; i++) + { + if (!playeringame[i]) + { + netbuffer->u.playerinfo[i].num = 255; // This slot is empty. continue; } - // Patch sprites - R_LoadSkinSprites(wadnum, &lump, &lastlump, skin); - //ST_LoadFaceGraphics(skinnum); -- nah let's do this elsewhere + netbuffer->u.playerinfo[i].num = i; + strncpy(netbuffer->u.playerinfo[i].name, (const char *)&player_names[i], MAXPLAYERNAME+1); + netbuffer->u.playerinfo[i].name[MAXPLAYERNAME] = '\0'; - R_FlushTranslationColormapCache(); + //fetch IP address + //No, don't do that, you fuckface. + memset(netbuffer->u.playerinfo[i].address, 0, 4); - if (!skin->availability) // Safe to print... - CONS_Printf(M_GetText("Patched skin '%s'\n"), skin->name); + if (G_GametypeHasTeams()) + { + if (!players[i].ctfteam) + netbuffer->u.playerinfo[i].team = 255; + else + netbuffer->u.playerinfo[i].team = (UINT8)players[i].ctfteam; + } + else + { + if (players[i].spectator) + netbuffer->u.playerinfo[i].team = 255; + else + netbuffer->u.playerinfo[i].team = 0; + } + + netbuffer->u.playerinfo[i].score = LONG(players[i].score); + netbuffer->u.playerinfo[i].timeinserver = SHORT((UINT16)(players[i].jointime / TICRATE)); + netbuffer->u.playerinfo[i].skin = (UINT8)(players[i].skin +#ifdef DEVELOP // it's safe to do this only because PLAYERINFO isn't read by the game itself + % 3 +#endif + ); + + // Extra data + netbuffer->u.playerinfo[i].data = 0; //players[i].skincolor; + + if (players[i].pflags & PF_TAGIT) + netbuffer->u.playerinfo[i].data |= 0x20; + + if (players[i].gotflag) + netbuffer->u.playerinfo[i].data |= 0x40; + + if (players[i].powers[pw_super]) + netbuffer->u.playerinfo[i].data |= 0x80; } - return; + + HSendPacket(node, false, 0, sizeof(plrinfo) * MAXPLAYERS); } -#undef HUDNAMEWRITE -#undef SYMBOLCONVERT +/** Sends a PT_SERVERCFG packet + * + * \param node The destination + * \return True if the packet was successfully sent + * + */ +static boolean SV_SendServerConfig(INT32 node) +{ + boolean waspacketsent; + + netbuffer->packettype = PT_SERVERCFG; + + netbuffer->u.servercfg.version = VERSION; + netbuffer->u.servercfg.subversion = SUBVERSION; + + netbuffer->u.servercfg.serverplayer = (UINT8)serverplayer; + netbuffer->u.servercfg.totalslotnum = (UINT8)(doomcom->numslots); + netbuffer->u.servercfg.gametic = (tic_t)LONG(gametic); + netbuffer->u.servercfg.clientnode = (UINT8)node; + netbuffer->u.servercfg.gamestate = (UINT8)gamestate; + netbuffer->u.servercfg.gametype = (UINT8)gametype; + netbuffer->u.servercfg.modifiedgame = (UINT8)modifiedgame; + + memcpy(netbuffer->u.servercfg.server_context, server_context, 8); + + { + const size_t len = sizeof (serverconfig_pak); + +#ifdef DEBUGFILE + if (debugfile) + { + fprintf(debugfile, "ServerConfig Packet about to be sent, size of packet:%s to node:%d\n", + sizeu1(len), node); + } +#endif + + waspacketsent = HSendPacket(node, true, 0, len); + } + +#ifdef DEBUGFILE + if (debugfile) + { + if (waspacketsent) + { + fprintf(debugfile, "ServerConfig Packet was sent\n"); + } + else + { + fprintf(debugfile, "ServerConfig Packet could not be sent right now\n"); + } + } +#endif + + return waspacketsent; +} + +#ifndef NONET +#define SAVEGAMESIZE (768*1024) + +static boolean SV_ResendingSavegameToAnyone(void) +{ + INT32 i; + + for (i = 0; i < MAXNETNODES; i++) + if (resendingsavegame[i]) + return true; + return false; +} + +static void SV_SendSaveGame(INT32 node, boolean resending) +{ + size_t length, compressedlen; + UINT8 *savebuffer; + UINT8 *compressedsave; + UINT8 *buffertosend; + + // first save it in a malloced buffer + savebuffer = (UINT8 *)malloc(SAVEGAMESIZE); + if (!savebuffer) + { + CONS_Alert(CONS_ERROR, M_GetText("No more free memory for savegame\n")); + return; + } + + // Leave room for the uncompressed length. + save_p = savebuffer + sizeof(UINT32); + + P_SaveNetGame(resending); + + length = save_p - savebuffer; + if (length > SAVEGAMESIZE) + { + free(savebuffer); + save_p = NULL; + I_Error("Savegame buffer overrun"); + } + + // Allocate space for compressed save: one byte fewer than for the + // uncompressed data to ensure that the compression is worthwhile. + compressedsave = malloc(length - 1); + if (!compressedsave) + { + CONS_Alert(CONS_ERROR, M_GetText("No more free memory for savegame\n")); + return; + } + + // Attempt to compress it. + if((compressedlen = lzf_compress(savebuffer + sizeof(UINT32), length - sizeof(UINT32), compressedsave + sizeof(UINT32), length - sizeof(UINT32) - 1))) + { + // Compressing succeeded; send compressed data + + free(savebuffer); + + // State that we're compressed. + buffertosend = compressedsave; + WRITEUINT32(compressedsave, length - sizeof(UINT32)); + length = compressedlen + sizeof(UINT32); + } + else + { + // Compression failed to make it smaller; send original + + free(compressedsave); + + // State that we're not compressed + buffertosend = savebuffer; + WRITEUINT32(savebuffer, 0); + } + + AddRamToSendQueue(node, buffertosend, length, SF_RAM, 0); + save_p = NULL; + + // Remember when we started sending the savegame so we can handle timeouts + sendingsavegame[node] = true; + freezetimeout[node] = I_GetTime() + jointimeout + length / 1024; // 1 extra tic for each kilobyte +} + +#ifdef DUMPCONSISTENCY +#define TMPSAVENAME "badmath.sav" +static consvar_t cv_dumpconsistency = CVAR_INIT ("dumpconsistency", "Off", CV_SAVE|CV_NETVAR, CV_OnOff, NULL); + +static void SV_SavedGame(void) +{ + size_t length; + UINT8 *savebuffer; + char tmpsave[256]; + + if (!cv_dumpconsistency.value) + return; + + sprintf(tmpsave, "%s" PATHSEP TMPSAVENAME, srb2home); + + // first save it in a malloced buffer + save_p = savebuffer = (UINT8 *)malloc(SAVEGAMESIZE); + if (!save_p) + { + CONS_Alert(CONS_ERROR, M_GetText("No more free memory for savegame\n")); + return; + } + + P_SaveNetGame(false); + + length = save_p - savebuffer; + if (length > SAVEGAMESIZE) + { + free(savebuffer); + save_p = NULL; + I_Error("Savegame buffer overrun"); + } + + // then save it! + if (!FIL_WriteFile(tmpsave, savebuffer, length)) + CONS_Printf(M_GetText("Didn't save %s for netgame"), tmpsave); + + free(savebuffer); + save_p = NULL; +} + +#undef TMPSAVENAME +#endif +#define TMPSAVENAME "$$$.sav" + + +static void CL_LoadReceivedSavegame(boolean reloading) +{ + UINT8 *savebuffer = NULL; + size_t length, decompressedlen; + char tmpsave[256]; + + sprintf(tmpsave, "%s" PATHSEP TMPSAVENAME, srb2home); + + length = FIL_ReadFile(tmpsave, &savebuffer); + + CONS_Printf(M_GetText("Loading savegame length %s\n"), sizeu1(length)); + if (!length) + { + I_Error("Can't read savegame sent"); + return; + } + + save_p = savebuffer; + + // Decompress saved game if necessary. + decompressedlen = READUINT32(save_p); + if(decompressedlen > 0) + { + UINT8 *decompressedbuffer = Z_Malloc(decompressedlen, PU_STATIC, NULL); + lzf_decompress(save_p, length - sizeof(UINT32), decompressedbuffer, decompressedlen); + Z_Free(savebuffer); + save_p = savebuffer = decompressedbuffer; + } + + paused = false; + demoplayback = false; + titlemapinaction = TITLEMAP_OFF; + titledemo = false; + automapactive = false; + + // load a base level + if (P_LoadNetGame(reloading)) + { + const UINT8 actnum = mapheaderinfo[gamemap-1]->actnum; + CONS_Printf(M_GetText("Map is now \"%s"), G_BuildMapName(gamemap)); + if (strcmp(mapheaderinfo[gamemap-1]->lvlttl, "")) + { + CONS_Printf(": %s", mapheaderinfo[gamemap-1]->lvlttl); + if (!(mapheaderinfo[gamemap-1]->levelflags & LF_NOZONE)) + CONS_Printf(M_GetText(" Zone")); + if (actnum > 0) + CONS_Printf(" %2d", actnum); + } + CONS_Printf("\"\n"); + } + else + { + CONS_Alert(CONS_ERROR, M_GetText("Can't load the level!\n")); + Z_Free(savebuffer); + save_p = NULL; + if (unlink(tmpsave) == -1) + CONS_Alert(CONS_ERROR, M_GetText("Can't delete %s\n"), tmpsave); + return; + } + + // done + Z_Free(savebuffer); + save_p = NULL; + if (unlink(tmpsave) == -1) + CONS_Alert(CONS_ERROR, M_GetText("Can't delete %s\n"), tmpsave); + consistancy[gametic%BACKUPTICS] = Consistancy(); + CON_ToggleOff(); + + // Tell the server we have received and reloaded the gamestate + // so they know they can resume the game + netbuffer->packettype = PT_RECEIVEDGAMESTATE; + HSendPacket(servernode, true, 0, 0); +} + +static void CL_ReloadReceivedSavegame(void) +{ + INT32 i; + + for (i = 0; i < MAXPLAYERS; i++) + { +#ifdef HAVE_BLUA + LUA_InvalidatePlayer(&players[i]); +#endif + sprintf(player_names[i], "Player %d", i + 1); + } + + CL_LoadReceivedSavegame(true); + + if (neededtic < gametic) + neededtic = gametic; + maketic = neededtic; + + ticcmd_oldangleturn[0] = players[consoleplayer].oldrelangleturn; + P_ForceLocalAngle(&players[consoleplayer], (angle_t)(players[consoleplayer].angleturn << 16)); + if (splitscreen) + { + ticcmd_oldangleturn[1] = players[secondarydisplayplayer].oldrelangleturn; + P_ForceLocalAngle(&players[secondarydisplayplayer], (angle_t)(players[secondarydisplayplayer].angleturn << 16)); + } + + camera.subsector = R_PointInSubsector(camera.x, camera.y); + camera2.subsector = R_PointInSubsector(camera2.x, camera2.y); + + cl_redownloadinggamestate = false; + + CONS_Printf(M_GetText("Game state reloaded\n")); +} +#endif + +#ifndef NONET +static void SendAskInfo(INT32 node) +{ + const tic_t asktime = I_GetTime(); + netbuffer->packettype = PT_ASKINFO; + netbuffer->u.askinfo.version = VERSION; + netbuffer->u.askinfo.time = (tic_t)LONG(asktime); + + // Even if this never arrives due to the host being firewalled, we've + // now allowed traffic from the host to us in, so once the MS relays + // our address to the host, it'll be able to speak to us. + HSendPacket(node, false, 0, sizeof (askinfo_pak)); +} + +serverelem_t serverlist[MAXSERVERLIST]; +UINT32 serverlistcount = 0; + +#define FORCECLOSE 0x8000 + +static void SL_ClearServerList(INT32 connectedserver) +{ + UINT32 i; + + for (i = 0; i < serverlistcount; i++) + if (connectedserver != serverlist[i].node) + { + Net_CloseConnection(serverlist[i].node|FORCECLOSE); + serverlist[i].node = 0; + } + serverlistcount = 0; +} + +static UINT32 SL_SearchServer(INT32 node) +{ + UINT32 i; + for (i = 0; i < serverlistcount; i++) + if (serverlist[i].node == node) + return i; + + return UINT32_MAX; +} + +static void SL_InsertServer(serverinfo_pak* info, SINT8 node) +{ + UINT32 i; + + // search if not already on it + i = SL_SearchServer(node); + if (i == UINT32_MAX) + { + // not found add it + if (serverlistcount >= MAXSERVERLIST) + return; // list full + + if (info->_255 != 255) + return;/* old packet format */ + + if (info->packetversion != PACKETVERSION) + return;/* old new packet format */ + + if (info->version != VERSION) + return; // Not same version. + + if (info->subversion != SUBVERSION) + return; // Close, but no cigar. + + if (strcmp(info->application, SRB2APPLICATION)) + return;/* that's a different mod */ + + i = serverlistcount++; + } + + serverlist[i].info = *info; + serverlist[i].node = node; + + // resort server list + M_SortServerList(); +} + +#if defined (MASTERSERVER) && defined (HAVE_THREADS) +struct Fetch_servers_ctx +{ + int room; + int id; +}; + +static void +Fetch_servers_thread (struct Fetch_servers_ctx *ctx) +{ + msg_server_t *server_list; + + server_list = GetShortServersList(ctx->room, ctx->id); + + if (server_list) + { + I_lock_mutex(&ms_QueryId_mutex); + { + if (ctx->id != ms_QueryId) + { + free(server_list); + server_list = NULL; + } + } + I_unlock_mutex(ms_QueryId_mutex); + + if (server_list) + { + I_lock_mutex(&m_menu_mutex); + { + if (m_waiting_mode == M_WAITING_SERVERS) + m_waiting_mode = M_NOT_WAITING; + } + I_unlock_mutex(m_menu_mutex); + + I_lock_mutex(&ms_ServerList_mutex); + { + ms_ServerList = server_list; + } + I_unlock_mutex(ms_ServerList_mutex); + } + } + + free(ctx); +} +#endif/*defined (MASTERSERVER) && defined (HAVE_THREADS)*/ + +void CL_QueryServerList (msg_server_t *server_list) +{ + INT32 i; + + for (i = 0; server_list[i].header.buffer[0]; i++) + { + // Make sure MS version matches our own, to + // thwart nefarious servers who lie to the MS. + + /* lol bruh, that version COMES from the servers */ + //if (strcmp(version, server_list[i].version) == 0) + { + INT32 node = I_NetMakeNodewPort(server_list[i].ip, server_list[i].port); + if (node == -1) + break; // no more node free + SendAskInfo(node); + // Force close the connection so that servers can't eat + // up nodes forever if we never get a reply back from them + // (usually when they've not forwarded their ports). + // + // Don't worry, we'll get in contact with the working + // servers again when they send SERVERINFO to us later! + // + // (Note: as a side effect this probably means every + // server in the list will probably be using the same node (e.g. node 1), + // not that it matters which nodes they use when + // the connections are closed afterwards anyway) + // -- Monster Iestyn 12/11/18 + Net_CloseConnection(node|FORCECLOSE); + } + } +} + +void CL_UpdateServerList(boolean internetsearch, INT32 room) +{ + (void)internetsearch; + (void)room; + + SL_ClearServerList(0); + + if (!netgame && I_NetOpenSocket) + { + if (I_NetOpenSocket()) + { + netgame = true; + multiplayer = true; + } + } + + // search for local servers + if (netgame) + SendAskInfo(BROADCASTADDR); + +#ifdef MASTERSERVER + if (internetsearch) + { +#ifdef HAVE_THREADS + struct Fetch_servers_ctx *ctx; + + ctx = malloc(sizeof *ctx); + + /* This called from M_Refresh so I don't use a mutex */ + m_waiting_mode = M_WAITING_SERVERS; + + I_lock_mutex(&ms_QueryId_mutex); + { + ctx->id = ms_QueryId; + } + I_unlock_mutex(ms_QueryId_mutex); + + ctx->room = room; + + I_spawn_thread("fetch-servers", (I_thread_fn)Fetch_servers_thread, ctx); +#else + msg_server_t *server_list; + + server_list = GetShortServersList(room, 0); + + if (server_list) + { + CL_QueryServerList(server_list); + free(server_list); + } +#endif + } +#endif/*MASTERSERVER*/ +} + +#endif // ifndef NONET + +/** Called by CL_ServerConnectionTicker + * + * \param asksent The last time we asked the server to join. We re-ask every second in case our request got lost in transmit. + * \return False if the connection was aborted + * \sa CL_ServerConnectionTicker + * \sa CL_ConnectToServer + * + */ +static boolean CL_ServerConnectionSearchTicker(tic_t *asksent) +{ +#ifndef NONET + INT32 i; + + // serverlist is updated by GetPacket function + if (serverlistcount > 0) + { + // this can be a responce to our broadcast request + if (servernode == -1 || servernode >= MAXNETNODES) + { + i = 0; + servernode = serverlist[i].node; + CONS_Printf(M_GetText("Found, ")); + } + else + { + i = SL_SearchServer(servernode); + if (i < 0) + return true; + } + + // Quit here rather than downloading files and being refused later. + if (serverlist[i].info.refusereason) + { + D_QuitNetGame(); + CL_Reset(); + D_StartTitle(); + 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; + } + + if (client) + { + D_ParseFileneeded(serverlist[i].info.fileneedednum, + serverlist[i].info.fileneeded); + CONS_Printf(M_GetText("Checking files...\n")); + i = CL_CheckFiles(); + if (i == 3) // too many files + { + D_QuitNetGame(); + CL_Reset(); + D_StartTitle(); + M_StartMessage(M_GetText( + "You have too many WAD files loaded\n" + "to add ones the server is using.\n" + "Please restart SRB2 before connecting.\n\n" + "Press ESC\n" + ), NULL, MM_NOTHING); + return false; + } + else if (i == 2) // cannot join for some reason + { + D_QuitNetGame(); + CL_Reset(); + D_StartTitle(); + M_StartMessage(M_GetText( + "You have the wrong addons loaded.\n\n" + "To play on this server, restart\n" + "the game and don't load any addons.\n" + "SRB2 will automatically add\n" + "everything you need when you join.\n\n" + "Press ESC\n" + ), NULL, MM_NOTHING); + return false; + } + else if (i == 1) + cl_mode = CL_ASKJOIN; + else + { + // must download something + // can we, though? + if (!CL_CheckDownloadable()) // nope! + { + D_QuitNetGame(); + CL_Reset(); + D_StartTitle(); + M_StartMessage(M_GetText( + "You cannot connect to this server\n" + "because you cannot download the files\n" + "that you are missing from the server.\n\n" + "See the console or log file for\n" + "more details.\n\n" + "Press ESC\n" + ), NULL, MM_NOTHING); + return false; + } + // no problem if can't send packet, we will retry later + if (CL_SendFileRequest()) + { + cl_mode = CL_DOWNLOADFILES; +#ifndef NONET + Snake_Initialise(); +#endif + } + } + } + else + cl_mode = CL_ASKJOIN; // files need not be checked for the server. + + return true; + } + + // Ask the info to the server (askinfo packet) + if (*asksent + NEWTICRATE < I_GetTime()) + { + SendAskInfo(servernode); + *asksent = I_GetTime(); + } +#else + (void)asksent; + // No netgames, so we skip this state. + cl_mode = CL_ASKJOIN; +#endif // ifndef NONET/else + + return true; +} + +/** Called by CL_ConnectToServer + * + * \param tmpsave The name of the gamestate file??? + * \param oldtic Used for knowing when to poll events and redraw + * \param asksent ??? + * \return False if the connection was aborted + * \sa CL_ServerConnectionSearchTicker + * \sa CL_ConnectToServer + * + */ +static boolean CL_ServerConnectionTicker(const char *tmpsave, tic_t *oldtic, tic_t *asksent) +{ + boolean waitmore; + INT32 i; + +#ifdef NONET + (void)tmpsave; +#endif + + switch (cl_mode) + { + case CL_SEARCHING: + if (!CL_ServerConnectionSearchTicker(asksent)) + return false; + break; + + case CL_DOWNLOADFILES: + waitmore = false; + for (i = 0; i < fileneedednum; i++) + if (fileneeded[i].status == FS_DOWNLOADING + || fileneeded[i].status == FS_REQUESTED) + { + waitmore = true; + break; + } + if (waitmore) + break; // exit the case + +#ifndef NONET + if (snake) + { + free(snake); + snake = NULL; + } +#endif + + cl_mode = CL_ASKJOIN; // don't break case continue to cljoin request now + /* FALLTHRU */ + + case CL_ASKJOIN: + CL_LoadServerFiles(); +#ifndef NONET + // prepare structures to save the file + // WARNING: this can be useless in case of server not in GS_LEVEL + // but since the network layer doesn't provide ordered packets... + CL_PrepareDownloadSaveGame(tmpsave); +#endif + if (CL_SendJoin()) + cl_mode = CL_WAITJOINRESPONSE; + break; + +#ifndef NONET + case CL_DOWNLOADSAVEGAME: + // At this state, the first (and only) needed file is the gamestate + if (fileneeded[0].status == FS_FOUND) + { + // Gamestate is now handled within CL_LoadReceivedSavegame() + CL_LoadReceivedSavegame(false); + cl_mode = CL_CONNECTED; + } // don't break case continue to CL_CONNECTED + else + break; +#endif + + case CL_WAITJOINRESPONSE: + case CL_CONNECTED: + default: + break; + + // Connection closed by cancel, timeout or refusal. + case CL_ABORTED: + cl_mode = CL_SEARCHING; + return false; + + } + + GetPackets(); + Net_AckTicker(); + + // Call it only once by tic + if (*oldtic != I_GetTime()) + { + I_OsPolling(); + for (; eventtail != eventhead; eventtail = (eventtail+1) & (MAXEVENTS-1)) + G_MapEventsToControls(&events[eventtail]); + + if (gamekeydown[KEY_ESCAPE] || gamekeydown[KEY_JOY1+1]) + { + CONS_Printf(M_GetText("Network game synchronization aborted.\n")); +// M_StartMessage(M_GetText("Network game synchronization aborted.\n\nPress ESC\n"), NULL, MM_NOTHING); + +#ifndef NONET + if (snake) + { + free(snake); + snake = NULL; + } +#endif + + D_QuitNetGame(); + CL_Reset(); + D_StartTitle(); + memset(gamekeydown, 0, NUMKEYS); + return false; + } +#ifndef NONET + else if (cl_mode == CL_DOWNLOADFILES && snake) + Snake_Handle(); +#endif + + if (client && (cl_mode == CL_DOWNLOADFILES || cl_mode == CL_DOWNLOADSAVEGAME)) + FileReceiveTicker(); + + // why are these here? this is for servers, we're a client + //if (key == 's' && server) + // doomcom->numnodes = (INT16)pnumnodes; + //FileSendTicker(); + *oldtic = I_GetTime(); + +#ifndef NONET + if (client && cl_mode != CL_CONNECTED && cl_mode != CL_ABORTED) + { + if (cl_mode != CL_DOWNLOADFILES && cl_mode != CL_DOWNLOADSAVEGAME) + { + F_MenuPresTicker(true); // title sky + F_TitleScreenTicker(true); + F_TitleScreenDrawer(); + } + CL_DrawConnectionStatus(); + I_UpdateNoVsync(); // page flip or blit buffer + if (moviemode) + M_SaveFrame(); + S_UpdateSounds(); + S_UpdateClosedCaptions(); + } +#else + CON_Drawer(); + I_UpdateNoVsync(); +#endif + } + else + I_Sleep(); + + return true; +} + +/** Use adaptive send using net_bandwidth and stat.sendbytes + * + * \todo Better description... + * + */ +static void CL_ConnectToServer(void) +{ + INT32 pnumnodes, nodewaited = doomcom->numnodes, i; + tic_t oldtic; +#ifndef NONET + tic_t asksent; + char tmpsave[256]; + + sprintf(tmpsave, "%s" PATHSEP TMPSAVENAME, srb2home); + + lastfilenum = -1; +#endif + + cl_mode = CL_SEARCHING; + +#ifndef NONET + // Don't get a corrupt savegame error because tmpsave already exists + if (FIL_FileExists(tmpsave) && unlink(tmpsave) == -1) + I_Error("Can't delete %s\n", tmpsave); +#endif + + if (netgame) + { + if (servernode < 0 || servernode >= MAXNETNODES) + CONS_Printf(M_GetText("Searching for a server...\n")); + else + CONS_Printf(M_GetText("Contacting the server...\n")); + } + + if (gamestate == GS_INTERMISSION) + Y_EndIntermission(); // clean up intermission graphics etc + + DEBFILE(va("waiting %d nodes\n", doomcom->numnodes)); + G_SetGamestate(GS_WAITINGPLAYERS); + wipegamestate = GS_WAITINGPLAYERS; + + ClearAdminPlayers(); + pnumnodes = 1; + oldtic = I_GetTime() - 1; +#ifndef NONET + asksent = (tic_t) - TICRATE; + + i = SL_SearchServer(servernode); + + if (i != -1) + { + char *gametypestr = serverlist[i].info.gametypename; + CONS_Printf(M_GetText("Connecting to: %s\n"), serverlist[i].info.servername); + gametypestr[sizeof serverlist[i].info.gametypename - 1] = '\0'; + CONS_Printf(M_GetText("Gametype: %s\n"), gametypestr); + CONS_Printf(M_GetText("Version: %d.%d.%u\n"), serverlist[i].info.version/100, + serverlist[i].info.version%100, serverlist[i].info.subversion); + } + SL_ClearServerList(servernode); +#endif + + do + { + // If the connection was aborted for some reason, leave +#ifndef NONET + if (!CL_ServerConnectionTicker(tmpsave, &oldtic, &asksent)) +#else + if (!CL_ServerConnectionTicker((char*)NULL, &oldtic, (tic_t *)NULL)) +#endif + return; + + if (server) + { + pnumnodes = 0; + for (i = 0; i < MAXNETNODES; i++) + if (nodeingame[i]) + pnumnodes++; + } + } + while (!(cl_mode == CL_CONNECTED && (client || (server && nodewaited <= pnumnodes)))); + + DEBFILE(va("Synchronisation Finished\n")); + + displayplayer = consoleplayer; +} + +#ifndef NONET +typedef struct banreason_s +{ + char *reason; + struct banreason_s *prev; //-1 + struct banreason_s *next; //+1 +} banreason_t; + +static banreason_t *reasontail = NULL; //last entry, use prev +static banreason_t *reasonhead = NULL; //1st entry, use next + +static void Command_ShowBan(void) //Print out ban list +{ + size_t i; + const char *address, *mask; + banreason_t *reasonlist = reasonhead; + + if (I_GetBanAddress) + CONS_Printf(M_GetText("Ban List:\n")); + else + return; + + for (i = 0;(address = I_GetBanAddress(i)) != NULL;i++) + { + if (!I_GetBanMask || (mask = I_GetBanMask(i)) == NULL) + CONS_Printf("%s: %s ", sizeu1(i+1), address); + else + CONS_Printf("%s: %s/%s ", sizeu1(i+1), address, mask); + + if (reasonlist && reasonlist->reason) + CONS_Printf("(%s)\n", reasonlist->reason); + else + CONS_Printf("\n"); + + if (reasonlist) reasonlist = reasonlist->next; + } + + if (i == 0 && !address) + CONS_Printf(M_GetText("(empty)\n")); +} + +void D_SaveBan(void) +{ + FILE *f; + size_t i; + banreason_t *reasonlist = reasonhead; + const char *address, *mask; + + if (!reasonhead) + return; + + f = fopen(va("%s"PATHSEP"%s", srb2home, "ban.txt"), "w"); + + if (!f) + { + CONS_Alert(CONS_WARNING, M_GetText("Could not save ban list into ban.txt\n")); + return; + } + + for (i = 0;(address = I_GetBanAddress(i)) != NULL;i++) + { + if (!I_GetBanMask || (mask = I_GetBanMask(i)) == NULL) + fprintf(f, "%s 0", address); + else + fprintf(f, "%s %s", address, mask); + + if (reasonlist && reasonlist->reason) + fprintf(f, " %s\n", reasonlist->reason); + else + fprintf(f, " %s\n", "NA"); + + if (reasonlist) reasonlist = reasonlist->next; + } + + fclose(f); +} + +static void Ban_Add(const char *reason) +{ + banreason_t *reasonlist = malloc(sizeof(*reasonlist)); + + if (!reasonlist) + return; + if (!reason) + reason = "NA"; + + reasonlist->next = NULL; + reasonlist->reason = Z_StrDup(reason); + if ((reasonlist->prev = reasontail) == NULL) + reasonhead = reasonlist; + else + reasontail->next = reasonlist; + reasontail = reasonlist; +} + +static void Command_ClearBans(void) +{ + banreason_t *temp; + + if (!I_ClearBans) + return; + + I_ClearBans(); + D_SaveBan(); + reasontail = NULL; + while (reasonhead) + { + temp = reasonhead->next; + Z_Free(reasonhead->reason); + free(reasonhead); + reasonhead = temp; + } +} + +static void Ban_Load_File(boolean warning) +{ + FILE *f; + size_t i; + const char *address, *mask; + char buffer[MAX_WADPATH]; + + f = fopen(va("%s"PATHSEP"%s", srb2home, "ban.txt"), "r"); + + if (!f) + { + if (warning) + CONS_Alert(CONS_WARNING, M_GetText("Could not open ban.txt for ban list\n")); + return; + } + + if (I_ClearBans) + Command_ClearBans(); + else + { + fclose(f); + return; + } + + for (i=0; fgets(buffer, (int)sizeof(buffer), f); i++) + { + address = strtok(buffer, " \t\r\n"); + mask = strtok(NULL, " \t\r\n"); + + I_SetBanAddress(address, mask); + + Ban_Add(strtok(NULL, "\r\n")); + } + + fclose(f); +} + +static void Command_ReloadBan(void) //recheck ban.txt +{ + Ban_Load_File(true); +} + +static void Command_connect(void) +{ + if (COM_Argc() < 2 || *COM_Argv(1) == 0) + { + CONS_Printf(M_GetText( + "Connect (port): connect to a server\n" + "Connect ANY: connect to the first lan server found\n" + //"Connect SELF: connect to your own server.\n" + )); + return; + } + + if (Playing() || titledemo) + { + CONS_Printf(M_GetText("You cannot connect while in a game. End this game first.\n")); + return; + } + + // modified game check: no longer handled + // we don't request a restart unless the filelist differs + + server = false; +/* + if (!stricmp(COM_Argv(1), "self")) + { + servernode = 0; + server = true; + /// \bug should be but... + //SV_SpawnServer(); + } + else +*/ + { + // used in menu to connect to a server in the list + if (netgame && !stricmp(COM_Argv(1), "node")) + { + servernode = (SINT8)atoi(COM_Argv(2)); + } + else if (netgame) + { + CONS_Printf(M_GetText("You cannot connect while in a game. End this game first.\n")); + return; + } + else if (I_NetOpenSocket) + { + I_NetOpenSocket(); + netgame = true; + multiplayer = true; + + if (!stricmp(COM_Argv(1), "any")) + servernode = BROADCASTADDR; + else if (I_NetMakeNodewPort) + { + if (COM_Argc() >= 3) // address AND port + servernode = I_NetMakeNodewPort(COM_Argv(1), COM_Argv(2)); + else // address only, or address:port + servernode = I_NetMakeNode(COM_Argv(1)); + } + else + { + CONS_Alert(CONS_ERROR, M_GetText("There is no server identification with this network driver\n")); + D_CloseConnection(); + return; + } + } + else + CONS_Alert(CONS_ERROR, M_GetText("There is no network driver\n")); + } + + splitscreen = false; + SplitScreen_OnChange(); + botingame = false; + botskin = 0; + CL_ConnectToServer(); +} +#endif + +static void ResetNode(INT32 node); + +// +// CL_ClearPlayer +// +// Clears the player data so that a future client can use this slot +// +void CL_ClearPlayer(INT32 playernum) +{ + if (players[playernum].mo) + P_RemoveMobj(players[playernum].mo); + memset(&players[playernum], 0, sizeof (player_t)); + memset(playeraddress[playernum], 0, sizeof(*playeraddress)); +} + +// +// CL_RemovePlayer +// +// Removes a player from the current game +// +static void CL_RemovePlayer(INT32 playernum, kickreason_t reason) +{ + // Sanity check: exceptional cases (i.e. c-fails) can cause multiple + // kick commands to be issued for the same player. + if (!playeringame[playernum]) + return; + + if (server && !demoplayback && playernode[playernum] != UINT8_MAX) + { + INT32 node = playernode[playernum]; + playerpernode[node]--; + if (playerpernode[node] <= 0) + { + nodeingame[node] = false; + Net_CloseConnection(node); + ResetNode(node); + } + } + + if (gametyperules & GTR_TEAMFLAGS) + P_PlayerFlagBurst(&players[playernum], false); // Don't take the flag with you! + + // If in a special stage, redistribute the player's spheres across + // the remaining players. + if (G_IsSpecialStage(gamemap)) + { + INT32 i, count, sincrement, spheres, rincrement, rings; + + for (i = 0, count = 0; i < MAXPLAYERS; i++) + { + if (playeringame[i]) + count++; + } + + count--; + sincrement = spheres = players[playernum].spheres; + rincrement = rings = players[playernum].rings; + + if (count) + { + sincrement /= count; + rincrement /= count; + } + + for (i = 0; i < MAXPLAYERS; i++) + { + if (playeringame[i] && i != playernum) + { + if (spheres < 2*sincrement) + { + P_GivePlayerSpheres(&players[i], spheres); + spheres = 0; + } + else + { + P_GivePlayerSpheres(&players[i], sincrement); + spheres -= sincrement; + } + + if (rings < 2*rincrement) + { + P_GivePlayerRings(&players[i], rings); + rings = 0; + } + else + { + P_GivePlayerRings(&players[i], rincrement); + rings -= rincrement; + } + } + } + } + + LUAh_PlayerQuit(&players[playernum], reason); // Lua hook for player quitting + + // don't look through someone's view who isn't there + if (playernum == displayplayer) + { + // Call ViewpointSwitch hooks here. + // The viewpoint was forcibly changed. + LUAh_ViewpointSwitch(&players[consoleplayer], &players[consoleplayer], true); + displayplayer = consoleplayer; + } + + // Reset player data + CL_ClearPlayer(playernum); + + // remove avatar of player + playeringame[playernum] = false; + playernode[playernum] = UINT8_MAX; + while (!playeringame[doomcom->numslots-1] && doomcom->numslots > 1) + doomcom->numslots--; + + // Reset the name + sprintf(player_names[playernum], "Player %d", playernum+1); + + player_name_changes[playernum] = 0; + + if (IsPlayerAdmin(playernum)) + { + RemoveAdminPlayer(playernum); // don't stay admin after you're gone + } + + LUA_InvalidatePlayer(&players[playernum]); + + if (G_TagGametype()) //Check if you still have a game. Location flexible. =P + P_CheckSurvivors(); + else if (gametyperules & GTR_RACE) + P_CheckRacers(); +} + +void CL_Reset(void) +{ + if (metalrecording) + G_StopMetalRecording(false); + if (metalplayback) + G_StopMetalDemo(); + if (demorecording) + G_CheckDemoStatus(); + + // reset client/server code + DEBFILE(va("\n-=-=-=-=-=-=-= Client reset =-=-=-=-=-=-=-\n\n")); + + if (servernode > 0 && servernode < MAXNETNODES) + { + nodeingame[(UINT8)servernode] = false; + Net_CloseConnection(servernode); + } + D_CloseConnection(); // netgame = false + multiplayer = false; + servernode = 0; + server = true; + doomcom->numnodes = 1; + doomcom->numslots = 1; + SV_StopServer(); + SV_ResetServer(); + CV_RevertNetVars(); + + // make sure we don't leave any fileneeded gunk over from a failed join + fileneedednum = 0; + memset(fileneeded, 0, sizeof(fileneeded)); + + // D_StartTitle should get done now, but the calling function will handle it +} + +#ifndef NONET +static void Command_GetPlayerNum(void) +{ + INT32 i; + + for (i = 0; i < MAXPLAYERS; i++) + if (playeringame[i]) + { + if (serverplayer == i) + CONS_Printf(M_GetText("num:%2d node:%2d %s\n"), i, playernode[i], player_names[i]); + else + CONS_Printf(M_GetText("\x82num:%2d node:%2d %s\n"), i, playernode[i], player_names[i]); + } +} + +SINT8 nametonum(const char *name) +{ + INT32 playernum, i; + + if (!strcmp(name, "0")) + return 0; + + playernum = (SINT8)atoi(name); + + if (playernum < 0 || playernum >= MAXPLAYERS) + return -1; + + if (playernum) + { + if (playeringame[playernum]) + return (SINT8)playernum; + else + return -1; + } + + for (i = 0; i < MAXPLAYERS; i++) + if (playeringame[i] && !stricmp(player_names[i], name)) + return (SINT8)i; + + CONS_Printf(M_GetText("There is no player named \"%s\"\n"), name); + + return -1; +} + +/** Lists all players and their player numbers. + * + * \sa Command_GetPlayerNum + */ +static void Command_Nodes(void) +{ + INT32 i; + size_t maxlen = 0; + const char *address; + + for (i = 0; i < MAXPLAYERS; i++) + { + const size_t plen = strlen(player_names[i]); + if (playeringame[i] && plen > maxlen) + maxlen = plen; + } + + for (i = 0; i < MAXPLAYERS; i++) + { + if (playeringame[i]) + { + CONS_Printf("%.2u: %*s", i, (int)maxlen, player_names[i]); + + if (playernode[i] != UINT8_MAX) + { + CONS_Printf(" - node %.2d", playernode[i]); + if (I_GetNodeAddress && (address = I_GetNodeAddress(playernode[i])) != NULL) + CONS_Printf(" - %s", address); + } + + if (IsPlayerAdmin(i)) + CONS_Printf(M_GetText(" (verified admin)")); + + if (players[i].spectator) + CONS_Printf(M_GetText(" (spectator)")); + + CONS_Printf("\n"); + } + } +} + +static void Command_Ban(void) +{ + if (COM_Argc() < 2) + { + CONS_Printf(M_GetText("Ban : ban and kick a player\n")); + return; + } + + if (!netgame) // Don't kick Tails in splitscreen! + { + CONS_Printf(M_GetText("This only works in a netgame.\n")); + return; + } + + if (server || IsPlayerAdmin(consoleplayer)) + { + UINT8 buf[3 + MAX_REASONLENGTH]; + UINT8 *p = buf; + const SINT8 pn = nametonum(COM_Argv(1)); + const INT32 node = playernode[(INT32)pn]; + + if (pn == -1 || pn == 0) + return; + + WRITEUINT8(p, pn); + + if (server && I_Ban && !I_Ban(node)) // only the server is allowed to do this right now + { + CONS_Alert(CONS_WARNING, M_GetText("Too many bans! Geez, that's a lot of people you're excluding...\n")); + WRITEUINT8(p, KICK_MSG_GO_AWAY); + SendNetXCmd(XD_KICK, &buf, 2); + } + else + { + if (server) // only the server is allowed to do this right now + { + Ban_Add(COM_Argv(2)); + D_SaveBan(); // save the ban list + } + + if (COM_Argc() == 2) + { + WRITEUINT8(p, KICK_MSG_BANNED); + SendNetXCmd(XD_KICK, &buf, 2); + } + else + { + size_t i, j = COM_Argc(); + char message[MAX_REASONLENGTH]; + + //Steal from the motd code so you don't have to put the reason in quotes. + strlcpy(message, COM_Argv(2), sizeof message); + for (i = 3; i < j; i++) + { + strlcat(message, " ", sizeof message); + strlcat(message, COM_Argv(i), sizeof message); + } + + WRITEUINT8(p, KICK_MSG_CUSTOM_BAN); + WRITESTRINGN(p, message, MAX_REASONLENGTH); + SendNetXCmd(XD_KICK, &buf, p - buf); + } + } + } + else + CONS_Printf(M_GetText("Only the server or a remote admin can use this.\n")); + +} + +static void Command_BanIP(void) +{ + if (COM_Argc() < 2) + { + CONS_Printf(M_GetText("banip : ban an ip address\n")); + return; + } + + if (server) // Only the server can use this, otherwise does nothing. + { + const char *address = (COM_Argv(1)); + const char *reason; + + if (COM_Argc() == 2) + reason = NULL; + else + reason = COM_Argv(2); + + + if (I_SetBanAddress && I_SetBanAddress(address, NULL)) + { + if (reason) + CONS_Printf("Banned IP address %s for: %s\n", address, reason); + else + CONS_Printf("Banned IP address %s\n", address); + + Ban_Add(reason); + D_SaveBan(); + } + else + { + return; + } + } +} + +static void Command_Kick(void) +{ + if (COM_Argc() < 2) + { + CONS_Printf(M_GetText("kick : kick a player\n")); + return; + } + + //if (!netgame) // Don't kick Tails in splitscreen! + //{ + // CONS_Printf(M_GetText("This only works in a netgame.\n")); + // return; + //} + + if (server || IsPlayerAdmin(consoleplayer)) + { + UINT8 buf[3 + MAX_REASONLENGTH]; + UINT8 *p = buf; + const SINT8 pn = nametonum(COM_Argv(1)); + + if (splitscreen && (pn == 0 || pn == 1)) + { + CONS_Printf(M_GetText("Splitscreen players cannot be kicked.\n")); + return; + } + if (pn == -1 || pn == 0) + return; + + // 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]]) + { + Net_ConnectionTimeout(playernode[pn]); + return; + } + + WRITESINT8(p, pn); + + if (COM_Argc() == 2) + { + WRITEUINT8(p, KICK_MSG_GO_AWAY); + SendNetXCmd(XD_KICK, &buf, 2); + } + else + { + size_t i, j = COM_Argc(); + char message[MAX_REASONLENGTH]; + + //Steal from the motd code so you don't have to put the reason in quotes. + strlcpy(message, COM_Argv(2), sizeof message); + for (i = 3; i < j; i++) + { + strlcat(message, " ", sizeof message); + strlcat(message, COM_Argv(i), sizeof message); + } + + WRITEUINT8(p, KICK_MSG_CUSTOM_KICK); + WRITESTRINGN(p, message, MAX_REASONLENGTH); + SendNetXCmd(XD_KICK, &buf, p - buf); + } + } + else + CONS_Printf(M_GetText("Only the server or a remote admin can use this.\n")); +} +#endif + +static void Got_KickCmd(UINT8 **p, INT32 playernum) +{ + INT32 pnum, msg; + char buf[3 + MAX_REASONLENGTH]; + char *reason = buf; + kickreason_t kickreason = KR_KICK; + boolean keepbody; + + pnum = READUINT8(*p); + msg = READUINT8(*p); + keepbody = (msg & KICK_MSG_KEEP_BODY) != 0; + msg &= ~KICK_MSG_KEEP_BODY; + + if (pnum == serverplayer && IsPlayerAdmin(playernum)) + { + CONS_Printf(M_GetText("Server is being shut down remotely. Goodbye!\n")); + + if (server) + COM_BufAddText("quit\n"); + + return; + } + + // Is playernum authorized to make this kick? + if (playernum != serverplayer && !IsPlayerAdmin(playernum) + && !(playernode[playernum] != UINT8_MAX && playerpernode[playernode[playernum]] == 2 + && nodetoplayer2[playernode[playernum]] == pnum)) + { + // We received a kick command from someone who isn't the + // server or admin, and who isn't in splitscreen removing + // player 2. Thus, it must be someone with a modified + // binary, trying to kick someone but without having + // authorization. + + // We deal with this by changing the kick reason to + // "consistency failure" and kicking the offending user + // instead. + + // Note: Splitscreen in netgames is broken because of + // this. Only the server has any idea of which players + // are using splitscreen on the same computer, so + // clients cannot always determine if a kick is + // legitimate. + + CONS_Alert(CONS_WARNING, M_GetText("Illegal kick command received from %s for player %d\n"), player_names[playernum], pnum); + + // In debug, print a longer message with more details. + // TODO Callum: Should we translate this? +/* + CONS_Debug(DBG_NETPLAY, + "So, you must be asking, why is this an illegal kick?\n" + "Well, let's take a look at the facts, shall we?\n" + "\n" + "playernum (this is the guy who did it), he's %d.\n" + "pnum (the guy he's trying to kick) is %d.\n" + "playernum's node is %d.\n" + "That node has %d players.\n" + "Player 2 on that node is %d.\n" + "pnum's node is %d.\n" + "That node has %d players.\n" + "Player 2 on that node is %d.\n" + "\n" + "If you think this is a bug, please report it, including all of the details above.\n", + playernum, pnum, + playernode[playernum], playerpernode[playernode[playernum]], + nodetoplayer2[playernode[playernum]], + playernode[pnum], playerpernode[playernode[pnum]], + nodetoplayer2[playernode[pnum]]); +*/ + pnum = playernum; + msg = KICK_MSG_CON_FAIL; + keepbody = true; + } + + //CONS_Printf("\x82%s ", player_names[pnum]); + + // If a verified admin banned someone, the server needs to know about it. + // If the playernum isn't zero (the server) then the server needs to record the ban. + if (server && playernum && (msg == KICK_MSG_BANNED || msg == KICK_MSG_CUSTOM_BAN)) + { + if (I_Ban && !I_Ban(playernode[(INT32)pnum])) + CONS_Alert(CONS_WARNING, M_GetText("Too many bans! Geez, that's a lot of people you're excluding...\n")); +#ifndef NONET + else + Ban_Add(reason); +#endif + } + + switch (msg) + { + case KICK_MSG_GO_AWAY: + if (!players[pnum].quittime) + HU_AddChatText(va("\x82*%s has been kicked (Go away)", player_names[pnum]), false); + kickreason = KR_KICK; + break; + case KICK_MSG_PING_HIGH: + HU_AddChatText(va("\x82*%s left the game (Broke ping limit)", player_names[pnum]), false); + kickreason = KR_PINGLIMIT; + break; + case KICK_MSG_CON_FAIL: + HU_AddChatText(va("\x82*%s left the game (Synch Failure)", player_names[pnum]), false); + kickreason = KR_SYNCH; + + if (M_CheckParm("-consisdump")) // Helps debugging some problems + { + INT32 i; + + CONS_Printf(M_GetText("Player kicked is #%d, dumping consistency...\n"), pnum); + + for (i = 0; i < MAXPLAYERS; i++) + { + if (!playeringame[i]) + continue; + CONS_Printf("-------------------------------------\n"); + CONS_Printf("Player %d: %s\n", i, player_names[i]); + CONS_Printf("Skin: %d\n", players[i].skin); + CONS_Printf("Color: %d\n", players[i].skincolor); + CONS_Printf("Speed: %d\n",players[i].speed>>FRACBITS); + if (players[i].mo) + { + if (!players[i].mo->skin) + CONS_Printf("Mobj skin: NULL!\n"); + else + CONS_Printf("Mobj skin: %s\n", ((skin_t *)players[i].mo->skin)->name); + CONS_Printf("Position: %d, %d, %d\n", players[i].mo->x, players[i].mo->y, players[i].mo->z); + if (!players[i].mo->state) + CONS_Printf("State: S_NULL\n"); + else + CONS_Printf("State: %d\n", (statenum_t)(players[i].mo->state-states)); + } + else + CONS_Printf("Mobj: NULL\n"); + CONS_Printf("-------------------------------------\n"); + } + } + break; + case KICK_MSG_TIMEOUT: + HU_AddChatText(va("\x82*%s left the game (Connection timeout)", player_names[pnum]), false); + kickreason = KR_TIMEOUT; + break; + case KICK_MSG_PLAYER_QUIT: + if (netgame && !players[pnum].quittime) // not splitscreen/bots or soulless body + HU_AddChatText(va("\x82*%s left the game", player_names[pnum]), false); + kickreason = KR_LEAVE; + break; + case KICK_MSG_BANNED: + HU_AddChatText(va("\x82*%s has been banned (Don't come back)", player_names[pnum]), false); + kickreason = KR_BAN; + break; + case KICK_MSG_CUSTOM_KICK: + READSTRINGN(*p, reason, MAX_REASONLENGTH+1); + HU_AddChatText(va("\x82*%s has been kicked (%s)", player_names[pnum], reason), false); + kickreason = KR_KICK; + break; + case KICK_MSG_CUSTOM_BAN: + READSTRINGN(*p, reason, MAX_REASONLENGTH+1); + HU_AddChatText(va("\x82*%s has been banned (%s)", player_names[pnum], reason), false); + kickreason = KR_BAN; + break; + } + + if (pnum == consoleplayer) + { + if (Playing()) + LUAh_GameQuit(); +#ifdef DUMPCONSISTENCY + if (msg == KICK_MSG_CON_FAIL) SV_SavedGame(); +#endif + D_QuitNetGame(); + CL_Reset(); + D_StartTitle(); + if (msg == KICK_MSG_CON_FAIL) + M_StartMessage(M_GetText("Server closed connection\n(synch failure)\nPress ESC\n"), NULL, MM_NOTHING); + else if (msg == KICK_MSG_PING_HIGH) + M_StartMessage(M_GetText("Server closed connection\n(Broke ping limit)\nPress ESC\n"), NULL, MM_NOTHING); + else if (msg == KICK_MSG_BANNED) + M_StartMessage(M_GetText("You have been banned by the server\n\nPress ESC\n"), NULL, MM_NOTHING); + else if (msg == KICK_MSG_CUSTOM_KICK) + M_StartMessage(va(M_GetText("You have been kicked\n(%s)\nPress ESC\n"), reason), NULL, MM_NOTHING); + else if (msg == KICK_MSG_CUSTOM_BAN) + M_StartMessage(va(M_GetText("You have been banned\n(%s)\nPress ESC\n"), reason), NULL, MM_NOTHING); + else + M_StartMessage(M_GetText("You have been kicked by the server\n\nPress ESC\n"), NULL, MM_NOTHING); + } + else if (keepbody) + { + if (server && !demoplayback && playernode[pnum] != UINT8_MAX) + { + INT32 node = playernode[pnum]; + playerpernode[node]--; + if (playerpernode[node] <= 0) + { + nodeingame[node] = false; + Net_CloseConnection(node); + ResetNode(node); + } + } + + playernode[pnum] = UINT8_MAX; + + players[pnum].quittime = 1; + } + else + CL_RemovePlayer(pnum, kickreason); +} + +static void Command_ResendGamestate(void) +{ + SINT8 playernum; + + if (COM_Argc() == 1) + { + CONS_Printf(M_GetText("resendgamestate : resend the game state to a player\n")); + return; + } + else if (client) + { + CONS_Printf(M_GetText("Only the server can use this.\n")); + return; + } + + playernum = nametonum(COM_Argv(1)); + if (playernum == -1 || playernum == 0) + return; + + // Send a PT_WILLRESENDGAMESTATE packet to the client so they know what's going on + netbuffer->packettype = PT_WILLRESENDGAMESTATE; + if (!HSendPacket(playernode[playernum], true, 0, 0)) + { + CONS_Alert(CONS_ERROR, M_GetText("A problem occured, please try again.\n")); + return; + } +} + +static CV_PossibleValue_t netticbuffer_cons_t[] = {{0, "MIN"}, {3, "MAX"}, {0, NULL}}; +consvar_t cv_netticbuffer = CVAR_INIT ("netticbuffer", "1", CV_SAVE, netticbuffer_cons_t, NULL); + +consvar_t cv_allownewplayer = CVAR_INIT ("allowjoin", "On", CV_SAVE|CV_NETVAR, CV_OnOff, NULL); +consvar_t cv_joinnextround = CVAR_INIT ("joinnextround", "Off", CV_SAVE|CV_NETVAR, CV_OnOff, NULL); /// \todo not done +static CV_PossibleValue_t maxplayers_cons_t[] = {{2, "MIN"}, {32, "MAX"}, {0, NULL}}; +consvar_t cv_maxplayers = CVAR_INIT ("maxplayers", "8", CV_SAVE|CV_NETVAR, maxplayers_cons_t, NULL); +static CV_PossibleValue_t joindelay_cons_t[] = {{1, "MIN"}, {3600, "MAX"}, {0, "Off"}, {0, NULL}}; +consvar_t cv_joindelay = CVAR_INIT ("joindelay", "10", CV_SAVE|CV_NETVAR, joindelay_cons_t, NULL); +static CV_PossibleValue_t rejointimeout_cons_t[] = {{1, "MIN"}, {60 * FRACUNIT, "MAX"}, {0, "Off"}, {0, NULL}}; +consvar_t cv_rejointimeout = CVAR_INIT ("rejointimeout", "Off", CV_SAVE|CV_NETVAR|CV_FLOAT, rejointimeout_cons_t, NULL); + +static CV_PossibleValue_t resynchattempts_cons_t[] = {{1, "MIN"}, {20, "MAX"}, {0, "No"}, {0, NULL}}; +consvar_t cv_resynchattempts = CVAR_INIT ("resynchattempts", "10", CV_SAVE|CV_NETVAR, resynchattempts_cons_t, NULL); +consvar_t cv_blamecfail = CVAR_INIT ("blamecfail", "Off", CV_SAVE|CV_NETVAR, CV_OnOff, NULL); + +// max file size to send to a player (in kilobytes) +static CV_PossibleValue_t maxsend_cons_t[] = {{0, "MIN"}, {51200, "MAX"}, {0, NULL}}; +consvar_t cv_maxsend = CVAR_INIT ("maxsend", "4096", CV_SAVE|CV_NETVAR, maxsend_cons_t, NULL); +consvar_t cv_noticedownload = CVAR_INIT ("noticedownload", "Off", CV_SAVE|CV_NETVAR, CV_OnOff, NULL); + +// Speed of file downloading (in packets per tic) +static CV_PossibleValue_t downloadspeed_cons_t[] = {{0, "MIN"}, {32, "MAX"}, {0, NULL}}; +consvar_t cv_downloadspeed = CVAR_INIT ("downloadspeed", "16", CV_SAVE|CV_NETVAR, downloadspeed_cons_t, NULL); + +static void Got_AddPlayer(UINT8 **p, INT32 playernum); + +// called one time at init +void D_ClientServerInit(void) +{ + DEBFILE(va("- - -== SRB2 v%d.%.2d.%d "VERSIONSTRING" debugfile ==- - -\n", + VERSION/100, VERSION%100, SUBVERSION)); + +#ifndef NONET + COM_AddCommand("getplayernum", Command_GetPlayerNum); + COM_AddCommand("kick", Command_Kick); + COM_AddCommand("ban", Command_Ban); + COM_AddCommand("banip", Command_BanIP); + COM_AddCommand("clearbans", Command_ClearBans); + COM_AddCommand("showbanlist", Command_ShowBan); + COM_AddCommand("reloadbans", Command_ReloadBan); + COM_AddCommand("connect", Command_connect); + COM_AddCommand("nodes", Command_Nodes); + COM_AddCommand("resendgamestate", Command_ResendGamestate); +#ifdef PACKETDROP + COM_AddCommand("drop", Command_Drop); + COM_AddCommand("droprate", Command_Droprate); +#endif +#ifdef _DEBUG + COM_AddCommand("numnodes", Command_Numnodes); +#endif +#endif + + RegisterNetXCmd(XD_KICK, Got_KickCmd); + RegisterNetXCmd(XD_ADDPLAYER, Got_AddPlayer); +#ifndef NONET +#ifdef DUMPCONSISTENCY + CV_RegisterVar(&cv_dumpconsistency); +#endif + Ban_Load_File(false); +#endif + + gametic = 0; + localgametic = 0; + + // do not send anything before the real begin + SV_StopServer(); + SV_ResetServer(); + if (dedicated) + SV_SpawnServer(); +} + +static void ResetNode(INT32 node) +{ + nodeingame[node] = false; + nodewaiting[node] = 0; + + nettics[node] = gametic; + supposedtics[node] = gametic; + + nodetoplayer[node] = -1; + nodetoplayer2[node] = -1; + playerpernode[node] = 0; + + sendingsavegame[node] = false; + resendingsavegame[node] = false; + savegameresendcooldown[node] = 0; +} + +void SV_ResetServer(void) +{ + INT32 i; + + // +1 because this command will be executed in com_executebuffer in + // tryruntic so gametic will be incremented, anyway maketic > gametic + // is not an issue + + maketic = gametic + 1; + neededtic = maketic; + tictoclear = maketic; + + joindelay = 0; + + for (i = 0; i < MAXNETNODES; i++) + ResetNode(i); + + for (i = 0; i < MAXPLAYERS; i++) + { + LUA_InvalidatePlayer(&players[i]); + playeringame[i] = false; + playernode[i] = UINT8_MAX; + memset(playeraddress[i], 0, sizeof(*playeraddress)); + sprintf(player_names[i], "Player %d", i + 1); + adminplayers[i] = -1; // Populate the entire adminplayers array with -1. + } + + memset(player_name_changes, 0, sizeof player_name_changes); + + mynode = 0; + cl_packetmissed = false; + cl_redownloadinggamestate = false; + + if (dedicated) + { + nodeingame[0] = true; + serverplayer = 0; + } + else + serverplayer = consoleplayer; + + if (server) + servernode = 0; + + doomcom->numslots = 0; + + // clear server_context + memset(server_context, '-', 8); + + DEBFILE("\n-=-=-=-=-=-=-= Server Reset =-=-=-=-=-=-=-\n\n"); +} + +static inline void SV_GenContext(void) +{ + UINT8 i; + // generate server_context, as exactly 8 bytes of randomly mixed A-Z and a-z + // (hopefully M_Random is initialized!! if not this will be awfully silly!) + for (i = 0; i < 8; i++) + { + const char a = M_RandomKey(26*2); + if (a < 26) // uppercase + server_context[i] = 'A'+a; + else // lowercase + server_context[i] = 'a'+(a-26); + } +} + +// +// D_QuitNetGame +// Called before quitting to leave a net game +// without hanging the other players +// +void D_QuitNetGame(void) +{ + if (!netgame || !netbuffer) + return; + + DEBFILE("===========================================================================\n" + " Quitting Game, closing connection\n" + "===========================================================================\n"); + + // abort send/receive of files + CloseNetFile(); + RemoveAllLuaFileTransfers(); + waitingforluafiletransfer = false; + waitingforluafilecommand = false; + + if (server) + { + INT32 i; + + netbuffer->packettype = PT_SERVERSHUTDOWN; + for (i = 0; i < MAXNETNODES; i++) + if (nodeingame[i]) + HSendPacket(i, true, 0, 0); +#ifdef MASTERSERVER + if (serverrunning && ms_RoomId > 0) + UnregisterServer(); +#endif + } + else if (servernode > 0 && servernode < MAXNETNODES && nodeingame[(UINT8)servernode]) + { + netbuffer->packettype = PT_CLIENTQUIT; + HSendPacket(servernode, true, 0, 0); + } + + D_CloseConnection(); + ClearAdminPlayers(); + + DEBFILE("===========================================================================\n" + " Log finish\n" + "===========================================================================\n"); +#ifdef DEBUGFILE + if (debugfile) + { + fclose(debugfile); + debugfile = NULL; + } +#endif +} + +// Adds a node to the game (player will follow at map change or at savegame....) +static inline void SV_AddNode(INT32 node) +{ + nettics[node] = gametic; + supposedtics[node] = gametic; + // little hack because the server connects to itself and puts + // nodeingame when connected not here + if (node) + nodeingame[node] = true; +} + +// Xcmd XD_ADDPLAYER +static void Got_AddPlayer(UINT8 **p, INT32 playernum) +{ + INT16 node, newplayernum; + boolean splitscreenplayer; + boolean rejoined; + player_t *newplayer; + + if (playernum != serverplayer && !IsPlayerAdmin(playernum)) + { + // protect against hacked/buggy client + CONS_Alert(CONS_WARNING, M_GetText("Illegal add player command received from %s\n"), player_names[playernum]); + if (server) + SendKick(playernum, KICK_MSG_CON_FAIL | KICK_MSG_KEEP_BODY); + return; + } + + node = READUINT8(*p); + newplayernum = READUINT8(*p); + splitscreenplayer = newplayernum & 0x80; + newplayernum &= ~0x80; + + rejoined = playeringame[newplayernum]; + + if (!rejoined) + { + // Clear player before joining, lest some things get set incorrectly + // HACK: don't do this for splitscreen, it relies on preset values + if (!splitscreen && !botingame) + CL_ClearPlayer(newplayernum); + playeringame[newplayernum] = true; + G_AddPlayer(newplayernum); + if (newplayernum+1 > doomcom->numslots) + doomcom->numslots = (INT16)(newplayernum+1); + + if (server && I_GetNodeAddress) + { + const char *address = I_GetNodeAddress(node); + char *port = NULL; + if (address) // MI: fix msvcrt.dll!_mbscat crash? + { + strcpy(playeraddress[newplayernum], address); + port = strchr(playeraddress[newplayernum], ':'); + if (port) + *port = '\0'; + } + } + } + + newplayer = &players[newplayernum]; + + newplayer->jointime = 0; + newplayer->quittime = 0; + + READSTRINGN(*p, player_names[newplayernum], MAXPLAYERNAME); + + // the server is creating my player + if (node == mynode) + { + playernode[newplayernum] = 0; // for information only + if (!splitscreenplayer) + { + consoleplayer = newplayernum; + displayplayer = newplayernum; + secondarydisplayplayer = newplayernum; + DEBFILE("spawning me\n"); + ticcmd_oldangleturn[0] = newplayer->oldrelangleturn; + } + else + { + secondarydisplayplayer = newplayernum; + DEBFILE("spawning my brother\n"); + if (botingame) + newplayer->bot = 1; + ticcmd_oldangleturn[1] = newplayer->oldrelangleturn; + } + P_ForceLocalAngle(newplayer, (angle_t)(newplayer->angleturn << 16)); + D_SendPlayerConfig(); + addedtogame = true; + + if (rejoined) + { + if (newplayer->mo) + { + newplayer->viewheight = 41*newplayer->height/48; + + if (newplayer->mo->eflags & MFE_VERTICALFLIP) + newplayer->viewz = newplayer->mo->z + newplayer->mo->height - newplayer->viewheight; + else + newplayer->viewz = newplayer->mo->z + newplayer->viewheight; + } + + // wake up the status bar + ST_Start(); + // wake up the heads up text + HU_Start(); + + if (camera.chase && !splitscreenplayer) + P_ResetCamera(newplayer, &camera); + if (camera2.chase && splitscreenplayer) + P_ResetCamera(newplayer, &camera2); + } + } + + if (netgame) + { + char joinmsg[256]; + + if (rejoined) + strcpy(joinmsg, M_GetText("\x82*%s has rejoined the game (player %d)")); + else + strcpy(joinmsg, M_GetText("\x82*%s has joined the game (player %d)")); + strcpy(joinmsg, va(joinmsg, player_names[newplayernum], newplayernum)); + + // Merge join notification + IP to avoid clogging console/chat + if (server && cv_showjoinaddress.value && I_GetNodeAddress) + { + const char *address = I_GetNodeAddress(node); + if (address) + strcat(joinmsg, va(" (%s)", address)); + } + + HU_AddChatText(joinmsg, false); + } + + if (server && multiplayer && motd[0] != '\0') + COM_BufAddText(va("sayto %d %s\n", newplayernum, motd)); + + if (!rejoined) + LUAh_PlayerJoin(newplayernum); +} + +static boolean SV_AddWaitingPlayers(const char *name, const char *name2) +{ + INT32 node, n, newplayer = false; + UINT8 buf[2 + MAXPLAYERNAME]; + UINT8 *p; + INT32 newplayernum; + + for (node = 0; node < MAXNETNODES; node++) + { + // splitscreen can allow 2 player in one node + for (; nodewaiting[node] > 0; nodewaiting[node]--) + { + newplayer = true; + + newplayernum = FindRejoinerNum(node); + if (newplayernum == -1) + { + // search for a free playernum + // we can't use playeringame since it is not updated here + for (newplayernum = dedicated ? 1 : 0; newplayernum < MAXPLAYERS; newplayernum++) + { + if (playeringame[newplayernum]) + continue; + for (n = 0; n < MAXNETNODES; n++) + if (nodetoplayer[n] == newplayernum || nodetoplayer2[n] == newplayernum) + break; + if (n == MAXNETNODES) + break; + } + } + + // should never happen since we check the playernum + // before accepting the join + I_Assert(newplayernum < MAXPLAYERS); + + playernode[newplayernum] = (UINT8)node; + + p = buf + 2; + buf[0] = (UINT8)node; + buf[1] = newplayernum; + if (playerpernode[node] < 1) + { + nodetoplayer[node] = newplayernum; + WRITESTRINGN(p, name, MAXPLAYERNAME); + } + else + { + nodetoplayer2[node] = newplayernum; + buf[1] |= 0x80; + WRITESTRINGN(p, name2, MAXPLAYERNAME); + } + playerpernode[node]++; + + SendNetXCmd(XD_ADDPLAYER, &buf, p - buf); + + DEBFILE(va("Server added player %d node %d\n", newplayernum, node)); + } + } + + return newplayer; +} + +void CL_AddSplitscreenPlayer(void) +{ + if (cl_mode == CL_CONNECTED) + CL_SendJoin(); +} + +void CL_RemoveSplitscreenPlayer(void) +{ + if (cl_mode != CL_CONNECTED) + return; + + SendKick(secondarydisplayplayer, KICK_MSG_PLAYER_QUIT); +} + +// is there a game running +boolean Playing(void) +{ + return (server && serverrunning) || (client && cl_mode == CL_CONNECTED); +} + +boolean SV_SpawnServer(void) +{ + if (demoplayback) + G_StopDemo(); // reset engine parameter + if (metalplayback) + G_StopMetalDemo(); + + if (!serverrunning) + { + CONS_Printf(M_GetText("Starting Server....\n")); + serverrunning = true; + SV_ResetServer(); + SV_GenContext(); + if (netgame && I_NetOpenSocket) + { + I_NetOpenSocket(); +#ifdef MASTERSERVER + if (ms_RoomId > 0) + RegisterServer(); +#endif + } + + // non dedicated server just connect to itself + if (!dedicated) + CL_ConnectToServer(); + else doomcom->numslots = 1; + } + + return SV_AddWaitingPlayers(cv_playername.zstring, cv_playername2.zstring); +} + +void SV_StopServer(void) +{ + tic_t i; + + if (gamestate == GS_INTERMISSION) + Y_EndIntermission(); + gamestate = wipegamestate = GS_NULL; + + localtextcmd[0] = 0; + localtextcmd2[0] = 0; + + for (i = firstticstosend; i < firstticstosend + BACKUPTICS; i++) + D_Clearticcmd(i); + + consoleplayer = 0; + cl_mode = CL_SEARCHING; + maketic = gametic+1; + neededtic = maketic; + serverrunning = false; +} + +// called at singleplayer start and stopdemo +void SV_StartSinglePlayerServer(void) +{ + server = true; + netgame = false; + multiplayer = false; + G_SetGametype(GT_COOP); + + // no more tic the game with this settings! + SV_StopServer(); + + if (splitscreen) + multiplayer = true; +} + +static void SV_SendRefuse(INT32 node, const char *reason) +{ + strcpy(netbuffer->u.serverrefuse.reason, reason); + + netbuffer->packettype = PT_SERVERREFUSE; + HSendPacket(node, true, 0, strlen(netbuffer->u.serverrefuse.reason) + 1); + Net_CloseConnection(node); +} + +// used at txtcmds received to check packetsize bound +static size_t TotalTextCmdPerTic(tic_t tic) +{ + INT32 i; + size_t total = 1; // num of textcmds in the tic (ntextcmd byte) + + for (i = 0; i < MAXPLAYERS; i++) + { + UINT8 *textcmd = D_GetExistingTextcmd(tic, i); + if ((!i || playeringame[i]) && textcmd) + total += 2 + textcmd[0]; // "+2" for size and playernum + } + + return total; +} + +/** Called when a PT_CLIENTJOIN packet is received + * + * \param node The packet sender + * + */ +static void HandleConnect(SINT8 node) +{ + char names[MAXSPLITSCREENPLAYERS][MAXPLAYERNAME + 1]; + INT32 rejoinernum; + INT32 i; + + rejoinernum = FindRejoinerNum(node); + + if (bannednode && bannednode[node]) + SV_SendRefuse(node, M_GetText("You have been banned\nfrom the server.")); + else if (netbuffer->u.clientcfg._255 != 255 || + netbuffer->u.clientcfg.packetversion != PACKETVERSION) + SV_SendRefuse(node, "Incompatible packet formats."); + else if (strncmp(netbuffer->u.clientcfg.application, SRB2APPLICATION, + sizeof netbuffer->u.clientcfg.application)) + SV_SendRefuse(node, "Different SRB2 modifications\nare not compatible."); + else if (netbuffer->u.clientcfg.version != VERSION + || 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.")); + 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? + 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.")); + else if (luafiletransfers) + SV_SendRefuse(node, M_GetText("The server is broadcasting a file\nrequested by a Lua script.\nPlease wait a bit and then\ntry rejoining.")); + else if (netgame && joindelay > 2 * (tic_t)cv_joindelay.value * TICRATE) + SV_SendRefuse(node, va(M_GetText("Too many people are connecting.\nPlease wait %d seconds and then\ntry rejoining."), + (joindelay - 2 * cv_joindelay.value * TICRATE) / TICRATE)); + else + { +#ifndef NONET + boolean newnode = false; +#endif + + for (i = 0; i < netbuffer->u.clientcfg.localplayers - playerpernode[node]; i++) + { + strlcpy(names[i], netbuffer->u.clientcfg.names[i], MAXPLAYERNAME + 1); + if (!EnsurePlayerNameIsGood(names[i], rejoinernum)) + { + SV_SendRefuse(node, "Bad player name"); + return; + } + } + + // client authorised to join + nodewaiting[node] = (UINT8)(netbuffer->u.clientcfg.localplayers - playerpernode[node]); + if (!nodeingame[node]) + { + gamestate_t backupstate = gamestate; +#ifndef NONET + newnode = true; +#endif + SV_AddNode(node); + + if (cv_joinnextround.value && gameaction == ga_nothing) + G_SetGamestate(GS_WAITINGPLAYERS); + if (!SV_SendServerConfig(node)) + { + G_SetGamestate(backupstate); + /// \note Shouldn't SV_SendRefuse be called before ResetNode? + ResetNode(node); + SV_SendRefuse(node, M_GetText("Server couldn't send info, please try again")); + /// \todo fix this !!! + return; // restart the while + } + //if (gamestate != GS_LEVEL) // GS_INTERMISSION, etc? + // SV_SendPlayerConfigs(node); // send bare minimum player info + G_SetGamestate(backupstate); + DEBFILE("new node joined\n"); + } +#ifndef NONET + if (nodewaiting[node]) + { + if ((gamestate == GS_LEVEL || gamestate == GS_INTERMISSION) && newnode) + { + SV_SendSaveGame(node, false); // send a complete game state + DEBFILE("send savegame\n"); + } + SV_AddWaitingPlayers(names[0], names[1]); + joindelay += cv_joindelay.value * TICRATE; + player_joining = true; + } +#endif + } +} + +/** Called when a PT_SERVERSHUTDOWN packet is received + * + * \param node The packet sender (should be the server) + * + */ +static void HandleShutdown(SINT8 node) +{ + (void)node; + if (Playing()) + LUAh_GameQuit(); + D_QuitNetGame(); + CL_Reset(); + D_StartTitle(); + M_StartMessage(M_GetText("Server has shutdown\n\nPress Esc\n"), NULL, MM_NOTHING); +} + +/** Called when a PT_NODETIMEOUT packet is received + * + * \param node The packet sender (should be the server) + * + */ +static void HandleTimeout(SINT8 node) +{ + (void)node; + if (Playing()) + LUAh_GameQuit(); + D_QuitNetGame(); + CL_Reset(); + D_StartTitle(); + M_StartMessage(M_GetText("Server Timeout\n\nPress Esc\n"), NULL, MM_NOTHING); +} + +#ifndef NONET +/** Called when a PT_SERVERINFO packet is received + * + * \param node The packet sender + * \note What happens if the packet comes from a client or something like that? + * + */ +static void HandleServerInfo(SINT8 node) +{ + // compute ping in ms + const tic_t ticnow = I_GetTime(); + const tic_t ticthen = (tic_t)LONG(netbuffer->u.serverinfo.time); + const tic_t ticdiff = (ticnow - ticthen)*1000/NEWTICRATE; + netbuffer->u.serverinfo.time = (tic_t)LONG(ticdiff); + netbuffer->u.serverinfo.servername[MAXSERVERNAME-1] = 0; + netbuffer->u.serverinfo.application + [sizeof netbuffer->u.serverinfo.application - 1] = '\0'; + netbuffer->u.serverinfo.gametypename + [sizeof netbuffer->u.serverinfo.gametypename - 1] = '\0'; + + SL_InsertServer(&netbuffer->u.serverinfo, node); +} +#endif + +static void PT_WillResendGamestate(void) +{ + char tmpsave[256]; + + if (server || cl_redownloadinggamestate) + return; + + // Send back a PT_CANRECEIVEGAMESTATE packet to the server + // so they know they can start sending the game state + netbuffer->packettype = PT_CANRECEIVEGAMESTATE; + if (!HSendPacket(servernode, true, 0, 0)) + return; + + CONS_Printf(M_GetText("Reloading game state...\n")); + + sprintf(tmpsave, "%s" PATHSEP TMPSAVENAME, srb2home); + + // Don't get a corrupt savegame error because tmpsave already exists + if (FIL_FileExists(tmpsave) && unlink(tmpsave) == -1) + I_Error("Can't delete %s\n", tmpsave); + + CL_PrepareDownloadSaveGame(tmpsave); + + cl_redownloadinggamestate = true; +} + +static void PT_CanReceiveGamestate(SINT8 node) +{ + if (client || sendingsavegame[node]) + return; + + CONS_Printf(M_GetText("Resending game state to %s...\n"), player_names[nodetoplayer[node]]); + + SV_SendSaveGame(node, true); // Resend a complete game state + resendingsavegame[node] = true; +} + +/** Handles a packet received from a node that isn't in game + * + * \param node The packet sender + * \todo Choose a better name, as the packet can also come from the server apparently? + * \sa HandlePacketFromPlayer + * \sa GetPackets + * + */ +static void HandlePacketFromAwayNode(SINT8 node) +{ + if (node != servernode) + DEBFILE(va("Received packet from unknown host %d\n", node)); + +// macro for packets that should only be sent by the server +// if it is NOT from the server, bail out and close the connection! +#define SERVERONLY \ + if (node != servernode) \ + { \ + Net_CloseConnection(node); \ + break; \ + } + switch (netbuffer->packettype) + { + case PT_ASKINFOVIAMS: +#if 0 + if (server && serverrunning) + { + INT32 clientnode; + if (ms_RoomId < 0) // ignore if we're not actually on the MS right now + { + Net_CloseConnection(node); // and yes, close connection + return; + } + clientnode = I_NetMakeNode(netbuffer->u.msaskinfo.clientaddr); + if (clientnode != -1) + { + SV_SendServerInfo(clientnode, (tic_t)LONG(netbuffer->u.msaskinfo.time)); + SV_SendPlayerInfo(clientnode); // Send extra info + Net_CloseConnection(clientnode); + // Don't close connection to MS... + } + else + Net_CloseConnection(node); // ...unless the IP address is not valid + } + else + Net_CloseConnection(node); // you're not supposed to get it, so ignore it +#else + Net_CloseConnection(node); +#endif + break; + + case PT_ASKINFO: + if (server && serverrunning) + { + SV_SendServerInfo(node, (tic_t)LONG(netbuffer->u.askinfo.time)); + SV_SendPlayerInfo(node); // Send extra info + } + Net_CloseConnection(node); + break; + + case PT_SERVERREFUSE: // Negative response of client join request + if (server && serverrunning) + { // But wait I thought I'm the server? + Net_CloseConnection(node); + break; + } + SERVERONLY + if (cl_mode == CL_WAITJOINRESPONSE) + { + // Save the reason so it can be displayed after quitting the netgame + char *reason = strdup(netbuffer->u.serverrefuse.reason); + if (!reason) + I_Error("Out of memory!\n"); + + D_QuitNetGame(); + CL_Reset(); + D_StartTitle(); + + M_StartMessage(va(M_GetText("Server refuses connection\n\nReason:\n%s"), + reason), NULL, MM_NOTHING); + + free(reason); + + // Will be reset by caller. Signals refusal. + cl_mode = CL_ABORTED; + } + break; + + case PT_SERVERCFG: // Positive response of client join request + { + if (server && serverrunning && node != servernode) + { // but wait I thought I'm the server? + Net_CloseConnection(node); + break; + } + SERVERONLY + /// \note how would this happen? and is it doing the right thing if it does? + if (cl_mode != CL_WAITJOINRESPONSE) + break; + + if (client) + { + maketic = gametic = neededtic = (tic_t)LONG(netbuffer->u.servercfg.gametic); + G_SetGametype(netbuffer->u.servercfg.gametype); + modifiedgame = netbuffer->u.servercfg.modifiedgame; + memcpy(server_context, netbuffer->u.servercfg.server_context, 8); + } + + nodeingame[(UINT8)servernode] = true; + serverplayer = netbuffer->u.servercfg.serverplayer; + doomcom->numslots = SHORT(netbuffer->u.servercfg.totalslotnum); + mynode = netbuffer->u.servercfg.clientnode; + if (serverplayer >= 0) + playernode[(UINT8)serverplayer] = servernode; + + if (netgame) +#ifndef NONET + CONS_Printf(M_GetText("Join accepted, waiting for complete game state...\n")); +#else + CONS_Printf(M_GetText("Join accepted, waiting for next level change...\n")); +#endif + DEBFILE(va("Server accept join gametic=%u mynode=%d\n", gametic, mynode)); + +#ifndef NONET + /// \note Wait. What if a Lua script uses some global custom variables synched with the NetVars hook? + /// Shouldn't them be downloaded even at intermission time? + /// Also, according to HandleConnect, the server will send the savegame even during intermission... + if (netbuffer->u.servercfg.gamestate == GS_LEVEL/* || + netbuffer->u.servercfg.gamestate == GS_INTERMISSION*/) + cl_mode = CL_DOWNLOADSAVEGAME; + else +#endif + cl_mode = CL_CONNECTED; + break; + } + + // Handled in d_netfil.c + case PT_FILEFRAGMENT: + if (server) + { // But wait I thought I'm the server? + Net_CloseConnection(node); + break; + } + SERVERONLY + PT_FileFragment(); + break; + + case PT_FILEACK: + if (server) + PT_FileAck(); + break; + + case PT_FILERECEIVED: + if (server) + PT_FileReceived(); + break; + + case PT_REQUESTFILE: + if (server) + { + if (!cv_downloading.value || !PT_RequestFile(node)) + Net_CloseConnection(node); // close connection if one of the requested files could not be sent, or you disabled downloading anyway + } + else + Net_CloseConnection(node); // nope + break; + + case PT_NODETIMEOUT: + case PT_CLIENTQUIT: + if (server) + Net_CloseConnection(node); + break; + + case PT_CLIENTCMD: + break; // This is not an "unknown packet" + + case PT_SERVERTICS: + // Do not remove my own server (we have just get a out of order packet) + if (node == servernode) + break; + /* FALLTHRU */ + + default: + DEBFILE(va("unknown packet received (%d) from unknown host\n",netbuffer->packettype)); + Net_CloseConnection(node); + break; // Ignore it + + } +#undef SERVERONLY +} + +/** Handles a packet received from a node that is in game + * + * \param node The packet sender + * \todo Choose a better name + * \sa HandlePacketFromAwayNode + * \sa GetPackets + * + */ +static void HandlePacketFromPlayer(SINT8 node) +{ + INT32 netconsole; + tic_t realend, realstart; + UINT8 *pak, *txtpak, numtxtpak; +#ifndef NOMD5 + UINT8 finalmd5[16];/* Well, it's the cool thing to do? */ +#endif + + txtpak = NULL; + + if (dedicated && node == 0) + netconsole = 0; + else + netconsole = nodetoplayer[node]; +#ifdef PARANOIA + if (netconsole >= MAXPLAYERS) + I_Error("bad table nodetoplayer: node %d player %d", doomcom->remotenode, netconsole); +#endif + + switch (netbuffer->packettype) + { +// -------------------------------------------- SERVER RECEIVE ---------- + case PT_CLIENTCMD: + case PT_CLIENT2CMD: + case PT_CLIENTMIS: + case PT_CLIENT2MIS: + case PT_NODEKEEPALIVE: + case PT_NODEKEEPALIVEMIS: + if (client) + break; + + // To save bytes, only the low byte of tic numbers are sent + // Use ExpandTics to figure out what the rest of the bytes are + realstart = ExpandTics(netbuffer->u.clientpak.client_tic, node); + realend = ExpandTics(netbuffer->u.clientpak.resendfrom, node); + + if (netbuffer->packettype == PT_CLIENTMIS || netbuffer->packettype == PT_CLIENT2MIS + || netbuffer->packettype == PT_NODEKEEPALIVEMIS + || supposedtics[node] < realend) + { + supposedtics[node] = realend; + } + // Discard out of order packet + if (nettics[node] > realend) + { + DEBFILE(va("out of order ticcmd discarded nettics = %u\n", nettics[node])); + break; + } + + // Update the nettics + nettics[node] = realend; + + // Don't do anything for packets of type NODEKEEPALIVE? + if (netconsole == -1 || netbuffer->packettype == PT_NODEKEEPALIVE + || netbuffer->packettype == PT_NODEKEEPALIVEMIS) + break; + + // As long as clients send valid ticcmds, the server can keep running, so reset the timeout + /// \todo Use a separate cvar for that kind of timeout? + freezetimeout[node] = I_GetTime() + connectiontimeout; + + // Copy ticcmd + G_MoveTiccmd(&netcmds[maketic%BACKUPTICS][netconsole], &netbuffer->u.clientpak.cmd, 1); + + // Check ticcmd for "speed hacks" + if (netcmds[maketic%BACKUPTICS][netconsole].forwardmove > MAXPLMOVE || netcmds[maketic%BACKUPTICS][netconsole].forwardmove < -MAXPLMOVE + || netcmds[maketic%BACKUPTICS][netconsole].sidemove > MAXPLMOVE || netcmds[maketic%BACKUPTICS][netconsole].sidemove < -MAXPLMOVE) + { + CONS_Alert(CONS_WARNING, M_GetText("Illegal movement value received from node %d\n"), netconsole); + //D_Clearticcmd(k); + + SendKick(netconsole, KICK_MSG_CON_FAIL); + break; + } + + // Splitscreen cmd + if ((netbuffer->packettype == PT_CLIENT2CMD || netbuffer->packettype == PT_CLIENT2MIS) + && nodetoplayer2[node] >= 0) + G_MoveTiccmd(&netcmds[maketic%BACKUPTICS][(UINT8)nodetoplayer2[node]], + &netbuffer->u.client2pak.cmd2, 1); + + // Check player consistancy during the level + if (realstart <= gametic && realstart + BACKUPTICS - 1 > gametic && gamestate == GS_LEVEL + && consistancy[realstart%BACKUPTICS] != SHORT(netbuffer->u.clientpak.consistancy) + && !resendingsavegame[node] && savegameresendcooldown[node] <= I_GetTime() + && !SV_ResendingSavegameToAnyone()) + { + if (cv_resynchattempts.value) + { + // Tell the client we are about to resend them the gamestate + netbuffer->packettype = PT_WILLRESENDGAMESTATE; + HSendPacket(node, true, 0, 0); + + resendingsavegame[node] = true; + + if (cv_blamecfail.value) + CONS_Printf(M_GetText("Synch failure for player %d (%s); expected %hd, got %hd\n"), + netconsole+1, player_names[netconsole], + consistancy[realstart%BACKUPTICS], + SHORT(netbuffer->u.clientpak.consistancy)); + DEBFILE(va("Restoring player %d (synch failure) [%update] %d!=%d\n", + netconsole, realstart, consistancy[realstart%BACKUPTICS], + SHORT(netbuffer->u.clientpak.consistancy))); + break; + } + else + { + SendKick(netconsole, KICK_MSG_CON_FAIL | KICK_MSG_KEEP_BODY); + DEBFILE(va("player %d kicked (synch failure) [%u] %d!=%d\n", + netconsole, realstart, consistancy[realstart%BACKUPTICS], + SHORT(netbuffer->u.clientpak.consistancy))); + break; + } + } + break; + case PT_TEXTCMD2: // splitscreen special + netconsole = nodetoplayer2[node]; + /* FALLTHRU */ + case PT_TEXTCMD: + if (client) + break; + + if (netconsole < 0 || netconsole >= MAXPLAYERS) + Net_UnAcknowledgePacket(node); + else + { + size_t j; + tic_t tic = maketic; + UINT8 *textcmd; + + // ignore if the textcmd has a reported size of zero + // this shouldn't be sent at all + if (!netbuffer->u.textcmd[0]) + { + DEBFILE(va("GetPacket: Textcmd with size 0 detected! (node %u, player %d)\n", + node, netconsole)); + Net_UnAcknowledgePacket(node); + break; + } + + // ignore if the textcmd size var is actually larger than it should be + // BASEPACKETSIZE + 1 (for size) + textcmd[0] should == datalength + if (netbuffer->u.textcmd[0] > (size_t)doomcom->datalength-BASEPACKETSIZE-1) + { + DEBFILE(va("GetPacket: Bad Textcmd packet size! (expected %d, actual %s, node %u, player %d)\n", + netbuffer->u.textcmd[0], sizeu1((size_t)doomcom->datalength-BASEPACKETSIZE-1), + node, netconsole)); + Net_UnAcknowledgePacket(node); + break; + } + + // check if tic that we are making isn't too large else we cannot send it :( + // doomcom->numslots+1 "+1" since doomcom->numslots can change within this time and sent time + j = software_MAXPACKETLENGTH + - (netbuffer->u.textcmd[0]+2+BASESERVERTICSSIZE + + (doomcom->numslots+1)*sizeof(ticcmd_t)); + + // search a tic that have enougth space in the ticcmd + while ((textcmd = D_GetExistingTextcmd(tic, netconsole)), + (TotalTextCmdPerTic(tic) > j || netbuffer->u.textcmd[0] + (textcmd ? textcmd[0] : 0) > MAXTEXTCMD) + && tic < firstticstosend + BACKUPTICS) + tic++; + + if (tic >= firstticstosend + BACKUPTICS) + { + DEBFILE(va("GetPacket: Textcmd too long (max %s, used %s, mak %d, " + "tosend %u, node %u, player %d)\n", sizeu1(j), sizeu2(TotalTextCmdPerTic(maketic)), + maketic, firstticstosend, node, netconsole)); + Net_UnAcknowledgePacket(node); + break; + } + + // Make sure we have a buffer + if (!textcmd) textcmd = D_GetTextcmd(tic, netconsole); + + DEBFILE(va("textcmd put in tic %u at position %d (player %d) ftts %u mk %u\n", + tic, textcmd[0]+1, netconsole, firstticstosend, maketic)); + + M_Memcpy(&textcmd[textcmd[0]+1], netbuffer->u.textcmd+1, netbuffer->u.textcmd[0]); + textcmd[0] += (UINT8)netbuffer->u.textcmd[0]; + } + break; + case PT_LOGIN: + if (client) + break; + +#ifndef NOMD5 + if (doomcom->datalength < 16)/* ignore partial sends */ + break; + + if (!adminpasswordset) + { + CONS_Printf(M_GetText("Password from %s failed (no password set).\n"), player_names[netconsole]); + break; + } + + // Do the final pass to compare with the sent md5 + D_MD5PasswordPass(adminpassmd5, 16, va("PNUM%02d", netconsole), &finalmd5); + + if (!memcmp(netbuffer->u.md5sum, finalmd5, 16)) + { + CONS_Printf(M_GetText("%s passed authentication.\n"), player_names[netconsole]); + COM_BufInsertText(va("promote %d\n", netconsole)); // do this immediately + } + else + CONS_Printf(M_GetText("Password from %s failed.\n"), player_names[netconsole]); +#endif + break; + case PT_NODETIMEOUT: + case PT_CLIENTQUIT: + if (client) + break; + + // nodeingame will be put false in the execution of kick command + // this allow to send some packets to the quitting client to have their ack back + nodewaiting[node] = 0; + if (netconsole != -1 && playeringame[netconsole]) + { + UINT8 kickmsg; + + if (netbuffer->packettype == PT_NODETIMEOUT) + kickmsg = KICK_MSG_TIMEOUT; + else + kickmsg = KICK_MSG_PLAYER_QUIT; + kickmsg |= KICK_MSG_KEEP_BODY; + + SendKick(netconsole, kickmsg); + nodetoplayer[node] = -1; + + if (nodetoplayer2[node] != -1 && nodetoplayer2[node] >= 0 + && playeringame[(UINT8)nodetoplayer2[node]]) + { + SendKick(nodetoplayer2[node], kickmsg); + nodetoplayer2[node] = -1; + } + } + Net_CloseConnection(node); + nodeingame[node] = false; + break; + case PT_CANRECEIVEGAMESTATE: + PT_CanReceiveGamestate(node); + break; + case PT_ASKLUAFILE: + if (server && luafiletransfers && luafiletransfers->nodestatus[node] == LFTNS_ASKED) + AddLuaFileToSendQueue(node, luafiletransfers->realfilename); + break; + case PT_HASLUAFILE: + if (server && luafiletransfers && luafiletransfers->nodestatus[node] == LFTNS_SENDING) + SV_HandleLuaFileSent(node); + break; + case PT_RECEIVEDGAMESTATE: + sendingsavegame[node] = false; + resendingsavegame[node] = false; + savegameresendcooldown[node] = I_GetTime() + 15 * TICRATE; + break; +// -------------------------------------------- CLIENT RECEIVE ---------- + case PT_SERVERTICS: + // Only accept PT_SERVERTICS from the server. + if (node != servernode) + { + CONS_Alert(CONS_WARNING, M_GetText("%s received from non-host %d\n"), "PT_SERVERTICS", node); + if (server) + SendKick(netconsole, KICK_MSG_CON_FAIL | KICK_MSG_KEEP_BODY); + break; + } + + realstart = netbuffer->u.serverpak.starttic; + realend = realstart + netbuffer->u.serverpak.numtics; + + if (!txtpak) + txtpak = (UINT8 *)&netbuffer->u.serverpak.cmds[netbuffer->u.serverpak.numslots + * netbuffer->u.serverpak.numtics]; + + if (realend > gametic + CLIENTBACKUPTICS) + realend = gametic + CLIENTBACKUPTICS; + cl_packetmissed = realstart > neededtic; + + if (realstart <= neededtic && realend > neededtic) + { + tic_t i, j; + pak = (UINT8 *)&netbuffer->u.serverpak.cmds; + + for (i = realstart; i < realend; i++) + { + // clear first + D_Clearticcmd(i); + + // copy the tics + pak = G_ScpyTiccmd(netcmds[i%BACKUPTICS], pak, + netbuffer->u.serverpak.numslots*sizeof (ticcmd_t)); + + // copy the textcmds + numtxtpak = *txtpak++; + for (j = 0; j < numtxtpak; j++) + { + INT32 k = *txtpak++; // playernum + const size_t txtsize = txtpak[0]+1; + + if (i >= gametic) // Don't copy old net commands + M_Memcpy(D_GetTextcmd(i, k), txtpak, txtsize); + txtpak += txtsize; + } + } + + neededtic = realend; + } + else + { + DEBFILE(va("frame not in bound: %u\n", neededtic)); + /*if (realend < neededtic - 2 * TICRATE || neededtic + 2 * TICRATE < realstart) + I_Error("Received an out of order PT_SERVERTICS packet!\n" + "Got tics %d-%d, needed tic %d\n\n" + "Please report this crash on the Master Board,\n" + "IRC or Discord so it can be fixed.\n", (INT32)realstart, (INT32)realend, (INT32)neededtic);*/ + } + break; + case PT_PING: + // Only accept PT_PING from the server. + if (node != servernode) + { + CONS_Alert(CONS_WARNING, M_GetText("%s received from non-host %d\n"), "PT_PING", node); + if (server) + SendKick(netconsole, KICK_MSG_CON_FAIL | KICK_MSG_KEEP_BODY); + break; + } + + //Update client ping table from the server. + if (client) + { + UINT8 i; + for (i = 0; i < MAXPLAYERS; i++) + if (playeringame[i]) + playerpingtable[i] = (tic_t)netbuffer->u.pingtable[i]; + + servermaxping = (tic_t)netbuffer->u.pingtable[MAXPLAYERS]; + } + + break; + case PT_SERVERCFG: + break; + case PT_FILEFRAGMENT: + // Only accept PT_FILEFRAGMENT from the server. + if (node != servernode) + { + CONS_Alert(CONS_WARNING, M_GetText("%s received from non-host %d\n"), "PT_FILEFRAGMENT", node); + if (server) + SendKick(netconsole, KICK_MSG_CON_FAIL | KICK_MSG_KEEP_BODY); + break; + } + if (client) + PT_FileFragment(); + break; + case PT_FILEACK: + if (server) + PT_FileAck(); + break; + case PT_FILERECEIVED: + if (server) + PT_FileReceived(); + break; + case PT_WILLRESENDGAMESTATE: + PT_WillResendGamestate(); + break; + case PT_SENDINGLUAFILE: + if (client) + CL_PrepareDownloadLuaFile(); + break; + default: + DEBFILE(va("UNKNOWN PACKET TYPE RECEIVED %d from host %d\n", + netbuffer->packettype, node)); + } // end switch +} + +/** Handles all received packets, if any + * + * \todo Add details to this description (lol) + * + */ +static void GetPackets(void) +{ + SINT8 node; // The packet sender + + player_joining = false; + + while (HGetPacket()) + { + node = (SINT8)doomcom->remotenode; + + if (netbuffer->packettype == PT_CLIENTJOIN && server) + { + HandleConnect(node); + continue; + } + if (node == servernode && client && cl_mode != CL_SEARCHING) + { + if (netbuffer->packettype == PT_SERVERSHUTDOWN) + { + HandleShutdown(node); + continue; + } + if (netbuffer->packettype == PT_NODETIMEOUT) + { + HandleTimeout(node); + continue; + } + } + +#ifndef NONET + if (netbuffer->packettype == PT_SERVERINFO) + { + HandleServerInfo(node); + continue; + } +#endif + + if (netbuffer->packettype == PT_PLAYERINFO) + continue; // We do nothing with PLAYERINFO, that's for the MS browser. + + // Packet received from someone already playing + if (nodeingame[node]) + HandlePacketFromPlayer(node); + // Packet received from someone not playing + else + HandlePacketFromAwayNode(node); + } +} + +// +// NetUpdate +// Builds ticcmds for console player, +// sends out a packet +// +// no more use random generator, because at very first tic isn't yet synchronized +// Note: It is called consistAncy on purpose. +// +static INT16 Consistancy(void) +{ + INT32 i; + UINT32 ret = 0; +#ifdef MOBJCONSISTANCY + thinker_t *th; + mobj_t *mo; +#endif + + DEBFILE(va("TIC %u ", gametic)); + + for (i = 0; i < MAXPLAYERS; i++) + { + if (!playeringame[i]) + ret ^= 0xCCCC; + else if (!players[i].mo); + else + { + ret += players[i].mo->x; + ret -= players[i].mo->y; + ret += players[i].powers[pw_shield]; + ret *= i+1; + } + } + // I give up + // Coop desynching enemies is painful + if (!G_PlatformGametype()) + ret += P_GetRandSeed(); + +#ifdef MOBJCONSISTANCY + for (th = thlist[THINK_MOBJ].next; th != &thlist[THINK_MOBJ]; th = th->next) + { + if (th->function.acp1 == (actionf_p1)P_RemoveThinkerDelayed) + continue; + + mo = (mobj_t *)th; + + if (mo->flags & (MF_SPECIAL | MF_SOLID | MF_PUSHABLE | MF_BOSS | MF_MISSILE | MF_SPRING | MF_MONITOR | MF_FIRE | MF_ENEMY | MF_PAIN | MF_STICKY)) + { + ret -= mo->type; + ret += mo->x; + ret -= mo->y; + ret += mo->z; + ret -= mo->momx; + ret += mo->momy; + ret -= mo->momz; + ret += mo->angle; + ret -= mo->flags; + ret += mo->flags2; + ret -= mo->eflags; + if (mo->target) + { + ret += mo->target->type; + ret -= mo->target->x; + ret += mo->target->y; + ret -= mo->target->z; + ret += mo->target->momx; + ret -= mo->target->momy; + ret += mo->target->momz; + ret -= mo->target->angle; + ret += mo->target->flags; + ret -= mo->target->flags2; + ret += mo->target->eflags; + ret -= mo->target->state - states; + ret += mo->target->tics; + ret -= mo->target->sprite; + ret += mo->target->frame; + } + else + ret ^= 0x3333; + if (mo->tracer && mo->tracer->type != MT_OVERLAY) + { + ret += mo->tracer->type; + ret -= mo->tracer->x; + ret += mo->tracer->y; + ret -= mo->tracer->z; + ret += mo->tracer->momx; + ret -= mo->tracer->momy; + ret += mo->tracer->momz; + ret -= mo->tracer->angle; + ret += mo->tracer->flags; + ret -= mo->tracer->flags2; + ret += mo->tracer->eflags; + ret -= mo->tracer->state - states; + ret += mo->tracer->tics; + ret -= mo->tracer->sprite; + ret += mo->tracer->frame; + } + else + ret ^= 0xAAAA; + ret -= mo->state - states; + ret += mo->tics; + ret -= mo->sprite; + ret += mo->frame; + } + } +#endif + + DEBFILE(va("Consistancy = %u\n", (ret & 0xFFFF))); + + return (INT16)(ret & 0xFFFF); +} + +// send the client packet to the server +static void CL_SendClientCmd(void) +{ + size_t packetsize = 0; + + netbuffer->packettype = PT_CLIENTCMD; + + if (cl_packetmissed) + netbuffer->packettype++; + netbuffer->u.clientpak.resendfrom = (UINT8)(neededtic & UINT8_MAX); + netbuffer->u.clientpak.client_tic = (UINT8)(gametic & UINT8_MAX); + + if (gamestate == GS_WAITINGPLAYERS) + { + // Send PT_NODEKEEPALIVE packet + netbuffer->packettype += 4; + packetsize = sizeof (clientcmd_pak) - sizeof (ticcmd_t) - sizeof (INT16); + HSendPacket(servernode, false, 0, packetsize); + } + else if (gamestate != GS_NULL && (addedtogame || dedicated)) + { + G_MoveTiccmd(&netbuffer->u.clientpak.cmd, &localcmds, 1); + netbuffer->u.clientpak.consistancy = SHORT(consistancy[gametic%BACKUPTICS]); + + // Send a special packet with 2 cmd for splitscreen + if (splitscreen || botingame) + { + netbuffer->packettype += 2; + G_MoveTiccmd(&netbuffer->u.client2pak.cmd2, &localcmds2, 1); + packetsize = sizeof (client2cmd_pak); + } + else + packetsize = sizeof (clientcmd_pak); + + HSendPacket(servernode, false, 0, packetsize); + } + + if (cl_mode == CL_CONNECTED || dedicated) + { + // Send extra data if needed + if (localtextcmd[0]) + { + netbuffer->packettype = PT_TEXTCMD; + M_Memcpy(netbuffer->u.textcmd,localtextcmd, localtextcmd[0]+1); + // All extra data have been sent + if (HSendPacket(servernode, true, 0, localtextcmd[0]+1)) // Send can fail... + localtextcmd[0] = 0; + } + + // Send extra data if needed for player 2 (splitscreen) + if (localtextcmd2[0]) + { + netbuffer->packettype = PT_TEXTCMD2; + M_Memcpy(netbuffer->u.textcmd, localtextcmd2, localtextcmd2[0]+1); + // All extra data have been sent + if (HSendPacket(servernode, true, 0, localtextcmd2[0]+1)) // Send can fail... + localtextcmd2[0] = 0; + } + } +} + +// send the server packet +// send tic from firstticstosend to maketic-1 +static void SV_SendTics(void) +{ + tic_t realfirsttic, lasttictosend, i; + UINT32 n; + INT32 j; + size_t packsize; + UINT8 *bufpos; + UINT8 *ntextcmd; + + // send to all client but not to me + // for each node create a packet with x tics and send it + // x is computed using supposedtics[n], max packet size and maketic + for (n = 1; n < MAXNETNODES; n++) + if (nodeingame[n]) + { + // assert supposedtics[n]>=nettics[n] + realfirsttic = supposedtics[n]; + lasttictosend = min(maketic, nettics[n] + CLIENTBACKUPTICS); + + if (realfirsttic >= lasttictosend) + { + // well we have sent all tics we will so use extrabandwidth + // to resent packet that are supposed lost (this is necessary since lost + // packet detection work when we have received packet with firsttic > neededtic + // (getpacket servertics case) + DEBFILE(va("Nothing to send node %u mak=%u sup=%u net=%u \n", + n, maketic, supposedtics[n], nettics[n])); + realfirsttic = nettics[n]; + if (realfirsttic >= lasttictosend || (I_GetTime() + n)&3) + // all tic are ok + continue; + DEBFILE(va("Sent %d anyway\n", realfirsttic)); + } + if (realfirsttic < firstticstosend) + realfirsttic = firstticstosend; + + // compute the length of the packet and cut it if too large + packsize = BASESERVERTICSSIZE; + for (i = realfirsttic; i < lasttictosend; i++) + { + packsize += sizeof (ticcmd_t) * doomcom->numslots; + packsize += TotalTextCmdPerTic(i); + + if (packsize > software_MAXPACKETLENGTH) + { + DEBFILE(va("packet too large (%s) at tic %d (should be from %d to %d)\n", + sizeu1(packsize), i, realfirsttic, lasttictosend)); + lasttictosend = i; + + // too bad: too much player have send extradata and there is too + // much data in one tic. + // To avoid it put the data on the next tic. (see getpacket + // textcmd case) but when numplayer changes the computation can be different + if (lasttictosend == realfirsttic) + { + if (packsize > MAXPACKETLENGTH) + I_Error("Too many players: can't send %s data for %d players to node %d\n" + "Well sorry nobody is perfect....\n", + sizeu1(packsize), doomcom->numslots, n); + else + { + lasttictosend++; // send it anyway! + DEBFILE("sending it anyway\n"); + } + } + break; + } + } + + // Send the tics + netbuffer->packettype = PT_SERVERTICS; + netbuffer->u.serverpak.starttic = realfirsttic; + netbuffer->u.serverpak.numtics = (UINT8)(lasttictosend - realfirsttic); + netbuffer->u.serverpak.numslots = (UINT8)SHORT(doomcom->numslots); + bufpos = (UINT8 *)&netbuffer->u.serverpak.cmds; + + for (i = realfirsttic; i < lasttictosend; i++) + { + bufpos = G_DcpyTiccmd(bufpos, netcmds[i%BACKUPTICS], doomcom->numslots * sizeof (ticcmd_t)); + } + + // add textcmds + for (i = realfirsttic; i < lasttictosend; i++) + { + ntextcmd = bufpos++; + *ntextcmd = 0; + for (j = 0; j < MAXPLAYERS; j++) + { + UINT8 *textcmd = D_GetExistingTextcmd(i, j); + INT32 size = textcmd ? textcmd[0] : 0; + + if ((!j || playeringame[j]) && size) + { + (*ntextcmd)++; + WRITEUINT8(bufpos, j); + M_Memcpy(bufpos, textcmd, size + 1); + bufpos += size + 1; + } + } + } + packsize = bufpos - (UINT8 *)&(netbuffer->u); + + HSendPacket(n, false, 0, packsize); + // when tic are too large, only one tic is sent so don't go backward! + if (lasttictosend-doomcom->extratics > realfirsttic) + supposedtics[n] = lasttictosend-doomcom->extratics; + else + supposedtics[n] = lasttictosend; + if (supposedtics[n] < nettics[n]) supposedtics[n] = nettics[n]; + } + // node 0 is me! + supposedtics[0] = maketic; +} + +// +// TryRunTics +// +static void Local_Maketic(INT32 realtics) +{ + I_OsPolling(); // I_Getevent + D_ProcessEvents(); // menu responder, cons responder, + // game responder calls HU_Responder, AM_Responder, + // and G_MapEventsToControls + if (!dedicated) rendergametic = gametic; + // translate inputs (keyboard/mouse/joystick) into game controls + G_BuildTiccmd(&localcmds, realtics, 1); + if (splitscreen || botingame) + G_BuildTiccmd(&localcmds2, realtics, 2); + + localcmds.angleturn |= TICCMD_RECEIVED; + localcmds2.angleturn |= TICCMD_RECEIVED; +} + +// create missed tic +static void SV_Maketic(void) +{ + INT32 i; + + for (i = 0; i < MAXPLAYERS; i++) + { + if (!playeringame[i]) + continue; + + // We didn't receive this tic + if ((netcmds[maketic % BACKUPTICS][i].angleturn & TICCMD_RECEIVED) == 0) + { + ticcmd_t * ticcmd = &netcmds[(maketic ) % BACKUPTICS][i]; + ticcmd_t *prevticcmd = &netcmds[(maketic - 1) % BACKUPTICS][i]; + + if (players[i].quittime) + { + // Copy the angle/aiming from the previous tic + // and empty the other inputs + memset(ticcmd, 0, sizeof(netcmds[0][0])); + ticcmd->angleturn = prevticcmd->angleturn | TICCMD_RECEIVED; + ticcmd->aiming = prevticcmd->aiming; + } + else + { + DEBFILE(va("MISS tic%4d for player %d\n", maketic, i)); + // Copy the input from the previous tic + *ticcmd = *prevticcmd; + ticcmd->angleturn &= ~TICCMD_RECEIVED; + } + } + } + + // all tic are now proceed make the next + maketic++; +} + +void TryRunTics(tic_t realtics) +{ + // the machine has lagged but it is not so bad + if (realtics > TICRATE/7) // FIXME: consistency failure!! + { + if (server) + realtics = 1; + else + realtics = TICRATE/7; + } + + if (singletics) + realtics = 1; + + if (realtics >= 1) + { + COM_BufTicker(); + if (mapchangepending) + D_MapChange(-1, 0, ultimatemode, false, 2, false, fromlevelselect); // finish the map change + } + + NetUpdate(); + + if (demoplayback) + { + neededtic = gametic + (realtics * cv_playbackspeed.value); + // start a game after a demo + maketic += realtics; + firstticstosend = maketic; + tictoclear = firstticstosend; + } + + GetPackets(); + +#ifdef DEBUGFILE + if (debugfile && (realtics || neededtic > gametic)) + { + //SoM: 3/30/2000: Need long INT32 in the format string for args 4 & 5. + //Shut up stupid warning! + fprintf(debugfile, "------------ Tryruntic: REAL:%d NEED:%d GAME:%d LOAD: %d\n", + realtics, neededtic, gametic, debugload); + debugload = 100000; + } +#endif + + if (player_joining) + return; + + if (neededtic > gametic) + { + if (advancedemo) + { + if (timedemo_quit) + COM_ImmedExecute("quit"); + else + D_StartTitle(); + } + else + // run the count * tics + while (neededtic > gametic) + { + DEBFILE(va("============ Running tic %d (local %d)\n", gametic, localgametic)); + + ps_tictime = I_GetTimeMicros(); + + G_Ticker((gametic % NEWTICRATERATIO) == 0); + ExtraDataTicker(); + gametic++; + consistancy[gametic%BACKUPTICS] = Consistancy(); + + ps_tictime = I_GetTimeMicros() - ps_tictime; + + // Leave a certain amount of tics present in the net buffer as long as we've ran at least one tic this frame. + if (client && gamestate == GS_LEVEL && leveltime > 3 && neededtic <= gametic + cv_netticbuffer.value) + break; + } + } +} + +/* +Ping Update except better: +We call this once per second and check for people's pings. If their ping happens to be too high, we increment some timer and kick them out. +If they're not lagging, decrement the timer by 1. Of course, reset all of this if they leave. +*/ + +static INT32 pingtimeout[MAXPLAYERS]; + +static inline void PingUpdate(void) +{ + INT32 i; + boolean laggers[MAXPLAYERS]; + UINT8 numlaggers = 0; + memset(laggers, 0, sizeof(boolean) * MAXPLAYERS); + + netbuffer->packettype = PT_PING; + + //check for ping limit breakage. + if (cv_maxping.value) + { + for (i = 1; i < MAXPLAYERS; i++) + { + if (playeringame[i] && !players[i].quittime + && (realpingtable[i] / pingmeasurecount > (unsigned)cv_maxping.value)) + { + if (players[i].jointime > 30 * TICRATE) + laggers[i] = true; + numlaggers++; + } + else + pingtimeout[i] = 0; + } + + //kick lagging players... unless everyone but the server's ping sucks. + //in that case, it is probably the server's fault. + if (numlaggers < D_NumPlayers() - 1) + { + for (i = 1; i < MAXPLAYERS; i++) + { + if (playeringame[i] && laggers[i]) + { + pingtimeout[i]++; + // ok your net has been bad for too long, you deserve to die. + if (pingtimeout[i] > cv_pingtimeout.value) + { + pingtimeout[i] = 0; + SendKick(i, KICK_MSG_PING_HIGH | KICK_MSG_KEEP_BODY); + } + } + /* + you aren't lagging, + but you aren't free yet. + In case you'll keep spiking, + we just make the timer go back down. (Very unstable net must still get kicked). + */ + else + pingtimeout[i] = (pingtimeout[i] == 0 ? 0 : pingtimeout[i]-1); + } + } + } + + //make the ping packet and clear server data for next one + for (i = 0; i < MAXPLAYERS; i++) + { + netbuffer->u.pingtable[i] = realpingtable[i] / pingmeasurecount; + //server takes a snapshot of the real ping for display. + //otherwise, pings fluctuate a lot and would be odd to look at. + playerpingtable[i] = realpingtable[i] / pingmeasurecount; + realpingtable[i] = 0; //Reset each as we go. + } + + // send the server's maxping as last element of our ping table. This is useful to let us know when we're about to get kicked. + netbuffer->u.pingtable[MAXPLAYERS] = cv_maxping.value; + + //send out our ping packets + for (i = 0; i < MAXNETNODES; i++) + if (nodeingame[i]) + HSendPacket(i, true, 0, sizeof(INT32) * (MAXPLAYERS+1)); + + pingmeasurecount = 1; //Reset count +} + +void NetUpdate(void) +{ + static tic_t gametime = 0; + static tic_t resptime = 0; + tic_t nowtime; + INT32 i; + INT32 realtics; + + nowtime = I_GetTime(); + realtics = nowtime - gametime; + + if (realtics <= 0) // nothing new to update + return; + if (realtics > 5) + { + if (server) + realtics = 1; + else + realtics = 5; + } + + gametime = nowtime; + + if (server) + { + if (netgame && !(gametime % 35)) // update once per second. + PingUpdate(); + // update node latency values so we can take an average later. + for (i = 0; i < MAXPLAYERS; i++) + if (playeringame[i] && playernode[i] != UINT8_MAX) + realpingtable[i] += G_TicsToMilliseconds(GetLag(playernode[i])); + pingmeasurecount++; + } + + if (client) + maketic = neededtic; + + Local_Maketic(realtics); // make local tic, and call menu? + + if (server) + CL_SendClientCmd(); // send it + + GetPackets(); // get packet from client or from server + + // client send the command after a receive of the server + // the server send before because in single player is beter + +#ifdef MASTERSERVER + MasterClient_Ticker(); // Acking the Master Server +#endif + + if (client) + { + // If the client just finished redownloading the game state, load it + if (cl_redownloadinggamestate && fileneeded[0].status == FS_FOUND) + CL_ReloadReceivedSavegame(); + + CL_SendClientCmd(); // Send tic cmd + hu_redownloadinggamestate = cl_redownloadinggamestate; + } + else + { + if (!demoplayback) + { + INT32 counts; + + hu_redownloadinggamestate = false; + + firstticstosend = gametic; + for (i = 0; i < MAXNETNODES; i++) + if (nodeingame[i] && nettics[i] < firstticstosend) + { + firstticstosend = nettics[i]; + + if (maketic + 1 >= nettics[i] + BACKUPTICS) + Net_ConnectionTimeout(i); + } + + // Don't erase tics not acknowledged + counts = realtics; + + if (maketic + counts >= firstticstosend + BACKUPTICS) + counts = firstticstosend+BACKUPTICS-maketic-1; + + for (i = 0; i < counts; i++) + SV_Maketic(); // Create missed tics and increment maketic + + for (; tictoclear < firstticstosend; tictoclear++) // Clear only when acknowledged + D_Clearticcmd(tictoclear); // Clear the maketic the new tic + + SV_SendTics(); + + neededtic = maketic; // The server is a client too + } + } + + Net_AckTicker(); + + // Handle timeouts to prevent definitive freezes from happenning + if (server) + { + for (i = 1; i < MAXNETNODES; i++) + if (nodeingame[i] && freezetimeout[i] < I_GetTime()) + Net_ConnectionTimeout(i); + + // In case the cvar value was lowered + if (joindelay) + joindelay = min(joindelay - 1, 3 * (tic_t)cv_joindelay.value * TICRATE); + } + + nowtime /= NEWTICRATERATIO; + if (nowtime > resptime) + { + resptime = nowtime; +#ifdef HAVE_THREADS + I_lock_mutex(&m_menu_mutex); +#endif + M_Ticker(); +#ifdef HAVE_THREADS + I_unlock_mutex(m_menu_mutex); +#endif + CON_Ticker(); + } + + FileSendTicker(); +} + +/** Returns the number of players playing. + * \return Number of players. Can be zero if we're running a ::dedicated + * server. + * \author Graue + */ +INT32 D_NumPlayers(void) +{ + INT32 num = 0, ix; + for (ix = 0; ix < MAXPLAYERS; ix++) + if (playeringame[ix]) + num++; + return num; +} + +tic_t GetLag(INT32 node) +{ + return gametic - nettics[node]; +} + +void D_MD5PasswordPass(const UINT8 *buffer, size_t len, const char *salt, void *dest) +{ +#ifdef NOMD5 + (void)buffer; + (void)len; + (void)salt; + memset(dest, 0, 16); +#else + char tmpbuf[256]; + const size_t sl = strlen(salt); + + if (len > 256-sl) + len = 256-sl; + + memcpy(tmpbuf, buffer, len); + memmove(&tmpbuf[len], salt, sl); + //strcpy(&tmpbuf[len], salt); + len += strlen(salt); + if (len < 256) + memset(&tmpbuf[len],0,256-len); + + // Yes, we intentionally md5 the ENTIRE buffer regardless of size... + md5_buffer(tmpbuf, 256, dest); +#endif +} From edc312066dbb7b3dc7c222c490d76f836c70eb6e Mon Sep 17 00:00:00 2001 From: Shane Ellis Date: Fri, 15 Jan 2021 22:01:51 +0000 Subject: [PATCH 010/126] =?UTF-8?q?Revert=20"CL=5FRemovePlayer()=20-=20All?= =?UTF-8?q?ow=20for=20removal=20of=20non-consoleplayer=20and=E2=80=A6"?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This reverts commit f166af4d8b7805e1ce5688cbef386f6ddc6e3ec1 --- src/r_skins.c | 5810 +++++++------------------------------------------ 1 file changed, 760 insertions(+), 5050 deletions(-) diff --git a/src/r_skins.c b/src/r_skins.c index 88b4d9387..155a64700 100644 --- a/src/r_skins.c +++ b/src/r_skins.c @@ -1,5 +1,6 @@ // 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. // @@ -7,5117 +8,826 @@ // terms of the GNU General Public License, version 2. // See the 'LICENSE' file for more details. //----------------------------------------------------------------------------- -/// \file d_clisrv.c -/// \brief SRB2 Network game communication and protocol, all OS independent parts. +/// \file r_skins.c +/// \brief Loading skins -#include -#ifdef __GNUC__ -#include //for unlink -#endif - -#include "i_net.h" -#include "i_system.h" -#include "i_video.h" -#include "d_net.h" -#include "d_main.h" -#include "g_game.h" -#include "st_stuff.h" -#include "hu_stuff.h" -#include "keys.h" -#include "g_input.h" // JOY1 -#include "m_menu.h" +#include "doomdef.h" #include "console.h" -#include "d_netfil.h" -#include "byteptr.h" -#include "p_saveg.h" -#include "z_zone.h" -#include "p_local.h" -#include "m_misc.h" -#include "am_map.h" -#include "m_random.h" -#include "mserv.h" -#include "y_inter.h" +#include "g_game.h" #include "r_local.h" -#include "m_argv.h" -#include "p_setup.h" -#include "lzf.h" -#include "lua_script.h" -#include "lua_hook.h" -#include "md5.h" -#include "m_perfstats.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 -#ifndef NONET -// cl loading screen -#include "v_video.h" -#include "f_finale.h" +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 // -// NETWORKING +// 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. // -// gametic is the tic about to (or currently being) run -// Server: -// maketic is the tic that hasn't had control made for it yet -// nettics is the tic for each node -// firstticstosend is the lowest value of nettics -// Client: -// neededtic is the tic needed by the client to run the game -// firstticstosend is used to optimize a condition -// Normally maketic >= gametic > 0 -#define PREDICTIONQUEUE BACKUPTICS -#define PREDICTIONMASK (PREDICTIONQUEUE-1) -#define MAX_REASONLENGTH 30 - -boolean server = true; // true or false but !server == client -#define client (!server) -boolean nodownload = false; -boolean serverrunning = false; -INT32 serverplayer = 0; -char motd[254], server_context[8]; // Message of the Day, Unique Context (even without Mumble support) - -// Server specific vars -UINT8 playernode[MAXPLAYERS]; -char playeraddress[MAXPLAYERS][64]; - -// Minimum timeout for sending the savegame -// The actual timeout will be longer depending on the savegame length -tic_t jointimeout = (10*TICRATE); -static boolean sendingsavegame[MAXNETNODES]; // Are we sending the savegame? -static boolean resendingsavegame[MAXNETNODES]; // Are we resending the savegame? -static tic_t savegameresendcooldown[MAXNETNODES]; // How long before we can resend again? -static tic_t freezetimeout[MAXNETNODES]; // Until when can this node freeze the server before getting a timeout? - -// Incremented by cv_joindelay when a client joins, decremented each tic. -// If higher than cv_joindelay * 2 (3 joins in a short timespan), joins are temporarily disabled. -static tic_t joindelay = 0; - -UINT16 pingmeasurecount = 1; -UINT32 realpingtable[MAXPLAYERS]; //the base table of ping where an average will be sent to everyone. -UINT32 playerpingtable[MAXPLAYERS]; //table of player latency values. -SINT8 nodetoplayer[MAXNETNODES]; -SINT8 nodetoplayer2[MAXNETNODES]; // say the numplayer for this node if any (splitscreen) -UINT8 playerpernode[MAXNETNODES]; // used specialy for scplitscreen -boolean nodeingame[MAXNETNODES]; // set false as nodes leave game -tic_t servermaxping = 800; // server's max ping. Defaults to 800 -static tic_t nettics[MAXNETNODES]; // what tic the client have received -static tic_t supposedtics[MAXNETNODES]; // nettics prevision for smaller packet -static UINT8 nodewaiting[MAXNETNODES]; -static tic_t firstticstosend; // min of the nettics -static tic_t tictoclear = 0; // optimize d_clearticcmd -static tic_t maketic; - -static INT16 consistancy[BACKUPTICS]; - -static UINT8 player_joining = false; -UINT8 hu_redownloadinggamestate = 0; - -UINT8 adminpassmd5[16]; -boolean adminpasswordset = false; - -// Client specific -static ticcmd_t localcmds; -static ticcmd_t localcmds2; -static boolean cl_packetmissed; -// here it is for the secondary local player (splitscreen) -static UINT8 mynode; // my address pointofview server -static boolean cl_redownloadinggamestate = false; - -static UINT8 localtextcmd[MAXTEXTCMD]; -static UINT8 localtextcmd2[MAXTEXTCMD]; // splitscreen -static tic_t neededtic; -SINT8 servernode = 0; // the number of the server node -/// \brief do we accept new players? -/// \todo WORK! -boolean acceptnewnode = true; - -// engine - -// Must be a power of two -#define TEXTCMD_HASH_SIZE 4 - -typedef struct textcmdplayer_s +UINT8 P_GetSkinSprite2(skin_t *skin, UINT8 spr2, player_t *player) { - INT32 playernum; - UINT8 cmd[MAXTEXTCMD]; - struct textcmdplayer_s *next; -} textcmdplayer_t; + UINT8 super = 0, i = 0; -typedef struct textcmdtic_s -{ - tic_t tic; - textcmdplayer_t *playercmds[TEXTCMD_HASH_SIZE]; - struct textcmdtic_s *next; -} textcmdtic_t; - -ticcmd_t netcmds[BACKUPTICS][MAXPLAYERS]; -static textcmdtic_t *textcmds[TEXTCMD_HASH_SIZE] = {NULL}; - - -consvar_t cv_showjoinaddress = CVAR_INIT ("showjoinaddress", "Off", CV_SAVE|CV_NETVAR, CV_OnOff, NULL); - -static CV_PossibleValue_t playbackspeed_cons_t[] = {{1, "MIN"}, {10, "MAX"}, {0, NULL}}; -consvar_t cv_playbackspeed = CVAR_INIT ("playbackspeed", "1", 0, playbackspeed_cons_t, NULL); - -static inline void *G_DcpyTiccmd(void* dest, const ticcmd_t* src, const size_t n) -{ - const size_t d = n / sizeof(ticcmd_t); - const size_t r = n % sizeof(ticcmd_t); - UINT8 *ret = dest; - - if (r) - M_Memcpy(dest, src, n); - else if (d) - G_MoveTiccmd(dest, src, d); - return ret+n; -} - -static inline void *G_ScpyTiccmd(ticcmd_t* dest, void* src, const size_t n) -{ - const size_t d = n / sizeof(ticcmd_t); - const size_t r = n % sizeof(ticcmd_t); - UINT8 *ret = src; - - if (r) - M_Memcpy(dest, src, n); - else if (d) - G_MoveTiccmd(dest, src, d); - return ret+n; -} - - - -// Some software don't support largest packet -// (original sersetup, not exactely, but the probability of sending a packet -// of 512 bytes is like 0.1) -UINT16 software_MAXPACKETLENGTH; - -/** Guesses the full value of a tic from its lowest byte, for a specific node - * - * \param low The lowest byte of the tic value - * \param node The node to deduce the tic for - * \return The full tic value - * - */ -tic_t ExpandTics(INT32 low, INT32 node) -{ - INT32 delta; - - delta = low - (nettics[node] & UINT8_MAX); - - if (delta >= -64 && delta <= 64) - return (nettics[node] & ~UINT8_MAX) + low; - else if (delta > 64) - return (nettics[node] & ~UINT8_MAX) - 256 + low; - else //if (delta < -64) - return (nettics[node] & ~UINT8_MAX) + 256 + low; -} - -// ----------------------------------------------------------------- -// Some extra data function for handle textcmd buffer -// ----------------------------------------------------------------- - -static void (*listnetxcmd[MAXNETXCMD])(UINT8 **p, INT32 playernum); - -void RegisterNetXCmd(netxcmd_t id, void (*cmd_f)(UINT8 **p, INT32 playernum)) -{ -#ifdef PARANOIA - if (id >= MAXNETXCMD) - I_Error("Command id %d too big", id); - if (listnetxcmd[id] != 0) - I_Error("Command id %d already used", id); -#endif - listnetxcmd[id] = cmd_f; -} - -void SendNetXCmd(netxcmd_t id, const void *param, size_t nparam) -{ - if (localtextcmd[0]+2+nparam > MAXTEXTCMD) - { - // for future reference: if (cv_debug) != debug disabled. - CONS_Alert(CONS_ERROR, M_GetText("NetXCmd buffer full, cannot add netcmd %d! (size: %d, needed: %s)\n"), id, localtextcmd[0], sizeu1(nparam)); - return; - } - localtextcmd[0]++; - localtextcmd[localtextcmd[0]] = (UINT8)id; - if (param && nparam) - { - M_Memcpy(&localtextcmd[localtextcmd[0]+1], param, nparam); - localtextcmd[0] = (UINT8)(localtextcmd[0] + (UINT8)nparam); - } -} - -// splitscreen player -void SendNetXCmd2(netxcmd_t id, const void *param, size_t nparam) -{ - if (localtextcmd2[0]+2+nparam > MAXTEXTCMD) - { - I_Error("No more place in the buffer for netcmd %d\n",id); - return; - } - localtextcmd2[0]++; - localtextcmd2[localtextcmd2[0]] = (UINT8)id; - if (param && nparam) - { - M_Memcpy(&localtextcmd2[localtextcmd2[0]+1], param, nparam); - localtextcmd2[0] = (UINT8)(localtextcmd2[0] + (UINT8)nparam); - } -} - -UINT8 GetFreeXCmdSize(void) -{ - // -1 for the size and another -1 for the ID. - return (UINT8)(localtextcmd[0] - 2); -} - -// Frees all textcmd memory for the specified tic -static void D_FreeTextcmd(tic_t tic) -{ - textcmdtic_t **tctprev = &textcmds[tic & (TEXTCMD_HASH_SIZE - 1)]; - textcmdtic_t *textcmdtic = *tctprev; - - while (textcmdtic && textcmdtic->tic != tic) - { - tctprev = &textcmdtic->next; - textcmdtic = textcmdtic->next; - } - - if (textcmdtic) - { - INT32 i; - - // Remove this tic from the list. - *tctprev = textcmdtic->next; - - // Free all players. - for (i = 0; i < TEXTCMD_HASH_SIZE; i++) - { - textcmdplayer_t *textcmdplayer = textcmdtic->playercmds[i]; - - while (textcmdplayer) - { - textcmdplayer_t *tcpnext = textcmdplayer->next; - Z_Free(textcmdplayer); - textcmdplayer = tcpnext; - } - } - - // Free this tic's own memory. - Z_Free(textcmdtic); - } -} - -// Gets the buffer for the specified ticcmd, or NULL if there isn't one -static UINT8* D_GetExistingTextcmd(tic_t tic, INT32 playernum) -{ - textcmdtic_t *textcmdtic = textcmds[tic & (TEXTCMD_HASH_SIZE - 1)]; - while (textcmdtic && textcmdtic->tic != tic) textcmdtic = textcmdtic->next; - - // Do we have an entry for the tic? If so, look for player. - if (textcmdtic) - { - textcmdplayer_t *textcmdplayer = textcmdtic->playercmds[playernum & (TEXTCMD_HASH_SIZE - 1)]; - while (textcmdplayer && textcmdplayer->playernum != playernum) textcmdplayer = textcmdplayer->next; - - if (textcmdplayer) return textcmdplayer->cmd; - } - - return NULL; -} - -// Gets the buffer for the specified ticcmd, creating one if necessary -static UINT8* D_GetTextcmd(tic_t tic, INT32 playernum) -{ - textcmdtic_t *textcmdtic = textcmds[tic & (TEXTCMD_HASH_SIZE - 1)]; - textcmdtic_t **tctprev = &textcmds[tic & (TEXTCMD_HASH_SIZE - 1)]; - textcmdplayer_t *textcmdplayer, **tcpprev; - - // Look for the tic. - while (textcmdtic && textcmdtic->tic != tic) - { - tctprev = &textcmdtic->next; - textcmdtic = textcmdtic->next; - } - - // If we don't have an entry for the tic, make it. - if (!textcmdtic) - { - textcmdtic = *tctprev = Z_Calloc(sizeof (textcmdtic_t), PU_STATIC, NULL); - textcmdtic->tic = tic; - } - - tcpprev = &textcmdtic->playercmds[playernum & (TEXTCMD_HASH_SIZE - 1)]; - textcmdplayer = *tcpprev; - - // Look for the player. - while (textcmdplayer && textcmdplayer->playernum != playernum) - { - tcpprev = &textcmdplayer->next; - textcmdplayer = textcmdplayer->next; - } - - // If we don't have an entry for the player, make it. - if (!textcmdplayer) - { - textcmdplayer = *tcpprev = Z_Calloc(sizeof (textcmdplayer_t), PU_STATIC, NULL); - textcmdplayer->playernum = playernum; - } - - return textcmdplayer->cmd; -} - -static void ExtraDataTicker(void) -{ - INT32 i; - - for (i = 0; i < MAXPLAYERS; i++) - if (playeringame[i] || i == 0) - { - UINT8 *bufferstart = D_GetExistingTextcmd(gametic, i); - - if (bufferstart) - { - UINT8 *curpos = bufferstart; - UINT8 *bufferend = &curpos[curpos[0]+1]; - - curpos++; - while (curpos < bufferend) - { - if (*curpos < MAXNETXCMD && listnetxcmd[*curpos]) - { - const UINT8 id = *curpos; - curpos++; - DEBFILE(va("executing x_cmd %s ply %u ", netxcmdnames[id - 1], i)); - (listnetxcmd[id])(&curpos, i); - DEBFILE("done\n"); - } - else - { - if (server) - { - SendKick(i, KICK_MSG_CON_FAIL | KICK_MSG_KEEP_BODY); - DEBFILE(va("player %d kicked [gametic=%u] reason as follows:\n", i, gametic)); - } - CONS_Alert(CONS_WARNING, M_GetText("Got unknown net command [%s]=%d (max %d)\n"), sizeu1(curpos - bufferstart), *curpos, bufferstart[0]); - break; - } - } - } - } - - // If you are a client, you can safely forget the net commands for this tic - // If you are the server, you need to remember them until every client has been acknowledged, - // because if you need to resend a PT_SERVERTICS packet, you will need to put the commands in it - if (client) - D_FreeTextcmd(gametic); -} - -static void D_Clearticcmd(tic_t tic) -{ - INT32 i; - - D_FreeTextcmd(tic); - - for (i = 0; i < MAXPLAYERS; i++) - netcmds[tic%BACKUPTICS][i].angleturn = 0; - - DEBFILE(va("clear tic %5u (%2u)\n", tic, tic%BACKUPTICS)); -} - -void D_ResetTiccmds(void) -{ - INT32 i; - - memset(&localcmds, 0, sizeof(ticcmd_t)); - memset(&localcmds2, 0, sizeof(ticcmd_t)); - - // Reset the net command list - for (i = 0; i < TEXTCMD_HASH_SIZE; i++) - while (textcmds[i]) - D_Clearticcmd(textcmds[i]->tic); -} - -void SendKick(UINT8 playernum, UINT8 msg) -{ - UINT8 buf[2]; - - if (!(server && cv_rejointimeout.value)) - msg &= ~KICK_MSG_KEEP_BODY; - - buf[0] = playernum; - buf[1] = msg; - SendNetXCmd(XD_KICK, &buf, 2); -} - -// ----------------------------------------------------------------- -// end of extra data function -// ----------------------------------------------------------------- - -// ----------------------------------------------------------------- -// extra data function for lmps -// ----------------------------------------------------------------- - -// if extradatabit is set, after the ziped tic you find this: -// -// type | description -// ---------+-------------- -// byte | size of the extradata -// byte | the extradata (xd) bits: see XD_... -// with this byte you know what parameter folow -// if (xd & XDNAMEANDCOLOR) -// byte | color -// char[MAXPLAYERNAME] | name of the player -// endif -// if (xd & XD_WEAPON_PREF) -// byte | original weapon switch: boolean, true if use the old -// | weapon switch methode -// char[NUMWEAPONS] | the weapon switch priority -// byte | autoaim: true if use the old autoaim system -// endif -/*boolean AddLmpExtradata(UINT8 **demo_point, INT32 playernum) -{ - UINT8 *textcmd = D_GetExistingTextcmd(gametic, playernum); - - if (!textcmd) - return false; - - M_Memcpy(*demo_point, textcmd, textcmd[0]+1); - *demo_point += textcmd[0]+1; - return true; -} - -void ReadLmpExtraData(UINT8 **demo_pointer, INT32 playernum) -{ - UINT8 nextra; - UINT8 *textcmd; - - if (!demo_pointer) - return; - - textcmd = D_GetTextcmd(gametic, playernum); - nextra = **demo_pointer; - M_Memcpy(textcmd, *demo_pointer, nextra + 1); - // increment demo pointer - *demo_pointer += nextra + 1; -}*/ - -// ----------------------------------------------------------------- -// end extra data function for lmps -// ----------------------------------------------------------------- - -static INT16 Consistancy(void); - -typedef enum -{ - CL_SEARCHING, - CL_DOWNLOADFILES, - CL_ASKJOIN, - CL_WAITJOINRESPONSE, - CL_DOWNLOADSAVEGAME, - CL_CONNECTED, - CL_ABORTED -} cl_mode_t; - -static void GetPackets(void); - -static cl_mode_t cl_mode = CL_SEARCHING; - -#ifndef NONET -#define SNAKE_SPEED 5 - -#define SNAKE_NUM_BLOCKS_X 20 -#define SNAKE_NUM_BLOCKS_Y 10 -#define SNAKE_BLOCK_SIZE 12 -#define SNAKE_BORDER_SIZE 12 - -#define SNAKE_MAP_WIDTH (SNAKE_NUM_BLOCKS_X * SNAKE_BLOCK_SIZE) -#define SNAKE_MAP_HEIGHT (SNAKE_NUM_BLOCKS_Y * SNAKE_BLOCK_SIZE) - -#define SNAKE_LEFT_X ((BASEVIDWIDTH - SNAKE_MAP_WIDTH) / 2 - SNAKE_BORDER_SIZE) -#define SNAKE_RIGHT_X (SNAKE_LEFT_X + SNAKE_MAP_WIDTH + SNAKE_BORDER_SIZE * 2 - 1) -#define SNAKE_BOTTOM_Y (BASEVIDHEIGHT - 48) -#define SNAKE_TOP_Y (SNAKE_BOTTOM_Y - SNAKE_MAP_HEIGHT - SNAKE_BORDER_SIZE * 2 + 1) - -enum snake_bonustype_s { - SNAKE_BONUS_NONE = 0, - SNAKE_BONUS_SLOW, - SNAKE_BONUS_FAST, - SNAKE_BONUS_GHOST, - SNAKE_BONUS_NUKE, - SNAKE_BONUS_SCISSORS, - SNAKE_BONUS_REVERSE, - SNAKE_BONUS_EGGMAN, - SNAKE_NUM_BONUSES, -}; - -static const char *snake_bonuspatches[] = { - NULL, - "DL_SLOW", - "TVSSC0", - "TVIVC0", - "TVARC0", - "DL_SCISSORS", - "TVRCC0", - "TVEGC0", -}; - -static const char *snake_backgrounds[] = { - "RVPUMICF", - "FRSTRCKF", - "TAR", - "MMFLRB4", - "RVDARKF1", - "RVZWALF1", - "RVZWALF4", - "RVZWALF5", - "RVZGRS02", - "RVZGRS04", -}; - -typedef struct snake_s -{ - boolean paused; - boolean pausepressed; - tic_t time; - tic_t nextupdate; - boolean gameover; - UINT8 background; - - UINT16 snakelength; - enum snake_bonustype_s snakebonus; - tic_t snakebonustime; - UINT8 snakex[SNAKE_NUM_BLOCKS_X * SNAKE_NUM_BLOCKS_Y]; - UINT8 snakey[SNAKE_NUM_BLOCKS_X * SNAKE_NUM_BLOCKS_Y]; - UINT8 snakedir[SNAKE_NUM_BLOCKS_X * SNAKE_NUM_BLOCKS_Y]; - - UINT8 applex; - UINT8 appley; - - enum snake_bonustype_s bonustype; - UINT8 bonusx; - UINT8 bonusy; -} snake_t; - -static snake_t *snake = NULL; - -static void Snake_Initialise(void) -{ - if (!snake) - snake = malloc(sizeof(snake_t)); - - snake->paused = false; - snake->pausepressed = false; - snake->time = 0; - snake->nextupdate = SNAKE_SPEED; - snake->gameover = false; - snake->background = M_RandomKey(sizeof(snake_backgrounds) / sizeof(*snake_backgrounds)); - - snake->snakelength = 1; - snake->snakebonus = SNAKE_BONUS_NONE; - snake->snakex[0] = M_RandomKey(SNAKE_NUM_BLOCKS_X); - snake->snakey[0] = M_RandomKey(SNAKE_NUM_BLOCKS_Y); - snake->snakedir[0] = 0; - snake->snakedir[1] = 0; - - snake->applex = M_RandomKey(SNAKE_NUM_BLOCKS_X); - snake->appley = M_RandomKey(SNAKE_NUM_BLOCKS_Y); - - snake->bonustype = SNAKE_BONUS_NONE; -} - -static UINT8 Snake_GetOppositeDir(UINT8 dir) -{ - if (dir == 1 || dir == 3) - return dir + 1; - else if (dir == 2 || dir == 4) - return dir - 1; - else - return 12 + 5 - dir; -} - -static void Snake_FindFreeSlot(UINT8 *freex, UINT8 *freey, UINT8 headx, UINT8 heady) -{ - UINT8 x, y; - UINT16 i; - - do - { - x = M_RandomKey(SNAKE_NUM_BLOCKS_X); - y = M_RandomKey(SNAKE_NUM_BLOCKS_Y); - - for (i = 0; i < snake->snakelength; i++) - if (x == snake->snakex[i] && y == snake->snakey[i]) - break; - } while (i < snake->snakelength || (x == headx && y == heady) - || (x == snake->applex && y == snake->appley) - || (snake->bonustype != SNAKE_BONUS_NONE && x == snake->bonusx && y == snake->bonusy)); - - *freex = x; - *freey = y; -} - -static void Snake_Handle(void) -{ - UINT8 x, y; - UINT8 oldx, oldy; - UINT16 i; - - // Handle retry - if (snake->gameover && (PLAYER1INPUTDOWN(gc_jump) || gamekeydown[KEY_ENTER])) - { - Snake_Initialise(); - snake->pausepressed = true; // Avoid accidental pause on respawn - } - - // Handle pause - if (PLAYER1INPUTDOWN(gc_pause) || gamekeydown[KEY_ENTER]) - { - if (!snake->pausepressed) - snake->paused = !snake->paused; - snake->pausepressed = true; - } - else - snake->pausepressed = false; - - if (snake->paused) - return; - - snake->time++; - - x = snake->snakex[0]; - y = snake->snakey[0]; - oldx = snake->snakex[1]; - oldy = snake->snakey[1]; - - // Update direction - if (gamekeydown[KEY_LEFTARROW]) - { - if (snake->snakelength < 2 || x <= oldx) - snake->snakedir[0] = 1; - } - else if (gamekeydown[KEY_RIGHTARROW]) - { - if (snake->snakelength < 2 || x >= oldx) - snake->snakedir[0] = 2; - } - else if (gamekeydown[KEY_UPARROW]) - { - if (snake->snakelength < 2 || y <= oldy) - snake->snakedir[0] = 3; - } - else if (gamekeydown[KEY_DOWNARROW]) - { - if (snake->snakelength < 2 || y >= oldy) - snake->snakedir[0] = 4; - } - - if (snake->snakebonustime) - { - snake->snakebonustime--; - if (!snake->snakebonustime) - snake->snakebonus = SNAKE_BONUS_NONE; - } - - snake->nextupdate--; - if (snake->nextupdate) - return; - if (snake->snakebonus == SNAKE_BONUS_SLOW) - snake->nextupdate = SNAKE_SPEED * 2; - else if (snake->snakebonus == SNAKE_BONUS_FAST) - snake->nextupdate = SNAKE_SPEED * 2 / 3; - else - snake->nextupdate = SNAKE_SPEED; - - if (snake->gameover) - return; - - // Find new position - switch (snake->snakedir[0]) - { - case 1: - if (x > 0) - x--; - else - snake->gameover = true; - break; - case 2: - if (x < SNAKE_NUM_BLOCKS_X - 1) - x++; - else - snake->gameover = true; - break; - case 3: - if (y > 0) - y--; - else - snake->gameover = true; - break; - case 4: - if (y < SNAKE_NUM_BLOCKS_Y - 1) - y++; - else - snake->gameover = true; - break; - } - - // Check collision with snake - if (snake->snakebonus != SNAKE_BONUS_GHOST) - for (i = 1; i < snake->snakelength - 1; i++) - if (x == snake->snakex[i] && y == snake->snakey[i]) - { - if (snake->snakebonus == SNAKE_BONUS_SCISSORS) - { - snake->snakebonus = SNAKE_BONUS_NONE; - snake->snakelength = i; - S_StartSound(NULL, sfx_adderr); - } - else - snake->gameover = true; - } - - if (snake->gameover) - { - S_StartSound(NULL, sfx_lose); - return; - } - - // Check collision with apple - if (x == snake->applex && y == snake->appley) - { - if (snake->snakelength + 3 < SNAKE_NUM_BLOCKS_X * SNAKE_NUM_BLOCKS_Y) - { - snake->snakelength++; - snake->snakex [snake->snakelength - 1] = snake->snakex [snake->snakelength - 2]; - snake->snakey [snake->snakelength - 1] = snake->snakey [snake->snakelength - 2]; - snake->snakedir[snake->snakelength - 1] = snake->snakedir[snake->snakelength - 2]; - } - - // Spawn new apple - Snake_FindFreeSlot(&snake->applex, &snake->appley, x, y); - - // Spawn new bonus - if (!(snake->snakelength % 5)) - { - do - { - snake->bonustype = M_RandomKey(SNAKE_NUM_BONUSES - 1) + 1; - } while (snake->snakelength > SNAKE_NUM_BLOCKS_X * SNAKE_NUM_BLOCKS_Y * 3 / 4 - && (snake->bonustype == SNAKE_BONUS_EGGMAN || snake->bonustype == SNAKE_BONUS_FAST || snake->bonustype == SNAKE_BONUS_REVERSE)); - - Snake_FindFreeSlot(&snake->bonusx, &snake->bonusy, x, y); - } - - S_StartSound(NULL, sfx_s3k6b); - } - - if (snake->snakelength > 1 && snake->snakedir[0]) - { - UINT8 dir = snake->snakedir[0]; - - oldx = snake->snakex[1]; - oldy = snake->snakey[1]; - - // Move - for (i = snake->snakelength - 1; i > 0; i--) - { - snake->snakex[i] = snake->snakex[i - 1]; - snake->snakey[i] = snake->snakey[i - 1]; - snake->snakedir[i] = snake->snakedir[i - 1]; - } - - // Handle corners - if (x < oldx && dir == 3) - dir = 5; - else if (x > oldx && dir == 3) - dir = 6; - else if (x < oldx && dir == 4) - dir = 7; - else if (x > oldx && dir == 4) - dir = 8; - else if (y < oldy && dir == 1) - dir = 9; - else if (y < oldy && dir == 2) - dir = 10; - else if (y > oldy && dir == 1) - dir = 11; - else if (y > oldy && dir == 2) - dir = 12; - snake->snakedir[1] = dir; - } - - snake->snakex[0] = x; - snake->snakey[0] = y; - - // Check collision with bonus - if (snake->bonustype != SNAKE_BONUS_NONE && x == snake->bonusx && y == snake->bonusy) - { - S_StartSound(NULL, sfx_ncchip); - - switch (snake->bonustype) - { - case SNAKE_BONUS_SLOW: - snake->snakebonus = SNAKE_BONUS_SLOW; - snake->snakebonustime = 20 * TICRATE; - break; - case SNAKE_BONUS_FAST: - snake->snakebonus = SNAKE_BONUS_FAST; - snake->snakebonustime = 20 * TICRATE; - break; - case SNAKE_BONUS_GHOST: - snake->snakebonus = SNAKE_BONUS_GHOST; - snake->snakebonustime = 10 * TICRATE; - break; - case SNAKE_BONUS_NUKE: - for (i = 0; i < snake->snakelength; i++) - { - snake->snakex [i] = snake->snakex [0]; - snake->snakey [i] = snake->snakey [0]; - snake->snakedir[i] = snake->snakedir[0]; - } - - S_StartSound(NULL, sfx_bkpoof); - break; - case SNAKE_BONUS_SCISSORS: - snake->snakebonus = SNAKE_BONUS_SCISSORS; - snake->snakebonustime = 60 * TICRATE; - break; - case SNAKE_BONUS_REVERSE: - for (i = 0; i < (snake->snakelength + 1) / 2; i++) - { - UINT16 i2 = snake->snakelength - 1 - i; - UINT8 tmpx = snake->snakex [i]; - UINT8 tmpy = snake->snakey [i]; - UINT8 tmpdir = snake->snakedir[i]; - - // Swap first segment with last segment - snake->snakex [i] = snake->snakex [i2]; - snake->snakey [i] = snake->snakey [i2]; - snake->snakedir[i] = Snake_GetOppositeDir(snake->snakedir[i2]); - snake->snakex [i2] = tmpx; - snake->snakey [i2] = tmpy; - snake->snakedir[i2] = Snake_GetOppositeDir(tmpdir); - } - - snake->snakedir[0] = 0; - - S_StartSound(NULL, sfx_gravch); - break; - default: - if (snake->snakebonus != SNAKE_BONUS_GHOST) - { - snake->gameover = true; - S_StartSound(NULL, sfx_lose); - } - } - - snake->bonustype = SNAKE_BONUS_NONE; - } -} - -static void Snake_Draw(void) -{ - INT16 i; - - // Background - V_DrawFlatFill( - SNAKE_LEFT_X + SNAKE_BORDER_SIZE, - SNAKE_TOP_Y + SNAKE_BORDER_SIZE, - SNAKE_MAP_WIDTH, - SNAKE_MAP_HEIGHT, - W_GetNumForName(snake_backgrounds[snake->background]) - ); - - // Borders - V_DrawFill(SNAKE_LEFT_X, SNAKE_TOP_Y, SNAKE_BORDER_SIZE + SNAKE_MAP_WIDTH, SNAKE_BORDER_SIZE, 242); // Top - V_DrawFill(SNAKE_LEFT_X + SNAKE_BORDER_SIZE + SNAKE_MAP_WIDTH, SNAKE_TOP_Y, SNAKE_BORDER_SIZE, SNAKE_BORDER_SIZE + SNAKE_MAP_HEIGHT, 242); // Right - V_DrawFill(SNAKE_LEFT_X + SNAKE_BORDER_SIZE, SNAKE_TOP_Y + SNAKE_BORDER_SIZE + SNAKE_MAP_HEIGHT, SNAKE_BORDER_SIZE + SNAKE_MAP_WIDTH, SNAKE_BORDER_SIZE, 242); // Bottom - V_DrawFill(SNAKE_LEFT_X, SNAKE_TOP_Y + SNAKE_BORDER_SIZE, SNAKE_BORDER_SIZE, SNAKE_BORDER_SIZE + SNAKE_MAP_HEIGHT, 242); // Left - - // Apple - V_DrawFixedPatch( - (SNAKE_LEFT_X + SNAKE_BORDER_SIZE + snake->applex * SNAKE_BLOCK_SIZE + SNAKE_BLOCK_SIZE / 2) * FRACUNIT, - (SNAKE_TOP_Y + SNAKE_BORDER_SIZE + snake->appley * SNAKE_BLOCK_SIZE + SNAKE_BLOCK_SIZE / 2) * FRACUNIT, - FRACUNIT / 4, - 0, - W_CachePatchLongName("DL_APPLE", PU_HUDGFX), - NULL - ); - - // Bonus - if (snake->bonustype != SNAKE_BONUS_NONE) - V_DrawFixedPatch( - (SNAKE_LEFT_X + SNAKE_BORDER_SIZE + snake->bonusx * SNAKE_BLOCK_SIZE + SNAKE_BLOCK_SIZE / 2 ) * FRACUNIT, - (SNAKE_TOP_Y + SNAKE_BORDER_SIZE + snake->bonusy * SNAKE_BLOCK_SIZE + SNAKE_BLOCK_SIZE / 2 + 4) * FRACUNIT, - FRACUNIT / 2, - 0, - W_CachePatchLongName(snake_bonuspatches[snake->bonustype], PU_HUDGFX), - NULL - ); - - // Snake - if (!snake->gameover || snake->time % 8 < 8 / 2) // Blink if game over - { - for (i = snake->snakelength - 1; i >= 0; i--) - { - const char *patchname; - UINT8 dir = snake->snakedir[i]; - - if (i == 0) // Head - { - switch (dir) - { - case 1: patchname = "DL_SNAKEHEAD_L"; break; - case 2: patchname = "DL_SNAKEHEAD_R"; break; - case 3: patchname = "DL_SNAKEHEAD_T"; break; - case 4: patchname = "DL_SNAKEHEAD_B"; break; - default: patchname = "DL_SNAKEHEAD_M"; - } - } - else // Body - { - switch (dir) - { - case 1: patchname = "DL_SNAKEBODY_L"; break; - case 2: patchname = "DL_SNAKEBODY_R"; break; - case 3: patchname = "DL_SNAKEBODY_T"; break; - case 4: patchname = "DL_SNAKEBODY_B"; break; - case 5: patchname = "DL_SNAKEBODY_LT"; break; - case 6: patchname = "DL_SNAKEBODY_RT"; break; - case 7: patchname = "DL_SNAKEBODY_LB"; break; - case 8: patchname = "DL_SNAKEBODY_RB"; break; - case 9: patchname = "DL_SNAKEBODY_TL"; break; - case 10: patchname = "DL_SNAKEBODY_TR"; break; - case 11: patchname = "DL_SNAKEBODY_BL"; break; - case 12: patchname = "DL_SNAKEBODY_BR"; break; - default: patchname = "DL_SNAKEBODY_B"; - } - } - - V_DrawFixedPatch( - (SNAKE_LEFT_X + SNAKE_BORDER_SIZE + snake->snakex[i] * SNAKE_BLOCK_SIZE + SNAKE_BLOCK_SIZE / 2) * FRACUNIT, - (SNAKE_TOP_Y + SNAKE_BORDER_SIZE + snake->snakey[i] * SNAKE_BLOCK_SIZE + SNAKE_BLOCK_SIZE / 2) * FRACUNIT, - i == 0 && dir == 0 ? FRACUNIT / 5 : FRACUNIT / 2, - snake->snakebonus == SNAKE_BONUS_GHOST ? V_TRANSLUCENT : 0, - W_CachePatchLongName(patchname, PU_HUDGFX), - NULL - ); - } - } - - // Length - V_DrawString(SNAKE_RIGHT_X + 4, SNAKE_TOP_Y, V_MONOSPACE, va("%u", snake->snakelength)); - - // Bonus - if (snake->snakebonus != SNAKE_BONUS_NONE - && (snake->snakebonustime >= 3 * TICRATE || snake->time % 4 < 4 / 2)) - V_DrawFixedPatch( - (SNAKE_RIGHT_X + 10) * FRACUNIT, - (SNAKE_TOP_Y + 24) * FRACUNIT, - FRACUNIT / 2, - 0, - W_CachePatchLongName(snake_bonuspatches[snake->snakebonus], PU_HUDGFX), - NULL - ); -} - -// -// CL_DrawConnectionStatus -// -// Keep the local client informed of our status. -// -static inline void CL_DrawConnectionStatus(void) -{ - INT32 ccstime = I_GetTime(); - - // Draw background fade - if (!menuactive) // menu already draws its own fade - V_DrawFadeScreen(0xFF00, 16); // force default - - // Draw the bottom box. - M_DrawTextBox(BASEVIDWIDTH/2-128-8, BASEVIDHEIGHT-16-8, 32, 1); - V_DrawCenteredString(BASEVIDWIDTH/2, BASEVIDHEIGHT-16-16, V_YELLOWMAP, "Press ESC to abort"); - - if (cl_mode != CL_DOWNLOADFILES) - { - INT32 i, animtime = ((ccstime / 4) & 15) + 16; - UINT8 palstart = (cl_mode == CL_SEARCHING) ? 32 : 96; - // 15 pal entries total. - const char *cltext; - - if (!(cl_mode == CL_DOWNLOADSAVEGAME && lastfilenum != -1)) - for (i = 0; i < 16; ++i) - V_DrawFill((BASEVIDWIDTH/2-128) + (i * 16), BASEVIDHEIGHT-16, 16, 8, palstart + ((animtime - i) & 15)); - - switch (cl_mode) - { - case CL_DOWNLOADSAVEGAME: - if (lastfilenum != -1) - { - UINT32 currentsize = fileneeded[lastfilenum].currentsize; - UINT32 totalsize = fileneeded[lastfilenum].totalsize; - INT32 dldlength; - - cltext = M_GetText("Downloading game state..."); - Net_GetNetStat(); - - dldlength = (INT32)((currentsize/(double)totalsize) * 256); - if (dldlength > 256) - dldlength = 256; - V_DrawFill(BASEVIDWIDTH/2-128, BASEVIDHEIGHT-16, 256, 8, 111); - V_DrawFill(BASEVIDWIDTH/2-128, BASEVIDHEIGHT-16, dldlength, 8, 96); - - V_DrawString(BASEVIDWIDTH/2-128, BASEVIDHEIGHT-16, V_20TRANS|V_MONOSPACE, - va(" %4uK/%4uK",currentsize>>10,totalsize>>10)); - - V_DrawRightAlignedString(BASEVIDWIDTH/2+128, BASEVIDHEIGHT-16, V_20TRANS|V_MONOSPACE, - va("%3.1fK/s ", ((double)getbps)/1024)); - } - else - cltext = M_GetText("Waiting to download game state..."); - break; - case CL_ASKJOIN: - case CL_WAITJOINRESPONSE: - cltext = M_GetText("Requesting to join..."); - break; - default: - cltext = M_GetText("Connecting to server..."); - break; - } - V_DrawCenteredString(BASEVIDWIDTH/2, BASEVIDHEIGHT-16-24, V_YELLOWMAP, cltext); - } - else - { - if (lastfilenum != -1) - { - INT32 dldlength; - static char tempname[28]; - fileneeded_t *file = &fileneeded[lastfilenum]; - char *filename = file->filename; - - Snake_Draw(); - - Net_GetNetStat(); - dldlength = (INT32)((file->currentsize/(double)file->totalsize) * 256); - if (dldlength > 256) - dldlength = 256; - V_DrawFill(BASEVIDWIDTH/2-128, BASEVIDHEIGHT-16, 256, 8, 111); - V_DrawFill(BASEVIDWIDTH/2-128, BASEVIDHEIGHT-16, dldlength, 8, 96); - - memset(tempname, 0, sizeof(tempname)); - // offset filename to just the name only part - filename += strlen(filename) - nameonlylength(filename); - - if (strlen(filename) > sizeof(tempname)-1) // too long to display fully - { - size_t endhalfpos = strlen(filename)-10; - // display as first 14 chars + ... + last 10 chars - // which should add up to 27 if our math(s) is correct - snprintf(tempname, sizeof(tempname), "%.14s...%.10s", filename, filename+endhalfpos); - } - else // we can copy the whole thing in safely - { - strncpy(tempname, filename, sizeof(tempname)-1); - } - - V_DrawCenteredString(BASEVIDWIDTH/2, BASEVIDHEIGHT-16-24, V_YELLOWMAP, - va(M_GetText("Downloading \"%s\""), tempname)); - V_DrawString(BASEVIDWIDTH/2-128, BASEVIDHEIGHT-16, V_20TRANS|V_MONOSPACE, - va(" %4uK/%4uK",fileneeded[lastfilenum].currentsize>>10,file->totalsize>>10)); - V_DrawRightAlignedString(BASEVIDWIDTH/2+128, BASEVIDHEIGHT-16, V_20TRANS|V_MONOSPACE, - va("%3.1fK/s ", ((double)getbps)/1024)); - } - else - V_DrawCenteredString(BASEVIDWIDTH/2, BASEVIDHEIGHT-16-24, V_YELLOWMAP, - M_GetText("Waiting to download files...")); - } -} -#endif - -/** Sends a special packet to declare how many players in local - * Used only in arbitratrenetstart() - * Sends a PT_CLIENTJOIN packet to the server - * - * \return True if the packet was successfully sent - * \todo Improve the description... - * Because to be honest, I have no idea what arbitratrenetstart is... - * Is it even used...? - * - */ -static boolean CL_SendJoin(void) -{ - UINT8 localplayers = 1; - if (netgame) - CONS_Printf(M_GetText("Sending join request...\n")); - netbuffer->packettype = PT_CLIENTJOIN; - - if (splitscreen || botingame) - localplayers++; - netbuffer->u.clientcfg.localplayers = localplayers; - netbuffer->u.clientcfg._255 = 255; - netbuffer->u.clientcfg.packetversion = PACKETVERSION; - netbuffer->u.clientcfg.version = VERSION; - netbuffer->u.clientcfg.subversion = SUBVERSION; - strncpy(netbuffer->u.clientcfg.application, SRB2APPLICATION, - sizeof netbuffer->u.clientcfg.application); - - CleanupPlayerName(consoleplayer, cv_playername.zstring); - if (splitscreen) - CleanupPlayerName(1, cv_playername2.zstring);/* 1 is a HACK? oh no */ - - strncpy(netbuffer->u.clientcfg.names[0], cv_playername.zstring, MAXPLAYERNAME); - strncpy(netbuffer->u.clientcfg.names[1], cv_playername2.zstring, MAXPLAYERNAME); - - 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; - - netbuffer->packettype = PT_SERVERINFO; - netbuffer->u.serverinfo._255 = 255; - netbuffer->u.serverinfo.packetversion = PACKETVERSION; - netbuffer->u.serverinfo.version = VERSION; - netbuffer->u.serverinfo.subversion = SUBVERSION; - strncpy(netbuffer->u.serverinfo.application, SRB2APPLICATION, - sizeof netbuffer->u.serverinfo.application); - // return back the time value so client can compute their ping - netbuffer->u.serverinfo.time = (tic_t)LONG(servertime); - netbuffer->u.serverinfo.leveltime = (tic_t)LONG(leveltime); - - netbuffer->u.serverinfo.numberofplayer = (UINT8)D_NumPlayers(); - netbuffer->u.serverinfo.maxplayer = (UINT8)cv_maxplayers.value; - - if (!node || 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; - netbuffer->u.serverinfo.cheatsenabled = CV_CheatsEnabled(); - netbuffer->u.serverinfo.isdedicated = (UINT8)dedicated; - strncpy(netbuffer->u.serverinfo.servername, cv_servername.string, - MAXSERVERNAME); - strncpy(netbuffer->u.serverinfo.mapname, G_BuildMapName(gamemap), 7); - - M_Memcpy(netbuffer->u.serverinfo.mapmd5, mapmd5, 16); - - memset(netbuffer->u.serverinfo.maptitle, 0, sizeof netbuffer->u.serverinfo.maptitle); - - if (mapheaderinfo[gamemap-1] && *mapheaderinfo[gamemap-1]->lvlttl) - { - char *read = mapheaderinfo[gamemap-1]->lvlttl, *writ = netbuffer->u.serverinfo.maptitle; - while (writ < (netbuffer->u.serverinfo.maptitle+32) && *read != '\0') - { - if (!(*read & 0x80)) - { - *writ = toupper(*read); - writ++; - } - read++; - } - *writ = '\0'; - //strncpy(netbuffer->u.serverinfo.maptitle, (char *)mapheaderinfo[gamemap-1]->lvlttl, 33); - } - else - strncpy(netbuffer->u.serverinfo.maptitle, "UNKNOWN", 32); - - if (mapheaderinfo[gamemap-1] && !(mapheaderinfo[gamemap-1]->levelflags & LF_NOZONE)) - netbuffer->u.serverinfo.iszone = 1; - else - netbuffer->u.serverinfo.iszone = 0; - - if (mapheaderinfo[gamemap-1]) - netbuffer->u.serverinfo.actnum = mapheaderinfo[gamemap-1]->actnum; - - p = PutFileNeeded(); - - HSendPacket(node, false, 0, p - ((UINT8 *)&netbuffer->u)); -} - -static void SV_SendPlayerInfo(INT32 node) -{ - UINT8 i; - netbuffer->packettype = PT_PLAYERINFO; - - for (i = 0; i < MAXPLAYERS; i++) - { - if (!playeringame[i]) - { - netbuffer->u.playerinfo[i].num = 255; // This slot is empty. - continue; - } - - netbuffer->u.playerinfo[i].num = i; - strncpy(netbuffer->u.playerinfo[i].name, (const char *)&player_names[i], MAXPLAYERNAME+1); - netbuffer->u.playerinfo[i].name[MAXPLAYERNAME] = '\0'; - - //fetch IP address - //No, don't do that, you fuckface. - memset(netbuffer->u.playerinfo[i].address, 0, 4); - - if (G_GametypeHasTeams()) - { - if (!players[i].ctfteam) - netbuffer->u.playerinfo[i].team = 255; - else - netbuffer->u.playerinfo[i].team = (UINT8)players[i].ctfteam; - } - else - { - if (players[i].spectator) - netbuffer->u.playerinfo[i].team = 255; - else - netbuffer->u.playerinfo[i].team = 0; - } - - netbuffer->u.playerinfo[i].score = LONG(players[i].score); - netbuffer->u.playerinfo[i].timeinserver = SHORT((UINT16)(players[i].jointime / TICRATE)); - netbuffer->u.playerinfo[i].skin = (UINT8)(players[i].skin -#ifdef DEVELOP // it's safe to do this only because PLAYERINFO isn't read by the game itself - % 3 -#endif - ); - - // Extra data - netbuffer->u.playerinfo[i].data = 0; //players[i].skincolor; - - if (players[i].pflags & PF_TAGIT) - netbuffer->u.playerinfo[i].data |= 0x20; - - if (players[i].gotflag) - netbuffer->u.playerinfo[i].data |= 0x40; - - if (players[i].powers[pw_super]) - netbuffer->u.playerinfo[i].data |= 0x80; - } - - HSendPacket(node, false, 0, sizeof(plrinfo) * MAXPLAYERS); -} - -/** Sends a PT_SERVERCFG packet - * - * \param node The destination - * \return True if the packet was successfully sent - * - */ -static boolean SV_SendServerConfig(INT32 node) -{ - boolean waspacketsent; - - netbuffer->packettype = PT_SERVERCFG; - - netbuffer->u.servercfg.version = VERSION; - netbuffer->u.servercfg.subversion = SUBVERSION; - - netbuffer->u.servercfg.serverplayer = (UINT8)serverplayer; - netbuffer->u.servercfg.totalslotnum = (UINT8)(doomcom->numslots); - netbuffer->u.servercfg.gametic = (tic_t)LONG(gametic); - netbuffer->u.servercfg.clientnode = (UINT8)node; - netbuffer->u.servercfg.gamestate = (UINT8)gamestate; - netbuffer->u.servercfg.gametype = (UINT8)gametype; - netbuffer->u.servercfg.modifiedgame = (UINT8)modifiedgame; - - memcpy(netbuffer->u.servercfg.server_context, server_context, 8); - - { - const size_t len = sizeof (serverconfig_pak); - -#ifdef DEBUGFILE - if (debugfile) - { - fprintf(debugfile, "ServerConfig Packet about to be sent, size of packet:%s to node:%d\n", - sizeu1(len), node); - } -#endif - - waspacketsent = HSendPacket(node, true, 0, len); - } - -#ifdef DEBUGFILE - if (debugfile) - { - if (waspacketsent) - { - fprintf(debugfile, "ServerConfig Packet was sent\n"); - } - else - { - fprintf(debugfile, "ServerConfig Packet could not be sent right now\n"); - } - } -#endif - - return waspacketsent; -} - -#ifndef NONET -#define SAVEGAMESIZE (768*1024) - -static boolean SV_ResendingSavegameToAnyone(void) -{ - INT32 i; - - for (i = 0; i < MAXNETNODES; i++) - if (resendingsavegame[i]) - return true; - return false; -} - -static void SV_SendSaveGame(INT32 node, boolean resending) -{ - size_t length, compressedlen; - UINT8 *savebuffer; - UINT8 *compressedsave; - UINT8 *buffertosend; - - // first save it in a malloced buffer - savebuffer = (UINT8 *)malloc(SAVEGAMESIZE); - if (!savebuffer) - { - CONS_Alert(CONS_ERROR, M_GetText("No more free memory for savegame\n")); - return; - } - - // Leave room for the uncompressed length. - save_p = savebuffer + sizeof(UINT32); - - P_SaveNetGame(resending); - - length = save_p - savebuffer; - if (length > SAVEGAMESIZE) - { - free(savebuffer); - save_p = NULL; - I_Error("Savegame buffer overrun"); - } - - // Allocate space for compressed save: one byte fewer than for the - // uncompressed data to ensure that the compression is worthwhile. - compressedsave = malloc(length - 1); - if (!compressedsave) - { - CONS_Alert(CONS_ERROR, M_GetText("No more free memory for savegame\n")); - return; - } - - // Attempt to compress it. - if((compressedlen = lzf_compress(savebuffer + sizeof(UINT32), length - sizeof(UINT32), compressedsave + sizeof(UINT32), length - sizeof(UINT32) - 1))) - { - // Compressing succeeded; send compressed data - - free(savebuffer); - - // State that we're compressed. - buffertosend = compressedsave; - WRITEUINT32(compressedsave, length - sizeof(UINT32)); - length = compressedlen + sizeof(UINT32); - } - else - { - // Compression failed to make it smaller; send original - - free(compressedsave); - - // State that we're not compressed - buffertosend = savebuffer; - WRITEUINT32(savebuffer, 0); - } - - AddRamToSendQueue(node, buffertosend, length, SF_RAM, 0); - save_p = NULL; - - // Remember when we started sending the savegame so we can handle timeouts - sendingsavegame[node] = true; - freezetimeout[node] = I_GetTime() + jointimeout + length / 1024; // 1 extra tic for each kilobyte -} - -#ifdef DUMPCONSISTENCY -#define TMPSAVENAME "badmath.sav" -static consvar_t cv_dumpconsistency = CVAR_INIT ("dumpconsistency", "Off", CV_SAVE|CV_NETVAR, CV_OnOff, NULL); - -static void SV_SavedGame(void) -{ - size_t length; - UINT8 *savebuffer; - char tmpsave[256]; - - if (!cv_dumpconsistency.value) - return; - - sprintf(tmpsave, "%s" PATHSEP TMPSAVENAME, srb2home); - - // first save it in a malloced buffer - save_p = savebuffer = (UINT8 *)malloc(SAVEGAMESIZE); - if (!save_p) - { - CONS_Alert(CONS_ERROR, M_GetText("No more free memory for savegame\n")); - return; - } - - P_SaveNetGame(false); - - length = save_p - savebuffer; - if (length > SAVEGAMESIZE) - { - free(savebuffer); - save_p = NULL; - I_Error("Savegame buffer overrun"); - } - - // then save it! - if (!FIL_WriteFile(tmpsave, savebuffer, length)) - CONS_Printf(M_GetText("Didn't save %s for netgame"), tmpsave); - - free(savebuffer); - save_p = NULL; -} - -#undef TMPSAVENAME -#endif -#define TMPSAVENAME "$$$.sav" - - -static void CL_LoadReceivedSavegame(boolean reloading) -{ - UINT8 *savebuffer = NULL; - size_t length, decompressedlen; - char tmpsave[256]; - - sprintf(tmpsave, "%s" PATHSEP TMPSAVENAME, srb2home); - - length = FIL_ReadFile(tmpsave, &savebuffer); - - CONS_Printf(M_GetText("Loading savegame length %s\n"), sizeu1(length)); - if (!length) - { - I_Error("Can't read savegame sent"); - return; - } - - save_p = savebuffer; - - // Decompress saved game if necessary. - decompressedlen = READUINT32(save_p); - if(decompressedlen > 0) - { - UINT8 *decompressedbuffer = Z_Malloc(decompressedlen, PU_STATIC, NULL); - lzf_decompress(save_p, length - sizeof(UINT32), decompressedbuffer, decompressedlen); - Z_Free(savebuffer); - save_p = savebuffer = decompressedbuffer; - } - - paused = false; - demoplayback = false; - titlemapinaction = TITLEMAP_OFF; - titledemo = false; - automapactive = false; - - // load a base level - if (P_LoadNetGame(reloading)) - { - const UINT8 actnum = mapheaderinfo[gamemap-1]->actnum; - CONS_Printf(M_GetText("Map is now \"%s"), G_BuildMapName(gamemap)); - if (strcmp(mapheaderinfo[gamemap-1]->lvlttl, "")) - { - CONS_Printf(": %s", mapheaderinfo[gamemap-1]->lvlttl); - if (!(mapheaderinfo[gamemap-1]->levelflags & LF_NOZONE)) - CONS_Printf(M_GetText(" Zone")); - if (actnum > 0) - CONS_Printf(" %2d", actnum); - } - CONS_Printf("\"\n"); - } - else - { - CONS_Alert(CONS_ERROR, M_GetText("Can't load the level!\n")); - Z_Free(savebuffer); - save_p = NULL; - if (unlink(tmpsave) == -1) - CONS_Alert(CONS_ERROR, M_GetText("Can't delete %s\n"), tmpsave); - return; - } - - // done - Z_Free(savebuffer); - save_p = NULL; - if (unlink(tmpsave) == -1) - CONS_Alert(CONS_ERROR, M_GetText("Can't delete %s\n"), tmpsave); - consistancy[gametic%BACKUPTICS] = Consistancy(); - CON_ToggleOff(); - - // Tell the server we have received and reloaded the gamestate - // so they know they can resume the game - netbuffer->packettype = PT_RECEIVEDGAMESTATE; - HSendPacket(servernode, true, 0, 0); -} - -static void CL_ReloadReceivedSavegame(void) -{ - INT32 i; - - for (i = 0; i < MAXPLAYERS; i++) - { -#ifdef HAVE_BLUA - LUA_InvalidatePlayer(&players[i]); -#endif - sprintf(player_names[i], "Player %d", i + 1); - } - - CL_LoadReceivedSavegame(true); - - if (neededtic < gametic) - neededtic = gametic; - maketic = neededtic; - - ticcmd_oldangleturn[0] = players[consoleplayer].oldrelangleturn; - P_ForceLocalAngle(&players[consoleplayer], (angle_t)(players[consoleplayer].angleturn << 16)); - if (splitscreen) - { - ticcmd_oldangleturn[1] = players[secondarydisplayplayer].oldrelangleturn; - P_ForceLocalAngle(&players[secondarydisplayplayer], (angle_t)(players[secondarydisplayplayer].angleturn << 16)); - } - - camera.subsector = R_PointInSubsector(camera.x, camera.y); - camera2.subsector = R_PointInSubsector(camera2.x, camera2.y); - - cl_redownloadinggamestate = false; - - CONS_Printf(M_GetText("Game state reloaded\n")); -} -#endif - -#ifndef NONET -static void SendAskInfo(INT32 node) -{ - const tic_t asktime = I_GetTime(); - netbuffer->packettype = PT_ASKINFO; - netbuffer->u.askinfo.version = VERSION; - netbuffer->u.askinfo.time = (tic_t)LONG(asktime); - - // Even if this never arrives due to the host being firewalled, we've - // now allowed traffic from the host to us in, so once the MS relays - // our address to the host, it'll be able to speak to us. - HSendPacket(node, false, 0, sizeof (askinfo_pak)); -} - -serverelem_t serverlist[MAXSERVERLIST]; -UINT32 serverlistcount = 0; - -#define FORCECLOSE 0x8000 - -static void SL_ClearServerList(INT32 connectedserver) -{ - UINT32 i; - - for (i = 0; i < serverlistcount; i++) - if (connectedserver != serverlist[i].node) - { - Net_CloseConnection(serverlist[i].node|FORCECLOSE); - serverlist[i].node = 0; - } - serverlistcount = 0; -} - -static UINT32 SL_SearchServer(INT32 node) -{ - UINT32 i; - for (i = 0; i < serverlistcount; i++) - if (serverlist[i].node == node) - return i; - - return UINT32_MAX; -} - -static void SL_InsertServer(serverinfo_pak* info, SINT8 node) -{ - UINT32 i; - - // search if not already on it - i = SL_SearchServer(node); - if (i == UINT32_MAX) - { - // not found add it - if (serverlistcount >= MAXSERVERLIST) - return; // list full - - if (info->_255 != 255) - return;/* old packet format */ - - if (info->packetversion != PACKETVERSION) - return;/* old new packet format */ - - if (info->version != VERSION) - return; // Not same version. - - if (info->subversion != SUBVERSION) - return; // Close, but no cigar. - - if (strcmp(info->application, SRB2APPLICATION)) - return;/* that's a different mod */ - - i = serverlistcount++; - } - - serverlist[i].info = *info; - serverlist[i].node = node; - - // resort server list - M_SortServerList(); -} - -#if defined (MASTERSERVER) && defined (HAVE_THREADS) -struct Fetch_servers_ctx -{ - int room; - int id; -}; - -static void -Fetch_servers_thread (struct Fetch_servers_ctx *ctx) -{ - msg_server_t *server_list; - - server_list = GetShortServersList(ctx->room, ctx->id); - - if (server_list) - { - I_lock_mutex(&ms_QueryId_mutex); - { - if (ctx->id != ms_QueryId) - { - free(server_list); - server_list = NULL; - } - } - I_unlock_mutex(ms_QueryId_mutex); - - if (server_list) - { - I_lock_mutex(&m_menu_mutex); - { - if (m_waiting_mode == M_WAITING_SERVERS) - m_waiting_mode = M_NOT_WAITING; - } - I_unlock_mutex(m_menu_mutex); - - I_lock_mutex(&ms_ServerList_mutex); - { - ms_ServerList = server_list; - } - I_unlock_mutex(ms_ServerList_mutex); - } - } - - free(ctx); -} -#endif/*defined (MASTERSERVER) && defined (HAVE_THREADS)*/ - -void CL_QueryServerList (msg_server_t *server_list) -{ - INT32 i; - - for (i = 0; server_list[i].header.buffer[0]; i++) - { - // Make sure MS version matches our own, to - // thwart nefarious servers who lie to the MS. - - /* lol bruh, that version COMES from the servers */ - //if (strcmp(version, server_list[i].version) == 0) - { - INT32 node = I_NetMakeNodewPort(server_list[i].ip, server_list[i].port); - if (node == -1) - break; // no more node free - SendAskInfo(node); - // Force close the connection so that servers can't eat - // up nodes forever if we never get a reply back from them - // (usually when they've not forwarded their ports). - // - // Don't worry, we'll get in contact with the working - // servers again when they send SERVERINFO to us later! - // - // (Note: as a side effect this probably means every - // server in the list will probably be using the same node (e.g. node 1), - // not that it matters which nodes they use when - // the connections are closed afterwards anyway) - // -- Monster Iestyn 12/11/18 - Net_CloseConnection(node|FORCECLOSE); - } - } -} - -void CL_UpdateServerList(boolean internetsearch, INT32 room) -{ - (void)internetsearch; - (void)room; - - SL_ClearServerList(0); - - if (!netgame && I_NetOpenSocket) - { - if (I_NetOpenSocket()) - { - netgame = true; - multiplayer = true; - } - } - - // search for local servers - if (netgame) - SendAskInfo(BROADCASTADDR); - -#ifdef MASTERSERVER - if (internetsearch) - { -#ifdef HAVE_THREADS - struct Fetch_servers_ctx *ctx; - - ctx = malloc(sizeof *ctx); - - /* This called from M_Refresh so I don't use a mutex */ - m_waiting_mode = M_WAITING_SERVERS; - - I_lock_mutex(&ms_QueryId_mutex); - { - ctx->id = ms_QueryId; - } - I_unlock_mutex(ms_QueryId_mutex); - - ctx->room = room; - - I_spawn_thread("fetch-servers", (I_thread_fn)Fetch_servers_thread, ctx); -#else - msg_server_t *server_list; - - server_list = GetShortServersList(room, 0); - - if (server_list) - { - CL_QueryServerList(server_list); - free(server_list); - } -#endif - } -#endif/*MASTERSERVER*/ -} - -#endif // ifndef NONET - -/** Called by CL_ServerConnectionTicker - * - * \param asksent The last time we asked the server to join. We re-ask every second in case our request got lost in transmit. - * \return False if the connection was aborted - * \sa CL_ServerConnectionTicker - * \sa CL_ConnectToServer - * - */ -static boolean CL_ServerConnectionSearchTicker(tic_t *asksent) -{ -#ifndef NONET - INT32 i; - - // serverlist is updated by GetPacket function - if (serverlistcount > 0) - { - // this can be a responce to our broadcast request - if (servernode == -1 || servernode >= MAXNETNODES) - { - i = 0; - servernode = serverlist[i].node; - CONS_Printf(M_GetText("Found, ")); - } - else - { - i = SL_SearchServer(servernode); - if (i < 0) - return true; - } - - // Quit here rather than downloading files and being refused later. - if (serverlist[i].info.refusereason) - { - D_QuitNetGame(); - CL_Reset(); - D_StartTitle(); - 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; - } - - if (client) - { - D_ParseFileneeded(serverlist[i].info.fileneedednum, - serverlist[i].info.fileneeded); - CONS_Printf(M_GetText("Checking files...\n")); - i = CL_CheckFiles(); - if (i == 3) // too many files - { - D_QuitNetGame(); - CL_Reset(); - D_StartTitle(); - M_StartMessage(M_GetText( - "You have too many WAD files loaded\n" - "to add ones the server is using.\n" - "Please restart SRB2 before connecting.\n\n" - "Press ESC\n" - ), NULL, MM_NOTHING); - return false; - } - else if (i == 2) // cannot join for some reason - { - D_QuitNetGame(); - CL_Reset(); - D_StartTitle(); - M_StartMessage(M_GetText( - "You have the wrong addons loaded.\n\n" - "To play on this server, restart\n" - "the game and don't load any addons.\n" - "SRB2 will automatically add\n" - "everything you need when you join.\n\n" - "Press ESC\n" - ), NULL, MM_NOTHING); - return false; - } - else if (i == 1) - cl_mode = CL_ASKJOIN; - else - { - // must download something - // can we, though? - if (!CL_CheckDownloadable()) // nope! - { - D_QuitNetGame(); - CL_Reset(); - D_StartTitle(); - M_StartMessage(M_GetText( - "You cannot connect to this server\n" - "because you cannot download the files\n" - "that you are missing from the server.\n\n" - "See the console or log file for\n" - "more details.\n\n" - "Press ESC\n" - ), NULL, MM_NOTHING); - return false; - } - // no problem if can't send packet, we will retry later - if (CL_SendFileRequest()) - { - cl_mode = CL_DOWNLOADFILES; -#ifndef NONET - Snake_Initialise(); -#endif - } - } - } - else - cl_mode = CL_ASKJOIN; // files need not be checked for the server. - - return true; - } - - // Ask the info to the server (askinfo packet) - if (*asksent + NEWTICRATE < I_GetTime()) - { - SendAskInfo(servernode); - *asksent = I_GetTime(); - } -#else - (void)asksent; - // No netgames, so we skip this state. - cl_mode = CL_ASKJOIN; -#endif // ifndef NONET/else - - return true; -} - -/** Called by CL_ConnectToServer - * - * \param tmpsave The name of the gamestate file??? - * \param oldtic Used for knowing when to poll events and redraw - * \param asksent ??? - * \return False if the connection was aborted - * \sa CL_ServerConnectionSearchTicker - * \sa CL_ConnectToServer - * - */ -static boolean CL_ServerConnectionTicker(const char *tmpsave, tic_t *oldtic, tic_t *asksent) -{ - boolean waitmore; - INT32 i; - -#ifdef NONET - (void)tmpsave; -#endif - - switch (cl_mode) - { - case CL_SEARCHING: - if (!CL_ServerConnectionSearchTicker(asksent)) - return false; - break; - - case CL_DOWNLOADFILES: - waitmore = false; - for (i = 0; i < fileneedednum; i++) - if (fileneeded[i].status == FS_DOWNLOADING - || fileneeded[i].status == FS_REQUESTED) - { - waitmore = true; - break; - } - if (waitmore) - break; // exit the case - -#ifndef NONET - if (snake) - { - free(snake); - snake = NULL; - } -#endif - - cl_mode = CL_ASKJOIN; // don't break case continue to cljoin request now - /* FALLTHRU */ - - case CL_ASKJOIN: - CL_LoadServerFiles(); -#ifndef NONET - // prepare structures to save the file - // WARNING: this can be useless in case of server not in GS_LEVEL - // but since the network layer doesn't provide ordered packets... - CL_PrepareDownloadSaveGame(tmpsave); -#endif - if (CL_SendJoin()) - cl_mode = CL_WAITJOINRESPONSE; - break; - -#ifndef NONET - case CL_DOWNLOADSAVEGAME: - // At this state, the first (and only) needed file is the gamestate - if (fileneeded[0].status == FS_FOUND) - { - // Gamestate is now handled within CL_LoadReceivedSavegame() - CL_LoadReceivedSavegame(false); - cl_mode = CL_CONNECTED; - } // don't break case continue to CL_CONNECTED - else - break; -#endif - - case CL_WAITJOINRESPONSE: - case CL_CONNECTED: - default: - break; - - // Connection closed by cancel, timeout or refusal. - case CL_ABORTED: - cl_mode = CL_SEARCHING; - return false; - - } - - GetPackets(); - Net_AckTicker(); - - // Call it only once by tic - if (*oldtic != I_GetTime()) - { - I_OsPolling(); - for (; eventtail != eventhead; eventtail = (eventtail+1) & (MAXEVENTS-1)) - G_MapEventsToControls(&events[eventtail]); - - if (gamekeydown[KEY_ESCAPE] || gamekeydown[KEY_JOY1+1]) - { - CONS_Printf(M_GetText("Network game synchronization aborted.\n")); -// M_StartMessage(M_GetText("Network game synchronization aborted.\n\nPress ESC\n"), NULL, MM_NOTHING); - -#ifndef NONET - if (snake) - { - free(snake); - snake = NULL; - } -#endif - - D_QuitNetGame(); - CL_Reset(); - D_StartTitle(); - memset(gamekeydown, 0, NUMKEYS); - return false; - } -#ifndef NONET - else if (cl_mode == CL_DOWNLOADFILES && snake) - Snake_Handle(); -#endif - - if (client && (cl_mode == CL_DOWNLOADFILES || cl_mode == CL_DOWNLOADSAVEGAME)) - FileReceiveTicker(); - - // why are these here? this is for servers, we're a client - //if (key == 's' && server) - // doomcom->numnodes = (INT16)pnumnodes; - //FileSendTicker(); - *oldtic = I_GetTime(); - -#ifndef NONET - if (client && cl_mode != CL_CONNECTED && cl_mode != CL_ABORTED) - { - if (cl_mode != CL_DOWNLOADFILES && cl_mode != CL_DOWNLOADSAVEGAME) - { - F_MenuPresTicker(true); // title sky - F_TitleScreenTicker(true); - F_TitleScreenDrawer(); - } - CL_DrawConnectionStatus(); - I_UpdateNoVsync(); // page flip or blit buffer - if (moviemode) - M_SaveFrame(); - S_UpdateSounds(); - S_UpdateClosedCaptions(); - } -#else - CON_Drawer(); - I_UpdateNoVsync(); -#endif - } - else - I_Sleep(); - - return true; -} - -/** Use adaptive send using net_bandwidth and stat.sendbytes - * - * \todo Better description... - * - */ -static void CL_ConnectToServer(void) -{ - INT32 pnumnodes, nodewaited = doomcom->numnodes, i; - tic_t oldtic; -#ifndef NONET - tic_t asksent; - char tmpsave[256]; - - sprintf(tmpsave, "%s" PATHSEP TMPSAVENAME, srb2home); - - lastfilenum = -1; -#endif - - cl_mode = CL_SEARCHING; - -#ifndef NONET - // Don't get a corrupt savegame error because tmpsave already exists - if (FIL_FileExists(tmpsave) && unlink(tmpsave) == -1) - I_Error("Can't delete %s\n", tmpsave); -#endif - - if (netgame) - { - if (servernode < 0 || servernode >= MAXNETNODES) - CONS_Printf(M_GetText("Searching for a server...\n")); - else - CONS_Printf(M_GetText("Contacting the server...\n")); - } - - if (gamestate == GS_INTERMISSION) - Y_EndIntermission(); // clean up intermission graphics etc - - DEBFILE(va("waiting %d nodes\n", doomcom->numnodes)); - G_SetGamestate(GS_WAITINGPLAYERS); - wipegamestate = GS_WAITINGPLAYERS; - - ClearAdminPlayers(); - pnumnodes = 1; - oldtic = I_GetTime() - 1; -#ifndef NONET - asksent = (tic_t) - TICRATE; - - i = SL_SearchServer(servernode); - - if (i != -1) - { - char *gametypestr = serverlist[i].info.gametypename; - CONS_Printf(M_GetText("Connecting to: %s\n"), serverlist[i].info.servername); - gametypestr[sizeof serverlist[i].info.gametypename - 1] = '\0'; - CONS_Printf(M_GetText("Gametype: %s\n"), gametypestr); - CONS_Printf(M_GetText("Version: %d.%d.%u\n"), serverlist[i].info.version/100, - serverlist[i].info.version%100, serverlist[i].info.subversion); - } - SL_ClearServerList(servernode); -#endif - - do - { - // If the connection was aborted for some reason, leave -#ifndef NONET - if (!CL_ServerConnectionTicker(tmpsave, &oldtic, &asksent)) -#else - if (!CL_ServerConnectionTicker((char*)NULL, &oldtic, (tic_t *)NULL)) -#endif - return; - - if (server) - { - pnumnodes = 0; - for (i = 0; i < MAXNETNODES; i++) - if (nodeingame[i]) - pnumnodes++; - } - } - while (!(cl_mode == CL_CONNECTED && (client || (server && nodewaited <= pnumnodes)))); - - DEBFILE(va("Synchronisation Finished\n")); - - displayplayer = consoleplayer; -} - -#ifndef NONET -typedef struct banreason_s -{ - char *reason; - struct banreason_s *prev; //-1 - struct banreason_s *next; //+1 -} banreason_t; - -static banreason_t *reasontail = NULL; //last entry, use prev -static banreason_t *reasonhead = NULL; //1st entry, use next - -static void Command_ShowBan(void) //Print out ban list -{ - size_t i; - const char *address, *mask; - banreason_t *reasonlist = reasonhead; - - if (I_GetBanAddress) - CONS_Printf(M_GetText("Ban List:\n")); - else - return; - - for (i = 0;(address = I_GetBanAddress(i)) != NULL;i++) - { - if (!I_GetBanMask || (mask = I_GetBanMask(i)) == NULL) - CONS_Printf("%s: %s ", sizeu1(i+1), address); - else - CONS_Printf("%s: %s/%s ", sizeu1(i+1), address, mask); - - if (reasonlist && reasonlist->reason) - CONS_Printf("(%s)\n", reasonlist->reason); - else - CONS_Printf("\n"); - - if (reasonlist) reasonlist = reasonlist->next; - } - - if (i == 0 && !address) - CONS_Printf(M_GetText("(empty)\n")); -} - -void D_SaveBan(void) -{ - FILE *f; - size_t i; - banreason_t *reasonlist = reasonhead; - const char *address, *mask; - - if (!reasonhead) - return; - - f = fopen(va("%s"PATHSEP"%s", srb2home, "ban.txt"), "w"); - - if (!f) - { - CONS_Alert(CONS_WARNING, M_GetText("Could not save ban list into ban.txt\n")); - return; - } - - for (i = 0;(address = I_GetBanAddress(i)) != NULL;i++) - { - if (!I_GetBanMask || (mask = I_GetBanMask(i)) == NULL) - fprintf(f, "%s 0", address); - else - fprintf(f, "%s %s", address, mask); - - if (reasonlist && reasonlist->reason) - fprintf(f, " %s\n", reasonlist->reason); - else - fprintf(f, " %s\n", "NA"); - - if (reasonlist) reasonlist = reasonlist->next; - } - - fclose(f); -} - -static void Ban_Add(const char *reason) -{ - banreason_t *reasonlist = malloc(sizeof(*reasonlist)); - - if (!reasonlist) - return; - if (!reason) - reason = "NA"; - - reasonlist->next = NULL; - reasonlist->reason = Z_StrDup(reason); - if ((reasonlist->prev = reasontail) == NULL) - reasonhead = reasonlist; - else - reasontail->next = reasonlist; - reasontail = reasonlist; -} - -static void Command_ClearBans(void) -{ - banreason_t *temp; - - if (!I_ClearBans) - return; - - I_ClearBans(); - D_SaveBan(); - reasontail = NULL; - while (reasonhead) - { - temp = reasonhead->next; - Z_Free(reasonhead->reason); - free(reasonhead); - reasonhead = temp; - } -} - -static void Ban_Load_File(boolean warning) -{ - FILE *f; - size_t i; - const char *address, *mask; - char buffer[MAX_WADPATH]; - - f = fopen(va("%s"PATHSEP"%s", srb2home, "ban.txt"), "r"); - - if (!f) - { - if (warning) - CONS_Alert(CONS_WARNING, M_GetText("Could not open ban.txt for ban list\n")); - return; - } - - if (I_ClearBans) - Command_ClearBans(); - else - { - fclose(f); - return; - } - - for (i=0; fgets(buffer, (int)sizeof(buffer), f); i++) - { - address = strtok(buffer, " \t\r\n"); - mask = strtok(NULL, " \t\r\n"); - - I_SetBanAddress(address, mask); - - Ban_Add(strtok(NULL, "\r\n")); - } - - fclose(f); -} - -static void Command_ReloadBan(void) //recheck ban.txt -{ - Ban_Load_File(true); -} - -static void Command_connect(void) -{ - if (COM_Argc() < 2 || *COM_Argv(1) == 0) - { - CONS_Printf(M_GetText( - "Connect (port): connect to a server\n" - "Connect ANY: connect to the first lan server found\n" - //"Connect SELF: connect to your own server.\n" - )); - return; - } - - if (Playing() || titledemo) - { - CONS_Printf(M_GetText("You cannot connect while in a game. End this game first.\n")); - return; - } - - // modified game check: no longer handled - // we don't request a restart unless the filelist differs - - server = false; -/* - if (!stricmp(COM_Argv(1), "self")) - { - servernode = 0; - server = true; - /// \bug should be but... - //SV_SpawnServer(); - } - else -*/ - { - // used in menu to connect to a server in the list - if (netgame && !stricmp(COM_Argv(1), "node")) - { - servernode = (SINT8)atoi(COM_Argv(2)); - } - else if (netgame) - { - CONS_Printf(M_GetText("You cannot connect while in a game. End this game first.\n")); - return; - } - else if (I_NetOpenSocket) - { - I_NetOpenSocket(); - netgame = true; - multiplayer = true; - - if (!stricmp(COM_Argv(1), "any")) - servernode = BROADCASTADDR; - else if (I_NetMakeNodewPort) - { - if (COM_Argc() >= 3) // address AND port - servernode = I_NetMakeNodewPort(COM_Argv(1), COM_Argv(2)); - else // address only, or address:port - servernode = I_NetMakeNode(COM_Argv(1)); - } - else - { - CONS_Alert(CONS_ERROR, M_GetText("There is no server identification with this network driver\n")); - D_CloseConnection(); - return; - } - } - else - CONS_Alert(CONS_ERROR, M_GetText("There is no network driver\n")); - } - - splitscreen = false; - SplitScreen_OnChange(); - botingame = false; - botskin = 0; - CL_ConnectToServer(); -} -#endif - -static void ResetNode(INT32 node); - -// -// CL_ClearPlayer -// -// Clears the player data so that a future client can use this slot -// -void CL_ClearPlayer(INT32 playernum) -{ - if (players[playernum].mo) - P_RemoveMobj(players[playernum].mo); - memset(&players[playernum], 0, sizeof (player_t)); - memset(playeraddress[playernum], 0, sizeof(*playeraddress)); -} - -// -// CL_RemovePlayer -// -// Removes a player from the current game -// -static void CL_RemovePlayer(INT32 playernum, kickreason_t reason) -{ - // Sanity check: exceptional cases (i.e. c-fails) can cause multiple - // kick commands to be issued for the same player. - if (!playeringame[playernum]) - return; - - if (server && !demoplayback && playernode[playernum] != UINT8_MAX) - { - INT32 node = playernode[playernum]; - playerpernode[node]--; - if (playerpernode[node] <= 0) - { - nodeingame[node] = false; - Net_CloseConnection(node); - ResetNode(node); - } - } - - if (gametyperules & GTR_TEAMFLAGS) - P_PlayerFlagBurst(&players[playernum], false); // Don't take the flag with you! - - // If in a special stage, redistribute the player's spheres across - // the remaining players. - if (G_IsSpecialStage(gamemap)) - { - INT32 i, count, sincrement, spheres, rincrement, rings; - - for (i = 0, count = 0; i < MAXPLAYERS; i++) - { - if (playeringame[i]) - count++; - } - - count--; - sincrement = spheres = players[playernum].spheres; - rincrement = rings = players[playernum].rings; - - if (count) - { - sincrement /= count; - rincrement /= count; - } - - for (i = 0; i < MAXPLAYERS; i++) - { - if (playeringame[i] && i != playernum) - { - if (spheres < 2*sincrement) - { - P_GivePlayerSpheres(&players[i], spheres); - spheres = 0; - } - else - { - P_GivePlayerSpheres(&players[i], sincrement); - spheres -= sincrement; - } - - if (rings < 2*rincrement) - { - P_GivePlayerRings(&players[i], rings); - rings = 0; - } - else - { - P_GivePlayerRings(&players[i], rincrement); - rings -= rincrement; - } - } - } - } - - LUAh_PlayerQuit(&players[playernum], reason); // Lua hook for player quitting - - // don't look through someone's view who isn't there - if (playernum == displayplayer) - { - // Call ViewpointSwitch hooks here. - // The viewpoint was forcibly changed. - LUAh_ViewpointSwitch(&players[consoleplayer], &players[consoleplayer], true); - displayplayer = consoleplayer; - } - - // Reset player data - CL_ClearPlayer(playernum); - - // remove avatar of player - playeringame[playernum] = false; - playernode[playernum] = UINT8_MAX; - while (!playeringame[doomcom->numslots-1] && doomcom->numslots > 1) - doomcom->numslots--; - - // Reset the name - sprintf(player_names[playernum], "Player %d", playernum+1); - - player_name_changes[playernum] = 0; - - if (IsPlayerAdmin(playernum)) - { - RemoveAdminPlayer(playernum); // don't stay admin after you're gone - } - - LUA_InvalidatePlayer(&players[playernum]); - - if (G_TagGametype()) //Check if you still have a game. Location flexible. =P - P_CheckSurvivors(); - else if (gametyperules & GTR_RACE) - P_CheckRacers(); -} - -void CL_Reset(void) -{ - if (metalrecording) - G_StopMetalRecording(false); - if (metalplayback) - G_StopMetalDemo(); - if (demorecording) - G_CheckDemoStatus(); - - // reset client/server code - DEBFILE(va("\n-=-=-=-=-=-=-= Client reset =-=-=-=-=-=-=-\n\n")); - - if (servernode > 0 && servernode < MAXNETNODES) - { - nodeingame[(UINT8)servernode] = false; - Net_CloseConnection(servernode); - } - D_CloseConnection(); // netgame = false - multiplayer = false; - servernode = 0; - server = true; - doomcom->numnodes = 1; - doomcom->numslots = 1; - SV_StopServer(); - SV_ResetServer(); - CV_RevertNetVars(); - - // make sure we don't leave any fileneeded gunk over from a failed join - fileneedednum = 0; - memset(fileneeded, 0, sizeof(fileneeded)); - - // D_StartTitle should get done now, but the calling function will handle it -} - -#ifndef NONET -static void Command_GetPlayerNum(void) -{ - INT32 i; - - for (i = 0; i < MAXPLAYERS; i++) - if (playeringame[i]) - { - if (serverplayer == i) - CONS_Printf(M_GetText("num:%2d node:%2d %s\n"), i, playernode[i], player_names[i]); - else - CONS_Printf(M_GetText("\x82num:%2d node:%2d %s\n"), i, playernode[i], player_names[i]); - } -} - -SINT8 nametonum(const char *name) -{ - INT32 playernum, i; - - if (!strcmp(name, "0")) + if (!skin) return 0; - playernum = (SINT8)atoi(name); + if ((playersprite_t)(spr2 & ~FF_SPR2SUPER) >= free_spr2) + return 0; - if (playernum < 0 || playernum >= MAXPLAYERS) - return -1; - - if (playernum) + while (!skin->sprites[spr2].numframes + && spr2 != SPR2_STND + && ++i < 32) // recursion limiter { - if (playeringame[playernum]) - return (SINT8)playernum; - else - return -1; + 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; } - for (i = 0; i < MAXPLAYERS; i++) - if (playeringame[i] && !stricmp(player_names[i], name)) - return (SINT8)i; + if (i >= 32) // probably an infinite loop... + return 0; - CONS_Printf(M_GetText("There is no player named \"%s\"\n"), name); + 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. + || players[playernum].bot //Force (player is a bot) + ); +} + +// 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; } -/** Lists all players and their player numbers. - * - * \sa Command_GetPlayerNum - */ -static void Command_Nodes(void) +// network code calls this when a 'skin change' is received +void SetPlayerSkin(INT32 playernum, const char *skinname) { - INT32 i; - size_t maxlen = 0; - const char *address; + INT32 i = R_SkinAvailable(skinname); + player_t *player = &players[playernum]; - for (i = 0; i < MAXPLAYERS; i++) + if ((i != -1) && R_SkinUsable(playernum, i)) { - const size_t plen = strlen(player_names[i]); - if (playeringame[i] && plen > maxlen) - maxlen = plen; + SetPlayerSkinByNum(playernum, i); + return; } - for (i = 0; i < MAXPLAYERS; i++) - { - if (playeringame[i]) - { - CONS_Printf("%.2u: %*s", i, (int)maxlen, player_names[i]); + 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); - if (playernode[i] != UINT8_MAX) - { - CONS_Printf(" - node %.2d", playernode[i]); - if (I_GetNodeAddress && (address = I_GetNodeAddress(playernode[i])) != NULL) - CONS_Printf(" - %s", address); - } - - if (IsPlayerAdmin(i)) - CONS_Printf(M_GetText(" (verified admin)")); - - if (players[i].spectator) - CONS_Printf(M_GetText(" (spectator)")); - - CONS_Printf("\n"); - } - } + SetPlayerSkinByNum(playernum, 0); } -static void Command_Ban(void) +// Same as SetPlayerSkin, but uses the skin #. +// network code calls this when a 'skin change' is received +void SetPlayerSkinByNum(INT32 playernum, INT32 skinnum) { - if (COM_Argc() < 2) + player_t *player = &players[playernum]; + skin_t *skin = &skins[skinnum]; + UINT16 newcolor = 0; + + if (skinnum >= 0 && skinnum < numskins && R_SkinUsable(playernum, skinnum)) // Make sure it exists! { - CONS_Printf(M_GetText("Ban : ban and kick a player\n")); - return; - } + player->skin = skinnum; - if (!netgame) // Don't kick Tails in splitscreen! - { - CONS_Printf(M_GetText("This only works in a netgame.\n")); - return; - } + player->camerascale = skin->camerascale; + player->shieldscale = skin->shieldscale; - if (server || IsPlayerAdmin(consoleplayer)) - { - UINT8 buf[3 + MAX_REASONLENGTH]; - UINT8 *p = buf; - const SINT8 pn = nametonum(COM_Argv(1)); - const INT32 node = playernode[(INT32)pn]; + player->charability = (UINT8)skin->ability; + player->charability2 = (UINT8)skin->ability2; - if (pn == -1 || pn == 0) - return; + player->charflags = (UINT32)skin->flags; - WRITEUINT8(p, pn); + 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 (server && I_Ban && !I_Ban(node)) // only the server is allowed to do this right now + 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)) { - CONS_Alert(CONS_WARNING, M_GetText("Too many bans! Geez, that's a lot of people you're excluding...\n")); - WRITEUINT8(p, KICK_MSG_GO_AWAY); - SendNetXCmd(XD_KICK, &buf, 2); + 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->bot && botingame) + { + botskin = (UINT8)(skinnum + 1); + botcolor = skin->prefcolor; + } } - else + + if (player->followmobj) { - if (server) // only the server is allowed to do this right now + 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. { - Ban_Add(COM_Argv(2)); - D_SaveBan(); // save the ban list + 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(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(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)) \ +{ \ + UINT16 color = R_GetColorByName(value); \ + skin->field = (color ? color : SKINCOLOR_GREEN); \ +} + GETSKINCOLOR(prefcolor) + GETSKINCOLOR(prefoppositecolor) +#undef GETSKINCOLOR + else if (!stricmp(stoken, "supercolor")) + { + UINT16 color = R_GetSuperColorByName(value); + skin->supercolor = (color ? color : SKINCOLOR_SUPERGOLD1); + } + +#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) + GETFLAG(NONIGHTSSUPER) +#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 } - if (COM_Argc() == 2) - { - WRITEUINT8(p, KICK_MSG_BANNED); - SendNetXCmd(XD_KICK, &buf, 2); - } - else - { - size_t i, j = COM_Argc(); - char message[MAX_REASONLENGTH]; + value = strtok(NULL, "\r\n= "); - //Steal from the motd code so you don't have to put the reason in quotes. - strlcpy(message, COM_Argv(2), sizeof message); - for (i = 3; i < j; i++) + 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 { - strlcat(message, " ", sizeof message); - strlcat(message, COM_Argv(i), sizeof message); + 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); } - WRITEUINT8(p, KICK_MSG_CUSTOM_BAN); - WRITESTRINGN(p, message, MAX_REASONLENGTH); - SendNetXCmd(XD_KICK, &buf, p - buf); - } - } - } - else - CONS_Printf(M_GetText("Only the server or a remote admin can use this.\n")); - -} - -static void Command_BanIP(void) -{ - if (COM_Argc() < 2) - { - CONS_Printf(M_GetText("banip : ban an ip address\n")); - return; - } - - if (server) // Only the server can use this, otherwise does nothing. - { - const char *address = (COM_Argv(1)); - const char *reason; - - if (COM_Argc() == 2) - reason = NULL; - else - reason = COM_Argv(2); - - - if (I_SetBanAddress && I_SetBanAddress(address, NULL)) - { - if (reason) - CONS_Printf("Banned IP address %s for: %s\n", address, reason); - else - CONS_Printf("Banned IP address %s\n", address); - - Ban_Add(reason); - D_SaveBan(); - } - else - { - return; - } - } -} - -static void Command_Kick(void) -{ - if (COM_Argc() < 2) - { - CONS_Printf(M_GetText("kick : kick a player\n")); - return; - } - - //if (!netgame) // Don't kick Tails in splitscreen! - //{ - // CONS_Printf(M_GetText("This only works in a netgame.\n")); - // return; - //} - - if (server || IsPlayerAdmin(consoleplayer)) - { - UINT8 buf[3 + MAX_REASONLENGTH]; - UINT8 *p = buf; - const SINT8 pn = nametonum(COM_Argv(1)); - - if (splitscreen && (pn == 0 || pn == 1)) - { - CONS_Printf(M_GetText("Splitscreen players cannot be kicked.\n")); - return; - } - if (pn == -1 || pn == 0) - return; - - // 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]]) - { - Net_ConnectionTimeout(playernode[pn]); - return; - } - - WRITESINT8(p, pn); - - if (COM_Argc() == 2) - { - WRITEUINT8(p, KICK_MSG_GO_AWAY); - SendNetXCmd(XD_KICK, &buf, 2); - } - else - { - size_t i, j = COM_Argc(); - char message[MAX_REASONLENGTH]; - - //Steal from the motd code so you don't have to put the reason in quotes. - strlcpy(message, COM_Argv(2), sizeof message); - for (i = 3; i < j; i++) - { - strlcat(message, " ", sizeof message); - strlcat(message, COM_Argv(i), sizeof message); - } - - WRITEUINT8(p, KICK_MSG_CUSTOM_KICK); - WRITESTRINGN(p, message, MAX_REASONLENGTH); - SendNetXCmd(XD_KICK, &buf, p - buf); - } - } - else - CONS_Printf(M_GetText("Only the server or a remote admin can use this.\n")); -} -#endif - -static void Got_KickCmd(UINT8 **p, INT32 playernum) -{ - INT32 pnum, msg; - char buf[3 + MAX_REASONLENGTH]; - char *reason = buf; - kickreason_t kickreason = KR_KICK; - boolean keepbody; - - pnum = READUINT8(*p); - msg = READUINT8(*p); - keepbody = (msg & KICK_MSG_KEEP_BODY) != 0; - msg &= ~KICK_MSG_KEEP_BODY; - - if (pnum == serverplayer && IsPlayerAdmin(playernum)) - { - CONS_Printf(M_GetText("Server is being shut down remotely. Goodbye!\n")); - - if (server) - COM_BufAddText("quit\n"); - - return; - } - - // Is playernum authorized to make this kick? - if (playernum != serverplayer && !IsPlayerAdmin(playernum) - && !(playernode[playernum] != UINT8_MAX && playerpernode[playernode[playernum]] == 2 - && nodetoplayer2[playernode[playernum]] == pnum)) - { - // We received a kick command from someone who isn't the - // server or admin, and who isn't in splitscreen removing - // player 2. Thus, it must be someone with a modified - // binary, trying to kick someone but without having - // authorization. - - // We deal with this by changing the kick reason to - // "consistency failure" and kicking the offending user - // instead. - - // Note: Splitscreen in netgames is broken because of - // this. Only the server has any idea of which players - // are using splitscreen on the same computer, so - // clients cannot always determine if a kick is - // legitimate. - - CONS_Alert(CONS_WARNING, M_GetText("Illegal kick command received from %s for player %d\n"), player_names[playernum], pnum); - - // In debug, print a longer message with more details. - // TODO Callum: Should we translate this? -/* - CONS_Debug(DBG_NETPLAY, - "So, you must be asking, why is this an illegal kick?\n" - "Well, let's take a look at the facts, shall we?\n" - "\n" - "playernum (this is the guy who did it), he's %d.\n" - "pnum (the guy he's trying to kick) is %d.\n" - "playernum's node is %d.\n" - "That node has %d players.\n" - "Player 2 on that node is %d.\n" - "pnum's node is %d.\n" - "That node has %d players.\n" - "Player 2 on that node is %d.\n" - "\n" - "If you think this is a bug, please report it, including all of the details above.\n", - playernum, pnum, - playernode[playernum], playerpernode[playernode[playernum]], - nodetoplayer2[playernode[playernum]], - playernode[pnum], playerpernode[playernode[pnum]], - nodetoplayer2[playernode[pnum]]); -*/ - pnum = playernum; - msg = KICK_MSG_CON_FAIL; - keepbody = true; - } - - //CONS_Printf("\x82%s ", player_names[pnum]); - - // If a verified admin banned someone, the server needs to know about it. - // If the playernum isn't zero (the server) then the server needs to record the ban. - if (server && playernum && (msg == KICK_MSG_BANNED || msg == KICK_MSG_CUSTOM_BAN)) - { - if (I_Ban && !I_Ban(playernode[(INT32)pnum])) - CONS_Alert(CONS_WARNING, M_GetText("Too many bans! Geez, that's a lot of people you're excluding...\n")); -#ifndef NONET - else - Ban_Add(reason); -#endif - } - - switch (msg) - { - case KICK_MSG_GO_AWAY: - if (!players[pnum].quittime) - HU_AddChatText(va("\x82*%s has been kicked (Go away)", player_names[pnum]), false); - kickreason = KR_KICK; - break; - case KICK_MSG_PING_HIGH: - HU_AddChatText(va("\x82*%s left the game (Broke ping limit)", player_names[pnum]), false); - kickreason = KR_PINGLIMIT; - break; - case KICK_MSG_CON_FAIL: - HU_AddChatText(va("\x82*%s left the game (Synch Failure)", player_names[pnum]), false); - kickreason = KR_SYNCH; - - if (M_CheckParm("-consisdump")) // Helps debugging some problems - { - INT32 i; - - CONS_Printf(M_GetText("Player kicked is #%d, dumping consistency...\n"), pnum); - - for (i = 0; i < MAXPLAYERS; i++) + // copy to hudname and fullname as a default. + if (!realname) { - if (!playeringame[i]) - continue; - CONS_Printf("-------------------------------------\n"); - CONS_Printf("Player %d: %s\n", i, player_names[i]); - CONS_Printf("Skin: %d\n", players[i].skin); - CONS_Printf("Color: %d\n", players[i].skincolor); - CONS_Printf("Speed: %d\n",players[i].speed>>FRACBITS); - if (players[i].mo) + STRBUFCPY(skin->realname, skin->name); + for (value = skin->realname; *value; value++) { - if (!players[i].mo->skin) - CONS_Printf("Mobj skin: NULL!\n"); - else - CONS_Printf("Mobj skin: %s\n", ((skin_t *)players[i].mo->skin)->name); - CONS_Printf("Position: %d, %d, %d\n", players[i].mo->x, players[i].mo->y, players[i].mo->z); - if (!players[i].mo->state) - CONS_Printf("State: S_NULL\n"); - else - CONS_Printf("State: %d\n", (statenum_t)(players[i].mo->state-states)); + 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_Printf("Mobj: NULL\n"); - CONS_Printf("-------------------------------------\n"); - } - } - break; - case KICK_MSG_TIMEOUT: - HU_AddChatText(va("\x82*%s left the game (Connection timeout)", player_names[pnum]), false); - kickreason = KR_TIMEOUT; - break; - case KICK_MSG_PLAYER_QUIT: - if (netgame && !players[pnum].quittime) // not splitscreen/bots or soulless body - HU_AddChatText(va("\x82*%s left the game", player_names[pnum]), false); - kickreason = KR_LEAVE; - break; - case KICK_MSG_BANNED: - HU_AddChatText(va("\x82*%s has been banned (Don't come back)", player_names[pnum]), false); - kickreason = KR_BAN; - break; - case KICK_MSG_CUSTOM_KICK: - READSTRINGN(*p, reason, MAX_REASONLENGTH+1); - HU_AddChatText(va("\x82*%s has been kicked (%s)", player_names[pnum], reason), false); - kickreason = KR_KICK; - break; - case KICK_MSG_CUSTOM_BAN: - READSTRINGN(*p, reason, MAX_REASONLENGTH+1); - HU_AddChatText(va("\x82*%s has been banned (%s)", player_names[pnum], reason), false); - kickreason = KR_BAN; - break; - } - - if (pnum == consoleplayer) - { - if (Playing()) - LUAh_GameQuit(); -#ifdef DUMPCONSISTENCY - if (msg == KICK_MSG_CON_FAIL) SV_SavedGame(); -#endif - D_QuitNetGame(); - CL_Reset(); - D_StartTitle(); - if (msg == KICK_MSG_CON_FAIL) - M_StartMessage(M_GetText("Server closed connection\n(synch failure)\nPress ESC\n"), NULL, MM_NOTHING); - else if (msg == KICK_MSG_PING_HIGH) - M_StartMessage(M_GetText("Server closed connection\n(Broke ping limit)\nPress ESC\n"), NULL, MM_NOTHING); - else if (msg == KICK_MSG_BANNED) - M_StartMessage(M_GetText("You have been banned by the server\n\nPress ESC\n"), NULL, MM_NOTHING); - else if (msg == KICK_MSG_CUSTOM_KICK) - M_StartMessage(va(M_GetText("You have been kicked\n(%s)\nPress ESC\n"), reason), NULL, MM_NOTHING); - else if (msg == KICK_MSG_CUSTOM_BAN) - M_StartMessage(va(M_GetText("You have been banned\n(%s)\nPress ESC\n"), reason), NULL, MM_NOTHING); - else - M_StartMessage(M_GetText("You have been kicked by the server\n\nPress ESC\n"), NULL, MM_NOTHING); - } - else if (keepbody) - { - if (server && !demoplayback && playernode[pnum] != UINT8_MAX) - { - INT32 node = playernode[pnum]; - playerpernode[node]--; - if (playerpernode[node] <= 0) - { - nodeingame[node] = false; - Net_CloseConnection(node); - ResetNode(node); - } - } - - playernode[pnum] = UINT8_MAX; - - players[pnum].quittime = 1; - } - else - CL_RemovePlayer(pnum, kickreason); -} - -static void Command_ResendGamestate(void) -{ - SINT8 playernum; - - if (COM_Argc() == 1) - { - CONS_Printf(M_GetText("resendgamestate : resend the game state to a player\n")); - return; - } - else if (client) - { - CONS_Printf(M_GetText("Only the server can use this.\n")); - return; - } - - playernum = nametonum(COM_Argv(1)); - if (playernum == -1 || playernum == 0) - return; - - // Send a PT_WILLRESENDGAMESTATE packet to the client so they know what's going on - netbuffer->packettype = PT_WILLRESENDGAMESTATE; - if (!HSendPacket(playernode[playernum], true, 0, 0)) - { - CONS_Alert(CONS_ERROR, M_GetText("A problem occured, please try again.\n")); - return; - } -} - -static CV_PossibleValue_t netticbuffer_cons_t[] = {{0, "MIN"}, {3, "MAX"}, {0, NULL}}; -consvar_t cv_netticbuffer = CVAR_INIT ("netticbuffer", "1", CV_SAVE, netticbuffer_cons_t, NULL); - -consvar_t cv_allownewplayer = CVAR_INIT ("allowjoin", "On", CV_SAVE|CV_NETVAR, CV_OnOff, NULL); -consvar_t cv_joinnextround = CVAR_INIT ("joinnextround", "Off", CV_SAVE|CV_NETVAR, CV_OnOff, NULL); /// \todo not done -static CV_PossibleValue_t maxplayers_cons_t[] = {{2, "MIN"}, {32, "MAX"}, {0, NULL}}; -consvar_t cv_maxplayers = CVAR_INIT ("maxplayers", "8", CV_SAVE|CV_NETVAR, maxplayers_cons_t, NULL); -static CV_PossibleValue_t joindelay_cons_t[] = {{1, "MIN"}, {3600, "MAX"}, {0, "Off"}, {0, NULL}}; -consvar_t cv_joindelay = CVAR_INIT ("joindelay", "10", CV_SAVE|CV_NETVAR, joindelay_cons_t, NULL); -static CV_PossibleValue_t rejointimeout_cons_t[] = {{1, "MIN"}, {60 * FRACUNIT, "MAX"}, {0, "Off"}, {0, NULL}}; -consvar_t cv_rejointimeout = CVAR_INIT ("rejointimeout", "Off", CV_SAVE|CV_NETVAR|CV_FLOAT, rejointimeout_cons_t, NULL); - -static CV_PossibleValue_t resynchattempts_cons_t[] = {{1, "MIN"}, {20, "MAX"}, {0, "No"}, {0, NULL}}; -consvar_t cv_resynchattempts = CVAR_INIT ("resynchattempts", "10", CV_SAVE|CV_NETVAR, resynchattempts_cons_t, NULL); -consvar_t cv_blamecfail = CVAR_INIT ("blamecfail", "Off", CV_SAVE|CV_NETVAR, CV_OnOff, NULL); - -// max file size to send to a player (in kilobytes) -static CV_PossibleValue_t maxsend_cons_t[] = {{0, "MIN"}, {51200, "MAX"}, {0, NULL}}; -consvar_t cv_maxsend = CVAR_INIT ("maxsend", "4096", CV_SAVE|CV_NETVAR, maxsend_cons_t, NULL); -consvar_t cv_noticedownload = CVAR_INIT ("noticedownload", "Off", CV_SAVE|CV_NETVAR, CV_OnOff, NULL); - -// Speed of file downloading (in packets per tic) -static CV_PossibleValue_t downloadspeed_cons_t[] = {{0, "MIN"}, {32, "MAX"}, {0, NULL}}; -consvar_t cv_downloadspeed = CVAR_INIT ("downloadspeed", "16", CV_SAVE|CV_NETVAR, downloadspeed_cons_t, NULL); - -static void Got_AddPlayer(UINT8 **p, INT32 playernum); - -// called one time at init -void D_ClientServerInit(void) -{ - DEBFILE(va("- - -== SRB2 v%d.%.2d.%d "VERSIONSTRING" debugfile ==- - -\n", - VERSION/100, VERSION%100, SUBVERSION)); - -#ifndef NONET - COM_AddCommand("getplayernum", Command_GetPlayerNum); - COM_AddCommand("kick", Command_Kick); - COM_AddCommand("ban", Command_Ban); - COM_AddCommand("banip", Command_BanIP); - COM_AddCommand("clearbans", Command_ClearBans); - COM_AddCommand("showbanlist", Command_ShowBan); - COM_AddCommand("reloadbans", Command_ReloadBan); - COM_AddCommand("connect", Command_connect); - COM_AddCommand("nodes", Command_Nodes); - COM_AddCommand("resendgamestate", Command_ResendGamestate); -#ifdef PACKETDROP - COM_AddCommand("drop", Command_Drop); - COM_AddCommand("droprate", Command_Droprate); -#endif -#ifdef _DEBUG - COM_AddCommand("numnodes", Command_Numnodes); -#endif -#endif - - RegisterNetXCmd(XD_KICK, Got_KickCmd); - RegisterNetXCmd(XD_ADDPLAYER, Got_AddPlayer); -#ifndef NONET -#ifdef DUMPCONSISTENCY - CV_RegisterVar(&cv_dumpconsistency); -#endif - Ban_Load_File(false); -#endif - - gametic = 0; - localgametic = 0; - - // do not send anything before the real begin - SV_StopServer(); - SV_ResetServer(); - if (dedicated) - SV_SpawnServer(); -} - -static void ResetNode(INT32 node) -{ - nodeingame[node] = false; - nodewaiting[node] = 0; - - nettics[node] = gametic; - supposedtics[node] = gametic; - - nodetoplayer[node] = -1; - nodetoplayer2[node] = -1; - playerpernode[node] = 0; - - sendingsavegame[node] = false; - resendingsavegame[node] = false; - savegameresendcooldown[node] = 0; -} - -void SV_ResetServer(void) -{ - INT32 i; - - // +1 because this command will be executed in com_executebuffer in - // tryruntic so gametic will be incremented, anyway maketic > gametic - // is not an issue - - maketic = gametic + 1; - neededtic = maketic; - tictoclear = maketic; - - joindelay = 0; - - for (i = 0; i < MAXNETNODES; i++) - ResetNode(i); - - for (i = 0; i < MAXPLAYERS; i++) - { - LUA_InvalidatePlayer(&players[i]); - playeringame[i] = false; - playernode[i] = UINT8_MAX; - memset(playeraddress[i], 0, sizeof(*playeraddress)); - sprintf(player_names[i], "Player %d", i + 1); - adminplayers[i] = -1; // Populate the entire adminplayers array with -1. - } - - memset(player_name_changes, 0, sizeof player_name_changes); - - mynode = 0; - cl_packetmissed = false; - cl_redownloadinggamestate = false; - - if (dedicated) - { - nodeingame[0] = true; - serverplayer = 0; - } - else - serverplayer = consoleplayer; - - if (server) - servernode = 0; - - doomcom->numslots = 0; - - // clear server_context - memset(server_context, '-', 8); - - DEBFILE("\n-=-=-=-=-=-=-= Server Reset =-=-=-=-=-=-=-\n\n"); -} - -static inline void SV_GenContext(void) -{ - UINT8 i; - // generate server_context, as exactly 8 bytes of randomly mixed A-Z and a-z - // (hopefully M_Random is initialized!! if not this will be awfully silly!) - for (i = 0; i < 8; i++) - { - const char a = M_RandomKey(26*2); - if (a < 26) // uppercase - server_context[i] = 'A'+a; - else // lowercase - server_context[i] = 'a'+(a-26); - } -} - -// -// D_QuitNetGame -// Called before quitting to leave a net game -// without hanging the other players -// -void D_QuitNetGame(void) -{ - if (!netgame || !netbuffer) - return; - - DEBFILE("===========================================================================\n" - " Quitting Game, closing connection\n" - "===========================================================================\n"); - - // abort send/receive of files - CloseNetFile(); - RemoveAllLuaFileTransfers(); - waitingforluafiletransfer = false; - waitingforluafilecommand = false; - - if (server) - { - INT32 i; - - netbuffer->packettype = PT_SERVERSHUTDOWN; - for (i = 0; i < MAXNETNODES; i++) - if (nodeingame[i]) - HSendPacket(i, true, 0, 0); -#ifdef MASTERSERVER - if (serverrunning && ms_RoomId > 0) - UnregisterServer(); -#endif - } - else if (servernode > 0 && servernode < MAXNETNODES && nodeingame[(UINT8)servernode]) - { - netbuffer->packettype = PT_CLIENTQUIT; - HSendPacket(servernode, true, 0, 0); - } - - D_CloseConnection(); - ClearAdminPlayers(); - - DEBFILE("===========================================================================\n" - " Log finish\n" - "===========================================================================\n"); -#ifdef DEBUGFILE - if (debugfile) - { - fclose(debugfile); - debugfile = NULL; - } -#endif -} - -// Adds a node to the game (player will follow at map change or at savegame....) -static inline void SV_AddNode(INT32 node) -{ - nettics[node] = gametic; - supposedtics[node] = gametic; - // little hack because the server connects to itself and puts - // nodeingame when connected not here - if (node) - nodeingame[node] = true; -} - -// Xcmd XD_ADDPLAYER -static void Got_AddPlayer(UINT8 **p, INT32 playernum) -{ - INT16 node, newplayernum; - boolean splitscreenplayer; - boolean rejoined; - player_t *newplayer; - - if (playernum != serverplayer && !IsPlayerAdmin(playernum)) - { - // protect against hacked/buggy client - CONS_Alert(CONS_WARNING, M_GetText("Illegal add player command received from %s\n"), player_names[playernum]); - if (server) - SendKick(playernum, KICK_MSG_CON_FAIL | KICK_MSG_KEEP_BODY); - return; - } - - node = READUINT8(*p); - newplayernum = READUINT8(*p); - splitscreenplayer = newplayernum & 0x80; - newplayernum &= ~0x80; - - rejoined = playeringame[newplayernum]; - - if (!rejoined) - { - // Clear player before joining, lest some things get set incorrectly - // HACK: don't do this for splitscreen, it relies on preset values - if (!splitscreen && !botingame) - CL_ClearPlayer(newplayernum); - playeringame[newplayernum] = true; - G_AddPlayer(newplayernum); - if (newplayernum+1 > doomcom->numslots) - doomcom->numslots = (INT16)(newplayernum+1); - - if (server && I_GetNodeAddress) - { - const char *address = I_GetNodeAddress(node); - char *port = NULL; - if (address) // MI: fix msvcrt.dll!_mbscat crash? - { - strcpy(playeraddress[newplayernum], address); - port = strchr(playeraddress[newplayernum], ':'); - if (port) - *port = '\0'; - } - } - } - - newplayer = &players[newplayernum]; - - newplayer->jointime = 0; - newplayer->quittime = 0; - - READSTRINGN(*p, player_names[newplayernum], MAXPLAYERNAME); - - // the server is creating my player - if (node == mynode) - { - playernode[newplayernum] = 0; // for information only - if (!splitscreenplayer) - { - consoleplayer = newplayernum; - displayplayer = newplayernum; - secondarydisplayplayer = newplayernum; - DEBFILE("spawning me\n"); - ticcmd_oldangleturn[0] = newplayer->oldrelangleturn; - } - else - { - secondarydisplayplayer = newplayernum; - DEBFILE("spawning my brother\n"); - if (botingame) - newplayer->bot = 1; - ticcmd_oldangleturn[1] = newplayer->oldrelangleturn; - } - P_ForceLocalAngle(newplayer, (angle_t)(newplayer->angleturn << 16)); - D_SendPlayerConfig(); - addedtogame = true; - - if (rejoined) - { - if (newplayer->mo) - { - newplayer->viewheight = 41*newplayer->height/48; - - if (newplayer->mo->eflags & MFE_VERTICALFLIP) - newplayer->viewz = newplayer->mo->z + newplayer->mo->height - newplayer->viewheight; - else - newplayer->viewz = newplayer->mo->z + newplayer->viewheight; - } - - // wake up the status bar - ST_Start(); - // wake up the heads up text - HU_Start(); - - if (camera.chase && !splitscreenplayer) - P_ResetCamera(newplayer, &camera); - if (camera2.chase && splitscreenplayer) - P_ResetCamera(newplayer, &camera2); - } - } - - if (netgame) - { - char joinmsg[256]; - - if (rejoined) - strcpy(joinmsg, M_GetText("\x82*%s has rejoined the game (player %d)")); - else - strcpy(joinmsg, M_GetText("\x82*%s has joined the game (player %d)")); - strcpy(joinmsg, va(joinmsg, player_names[newplayernum], newplayernum)); - - // Merge join notification + IP to avoid clogging console/chat - if (server && cv_showjoinaddress.value && I_GetNodeAddress) - { - const char *address = I_GetNodeAddress(node); - if (address) - strcat(joinmsg, va(" (%s)", address)); - } - - HU_AddChatText(joinmsg, false); - } - - if (server && multiplayer && motd[0] != '\0') - COM_BufAddText(va("sayto %d %s\n", newplayernum, motd)); - - if (!rejoined) - LUAh_PlayerJoin(newplayernum); -} - -static boolean SV_AddWaitingPlayers(const char *name, const char *name2) -{ - INT32 node, n, newplayer = false; - UINT8 buf[2 + MAXPLAYERNAME]; - UINT8 *p; - INT32 newplayernum; - - for (node = 0; node < MAXNETNODES; node++) - { - // splitscreen can allow 2 player in one node - for (; nodewaiting[node] > 0; nodewaiting[node]--) - { - newplayer = true; - - newplayernum = FindRejoinerNum(node); - if (newplayernum == -1) - { - // search for a free playernum - // we can't use playeringame since it is not updated here - for (newplayernum = dedicated ? 1 : 0; newplayernum < MAXPLAYERS; newplayernum++) - { - if (playeringame[newplayernum]) - continue; - for (n = 0; n < MAXNETNODES; n++) - if (nodetoplayer[n] == newplayernum || nodetoplayer2[n] == newplayernum) - break; - if (n == MAXNETNODES) - break; - } - } - - // should never happen since we check the playernum - // before accepting the join - I_Assert(newplayernum < MAXPLAYERS); - - playernode[newplayernum] = (UINT8)node; - - p = buf + 2; - buf[0] = (UINT8)node; - buf[1] = newplayernum; - if (playerpernode[node] < 1) - { - nodetoplayer[node] = newplayernum; - WRITESTRINGN(p, name, MAXPLAYERNAME); - } - else - { - nodetoplayer2[node] = newplayernum; - buf[1] |= 0x80; - WRITESTRINGN(p, name2, MAXPLAYERNAME); - } - playerpernode[node]++; - - SendNetXCmd(XD_ADDPLAYER, &buf, p - buf); - - DEBFILE(va("Server added player %d node %d\n", newplayernum, node)); - } - } - - return newplayer; -} - -void CL_AddSplitscreenPlayer(void) -{ - if (cl_mode == CL_CONNECTED) - CL_SendJoin(); -} - -void CL_RemoveSplitscreenPlayer(void) -{ - if (cl_mode != CL_CONNECTED) - return; - - SendKick(secondarydisplayplayer, KICK_MSG_PLAYER_QUIT); -} - -// is there a game running -boolean Playing(void) -{ - return (server && serverrunning) || (client && cl_mode == CL_CONNECTED); -} - -boolean SV_SpawnServer(void) -{ - if (demoplayback) - G_StopDemo(); // reset engine parameter - if (metalplayback) - G_StopMetalDemo(); - - if (!serverrunning) - { - CONS_Printf(M_GetText("Starting Server....\n")); - serverrunning = true; - SV_ResetServer(); - SV_GenContext(); - if (netgame && I_NetOpenSocket) - { - I_NetOpenSocket(); -#ifdef MASTERSERVER - if (ms_RoomId > 0) - RegisterServer(); -#endif - } - - // non dedicated server just connect to itself - if (!dedicated) - CL_ConnectToServer(); - else doomcom->numslots = 1; - } - - return SV_AddWaitingPlayers(cv_playername.zstring, cv_playername2.zstring); -} - -void SV_StopServer(void) -{ - tic_t i; - - if (gamestate == GS_INTERMISSION) - Y_EndIntermission(); - gamestate = wipegamestate = GS_NULL; - - localtextcmd[0] = 0; - localtextcmd2[0] = 0; - - for (i = firstticstosend; i < firstticstosend + BACKUPTICS; i++) - D_Clearticcmd(i); - - consoleplayer = 0; - cl_mode = CL_SEARCHING; - maketic = gametic+1; - neededtic = maketic; - serverrunning = false; -} - -// called at singleplayer start and stopdemo -void SV_StartSinglePlayerServer(void) -{ - server = true; - netgame = false; - multiplayer = false; - G_SetGametype(GT_COOP); - - // no more tic the game with this settings! - SV_StopServer(); - - if (splitscreen) - multiplayer = true; -} - -static void SV_SendRefuse(INT32 node, const char *reason) -{ - strcpy(netbuffer->u.serverrefuse.reason, reason); - - netbuffer->packettype = PT_SERVERREFUSE; - HSendPacket(node, true, 0, strlen(netbuffer->u.serverrefuse.reason) + 1); - Net_CloseConnection(node); -} - -// used at txtcmds received to check packetsize bound -static size_t TotalTextCmdPerTic(tic_t tic) -{ - INT32 i; - size_t total = 1; // num of textcmds in the tic (ntextcmd byte) - - for (i = 0; i < MAXPLAYERS; i++) - { - UINT8 *textcmd = D_GetExistingTextcmd(tic, i); - if ((!i || playeringame[i]) && textcmd) - total += 2 + textcmd[0]; // "+2" for size and playernum - } - - return total; -} - -/** Called when a PT_CLIENTJOIN packet is received - * - * \param node The packet sender - * - */ -static void HandleConnect(SINT8 node) -{ - char names[MAXSPLITSCREENPLAYERS][MAXPLAYERNAME + 1]; - INT32 rejoinernum; - INT32 i; - - rejoinernum = FindRejoinerNum(node); - - if (bannednode && bannednode[node]) - SV_SendRefuse(node, M_GetText("You have been banned\nfrom the server.")); - else if (netbuffer->u.clientcfg._255 != 255 || - netbuffer->u.clientcfg.packetversion != PACKETVERSION) - SV_SendRefuse(node, "Incompatible packet formats."); - else if (strncmp(netbuffer->u.clientcfg.application, SRB2APPLICATION, - sizeof netbuffer->u.clientcfg.application)) - SV_SendRefuse(node, "Different SRB2 modifications\nare not compatible."); - else if (netbuffer->u.clientcfg.version != VERSION - || 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.")); - 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? - 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.")); - else if (luafiletransfers) - SV_SendRefuse(node, M_GetText("The server is broadcasting a file\nrequested by a Lua script.\nPlease wait a bit and then\ntry rejoining.")); - else if (netgame && joindelay > 2 * (tic_t)cv_joindelay.value * TICRATE) - SV_SendRefuse(node, va(M_GetText("Too many people are connecting.\nPlease wait %d seconds and then\ntry rejoining."), - (joindelay - 2 * cv_joindelay.value * TICRATE) / TICRATE)); - else - { -#ifndef NONET - boolean newnode = false; -#endif - - for (i = 0; i < netbuffer->u.clientcfg.localplayers - playerpernode[node]; i++) - { - strlcpy(names[i], netbuffer->u.clientcfg.names[i], MAXPLAYERNAME + 1); - if (!EnsurePlayerNameIsGood(names[i], rejoinernum)) - { - SV_SendRefuse(node, "Bad player name"); - return; - } - } - - // client authorised to join - nodewaiting[node] = (UINT8)(netbuffer->u.clientcfg.localplayers - playerpernode[node]); - if (!nodeingame[node]) - { - gamestate_t backupstate = gamestate; -#ifndef NONET - newnode = true; -#endif - SV_AddNode(node); - - if (cv_joinnextround.value && gameaction == ga_nothing) - G_SetGamestate(GS_WAITINGPLAYERS); - if (!SV_SendServerConfig(node)) - { - G_SetGamestate(backupstate); - /// \note Shouldn't SV_SendRefuse be called before ResetNode? - ResetNode(node); - SV_SendRefuse(node, M_GetText("Server couldn't send info, please try again")); - /// \todo fix this !!! - return; // restart the while - } - //if (gamestate != GS_LEVEL) // GS_INTERMISSION, etc? - // SV_SendPlayerConfigs(node); // send bare minimum player info - G_SetGamestate(backupstate); - DEBFILE("new node joined\n"); - } -#ifndef NONET - if (nodewaiting[node]) - { - if ((gamestate == GS_LEVEL || gamestate == GS_INTERMISSION) && newnode) - { - SV_SendSaveGame(node, false); // send a complete game state - DEBFILE("send savegame\n"); - } - SV_AddWaitingPlayers(names[0], names[1]); - joindelay += cv_joindelay.value * TICRATE; - player_joining = true; - } -#endif - } -} - -/** Called when a PT_SERVERSHUTDOWN packet is received - * - * \param node The packet sender (should be the server) - * - */ -static void HandleShutdown(SINT8 node) -{ - (void)node; - if (Playing()) - LUAh_GameQuit(); - D_QuitNetGame(); - CL_Reset(); - D_StartTitle(); - M_StartMessage(M_GetText("Server has shutdown\n\nPress Esc\n"), NULL, MM_NOTHING); -} - -/** Called when a PT_NODETIMEOUT packet is received - * - * \param node The packet sender (should be the server) - * - */ -static void HandleTimeout(SINT8 node) -{ - (void)node; - if (Playing()) - LUAh_GameQuit(); - D_QuitNetGame(); - CL_Reset(); - D_StartTitle(); - M_StartMessage(M_GetText("Server Timeout\n\nPress Esc\n"), NULL, MM_NOTHING); -} - -#ifndef NONET -/** Called when a PT_SERVERINFO packet is received - * - * \param node The packet sender - * \note What happens if the packet comes from a client or something like that? - * - */ -static void HandleServerInfo(SINT8 node) -{ - // compute ping in ms - const tic_t ticnow = I_GetTime(); - const tic_t ticthen = (tic_t)LONG(netbuffer->u.serverinfo.time); - const tic_t ticdiff = (ticnow - ticthen)*1000/NEWTICRATE; - netbuffer->u.serverinfo.time = (tic_t)LONG(ticdiff); - netbuffer->u.serverinfo.servername[MAXSERVERNAME-1] = 0; - netbuffer->u.serverinfo.application - [sizeof netbuffer->u.serverinfo.application - 1] = '\0'; - netbuffer->u.serverinfo.gametypename - [sizeof netbuffer->u.serverinfo.gametypename - 1] = '\0'; - - SL_InsertServer(&netbuffer->u.serverinfo, node); -} -#endif - -static void PT_WillResendGamestate(void) -{ - char tmpsave[256]; - - if (server || cl_redownloadinggamestate) - return; - - // Send back a PT_CANRECEIVEGAMESTATE packet to the server - // so they know they can start sending the game state - netbuffer->packettype = PT_CANRECEIVEGAMESTATE; - if (!HSendPacket(servernode, true, 0, 0)) - return; - - CONS_Printf(M_GetText("Reloading game state...\n")); - - sprintf(tmpsave, "%s" PATHSEP TMPSAVENAME, srb2home); - - // Don't get a corrupt savegame error because tmpsave already exists - if (FIL_FileExists(tmpsave) && unlink(tmpsave) == -1) - I_Error("Can't delete %s\n", tmpsave); - - CL_PrepareDownloadSaveGame(tmpsave); - - cl_redownloadinggamestate = true; -} - -static void PT_CanReceiveGamestate(SINT8 node) -{ - if (client || sendingsavegame[node]) - return; - - CONS_Printf(M_GetText("Resending game state to %s...\n"), player_names[nodetoplayer[node]]); - - SV_SendSaveGame(node, true); // Resend a complete game state - resendingsavegame[node] = true; -} - -/** Handles a packet received from a node that isn't in game - * - * \param node The packet sender - * \todo Choose a better name, as the packet can also come from the server apparently? - * \sa HandlePacketFromPlayer - * \sa GetPackets - * - */ -static void HandlePacketFromAwayNode(SINT8 node) -{ - if (node != servernode) - DEBFILE(va("Received packet from unknown host %d\n", node)); - -// macro for packets that should only be sent by the server -// if it is NOT from the server, bail out and close the connection! -#define SERVERONLY \ - if (node != servernode) \ - { \ - Net_CloseConnection(node); \ - break; \ - } - switch (netbuffer->packettype) - { - case PT_ASKINFOVIAMS: -#if 0 - if (server && serverrunning) - { - INT32 clientnode; - if (ms_RoomId < 0) // ignore if we're not actually on the MS right now - { - Net_CloseConnection(node); // and yes, close connection - return; - } - clientnode = I_NetMakeNode(netbuffer->u.msaskinfo.clientaddr); - if (clientnode != -1) - { - SV_SendServerInfo(clientnode, (tic_t)LONG(netbuffer->u.msaskinfo.time)); - SV_SendPlayerInfo(clientnode); // Send extra info - Net_CloseConnection(clientnode); - // Don't close connection to MS... - } - else - Net_CloseConnection(node); // ...unless the IP address is not valid - } - else - Net_CloseConnection(node); // you're not supposed to get it, so ignore it -#else - Net_CloseConnection(node); -#endif - break; - - case PT_ASKINFO: - if (server && serverrunning) - { - SV_SendServerInfo(node, (tic_t)LONG(netbuffer->u.askinfo.time)); - SV_SendPlayerInfo(node); // Send extra info - } - Net_CloseConnection(node); - break; - - case PT_SERVERREFUSE: // Negative response of client join request - if (server && serverrunning) - { // But wait I thought I'm the server? - Net_CloseConnection(node); - break; - } - SERVERONLY - if (cl_mode == CL_WAITJOINRESPONSE) - { - // Save the reason so it can be displayed after quitting the netgame - char *reason = strdup(netbuffer->u.serverrefuse.reason); - if (!reason) - I_Error("Out of memory!\n"); - - D_QuitNetGame(); - CL_Reset(); - D_StartTitle(); - - M_StartMessage(va(M_GetText("Server refuses connection\n\nReason:\n%s"), - reason), NULL, MM_NOTHING); - - free(reason); - - // Will be reset by caller. Signals refusal. - cl_mode = CL_ABORTED; - } - break; - - case PT_SERVERCFG: // Positive response of client join request - { - if (server && serverrunning && node != servernode) - { // but wait I thought I'm the server? - Net_CloseConnection(node); - break; - } - SERVERONLY - /// \note how would this happen? and is it doing the right thing if it does? - if (cl_mode != CL_WAITJOINRESPONSE) - break; - - if (client) - { - maketic = gametic = neededtic = (tic_t)LONG(netbuffer->u.servercfg.gametic); - G_SetGametype(netbuffer->u.servercfg.gametype); - modifiedgame = netbuffer->u.servercfg.modifiedgame; - memcpy(server_context, netbuffer->u.servercfg.server_context, 8); - } - - nodeingame[(UINT8)servernode] = true; - serverplayer = netbuffer->u.servercfg.serverplayer; - doomcom->numslots = SHORT(netbuffer->u.servercfg.totalslotnum); - mynode = netbuffer->u.servercfg.clientnode; - if (serverplayer >= 0) - playernode[(UINT8)serverplayer] = servernode; - - if (netgame) -#ifndef NONET - CONS_Printf(M_GetText("Join accepted, waiting for complete game state...\n")); -#else - CONS_Printf(M_GetText("Join accepted, waiting for next level change...\n")); -#endif - DEBFILE(va("Server accept join gametic=%u mynode=%d\n", gametic, mynode)); - -#ifndef NONET - /// \note Wait. What if a Lua script uses some global custom variables synched with the NetVars hook? - /// Shouldn't them be downloaded even at intermission time? - /// Also, according to HandleConnect, the server will send the savegame even during intermission... - if (netbuffer->u.servercfg.gamestate == GS_LEVEL/* || - netbuffer->u.servercfg.gamestate == GS_INTERMISSION*/) - cl_mode = CL_DOWNLOADSAVEGAME; - else -#endif - cl_mode = CL_CONNECTED; - break; - } - - // Handled in d_netfil.c - case PT_FILEFRAGMENT: - if (server) - { // But wait I thought I'm the server? - Net_CloseConnection(node); - break; - } - SERVERONLY - PT_FileFragment(); - break; - - case PT_FILEACK: - if (server) - PT_FileAck(); - break; - - case PT_FILERECEIVED: - if (server) - PT_FileReceived(); - break; - - case PT_REQUESTFILE: - if (server) - { - if (!cv_downloading.value || !PT_RequestFile(node)) - Net_CloseConnection(node); // close connection if one of the requested files could not be sent, or you disabled downloading anyway - } - else - Net_CloseConnection(node); // nope - break; - - case PT_NODETIMEOUT: - case PT_CLIENTQUIT: - if (server) - Net_CloseConnection(node); - break; - - case PT_CLIENTCMD: - break; // This is not an "unknown packet" - - case PT_SERVERTICS: - // Do not remove my own server (we have just get a out of order packet) - if (node == servernode) - break; - /* FALLTHRU */ - - default: - DEBFILE(va("unknown packet received (%d) from unknown host\n",netbuffer->packettype)); - Net_CloseConnection(node); - break; // Ignore it - - } -#undef SERVERONLY -} - -/** Handles a packet received from a node that is in game - * - * \param node The packet sender - * \todo Choose a better name - * \sa HandlePacketFromAwayNode - * \sa GetPackets - * - */ -static void HandlePacketFromPlayer(SINT8 node) -{ - INT32 netconsole; - tic_t realend, realstart; - UINT8 *pak, *txtpak, numtxtpak; -#ifndef NOMD5 - UINT8 finalmd5[16];/* Well, it's the cool thing to do? */ -#endif - - txtpak = NULL; - - if (dedicated && node == 0) - netconsole = 0; - else - netconsole = nodetoplayer[node]; -#ifdef PARANOIA - if (netconsole >= MAXPLAYERS) - I_Error("bad table nodetoplayer: node %d player %d", doomcom->remotenode, netconsole); -#endif - - switch (netbuffer->packettype) - { -// -------------------------------------------- SERVER RECEIVE ---------- - case PT_CLIENTCMD: - case PT_CLIENT2CMD: - case PT_CLIENTMIS: - case PT_CLIENT2MIS: - case PT_NODEKEEPALIVE: - case PT_NODEKEEPALIVEMIS: - if (client) - break; - - // To save bytes, only the low byte of tic numbers are sent - // Use ExpandTics to figure out what the rest of the bytes are - realstart = ExpandTics(netbuffer->u.clientpak.client_tic, node); - realend = ExpandTics(netbuffer->u.clientpak.resendfrom, node); - - if (netbuffer->packettype == PT_CLIENTMIS || netbuffer->packettype == PT_CLIENT2MIS - || netbuffer->packettype == PT_NODEKEEPALIVEMIS - || supposedtics[node] < realend) - { - supposedtics[node] = realend; - } - // Discard out of order packet - if (nettics[node] > realend) - { - DEBFILE(va("out of order ticcmd discarded nettics = %u\n", nettics[node])); - break; - } - - // Update the nettics - nettics[node] = realend; - - // Don't do anything for packets of type NODEKEEPALIVE? - if (netconsole == -1 || netbuffer->packettype == PT_NODEKEEPALIVE - || netbuffer->packettype == PT_NODEKEEPALIVEMIS) - break; - - // As long as clients send valid ticcmds, the server can keep running, so reset the timeout - /// \todo Use a separate cvar for that kind of timeout? - freezetimeout[node] = I_GetTime() + connectiontimeout; - - // Copy ticcmd - G_MoveTiccmd(&netcmds[maketic%BACKUPTICS][netconsole], &netbuffer->u.clientpak.cmd, 1); - - // Check ticcmd for "speed hacks" - if (netcmds[maketic%BACKUPTICS][netconsole].forwardmove > MAXPLMOVE || netcmds[maketic%BACKUPTICS][netconsole].forwardmove < -MAXPLMOVE - || netcmds[maketic%BACKUPTICS][netconsole].sidemove > MAXPLMOVE || netcmds[maketic%BACKUPTICS][netconsole].sidemove < -MAXPLMOVE) - { - CONS_Alert(CONS_WARNING, M_GetText("Illegal movement value received from node %d\n"), netconsole); - //D_Clearticcmd(k); - - SendKick(netconsole, KICK_MSG_CON_FAIL); - break; - } - - // Splitscreen cmd - if ((netbuffer->packettype == PT_CLIENT2CMD || netbuffer->packettype == PT_CLIENT2MIS) - && nodetoplayer2[node] >= 0) - G_MoveTiccmd(&netcmds[maketic%BACKUPTICS][(UINT8)nodetoplayer2[node]], - &netbuffer->u.client2pak.cmd2, 1); - - // Check player consistancy during the level - if (realstart <= gametic && realstart + BACKUPTICS - 1 > gametic && gamestate == GS_LEVEL - && consistancy[realstart%BACKUPTICS] != SHORT(netbuffer->u.clientpak.consistancy) - && !resendingsavegame[node] && savegameresendcooldown[node] <= I_GetTime() - && !SV_ResendingSavegameToAnyone()) - { - if (cv_resynchattempts.value) - { - // Tell the client we are about to resend them the gamestate - netbuffer->packettype = PT_WILLRESENDGAMESTATE; - HSendPacket(node, true, 0, 0); - - resendingsavegame[node] = true; - - if (cv_blamecfail.value) - CONS_Printf(M_GetText("Synch failure for player %d (%s); expected %hd, got %hd\n"), - netconsole+1, player_names[netconsole], - consistancy[realstart%BACKUPTICS], - SHORT(netbuffer->u.clientpak.consistancy)); - DEBFILE(va("Restoring player %d (synch failure) [%update] %d!=%d\n", - netconsole, realstart, consistancy[realstart%BACKUPTICS], - SHORT(netbuffer->u.clientpak.consistancy))); - break; - } - else - { - SendKick(netconsole, KICK_MSG_CON_FAIL | KICK_MSG_KEEP_BODY); - DEBFILE(va("player %d kicked (synch failure) [%u] %d!=%d\n", - netconsole, realstart, consistancy[realstart%BACKUPTICS], - SHORT(netbuffer->u.clientpak.consistancy))); - break; - } - } - break; - case PT_TEXTCMD2: // splitscreen special - netconsole = nodetoplayer2[node]; - /* FALLTHRU */ - case PT_TEXTCMD: - if (client) - break; - - if (netconsole < 0 || netconsole >= MAXPLAYERS) - Net_UnAcknowledgePacket(node); - else - { - size_t j; - tic_t tic = maketic; - UINT8 *textcmd; - - // ignore if the textcmd has a reported size of zero - // this shouldn't be sent at all - if (!netbuffer->u.textcmd[0]) - { - DEBFILE(va("GetPacket: Textcmd with size 0 detected! (node %u, player %d)\n", - node, netconsole)); - Net_UnAcknowledgePacket(node); - break; - } - - // ignore if the textcmd size var is actually larger than it should be - // BASEPACKETSIZE + 1 (for size) + textcmd[0] should == datalength - if (netbuffer->u.textcmd[0] > (size_t)doomcom->datalength-BASEPACKETSIZE-1) - { - DEBFILE(va("GetPacket: Bad Textcmd packet size! (expected %d, actual %s, node %u, player %d)\n", - netbuffer->u.textcmd[0], sizeu1((size_t)doomcom->datalength-BASEPACKETSIZE-1), - node, netconsole)); - Net_UnAcknowledgePacket(node); - break; - } - - // check if tic that we are making isn't too large else we cannot send it :( - // doomcom->numslots+1 "+1" since doomcom->numslots can change within this time and sent time - j = software_MAXPACKETLENGTH - - (netbuffer->u.textcmd[0]+2+BASESERVERTICSSIZE - + (doomcom->numslots+1)*sizeof(ticcmd_t)); - - // search a tic that have enougth space in the ticcmd - while ((textcmd = D_GetExistingTextcmd(tic, netconsole)), - (TotalTextCmdPerTic(tic) > j || netbuffer->u.textcmd[0] + (textcmd ? textcmd[0] : 0) > MAXTEXTCMD) - && tic < firstticstosend + BACKUPTICS) - tic++; - - if (tic >= firstticstosend + BACKUPTICS) - { - DEBFILE(va("GetPacket: Textcmd too long (max %s, used %s, mak %d, " - "tosend %u, node %u, player %d)\n", sizeu1(j), sizeu2(TotalTextCmdPerTic(maketic)), - maketic, firstticstosend, node, netconsole)); - Net_UnAcknowledgePacket(node); - break; - } - - // Make sure we have a buffer - if (!textcmd) textcmd = D_GetTextcmd(tic, netconsole); - - DEBFILE(va("textcmd put in tic %u at position %d (player %d) ftts %u mk %u\n", - tic, textcmd[0]+1, netconsole, firstticstosend, maketic)); - - M_Memcpy(&textcmd[textcmd[0]+1], netbuffer->u.textcmd+1, netbuffer->u.textcmd[0]); - textcmd[0] += (UINT8)netbuffer->u.textcmd[0]; - } - break; - case PT_LOGIN: - if (client) - break; - -#ifndef NOMD5 - if (doomcom->datalength < 16)/* ignore partial sends */ - break; - - if (!adminpasswordset) - { - CONS_Printf(M_GetText("Password from %s failed (no password set).\n"), player_names[netconsole]); - break; - } - - // Do the final pass to compare with the sent md5 - D_MD5PasswordPass(adminpassmd5, 16, va("PNUM%02d", netconsole), &finalmd5); - - if (!memcmp(netbuffer->u.md5sum, finalmd5, 16)) - { - CONS_Printf(M_GetText("%s passed authentication.\n"), player_names[netconsole]); - COM_BufInsertText(va("promote %d\n", netconsole)); // do this immediately - } - else - CONS_Printf(M_GetText("Password from %s failed.\n"), player_names[netconsole]); -#endif - break; - case PT_NODETIMEOUT: - case PT_CLIENTQUIT: - if (client) - break; - - // nodeingame will be put false in the execution of kick command - // this allow to send some packets to the quitting client to have their ack back - nodewaiting[node] = 0; - if (netconsole != -1 && playeringame[netconsole]) - { - UINT8 kickmsg; - - if (netbuffer->packettype == PT_NODETIMEOUT) - kickmsg = KICK_MSG_TIMEOUT; - else - kickmsg = KICK_MSG_PLAYER_QUIT; - kickmsg |= KICK_MSG_KEEP_BODY; - - SendKick(netconsole, kickmsg); - nodetoplayer[node] = -1; - - if (nodetoplayer2[node] != -1 && nodetoplayer2[node] >= 0 - && playeringame[(UINT8)nodetoplayer2[node]]) - { - SendKick(nodetoplayer2[node], kickmsg); - nodetoplayer2[node] = -1; - } - } - Net_CloseConnection(node); - nodeingame[node] = false; - break; - case PT_CANRECEIVEGAMESTATE: - PT_CanReceiveGamestate(node); - break; - case PT_ASKLUAFILE: - if (server && luafiletransfers && luafiletransfers->nodestatus[node] == LFTNS_ASKED) - AddLuaFileToSendQueue(node, luafiletransfers->realfilename); - break; - case PT_HASLUAFILE: - if (server && luafiletransfers && luafiletransfers->nodestatus[node] == LFTNS_SENDING) - SV_HandleLuaFileSent(node); - break; - case PT_RECEIVEDGAMESTATE: - sendingsavegame[node] = false; - resendingsavegame[node] = false; - savegameresendcooldown[node] = I_GetTime() + 15 * TICRATE; - break; -// -------------------------------------------- CLIENT RECEIVE ---------- - case PT_SERVERTICS: - // Only accept PT_SERVERTICS from the server. - if (node != servernode) - { - CONS_Alert(CONS_WARNING, M_GetText("%s received from non-host %d\n"), "PT_SERVERTICS", node); - if (server) - SendKick(netconsole, KICK_MSG_CON_FAIL | KICK_MSG_KEEP_BODY); - break; - } - - realstart = netbuffer->u.serverpak.starttic; - realend = realstart + netbuffer->u.serverpak.numtics; - - if (!txtpak) - txtpak = (UINT8 *)&netbuffer->u.serverpak.cmds[netbuffer->u.serverpak.numslots - * netbuffer->u.serverpak.numtics]; - - if (realend > gametic + CLIENTBACKUPTICS) - realend = gametic + CLIENTBACKUPTICS; - cl_packetmissed = realstart > neededtic; - - if (realstart <= neededtic && realend > neededtic) - { - tic_t i, j; - pak = (UINT8 *)&netbuffer->u.serverpak.cmds; - - for (i = realstart; i < realend; i++) - { - // clear first - D_Clearticcmd(i); - - // copy the tics - pak = G_ScpyTiccmd(netcmds[i%BACKUPTICS], pak, - netbuffer->u.serverpak.numslots*sizeof (ticcmd_t)); - - // copy the textcmds - numtxtpak = *txtpak++; - for (j = 0; j < numtxtpak; j++) { - INT32 k = *txtpak++; // playernum - const size_t txtsize = txtpak[0]+1; - - if (i >= gametic) // Don't copy old net commands - M_Memcpy(D_GetTextcmd(i, k), txtpak, txtsize); - txtpak += txtsize; - } - } - - neededtic = realend; - } - else - { - DEBFILE(va("frame not in bound: %u\n", neededtic)); - /*if (realend < neededtic - 2 * TICRATE || neededtic + 2 * TICRATE < realstart) - I_Error("Received an out of order PT_SERVERTICS packet!\n" - "Got tics %d-%d, needed tic %d\n\n" - "Please report this crash on the Master Board,\n" - "IRC or Discord so it can be fixed.\n", (INT32)realstart, (INT32)realend, (INT32)neededtic);*/ - } - break; - case PT_PING: - // Only accept PT_PING from the server. - if (node != servernode) - { - CONS_Alert(CONS_WARNING, M_GetText("%s received from non-host %d\n"), "PT_PING", node); - if (server) - SendKick(netconsole, KICK_MSG_CON_FAIL | KICK_MSG_KEEP_BODY); - break; - } - - //Update client ping table from the server. - if (client) - { - UINT8 i; - for (i = 0; i < MAXPLAYERS; i++) - if (playeringame[i]) - playerpingtable[i] = (tic_t)netbuffer->u.pingtable[i]; - - servermaxping = (tic_t)netbuffer->u.pingtable[MAXPLAYERS]; - } - - break; - case PT_SERVERCFG: - break; - case PT_FILEFRAGMENT: - // Only accept PT_FILEFRAGMENT from the server. - if (node != servernode) - { - CONS_Alert(CONS_WARNING, M_GetText("%s received from non-host %d\n"), "PT_FILEFRAGMENT", node); - if (server) - SendKick(netconsole, KICK_MSG_CON_FAIL | KICK_MSG_KEEP_BODY); - break; - } - if (client) - PT_FileFragment(); - break; - case PT_FILEACK: - if (server) - PT_FileAck(); - break; - case PT_FILERECEIVED: - if (server) - PT_FileReceived(); - break; - case PT_WILLRESENDGAMESTATE: - PT_WillResendGamestate(); - break; - case PT_SENDINGLUAFILE: - if (client) - CL_PrepareDownloadLuaFile(); - break; - default: - DEBFILE(va("UNKNOWN PACKET TYPE RECEIVED %d from host %d\n", - netbuffer->packettype, node)); - } // end switch -} - -/** Handles all received packets, if any - * - * \todo Add details to this description (lol) - * - */ -static void GetPackets(void) -{ - SINT8 node; // The packet sender - - player_joining = false; - - while (HGetPacket()) - { - node = (SINT8)doomcom->remotenode; - - if (netbuffer->packettype == PT_CLIENTJOIN && server) - { - HandleConnect(node); - continue; - } - if (node == servernode && client && cl_mode != CL_SEARCHING) - { - if (netbuffer->packettype == PT_SERVERSHUTDOWN) - { - HandleShutdown(node); - continue; - } - if (netbuffer->packettype == PT_NODETIMEOUT) - { - HandleTimeout(node); - continue; - } - } - -#ifndef NONET - if (netbuffer->packettype == PT_SERVERINFO) - { - HandleServerInfo(node); - continue; - } -#endif - - if (netbuffer->packettype == PT_PLAYERINFO) - continue; // We do nothing with PLAYERINFO, that's for the MS browser. - - // Packet received from someone already playing - if (nodeingame[node]) - HandlePacketFromPlayer(node); - // Packet received from someone not playing - else - HandlePacketFromAwayNode(node); - } -} - -// -// NetUpdate -// Builds ticcmds for console player, -// sends out a packet -// -// no more use random generator, because at very first tic isn't yet synchronized -// Note: It is called consistAncy on purpose. -// -static INT16 Consistancy(void) -{ - INT32 i; - UINT32 ret = 0; -#ifdef MOBJCONSISTANCY - thinker_t *th; - mobj_t *mo; -#endif - - DEBFILE(va("TIC %u ", gametic)); - - for (i = 0; i < MAXPLAYERS; i++) - { - if (!playeringame[i]) - ret ^= 0xCCCC; - else if (!players[i].mo); - else - { - ret += players[i].mo->x; - ret -= players[i].mo->y; - ret += players[i].powers[pw_shield]; - ret *= i+1; - } - } - // I give up - // Coop desynching enemies is painful - if (!G_PlatformGametype()) - ret += P_GetRandSeed(); - -#ifdef MOBJCONSISTANCY - for (th = thlist[THINK_MOBJ].next; th != &thlist[THINK_MOBJ]; th = th->next) - { - if (th->function.acp1 == (actionf_p1)P_RemoveThinkerDelayed) - continue; - - mo = (mobj_t *)th; - - if (mo->flags & (MF_SPECIAL | MF_SOLID | MF_PUSHABLE | MF_BOSS | MF_MISSILE | MF_SPRING | MF_MONITOR | MF_FIRE | MF_ENEMY | MF_PAIN | MF_STICKY)) - { - ret -= mo->type; - ret += mo->x; - ret -= mo->y; - ret += mo->z; - ret -= mo->momx; - ret += mo->momy; - ret -= mo->momz; - ret += mo->angle; - ret -= mo->flags; - ret += mo->flags2; - ret -= mo->eflags; - if (mo->target) - { - ret += mo->target->type; - ret -= mo->target->x; - ret += mo->target->y; - ret -= mo->target->z; - ret += mo->target->momx; - ret -= mo->target->momy; - ret += mo->target->momz; - ret -= mo->target->angle; - ret += mo->target->flags; - ret -= mo->target->flags2; - ret += mo->target->eflags; - ret -= mo->target->state - states; - ret += mo->target->tics; - ret -= mo->target->sprite; - ret += mo->target->frame; - } - else - ret ^= 0x3333; - if (mo->tracer && mo->tracer->type != MT_OVERLAY) - { - ret += mo->tracer->type; - ret -= mo->tracer->x; - ret += mo->tracer->y; - ret -= mo->tracer->z; - ret += mo->tracer->momx; - ret -= mo->tracer->momy; - ret += mo->tracer->momz; - ret -= mo->tracer->angle; - ret += mo->tracer->flags; - ret -= mo->tracer->flags2; - ret += mo->tracer->eflags; - ret -= mo->tracer->state - states; - ret += mo->tracer->tics; - ret -= mo->tracer->sprite; - ret += mo->tracer->frame; - } - else - ret ^= 0xAAAA; - ret -= mo->state - states; - ret += mo->tics; - ret -= mo->sprite; - ret += mo->frame; - } - } -#endif - - DEBFILE(va("Consistancy = %u\n", (ret & 0xFFFF))); - - return (INT16)(ret & 0xFFFF); -} - -// send the client packet to the server -static void CL_SendClientCmd(void) -{ - size_t packetsize = 0; - - netbuffer->packettype = PT_CLIENTCMD; - - if (cl_packetmissed) - netbuffer->packettype++; - netbuffer->u.clientpak.resendfrom = (UINT8)(neededtic & UINT8_MAX); - netbuffer->u.clientpak.client_tic = (UINT8)(gametic & UINT8_MAX); - - if (gamestate == GS_WAITINGPLAYERS) - { - // Send PT_NODEKEEPALIVE packet - netbuffer->packettype += 4; - packetsize = sizeof (clientcmd_pak) - sizeof (ticcmd_t) - sizeof (INT16); - HSendPacket(servernode, false, 0, packetsize); - } - else if (gamestate != GS_NULL && (addedtogame || dedicated)) - { - G_MoveTiccmd(&netbuffer->u.clientpak.cmd, &localcmds, 1); - netbuffer->u.clientpak.consistancy = SHORT(consistancy[gametic%BACKUPTICS]); - - // Send a special packet with 2 cmd for splitscreen - if (splitscreen || botingame) - { - netbuffer->packettype += 2; - G_MoveTiccmd(&netbuffer->u.client2pak.cmd2, &localcmds2, 1); - packetsize = sizeof (client2cmd_pak); - } - else - packetsize = sizeof (clientcmd_pak); - - HSendPacket(servernode, false, 0, packetsize); - } - - if (cl_mode == CL_CONNECTED || dedicated) - { - // Send extra data if needed - if (localtextcmd[0]) - { - netbuffer->packettype = PT_TEXTCMD; - M_Memcpy(netbuffer->u.textcmd,localtextcmd, localtextcmd[0]+1); - // All extra data have been sent - if (HSendPacket(servernode, true, 0, localtextcmd[0]+1)) // Send can fail... - localtextcmd[0] = 0; - } - - // Send extra data if needed for player 2 (splitscreen) - if (localtextcmd2[0]) - { - netbuffer->packettype = PT_TEXTCMD2; - M_Memcpy(netbuffer->u.textcmd, localtextcmd2, localtextcmd2[0]+1); - // All extra data have been sent - if (HSendPacket(servernode, true, 0, localtextcmd2[0]+1)) // Send can fail... - localtextcmd2[0] = 0; - } - } -} - -// send the server packet -// send tic from firstticstosend to maketic-1 -static void SV_SendTics(void) -{ - tic_t realfirsttic, lasttictosend, i; - UINT32 n; - INT32 j; - size_t packsize; - UINT8 *bufpos; - UINT8 *ntextcmd; - - // send to all client but not to me - // for each node create a packet with x tics and send it - // x is computed using supposedtics[n], max packet size and maketic - for (n = 1; n < MAXNETNODES; n++) - if (nodeingame[n]) - { - // assert supposedtics[n]>=nettics[n] - realfirsttic = supposedtics[n]; - lasttictosend = min(maketic, nettics[n] + CLIENTBACKUPTICS); - - if (realfirsttic >= lasttictosend) - { - // well we have sent all tics we will so use extrabandwidth - // to resent packet that are supposed lost (this is necessary since lost - // packet detection work when we have received packet with firsttic > neededtic - // (getpacket servertics case) - DEBFILE(va("Nothing to send node %u mak=%u sup=%u net=%u \n", - n, maketic, supposedtics[n], nettics[n])); - realfirsttic = nettics[n]; - if (realfirsttic >= lasttictosend || (I_GetTime() + n)&3) - // all tic are ok - continue; - DEBFILE(va("Sent %d anyway\n", realfirsttic)); - } - if (realfirsttic < firstticstosend) - realfirsttic = firstticstosend; - - // compute the length of the packet and cut it if too large - packsize = BASESERVERTICSSIZE; - for (i = realfirsttic; i < lasttictosend; i++) - { - packsize += sizeof (ticcmd_t) * doomcom->numslots; - packsize += TotalTextCmdPerTic(i); - - if (packsize > software_MAXPACKETLENGTH) - { - DEBFILE(va("packet too large (%s) at tic %d (should be from %d to %d)\n", - sizeu1(packsize), i, realfirsttic, lasttictosend)); - lasttictosend = i; - - // too bad: too much player have send extradata and there is too - // much data in one tic. - // To avoid it put the data on the next tic. (see getpacket - // textcmd case) but when numplayer changes the computation can be different - if (lasttictosend == realfirsttic) - { - if (packsize > MAXPACKETLENGTH) - I_Error("Too many players: can't send %s data for %d players to node %d\n" - "Well sorry nobody is perfect....\n", - sizeu1(packsize), doomcom->numslots, n); - else - { - lasttictosend++; // send it anyway! - DEBFILE("sending it anyway\n"); - } - } - break; - } - } - - // Send the tics - netbuffer->packettype = PT_SERVERTICS; - netbuffer->u.serverpak.starttic = realfirsttic; - netbuffer->u.serverpak.numtics = (UINT8)(lasttictosend - realfirsttic); - netbuffer->u.serverpak.numslots = (UINT8)SHORT(doomcom->numslots); - bufpos = (UINT8 *)&netbuffer->u.serverpak.cmds; - - for (i = realfirsttic; i < lasttictosend; i++) - { - bufpos = G_DcpyTiccmd(bufpos, netcmds[i%BACKUPTICS], doomcom->numslots * sizeof (ticcmd_t)); - } - - // add textcmds - for (i = realfirsttic; i < lasttictosend; i++) - { - ntextcmd = bufpos++; - *ntextcmd = 0; - for (j = 0; j < MAXPLAYERS; j++) - { - UINT8 *textcmd = D_GetExistingTextcmd(i, j); - INT32 size = textcmd ? textcmd[0] : 0; - - if ((!j || playeringame[j]) && size) - { - (*ntextcmd)++; - WRITEUINT8(bufpos, j); - M_Memcpy(bufpos, textcmd, size + 1); - bufpos += size + 1; + 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; } } } - packsize = bufpos - (UINT8 *)&(netbuffer->u); + 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); + } - HSendPacket(n, false, 0, packsize); - // when tic are too large, only one tic is sent so don't go backward! - if (lasttictosend-doomcom->extratics > realfirsttic) - supposedtics[n] = lasttictosend-doomcom->extratics; - else - supposedtics[n] = lasttictosend; - if (supposedtics[n] < nettics[n]) supposedtics[n] = nettics[n]; + if (!skin) + break; + +next_token: + stoken = strtok(NULL, "\r\n= "); } - // node 0 is me! - supposedtics[0] = maketic; -} + free(buf2); -// -// TryRunTics -// -static void Local_Maketic(INT32 realtics) -{ - I_OsPolling(); // I_Getevent - D_ProcessEvents(); // menu responder, cons responder, - // game responder calls HU_Responder, AM_Responder, - // and G_MapEventsToControls - if (!dedicated) rendergametic = gametic; - // translate inputs (keyboard/mouse/joystick) into game controls - G_BuildTiccmd(&localcmds, realtics, 1); - if (splitscreen || botingame) - G_BuildTiccmd(&localcmds2, realtics, 2); - - localcmds.angleturn |= TICCMD_RECEIVED; - localcmds2.angleturn |= TICCMD_RECEIVED; -} - -// create missed tic -static void SV_Maketic(void) -{ - INT32 i; - - for (i = 0; i < MAXPLAYERS; i++) - { - if (!playeringame[i]) + 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; - - // We didn't receive this tic - if ((netcmds[maketic % BACKUPTICS][i].angleturn & TICCMD_RECEIVED) == 0) - { - ticcmd_t * ticcmd = &netcmds[(maketic ) % BACKUPTICS][i]; - ticcmd_t *prevticcmd = &netcmds[(maketic - 1) % BACKUPTICS][i]; - - if (players[i].quittime) - { - // Copy the angle/aiming from the previous tic - // and empty the other inputs - memset(ticcmd, 0, sizeof(netcmds[0][0])); - ticcmd->angleturn = prevticcmd->angleturn | TICCMD_RECEIVED; - ticcmd->aiming = prevticcmd->aiming; - } - else - { - DEBFILE(va("MISS tic%4d for player %d\n", maketic, i)); - // Copy the input from the previous tic - *ticcmd = *prevticcmd; - ticcmd->angleturn &= ~TICCMD_RECEIVED; - } - } - } - - // all tic are now proceed make the next - maketic++; -} - -void TryRunTics(tic_t realtics) -{ - // the machine has lagged but it is not so bad - if (realtics > TICRATE/7) // FIXME: consistency failure!! - { - if (server) - realtics = 1; - else - realtics = TICRATE/7; - } - - if (singletics) - realtics = 1; - - if (realtics >= 1) - { - COM_BufTicker(); - if (mapchangepending) - D_MapChange(-1, 0, ultimatemode, false, 2, false, fromlevelselect); // finish the map change - } - - NetUpdate(); - - if (demoplayback) - { - neededtic = gametic + (realtics * cv_playbackspeed.value); - // start a game after a demo - maketic += realtics; - firstticstosend = maketic; - tictoclear = firstticstosend; - } - - GetPackets(); - -#ifdef DEBUGFILE - if (debugfile && (realtics || neededtic > gametic)) - { - //SoM: 3/30/2000: Need long INT32 in the format string for args 4 & 5. - //Shut up stupid warning! - fprintf(debugfile, "------------ Tryruntic: REAL:%d NEED:%d GAME:%d LOAD: %d\n", - realtics, neededtic, gametic, debugload); - debugload = 100000; - } -#endif - - if (player_joining) - return; - - if (neededtic > gametic) - { - if (advancedemo) - { - if (timedemo_quit) - COM_ImmedExecute("quit"); - else - D_StartTitle(); - } - else - // run the count * tics - while (neededtic > gametic) - { - DEBFILE(va("============ Running tic %d (local %d)\n", gametic, localgametic)); - - ps_tictime = I_GetTimeMicros(); - - G_Ticker((gametic % NEWTICRATERATIO) == 0); - ExtraDataTicker(); - gametic++; - consistancy[gametic%BACKUPTICS] = Consistancy(); - - ps_tictime = I_GetTimeMicros() - ps_tictime; - - // Leave a certain amount of tics present in the net buffer as long as we've ran at least one tic this frame. - if (client && gamestate == GS_LEVEL && leveltime > 3 && neededtic <= gametic + cv_netticbuffer.value) - break; - } - } -} - -/* -Ping Update except better: -We call this once per second and check for people's pings. If their ping happens to be too high, we increment some timer and kick them out. -If they're not lagging, decrement the timer by 1. Of course, reset all of this if they leave. -*/ - -static INT32 pingtimeout[MAXPLAYERS]; - -static inline void PingUpdate(void) -{ - INT32 i; - boolean laggers[MAXPLAYERS]; - UINT8 numlaggers = 0; - memset(laggers, 0, sizeof(boolean) * MAXPLAYERS); - - netbuffer->packettype = PT_PING; - - //check for ping limit breakage. - if (cv_maxping.value) - { - for (i = 1; i < MAXPLAYERS; i++) - { - if (playeringame[i] && !players[i].quittime - && (realpingtable[i] / pingmeasurecount > (unsigned)cv_maxping.value)) - { - if (players[i].jointime > 30 * TICRATE) - laggers[i] = true; - numlaggers++; - } - else - pingtimeout[i] = 0; } - //kick lagging players... unless everyone but the server's ping sucks. - //in that case, it is probably the server's fault. - if (numlaggers < D_NumPlayers() - 1) - { - for (i = 1; i < MAXPLAYERS; i++) - { - if (playeringame[i] && laggers[i]) - { - pingtimeout[i]++; - // ok your net has been bad for too long, you deserve to die. - if (pingtimeout[i] > cv_pingtimeout.value) - { - pingtimeout[i] = 0; - SendKick(i, KICK_MSG_PING_HIGH | KICK_MSG_KEEP_BODY); - } - } - /* - you aren't lagging, - but you aren't free yet. - In case you'll keep spiking, - we just make the timer go back down. (Very unstable net must still get kicked). - */ - else - pingtimeout[i] = (pingtimeout[i] == 0 ? 0 : pingtimeout[i]-1); - } - } + // 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); } - - //make the ping packet and clear server data for next one - for (i = 0; i < MAXPLAYERS; i++) - { - netbuffer->u.pingtable[i] = realpingtable[i] / pingmeasurecount; - //server takes a snapshot of the real ping for display. - //otherwise, pings fluctuate a lot and would be odd to look at. - playerpingtable[i] = realpingtable[i] / pingmeasurecount; - realpingtable[i] = 0; //Reset each as we go. - } - - // send the server's maxping as last element of our ping table. This is useful to let us know when we're about to get kicked. - netbuffer->u.pingtable[MAXPLAYERS] = cv_maxping.value; - - //send out our ping packets - for (i = 0; i < MAXNETNODES; i++) - if (nodeingame[i]) - HSendPacket(i, true, 0, sizeof(INT32) * (MAXPLAYERS+1)); - - pingmeasurecount = 1; //Reset count + return; } -void NetUpdate(void) -{ - static tic_t gametime = 0; - static tic_t resptime = 0; - tic_t nowtime; - INT32 i; - INT32 realtics; - - nowtime = I_GetTime(); - realtics = nowtime - gametime; - - if (realtics <= 0) // nothing new to update - return; - if (realtics > 5) - { - if (server) - realtics = 1; - else - realtics = 5; - } - - gametime = nowtime; - - if (server) - { - if (netgame && !(gametime % 35)) // update once per second. - PingUpdate(); - // update node latency values so we can take an average later. - for (i = 0; i < MAXPLAYERS; i++) - if (playeringame[i] && playernode[i] != UINT8_MAX) - realpingtable[i] += G_TicsToMilliseconds(GetLag(playernode[i])); - pingmeasurecount++; - } - - if (client) - maketic = neededtic; - - Local_Maketic(realtics); // make local tic, and call menu? - - if (server) - CL_SendClientCmd(); // send it - - GetPackets(); // get packet from client or from server - - // client send the command after a receive of the server - // the server send before because in single player is beter - -#ifdef MASTERSERVER - MasterClient_Ticker(); // Acking the Master Server -#endif - - if (client) - { - // If the client just finished redownloading the game state, load it - if (cl_redownloadinggamestate && fileneeded[0].status == FS_FOUND) - CL_ReloadReceivedSavegame(); - - CL_SendClientCmd(); // Send tic cmd - hu_redownloadinggamestate = cl_redownloadinggamestate; - } - else - { - if (!demoplayback) - { - INT32 counts; - - hu_redownloadinggamestate = false; - - firstticstosend = gametic; - for (i = 0; i < MAXNETNODES; i++) - if (nodeingame[i] && nettics[i] < firstticstosend) - { - firstticstosend = nettics[i]; - - if (maketic + 1 >= nettics[i] + BACKUPTICS) - Net_ConnectionTimeout(i); - } - - // Don't erase tics not acknowledged - counts = realtics; - - if (maketic + counts >= firstticstosend + BACKUPTICS) - counts = firstticstosend+BACKUPTICS-maketic-1; - - for (i = 0; i < counts; i++) - SV_Maketic(); // Create missed tics and increment maketic - - for (; tictoclear < firstticstosend; tictoclear++) // Clear only when acknowledged - D_Clearticcmd(tictoclear); // Clear the maketic the new tic - - SV_SendTics(); - - neededtic = maketic; // The server is a client too - } - } - - Net_AckTicker(); - - // Handle timeouts to prevent definitive freezes from happenning - if (server) - { - for (i = 1; i < MAXNETNODES; i++) - if (nodeingame[i] && freezetimeout[i] < I_GetTime()) - Net_ConnectionTimeout(i); - - // In case the cvar value was lowered - if (joindelay) - joindelay = min(joindelay - 1, 3 * (tic_t)cv_joindelay.value * TICRATE); - } - - nowtime /= NEWTICRATERATIO; - if (nowtime > resptime) - { - resptime = nowtime; -#ifdef HAVE_THREADS - I_lock_mutex(&m_menu_mutex); -#endif - M_Ticker(); -#ifdef HAVE_THREADS - I_unlock_mutex(m_menu_mutex); -#endif - CON_Ticker(); - } - - FileSendTicker(); -} - -/** Returns the number of players playing. - * \return Number of players. Can be zero if we're running a ::dedicated - * server. - * \author Graue - */ -INT32 D_NumPlayers(void) -{ - INT32 num = 0, ix; - for (ix = 0; ix < MAXPLAYERS; ix++) - if (playeringame[ix]) - num++; - return num; -} - -tic_t GetLag(INT32 node) -{ - return gametic - nettics[node]; -} - -void D_MD5PasswordPass(const UINT8 *buffer, size_t len, const char *salt, void *dest) -{ -#ifdef NOMD5 - (void)buffer; - (void)len; - (void)salt; - memset(dest, 0, 16); -#else - char tmpbuf[256]; - const size_t sl = strlen(salt); - - if (len > 256-sl) - len = 256-sl; - - memcpy(tmpbuf, buffer, len); - memmove(&tmpbuf[len], salt, sl); - //strcpy(&tmpbuf[len], salt); - len += strlen(salt); - if (len < 256) - memset(&tmpbuf[len],0,256-len); - - // Yes, we intentionally md5 the ENTIRE buffer regardless of size... - md5_buffer(tmpbuf, 256, dest); -#endif -} +#undef HUDNAMEWRITE +#undef SYMBOLCONVERT From 079fe9ba7e488e880561318e1200e8630289d117 Mon Sep 17 00:00:00 2001 From: Shane Ellis Date: Fri, 15 Jan 2021 17:08:16 -0500 Subject: [PATCH 011/126] Command_Kick() - Allow removal of non-consoleplayer/secondaryviewplayer player instances (e.g. player bots) --- src/d_clisrv.c | 66 ++++++++++++++++++++++++++------------------------ 1 file changed, 34 insertions(+), 32 deletions(-) diff --git a/src/d_clisrv.c b/src/d_clisrv.c index 4fdc7e7ee..88b4d9387 100644 --- a/src/d_clisrv.c +++ b/src/d_clisrv.c @@ -1595,7 +1595,9 @@ static void CL_ReloadReceivedSavegame(void) for (i = 0; i < MAXPLAYERS; i++) { +#ifdef HAVE_BLUA LUA_InvalidatePlayer(&players[i]); +#endif sprintf(player_names[i], "Player %d", i + 1); } @@ -2258,15 +2260,11 @@ void D_SaveBan(void) size_t i; banreason_t *reasonlist = reasonhead; const char *address, *mask; - const char *path = va("%s"PATHSEP"%s", srb2home, "ban.txt"); if (!reasonhead) - { - remove(path); return; - } - f = fopen(path, "w"); + f = fopen(va("%s"PATHSEP"%s", srb2home, "ban.txt"), "w"); if (!f) { @@ -2310,14 +2308,16 @@ static void Ban_Add(const char *reason) reasontail = reasonlist; } -static void Ban_Clear(void) +static void Command_ClearBans(void) { banreason_t *temp; + if (!I_ClearBans) + return; + I_ClearBans(); - + D_SaveBan(); reasontail = NULL; - while (reasonhead) { temp = reasonhead->next; @@ -2327,15 +2327,6 @@ static void Ban_Clear(void) } } -static void Command_ClearBans(void) -{ - if (!I_ClearBans) - return; - - Ban_Clear(); - D_SaveBan(); -} - static void Ban_Load_File(boolean warning) { FILE *f; @@ -2343,9 +2334,6 @@ static void Ban_Load_File(boolean warning) const char *address, *mask; char buffer[MAX_WADPATH]; - if (!I_ClearBans) - return; - f = fopen(va("%s"PATHSEP"%s", srb2home, "ban.txt"), "r"); if (!f) @@ -2355,7 +2343,13 @@ static void Ban_Load_File(boolean warning) return; } - Ban_Clear(); + if (I_ClearBans) + Command_ClearBans(); + else + { + fclose(f); + return; + } for (i=0; fgets(buffer, (int)sizeof(buffer), f); i++) { @@ -2815,11 +2809,11 @@ static void Command_Kick(void) return; } - if (!netgame) // Don't kick Tails in splitscreen! - { - CONS_Printf(M_GetText("This only works in a netgame.\n")); - return; - } + //if (!netgame) // Don't kick Tails in splitscreen! + //{ + // CONS_Printf(M_GetText("This only works in a netgame.\n")); + // return; + //} if (server || IsPlayerAdmin(consoleplayer)) { @@ -2827,9 +2821,14 @@ static void Command_Kick(void) UINT8 *p = buf; const SINT8 pn = nametonum(COM_Argv(1)); + if (splitscreen && (pn == 0 || pn == 1)) + { + CONS_Printf(M_GetText("Splitscreen players cannot be kicked.\n")); + return; + } if (pn == -1 || pn == 0) return; - + // 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 @@ -3032,7 +3031,8 @@ static void Got_KickCmd(UINT8 **p, INT32 playernum) if (pnum == consoleplayer) { - LUAh_GameQuit(false); + if (Playing()) + LUAh_GameQuit(); #ifdef DUMPCONSISTENCY if (msg == KICK_MSG_CON_FAIL) SV_SavedGame(); #endif @@ -3732,7 +3732,8 @@ static void HandleConnect(SINT8 node) static void HandleShutdown(SINT8 node) { (void)node; - LUAh_GameQuit(false); + if (Playing()) + LUAh_GameQuit(); D_QuitNetGame(); CL_Reset(); D_StartTitle(); @@ -3747,7 +3748,8 @@ static void HandleShutdown(SINT8 node) static void HandleTimeout(SINT8 node) { (void)node; - LUAh_GameQuit(false); + if (Playing()) + LUAh_GameQuit(); D_QuitNetGame(); CL_Reset(); D_StartTitle(); @@ -4850,14 +4852,14 @@ void TryRunTics(tic_t realtics) { DEBFILE(va("============ Running tic %d (local %d)\n", gametic, localgametic)); - ps_tictime = I_GetPreciseTime(); + ps_tictime = I_GetTimeMicros(); G_Ticker((gametic % NEWTICRATERATIO) == 0); ExtraDataTicker(); gametic++; consistancy[gametic%BACKUPTICS] = Consistancy(); - ps_tictime = I_GetPreciseTime() - ps_tictime; + ps_tictime = I_GetTimeMicros() - ps_tictime; // Leave a certain amount of tics present in the net buffer as long as we've ran at least one tic this frame. if (client && gamestate == GS_LEVEL && leveltime > 3 && neededtic <= gametic + cv_netticbuffer.value) From a68fee3303b0804cd35c186c4d89b858b790c385 Mon Sep 17 00:00:00 2001 From: Shane Ellis Date: Fri, 15 Jan 2021 17:22:38 -0500 Subject: [PATCH 012/126] oops --- src/p_inter.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/p_inter.c b/src/p_inter.c index 778ec703b..8190f5cfa 100644 --- a/src/p_inter.c +++ b/src/p_inter.c @@ -1387,6 +1387,10 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck) if (player->bot && player->bot != 3) return; + + // Initialize my junk + junk.tags.tags = NULL; + junk.tags.count = 0; Tag_FSet(&junk.tags, LE_AXE); EV_DoElevator(&junk, bridgeFall, false); From 56e9b99f283923855efc6cf621d988700c305634 Mon Sep 17 00:00:00 2001 From: Shane Ellis Date: Fri, 15 Jan 2021 22:28:01 +0000 Subject: [PATCH 013/126] Revert "Add conditions for new player bot type" This reverts commit 4b9a95a53837166a080b200b34982ce867a31ffd --- src/p_enemy.c | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/src/p_enemy.c b/src/p_enemy.c index 38df59855..203e04af1 100644 --- a/src/p_enemy.c +++ b/src/p_enemy.c @@ -743,7 +743,7 @@ boolean P_LookForPlayers(mobj_t *actor, boolean allaround, boolean tracer, fixed if (player->mo->health <= 0) continue; // dead - if (player->bot && player->bot != 3) + if (player->bot) continue; // ignore bots if (player->quittime) @@ -1834,7 +1834,7 @@ void A_SnailerThink(mobj_t *actor) fixed_t dist; fixed_t dx, dy; - dist = P_AproxDistance(actor->x - actor->target->x, actor->y - actor->target->y); + dist = R_PointToDist2(0, 0, actor->x - actor->target->x, actor->y - actor->target->y); if (an > ANGLE_45 && an <= ANGLE_90) // fire at 45 degrees to the left { @@ -3924,6 +3924,10 @@ void A_BossDeath(mobj_t *mo) } else { + // Initialize my junk + junk.tags.tags = NULL; + junk.tags.count = 0; + // Bring the egg trap up to the surface // Incredibly shitty code ahead Tag_FSet(&junk.tags, LE_CAPSULE0); @@ -4053,6 +4057,10 @@ bossjustdie: } case MT_KOOPA: { + // Initialize my junk + junk.tags.tags = NULL; + junk.tags.count = 0; + Tag_FSet(&junk.tags, LE_KOOPA); EV_DoCeiling(&junk, raiseToHighest); return; From 161e1c42cb6bed9ca7af062df4f7fd091f0299b7 Mon Sep 17 00:00:00 2001 From: Shane Ellis Date: Fri, 15 Jan 2021 17:29:15 -0500 Subject: [PATCH 014/126] Update p_enemy.c --- src/p_enemy.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/p_enemy.c b/src/p_enemy.c index 203e04af1..637eba83f 100644 --- a/src/p_enemy.c +++ b/src/p_enemy.c @@ -743,7 +743,7 @@ boolean P_LookForPlayers(mobj_t *actor, boolean allaround, boolean tracer, fixed if (player->mo->health <= 0) continue; // dead - if (player->bot) + if (player->bot && player->bot != 3) continue; // ignore bots if (player->quittime) From 759ff44dfd366bfd42ae252d90f78a1e85b947c2 Mon Sep 17 00:00:00 2001 From: Shane Ellis Date: Fri, 15 Jan 2021 22:32:02 +0000 Subject: [PATCH 015/126] Revert "Add conditions for new player bot type" This reverts commit b995e3cb75b67116d51583c1ae85debf25013621 --- src/p_user.c | 125 +++++++++++++++++++++++++++++++-------------------- 1 file changed, 77 insertions(+), 48 deletions(-) diff --git a/src/p_user.c b/src/p_user.c index 2466310bb..a70dceb8b 100644 --- a/src/p_user.c +++ b/src/p_user.c @@ -1189,7 +1189,7 @@ void P_GivePlayerRings(player_t *player, INT32 num_rings) if (!player) return; - if (player->bot && player->bot != 3) + if (player->bot) player = &players[consoleplayer]; if (!player->mo) @@ -1234,7 +1234,7 @@ void P_GivePlayerSpheres(player_t *player, INT32 num_spheres) if (!player) return; - if (player->bot && player->bot != 3) + if (player->bot) player = &players[consoleplayer]; if (!player->mo) @@ -1261,7 +1261,7 @@ void P_GivePlayerLives(player_t *player, INT32 numlives) if (!player) return; - if (player->bot && player->bot != 3) + if (player->bot) player = &players[consoleplayer]; if (gamestate == GS_LEVEL) @@ -1341,7 +1341,7 @@ void P_DoSuperTransformation(player_t *player, boolean giverings) // Transformation animation P_SetPlayerMobjState(player->mo, S_PLAY_SUPER_TRANS1); - if (giverings) + if (giverings && player->rings < 50) player->rings = 50; // Just in case. @@ -1367,7 +1367,7 @@ void P_AddPlayerScore(player_t *player, UINT32 amount) { UINT32 oldscore; - if (player->bot && player->bot != 3) + if (player->bot) player = &players[consoleplayer]; // NiGHTS does it different! @@ -1491,10 +1491,10 @@ void P_PlayLivesJingle(player_t *player) if (player && !P_IsLocalPlayer(player)) return; - if (use1upSound || cv_1upsound.value) - S_StartSound(NULL, sfx_oneup); - else if (mariomode) + if (mariomode) S_StartSound(NULL, sfx_marioa); + else if (use1upSound || cv_1upsound.value) + S_StartSound(NULL, sfx_oneup); else { P_PlayJingle(player, JT_1UP); @@ -2329,7 +2329,8 @@ boolean P_PlayerHitFloor(player_t *player, boolean dorollstuff) P_MobjCheckWater(player->mo); if (player->pflags & PF_SPINNING) { - if (player->mo->state-states != S_PLAY_ROLL && !(player->pflags & PF_STARTDASH)) + if (!(player->pflags & PF_STARTDASH) && player->panim != PA_ROLL && player->panim != PA_ETC + && player->panim != PA_ABILITY && player->panim != PA_ABILITY2) { P_SetPlayerMobjState(player->mo, S_PLAY_ROLL); S_StartSound(player->mo, sfx_spin); @@ -2612,10 +2613,10 @@ static void P_CheckBustableBlocks(player_t *player) if ((netgame || multiplayer) && player->spectator) return; - + oldx = player->mo->x; oldy = player->mo->y; - + if (!(player->pflags & PF_BOUNCING)) // Bouncers only get to break downwards, not sideways { P_UnsetThingPosition(player->mo); @@ -2634,7 +2635,7 @@ static void P_CheckBustableBlocks(player_t *player) if (!node->m_sector->ffloors) continue; - + for (rover = node->m_sector->ffloors; rover; rover = rover->next) { if (!P_PlayerCanBust(player, rover)) @@ -2992,7 +2993,7 @@ static void P_CheckUnderwaterAndSpaceTimer(player_t *player) player->powers[pw_spacetime] = 0; // Underwater audio cues - if (P_IsLocalPlayer(player) && !player->bot && player->bot != 3) + if (P_IsLocalPlayer(player) && !player->bot) { if ((player->powers[pw_underwater] == 25*TICRATE + 1) || (player->powers[pw_underwater] == 20*TICRATE + 1) @@ -4525,6 +4526,9 @@ void P_DoJump(player_t *player, boolean soundandstate) player->pflags |= P_GetJumpFlags(player);; + if (player->charflags & SF_NOJUMPDAMAGE) + player->pflags &= ~PF_SPINNING; + if (soundandstate) { if (!player->spectator) @@ -5020,7 +5024,7 @@ static boolean P_PlayerShieldThink(player_t *player, ticcmd_t *cmd, mobj_t *lock if ((player->powers[pw_shield] & SH_NOSTACK) && !player->powers[pw_super] && !(player->pflags & PF_SPINDOWN) && ((!(player->pflags & PF_THOKKED) || (((player->powers[pw_shield] & SH_NOSTACK) == SH_BUBBLEWRAP || (player->powers[pw_shield] & SH_NOSTACK) == SH_ATTRACT) && player->secondjump == UINT8_MAX) ))) // thokked is optional if you're bubblewrapped / 3dblasted { - if ((player->powers[pw_shield] & SH_NOSTACK) == SH_ATTRACT) + if ((player->powers[pw_shield] & SH_NOSTACK) == SH_ATTRACT && !(player->charflags & SF_NOSHIELDABILITY)) { if ((lockonshield = P_LookForEnemies(player, false, false))) { @@ -5043,7 +5047,7 @@ static boolean P_PlayerShieldThink(player_t *player, ticcmd_t *cmd, mobj_t *lock } } } - if (cmd->buttons & BT_SPIN && !LUAh_ShieldSpecial(player)) // Spin button effects + if ((!(player->charflags & SF_NOSHIELDABILITY)) && (cmd->buttons & BT_SPIN && !LUAh_ShieldSpecial(player))) // Spin button effects { // Force stop if ((player->powers[pw_shield] & ~(SH_FORCEHP|SH_STACK)) == SH_FORCE) @@ -5491,7 +5495,7 @@ static void P_DoJumpStuff(player_t *player, ticcmd_t *cmd) break; } } - else if ((player->powers[pw_shield] & SH_NOSTACK) == SH_WHIRLWIND && !player->powers[pw_super]) + else if ((!(player->charflags & SF_NOSHIELDABILITY)) && ((player->powers[pw_shield] & SH_NOSTACK) == SH_WHIRLWIND && !player->powers[pw_super] && !LUAh_ShieldSpecial(player))) P_DoJumpShield(player); } @@ -5920,7 +5924,7 @@ static void P_3dMovement(player_t *player) player->rmomy = player->mo->momy - player->cmomy; // Calculates player's speed based on distance-of-a-line formula - player->speed = P_AproxDistance(player->rmomx, player->rmomy); + player->speed = R_PointToDist2(0, 0, player->rmomx, player->rmomy); // Monster Iestyn - 04-11-13 // Quadrants are stupid, excessive and broken, let's do this a much simpler way! @@ -5953,6 +5957,22 @@ static void P_3dMovement(player_t *player) acceleration = 96 + (FixedDiv(player->speed, player->mo->scale)>>FRACBITS) * 40; topspeed = normalspd; } + else if (player->bot) + { // Bot steals player 1's stats + normalspd = FixedMul(players[consoleplayer].normalspeed, player->mo->scale); + thrustfactor = players[consoleplayer].thrustfactor; + acceleration = players[consoleplayer].accelstart + (FixedDiv(player->speed, player->mo->scale)>>FRACBITS) * players[consoleplayer].acceleration; + + if (player->powers[pw_tailsfly]) + topspeed = normalspd/2; + else if (player->mo->eflags & (MFE_UNDERWATER|MFE_GOOWATER)) + { + topspeed = normalspd/2; + acceleration = 2*acceleration/3; + } + else + topspeed = normalspd; + } else { if (player->powers[pw_super] || player->powers[pw_sneakers]) @@ -7736,6 +7756,11 @@ void P_ElementalFire(player_t *player, boolean cropcircle) flame->eflags = (flame->eflags & ~MFE_VERTICALFLIP)|(player->mo->eflags & MFE_VERTICALFLIP); P_InstaThrust(flame, flame->angle, FixedMul(3*FRACUNIT, flame->scale)); P_SetObjectMomZ(flame, 3*FRACUNIT, false); + if (!(gametyperules & GTR_FRIENDLY)) + { + P_SetMobjState(flame, S_TEAM_SPINFIRE1); + flame->color = player->mo->color; + } } #undef limitangle #undef numangles @@ -7763,6 +7788,11 @@ void P_ElementalFire(player_t *player, boolean cropcircle) flame->destscale = player->mo->scale; P_SetScale(flame, player->mo->scale); flame->eflags = (flame->eflags & ~MFE_VERTICALFLIP)|(player->mo->eflags & MFE_VERTICALFLIP); + if (!(gametyperules & GTR_FRIENDLY)) + { + P_SetMobjState(flame, S_TEAM_SPINFIRE1); + flame->color = player->mo->color; + } flame->momx = 8; // this is a hack which is used to ensure it still behaves as a missile and can damage others P_XYMovement(flame); @@ -8589,12 +8619,6 @@ void P_MovePlayer(player_t *player) player->climbing--; } - if (!player->climbing) - { - player->lastsidehit = -1; - player->lastlinehit = -1; - } - // Make sure you're not teetering when you shouldn't be. if (player->panim == PA_EDGE && (player->mo->momx || player->mo->momy || player->mo->momz)) @@ -8619,6 +8643,7 @@ void P_MovePlayer(player_t *player) P_DoFiring(player, cmd); { + boolean atspinheight = false; fixed_t oldheight = player->mo->height; // Less height while spinning. Good for spinning under things...? @@ -8628,32 +8653,35 @@ void P_MovePlayer(player_t *player) || player->powers[pw_tailsfly] || player->pflags & PF_GLIDING || (player->charability == CA_GLIDEANDCLIMB && player->mo->state-states == S_PLAY_GLIDE_LANDING) || (player->charability == CA_FLY && player->mo->state-states == S_PLAY_FLY_TIRED)) + { player->mo->height = P_GetPlayerSpinHeight(player); + atspinheight = true; + } else player->mo->height = P_GetPlayerHeight(player); if (player->mo->eflags & MFE_VERTICALFLIP && player->mo->height != oldheight) // adjust z height for reverse gravity, similar to how it's done for scaling player->mo->z -= player->mo->height - oldheight; - } - // Crush test... - if ((player->mo->ceilingz - player->mo->floorz < player->mo->height) - && !(player->mo->flags & MF_NOCLIP)) - { - if ((player->charability2 == CA2_SPINDASH) && !(player->pflags & PF_SPINNING)) + // Crush test... + if ((player->mo->ceilingz - player->mo->floorz < player->mo->height) + && !(player->mo->flags & MF_NOCLIP)) { - player->pflags |= PF_SPINNING; - P_SetPlayerMobjState(player->mo, S_PLAY_ROLL); - } - else if (player->mo->ceilingz - player->mo->floorz < player->mo->height) - { - if ((netgame || multiplayer) && player->spectator) - P_DamageMobj(player->mo, NULL, NULL, 1, DMG_SPECTATOR); // Respawn crushed spectators - else - P_DamageMobj(player->mo, NULL, NULL, 1, DMG_CRUSHED); + if (!atspinheight) + { + player->pflags |= PF_SPINNING; + P_SetPlayerMobjState(player->mo, S_PLAY_ROLL); + } + else if (player->mo->ceilingz - player->mo->floorz < player->mo->height) + { + if ((netgame || multiplayer) && player->spectator) + P_DamageMobj(player->mo, NULL, NULL, 1, DMG_SPECTATOR); // Respawn crushed spectators + else + P_DamageMobj(player->mo, NULL, NULL, 1, DMG_CRUSHED); - if (player->playerstate == PST_DEAD) - return; + if (player->playerstate == PST_DEAD) + return; + } } } @@ -9469,11 +9497,11 @@ static void P_DeathThink(player_t *player) if (player->deadtimer < INT32_MAX) player->deadtimer++; - if (player->bot && player->bot != 3) // don't allow bots to do any of the below, B_CheckRespawn does all they need for respawning already + if (player->bot) // don't allow bots to do any of the below, B_CheckRespawn does all they need for respawning already goto notrealplayer; // continue logic - if (!(netgame || multiplayer) && player->lives <= 0 && player ==&players[consoleplayer]) //Extra players in SP can't be allowed to continue or end game + if (!(netgame || multiplayer) && player->lives <= 0) { if (player->deadtimer > (3*TICRATE) && (cmd->buttons & BT_SPIN || cmd->buttons & BT_JUMP) && (!continuesInSession || player->continues > 0)) G_UseContinue(); @@ -11452,7 +11480,7 @@ void P_PlayerThink(player_t *player) player->playerstate = PST_DEAD; } - if (player->bot && player->bot != 3) + if (player->bot) { if (player->playerstate == PST_LIVE || player->playerstate == PST_DEAD) { @@ -11466,7 +11494,6 @@ void P_PlayerThink(player_t *player) } } -#ifdef SEENAMES if (netgame && player == &players[displayplayer] && !(leveltime % (TICRATE/5))) { seenplayer = NULL; @@ -11491,7 +11518,6 @@ void P_PlayerThink(player_t *player) } } } -#endif if (player->awayviewmobj && P_MobjWasRemoved(player->awayviewmobj)) { @@ -12562,13 +12588,16 @@ void P_PlayerAfterThink(player_t *player) player->powers[pw_carry] = CR_NONE; else { - P_TryMove(player->mo, tails->x + P_ReturnThrustX(tails, tails->player->drawangle, 4*FRACUNIT), tails->y + P_ReturnThrustY(tails, tails->player->drawangle, 4*FRACUNIT), true); + if (tails->player) + P_TryMove(player->mo, tails->x + P_ReturnThrustX(tails, tails->player->drawangle, 4*FRACUNIT), tails->y + P_ReturnThrustY(tails, tails->player->drawangle, 4*FRACUNIT), true); + else + P_TryMove(player->mo, tails->x + P_ReturnThrustX(tails, tails->angle, 4*FRACUNIT), tails->y + P_ReturnThrustY(tails, tails->angle, 4*FRACUNIT), true); player->mo->momx = tails->momx; player->mo->momy = tails->momy; player->mo->momz = tails->momz; } - if (G_CoopGametype() && (!tails->player || tails->player->bot != 1)) + if (G_CoopGametype() && tails->player && tails->player->bot != 1) { player->mo->angle = tails->angle; @@ -12583,7 +12612,7 @@ void P_PlayerAfterThink(player_t *player) { if (player->mo->state-states != S_PLAY_RIDE) P_SetPlayerMobjState(player->mo, S_PLAY_RIDE); - if ((tails->skin && ((skin_t *)(tails->skin))->sprites[SPR2_SWIM].numframes) && (tails->eflags & MFE_UNDERWATER)) + if (tails->player && (tails->skin && ((skin_t *)(tails->skin))->sprites[SPR2_SWIM].numframes) && (tails->eflags & MFE_UNDERWATER)) tails->player->powers[pw_tailsfly] = 0; } else From 6103d7a51e2e7905d5298f5fde80be3554d1751f Mon Sep 17 00:00:00 2001 From: Shane Ellis Date: Fri, 15 Jan 2021 17:41:16 -0500 Subject: [PATCH 016/126] Update p_user.c --- src/p_user.c | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/src/p_user.c b/src/p_user.c index a70dceb8b..9e86e2d73 100644 --- a/src/p_user.c +++ b/src/p_user.c @@ -777,7 +777,7 @@ void P_NightserizePlayer(player_t *player, INT32 nighttime) UINT8 oldmare, oldmarelap, oldmarebonuslap; // Bots can't be NiGHTSerized, silly!1 :P - if (player->bot) + if (player->bot && player->bot != 3) return; if (player->powers[pw_carry] != CR_NIGHTSMODE) @@ -1189,7 +1189,7 @@ void P_GivePlayerRings(player_t *player, INT32 num_rings) if (!player) return; - if (player->bot) + if (player->bot && player->bot != 3) player = &players[consoleplayer]; if (!player->mo) @@ -1234,7 +1234,7 @@ void P_GivePlayerSpheres(player_t *player, INT32 num_spheres) if (!player) return; - if (player->bot) + if (player->bot && player->bot != 3) player = &players[consoleplayer]; if (!player->mo) @@ -1261,7 +1261,7 @@ void P_GivePlayerLives(player_t *player, INT32 numlives) if (!player) return; - if (player->bot) + if (player->bot && player->bot != 3) player = &players[consoleplayer]; if (gamestate == GS_LEVEL) @@ -1367,7 +1367,7 @@ void P_AddPlayerScore(player_t *player, UINT32 amount) { UINT32 oldscore; - if (player->bot) + if (player->bot && player->bot != 3) player = &players[consoleplayer]; // NiGHTS does it different! @@ -5957,7 +5957,7 @@ static void P_3dMovement(player_t *player) acceleration = 96 + (FixedDiv(player->speed, player->mo->scale)>>FRACBITS) * 40; topspeed = normalspd; } - else if (player->bot) + else if (player->bot && player->bot != 3) { // Bot steals player 1's stats normalspd = FixedMul(players[consoleplayer].normalspeed, player->mo->scale); thrustfactor = players[consoleplayer].thrustfactor; @@ -9497,11 +9497,11 @@ static void P_DeathThink(player_t *player) if (player->deadtimer < INT32_MAX) player->deadtimer++; - if (player->bot) // don't allow bots to do any of the below, B_CheckRespawn does all they need for respawning already + if (player->bot && player->bot != 3) // don't allow bots to do any of the below, B_CheckRespawn does all they need for respawning already goto notrealplayer; // continue logic - if (!(netgame || multiplayer) && player->lives <= 0) + if (!(netgame || multiplayer) && player->lives <= 0 && player == &players[consoleplayer]) //Extra players in SP can't be allowed to continue or end game { if (player->deadtimer > (3*TICRATE) && (cmd->buttons & BT_SPIN || cmd->buttons & BT_JUMP) && (!continuesInSession || player->continues > 0)) G_UseContinue(); @@ -11480,7 +11480,7 @@ void P_PlayerThink(player_t *player) player->playerstate = PST_DEAD; } - if (player->bot) + if (player->bot && player->bot != 3) { if (player->playerstate == PST_LIVE || player->playerstate == PST_DEAD) { From 00462323c3fe6dda7ece834cfa92431525f54e85 Mon Sep 17 00:00:00 2001 From: Shane Ellis Date: Fri, 15 Jan 2021 22:42:35 +0000 Subject: [PATCH 017/126] Revert "Implementation of lua function P_AddPlayer()" This reverts commit 5e8313e157ab0bef4f2e2a346906aa1a6efdd58b --- src/lua_baselib.c | 89 +++++------------------------------------------ 1 file changed, 8 insertions(+), 81 deletions(-) diff --git a/src/lua_baselib.c b/src/lua_baselib.c index 9bc8813a2..c5f847be6 100644 --- a/src/lua_baselib.c +++ b/src/lua_baselib.c @@ -155,6 +155,8 @@ static const struct { {META_PIVOTLIST, "spriteframepivot_t[]"}, {META_FRAMEPIVOT, "spriteframepivot_t"}, + {META_TAGLIST, "taglist"}, + {META_MOBJ, "mobj_t"}, {META_MAPTHING, "mapthing_t"}, @@ -163,6 +165,8 @@ static const struct { {META_SKIN, "skin_t"}, {META_POWERS, "player_t.powers"}, {META_SOUNDSID, "skin_t.soundsid"}, + {META_SKINSPRITES, "skin_t.sprites"}, + {META_SKINSPRITESLIST, "skin_t.sprites[]"}, {META_VERTEX, "vertex_t"}, {META_LINE, "line_t"}, @@ -184,6 +188,9 @@ static const struct { {META_CVAR, "consvar_t"}, {META_SECTORLINES, "sector_t.lines"}, +#ifdef MUTABLE_TAGS + {META_SECTORTAGLIST, "sector_t.taglist"}, +#endif {META_SIDENUM, "line_t.sidenum"}, {META_LINEARGS, "line_t.args"}, {META_LINESTRINGARGS, "line_t.stringargs"}, @@ -2632,7 +2639,7 @@ static int lib_rSkinUsable(lua_State *L) else // skin name { const char *skinname = luaL_checkstring(L, 2); - if (R_SkinAvailable(skinname) >= 0) + i = R_SkinAvailable(skinname); if (i == -1) return luaL_error(L, "skin %s (argument 2) is not loaded", skinname); } @@ -3400,85 +3407,6 @@ static int lib_gAddGametype(lua_State *L) return 0; } -// Bot adding function! -// Partly lifted from Got_AddPlayer -static int lib_gAddPlayer(lua_State *L) -{ - INT16 i, newplayernum, botcount = 1; - player_t *newplayer; - INT8 skinnum = 0, bot; - - for (i = 0; i < MAXPLAYERS; i++) - { - if (!playeringame[i]) - break; - - if (players[i].bot) - botcount++; // How many of us are there already? - } - if (i >= MAXPLAYERS) - { - lua_pushnil(L); - return 1; - } - - - newplayernum = i; - - //if (!splitscreen && !botingame) - CL_ClearPlayer(newplayernum); - - playeringame[newplayernum] = true; - G_AddPlayer(newplayernum); - newplayer = &players[newplayernum]; - - newplayer->jointime = 0; - newplayer->quittime = 0; - - // I hereby name you Bot X - strcpy(player_names[newplayernum], va("Bot %d", botcount)); - - // Read the skin string! - if (!lua_isnoneornil(L, 1)) - { - skinnum = R_SkinAvailable(luaL_checkstring(L, 1)); - skinnum = skinnum < 0 ? 0 : skinnum; - - // Bots can be whatever they want - if (!R_SkinUsable(newplayernum, skinnum)) - newplayer->availabilities |= 1 << skinnum; - } - - // Read the color! - if (!lua_isnoneornil(L, 2)) - newplayer->skincolor = R_GetColorByName(luaL_checkstring(L, 2)); - else - newplayer->skincolor = skins[newplayer->skin].prefcolor; - - // Read the bot name, if given! - if (!lua_isnoneornil(L, 3)) - strcpy(player_names[newplayernum], luaL_checkstring(L, 3)); - - bot = luaL_optinteger(L, 4, 3); - newplayer->bot = (bot >= 0 && bot <= 3) ? bot : 3; - - // Set the skin - SetPlayerSkinByNum(newplayernum, skinnum); - - - if (netgame) - { - char joinmsg[256]; - - strcpy(joinmsg, M_GetText("\x82*Bot %s has joined the game (player %d)")); - strcpy(joinmsg, va(joinmsg, player_names[newplayernum], newplayernum)); - HU_AddChatText(joinmsg, false); - } - - LUA_PushUserdata(L, newplayer, META_PLAYER); - return 0; -} - static int Lcheckmapnumber (lua_State *L, int idx, const char *fun) { if (ISINLEVEL) @@ -4059,7 +3987,6 @@ static luaL_Reg lib[] = { // g_game {"G_AddGametype", lib_gAddGametype}, - {"G_AddPlayer", lib_gAddPlayer}, {"G_BuildMapName",lib_gBuildMapName}, {"G_BuildMapTitle",lib_gBuildMapTitle}, {"G_FindMap",lib_gFindMap}, From 4e029dbc67a51529d88c2e16c696da97b5f73703 Mon Sep 17 00:00:00 2001 From: Shane Ellis Date: Fri, 15 Jan 2021 17:45:41 -0500 Subject: [PATCH 018/126] Update lua_baselib.c --- src/lua_baselib.c | 77 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 77 insertions(+) diff --git a/src/lua_baselib.c b/src/lua_baselib.c index c5f847be6..438c20b81 100644 --- a/src/lua_baselib.c +++ b/src/lua_baselib.c @@ -3407,6 +3407,82 @@ static int lib_gAddGametype(lua_State *L) return 0; } +// Bot adding function! +// Partly lifted from Got_AddPlayer +static int lib_gAddPlayer(lua_State *L) +{ + INT16 i, newplayernum, botcount = 1; + player_t *newplayer; + INT8 skinnum = 0, bot; + + for (i = 0; i < MAXPLAYERS; i++) + { + if (!playeringame[i]) + break; + + if (players[i].bot) + botcount++; // How many of us are there already? + } + if (i >= MAXPLAYERS) + { + lua_pushnil(L); + return 1; + } + + + newplayernum = i; + + //if (!splitscreen && !botingame) + CL_ClearPlayer(newplayernum); + + playeringame[newplayernum] = true; + G_AddPlayer(newplayernum); + newplayer = &players[newplayernum]; + + newplayer->jointime = 0; + newplayer->quittime = 0; + + // I hereby name you Bot X + strcpy(player_names[newplayernum], va("Bot %d", botcount)); + + // Read the skin string! + if (!lua_isnoneornil(L, 1)) + { + skinnum = R_SkinAvailable(luaL_checkstring(L, 1)); + skinnum = skinnum < 0 ? 0 : skinnum; + } + + // Read the color! + if (!lua_isnoneornil(L, 2)) + newplayer->skincolor = R_GetColorByName(luaL_checkstring(L, 2)); + else + newplayer->skincolor = skins[newplayer->skin].prefcolor; + + // Read the bot name, if given! + if (!lua_isnoneornil(L, 3)) + strcpy(player_names[newplayernum], luaL_checkstring(L, 3)); + + bot = luaL_optinteger(L, 4, 3); + newplayer->bot = (bot >= 0 && bot <= 3) ? bot : 3; + + // Set the skin (needed to set bot first!) + SetPlayerSkinByNum(newplayernum, skinnum); + + + if (netgame) + { + char joinmsg[256]; + + strcpy(joinmsg, M_GetText("\x82*Bot %s has joined the game (player %d)")); + strcpy(joinmsg, va(joinmsg, player_names[newplayernum], newplayernum)); + HU_AddChatText(joinmsg, false); + } + + LUA_PushUserdata(L, newplayer, META_PLAYER); + return 0; +} + + static int Lcheckmapnumber (lua_State *L, int idx, const char *fun) { if (ISINLEVEL) @@ -3987,6 +4063,7 @@ static luaL_Reg lib[] = { // g_game {"G_AddGametype", lib_gAddGametype}, + {"G_AddPlayer", lib_gAddPlayer}, {"G_BuildMapName",lib_gBuildMapName}, {"G_BuildMapTitle",lib_gBuildMapTitle}, {"G_FindMap",lib_gFindMap}, From 8fa8ca622249af77e1aad9ffe6e2800f92448380 Mon Sep 17 00:00:00 2001 From: Shane Ellis Date: Fri, 15 Jan 2021 17:47:26 -0500 Subject: [PATCH 019/126] Update r_skins.c --- src/r_skins.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/r_skins.c b/src/r_skins.c index 155a64700..a29209873 100644 --- a/src/r_skins.c +++ b/src/r_skins.c @@ -512,6 +512,10 @@ static boolean R_ProcessPatchableFields(skin_t *skin, char *stoken, char *value) GETFLAG(MULTIABILITY) GETFLAG(NONIGHTSROTATION) GETFLAG(NONIGHTSSUPER) + GETFLAG(NOSUPERSPRITES) + GETFLAG(NOSUPERJUMPBOOST) + GETFLAG(CANBUSTWALLS) + GETFLAG(NOSHIELDABILITY) #undef GETFLAG else // let's check if it's a sound, otherwise error out From 93562b782e9e001d82fa6d8c94860ea3170a2bc9 Mon Sep 17 00:00:00 2001 From: Shane Ellis Date: Fri, 15 Jan 2021 22:50:20 +0000 Subject: [PATCH 020/126] =?UTF-8?q?Revert=20"Command=5FKick()=20-=20Allow?= =?UTF-8?q?=20removal=20of=20non-consoleplayer/secondaryviewplayer=20playe?= =?UTF-8?q?r=E2=80=A6"?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This reverts commit 079fe9ba7e488e880561318e1200e8630289d117 --- src/d_clisrv.c | 66 ++++++++++++++++++++++++-------------------------- 1 file changed, 32 insertions(+), 34 deletions(-) diff --git a/src/d_clisrv.c b/src/d_clisrv.c index 88b4d9387..4fdc7e7ee 100644 --- a/src/d_clisrv.c +++ b/src/d_clisrv.c @@ -1595,9 +1595,7 @@ static void CL_ReloadReceivedSavegame(void) for (i = 0; i < MAXPLAYERS; i++) { -#ifdef HAVE_BLUA LUA_InvalidatePlayer(&players[i]); -#endif sprintf(player_names[i], "Player %d", i + 1); } @@ -2260,11 +2258,15 @@ void D_SaveBan(void) size_t i; banreason_t *reasonlist = reasonhead; const char *address, *mask; + const char *path = va("%s"PATHSEP"%s", srb2home, "ban.txt"); if (!reasonhead) + { + remove(path); return; + } - f = fopen(va("%s"PATHSEP"%s", srb2home, "ban.txt"), "w"); + f = fopen(path, "w"); if (!f) { @@ -2308,16 +2310,14 @@ static void Ban_Add(const char *reason) reasontail = reasonlist; } -static void Command_ClearBans(void) +static void Ban_Clear(void) { banreason_t *temp; - if (!I_ClearBans) - return; - I_ClearBans(); - D_SaveBan(); + reasontail = NULL; + while (reasonhead) { temp = reasonhead->next; @@ -2327,6 +2327,15 @@ static void Command_ClearBans(void) } } +static void Command_ClearBans(void) +{ + if (!I_ClearBans) + return; + + Ban_Clear(); + D_SaveBan(); +} + static void Ban_Load_File(boolean warning) { FILE *f; @@ -2334,6 +2343,9 @@ static void Ban_Load_File(boolean warning) const char *address, *mask; char buffer[MAX_WADPATH]; + if (!I_ClearBans) + return; + f = fopen(va("%s"PATHSEP"%s", srb2home, "ban.txt"), "r"); if (!f) @@ -2343,13 +2355,7 @@ static void Ban_Load_File(boolean warning) return; } - if (I_ClearBans) - Command_ClearBans(); - else - { - fclose(f); - return; - } + Ban_Clear(); for (i=0; fgets(buffer, (int)sizeof(buffer), f); i++) { @@ -2809,11 +2815,11 @@ static void Command_Kick(void) return; } - //if (!netgame) // Don't kick Tails in splitscreen! - //{ - // CONS_Printf(M_GetText("This only works in a netgame.\n")); - // return; - //} + if (!netgame) // Don't kick Tails in splitscreen! + { + CONS_Printf(M_GetText("This only works in a netgame.\n")); + return; + } if (server || IsPlayerAdmin(consoleplayer)) { @@ -2821,14 +2827,9 @@ static void Command_Kick(void) UINT8 *p = buf; const SINT8 pn = nametonum(COM_Argv(1)); - if (splitscreen && (pn == 0 || pn == 1)) - { - CONS_Printf(M_GetText("Splitscreen players cannot be kicked.\n")); - return; - } if (pn == -1 || pn == 0) return; - + // 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 @@ -3031,8 +3032,7 @@ static void Got_KickCmd(UINT8 **p, INT32 playernum) if (pnum == consoleplayer) { - if (Playing()) - LUAh_GameQuit(); + LUAh_GameQuit(false); #ifdef DUMPCONSISTENCY if (msg == KICK_MSG_CON_FAIL) SV_SavedGame(); #endif @@ -3732,8 +3732,7 @@ static void HandleConnect(SINT8 node) static void HandleShutdown(SINT8 node) { (void)node; - if (Playing()) - LUAh_GameQuit(); + LUAh_GameQuit(false); D_QuitNetGame(); CL_Reset(); D_StartTitle(); @@ -3748,8 +3747,7 @@ static void HandleShutdown(SINT8 node) static void HandleTimeout(SINT8 node) { (void)node; - if (Playing()) - LUAh_GameQuit(); + LUAh_GameQuit(false); D_QuitNetGame(); CL_Reset(); D_StartTitle(); @@ -4852,14 +4850,14 @@ void TryRunTics(tic_t realtics) { DEBFILE(va("============ Running tic %d (local %d)\n", gametic, localgametic)); - ps_tictime = I_GetTimeMicros(); + ps_tictime = I_GetPreciseTime(); G_Ticker((gametic % NEWTICRATERATIO) == 0); ExtraDataTicker(); gametic++; consistancy[gametic%BACKUPTICS] = Consistancy(); - ps_tictime = I_GetTimeMicros() - ps_tictime; + ps_tictime = I_GetPreciseTime() - ps_tictime; // Leave a certain amount of tics present in the net buffer as long as we've ran at least one tic this frame. if (client && gamestate == GS_LEVEL && leveltime > 3 && neededtic <= gametic + cv_netticbuffer.value) From 9bbebcbe9e4d6ed144bed21c0cf0b37d6918f20c Mon Sep 17 00:00:00 2001 From: Shane Ellis Date: Fri, 15 Jan 2021 17:55:14 -0500 Subject: [PATCH 021/126] Update d_clisrv.c --- src/d_clisrv.c | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/src/d_clisrv.c b/src/d_clisrv.c index 4fdc7e7ee..4574e5a1c 100644 --- a/src/d_clisrv.c +++ b/src/d_clisrv.c @@ -2815,18 +2815,22 @@ static void Command_Kick(void) return; } - if (!netgame) // Don't kick Tails in splitscreen! - { - CONS_Printf(M_GetText("This only works in a netgame.\n")); - return; - } - if (server || IsPlayerAdmin(consoleplayer)) { UINT8 buf[3 + MAX_REASONLENGTH]; UINT8 *p = buf; const SINT8 pn = nametonum(COM_Argv(1)); + // Unlike bans, kicks are used especially to remove bot players, so we'll + // need to run a more specific check which allows kicking offline, but + // not against splitscreen players. + if (splitscreen && (pn == 0 || pn == 1)) + { + CONS_Printf(M_GetText("Splitscreen players cannot be kicked.\n")); + return; + } + + if (pn == -1 || pn == 0) return; From 225e7c72ace052a5945454b46ba14ab623209396 Mon Sep 17 00:00:00 2001 From: Shane Ellis Date: Fri, 15 Jan 2021 18:00:00 -0500 Subject: [PATCH 022/126] Update p_mobj.c --- src/p_mobj.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/p_mobj.c b/src/p_mobj.c index 58124fb9b..f27ab2914 100644 --- a/src/p_mobj.c +++ b/src/p_mobj.c @@ -1840,7 +1840,7 @@ void P_XYMovement(mobj_t *mo) moved = false; if (player) { - if (player->bot) + if (player->bot and player->bot != 3) B_MoveBlocked(player); } @@ -4135,7 +4135,7 @@ boolean P_BossTargetPlayer(mobj_t *actor, boolean closest) player = &players[actor->lastlook]; - if (player->pflags & PF_INVIS || player->bot || player->spectator) + if (player->pflags & PF_INVIS || (player->bot && player->bot != 3) || player->spectator) continue; // ignore notarget if (!player->mo || P_MobjWasRemoved(player->mo)) From 505ba1ff631c91d3f91a65bd728817fe19ea6d76 Mon Sep 17 00:00:00 2001 From: Shane Ellis Date: Fri, 15 Jan 2021 18:06:36 -0500 Subject: [PATCH 023/126] Update g_game.c --- src/g_game.c | 25 ++++++++++++++++++++++++- 1 file changed, 24 insertions(+), 1 deletion(-) diff --git a/src/g_game.c b/src/g_game.c index f1cae8cf5..43906e558 100644 --- a/src/g_game.c +++ b/src/g_game.c @@ -2974,7 +2974,8 @@ void G_DoReborn(INT32 playernum) // Make sure objectplace is OFF when you first start the level! OP_ResetObjectplace(); - if (player->bot && playernum != consoleplayer) + // Tailsbot + if (player->bot && player->bot != 3 && playernum != consoleplayer) { // Bots respawn next to their master. mobj_t *oldmo = NULL; @@ -2992,6 +2993,28 @@ void G_DoReborn(INT32 playernum) return; } + + // Additional players (e.g. independent bots) in Single Player + if (playernum != consoleplayer && !(netgame || multiplayer)) + { + mobj_t *oldmo = NULL; + // Do nothing if out of lives + if (player->lives <= 0) + return; + + // Otherwise do respawn, starting by removing the player object + if (player->mo) + { + oldmo = player->mo; + P_RemoveMobj(player->mo); + } + // Do spawning + G_SpawnPlayer(playernum); + if (oldmo) + G_ChangePlayerReferences(oldmo, players[playernum].mo); + + return; //Exit function to avoid proccing other SP related mechanics + } if (countdowntimeup || (!(netgame || multiplayer) && (gametyperules & GTR_CAMPAIGN))) resetlevel = true; From 1583bf126b3105f98d647634a4256d22bc1e7e4d Mon Sep 17 00:00:00 2001 From: Shane Ellis Date: Fri, 15 Jan 2021 18:31:09 -0500 Subject: [PATCH 024/126] lua habits --- src/p_mobj.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/p_mobj.c b/src/p_mobj.c index f27ab2914..a660a8a92 100644 --- a/src/p_mobj.c +++ b/src/p_mobj.c @@ -1840,7 +1840,7 @@ void P_XYMovement(mobj_t *mo) moved = false; if (player) { - if (player->bot and player->bot != 3) + if (player->bot && player->bot != 3) B_MoveBlocked(player); } From 0e9b4acb587491b6f5d0b9c9c898a1c37811de85 Mon Sep 17 00:00:00 2001 From: Shane Ellis Date: Fri, 15 Jan 2021 19:29:03 -0500 Subject: [PATCH 025/126] Update g_game.c (Account for bot type 3) --- src/g_game.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/g_game.c b/src/g_game.c index 43906e558..5e2b4e708 100644 --- a/src/g_game.c +++ b/src/g_game.c @@ -2627,8 +2627,10 @@ void G_PlayerReborn(INT32 player, boolean betweenmaps) p->totalring = totalring; p->mare = mare; - if (bot) + if (bot == 2) p->bot = 1; // reset to AI-controlled + else + p->bot = bot; p->pity = pity; p->rings = rings; p->spheres = spheres; From b73a15a0bfe628e8c1001dc97aef7d2720940ff5 Mon Sep 17 00:00:00 2001 From: Shane Ellis Date: Fri, 15 Jan 2021 22:50:15 -0500 Subject: [PATCH 026/126] fixed G_AddPlayer not sending return value --- src/lua_baselib.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lua_baselib.c b/src/lua_baselib.c index 438c20b81..f54662278 100644 --- a/src/lua_baselib.c +++ b/src/lua_baselib.c @@ -3479,7 +3479,7 @@ static int lib_gAddPlayer(lua_State *L) } LUA_PushUserdata(L, newplayer, META_PLAYER); - return 0; + return 1; } From e19b0856846bbdcc860c735b9da95082965f604b Mon Sep 17 00:00:00 2001 From: Shane Ellis Date: Fri, 15 Jan 2021 22:56:48 -0500 Subject: [PATCH 027/126] Netcode failsafe. At least until I can figure out the best way to produce bot cmds outside of player sends. --- src/b_bot.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/b_bot.c b/src/b_bot.c index d3635f32c..35b14bd78 100644 --- a/src/b_bot.c +++ b/src/b_bot.c @@ -362,6 +362,12 @@ void B_BuildTiccmd(player_t *player, ticcmd_t *cmd) // Bot AI isn't programmed in analog. CV_SetValue(&cv_analog[1], false); + // Bot cmd functions and hooks are not currently netplay compatible + // Necessary failsafe, as a bot in the P2 position in a netgame can inherit + // the last input from a hook triggered in splitscreen or SP. + if (netgame) + return; + // Let Lua scripts build ticcmds if (LUAh_BotTiccmd(player, cmd)) return; From ce8e389a2d9cec8c36f43a9573f0d9a29bf50d18 Mon Sep 17 00:00:00 2001 From: Shane Ellis Date: Sat, 23 Jan 2021 09:49:57 -0500 Subject: [PATCH 028/126] Add lua player.botleader and player.buttons_last (read+write) --- src/lua_playerlib.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/lua_playerlib.c b/src/lua_playerlib.c index 0eb54808f..a8dc64a15 100644 --- a/src/lua_playerlib.c +++ b/src/lua_playerlib.c @@ -370,6 +370,10 @@ static int player_get(lua_State *L) lua_pushboolean(L, plr->outofcoop); else if (fastcmp(field,"bot")) lua_pushinteger(L, plr->bot); + else if (fastcmp(field,"botleader")) + LUA_PushUserdata(L, plr->botleader, META_PLAYER); + else if (fastcmp(field,"buttons_last")) + lua_pushinteger(L, plr->buttons_last); else if (fastcmp(field,"jointime")) lua_pushinteger(L, plr->jointime); else if (fastcmp(field,"quittime")) From c391d76c11331dec29f3eedddb9bcc9344eb82e7 Mon Sep 17 00:00:00 2001 From: Shane Ellis Date: Sat, 23 Jan 2021 10:06:47 -0500 Subject: [PATCH 029/126] buttons_last -> lastbuttons --- src/lua_playerlib.c | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/src/lua_playerlib.c b/src/lua_playerlib.c index a8dc64a15..51eb4ac0c 100644 --- a/src/lua_playerlib.c +++ b/src/lua_playerlib.c @@ -372,8 +372,8 @@ static int player_get(lua_State *L) lua_pushinteger(L, plr->bot); else if (fastcmp(field,"botleader")) LUA_PushUserdata(L, plr->botleader, META_PLAYER); - else if (fastcmp(field,"buttons_last")) - lua_pushinteger(L, plr->buttons_last); + else if (fastcmp(field,"lastbuttons")) + lua_pushinteger(L, plr->lastbuttons); else if (fastcmp(field,"jointime")) lua_pushinteger(L, plr->jointime); else if (fastcmp(field,"quittime")) @@ -723,6 +723,15 @@ static int player_set(lua_State *L) plr->outofcoop = lua_toboolean(L, 3); else if (fastcmp(field,"bot")) return NOSET; + else if (fastcmp(field,"botleader")) + { + player_t *player = NULL; + if (!lua_isnil(L, 3)) + player = *((player_t **)luaL_checkudata(L, 3, META_PLAYER)); + plr->botleader = player; + } + else if (fastcmp(field,"lastbuttons")) + plr->lastbuttons = (UINT16)luaL_checkinteger(L, 3); else if (fastcmp(field,"jointime")) plr->jointime = (tic_t)luaL_checkinteger(L, 3); else if (fastcmp(field,"quittime")) From 2abf89e8000b6dbda26d5dd59b9306c5d32624b6 Mon Sep 17 00:00:00 2001 From: Shane Ellis Date: Sat, 23 Jan 2021 10:13:16 -0500 Subject: [PATCH 030/126] Update G_AddPlayer() --- src/lua_baselib.c | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/src/lua_baselib.c b/src/lua_baselib.c index f54662278..525ce89ec 100644 --- a/src/lua_baselib.c +++ b/src/lua_baselib.c @@ -3432,8 +3432,7 @@ static int lib_gAddPlayer(lua_State *L) newplayernum = i; - //if (!splitscreen && !botingame) - CL_ClearPlayer(newplayernum); + CL_ClearPlayer(newplayernum); playeringame[newplayernum] = true; G_AddPlayer(newplayernum); @@ -3442,30 +3441,34 @@ static int lib_gAddPlayer(lua_State *L) newplayer->jointime = 0; newplayer->quittime = 0; - // I hereby name you Bot X + // Set the bot name (defaults to Bot #) strcpy(player_names[newplayernum], va("Bot %d", botcount)); - // Read the skin string! + // Read the skin argument (defaults to Sonic) if (!lua_isnoneornil(L, 1)) { skinnum = R_SkinAvailable(luaL_checkstring(L, 1)); skinnum = skinnum < 0 ? 0 : skinnum; } - // Read the color! + // Read the color (defaults to skin prefcolor) if (!lua_isnoneornil(L, 2)) newplayer->skincolor = R_GetColorByName(luaL_checkstring(L, 2)); else newplayer->skincolor = skins[newplayer->skin].prefcolor; - // Read the bot name, if given! + // Read the bot name, if given if (!lua_isnoneornil(L, 3)) strcpy(player_names[newplayernum], luaL_checkstring(L, 3)); bot = luaL_optinteger(L, 4, 3); newplayer->bot = (bot >= 0 && bot <= 3) ? bot : 3; - - // Set the skin (needed to set bot first!) + + // If our bot is a 2P type, we'll need to set its leader so it can spawn + if (newplayer->bot == BOT_2PAI || newplayer->bot == BOT_2PHUMAN) + B_UpdateBotleader(newplayer); + + // Set the skin (can't do this until AFTER bot type is set!) SetPlayerSkinByNum(newplayernum, skinnum); From 2e528216ac855fbfdf9f73df3a534a237936ccbd Mon Sep 17 00:00:00 2001 From: Shane Ellis Date: Sat, 23 Jan 2021 10:15:33 -0500 Subject: [PATCH 031/126] Updated references to player->bot --- src/p_user.c | 48 ++++++++++++++++++++++++++---------------------- 1 file changed, 26 insertions(+), 22 deletions(-) diff --git a/src/p_user.c b/src/p_user.c index 9e86e2d73..c631f49b2 100644 --- a/src/p_user.c +++ b/src/p_user.c @@ -776,8 +776,8 @@ void P_NightserizePlayer(player_t *player, INT32 nighttime) { UINT8 oldmare, oldmarelap, oldmarebonuslap; - // Bots can't be NiGHTSerized, silly!1 :P - if (player->bot && player->bot != 3) + //! Bots can't be NiGHTSerized, silly!1 :P + if (player->bot == BOT_2PAI || player->bot || BOT_2PHUMAN) return; if (player->powers[pw_carry] != CR_NIGHTSMODE) @@ -1188,9 +1188,9 @@ void P_GivePlayerRings(player_t *player, INT32 num_rings) { if (!player) return; - - if (player->bot && player->bot != 3) - player = &players[consoleplayer]; + //! + if ((player->bot == BOT_2PAI || player->bot == BOT_2PHUMAN) && player->botleader) + player = player->botleader; if (!player->mo) return; @@ -1234,8 +1234,8 @@ void P_GivePlayerSpheres(player_t *player, INT32 num_spheres) if (!player) return; - if (player->bot && player->bot != 3) - player = &players[consoleplayer]; + if ((player->bot == BOT_2PAI || player->bot == BOT_2PHUMAN) && player->botleader) + player = player->botleader; if (!player->mo) return; @@ -1261,8 +1261,8 @@ void P_GivePlayerLives(player_t *player, INT32 numlives) if (!player) return; - if (player->bot && player->bot != 3) - player = &players[consoleplayer]; + if ((player->bot == BOT_2PAI || player->bot == BOT_2PHUMAN) && player->botleader) + player = player->botleader; if (gamestate == GS_LEVEL) { @@ -1367,8 +1367,8 @@ void P_AddPlayerScore(player_t *player, UINT32 amount) { UINT32 oldscore; - if (player->bot && player->bot != 3) - player = &players[consoleplayer]; + if ((player->bot == BOT_2PAI || player->bot == BOT_2PHUMAN) && player->botleader) + player = player->botleader; // NiGHTS does it different! if (gamestate == GS_LEVEL && mapheaderinfo[gamemap-1]->typeoflevel & TOL_NIGHTS) @@ -5370,7 +5370,7 @@ static void P_DoJumpStuff(player_t *player, ticcmd_t *cmd) player->powers[pw_tailsfly] = tailsflytics + 1; // Set the fly timer player->pflags &= ~(PF_JUMPED|PF_NOJUMPDAMAGE|PF_SPINNING|PF_STARTDASH); - if (player->bot == 1) + if (player->bot == BOT_2PAI) player->pflags |= PF_THOKKED; else player->pflags |= (PF_THOKKED|PF_CANCARRY); @@ -5957,7 +5957,8 @@ static void P_3dMovement(player_t *player) acceleration = 96 + (FixedDiv(player->speed, player->mo->scale)>>FRACBITS) * 40; topspeed = normalspd; } - else if (player->bot && player->bot != 3) + //! Kill this! + /* else if (player->bot == BOT_2PAI || player->bot == BOT_2PHUMAN) { // Bot steals player 1's stats normalspd = FixedMul(players[consoleplayer].normalspeed, player->mo->scale); thrustfactor = players[consoleplayer].thrustfactor; @@ -5972,7 +5973,7 @@ static void P_3dMovement(player_t *player) } else topspeed = normalspd; - } + } */ else { if (player->powers[pw_super] || player->powers[pw_sneakers]) @@ -9497,7 +9498,7 @@ static void P_DeathThink(player_t *player) if (player->deadtimer < INT32_MAX) player->deadtimer++; - if (player->bot && player->bot != 3) // don't allow bots to do any of the below, B_CheckRespawn does all they need for respawning already + if (player->bot == BOT_2PAI || player->bot == BOT_2PHUMAN) // don't allow followbots to do any of the below, B_CheckRespawn does all they need for respawning already goto notrealplayer; // continue logic @@ -11473,6 +11474,9 @@ void P_PlayerThink(player_t *player) I_Error("p_playerthink: players[%s].mo == NULL", sizeu1(playeri)); #endif + //! Reset terrain blocked status for this frame + player->blocked = false; + // todo: Figure out what is actually causing these problems in the first place... if (player->mo->health <= 0 && player->playerstate == PST_LIVE) //you should be DEAD! { @@ -11480,7 +11484,7 @@ void P_PlayerThink(player_t *player) player->playerstate = PST_DEAD; } - if (player->bot && player->bot != 3) + if (player->bot == BOT_2PAI || player->bot == BOT_2PHUMAN) { if (player->playerstate == PST_LIVE || player->playerstate == PST_DEAD) { @@ -11623,8 +11627,8 @@ void P_PlayerThink(player_t *player) INT32 i; for (i = 0; i < MAXPLAYERS; i++) - { - if (!playeringame[i] || players[i].spectator || players[i].bot) + { //! + if (!playeringame[i] || players[i].spectator || players[i].bot == BOT_2PAI || players[i].bot == BOT_2PHUMAN) continue; if (players[i].lives <= 0) continue; @@ -11655,8 +11659,8 @@ void P_PlayerThink(player_t *player) INT32 i, total = 0, exiting = 0; for (i = 0; i < MAXPLAYERS; i++) - { - if (!playeringame[i] || players[i].spectator || players[i].bot) + { //! + if (!playeringame[i] || players[i].spectator || players[i].bot == BOT_2PAI || players[i].bot == BOT_2PHUMAN) continue; if (players[i].quittime > 30 * TICRATE) continue; @@ -12596,8 +12600,8 @@ void P_PlayerAfterThink(player_t *player) player->mo->momy = tails->momy; player->mo->momz = tails->momz; } - - if (G_CoopGametype() && tails->player && tails->player->bot != 1) + //! + if (G_CoopGametype() && tails->player && tails->player->bot != BOT_2PAI) { player->mo->angle = tails->angle; From 2016caa70b584286af7aff80e83eb63c32f16534 Mon Sep 17 00:00:00 2001 From: Shane Ellis Date: Sat, 23 Jan 2021 10:16:27 -0500 Subject: [PATCH 032/126] Update references to player->bot --- src/p_mobj.c | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/src/p_mobj.c b/src/p_mobj.c index a660a8a92..43d81077e 100644 --- a/src/p_mobj.c +++ b/src/p_mobj.c @@ -1839,10 +1839,9 @@ void P_XYMovement(mobj_t *mo) // blocked move moved = false; - if (player) { - if (player->bot && player->bot != 3) - B_MoveBlocked(player); - } + //!!! + if (player) + B_MoveBlocked(player); if (LUAh_MobjMoveBlocked(mo)) { @@ -4135,7 +4134,7 @@ boolean P_BossTargetPlayer(mobj_t *actor, boolean closest) player = &players[actor->lastlook]; - if (player->pflags & PF_INVIS || (player->bot && player->bot != 3) || player->spectator) + if (player->pflags & PF_INVIS || player->bot == BOT_2PAI || player->bot == BOT_2PHUMAN || player->spectator) continue; // ignore notarget if (!player->mo || P_MobjWasRemoved(player->mo)) @@ -4176,7 +4175,7 @@ boolean P_SupermanLook4Players(mobj_t *actor) if (players[c].pflags & PF_INVIS) continue; // ignore notarget - if (!players[c].mo || players[c].bot) + if (!players[c].mo || players[c].bot == BOT_2PAI || players[c].bot == BOT_2PHUMAN) continue; if (players[c].mo->health <= 0) @@ -7307,7 +7306,7 @@ static void P_RosySceneryThink(mobj_t *mobj) continue; if (!players[i].mo) continue; - if (players[i].bot) + if (players[i].bot == BOT_2PAI || players[i].bot == BOT_2PHUMAN) continue; if (!players[i].mo->health) continue; From fd536e91a3bb73c44adcc13486631c18e83d9620 Mon Sep 17 00:00:00 2001 From: Shane Ellis Date: Sat, 23 Jan 2021 10:17:05 -0500 Subject: [PATCH 033/126] void B_UpdateBotleader(player_t *player); --- src/b_bot.h | 1 + 1 file changed, 1 insertion(+) diff --git a/src/b_bot.h b/src/b_bot.h index 2806bd68f..53114df48 100644 --- a/src/b_bot.h +++ b/src/b_bot.h @@ -10,6 +10,7 @@ /// \file b_bot.h /// \brief Basic bot handling +void B_UpdateBotleader(player_t *player); void B_BuildTiccmd(player_t *player, ticcmd_t *cmd); void B_KeysToTiccmd(mobj_t *mo, ticcmd_t *cmd, boolean forward, boolean backward, boolean left, boolean right, boolean strafeleft, boolean straferight, boolean jump, boolean spin); boolean B_CheckRespawn(player_t *player); From b8fea55f6777d3fa9891d845c6d1430f66acb363 Mon Sep 17 00:00:00 2001 From: Shane Ellis Date: Sat, 23 Jan 2021 10:25:35 -0500 Subject: [PATCH 034/126] Update b_bot.c --- src/b_bot.c | 251 +++++++++++++++++++++++++++++----------------------- 1 file changed, 139 insertions(+), 112 deletions(-) diff --git a/src/b_bot.c b/src/b_bot.c index 35b14bd78..151aaa633 100644 --- a/src/b_bot.c +++ b/src/b_bot.c @@ -18,29 +18,41 @@ #include "b_bot.h" #include "lua_hook.h" -// If you want multiple bots, variables like this will -// have to be stuffed in something accessible through player_t. -static boolean lastForward = false; -static boolean lastBlocked = false; -static boolean blocked = false; - -static boolean jump_last = false; -static boolean spin_last = false; -static UINT8 anxiety = 0; -static boolean panic = false; -static UINT8 flymode = 0; -static boolean spinmode = false; -static boolean thinkfly = false; - -static inline void B_ResetAI(void) +void B_UpdateBotleader(player_t *player) { - jump_last = false; - spin_last = false; - anxiety = 0; - panic = false; - flymode = 0; - spinmode = false; - thinkfly = false; + UINT32 i; + fixed_t dist; + fixed_t neardist = INT32_MAX; + player_t *nearplayer = NULL; + //Find new botleader + //if (!player->botleader) + //{ + for (i = 0; i < MAXPLAYERS; i++) + { + if (players[i].bot || players[i].playerstate != PST_LIVE || players[i].spectator || !players[i].mo) + continue; + if (!player->mo) //Can't do distance calculations if there's no player object, so we'll just take the first we find + { + player->botleader = &players[i]; + return; + } + //Update best candidate based on nearest distance + dist = R_PointToDist2(player->mo->x, player->mo->y, players[i].mo->x, players[i].mo->y); + if (neardist > dist) + { + neardist = dist; + nearplayer = &players[i]; + } + } + //Set botleader to best candidate (or null if none available) + player->botleader = nearplayer; + //} +} + +static inline void B_ResetAI(botmem_t *mem) +{ + mem->thinkstate = AI_FOLLOW; + mem->catchup_tics = 0; } static void B_BuildTailsTiccmd(mobj_t *sonic, mobj_t *tails, ticcmd_t *cmd) @@ -49,39 +61,48 @@ static void B_BuildTailsTiccmd(mobj_t *sonic, mobj_t *tails, ticcmd_t *cmd) player_t *player = sonic->player, *bot = tails->player; ticcmd_t *pcmd = &player->cmd; - boolean water = tails->eflags & MFE_UNDERWATER; + botmem_t *mem = &bot->botmem; + boolean water = (tails->eflags & MFE_UNDERWATER); SINT8 flip = P_MobjFlip(tails); boolean _2d = (tails->flags2 & MF2_TWOD) || twodlevel; fixed_t scale = tails->scale; + boolean jump_last = (bot->lastbuttons & BT_JUMP); + boolean spin_last = (bot->lastbuttons & BT_SPIN); fixed_t dist = P_AproxDistance(sonic->x - tails->x, sonic->y - tails->y); fixed_t zdist = flip * (sonic->z - tails->z); angle_t ang = sonic->angle; fixed_t pmom = P_AproxDistance(sonic->momx, sonic->momy); fixed_t bmom = P_AproxDistance(tails->momx, tails->momy); - fixed_t followmax = 128 * 8 * scale; // Max follow distance before AI begins to enter "panic" state + fixed_t followmax = 128 * 8 * scale; // Max follow distance before AI begins to enter catchup state fixed_t followthres = 92 * scale; // Distance that AI will try to reach fixed_t followmin = 32 * scale; fixed_t comfortheight = 96 * scale; fixed_t touchdist = 24 * scale; boolean stalled = (bmom < scale >> 1) && dist > followthres; // Helps to see if the AI is having trouble catching up boolean samepos = (sonic->x == tails->x && sonic->y == tails->y); - + boolean blocked = bot->blocked; + if (!samepos) ang = R_PointToAngle2(tails->x, tails->y, sonic->x, sonic->y); - // We can't follow Sonic if he's not around! - if (!sonic || sonic->health <= 0) - return; - // Lua can handle it! if (LUAh_BotAI(sonic, tails, cmd)) return; + // We can't follow Sonic if he's not around! + if (!sonic || sonic->health <= 0) + { + mem->thinkstate = AI_STANDBY; + return; + } + else if (mem->thinkstate == AI_STANDBY) + mem->thinkstate = AI_FOLLOW; + if (tails->player->powers[pw_carry] == CR_MACESPIN || tails->player->powers[pw_carry] == CR_GENERIC) { boolean isrelevant = (sonic->player->powers[pw_carry] == CR_MACESPIN || sonic->player->powers[pw_carry] == CR_GENERIC); - dist = P_AproxDistance(tails->x-sonic->x, tails->y-sonic->y); + //dist = P_AproxDistance(tails->x-sonic->x, tails->y-sonic->y); //! This is totally redundant. if (sonic->player->cmd.buttons & BT_JUMP && (sonic->player->pflags & PF_JUMPED) && isrelevant) cmd->buttons |= BT_JUMP; if (isrelevant) @@ -103,56 +124,57 @@ static void B_BuildTailsTiccmd(mobj_t *sonic, mobj_t *tails, ticcmd_t *cmd) followmin = 0; followthres = 16*scale; followmax >>= 1; - thinkfly = false; + if (mem->thinkstate == AI_THINKFLY) + mem->thinkstate = AI_FOLLOW; } - // Check anxiety - if (spinmode) + // Update catchup_tics + if (mem->thinkstate == AI_SPINFOLLOW) { - anxiety = 0; - panic = false; + mem-> catchup_tics = 0; } else if (dist > followmax || zdist > comfortheight || stalled) { - anxiety = min(anxiety + 2, 70); - if (anxiety >= 70) - panic = true; + mem-> catchup_tics = min(mem-> catchup_tics + 2, 70); + if (mem-> catchup_tics >= 70) + mem->thinkstate = AI_CATCHUP; } else { - anxiety = max(anxiety - 1, 0); - panic = false; + mem-> catchup_tics = max(mem-> catchup_tics - 1, 0); + if (mem->thinkstate == AI_CATCHUP) + mem->thinkstate = AI_FOLLOW; } // Orientation + // cmd->angleturn won't be relative to player angle, since we're not going through G_BuildTiccmd. if (bot->pflags & (PF_SPINNING|PF_STARTDASH)) { - cmd->angleturn = (sonic->angle - tails->angle) >> 16; // NOT FRACBITS DAMNIT + cmd->angleturn = (sonic->angle) >> 16; // NOT FRACBITS DAMNIT } - else if (flymode == 2) + else if (mem->thinkstate == AI_FLYCARRY) { - cmd->angleturn = sonic->player->cmd.angleturn - (tails->angle >> 16); + cmd->angleturn = sonic->player->cmd.angleturn; } else { - cmd->angleturn = (ang - tails->angle) >> 16; // NOT FRACBITS DAMNIT + cmd->angleturn = (ang) >> 16; // NOT FRACBITS DAMNIT } // ******** // FLY MODE - // spinmode check - if (spinmode || player->exiting) - thinkfly = false; + // exiting check + if (player->exiting && mem->thinkstate == AI_THINKFLY) + mem->thinkstate = AI_FOLLOW; else { // Activate co-op flight - if (thinkfly && player->pflags & PF_JUMPED) + if (mem->thinkstate == AI_THINKFLY && player->pflags & PF_JUMPED) { if (!jump_last) { jump = true; - flymode = 1; - thinkfly = false; + mem->thinkstate = AI_FLYSTANDBY; bot->pflags |= PF_CANCARRY; } } @@ -165,20 +187,19 @@ static void B_BuildTailsTiccmd(mobj_t *sonic, mobj_t *tails, ticcmd_t *cmd) && P_IsObjectOnGround(sonic) && P_IsObjectOnGround(tails) && !(player->pflags & PF_STASIS) && bot->charability == CA_FLY) - thinkfly = true; - else - thinkfly = false; + mem->thinkstate = AI_THINKFLY; + else if (mem->thinkstate == AI_THINKFLY) + mem->thinkstate = AI_FOLLOW; // Set carried state if (player->powers[pw_carry] == CR_PLAYER && sonic->tracer == tails) { - flymode = 2; + mem->thinkstate = AI_FLYCARRY; } // Ready for takeoff - if (flymode == 1) + if (mem->thinkstate == AI_FLYSTANDBY) { - thinkfly = false; if (zdist < -64*scale || (flip * tails->momz) > scale) // Make sure we're not too high up spin = true; else if (!jump_last) @@ -186,10 +207,10 @@ static void B_BuildTailsTiccmd(mobj_t *sonic, mobj_t *tails, ticcmd_t *cmd) // Abort if the player moves away or spins if (dist > followthres || player->dashspeed) - flymode = 0; + mem->thinkstate = AI_FOLLOW; } // Read player inputs while carrying - else if (flymode == 2) + else if (mem->thinkstate == AI_FLYCARRY) { cmd->forwardmove = pcmd->forwardmove; cmd->sidemove = pcmd->sidemove; @@ -203,19 +224,19 @@ static void B_BuildTailsTiccmd(mobj_t *sonic, mobj_t *tails, ticcmd_t *cmd) // End flymode if (player->powers[pw_carry] != CR_PLAYER) { - flymode = 0; + mem->thinkstate = AI_FOLLOW; } } } - if (flymode && P_IsObjectOnGround(tails) && !(pcmd->buttons & BT_JUMP)) - flymode = 0; + if (P_IsObjectOnGround(tails) && !(pcmd->buttons & BT_JUMP) && (mem->thinkstate == AI_FLYSTANDBY || mem->thinkstate == AI_FLYCARRY)) + mem->thinkstate = AI_FOLLOW; // ******** // SPINNING - if (panic || flymode || !(player->pflags & PF_SPINNING) || (player->pflags & PF_JUMPED)) - spinmode = false; - else + if (!(player->pflags & (PF_SPINNING|PF_STARTDASH)) && mem->thinkstate == AI_SPINFOLLOW) + mem->thinkstate = AI_FOLLOW; + else if (mem->thinkstate == AI_FOLLOW || mem->thinkstate == AI_SPINFOLLOW) { if (!_2d) { @@ -224,21 +245,21 @@ static void B_BuildTailsTiccmd(mobj_t *sonic, mobj_t *tails, ticcmd_t *cmd) { if (dist < followthres && dist > touchdist) // Do positioning { - cmd->angleturn = (ang - tails->angle) >> 16; // NOT FRACBITS DAMNIT + cmd->angleturn = (ang) >> 16; // NOT FRACBITS DAMNIT cmd->forwardmove = 50; - spinmode = true; + mem->thinkstate = AI_SPINFOLLOW; } else if (dist < touchdist) { if (!bmom && (!(bot->pflags & PF_SPINNING) || (bot->dashspeed && bot->pflags & PF_SPINNING))) { - cmd->angleturn = (sonic->angle - tails->angle) >> 16; // NOT FRACBITS DAMNIT + cmd->angleturn = (sonic->angle) >> 16; // NOT FRACBITS DAMNIT spin = true; } - spinmode = true; + mem->thinkstate = AI_SPINFOLLOW; } else - spinmode = false; + mem->thinkstate = AI_FOLLOW; } // Spin else if (player->dashspeed == bot->dashspeed && player->pflags & PF_SPINNING) @@ -246,12 +267,12 @@ static void B_BuildTailsTiccmd(mobj_t *sonic, mobj_t *tails, ticcmd_t *cmd) if (bot->pflags & PF_SPINNING || !spin_last) { spin = true; - cmd->angleturn = (sonic->angle - tails->angle) >> 16; // NOT FRACBITS DAMNIT + cmd->angleturn = (sonic->angle) >> 16; // NOT FRACBITS DAMNIT cmd->forwardmove = MAXPLMOVE; - spinmode = true; + mem->thinkstate = AI_SPINFOLLOW; } else - spinmode = false; + mem->thinkstate = AI_FOLLOW; } } // 2D mode @@ -261,17 +282,19 @@ static void B_BuildTailsTiccmd(mobj_t *sonic, mobj_t *tails, ticcmd_t *cmd) && ((bot->pflags & PF_SPINNING) || !spin_last)) { spin = true; - spinmode = true; + mem->thinkstate = AI_SPINFOLLOW; } + else + mem->thinkstate = AI_FOLLOW; } } // ******** // FOLLOW - if (!(flymode || spinmode)) + if (mem->thinkstate == AI_FOLLOW || mem->thinkstate == AI_CATCHUP) { // Too far - if (panic || dist > followthres) + if (mem->thinkstate == AI_CATCHUP || dist > followthres) { if (!_2d) cmd->forwardmove = MAXPLMOVE; @@ -281,7 +304,7 @@ static void B_BuildTailsTiccmd(mobj_t *sonic, mobj_t *tails, ticcmd_t *cmd) cmd->sidemove = -MAXPLMOVE; } // Within threshold - else if (!panic && dist > followmin && abs(zdist) < 192*scale) + else if (dist > followmin && abs(zdist) < 192*scale) { if (!_2d) cmd->forwardmove = FixedHypot(pcmd->forwardmove, pcmd->sidemove); @@ -292,7 +315,7 @@ static void B_BuildTailsTiccmd(mobj_t *sonic, mobj_t *tails, ticcmd_t *cmd) else if (dist < followmin) { // Copy inputs - cmd->angleturn = (sonic->angle - tails->angle) >> 16; // NOT FRACBITS DAMNIT + cmd->angleturn = (sonic->angle) >> 16; // NOT FRACBITS DAMNIT bot->drawangle = ang; cmd->forwardmove = 8 * pcmd->forwardmove / 10; cmd->sidemove = 8 * pcmd->sidemove / 10; @@ -301,7 +324,7 @@ static void B_BuildTailsTiccmd(mobj_t *sonic, mobj_t *tails, ticcmd_t *cmd) // ******** // JUMP - if (!(flymode || spinmode)) + if (mem->thinkstate == AI_FOLLOW || mem->thinkstate == AI_CATCHUP || (mem->thinkstate == AI_SPINFOLLOW && player->pflags & PF_JUMPED)) { // Flying catch-up if (bot->pflags & PF_THOKKED) @@ -319,31 +342,30 @@ static void B_BuildTailsTiccmd(mobj_t *sonic, mobj_t *tails, ticcmd_t *cmd) // Start jump else if (!jump_last && !(bot->pflags & PF_JUMPED) //&& !(player->pflags & PF_SPINNING) && ((zdist > 32*scale && player->pflags & PF_JUMPED) // Following - || (zdist > 64*scale && panic) // Vertical catch-up - || (stalled && anxiety > 20 && bot->powers[pw_carry] == CR_NONE) + || (zdist > 64*scale && mem->thinkstate == AI_CATCHUP) // Vertical catch-up + || (stalled && mem-> catchup_tics > 20 && bot->powers[pw_carry] == CR_NONE) //|| (bmom < scale>>3 && dist > followthres && !(bot->powers[pw_carry])) // Stopped & not in carry state || (bot->pflags & PF_SPINNING && !(bot->pflags & PF_JUMPED)))) // Spinning jump = true; // Hold jump - else if (bot->pflags & PF_JUMPED && jump_last && tails->momz*flip > 0 && (zdist > 0 || panic)) + else if (bot->pflags & PF_JUMPED && jump_last && tails->momz*flip > 0 && (zdist > 0 || mem->thinkstate == AI_CATCHUP)) jump = true; // Start flying - else if (bot->pflags & PF_JUMPED && panic && !jump_last && bot->charability == CA_FLY) + else if (bot->pflags & PF_JUMPED && mem->thinkstate == AI_CATCHUP && !jump_last && bot->charability == CA_FLY) jump = true; } // ******** // HISTORY - jump_last = jump; - spin_last = spin; + //jump_last = jump; + //spin_last = spin; // Turn the virtual keypresses into ticcmd_t. B_KeysToTiccmd(tails, cmd, forward, backward, left, right, false, false, jump, spin); // Update our status - lastForward = forward; - lastBlocked = blocked; - blocked = false; + mem->lastForward = forward; + mem->lastBlocked = blocked; } void B_BuildTiccmd(player_t *player, ticcmd_t *cmd) @@ -362,32 +384,31 @@ void B_BuildTiccmd(player_t *player, ticcmd_t *cmd) // Bot AI isn't programmed in analog. CV_SetValue(&cv_analog[1], false); - // Bot cmd functions and hooks are not currently netplay compatible - // Necessary failsafe, as a bot in the P2 position in a netgame can inherit - // the last input from a hook triggered in splitscreen or SP. - if (netgame) - return; - // Let Lua scripts build ticcmds if (LUAh_BotTiccmd(player, cmd)) return; - // We don't have any main character AI, sorry. D: - if (player-players == consoleplayer) + // Make sure we have a valid main character to follow + B_UpdateBotleader(player); + if (!player->botleader) return; - // Basic Tails AI - B_BuildTailsTiccmd(players[consoleplayer].mo, player->mo, cmd); + // Single Player Tails AI + //B_BuildTailsTiccmd(players[consoleplayer].mo, player->mo, cmd); + B_BuildTailsTiccmd(player->botleader->mo, player->mo, cmd); } void B_KeysToTiccmd(mobj_t *mo, ticcmd_t *cmd, boolean forward, boolean backward, boolean left, boolean right, boolean strafeleft, boolean straferight, boolean jump, boolean spin) { + player_t *player = mo->player; // don't try to do stuff if your sonic is in a minecart or something - if (players[consoleplayer].powers[pw_carry] && players[consoleplayer].powers[pw_carry] != CR_PLAYER) + //if (players[consoleplayer].powers[pw_carry] && players[consoleplayer].powers[pw_carry] != CR_PLAYER) + //!!! + if (&player->botleader && player->botleader->powers[pw_carry] && player->botleader->powers[pw_carry] != CR_PLAYER) return; // Turn the virtual keypresses into ticcmd_t. if (twodlevel || mo->flags2 & MF2_TWOD) { - if (players[consoleplayer].climbing + if (player->botleader->climbing || mo->player->pflags & PF_GLIDING) { // Don't mess with bot inputs during these unhandled movement conditions. // The normal AI doesn't use abilities, so custom AI should be sending us exactly what it wants anyway. @@ -426,10 +447,10 @@ void B_KeysToTiccmd(mobj_t *mo, ticcmd_t *cmd, boolean forward, boolean backward cmd->forwardmove += MAXPLMOVE<>16; if (backward) cmd->forwardmove -= MAXPLMOVE<>16; - if (left) + if (left) cmd->angleturn += 1280; if (right) - cmd->angleturn -= 1280; + cmd->angleturn -= 1280; if (strafeleft) cmd->sidemove -= MAXPLMOVE<>16; if (straferight) @@ -453,14 +474,19 @@ void B_KeysToTiccmd(mobj_t *mo, ticcmd_t *cmd, boolean forward, boolean backward void B_MoveBlocked(player_t *player) { (void)player; - blocked = true; + player->blocked = true; } boolean B_CheckRespawn(player_t *player) { - mobj_t *sonic = players[consoleplayer].mo; + mobj_t *sonic; mobj_t *tails = player->mo; + //We don't have a main player to spawn to! + if (!player->botleader) + return false; + + sonic = player->botleader->mo; // We can't follow Sonic if he's not around! if (!sonic || sonic->health <= 0) return false; @@ -511,15 +537,19 @@ void B_RespawnBot(INT32 playernum) { player_t *player = &players[playernum]; fixed_t x,y,z; - mobj_t *sonic = players[consoleplayer].mo; + mobj_t *sonic; mobj_t *tails; + if (!player->botleader) + return; + + sonic = player->botleader->mo; if (!sonic || sonic->health <= 0) return; - B_ResetAI(); + B_ResetAI(&player->botmem); - player->bot = 1; + player->bot = BOT_2PAI; P_SpawnPlayer(playernum); tails = player->mo; @@ -546,10 +576,7 @@ void B_RespawnBot(INT32 playernum) player->powers[pw_spacetime] = sonic->player->powers[pw_spacetime]; player->powers[pw_gravityboots] = sonic->player->powers[pw_gravityboots]; player->powers[pw_nocontrol] = sonic->player->powers[pw_nocontrol]; - player->acceleration = sonic->player->acceleration; - player->accelstart = sonic->player->accelstart; - player->thrustfactor = sonic->player->thrustfactor; - player->normalspeed = sonic->player->normalspeed; + //!!! Nuke the speed equivalencies player->pflags |= PF_AUTOBRAKE|(sonic->player->pflags & PF_DIRECTIONCHAR); P_TeleportMove(tails, x, y, z); @@ -567,11 +594,11 @@ void B_RespawnBot(INT32 playernum) void B_HandleFlightIndicator(player_t *player) { mobj_t *tails = player->mo; - + botmem_t *mem = &player->botmem; if (!tails) return; - if (thinkfly && player->bot == 1 && tails->health) + if (mem->thinkstate == AI_THINKFLY && player->bot == BOT_2PAI && tails->health) { if (!tails->hnext) { From 4e47f240adc21eab710b6c17673d6053539579eb Mon Sep 17 00:00:00 2001 From: Shane Ellis Date: Sat, 23 Jan 2021 10:30:07 -0500 Subject: [PATCH 035/126] Add new botstuffs --- src/d_player.h | 40 +++++++++++++++++++++++++++++++++++++++- 1 file changed, 39 insertions(+), 1 deletion(-) diff --git a/src/d_player.h b/src/d_player.h index 2e7afed88..bb206c889 100644 --- a/src/d_player.h +++ b/src/d_player.h @@ -313,9 +313,43 @@ typedef enum RW_RAIL = 32 } ringweapons_t; +//Bot types +typedef enum +{ + BOT_NONE = 0, + BOT_2PAI, + BOT_2PHUMAN, + BOT_MPAI +} bottype_t; + +//AI states +typedef enum +{ + AI_STANDBY = 0, + AI_FOLLOW, + AI_CATCHUP, + AI_THINKFLY, + AI_FLYSTANDBY, + AI_FLYCARRY, + AI_SPINFOLLOW +} aistatetype_t; + + // ======================================================================== // PLAYER STRUCTURE // ======================================================================== + +//Bot memory struct +typedef struct botmem_s +{ + boolean lastForward; + boolean lastBlocked; + boolean blocked; + UINT8 catchup_tics; + UINT8 thinkstate; +} botmem_t; + +//Main struct typedef struct player_s { mobj_t *mo; @@ -526,7 +560,11 @@ typedef struct player_s boolean spectator; boolean outofcoop; UINT8 bot; - + struct player_s *botleader; + UINT16 lastbuttons; + botmem_t botmem; + boolean blocked; + tic_t jointime; // Timer when player joins game to change skin/color tic_t quittime; // Time elapsed since user disconnected, zero if connected #ifdef HWRENDER From 25bf4b54e2cccb666acb143aecaaa6f26993fc88 Mon Sep 17 00:00:00 2001 From: Shane Ellis Date: Sat, 23 Jan 2021 10:36:44 -0500 Subject: [PATCH 036/126] Restructured botticcmds into G_Ticker --- src/g_game.c | 88 ++++++++++++++++++++++++++++++++-------------------- 1 file changed, 54 insertions(+), 34 deletions(-) diff --git a/src/g_game.c b/src/g_game.c index 5e2b4e708..3d6fc52b7 100644 --- a/src/g_game.c +++ b/src/g_game.c @@ -1087,7 +1087,7 @@ void G_BuildTiccmd(ticcmd_t *cmd, INT32 realtics, UINT8 ssplayer) boolean turnleft, turnright, strafelkey, straferkey, movefkey, movebkey, mouseaiming, analogjoystickmove, gamepadjoystickmove, thisjoyaiming; boolean strafeisturn; // Simple controls only player_t *player = &players[ssplayer == 2 ? secondarydisplayplayer : consoleplayer]; - camera_t *thiscam = ((ssplayer == 1 || player->bot == 2) ? &camera : &camera2); + camera_t *thiscam = ((ssplayer == 1 || player->bot == BOT_2PHUMAN) ? &camera : &camera2); angle_t *myangle = (ssplayer == 1 ? &localangle : &localangle2); INT32 *myaiming = (ssplayer == 1 ? &localaiming : &localaiming2); @@ -1560,23 +1560,14 @@ void G_BuildTiccmd(ticcmd_t *cmd, INT32 realtics, UINT8 ssplayer) cmd->forwardmove = (SINT8)(cmd->forwardmove + forward); cmd->sidemove = (SINT8)(cmd->sidemove + side); - if (player->bot == 1) { // Tailsbot for P2 - if (!player->powers[pw_tailsfly] && (cmd->forwardmove || cmd->sidemove || cmd->buttons)) - { - player->bot = 2; // A player-controlled bot. Returns to AI when it respawns. - CV_SetValue(&cv_analog[1], true); - } - else - { - G_CopyTiccmd(cmd, I_BaseTiccmd2(), 1); // empty, or external driver - B_BuildTiccmd(player, cmd); - } - B_HandleFlightIndicator(player); - } - else if (player->bot == 2) + //Note: Majority of botstuffs are handled in G_Ticker now. + if (player->bot == BOT_2PHUMAN) //Player-controlled bot + { + G_CopyTiccmd(cmd, I_BaseTiccmd2(), 1); // empty, or external driver // Fix offset angle for P2-controlled Tailsbot when P2's controls are set to non-Legacy cmd->angleturn = (INT16)((localangle - *myangle) >> 16); - + } + *myangle += (cmd->angleturn<<16); if (controlstyle == CS_LMAOGALOG) { @@ -2290,22 +2281,51 @@ void G_Ticker(boolean run) for (i = 0; i < MAXPLAYERS; i++) { if (playeringame[i]) - { + { //!!! INT16 received; + //Save last frame's button readings + players[i].lastbuttons = players[i].cmd.buttons; G_CopyTiccmd(&players[i].cmd, &netcmds[buf][i], 1); + //Bot ticcmd handling + //Yes, ordinarily this would be handled in G_BuildTiccmd... + //...however, bot players won't have a corresponding consoleplayer or splitscreen player 2 to send that information. + //Therefore, this has to be done after ticcmd sends are received. + if (players[i].bot == BOT_2PAI) { // Tailsbot for P2 + if (!players[i].powers[pw_tailsfly] && (players[i].cmd.forwardmove || players[i].cmd.sidemove || players[i].cmd.buttons)) + { + players[i].bot = BOT_2PHUMAN; // A player-controlled bot. Returns to AI when it respawns. + CV_SetValue(&cv_analog[1], true); + } + else + { + B_BuildTiccmd(&players[i], &players[i].cmd); + } + B_HandleFlightIndicator(&players[i]); + } + else if (players[i].bot == BOT_MPAI) { + B_BuildTiccmd(&players[i], &players[i].cmd); + } + + // Do angle adjustments. + if (players[i].bot == BOT_NONE || players[i].bot == BOT_2PHUMAN) + { + received = (players[i].cmd.angleturn & TICCMD_RECEIVED); + players[i].angleturn += players[i].cmd.angleturn - players[i].oldrelangleturn; + players[i].oldrelangleturn = players[i].cmd.angleturn; + if (P_ControlStyle(&players[i]) == CS_LMAOGALOG) + P_ForceLocalAngle(&players[i], players[i].angleturn << 16); + else + players[i].cmd.angleturn = players[i].angleturn; - received = (players[i].cmd.angleturn & TICCMD_RECEIVED); - - players[i].angleturn += players[i].cmd.angleturn - players[i].oldrelangleturn; - players[i].oldrelangleturn = players[i].cmd.angleturn; - if (P_ControlStyle(&players[i]) == CS_LMAOGALOG) - P_ForceLocalAngle(&players[i], players[i].angleturn << 16); - else - players[i].cmd.angleturn = players[i].angleturn; - - players[i].cmd.angleturn &= ~TICCMD_RECEIVED; - players[i].cmd.angleturn |= received; + players[i].cmd.angleturn &= ~TICCMD_RECEIVED; + players[i].cmd.angleturn |= received; + } + else // Less work is required if we're building a bot ticcmd. + { + players[i].angleturn = players[i].cmd.angleturn; + players[i].oldrelangleturn = players[i].cmd.angleturn; + } } } @@ -2627,8 +2647,8 @@ void G_PlayerReborn(INT32 player, boolean betweenmaps) p->totalring = totalring; p->mare = mare; - if (bot == 2) - p->bot = 1; // reset to AI-controlled + if (bot == BOT_2PHUMAN) + p->bot = BOT_2PAI; // reset to AI-controlled else p->bot = bot; p->pity = pity; @@ -2976,8 +2996,8 @@ void G_DoReborn(INT32 playernum) // Make sure objectplace is OFF when you first start the level! OP_ResetObjectplace(); - // Tailsbot - if (player->bot && player->bot != 3 && playernum != consoleplayer) + //! Tailsbot + if (player->bot == BOT_2PAI || player->bot == BOT_2PHUMAN) { // Bots respawn next to their master. mobj_t *oldmo = NULL; @@ -3198,7 +3218,7 @@ void G_AddPlayer(INT32 playernum) if (!playeringame[i]) continue; - if (players[i].bot) // ignore dumb, stupid tails + if (players[i].bot == BOT_2PAI || players[i].bot == BOT_2PHUMAN) // ignore dumb, stupid tails continue; countplayers++; @@ -3239,7 +3259,7 @@ boolean G_EnoughPlayersFinished(void) for (i = 0; i < MAXPLAYERS; i++) { - if (!playeringame[i] || players[i].spectator || players[i].bot) + if (!playeringame[i] || players[i].spectator || players[i].bot == BOT_2PAI || players[i].bot == BOT_2PHUMAN) continue; if (players[i].quittime > 30 * TICRATE) continue; From 1274b4651e6decce71a8b4627bb367377a483465 Mon Sep 17 00:00:00 2001 From: Shane Ellis Date: Sat, 23 Jan 2021 10:39:59 -0500 Subject: [PATCH 037/126] Added botstuffs to savestate --- src/p_saveg.c | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/src/p_saveg.c b/src/p_saveg.c index c1364e08f..d80495e5b 100644 --- a/src/p_saveg.c +++ b/src/p_saveg.c @@ -158,6 +158,18 @@ static void P_NetArchivePlayers(void) WRITEUINT32(save_p, players[i].dashmode); WRITEUINT32(save_p, players[i].skidtime); + ////////// + // Bots // + ////////// + WRITEUINT8(save_p, players[i].bot); + WRITEUINT8(save_p, players[i].botmem.lastForward); + WRITEUINT8(save_p, players[i].botmem.lastBlocked); + WRITEUINT8(save_p, players[i].botmem.catchup_tics); + WRITEUINT8(save_p, players[i].botmem.thinkstate); + + WRITEUINT8(save_p, players[i].blocked); + WRITEUINT16(save_p, players[i].lastbuttons); + //////////////////////////// // Conveyor Belt Movement // //////////////////////////// @@ -372,6 +384,19 @@ static void P_NetUnArchivePlayers(void) players[i].dashmode = READUINT32(save_p); // counter for dashmode ability players[i].skidtime = READUINT32(save_p); // Skid timer + ////////// + // Bots // + ////////// + players[i].bot = READUINT8(save_p); + + players[i].botmem.lastForward = READUINT8(save_p); + players[i].botmem.lastBlocked = READUINT8(save_p); + players[i].botmem.catchup_tics = READUINT8(save_p); + players[i].botmem.thinkstate = READUINT8(save_p); + + players[i].blocked = READUINT8(save_p); + players[i].lastbuttons = READUINT16(save_p); + //////////////////////////// // Conveyor Belt Movement // //////////////////////////// From 135032c2932afa6474b15b0859717fe1771a3dc7 Mon Sep 17 00:00:00 2001 From: Shane Ellis Date: Sat, 23 Jan 2021 10:58:34 -0500 Subject: [PATCH 038/126] Correction to implicit declaration of B_UpdateBotleader() --- src/lua_baselib.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/lua_baselib.c b/src/lua_baselib.c index 525ce89ec..b6adc0ccd 100644 --- a/src/lua_baselib.c +++ b/src/lua_baselib.c @@ -28,6 +28,7 @@ #include "console.h" #include "d_netcmd.h" // IsPlayerAdmin #include "m_menu.h" // Player Setup menu color stuff +#include "b_bot.h" // B_UpdateBotleader #include "lua_script.h" #include "lua_libs.h" From 0a60fe0b1db198d8ed9ead70bab43a4e7711810b Mon Sep 17 00:00:00 2001 From: CobaltBW Date: Sat, 23 Jan 2021 14:05:36 -0800 Subject: [PATCH 039/126] Almost forgot: player.blocked --- src/lua_playerlib.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/lua_playerlib.c b/src/lua_playerlib.c index 51eb4ac0c..8156c2ac1 100644 --- a/src/lua_playerlib.c +++ b/src/lua_playerlib.c @@ -374,6 +374,8 @@ static int player_get(lua_State *L) LUA_PushUserdata(L, plr->botleader, META_PLAYER); else if (fastcmp(field,"lastbuttons")) lua_pushinteger(L, plr->lastbuttons); + else if (fastcmp(field,"blocked")) + lua_pushboolean(L, plr->blocked); else if (fastcmp(field,"jointime")) lua_pushinteger(L, plr->jointime); else if (fastcmp(field,"quittime")) @@ -732,6 +734,8 @@ static int player_set(lua_State *L) } else if (fastcmp(field,"lastbuttons")) plr->lastbuttons = (UINT16)luaL_checkinteger(L, 3); + else if (fastcmp(field,"blocked")) + plr->blocked = (UINT8)luaL_checkinteger(L, 3); else if (fastcmp(field,"jointime")) plr->jointime = (tic_t)luaL_checkinteger(L, 3); else if (fastcmp(field,"quittime")) From 93ec472c7849fecb0fe53e99e7802904a2b7c1ba Mon Sep 17 00:00:00 2001 From: CobaltBW Date: Sat, 23 Jan 2021 14:49:20 -0800 Subject: [PATCH 040/126] Expose BOT_ to lua --- src/deh_tables.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/deh_tables.c b/src/deh_tables.c index 3039bf7de..cd6ceb3c6 100644 --- a/src/deh_tables.c +++ b/src/deh_tables.c @@ -5167,6 +5167,12 @@ struct int_const_s const INT_CONST[] = { {"GF_REDFLAG",GF_REDFLAG}, {"GF_BLUEFLAG",GF_BLUEFLAG}, + // Bot types + {"BOT_NONE",BOT_NONE}, + {"BOT_2PAI",BOT_2PAI}, + {"BOT_2PHUMAN",BOT_2PHUMAN}, + {"BOT_MPAI",BOT_MPAI}, + // Customisable sounds for Skins, from sounds.h {"SKSSPIN",SKSSPIN}, {"SKSPUTPUT",SKSPUTPUT}, From a2fce68f14fe8002394639ce1f8facf109f3ea3c Mon Sep 17 00:00:00 2001 From: CobaltBW Date: Sat, 23 Jan 2021 17:01:48 -0800 Subject: [PATCH 041/126] Specialized Lua function for bot removal --- src/d_clisrv.c | 18 +++++++----------- src/d_clisrv.h | 1 + src/lua_baselib.c | 35 ++++++++++++++++++++++++++++++++++- 3 files changed, 42 insertions(+), 12 deletions(-) diff --git a/src/d_clisrv.c b/src/d_clisrv.c index 4574e5a1c..691408b54 100644 --- a/src/d_clisrv.c +++ b/src/d_clisrv.c @@ -2472,7 +2472,7 @@ void CL_ClearPlayer(INT32 playernum) // // Removes a player from the current game // -static void CL_RemovePlayer(INT32 playernum, kickreason_t reason) +void CL_RemovePlayer(INT32 playernum, kickreason_t reason) { // Sanity check: exceptional cases (i.e. c-fails) can cause multiple // kick commands to be issued for the same player. @@ -2815,22 +2815,18 @@ static void Command_Kick(void) return; } + if (!netgame) // Don't kick Tails in splitscreen! + { + CONS_Printf(M_GetText("This only works in a netgame.\n")); + return; + } + if (server || IsPlayerAdmin(consoleplayer)) { UINT8 buf[3 + MAX_REASONLENGTH]; UINT8 *p = buf; const SINT8 pn = nametonum(COM_Argv(1)); - // Unlike bans, kicks are used especially to remove bot players, so we'll - // need to run a more specific check which allows kicking offline, but - // not against splitscreen players. - if (splitscreen && (pn == 0 || pn == 1)) - { - CONS_Printf(M_GetText("Splitscreen players cannot be kicked.\n")); - return; - } - - if (pn == -1 || pn == 0) return; diff --git a/src/d_clisrv.h b/src/d_clisrv.h index 3d67525da..adb611ecf 100644 --- a/src/d_clisrv.h +++ b/src/d_clisrv.h @@ -401,6 +401,7 @@ void CL_Reset(void); void CL_ClearPlayer(INT32 playernum); void CL_QueryServerList(msg_server_t *list); void CL_UpdateServerList(boolean internetsearch, INT32 room); +void CL_RemovePlayer(INT32 playernum, kickreason_t reason); // Is there a game running boolean Playing(void); diff --git a/src/lua_baselib.c b/src/lua_baselib.c index b6adc0ccd..9aeabe1fc 100644 --- a/src/lua_baselib.c +++ b/src/lua_baselib.c @@ -29,6 +29,7 @@ #include "d_netcmd.h" // IsPlayerAdmin #include "m_menu.h" // Player Setup menu color stuff #include "b_bot.h" // B_UpdateBotleader +#include "d_clisrv.h" // CL_RemovePlayer #include "lua_script.h" #include "lua_libs.h" @@ -3463,7 +3464,7 @@ static int lib_gAddPlayer(lua_State *L) strcpy(player_names[newplayernum], luaL_checkstring(L, 3)); bot = luaL_optinteger(L, 4, 3); - newplayer->bot = (bot >= 0 && bot <= 3) ? bot : 3; + newplayer->bot = (bot >= BOT_NONE && bot <= BOT_MPAI) ? bot : BOT_MPAI; // If our bot is a 2P type, we'll need to set its leader so it can spawn if (newplayer->bot == BOT_2PAI || newplayer->bot == BOT_2PHUMAN) @@ -3487,6 +3488,37 @@ static int lib_gAddPlayer(lua_State *L) } +// Bot removing function +static int lib_gRemovePlayer(lua_State *L) +{ + UINT8 pnum = -1; + //const char *kickreason = luaL_checkstring(L, 2); + + if (!lua_isnoneornil(L, 1)) + pnum = luaL_checkinteger(L, 1); + if (&players[pnum]) + { + if (players[pnum].bot != BOT_NONE) + { +// CL_RemovePlayer(pnum, *kickreason); + CL_RemovePlayer(pnum, pnum); + if (netgame) + { + char kickmsg[256]; + + strcpy(kickmsg, M_GetText("\x82*Bot %s has been removed")); + strcpy(kickmsg, va(kickmsg, player_names[pnum], pnum)); + HU_AddChatText(kickmsg, false); + } + lua_pushboolean(L, true); + return 1; + } + } + lua_pushboolean(L, false); + return 1; +} + + static int Lcheckmapnumber (lua_State *L, int idx, const char *fun) { if (ISINLEVEL) @@ -4068,6 +4100,7 @@ static luaL_Reg lib[] = { // g_game {"G_AddGametype", lib_gAddGametype}, {"G_AddPlayer", lib_gAddPlayer}, + {"G_RemovePlayer", lib_gRemovePlayer}, {"G_BuildMapName",lib_gBuildMapName}, {"G_BuildMapTitle",lib_gBuildMapTitle}, {"G_FindMap",lib_gFindMap}, From ea6879b5c290811395a74c442924f2a200c491ce Mon Sep 17 00:00:00 2001 From: Shane Ellis Date: Sun, 24 Jan 2021 12:43:13 -0500 Subject: [PATCH 042/126] missed a couple spots --- src/p_enemy.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/p_enemy.c b/src/p_enemy.c index 637eba83f..d0e0cc9ee 100644 --- a/src/p_enemy.c +++ b/src/p_enemy.c @@ -743,8 +743,8 @@ boolean P_LookForPlayers(mobj_t *actor, boolean allaround, boolean tracer, fixed if (player->mo->health <= 0) continue; // dead - if (player->bot && player->bot != 3) - continue; // ignore bots + if (player->bot == BOT_2PAI || player->bot == BOT_2PHUMAN) + continue; // ignore followbots if (player->quittime) continue; // Ignore uncontrolled bodies @@ -3590,7 +3590,7 @@ void A_1upThinker(mobj_t *actor) for (i = 0; i < MAXPLAYERS; i++) { - if (!playeringame[i] || players[i].bot || players[i].spectator) + if (!playeringame[i] || players[i].bot == BOT_2PAI || players[i].bot == BOT_2PHUMAN || players[i].spectator) continue; if (!players[i].mo) From ca00e2d5086af7f6a5ba63a3caf82b74742680a1 Mon Sep 17 00:00:00 2001 From: Shane Ellis Date: Sun, 24 Jan 2021 12:46:03 -0500 Subject: [PATCH 043/126] correction to nights bot clause --- 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 c631f49b2..f0cbb6f37 100644 --- a/src/p_user.c +++ b/src/p_user.c @@ -777,7 +777,7 @@ void P_NightserizePlayer(player_t *player, INT32 nighttime) UINT8 oldmare, oldmarelap, oldmarebonuslap; //! Bots can't be NiGHTSerized, silly!1 :P - if (player->bot == BOT_2PAI || player->bot || BOT_2PHUMAN) + if (player->bot == BOT_2PAI || player->bot == BOT_2PHUMAN) return; if (player->powers[pw_carry] != CR_NIGHTSMODE) From dca158096df47f730e2827736bf650c1ddb8fe3d Mon Sep 17 00:00:00 2001 From: Jaime Ita Passos Date: Mon, 22 Mar 2021 23:56:55 -0300 Subject: [PATCH 044/126] Experimental implementation --- src/blua/lbaselib.c | 2 +- src/d_clisrv.c | 4 +- src/d_main.c | 35 ++++- src/d_netcmd.c | 204 +++++++++++++++++++++++- src/d_netcmd.h | 14 +- src/d_netfil.c | 48 +++++- src/d_netfil.h | 6 + src/filesrch.c | 345 +++++++++++++++++++++++++++++++++++++++-- src/filesrch.h | 8 +- src/hardware/hw_main.c | 2 +- src/m_misc.c | 19 +++ src/m_misc.h | 3 + src/p_setup.c | 45 ++++-- src/p_setup.h | 1 + src/r_textures.c | 17 +- src/r_things.c | 1 + src/w_wad.c | 337 ++++++++++++++++++++++++++++++++++++---- src/w_wad.h | 11 +- 18 files changed, 1007 insertions(+), 95 deletions(-) diff --git a/src/blua/lbaselib.c b/src/blua/lbaselib.c index 644565c28..0fc222038 100644 --- a/src/blua/lbaselib.c +++ b/src/blua/lbaselib.c @@ -274,7 +274,7 @@ static int luaB_dofile (lua_State *L) { UINT16 lumpnum; int n = lua_gettop(L); - if (wadfiles[numwadfiles - 1]->type != RET_PK3) + if (!W_FileHasFolders(wadfiles[numwadfiles - 1])) luaL_error(L, "dofile() only works with PK3 files"); snprintf(fullfilename, sizeof(fullfilename), "Lua/%s", filename); diff --git a/src/d_clisrv.c b/src/d_clisrv.c index 7c2dec6a1..3dfb5cea8 100644 --- a/src/d_clisrv.c +++ b/src/d_clisrv.c @@ -4486,9 +4486,9 @@ static INT16 Consistancy(void) { if (th->function.acp1 == (actionf_p1)P_RemoveThinkerDelayed) continue; - + mo = (mobj_t *)th; - + if (mo->flags & (MF_SPECIAL | MF_SOLID | MF_PUSHABLE | MF_BOSS | MF_MISSILE | MF_SPRING | MF_MONITOR | MF_FIRE | MF_ENEMY | MF_PAIN | MF_STICKY)) { ret -= mo->type; diff --git a/src/d_main.c b/src/d_main.c index 23a2c0133..09f678ba6 100644 --- a/src/d_main.c +++ b/src/d_main.c @@ -873,10 +873,26 @@ static void D_AddFile(char **list, const char *file) newfile = malloc(strlen(file) + 1); if (!newfile) - { I_Error("No more free memory to AddFile %s",file); - } + strcpy(newfile, file); + list[pnumwadfiles] = newfile; +} + +static void D_AddFolder(char **list, const char *file) +{ + size_t pnumwadfiles, len = strlen(file); + char *newfile; + + for (pnumwadfiles = 0; list[pnumwadfiles]; pnumwadfiles++) + ; + + newfile = malloc(len + 2); // NULL terminator + path separator + if (!newfile) + I_Error("No more free memory to AddFolder %s",file); + + strcpy(newfile, file); + strcat(newfile, PATHSEP); list[pnumwadfiles] = newfile; } @@ -1180,7 +1196,7 @@ void D_SRB2Main(void) { if (M_CheckParm("-file")) { - // the parms after p are wadfile/lump names, + // the parms after p are wadfile names, // until end of parms or another - preceded parm while (M_IsNextParm()) { @@ -1190,6 +1206,19 @@ void D_SRB2Main(void) D_AddFile(startuppwads, s); } } + + if (M_CheckParm("-folder")) + { + // the parms after p are folder names, + // until end of parms or another - preceded parm + while (M_IsNextParm()) + { + const char *s = M_GetNextParm(); + + if (s) // Check for NULL? + D_AddFolder(startuppwads, s); + } + } } // get map from parms diff --git a/src/d_netcmd.c b/src/d_netcmd.c index 09f9d4651..c177e4a4a 100644 --- a/src/d_netcmd.c +++ b/src/d_netcmd.c @@ -63,7 +63,9 @@ static void Got_WeaponPref(UINT8 **cp, INT32 playernum); static void Got_Mapcmd(UINT8 **cp, INT32 playernum); static void Got_ExitLevelcmd(UINT8 **cp, INT32 playernum); static void Got_RequestAddfilecmd(UINT8 **cp, INT32 playernum); +static void Got_RequestAddfoldercmd(UINT8 **cp, INT32 playernum); static void Got_Addfilecmd(UINT8 **cp, INT32 playernum); +static void Got_Addfoldercmd(UINT8 **cp, INT32 playernum); static void Got_Pause(UINT8 **cp, INT32 playernum); static void Got_Suicide(UINT8 **cp, INT32 playernum); static void Got_RandomSeed(UINT8 **cp, INT32 playernum); @@ -115,6 +117,7 @@ static void Command_Map_f(void); static void Command_ResetCamera_f(void); static void Command_Addfile(void); +static void Command_Addfolder(void); static void Command_ListWADS_f(void); static void Command_RunSOC(void); static void Command_Pause(void); @@ -398,16 +401,16 @@ const char *netxcmdnames[MAXNETXCMD - 1] = "MAP", "EXITLEVEL", "ADDFILE", + "ADDFOLDER", "PAUSE", "ADDPLAYER", "TEAMCHANGE", "CLEARSCORES", - "LOGIN", "VERIFIED", "RANDOMSEED", "RUNSOC", "REQADDFILE", - "DELFILE", // replace next time we add an XD + "REQADDFOLDER", "SETMOTD", "SUICIDE", "LUACMD", @@ -441,7 +444,9 @@ void D_RegisterServerCommands(void) RegisterNetXCmd(XD_MAP, Got_Mapcmd); RegisterNetXCmd(XD_EXITLEVEL, Got_ExitLevelcmd); RegisterNetXCmd(XD_ADDFILE, Got_Addfilecmd); + RegisterNetXCmd(XD_ADDFOLDER, Got_Addfoldercmd); RegisterNetXCmd(XD_REQADDFILE, Got_RequestAddfilecmd); + RegisterNetXCmd(XD_REQADDFOLDER, Got_RequestAddfoldercmd); RegisterNetXCmd(XD_PAUSE, Got_Pause); RegisterNetXCmd(XD_SUICIDE, Got_Suicide); RegisterNetXCmd(XD_RUNSOC, Got_RunSOCcmd); @@ -472,6 +477,7 @@ void D_RegisterServerCommands(void) COM_AddCommand("showmap", Command_Showmap_f); COM_AddCommand("mapmd5", Command_Mapmd5_f); + COM_AddCommand("addfolder", Command_Addfolder); COM_AddCommand("addfile", Command_Addfile); COM_AddCommand("listwad", Command_ListWADS_f); @@ -3323,9 +3329,9 @@ static void Command_Addfile(void) ++p; // check total packet size and no of files currently loaded - // See W_LoadWadFile in w_wad.c + // See W_InitFile in w_wad.c if ((numwadfiles >= MAX_WADFILES) - || ((packetsizetally + nameonlylength(fn) + 22) > MAXFILENEEDED*sizeof(UINT8))) + || ((packetsizetally + nameonlylength(fn) + FILENEEDEDSIZE) > MAXFILENEEDED*sizeof(UINT8))) { CONS_Alert(CONS_ERROR, M_GetText("Too many files loaded to add %s\n"), fn); return; @@ -3373,6 +3379,89 @@ static void Command_Addfile(void) } } +static void Command_Addfolder(void) +{ + size_t argc = COM_Argc(); // amount of arguments total + size_t curarg; // current argument index + + const char *addedfolders[argc]; // list of filenames already processed + size_t numfoldersadded = 0; // the amount of filenames processed + + if (argc < 2) + { + CONS_Printf(M_GetText("addfolder [path2...] [...]: Load add-ons\n")); + return; + } + + // start at one to skip command name + for (curarg = 1; curarg < argc; curarg++) + { + const char *fn, *p; + char buf[256]; + char *buf_p = buf; + INT32 i; + size_t ii; + boolean folderadded = false; + + fn = COM_Argv(curarg); + + // For the amount of filenames previously processed... + for (ii = 0; ii < numfoldersadded; ii++) + { + // If this is one of them, don't try to add it. + if (!strcmp(fn, addedfolders[ii])) + { + folderadded = true; + break; + } + } + + // If we've added this one, skip to the next one. + if (folderadded) + { + CONS_Alert(CONS_WARNING, M_GetText("Already processed %s, skipping\n"), fn); + continue; + } + + // Disallow non-printing characters and semicolons. + for (i = 0; fn[i] != '\0'; i++) + if (!isprint(fn[i]) || fn[i] == ';') + return; + + // Add file on your client directly if you aren't in a netgame. + if (!(netgame || multiplayer)) + { + P_AddFolder(fn); + addedfolders[numfoldersadded++] = fn; + continue; + } + + p = fn+strlen(fn); + while(--p >= fn) + if (*p == '\\' || *p == '/' || *p == ':') + break; + ++p; + + // check total packet size and no of files currently loaded + // See W_InitFile in w_wad.c + if ((numwadfiles >= MAX_WADFILES) + || ((packetsizetally + strlen(fn) + FILENEEDEDSIZE) > MAXFILENEEDED*sizeof(UINT8))) + { + CONS_Alert(CONS_ERROR, M_GetText("Too many files loaded to add %s\n"), fn); + return; + } + + WRITESTRINGN(buf_p,p,240); + + addedfolders[numfoldersadded++] = fn; + + if (IsPlayerAdmin(consoleplayer) && (!server)) // Request to add file + SendNetXCmd(XD_REQADDFOLDER, buf, buf_p - buf); + else + SendNetXCmd(XD_ADDFOLDER, buf, buf_p - buf); + } +} + static void Got_RequestAddfilecmd(UINT8 **cp, INT32 playernum) { char filename[241]; @@ -3401,9 +3490,9 @@ static void Got_RequestAddfilecmd(UINT8 **cp, INT32 playernum) return; } - // See W_LoadWadFile in w_wad.c + // See W_InitFile in w_wad.c if ((numwadfiles >= MAX_WADFILES) - || ((packetsizetally + nameonlylength(filename) + 22) > MAXFILENEEDED*sizeof(UINT8))) + || ((packetsizetally + nameonlylength(filename) + FILENEEDEDSIZE) > MAXFILENEEDED*sizeof(UINT8))) toomany = true; else ncs = findfile(filename,md5sum,true); @@ -3433,6 +3522,64 @@ static void Got_RequestAddfilecmd(UINT8 **cp, INT32 playernum) COM_BufAddText(va("addfile %s\n", filename)); } +static void Got_RequestAddfoldercmd(UINT8 **cp, INT32 playernum) +{ + char path[241]; + filestatus_t ncs = FS_NOTFOUND; + boolean kick = false; + boolean toomany = false; + INT32 i,j; + + READSTRINGN(*cp, path, 240); + + /// \todo Integrity checks. + + // Only the server processes this message. + if (client) + return; + + // Disallow non-printing characters and semicolons. + for (i = 0; path[i] != '\0'; i++) + if (!isprint(path[i]) || path[i] == ';') + kick = true; + + if ((playernum != serverplayer && !IsPlayerAdmin(playernum)) || kick) + { + CONS_Alert(CONS_WARNING, M_GetText("Illegal addfolder command received from %s\n"), player_names[playernum]); + SendKick(playernum, KICK_MSG_CON_FAIL | KICK_MSG_KEEP_BODY); + return; + } + + // See W_InitFile in w_wad.c + if ((numwadfiles >= MAX_WADFILES) + || ((packetsizetally + strlen(path) + FILENEEDEDSIZE) > MAXFILENEEDED*sizeof(UINT8))) + toomany = true; + else + ncs = findfolder(path); + + if (ncs != FS_FOUND || toomany) + { + char message[256]; + + if (toomany) + sprintf(message, M_GetText("Too many files loaded to add %s\n"), path); + else if (ncs == FS_NOTFOUND) + sprintf(message, M_GetText("The server doesn't have %s\n"), path); + else + sprintf(message, M_GetText("Unknown error finding folder (%s)\n"), path); + + CONS_Printf("%s",message); + + for (j = 0; j < MAXPLAYERS; j++) + if (adminplayers[j]) + COM_BufAddText(va("sayto %d %s", adminplayers[j], message)); + + return; + } + + COM_BufAddText(va("addfolder \"%s\"\n", path)); +} + static void Got_Addfilecmd(UINT8 **cp, INT32 playernum) { char filename[241]; @@ -3481,6 +3628,49 @@ static void Got_Addfilecmd(UINT8 **cp, INT32 playernum) G_SetGameModified(true); } +static void Got_Addfoldercmd(UINT8 **cp, INT32 playernum) +{ + char path[241]; + filestatus_t ncs = FS_NOTFOUND; + + READSTRINGN(*cp, path, 240); + + /// \todo Integrity checks. + + if (playernum != serverplayer) + { + CONS_Alert(CONS_WARNING, M_GetText("Illegal addfolder command received from %s\n"), player_names[playernum]); + if (server) + SendKick(playernum, KICK_MSG_CON_FAIL | KICK_MSG_KEEP_BODY); + return; + } + + ncs = findfolder(path); + + if (ncs != FS_FOUND || !P_AddFolder(path)) + { + Command_ExitGame_f(); + if (ncs == FS_FOUND) + { + CONS_Printf(M_GetText("The server tried to add %s,\nbut you have too many files added.\nRestart the game to clear loaded files\nand play on this server."), path); + M_StartMessage(va("The server added a folder \n(%s)\nbut you have too many files added.\nRestart the game to clear loaded files.\n\nPress ESC\n",path), NULL, MM_NOTHING); + } + else if (ncs == FS_NOTFOUND) + { + CONS_Printf(M_GetText("The server tried to add %s,\nbut you don't have this file.\nYou need to find it in order\nto play on this server."), path); + M_StartMessage(va("The server added a folder \n(%s)\nthat you do not have.\n\nPress ESC\n",path), NULL, MM_NOTHING); + } + else + { + CONS_Printf(M_GetText("Unknown error finding folder (%s) the server added.\n"), path); + M_StartMessage(va("Unknown error trying to load a folder\nthat the server added \n(%s).\n\nPress ESC\n",path), NULL, MM_NOTHING); + } + return; + } + + G_SetGameModified(true); +} + static void Command_ListWADS_f(void) { INT32 i = numwadfiles; @@ -3495,6 +3685,8 @@ static void Command_ListWADS_f(void) CONS_Printf("\x82 * %.2d\x80: %s\n", i, tempname); else if (!wadfiles[i]->important) CONS_Printf("\x86 %.2d: %s\n", i, tempname); + else if (wadfiles[i]->type == RET_FOLDER) + CONS_Printf("\x82 * %.2d\x84: %s\n", i, tempname); else CONS_Printf(" %.2d: %s\n", i, tempname); } diff --git a/src/d_netcmd.h b/src/d_netcmd.h index ac39626a4..e7076cabf 100644 --- a/src/d_netcmd.h +++ b/src/d_netcmd.h @@ -128,16 +128,16 @@ typedef enum XD_MAP, // 6 XD_EXITLEVEL, // 7 XD_ADDFILE, // 8 - XD_PAUSE, // 9 - XD_ADDPLAYER, // 10 - XD_TEAMCHANGE, // 11 - XD_CLEARSCORES, // 12 - // UNUSED 13 (Because I don't want to change these comments) - XD_VERIFIED = 14,//14 + XD_ADDFOLDER, // 9 + XD_PAUSE, // 10 + XD_ADDPLAYER, // 11 + XD_TEAMCHANGE, // 12 + XD_CLEARSCORES, // 13 + XD_VERIFIED, // 14 XD_RANDOMSEED, // 15 XD_RUNSOC, // 16 XD_REQADDFILE, // 17 - XD_DELFILE, // 18 - replace next time we add an XD + XD_REQADDFOLDER,// 18 XD_SETMOTD, // 19 XD_SUICIDE, // 20 XD_DEMOTED, // 21 diff --git a/src/d_netfil.c b/src/d_netfil.c index 8f661bb5f..4e93f6600 100644 --- a/src/d_netfil.c +++ b/src/d_netfil.c @@ -120,7 +120,7 @@ char luafiledir[256 + 16] = "luafiles"; /** Fills a serverinfo packet with information about wad files loaded. * * \todo Give this function a better name since it is in global scope. - * Used to have size limiting built in - now handled via W_LoadWadFile in w_wad.c + * Used to have size limiting built in - now handled via W_InitFile in w_wad.c * */ UINT8 *PutFileNeeded(void) @@ -128,7 +128,7 @@ UINT8 *PutFileNeeded(void) size_t i, count = 0; UINT8 *p = netbuffer->u.serverinfo.fileneeded; char wadfilename[MAX_WADPATH] = ""; - UINT8 filestatus; + UINT8 filestatus, folder; for (i = 0; i < numwadfiles; i++) { @@ -137,9 +137,10 @@ UINT8 *PutFileNeeded(void) continue; filestatus = 1; // Importance - not really used any more, holds 1 by default for backwards compat with MS + folder = (wadfiles[i]->type == RET_FOLDER); // Store in the upper four bits - if (!cv_downloading.value) + if (!cv_downloading.value || folder) /// \todo Implement folder downloading. filestatus += (2 << 4); // Won't send else if ((wadfiles[i]->filesize <= (UINT32)cv_maxsend.value * 1024)) filestatus += (1 << 4); // Will send if requested @@ -147,6 +148,7 @@ UINT8 *PutFileNeeded(void) // filestatus += (0 << 4); -- Won't send, too big WRITEUINT8(p, filestatus); + WRITEUINT8(p, folder); count++; WRITEUINT32(p, wadfiles[i]->filesize); @@ -178,6 +180,7 @@ void D_ParseFileneeded(INT32 fileneedednum_parm, UINT8 *fileneededstr) fileneeded[i].status = FS_NOTFOUND; // We haven't even started looking for the file yet fileneeded[i].justdownloaded = false; filestatus = READUINT8(p); // The first byte is the file status + fileneeded[i].folder = READUINT8(p); // The second byte is the folder flag fileneeded[i].willsend = (UINT8)(filestatus >> 4); fileneeded[i].totalsize = READUINT32(p); // The four next bytes are the file size fileneeded[i].file = NULL; // The file isn't open yet @@ -420,7 +423,7 @@ INT32 CL_CheckFiles(void) return 1; } - // See W_LoadWadFile in w_wad.c + // See W_InitFile in w_wad.c packetsize = packetsizetally; for (i = 1; i < fileneedednum; i++) @@ -442,7 +445,10 @@ INT32 CL_CheckFiles(void) if (fileneeded[i].status != FS_NOTFOUND) continue; - packetsize += nameonlylength(fileneeded[i].filename) + 22; + if (fileneeded[i].folder) + packetsize += strlen(fileneeded[i].filename) + FILENEEDEDSIZE; + else + packetsize += nameonlylength(fileneeded[i].filename) + FILENEEDEDSIZE; if ((numwadfiles+filestoget >= MAX_WADFILES) || (packetsize > MAXFILENEEDED*sizeof(UINT8))) @@ -450,7 +456,10 @@ INT32 CL_CheckFiles(void) filestoget++; - fileneeded[i].status = findfile(fileneeded[i].filename, fileneeded[i].md5sum, true); + if (fileneeded[i].folder) + fileneeded[i].status = findfolder(fileneeded[i].filename); + else + fileneeded[i].status = findfile(fileneeded[i].filename, fileneeded[i].md5sum, true); CONS_Debug(DBG_NETPLAY, "found %d\n", fileneeded[i].status); if (fileneeded[i].status != FS_FOUND) ret = 0; @@ -472,7 +481,10 @@ void CL_LoadServerFiles(void) continue; // Already loaded else if (fileneeded[i].status == FS_FOUND) { - P_AddWadFile(fileneeded[i].filename); + if (fileneeded[i].folder) + P_AddFolder(fileneeded[i].filename); + else + P_AddWadFile(fileneeded[i].filename); G_SetGameModified(true); fileneeded[i].status = FS_OPEN; } @@ -757,7 +769,7 @@ static boolean AddFileToSendQueue(INT32 node, const char *filename, UINT8 fileid // This formerly checked if (!findfile(p->id.filename, NULL, true)) // Not found - // Don't inform client (probably someone who thought they could leak 2.2 ACZ) + // Don't inform client DEBFILE(va("Client %d request %s: not found\n", node, filename)); free(p->id.filename); free(p); @@ -1556,3 +1568,23 @@ filestatus_t findfile(char *filename, const UINT8 *wantedmd5sum, boolean complet return (badmd5 ? FS_MD5SUMBAD : FS_NOTFOUND); // md5 sum bad or file not found } + +filestatus_t findfolder(const char *path) +{ + // Check the path by itself first. + if (checkfolderpath(path, NULL, true)) + return FS_FOUND; + +#define checkpath(startpath) { \ + if (checkfolderpath(path, startpath, true)) \ + return FS_FOUND; \ + } + + checkpath(srb2home) // Then, look in srb2home. + checkpath(srb2path) // Now, look in srb2path. + checkpath(".") // Finally, look in ".". + +#undef checkpath + + return FS_NOTFOUND; +} diff --git a/src/d_netfil.h b/src/d_netfil.h index 1b399be75..7cd6e06d3 100644 --- a/src/d_netfil.h +++ b/src/d_netfil.h @@ -38,6 +38,7 @@ typedef enum typedef struct { UINT8 willsend; // Is the server willing to send it? + UINT8 folder; // File is a folder char filename[MAX_WADPATH]; UINT8 md5sum[16]; filestatus_t status; // The value returned by recsearch @@ -54,6 +55,8 @@ typedef struct UINT32 ackresendposition; // Used when resuming downloads } fileneeded_t; +#define FILENEEDEDSIZE 23 + extern INT32 fileneedednum; extern fileneeded_t fileneeded[MAX_WADFILES]; extern char downloaddir[512]; @@ -133,6 +136,9 @@ filestatus_t findfile(char *filename, const UINT8 *wantedmd5sum, boolean completepath); filestatus_t checkfilemd5(char *filename, const UINT8 *wantedmd5sum); +// Searches for a folder +filestatus_t findfolder(const char *path); + void nameonly(char *s); size_t nameonlylength(const char *s); diff --git a/src/filesrch.c b/src/filesrch.c index cb53d07be..f01fc0bd2 100644 --- a/src/filesrch.c +++ b/src/filesrch.c @@ -29,6 +29,7 @@ #include "m_misc.h" #include "z_zone.h" #include "m_menu.h" // Addons_option_Onchange +#include "w_wad.h" #if defined (_WIN32) && defined (_MSC_VER) @@ -340,6 +341,11 @@ char *refreshdirname = NULL; size_t packetsizetally = 0; size_t mainwadstally = 0; +#define folderpathlen 1024 +#define maxfolderdepth 48 + +#define isuptree(dirent) ((dirent)[0]=='.' && ((dirent)[1]=='\0' || ((dirent)[1]=='.' && (dirent)[2]=='\0'))) + filestatus_t filesearch(char *filename, const char *startpath, const UINT8 *wantedmd5sum, boolean completepath, int maxsearchdepth) { filestatus_t retval = FS_NOTFOUND; @@ -387,10 +393,7 @@ filestatus_t filesearch(char *filename, const char *startpath, const UINT8 *want continue; } - if (dent->d_name[0]=='.' && - (dent->d_name[1]=='\0' || - (dent->d_name[1]=='.' && - dent->d_name[2]=='\0'))) + if (isuptree(dent->d_name)) { // we don't want to scan uptree continue; @@ -445,6 +448,329 @@ filestatus_t filesearch(char *filename, const char *startpath, const UINT8 *want return retval; } +// Called from findfolder and ResGetLumpsFolder in w_wad.c. +// Call with cleanup true if the path has to be verified. +boolean checkfolderpath(const char *path, const char *startpath, boolean cleanup) +{ + char folderpath[folderpathlen], basepath[folderpathlen], *fn = NULL; + DIR *dirhandle; + + // Remove path separators from the filename, and don't try adding "/". + // See also the same code in W_InitFolder. + if (cleanup) + { + const char *p = path + strlen(path); + size_t len; + + --p; + while (*p == '\\' || *p == '/' || *p == ':') + { + p--; + if (p < path) + return false; + } + ++p; + + // Allocate the new path name. + len = (p - path) + 1; + fn = ZZ_Alloc(len); + strlcpy(fn, path, len); + } + + if (startpath) + { + snprintf(basepath, sizeof basepath, "%s" PATHSEP, startpath); + + if (cleanup) + { + snprintf(folderpath, sizeof folderpath, "%s%s", basepath, fn); + Z_Free(fn); // Don't need this anymore. + } + else + snprintf(folderpath, sizeof folderpath, "%s%s", basepath, path); + + // Home path and folder path are the same? Not valid. + if (!strcmp(basepath, folderpath)) + return false; + } + else if (cleanup) + { + snprintf(folderpath, sizeof folderpath, "%s", fn); + Z_Free(fn); // Don't need this anymore. + } + else + snprintf(folderpath, sizeof folderpath, "%s", path); + + dirhandle = opendir(folderpath); + if (dirhandle == NULL) + return false; + else + closedir(dirhandle); + + return true; +} + +INT32 pathisfolder(const char *path) +{ + struct stat fsstat; + + if (stat(path, &fsstat) < 0) + return -1; + else if (S_ISDIR(fsstat.st_mode)) + return 1; + + return 0; +} + +INT32 samepaths(const char *path1, const char *path2) +{ + struct stat stat1; + struct stat stat2; + + if (stat(path1, &stat1) < 0) + return -1; + if (stat(path2, &stat2) < 0) + return -1; + + if (stat1.st_dev == stat2.st_dev) + { +#if !defined(_WIN32) + return (stat1.st_ino == stat2.st_ino); +#else + HANDLE file1 = CreateFileA(path1, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL); + HANDLE file2 = CreateFileA(path2, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL); + BY_HANDLE_FILE_INFORMATION file1info, file2info; + boolean ok = false; + + if (file1 != INVALID_HANDLE_VALUE && file2 != INVALID_HANDLE_VALUE) + { + if (GetFileInformationByHandle(file1, &file1info) && GetFileInformationByHandle(file2, &file2info)) + { + if (file1info.dwVolumeSerialNumber == file2info.dwVolumeSerialNumber + && file1info.nFileIndexLow == file2info.nFileIndexLow + && file1info.nFileIndexHigh == file2info.nFileIndexHigh) + ok = true; + } + } + + if (file1 != INVALID_HANDLE_VALUE) + CloseHandle(file1); + if (file2 != INVALID_HANDLE_VALUE) + CloseHandle(file2); + + return ok; +#endif + } + + return false; +} + +// +// Folder loading +// + +static void initfolderpath(char *folderpath, size_t *folderpathindex, int depthleft) +{ + folderpathindex[depthleft] = strlen(folderpath) + 1; + + if (folderpath[folderpathindex[depthleft]-2] != PATHSEP[0]) + { + folderpath[folderpathindex[depthleft]-1] = PATHSEP[0]; + folderpath[folderpathindex[depthleft]] = 0; + } + else + folderpathindex[depthleft]--; +} + +lumpinfo_t *getfolderfiles(const char *path, UINT16 *nlmp, UINT16 *nfiles, UINT16 *nfolders) +{ + DIR **dirhandle; + struct dirent *dent; + struct stat fsstat; + + int rootfolder = (maxfolderdepth - 1); + int depthleft = rootfolder; + + char folderpath[folderpathlen]; + size_t *folderpathindex; + + lumpinfo_t *lumpinfo, *lump_p; + UINT16 i = 0, numlumps = (*nlmp); + + dirhandle = (DIR **)malloc(maxfolderdepth * sizeof (DIR*)); + folderpathindex = (size_t *)malloc(maxfolderdepth * sizeof(size_t)); + + // Open the root directory + strlcpy(folderpath, path, folderpathlen); + dirhandle[depthleft] = opendir(folderpath); + + if (dirhandle[depthleft] == NULL) + { + free(dirhandle); + free(folderpathindex); + return NULL; + } + + initfolderpath(folderpath, folderpathindex, depthleft); + (*nfiles) = 0; + (*nfolders) = 0; + + // Count files and directories + while (depthleft < maxfolderdepth) + { + folderpath[folderpathindex[depthleft]] = 0; + dent = readdir(dirhandle[depthleft]); + + if (!dent) + { + if (depthleft != rootfolder) // Don't close the root directory + closedir(dirhandle[depthleft]); + depthleft++; + continue; + } + else if (isuptree(dent->d_name)) + continue; + + strcpy(&folderpath[folderpathindex[depthleft]], dent->d_name); + + if (stat(folderpath, &fsstat) < 0) + ; + else if (S_ISDIR(fsstat.st_mode) && depthleft) + { + folderpathindex[--depthleft] = strlen(folderpath) + 1; + dirhandle[depthleft] = opendir(folderpath); + + if (dirhandle[depthleft]) + { + numlumps++; + (*nfolders)++; + } + else + depthleft++; + + folderpath[folderpathindex[depthleft]-1] = '/'; + folderpath[folderpathindex[depthleft]] = 0; + } + else + { + numlumps++; + (*nfiles)++; + } + + if (numlumps == (UINT16_MAX-1)) + break; + } + + // Failure: No files have been found. + if (!(*nfiles)) + { + (*nfiles) = UINT16_MAX; + free(folderpathindex); + free(dirhandle); + for (; depthleft < maxfolderdepth; closedir(dirhandle[depthleft++])); // Close any open directories. + return NULL; + } + + // Create the files and directories as lump entries + // It's possible to create lumps and count files at the same time, + // but I didn't to constantly have to reallocate memory for every lump. + rewinddir(dirhandle[rootfolder]); + depthleft = rootfolder; + + strlcpy(folderpath, path, folderpathlen); + initfolderpath(folderpath, folderpathindex, depthleft); + + lump_p = lumpinfo = Z_Calloc(numlumps * sizeof(lumpinfo_t), PU_STATIC, NULL); + + while (depthleft < maxfolderdepth) + { + char *fullname, *trimname; + + folderpath[folderpathindex[depthleft]] = 0; + dent = readdir(dirhandle[depthleft]); + + if (!dent) + { + closedir(dirhandle[depthleft++]); + continue; + } + else if (isuptree(dent->d_name)) + continue; + + strcpy(&folderpath[folderpathindex[depthleft]], dent->d_name); + + if (stat(folderpath, &fsstat) < 0) + continue; + else if (S_ISDIR(fsstat.st_mode) && depthleft) + { + folderpathindex[--depthleft] = strlen(folderpath) + 1; + dirhandle[depthleft] = opendir(folderpath); + + if (!dirhandle[depthleft]) + { + depthleft++; + continue; + } + + folderpath[folderpathindex[depthleft]-1] = '/'; + folderpath[folderpathindex[depthleft]] = 0; + } + + lump_p->diskpath = Z_StrDup(folderpath); // Path in the filesystem to the file + lump_p->compression = CM_NOCOMPRESSION; // Lump is uncompressed + + // Remove the folder path. + fullname = lump_p->diskpath; + if (strstr(fullname, path)) + fullname += strlen(path) + 1; + + // Get the 8-character long lump name. + trimname = strrchr(fullname, '/'); + if (trimname) + trimname++; + else + trimname = fullname; + + if (trimname[0]) + { + char *dotpos = strrchr(trimname, '.'); + if (dotpos == NULL) + dotpos = fullname + strlen(fullname); + + strncpy(lump_p->name, trimname, min(8, dotpos - trimname)); + + // The name of the file, without the extension. + lump_p->longname = Z_Calloc(dotpos - trimname + 1, PU_STATIC, NULL); + strlcpy(lump_p->longname, trimname, dotpos - trimname + 1); + } + else + lump_p->longname = Z_Calloc(1, PU_STATIC, NULL); + + // The complete name of the file, with its extension, + // excluding the path of the folder where it resides. + lump_p->fullname = Z_StrDup(fullname); + + lump_p++; + i++; + + if (i > numlumps || i == (UINT16_MAX-1)) + { + for (; depthleft < maxfolderdepth; closedir(dirhandle[depthleft++])); // Close any open directories. + break; + } + } + + free(folderpathindex); + free(dirhandle); + + (*nlmp) = numlumps; + return lumpinfo; +} + +// +// Addons menu +// + char exttable[NUM_EXT_TABLE][7] = { // maximum extension length (currently 4) plus 3 (null terminator, stop, and length including previous two) "\5.txt", "\5.cfg", // exec "\5.wad", @@ -455,7 +781,6 @@ char exttable[NUM_EXT_TABLE][7] = { // maximum extension length (currently 4) pl char filenamebuf[MAX_WADFILES][MAX_WADPATH]; - static boolean filemenucmp(char *haystack, char *needle) { static char localhaystack[128]; @@ -640,10 +965,7 @@ boolean preparefilemenu(boolean samedepth) if (!dent) break; - else if (dent->d_name[0]=='.' && - (dent->d_name[1]=='\0' || - (dent->d_name[1]=='.' && - dent->d_name[2]=='\0'))) + else if (isuptree(dent->d_name)) continue; // we don't want to scan uptree strcpy(&menupath[menupathindex[menudepthleft]],dent->d_name); @@ -704,10 +1026,7 @@ boolean preparefilemenu(boolean samedepth) if (!dent) break; - else if (dent->d_name[0]=='.' && - (dent->d_name[1]=='\0' || - (dent->d_name[1]=='.' && - dent->d_name[2]=='\0'))) + else if (isuptree(dent->d_name)) continue; // we don't want to scan uptree strcpy(&menupath[menupathindex[menudepthleft]],dent->d_name); diff --git a/src/filesrch.h b/src/filesrch.h index dfea8979e..92e3341f3 100644 --- a/src/filesrch.h +++ b/src/filesrch.h @@ -7,6 +7,7 @@ #include "doomdef.h" #include "d_netfil.h" #include "m_menu.h" // MAXSTRINGLENGTH +#include "w_wad.h" extern consvar_t cv_addons_option, cv_addons_folder, cv_addons_md5, cv_addons_showall, cv_addons_search_case, cv_addons_search_type; @@ -28,6 +29,12 @@ extern consvar_t cv_addons_option, cv_addons_folder, cv_addons_md5, cv_addons_sh filestatus_t filesearch(char *filename, const char *startpath, const UINT8 *wantedmd5sum, boolean completepath, int maxsearchdepth); +INT32 pathisfolder(const char *path); +boolean checkfolderpath(const char *path, const char *startpath, boolean cleanup); +INT32 samepaths(const char *path1, const char *path2); + +lumpinfo_t *getfolderfiles(const char *path, UINT16 *nlmp, UINT16 *nfiles, UINT16 *nfolders); + #define menudepth 20 extern char menupath[1024]; @@ -94,5 +101,4 @@ typedef enum void closefilemenu(boolean validsize); void searchfilemenu(char *tempname); boolean preparefilemenu(boolean samedepth); - #endif // __FILESRCH_H__ diff --git a/src/hardware/hw_main.c b/src/hardware/hw_main.c index c2d617eaf..8bbe78f00 100644 --- a/src/hardware/hw_main.c +++ b/src/hardware/hw_main.c @@ -6681,7 +6681,7 @@ void HWR_LoadAllCustomShaders(void) // read every custom shader for (i = 0; i < numwadfiles; i++) - HWR_LoadCustomShadersFromFile(i, (wadfiles[i]->type == RET_PK3)); + HWR_LoadCustomShadersFromFile(i, W_FileHasFolders(wadfiles[i])); } void HWR_LoadCustomShadersFromFile(UINT16 wadnum, boolean PK3) diff --git a/src/m_misc.c b/src/m_misc.c index ad2d133ab..74c30dedd 100644 --- a/src/m_misc.c +++ b/src/m_misc.c @@ -2688,3 +2688,22 @@ const char * M_Ftrim (double f) return &dig[1];/* skip the 0 */ } } + +// Returns true if the string is empty. +boolean M_IsStringEmpty(const char *s) +{ + const char *ch = s; + + if (ch == NULL || (ch && strlen(ch) < 1)) + return true; + + for (;;ch++) + { + if (!(*ch)) + break; + if (!isspace((*ch))) + return false; + } + + return true; +} diff --git a/src/m_misc.h b/src/m_misc.h index c5ef9f9f2..d23c53978 100644 --- a/src/m_misc.h +++ b/src/m_misc.h @@ -117,6 +117,9 @@ trailing zeros, or "" if the fractional part is zero. */ const char * M_Ftrim (double); +// Returns true if the string is empty. +boolean M_IsStringEmpty(const char *s); + // counting bits, for weapon ammo code, usually FUNCMATH UINT8 M_CountBits(UINT32 num, UINT8 size); diff --git a/src/p_setup.c b/src/p_setup.c index 66243fb0e..d1c0f1740 100644 --- a/src/p_setup.c +++ b/src/p_setup.c @@ -4385,10 +4385,9 @@ static lumpinfo_t* FindFolder(const char *folName, UINT16 *start, UINT16 *end, l // Add a wadfile to the active wad files, // replace sounds, musics, patches, textures, sprites and maps // -boolean P_AddWadFile(const char *wadfilename) +static boolean P_LoadAddon(UINT16 wadnum, UINT16 numlumps) { size_t i, j, sreplaces = 0, mreplaces = 0, digmreplaces = 0; - UINT16 numlumps, wadnum; char *name; lumpinfo_t *lumpinfo; @@ -4409,18 +4408,10 @@ boolean P_AddWadFile(const char *wadfilename) // UINT16 flaPos, flaNum = 0; // UINT16 mapPos, mapNum = 0; - // Init file. - if ((numlumps = W_InitFile(wadfilename, false, false)) == INT16_MAX) - { - refreshdirmenu |= REFRESHDIR_NOTLOADED; - return false; - } - else - wadnum = (UINT16)(numwadfiles-1); - switch(wadfiles[wadnum]->type) { case RET_PK3: + case RET_FOLDER: // Look for the lumps that act as resource delimitation markers. lumpinfo = wadfiles[wadnum]->lumpinfo; for (i = 0; i < numlumps; i++, lumpinfo++) @@ -4584,3 +4575,35 @@ boolean P_AddWadFile(const char *wadfilename) return true; } + +boolean P_AddWadFile(const char *wadfilename) +{ + UINT16 numlumps, wadnum; + + // Init file. + if ((numlumps = W_InitFile(wadfilename, false, false)) == INT16_MAX) + { + refreshdirmenu |= REFRESHDIR_NOTLOADED; + return false; + } + else + wadnum = (UINT16)(numwadfiles-1); + + return P_LoadAddon(wadnum, numlumps); +} + +boolean P_AddFolder(const char *folderpath) +{ + UINT16 numlumps, wadnum; + + // Init file. + if ((numlumps = W_InitFolder(folderpath, false, false)) == INT16_MAX) + { + refreshdirmenu |= REFRESHDIR_NOTLOADED; + return false; + } + else + wadnum = (UINT16)(numwadfiles-1); + + return P_LoadAddon(wadnum, numlumps); +} diff --git a/src/p_setup.h b/src/p_setup.h index 5d13ae7d4..ae849acbf 100644 --- a/src/p_setup.h +++ b/src/p_setup.h @@ -103,6 +103,7 @@ boolean P_LoadLevel(boolean fromnetsave, boolean reloadinggamestate); void HWR_LoadLevel(void); #endif boolean P_AddWadFile(const char *wadfilename); +boolean P_AddFolder(const char *folderpath); boolean P_RunSOC(const char *socfilename); void P_LoadSoundsRange(UINT16 wadnum, UINT16 first, UINT16 num); void P_LoadMusicsRange(UINT16 wadnum, UINT16 first, UINT16 num); diff --git a/src/r_textures.c b/src/r_textures.c index a006d739f..0a1248100 100644 --- a/src/r_textures.c +++ b/src/r_textures.c @@ -732,7 +732,7 @@ Rloadflats (INT32 i, INT32 w) texpatch_t *patch; // Yes - if (wadfiles[w]->type == RET_PK3) + if (W_FileHasFolders(wadfiles[w])) { texstart = W_CheckNumForFolderStartPK3("flats/", (UINT16)w, 0); texend = W_CheckNumForFolderEndPK3("flats/", (UINT16)w, texstart); @@ -754,7 +754,7 @@ Rloadflats (INT32 i, INT32 w) size_t lumplength; size_t flatsize = 0; - if (wadfiles[w]->type == RET_PK3) + if (W_FileHasFolders(wadfiles[w])) { if (W_IsLumpFolder(wadnum, lumpnum)) // Check if lump is a folder continue; // If it is then SKIP IT @@ -844,7 +844,7 @@ Rloadtextures (INT32 i, INT32 w) texpatch_t *patch; // Get the lump numbers for the markers in the WAD, if they exist. - if (wadfiles[w]->type == RET_PK3) + if (W_FileHasFolders(wadfiles[w])) { texstart = W_CheckNumForFolderStartPK3("textures/", (UINT16)w, 0); texend = W_CheckNumForFolderEndPK3("textures/", (UINT16)w, texstart); @@ -875,7 +875,7 @@ Rloadtextures (INT32 i, INT32 w) size_t lumplength; #endif - if (wadfiles[w]->type == RET_PK3) + if (W_FileHasFolders(wadfiles[w])) { if (W_IsLumpFolder(wadnum, lumpnum)) // Check if lump is a folder continue; // If it is then SKIP IT @@ -964,7 +964,7 @@ void R_LoadTextures(void) { #ifdef WALLFLATS // Count flats - if (wadfiles[w]->type == RET_PK3) + if (W_FileHasFolders(wadfiles[w])) { texstart = W_CheckNumForFolderStartPK3("flats/", (UINT16)w, 0); texend = W_CheckNumForFolderEndPK3("flats/", (UINT16)w, texstart); @@ -978,7 +978,7 @@ void R_LoadTextures(void) if (!( texstart == INT16_MAX || texend == INT16_MAX )) { // PK3s have subfolders, so we can't just make a simple sum - if (wadfiles[w]->type == RET_PK3) + if (W_FileHasFolders(wadfiles[w])) { for (j = texstart; j < texend; j++) { @@ -1002,7 +1002,7 @@ void R_LoadTextures(void) } // Count single-patch textures - if (wadfiles[w]->type == RET_PK3) + if (W_FileHasFolders(wadfiles[w])) { texstart = W_CheckNumForFolderStartPK3("textures/", (UINT16)w, 0); texend = W_CheckNumForFolderEndPK3("textures/", (UINT16)w, texstart); @@ -1017,7 +1017,7 @@ void R_LoadTextures(void) continue; // PK3s have subfolders, so we can't just make a simple sum - if (wadfiles[w]->type == RET_PK3) + if (W_FileHasFolders(wadfiles[w])) { for (j = texstart; j < texend; j++) { @@ -1558,6 +1558,7 @@ lumpnum_t R_GetFlatNumForName(const char *name) continue; break; case RET_PK3: + case RET_FOLDER: if ((start = W_CheckNumForFolderStartPK3("Flats/", i, 0)) == INT16_MAX) continue; if ((end = W_CheckNumForFolderEndPK3("Flats/", i, start)) == INT16_MAX) diff --git a/src/r_things.c b/src/r_things.c index 14eed9cf2..135c4c5bf 100644 --- a/src/r_things.c +++ b/src/r_things.c @@ -443,6 +443,7 @@ void R_AddSpriteDefs(UINT16 wadnum) end = W_CheckNumForNamePwad("SS_END",wadnum,start); //deutex compatib. break; case RET_PK3: + case RET_FOLDER: start = W_CheckNumForFolderStartPK3("Sprites/", wadnum, 0); end = W_CheckNumForFolderEndPK3("Sprites/", wadnum, start); break; diff --git a/src/w_wad.c b/src/w_wad.c index 2cbcdecb5..b305d89e9 100644 --- a/src/w_wad.c +++ b/src/w_wad.c @@ -50,16 +50,17 @@ #include "filesrch.h" -#include "i_video.h" // rendermode +#include "d_main.h" #include "d_netfil.h" -#include "dehacked.h" #include "d_clisrv.h" +#include "dehacked.h" #include "r_defs.h" #include "r_data.h" #include "r_textures.h" #include "r_patch.h" #include "r_picformats.h" #include "i_system.h" +#include "i_video.h" // rendermode #include "md5.h" #include "lua_script.h" #ifdef SCANTHINGS @@ -117,10 +118,15 @@ void W_Shutdown(void) { wadfile_t *wad = wadfiles[numwadfiles]; - fclose(wad->handle); + if (wad->handle) + fclose(wad->handle); Z_Free(wad->filename); + if (wad->path) + Z_Free(wad->path); while (wad->numlumps--) { + if (wad->lumpinfo[wad->numlumps].diskpath) + Z_Free(wad->lumpinfo[wad->numlumps].diskpath); Z_Free(wad->lumpinfo[wad->numlumps].longname); Z_Free(wad->lumpinfo[wad->numlumps].fullname); } @@ -421,6 +427,7 @@ static lumpinfo_t* ResGetLumpsWad (FILE* handle, UINT16* nlmp, const char* filen { lump_p->position = LONG(fileinfo->filepos); lump_p->size = lump_p->disksize = LONG(fileinfo->size); + lump_p->diskpath = NULL; if (compressed) // wad is compressed, lump might be { UINT32 realsize = 0; @@ -602,6 +609,7 @@ static lumpinfo_t* ResGetLumpsZip (FILE* handle, UINT16* nlmp) lump_p->position = zentry.offset; // NOT ACCURATE YET: we still need to read the local entry to find our true position lump_p->disksize = zentry.compsize; + lump_p->diskpath = NULL; lump_p->size = zentry.size; fullname = malloc(zentry.namelen + 1); @@ -679,6 +687,58 @@ static lumpinfo_t* ResGetLumpsZip (FILE* handle, UINT16* nlmp) return lumpinfo; } +// Checks if the combination of the first path and the second path are valid. +// If they are, the concatenated path is returned. +static char *W_CheckFolderPath(const char *startpath, const char *path) +{ + if (checkfolderpath(path, startpath, false)) + { + char *fn; + + if (startpath) + { + size_t len = strlen(startpath) + strlen(path) + strlen(PATHSEP) + 1; + fn = ZZ_Alloc(len); + snprintf(fn, len, "%s" PATHSEP "%s", startpath, path); + } + else + fn = Z_StrDup(path); + + return fn; + } + + return NULL; +} + +// Returns the first valid path for a folder. +static char *W_GetFullFolderPath(const char *path) +{ + // Check the path by itself first. + char *fn = W_CheckFolderPath(NULL, path); + if (fn) + return fn; + +#define checkpath(startpath) { \ + fn = W_CheckFolderPath(startpath, path); \ + if (fn) \ + return fn; \ +} \ + + checkpath(srb2home) // Then, look in srb2home. + checkpath(srb2path) // Now, look in srb2path. + checkpath(".") // Finally, look in ".". + +#undef checkpath + + return NULL; +} + +// Loads files from a folder into a lumpinfo structure. +static lumpinfo_t *ResGetLumpsFolder(const char *path, UINT16 *nlmp, UINT16 *nfiles, UINT16 *nfolders) +{ + return getfolderfiles(path, nlmp, nfiles, nfolders); +} + static UINT16 W_InitFileError (const char *filename, boolean exitworthy) { if (exitworthy) @@ -694,6 +754,19 @@ static UINT16 W_InitFileError (const char *filename, boolean exitworthy) return INT16_MAX; } +static void W_ReadFileShaders(wadfile_t *wadfile) +{ +#ifdef HWRENDER + if (rendermode == render_opengl && (vid.glstate == VID_GL_LIBRARY_LOADED)) + { + HWR_LoadCustomShadersFromFile(numwadfiles - 1, W_FileHasFolders(wadfile)); + HWR_CompileShaders(); + } +#else + (void)wadfile; +#endif +} + // Allocate a wadfile, setup the lumpinfo (directory) and // lumpcache, add the wadfile to the current active wadfiles // @@ -760,7 +833,7 @@ UINT16 W_InitFile(const char *filename, boolean mainfile, boolean startup) // see PutFileNeeded in d_netfil.c if ((important = !important)) { - packetsize = packetsizetally + nameonlylength(filename) + 22; + packetsize = packetsizetally + nameonlylength(filename) + FILENEEDEDSIZE; if (packetsize > MAXFILENEEDED*sizeof(UINT8)) { @@ -788,7 +861,7 @@ UINT16 W_InitFile(const char *filename, boolean mainfile, boolean startup) { CONS_Alert(CONS_ERROR, M_GetText("%s is already loaded\n"), filename); if (important) - packetsizetally -= nameonlylength(filename) + 22; + packetsizetally -= nameonlylength(filename) + FILENEEDEDSIZE; if (handle) fclose(handle); return W_InitFileError(filename, false); @@ -828,9 +901,11 @@ UINT16 W_InitFile(const char *filename, boolean mainfile, boolean startup) // wadfile = Z_Malloc(sizeof (*wadfile), PU_STATIC, NULL); wadfile->filename = Z_StrDup(filename); + wadfile->path = NULL; wadfile->type = type; wadfile->handle = handle; - wadfile->numlumps = (UINT16)numlumps; + wadfile->numlumps = numlumps; + wadfile->filecount = wadfile->foldercount = 0; wadfile->lumpinfo = lumpinfo; wadfile->important = important; fseek(handle, 0, SEEK_END); @@ -853,14 +928,8 @@ UINT16 W_InitFile(const char *filename, boolean mainfile, boolean startup) wadfiles[numwadfiles] = wadfile; numwadfiles++; // must come BEFORE W_LoadDehackedLumps, so any addfile called by COM_BufInsertText called by Lua doesn't overwrite what we just loaded -#ifdef HWRENDER // Read shaders from file - if (rendermode == render_opengl && (vid.glstate == VID_GL_LIBRARY_LOADED)) - { - HWR_LoadCustomShadersFromFile(numwadfiles - 1, (type == RET_PK3)); - HWR_CompileShaders(); - } -#endif // HWRENDER + W_ReadFileShaders(wadfile); // TODO: HACK ALERT - Load Lua & SOC stuff right here. I feel like this should be out of this place, but... Let's stick with this for now. switch (wadfile->type) @@ -886,6 +955,153 @@ UINT16 W_InitFile(const char *filename, boolean mainfile, boolean startup) return wadfile->numlumps; } +// +// Loads a folder as a WAD. +// +UINT16 W_InitFolder(const char *path, boolean mainfile, boolean startup) +{ + lumpinfo_t *lumpinfo = NULL; + wadfile_t *wadfile; + UINT16 numlumps = 0; + UINT16 filecount, foldercount; + size_t i; + char *fn, *fullpath; + const char *p; + int important; + + if (!(refreshdirmenu & REFRESHDIR_ADDFILE)) + refreshdirmenu = REFRESHDIR_NORMAL|REFRESHDIR_ADDFILE; // clean out cons_alerts that happened earlier + + if (refreshdirname) + Z_Free(refreshdirname); + if (dirmenu) + refreshdirname = Z_StrDup(path); + else + refreshdirname = NULL; + + if (numwadfiles >= MAX_WADFILES) + { + CONS_Alert(CONS_ERROR, M_GetText("Maximum wad files reached\n")); + refreshdirmenu |= REFRESHDIR_MAX; + return W_InitFileError(path, startup); + } + + important = 0; // ??? + + /// \todo Implement a W_VerifyFolder. + if ((important = !important)) + { + size_t packetsize = packetsizetally + strlen(path) + FILENEEDEDSIZE; + + if (packetsize > MAXFILENEEDED*sizeof(UINT8)) + { + CONS_Alert(CONS_ERROR, M_GetText("Maximum wad files reached\n")); + refreshdirmenu |= REFRESHDIR_MAX; + return W_InitFileError(path, startup); + } + + packetsizetally = packetsize; + } + + // Remove path separators from the filename, and don't try adding "/". + p = path+strlen(path); + --p; + + while (*p == '\\' || *p == '/' || *p == ':') + { + p--; + if (p < path) + { + CONS_Alert(CONS_ERROR, M_GetText("Path %s is prohibited\n"), path); + return W_InitFileError(path, startup); + } + } + p++; + + // Allocate the new path name. + i = (p - path) + 1; + fn = ZZ_Alloc(i); + strlcpy(fn, path, i); + + if (M_IsStringEmpty(fn)) + { + CONS_Alert(CONS_ERROR, M_GetText("Folder name is empty\n")); + Z_Free(fn); + + if (startup) + return W_InitFileError("A folder", true); + else + return W_InitFileError("a folder", false); + } + + // Get the full path for this filename. + fullpath = W_GetFullFolderPath(fn); + if (fullpath == NULL) + { + Z_Free(fn); + return W_InitFileError(path, false); + } + + for (i = 0; i < numwadfiles; i++) + { + if (wadfiles[i]->type != RET_FOLDER) + continue; + + if (samepaths(wadfiles[i]->path, fullpath) > 0) + { + CONS_Alert(CONS_ERROR, M_GetText("%s is already loaded\n"), path); + if (important) + packetsizetally -= strlen(path) + FILENEEDEDSIZE; + Z_Free(fn); + Z_Free(fullpath); + return W_InitFileError(path, false); + } + } + + lumpinfo = ResGetLumpsFolder(fullpath, &numlumps, &filecount, &foldercount); + if (lumpinfo == NULL) + { + if (filecount == UINT16_MAX) + CONS_Alert(CONS_ERROR, M_GetText("Folder %s is empty\n"), path); + + Z_Free(fn); + Z_Free(fullpath); + + return W_InitFileError(path, startup); + } + + if (important && !mainfile) + G_SetGameModified(true); + + wadfile = Z_Malloc(sizeof (*wadfile), PU_STATIC, NULL); + wadfile->filename = fn; + wadfile->path = fullpath; + wadfile->type = RET_FOLDER; + wadfile->handle = NULL; + wadfile->numlumps = numlumps; + wadfile->filecount = filecount; + wadfile->foldercount = foldercount; + wadfile->lumpinfo = lumpinfo; + wadfile->important = important; + + // Irrelevant. + wadfile->filesize = 0; + memset(wadfile->md5sum, 0x00, 16); + + Z_Calloc(numlumps * sizeof (*wadfile->lumpcache), PU_STATIC, &wadfile->lumpcache); + Z_Calloc(numlumps * sizeof (*wadfile->patchcache), PU_STATIC, &wadfile->patchcache); + + CONS_Printf(M_GetText("Added folder %s (%u files, %u folders)\n"), fn, filecount, foldercount); + wadfiles[numwadfiles] = wadfile; + numwadfiles++; + + W_ReadFileShaders(wadfile); + W_LoadDehackedLumpsPK3(numwadfiles - 1, mainfile); + W_InvalidateLumpnumCache(); + + return wadfile->numlumps; +} + /** Tries to load a series of files. * All files are wads unless they have an extension of ".soc" or ".lua". * @@ -897,11 +1113,18 @@ UINT16 W_InitFile(const char *filename, boolean mainfile, boolean startup) */ void W_InitMultipleFiles(char **filenames) { - // will be realloced as lumps are added for (; *filenames; filenames++) { - //CONS_Debug(DBG_SETUP, "Loading %s\n", *filenames); - W_InitFile(*filenames, numwadfiles < mainwads, true); + const char *fn = (*filenames); + char pathsep = fn[strlen(fn) - 1]; + boolean mainfile = (numwadfiles < mainwads); + + //CONS_Debug(DBG_SETUP, "Loading %s\n", fn); + + if (pathsep == '\\' || pathsep == '/') + W_InitFolder(fn, mainfile, true); + else + W_InitFile(fn, mainfile, true); } } @@ -1175,7 +1398,7 @@ lumpnum_t W_CheckNumForMap(const char *name) if (!strncmp(name, (wadfiles[i]->lumpinfo + lumpNum)->name, 8)) return (i<<16) + lumpNum; } - else if (wadfiles[i]->type == RET_PK3) + else if (W_FileHasFolders(wadfiles[i])) { lumpNum = W_CheckNumForFolderStartPK3("maps/", i, 0); if (lumpNum != INT16_MAX) @@ -1273,9 +1496,34 @@ UINT8 W_LumpExists(const char *name) size_t W_LumpLengthPwad(UINT16 wad, UINT16 lump) { + lumpinfo_t *l; + if (!TestValidLump(wad, lump)) return 0; - return wadfiles[wad]->lumpinfo[lump].size; + + l = wadfiles[wad]->lumpinfo + lump; + + if (wadfiles[wad]->type == RET_FOLDER) + { + INT32 stat = pathisfolder(l->diskpath); + + if (stat < 0) + I_Error("W_LumpLengthPwad: could not stat %s", l->diskpath); + else if (stat == 1) // Path is a folder. + return 0; + else + { + FILE *handle = fopen(l->diskpath, "rb"); + if (handle == NULL) + I_Error("W_LumpLengthPwad: could not open file %s", l->diskpath); + + fseek(handle, 0, SEEK_END); + l->size = l->disksize = ftell(handle); + fclose(handle); + } + } + + return l->size; } /** Returns the buffer size needed to load the given lump. @@ -1294,7 +1542,7 @@ size_t W_LumpLength(lumpnum_t lumpnum) // boolean W_IsLumpWad(lumpnum_t lumpnum) { - if (wadfiles[WADFILENUM(lumpnum)]->type == RET_PK3) + if (W_FileHasFolders(wadfiles[WADFILENUM(lumpnum)])) { const char *lumpfullName = (wadfiles[WADFILENUM(lumpnum)]->lumpinfo + LUMPNUM(lumpnum))->fullname; @@ -1312,7 +1560,7 @@ boolean W_IsLumpWad(lumpnum_t lumpnum) // boolean W_IsLumpFolder(UINT16 wad, UINT16 lump) { - if (wadfiles[wad]->type == RET_PK3) + if (W_FileHasFolders(wadfiles[wad])) { const char *name = wadfiles[wad]->lumpinfo[lump].fullname; @@ -1362,17 +1610,44 @@ void zerr(int ret) */ size_t W_ReadLumpHeaderPwad(UINT16 wad, UINT16 lump, void *dest, size_t size, size_t offset) { - size_t lumpsize; + size_t lumpsize, bytesread; lumpinfo_t *l; - FILE *handle; + FILE *handle = NULL; if (!TestValidLump(wad,lump)) return 0; + l = wadfiles[wad]->lumpinfo + lump; + + // Open the external file for this lump, if the WAD is a folder. + if (wadfiles[wad]->type == RET_FOLDER) + { + INT32 stat = pathisfolder(l->diskpath); + + if (stat < 0) + I_Error("W_ReadLumpHeaderPwad: could not stat %s", l->diskpath); + else if (stat == 1) // Path is a folder. + return 0; + else + { + handle = fopen(l->diskpath, "rb"); + if (handle == NULL) + I_Error("W_ReadLumpHeaderPwad: could not open file %s", l->diskpath); + + // Find length of file + fseek(handle, 0, SEEK_END); + l->size = l->disksize = ftell(handle); + } + } + lumpsize = wadfiles[wad]->lumpinfo[lump].size; // empty resource (usually markers like S_START, F_END ..) if (!lumpsize || lumpsizetype == RET_FOLDER) + fclose(handle); return 0; + } // zero size means read all the lump if (!size || size+offset > lumpsize) @@ -1380,24 +1655,22 @@ size_t W_ReadLumpHeaderPwad(UINT16 wad, UINT16 lump, void *dest, size_t size, si // Let's get the raw lump data. // We setup the desired file handle to read the lump data. - l = wadfiles[wad]->lumpinfo + lump; - handle = wadfiles[wad]->handle; + if (wadfiles[wad]->type != RET_FOLDER) + handle = wadfiles[wad]->handle; fseek(handle, (long)(l->position + offset), SEEK_SET); // But let's not copy it yet. We support different compression formats on lumps, so we need to take that into account. switch(wadfiles[wad]->lumpinfo[lump].compression) { case CM_NOCOMPRESSION: // If it's uncompressed, we directly write the data into our destination, and return the bytes read. + bytesread = fread(dest, 1, size, handle); + if (wadfiles[wad]->type == RET_FOLDER) + fclose(handle); #ifdef NO_PNG_LUMPS - { - size_t bytesread = fread(dest, 1, size, handle); - if (Picture_IsLumpPNG((UINT8 *)dest, bytesread)) - Picture_ThrowPNGError(l->fullname, wadfiles[wad]->filename); - return bytesread; - } -#else - return fread(dest, 1, size, handle); + if (Picture_IsLumpPNG((UINT8 *)dest, bytesread)) + Picture_ThrowPNGError(l->fullname, wadfiles[wad]->filename); #endif + return bytesread; case CM_LZF: // Is it LZF compressed? Used by ZWADs. { #ifdef ZWAD diff --git a/src/w_wad.h b/src/w_wad.h index d0a86bcb4..8b3c3808e 100644 --- a/src/w_wad.h +++ b/src/w_wad.h @@ -69,6 +69,7 @@ typedef struct char name[9]; // filelump_t name[] e.g. "LongEntr" char *longname; // e.g. "LongEntryName" char *fullname; // e.g. "Folder/Subfolder/LongEntryName.extension" + char *diskpath; // path to the file e.g. "/usr/games/srb2/Addon/Folder/Subfolder/LongEntryName.extension" size_t size; // real (uncompressed) size compmethod compression; // lump compression method } lumpinfo_t; @@ -109,17 +110,19 @@ typedef enum restype RET_SOC, RET_LUA, RET_PK3, + RET_FOLDER, RET_UNKNOWN, } restype_t; typedef struct wadfile_s { - char *filename; + char *filename, *path; restype_t type; lumpinfo_t *lumpinfo; lumpcache_t *lumpcache; lumpcache_t *patchcache; UINT16 numlumps; // this wad's number of resources + UINT16 filecount, foldercount; // file and folder count FILE *handle; UINT32 filesize; // for network UINT8 md5sum[16]; @@ -127,7 +130,7 @@ typedef struct wadfile_s boolean important; // also network - !W_VerifyNMUSlumps } wadfile_t; -#define WADFILENUM(lumpnum) (UINT16)((lumpnum)>>16) // wad flumpnum>>16) // wad file number in upper word +#define WADFILENUM(lumpnum) (UINT16)((lumpnum)>>16) // wad file number in upper word #define LUMPNUM(lumpnum) (UINT16)((lumpnum)&0xFFFF) // lump number for this pwad extern UINT16 numwadfiles; @@ -141,10 +144,14 @@ void W_Shutdown(void); FILE *W_OpenWadFile(const char **filename, boolean useerrors); // Load and add a wadfile to the active wad files, returns numbers of lumps, INT16_MAX on error UINT16 W_InitFile(const char *filename, boolean mainfile, boolean startup); +// Adds a folder as a file +UINT16 W_InitFolder(const char *path, boolean mainfile, boolean startup); // W_InitMultipleFiles exits if a file was not found, but not if all is okay. void W_InitMultipleFiles(char **filenames); +#define W_FileHasFolders(wadfile) ((wadfile)->type == RET_PK3 || (wadfile)->type == RET_FOLDER) + const char *W_CheckNameForNumPwad(UINT16 wad, UINT16 lump); const char *W_CheckNameForNum(lumpnum_t lumpnum); From 08937f892a4eec9c6cdf7c0c3c1ce255941c8ba6 Mon Sep 17 00:00:00 2001 From: Jaime Ita Passos Date: Tue, 23 Mar 2021 01:18:28 -0300 Subject: [PATCH 045/126] Allocate a buffer for non-RGBA to RGBA texture conversions. UpdateTexture will I_Error (from AllocTextureBuffer) if the allocation fails. --- src/hardware/r_opengl/r_opengl.c | 124 ++++++++++++++++++++----------- 1 file changed, 82 insertions(+), 42 deletions(-) diff --git a/src/hardware/r_opengl/r_opengl.c b/src/hardware/r_opengl/r_opengl.c index 6967bab74..7c77c27ea 100644 --- a/src/hardware/r_opengl/r_opengl.c +++ b/src/hardware/r_opengl/r_opengl.c @@ -131,7 +131,6 @@ static const GLfloat byte2float[256] = { // -----------------+ // GL_DBG_Printf : Output debug messages to debug log if DEBUG_TO_FILE is defined, // : else do nothing -// Returns : // -----------------+ #ifdef DEBUG_TO_FILE @@ -159,8 +158,6 @@ FUNCPRINTF void GL_DBG_Printf(const char *format, ...) // -----------------+ // GL_MSG_Warning : Raises a warning. -// : -// Returns : // -----------------+ static void GL_MSG_Warning(const char *format, ...) @@ -184,8 +181,6 @@ static void GL_MSG_Warning(const char *format, ...) // -----------------+ // GL_MSG_Error : Raises an error. -// : -// Returns : // -----------------+ static void GL_MSG_Error(const char *format, ...) @@ -207,6 +202,32 @@ static void GL_MSG_Error(const char *format, ...) #endif } +// ----------------------+ +// GetTextureFormatName : Returns the corresponding texture format string from the texture format enumeration. +// ----------------------+ +static const char *GetTextureFormatName(INT32 format) +{ + static char num[12]; + + switch (format) + { + case GL_TEXFMT_P_8: return "GL_TEXFMT_P_8"; + case GL_TEXFMT_AP_88: return "GL_TEXFMT_AP_88"; + case GL_TEXFMT_RGBA: return "GL_TEXFMT_RGBA"; + case GL_TEXFMT_ALPHA_8: return "GL_TEXFMT_ALPHA_8"; + case GL_TEXFMT_INTENSITY_8: return "GL_TEXFMT_INTENSITY_8"; + case GL_TEXFMT_ALPHA_INTENSITY_88: return "GL_TEXFMT_ALPHA_INTENSITY_88"; + default: break; + } + + // If the texture format is not known (due to it being invalid), + // return a string containing the format index instead. + format = INT32_MIN; + snprintf(num, sizeof(num), "%d", format); + + return num; +} + #ifdef STATIC_OPENGL /* 1.0 functions */ /* Miscellaneous */ @@ -1375,7 +1396,6 @@ INT32 isExtAvailable(const char *extension, const GLubyte *start) // -----------------+ // Init : Initialise the OpenGL interface API -// Returns : // -----------------+ EXPORT boolean HWRAPI(Init) (void) { @@ -1737,37 +1757,59 @@ EXPORT void HWRAPI(SetBlend) (FBITFIELD PolyFlags) CurrentPolyFlags = PolyFlags; } +// -------------------+ +// AllocTextureBuffer : Allocates memory for converting a non-RGBA texture into an RGBA texture. +// -------------------+ +static RGBA_t *AllocTextureBuffer(GLMipmap_t *pTexInfo) +{ + size_t len = (pTexInfo->width * pTexInfo->height); + RGBA_t *tex = calloc(len, sizeof(RGBA_t)); + + if (tex == NULL) + I_Error("AllocTextureBuffer: out of memory allocating %s bytes for texture %d, format %s", + sizeu1(len * sizeof(RGBA_t)), pTexInfo->downloaded, GetTextureFormatName(pTexInfo->format)); + + return tex; +} + +// ------------------+ +// FreeTextureBuffer : Frees memory allocated by AllocTextureBuffer. +// ------------------+ +static void FreeTextureBuffer(RGBA_t *tex) +{ + if (tex) + free(tex); +} + // -----------------+ -// UpdateTexture : Updates the texture data. +// UpdateTexture : Updates texture data. // -----------------+ EXPORT void HWRAPI(UpdateTexture) (GLMipmap_t *pTexInfo) { - // Download a mipmap - boolean updatemipmap = true; - static RGBA_t tex[2048*2048]; - const GLvoid *ptex = tex; - INT32 w, h; - GLuint texnum = 0; + // Upload a texture + GLuint num = pTexInfo->downloaded; + boolean update = true; - if (!pTexInfo->downloaded) + INT32 w = pTexInfo->width, h = pTexInfo->height; + INT32 i, j; + + const GLubyte *pImgData = (const GLubyte *)pTexInfo->data; + const GLvoid *ptex = NULL; + RGBA_t *tex = NULL; + + // Generate a new texture name. + if (!num) { - pglGenTextures(1, &texnum); - pTexInfo->downloaded = texnum; - updatemipmap = false; + pglGenTextures(1, &num); + pTexInfo->downloaded = num; + update = false; } - else - texnum = pTexInfo->downloaded; - //GL_DBG_Printf ("DownloadMipmap %d %x\n",(INT32)texnum,pTexInfo->data); + //GL_DBG_Printf("UpdateTexture %d %x\n", (INT32)num, pImgData); - w = pTexInfo->width; - h = pTexInfo->height; - - if ((pTexInfo->format == GL_TEXFMT_P_8) || - (pTexInfo->format == GL_TEXFMT_AP_88)) + if ((pTexInfo->format == GL_TEXFMT_P_8) || (pTexInfo->format == GL_TEXFMT_AP_88)) { - const GLubyte *pImgData = (const GLubyte *)pTexInfo->data; - INT32 i, j; + ptex = tex = AllocTextureBuffer(pTexInfo); for (j = 0; j < h; j++) { @@ -1798,20 +1840,17 @@ EXPORT void HWRAPI(UpdateTexture) (GLMipmap_t *pTexInfo) tex[w*j+i].s.alpha = *pImgData; pImgData++; } - } } } else if (pTexInfo->format == GL_TEXFMT_RGBA) { - // corona test : passed as ARGB 8888, which is not in glide formats - // Hurdler: not used for coronas anymore, just for dynamic lighting - ptex = pTexInfo->data; + // Directly upload the texture data without any kind of conversion. + ptex = pImgData; } else if (pTexInfo->format == GL_TEXFMT_ALPHA_INTENSITY_88) { - const GLubyte *pImgData = (const GLubyte *)pTexInfo->data; - INT32 i, j; + ptex = tex = AllocTextureBuffer(pTexInfo); for (j = 0; j < h; j++) { @@ -1828,8 +1867,7 @@ EXPORT void HWRAPI(UpdateTexture) (GLMipmap_t *pTexInfo) } else if (pTexInfo->format == GL_TEXFMT_ALPHA_8) // Used for fade masks { - const GLubyte *pImgData = (const GLubyte *)pTexInfo->data; - INT32 i, j; + ptex = tex = AllocTextureBuffer(pTexInfo); for (j = 0; j < h; j++) { @@ -1844,11 +1882,10 @@ EXPORT void HWRAPI(UpdateTexture) (GLMipmap_t *pTexInfo) } } else - GL_MSG_Warning ("SetTexture(bad format) %ld\n", pTexInfo->format); + GL_MSG_Warning("UpdateTexture: bad format %d\n", pTexInfo->format); - // the texture number was already generated by pglGenTextures - pglBindTexture(GL_TEXTURE_2D, texnum); - tex_downloaded = texnum; + pglBindTexture(GL_TEXTURE_2D, num); + tex_downloaded = num; // disable texture filtering on any texture that has holes so there's no dumb borders or blending issues if (pTexInfo->flags & TF_TRANSPARENT) @@ -1877,7 +1914,7 @@ EXPORT void HWRAPI(UpdateTexture) (GLMipmap_t *pTexInfo) } else { - if (updatemipmap) + if (update) pglTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, w, h, GL_RGBA, GL_UNSIGNED_BYTE, ptex); else pglTexImage2D(GL_TEXTURE_2D, 0, GL_LUMINANCE_ALPHA, w, h, 0, GL_RGBA, GL_UNSIGNED_BYTE, ptex); @@ -1898,7 +1935,7 @@ EXPORT void HWRAPI(UpdateTexture) (GLMipmap_t *pTexInfo) } else { - if (updatemipmap) + if (update) pglTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, w, h, GL_RGBA, GL_UNSIGNED_BYTE, ptex); else pglTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, w, h, 0, GL_RGBA, GL_UNSIGNED_BYTE, ptex); @@ -1918,13 +1955,16 @@ EXPORT void HWRAPI(UpdateTexture) (GLMipmap_t *pTexInfo) } else { - if (updatemipmap) + if (update) pglTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, w, h, GL_RGBA, GL_UNSIGNED_BYTE, ptex); else pglTexImage2D(GL_TEXTURE_2D, 0, textureformatGL, w, h, 0, GL_RGBA, GL_UNSIGNED_BYTE, ptex); } } + // Free the texture buffer + FreeTextureBuffer(tex); + if (pTexInfo->flags & TF_WRAPX) pglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); else From 21da6ce704c5665a77fcb42e21beb995fce65453 Mon Sep 17 00:00:00 2001 From: Zwip-Zwap Zapony Date: Tue, 27 Apr 2021 21:30:00 +0200 Subject: [PATCH 046/126] Auto-crop at splitscreen borders V_DrawCroppedPatch will no longer go beyond splitscreen borders when V_PERPLAYER is used --- src/hardware/hw_draw.c | 70 ++++++++++++++++++++++++++++++++++++++++++ src/v_video.c | 31 +++++++++++++++++++ 2 files changed, 101 insertions(+) diff --git a/src/hardware/hw_draw.c b/src/hardware/hw_draw.c index 8722495e4..7deb36b69 100644 --- a/src/hardware/hw_draw.c +++ b/src/hardware/hw_draw.c @@ -596,6 +596,76 @@ void HWR_DrawCroppedPatch(patch_t *gpatch, fixed_t x, fixed_t y, fixed_t pscale, if (option & V_WRAPY) flags |= PF_ForceWrapY; + // Auto-crop at splitscreen borders! + if (splitscreen && (option & V_PERPLAYER)) + { +#define flerp(a,b,amount) (((a) * (1.0f - (amount))) + ((b) * (amount))) // Float lerp + +#ifdef QUADS + if (splitscreen > 1) // 3 or 4 players + { + #error Auto-cropping doesnt take quadscreen into account! Fix it! + // Hint: For player 1/2, copy player 1's code below. For player 3/4, copy player 2's code below + // For player 1/3 and 2/4, mangle the below code to apply horizontally instead of vertically + } + else +#endif + // 2 players + { + if (stplyr == &players[displayplayer]) // Player 1's screen, crop at the bottom + { + if ((cy - fheight) < 0) // If the bottom is below the border + { + if (cy <= 0) // If the whole patch is beyond the border... + return; // ...crop away the entire patch, don't draw anything + + if (fheight <= 0) // Don't divide by zero + return; + + v[2].y = v[3].y = 0; // Clamp the polygon edge vertex position + // Now for the UV-map... Uh-oh, math time! + + // On second thought, a basic linear interpolation suffices + //float full_height = fheight; + //float cropped_height = fheight - cy; + //float remaining_height = cy; + //float cropped_percentage = (fheight - cy) / fheight; + //float remaining_percentage = cy / fheight; + //v[2].t = v[3].t = lerp(v[2].t, v[0].t, cropped_percentage); + // By swapping v[2] and v[0], we can use remaining_percentage for less operations + //v[2].t = v[3].t = lerp(v[0].t, v[2].t, remaining_percentage); + + v[2].t = v[3].t = flerp(v[0].t, v[2].t, cy/fheight); + } + } + else //if (stplyr == &players[secondarydisplayplayer]) // Player 2's screen, crop at the top + { + if (cy > 0) // If the top is above the border + { + if ((cy - fheight) >= 0) // If the whole patch is beyond the border... + return; // ...crop away the entire patch, don't draw anything + + if (fheight <= 0) // Don't divide by zero + return; + + v[0].y = v[1].y = 0; // Clamp the polygon edge vertex position + // Now for the UV-map... Uh-oh, math time! + + // On second thought, a basic linear interpolation suffices + //float full_height = fheight; + //float cropped_height = cy; + //float remaining_height = fheight - cy; + //float cropped_percentage = cy / fheight; + //float remaining_percentage = (fheight - cy) / fheight; + //v[0].t = v[1].t = lerp(v[0].t, v[2].t, cropped_percentage); + + v[0].t = v[1].t = flerp(v[0].t, v[2].t, cy/fheight); + } + } + } +#undef flerp + } + // clip it since it is used for bunny scroll in doom I if (alphalevel) { diff --git a/src/v_video.c b/src/v_video.c index 4ac8e4d27..bc919af72 100644 --- a/src/v_video.c +++ b/src/v_video.c @@ -1019,6 +1019,37 @@ void V_DrawCroppedPatch(fixed_t x, fixed_t y, fixed_t pscale, fixed_t vscale, IN desttop += (y*vid.width) + x; } + // Auto-crop at splitscreen borders! + if (splitscreen && (scrn & V_PERPLAYER)) + { +#ifdef QUADS + if (splitscreen > 1) // 3 or 4 players + { + #error Auto-cropping doesnt take quadscreen into account! Fix it! + // Hint: For player 1/2, copy player 1's code below. For player 3/4, copy player 2's code below + // For player 1/3 and 2/4, hijack the X wrap prevention lines? That's probably easiest + } + else +#endif + // 2 players + { + if (stplyr == &players[displayplayer]) // Player 1's screen, crop at the bottom + { + // Just put a big old stop sign halfway through the screen + deststop -= vid.rowbytes * (vid.height>>1); + } + else //if (stplyr == &players[secondarydisplayplayer]) // Player 2's screen, crop at the top + { + if (y < (vid.height>>1)) // If the top is above the border + { + sy += ((vid.height>>1) - y) * rowfrac; // Start further down on the patch + h -= ((vid.height>>1) - y) * rowfrac; // Draw less downwards from the start + desttop += ((vid.height>>1) - y) * vid.width; // Start drawing at the border + } + } + } + } + for (col = sx; (col>>FRACBITS) < patch->width && (col - sx) < w; col += colfrac, ++x, desttop++) { INT32 topdelta, prevdelta = -1; From bda0ffe94b4491628df2626d4831c98a475a6e1f Mon Sep 17 00:00:00 2001 From: SMS Alfredo <65426124+SMS-Alfredo@users.noreply.github.com> Date: Fri, 30 Apr 2021 18:20:55 -0500 Subject: [PATCH 047/126] You've been blocked --- src/lua_hook.h | 2 +- src/lua_hooklib.c | 74 +++++++++++++++++++++++++++++++++++++++++++++++ src/p_mobj.c | 2 +- 3 files changed, 76 insertions(+), 2 deletions(-) diff --git a/src/lua_hook.h b/src/lua_hook.h index 0d631aa4e..8d80ec8c6 100644 --- a/src/lua_hook.h +++ b/src/lua_hook.h @@ -106,7 +106,7 @@ boolean LUAh_HurtMsg(player_t *player, mobj_t *inflictor, mobj_t *source, UINT8 #define LUAh_PlayerSpawn(player) LUAh_PlayerHook(player, hook_PlayerSpawn) // Hook for G_SpawnPlayer #define LUAh_ShieldSpawn(player) LUAh_PlayerHook(player, hook_ShieldSpawn) // Hook for P_SpawnShieldOrb #define LUAh_ShieldSpecial(player) LUAh_PlayerHook(player, hook_ShieldSpecial) // Hook for shield abilities -#define LUAh_MobjMoveBlocked(mo) LUAh_MobjHook(mo, hook_MobjMoveBlocked) // Hook for P_XYMovement (when movement is blocked) +boolean LUAh_MobjMoveBlocked(mobj_t *mo, mobj_t *thing, line_t *line); // Hook for P_XYMovement (when movement is blocked) boolean LUAh_MapThingSpawn(mobj_t *mo, mapthing_t *mthing); // Hook for P_SpawnMapThing by mobj type boolean LUAh_FollowMobj(player_t *player, mobj_t *mobj); // Hook for P_PlayerAfterThink Smiles mobj-following UINT8 LUAh_PlayerCanDamage(player_t *player, mobj_t *mobj); // Hook for P_PlayerCanDamage diff --git a/src/lua_hooklib.c b/src/lua_hooklib.c index 1665e36b0..2b4eda525 100644 --- a/src/lua_hooklib.c +++ b/src/lua_hooklib.c @@ -1414,6 +1414,80 @@ void LUAh_NetArchiveHook(lua_CFunction archFunc) // stack: tables } +boolean LUAh_MobjMoveBlocked(mobj_t *mo, mobj_t *thing, line_t *line) +{ + hook_p hookp; + boolean hooked = false; + if (!gL || !(hooksAvailable[hook_MobjMoveBlocked/8] & (1<<(hook_MobjMoveBlocked%8)))) + return false; + + if (!(mobjhooks[MT_NULL] || mobjhooks[mo->type])) + return false; + + lua_settop(gL, 0); + lua_pushcfunction(gL, LUA_GetErrorMessage); + + // Look for all generic mobj move blocked hooks + for (hookp = mobjhooks[MT_NULL]; hookp; hookp = hookp->next) + { + if (hookp->type != hook_MobjMoveBlocked) + continue; + + ps_lua_mobjhooks++; + if (lua_gettop(gL) == 1) + { + LUA_PushUserdata(gL, mo, META_MOBJ); + LUA_PushUserdata(gL, thing, META_MOBJ); + LUA_PushUserdata(gL, line, META_LINE); + } + PushHook(gL, hookp); + lua_pushvalue(gL, -4); + lua_pushvalue(gL, -4); + lua_pushvalue(gL, -4); + if (lua_pcall(gL, 3, 1, 1)) { + if (!hookp->error || cv_debug & DBG_LUA) + CONS_Alert(CONS_WARNING,"%s\n",lua_tostring(gL, -1)); + lua_pop(gL, 1); + hookp->error = true; + continue; + } + if (lua_toboolean(gL, -1)) + hooked = true; + lua_pop(gL, 1); + } + + for (hookp = mobjhooks[mo->type]; hookp; hookp = hookp->next) + { + if (hookp->type != hook_MobjMoveBlocked) + continue; + + ps_lua_mobjhooks++; + if (lua_gettop(gL) == 1) + { + LUA_PushUserdata(gL, mo, META_MOBJ); + LUA_PushUserdata(gL, thing, META_MOBJ); + LUA_PushUserdata(gL, line, META_LINE); + } + PushHook(gL, hookp); + lua_pushvalue(gL, -4); + lua_pushvalue(gL, -4); + lua_pushvalue(gL, -4); + if (lua_pcall(gL, 3, 1, 1)) { + if (!hookp->error || cv_debug & DBG_LUA) + CONS_Alert(CONS_WARNING,"%s\n",lua_tostring(gL, -1)); + lua_pop(gL, 1); + hookp->error = true; + continue; + } + if (lua_toboolean(gL, -1)) + hooked = true; + lua_pop(gL, 1); + } + + lua_settop(gL, 0); + return hooked; +} + boolean LUAh_MapThingSpawn(mobj_t *mo, mapthing_t *mthing) { hook_p hookp; diff --git a/src/p_mobj.c b/src/p_mobj.c index 49db6daee..bac3cd2df 100644 --- a/src/p_mobj.c +++ b/src/p_mobj.c @@ -1844,7 +1844,7 @@ void P_XYMovement(mobj_t *mo) B_MoveBlocked(player); } - if (LUAh_MobjMoveBlocked(mo)) + if (LUAh_MobjMoveBlocked(mo, tmhitthing, blockingline)) { if (P_MobjWasRemoved(mo)) return; From 0edbca5e02dfc0ffa23696e4c1d86cd12f8e516b Mon Sep 17 00:00:00 2001 From: James R Date: Mon, 10 May 2021 16:51:18 -0700 Subject: [PATCH 048/126] Actually report server version mismatches Incompatible servers get dropped from the server list. It turns out the server list is also used when connecting directly to a server. This meant that if you tried connecting to a server that was incompatible, you'd just get an infinite connection screen, as if the server was completely unreachable. Now it won't drop the server if you are directly connecting to it. I also copied some incompatibility messages from HandleConnect. --- src/d_clisrv.c | 113 ++++++++++++++++++++++++++++++++++++++----------- 1 file changed, 88 insertions(+), 25 deletions(-) diff --git a/src/d_clisrv.c b/src/d_clisrv.c index 7fa6d8d59..8b80e58f5 100644 --- a/src/d_clisrv.c +++ b/src/d_clisrv.c @@ -1667,20 +1667,24 @@ static void SL_InsertServer(serverinfo_pak* info, SINT8 node) if (serverlistcount >= MAXSERVERLIST) return; // list full - if (info->_255 != 255) - return;/* old packet format */ + /* check it later if connecting to this one */ + if (node != servernode) + { + if (info->_255 != 255) + return;/* old packet format */ - if (info->packetversion != PACKETVERSION) - return;/* old new packet format */ + if (info->packetversion != PACKETVERSION) + return;/* old new packet format */ - if (info->version != VERSION) - return; // Not same version. + if (info->version != VERSION) + return; // Not same version. - if (info->subversion != SUBVERSION) - return; // Close, but no cigar. + if (info->subversion != SUBVERSION) + return; // Close, but no cigar. - if (strcmp(info->application, SRB2APPLICATION)) - return;/* that's a different mod */ + if (strcmp(info->application, SRB2APPLICATION)) + return;/* that's a different mod */ + } i = serverlistcount++; } @@ -1829,6 +1833,65 @@ void CL_UpdateServerList(boolean internetsearch, INT32 room) #endif // ifndef NONET +static const char * InvalidServerReason (INT32 i) +{ +#define EOT "\nPress ESC\n" + + serverinfo_pak *info = &serverlist[i].info; + + /* magic number for new packet format */ + if (info->_255 != 255) + { + return + "Outdated server (version unknown).\n" EOT; + } + + if (strncmp(info->application, SRB2APPLICATION, sizeof + info->application)) + { + return va( + "%s cannot connect\n" + "to %s servers.\n" EOT, + SRB2APPLICATION, + info->application); + } + + if ( + info->packetversion != PACKETVERSION || + info->version != VERSION || + info->subversion != SUBVERSION + ){ + return va( + "Incompatible %s versions.\n" + "(server version %d.%d.%d)\n" EOT, + SRB2APPLICATION, + info->version / 100, + info->version % 100, + info->subversion); + } + + if (info->refusereason) + { + if (serverlist[i].info.refusereason == 1) + return + "The server is not accepting\n" + "joins for the moment.\n" EOT; + else if (serverlist[i].info.refusereason == 2) + return va( + "Maximum players reached: %d\n" EOT, + info->maxplayer); + else + return + "You can't join.\n" + "I don't know why,\n" + "but you can't join.\n" EOT; + } + + return NULL; + +#undef EOT +} + /** Called by CL_ServerConnectionTicker * * \param asksent The last time we asked the server to join. We re-ask every second in case our request got lost in transmit. @@ -1859,23 +1922,23 @@ static boolean CL_ServerConnectionSearchTicker(tic_t *asksent) return true; } - // Quit here rather than downloading files and being refused later. - if (serverlist[i].info.refusereason) - { - D_QuitNetGame(); - CL_Reset(); - D_StartTitle(); - 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; - } - if (client) { + const char *reason = InvalidServerReason(i); + + // Quit here rather than downloading files + // and being refused later. + if (reason) + { + char *message = Z_StrDup(reason); + D_QuitNetGame(); + CL_Reset(); + D_StartTitle(); + M_StartMessage(message, NULL, MM_NOTHING); + Z_Free(message); + return false; + } + D_ParseFileneeded(serverlist[i].info.fileneedednum, serverlist[i].info.fileneeded); CONS_Printf(M_GetText("Checking files...\n")); From 22bfc2db78e2c0afbe839681310a7470b17a9325 Mon Sep 17 00:00:00 2001 From: James R Date: Mon, 10 May 2021 17:50:01 -0700 Subject: [PATCH 049/126] Refactor HandleConnect refusals Also removed some version fields from serverconfig_pak and clientconfig_pak. Client version will be checked with SRB2APPLICATION and MODVERSION. Not updating PACKETVERSION because those packets shouldn't have been packetversion'd in the first place. --- src/d_clisrv.c | 115 +++++++++++++++++++++++++++++++++++-------------- src/d_clisrv.h | 8 +--- 2 files changed, 83 insertions(+), 40 deletions(-) diff --git a/src/d_clisrv.c b/src/d_clisrv.c index 8b80e58f5..27e104596 100644 --- a/src/d_clisrv.c +++ b/src/d_clisrv.c @@ -1150,15 +1150,14 @@ static boolean CL_SendJoin(void) CONS_Printf(M_GetText("Sending join request...\n")); netbuffer->packettype = PT_CLIENTJOIN; + netbuffer->u.clientcfg.modversion = MODVERSION; + strncpy(netbuffer->u.clientcfg.application, + SRB2APPLICATION, + sizeof netbuffer->u.clientcfg.application); + if (splitscreen || botingame) localplayers++; netbuffer->u.clientcfg.localplayers = localplayers; - netbuffer->u.clientcfg._255 = 255; - netbuffer->u.clientcfg.packetversion = PACKETVERSION; - netbuffer->u.clientcfg.version = VERSION; - netbuffer->u.clientcfg.subversion = SUBVERSION; - strncpy(netbuffer->u.clientcfg.application, SRB2APPLICATION, - sizeof netbuffer->u.clientcfg.application); CleanupPlayerName(consoleplayer, cv_playername.zstring); if (splitscreen) @@ -1344,9 +1343,6 @@ static boolean SV_SendServerConfig(INT32 node) netbuffer->packettype = PT_SERVERCFG; - netbuffer->u.servercfg.version = VERSION; - netbuffer->u.servercfg.subversion = SUBVERSION; - netbuffer->u.servercfg.serverplayer = (UINT8)serverplayer; netbuffer->u.servercfg.totalslotnum = (UINT8)(doomcom->numslots); netbuffer->u.servercfg.gametic = (tic_t)LONG(gametic); @@ -3683,6 +3679,78 @@ static size_t TotalTextCmdPerTic(tic_t tic) return total; } +static const char * +ConnectionRefused (SINT8 node, INT32 rejoinernum) +{ + clientconfig_pak *cc = &netbuffer->u.clientcfg; + + boolean rejoining = (rejoinernum != -1); + + if (!node)/* server connecting to itself */ + return NULL; + + if ( + cc->modversion != MODVERSION || + strncmp(cc->application, SRB2APPLICATION, + sizeof cc->application) + ){ + return/* this is probably client's fault */ + "Incompatible."; + } + else if (bannednode && bannednode[node]) + { + return + "You have been banned\n" + "from the server."; + } + else if (cc->localplayers != 1) + { + return + "Wrong player count."; + } + + if (!rejoining) + { + if (!cv_allownewplayer.value) + { + return + "The server is not accepting\n" + "joins for the moment."; + } + else if (D_NumPlayers() >= cv_maxplayers.value) + { + return va( + "Maximum players reached: %d", + cv_maxplayers.value); + } + } + + if (luafiletransfers) + { + return + "The serveris broadcasting a file\n" + "requested by a Lua script.\n" + "Please wait a bit and then\n" + "try rejoining."; + } + + if (netgame) + { + const tic_t th = 2 * cv_joindelay.value * TICRATE; + + if (joindelay > th) + { + return va( + "Too many people are connecting.\n" + "Please wait %d seconds and then\n" + "try rejoining.", + (joindelay - th) / TICRATE); + } + } + + return NULL; +} + /** Called when a PT_CLIENTJOIN packet is received * * \param node The packet sender @@ -3693,33 +3761,14 @@ static void HandleConnect(SINT8 node) char names[MAXSPLITSCREENPLAYERS][MAXPLAYERNAME + 1]; INT32 rejoinernum; INT32 i; + const char *refuse; rejoinernum = FindRejoinerNum(node); - if (bannednode && bannednode[node]) - SV_SendRefuse(node, M_GetText("You have been banned\nfrom the server.")); - else if (netbuffer->u.clientcfg._255 != 255 || - netbuffer->u.clientcfg.packetversion != PACKETVERSION) - SV_SendRefuse(node, "Incompatible packet formats."); - else if (strncmp(netbuffer->u.clientcfg.application, SRB2APPLICATION, - sizeof netbuffer->u.clientcfg.application)) - SV_SendRefuse(node, "Different SRB2 modifications\nare not compatible."); - else if (netbuffer->u.clientcfg.version != VERSION - || 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.")); - 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? - 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.")); - else if (luafiletransfers) - SV_SendRefuse(node, M_GetText("The server is broadcasting a file\nrequested by a Lua script.\nPlease wait a bit and then\ntry rejoining.")); - else if (netgame && joindelay > 2 * (tic_t)cv_joindelay.value * TICRATE) - SV_SendRefuse(node, va(M_GetText("Too many people are connecting.\nPlease wait %d seconds and then\ntry rejoining."), - (joindelay - 2 * cv_joindelay.value * TICRATE) / TICRATE)); + refuse = ConnectionRefused(node, rejoinernum); + + if (refuse) + SV_SendRefuse(node, refuse); else { #ifndef NONET diff --git a/src/d_clisrv.h b/src/d_clisrv.h index 3d67525da..9b690da84 100644 --- a/src/d_clisrv.h +++ b/src/d_clisrv.h @@ -141,9 +141,6 @@ typedef struct typedef struct { - UINT8 version; // Different versions don't work - UINT8 subversion; // Contains build version - // Server launch stuffs UINT8 serverplayer; UINT8 totalslotnum; // "Slots": highest player number in use plus one. @@ -190,11 +187,8 @@ typedef struct typedef struct { - UINT8 _255;/* see serverinfo_pak */ - UINT8 packetversion; + UINT8 modversion; char application[MAXAPPLICATION]; - UINT8 version; // Different versions don't work - UINT8 subversion; // Contains build version UINT8 localplayers; UINT8 mode; char names[MAXSPLITSCREENPLAYERS][MAXPLAYERNAME]; From 8486a9386d84293375465420fa80cfeed9859cd1 Mon Sep 17 00:00:00 2001 From: James R Date: Mon, 10 May 2021 18:10:16 -0700 Subject: [PATCH 050/126] serverinfo: enumerate refusereason --- src/d_clisrv.c | 41 +++++++++++++++++++++++++---------------- src/d_clisrv.h | 7 ++++++- 2 files changed, 31 insertions(+), 17 deletions(-) diff --git a/src/d_clisrv.c b/src/d_clisrv.c index 27e104596..a06a935a7 100644 --- a/src/d_clisrv.c +++ b/src/d_clisrv.c @@ -1200,6 +1200,19 @@ static INT32 FindRejoinerNum(SINT8 node) return -1; } +static UINT8 +GetRefuseReason (INT32 node) +{ + if (!node || FindRejoinerNum(node) != -1) + return 0; + else if (!cv_allownewplayer.value) + return REFUSE_JOINS_DISABLED; + else if (D_NumPlayers() >= cv_maxplayers.value) + return REFUSE_SLOTS_FULL; + else + return 0; +} + static void SV_SendServerInfo(INT32 node, tic_t servertime) { UINT8 *p; @@ -1218,14 +1231,7 @@ 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 (!node || 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; + netbuffer->u.serverinfo.refusereason = GetRefuseReason(node); strncpy(netbuffer->u.serverinfo.gametypename, Gametype_Names[gametype], sizeof netbuffer->u.serverinfo.gametypename); @@ -1866,21 +1872,24 @@ static const char * InvalidServerReason (INT32 i) info->subversion); } - if (info->refusereason) + switch (info->refusereason) { - if (serverlist[i].info.refusereason == 1) + case REFUSE_JOINS_DISABLED: return "The server is not accepting\n" "joins for the moment.\n" EOT; - else if (serverlist[i].info.refusereason == 2) + case REFUSE_SLOTS_FULL: return va( "Maximum players reached: %d\n" EOT, info->maxplayer); - else - return - "You can't join.\n" - "I don't know why,\n" - "but you can't join.\n" EOT; + default: + if (info->refusereason) + { + return + "You can't join.\n" + "I don't know why,\n" + "but you can't join.\n" EOT; + } } return NULL; diff --git a/src/d_clisrv.h b/src/d_clisrv.h index 9b690da84..0e8e4c2b8 100644 --- a/src/d_clisrv.h +++ b/src/d_clisrv.h @@ -194,6 +194,11 @@ typedef struct char names[MAXSPLITSCREENPLAYERS][MAXPLAYERNAME]; } ATTRPACK clientconfig_pak; +enum { + REFUSE_JOINS_DISABLED = 1, + REFUSE_SLOTS_FULL, +}; + #define MAXSERVERNAME 32 #define MAXFILENEEDED 915 // This packet is too large @@ -211,7 +216,7 @@ typedef struct UINT8 subversion; UINT8 numberofplayer; UINT8 maxplayer; - UINT8 refusereason; // 0: joinable, 1: joins disabled, 2: full + UINT8 refusereason; // 0: joinable, REFUSE enum char gametypename[24]; UINT8 modifiedgame; UINT8 cheatsenabled; From c13e1a74ddb5fba981d801d1137c11537d4505f7 Mon Sep 17 00:00:00 2001 From: James R Date: Mon, 10 May 2021 18:10:53 -0700 Subject: [PATCH 051/126] Reject banned players with refusereason --- src/d_clisrv.c | 6 ++++++ src/d_clisrv.h | 13 +++++++++---- 2 files changed, 15 insertions(+), 4 deletions(-) diff --git a/src/d_clisrv.c b/src/d_clisrv.c index a06a935a7..e87edf43d 100644 --- a/src/d_clisrv.c +++ b/src/d_clisrv.c @@ -1205,6 +1205,8 @@ GetRefuseReason (INT32 node) { if (!node || FindRejoinerNum(node) != -1) return 0; + else if (bannednode && bannednode[node]) + return REFUSE_BANNED; else if (!cv_allownewplayer.value) return REFUSE_JOINS_DISABLED; else if (D_NumPlayers() >= cv_maxplayers.value) @@ -1874,6 +1876,10 @@ static const char * InvalidServerReason (INT32 i) switch (info->refusereason) { + case REFUSE_BANNED: + return + "You have been banned\n" + "from the server.\n" EOT; case REFUSE_JOINS_DISABLED: return "The server is not accepting\n" diff --git a/src/d_clisrv.h b/src/d_clisrv.h index 0e8e4c2b8..99274b5de 100644 --- a/src/d_clisrv.h +++ b/src/d_clisrv.h @@ -22,11 +22,15 @@ #include "mserv.h" /* -The 'packet version' is used to distinguish packet formats. -This version is independent of VERSION and SUBVERSION. Different -applications may follow different packet versions. +The 'packet version' is used to distinguish packet +formats. This version is independent of VERSION and +SUBVERSION. Different applications may follow different +packet versions. + +If you change the struct or the meaning of a field +therein, increment this number. */ -#define PACKETVERSION 3 +#define PACKETVERSION 4 // Network play related stuff. // There is a data struct that stores network @@ -197,6 +201,7 @@ typedef struct enum { REFUSE_JOINS_DISABLED = 1, REFUSE_SLOTS_FULL, + REFUSE_BANNED, }; #define MAXSERVERNAME 32 From 7c11bc8ac5807414fee671df0b6d82c040a067bc Mon Sep 17 00:00:00 2001 From: lachablock Date: Fri, 21 May 2021 20:28:50 +1000 Subject: [PATCH 052/126] Remove camera dependency from P_GetPlayerControlDirection --- src/p_user.c | 18 +++++------------- 1 file changed, 5 insertions(+), 13 deletions(-) diff --git a/src/p_user.c b/src/p_user.c index 6ebee5afc..3b8248e97 100644 --- a/src/p_user.c +++ b/src/p_user.c @@ -5624,16 +5624,10 @@ INT32 P_GetPlayerControlDirection(player_t *player) { ticcmd_t *cmd = &player->cmd; angle_t controllerdirection, controlplayerdirection; - camera_t *thiscam; angle_t dangle; fixed_t tempx = 0, tempy = 0; angle_t tempangle, origtempangle; - if (splitscreen && player == &players[secondarydisplayplayer]) - thiscam = &camera2; - else - thiscam = &camera; - if (!cmd->forwardmove && !cmd->sidemove) return 0; @@ -5649,17 +5643,15 @@ INT32 P_GetPlayerControlDirection(player_t *player) origtempangle = tempangle = 0; // relative to the axis rather than the player! controlplayerdirection = R_PointToAngle2(0, 0, player->mo->momx, player->mo->momy); } - else if ((P_ControlStyle(player) & CS_LMAOGALOG) && thiscam->chase) + else { if (player->awayviewtics) origtempangle = tempangle = player->awayviewmobj->angle; + else if (P_ControlStyle(player) & CS_LMAOGALOG) + origtempangle = tempangle = (cmd->angleturn << 16); else - origtempangle = tempangle = thiscam->angle; - controlplayerdirection = player->mo->angle; - } - else - { - origtempangle = tempangle = player->mo->angle; + origtempangle = tempangle = player->mo->angle; + controlplayerdirection = R_PointToAngle2(0, 0, player->mo->momx, player->mo->momy); } From a1553c46232f6a4df12b71af9ff7f29bf36b9607 Mon Sep 17 00:00:00 2001 From: Jaime Ita Passos Date: Sat, 22 May 2021 20:08:11 -0300 Subject: [PATCH 053/126] Update r_opengl.c --- src/hardware/r_opengl/r_opengl.c | 75 ++++++++++---------------------- 1 file changed, 22 insertions(+), 53 deletions(-) diff --git a/src/hardware/r_opengl/r_opengl.c b/src/hardware/r_opengl/r_opengl.c index 003a1b3ca..de0e8c6a6 100644 --- a/src/hardware/r_opengl/r_opengl.c +++ b/src/hardware/r_opengl/r_opengl.c @@ -62,6 +62,9 @@ static FBITFIELD CurrentPolyFlags; static FTextureInfo *TexCacheTail = NULL; static FTextureInfo *TexCacheHead = NULL; +static RGBA_t *textureBuffer = NULL; +static size_t textureBufferSize = 0; + RGBA_t myPaletteData[256]; GLint screen_width = 0; // used by Draw2DLine() GLint screen_height = 0; @@ -202,32 +205,6 @@ static void GL_MSG_Error(const char *format, ...) #endif } -// ----------------------+ -// GetTextureFormatName : Returns the corresponding texture format string from the texture format enumeration. -// ----------------------+ -static const char *GetTextureFormatName(INT32 format) -{ - static char num[12]; - - switch (format) - { - case GL_TEXFMT_P_8: return "GL_TEXFMT_P_8"; - case GL_TEXFMT_AP_88: return "GL_TEXFMT_AP_88"; - case GL_TEXFMT_RGBA: return "GL_TEXFMT_RGBA"; - case GL_TEXFMT_ALPHA_8: return "GL_TEXFMT_ALPHA_8"; - case GL_TEXFMT_INTENSITY_8: return "GL_TEXFMT_INTENSITY_8"; - case GL_TEXFMT_ALPHA_INTENSITY_88: return "GL_TEXFMT_ALPHA_INTENSITY_88"; - default: break; - } - - // If the texture format is not known (due to it being invalid), - // return a string containing the format index instead. - format = INT32_MIN; - snprintf(num, sizeof(num), "%d", format); - - return num; -} - #ifdef STATIC_OPENGL /* 1.0 functions */ /* Miscellaneous */ @@ -1366,6 +1343,10 @@ void Flush(void) TexCacheTail = TexCacheHead = NULL; //Hurdler: well, TexCacheHead is already NULL tex_downloaded = 0; + + free(textureBuffer); + textureBuffer = NULL; + textureBufferSize = 0; } @@ -1758,28 +1739,16 @@ EXPORT void HWRAPI(SetBlend) (FBITFIELD PolyFlags) CurrentPolyFlags = PolyFlags; } -// -------------------+ -// AllocTextureBuffer : Allocates memory for converting a non-RGBA texture into an RGBA texture. -// -------------------+ -static RGBA_t *AllocTextureBuffer(GLMipmap_t *pTexInfo) +static void AllocTextureBuffer(GLMipmap_t *pTexInfo) { - size_t len = (pTexInfo->width * pTexInfo->height); - RGBA_t *tex = calloc(len, sizeof(RGBA_t)); - - if (tex == NULL) - I_Error("AllocTextureBuffer: out of memory allocating %s bytes for texture %d, format %s", - sizeu1(len * sizeof(RGBA_t)), pTexInfo->downloaded, GetTextureFormatName(pTexInfo->format)); - - return tex; -} - -// ------------------+ -// FreeTextureBuffer : Frees memory allocated by AllocTextureBuffer. -// ------------------+ -static void FreeTextureBuffer(RGBA_t *tex) -{ - if (tex) - free(tex); + size_t size = pTexInfo->width * pTexInfo->height; + if (size > textureBufferSize) + { + textureBuffer = realloc(textureBuffer, size * sizeof(RGBA_t)); + if (textureBuffer == NULL) + I_Error("AllocTextureBuffer: out of memory allocating %s bytes", sizeu1(size * sizeof(RGBA_t))); + textureBufferSize = size; + } } // -----------------+ @@ -1810,7 +1779,8 @@ EXPORT void HWRAPI(UpdateTexture) (GLMipmap_t *pTexInfo) if ((pTexInfo->format == GL_TEXFMT_P_8) || (pTexInfo->format == GL_TEXFMT_AP_88)) { - ptex = tex = AllocTextureBuffer(pTexInfo); + AllocTextureBuffer(pTexInfo); + ptex = tex = textureBuffer; for (j = 0; j < h; j++) { @@ -1851,7 +1821,8 @@ EXPORT void HWRAPI(UpdateTexture) (GLMipmap_t *pTexInfo) } else if (pTexInfo->format == GL_TEXFMT_ALPHA_INTENSITY_88) { - ptex = tex = AllocTextureBuffer(pTexInfo); + AllocTextureBuffer(pTexInfo); + ptex = tex = textureBuffer; for (j = 0; j < h; j++) { @@ -1868,7 +1839,8 @@ EXPORT void HWRAPI(UpdateTexture) (GLMipmap_t *pTexInfo) } else if (pTexInfo->format == GL_TEXFMT_ALPHA_8) // Used for fade masks { - ptex = tex = AllocTextureBuffer(pTexInfo); + AllocTextureBuffer(pTexInfo); + ptex = tex = textureBuffer; for (j = 0; j < h; j++) { @@ -1963,9 +1935,6 @@ EXPORT void HWRAPI(UpdateTexture) (GLMipmap_t *pTexInfo) } } - // Free the texture buffer - FreeTextureBuffer(tex); - if (pTexInfo->flags & TF_WRAPX) pglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); else From 21dab7bd6652688930879af9e42c2ec54dd46859 Mon Sep 17 00:00:00 2001 From: Riku Salminen <38985578+Riku-S@users.noreply.github.com> Date: Fri, 28 May 2021 22:30:43 +0300 Subject: [PATCH 054/126] Add previous demo version to acceptable demo versions for demo comparison --- src/g_demo.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/g_demo.c b/src/g_demo.c index ea71c9bd4..635b42b13 100644 --- a/src/g_demo.c +++ b/src/g_demo.c @@ -1668,6 +1668,7 @@ UINT8 G_CmpDemoTime(char *oldname, char *newname) switch(oldversion) // demoversion { case DEMOVERSION: // latest always supported + case 0x000d: // latest always supported case 0x000c: // all that changed between then and now was longer color name break; // too old, cannot support. From 22aaffa35d3bf2abb73a16b27f887162722ca0f4 Mon Sep 17 00:00:00 2001 From: Riku Salminen <38985578+Riku-S@users.noreply.github.com> Date: Fri, 28 May 2021 22:32:11 +0300 Subject: [PATCH 055/126] non-misleading comment --- src/g_demo.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/g_demo.c b/src/g_demo.c index 635b42b13..16d29fb88 100644 --- a/src/g_demo.c +++ b/src/g_demo.c @@ -1668,7 +1668,7 @@ UINT8 G_CmpDemoTime(char *oldname, char *newname) switch(oldversion) // demoversion { case DEMOVERSION: // latest always supported - case 0x000d: // latest always supported + case 0x000d: // The previous demoversion also supported case 0x000c: // all that changed between then and now was longer color name break; // too old, cannot support. From 8cb62eeca5515552d8d9745ea9356291697792a0 Mon Sep 17 00:00:00 2001 From: Nev3r Date: Wed, 2 Jun 2021 10:57:57 +0200 Subject: [PATCH 056/126] Initialize slopes before the map loads. --- src/p_setup.c | 2 ++ src/p_slopes.c | 10 +++++++--- src/p_slopes.h | 1 + 3 files changed, 10 insertions(+), 3 deletions(-) diff --git a/src/p_setup.c b/src/p_setup.c index 51d2f474d..aa22fed86 100644 --- a/src/p_setup.c +++ b/src/p_setup.c @@ -4274,6 +4274,8 @@ boolean P_LoadLevel(boolean fromnetsave, boolean reloadinggamestate) P_MapStart(); // tmthing can be used starting from this point + P_InitSlopes(); + if (!P_LoadMapFromFile()) return false; diff --git a/src/p_slopes.c b/src/p_slopes.c index 4e93e4a45..9ce5af838 100644 --- a/src/p_slopes.c +++ b/src/p_slopes.c @@ -635,9 +635,6 @@ pslope_t *P_SlopeById(UINT16 id) void P_SpawnSlopes(const boolean fromsave) { size_t i; - slopelist = NULL; - slopecount = 0; - /// Generates vertex slopes. SpawnVertexSlopes(); @@ -671,6 +668,13 @@ void P_SpawnSlopes(const boolean fromsave) { } } +/// Initializes slopes. +void P_InitSlopes(void) +{ + slopelist = NULL; + slopecount = 0; +} + // ============================================================================ // // Various utilities related to slopes diff --git a/src/p_slopes.h b/src/p_slopes.h index ae040ae56..f627cae70 100644 --- a/src/p_slopes.h +++ b/src/p_slopes.h @@ -50,6 +50,7 @@ typedef enum void P_LinkSlopeThinkers (void); void P_CalculateSlopeNormal(pslope_t *slope); +void P_InitSlopes(void); void P_SpawnSlopes(const boolean fromsave); // From 36ce44e0a37b3f8deac01f109f5eb3f83ed9972a Mon Sep 17 00:00:00 2001 From: Nev3r Date: Wed, 2 Jun 2021 10:58:36 +0200 Subject: [PATCH 057/126] Add slope equation constant parsing functionality. --- src/p_slopes.c | 40 ++++++++++++++++++++++++++++++++++++++++ src/p_slopes.h | 1 + 2 files changed, 41 insertions(+) diff --git a/src/p_slopes.c b/src/p_slopes.c index 9ce5af838..09094a32a 100644 --- a/src/p_slopes.c +++ b/src/p_slopes.c @@ -90,6 +90,36 @@ static void ReconfigureViaVertexes (pslope_t *slope, const vector3_t v1, const v } } +/// Setup slope via constants. +static void ReconfigureViaConstants (pslope_t *slope, const fixed_t a, const fixed_t b, const fixed_t c, const fixed_t d) +{ + fixed_t m; + vector3_t *normal = &slope->normal; + + // Set origin. + FV3_Load(&slope->o, 0, 0, c ? -FixedDiv(d, c) : 0); + + // Get slope's normal. + FV3_Load(normal, a, b, c); + FV3_Normalize(normal); + + // Invert normal if it's facing down. + if (normal->z < 0) + FV3_Negate(normal); + + // Get direction vector + m = FixedHypot(normal->x, normal->y); + slope->d.x = -FixedDiv(normal->x, m); + slope->d.y = -FixedDiv(normal->y, m); + + // Z delta + slope->zdelta = FixedDiv(m, normal->z); + + // Get angles + slope->xydirection = R_PointToAngle2(0, 0, slope->d.x, slope->d.y)+ANGLE_180; + slope->zangle = InvAngle(R_PointToAngle2(0, 0, FRACUNIT, slope->zdelta)); +} + /// Recalculate dynamic slopes. void T_DynamicSlopeLine (dynplanethink_t* th) { @@ -631,6 +661,16 @@ pslope_t *P_SlopeById(UINT16 id) return ret; } +/// Creates a new slope from equation constants. +pslope_t *MakeViaEquationConstants(const fixed_t a, const fixed_t b, const fixed_t c, const fixed_t d) +{ + pslope_t* ret = Slope_Add(0); + + ReconfigureViaConstants(ret, a, b, c, d); + + return ret; +} + /// Initializes and reads the slopes from the map data. void P_SpawnSlopes(const boolean fromsave) { size_t i; diff --git a/src/p_slopes.h b/src/p_slopes.h index f627cae70..43cd3edb0 100644 --- a/src/p_slopes.h +++ b/src/p_slopes.h @@ -87,6 +87,7 @@ fixed_t P_GetWallTransferMomZ(mobj_t *mo, pslope_t *slope); void P_HandleSlopeLanding(mobj_t *thing, pslope_t *slope); void P_ButteredSlope(mobj_t *mo); +pslope_t *MakeViaEquationConstants(const fixed_t a, const fixed_t b, const fixed_t c, const fixed_t d); /// Dynamic plane type enum for the thinker. Will have a different functionality depending on this. typedef enum { From aec1ab304a154deb156a8c8e13fe39f6e74b8714 Mon Sep 17 00:00:00 2001 From: Nev3r Date: Wed, 2 Jun 2021 10:59:05 +0200 Subject: [PATCH 058/126] Let equation slopes be read from textmaps. --- src/p_setup.c | 73 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 73 insertions(+) diff --git a/src/p_setup.c b/src/p_setup.c index aa22fed86..330b3e1fc 100644 --- a/src/p_setup.c +++ b/src/p_setup.c @@ -1501,6 +1501,22 @@ typedef struct textmap_colormap_s { textmap_colormap_t textmap_colormap = { false, 0, 25, 0, 25, 0, 31, 0 }; +typedef enum +{ + PD_A = 1, + PD_B = 1<<1, + PD_C = 1<<2, + PD_D = 1<<3, +} planedef_t; + +typedef struct textmap_plane_s { + UINT8 defined; + fixed_t a, b, c, d; +} textmap_plane_t; + +textmap_plane_t textmap_planefloor = {0, 0, 0, 0, 0}; +textmap_plane_t textmap_planeceiling = {0, 0, 0, 0, 0}; + static void ParseTextmapSectorParameter(UINT32 i, char *param, char *val) { if (fastcmp(param, "heightfloor")) @@ -1539,6 +1555,46 @@ static void ParseTextmapSectorParameter(UINT32 i, char *param, char *val) sectors[i].floorpic_angle = FixedAngle(FLOAT_TO_FIXED(atof(val))); else if (fastcmp(param, "rotationceiling")) sectors[i].ceilingpic_angle = FixedAngle(FLOAT_TO_FIXED(atof(val))); + else if (fastcmp(param, "floorplane_a")) + { + textmap_planefloor.defined |= PD_A; + textmap_planefloor.a = FLOAT_TO_FIXED(atof(val)); + } + else if (fastcmp(param, "floorplane_b")) + { + textmap_planefloor.defined |= PD_B; + textmap_planefloor.a = FLOAT_TO_FIXED(atof(val)); + } + else if (fastcmp(param, "floorplane_c")) + { + textmap_planefloor.defined |= PD_C; + textmap_planefloor.a = FLOAT_TO_FIXED(atof(val)); + } + else if (fastcmp(param, "floorplane_d")) + { + textmap_planefloor.defined |= PD_D; + textmap_planefloor.a = FLOAT_TO_FIXED(atof(val)); + } + else if (fastcmp(param, "ceilingplane_a")) + { + textmap_planeceiling.defined |= PD_A; + textmap_planeceiling.a = FLOAT_TO_FIXED(atof(val)); + } + else if (fastcmp(param, "ceilingplane_b")) + { + textmap_planeceiling.defined |= PD_B; + textmap_planeceiling.a = FLOAT_TO_FIXED(atof(val)); + } + else if (fastcmp(param, "ceilingplane_c")) + { + textmap_planeceiling.defined |= PD_C; + textmap_planeceiling.a = FLOAT_TO_FIXED(atof(val)); + } + else if (fastcmp(param, "ceilingplane_d")) + { + textmap_planeceiling.defined |= PD_D; + textmap_planeceiling.a = FLOAT_TO_FIXED(atof(val)); + } else if (fastcmp(param, "lightcolor")) { textmap_colormap.used = true; @@ -1868,6 +1924,10 @@ static void P_LoadTextmap(void) textmap_colormap.fadestart = 0; textmap_colormap.fadeend = 31; textmap_colormap.flags = 0; + + textmap_planefloor.defined = 0; + textmap_planeceiling.defined = 0; + TextmapParse(sectorsPos[i], i, ParseTextmapSectorParameter); P_InitializeSector(sc); @@ -1877,6 +1937,19 @@ static void P_LoadTextmap(void) INT32 fadergba = P_ColorToRGBA(textmap_colormap.fadecolor, textmap_colormap.fadealpha); sc->extra_colormap = sc->spawn_extra_colormap = R_CreateColormap(rgba, fadergba, textmap_colormap.fadestart, textmap_colormap.fadeend, textmap_colormap.flags); } + + if (textmap_planefloor.defined == (PD_A|PD_B|PD_C|PD_D)) + { + sc->f_slope = MakeViaEquationConstants(textmap_planefloor.a, textmap_planefloor.b, textmap_planefloor.c, textmap_planefloor.d); + sc->hasslope = true; + } + + if (textmap_planeceiling.defined == (PD_A|PD_B|PD_C|PD_D)) + { + sc->c_slope = MakeViaEquationConstants(textmap_planeceiling.a, textmap_planeceiling.b, textmap_planeceiling.c, textmap_planeceiling.d); + sc->hasslope = true; + } + TextmapFixFlatOffsets(sc); } From 9c68f8cbb0982a09eeeff4ec8df2486ac38f200f Mon Sep 17 00:00:00 2001 From: Nev3r Date: Wed, 2 Jun 2021 11:21:37 +0200 Subject: [PATCH 059/126] Fix the equation constant fields not being filled properly. --- src/p_setup.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/p_setup.c b/src/p_setup.c index 330b3e1fc..c198d0c08 100644 --- a/src/p_setup.c +++ b/src/p_setup.c @@ -1563,17 +1563,17 @@ static void ParseTextmapSectorParameter(UINT32 i, char *param, char *val) else if (fastcmp(param, "floorplane_b")) { textmap_planefloor.defined |= PD_B; - textmap_planefloor.a = FLOAT_TO_FIXED(atof(val)); + textmap_planefloor.b = FLOAT_TO_FIXED(atof(val)); } else if (fastcmp(param, "floorplane_c")) { textmap_planefloor.defined |= PD_C; - textmap_planefloor.a = FLOAT_TO_FIXED(atof(val)); + textmap_planefloor.c = FLOAT_TO_FIXED(atof(val)); } else if (fastcmp(param, "floorplane_d")) { textmap_planefloor.defined |= PD_D; - textmap_planefloor.a = FLOAT_TO_FIXED(atof(val)); + textmap_planefloor.d = FLOAT_TO_FIXED(atof(val)); } else if (fastcmp(param, "ceilingplane_a")) { @@ -1583,17 +1583,17 @@ static void ParseTextmapSectorParameter(UINT32 i, char *param, char *val) else if (fastcmp(param, "ceilingplane_b")) { textmap_planeceiling.defined |= PD_B; - textmap_planeceiling.a = FLOAT_TO_FIXED(atof(val)); + textmap_planeceiling.b = FLOAT_TO_FIXED(atof(val)); } else if (fastcmp(param, "ceilingplane_c")) { textmap_planeceiling.defined |= PD_C; - textmap_planeceiling.a = FLOAT_TO_FIXED(atof(val)); + textmap_planeceiling.c = FLOAT_TO_FIXED(atof(val)); } else if (fastcmp(param, "ceilingplane_d")) { textmap_planeceiling.defined |= PD_D; - textmap_planeceiling.a = FLOAT_TO_FIXED(atof(val)); + textmap_planeceiling.d = FLOAT_TO_FIXED(atof(val)); } else if (fastcmp(param, "lightcolor")) { @@ -3308,7 +3308,7 @@ static void P_ConvertBinaryMap(void) lines[i].args[4] |= TMSC_BACKTOFRONTCEILING; lines[i].special = 720; break; - + case 900: //Translucent wall (10%) case 901: //Translucent wall (20%) case 902: //Translucent wall (30%) From a4300220e94d924d49a4a3067de0a348e16a3705 Mon Sep 17 00:00:00 2001 From: Nev3r Date: Wed, 2 Jun 2021 11:36:52 +0200 Subject: [PATCH 060/126] whitespace --- src/p_slopes.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/p_slopes.c b/src/p_slopes.c index 09094a32a..41cfbf337 100644 --- a/src/p_slopes.c +++ b/src/p_slopes.c @@ -817,7 +817,7 @@ void P_SlopeLaunch(mobj_t *mo) mo->momx = slopemom.x; mo->momy = slopemom.y; mo->momz = slopemom.z/2; - + if (mo->player) mo->player->powers[pw_justlaunched] = 1; } From cb5e433a49c61fda688de6d5dd5910c106df34e3 Mon Sep 17 00:00:00 2001 From: Radicalicious Date: Thu, 3 Jun 2021 09:34:18 -0400 Subject: [PATCH 061/126] Update lua_hudlib.c --- src/lua_hudlib.c | 38 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) diff --git a/src/lua_hudlib.c b/src/lua_hudlib.c index 684e47c38..14d7d7f3c 100644 --- a/src/lua_hudlib.c +++ b/src/lua_hudlib.c @@ -794,6 +794,27 @@ static int libd_drawString(lua_State *L) return 0; } +static int libd_drawLevelActNum(lua_State *L) +{ + INT32 x; + INT32 y; + INT32 flags; + UINT8 num; + + HUDONLY + + x = luaL_checkinteger(L, 1); + y = luaL_checkinteger(L, 2); + flags = luaL_optinteger(L, 3, 0); + num = luaL_checkinteger(L, 4); + + flags &= ~V_PARAMMASK; // Don't let crashes happen. + + V_DrawLevelActNum(x, y, flags, num); + return 0; +} + + static int libd_drawNameTag(lua_State *L) { INT32 x; @@ -878,6 +899,20 @@ static int libd_stringWidth(lua_State *L) return 1; } +static int libd_levelActNumWidth(lua_State *L) +{ + HUDONLY + lua_pushinteger(L, V_LevelActNumWidth(luaL_checkinteger(L, 1))); + return 1; +} + +static int libd_levelActNumHeight(lua_State *L) +{ + HUDONLY + lua_pushinteger(L, V_LevelActNumHeight(luaL_checkinteger(L, 1))); + return 1; +} + static int libd_nameTagWidth(lua_State *L) { HUDONLY @@ -1086,11 +1121,14 @@ static luaL_Reg lib_draw[] = { {"drawPaddedNum", libd_drawPaddedNum}, {"drawFill", libd_drawFill}, {"drawString", libd_drawString}, + {"drawLevelActNum", libd_drawLevelActNum}, {"drawNameTag", libd_drawNameTag}, {"drawScaledNameTag", libd_drawScaledNameTag}, {"fadeScreen", libd_fadeScreen}, // misc {"stringWidth", libd_stringWidth}, + {"levelActNumWidth", libd_levelActNumWidth}, + {"levelActNumHeight", libd_levelActNumHeight}, {"nameTagWidth", libd_nameTagWidth}, // m_random {"RandomFixed",libd_RandomFixed}, From 6ff212b79f4bc97b2f34edf2ebc62e79e64ca519 Mon Sep 17 00:00:00 2001 From: sphere Date: Thu, 3 Jun 2021 16:01:09 +0200 Subject: [PATCH 062/126] Use floating-point math for polyobject planes as well. --- src/r_plane.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/r_plane.c b/src/r_plane.c index 4c757d8ca..818770906 100644 --- a/src/r_plane.c +++ b/src/r_plane.c @@ -380,9 +380,11 @@ visplane_t *R_FindPlane(fixed_t height, INT32 picnum, INT32 lightlevel, { if (polyobj->angle != 0) { - angle_t fineshift = polyobj->angle >> ANGLETOFINESHIFT; - xoff -= FixedMul(FINECOSINE(fineshift), polyobj->centerPt.x)+FixedMul(FINESINE(fineshift), polyobj->centerPt.y); - yoff -= FixedMul(FINESINE(fineshift), polyobj->centerPt.x)-FixedMul(FINECOSINE(fineshift), polyobj->centerPt.y); + float ang = ANG2RAD(polyobj->angle); + float x = FixedToFloat(polyobj->centerPt.x); + float y = FixedToFloat(polyobj->centerPt.y); + xoff -= FloatToFixed(x * cos(ang) + y * sin(ang)); + yoff -= FloatToFixed(x * sin(ang) - y * cos(ang)); } else { From 7483a9d049230e983e5fd8b44dd733c7bf26bb16 Mon Sep 17 00:00:00 2001 From: Radicalicious Date: Thu, 3 Jun 2021 10:55:42 -0400 Subject: [PATCH 063/126] Switch num and flags so flags is optional --- src/lua_hudlib.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/lua_hudlib.c b/src/lua_hudlib.c index 14d7d7f3c..5e974675f 100644 --- a/src/lua_hudlib.c +++ b/src/lua_hudlib.c @@ -798,15 +798,15 @@ static int libd_drawLevelActNum(lua_State *L) { INT32 x; INT32 y; - INT32 flags; UINT8 num; + INT32 flags; HUDONLY x = luaL_checkinteger(L, 1); y = luaL_checkinteger(L, 2); - flags = luaL_optinteger(L, 3, 0); - num = luaL_checkinteger(L, 4); + num = luaL_checkinteger(L, 3); + flags = luaL_optinteger(L, 4, 0); flags &= ~V_PARAMMASK; // Don't let crashes happen. From 4d1b3edb035b362cf6c3e37f78ea755f4e61cf1b Mon Sep 17 00:00:00 2001 From: lachablock Date: Tue, 8 Jun 2021 17:21:54 +1000 Subject: [PATCH 064/126] Split up x/y/z averages in A_Boss3ShockThink --- src/p_enemy.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/p_enemy.c b/src/p_enemy.c index 9a9edb5e3..eb4b0c080 100644 --- a/src/p_enemy.c +++ b/src/p_enemy.c @@ -8272,7 +8272,7 @@ void A_Boss3ShockThink(mobj_t *actor) fixed_t x0, y0, x1, y1; // Break the link if movements are too different - if (FixedHypot(snext->momx - actor->momx, snext->momy - actor->momy) > 12*actor->scale) + if (R_PointToDist2(0, 0, snext->momx - actor->momx, snext->momy - actor->momy) > 12*actor->scale) { P_SetTarget(&actor->hnext, NULL); return; @@ -8283,9 +8283,11 @@ void A_Boss3ShockThink(mobj_t *actor) y0 = actor->y; x1 = snext->x; y1 = snext->y; - if (FixedHypot(x1 - x0, y1 - y0) > 2*actor->radius) + if (R_PointToDist2(0, 0, x1 - x0, y1 - y0) > 2*actor->radius) { - snew = P_SpawnMobj((x0 + x1) >> 1, (y0 + y1) >> 1, (actor->z + snext->z) >> 1, actor->type); + snew = P_SpawnMobj((x0 >> 1) + (x1 >> 1), + (y0 >> 1) + (y1 >> 1), + (actor->z >> 1) + (snext->z >> 1), actor->type); snew->momx = (actor->momx + snext->momx) >> 1; snew->momy = (actor->momy + snext->momy) >> 1; snew->momz = (actor->momz + snext->momz) >> 1; // is this really needed? From fbeabad7972c966776a3d86617caefc799a1e440 Mon Sep 17 00:00:00 2001 From: Riku Salminen <38985578+Riku-S@users.noreply.github.com> Date: Wed, 9 Jun 2021 19:28:14 +0300 Subject: [PATCH 065/126] Revert "Made height/spinheight and height change values in replays more accurate" This reverts commit 3daee0ebf882d795ec0aef7fb54d48b74f940c59. --- src/g_demo.c | 23 +++++++++++++---------- 1 file changed, 13 insertions(+), 10 deletions(-) diff --git a/src/g_demo.c b/src/g_demo.c index 7793e0272..80968cd1c 100644 --- a/src/g_demo.c +++ b/src/g_demo.c @@ -463,7 +463,8 @@ void G_WriteGhostTic(mobj_t *ghost) WRITEUINT16(demo_p,oldghost.sprite); if (ghostext.flags & EZT_HEIGHT) { - WRITEFIXED(demo_p, height); + height >>= FRACBITS; + WRITEINT16(demo_p, height); } ghostext.flags = 0; } @@ -619,7 +620,7 @@ void G_ConsGhostTic(void) if (xziptic & EZT_SPRITE) demo_p += sizeof(UINT16); if (xziptic & EZT_HEIGHT) - demo_p += (demoversion < 0x000e) ? sizeof(INT16) : sizeof(fixed_t); + demo_p += sizeof(INT16); } if (ziptic & GZT_FOLLOW) @@ -853,7 +854,7 @@ void G_GhostTicker(void) g->mo->sprite = READUINT16(g->p); if (xziptic & EZT_HEIGHT) { - fixed_t temp = (g->version < 0x000e) ? READINT16(g->p)<p); + fixed_t temp = READINT16(g->p)<mo->height = FixedMul(temp, g->mo->scale); } } @@ -1117,7 +1118,7 @@ void G_ReadMetalTic(mobj_t *metal) metal->sprite = READUINT16(metal_p); if (xziptic & EZT_HEIGHT) { - fixed_t temp = (metalversion < 0x000e) ? READINT16(metal_p)<height = FixedMul(temp, metal->scale); } } @@ -1304,7 +1305,8 @@ void G_WriteMetalTic(mobj_t *metal) WRITEUINT16(demo_p,oldmetal.sprite); if (ghostext.flags & EZT_HEIGHT) { - WRITEFIXED(demo_p, height); + height >>= FRACBITS; + WRITEINT16(demo_p, height); } ghostext.flags = 0; } @@ -1484,8 +1486,8 @@ void G_BeginRecording(void) WRITEUINT8(demo_p,player->thrustfactor); WRITEUINT8(demo_p,player->accelstart); WRITEUINT8(demo_p,player->acceleration); - WRITEFIXED(demo_p,player->height); - WRITEFIXED(demo_p,player->spinheight); + WRITEUINT8(demo_p,player->height>>FRACBITS); + WRITEUINT8(demo_p,player->spinheight>>FRACBITS); WRITEUINT8(demo_p,player->camerascale>>FRACBITS); WRITEUINT8(demo_p,player->shieldscale>>FRACBITS); @@ -1911,8 +1913,8 @@ void G_DoPlayDemo(char *defdemoname) thrustfactor = READUINT8(demo_p); accelstart = READUINT8(demo_p); acceleration = READUINT8(demo_p); - height = (demoversion < 0x000e) ? (fixed_t)READUINT8(demo_p)< Date: Wed, 9 Jun 2021 19:40:59 +0300 Subject: [PATCH 066/126] Revert "Revert "Made height/spinheight and height change values in replays more accurate"" This reverts commit fbeabad7972c966776a3d86617caefc799a1e440. --- src/g_demo.c | 23 ++++++++++------------- 1 file changed, 10 insertions(+), 13 deletions(-) diff --git a/src/g_demo.c b/src/g_demo.c index 80968cd1c..7793e0272 100644 --- a/src/g_demo.c +++ b/src/g_demo.c @@ -463,8 +463,7 @@ void G_WriteGhostTic(mobj_t *ghost) WRITEUINT16(demo_p,oldghost.sprite); if (ghostext.flags & EZT_HEIGHT) { - height >>= FRACBITS; - WRITEINT16(demo_p, height); + WRITEFIXED(demo_p, height); } ghostext.flags = 0; } @@ -620,7 +619,7 @@ void G_ConsGhostTic(void) if (xziptic & EZT_SPRITE) demo_p += sizeof(UINT16); if (xziptic & EZT_HEIGHT) - demo_p += sizeof(INT16); + demo_p += (demoversion < 0x000e) ? sizeof(INT16) : sizeof(fixed_t); } if (ziptic & GZT_FOLLOW) @@ -854,7 +853,7 @@ void G_GhostTicker(void) g->mo->sprite = READUINT16(g->p); if (xziptic & EZT_HEIGHT) { - fixed_t temp = READINT16(g->p)<version < 0x000e) ? READINT16(g->p)<p); g->mo->height = FixedMul(temp, g->mo->scale); } } @@ -1118,7 +1117,7 @@ void G_ReadMetalTic(mobj_t *metal) metal->sprite = READUINT16(metal_p); if (xziptic & EZT_HEIGHT) { - fixed_t temp = READINT16(metal_p)<height = FixedMul(temp, metal->scale); } } @@ -1305,8 +1304,7 @@ void G_WriteMetalTic(mobj_t *metal) WRITEUINT16(demo_p,oldmetal.sprite); if (ghostext.flags & EZT_HEIGHT) { - height >>= FRACBITS; - WRITEINT16(demo_p, height); + WRITEFIXED(demo_p, height); } ghostext.flags = 0; } @@ -1486,8 +1484,8 @@ void G_BeginRecording(void) WRITEUINT8(demo_p,player->thrustfactor); WRITEUINT8(demo_p,player->accelstart); WRITEUINT8(demo_p,player->acceleration); - WRITEUINT8(demo_p,player->height>>FRACBITS); - WRITEUINT8(demo_p,player->spinheight>>FRACBITS); + WRITEFIXED(demo_p,player->height); + WRITEFIXED(demo_p,player->spinheight); WRITEUINT8(demo_p,player->camerascale>>FRACBITS); WRITEUINT8(demo_p,player->shieldscale>>FRACBITS); @@ -1913,8 +1911,8 @@ void G_DoPlayDemo(char *defdemoname) thrustfactor = READUINT8(demo_p); accelstart = READUINT8(demo_p); acceleration = READUINT8(demo_p); - height = (fixed_t)READUINT8(demo_p)< Date: Thu, 10 Jun 2021 12:59:54 -0400 Subject: [PATCH 067/126] Cut cv_hidetime's minimum value to 5 seconds --- src/d_netcmd.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/d_netcmd.c b/src/d_netcmd.c index 31c10f58a..78fc83474 100644 --- a/src/d_netcmd.c +++ b/src/d_netcmd.c @@ -286,7 +286,7 @@ consvar_t cv_gravity = CVAR_INIT ("gravity", "0.5", CV_RESTRICT|CV_FLOAT|CV_CALL consvar_t cv_soundtest = CVAR_INIT ("soundtest", "0", CV_CALL, NULL, SoundTest_OnChange); -static CV_PossibleValue_t minitimelimit_cons_t[] = {{15, "MIN"}, {9999, "MAX"}, {0, NULL}}; +static CV_PossibleValue_t minitimelimit_cons_t[] = {{5, "MIN"}, {9999, "MAX"}, {0, NULL}}; consvar_t cv_countdowntime = CVAR_INIT ("countdowntime", "60", CV_SAVE|CV_NETVAR|CV_CHEAT, minitimelimit_cons_t, NULL); consvar_t cv_touchtag = CVAR_INIT ("touchtag", "Off", CV_SAVE|CV_NETVAR, CV_OnOff, NULL); From f1a96bd7ee1df2e3cf0c2ab7b6b6f9ce4841778b Mon Sep 17 00:00:00 2001 From: Riku Salminen <38985578+Riku-S@users.noreply.github.com> Date: Thu, 10 Jun 2021 21:18:45 +0300 Subject: [PATCH 068/126] Add support for ghosts with netvars in 2.2.6 and before --- src/g_demo.c | 21 ++++++++++++++++----- 1 file changed, 16 insertions(+), 5 deletions(-) diff --git a/src/g_demo.c b/src/g_demo.c index 7793e0272..45d09fdbd 100644 --- a/src/g_demo.c +++ b/src/g_demo.c @@ -726,6 +726,7 @@ void G_GhostTicker(void) g->mo->y = g->oldmo.y; g->mo->z = g->oldmo.z; P_SetThingPosition(g->mo); + g->mo->frame = g->oldmo.frame | tr_trans30<fadein) { @@ -2023,7 +2024,7 @@ void G_AddGhost(char *defdemoname) char name[17],skin[17],color[MAXCOLORNAME+1],*n,*pdemoname,md5[16]; UINT8 cnamelen; demoghost *gh; - UINT8 flags; + UINT8 flags, subversion; UINT8 *buffer,*p; mapthing_t *mthing; UINT16 count, ghostversion; @@ -2071,7 +2072,7 @@ void G_AddGhost(char *defdemoname) return; } p += 12; // DEMOHEADER p++; // VERSION - p++; // SUBVERSION + subversion = READUINT8(p); // SUBVERSION ghostversion = READUINT16(p); switch(ghostversion) { @@ -2170,9 +2171,19 @@ void G_AddGhost(char *defdemoname) count = READUINT16(p); while (count--) { - SKIPSTRING(p); - SKIPSTRING(p); - p++; + // In 2.2.7 netvar saving was updated + if (subversion < 7) + { + p += 2; + SKIPSTRING(p); + p++; + } + else + { + SKIPSTRING(p); + SKIPSTRING(p); + p++; + } } if (*p == DEMOMARKER) From 2a891546672cedc06174ab753c92d26316eb0240 Mon Sep 17 00:00:00 2001 From: Ors Date: Thu, 10 Jun 2021 14:23:51 -0400 Subject: [PATCH 069/126] Revert an accidental whitespace change --- src/g_demo.c | 1 - 1 file changed, 1 deletion(-) diff --git a/src/g_demo.c b/src/g_demo.c index 45d09fdbd..bf2013107 100644 --- a/src/g_demo.c +++ b/src/g_demo.c @@ -726,7 +726,6 @@ void G_GhostTicker(void) g->mo->y = g->oldmo.y; g->mo->z = g->oldmo.z; P_SetThingPosition(g->mo); - g->mo->frame = g->oldmo.frame | tr_trans30<fadein) { From 460d46bbc9095820c9a3c84bf716079735977fea Mon Sep 17 00:00:00 2001 From: Radicalicious Date: Fri, 11 Jun 2021 10:52:31 -0400 Subject: [PATCH 070/126] Cut cv_hidetime's minimum value all the way down to 1 --- src/d_netcmd.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/d_netcmd.c b/src/d_netcmd.c index 78fc83474..c6091d9e0 100644 --- a/src/d_netcmd.c +++ b/src/d_netcmd.c @@ -286,7 +286,7 @@ consvar_t cv_gravity = CVAR_INIT ("gravity", "0.5", CV_RESTRICT|CV_FLOAT|CV_CALL consvar_t cv_soundtest = CVAR_INIT ("soundtest", "0", CV_CALL, NULL, SoundTest_OnChange); -static CV_PossibleValue_t minitimelimit_cons_t[] = {{5, "MIN"}, {9999, "MAX"}, {0, NULL}}; +static CV_PossibleValue_t minitimelimit_cons_t[] = {{1, "MIN"}, {9999, "MAX"}, {0, NULL}}; consvar_t cv_countdowntime = CVAR_INIT ("countdowntime", "60", CV_SAVE|CV_NETVAR|CV_CHEAT, minitimelimit_cons_t, NULL); consvar_t cv_touchtag = CVAR_INIT ("touchtag", "Off", CV_SAVE|CV_NETVAR, CV_OnOff, NULL); From 0e030466e15152e61050350ad7a01187b1265bc4 Mon Sep 17 00:00:00 2001 From: sphere Date: Thu, 17 Jun 2021 13:23:27 +0200 Subject: [PATCH 071/126] Add flag to line slopes for copying their slopes to the other side. --- extras/conf/SRB2-22.cfg | 16 ++++++++++++++++ src/p_setup.c | 6 ++++++ src/p_slopes.c | 3 +++ 3 files changed, 25 insertions(+) diff --git a/extras/conf/SRB2-22.cfg b/extras/conf/SRB2-22.cfg index 5a464eb5d..a9027f14a 100644 --- a/extras/conf/SRB2-22.cfg +++ b/extras/conf/SRB2-22.cfg @@ -2950,8 +2950,10 @@ linedeftypes prefix = "(700)"; flags2048text = "[11] No physics"; flags4096text = "[12] Dynamic"; + flags32768text = "[15] Copy to other side"; slope = "regular"; slopeargs = 1; + copyslopeargs = 1; } 701 @@ -2960,8 +2962,10 @@ linedeftypes prefix = "(701)"; flags2048text = "[11] No physics"; flags4096text = "[12] Dynamic"; + flags32768text = "[15] Copy to other side"; slope = "regular"; slopeargs = 2; + copyslopeargs = 4; } 702 @@ -2970,8 +2974,10 @@ linedeftypes prefix = "(702)"; flags2048text = "[11] No physics"; flags4096text = "[12] Dynamic"; + flags32768text = "[15] Copy to other side"; slope = "regular"; slopeargs = 3; + copyslopeargs = 5; } 703 @@ -2980,8 +2986,10 @@ linedeftypes prefix = "(703)"; flags2048text = "[11] No physics"; flags4096text = "[12] Dynamic"; + flags32768text = "[15] Copy to other side"; slope = "regular"; slopeargs = 9; + copyslopeargs = 8; } 704 @@ -3012,8 +3020,10 @@ linedeftypes prefix = "(710)"; flags2048text = "[11] No physics"; flags4096text = "[12] Dynamic"; + flags32768text = "[15] Copy to other side"; slope = "regular"; slopeargs = 4; + copyslopeargs = 2; } 711 @@ -3022,8 +3032,10 @@ linedeftypes prefix = "(711)"; flags2048text = "[11] No physics"; flags4096text = "[12] Dynamic"; + flags32768text = "[15] Copy to other side"; slope = "regular"; slopeargs = 8; + copyslopeargs = 8; } 712 @@ -3032,8 +3044,10 @@ linedeftypes prefix = "(712)"; flags2048text = "[11] No physics"; flags4096text = "[12] Dynamic"; + flags32768text = "[15] Copy to other side"; slope = "regular"; slopeargs = 12; + copyslopeargs = 10; } 713 @@ -3042,8 +3056,10 @@ linedeftypes prefix = "(713)"; flags2048text = "[11] No physics"; flags4096text = "[12] Dynamic"; + flags32768text = "[15] Copy to other side"; slope = "regular"; slopeargs = 6; + copyslopeargs = 6; } 714 diff --git a/src/p_setup.c b/src/p_setup.c index 51d2f474d..8d2ba4b01 100644 --- a/src/p_setup.c +++ b/src/p_setup.c @@ -3154,6 +3154,12 @@ static void P_ConvertBinaryMap(void) if (lines[i].flags & ML_NONET) lines[i].args[2] |= TMSL_DYNAMIC; + if (lines[i].flags & ML_TFERLINE) + { + lines[i].args[4] |= backfloor ? TMSC_BACKTOFRONTFLOOR : (frontfloor ? TMSC_FRONTTOBACKFLOOR : 0); + lines[i].args[4] |= backceil ? TMSC_BACKTOFRONTCEILING : (frontceil ? TMSC_FRONTTOBACKCEILING : 0); + } + lines[i].special = 700; break; } diff --git a/src/p_slopes.c b/src/p_slopes.c index 4e93e4a45..e99ecc64c 100644 --- a/src/p_slopes.c +++ b/src/p_slopes.c @@ -664,6 +664,9 @@ void P_SpawnSlopes(const boolean fromsave) { for (i = 0; i < numlines; i++) switch (lines[i].special) { + case 700: + if (lines[i].flags & ML_TFERLINE) P_CopySectorSlope(&lines[i]); + break; case 720: P_CopySectorSlope(&lines[i]); default: From e46afda89620a85fbfcf47afeaac5b436a4e587c Mon Sep 17 00:00:00 2001 From: Radicalicious Date: Fri, 18 Jun 2021 01:04:49 -0400 Subject: [PATCH 072/126] Hopefully fix ghosts --- src/g_demo.c | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/src/g_demo.c b/src/g_demo.c index 9d3b86015..1367c87b9 100644 --- a/src/g_demo.c +++ b/src/g_demo.c @@ -784,6 +784,18 @@ void G_GhostTicker(void) mobj = P_SpawnGhostMobj(g->mo); // does a large portion of the work for us mobj->frame = (mobj->frame & ~FF_FRAMEMASK)|tr_trans60<mo, 0, 0, g->mo->scale * 24, type); + mobj->angle = g->mo->angle + ANGLE_90; + mobj->fuse = 7; + mobj->scale = FRACUNIT / 3; + mobj->destscale = 10*FRACUNIT; + mobj->colorized = true; + mobj->color = g->mo->color; + mobj->momx = -g->mo->momx / 2; + mobj->momy = -g->mo->momy / 2; + } else { mobj = P_SpawnMobjFromMobj(g->mo, 0, 0, -FixedDiv(FixedMul(g->mo->info->height, g->mo->scale) - g->mo->height,3*FRACUNIT), MT_THOK); @@ -1074,6 +1086,18 @@ void G_ReadMetalTic(mobj_t *metal) { mobj = P_SpawnGhostMobj(metal); // does a large portion of the work for us } + else if (type == MT_THOKEFFECT) + { + mobj = P_SpawnMobjFromMobj(metal, 0, 0, metal->scale * 24, type); + mobj->angle = metal->angle + ANGLE_90; + mobj->fuse = 7; + mobj->scale = FRACUNIT / 3; + mobj->destscale = 10*FRACUNIT; + mobj->colorized = true; + mobj->color = metal->color; + mobj->momx = -metal->momx / 2; + mobj->momy = -metal->momy / 2; + } else { mobj = P_SpawnMobjFromMobj(metal, 0, 0, -FixedDiv(FixedMul(metal->info->height, metal->scale) - metal->height,3*FRACUNIT), MT_THOK); From 63d217b689ee4d39fc906a77e5b93b3557898fca Mon Sep 17 00:00:00 2001 From: Radicalicious Date: Fri, 18 Jun 2021 05:06:52 +0000 Subject: [PATCH 073/126] Revert "Hopefully fix ghosts" This reverts commit e46afda89620a85fbfcf47afeaac5b436a4e587c --- src/g_demo.c | 24 ------------------------ 1 file changed, 24 deletions(-) diff --git a/src/g_demo.c b/src/g_demo.c index 1367c87b9..9d3b86015 100644 --- a/src/g_demo.c +++ b/src/g_demo.c @@ -784,18 +784,6 @@ void G_GhostTicker(void) mobj = P_SpawnGhostMobj(g->mo); // does a large portion of the work for us mobj->frame = (mobj->frame & ~FF_FRAMEMASK)|tr_trans60<mo, 0, 0, g->mo->scale * 24, type); - mobj->angle = g->mo->angle + ANGLE_90; - mobj->fuse = 7; - mobj->scale = FRACUNIT / 3; - mobj->destscale = 10*FRACUNIT; - mobj->colorized = true; - mobj->color = g->mo->color; - mobj->momx = -g->mo->momx / 2; - mobj->momy = -g->mo->momy / 2; - } else { mobj = P_SpawnMobjFromMobj(g->mo, 0, 0, -FixedDiv(FixedMul(g->mo->info->height, g->mo->scale) - g->mo->height,3*FRACUNIT), MT_THOK); @@ -1086,18 +1074,6 @@ void G_ReadMetalTic(mobj_t *metal) { mobj = P_SpawnGhostMobj(metal); // does a large portion of the work for us } - else if (type == MT_THOKEFFECT) - { - mobj = P_SpawnMobjFromMobj(metal, 0, 0, metal->scale * 24, type); - mobj->angle = metal->angle + ANGLE_90; - mobj->fuse = 7; - mobj->scale = FRACUNIT / 3; - mobj->destscale = 10*FRACUNIT; - mobj->colorized = true; - mobj->color = metal->color; - mobj->momx = -metal->momx / 2; - mobj->momy = -metal->momy / 2; - } else { mobj = P_SpawnMobjFromMobj(metal, 0, 0, -FixedDiv(FixedMul(metal->info->height, metal->scale) - metal->height,3*FRACUNIT), MT_THOK); From c3fa9bea0fd6a426e448d4ecb8240a75593ecd50 Mon Sep 17 00:00:00 2001 From: CobaltBW Date: Tue, 29 Jun 2021 13:30:10 -0700 Subject: [PATCH 074/126] ri# mified: src/tazx# mofied: src/tazx# modified: src/tazx# modified: src/tazxc --- src/g_game.c | 5 +---- src/r_skins.c | 1 - 2 files changed, 1 insertion(+), 5 deletions(-) diff --git a/src/g_game.c b/src/g_game.c index 83531bb35..9f2d05ff0 100644 --- a/src/g_game.c +++ b/src/g_game.c @@ -2317,16 +2317,13 @@ void G_Ticker(boolean run) P_ForceLocalAngle(&players[i], players[i].angleturn << 16); else players[i].cmd.angleturn = players[i].angleturn; - players[i].angleturn += players[i].cmd.angleturn - players[i].oldrelangleturn; - players[i].oldrelangleturn = players[i].cmd.angleturn; if (P_ControlStyle(&players[i]) == CS_LMAOGALOG) P_ForceLocalAngle(&players[i], players[i].angleturn << 16); else players[i].cmd.angleturn = players[i].angleturn; players[i].cmd.angleturn &= ~TICCMD_RECEIVED; - - // Use the leveltime sent in the player's ticcmd to determine control lag + // Use the leveltime sent in the player's ticcmd to determine control lag players[i].cmd.latency = min(((leveltime & 0xFF) - players[i].cmd.latency) & 0xFF, MAXPREDICTTICS-1); } else // Less work is required if we're building a bot ticcmd. diff --git a/src/r_skins.c b/src/r_skins.c index f67f2afd4..c734b6001 100644 --- a/src/r_skins.c +++ b/src/r_skins.c @@ -286,7 +286,6 @@ boolean R_SkinUsable(INT32 playernum, INT32 skinnum) // We want to check our global unlockables. return (unlockables[unlockID].unlocked); } ->>>>>>> src/r_skins.c } // returns true if the skin name is found (loaded from pwad) From dad361721fa3c1f572ba5d40ae5af0884258fb17 Mon Sep 17 00:00:00 2001 From: Shane Ellis Date: Wed, 30 Jun 2021 01:32:24 -0400 Subject: [PATCH 075/126] comment cleanup --- src/p_user.c | 27 ++++----------------------- 1 file changed, 4 insertions(+), 23 deletions(-) diff --git a/src/p_user.c b/src/p_user.c index dadc23193..7ab7d8105 100644 --- a/src/p_user.c +++ b/src/p_user.c @@ -776,7 +776,7 @@ void P_NightserizePlayer(player_t *player, INT32 nighttime) { UINT8 oldmare, oldmarelap, oldmarebonuslap; - //! Bots can't be NiGHTSerized, silly!1 :P + // Bots can't be NiGHTSerized, silly!1 :P if (player->bot == BOT_2PAI || player->bot == BOT_2PHUMAN) return; @@ -1188,7 +1188,6 @@ void P_GivePlayerRings(player_t *player, INT32 num_rings) { if (!player) return; - //! if ((player->bot == BOT_2PAI || player->bot == BOT_2PHUMAN) && player->botleader) player = player->botleader; @@ -5965,23 +5964,6 @@ static void P_3dMovement(player_t *player) acceleration = 96 + (FixedDiv(player->speed, player->mo->scale)>>FRACBITS) * 40; topspeed = normalspd; } - //! Kill this! - /* else if (player->bot == BOT_2PAI || player->bot == BOT_2PHUMAN) - { // Bot steals player 1's stats - normalspd = FixedMul(players[consoleplayer].normalspeed, player->mo->scale); - thrustfactor = players[consoleplayer].thrustfactor; - acceleration = players[consoleplayer].accelstart + (FixedDiv(player->speed, player->mo->scale)>>FRACBITS) * players[consoleplayer].acceleration; - - if (player->powers[pw_tailsfly]) - topspeed = normalspd/2; - else if (player->mo->eflags & (MFE_UNDERWATER|MFE_GOOWATER)) - { - topspeed = normalspd/2; - acceleration = 2*acceleration/3; - } - else - topspeed = normalspd; - } */ else { if (player->powers[pw_super] || player->powers[pw_sneakers]) @@ -11488,7 +11470,7 @@ void P_PlayerThink(player_t *player) I_Error("p_playerthink: players[%s].mo == NULL", sizeu1(playeri)); #endif - //! Reset terrain blocked status for this frame + // Reset terrain blocked status for this frame player->blocked = false; // todo: Figure out what is actually causing these problems in the first place... @@ -11641,7 +11623,7 @@ void P_PlayerThink(player_t *player) INT32 i; for (i = 0; i < MAXPLAYERS; i++) - { //! + { if (!playeringame[i] || players[i].spectator || players[i].bot == BOT_2PAI || players[i].bot == BOT_2PHUMAN) continue; if (players[i].lives <= 0) @@ -11673,7 +11655,7 @@ void P_PlayerThink(player_t *player) INT32 i, total = 0, exiting = 0; for (i = 0; i < MAXPLAYERS; i++) - { //! + { if (!playeringame[i] || players[i].spectator || players[i].bot == BOT_2PAI || players[i].bot == BOT_2PHUMAN) continue; if (players[i].quittime > 30 * TICRATE) @@ -12614,7 +12596,6 @@ void P_PlayerAfterThink(player_t *player) player->mo->momy = tails->momy; player->mo->momz = tails->momz; } - //! if (G_CoopGametype() && tails->player && tails->player->bot != BOT_2PAI) { player->mo->angle = tails->angle; From ee765d1043893bb798cdd84cf22d3383812812e6 Mon Sep 17 00:00:00 2001 From: Shane Ellis Date: Wed, 30 Jun 2021 01:34:21 -0400 Subject: [PATCH 076/126] comment cleanup --- src/g_game.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/g_game.c b/src/g_game.c index 0643350b6..6e900d026 100644 --- a/src/g_game.c +++ b/src/g_game.c @@ -3005,7 +3005,7 @@ void G_DoReborn(INT32 playernum) // Make sure objectplace is OFF when you first start the level! OP_ResetObjectplace(); - //! Tailsbot + // Tailsbot if (player->bot == BOT_2PAI || player->bot == BOT_2PHUMAN) { // Bots respawn next to their master. mobj_t *oldmo = NULL; From 94441d6eee252c3ebc7cd438c32d3d15dbb3c012 Mon Sep 17 00:00:00 2001 From: Shane Ellis Date: Wed, 30 Jun 2021 01:36:28 -0400 Subject: [PATCH 077/126] comment cleanup --- src/b_bot.c | 7 ------- 1 file changed, 7 deletions(-) diff --git a/src/b_bot.c b/src/b_bot.c index c61e56f5f..cdd74fc07 100644 --- a/src/b_bot.c +++ b/src/b_bot.c @@ -25,8 +25,6 @@ void B_UpdateBotleader(player_t *player) fixed_t neardist = INT32_MAX; player_t *nearplayer = NULL; //Find new botleader - //if (!player->botleader) - //{ for (i = 0; i < MAXPLAYERS; i++) { if (players[i].bot || players[i].playerstate != PST_LIVE || players[i].spectator || !players[i].mo) @@ -46,7 +44,6 @@ void B_UpdateBotleader(player_t *player) } //Set botleader to best candidate (or null if none available) player->botleader = nearplayer; - //} } static inline void B_ResetAI(botmem_t *mem) @@ -102,7 +99,6 @@ static void B_BuildTailsTiccmd(mobj_t *sonic, mobj_t *tails, ticcmd_t *cmd) if (tails->player->powers[pw_carry] == CR_MACESPIN || tails->player->powers[pw_carry] == CR_GENERIC) { boolean isrelevant = (sonic->player->powers[pw_carry] == CR_MACESPIN || sonic->player->powers[pw_carry] == CR_GENERIC); - //dist = P_AproxDistance(tails->x-sonic->x, tails->y-sonic->y); //! This is totally redundant. if (sonic->player->cmd.buttons & BT_JUMP && (sonic->player->pflags & PF_JUMPED) && isrelevant) cmd->buttons |= BT_JUMP; if (isrelevant) @@ -402,8 +398,6 @@ void B_KeysToTiccmd(mobj_t *mo, ticcmd_t *cmd, boolean forward, boolean backward { player_t *player = mo->player; // don't try to do stuff if your sonic is in a minecart or something - //if (players[consoleplayer].powers[pw_carry] && players[consoleplayer].powers[pw_carry] != CR_PLAYER) - //!!! if (&player->botleader && player->botleader->powers[pw_carry] && player->botleader->powers[pw_carry] != CR_PLAYER) return; // Turn the virtual keypresses into ticcmd_t. @@ -576,7 +570,6 @@ void B_RespawnBot(INT32 playernum) player->powers[pw_spacetime] = sonic->player->powers[pw_spacetime]; player->powers[pw_gravityboots] = sonic->player->powers[pw_gravityboots]; player->powers[pw_nocontrol] = sonic->player->powers[pw_nocontrol]; - //!!! Nuke the speed equivalencies player->pflags |= PF_AUTOBRAKE|(sonic->player->pflags & PF_DIRECTIONCHAR); P_TeleportMove(tails, x, y, z); From afa8466b304207f15367c30352da5af7e1ede59f Mon Sep 17 00:00:00 2001 From: Shane Ellis Date: Wed, 30 Jun 2021 01:38:04 -0400 Subject: [PATCH 078/126] comment cleanup --- src/p_mobj.c | 1 - 1 file changed, 1 deletion(-) diff --git a/src/p_mobj.c b/src/p_mobj.c index 4924ec053..8af7ab0d0 100644 --- a/src/p_mobj.c +++ b/src/p_mobj.c @@ -1839,7 +1839,6 @@ void P_XYMovement(mobj_t *mo) // blocked move moved = false; - //!!! if (player) B_MoveBlocked(player); From 0482eacb7c9a625c47a19c017304a3f4af2bd24c Mon Sep 17 00:00:00 2001 From: LJ Sonic Date: Sat, 3 Jul 2021 19:58:59 +0200 Subject: [PATCH 079/126] Load add-ons in the order in which the -file and -folder arguments are specified --- src/d_main.c | 41 ++++++++++++++++------------------------- 1 file changed, 16 insertions(+), 25 deletions(-) diff --git a/src/d_main.c b/src/d_main.c index 0d0e2434a..7866ccbed 100644 --- a/src/d_main.c +++ b/src/d_main.c @@ -1253,34 +1253,25 @@ void D_SRB2Main(void) // Do this up here so that WADs loaded through the command line can use ExecCfg COM_Init(); - // add any files specified on the command line with -file wadfile - // to the wad list + // Add any files specified on the command line with + // "-file " or "-folder " to the add-on list if (!((M_GetUrlProtocolArg() || M_CheckParm("-connect")) && !M_CheckParm("-server"))) { - if (M_CheckParm("-file")) + INT32 addontype = 0; + INT32 i; + + for (i = 1; i < myargc; i++) { - // the parms after p are wadfile names, - // until end of parms or another - preceded parm - while (M_IsNextParm()) - { - const char *s = M_GetNextParm(); - - if (s) // Check for NULL? - D_AddFile(startuppwads, s); - } - } - - if (M_CheckParm("-folder")) - { - // the parms after p are folder names, - // until end of parms or another - preceded parm - while (M_IsNextParm()) - { - const char *s = M_GetNextParm(); - - if (s) // Check for NULL? - D_AddFolder(startuppwads, s); - } + if (!strcasecmp(myargv[i], "-file")) + addontype = 1; + else if (!strcasecmp(myargv[i], "-folder")) + addontype = 2; + else if (myargv[i][0] == '-' || myargv[i][0] == '+') + addontype = 0; + else if (addontype == 1) + D_AddFile(startuppwads, myargv[i]); + else if (addontype == 2) + D_AddFolder(startuppwads, myargv[i]); } } From 18cbc1e37047f2fabf61e32a4bc2997125ad21b4 Mon Sep 17 00:00:00 2001 From: James R Date: Mon, 5 Jul 2021 18:31:04 -0700 Subject: [PATCH 080/126] Fix aliases bypass COM_SAFE --- src/command.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/command.c b/src/command.c index 95b1fd67d..0e0b1a685 100644 --- a/src/command.c +++ b/src/command.c @@ -650,7 +650,7 @@ static void COM_ExecuteString(char *ptext) else { // Monster Iestyn: keep track of how many levels of recursion we're in recursion++; - COM_BufInsertText(a->value); + COM_BufInsertTextEx(a->value, com_flags); recursion--; } return; From 14c5d2c916e42c926dcf8267df3b49bd3dcab31d Mon Sep 17 00:00:00 2001 From: James R Date: Mon, 5 Jul 2021 18:39:12 -0700 Subject: [PATCH 081/126] Warn if Lua attempted access NOLUA consvar And quote variable name. --- src/command.c | 5 ++++- src/lua_consolelib.c | 2 +- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/src/command.c b/src/command.c index 0e0b1a685..e6c6587e8 100644 --- a/src/command.c +++ b/src/command.c @@ -2364,7 +2364,10 @@ static boolean CV_Command(void) return false; if (( com_flags & COM_SAFE ) && ( v->flags & CV_NOLUA )) - return false; + { + CONS_Alert(CONS_WARNING, "Variable '%s' cannot be changed from Lua.\n", v->name); + return true; + } // perform a variable print or set if (COM_Argc() == 1) diff --git a/src/lua_consolelib.c b/src/lua_consolelib.c index 414d9692a..2b8cad69b 100644 --- a/src/lua_consolelib.c +++ b/src/lua_consolelib.c @@ -433,7 +433,7 @@ static int CVarSetFunction consvar_t *cvar = *(consvar_t **)luaL_checkudata(L, 1, META_CVAR); if (cvar->flags & CV_NOLUA) - return luaL_error(L, "Variable %s cannot be set from Lua.", cvar->name); + return luaL_error(L, "Variable '%s' cannot be set from Lua.", cvar->name); switch (lua_type(L, 2)) { From cb619fad5d762ba8d8dd20e9a979a04ced7c943f Mon Sep 17 00:00:00 2001 From: SMS Alfredo <65426124+SMS-Alfredo@users.noreply.github.com> Date: Wed, 7 Jul 2021 19:57:28 -0500 Subject: [PATCH 082/126] Rebase on !1307 --- src/lua_hook.h | 1 + src/lua_hooklib.c | 13 +++++++++++++ src/p_mobj.c | 2 +- 3 files changed, 15 insertions(+), 1 deletion(-) diff --git a/src/lua_hook.h b/src/lua_hook.h index 223b83c61..1af28aac5 100644 --- a/src/lua_hook.h +++ b/src/lua_hook.h @@ -115,6 +115,7 @@ int LUA_HookTouchSpecial(mobj_t *special, mobj_t *toucher); int LUA_HookShouldDamage(mobj_t *target, mobj_t *inflictor, mobj_t *source, INT32 damage, UINT8 damagetype); int LUA_HookMobjDamage(mobj_t *target, mobj_t *inflictor, mobj_t *source, INT32 damage, UINT8 damagetype); int LUA_HookMobjDeath(mobj_t *target, mobj_t *inflictor, mobj_t *source, UINT8 damagetype); +int LUA_HookMobjMoveBlocked(mobj_t *, mobj_t *, line_t *); int LUA_HookBotAI(mobj_t *sonic, mobj_t *tails, ticcmd_t *cmd); void LUA_HookLinedefExecute(line_t *, mobj_t *, sector_t *); int LUA_HookPlayerMsg(int source, int target, int flags, char *msg); diff --git a/src/lua_hooklib.c b/src/lua_hooklib.c index d1b0d3bdd..f2e9b5233 100644 --- a/src/lua_hooklib.c +++ b/src/lua_hooklib.c @@ -713,6 +713,19 @@ int LUA_HookMobjDeath(mobj_t *target, mobj_t *inflictor, mobj_t *source, UINT8 d MOBJ_HOOK(MobjDeath), 4, res_true); } +int LUA_HookMobjMoveBlocked(mobj_t *t1, mobj_t *t2, line_t *line) +{ + Hook_State hook; + if (prepare_mobj_hook(&hook, 0, MOBJ_HOOK(MobjMoveBlocked), t1->type)) + { + LUA_PushUserdata(gL, t1, META_MOBJ); + LUA_PushUserdata(gL, t2, META_MOBJ); + LUA_PushUserdata(gL, line, META_LINE); + call_hooks(&hook, 3, 1, res_true); + } + return hook.status; +} + typedef struct { mobj_t * tails; ticcmd_t * cmd; diff --git a/src/p_mobj.c b/src/p_mobj.c index 10220fff6..f2fae6cc6 100644 --- a/src/p_mobj.c +++ b/src/p_mobj.c @@ -1844,7 +1844,7 @@ void P_XYMovement(mobj_t *mo) B_MoveBlocked(player); } - if (LUA_HookMobj(mo, MOBJ_HOOK(MobjMoveBlocked))) + if (LUA_HookMobjMoveBlocked(mo, tmhitthing, blockingline)) { if (P_MobjWasRemoved(mo)) return; From e30d4f954b9ec90632484b31673b7fe6185481a5 Mon Sep 17 00:00:00 2001 From: James R Date: Thu, 8 Jul 2021 14:37:03 -0700 Subject: [PATCH 083/126] Revert netvars after demo finishes playback (Demos do not call CL_Reset BTW.) --- src/command.c | 4 +++- src/d_clisrv.c | 3 ++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/src/command.c b/src/command.c index 95b1fd67d..da777310a 100644 --- a/src/command.c +++ b/src/command.c @@ -1738,6 +1738,8 @@ void CV_SaveVars(UINT8 **p, boolean in_demo) static void CV_LoadVars(UINT8 **p, consvar_t *(*got)(UINT8 **p, char **ret_value, boolean *ret_stealth)) { + const boolean store = (client || demoplayback); + consvar_t *cvar; UINT16 count; @@ -1751,7 +1753,7 @@ static void CV_LoadVars(UINT8 **p, { if (cvar->flags & CV_NETVAR) { - if (client && cvar->revert.v.string == NULL) + if (store && cvar->revert.v.string == NULL) { cvar->revert.v.const_munge = cvar->string; cvar->revert.allocated = ( cvar->zstring != NULL ); diff --git a/src/d_clisrv.c b/src/d_clisrv.c index 1549811c1..1d895c0e1 100644 --- a/src/d_clisrv.c +++ b/src/d_clisrv.c @@ -2599,7 +2599,6 @@ void CL_Reset(void) doomcom->numslots = 1; SV_StopServer(); SV_ResetServer(); - CV_RevertNetVars(); // make sure we don't leave any fileneeded gunk over from a failed join fileneedednum = 0; @@ -3231,6 +3230,8 @@ void SV_ResetServer(void) // clear server_context memset(server_context, '-', 8); + CV_RevertNetVars(); + DEBFILE("\n-=-=-=-=-=-=-= Server Reset =-=-=-=-=-=-=-\n\n"); } From 366b1f65a18a89b26afcd30c71255bcd9d1995be Mon Sep 17 00:00:00 2001 From: lachablock Date: Sat, 10 Jul 2021 18:22:07 +1000 Subject: [PATCH 084/126] Allow Lua write access to camera_t variables & expose the cameras globally --- src/lua_hudlib.c | 56 ++++++++++++++++++++++++++++++++++++++++++++++++ src/lua_script.c | 8 +++++++ 2 files changed, 64 insertions(+) diff --git a/src/lua_hudlib.c b/src/lua_hudlib.c index 9a3e676d5..306ffaf94 100644 --- a/src/lua_hudlib.c +++ b/src/lua_hudlib.c @@ -384,6 +384,59 @@ static int camera_get(lua_State *L) return 1; } +static int camera_set(lua_State *L) +{ + camera_t *cam = *((camera_t **)luaL_checkudata(L, 1, META_CAMERA)); + enum cameraf field = luaL_checkoption(L, 2, NULL, camera_opt); + + I_Assert(cam != NULL); + + switch(field) + { + case camera_subsector: + case camera_floorz: + case camera_ceilingz: + case camera_height: + case camera_radius: + return luaL_error(L, LUA_QL("camera_t") " field " LUA_QS " should not be set directly.", camera_opt[field]); + case camera_chase: + if (cam == &camera) + CV_SetValue(&cv_chasecam, (INT32)luaL_checkboolean(L, 3)); + else if (cam == &camera2) + CV_SetValue(&cv_chasecam2, (INT32)luaL_checkboolean(L, 3)); + else // ??? this should never happen, but ok + cam->chase = luaL_checkboolean(L, 3); + break; + case camera_aiming: + cam->aiming = luaL_checkangle(L, 3); + break; + case camera_x: + cam->x = luaL_checkfixed(L, 3); + break; + case camera_y: + cam->y = luaL_checkfixed(L, 3); + break; + case camera_z: + cam->z = luaL_checkfixed(L, 3); + break; + case camera_angle: + cam->angle = luaL_checkangle(L, 3); + break; + case camera_momx: + cam->momx = luaL_checkfixed(L, 3); + break; + case camera_momy: + cam->momy = luaL_checkfixed(L, 3); + break; + case camera_momz: + cam->momz = luaL_checkfixed(L, 3); + break; + default: + return luaL_error(L, LUA_QL("camera_t") " has no field named " LUA_QS, camera_opt[field]); + } + return 0; +} + // // lib_draw // @@ -1283,6 +1336,9 @@ int LUA_HudLib(lua_State *L) luaL_newmetatable(L, META_CAMERA); lua_pushcfunction(L, camera_get); lua_setfield(L, -2, "__index"); + + lua_pushcfunction(L, camera_set); + lua_setfield(L, -2, "__newindex"); lua_pop(L,1); luaL_register(L, "hud", lib_hud); diff --git a/src/lua_script.c b/src/lua_script.c index 6faff8729..54ab124fc 100644 --- a/src/lua_script.c +++ b/src/lua_script.c @@ -393,6 +393,14 @@ int LUA_PushGlobals(lua_State *L, const char *word) } else if (fastcmp(word, "mouse2")) { LUA_PushUserdata(L, &mouse2, META_MOUSE); return 1; + } else if (fastcmp(word, "camera")) { + LUA_PushUserdata(L, &camera, META_CAMERA); + return 1; + } else if (fastcmp(word, "camera2")) { + if (!splitscreen) + return 0; + LUA_PushUserdata(L, &camera2, META_CAMERA); + return 1; } return 0; } From 117e3e267087d8c13914037f14b282d47177d474 Mon Sep 17 00:00:00 2001 From: lachablock Date: Sun, 11 Jul 2021 16:23:50 +1000 Subject: [PATCH 085/126] Expose P_TryCameraMove and P_TeleportCameraMove, disallow write access to camera.x and camera.y, allow write access to camera.height and camera.radius --- src/lua_baselib.c | 33 +++++++++++++++++++++++++++++++++ src/lua_hudlib.c | 31 ++++++++++++++++++++++--------- 2 files changed, 55 insertions(+), 9 deletions(-) diff --git a/src/lua_baselib.c b/src/lua_baselib.c index d6f40846c..a3f1d9811 100644 --- a/src/lua_baselib.c +++ b/src/lua_baselib.c @@ -1883,6 +1883,37 @@ static int lib_pDoSpring(lua_State *L) return 1; } +static int lib_pTryCameraMove(lua_State *L) +{ + camera_t *cam = *((camera_t **)luaL_checkudata(L, 1, META_CAMERA)); + fixed_t x = luaL_checkfixed(L, 2); + fixed_t y = luaL_checkfixed(L, 3); + + if (!cam) + return LUA_ErrInvalid(L, "camera_t"); + lua_pushboolean(L, P_TryCameraMove(x, y, cam)); + return 1; +} + +static int lib_pTeleportCameraMove(lua_State *L) +{ + camera_t *cam = *((camera_t **)luaL_checkudata(L, 1, META_CAMERA)); + fixed_t x = luaL_checkfixed(L, 2); + fixed_t y = luaL_checkfixed(L, 3); + fixed_t z = luaL_checkfixed(L, 4); + + if (!cam) + return LUA_ErrInvalid(L, "camera_t"); + cam->x = x; + cam->y = y; + cam->z = z; + P_CheckCameraPosition(x, y, cam); + cam->subsector = R_PointInSubsector(x, y); + cam->floorz = tmfloorz; + cam->ceilingz = tmceilingz; + return 0; +} + // P_INTER //////////// @@ -3881,6 +3912,8 @@ static luaL_Reg lib[] = { {"P_FloorzAtPos",lib_pFloorzAtPos}, {"P_CeilingzAtPos",lib_pCeilingzAtPos}, {"P_DoSpring",lib_pDoSpring}, + {"P_TryCameraMove", lib_pTryCameraMove}, + {"P_TeleportCameraMove", lib_pTeleportCameraMove}, // p_inter {"P_RemoveShield",lib_pRemoveShield}, diff --git a/src/lua_hudlib.c b/src/lua_hudlib.c index 306ffaf94..0f8ccb089 100644 --- a/src/lua_hudlib.c +++ b/src/lua_hudlib.c @@ -396,9 +396,9 @@ static int camera_set(lua_State *L) case camera_subsector: case camera_floorz: case camera_ceilingz: - case camera_height: - case camera_radius: - return luaL_error(L, LUA_QL("camera_t") " field " LUA_QS " should not be set directly.", camera_opt[field]); + case camera_x: + case camera_y: + return luaL_error(L, LUA_QL("camera_t") " field " LUA_QS " should not be set directly. Use " LUA_QL("P_TryCameraMove") " or " LUA_QL("P_TeleportCameraMove") " instead.", camera_opt[field]); case camera_chase: if (cam == &camera) CV_SetValue(&cv_chasecam, (INT32)luaL_checkboolean(L, 3)); @@ -410,18 +410,31 @@ static int camera_set(lua_State *L) case camera_aiming: cam->aiming = luaL_checkangle(L, 3); break; - case camera_x: - cam->x = luaL_checkfixed(L, 3); - break; - case camera_y: - cam->y = luaL_checkfixed(L, 3); - break; case camera_z: cam->z = luaL_checkfixed(L, 3); + P_CheckCameraPosition(cam->x, cam->y, cam); + cam->floorz = tmfloorz; + cam->ceilingz = tmceilingz; break; case camera_angle: cam->angle = luaL_checkangle(L, 3); break; + case camera_radius: + cam->radius = luaL_checkfixed(L, 3); + if (cam->radius < 0) + cam->radius = 0; + P_CheckCameraPosition(cam->x, cam->y, cam); + cam->floorz = tmfloorz; + cam->ceilingz = tmceilingz; + break; + case camera_height: + cam->height = luaL_checkfixed(L, 3); + if (cam->height < 0) + cam->height = 0; + P_CheckCameraPosition(cam->x, cam->y, cam); + cam->floorz = tmfloorz; + cam->ceilingz = tmceilingz; + break; case camera_momx: cam->momx = luaL_checkfixed(L, 3); break; From c337709d105696f91ed923371346326e11dd93ee Mon Sep 17 00:00:00 2001 From: Monster Iestyn Date: Sun, 11 Jul 2021 16:32:36 -0400 Subject: [PATCH 086/126] Update f_finale.c --- src/f_finale.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/f_finale.c b/src/f_finale.c index e8757c18a..4d9a8f825 100644 --- a/src/f_finale.c +++ b/src/f_finale.c @@ -1166,7 +1166,6 @@ static const char *credits[] = { "Alexander \"DrTapeworm\" Moench-Ford", "Stefan \"Stuf\" Rimalia", "Shane Mychal Sexton", - "\"Spazzo\"", "David \"Big Wave Dave\" Spencer Sr.", "David \"Instant Sonic\" Spencer Jr.", "\"SSNTails\"", @@ -1191,7 +1190,6 @@ static const char *credits[] = { "\"Revan\"", "Anna \"QueenDelta\" Sandlin", "Wessel \"sphere\" Smit", - "\"Spazzo\"", "\"SSNTails\"", "Rob Tisdell", "\"Torgo\"", From 0faecf095987e4df5333a9d4fed6f06cd75bd9f0 Mon Sep 17 00:00:00 2001 From: SteelT Date: Sun, 11 Jul 2021 20:51:06 -0400 Subject: [PATCH 087/126] version.h: Update comment about contacting an MS admin --- src/version.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/version.h b/src/version.h index 4470fbd6e..28fc71c36 100644 --- a/src/version.h +++ b/src/version.h @@ -1,6 +1,6 @@ #define SRB2VERSION "2.2.9"/* this must be the first line, for cmake !! */ -// The Modification ID; must be obtained from a Master Server Admin ( https://mb.srb2.org/showgroups.php ). +// The Modification ID; must be obtained from a Master Server Admin ( https://mb.srb2.org/members/?key=ms_admin ). // DO NOT try to set this otherwise, or your modification will be unplayable through the Master Server. // "18" is the default mod ID for version 2.2 #define MODID 18 From 58fa44e8dc0444eecbe701f31872fad9fa563a6a Mon Sep 17 00:00:00 2001 From: James R Date: Mon, 12 Jul 2021 03:50:44 -0700 Subject: [PATCH 088/126] CMP0115 --- src/CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 4c125c4b8..721cd6dca 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -4,7 +4,7 @@ add_executable(SRB2SDL2 MACOSX_BUNDLE WIN32) # Core sources target_sourcefile(c) -target_sources(SRB2SDL2 PRIVATE comptime.c md5.c config.h) +target_sources(SRB2SDL2 PRIVATE comptime.c md5.c config.h.in) set(SRB2_ASM_SOURCES vid_copy.s) From 33ae95bf134571967f3f9875e5b3efeb2a352127 Mon Sep 17 00:00:00 2001 From: Shane Ellis Date: Mon, 12 Jul 2021 23:38:52 -0400 Subject: [PATCH 089/126] G_RemovePlayer error handling --- src/lua_baselib.c | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/src/lua_baselib.c b/src/lua_baselib.c index 9946a9fd3..34d1b1a94 100644 --- a/src/lua_baselib.c +++ b/src/lua_baselib.c @@ -3482,15 +3482,16 @@ static int lib_gAddPlayer(lua_State *L) static int lib_gRemovePlayer(lua_State *L) { UINT8 pnum = -1; - //const char *kickreason = luaL_checkstring(L, 2); - if (!lua_isnoneornil(L, 1)) pnum = luaL_checkinteger(L, 1); - if (&players[pnum]) + else // No argument + return luaL_error(L, "argument #1 not given (expected number)"); + if (pnum >= MAXPLAYERS) // Out of range + return luaL_error(L, "playernum %d out of range (0 - %d)", pnum, MAXPLAYERS-1); + if (playeringame[pnum]) // Found player { - if (players[pnum].bot != BOT_NONE) + if (players[pnum].bot != BOT_NONE) // Can't remove clients. { -// CL_RemovePlayer(pnum, *kickreason); CL_RemovePlayer(pnum, pnum); if (netgame) { @@ -3503,9 +3504,11 @@ static int lib_gRemovePlayer(lua_State *L) lua_pushboolean(L, true); return 1; } + else + return luaL_error(L, "G_RemovePlayer can only be used on players with a bot value other than BOT_NONE."); } - lua_pushboolean(L, false); - return 1; + // Fell through. Invalid player + return LUA_ErrInvalid(L, "player_t"); } From aed86781fc2794bb50a8245cbd0f2e5ea455d5c4 Mon Sep 17 00:00:00 2001 From: Nev3r Date: Tue, 13 Jul 2021 17:41:38 +0200 Subject: [PATCH 090/126] Bugfix - Fix sporadically occurring incorrect userdata types in Lua, caused by previously loaded userdata which didn't get invalidated in previous sessions. Invalidate userdata for line and mapthing args. Invalidate userdata for slopes and their normal, origin and direction vectors. --- src/lua_script.c | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/src/lua_script.c b/src/lua_script.c index 6faff8729..506a888ea 100644 --- a/src/lua_script.c +++ b/src/lua_script.c @@ -25,7 +25,7 @@ #include "byteptr.h" #include "p_saveg.h" #include "p_local.h" -#include "p_slopes.h" // for P_SlopeById +#include "p_slopes.h" // for P_SlopeById and slopelist #include "p_polyobj.h" // polyobj_t, PolyObjects #ifdef LUA_ALLOW_BYTECODE #include "d_netfil.h" // for LUA_DumpFile @@ -851,6 +851,7 @@ void LUA_InvalidateLevel(void) { LUA_InvalidateUserdata(&lines[i]); LUA_InvalidateUserdata(&lines[i].tags); + LUA_InvalidateUserdata(&lines[i].args); LUA_InvalidateUserdata(lines[i].sidenum); } for (i = 0; i < numsides; i++) @@ -863,6 +864,13 @@ void LUA_InvalidateLevel(void) LUA_InvalidateUserdata(&PolyObjects[i].vertices); LUA_InvalidateUserdata(&PolyObjects[i].lines); } + for (pslope_t *slope = slopelist; slope; slope = slope->next) + { + LUA_InvalidateUserdata(slope); + LUA_InvalidateUserdata(&slope->normal); + LUA_InvalidateUserdata(&slope->o); + LUA_InvalidateUserdata(&slope->d); + } #ifdef HAVE_LUA_SEGS for (i = 0; i < numsegs; i++) LUA_InvalidateUserdata(&segs[i]); @@ -885,6 +893,7 @@ void LUA_InvalidateMapthings(void) { LUA_InvalidateUserdata(&mapthings[i]); LUA_InvalidateUserdata(&mapthings[i].tags); + LUA_InvalidateUserdata(&mapthings[i].args); } } From 22dfa05c318b02bd77b9ee75990482b2d1378ad9 Mon Sep 17 00:00:00 2001 From: Nev3r Date: Tue, 13 Jul 2021 17:44:28 +0200 Subject: [PATCH 091/126] Forgot the stringargs. --- src/lua_script.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/lua_script.c b/src/lua_script.c index 506a888ea..a7bd67456 100644 --- a/src/lua_script.c +++ b/src/lua_script.c @@ -852,6 +852,7 @@ void LUA_InvalidateLevel(void) LUA_InvalidateUserdata(&lines[i]); LUA_InvalidateUserdata(&lines[i].tags); LUA_InvalidateUserdata(&lines[i].args); + LUA_InvalidateUserdata(&lines[i].stringargs); LUA_InvalidateUserdata(lines[i].sidenum); } for (i = 0; i < numsides; i++) @@ -894,6 +895,7 @@ void LUA_InvalidateMapthings(void) LUA_InvalidateUserdata(&mapthings[i]); LUA_InvalidateUserdata(&mapthings[i].tags); LUA_InvalidateUserdata(&mapthings[i].args); + LUA_InvalidateUserdata(&mapthings[i].stringargs); } } From 3a49b9519dc0faefa8f6f88d1354ac235b29c6c1 Mon Sep 17 00:00:00 2001 From: Nev3r Date: Tue, 13 Jul 2021 17:55:06 +0200 Subject: [PATCH 092/126] Remove &, since args and stringargs are arrays --- src/lua_script.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/lua_script.c b/src/lua_script.c index a7bd67456..9eb1912b3 100644 --- a/src/lua_script.c +++ b/src/lua_script.c @@ -851,8 +851,8 @@ void LUA_InvalidateLevel(void) { LUA_InvalidateUserdata(&lines[i]); LUA_InvalidateUserdata(&lines[i].tags); - LUA_InvalidateUserdata(&lines[i].args); - LUA_InvalidateUserdata(&lines[i].stringargs); + LUA_InvalidateUserdata(lines[i].args); + LUA_InvalidateUserdata(lines[i].stringargs); LUA_InvalidateUserdata(lines[i].sidenum); } for (i = 0; i < numsides; i++) @@ -894,8 +894,8 @@ void LUA_InvalidateMapthings(void) { LUA_InvalidateUserdata(&mapthings[i]); LUA_InvalidateUserdata(&mapthings[i].tags); - LUA_InvalidateUserdata(&mapthings[i].args); - LUA_InvalidateUserdata(&mapthings[i].stringargs); + LUA_InvalidateUserdata(mapthings[i].args); + LUA_InvalidateUserdata(mapthings[i].stringargs); } } From 1c3a898bbbd5539f666b08d085692a356501f219 Mon Sep 17 00:00:00 2001 From: CobaltBW Date: Thu, 15 Jul 2021 12:53:13 -0700 Subject: [PATCH 093/126] Fixed locked characters being visible in multiplayer select screen --- src/r_skins.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/r_skins.c b/src/r_skins.c index c734b6001..86c0bbc54 100644 --- a/src/r_skins.c +++ b/src/r_skins.c @@ -242,7 +242,7 @@ boolean R_SkinUsable(INT32 playernum, INT32 skinnum) // Force 3. return true; } - if (players[playernum].bot) + if (playernum != -1 && players[playernum].bot) { //Force 4. return true; From 4486ff065af8d6ad3567043af16a58a300aff041 Mon Sep 17 00:00:00 2001 From: CobaltBW Date: Thu, 15 Jul 2021 13:03:26 -0700 Subject: [PATCH 094/126] All remaining player->bot checks modified to rely on BOT_ constants --- src/p_inter.c | 54 +++++++++++++++++++++++++-------------------------- 1 file changed, 27 insertions(+), 27 deletions(-) diff --git a/src/p_inter.c b/src/p_inter.c index 7e43c46a8..21e3bfa3d 100644 --- a/src/p_inter.c +++ b/src/p_inter.c @@ -151,7 +151,7 @@ boolean P_CanPickupItem(player_t *player, boolean weapon) if (!player->mo || player->mo->health <= 0) return false; - if (player->bot && player->bot != 3) + if (player->bot && player->bot != BOT_MPAI) { if (weapon) return false; @@ -178,7 +178,7 @@ void P_DoNightsScore(player_t *player) return; // Don't do any fancy shit for failures. dummymo = P_SpawnMobj(player->mo->x, player->mo->y, player->mo->z+player->mo->height/2, MT_NIGHTSCORE); - if (player->bot && player->bot != 3) + if (player->bot && player->bot != BOT_MPAI) player = &players[consoleplayer]; if (G_IsSpecialStage(gamemap)) // Global link count? Maybe not a good idea... @@ -630,7 +630,7 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck) // ***************************** // // Special Stage Token case MT_TOKEN: - if (player->bot && player->bot != 3) + if (player->bot && player->bot != BOT_MPAI) return; P_AddPlayerScore(player, 1000); @@ -670,7 +670,7 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck) // Emerald Hunt case MT_EMERHUNT: - if (player->bot && player->bot != 3) + if (player->bot && player->bot != BOT_MPAI) return; if (hunt1 == special) @@ -701,7 +701,7 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck) case MT_EMERALD5: case MT_EMERALD6: case MT_EMERALD7: - if (player->bot && player->bot != 3) + if (player->bot && player->bot != BOT_MPAI) return; if (special->threshold) @@ -738,7 +738,7 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck) // Secret emblem thingy case MT_EMBLEM: { - if (demoplayback || (player->bot && player->bot != 3)) + if (demoplayback || (player->bot && player->bot != BOT_MPAI)) return; emblemlocations[special->health-1].collected = true; @@ -751,7 +751,7 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck) // CTF Flags case MT_REDFLAG: case MT_BLUEFLAG: - if (player->bot && player->bot != 3) + if (player->bot && player->bot != BOT_MPAI) return; if (player->powers[pw_flashing] || player->tossdelay) return; @@ -826,7 +826,7 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck) { boolean spec = G_IsSpecialStage(gamemap); boolean cangiveemmy = false; - if (player->bot && player->bot != 3) + if (player->bot && player->bot != BOT_MPAI) return; if (player->exiting) return; @@ -1072,7 +1072,7 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck) } return; case MT_EGGCAPSULE: - if (player->bot && player->bot != 3) + if (player->bot && player->bot != BOT_MPAI) return; // make sure everything is as it should be, THEN take rings from players in special stages @@ -1164,7 +1164,7 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck) } return; case MT_NIGHTSSUPERLOOP: - if ((player->bot && player->bot != 3) || !(player->powers[pw_carry] == CR_NIGHTSMODE)) + if ((player->bot && player->bot != BOT_MPAI) || !(player->powers[pw_carry] == CR_NIGHTSMODE)) return; if (!G_IsSpecialStage(gamemap)) player->powers[pw_nights_superloop] = (UINT16)special->info->speed; @@ -1186,7 +1186,7 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck) } break; case MT_NIGHTSDRILLREFILL: - if ((player->bot && player->bot != 3) || !(player->powers[pw_carry] == CR_NIGHTSMODE)) + if ((player->bot && player->bot != BOT_MPAI) || !(player->powers[pw_carry] == CR_NIGHTSMODE)) return; if (!G_IsSpecialStage(gamemap)) player->drillmeter = special->info->speed; @@ -1208,7 +1208,7 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck) } break; case MT_NIGHTSHELPER: - if ((player->bot && player->bot != 3) || !(player->powers[pw_carry] == CR_NIGHTSMODE)) + if ((player->bot && player->bot != BOT_MPAI) || !(player->powers[pw_carry] == CR_NIGHTSMODE)) return; if (!G_IsSpecialStage(gamemap)) { @@ -1240,7 +1240,7 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck) } break; case MT_NIGHTSEXTRATIME: - if ((player->bot && player->bot != 3) || !(player->powers[pw_carry] == CR_NIGHTSMODE)) + if ((player->bot && player->bot != BOT_MPAI) || !(player->powers[pw_carry] == CR_NIGHTSMODE)) return; if (!G_IsSpecialStage(gamemap)) { @@ -1272,7 +1272,7 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck) } break; case MT_NIGHTSLINKFREEZE: - if ((player->bot && player->bot != 3) || !(player->powers[pw_carry] == CR_NIGHTSMODE)) + if ((player->bot && player->bot != BOT_MPAI) || !(player->powers[pw_carry] == CR_NIGHTSMODE)) return; if (!G_IsSpecialStage(gamemap)) { @@ -1332,7 +1332,7 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck) if (playeringame[i] && players[i].powers[pw_carry] == CR_NIGHTSMODE) players[i].drillmeter += TICRATE/2; } - else if (player->bot && player->bot != 3) + else if (player->bot && player->bot != BOT_MPAI) players[consoleplayer].drillmeter += TICRATE/2; else player->drillmeter += TICRATE/2; @@ -1385,7 +1385,7 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck) thinker_t *th; mobj_t *mo2; - if (player->bot && player->bot != 3) + if (player->bot && player->bot != BOT_MPAI) return; // Initialize my junk @@ -1423,7 +1423,7 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck) return; } case MT_FIREFLOWER: - if (player->bot && player->bot != 3) + if (player->bot && player->bot != BOT_MPAI) return; S_StartSound(toucher, sfx_mario3); @@ -1685,7 +1685,7 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck) return; // Only go in the mouth // Eaten by player! - if ((!player->bot || player->bot == 3) && (player->powers[pw_underwater] && player->powers[pw_underwater] <= 12*TICRATE + 1)) + if ((!player->bot || player->bot == BOT_MPAI) && (player->powers[pw_underwater] && player->powers[pw_underwater] <= 12*TICRATE + 1)) { player->powers[pw_underwater] = underwatertics + 1; P_RestoreMusic(player); @@ -1696,7 +1696,7 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck) if (!player->climbing) { - if (player->bot && player->bot != 3 && toucher->state-states != S_PLAY_GASP) + if (player->bot && player->bot != BOT_MPAI && toucher->state-states != S_PLAY_GASP) S_StartSound(toucher, special->info->deathsound); // Force it to play a sound for bots P_SetPlayerMobjState(toucher, S_PLAY_GASP); P_ResetPlayer(player); @@ -1704,7 +1704,7 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck) toucher->momx = toucher->momy = toucher->momz = 0; - if (player->bot && player->bot != 3) + if (player->bot && player->bot != BOT_MPAI) return; else break; @@ -1736,7 +1736,7 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck) return; case MT_MINECARTSPAWNER: - if (!player->bot && player->bot != 3 && special->fuse <= TICRATE && player->powers[pw_carry] != CR_MINECART && !(player->powers[pw_ignorelatch] & (1<<15))) + if (!player->bot && player->bot != BOT_MPAI && special->fuse <= TICRATE && player->powers[pw_carry] != CR_MINECART && !(player->powers[pw_ignorelatch] & (1<<15))) { mobj_t *mcart = P_SpawnMobj(special->x, special->y, special->z, MT_MINECART); P_SetTarget(&mcart->target, toucher); @@ -1789,7 +1789,7 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck) } return; default: // SOC or script pickup - if (player->bot && player->bot != 3) + if (player->bot && player->bot != BOT_MPAI) return; P_SetTarget(&special->target, toucher); break; @@ -1813,7 +1813,7 @@ void P_TouchStarPost(mobj_t *post, player_t *player, boolean snaptopost) mobj_t *toucher = player->mo; mobj_t *checkbase = snaptopost ? post : toucher; - if (player->bot && player->bot != 3) + if (player->bot && player->bot != BOT_MPAI) return; // In circuit, player must have touched all previous starposts if (circuitmap @@ -2555,7 +2555,7 @@ void P_KillMobj(mobj_t *target, mobj_t *inflictor, mobj_t *source, UINT8 damaget if ((target->player->lives <= 1) && (netgame || multiplayer) && G_GametypeUsesCoopLives() && (cv_cooplives.value == 0)) ; - else if ((!target->player->bot || target->player->bot == 3) && !target->player->spectator && (target->player->lives != INFLIVES) + else if ((!target->player->bot || target->player->bot == BOT_MPAI) && !target->player->spectator && (target->player->lives != INFLIVES) && G_GametypeUsesLives()) { if (!(target->player->pflags & PF_FINISHED)) @@ -3475,7 +3475,7 @@ void P_SpecialStageDamage(player_t *player, mobj_t *inflictor, mobj_t *source) if (inflictor && inflictor->type == MT_LHRT) return; - if (player->powers[pw_shield] || (player->bot && player->bot != 3)) //If One-Hit Shield + if (player->powers[pw_shield] || (player->bot && player->bot != BOT_MPAI)) //If One-Hit Shield { P_RemoveShield(player); S_StartSound(player->mo, sfx_shldls); // Ba-Dum! Shield loss. @@ -3566,7 +3566,7 @@ boolean P_DamageMobj(mobj_t *target, mobj_t *inflictor, mobj_t *source, INT32 da return false; // Make sure that boxes cannot be popped by enemies, red rings, etc. - if (target->flags & MF_MONITOR && ((!source || !source->player || (source->player->bot && source->player->bot != 3)) + if (target->flags & MF_MONITOR && ((!source || !source->player || (source->player->bot && source->player->bot != BOT_MPAI)) || (inflictor && (inflictor->type == MT_REDRING || (inflictor->type >= MT_THROWNBOUNCE && inflictor->type <= MT_THROWNGRENADE))))) return false; } @@ -3701,7 +3701,7 @@ boolean P_DamageMobj(mobj_t *target, mobj_t *inflictor, mobj_t *source, INT32 da } else if (LUA_HookMobjDamage(target, inflictor, source, damage, damagetype)) return true; - else if (player->powers[pw_shield] || (player->bot && player->bot != 3 && !ultimatemode)) //If One-Hit Shield + else if (player->powers[pw_shield] || (player->bot && player->bot != BOT_MPAI && !ultimatemode)) //If One-Hit Shield { P_ShieldDamage(player, inflictor, source, damage, damagetype); damage = 0; From 48514ee88d54a5d36a92c2af0fdd1feb587acf2d Mon Sep 17 00:00:00 2001 From: CobaltBW Date: Thu, 15 Jul 2021 15:04:24 -0700 Subject: [PATCH 095/126] Fixed G_RemovePlayer crash in players.iterate This was done by storing flag-for-removal status as a boolean inside the player struct. Bot players are instead removed at the start of G_Ticker, rather than being removed immediately by G_RemovePlayer. --- src/d_player.h | 1 + src/g_game.c | 22 +++++++++++++++++++++- src/lua_baselib.c | 14 ++------------ src/p_saveg.c | 2 ++ 4 files changed, 26 insertions(+), 13 deletions(-) diff --git a/src/d_player.h b/src/d_player.h index 89776fe5e..a0db1402d 100644 --- a/src/d_player.h +++ b/src/d_player.h @@ -559,6 +559,7 @@ typedef struct player_s boolean spectator; boolean outofcoop; + boolean removing; UINT8 bot; struct player_s *botleader; UINT16 lastbuttons; diff --git a/src/g_game.c b/src/g_game.c index 0643350b6..4d501f526 100644 --- a/src/g_game.c +++ b/src/g_game.c @@ -1545,7 +1545,7 @@ void G_BuildTiccmd(ticcmd_t *cmd, INT32 realtics, UINT8 ssplayer) cmd->forwardmove = (SINT8)(cmd->forwardmove + forward); cmd->sidemove = (SINT8)(cmd->sidemove + side); - //Note: Majority of botstuffs are handled in G_Ticker now. + // Note: Majority of botstuffs are handled in G_Ticker now. if (player->bot == BOT_2PHUMAN) //Player-controlled bot { G_CopyTiccmd(cmd, I_BaseTiccmd2(), 1); // empty, or external driver @@ -2198,6 +2198,23 @@ void G_Ticker(boolean run) UINT32 i; INT32 buf; + // Bot players queued for removal + for (i = MAXPLAYERS-1; i != UINT32_MAX; i--) + { + if (playeringame[i] && players[i].removing) + { + CL_RemovePlayer(i, i); + if (netgame) + { + char kickmsg[256]; + + strcpy(kickmsg, M_GetText("\x82*Bot %s has been removed")); + strcpy(kickmsg, va(kickmsg, player_names[i], i)); + HU_AddChatText(kickmsg, false); + } + } + } + // see also SCR_DisplayMarathonInfo if ((marathonmode & (MA_INIT|MA_INGAME)) == MA_INGAME && gamestate == GS_LEVEL) marathontime++; @@ -2520,6 +2537,7 @@ void G_PlayerReborn(INT32 player, boolean betweenmaps) tic_t quittime; boolean spectator; boolean outofcoop; + boolean removing; INT16 bot; SINT8 pity; INT16 rings; @@ -2536,6 +2554,7 @@ void G_PlayerReborn(INT32 player, boolean betweenmaps) quittime = players[player].quittime; spectator = players[player].spectator; outofcoop = players[player].outofcoop; + removing = players[player].removing; pflags = (players[player].pflags & (PF_FLIPCAM|PF_ANALOGMODE|PF_DIRECTIONCHAR|PF_AUTOBRAKE|PF_TAGIT|PF_GAMETYPEOVER)); playerangleturn = players[player].angleturn; oldrelangleturn = players[player].oldrelangleturn; @@ -2612,6 +2631,7 @@ void G_PlayerReborn(INT32 player, boolean betweenmaps) p->quittime = quittime; p->spectator = spectator; p->outofcoop = outofcoop; + p->removing = removing; p->angleturn = playerangleturn; p->oldrelangleturn = oldrelangleturn; diff --git a/src/lua_baselib.c b/src/lua_baselib.c index 9946a9fd3..9e01a98c0 100644 --- a/src/lua_baselib.c +++ b/src/lua_baselib.c @@ -3482,24 +3482,14 @@ static int lib_gAddPlayer(lua_State *L) static int lib_gRemovePlayer(lua_State *L) { UINT8 pnum = -1; - //const char *kickreason = luaL_checkstring(L, 2); if (!lua_isnoneornil(L, 1)) pnum = luaL_checkinteger(L, 1); if (&players[pnum]) { - if (players[pnum].bot != BOT_NONE) + if (players[pnum].bot != BOT_NONE && players[pnum].removing == false) { -// CL_RemovePlayer(pnum, *kickreason); - CL_RemovePlayer(pnum, pnum); - if (netgame) - { - char kickmsg[256]; - - strcpy(kickmsg, M_GetText("\x82*Bot %s has been removed")); - strcpy(kickmsg, va(kickmsg, player_names[pnum], pnum)); - HU_AddChatText(kickmsg, false); - } + players[pnum].removing = true; // This function can be run in players.iterate(), which isn't equipped to deal with players being removed mid-process. Instead we'll remove the player at the beginning of the next ticframe. lua_pushboolean(L, true); return 1; } diff --git a/src/p_saveg.c b/src/p_saveg.c index 7e9d7b6d4..1270064c0 100644 --- a/src/p_saveg.c +++ b/src/p_saveg.c @@ -201,6 +201,7 @@ static void P_NetArchivePlayers(void) WRITEUINT8(save_p, players[i].botmem.lastBlocked); WRITEUINT8(save_p, players[i].botmem.catchup_tics); WRITEUINT8(save_p, players[i].botmem.thinkstate); + WRITEUINT8(save_p, players[i].removing); WRITEUINT8(save_p, players[i].blocked); WRITEUINT16(save_p, players[i].lastbuttons); @@ -428,6 +429,7 @@ static void P_NetUnArchivePlayers(void) players[i].botmem.lastBlocked = READUINT8(save_p); players[i].botmem.catchup_tics = READUINT8(save_p); players[i].botmem.thinkstate = READUINT8(save_p); + players[i].removing = READUINT8(save_p); players[i].blocked = READUINT8(save_p); players[i].lastbuttons = READUINT16(save_p); From 22f42efb61e242a75a017712d6b753149c3a91ce Mon Sep 17 00:00:00 2001 From: CobaltBW Date: Thu, 15 Jul 2021 15:09:02 -0700 Subject: [PATCH 096/126] Cleaned up leftover comments --- src/b_bot.c | 4 ---- src/g_game.c | 2 +- src/p_mobj.c | 1 - src/p_user.c | 29 ++++++----------------------- 4 files changed, 7 insertions(+), 29 deletions(-) diff --git a/src/b_bot.c b/src/b_bot.c index c61e56f5f..6f4b0ce15 100644 --- a/src/b_bot.c +++ b/src/b_bot.c @@ -102,7 +102,6 @@ static void B_BuildTailsTiccmd(mobj_t *sonic, mobj_t *tails, ticcmd_t *cmd) if (tails->player->powers[pw_carry] == CR_MACESPIN || tails->player->powers[pw_carry] == CR_GENERIC) { boolean isrelevant = (sonic->player->powers[pw_carry] == CR_MACESPIN || sonic->player->powers[pw_carry] == CR_GENERIC); - //dist = P_AproxDistance(tails->x-sonic->x, tails->y-sonic->y); //! This is totally redundant. if (sonic->player->cmd.buttons & BT_JUMP && (sonic->player->pflags & PF_JUMPED) && isrelevant) cmd->buttons |= BT_JUMP; if (isrelevant) @@ -402,8 +401,6 @@ void B_KeysToTiccmd(mobj_t *mo, ticcmd_t *cmd, boolean forward, boolean backward { player_t *player = mo->player; // don't try to do stuff if your sonic is in a minecart or something - //if (players[consoleplayer].powers[pw_carry] && players[consoleplayer].powers[pw_carry] != CR_PLAYER) - //!!! if (&player->botleader && player->botleader->powers[pw_carry] && player->botleader->powers[pw_carry] != CR_PLAYER) return; // Turn the virtual keypresses into ticcmd_t. @@ -576,7 +573,6 @@ void B_RespawnBot(INT32 playernum) player->powers[pw_spacetime] = sonic->player->powers[pw_spacetime]; player->powers[pw_gravityboots] = sonic->player->powers[pw_gravityboots]; player->powers[pw_nocontrol] = sonic->player->powers[pw_nocontrol]; - //!!! Nuke the speed equivalencies player->pflags |= PF_AUTOBRAKE|(sonic->player->pflags & PF_DIRECTIONCHAR); P_TeleportMove(tails, x, y, z); diff --git a/src/g_game.c b/src/g_game.c index 4d501f526..8cdeaf079 100644 --- a/src/g_game.c +++ b/src/g_game.c @@ -3025,7 +3025,7 @@ void G_DoReborn(INT32 playernum) // Make sure objectplace is OFF when you first start the level! OP_ResetObjectplace(); - //! Tailsbot + // Tailsbot if (player->bot == BOT_2PAI || player->bot == BOT_2PHUMAN) { // Bots respawn next to their master. mobj_t *oldmo = NULL; diff --git a/src/p_mobj.c b/src/p_mobj.c index 4924ec053..8af7ab0d0 100644 --- a/src/p_mobj.c +++ b/src/p_mobj.c @@ -1839,7 +1839,6 @@ void P_XYMovement(mobj_t *mo) // blocked move moved = false; - //!!! if (player) B_MoveBlocked(player); diff --git a/src/p_user.c b/src/p_user.c index dadc23193..b3535623c 100644 --- a/src/p_user.c +++ b/src/p_user.c @@ -776,7 +776,7 @@ void P_NightserizePlayer(player_t *player, INT32 nighttime) { UINT8 oldmare, oldmarelap, oldmarebonuslap; - //! Bots can't be NiGHTSerized, silly!1 :P + // Bots can't be NiGHTSerized, silly!1 :P if (player->bot == BOT_2PAI || player->bot == BOT_2PHUMAN) return; @@ -1188,7 +1188,7 @@ void P_GivePlayerRings(player_t *player, INT32 num_rings) { if (!player) return; - //! + if ((player->bot == BOT_2PAI || player->bot == BOT_2PHUMAN) && player->botleader) player = player->botleader; @@ -5965,23 +5965,6 @@ static void P_3dMovement(player_t *player) acceleration = 96 + (FixedDiv(player->speed, player->mo->scale)>>FRACBITS) * 40; topspeed = normalspd; } - //! Kill this! - /* else if (player->bot == BOT_2PAI || player->bot == BOT_2PHUMAN) - { // Bot steals player 1's stats - normalspd = FixedMul(players[consoleplayer].normalspeed, player->mo->scale); - thrustfactor = players[consoleplayer].thrustfactor; - acceleration = players[consoleplayer].accelstart + (FixedDiv(player->speed, player->mo->scale)>>FRACBITS) * players[consoleplayer].acceleration; - - if (player->powers[pw_tailsfly]) - topspeed = normalspd/2; - else if (player->mo->eflags & (MFE_UNDERWATER|MFE_GOOWATER)) - { - topspeed = normalspd/2; - acceleration = 2*acceleration/3; - } - else - topspeed = normalspd; - } */ else { if (player->powers[pw_super] || player->powers[pw_sneakers]) @@ -11488,7 +11471,7 @@ void P_PlayerThink(player_t *player) I_Error("p_playerthink: players[%s].mo == NULL", sizeu1(playeri)); #endif - //! Reset terrain blocked status for this frame + // Reset terrain blocked status for this frame player->blocked = false; // todo: Figure out what is actually causing these problems in the first place... @@ -11641,7 +11624,7 @@ void P_PlayerThink(player_t *player) INT32 i; for (i = 0; i < MAXPLAYERS; i++) - { //! + { if (!playeringame[i] || players[i].spectator || players[i].bot == BOT_2PAI || players[i].bot == BOT_2PHUMAN) continue; if (players[i].lives <= 0) @@ -11673,7 +11656,7 @@ void P_PlayerThink(player_t *player) INT32 i, total = 0, exiting = 0; for (i = 0; i < MAXPLAYERS; i++) - { //! + { if (!playeringame[i] || players[i].spectator || players[i].bot == BOT_2PAI || players[i].bot == BOT_2PHUMAN) continue; if (players[i].quittime > 30 * TICRATE) @@ -12614,7 +12597,7 @@ void P_PlayerAfterThink(player_t *player) player->mo->momy = tails->momy; player->mo->momz = tails->momz; } - //! + if (G_CoopGametype() && tails->player && tails->player->bot != BOT_2PAI) { player->mo->angle = tails->angle; From 95359fef5142d1dce33ebce35b18d13846e2ca56 Mon Sep 17 00:00:00 2001 From: CobaltBW Date: Thu, 15 Jul 2021 15:19:47 -0700 Subject: [PATCH 097/126] Amendment to G_RemovePlayer to preserve lua error handlers --- src/lua_baselib.c | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/src/lua_baselib.c b/src/lua_baselib.c index 9e01a98c0..f287fb78c 100644 --- a/src/lua_baselib.c +++ b/src/lua_baselib.c @@ -3482,20 +3482,25 @@ static int lib_gAddPlayer(lua_State *L) static int lib_gRemovePlayer(lua_State *L) { UINT8 pnum = -1; - if (!lua_isnoneornil(L, 1)) pnum = luaL_checkinteger(L, 1); - if (&players[pnum]) + else // No argument + return luaL_error(L, "argument #1 not given (expected number)"); + if (pnum >= MAXPLAYERS) // Out of range + return luaL_error(L, "playernum %d out of range (0 - %d)", pnum, MAXPLAYERS-1); + if (playeringame[pnum]) // Found player { - if (players[pnum].bot != BOT_NONE && players[pnum].removing == false) + if (players[pnum].bot == BOT_NONE) // Can't remove clients. + return luaL_error(L, "G_RemovePlayer can only be used on players with a bot value other than BOT_NONE."); + else { - players[pnum].removing = true; // This function can be run in players.iterate(), which isn't equipped to deal with players being removed mid-process. Instead we'll remove the player at the beginning of the next ticframe. + players[pnum].removing = true; lua_pushboolean(L, true); return 1; } } - lua_pushboolean(L, false); - return 1; + // Fell through. Invalid player + return LUA_ErrInvalid(L, "player_t"); } From 71f905f95bd4fe62dbca87208cf71b7309f12de1 Mon Sep 17 00:00:00 2001 From: SteelT Date: Fri, 16 Jul 2021 15:26:09 -0400 Subject: [PATCH 098/126] Fix gme support being effectively disabled I found this easier than trying to adjust the makefile, as it uses the same thing to automatically generate the various NO* compile options. --- src/CMakeLists.txt | 2 +- src/sdl/mixer_sound.c | 46 +++++++++++++++++++++---------------------- 2 files changed, 24 insertions(+), 24 deletions(-) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 721cd6dca..ae93aac37 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -60,7 +60,7 @@ if(${SRB2_CONFIG_HAVE_GME}) endif() if(${GME_FOUND}) set(SRB2_HAVE_GME ON) - target_compile_definitions(SRB2SDL2 PRIVATE -DHAVE_LIBGME) + target_compile_definitions(SRB2SDL2 PRIVATE -DHAVE_GME) else() message(WARNING "You have specified that GME is available but it was not found.") endif() diff --git a/src/sdl/mixer_sound.c b/src/sdl/mixer_sound.c index 2f1a87266..35a79acc0 100644 --- a/src/sdl/mixer_sound.c +++ b/src/sdl/mixer_sound.c @@ -9,7 +9,7 @@ /// \file /// \brief SDL Mixer interface for sound -#ifdef HAVE_LIBGME +#ifdef HAVE_GME #ifdef HAVE_ZLIB #ifndef _MSC_VER #ifndef _LARGEFILE64_SOURCE @@ -27,7 +27,7 @@ #include #endif // HAVE_ZLIB -#endif // HAVE_LIBGME +#endif // HAVE_GME #include "../doomdef.h" #include "../doomstat.h" // menuactive @@ -73,11 +73,11 @@ #define MUS_MODPLUG MUS_MODPLUG_UNUSED #endif -#ifdef HAVE_LIBGME +#ifdef HAVE_GME #include "gme/gme.h" #define GME_TREBLE 5.0f #define GME_BASS 1.0f -#endif // HAVE_LIBGME +#endif // HAVE_GME static UINT16 BUFFERSIZE = 2048; static UINT16 SAMPLERATE = 44100; @@ -110,7 +110,7 @@ static INT32 fading_id; static void (*fading_callback)(void); static boolean fading_nocleanup; -#ifdef HAVE_LIBGME +#ifdef HAVE_GME static Music_Emu *gme; static UINT16 current_track; #endif @@ -220,7 +220,7 @@ static void var_cleanup(void) internal_volume = 100; } -#if defined (HAVE_LIBGME) && defined (HAVE_ZLIB) +#if defined (HAVE_GME) && defined (HAVE_ZLIB) static const char* get_zlib_error(int zErr) { switch (zErr) @@ -318,7 +318,7 @@ void I_ShutdownSound(void) SDL_QuitSubSystem(SDL_INIT_AUDIO); -#ifdef HAVE_LIBGME +#ifdef HAVE_GME if (gme) gme_delete(gme); #endif @@ -453,7 +453,7 @@ void *I_GetSfx(sfxinfo_t *sfx) void *lump; Mix_Chunk *chunk; SDL_RWops *rw; -#ifdef HAVE_LIBGME +#ifdef HAVE_GME Music_Emu *emu; gme_info_t *info; #endif @@ -473,7 +473,7 @@ void *I_GetSfx(sfxinfo_t *sfx) } // Not a doom sound? Try something else. -#ifdef HAVE_LIBGME +#ifdef HAVE_GME // VGZ format if (((UINT8 *)lump)[0] == 0x1F && ((UINT8 *)lump)[1] == 0x8B) @@ -729,7 +729,7 @@ static UINT32 music_fade(UINT32 interval, void *param) } } -#ifdef HAVE_LIBGME +#ifdef HAVE_GME static void mix_gme(void *udata, Uint8 *stream, int len) { int i; @@ -797,7 +797,7 @@ void I_ShutdownMusic(void) musictype_t I_SongType(void) { -#ifdef HAVE_LIBGME +#ifdef HAVE_GME if (gme) return MU_GME; else @@ -828,7 +828,7 @@ musictype_t I_SongType(void) boolean I_SongPlaying(void) { return ( -#ifdef HAVE_LIBGME +#ifdef HAVE_GME (I_SongType() == MU_GME && gme) || #endif #ifdef HAVE_OPENMPT @@ -851,7 +851,7 @@ boolean I_SetSongSpeed(float speed) { if (speed > 250.0f) speed = 250.0f; //limit speed up to 250x -#ifdef HAVE_LIBGME +#ifdef HAVE_GME if (gme) { SDL_LockAudio(); @@ -893,7 +893,7 @@ UINT32 I_GetSongLength(void) { INT32 length; -#ifdef HAVE_LIBGME +#ifdef HAVE_GME if (gme) { gme_info_t *info; @@ -963,7 +963,7 @@ boolean I_SetSongLoopPoint(UINT32 looppoint) UINT32 I_GetSongLoopPoint(void) { -#ifdef HAVE_LIBGME +#ifdef HAVE_GME if (gme) { INT32 looppoint; @@ -992,7 +992,7 @@ UINT32 I_GetSongLoopPoint(void) boolean I_SetSongPosition(UINT32 position) { UINT32 length; -#ifdef HAVE_LIBGME +#ifdef HAVE_GME if (gme) { // this is unstable, so fail silently @@ -1055,7 +1055,7 @@ boolean I_SetSongPosition(UINT32 position) UINT32 I_GetSongPosition(void) { -#ifdef HAVE_LIBGME +#ifdef HAVE_GME if (gme) { INT32 position = gme_tell(gme); @@ -1124,7 +1124,7 @@ boolean I_LoadSong(char *data, size_t len) SDL_RWops *rw; if (music -#ifdef HAVE_LIBGME +#ifdef HAVE_GME || gme #endif #ifdef HAVE_OPENMPT @@ -1136,7 +1136,7 @@ boolean I_LoadSong(char *data, size_t len) // always do this whether or not a music already exists var_cleanup(); -#ifdef HAVE_LIBGME +#ifdef HAVE_GME if ((UINT8)data[0] == 0x1F && (UINT8)data[1] == 0x8B) { @@ -1271,7 +1271,7 @@ void I_UnloadSong(void) { I_StopSong(); -#ifdef HAVE_LIBGME +#ifdef HAVE_GME if (gme) { gme_delete(gme); @@ -1294,7 +1294,7 @@ void I_UnloadSong(void) boolean I_PlaySong(boolean looping) { -#ifdef HAVE_LIBGME +#ifdef HAVE_GME if (gme) { gme_equalizer_t eq = {GME_TREBLE, GME_BASS, 0,0,0,0,0,0,0,0}; @@ -1360,7 +1360,7 @@ void I_StopSong(void) if (!fading_nocleanup) I_StopFadingSong(); -#ifdef HAVE_LIBGME +#ifdef HAVE_GME if (gme) { Mix_HookMusic(NULL, NULL); @@ -1433,7 +1433,7 @@ void I_SetMusicVolume(UINT8 volume) boolean I_SetSongTrack(int track) { -#ifdef HAVE_LIBGME +#ifdef HAVE_GME // If the specified track is within the number of tracks playing, then change it if (gme) { From 58db5a6904943ef944f76ca45d260a1bc123463f Mon Sep 17 00:00:00 2001 From: Radicalicious Date: Wed, 28 Jul 2021 15:42:44 +0000 Subject: [PATCH 099/126] Fix P_PlayerInPain crash. --- src/p_user.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/p_user.c b/src/p_user.c index c5f919c78..f3ebc7dff 100644 --- a/src/p_user.c +++ b/src/p_user.c @@ -969,6 +969,9 @@ pflags_t P_GetJumpFlags(player_t *player) // boolean P_PlayerInPain(player_t *player) { + // If the player doesn't have a mobj, it can't be in pain. + if !(player->mo) + return false; // no silly, sliding isn't pain if (!(player->pflags & PF_SLIDING) && player->mo->state == &states[player->mo->info->painstate] && player->powers[pw_flashing]) return true; From d50e3bff70f1ac28fc74e85f8e93642a979f0b52 Mon Sep 17 00:00:00 2001 From: Radicalicious Date: Thu, 29 Jul 2021 15:55:19 +0000 Subject: [PATCH 100/126] ACTUALLY fix the issue. --- 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 f3ebc7dff..740688b45 100644 --- a/src/p_user.c +++ b/src/p_user.c @@ -970,7 +970,7 @@ pflags_t P_GetJumpFlags(player_t *player) boolean P_PlayerInPain(player_t *player) { // If the player doesn't have a mobj, it can't be in pain. - if !(player->mo) + if (!player->mo) return false; // no silly, sliding isn't pain if (!(player->pflags & PF_SLIDING) && player->mo->state == &states[player->mo->info->painstate] && player->powers[pw_flashing]) From 7dddc631d798469725e40bd3fbc1a2617f4982ee Mon Sep 17 00:00:00 2001 From: Monster Iestyn Date: Sat, 31 Jul 2021 21:14:48 +0100 Subject: [PATCH 101/126] P_ZMovement: add a P_MobjWasRemoved check after P_CheckPosition, so we can bail out if the mobj was removed (by Lua most likely) --- src/p_mobj.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/p_mobj.c b/src/p_mobj.c index 10220fff6..3bb7ac58d 100644 --- a/src/p_mobj.c +++ b/src/p_mobj.c @@ -2549,6 +2549,10 @@ boolean P_ZMovement(mobj_t *mo) } P_CheckPosition(mo, mo->x, mo->y); // Sets mo->standingslope correctly + + if (P_MobjWasRemoved(mobj)) // mobjs can be removed by P_CheckPosition -- Monster Iestyn 31/07/21 + return false; + if (((mo->eflags & MFE_VERTICALFLIP) ? tmceilingslope : tmfloorslope) && (mo->type != MT_STEAM)) { mo->standingslope = (mo->eflags & MFE_VERTICALFLIP) ? tmceilingslope : tmfloorslope; From df99cde40ffd1163cfec164e72f7b5d3b31261cb Mon Sep 17 00:00:00 2001 From: Monster Iestyn Date: Sat, 31 Jul 2021 22:11:44 +0100 Subject: [PATCH 102/126] mo not mobj! --- src/p_mobj.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/p_mobj.c b/src/p_mobj.c index 3bb7ac58d..da7385be5 100644 --- a/src/p_mobj.c +++ b/src/p_mobj.c @@ -2550,7 +2550,7 @@ boolean P_ZMovement(mobj_t *mo) P_CheckPosition(mo, mo->x, mo->y); // Sets mo->standingslope correctly - if (P_MobjWasRemoved(mobj)) // mobjs can be removed by P_CheckPosition -- Monster Iestyn 31/07/21 + if (P_MobjWasRemoved(mo)) // mobjs can be removed by P_CheckPosition -- Monster Iestyn 31/07/21 return false; if (((mo->eflags & MFE_VERTICALFLIP) ? tmceilingslope : tmfloorslope) && (mo->type != MT_STEAM)) From 2d218859ff4f879ee13fbf3e45d6dc890be9012a Mon Sep 17 00:00:00 2001 From: SMS Alfredo <65426124+SMS-Alfredo@users.noreply.github.com> Date: Fri, 6 Aug 2021 23:25:19 -0500 Subject: [PATCH 103/126] Give coins a drop shadow --- src/p_mobj.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/p_mobj.c b/src/p_mobj.c index da7385be5..c7e0a7909 100644 --- a/src/p_mobj.c +++ b/src/p_mobj.c @@ -10391,6 +10391,9 @@ static fixed_t P_DefaultMobjShadowScale (mobj_t *thing) case MT_RING: case MT_FLINGRING: + + case MT_COIN: + case MT_FLINGCOIN: case MT_BLUESPHERE: case MT_FLINGBLUESPHERE: From d1e8b05749447da3c7944d852b0484c6c9dc3842 Mon Sep 17 00:00:00 2001 From: LJ Sonic Date: Sun, 8 Aug 2021 19:43:42 +0200 Subject: [PATCH 104/126] Don't call map load trigger linedefs when joining or reloading gamestate --- src/p_spec.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/p_spec.c b/src/p_spec.c index 4b566acfb..e5e546b7a 100644 --- a/src/p_spec.c +++ b/src/p_spec.c @@ -7102,7 +7102,8 @@ void P_SpawnSpecials(boolean fromnetsave) } } - P_RunLevelLoadExecutors(); + if (!fromnetsave) + P_RunLevelLoadExecutors(); } /** Adds 3Dfloors as appropriate based on a common control linedef. From a9b35a6f7a68d7dfd2e0df907b628f8bc3135a1f Mon Sep 17 00:00:00 2001 From: LJ Sonic Date: Tue, 10 Aug 2021 00:15:44 +0200 Subject: [PATCH 105/126] Fix incorrect error message during Lua archiving --- src/lua_script.c | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/src/lua_script.c b/src/lua_script.c index 9eb1912b3..25e0a8f37 100644 --- a/src/lua_script.c +++ b/src/lua_script.c @@ -1384,21 +1384,13 @@ static void ArchiveTables(void) // Write key e = ArchiveValue(TABLESINDEX, -2); // key should be either a number or a string, ArchiveValue can handle this. if (e == 2) // invalid key type (function, thread, lightuserdata, or anything we don't recognise) - { - lua_pushvalue(gL, -2); - CONS_Alert(CONS_ERROR, "Index '%s' (%s) of table %d could not be archived!\n", lua_tostring(gL, -1), luaL_typename(gL, -1), i); - lua_pop(gL, 1); - } + CONS_Alert(CONS_ERROR, "Index '%s' (%s) of table %d could not be archived!\n", lua_tostring(gL, -2), luaL_typename(gL, -2), i); // Write value e = ArchiveValue(TABLESINDEX, -1); if (e == 1) n++; // the table contained a new table we'll have to archive. :( else if (e == 2) // invalid value type - { - lua_pushvalue(gL, -2); CONS_Alert(CONS_ERROR, "Type of value for table %d entry '%s' (%s) could not be archived!\n", i, lua_tostring(gL, -1), luaL_typename(gL, -1)); - lua_pop(gL, 1); - } lua_pop(gL, 1); } From 6403a38c72cfdb57a25ccd380037034eb831fcff Mon Sep 17 00:00:00 2001 From: LJ Sonic Date: Tue, 10 Aug 2021 01:48:26 +0200 Subject: [PATCH 106/126] Fix again --- src/lua_script.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lua_script.c b/src/lua_script.c index 25e0a8f37..bf262c065 100644 --- a/src/lua_script.c +++ b/src/lua_script.c @@ -1390,7 +1390,7 @@ static void ArchiveTables(void) if (e == 1) n++; // the table contained a new table we'll have to archive. :( else if (e == 2) // invalid value type - CONS_Alert(CONS_ERROR, "Type of value for table %d entry '%s' (%s) could not be archived!\n", i, lua_tostring(gL, -1), luaL_typename(gL, -1)); + CONS_Alert(CONS_ERROR, "Type of value for table %d entry '%s' (%s) could not be archived!\n", i, lua_tostring(gL, -2), luaL_typename(gL, -1)); lua_pop(gL, 1); } From 8adf4b672acc12b6726192be980f18e30e70624f Mon Sep 17 00:00:00 2001 From: LJ Sonic Date: Fri, 13 Aug 2021 21:58:34 +0200 Subject: [PATCH 107/126] Put Lua input library in its own namespace --- src/lua_inputlib.c | 30 ++++++++++++++---------------- 1 file changed, 14 insertions(+), 16 deletions(-) diff --git a/src/lua_inputlib.c b/src/lua_inputlib.c index 71eb1033f..7ad265eff 100644 --- a/src/lua_inputlib.c +++ b/src/lua_inputlib.c @@ -127,19 +127,19 @@ static int lib_getCursorPosition(lua_State *L) } static luaL_Reg lib[] = { - {"G_GameControlDown", lib_gameControlDown}, - {"G_GameControl2Down", lib_gameControl2Down}, - {"G_GameControlToKeyNum", lib_gameControlToKeyNum}, - {"G_GameControl2ToKeyNum", lib_gameControl2ToKeyNum}, - {"G_JoyAxis", lib_joyAxis}, - {"G_Joy2Axis", lib_joy2Axis}, - {"G_KeyNumToString", lib_keyNumToString}, - {"G_KeyStringToNum", lib_keyStringToNum}, - {"HU_KeyNumPrintable", lib_keyNumPrintable}, - {"HU_ShiftKeyNum", lib_shiftKeyNum}, - {"I_GetMouseGrab", lib_getMouseGrab}, - {"I_SetMouseGrab", lib_setMouseGrab}, - {"I_GetCursorPosition", lib_getCursorPosition}, + {"gameControlDown", lib_gameControlDown}, + {"gameControl2Down", lib_gameControl2Down}, + {"gameControlToKeyNum", lib_gameControlToKeyNum}, + {"gameControl2ToKeyNum", lib_gameControl2ToKeyNum}, + {"joyAxis", lib_joyAxis}, + {"joy2Axis", lib_joy2Axis}, + {"keyNumToString", lib_keyNumToString}, + {"keyStringToNum", lib_keyStringToNum}, + {"keyNumPrintable", lib_keyNumPrintable}, + {"shiftKeyNum", lib_shiftKeyNum}, + {"getMouseGrab", lib_getMouseGrab}, + {"setMouseGrab", lib_setMouseGrab}, + {"getCursorPosition", lib_getCursorPosition}, {NULL, NULL} }; @@ -235,8 +235,6 @@ int LUA_InputLib(lua_State *L) lua_setfield(L, -2, "__len"); lua_pop(L, 1); - // Set global functions - lua_pushvalue(L, LUA_GLOBALSINDEX); - luaL_register(L, NULL, lib); + luaL_register(L, "input", lib); return 0; } From 38e82e55fcf39ccd0e4d2316839e502958ba76cf Mon Sep 17 00:00:00 2001 From: LJ Sonic Date: Sat, 14 Aug 2021 20:33:20 +0200 Subject: [PATCH 108/126] Add a "repeated" field to event_t --- src/d_event.h | 1 + src/sdl/i_system.c | 4 ++-- src/sdl/i_video.c | 1 + 3 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/d_event.h b/src/d_event.h index 1fd2e3824..70b55c328 100644 --- a/src/d_event.h +++ b/src/d_event.h @@ -36,6 +36,7 @@ typedef struct INT32 data1; // keys / mouse/joystick buttons INT32 data2; // mouse/joystick x move INT32 data3; // mouse/joystick y move + boolean repeated; // key repeat } event_t; // diff --git a/src/sdl/i_system.c b/src/sdl/i_system.c index d68e3e435..1594c8d61 100644 --- a/src/sdl/i_system.c +++ b/src/sdl/i_system.c @@ -1012,7 +1012,7 @@ void I_ShutdownJoystick(void) void I_GetJoystickEvents(void) { - static event_t event = {0,0,0,0}; + static event_t event = {0,0,0,0,false}; INT32 i = 0; UINT64 joyhats = 0; #if 0 @@ -1282,7 +1282,7 @@ void I_ShutdownJoystick2(void) void I_GetJoystick2Events(void) { - static event_t event = {0,0,0,0}; + static event_t event = {0,0,0,0,false}; INT32 i = 0; UINT64 joyhats = 0; #if 0 diff --git a/src/sdl/i_video.c b/src/sdl/i_video.c index 819589eaf..1f4b866c1 100644 --- a/src/sdl/i_video.c +++ b/src/sdl/i_video.c @@ -664,6 +664,7 @@ static void Impl_HandleKeyboardEvent(SDL_KeyboardEvent evt, Uint32 type) return; } event.data1 = Impl_SDL_Scancode_To_Keycode(evt.keysym.scancode); + event.repeated = (evt.repeat != 0); if (event.data1) D_PostEvent(&event); } From 5b949a6751c34a939a3aa6cef3cdb7d0dfc2f181 Mon Sep 17 00:00:00 2001 From: LJ Sonic Date: Sat, 14 Aug 2021 20:33:42 +0200 Subject: [PATCH 109/126] Expose keyevent_t to Lua --- src/lua_inputlib.c | 28 ++++++++++++++++++++++++++++ src/lua_libs.h | 1 + 2 files changed, 29 insertions(+) diff --git a/src/lua_inputlib.c b/src/lua_inputlib.c index 7ad265eff..01ad9af3c 100644 --- a/src/lua_inputlib.c +++ b/src/lua_inputlib.c @@ -172,6 +172,29 @@ static int lib_lenGameKeyDown(lua_State *L) return 1; } +/////////////// +// KEY EVENT // +/////////////// + +static int keyevent_get(lua_State *L) +{ + event_t *event = *((event_t **)luaL_checkudata(L, 1, META_KEYEVENT)); + const char *field = luaL_checkstring(L, 2); + + I_Assert(event != NULL); + + if (fastcmp(field,"name")) + lua_pushstring(L, G_KeyNumToString(event->data1)); + else if (fastcmp(field,"num")) + lua_pushinteger(L, event->data1); + else if (fastcmp(field,"repeated")) + lua_pushboolean(L, event->repeated); + else + return luaL_error(L, "keyevent_t has no field named %s", field); + + return 1; +} + /////////// // MOUSE // /////////// @@ -227,6 +250,11 @@ int LUA_InputLib(lua_State *L) lua_setmetatable(L, -2); lua_setglobal(L, "gamekeydown"); + luaL_newmetatable(L, META_KEYEVENT); + lua_pushcfunction(L, keyevent_get); + lua_setfield(L, -2, "__index"); + lua_pop(L, 1); + luaL_newmetatable(L, META_MOUSE); lua_pushcfunction(L, mouse_get); lua_setfield(L, -2, "__index"); diff --git a/src/lua_libs.h b/src/lua_libs.h index 668eb99b0..de174283c 100644 --- a/src/lua_libs.h +++ b/src/lua_libs.h @@ -88,6 +88,7 @@ extern lua_State *gL; #define META_LUABANKS "LUABANKS[]*" +#define META_KEYEVENT "KEYEVENT_T*" #define META_MOUSE "MOUSE_T*" boolean luaL_checkboolean(lua_State *L, int narg); From c2907b89f703a4e09cf236907b9be28c28242b54 Mon Sep 17 00:00:00 2001 From: LJ Sonic Date: Sat, 14 Aug 2021 20:34:59 +0200 Subject: [PATCH 110/126] Pass a keyevent_t userdatum to key hooks --- src/g_game.c | 18 +++++++++++++++--- src/lua_hook.h | 3 ++- src/lua_hooklib.c | 4 ++-- 3 files changed, 19 insertions(+), 6 deletions(-) diff --git a/src/g_game.c b/src/g_game.c index 13fdab877..a35d89aa1 100644 --- a/src/g_game.c +++ b/src/g_game.c @@ -45,6 +45,7 @@ #include "lua_hook.h" #include "b_bot.h" #include "m_cond.h" // condition sets +#include "lua_script.h" #include "lua_hud.h" @@ -2194,8 +2195,20 @@ boolean G_Responder(event_t *ev) // boolean G_LuaResponder(event_t *ev) { - return (ev->type == ev_keydown && LUA_HookKey(ev->data1, HOOK(KeyDown))) || - (ev->type == ev_keyup && LUA_HookKey(ev->data1, HOOK(KeyUp))); + boolean cancelled = false; + + if (ev->type == ev_keydown) + { + cancelled = LUA_HookKey(ev, HOOK(KeyDown)); + LUA_InvalidateUserdata(ev); + } + else if (ev->type == ev_keyup) + { + cancelled = LUA_HookKey(ev, HOOK(KeyUp)); + LUA_InvalidateUserdata(ev); + } + + return cancelled; } // @@ -5240,4 +5253,3 @@ INT32 G_TicsToMilliseconds(tic_t tics) { return (INT32)((tics%TICRATE) * (1000.00f/TICRATE)); } - diff --git a/src/lua_hook.h b/src/lua_hook.h index 223b83c61..55f3bb817 100644 --- a/src/lua_hook.h +++ b/src/lua_hook.h @@ -13,6 +13,7 @@ #include "r_defs.h" #include "d_player.h" #include "s_sound.h" +#include "d_event.h" /* Do you know what an 'X Macro' is? Such a macro is called over each element of @@ -107,7 +108,7 @@ void LUA_HookInt(INT32 integer, int hook); void LUA_HookBool(boolean value, int hook); int LUA_HookPlayer(player_t *, int hook); int LUA_HookTiccmd(player_t *, ticcmd_t *, int hook); -int LUA_HookKey(INT32 keycode, int hook); // Hooks for key events +int LUA_HookKey(event_t *event, int hook); // Hooks for key events void LUA_HookThinkFrame(void); int LUA_HookMobjLineCollide(mobj_t *, line_t *); diff --git a/src/lua_hooklib.c b/src/lua_hooklib.c index d1b0d3bdd..1e0462126 100644 --- a/src/lua_hooklib.c +++ b/src/lua_hooklib.c @@ -588,12 +588,12 @@ int LUA_HookTiccmd(player_t *player, ticcmd_t *cmd, int hook_type) return hook.status; } -int LUA_HookKey(INT32 keycode, int hook_type) +int LUA_HookKey(event_t *event, int hook_type) { Hook_State hook; if (prepare_hook(&hook, false, hook_type)) { - lua_pushinteger(gL, keycode); + LUA_PushUserdata(gL, event, META_KEYEVENT); call_hooks(&hook, 1, 1, res_true); } return hook.status; From 5bc0ce7a62c816e33e6d2a7ddc4b4b1360fd9df4 Mon Sep 17 00:00:00 2001 From: LJ Sonic Date: Sat, 14 Aug 2021 23:42:39 +0200 Subject: [PATCH 111/126] Give fields in event_t better names --- src/am_map.c | 6 +- src/console.c | 4 +- src/d_event.h | 6 +- src/d_main.c | 14 +-- src/f_finale.c | 6 +- src/g_game.c | 20 ++--- src/g_input.c | 32 +++---- src/hu_stuff.c | 18 ++-- src/lua_inputlib.c | 4 +- src/m_cheat.c | 6 +- src/m_menu.c | 34 ++++---- src/m_misc.c | 2 +- src/sdl/i_system.c | 108 +++++++++++------------ src/sdl/i_video.c | 46 +++++----- src/win32/win_main.c | 24 ++--- src/win32/win_sys.c | 202 +++++++++++++++++++++---------------------- 16 files changed, 266 insertions(+), 266 deletions(-) diff --git a/src/am_map.c b/src/am_map.c index ef0ebb88c..24379e2f1 100644 --- a/src/am_map.c +++ b/src/am_map.c @@ -458,7 +458,7 @@ boolean AM_Responder(event_t *ev) { if (!automapactive) { - if (ev->type == ev_keydown && ev->data1 == AM_TOGGLEKEY) + if (ev->type == ev_keydown && ev->key == AM_TOGGLEKEY) { //faB: prevent alt-tab in win32 version to activate automap just before // minimizing the app; doesn't do any harm to the DOS version @@ -473,7 +473,7 @@ boolean AM_Responder(event_t *ev) else if (ev->type == ev_keydown) { rc = true; - switch (ev->data1) + switch (ev->key) { case AM_PANRIGHTKEY: // pan right if (!followplayer) @@ -550,7 +550,7 @@ boolean AM_Responder(event_t *ev) else if (ev->type == ev_keyup) { rc = false; - switch (ev->data1) + switch (ev->key) { case AM_PANRIGHTKEY: if (!followplayer) diff --git a/src/console.c b/src/console.c index b3c413840..7941e1dbb 100644 --- a/src/console.c +++ b/src/console.c @@ -913,12 +913,12 @@ boolean CON_Responder(event_t *ev) // let go keyup events, don't eat them if (ev->type != ev_keydown && ev->type != ev_console) { - if (ev->data1 == gamecontrol[gc_console][0] || ev->data1 == gamecontrol[gc_console][1]) + if (ev->key == gamecontrol[gc_console][0] || ev->key == gamecontrol[gc_console][1]) consdown = false; return false; } - key = ev->data1; + key = ev->key; // check for console toggle key if (ev->type != ev_console) diff --git a/src/d_event.h b/src/d_event.h index 70b55c328..c30a8ced2 100644 --- a/src/d_event.h +++ b/src/d_event.h @@ -33,9 +33,9 @@ typedef enum typedef struct { evtype_t type; - INT32 data1; // keys / mouse/joystick buttons - INT32 data2; // mouse/joystick x move - INT32 data3; // mouse/joystick y move + INT32 key; // keys/mouse/joystick buttons + INT32 x; // mouse/joystick x move + INT32 y; // mouse/joystick y move boolean repeated; // key repeat } event_t; diff --git a/src/d_main.c b/src/d_main.c index 1b3449ec1..5acb9073f 100644 --- a/src/d_main.c +++ b/src/d_main.c @@ -191,22 +191,22 @@ void D_ProcessEvents(void) if (ev->type == ev_keydown || ev->type == ev_keyup) { // Mouse buttons - if ((UINT32)(ev->data1 - KEY_MOUSE1) < MOUSEBUTTONS) + if ((UINT32)(ev->key - KEY_MOUSE1) < MOUSEBUTTONS) { if (ev->type == ev_keydown) - mouse.buttons |= 1 << (ev->data1 - KEY_MOUSE1); + mouse.buttons |= 1 << (ev->key - KEY_MOUSE1); else - mouse.buttons &= ~(1 << (ev->data1 - KEY_MOUSE1)); + mouse.buttons &= ~(1 << (ev->key - KEY_MOUSE1)); } - else if ((UINT32)(ev->data1 - KEY_2MOUSE1) < MOUSEBUTTONS) + else if ((UINT32)(ev->key - KEY_2MOUSE1) < MOUSEBUTTONS) { if (ev->type == ev_keydown) - mouse2.buttons |= 1 << (ev->data1 - KEY_2MOUSE1); + mouse2.buttons |= 1 << (ev->key - KEY_2MOUSE1); else - mouse2.buttons &= ~(1 << (ev->data1 - KEY_2MOUSE1)); + mouse2.buttons &= ~(1 << (ev->key - KEY_2MOUSE1)); } // Scroll (has no keyup event) - else switch (ev->data1) { + else switch (ev->key) { case KEY_MOUSEWHEELUP: mouse.buttons |= MB_SCROLLUP; break; diff --git a/src/f_finale.c b/src/f_finale.c index 4d9a8f825..4887b11a4 100644 --- a/src/f_finale.c +++ b/src/f_finale.c @@ -1011,7 +1011,7 @@ void F_IntroTicker(void) // boolean F_IntroResponder(event_t *event) { - INT32 key = event->data1; + INT32 key = event->key; // remap virtual keys (mouse & joystick buttons) switch (key) @@ -1397,7 +1397,7 @@ void F_CreditTicker(void) boolean F_CreditResponder(event_t *event) { - INT32 key = event->data1; + INT32 key = event->key; // remap virtual keys (mouse & joystick buttons) switch (key) @@ -3821,7 +3821,7 @@ void F_ContinueTicker(void) boolean F_ContinueResponder(event_t *event) { - INT32 key = event->data1; + INT32 key = event->key; if (keypressed) return true; diff --git a/src/g_game.c b/src/g_game.c index a35d89aa1..5c89bcae8 100644 --- a/src/g_game.c +++ b/src/g_game.c @@ -1969,7 +1969,7 @@ boolean G_Responder(event_t *ev) if (gameaction == ga_nothing && !singledemo && ((demoplayback && !modeattacking && !titledemo) || gamestate == GS_TITLESCREEN)) { - if (ev->type == ev_keydown && ev->data1 != 301 && !(gamestate == GS_TITLESCREEN && finalecount < TICRATE)) + if (ev->type == ev_keydown && ev->key != 301 && !(gamestate == GS_TITLESCREEN && finalecount < TICRATE)) { M_StartControlPanel(); return true; @@ -2045,7 +2045,7 @@ boolean G_Responder(event_t *ev) // allow spy mode changes even during the demo if (gamestate == GS_LEVEL && ev->type == ev_keydown - && (ev->data1 == KEY_F12 || ev->data1 == gamecontrol[gc_viewpoint][0] || ev->data1 == gamecontrol[gc_viewpoint][1])) + && (ev->key == KEY_F12 || ev->key == gamecontrol[gc_viewpoint][0] || ev->key == gamecontrol[gc_viewpoint][1])) { // ViewpointSwitch Lua hook. UINT8 canSwitchView = 0; @@ -2118,13 +2118,13 @@ boolean G_Responder(event_t *ev) switch (ev->type) { case ev_keydown: - if (ev->data1 == gamecontrol[gc_pause][0] - || ev->data1 == gamecontrol[gc_pause][1] - || ev->data1 == KEY_PAUSE) + if (ev->key == gamecontrol[gc_pause][0] + || ev->key == gamecontrol[gc_pause][1] + || ev->key == KEY_PAUSE) { if (modeattacking && !demoplayback && (gamestate == GS_LEVEL)) { - pausebreakkey = (ev->data1 == KEY_PAUSE); + pausebreakkey = (ev->key == KEY_PAUSE); if (menuactive || pausedelay < 0 || leveltime < 2) return true; @@ -2149,8 +2149,8 @@ boolean G_Responder(event_t *ev) } } } - if (ev->data1 == gamecontrol[gc_camtoggle][0] - || ev->data1 == gamecontrol[gc_camtoggle][1]) + if (ev->key == gamecontrol[gc_camtoggle][0] + || ev->key == gamecontrol[gc_camtoggle][1]) { if (!camtoggledelay) { @@ -2158,8 +2158,8 @@ boolean G_Responder(event_t *ev) CV_SetValue(&cv_chasecam, cv_chasecam.value ? 0 : 1); } } - if (ev->data1 == gamecontrolbis[gc_camtoggle][0] - || ev->data1 == gamecontrolbis[gc_camtoggle][1]) + if (ev->key == gamecontrolbis[gc_camtoggle][0] + || ev->key == gamecontrolbis[gc_camtoggle][1]) { if (!camtoggledelay2) { diff --git a/src/g_input.c b/src/g_input.c index 2f7980c64..e0a27d1ca 100644 --- a/src/g_input.c +++ b/src/g_input.c @@ -115,54 +115,54 @@ void G_MapEventsToControls(event_t *ev) switch (ev->type) { case ev_keydown: - if (ev->data1 < NUMINPUTS) - gamekeydown[ev->data1] = 1; + if (ev->key < NUMINPUTS) + gamekeydown[ev->key] = 1; #ifdef PARANOIA else { - CONS_Debug(DBG_GAMELOGIC, "Bad downkey input %d\n",ev->data1); + CONS_Debug(DBG_GAMELOGIC, "Bad downkey input %d\n",ev->key); } #endif break; case ev_keyup: - if (ev->data1 < NUMINPUTS) - gamekeydown[ev->data1] = 0; + if (ev->key < NUMINPUTS) + gamekeydown[ev->key] = 0; #ifdef PARANOIA else { - CONS_Debug(DBG_GAMELOGIC, "Bad upkey input %d\n",ev->data1); + CONS_Debug(DBG_GAMELOGIC, "Bad upkey input %d\n",ev->key); } #endif break; case ev_mouse: // buttons are virtual keys - mouse.rdx = ev->data2; - mouse.rdy = ev->data3; + mouse.rdx = ev->x; + mouse.rdy = ev->y; break; case ev_joystick: // buttons are virtual keys - i = ev->data1; + i = ev->key; if (i >= JOYAXISSET || menuactive || CON_Ready() || chat_on) break; - if (ev->data2 != INT32_MAX) joyxmove[i] = ev->data2; - if (ev->data3 != INT32_MAX) joyymove[i] = ev->data3; + if (ev->x != INT32_MAX) joyxmove[i] = ev->x; + if (ev->y != INT32_MAX) joyymove[i] = ev->y; break; case ev_joystick2: // buttons are virtual keys - i = ev->data1; + i = ev->key; if (i >= JOYAXISSET || menuactive || CON_Ready() || chat_on) break; - if (ev->data2 != INT32_MAX) joy2xmove[i] = ev->data2; - if (ev->data3 != INT32_MAX) joy2ymove[i] = ev->data3; + if (ev->x != INT32_MAX) joy2xmove[i] = ev->x; + if (ev->y != INT32_MAX) joy2ymove[i] = ev->y; break; case ev_mouse2: // buttons are virtual keys if (menuactive || CON_Ready() || chat_on) break; - mouse2.rdx = ev->data2; - mouse2.rdy = ev->data3; + mouse2.rdx = ev->x; + mouse2.rdy = ev->y; break; default: diff --git a/src/hu_stuff.c b/src/hu_stuff.c index e0eaf8fb1..3bd263210 100644 --- a/src/hu_stuff.c +++ b/src/hu_stuff.c @@ -1111,12 +1111,12 @@ boolean HU_Responder(event_t *ev) // (Unless if you're sharing a keyboard, since you probably establish when you start chatting that you have dibs on it...) // (Ahhh, the good ol days when I was a kid who couldn't afford an extra USB controller...) - if (ev->data1 >= KEY_MOUSE1) + if (ev->key >= KEY_MOUSE1) { INT32 i; for (i = 0; i < num_gamecontrols; i++) { - if (gamecontrol[i][0] == ev->data1 || gamecontrol[i][1] == ev->data1) + if (gamecontrol[i][0] == ev->key || gamecontrol[i][1] == ev->key) break; } @@ -1125,12 +1125,12 @@ boolean HU_Responder(event_t *ev) }*/ //We don't actually care about that unless we get splitscreen netgames. :V #ifndef NONET - c = (INT32)ev->data1; + c = (INT32)ev->key; if (!chat_on) { // enter chat mode - if ((ev->data1 == gamecontrol[gc_talkkey][0] || ev->data1 == gamecontrol[gc_talkkey][1]) + if ((ev->key == gamecontrol[gc_talkkey][0] || ev->key == gamecontrol[gc_talkkey][1]) && netgame && !OLD_MUTE) // check for old chat mute, still let the players open the chat incase they want to scroll otherwise. { chat_on = true; @@ -1140,7 +1140,7 @@ boolean HU_Responder(event_t *ev) typelines = 1; return true; } - if ((ev->data1 == gamecontrol[gc_teamkey][0] || ev->data1 == gamecontrol[gc_teamkey][1]) + if ((ev->key == gamecontrol[gc_teamkey][0] || ev->key == gamecontrol[gc_teamkey][1]) && netgame && !OLD_MUTE) { chat_on = true; @@ -1157,12 +1157,12 @@ boolean HU_Responder(event_t *ev) // Ignore modifier keys // Note that we do this here so users can still set // their chat keys to one of these, if they so desire. - if (ev->data1 == KEY_LSHIFT || ev->data1 == KEY_RSHIFT - || ev->data1 == KEY_LCTRL || ev->data1 == KEY_RCTRL - || ev->data1 == KEY_LALT || ev->data1 == KEY_RALT) + if (ev->key == KEY_LSHIFT || ev->key == KEY_RSHIFT + || ev->key == KEY_LCTRL || ev->key == KEY_RCTRL + || ev->key == KEY_LALT || ev->key == KEY_RALT) return true; - c = (INT32)ev->data1; + c = (INT32)ev->key; // I know this looks very messy but this works. If it ain't broke, don't fix it! // shift LETTERS to uppercase if we have capslock or are holding shift diff --git a/src/lua_inputlib.c b/src/lua_inputlib.c index 01ad9af3c..01383a57b 100644 --- a/src/lua_inputlib.c +++ b/src/lua_inputlib.c @@ -184,9 +184,9 @@ static int keyevent_get(lua_State *L) I_Assert(event != NULL); if (fastcmp(field,"name")) - lua_pushstring(L, G_KeyNumToString(event->data1)); + lua_pushstring(L, G_KeyNumToString(event->key)); else if (fastcmp(field,"num")) - lua_pushinteger(L, event->data1); + lua_pushinteger(L, event->key); else if (fastcmp(field,"repeated")) lua_pushboolean(L, event->repeated); else diff --git a/src/m_cheat.c b/src/m_cheat.c index c958bb4a4..ef896c991 100644 --- a/src/m_cheat.c +++ b/src/m_cheat.c @@ -203,11 +203,11 @@ boolean cht_Responder(event_t *ev) if (ev->type != ev_keydown) return false; - if (ev->data1 > 0xFF) + if (ev->key > 0xFF) { // map some fake (joy) inputs into keys // map joy inputs into keys - switch (ev->data1) + switch (ev->key) { case KEY_JOY1: case KEY_JOY1 + 2: @@ -231,7 +231,7 @@ boolean cht_Responder(event_t *ev) } } else - ch = (UINT8)ev->data1; + ch = (UINT8)ev->key; ret += cht_CheckCheat(&cheat_ultimate, (char)ch); ret += cht_CheckCheat(&cheat_ultimate_joy, (char)ch); diff --git a/src/m_menu.c b/src/m_menu.c index db2aa09c6..92754705b 100644 --- a/src/m_menu.c +++ b/src/m_menu.c @@ -3229,7 +3229,7 @@ boolean M_Responder(event_t *ev) if (ev->type == ev_keydown) { keydown++; - ch = ev->data1; + ch = ev->key; // added 5-2-98 remap virtual keys (mouse & joystick buttons) switch (ch) @@ -3262,44 +3262,44 @@ boolean M_Responder(event_t *ev) break; } } - else if (ev->type == ev_joystick && ev->data1 == 0 && joywait < I_GetTime()) + else if (ev->type == ev_joystick && ev->key == 0 && joywait < I_GetTime()) { const INT32 jdeadzone = (JOYAXISRANGE * cv_digitaldeadzone.value) / FRACUNIT; - if (ev->data3 != INT32_MAX) + if (ev->y != INT32_MAX) { - if (Joystick.bGamepadStyle || abs(ev->data3) > jdeadzone) + if (Joystick.bGamepadStyle || abs(ev->y) > jdeadzone) { - if (ev->data3 < 0 && pjoyy >= 0) + if (ev->y < 0 && pjoyy >= 0) { ch = KEY_UPARROW; joywait = I_GetTime() + NEWTICRATE/7; } - else if (ev->data3 > 0 && pjoyy <= 0) + else if (ev->y > 0 && pjoyy <= 0) { ch = KEY_DOWNARROW; joywait = I_GetTime() + NEWTICRATE/7; } - pjoyy = ev->data3; + pjoyy = ev->y; } else pjoyy = 0; } - if (ev->data2 != INT32_MAX) + if (ev->x != INT32_MAX) { - if (Joystick.bGamepadStyle || abs(ev->data2) > jdeadzone) + if (Joystick.bGamepadStyle || abs(ev->x) > jdeadzone) { - if (ev->data2 < 0 && pjoyx >= 0) + if (ev->x < 0 && pjoyx >= 0) { ch = KEY_LEFTARROW; joywait = I_GetTime() + NEWTICRATE/17; } - else if (ev->data2 > 0 && pjoyx <= 0) + else if (ev->x > 0 && pjoyx <= 0) { ch = KEY_RIGHTARROW; joywait = I_GetTime() + NEWTICRATE/17; } - pjoyx = ev->data2; + pjoyx = ev->x; } else pjoyx = 0; @@ -3307,7 +3307,7 @@ boolean M_Responder(event_t *ev) } else if (ev->type == ev_mouse && mousewait < I_GetTime()) { - pmousey -= ev->data3; + pmousey -= ev->y; if (pmousey < lasty-30) { ch = KEY_DOWNARROW; @@ -3321,7 +3321,7 @@ boolean M_Responder(event_t *ev) pmousey = lasty += 30; } - pmousex += ev->data2; + pmousex += ev->x; if (pmousex < lastx - 30) { ch = KEY_LEFTARROW; @@ -3339,7 +3339,7 @@ boolean M_Responder(event_t *ev) keydown = 0; } else if (ev->type == ev_keydown) // Preserve event for other responders - ch = ev->data1; + ch = ev->key; if (ch == -1) return false; @@ -12859,7 +12859,7 @@ static void M_ChangecontrolResponse(event_t *ev) { INT32 control; INT32 found; - INT32 ch = ev->data1; + INT32 ch = ev->key; // ESCAPE cancels; dummy out PAUSE if (ch != KEY_ESCAPE && ch != KEY_PAUSE) @@ -12878,7 +12878,7 @@ static void M_ChangecontrolResponse(event_t *ev) // keypad arrows are converted for the menu in cursor arrows // so use the event instead of ch case ev_keydown: - ch = ev->data1; + ch = ev->key; break; default: diff --git a/src/m_misc.c b/src/m_misc.c index ac60d49c7..f9a23ad44 100644 --- a/src/m_misc.c +++ b/src/m_misc.c @@ -1631,7 +1631,7 @@ boolean M_ScreenshotResponder(event_t *ev) if (dedicated || ev->type != ev_keydown) return false; - ch = ev->data1; + ch = ev->key; if (ch >= KEY_MOUSE1 && menuactive) // If it's not a keyboard key, then don't allow it in the menus! return false; diff --git a/src/sdl/i_system.c b/src/sdl/i_system.c index 1594c8d61..a3908c570 100644 --- a/src/sdl/i_system.c +++ b/src/sdl/i_system.c @@ -572,7 +572,7 @@ void I_GetConsoleEvents(void) tty_con.buffer[tty_con.cursor] = '\0'; tty_Back(); } - ev.data1 = KEY_BACKSPACE; + ev.key = KEY_BACKSPACE; } else if (key < ' ') // check if this is a control char { @@ -580,19 +580,19 @@ void I_GetConsoleEvents(void) { tty_Clear(); tty_con.cursor = 0; - ev.data1 = KEY_ENTER; + ev.key = KEY_ENTER; } else return; } else { // push regular character - ev.data1 = tty_con.buffer[tty_con.cursor] = key; + ev.key = tty_con.buffer[tty_con.cursor] = key; tty_con.cursor++; // print the current line (this is differential) d = write(STDOUT_FILENO, &key, 1); } - if (ev.data1) D_PostEvent(&ev); + if (ev.key) D_PostEvent(&ev); //tty_FlushIn(); (void)d; } @@ -626,18 +626,18 @@ static void Impl_HandleKeyboardConsoleEvent(KEY_EVENT_RECORD evt, HANDLE co) { case VK_ESCAPE: case VK_TAB: - event.data1 = KEY_NULL; + event.key = KEY_NULL; break; case VK_RETURN: entering_con_command = false; /* FALLTHRU */ default: - //event.data1 = MapVirtualKey(evt.wVirtualKeyCode,2); // convert in to char - event.data1 = evt.uChar.AsciiChar; + //event.key = MapVirtualKey(evt.wVirtualKeyCode,2); // convert in to char + event.key = evt.uChar.AsciiChar; } if (co != INVALID_HANDLE_VALUE && GetFileType(co) == FILE_TYPE_CHAR && GetConsoleMode(co, &t)) { - if (event.data1 && event.data1 != KEY_LSHIFT && event.data1 != KEY_RSHIFT) + if (event.key && event.key != KEY_LSHIFT && event.key != KEY_RSHIFT) { #ifdef _UNICODE WriteConsole(co, &evt.uChar.UnicodeChar, 1, &t, NULL); @@ -652,7 +652,7 @@ static void Impl_HandleKeyboardConsoleEvent(KEY_EVENT_RECORD evt, HANDLE co) } } } - if (event.data1) D_PostEvent(&event); + if (event.key) D_PostEvent(&event); } void I_GetConsoleEvents(void) @@ -917,7 +917,7 @@ INT32 I_GetKey (void) ev = &events[eventtail]; if (ev->type == ev_keydown || ev->type == ev_console) { - rc = ev->data1; + rc = ev->key; continue; } } @@ -977,22 +977,22 @@ void I_ShutdownJoystick(void) INT32 i; event_t event; event.type=ev_keyup; - event.data2 = 0; - event.data3 = 0; + event.x = 0; + event.y = 0; lastjoybuttons = lastjoyhats = 0; // emulate the up of all joystick buttons for (i=0;i= 0; i--) { - event.data1 = i; + event.key = i; if (i*2 + 1 <= JoyInfo.axises) axisx = SDL_JoystickGetAxis(JoyInfo.dev, i*2 + 0); else axisx = 0; @@ -1110,15 +1110,15 @@ void I_GetJoystickEvents(void) { // gamepad control type, on or off, live or die if (axisx < -(JOYAXISRANGE/2)) - event.data2 = -1; + event.x = -1; else if (axisx > (JOYAXISRANGE/2)) - event.data2 = 1; - else event.data2 = 0; + event.x = 1; + else event.x = 0; if (axisy < -(JOYAXISRANGE/2)) - event.data3 = -1; + event.y = -1; else if (axisy > (JOYAXISRANGE/2)) - event.data3 = 1; - else event.data3 = 0; + event.y = 1; + else event.y = 0; } else { @@ -1132,8 +1132,8 @@ void I_GetJoystickEvents(void) #endif // analog control style , just send the raw data - event.data2 = axisx; // x axis - event.data3 = axisy; // y axis + event.x = axisx; // x axis + event.y = axisy; // y axis } D_PostEvent(&event); } @@ -1247,22 +1247,22 @@ void I_ShutdownJoystick2(void) INT32 i; event_t event; event.type = ev_keyup; - event.data2 = 0; - event.data3 = 0; + event.x = 0; + event.y = 0; lastjoy2buttons = lastjoy2hats = 0; // emulate the up of all joystick buttons for (i = 0; i < JOYBUTTONS; i++) { - event.data1 = KEY_2JOY1 + i; + event.key = KEY_2JOY1 + i; D_PostEvent(&event); } // emulate the up of all joystick hats for (i = 0; i < JOYHATS*4; i++) { - event.data1 = KEY_2HAT1 + i; + event.key = KEY_2HAT1 + i; D_PostEvent(&event); } @@ -1270,7 +1270,7 @@ void I_ShutdownJoystick2(void) event.type = ev_joystick2; for (i = 0; i < JOYAXISSET; i++) { - event.data1 = i; + event.key = i; D_PostEvent(&event); } @@ -1321,7 +1321,7 @@ void I_GetJoystick2Events(void) event.type = ev_keydown; else event.type = ev_keyup; - event.data1 = KEY_2JOY1 + i; + event.key = KEY_2JOY1 + i; D_PostEvent(&event); } } @@ -1352,7 +1352,7 @@ void I_GetJoystick2Events(void) event.type = ev_keydown; else event.type = ev_keyup; - event.data1 = KEY_2HAT1 + i; + event.key = KEY_2HAT1 + i; D_PostEvent(&event); } } @@ -1364,7 +1364,7 @@ void I_GetJoystick2Events(void) for (i = JOYAXISSET - 1; i >= 0; i--) { - event.data1 = i; + event.key = i; if (i*2 + 1 <= JoyInfo2.axises) axisx = SDL_JoystickGetAxis(JoyInfo2.dev, i*2 + 0); else axisx = 0; @@ -1380,17 +1380,17 @@ void I_GetJoystick2Events(void) { // gamepad control type, on or off, live or die if (axisx < -(JOYAXISRANGE/2)) - event.data2 = -1; + event.x = -1; else if (axisx > (JOYAXISRANGE/2)) - event.data2 = 1; + event.x = 1; else - event.data2 = 0; + event.x = 0; if (axisy < -(JOYAXISRANGE/2)) - event.data3 = -1; + event.y = -1; else if (axisy > (JOYAXISRANGE/2)) - event.data3 = 1; + event.y = 1; else - event.data3 = 0; + event.y = 0; } else { @@ -1404,8 +1404,8 @@ void I_GetJoystick2Events(void) #endif // analog control style , just send the raw data - event.data2 = axisx; // x axis - event.data3 = axisy; // y axis + event.x = axisx; // x axis + event.y = axisy; // y axis } D_PostEvent(&event); } @@ -1804,7 +1804,7 @@ void I_GetMouseEvents(void) if (!(button & (1< 0) { - event.data1 = KEY_MOUSEWHEELUP; + event.key = KEY_MOUSEWHEELUP; event.type = ev_keydown; } if (evt.y < 0) { - event.data1 = KEY_MOUSEWHEELDOWN; + event.key = KEY_MOUSEWHEELDOWN; event.type = ev_keydown; } if (evt.y == 0) { - event.data1 = 0; + event.key = 0; event.type = ev_keyup; } if (event.type == ev_keyup || event.type == ev_keydown) @@ -796,7 +796,7 @@ static void Impl_HandleJoystickAxisEvent(SDL_JoyAxisEvent evt) joyid[1] = SDL_JoystickInstanceID(JoyInfo2.dev); evt.axis++; - event.data1 = event.data2 = event.data3 = INT32_MAX; + event.key = event.x = event.y = INT32_MAX; if (evt.which == joyid[0]) { @@ -813,14 +813,14 @@ static void Impl_HandleJoystickAxisEvent(SDL_JoyAxisEvent evt) //vaule if (evt.axis%2) { - event.data1 = evt.axis / 2; - event.data2 = SDLJoyAxis(evt.value, event.type); + event.key = evt.axis / 2; + event.x = SDLJoyAxis(evt.value, event.type); } else { evt.axis--; - event.data1 = evt.axis / 2; - event.data3 = SDLJoyAxis(evt.value, event.type); + event.key = evt.axis / 2; + event.y = SDLJoyAxis(evt.value, event.type); } D_PostEvent(&event); } @@ -840,11 +840,11 @@ static void Impl_HandleJoystickHatEvent(SDL_JoyHatEvent evt) if (evt.which == joyid[0]) { - event.data1 = KEY_HAT1 + (evt.hat*4); + event.key = KEY_HAT1 + (evt.hat*4); } else if (evt.which == joyid[1]) { - event.data1 = KEY_2HAT1 + (evt.hat*4); + event.key = KEY_2HAT1 + (evt.hat*4); } else return; @@ -863,11 +863,11 @@ static void Impl_HandleJoystickButtonEvent(SDL_JoyButtonEvent evt, Uint32 type) if (evt.which == joyid[0]) { - event.data1 = KEY_JOY1; + event.key = KEY_JOY1; } else if (evt.which == joyid[1]) { - event.data1 = KEY_2JOY1; + event.key = KEY_2JOY1; } else return; if (type == SDL_JOYBUTTONUP) @@ -881,7 +881,7 @@ static void Impl_HandleJoystickButtonEvent(SDL_JoyButtonEvent evt, Uint32 type) else return; if (evt.button < JOYBUTTONS) { - event.data1 += evt.button; + event.key += evt.button; } else return; @@ -1085,9 +1085,9 @@ void I_GetEvent(void) SDL_GetWindowSize(window, &wwidth, &wheight); //SDL_memset(&event, 0, sizeof(event_t)); event.type = ev_mouse; - event.data1 = 0; - event.data2 = (INT32)lround(mousemovex * ((float)wwidth / (float)realwidth)); - event.data3 = (INT32)lround(mousemovey * ((float)wheight / (float)realheight)); + event.key = 0; + event.x = (INT32)lround(mousemovex * ((float)wwidth / (float)realwidth)); + event.y = (INT32)lround(mousemovey * ((float)wheight / (float)realheight)); D_PostEvent(&event); } diff --git a/src/win32/win_main.c b/src/win32/win_main.c index e1d90881b..a5ebf3211 100644 --- a/src/win32/win_main.c +++ b/src/win32/win_main.c @@ -188,11 +188,11 @@ static LRESULT CALLBACK MainWndproc(HWND hWnd, UINT message, WPARAM wParam, LPAR ev.type = ev_keydown; handleKeyDoom: - ev.data1 = 0; + ev.key = 0; if (wParam == VK_PAUSE) // intercept PAUSE key { - ev.data1 = KEY_PAUSE; + ev.key = KEY_PAUSE; } else if (!keyboard_started) // post some keys during the game startup @@ -201,14 +201,14 @@ static LRESULT CALLBACK MainWndproc(HWND hWnd, UINT message, WPARAM wParam, LPAR { switch (wParam) { - case VK_ESCAPE: ev.data1 = KEY_ESCAPE; break; - case VK_RETURN: ev.data1 = KEY_ENTER; break; - case VK_SHIFT: ev.data1 = KEY_LSHIFT; break; - default: ev.data1 = MapVirtualKey((DWORD)wParam,2); // convert in to char + case VK_ESCAPE: ev.key = KEY_ESCAPE; break; + case VK_RETURN: ev.key = KEY_ENTER; break; + case VK_SHIFT: ev.key = KEY_LSHIFT; break; + default: ev.key = MapVirtualKey((DWORD)wParam,2); // convert in to char } } - if (ev.data1) + if (ev.key) D_PostEvent (&ev); return 0; @@ -240,7 +240,7 @@ static LRESULT CALLBACK MainWndproc(HWND hWnd, UINT message, WPARAM wParam, LPAR if (nodinput) { ev.type = ev_keyup; - ev.data1 = KEY_MOUSE1 + 3 + HIWORD(wParam); + ev.key = KEY_MOUSE1 + 3 + HIWORD(wParam); D_PostEvent(&ev); return TRUE; } @@ -249,7 +249,7 @@ static LRESULT CALLBACK MainWndproc(HWND hWnd, UINT message, WPARAM wParam, LPAR if (nodinput) { ev.type = ev_keydown; - ev.data1 = KEY_MOUSE1 + 3 + HIWORD(wParam); + ev.key = KEY_MOUSE1 + 3 + HIWORD(wParam); D_PostEvent(&ev); return TRUE; } @@ -258,9 +258,9 @@ static LRESULT CALLBACK MainWndproc(HWND hWnd, UINT message, WPARAM wParam, LPAR //I_OutputMsg("MW_WHEEL dispatched.\n"); ev.type = ev_keydown; if ((INT16)HIWORD(wParam) > 0) - ev.data1 = KEY_MOUSEWHEELUP; + ev.key = KEY_MOUSEWHEELUP; else - ev.data1 = KEY_MOUSEWHEELDOWN; + ev.key = KEY_MOUSEWHEELDOWN; D_PostEvent(&ev); break; @@ -271,7 +271,7 @@ static LRESULT CALLBACK MainWndproc(HWND hWnd, UINT message, WPARAM wParam, LPAR case WM_CLOSE: PostQuitMessage(0); //to quit while in-game - ev.data1 = KEY_ESCAPE; //to exit network synchronization + ev.key = KEY_ESCAPE; //to exit network synchronization ev.type = ev_keydown; D_PostEvent (&ev); return 0; diff --git a/src/win32/win_sys.c b/src/win32/win_sys.c index da0d5b47e..ff443935f 100644 --- a/src/win32/win_sys.c +++ b/src/win32/win_sys.c @@ -322,20 +322,20 @@ static inline VOID I_GetConsoleEvents(VOID) { case VK_ESCAPE: case VK_TAB: - ev.data1 = KEY_NULL; + ev.key = KEY_NULL; break; case VK_SHIFT: - ev.data1 = KEY_LSHIFT; + ev.key = KEY_LSHIFT; break; case VK_RETURN: entering_con_command = false; /* FALLTHRU */ default: - ev.data1 = MapVirtualKey(input.Event.KeyEvent.wVirtualKeyCode,2); // convert in to char + ev.key = MapVirtualKey(input.Event.KeyEvent.wVirtualKeyCode,2); // convert in to char } if (co != INVALID_HANDLE_VALUE && GetFileType(co) == FILE_TYPE_CHAR && GetConsoleMode(co, &t)) { - if (ev.data1 && ev.data1 != KEY_LSHIFT && ev.data1 != KEY_RSHIFT) + if (ev.key && ev.key != KEY_LSHIFT && ev.key != KEY_RSHIFT) { #ifdef UNICODE WriteConsole(co, &input.Event.KeyEvent.uChar.UnicodeChar, 1, &t, NULL); @@ -356,13 +356,13 @@ static inline VOID I_GetConsoleEvents(VOID) switch (input.Event.KeyEvent.wVirtualKeyCode) { case VK_SHIFT: - ev.data1 = KEY_LSHIFT; + ev.key = KEY_LSHIFT; break; default: break; } } - if (ev.data1) D_PostEvent(&ev); + if (ev.key) D_PostEvent(&ev); break; case MOUSE_EVENT: case WINDOW_BUFFER_SIZE_EVENT: @@ -945,7 +945,7 @@ static void I_ShutdownMouse2(VOID) for (i = 0; i < MOUSEBUTTONS; i++) { event.type = ev_keyup; - event.data1 = KEY_2MOUSE1 + i; + event.key = KEY_2MOUSE1 + i; D_PostEvent(&event); } @@ -1135,14 +1135,14 @@ VOID I_GetSysMouseEvents(INT mouse_state) if ((mouse_state & (1 << i)) && !(old_mouse_state & (1 << i))) { event.type = ev_keydown; - event.data1 = KEY_MOUSE1 + i; + event.key = KEY_MOUSE1 + i; D_PostEvent(&event); } // check if button released if (!(mouse_state & (1 << i)) && (old_mouse_state & (1 << i))) { event.type = ev_keyup; - event.data1 = KEY_MOUSE1 + i; + event.key = KEY_MOUSE1 + i; D_PostEvent(&event); } } @@ -1156,9 +1156,9 @@ VOID I_GetSysMouseEvents(INT mouse_state) if (xmickeys || ymickeys) { event.type = ev_mouse; - event.data1 = 0; - event.data2 = xmickeys; - event.data3 = -ymickeys; + event.key = 0; + event.x = xmickeys; + event.y = -ymickeys; D_PostEvent(&event); SetCursorPos(center_x, center_y); } @@ -1240,7 +1240,7 @@ static void I_ShutdownMouse(void) for (i = 0; i < MOUSEBUTTONS; i++) { event.type = ev_keyup; - event.data1 = KEY_MOUSE1 + i; + event.key = KEY_MOUSE1 + i; D_PostEvent(&event); } if (nodinput) @@ -1281,7 +1281,7 @@ void I_GetMouseEvents(void) event.type = ev_keydown; else event.type = ev_keyup; - event.data1 = KEY_2MOUSE1 + i; + event.key = KEY_2MOUSE1 + i; D_PostEvent(&event); } } @@ -1289,9 +1289,9 @@ void I_GetMouseEvents(void) if (handlermouse2x || handlermouse2y) { event.type = ev_mouse2; - event.data1 = 0; - event.data2 = handlermouse2x<<1; - event.data3 = -handlermouse2y<<1; + event.key = 0; + event.x = handlermouse2x<<1; + event.y = -handlermouse2y<<1; handlermouse2x = 0; handlermouse2y = 0; @@ -1330,7 +1330,7 @@ getBufferedData: else event.type = ev_keyup; // Button up - event.data1 = rgdod[d].dwOfs - DIMOFS_BUTTON0 + KEY_MOUSE1; + event.key = rgdod[d].dwOfs - DIMOFS_BUTTON0 + KEY_MOUSE1; D_PostEvent(&event); } else if (rgdod[d].dwOfs == DIMOFS_X) @@ -1342,9 +1342,9 @@ getBufferedData: { // z-axes the wheel if ((int)rgdod[d].dwData > 0) - event.data1 = KEY_MOUSEWHEELUP; + event.key = KEY_MOUSEWHEELUP; else - event.data1 = KEY_MOUSEWHEELDOWN; + event.key = KEY_MOUSEWHEELDOWN; event.type = ev_keydown; D_PostEvent(&event); } @@ -1354,9 +1354,9 @@ getBufferedData: if (xmickeys || ymickeys) { event.type = ev_mouse; - event.data1 = 0; - event.data2 = xmickeys; - event.data3 = -ymickeys; + event.key = 0; + event.x = xmickeys; + event.y = -ymickeys; D_PostEvent(&event); } } @@ -2395,14 +2395,14 @@ static VOID I_ShutdownJoystick(VOID) // emulate the up of all joystick buttons for (i = 0;i < JOYBUTTONS;i++) { - event.data1 = KEY_JOY1+i; + event.key = KEY_JOY1+i; D_PostEvent(&event); } // emulate the up of all joystick hats for (i = 0;i < JOYHATS*4;i++) { - event.data1 = KEY_HAT1+i; + event.key = KEY_HAT1+i; D_PostEvent(&event); } @@ -2410,7 +2410,7 @@ static VOID I_ShutdownJoystick(VOID) event.type = ev_joystick; for (i = 0;i < JOYAXISSET; i++) { - event.data1 = i; + event.key = i; D_PostEvent(&event); } @@ -2460,14 +2460,14 @@ static VOID I_ShutdownJoystick2(VOID) // emulate the up of all joystick buttons for (i = 0;i < JOYBUTTONS;i++) { - event.data1 = KEY_2JOY1+i; + event.key = KEY_2JOY1+i; D_PostEvent(&event); } // emulate the up of all joystick hats for (i = 0;i < JOYHATS*4;i++) { - event.data1 = KEY_2HAT1+i; + event.key = KEY_2HAT1+i; D_PostEvent(&event); } @@ -2475,7 +2475,7 @@ static VOID I_ShutdownJoystick2(VOID) event.type = ev_joystick2; for (i = 0;i < JOYAXISSET; i++) { - event.data1 = i; + event.key = i; D_PostEvent(&event); } @@ -2598,7 +2598,7 @@ acquire: event.type = ev_keydown; else event.type = ev_keyup; - event.data1 = KEY_JOY1 + i; + event.key = KEY_JOY1 + i; D_PostEvent(&event); } } @@ -2618,7 +2618,7 @@ acquire: event.type = ev_keydown; else event.type = ev_keyup; - event.data1 = KEY_HAT1 + i; + event.key = KEY_HAT1 + i; D_PostEvent(&event); } } @@ -2627,7 +2627,7 @@ acquire: // send joystick axis positions event.type = ev_joystick; - event.data1 = event.data2 = event.data3 = 0; + event.key = event.x = event.y = 0; if (Joystick.bGamepadStyle) { @@ -2635,29 +2635,29 @@ acquire: if (JoyInfo.X) { if (js.lX < -(JOYAXISRANGE/2)) - event.data2 = -1; + event.x = -1; else if (js.lX > JOYAXISRANGE/2) - event.data2 = 1; + event.x = 1; } if (JoyInfo.Y) { if (js.lY < -(JOYAXISRANGE/2)) - event.data3 = -1; + event.y = -1; else if (js.lY > JOYAXISRANGE/2) - event.data3 = 1; + event.y = 1; } } else { // analog control style, just send the raw data - if (JoyInfo.X) event.data2 = js.lX; // x axis - if (JoyInfo.Y) event.data3 = js.lY; // y axis + if (JoyInfo.X) event.x = js.lX; // x axis + if (JoyInfo.Y) event.y = js.lY; // y axis } D_PostEvent(&event); #if JOYAXISSET > 1 - event.data1 = 1; - event.data2 = event.data3 = 0; + event.key = 1; + event.x = event.y = 0; if (Joystick.bGamepadStyle) { @@ -2665,30 +2665,30 @@ acquire: if (JoyInfo.Z) { if (js.lZ < -(JOYAXISRANGE/2)) - event.data2 = -1; + event.x = -1; else if (js.lZ > JOYAXISRANGE/2) - event.data2 = 1; + event.x = 1; } if (JoyInfo.Rx) { if (js.lRx < -(JOYAXISRANGE/2)) - event.data3 = -1; + event.y = -1; else if (js.lRx > JOYAXISRANGE/2) - event.data3 = 1; + event.y = 1; } } else { // analog control style, just send the raw data - if (JoyInfo.Z) event.data2 = js.lZ; // z axis - if (JoyInfo.Rx) event.data3 = js.lRx; // rx axis + if (JoyInfo.Z) event.x = js.lZ; // z axis + if (JoyInfo.Rx) event.y = js.lRx; // rx axis } D_PostEvent(&event); #endif #if JOYAXISSET > 2 - event.data1 = 2; - event.data2 = event.data3 = 0; + event.key = 2; + event.x = event.y = 0; if (Joystick.bGamepadStyle) { @@ -2696,53 +2696,53 @@ acquire: if (JoyInfo.Rx) { if (js.lRy < -(JOYAXISRANGE/2)) - event.data2 = -1; + event.x = -1; else if (js.lRy > JOYAXISRANGE/2) - event.data2 = 1; + event.x = 1; } if (JoyInfo.Rz) { if (js.lRz < -(JOYAXISRANGE/2)) - event.data3 = -1; + event.y = -1; else if (js.lRz > JOYAXISRANGE/2) - event.data3 = 1; + event.y = 1; } } else { // analog control style, just send the raw data - if (JoyInfo.Ry) event.data2 = js.lRy; // ry axis - if (JoyInfo.Rz) event.data3 = js.lRz; // rz axis + if (JoyInfo.Ry) event.x = js.lRy; // ry axis + if (JoyInfo.Rz) event.y = js.lRz; // rz axis } D_PostEvent(&event); #endif #if JOYAXISSET > 3 - event.data1 = 3; - event.data2 = event.data3 = 0; + event.key = 3; + event.x = event.y = 0; if (Joystick.bGamepadStyle) { // gamepad control type, on or off, live or die if (JoyInfo.U) { if (js.rglSlider[0] < -(JOYAXISRANGE/2)) - event.data2 = -1; + event.x = -1; else if (js.rglSlider[0] > JOYAXISRANGE/2) - event.data2 = 1; + event.x = 1; } if (JoyInfo.V) { if (js.rglSlider[1] < -(JOYAXISRANGE/2)) - event.data3 = -1; + event.y = -1; else if (js.rglSlider[1] > JOYAXISRANGE/2) - event.data3 = 1; + event.y = 1; } } else { // analog control style, just send the raw data - if (JoyInfo.U) event.data2 = js.rglSlider[0]; // U axis - if (JoyInfo.V) event.data3 = js.rglSlider[1]; // V axis + if (JoyInfo.U) event.x = js.rglSlider[0]; // U axis + if (JoyInfo.V) event.y = js.rglSlider[1]; // V axis } D_PostEvent(&event); #endif @@ -2842,7 +2842,7 @@ acquire: event.type = ev_keydown; else event.type = ev_keyup; - event.data1 = KEY_2JOY1 + i; + event.key = KEY_2JOY1 + i; D_PostEvent(&event); } } @@ -2862,7 +2862,7 @@ acquire: event.type = ev_keydown; else event.type = ev_keyup; - event.data1 = KEY_2HAT1 + i; + event.key = KEY_2HAT1 + i; D_PostEvent(&event); } } @@ -2871,7 +2871,7 @@ acquire: // send joystick axis positions event.type = ev_joystick2; - event.data1 = event.data2 = event.data3 = 0; + event.key = event.x = event.y = 0; if (Joystick2.bGamepadStyle) { @@ -2879,29 +2879,29 @@ acquire: if (JoyInfo2.X) { if (js.lX < -(JOYAXISRANGE/2)) - event.data2 = -1; + event.x = -1; else if (js.lX > JOYAXISRANGE/2) - event.data2 = 1; + event.x = 1; } if (JoyInfo2.Y) { if (js.lY < -(JOYAXISRANGE/2)) - event.data3 = -1; + event.y = -1; else if (js.lY > JOYAXISRANGE/2) - event.data3 = 1; + event.y = 1; } } else { // analog control style, just send the raw data - if (JoyInfo2.X) event.data2 = js.lX; // x axis - if (JoyInfo2.Y) event.data3 = js.lY; // y axis + if (JoyInfo2.X) event.x = js.lX; // x axis + if (JoyInfo2.Y) event.y = js.lY; // y axis } D_PostEvent(&event); #if JOYAXISSET > 1 - event.data1 = 1; - event.data2 = event.data3 = 0; + event.key = 1; + event.x = event.y = 0; if (Joystick2.bGamepadStyle) { @@ -2909,30 +2909,30 @@ acquire: if (JoyInfo2.Z) { if (js.lZ < -(JOYAXISRANGE/2)) - event.data2 = -1; + event.x = -1; else if (js.lZ > JOYAXISRANGE/2) - event.data2 = 1; + event.x = 1; } if (JoyInfo2.Rx) { if (js.lRx < -(JOYAXISRANGE/2)) - event.data3 = -1; + event.y = -1; else if (js.lRx > JOYAXISRANGE/2) - event.data3 = 1; + event.y = 1; } } else { // analog control style, just send the raw data - if (JoyInfo2.Z) event.data2 = js.lZ; // z axis - if (JoyInfo2.Rx) event.data3 = js.lRx; // rx axis + if (JoyInfo2.Z) event.x = js.lZ; // z axis + if (JoyInfo2.Rx) event.y = js.lRx; // rx axis } D_PostEvent(&event); #endif #if JOYAXISSET > 2 - event.data1 = 2; - event.data2 = event.data3 = 0; + event.key = 2; + event.x = event.y = 0; if (Joystick2.bGamepadStyle) { @@ -2940,53 +2940,53 @@ acquire: if (JoyInfo2.Rx) { if (js.lRy < -(JOYAXISRANGE/2)) - event.data2 = -1; + event.x = -1; else if (js.lRy > JOYAXISRANGE/2) - event.data2 = 1; + event.x = 1; } if (JoyInfo2.Rz) { if (js.lRz < -(JOYAXISRANGE/2)) - event.data3 = -1; + event.y = -1; else if (js.lRz > JOYAXISRANGE/2) - event.data3 = 1; + event.y = 1; } } else { // analog control style, just send the raw data - if (JoyInfo2.Ry) event.data2 = js.lRy; // ry axis - if (JoyInfo2.Rz) event.data3 = js.lRz; // rz axis + if (JoyInfo2.Ry) event.x = js.lRy; // ry axis + if (JoyInfo2.Rz) event.y = js.lRz; // rz axis } D_PostEvent(&event); #endif #if JOYAXISSET > 3 - event.data1 = 3; - event.data2 = event.data3 = 0; + event.key = 3; + event.x = event.y = 0; if (Joystick2.bGamepadStyle) { // gamepad control type, on or off, live or die if (JoyInfo2.U) { if (js.rglSlider[0] < -(JOYAXISRANGE/2)) - event.data2 = -1; + event.x = -1; else if (js.rglSlider[0] > JOYAXISRANGE/2) - event.data2 = 1; + event.x = 1; } if (JoyInfo2.V) { if (js.rglSlider[1] < -(JOYAXISRANGE/2)) - event.data3 = -1; + event.y = -1; else if (js.rglSlider[1] > JOYAXISRANGE/2) - event.data3 = 1; + event.y = 1; } } else { // analog control style, just send the raw data - if (JoyInfo2.U) event.data2 = js.rglSlider[0]; // U axis - if (JoyInfo2.V) event.data3 = js.rglSlider[1]; // V axis + if (JoyInfo2.U) event.x = js.rglSlider[0]; // U axis + if (JoyInfo2.V) event.y = js.rglSlider[1]; // V axis } D_PostEvent(&event); #endif @@ -3194,7 +3194,7 @@ INT32 I_GetKey(void) ev = &events[eventtail]; eventtail = (eventtail+1) & (MAXEVENTS-1); if (ev->type == ev_keydown || ev->type == ev_console) - return ev->data1; + return ev->key; else return 0; } @@ -3308,7 +3308,7 @@ static VOID I_GetKeyboardEvents(VOID) if (!appActive && RepeatKeyCode) // Stop when lost focus { event.type = ev_keyup; - event.data1 = RepeatKeyCode; + event.key = RepeatKeyCode; D_PostEvent(&event); RepeatKeyCode = 0; } @@ -3363,9 +3363,9 @@ getBufferedData: ch = rgdod[d].dwOfs & 0xFF; if (ASCIINames[ch]) - event.data1 = ASCIINames[ch]; + event.key = ASCIINames[ch]; else - event.data1 = 0x80; + event.key = 0x80; D_PostEvent(&event); } @@ -3378,7 +3378,7 @@ getBufferedData: // delay is tripled for first repeating key RepeatKeyTics = hacktics + (KEY_REPEAT_DELAY*3); if (event.type == ev_keydown) // use the last event! - RepeatKeyCode = event.data1; + RepeatKeyCode = event.key; } else { @@ -3386,7 +3386,7 @@ getBufferedData: if (RepeatKeyCode && hacktics - RepeatKeyTics > KEY_REPEAT_DELAY) { event.type = ev_keydown; - event.data1 = RepeatKeyCode; + event.key = RepeatKeyCode; D_PostEvent(&event); RepeatKeyTics = hacktics; From 824b1ab28cdddfd1c018454a9af14dabaab49fcd Mon Sep 17 00:00:00 2001 From: James R Date: Sat, 14 Aug 2021 15:29:21 -0700 Subject: [PATCH 112/126] Makefile: use full stem in dependency generation Previously took only the filename, so the directory component was stripped. This broke dependencies for basically every file. --- src/Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Makefile b/src/Makefile index ce0e84987..9659a4994 100644 --- a/src/Makefile +++ b/src/Makefile @@ -376,7 +376,7 @@ ifdef Echo_name @printf '%-20.20s\r' $$< endif endif - $(.)$(cc) -MM -MF $$@ -MT $(objdir)/$$(*F).o $(2) $$< + $(.)$(cc) -MM -MF $$@ -MT $(objdir)/$$*.o $(2) $$< endef $(eval $(call _recipe,c)) From 772695741c7935a3798d3e48d0d5c9151df7ae28 Mon Sep 17 00:00:00 2001 From: LJ Sonic Date: Sun, 15 Aug 2021 15:24:23 +0200 Subject: [PATCH 113/126] Rename KeyNumToString to KeyNumToName and vice-versa --- src/console.c | 4 ++-- src/g_input.c | 16 ++++++++-------- src/g_input.h | 4 ++-- src/lua_inputlib.c | 14 +++++++------- src/m_menu.c | 4 ++-- 5 files changed, 21 insertions(+), 21 deletions(-) diff --git a/src/console.c b/src/console.c index 7941e1dbb..d615f3e6b 100644 --- a/src/console.c +++ b/src/console.c @@ -221,7 +221,7 @@ static void CONS_Bind_f(void) for (key = 0; key < NUMINPUTS; key++) if (bindtable[key]) { - CONS_Printf("%s : \"%s\"\n", G_KeyNumToString(key), bindtable[key]); + CONS_Printf("%s : \"%s\"\n", G_KeyNumToName(key), bindtable[key]); na = 1; } if (!na) @@ -229,7 +229,7 @@ static void CONS_Bind_f(void) return; } - key = G_KeyStringToNum(COM_Argv(1)); + key = G_KeyNameToNum(COM_Argv(1)); if (key <= 0 || key >= NUMINPUTS) { CONS_Alert(CONS_NOTICE, M_GetText("Invalid key name\n")); diff --git a/src/g_input.c b/src/g_input.c index e0a27d1ca..494edbd56 100644 --- a/src/g_input.c +++ b/src/g_input.c @@ -624,7 +624,7 @@ void G_ClearAllControlKeys(void) // Returns the name of a key (or virtual key for mouse and joy) // the input value being an keynum // -const char *G_KeyNumToString(INT32 keynum) +const char *G_KeyNumToName(INT32 keynum) { static char keynamestr[8]; @@ -648,7 +648,7 @@ const char *G_KeyNumToString(INT32 keynum) return keynamestr; } -INT32 G_KeyStringToNum(const char *keystr) +INT32 G_KeyNameToNum(const char *keystr) { UINT32 j; @@ -811,10 +811,10 @@ void G_SaveKeySetting(FILE *f, INT32 (*fromcontrols)[2], INT32 (*fromcontrolsbis for (i = 1; i < num_gamecontrols; i++) { fprintf(f, "setcontrol \"%s\" \"%s\"", gamecontrolname[i], - G_KeyNumToString(fromcontrols[i][0])); + G_KeyNumToName(fromcontrols[i][0])); if (fromcontrols[i][1]) - fprintf(f, " \"%s\"\n", G_KeyNumToString(fromcontrols[i][1])); + fprintf(f, " \"%s\"\n", G_KeyNumToName(fromcontrols[i][1])); else fprintf(f, "\n"); } @@ -822,10 +822,10 @@ void G_SaveKeySetting(FILE *f, INT32 (*fromcontrols)[2], INT32 (*fromcontrolsbis for (i = 1; i < num_gamecontrols; i++) { fprintf(f, "setcontrol2 \"%s\" \"%s\"", gamecontrolname[i], - G_KeyNumToString(fromcontrolsbis[i][0])); + G_KeyNumToName(fromcontrolsbis[i][0])); if (fromcontrolsbis[i][1]) - fprintf(f, " \"%s\"\n", G_KeyNumToString(fromcontrolsbis[i][1])); + fprintf(f, " \"%s\"\n", G_KeyNumToName(fromcontrolsbis[i][1])); else fprintf(f, "\n"); } @@ -1001,8 +1001,8 @@ static void setcontrol(INT32 (*gc)[2]) CONS_Printf(M_GetText("Control '%s' unknown\n"), namectrl); return; } - keynum1 = G_KeyStringToNum(COM_Argv(2)); - keynum2 = G_KeyStringToNum(COM_Argv(3)); + keynum1 = G_KeyNameToNum(COM_Argv(2)); + keynum2 = G_KeyNameToNum(COM_Argv(3)); keynum = G_FilterKeyByVersion(numctrl, 0, player, &keynum1, &keynum2, &nestedoverride); if (keynum >= 0) diff --git a/src/g_input.h b/src/g_input.h index ffd0cb560..39c0180e7 100644 --- a/src/g_input.h +++ b/src/g_input.h @@ -181,8 +181,8 @@ extern const INT32 gcl_jump_spin[num_gcl_jump_spin]; void G_MapEventsToControls(event_t *ev); // returns the name of a key -const char *G_KeyNumToString(INT32 keynum); -INT32 G_KeyStringToNum(const char *keystr); +const char *G_KeyNumToName(INT32 keynum); +INT32 G_KeyNameToNum(const char *keystr); // detach any keys associated to the given game control void G_ClearControlKeys(INT32 (*setupcontrols)[2], INT32 control); diff --git a/src/lua_inputlib.c b/src/lua_inputlib.c index 01383a57b..3affeea7b 100644 --- a/src/lua_inputlib.c +++ b/src/lua_inputlib.c @@ -75,17 +75,17 @@ static int lib_joy2Axis(lua_State *L) return 1; } -static int lib_keyNumToString(lua_State *L) +static int lib_keyNumToName(lua_State *L) { int i = luaL_checkinteger(L, 1); - lua_pushstring(L, G_KeyNumToString(i)); + lua_pushstring(L, G_KeyNumToName(i)); return 1; } -static int lib_keyStringToNum(lua_State *L) +static int lib_keyNameToNum(lua_State *L) { const char *str = luaL_checkstring(L, 1); - lua_pushinteger(L, G_KeyStringToNum(str)); + lua_pushinteger(L, G_KeyNameToNum(str)); return 1; } @@ -133,8 +133,8 @@ static luaL_Reg lib[] = { {"gameControl2ToKeyNum", lib_gameControl2ToKeyNum}, {"joyAxis", lib_joyAxis}, {"joy2Axis", lib_joy2Axis}, - {"keyNumToString", lib_keyNumToString}, - {"keyStringToNum", lib_keyStringToNum}, + {"keyNumToName", lib_keyNumToName}, + {"keyNameToNum", lib_keyNameToNum}, {"keyNumPrintable", lib_keyNumPrintable}, {"shiftKeyNum", lib_shiftKeyNum}, {"getMouseGrab", lib_getMouseGrab}, @@ -184,7 +184,7 @@ static int keyevent_get(lua_State *L) I_Assert(event != NULL); if (fastcmp(field,"name")) - lua_pushstring(L, G_KeyNumToString(event->key)); + lua_pushstring(L, G_KeyNumToName(event->key)); else if (fastcmp(field,"num")) lua_pushinteger(L, event->key); else if (fastcmp(field,"repeated")) diff --git a/src/m_menu.c b/src/m_menu.c index 92754705b..4a0fefb1b 100644 --- a/src/m_menu.c +++ b/src/m_menu.c @@ -12826,13 +12826,13 @@ static void M_DrawControl(void) else { if (keys[0] != KEY_NULL) - strcat (tmp, G_KeyNumToString (keys[0])); + strcat (tmp, G_KeyNumToName (keys[0])); if (keys[0] != KEY_NULL && keys[1] != KEY_NULL) strcat(tmp," or "); if (keys[1] != KEY_NULL) - strcat (tmp, G_KeyNumToString (keys[1])); + strcat (tmp, G_KeyNumToName (keys[1])); } From 5340db5f6755fb927b36c33aacd845c11ea5cca4 Mon Sep 17 00:00:00 2001 From: LJ Sonic Date: Sun, 15 Aug 2021 16:15:28 +0200 Subject: [PATCH 114/126] Make gc_ constants uppercase --- src/console.c | 4 +- src/d_clisrv.c | 4 +- src/deh_tables.c | 86 ++++++++--------- src/g_game.c | 62 ++++++------ src/g_input.c | 228 ++++++++++++++++++++++----------------------- src/g_input.h | 94 +++++++++---------- src/hu_stuff.c | 14 +-- src/lua_inputlib.c | 16 ++-- src/m_menu.c | 80 ++++++++-------- src/m_misc.c | 4 +- src/p_user.c | 4 +- 11 files changed, 298 insertions(+), 298 deletions(-) diff --git a/src/console.c b/src/console.c index d615f3e6b..b9348d10e 100644 --- a/src/console.c +++ b/src/console.c @@ -913,7 +913,7 @@ boolean CON_Responder(event_t *ev) // let go keyup events, don't eat them if (ev->type != ev_keydown && ev->type != ev_console) { - if (ev->key == gamecontrol[gc_console][0] || ev->key == gamecontrol[gc_console][1]) + if (ev->key == gamecontrol[GC_CONSOLE][0] || ev->key == gamecontrol[GC_CONSOLE][1]) consdown = false; return false; } @@ -926,7 +926,7 @@ boolean CON_Responder(event_t *ev) if (modeattacking || metalrecording || marathonmode) return false; - if (key == gamecontrol[gc_console][0] || key == gamecontrol[gc_console][1]) + if (key == gamecontrol[GC_CONSOLE][0] || key == gamecontrol[GC_CONSOLE][1]) { if (consdown) // ignore repeat return true; diff --git a/src/d_clisrv.c b/src/d_clisrv.c index 36a0d7df7..326105a3b 100644 --- a/src/d_clisrv.c +++ b/src/d_clisrv.c @@ -663,14 +663,14 @@ static void Snake_Handle(void) UINT16 i; // Handle retry - if (snake->gameover && (PLAYER1INPUTDOWN(gc_jump) || gamekeydown[KEY_ENTER])) + if (snake->gameover && (PLAYER1INPUTDOWN(GC_JUMP) || gamekeydown[KEY_ENTER])) { Snake_Initialise(); snake->pausepressed = true; // Avoid accidental pause on respawn } // Handle pause - if (PLAYER1INPUTDOWN(gc_pause) || gamekeydown[KEY_ENTER]) + if (PLAYER1INPUTDOWN(GC_PAUSE) || gamekeydown[KEY_ENTER]) { if (!snake->pausepressed) snake->paused = !snake->paused; diff --git a/src/deh_tables.c b/src/deh_tables.c index 677b23214..088bc26c0 100644 --- a/src/deh_tables.c +++ b/src/deh_tables.c @@ -5472,49 +5472,49 @@ struct int_const_s const INT_CONST[] = { {"JOYAXISRANGE",JOYAXISRANGE}, // Game controls - {"gc_null",gc_null}, - {"gc_forward",gc_forward}, - {"gc_backward",gc_backward}, - {"gc_strafeleft",gc_strafeleft}, - {"gc_straferight",gc_straferight}, - {"gc_turnleft",gc_turnleft}, - {"gc_turnright",gc_turnright}, - {"gc_weaponnext",gc_weaponnext}, - {"gc_weaponprev",gc_weaponprev}, - {"gc_wepslot1",gc_wepslot1}, - {"gc_wepslot2",gc_wepslot2}, - {"gc_wepslot3",gc_wepslot3}, - {"gc_wepslot4",gc_wepslot4}, - {"gc_wepslot5",gc_wepslot5}, - {"gc_wepslot6",gc_wepslot6}, - {"gc_wepslot7",gc_wepslot7}, - {"gc_wepslot8",gc_wepslot8}, - {"gc_wepslot9",gc_wepslot9}, - {"gc_wepslot10",gc_wepslot10}, - {"gc_fire",gc_fire}, - {"gc_firenormal",gc_firenormal}, - {"gc_tossflag",gc_tossflag}, - {"gc_spin",gc_spin}, - {"gc_camtoggle",gc_camtoggle}, - {"gc_camreset",gc_camreset}, - {"gc_lookup",gc_lookup}, - {"gc_lookdown",gc_lookdown}, - {"gc_centerview",gc_centerview}, - {"gc_mouseaiming",gc_mouseaiming}, - {"gc_talkkey",gc_talkkey}, - {"gc_teamkey",gc_teamkey}, - {"gc_scores",gc_scores}, - {"gc_jump",gc_jump}, - {"gc_console",gc_console}, - {"gc_pause",gc_pause}, - {"gc_systemmenu",gc_systemmenu}, - {"gc_screenshot",gc_screenshot}, - {"gc_recordgif",gc_recordgif}, - {"gc_viewpoint",gc_viewpoint}, - {"gc_custom1",gc_custom1}, - {"gc_custom2",gc_custom2}, - {"gc_custom3",gc_custom3}, - {"num_gamecontrols",num_gamecontrols}, + {"GC_NULL",GC_NULL}, + {"GC_FORWARD",GC_FORWARD}, + {"GC_BACKWARD",GC_BACKWARD}, + {"GC_STRAFELEFT",GC_STRAFELEFT}, + {"GC_STRAFERIGHT",GC_STRAFERIGHT}, + {"GC_TURNLEFT",GC_TURNLEFT}, + {"GC_TURNRIGHT",GC_TURNRIGHT}, + {"GC_WEAPONNEXT",GC_WEAPONNEXT}, + {"GC_WEAPONPREV",GC_WEAPONPREV}, + {"GC_WEPSLOT1",GC_WEPSLOT1}, + {"GC_WEPSLOT2",GC_WEPSLOT2}, + {"GC_WEPSLOT3",GC_WEPSLOT3}, + {"GC_WEPSLOT4",GC_WEPSLOT4}, + {"GC_WEPSLOT5",GC_WEPSLOT5}, + {"GC_WEPSLOT6",GC_WEPSLOT6}, + {"GC_WEPSLOT7",GC_WEPSLOT7}, + {"GC_WEPSLOT8",GC_WEPSLOT8}, + {"GC_WEPSLOT9",GC_WEPSLOT9}, + {"GC_WEPSLOT10",GC_WEPSLOT10}, + {"GC_FIRE",GC_FIRE}, + {"GC_FIRENORMAL",GC_FIRENORMAL}, + {"GC_TOSSFLAG",GC_TOSSFLAG}, + {"GC_SPIN",GC_SPIN}, + {"GC_CAMTOGGLE",GC_CAMTOGGLE}, + {"GC_CAMRESET",GC_CAMRESET}, + {"GC_LOOKUP",GC_LOOKUP}, + {"GC_LOOKDOWN",GC_LOOKDOWN}, + {"GC_CENTERVIEW",GC_CENTERVIEW}, + {"GC_MOUSEAIMING",GC_MOUSEAIMING}, + {"GC_TALKKEY",GC_TALKKEY}, + {"GC_TEAMKEY",GC_TEAMKEY}, + {"GC_SCORES",GC_SCORES}, + {"GC_JUMP",GC_JUMP}, + {"GC_CONSOLE",GC_CONSOLE}, + {"GC_PAUSE",GC_PAUSE}, + {"GC_SYSTEMMENU",GC_SYSTEMMENU}, + {"GC_SCREENSHOT",GC_SCREENSHOT}, + {"GC_RECORDGIF",GC_RECORDGIF}, + {"GC_VIEWPOINT",GC_VIEWPOINT}, + {"GC_CUSTOM1",GC_CUSTOM1}, + {"GC_CUSTOM2",GC_CUSTOM2}, + {"GC_CUSTOM3",GC_CUSTOM3}, + {"NUM_GAMECONTROLS",NUM_GAMECONTROLS}, // Mouse buttons {"MB_BUTTON1",MB_BUTTON1}, diff --git a/src/g_game.c b/src/g_game.c index 5c89bcae8..bc86e255a 100644 --- a/src/g_game.c +++ b/src/g_game.c @@ -1140,13 +1140,13 @@ void G_BuildTiccmd(ticcmd_t *cmd, INT32 realtics, UINT8 ssplayer) return; } - turnright = PLAYERINPUTDOWN(ssplayer, gc_turnright); - turnleft = PLAYERINPUTDOWN(ssplayer, gc_turnleft); + turnright = PLAYERINPUTDOWN(ssplayer, GC_TURNRIGHT); + turnleft = PLAYERINPUTDOWN(ssplayer, GC_TURNLEFT); - straferkey = PLAYERINPUTDOWN(ssplayer, gc_straferight); - strafelkey = PLAYERINPUTDOWN(ssplayer, gc_strafeleft); - movefkey = PLAYERINPUTDOWN(ssplayer, gc_forward); - movebkey = PLAYERINPUTDOWN(ssplayer, gc_backward); + straferkey = PLAYERINPUTDOWN(ssplayer, GC_STRAFERIGHT); + strafelkey = PLAYERINPUTDOWN(ssplayer, GC_STRAFELEFT); + movefkey = PLAYERINPUTDOWN(ssplayer, GC_FORWARD); + movebkey = PLAYERINPUTDOWN(ssplayer, GC_BACKWARD); if (strafeisturn) { @@ -1155,7 +1155,7 @@ void G_BuildTiccmd(ticcmd_t *cmd, INT32 realtics, UINT8 ssplayer) straferkey = strafelkey = false; } - mouseaiming = (PLAYERINPUTDOWN(ssplayer, gc_mouseaiming)) ^ + mouseaiming = (PLAYERINPUTDOWN(ssplayer, GC_MOUSEAIMING)) ^ ((chasecam && !player->spectator) ? chasefreelook : alwaysfreelook); analogjoystickmove = usejoystick && !Joystick.bGamepadStyle; gamepadjoystickmove = usejoystick && Joystick.bGamepadStyle; @@ -1271,11 +1271,11 @@ void G_BuildTiccmd(ticcmd_t *cmd, INT32 realtics, UINT8 ssplayer) // forward with key or button if (movefkey || (gamepadjoystickmove && movejoystickvector.yaxis < 0) || ((player->powers[pw_carry] == CR_NIGHTSMODE) - && (PLAYERINPUTDOWN(ssplayer, gc_lookup) || (gamepadjoystickmove && lookjoystickvector.yaxis > 0)))) + && (PLAYERINPUTDOWN(ssplayer, GC_LOOKUP) || (gamepadjoystickmove && lookjoystickvector.yaxis > 0)))) forward = forwardmove[speed]; if (movebkey || (gamepadjoystickmove && movejoystickvector.yaxis > 0) || ((player->powers[pw_carry] == CR_NIGHTSMODE) - && (PLAYERINPUTDOWN(ssplayer, gc_lookdown) || (gamepadjoystickmove && lookjoystickvector.yaxis < 0)))) + && (PLAYERINPUTDOWN(ssplayer, GC_LOOKDOWN) || (gamepadjoystickmove && lookjoystickvector.yaxis < 0)))) forward -= forwardmove[speed]; if (analogjoystickmove && movejoystickvector.yaxis != 0) @@ -1288,9 +1288,9 @@ void G_BuildTiccmd(ticcmd_t *cmd, INT32 realtics, UINT8 ssplayer) if (strafelkey) side -= sidemove[speed]; - if (PLAYERINPUTDOWN(ssplayer, gc_weaponnext)) + if (PLAYERINPUTDOWN(ssplayer, GC_WEAPONNEXT)) cmd->buttons |= BT_WEAPONNEXT; // Next Weapon - if (PLAYERINPUTDOWN(ssplayer, gc_weaponprev)) + if (PLAYERINPUTDOWN(ssplayer, GC_WEAPONPREV)) cmd->buttons |= BT_WEAPONPREV; // Previous Weapon #if NUM_WEAPONS > 10 @@ -1299,7 +1299,7 @@ void G_BuildTiccmd(ticcmd_t *cmd, INT32 realtics, UINT8 ssplayer) //use the four avaliable bits to determine the weapon. cmd->buttons &= ~BT_WEAPONMASK; for (i = 0; i < NUM_WEAPONS; ++i) - if (PLAYERINPUTDOWN(ssplayer, gc_wepslot1 + i)) + if (PLAYERINPUTDOWN(ssplayer, GC_WEPSLOT1 + i)) { cmd->buttons |= (UINT16)(i + 1); break; @@ -1307,34 +1307,34 @@ void G_BuildTiccmd(ticcmd_t *cmd, INT32 realtics, UINT8 ssplayer) // fire with any button/key axis = PlayerJoyAxis(ssplayer, JA_FIRE); - if (PLAYERINPUTDOWN(ssplayer, gc_fire) || (usejoystick && axis > 0)) + if (PLAYERINPUTDOWN(ssplayer, GC_FIRE) || (usejoystick && axis > 0)) cmd->buttons |= BT_ATTACK; // fire normal with any button/key axis = PlayerJoyAxis(ssplayer, JA_FIRENORMAL); - if (PLAYERINPUTDOWN(ssplayer, gc_firenormal) || (usejoystick && axis > 0)) + if (PLAYERINPUTDOWN(ssplayer, GC_FIRENORMAL) || (usejoystick && axis > 0)) cmd->buttons |= BT_FIRENORMAL; - if (PLAYERINPUTDOWN(ssplayer, gc_tossflag)) + if (PLAYERINPUTDOWN(ssplayer, GC_TOSSFLAG)) cmd->buttons |= BT_TOSSFLAG; // Lua scriptable buttons - if (PLAYERINPUTDOWN(ssplayer, gc_custom1)) + if (PLAYERINPUTDOWN(ssplayer, GC_CUSTOM1)) cmd->buttons |= BT_CUSTOM1; - if (PLAYERINPUTDOWN(ssplayer, gc_custom2)) + if (PLAYERINPUTDOWN(ssplayer, GC_CUSTOM2)) cmd->buttons |= BT_CUSTOM2; - if (PLAYERINPUTDOWN(ssplayer, gc_custom3)) + if (PLAYERINPUTDOWN(ssplayer, GC_CUSTOM3)) cmd->buttons |= BT_CUSTOM3; // use with any button/key axis = PlayerJoyAxis(ssplayer, JA_SPIN); - if (PLAYERINPUTDOWN(ssplayer, gc_spin) || (usejoystick && axis > 0)) + if (PLAYERINPUTDOWN(ssplayer, GC_SPIN) || (usejoystick && axis > 0)) cmd->buttons |= BT_SPIN; // Centerview can be a toggle in simple mode! { static boolean last_centerviewdown[2], centerviewhold[2]; // detect taps for toggle behavior - boolean down = PLAYERINPUTDOWN(ssplayer, gc_centerview); + boolean down = PLAYERINPUTDOWN(ssplayer, GC_CENTERVIEW); if (!(controlstyle == CS_SIMPLE && cv_cam_centertoggle[forplayer].value)) centerviewdown = down; @@ -1433,7 +1433,7 @@ void G_BuildTiccmd(ticcmd_t *cmd, INT32 realtics, UINT8 ssplayer) if (ticcmd_centerviewdown[forplayer] && controlstyle == CS_SIMPLE) controlstyle = CS_LEGACY; - if (PLAYERINPUTDOWN(ssplayer, gc_camreset)) + if (PLAYERINPUTDOWN(ssplayer, GC_CAMRESET)) { if (thiscam->chase && !resetdown[forplayer]) P_ResetCamera(&players[ssplayer == 1 ? displayplayer : secondarydisplayplayer], thiscam); @@ -1446,7 +1446,7 @@ void G_BuildTiccmd(ticcmd_t *cmd, INT32 realtics, UINT8 ssplayer) // jump button axis = PlayerJoyAxis(ssplayer, JA_JUMP); - if (PLAYERINPUTDOWN(ssplayer, gc_jump) || (usejoystick && axis > 0)) + if (PLAYERINPUTDOWN(ssplayer, GC_JUMP) || (usejoystick && axis > 0)) cmd->buttons |= BT_JUMP; // player aiming shit, ahhhh... @@ -1476,12 +1476,12 @@ void G_BuildTiccmd(ticcmd_t *cmd, INT32 realtics, UINT8 ssplayer) if (!(player->powers[pw_carry] == CR_NIGHTSMODE)) { - if (PLAYERINPUTDOWN(ssplayer, gc_lookup) || (gamepadjoystickmove && lookjoystickvector.yaxis < 0)) + if (PLAYERINPUTDOWN(ssplayer, GC_LOOKUP) || (gamepadjoystickmove && lookjoystickvector.yaxis < 0)) { *myaiming += KB_LOOKSPEED * screen_invert; keyboard_look[forplayer] = true; } - else if (PLAYERINPUTDOWN(ssplayer, gc_lookdown) || (gamepadjoystickmove && lookjoystickvector.yaxis > 0)) + else if (PLAYERINPUTDOWN(ssplayer, GC_LOOKDOWN) || (gamepadjoystickmove && lookjoystickvector.yaxis > 0)) { *myaiming -= KB_LOOKSPEED * screen_invert; keyboard_look[forplayer] = true; @@ -2045,7 +2045,7 @@ boolean G_Responder(event_t *ev) // allow spy mode changes even during the demo if (gamestate == GS_LEVEL && ev->type == ev_keydown - && (ev->key == KEY_F12 || ev->key == gamecontrol[gc_viewpoint][0] || ev->key == gamecontrol[gc_viewpoint][1])) + && (ev->key == KEY_F12 || ev->key == gamecontrol[GC_VIEWPOINT][0] || ev->key == gamecontrol[GC_VIEWPOINT][1])) { // ViewpointSwitch Lua hook. UINT8 canSwitchView = 0; @@ -2118,8 +2118,8 @@ boolean G_Responder(event_t *ev) switch (ev->type) { case ev_keydown: - if (ev->key == gamecontrol[gc_pause][0] - || ev->key == gamecontrol[gc_pause][1] + if (ev->key == gamecontrol[GC_PAUSE][0] + || ev->key == gamecontrol[GC_PAUSE][1] || ev->key == KEY_PAUSE) { if (modeattacking && !demoplayback && (gamestate == GS_LEVEL)) @@ -2149,8 +2149,8 @@ boolean G_Responder(event_t *ev) } } } - if (ev->key == gamecontrol[gc_camtoggle][0] - || ev->key == gamecontrol[gc_camtoggle][1]) + if (ev->key == gamecontrol[GC_CAMTOGGLE][0] + || ev->key == gamecontrol[GC_CAMTOGGLE][1]) { if (!camtoggledelay) { @@ -2158,8 +2158,8 @@ boolean G_Responder(event_t *ev) CV_SetValue(&cv_chasecam, cv_chasecam.value ? 0 : 1); } } - if (ev->key == gamecontrolbis[gc_camtoggle][0] - || ev->key == gamecontrolbis[gc_camtoggle][1]) + if (ev->key == gamecontrolbis[GC_CAMTOGGLE][0] + || ev->key == gamecontrolbis[GC_CAMTOGGLE][1]) { if (!camtoggledelay2) { diff --git a/src/g_input.c b/src/g_input.c index 494edbd56..cd4536bba 100644 --- a/src/g_input.c +++ b/src/g_input.c @@ -41,49 +41,49 @@ INT32 joyxmove[JOYAXISSET], joyymove[JOYAXISSET], joy2xmove[JOYAXISSET], joy2ymo UINT8 gamekeydown[NUMINPUTS]; // two key codes (or virtual key) per game control -INT32 gamecontrol[num_gamecontrols][2]; -INT32 gamecontrolbis[num_gamecontrols][2]; // secondary splitscreen player -INT32 gamecontroldefault[num_gamecontrolschemes][num_gamecontrols][2]; // default control storage, use 0 (gcs_custom) for memory retention -INT32 gamecontrolbisdefault[num_gamecontrolschemes][num_gamecontrols][2]; +INT32 gamecontrol[NUM_GAMECONTROLS][2]; +INT32 gamecontrolbis[NUM_GAMECONTROLS][2]; // secondary splitscreen player +INT32 gamecontroldefault[num_gamecontrolschemes][NUM_GAMECONTROLS][2]; // default control storage, use 0 (gcs_custom) for memory retention +INT32 gamecontrolbisdefault[num_gamecontrolschemes][NUM_GAMECONTROLS][2]; // lists of GC codes for selective operation const INT32 gcl_tutorial_check[num_gcl_tutorial_check] = { - gc_forward, gc_backward, gc_strafeleft, gc_straferight, - gc_turnleft, gc_turnright + GC_FORWARD, GC_BACKWARD, GC_STRAFELEFT, GC_STRAFERIGHT, + GC_TURNLEFT, GC_TURNRIGHT }; const INT32 gcl_tutorial_used[num_gcl_tutorial_used] = { - gc_forward, gc_backward, gc_strafeleft, gc_straferight, - gc_turnleft, gc_turnright, - gc_jump, gc_spin + GC_FORWARD, GC_BACKWARD, GC_STRAFELEFT, GC_STRAFERIGHT, + GC_TURNLEFT, GC_TURNRIGHT, + GC_JUMP, GC_SPIN }; const INT32 gcl_tutorial_full[num_gcl_tutorial_full] = { - gc_forward, gc_backward, gc_strafeleft, gc_straferight, - gc_lookup, gc_lookdown, gc_turnleft, gc_turnright, gc_centerview, - gc_jump, gc_spin, - gc_fire, gc_firenormal + GC_FORWARD, GC_BACKWARD, GC_STRAFELEFT, GC_STRAFERIGHT, + GC_LOOKUP, GC_LOOKDOWN, GC_TURNLEFT, GC_TURNRIGHT, GC_CENTERVIEW, + GC_JUMP, GC_SPIN, + GC_FIRE, GC_FIRENORMAL }; const INT32 gcl_movement[num_gcl_movement] = { - gc_forward, gc_backward, gc_strafeleft, gc_straferight + GC_FORWARD, GC_BACKWARD, GC_STRAFELEFT, GC_STRAFERIGHT }; const INT32 gcl_camera[num_gcl_camera] = { - gc_turnleft, gc_turnright + GC_TURNLEFT, GC_TURNRIGHT }; const INT32 gcl_movement_camera[num_gcl_movement_camera] = { - gc_forward, gc_backward, gc_strafeleft, gc_straferight, - gc_turnleft, gc_turnright + GC_FORWARD, GC_BACKWARD, GC_STRAFELEFT, GC_STRAFERIGHT, + GC_TURNLEFT, GC_TURNRIGHT }; -const INT32 gcl_jump[num_gcl_jump] = { gc_jump }; +const INT32 gcl_jump[num_gcl_jump] = { GC_JUMP }; -const INT32 gcl_spin[num_gcl_spin] = { gc_spin }; +const INT32 gcl_spin[num_gcl_spin] = { GC_SPIN }; const INT32 gcl_jump_spin[num_gcl_jump_spin] = { - gc_jump, gc_spin + GC_JUMP, GC_SPIN }; typedef struct @@ -553,9 +553,9 @@ static keyname_t keynames[] = }; -static const char *gamecontrolname[num_gamecontrols] = +static const char *gamecontrolname[NUM_GAMECONTROLS] = { - "nothing", // a key/button mapped to gc_null has no effect + "nothing", // a key/button mapped to GC_NULL has no effect "forward", "backward", "strafeleft", @@ -613,7 +613,7 @@ void G_ClearControlKeys(INT32 (*setupcontrols)[2], INT32 control) void G_ClearAllControlKeys(void) { INT32 i; - for (i = 0; i < num_gamecontrols; i++) + for (i = 0; i < NUM_GAMECONTROLS; i++) { G_ClearControlKeys(gamecontrol, i); G_ClearControlKeys(gamecontrolbis, i); @@ -676,92 +676,92 @@ void G_DefineDefaultControls(void) INT32 i; // FPS game controls (WASD) - gamecontroldefault[gcs_fps][gc_forward ][0] = 'w'; - gamecontroldefault[gcs_fps][gc_backward ][0] = 's'; - gamecontroldefault[gcs_fps][gc_strafeleft ][0] = 'a'; - gamecontroldefault[gcs_fps][gc_straferight][0] = 'd'; - gamecontroldefault[gcs_fps][gc_lookup ][0] = KEY_UPARROW; - gamecontroldefault[gcs_fps][gc_lookdown ][0] = KEY_DOWNARROW; - gamecontroldefault[gcs_fps][gc_turnleft ][0] = KEY_LEFTARROW; - gamecontroldefault[gcs_fps][gc_turnright ][0] = KEY_RIGHTARROW; - gamecontroldefault[gcs_fps][gc_centerview ][0] = KEY_END; - gamecontroldefault[gcs_fps][gc_jump ][0] = KEY_SPACE; - gamecontroldefault[gcs_fps][gc_spin ][0] = KEY_LSHIFT; - gamecontroldefault[gcs_fps][gc_fire ][0] = KEY_RCTRL; - gamecontroldefault[gcs_fps][gc_fire ][1] = KEY_MOUSE1+0; - gamecontroldefault[gcs_fps][gc_firenormal ][0] = 'c'; + gamecontroldefault[gcs_fps][GC_FORWARD ][0] = 'w'; + gamecontroldefault[gcs_fps][GC_BACKWARD ][0] = 's'; + gamecontroldefault[gcs_fps][GC_STRAFELEFT ][0] = 'a'; + gamecontroldefault[gcs_fps][GC_STRAFERIGHT][0] = 'd'; + gamecontroldefault[gcs_fps][GC_LOOKUP ][0] = KEY_UPARROW; + gamecontroldefault[gcs_fps][GC_LOOKDOWN ][0] = KEY_DOWNARROW; + gamecontroldefault[gcs_fps][GC_TURNLEFT ][0] = KEY_LEFTARROW; + gamecontroldefault[gcs_fps][GC_TURNRIGHT ][0] = KEY_RIGHTARROW; + gamecontroldefault[gcs_fps][GC_CENTERVIEW ][0] = KEY_END; + gamecontroldefault[gcs_fps][GC_JUMP ][0] = KEY_SPACE; + gamecontroldefault[gcs_fps][GC_SPIN ][0] = KEY_LSHIFT; + gamecontroldefault[gcs_fps][GC_FIRE ][0] = KEY_RCTRL; + gamecontroldefault[gcs_fps][GC_FIRE ][1] = KEY_MOUSE1+0; + gamecontroldefault[gcs_fps][GC_FIRENORMAL ][0] = 'c'; // Platform game controls (arrow keys) - gamecontroldefault[gcs_platform][gc_forward ][0] = KEY_UPARROW; - gamecontroldefault[gcs_platform][gc_backward ][0] = KEY_DOWNARROW; - gamecontroldefault[gcs_platform][gc_strafeleft ][0] = 'a'; - gamecontroldefault[gcs_platform][gc_straferight][0] = 'd'; - gamecontroldefault[gcs_platform][gc_lookup ][0] = KEY_PGUP; - gamecontroldefault[gcs_platform][gc_lookdown ][0] = KEY_PGDN; - gamecontroldefault[gcs_platform][gc_turnleft ][0] = KEY_LEFTARROW; - gamecontroldefault[gcs_platform][gc_turnright ][0] = KEY_RIGHTARROW; - gamecontroldefault[gcs_platform][gc_centerview ][0] = KEY_END; - gamecontroldefault[gcs_platform][gc_jump ][0] = KEY_SPACE; - gamecontroldefault[gcs_platform][gc_spin ][0] = KEY_LSHIFT; - gamecontroldefault[gcs_platform][gc_fire ][0] = 's'; - gamecontroldefault[gcs_platform][gc_fire ][1] = KEY_MOUSE1+0; - gamecontroldefault[gcs_platform][gc_firenormal ][0] = 'w'; + gamecontroldefault[gcs_platform][GC_FORWARD ][0] = KEY_UPARROW; + gamecontroldefault[gcs_platform][GC_BACKWARD ][0] = KEY_DOWNARROW; + gamecontroldefault[gcs_platform][GC_STRAFELEFT ][0] = 'a'; + gamecontroldefault[gcs_platform][GC_STRAFERIGHT][0] = 'd'; + gamecontroldefault[gcs_platform][GC_LOOKUP ][0] = KEY_PGUP; + gamecontroldefault[gcs_platform][GC_LOOKDOWN ][0] = KEY_PGDN; + gamecontroldefault[gcs_platform][GC_TURNLEFT ][0] = KEY_LEFTARROW; + gamecontroldefault[gcs_platform][GC_TURNRIGHT ][0] = KEY_RIGHTARROW; + gamecontroldefault[gcs_platform][GC_CENTERVIEW ][0] = KEY_END; + gamecontroldefault[gcs_platform][GC_JUMP ][0] = KEY_SPACE; + gamecontroldefault[gcs_platform][GC_SPIN ][0] = KEY_LSHIFT; + gamecontroldefault[gcs_platform][GC_FIRE ][0] = 's'; + gamecontroldefault[gcs_platform][GC_FIRE ][1] = KEY_MOUSE1+0; + gamecontroldefault[gcs_platform][GC_FIRENORMAL ][0] = 'w'; for (i = 1; i < num_gamecontrolschemes; i++) // skip gcs_custom (0) { - gamecontroldefault[i][gc_weaponnext ][0] = KEY_MOUSEWHEELUP+0; - gamecontroldefault[i][gc_weaponprev ][0] = KEY_MOUSEWHEELDOWN+0; - gamecontroldefault[i][gc_wepslot1 ][0] = '1'; - gamecontroldefault[i][gc_wepslot2 ][0] = '2'; - gamecontroldefault[i][gc_wepslot3 ][0] = '3'; - gamecontroldefault[i][gc_wepslot4 ][0] = '4'; - gamecontroldefault[i][gc_wepslot5 ][0] = '5'; - gamecontroldefault[i][gc_wepslot6 ][0] = '6'; - gamecontroldefault[i][gc_wepslot7 ][0] = '7'; - gamecontroldefault[i][gc_wepslot8 ][0] = '8'; - gamecontroldefault[i][gc_wepslot9 ][0] = '9'; - gamecontroldefault[i][gc_wepslot10 ][0] = '0'; - gamecontroldefault[i][gc_tossflag ][0] = '\''; - gamecontroldefault[i][gc_camtoggle ][0] = 'v'; - gamecontroldefault[i][gc_camreset ][0] = 'r'; - gamecontroldefault[i][gc_talkkey ][0] = 't'; - gamecontroldefault[i][gc_teamkey ][0] = 'y'; - gamecontroldefault[i][gc_scores ][0] = KEY_TAB; - gamecontroldefault[i][gc_console ][0] = KEY_CONSOLE; - gamecontroldefault[i][gc_pause ][0] = 'p'; - gamecontroldefault[i][gc_screenshot ][0] = KEY_F8; - gamecontroldefault[i][gc_recordgif ][0] = KEY_F9; - gamecontroldefault[i][gc_viewpoint ][0] = KEY_F12; + gamecontroldefault[i][GC_WEAPONNEXT ][0] = KEY_MOUSEWHEELUP+0; + gamecontroldefault[i][GC_WEAPONPREV ][0] = KEY_MOUSEWHEELDOWN+0; + gamecontroldefault[i][GC_WEPSLOT1 ][0] = '1'; + gamecontroldefault[i][GC_WEPSLOT2 ][0] = '2'; + gamecontroldefault[i][GC_WEPSLOT3 ][0] = '3'; + gamecontroldefault[i][GC_WEPSLOT4 ][0] = '4'; + gamecontroldefault[i][GC_WEPSLOT5 ][0] = '5'; + gamecontroldefault[i][GC_WEPSLOT6 ][0] = '6'; + gamecontroldefault[i][GC_WEPSLOT7 ][0] = '7'; + gamecontroldefault[i][GC_WEPSLOT8 ][0] = '8'; + gamecontroldefault[i][GC_WEPSLOT9 ][0] = '9'; + gamecontroldefault[i][GC_WEPSLOT10 ][0] = '0'; + gamecontroldefault[i][GC_TOSSFLAG ][0] = '\''; + gamecontroldefault[i][GC_CAMTOGGLE ][0] = 'v'; + gamecontroldefault[i][GC_CAMRESET ][0] = 'r'; + gamecontroldefault[i][GC_TALKKEY ][0] = 't'; + gamecontroldefault[i][GC_TEAMKEY ][0] = 'y'; + gamecontroldefault[i][GC_SCORES ][0] = KEY_TAB; + gamecontroldefault[i][GC_CONSOLE ][0] = KEY_CONSOLE; + gamecontroldefault[i][GC_PAUSE ][0] = 'p'; + gamecontroldefault[i][GC_SCREENSHOT ][0] = KEY_F8; + gamecontroldefault[i][GC_RECORDGIF ][0] = KEY_F9; + gamecontroldefault[i][GC_VIEWPOINT ][0] = KEY_F12; // Gamepad controls -- same for both schemes - gamecontroldefault[i][gc_weaponnext ][1] = KEY_JOY1+1; // B - gamecontroldefault[i][gc_weaponprev ][1] = KEY_JOY1+2; // X - gamecontroldefault[i][gc_tossflag ][1] = KEY_JOY1+0; // A - gamecontroldefault[i][gc_spin ][1] = KEY_JOY1+4; // LB - gamecontroldefault[i][gc_camtoggle ][1] = KEY_HAT1+0; // D-Pad Up - gamecontroldefault[i][gc_camreset ][1] = KEY_JOY1+3; // Y - gamecontroldefault[i][gc_centerview ][1] = KEY_JOY1+9; // Right Stick - gamecontroldefault[i][gc_talkkey ][1] = KEY_HAT1+2; // D-Pad Left - gamecontroldefault[i][gc_scores ][1] = KEY_HAT1+3; // D-Pad Right - gamecontroldefault[i][gc_jump ][1] = KEY_JOY1+5; // RB - gamecontroldefault[i][gc_pause ][1] = KEY_JOY1+6; // Back - gamecontroldefault[i][gc_screenshot ][1] = KEY_HAT1+1; // D-Pad Down - gamecontroldefault[i][gc_systemmenu ][0] = KEY_JOY1+7; // Start + gamecontroldefault[i][GC_WEAPONNEXT ][1] = KEY_JOY1+1; // B + gamecontroldefault[i][GC_WEAPONPREV ][1] = KEY_JOY1+2; // X + gamecontroldefault[i][GC_TOSSFLAG ][1] = KEY_JOY1+0; // A + gamecontroldefault[i][GC_SPIN ][1] = KEY_JOY1+4; // LB + gamecontroldefault[i][GC_CAMTOGGLE ][1] = KEY_HAT1+0; // D-Pad Up + gamecontroldefault[i][GC_CAMRESET ][1] = KEY_JOY1+3; // Y + gamecontroldefault[i][GC_CENTERVIEW ][1] = KEY_JOY1+9; // Right Stick + gamecontroldefault[i][GC_TALKKEY ][1] = KEY_HAT1+2; // D-Pad Left + gamecontroldefault[i][GC_SCORES ][1] = KEY_HAT1+3; // D-Pad Right + gamecontroldefault[i][GC_JUMP ][1] = KEY_JOY1+5; // RB + gamecontroldefault[i][GC_PAUSE ][1] = KEY_JOY1+6; // Back + gamecontroldefault[i][GC_SCREENSHOT ][1] = KEY_HAT1+1; // D-Pad Down + gamecontroldefault[i][GC_SYSTEMMENU ][0] = KEY_JOY1+7; // Start // Second player controls only have joypad defaults - gamecontrolbisdefault[i][gc_weaponnext][0] = KEY_2JOY1+1; // B - gamecontrolbisdefault[i][gc_weaponprev][0] = KEY_2JOY1+2; // X - gamecontrolbisdefault[i][gc_tossflag ][0] = KEY_2JOY1+0; // A - gamecontrolbisdefault[i][gc_spin ][0] = KEY_2JOY1+4; // LB - gamecontrolbisdefault[i][gc_camreset ][0] = KEY_2JOY1+3; // Y - gamecontrolbisdefault[i][gc_centerview][0] = KEY_2JOY1+9; // Right Stick - gamecontrolbisdefault[i][gc_jump ][0] = KEY_2JOY1+5; // RB - //gamecontrolbisdefault[i][gc_pause ][0] = KEY_2JOY1+6; // Back - //gamecontrolbisdefault[i][gc_systemmenu][0] = KEY_2JOY1+7; // Start - gamecontrolbisdefault[i][gc_camtoggle ][0] = KEY_2HAT1+0; // D-Pad Up - gamecontrolbisdefault[i][gc_screenshot][0] = KEY_2HAT1+1; // D-Pad Down - //gamecontrolbisdefault[i][gc_talkkey ][0] = KEY_2HAT1+2; // D-Pad Left - //gamecontrolbisdefault[i][gc_scores ][0] = KEY_2HAT1+3; // D-Pad Right + gamecontrolbisdefault[i][GC_WEAPONNEXT][0] = KEY_2JOY1+1; // B + gamecontrolbisdefault[i][GC_WEAPONPREV][0] = KEY_2JOY1+2; // X + gamecontrolbisdefault[i][GC_TOSSFLAG ][0] = KEY_2JOY1+0; // A + gamecontrolbisdefault[i][GC_SPIN ][0] = KEY_2JOY1+4; // LB + gamecontrolbisdefault[i][GC_CAMRESET ][0] = KEY_2JOY1+3; // Y + gamecontrolbisdefault[i][GC_CENTERVIEW][0] = KEY_2JOY1+9; // Right Stick + gamecontrolbisdefault[i][GC_JUMP ][0] = KEY_2JOY1+5; // RB + //gamecontrolbisdefault[i][GC_PAUSE ][0] = KEY_2JOY1+6; // Back + //gamecontrolbisdefault[i][GC_SYSTEMMENU][0] = KEY_2JOY1+7; // Start + gamecontrolbisdefault[i][GC_CAMTOGGLE ][0] = KEY_2HAT1+0; // D-Pad Up + gamecontrolbisdefault[i][GC_SCREENSHOT][0] = KEY_2HAT1+1; // D-Pad Down + //gamecontrolbisdefault[i][GC_TALKKEY ][0] = KEY_2HAT1+2; // D-Pad Left + //gamecontrolbisdefault[i][GC_SCORES ][0] = KEY_2HAT1+3; // D-Pad Right } } @@ -773,7 +773,7 @@ INT32 G_GetControlScheme(INT32 (*fromcontrols)[2], const INT32 *gclist, INT32 gc for (i = 1; i < num_gamecontrolschemes; i++) // skip gcs_custom (0) { skipscheme = false; - for (j = 0; j < (gclist && gclen ? gclen : num_gamecontrols); j++) + for (j = 0; j < (gclist && gclen ? gclen : NUM_GAMECONTROLS); j++) { gc = (gclist && gclen) ? gclist[j] : j; if (((fromcontrols[gc][0] && gamecontroldefault[i][gc][0]) ? fromcontrols[gc][0] != gamecontroldefault[i][gc][0] : true) && @@ -796,7 +796,7 @@ void G_CopyControls(INT32 (*setupcontrols)[2], INT32 (*fromcontrols)[2], const I { INT32 i, gc; - for (i = 0; i < (gclist && gclen ? gclen : num_gamecontrols); i++) + for (i = 0; i < (gclist && gclen ? gclen : NUM_GAMECONTROLS); i++) { gc = (gclist && gclen) ? gclist[i] : i; setupcontrols[gc][0] = fromcontrols[gc][0]; @@ -808,7 +808,7 @@ void G_SaveKeySetting(FILE *f, INT32 (*fromcontrols)[2], INT32 (*fromcontrolsbis { INT32 i; - for (i = 1; i < num_gamecontrols; i++) + for (i = 1; i < NUM_GAMECONTROLS; i++) { fprintf(f, "setcontrol \"%s\" \"%s\"", gamecontrolname[i], G_KeyNumToName(fromcontrols[i][0])); @@ -819,7 +819,7 @@ void G_SaveKeySetting(FILE *f, INT32 (*fromcontrols)[2], INT32 (*fromcontrolsbis fprintf(f, "\n"); } - for (i = 1; i < num_gamecontrols; i++) + for (i = 1; i < NUM_GAMECONTROLS; i++) { fprintf(f, "setcontrol2 \"%s\" \"%s\"", gamecontrolname[i], G_KeyNumToName(fromcontrolsbis[i][0])); @@ -833,11 +833,11 @@ void G_SaveKeySetting(FILE *f, INT32 (*fromcontrols)[2], INT32 (*fromcontrolsbis INT32 G_CheckDoubleUsage(INT32 keynum, boolean modify) { - INT32 result = gc_null; + INT32 result = GC_NULL; if (cv_controlperkey.value == 1) { INT32 i; - for (i = 0; i < num_gamecontrols; i++) + for (i = 0; i < NUM_GAMECONTROLS; i++) { if (gamecontrol[i][0] == keynum) { @@ -883,11 +883,11 @@ static INT32 G_FilterKeyByVersion(INT32 numctrl, INT32 keyidx, INT32 player, INT return -1; // skip setting control if (GETMAJOREXECVERSION(cv_execversion.value) < 27 && ( // v2.1.22 - numctrl == gc_weaponnext || numctrl == gc_weaponprev || numctrl == gc_tossflag || - numctrl == gc_spin || numctrl == gc_camreset || numctrl == gc_jump || - numctrl == gc_pause || numctrl == gc_systemmenu || numctrl == gc_camtoggle || - numctrl == gc_screenshot || numctrl == gc_talkkey || numctrl == gc_scores || - numctrl == gc_centerview + numctrl == GC_WEAPONNEXT || numctrl == GC_WEAPONPREV || numctrl == GC_TOSSFLAG || + numctrl == GC_SPIN || numctrl == GC_CAMRESET || numctrl == GC_JUMP || + numctrl == GC_PAUSE || numctrl == GC_SYSTEMMENU || numctrl == GC_CAMTOGGLE || + numctrl == GC_SCREENSHOT || numctrl == GC_TALKKEY || numctrl == GC_SCORES || + numctrl == GC_CENTERVIEW )) { INT32 keynum = 0, existingctrl = 0; @@ -895,7 +895,7 @@ static INT32 G_FilterKeyByVersion(INT32 numctrl, INT32 keyidx, INT32 player, INT boolean defaultoverride = false; // get the default gamecontrol - if (player == 0 && numctrl == gc_systemmenu) + if (player == 0 && numctrl == GC_SYSTEMMENU) defaultkey = gamecontrol[numctrl][0]; else defaultkey = (player == 1 ? gamecontrolbis[numctrl][0] : gamecontrol[numctrl][1]); @@ -993,10 +993,10 @@ static void setcontrol(INT32 (*gc)[2]) // Update me for 2.3 namectrl = (stricmp(COM_Argv(1), "use")) ? COM_Argv(1) : "spin"; - for (numctrl = 0; numctrl < num_gamecontrols && stricmp(namectrl, gamecontrolname[numctrl]); + for (numctrl = 0; numctrl < NUM_GAMECONTROLS && stricmp(namectrl, gamecontrolname[numctrl]); numctrl++) ; - if (numctrl == num_gamecontrols) + if (numctrl == NUM_GAMECONTROLS) { CONS_Printf(M_GetText("Control '%s' unknown\n"), namectrl); return; diff --git a/src/g_input.h b/src/g_input.h index 39c0180e7..2e9f53dcf 100644 --- a/src/g_input.h +++ b/src/g_input.h @@ -58,49 +58,49 @@ typedef enum typedef enum { - gc_null = 0, // a key/button mapped to gc_null has no effect - gc_forward, - gc_backward, - gc_strafeleft, - gc_straferight, - gc_turnleft, - gc_turnright, - gc_weaponnext, - gc_weaponprev, - gc_wepslot1, - gc_wepslot2, - gc_wepslot3, - gc_wepslot4, - gc_wepslot5, - gc_wepslot6, - gc_wepslot7, - gc_wepslot8, - gc_wepslot9, - gc_wepslot10, - gc_fire, - gc_firenormal, - gc_tossflag, - gc_spin, - gc_camtoggle, - gc_camreset, - gc_lookup, - gc_lookdown, - gc_centerview, - gc_mouseaiming, // mouse aiming is momentary (toggleable in the menu) - gc_talkkey, - gc_teamkey, - gc_scores, - gc_jump, - gc_console, - gc_pause, - gc_systemmenu, - gc_screenshot, - gc_recordgif, - gc_viewpoint, - gc_custom1, // Lua scriptable - gc_custom2, // Lua scriptable - gc_custom3, // Lua scriptable - num_gamecontrols + GC_NULL = 0, // a key/button mapped to GC_NULL has no effect + GC_FORWARD, + GC_BACKWARD, + GC_STRAFELEFT, + GC_STRAFERIGHT, + GC_TURNLEFT, + GC_TURNRIGHT, + GC_WEAPONNEXT, + GC_WEAPONPREV, + GC_WEPSLOT1, + GC_WEPSLOT2, + GC_WEPSLOT3, + GC_WEPSLOT4, + GC_WEPSLOT5, + GC_WEPSLOT6, + GC_WEPSLOT7, + GC_WEPSLOT8, + GC_WEPSLOT9, + GC_WEPSLOT10, + GC_FIRE, + GC_FIRENORMAL, + GC_TOSSFLAG, + GC_SPIN, + GC_CAMTOGGLE, + GC_CAMRESET, + GC_LOOKUP, + GC_LOOKDOWN, + GC_CENTERVIEW, + GC_MOUSEAIMING, // mouse aiming is momentary (toggleable in the menu) + GC_TALKKEY, + GC_TEAMKEY, + GC_SCORES, + GC_JUMP, + GC_CONSOLE, + GC_PAUSE, + GC_SYSTEMMENU, + GC_SCREENSHOT, + GC_RECORDGIF, + GC_VIEWPOINT, + GC_CUSTOM1, // Lua scriptable + GC_CUSTOM2, // Lua scriptable + GC_CUSTOM3, // Lua scriptable + NUM_GAMECONTROLS } gamecontrols_e; typedef enum @@ -146,10 +146,10 @@ extern INT32 joyxmove[JOYAXISSET], joyymove[JOYAXISSET], joy2xmove[JOYAXISSET], extern UINT8 gamekeydown[NUMINPUTS]; // two key codes (or virtual key) per game control -extern INT32 gamecontrol[num_gamecontrols][2]; -extern INT32 gamecontrolbis[num_gamecontrols][2]; // secondary splitscreen player -extern INT32 gamecontroldefault[num_gamecontrolschemes][num_gamecontrols][2]; // default control storage, use 0 (gcs_custom) for memory retention -extern INT32 gamecontrolbisdefault[num_gamecontrolschemes][num_gamecontrols][2]; +extern INT32 gamecontrol[NUM_GAMECONTROLS][2]; +extern INT32 gamecontrolbis[NUM_GAMECONTROLS][2]; // secondary splitscreen player +extern INT32 gamecontroldefault[num_gamecontrolschemes][NUM_GAMECONTROLS][2]; // default control storage, use 0 (gcs_custom) for memory retention +extern INT32 gamecontrolbisdefault[num_gamecontrolschemes][NUM_GAMECONTROLS][2]; #define PLAYER1INPUTDOWN(gc) (gamekeydown[gamecontrol[gc][0]] || gamekeydown[gamecontrol[gc][1]]) #define PLAYER2INPUTDOWN(gc) (gamekeydown[gamecontrolbis[gc][0]] || gamekeydown[gamecontrolbis[gc][1]]) #define PLAYERINPUTDOWN(p, gc) ((p) == 2 ? PLAYER2INPUTDOWN(gc) : PLAYER1INPUTDOWN(gc)) diff --git a/src/hu_stuff.c b/src/hu_stuff.c index 3bd263210..d8891d508 100644 --- a/src/hu_stuff.c +++ b/src/hu_stuff.c @@ -936,7 +936,7 @@ void HU_Ticker(void) hu_tick++; hu_tick &= 7; // currently only to blink chat input cursor - if (PLAYER1INPUTDOWN(gc_scores)) + if (PLAYER1INPUTDOWN(GC_SCORES)) hu_showscores = !chat_on; else hu_showscores = false; @@ -1114,13 +1114,13 @@ boolean HU_Responder(event_t *ev) if (ev->key >= KEY_MOUSE1) { INT32 i; - for (i = 0; i < num_gamecontrols; i++) + for (i = 0; i < NUM_GAMECONTROLS; i++) { if (gamecontrol[i][0] == ev->key || gamecontrol[i][1] == ev->key) break; } - if (i == num_gamecontrols) + if (i == NUM_GAMECONTROLS) return false; }*/ //We don't actually care about that unless we get splitscreen netgames. :V @@ -1130,7 +1130,7 @@ boolean HU_Responder(event_t *ev) if (!chat_on) { // enter chat mode - if ((ev->key == gamecontrol[gc_talkkey][0] || ev->key == gamecontrol[gc_talkkey][1]) + if ((ev->key == gamecontrol[GC_TALKKEY][0] || ev->key == gamecontrol[GC_TALKKEY][1]) && netgame && !OLD_MUTE) // check for old chat mute, still let the players open the chat incase they want to scroll otherwise. { chat_on = true; @@ -1140,7 +1140,7 @@ boolean HU_Responder(event_t *ev) typelines = 1; return true; } - if ((ev->key == gamecontrol[gc_teamkey][0] || ev->key == gamecontrol[gc_teamkey][1]) + if ((ev->key == gamecontrol[GC_TEAMKEY][0] || ev->key == gamecontrol[GC_TEAMKEY][1]) && netgame && !OLD_MUTE) { chat_on = true; @@ -1234,8 +1234,8 @@ boolean HU_Responder(event_t *ev) I_UpdateMouseGrab(); } else if (c == KEY_ESCAPE - || ((c == gamecontrol[gc_talkkey][0] || c == gamecontrol[gc_talkkey][1] - || c == gamecontrol[gc_teamkey][0] || c == gamecontrol[gc_teamkey][1]) + || ((c == gamecontrol[GC_TALKKEY][0] || c == gamecontrol[GC_TALKKEY][1] + || c == gamecontrol[GC_TEAMKEY][0] || c == gamecontrol[GC_TEAMKEY][1]) && c >= KEY_MOUSE1)) // If it's not a keyboard key, then the chat button is used as a toggle. { chat_on = false; diff --git a/src/lua_inputlib.c b/src/lua_inputlib.c index 3affeea7b..6e3085e94 100644 --- a/src/lua_inputlib.c +++ b/src/lua_inputlib.c @@ -26,8 +26,8 @@ static int lib_gameControlDown(lua_State *L) { int i = luaL_checkinteger(L, 1); - if (i < 0 || i >= num_gamecontrols) - return luaL_error(L, "gc_* constant %d out of range (0 - %d)", i, num_gamecontrols-1); + if (i < 0 || i >= NUM_GAMECONTROLS) + return luaL_error(L, "GC_* constant %d out of range (0 - %d)", i, NUM_GAMECONTROLS-1); lua_pushinteger(L, PLAYER1INPUTDOWN(i)); return 1; } @@ -35,8 +35,8 @@ static int lib_gameControlDown(lua_State *L) static int lib_gameControl2Down(lua_State *L) { int i = luaL_checkinteger(L, 1); - if (i < 0 || i >= num_gamecontrols) - return luaL_error(L, "gc_* constant %d out of range (0 - %d)", i, num_gamecontrols-1); + if (i < 0 || i >= NUM_GAMECONTROLS) + return luaL_error(L, "GC_* constant %d out of range (0 - %d)", i, NUM_GAMECONTROLS-1); lua_pushinteger(L, PLAYER2INPUTDOWN(i)); return 1; } @@ -44,8 +44,8 @@ static int lib_gameControl2Down(lua_State *L) static int lib_gameControlToKeyNum(lua_State *L) { int i = luaL_checkinteger(L, 1); - if (i < 0 || i >= num_gamecontrols) - return luaL_error(L, "gc_* constant %d out of range (0 - %d)", i, num_gamecontrols-1); + if (i < 0 || i >= NUM_GAMECONTROLS) + return luaL_error(L, "GC_* constant %d out of range (0 - %d)", i, NUM_GAMECONTROLS-1); lua_pushinteger(L, gamecontrol[i][0]); lua_pushinteger(L, gamecontrol[i][1]); return 2; @@ -54,8 +54,8 @@ static int lib_gameControlToKeyNum(lua_State *L) static int lib_gameControl2ToKeyNum(lua_State *L) { int i = luaL_checkinteger(L, 1); - if (i < 0 || i >= num_gamecontrols) - return luaL_error(L, "gc_* constant %d out of range (0 - %d)", i, num_gamecontrols-1); + if (i < 0 || i >= NUM_GAMECONTROLS) + return luaL_error(L, "GC_* constant %d out of range (0 - %d)", i, NUM_GAMECONTROLS-1); lua_pushinteger(L, gamecontrolbis[i][0]); lua_pushinteger(L, gamecontrolbis[i][1]); return 2; diff --git a/src/m_menu.c b/src/m_menu.c index 4a0fefb1b..56d7e889e 100644 --- a/src/m_menu.c +++ b/src/m_menu.c @@ -1106,55 +1106,55 @@ static menuitem_t OP_ChangeControlsMenu[] = { {IT_HEADER, NULL, "Movement", NULL, 0}, {IT_SPACE, NULL, NULL, NULL, 0}, // padding - {IT_CALL | IT_STRING2, NULL, "Move Forward", M_ChangeControl, gc_forward }, - {IT_CALL | IT_STRING2, NULL, "Move Backward", M_ChangeControl, gc_backward }, - {IT_CALL | IT_STRING2, NULL, "Move Left", M_ChangeControl, gc_strafeleft }, - {IT_CALL | IT_STRING2, NULL, "Move Right", M_ChangeControl, gc_straferight }, - {IT_CALL | IT_STRING2, NULL, "Jump", M_ChangeControl, gc_jump }, - {IT_CALL | IT_STRING2, NULL, "Spin", M_ChangeControl, gc_spin }, + {IT_CALL | IT_STRING2, NULL, "Move Forward", M_ChangeControl, GC_FORWARD }, + {IT_CALL | IT_STRING2, NULL, "Move Backward", M_ChangeControl, GC_BACKWARD }, + {IT_CALL | IT_STRING2, NULL, "Move Left", M_ChangeControl, GC_STRAFELEFT }, + {IT_CALL | IT_STRING2, NULL, "Move Right", M_ChangeControl, GC_STRAFERIGHT }, + {IT_CALL | IT_STRING2, NULL, "Jump", M_ChangeControl, GC_JUMP }, + {IT_CALL | IT_STRING2, NULL, "Spin", M_ChangeControl, GC_SPIN }, {IT_HEADER, NULL, "Camera", NULL, 0}, {IT_SPACE, NULL, NULL, NULL, 0}, // padding - {IT_CALL | IT_STRING2, NULL, "Look Up", M_ChangeControl, gc_lookup }, - {IT_CALL | IT_STRING2, NULL, "Look Down", M_ChangeControl, gc_lookdown }, - {IT_CALL | IT_STRING2, NULL, "Look Left", M_ChangeControl, gc_turnleft }, - {IT_CALL | IT_STRING2, NULL, "Look Right", M_ChangeControl, gc_turnright }, - {IT_CALL | IT_STRING2, NULL, "Center View", M_ChangeControl, gc_centerview }, - {IT_CALL | IT_STRING2, NULL, "Toggle Mouselook", M_ChangeControl, gc_mouseaiming }, - {IT_CALL | IT_STRING2, NULL, "Toggle Third-Person", M_ChangeControl, gc_camtoggle}, - {IT_CALL | IT_STRING2, NULL, "Reset Camera", M_ChangeControl, gc_camreset }, + {IT_CALL | IT_STRING2, NULL, "Look Up", M_ChangeControl, GC_LOOKUP }, + {IT_CALL | IT_STRING2, NULL, "Look Down", M_ChangeControl, GC_LOOKDOWN }, + {IT_CALL | IT_STRING2, NULL, "Look Left", M_ChangeControl, GC_TURNLEFT }, + {IT_CALL | IT_STRING2, NULL, "Look Right", M_ChangeControl, GC_TURNRIGHT }, + {IT_CALL | IT_STRING2, NULL, "Center View", M_ChangeControl, GC_CENTERVIEW }, + {IT_CALL | IT_STRING2, NULL, "Toggle Mouselook", M_ChangeControl, GC_MOUSEAIMING }, + {IT_CALL | IT_STRING2, NULL, "Toggle Third-Person", M_ChangeControl, GC_CAMTOGGLE}, + {IT_CALL | IT_STRING2, NULL, "Reset Camera", M_ChangeControl, GC_CAMRESET }, {IT_HEADER, NULL, "Meta", NULL, 0}, {IT_SPACE, NULL, NULL, NULL, 0}, // padding {IT_CALL | IT_STRING2, NULL, "Game Status", - M_ChangeControl, gc_scores }, - {IT_CALL | IT_STRING2, NULL, "Pause / Run Retry", M_ChangeControl, gc_pause }, - {IT_CALL | IT_STRING2, NULL, "Screenshot", M_ChangeControl, gc_screenshot }, - {IT_CALL | IT_STRING2, NULL, "Toggle GIF Recording", M_ChangeControl, gc_recordgif }, - {IT_CALL | IT_STRING2, NULL, "Open/Close Menu (ESC)", M_ChangeControl, gc_systemmenu }, - {IT_CALL | IT_STRING2, NULL, "Change Viewpoint", M_ChangeControl, gc_viewpoint }, - {IT_CALL | IT_STRING2, NULL, "Console", M_ChangeControl, gc_console }, + M_ChangeControl, GC_SCORES }, + {IT_CALL | IT_STRING2, NULL, "Pause / Run Retry", M_ChangeControl, GC_PAUSE }, + {IT_CALL | IT_STRING2, NULL, "Screenshot", M_ChangeControl, GC_SCREENSHOT }, + {IT_CALL | IT_STRING2, NULL, "Toggle GIF Recording", M_ChangeControl, GC_RECORDGIF }, + {IT_CALL | IT_STRING2, NULL, "Open/Close Menu (ESC)", M_ChangeControl, GC_SYSTEMMENU }, + {IT_CALL | IT_STRING2, NULL, "Change Viewpoint", M_ChangeControl, GC_VIEWPOINT }, + {IT_CALL | IT_STRING2, NULL, "Console", M_ChangeControl, GC_CONSOLE }, {IT_HEADER, NULL, "Multiplayer", NULL, 0}, {IT_SPACE, NULL, NULL, NULL, 0}, // padding - {IT_CALL | IT_STRING2, NULL, "Talk", M_ChangeControl, gc_talkkey }, - {IT_CALL | IT_STRING2, NULL, "Talk (Team only)", M_ChangeControl, gc_teamkey }, + {IT_CALL | IT_STRING2, NULL, "Talk", M_ChangeControl, GC_TALKKEY }, + {IT_CALL | IT_STRING2, NULL, "Talk (Team only)", M_ChangeControl, GC_TEAMKEY }, {IT_HEADER, NULL, "Ringslinger (Match, CTF, Tag, H&S)", NULL, 0}, {IT_SPACE, NULL, NULL, NULL, 0}, // padding - {IT_CALL | IT_STRING2, NULL, "Fire", M_ChangeControl, gc_fire }, - {IT_CALL | IT_STRING2, NULL, "Fire Normal", M_ChangeControl, gc_firenormal }, - {IT_CALL | IT_STRING2, NULL, "Toss Flag", M_ChangeControl, gc_tossflag }, - {IT_CALL | IT_STRING2, NULL, "Next Weapon", M_ChangeControl, gc_weaponnext }, - {IT_CALL | IT_STRING2, NULL, "Prev Weapon", M_ChangeControl, gc_weaponprev }, - {IT_CALL | IT_STRING2, NULL, "Normal / Infinity", M_ChangeControl, gc_wepslot1 }, - {IT_CALL | IT_STRING2, NULL, "Automatic", M_ChangeControl, gc_wepslot2 }, - {IT_CALL | IT_STRING2, NULL, "Bounce", M_ChangeControl, gc_wepslot3 }, - {IT_CALL | IT_STRING2, NULL, "Scatter", M_ChangeControl, gc_wepslot4 }, - {IT_CALL | IT_STRING2, NULL, "Grenade", M_ChangeControl, gc_wepslot5 }, - {IT_CALL | IT_STRING2, NULL, "Explosion", M_ChangeControl, gc_wepslot6 }, - {IT_CALL | IT_STRING2, NULL, "Rail", M_ChangeControl, gc_wepslot7 }, + {IT_CALL | IT_STRING2, NULL, "Fire", M_ChangeControl, GC_FIRE }, + {IT_CALL | IT_STRING2, NULL, "Fire Normal", M_ChangeControl, GC_FIRENORMAL }, + {IT_CALL | IT_STRING2, NULL, "Toss Flag", M_ChangeControl, GC_TOSSFLAG }, + {IT_CALL | IT_STRING2, NULL, "Next Weapon", M_ChangeControl, GC_WEAPONNEXT }, + {IT_CALL | IT_STRING2, NULL, "Prev Weapon", M_ChangeControl, GC_WEAPONPREV }, + {IT_CALL | IT_STRING2, NULL, "Normal / Infinity", M_ChangeControl, GC_WEPSLOT1 }, + {IT_CALL | IT_STRING2, NULL, "Automatic", M_ChangeControl, GC_WEPSLOT2 }, + {IT_CALL | IT_STRING2, NULL, "Bounce", M_ChangeControl, GC_WEPSLOT3 }, + {IT_CALL | IT_STRING2, NULL, "Scatter", M_ChangeControl, GC_WEPSLOT4 }, + {IT_CALL | IT_STRING2, NULL, "Grenade", M_ChangeControl, GC_WEPSLOT5 }, + {IT_CALL | IT_STRING2, NULL, "Explosion", M_ChangeControl, GC_WEPSLOT6 }, + {IT_CALL | IT_STRING2, NULL, "Rail", M_ChangeControl, GC_WEPSLOT7 }, {IT_HEADER, NULL, "Add-ons", NULL, 0}, {IT_SPACE, NULL, NULL, NULL, 0}, // padding - {IT_CALL | IT_STRING2, NULL, "Custom Action 1", M_ChangeControl, gc_custom1 }, - {IT_CALL | IT_STRING2, NULL, "Custom Action 2", M_ChangeControl, gc_custom2 }, - {IT_CALL | IT_STRING2, NULL, "Custom Action 3", M_ChangeControl, gc_custom3 }, + {IT_CALL | IT_STRING2, NULL, "Custom Action 1", M_ChangeControl, GC_CUSTOM1 }, + {IT_CALL | IT_STRING2, NULL, "Custom Action 2", M_ChangeControl, GC_CUSTOM2 }, + {IT_CALL | IT_STRING2, NULL, "Custom Action 3", M_ChangeControl, GC_CUSTOM3 }, }; static menuitem_t OP_Joystick1Menu[] = @@ -3343,7 +3343,7 @@ boolean M_Responder(event_t *ev) if (ch == -1) return false; - else if (ch == gamecontrol[gc_systemmenu][0] || ch == gamecontrol[gc_systemmenu][1]) // allow remappable ESC key + else if (ch == gamecontrol[GC_SYSTEMMENU][0] || ch == gamecontrol[GC_SYSTEMMENU][1]) // allow remappable ESC key ch = KEY_ESCAPE; // F-Keys @@ -12929,7 +12929,7 @@ static void M_ChangecontrolResponse(event_t *ev) static char tmp[158]; menu_t *prev = currentMenu->prevMenu; - if (controltochange == gc_pause) + if (controltochange == GC_PAUSE) sprintf(tmp, M_GetText("The \x82Pause Key \x80is enabled, but \nit cannot be used to retry runs \nduring Record Attack. \n\nHit another key for\n%s\nESC for Cancel"), controltochangetext); else diff --git a/src/m_misc.c b/src/m_misc.c index f9a23ad44..dcdddf9d5 100644 --- a/src/m_misc.c +++ b/src/m_misc.c @@ -1636,9 +1636,9 @@ boolean M_ScreenshotResponder(event_t *ev) if (ch >= KEY_MOUSE1 && menuactive) // If it's not a keyboard key, then don't allow it in the menus! return false; - if (ch == KEY_F8 || ch == gamecontrol[gc_screenshot][0] || ch == gamecontrol[gc_screenshot][1]) // remappable F8 + if (ch == KEY_F8 || ch == gamecontrol[GC_SCREENSHOT][0] || ch == gamecontrol[GC_SCREENSHOT][1]) // remappable F8 M_ScreenShot(); - else if (ch == KEY_F9 || ch == gamecontrol[gc_recordgif][0] || ch == gamecontrol[gc_recordgif][1]) // remappable F9 + else if (ch == KEY_F9 || ch == gamecontrol[GC_RECORDGIF][0] || ch == gamecontrol[GC_RECORDGIF][1]) // remappable F9 ((moviemode) ? M_StopMovie : M_StartMovie)(); else return false; diff --git a/src/p_user.c b/src/p_user.c index c3184b52f..d12affd1d 100644 --- a/src/p_user.c +++ b/src/p_user.c @@ -5357,9 +5357,9 @@ static void P_DoJumpStuff(player_t *player, ticcmd_t *cmd) // disabled because it seemed to disorient people and Z-targeting exists now /*if (!demoplayback) { - if (player == &players[consoleplayer] && cv_cam_turnfacingability[0].value > 0 && !(PLAYER1INPUTDOWN(gc_turnleft) || PLAYER1INPUTDOWN(gc_turnright))) + if (player == &players[consoleplayer] && cv_cam_turnfacingability[0].value > 0 && !(PLAYER1INPUTDOWN(GC_TURNLEFT) || PLAYER1INPUTDOWN(GC_TURNRIGHT))) P_SetPlayerAngle(player, player->mo->angle);; - else if (player == &players[secondarydisplayplayer] && cv_cam_turnfacingability[1].value > 0 && !(PLAYER2INPUTDOWN(gc_turnleft) || PLAYER2INPUTDOWN(gc_turnright))) + else if (player == &players[secondarydisplayplayer] && cv_cam_turnfacingability[1].value > 0 && !(PLAYER2INPUTDOWN(GC_TURNLEFT) || PLAYER2INPUTDOWN(GC_TURNRIGHT))) P_SetPlayerAngle(player, player->mo->angle); }*/ } From 9b7263855e573747cc396c6af6f789fdb95ac5aa Mon Sep 17 00:00:00 2001 From: LJ Sonic Date: Wed, 18 Aug 2021 20:58:13 +0200 Subject: [PATCH 115/126] Prevent input.setMouseGrab from interfering with window focus --- src/d_clisrv.c | 4 ++++ src/lua_inputlib.c | 8 +++++--- src/lua_libs.h | 2 ++ src/sdl/i_video.c | 4 ++++ 4 files changed, 15 insertions(+), 3 deletions(-) diff --git a/src/d_clisrv.c b/src/d_clisrv.c index 326105a3b..4cf7259bf 100644 --- a/src/d_clisrv.c +++ b/src/d_clisrv.c @@ -43,6 +43,7 @@ #include "lzf.h" #include "lua_script.h" #include "lua_hook.h" +#include "lua_libs.h" #include "md5.h" #include "m_perfstats.h" @@ -3331,6 +3332,9 @@ static inline void SV_GenContext(void) // void D_QuitNetGame(void) { + mousegrabbedbylua = true; + I_UpdateMouseGrab(); + if (!netgame || !netbuffer) return; diff --git a/src/lua_inputlib.c b/src/lua_inputlib.c index 6e3085e94..661d93641 100644 --- a/src/lua_inputlib.c +++ b/src/lua_inputlib.c @@ -19,6 +19,8 @@ #include "lua_script.h" #include "lua_libs.h" +boolean mousegrabbedbylua = true; + /////////////// // FUNCTIONS // /////////////// @@ -106,14 +108,14 @@ static int lib_shiftKeyNum(lua_State *L) static int lib_getMouseGrab(lua_State *L) { - lua_pushboolean(L, I_GetMouseGrab()); + lua_pushboolean(L, mousegrabbedbylua); return 1; } static int lib_setMouseGrab(lua_State *L) { - boolean grab = luaL_checkboolean(L, 1); - I_SetMouseGrab(grab); + mousegrabbedbylua = luaL_checkboolean(L, 1); + I_UpdateMouseGrab(); return 0; } diff --git a/src/lua_libs.h b/src/lua_libs.h index de174283c..8903834e8 100644 --- a/src/lua_libs.h +++ b/src/lua_libs.h @@ -12,6 +12,8 @@ extern lua_State *gL; +extern boolean mousegrabbedbylua; + #define MUTABLE_TAGS #define LREG_VALID "VALID_USERDATA" diff --git a/src/sdl/i_video.c b/src/sdl/i_video.c index a18ea32ba..97e4a7214 100644 --- a/src/sdl/i_video.c +++ b/src/sdl/i_video.c @@ -73,6 +73,8 @@ #include "../console.h" #include "../command.h" #include "../r_main.h" +#include "../lua_script.h" +#include "../lua_libs.h" #include "../lua_hook.h" #include "sdlmain.h" #ifdef HWRENDER @@ -372,6 +374,8 @@ static boolean IgnoreMouse(void) if (gamestate != GS_LEVEL && gamestate != GS_INTERMISSION && gamestate != GS_CONTINUING && gamestate != GS_CUTSCENE) return true; + if (!mousegrabbedbylua) + return true; return false; } From 450955cba264ea3dde9eb3f745ff9816307635ce Mon Sep 17 00:00:00 2001 From: Jaime Ita Passos Date: Thu, 26 Aug 2021 13:22:32 -0300 Subject: [PATCH 116/126] Fix floor sprite projection --- src/r_plane.c | 124 ++++++++----------------------------------------- src/r_plane.h | 1 - src/r_splats.c | 91 ++++++++++++++---------------------- src/r_splats.h | 3 +- src/r_things.c | 5 +- src/r_things.h | 10 +++- 6 files changed, 68 insertions(+), 166 deletions(-) diff --git a/src/r_plane.c b/src/r_plane.c index 818770906..d844048ae 100644 --- a/src/r_plane.c +++ b/src/r_plane.c @@ -89,8 +89,6 @@ static fixed_t planeheight; fixed_t yslopetab[MAXVIDHEIGHT*16]; fixed_t *yslope; -fixed_t basexscale, baseyscale; - fixed_t cachedheight[MAXVIDHEIGHT]; fixed_t cacheddistance[MAXVIDHEIGHT]; fixed_t cachedxstep[MAXVIDHEIGHT]; @@ -114,7 +112,7 @@ void R_InitPlanes(void) // Sets planeripple.xfrac and planeripple.yfrac, added to ds_xfrac and ds_yfrac, if the span is not tilted. // -struct +static struct { INT32 offset; fixed_t xfrac, yfrac; @@ -143,15 +141,6 @@ static void R_UpdatePlaneRipple(void) planeripple.offset = (leveltime * 140); } -// -// R_MapPlane -// -// Uses global vars: -// planeheight -// basexscale -// baseyscale -// centerx - static void R_MapPlane(INT32 y, INT32 x1, INT32 x2) { angle_t angle, planecos, planesin; @@ -176,16 +165,13 @@ static void R_MapPlane(INT32 y, INT32 x1, INT32 x2) cacheddistance[y] = distance = FixedMul(planeheight, yslope[y]); span = abs(centery - y); - if (span) // don't divide by zero + if (span) // Don't divide by zero { ds_xstep = FixedMul(planesin, planeheight) / span; ds_ystep = FixedMul(planecos, planeheight) / span; } else - { - ds_xstep = FixedMul(distance, basexscale); - ds_ystep = FixedMul(distance, baseyscale); - } + ds_xstep = ds_ystep = FRACUNIT; cachedxstep[y] = ds_xstep; cachedystep[y] = ds_ystep; @@ -197,6 +183,11 @@ static void R_MapPlane(INT32 y, INT32 x1, INT32 x2) ds_ystep = cachedystep[y]; } + // [RH] Instead of using the xtoviewangle array, I calculated the fractional values + // at the middle of the screen, then used the calculated ds_xstep and ds_ystep + // to step from those to the proper texture coordinate to start drawing at. + // That way, the texture coordinate is always calculated by its position + // on the screen and not by its position relative to the edge of the visplane. ds_xfrac = xoffs + FixedMul(planecos, distance) + (x1 - centerx) * ds_xstep; ds_yfrac = yoffs - FixedMul(planesin, distance) + (x1 - centerx) * ds_ystep; @@ -295,7 +286,6 @@ void R_ClearFFloorClips (void) void R_ClearPlanes(void) { INT32 i, p; - angle_t angle; // opening / clipping determination for (i = 0; i < viewwidth; i++) @@ -321,13 +311,6 @@ void R_ClearPlanes(void) // texture calculation memset(cachedheight, 0, sizeof (cachedheight)); - - // left to right mapping - angle = (viewangle-ANGLE_90)>>ANGLETOFINESHIFT; - - // scale will be unit scale at SCREENWIDTH/2 distance - basexscale = FixedDiv (FINECOSINE(angle),centerxfrac); - baseyscale = -FixedDiv (FINESINE(angle),centerxfrac); } static visplane_t *new_visplane(unsigned hash) @@ -532,53 +515,22 @@ visplane_t *R_CheckPlane(visplane_t *pl, INT32 start, INT32 stop) // // R_ExpandPlane // -// This function basically expands the visplane or I_Errors. +// This function basically expands the visplane. // The reason for this is that when creating 3D floor planes, there is no // need to create new ones with R_CheckPlane, because 3D floor planes // are created by subsector and there is no way a subsector can graphically // overlap. void R_ExpandPlane(visplane_t *pl, INT32 start, INT32 stop) { -// INT32 unionl, unionh; -// INT32 x; - // Don't expand polyobject planes here - we do that on our own. if (pl->polyobj) return; if (pl->minx > start) pl->minx = start; if (pl->maxx < stop) pl->maxx = stop; -/* - if (start < pl->minx) - { - unionl = start; - } - else - { - unionl = pl->minx; - } - - if (stop > pl->maxx) - { - unionh = stop; - } - else - { - unionh = pl->maxx; - } - for (x = start; x <= stop; x++) - if (pl->top[x] != 0xffff || pl->bottom[x] != 0x0000) - break; - - if (x <= stop) - I_Error("R_ExpandPlane: planes in same subsector overlap?!\nminx: %d, maxx: %d, start: %d, stop: %d\n", pl->minx, pl->maxx, start, stop); - - pl->minx = unionl, pl->maxx = unionh; -*/ - } -static void R_MakeSpans(INT32 x, INT32 t1, INT32 b1, INT32 t2, INT32 b2) +static void R_MakeSpans(void (*mapfunc)(INT32, INT32, INT32), INT32 x, INT32 t1, INT32 b1, INT32 t2, INT32 b2) { // Alam: from r_splats's R_RasterizeFloorSplat if (t1 >= vid.height) t1 = vid.height-1; @@ -589,38 +541,12 @@ static void R_MakeSpans(INT32 x, INT32 t1, INT32 b1, INT32 t2, INT32 b2) while (t1 < t2 && t1 <= b1) { - R_MapPlane(t1, spanstart[t1], x - 1); + mapfunc(t1, spanstart[t1], x - 1); t1++; } while (b1 > b2 && b1 >= t1) { - R_MapPlane(b1, spanstart[b1], x - 1); - b1--; - } - - while (t2 < t1 && t2 <= b2) - spanstart[t2++] = x; - while (b2 > b1 && b2 >= t2) - spanstart[b2--] = x; -} - -static void R_MakeTiltedSpans(INT32 x, INT32 t1, INT32 b1, INT32 t2, INT32 b2) -{ - // Alam: from r_splats's R_RasterizeFloorSplat - if (t1 >= vid.height) t1 = vid.height-1; - if (b1 >= vid.height) b1 = vid.height-1; - if (t2 >= vid.height) t2 = vid.height-1; - if (b2 >= vid.height) b2 = vid.height-1; - if (x-1 >= vid.width) x = vid.width; - - while (t1 < t2 && t1 <= b1) - { - R_MapTiltedPlane(t1, spanstart[t1], x - 1); - t1++; - } - while (b1 > b2 && b1 >= t1) - { - R_MapTiltedPlane(b1, spanstart[b1], x - 1); + mapfunc(b1, spanstart[b1], x - 1); b1--; } @@ -867,11 +793,10 @@ void R_DrawSinglePlane(visplane_t *pl) { levelflat_t *levelflat; INT32 light = 0; - INT32 x; - INT32 stop, angle; + INT32 x, stop; ffloor_t *rover; - INT32 type; - INT32 spanfunctype = BASEDRAWFUNC; + INT32 type, spanfunctype = BASEDRAWFUNC; + void (*mapfunc)(INT32, INT32, INT32) = R_MapPlane; if (!(pl->minx <= pl->maxx)) return; @@ -1023,9 +948,6 @@ void R_DrawSinglePlane(visplane_t *pl) && viewangle != pl->viewangle+pl->plangle) { memset(cachedheight, 0, sizeof (cachedheight)); - angle = (pl->viewangle+pl->plangle-ANGLE_90)>>ANGLETOFINESHIFT; - basexscale = FixedDiv(FINECOSINE(angle),centerxfrac); - baseyscale = -FixedDiv(FINESINE(angle),centerxfrac); viewangle = pl->viewangle+pl->plangle; } @@ -1040,6 +962,8 @@ void R_DrawSinglePlane(visplane_t *pl) if (pl->slope) { + mapfunc = R_MapTiltedPlane; + if (!pl->plangle) { if (ds_powersoftwo) @@ -1107,16 +1031,8 @@ void R_DrawSinglePlane(visplane_t *pl) stop = pl->maxx + 1; - if (pl->slope) - { - for (x = pl->minx; x <= stop; x++) - R_MakeTiltedSpans(x, pl->top[x-1], pl->bottom[x-1], pl->top[x], pl->bottom[x]); - } - else - { - for (x = pl->minx; x <= stop; x++) - R_MakeSpans(x, pl->top[x-1], pl->bottom[x-1], pl->top[x], pl->bottom[x]); - } + for (x = pl->minx; x <= stop; x++) + R_MakeSpans(mapfunc, x, pl->top[x-1], pl->bottom[x-1], pl->top[x], pl->bottom[x]); /* QUINCUNX anti-aliasing technique (sort of) @@ -1183,7 +1099,7 @@ using the palette colors. stop = pl->maxx + 1; for (x = pl->minx; x <= stop; x++) - R_MakeSpans(x, pl->top[x-1], pl->bottom[x-1], + R_MakeSpans(mapfunc, x, pl->top[x-1], pl->bottom[x-1], pl->top[x], pl->bottom[x]); } } diff --git a/src/r_plane.h b/src/r_plane.h index bdad77930..862b95069 100644 --- a/src/r_plane.h +++ b/src/r_plane.h @@ -69,7 +69,6 @@ extern fixed_t cachedheight[MAXVIDHEIGHT]; extern fixed_t cacheddistance[MAXVIDHEIGHT]; extern fixed_t cachedxstep[MAXVIDHEIGHT]; extern fixed_t cachedystep[MAXVIDHEIGHT]; -extern fixed_t basexscale, baseyscale; extern fixed_t *yslope; extern lighttable_t **planezlight; diff --git a/src/r_splats.c b/src/r_splats.c index 4783fb640..c554e9b1f 100644 --- a/src/r_splats.c +++ b/src/r_splats.c @@ -155,7 +155,6 @@ void R_DrawFloorSplat(vissprite_t *spr) fixed_t xscale, yscale; fixed_t xoffset, yoffset; fixed_t leftoffset, topoffset; - pslope_t *slope = NULL; INT32 i; boolean hflip = (spr->xiscale < 0); @@ -188,7 +187,7 @@ void R_DrawFloorSplat(vissprite_t *spr) if (spr->rotateflags & SRF_3D || renderflags & RF_NOSPLATBILLBOARD) splatangle = mobj->angle; else - splatangle = spr->viewangle; + splatangle = spr->viewpoint.angle; if (!(spr->cut & SC_ISROTATED)) splatangle += mobj->rollangle; @@ -218,7 +217,7 @@ void R_DrawFloorSplat(vissprite_t *spr) splat.x = x; splat.y = y; splat.z = mobj->z; - splat.tilted = false; + splat.slope = NULL; // Set positions @@ -238,9 +237,9 @@ void R_DrawFloorSplat(vissprite_t *spr) splat.verts[3].x = w - xoffset; splat.verts[3].y = -h + yoffset; - angle = -splat.angle; - ca = FINECOSINE(angle>>ANGLETOFINESHIFT); - sa = FINESINE(angle>>ANGLETOFINESHIFT); + angle = -splat.angle>>ANGLETOFINESHIFT; + ca = FINECOSINE(angle); + sa = FINESINE(angle); // Rotate for (i = 0; i < 4; i++) @@ -255,36 +254,10 @@ void R_DrawFloorSplat(vissprite_t *spr) // The slope that was defined for the sprite. if (renderflags & RF_SLOPESPLAT) - slope = mobj->floorspriteslope; + splat.slope = mobj->floorspriteslope; if (standingslope && (renderflags & RF_OBJECTSLOPESPLAT)) - slope = standingslope; - - // Set splat as tilted - splat.tilted = (slope != NULL); - } - - if (splat.tilted) - { - pslope_t *s = &splat.slope; - - s->o.x = slope->o.x; - s->o.y = slope->o.y; - s->o.z = slope->o.z; - - s->d.x = slope->d.x; - s->d.y = slope->d.y; - - s->normal.x = slope->normal.x; - s->normal.y = slope->normal.y; - s->normal.z = slope->normal.z; - - s->zdelta = slope->zdelta; - s->zangle = slope->zangle; - s->xydirection = slope->xydirection; - - s->next = NULL; - s->flags = 0; + splat.slope = standingslope; } // Translate @@ -293,9 +266,9 @@ void R_DrawFloorSplat(vissprite_t *spr) tr_x = rotated[i].x + x; tr_y = rotated[i].y + y; - if (slope) + if (splat.slope) { - rot_z = P_GetSlopeZAt(slope, tr_x, tr_y); + rot_z = P_GetSlopeZAt(splat.slope, tr_x, tr_y); splat.verts[i].z = rot_z; } else @@ -305,18 +278,23 @@ void R_DrawFloorSplat(vissprite_t *spr) splat.verts[i].y = tr_y; } + angle = spr->viewpoint.angle >> ANGLETOFINESHIFT; + ca = FINECOSINE(angle); + sa = FINESINE(angle); + + // Project for (i = 0; i < 4; i++) { v3d = &splat.verts[i]; // transform the origin point - tr_x = v3d->x - viewx; - tr_y = v3d->y - viewy; + tr_x = v3d->x - spr->viewpoint.x; + tr_y = v3d->y - spr->viewpoint.y; // rotation around vertical y axis - rot_x = FixedMul(tr_x, viewsin) - FixedMul(tr_y, viewcos); - rot_y = FixedMul(tr_x, viewcos) + FixedMul(tr_y, viewsin); - rot_z = v3d->z - viewz; + rot_x = FixedMul(tr_x, sa) - FixedMul(tr_y, ca); + rot_y = FixedMul(tr_x, ca) + FixedMul(tr_y, sa); + rot_z = v3d->z - spr->viewpoint.z; if (rot_y < FRACUNIT) return; @@ -416,31 +394,32 @@ static void R_RasterizeFloorSplat(floorsplat_t *pSplat, vector2_t *verts, visspr if (R_CheckPowersOfTwo()) R_CheckFlatLength(ds_flatwidth * ds_flatheight); - if (pSplat->tilted) + if (pSplat->slope) { R_SetTiltedSpan(0); - R_SetScaledSlopePlane(&pSplat->slope, viewx, viewy, viewz, pSplat->xscale, pSplat->yscale, -pSplat->verts[0].x, pSplat->verts[0].y, vis->viewangle, pSplat->angle); + R_SetScaledSlopePlane(pSplat->slope, vis->viewpoint.x, vis->viewpoint.y, vis->viewpoint.z, pSplat->xscale, pSplat->yscale, -pSplat->verts[0].x, pSplat->verts[0].y, vis->viewpoint.angle, pSplat->angle); R_CalculateSlopeVectors(); spanfunctype = SPANDRAWFUNC_TILTEDSPRITE; } else { - planeheight = abs(pSplat->z - viewz); + planeheight = abs(pSplat->z - vis->viewpoint.z); if (pSplat->angle) { - // Add the view offset, rotated by the plane angle. - fixed_t a = -pSplat->verts[0].x + viewx; - fixed_t b = -pSplat->verts[0].y + viewy; - angle_t angle = (pSplat->angle >> ANGLETOFINESHIFT); - offsetx = FixedMul(a, FINECOSINE(angle)) - FixedMul(b,FINESINE(angle)); - offsety = -FixedMul(a, FINESINE(angle)) - FixedMul(b,FINECOSINE(angle)); memset(cachedheight, 0, sizeof(cachedheight)); + + // Add the view offset, rotated by the plane angle. + fixed_t a = -pSplat->verts[0].x + vis->viewpoint.x; + fixed_t b = -pSplat->verts[0].y + vis->viewpoint.y; + angle_t angle = (pSplat->angle >> ANGLETOFINESHIFT); + offsetx = FixedMul(a, FINECOSINE(angle)) - FixedMul(b, FINESINE(angle)); + offsety = -FixedMul(a, FINESINE(angle)) - FixedMul(b, FINECOSINE(angle)); } else { - offsetx = viewx - pSplat->verts[0].x; - offsety = pSplat->verts[0].y - viewy; + offsetx = vis->viewpoint.x - pSplat->verts[0].x; + offsety = pSplat->verts[0].y - vis->viewpoint.y; } } @@ -461,7 +440,7 @@ static void R_RasterizeFloorSplat(floorsplat_t *pSplat, vector2_t *verts, visspr { ds_transmap = vis->transmap; - if (pSplat->tilted) + if (pSplat->slope) spanfunctype = SPANDRAWFUNC_TILTEDTRANSSPRITE; else spanfunctype = SPANDRAWFUNC_TRANSSPRITE; @@ -528,12 +507,12 @@ static void R_RasterizeFloorSplat(floorsplat_t *pSplat, vector2_t *verts, visspr if (x2 < x1) continue; - if (!pSplat->tilted) + if (!pSplat->slope) { fixed_t xstep, ystep; fixed_t distance, span; - angle_t angle = (vis->viewangle + pSplat->angle)>>ANGLETOFINESHIFT; + angle_t angle = (vis->viewpoint.angle + pSplat->angle)>>ANGLETOFINESHIFT; angle_t planecos = FINECOSINE(angle); angle_t planesin = FINESINE(angle); @@ -577,7 +556,7 @@ static void R_RasterizeFloorSplat(floorsplat_t *pSplat, vector2_t *verts, visspr rastertab[y].maxx = INT32_MIN; } - if (pSplat->angle && !pSplat->tilted) + if (pSplat->angle && !pSplat->slope) memset(cachedheight, 0, sizeof(cachedheight)); } diff --git a/src/r_splats.h b/src/r_splats.h index cab3d63b6..7e31406d1 100644 --- a/src/r_splats.h +++ b/src/r_splats.h @@ -34,8 +34,7 @@ typedef struct floorsplat_s INT32 width, height; fixed_t scale, xscale, yscale; angle_t angle; - boolean tilted; // Uses the tilted drawer - pslope_t slope; + pslope_t *slope; vector3_t verts[4]; // (x,y,z) as viewed from above on map fixed_t x, y, z; // position diff --git a/src/r_things.c b/src/r_things.c index 0283712b8..f1996d743 100644 --- a/src/r_things.c +++ b/src/r_things.c @@ -1956,9 +1956,12 @@ static void R_ProjectSprite(mobj_t *thing) vis->paperoffset = paperoffset; vis->paperdistance = paperdistance; vis->centerangle = centerangle; - vis->viewangle = viewangle; vis->shear.tan = sheartan; vis->shear.offset = 0; + vis->viewpoint.x = viewx; + vis->viewpoint.y = viewy; + vis->viewpoint.z = viewz; + vis->viewpoint.angle = viewangle; vis->mobj = thing; // Easy access! Tails 06-07-2002 diff --git a/src/r_things.h b/src/r_things.h index 9315b36e9..79dc80d94 100644 --- a/src/r_things.h +++ b/src/r_things.h @@ -164,7 +164,12 @@ typedef struct vissprite_s fixed_t xiscale; // negative if flipped angle_t centerangle; // for paper sprites - angle_t viewangle; // for floor sprites, the viewpoint's current angle + + // for floor sprites + struct { + fixed_t x, y, z; // the viewpoint's current position + angle_t angle; // the viewpoint's current angle + } viewpoint; struct { fixed_t tan; // The amount to shear the sprite vertically per row @@ -185,9 +190,10 @@ typedef struct vissprite_s extracolormap_t *extra_colormap; // global colormaps - // Precalculated top and bottom screen coords for the sprite. fixed_t thingheight; // The actual height of the thing (for 3D floors) sector_t *sector; // The sector containing the thing. + + // Precalculated top and bottom screen coords for the sprite. INT16 sz, szt; spritecut_e cut; From 3ff8d9d3b1d3fec83cb4ba20ff40fefb97ec2dde Mon Sep 17 00:00:00 2001 From: Jaime Ita Passos Date: Thu, 26 Aug 2021 13:37:52 -0300 Subject: [PATCH 117/126] Fix issue with png_get_tRNS This fixes an oversight where the return value of png_get_tRNS was being ignored. --- src/r_picformats.c | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/src/r_picformats.c b/src/r_picformats.c index 59b1d16c5..5c81d1e02 100644 --- a/src/r_picformats.c +++ b/src/r_picformats.c @@ -901,9 +901,8 @@ static png_bytep *PNG_Read( png_colorp palette; int palette_size; - png_bytep trans; - int trans_num; - png_color_16p trans_values; + png_bytep trans = NULL; + int num_trans = 0; #ifdef PNG_SETJMP_SUPPORTED #ifdef USE_FAR_KEYWORD @@ -998,12 +997,12 @@ static png_bytep *PNG_Read( // color is present on the image, the palette flag is disabled. if (usepal) { - png_get_tRNS(png_ptr, png_info_ptr, &trans, &trans_num, &trans_values); + png_uint_32 result = png_get_tRNS(png_ptr, png_info_ptr, &trans, &num_trans, NULL); - if (trans && trans_num > 0) + if ((result & PNG_INFO_tRNS) && num_trans > 0 && trans != NULL) { INT32 i; - for (i = 0; i < trans_num; i++) + for (i = 0; i < num_trans; i++) { // libpng will transform this image into RGBA even if // the transparent index does not exist in the image, From b73c24a0c5a0a01078c27242ec72d98a31f8b524 Mon Sep 17 00:00:00 2001 From: Shane Ellis Date: Sat, 28 Aug 2021 17:14:36 +0000 Subject: [PATCH 118/126] Add VERSIONSTRING_RC to DEVELOP builds for compiler compatibility --- src/doomdef.h | 1 + 1 file changed, 1 insertion(+) diff --git a/src/doomdef.h b/src/doomdef.h index 11ca80538..37edca896 100644 --- a/src/doomdef.h +++ b/src/doomdef.h @@ -127,6 +127,7 @@ extern char logfilename[1024]; //#define DEVELOP // Disable this for release builds to remove excessive cheat commands and enable MD5 checking and stuff, all in one go. :3 #ifdef DEVELOP #define VERSIONSTRING "Development EXE" +#define VERSIONSTRING_RC "Development EXE" "\0" // most interface strings are ignored in development mode. // we use comprevision and compbranch instead. // VERSIONSTRING_RC is for the resource-definition script used by windows builds From 1a8ec7975c20e1d652cbbdb7a9e366192e41c05f Mon Sep 17 00:00:00 2001 From: GoldenTails Date: Sat, 28 Aug 2021 15:39:34 -0500 Subject: [PATCH 119/126] Makefile: Improve gcc detection Wasn't working for me until I fixed it. Turns out gcc really doesn't like giving its name out. Most of the time it reads argv[0]. --- src/Makefile.d/detect.mk | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/src/Makefile.d/detect.mk b/src/Makefile.d/detect.mk index 9c8a0a227..f458b044c 100644 --- a/src/Makefile.d/detect.mk +++ b/src/Makefile.d/detect.mk @@ -71,13 +71,17 @@ latest_gcc_version:=10.2 # manually set. And don't bother if this is a clean only # run. ifeq (,$(call Wildvar,GCC% destructive)) -version:=$(shell $(CC) --version) + +# can't use $(CC) --version here since that uses argv[0] to display the name +# also gcc outputs the information to stderr, so I had to do 2>&1 +# this program really doesn't like identifying itself +version:=$(shell $(CC) -v 2>&1) # check if this is in fact GCC -ifneq (,$(or $(findstring gcc,$(version)),\ - $(findstring GCC,$(version)))) +ifneq (,$(findstring gcc version,$(version))) -version:=$(shell $(CC) -dumpversion) +# in stark contrast to the name, gcc will give me a nicely formatted version number for free +version:=$(shell $(CC) -dumpfullversion) # Turn version into words of major, minor v:=$(subst ., ,$(version)) From f4f7af6f6d87451c3271606c965762755617cdc1 Mon Sep 17 00:00:00 2001 From: James R Date: Mon, 30 Aug 2021 18:40:36 -0700 Subject: [PATCH 120/126] Add my full name --- src/f_finale.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/f_finale.c b/src/f_finale.c index 4d9a8f825..acad7b248 100644 --- a/src/f_finale.c +++ b/src/f_finale.c @@ -1089,7 +1089,6 @@ static const char *credits[] = { "\"Hannu_Hanhi\"", // For many OpenGL performance improvements! "Kepa \"Nev3r\" Iceta", "Thomas \"Shadow Hog\" Igoe", - "\"james\"", "Iestyn \"Monster Iestyn\" Jealous", "\"Jimita\"", "\"Kaito Sinclaire\"", @@ -1102,6 +1101,7 @@ static const char *credits[] = { "Louis-Antoine \"LJ Sonic\" de Moulins", // de Rochefort doesn't quite fit on the screen sorry lol "John \"JTE\" Muniz", "Colin \"Sonict\" Pfaff", + "James \"james\" Robert Roman", "Sean \"Sryder13\" Ryder", "Ehab \"Wolfy\" Saeed", "Tasos \"tatokis\" Sahanidis", // Corrected C FixedMul, making 64-bit builds netplay compatible From 39bd9c6da8dbcb47660cc41ac53bbbacb4c020ab Mon Sep 17 00:00:00 2001 From: namishere Date: Mon, 30 Aug 2021 23:18:25 -0700 Subject: [PATCH 121/126] Make next compile again --- src/lua_baselib.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lua_baselib.c b/src/lua_baselib.c index f287fb78c..1c3b483fa 100644 --- a/src/lua_baselib.c +++ b/src/lua_baselib.c @@ -3405,7 +3405,7 @@ static int lib_gAddPlayer(lua_State *L) { INT16 i, newplayernum, botcount = 1; player_t *newplayer; - INT8 skinnum = 0, bot; + SINT8 skinnum = 0, bot; for (i = 0; i < MAXPLAYERS; i++) { From 8e0831a183dbcd3dad5696c6f6514c95df861417 Mon Sep 17 00:00:00 2001 From: James R Date: Fri, 3 Sep 2021 17:13:12 -0700 Subject: [PATCH 122/126] Fix unused variable warning under NOPNG --- src/r_picformats.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/r_picformats.h b/src/r_picformats.h index b1bb35edd..c74f8a13a 100644 --- a/src/r_picformats.h +++ b/src/r_picformats.h @@ -116,9 +116,9 @@ void *Picture_PNGConvert( size_t insize, size_t *outsize, pictureflags_t flags); boolean Picture_PNGDimensions(UINT8 *png, INT32 *width, INT32 *height, INT16 *topoffset, INT16 *leftoffset, size_t size); -#endif #define PICTURE_PNG_USELOOKUP +#endif // SpriteInfo extern spriteinfo_t spriteinfo[NUMSPRITES]; From 50fe45efe68f01dc8dd79d4f040a776d5d043857 Mon Sep 17 00:00:00 2001 From: katsy <205-katsy@users.noreply.git.do.srb2.org> Date: Fri, 10 Sep 2021 17:00:43 +0000 Subject: [PATCH 123/126] Refactor weather switching (fixes #541) --- src/p_mobj.c | 14 ++------ src/p_spec.c | 93 ++++++++++------------------------------------------ 2 files changed, 21 insertions(+), 86 deletions(-) diff --git a/src/p_mobj.c b/src/p_mobj.c index 10220fff6..c32371888 100644 --- a/src/p_mobj.c +++ b/src/p_mobj.c @@ -11051,7 +11051,7 @@ void P_SpawnPrecipitation(void) subsector_t *precipsector = NULL; precipmobj_t *rainmo = NULL; - if (dedicated || !(cv_drawdist_precip.value) || curWeather == PRECIP_NONE) + if (dedicated || !(cv_drawdist_precip.value) || curWeather == PRECIP_NONE || curWeather == PRECIP_STORM_NORAIN) return; // Use the blockmap to narrow down our placing patterns @@ -11097,22 +11097,14 @@ void P_SpawnPrecipitation(void) continue; rainmo = P_SpawnRainMobj(x, y, height, MT_RAIN); + if (curWeather == PRECIP_BLANK) + rainmo->precipflags |= PCF_INVISIBLE; } // Randomly assign a height, now that floorz is set. rainmo->z = M_RandomRange(rainmo->floorz>>FRACBITS, rainmo->ceilingz>>FRACBITS)<flags = mobjinfo[MT_RAIN].flags; st = &states[mobjinfo[MT_RAIN].spawnstate]; @@ -2078,7 +2039,7 @@ void P_SwitchWeather(INT32 weathernum) precipmobj->precipflags |= PCF_RAIN; //think->function.acp1 = (actionf_p1)P_RainThinker; } - else if (swap == PRECIP_SNOW) // Rain To Snow + else if (weathernum == PRECIP_SNOW) // Rain To Snow { INT32 z; @@ -2103,7 +2064,7 @@ void P_SwitchWeather(INT32 weathernum) //think->function.acp1 = (actionf_p1)P_SnowThinker; } - else if (swap == PRECIP_BLANK || swap == PRECIP_STORM_NORAIN) // Remove precip, but keep it around for reuse. + else // Remove precip, but keep it around for reuse. { //think->function.acp1 = (actionf_p1)P_NullPrecipThinker; @@ -2116,49 +2077,34 @@ void P_SwitchWeather(INT32 weathernum) { case PRECIP_SNOW: // snow curWeather = PRECIP_SNOW; - - if (!swap) + + if (purge) P_SpawnPrecipitation(); break; case PRECIP_RAIN: // rain { - boolean dontspawn = false; - - if (curWeather == PRECIP_RAIN || curWeather == PRECIP_STORM || curWeather == PRECIP_STORM_NOSTRIKES) - dontspawn = true; - curWeather = PRECIP_RAIN; - if (!dontspawn && !swap) + if (purge) P_SpawnPrecipitation(); break; } case PRECIP_STORM: // storm { - boolean dontspawn = false; - - if (curWeather == PRECIP_RAIN || curWeather == PRECIP_STORM || curWeather == PRECIP_STORM_NOSTRIKES) - dontspawn = true; - curWeather = PRECIP_STORM; - if (!dontspawn && !swap) + if (purge) P_SpawnPrecipitation(); break; } case PRECIP_STORM_NOSTRIKES: // storm w/o lightning { - boolean dontspawn = false; - - if (curWeather == PRECIP_RAIN || curWeather == PRECIP_STORM || curWeather == PRECIP_STORM_NOSTRIKES) - dontspawn = true; - curWeather = PRECIP_STORM_NOSTRIKES; - if (!dontspawn && !swap) + if (purge) P_SpawnPrecipitation(); break; @@ -2166,14 +2112,11 @@ void P_SwitchWeather(INT32 weathernum) case PRECIP_STORM_NORAIN: // storm w/o rain curWeather = PRECIP_STORM_NORAIN; - if (!swap) - P_SpawnPrecipitation(); - break; - case PRECIP_BLANK: + case PRECIP_BLANK: //preloaded curWeather = PRECIP_BLANK; - if (!swap) + if (purge) P_SpawnPrecipitation(); break; From aaf4653f1e111edfd5efe1f2004f1f48e34883ad Mon Sep 17 00:00:00 2001 From: LZA <73-LZA@users.noreply.git.do.srb2.org> Date: Sun, 12 Sep 2021 21:08:06 +0000 Subject: [PATCH 124/126] Fix minor issues with folder addons --- src/d_main.c | 4 +- src/d_netcmd.c | 56 ++++++++- src/d_netfil.c | 19 +-- src/filesrch.c | 329 ++++++++++++++++++++++++++++--------------------- src/filesrch.h | 10 +- src/m_misc.c | 2 +- src/w_wad.c | 168 ++++++++++++++++++++----- src/w_wad.h | 5 +- 8 files changed, 405 insertions(+), 188 deletions(-) diff --git a/src/d_main.c b/src/d_main.c index 7866ccbed..36f41e0a4 100644 --- a/src/d_main.c +++ b/src/d_main.c @@ -944,13 +944,13 @@ static void D_AddFile(char **list, const char *file) static void D_AddFolder(char **list, const char *file) { - size_t pnumwadfiles, len = strlen(file); + size_t pnumwadfiles; char *newfile; for (pnumwadfiles = 0; list[pnumwadfiles]; pnumwadfiles++) ; - newfile = malloc(len + 2); // NULL terminator + path separator + newfile = malloc(strlen(file) + 2); // Path delimiter + NULL terminator if (!newfile) I_Error("No more free memory to AddFolder %s",file); diff --git a/src/d_netcmd.c b/src/d_netcmd.c index ea0d9ca80..1e4e2b464 100644 --- a/src/d_netcmd.c +++ b/src/d_netcmd.c @@ -1518,7 +1518,7 @@ static void Got_NameAndColor(UINT8 **cp, INT32 playernum) { illegalMask &= ~(1 << i); } - + if ((p->availabilities & illegalMask) != 0) { kick = true; @@ -3416,9 +3416,10 @@ static void Command_Addfolder(void) for (curarg = 1; curarg < argc; curarg++) { const char *fn, *p; + char *fullpath; char buf[256]; char *buf_p = buf; - INT32 i; + INT32 i, stat; size_t ii; boolean folderadded = false; @@ -3461,6 +3462,13 @@ static void Command_Addfolder(void) break; ++p; + // Don't add an empty path. + if (M_IsStringEmpty(fn)) + { + CONS_Alert(CONS_WARNING, M_GetText("Folder name is empty, skipping\n")); + continue; + } + // check total packet size and no of files currently loaded // See W_InitFile in w_wad.c if ((numwadfiles >= MAX_WADFILES) @@ -3470,10 +3478,52 @@ static void Command_Addfolder(void) return; } - WRITESTRINGN(buf_p,p,240); + // Check if the path is valid. + stat = W_IsPathToFolderValid(fn); + + if (stat == 0) + { + CONS_Alert(CONS_WARNING, M_GetText("Path %s is invalid, skipping\n"), fn); + continue; + } + else if (stat < 0) + { +#ifndef AVOID_ERRNO + CONS_Alert(CONS_WARNING, M_GetText("Error accessing %s (%s), skipping\n"), fn, strerror(direrror)); +#else + CONS_Alert(CONS_WARNING, M_GetText("Error accessing %s, skipping\n"), fn); +#endif + continue; + } + + // Get the full path for this folder. + fullpath = W_GetFullFolderPath(fn); + + if (fullpath == NULL) + { + CONS_Alert(CONS_WARNING, M_GetText("Path %s is invalid, skipping\n"), fn); + continue; + } + + // Check if the folder is already added. + for (i = 0; i < numwadfiles; i++) + { + if (wadfiles[i]->type != RET_FOLDER) + continue; + + if (samepaths(wadfiles[i]->path, fullpath) > 0) + { + CONS_Alert(CONS_ERROR, M_GetText("%s is already loaded\n"), fn); + continue; + } + } + + Z_Free(fullpath); addedfolders[numfoldersadded++] = fn; + WRITESTRINGN(buf_p,p,240); + if (IsPlayerAdmin(consoleplayer) && (!server)) // Request to add file SendNetXCmd(XD_REQADDFOLDER, buf, buf_p - buf); else diff --git a/src/d_netfil.c b/src/d_netfil.c index 15f9f1ff5..12c5ee6a2 100644 --- a/src/d_netfil.c +++ b/src/d_netfil.c @@ -1584,20 +1584,23 @@ filestatus_t findfile(char *filename, const UINT8 *wantedmd5sum, boolean complet return (badmd5 ? FS_MD5SUMBAD : FS_NOTFOUND); // md5 sum bad or file not found } +// Searches for a folder. +// This can be used with a full path, or an incomplete path. +// In the latter case, the function will try to find folders in +// srb2home, srb2path, and the current directory. filestatus_t findfolder(const char *path) { // Check the path by itself first. - if (checkfolderpath(path, NULL, true)) + if (concatpaths(path, NULL) == 1) return FS_FOUND; -#define checkpath(startpath) { \ - if (checkfolderpath(path, startpath, true)) \ - return FS_FOUND; \ - } +#define checkpath(startpath) \ + if (concatpaths(path, startpath) == 1) \ + return FS_FOUND - checkpath(srb2home) // Then, look in srb2home. - checkpath(srb2path) // Now, look in srb2path. - checkpath(".") // Finally, look in ".". + checkpath(srb2home); // Then, look in srb2home. + checkpath(srb2path); // Now, look in srb2path. + checkpath("."); // Finally, look in the current directory. #undef checkpath diff --git a/src/filesrch.c b/src/filesrch.c index f01fc0bd2..e5c9b2158 100644 --- a/src/filesrch.c +++ b/src/filesrch.c @@ -341,8 +341,8 @@ char *refreshdirname = NULL; size_t packetsizetally = 0; size_t mainwadstally = 0; -#define folderpathlen 1024 -#define maxfolderdepth 48 +#define dirpathlen 1024 +#define maxdirdepth 48 #define isuptree(dirent) ((dirent)[0]=='.' && ((dirent)[1]=='\0' || ((dirent)[1]=='.' && (dirent)[2]=='\0'))) @@ -448,182 +448,227 @@ filestatus_t filesearch(char *filename, const char *startpath, const UINT8 *want return retval; } -// Called from findfolder and ResGetLumpsFolder in w_wad.c. -// Call with cleanup true if the path has to be verified. -boolean checkfolderpath(const char *path, const char *startpath, boolean cleanup) -{ - char folderpath[folderpathlen], basepath[folderpathlen], *fn = NULL; - DIR *dirhandle; +#ifndef AVOID_ERRNO +int direrror = 0; +#endif - // Remove path separators from the filename, and don't try adding "/". - // See also the same code in W_InitFolder. - if (cleanup) - { - const char *p = path + strlen(path); - size_t len; - - --p; - while (*p == '\\' || *p == '/' || *p == ':') - { - p--; - if (p < path) - return false; - } - ++p; - - // Allocate the new path name. - len = (p - path) + 1; - fn = ZZ_Alloc(len); - strlcpy(fn, path, len); - } - - if (startpath) - { - snprintf(basepath, sizeof basepath, "%s" PATHSEP, startpath); - - if (cleanup) - { - snprintf(folderpath, sizeof folderpath, "%s%s", basepath, fn); - Z_Free(fn); // Don't need this anymore. - } - else - snprintf(folderpath, sizeof folderpath, "%s%s", basepath, path); - - // Home path and folder path are the same? Not valid. - if (!strcmp(basepath, folderpath)) - return false; - } - else if (cleanup) - { - snprintf(folderpath, sizeof folderpath, "%s", fn); - Z_Free(fn); // Don't need this anymore. - } - else - snprintf(folderpath, sizeof folderpath, "%s", path); - - dirhandle = opendir(folderpath); - if (dirhandle == NULL) - return false; - else - closedir(dirhandle); - - return true; -} - -INT32 pathisfolder(const char *path) +// Checks if the specified path is a directory. +// Returns 1 if so, 0 if not, and -1 if an error occurred. +// direrror is set if there was an error. +INT32 pathisdirectory(const char *path) { struct stat fsstat; if (stat(path, &fsstat) < 0) + { +#ifndef AVOID_ERRNO + direrror = errno; +#endif return -1; + } else if (S_ISDIR(fsstat.st_mode)) return 1; return 0; } +// Concatenates two paths, and checks if it is a directory that can be opened. +// Returns 1 if so, 0 if not, and -1 if an error occurred. +INT32 concatpaths(const char *path, const char *startpath) +{ + char dirpath[dirpathlen]; + DIR *dirhandle; + INT32 stat; + + if (startpath) + { + char basepath[dirpathlen]; + + snprintf(basepath, sizeof basepath, "%s" PATHSEP, startpath); + snprintf(dirpath, sizeof dirpath, "%s%s", basepath, path); + + // Base path and directory path are the same? Not valid. + stat = samepaths(basepath, dirpath); + + if (stat == 1) + return 0; + else if (stat < 0) + return -1; + } + else + snprintf(dirpath, sizeof dirpath, "%s", path); + + // Check if the path is a directory. + // Will return -1 if there was an error. + stat = pathisdirectory(dirpath); + if (stat == 0) + return 0; + else if (stat < 0) + { + // The path doesn't exist, so it can't be a directory. + if (direrror == ENOENT) + return 0; + + return -1; + } + + // Open the directory. + // Will return 0 if it couldn't be opened. + dirhandle = opendir(dirpath); + if (dirhandle == NULL) + return 0; + else + closedir(dirhandle); + + return 1; +} + +// Checks if two paths are the same. Returns 1 if so, and 0 if not. +// Returns -1 if an error occurred with the first path, +// and returns -2 if an error occurred with the second path. +// direrror is set if there was an error. INT32 samepaths(const char *path1, const char *path2) { struct stat stat1; struct stat stat2; if (stat(path1, &stat1) < 0) + { +#ifndef AVOID_ERRNO + direrror = errno; +#endif return -1; + } if (stat(path2, &stat2) < 0) - return -1; + { +#ifndef AVOID_ERRNO + direrror = errno; +#endif + return -2; + } if (stat1.st_dev == stat2.st_dev) { #if !defined(_WIN32) return (stat1.st_ino == stat2.st_ino); #else + // The above doesn't work on NTFS or FAT. HANDLE file1 = CreateFileA(path1, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL); HANDLE file2 = CreateFileA(path2, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL); BY_HANDLE_FILE_INFORMATION file1info, file2info; - boolean ok = false; - if (file1 != INVALID_HANDLE_VALUE && file2 != INVALID_HANDLE_VALUE) + if (file1 == INVALID_HANDLE_VALUE) { - if (GetFileInformationByHandle(file1, &file1info) && GetFileInformationByHandle(file2, &file2info)) - { - if (file1info.dwVolumeSerialNumber == file2info.dwVolumeSerialNumber - && file1info.nFileIndexLow == file2info.nFileIndexLow - && file1info.nFileIndexHigh == file2info.nFileIndexHigh) - ok = true; - } +#ifndef AVOID_ERRNO + direrror = ENOENT; +#endif + return -1; + } + else if (file2 == INVALID_HANDLE_VALUE) + { + CloseHandle(file1); +#ifndef AVOID_ERRNO + direrror = ENOENT; +#endif + return -2; } - if (file1 != INVALID_HANDLE_VALUE) + // I have no idea why GetFileInformationByHandle would fail. + // Microsoft's documentation doesn't tell me. + // I'll just use EIO... + if (!GetFileInformationByHandle(file1, &file1info)) + { +#ifndef AVOID_ERRNO + direrror = EIO; +#endif + return -1; + } + else if (!GetFileInformationByHandle(file2, &file2info)) + { CloseHandle(file1); - if (file2 != INVALID_HANDLE_VALUE) CloseHandle(file2); +#ifndef AVOID_ERRNO + direrror = EIO; +#endif + return -2; + } - return ok; + if (file1info.dwVolumeSerialNumber == file2info.dwVolumeSerialNumber + && file1info.nFileIndexLow == file2info.nFileIndexLow + && file1info.nFileIndexHigh == file2info.nFileIndexHigh) + { + CloseHandle(file1); + CloseHandle(file2); + return 1; + } + + return 0; #endif } - return false; + return 0; } // -// Folder loading +// Directory loading // -static void initfolderpath(char *folderpath, size_t *folderpathindex, int depthleft) +static void initdirpath(char *dirpath, size_t *dirpathindex, int depthleft) { - folderpathindex[depthleft] = strlen(folderpath) + 1; + dirpathindex[depthleft] = strlen(dirpath) + 1; - if (folderpath[folderpathindex[depthleft]-2] != PATHSEP[0]) + if (dirpath[dirpathindex[depthleft]-2] != PATHSEP[0]) { - folderpath[folderpathindex[depthleft]-1] = PATHSEP[0]; - folderpath[folderpathindex[depthleft]] = 0; + dirpath[dirpathindex[depthleft]-1] = PATHSEP[0]; + dirpath[dirpathindex[depthleft]] = 0; } else - folderpathindex[depthleft]--; + dirpathindex[depthleft]--; } -lumpinfo_t *getfolderfiles(const char *path, UINT16 *nlmp, UINT16 *nfiles, UINT16 *nfolders) +lumpinfo_t *getdirectoryfiles(const char *path, UINT16 *nlmp, UINT16 *nfolders) { DIR **dirhandle; struct dirent *dent; struct stat fsstat; - int rootfolder = (maxfolderdepth - 1); - int depthleft = rootfolder; + int rootdir = (maxdirdepth - 1); + int depthleft = rootdir; - char folderpath[folderpathlen]; - size_t *folderpathindex; + char dirpath[dirpathlen]; + size_t *dirpathindex; lumpinfo_t *lumpinfo, *lump_p; - UINT16 i = 0, numlumps = (*nlmp); + UINT16 i = 0, numlumps = 0; - dirhandle = (DIR **)malloc(maxfolderdepth * sizeof (DIR*)); - folderpathindex = (size_t *)malloc(maxfolderdepth * sizeof(size_t)); + boolean failure = false; + + dirhandle = (DIR **)malloc(maxdirdepth * sizeof (DIR*)); + dirpathindex = (size_t *)malloc(maxdirdepth * sizeof(size_t)); // Open the root directory - strlcpy(folderpath, path, folderpathlen); - dirhandle[depthleft] = opendir(folderpath); + strlcpy(dirpath, path, dirpathlen); + dirhandle[depthleft] = opendir(dirpath); if (dirhandle[depthleft] == NULL) { free(dirhandle); - free(folderpathindex); + free(dirpathindex); return NULL; } - initfolderpath(folderpath, folderpathindex, depthleft); - (*nfiles) = 0; + initdirpath(dirpath, dirpathindex, depthleft); (*nfolders) = 0; // Count files and directories - while (depthleft < maxfolderdepth) + while (depthleft < maxdirdepth) { - folderpath[folderpathindex[depthleft]] = 0; + dirpath[dirpathindex[depthleft]] = 0; dent = readdir(dirhandle[depthleft]); if (!dent) { - if (depthleft != rootfolder) // Don't close the root directory + if (depthleft != rootdir) // Don't close the root directory closedir(dirhandle[depthleft]); depthleft++; continue; @@ -631,62 +676,67 @@ lumpinfo_t *getfolderfiles(const char *path, UINT16 *nlmp, UINT16 *nfiles, UINT1 else if (isuptree(dent->d_name)) continue; - strcpy(&folderpath[folderpathindex[depthleft]], dent->d_name); + strcpy(&dirpath[dirpathindex[depthleft]], dent->d_name); - if (stat(folderpath, &fsstat) < 0) + if (stat(dirpath, &fsstat) < 0) ; else if (S_ISDIR(fsstat.st_mode) && depthleft) { - folderpathindex[--depthleft] = strlen(folderpath) + 1; - dirhandle[depthleft] = opendir(folderpath); + dirpathindex[--depthleft] = strlen(dirpath) + 1; + dirhandle[depthleft] = opendir(dirpath); if (dirhandle[depthleft]) - { - numlumps++; (*nfolders)++; - } else depthleft++; - folderpath[folderpathindex[depthleft]-1] = '/'; - folderpath[folderpathindex[depthleft]] = 0; + dirpath[dirpathindex[depthleft]-1] = '/'; + dirpath[dirpathindex[depthleft]] = 0; } else - { numlumps++; - (*nfiles)++; - } - if (numlumps == (UINT16_MAX-1)) + // Failure: Too many files. + if (numlumps == UINT16_MAX) + { + (*nlmp) = UINT16_MAX; + failure = true; break; + } } // Failure: No files have been found. - if (!(*nfiles)) + if (!numlumps) { - (*nfiles) = UINT16_MAX; - free(folderpathindex); + (*nlmp) = 0; + failure = true; + } + + // Close any open directories and return if something went wrong. + if (failure) + { + free(dirpathindex); free(dirhandle); - for (; depthleft < maxfolderdepth; closedir(dirhandle[depthleft++])); // Close any open directories. + for (; depthleft < maxdirdepth; closedir(dirhandle[depthleft++])); return NULL; } // Create the files and directories as lump entries // It's possible to create lumps and count files at the same time, - // but I didn't to constantly have to reallocate memory for every lump. - rewinddir(dirhandle[rootfolder]); - depthleft = rootfolder; + // but I didn't want to have to reallocate memory for every lump. + rewinddir(dirhandle[rootdir]); + depthleft = rootdir; - strlcpy(folderpath, path, folderpathlen); - initfolderpath(folderpath, folderpathindex, depthleft); + strlcpy(dirpath, path, dirpathlen); + initdirpath(dirpath, dirpathindex, depthleft); lump_p = lumpinfo = Z_Calloc(numlumps * sizeof(lumpinfo_t), PU_STATIC, NULL); - while (depthleft < maxfolderdepth) + while (depthleft < maxdirdepth) { char *fullname, *trimname; - folderpath[folderpathindex[depthleft]] = 0; + dirpath[dirpathindex[depthleft]] = 0; dent = readdir(dirhandle[depthleft]); if (!dent) @@ -697,29 +747,30 @@ lumpinfo_t *getfolderfiles(const char *path, UINT16 *nlmp, UINT16 *nfiles, UINT1 else if (isuptree(dent->d_name)) continue; - strcpy(&folderpath[folderpathindex[depthleft]], dent->d_name); + strcpy(&dirpath[dirpathindex[depthleft]], dent->d_name); - if (stat(folderpath, &fsstat) < 0) + if (stat(dirpath, &fsstat) < 0) continue; else if (S_ISDIR(fsstat.st_mode) && depthleft) { - folderpathindex[--depthleft] = strlen(folderpath) + 1; - dirhandle[depthleft] = opendir(folderpath); + dirpathindex[--depthleft] = strlen(dirpath) + 1; + dirhandle[depthleft] = opendir(dirpath); - if (!dirhandle[depthleft]) + if (dirhandle[depthleft]) { - depthleft++; - continue; + dirpath[dirpathindex[depthleft]-1] = '/'; + dirpath[dirpathindex[depthleft]] = 0; } + else + depthleft++; - folderpath[folderpathindex[depthleft]-1] = '/'; - folderpath[folderpathindex[depthleft]] = 0; + continue; } - lump_p->diskpath = Z_StrDup(folderpath); // Path in the filesystem to the file + lump_p->diskpath = Z_StrDup(dirpath); // Path in the filesystem to the file lump_p->compression = CM_NOCOMPRESSION; // Lump is uncompressed - // Remove the folder path. + // Remove the directory's path. fullname = lump_p->diskpath; if (strstr(fullname, path)) fullname += strlen(path) + 1; @@ -747,7 +798,7 @@ lumpinfo_t *getfolderfiles(const char *path, UINT16 *nlmp, UINT16 *nfiles, UINT1 lump_p->longname = Z_Calloc(1, PU_STATIC, NULL); // The complete name of the file, with its extension, - // excluding the path of the folder where it resides. + // excluding the path of the directory where it resides. lump_p->fullname = Z_StrDup(fullname); lump_p++; @@ -755,12 +806,12 @@ lumpinfo_t *getfolderfiles(const char *path, UINT16 *nlmp, UINT16 *nfiles, UINT1 if (i > numlumps || i == (UINT16_MAX-1)) { - for (; depthleft < maxfolderdepth; closedir(dirhandle[depthleft++])); // Close any open directories. + for (; depthleft < maxdirdepth; closedir(dirhandle[depthleft++])); // Close any open directories. break; } } - free(folderpathindex); + free(dirpathindex); free(dirhandle); (*nlmp) = numlumps; diff --git a/src/filesrch.h b/src/filesrch.h index 92e3341f3..e358d7993 100644 --- a/src/filesrch.h +++ b/src/filesrch.h @@ -29,11 +29,15 @@ extern consvar_t cv_addons_option, cv_addons_folder, cv_addons_md5, cv_addons_sh filestatus_t filesearch(char *filename, const char *startpath, const UINT8 *wantedmd5sum, boolean completepath, int maxsearchdepth); -INT32 pathisfolder(const char *path); -boolean checkfolderpath(const char *path, const char *startpath, boolean cleanup); +INT32 pathisdirectory(const char *path); INT32 samepaths(const char *path1, const char *path2); +boolean concatpaths(const char *path, const char *startpath); -lumpinfo_t *getfolderfiles(const char *path, UINT16 *nlmp, UINT16 *nfiles, UINT16 *nfolders); +#ifndef AVOID_ERRNO +extern int direrror; +#endif + +lumpinfo_t *getdirectoryfiles(const char *path, UINT16 *nlmp, UINT16 *nfolders); #define menudepth 20 diff --git a/src/m_misc.c b/src/m_misc.c index 4100a8f17..17d6d738b 100644 --- a/src/m_misc.c +++ b/src/m_misc.c @@ -2694,7 +2694,7 @@ boolean M_IsStringEmpty(const char *s) { const char *ch = s; - if (ch == NULL || (ch && strlen(ch) < 1)) + if (s == NULL || s[0] == '\0') return true; for (;;ch++) diff --git a/src/w_wad.c b/src/w_wad.c index e37c86bac..3ff301117 100644 --- a/src/w_wad.c +++ b/src/w_wad.c @@ -687,11 +687,67 @@ static lumpinfo_t* ResGetLumpsZip (FILE* handle, UINT16* nlmp) return lumpinfo; } +static INT32 CheckPathsNotEqual(const char *path1, const char *path2) +{ + INT32 stat = samepaths(path1, path2); + + if (stat == 1) + return 0; + else if (stat < 0) + return -1; + + return 1; +} + +// Returns 1 if the path is valid, 0 if not, and -1 if there was an error. +INT32 W_IsPathToFolderValid(const char *path) +{ + INT32 stat; + + // Remove path delimiters. + const char *p = path + (strlen(path) - 1); + while (*p == '\\' || *p == '/' || *p == ':') + { + p--; + if (p < path) + return 0; + } + + // Check if the path is a directory. + stat = pathisdirectory(path); + if (stat == 0) + return 0; + else if (stat < 0) + { + // The path doesn't exist, so it can't be a directory. + if (direrror == ENOENT) + return 0; + + return -1; + } + + // Don't add your home, you sodding tic tac. + stat = CheckPathsNotEqual(path, srb2home); + if (stat != 1) + return stat; + + // Do the same checks for SRB2's path, and the current directory. + stat = CheckPathsNotEqual(path, srb2path); + if (stat != 1) + return stat; + + stat = CheckPathsNotEqual(path, "."); + if (stat != 1) + return stat; + + return 1; +} + // Checks if the combination of the first path and the second path are valid. // If they are, the concatenated path is returned. -static char *W_CheckFolderPath(const char *startpath, const char *path) +static char *CheckConcatFolderPath(const char *startpath, const char *path) { - if (checkfolderpath(path, startpath, false)) + if (concatpaths(path, startpath) == 1) { char *fn; @@ -710,23 +766,23 @@ static char *W_CheckFolderPath(const char *startpath, const char *path) return NULL; } -// Returns the first valid path for a folder. -static char *W_GetFullFolderPath(const char *path) +// Looks for the first valid full path for a folder. +// Returns NULL if the folder doesn't exist, or it isn't valid. +char *W_GetFullFolderPath(const char *path) { // Check the path by itself first. - char *fn = W_CheckFolderPath(NULL, path); + char *fn = CheckConcatFolderPath(NULL, path); if (fn) return fn; -#define checkpath(startpath) { \ - fn = W_CheckFolderPath(startpath, path); \ +#define checkpath(startpath) \ + fn = CheckConcatFolderPath(startpath, path); \ if (fn) \ - return fn; \ -} \ + return fn - checkpath(srb2home) // Then, look in srb2home. - checkpath(srb2path) // Now, look in srb2path. - checkpath(".") // Finally, look in ".". + checkpath(srb2home); // Then, look in srb2home. + checkpath(srb2path); // Now, look in srb2path. + checkpath("."); // Finally, look in the current directory. #undef checkpath @@ -734,9 +790,9 @@ static char *W_GetFullFolderPath(const char *path) } // Loads files from a folder into a lumpinfo structure. -static lumpinfo_t *ResGetLumpsFolder(const char *path, UINT16 *nlmp, UINT16 *nfiles, UINT16 *nfolders) +static lumpinfo_t *ResGetLumpsFolder(const char *path, UINT16 *nlmp, UINT16 *nfolders) { - return getfolderfiles(path, nlmp, nfiles, nfolders); + return getdirectoryfiles(path, nlmp, nfolders); } static UINT16 W_InitFileError (const char *filename, boolean exitworthy) @@ -908,7 +964,7 @@ UINT16 W_InitFile(const char *filename, boolean mainfile, boolean startup) wadfile->type = type; wadfile->handle = handle; wadfile->numlumps = numlumps; - wadfile->filecount = wadfile->foldercount = 0; + wadfile->foldercount = 0; wadfile->lumpinfo = lumpinfo; wadfile->important = important; fseek(handle, 0, SEEK_END); @@ -966,11 +1022,12 @@ UINT16 W_InitFolder(const char *path, boolean mainfile, boolean startup) lumpinfo_t *lumpinfo = NULL; wadfile_t *wadfile; UINT16 numlumps = 0; - UINT16 filecount, foldercount; + UINT16 foldercount; size_t i; char *fn, *fullpath; const char *p; int important; + INT32 stat; if (!(refreshdirmenu & REFRESHDIR_ADDFILE)) refreshdirmenu = REFRESHDIR_NORMAL|REFRESHDIR_ADDFILE; // clean out cons_alerts that happened earlier @@ -1006,16 +1063,15 @@ UINT16 W_InitFolder(const char *path, boolean mainfile, boolean startup) packetsizetally = packetsize; } - // Remove path separators from the filename, and don't try adding "/". - p = path+strlen(path); - --p; + // Remove path delimiters. + p = path + (strlen(path) - 1); while (*p == '\\' || *p == '/' || *p == ':') { p--; if (p < path) { - CONS_Alert(CONS_ERROR, M_GetText("Path %s is prohibited\n"), path); + CONS_Alert(CONS_ERROR, M_GetText("Path %s is invalid\n"), path); return W_InitFileError(path, startup); } } @@ -1026,6 +1082,7 @@ UINT16 W_InitFolder(const char *path, boolean mainfile, boolean startup) fn = ZZ_Alloc(i); strlcpy(fn, path, i); + // Don't add an empty path. if (M_IsStringEmpty(fn)) { CONS_Alert(CONS_ERROR, M_GetText("Folder name is empty\n")); @@ -1037,14 +1094,36 @@ UINT16 W_InitFolder(const char *path, boolean mainfile, boolean startup) return W_InitFileError("a folder", false); } - // Get the full path for this filename. + // Check if the path is valid. + stat = W_IsPathToFolderValid(fn); + + if (stat != 1) + { + if (stat == 0) + CONS_Alert(CONS_ERROR, M_GetText("Path %s is invalid\n"), fn); + else if (stat < 0) + { +#ifndef AVOID_ERRNO + CONS_Alert(CONS_ERROR, M_GetText("Could not stat %s: %s\n"), fn, strerror(direrror)); +#else + CONS_Alert(CONS_ERROR, M_GetText("Could not stat %s\n"), fn); +#endif + } + + Z_Free(fn); + return W_InitFileError(path, startup); + } + + // Get the full path for this folder. fullpath = W_GetFullFolderPath(fn); if (fullpath == NULL) { + CONS_Alert(CONS_ERROR, M_GetText("Path %s is invalid\n"), fn); Z_Free(fn); - return W_InitFileError(path, false); + return W_InitFileError(path, startup); } + // Check if the folder is already added. for (i = 0; i < numwadfiles; i++) { if (wadfiles[i]->type != RET_FOLDER) @@ -1061,11 +1140,16 @@ UINT16 W_InitFolder(const char *path, boolean mainfile, boolean startup) } } - lumpinfo = ResGetLumpsFolder(fullpath, &numlumps, &filecount, &foldercount); + lumpinfo = ResGetLumpsFolder(fullpath, &numlumps, &foldercount); + if (lumpinfo == NULL) { - if (filecount == UINT16_MAX) + if (!numlumps) CONS_Alert(CONS_ERROR, M_GetText("Folder %s is empty\n"), path); + else if (numlumps == UINT16_MAX) + CONS_Alert(CONS_ERROR, M_GetText("Folder %s contains too many files\n"), path); + else + CONS_Alert(CONS_ERROR, M_GetText("Unknown error enumerating files from folder %s\n"), path); Z_Free(fn); Z_Free(fullpath); @@ -1082,7 +1166,6 @@ UINT16 W_InitFolder(const char *path, boolean mainfile, boolean startup) wadfile->type = RET_FOLDER; wadfile->handle = NULL; wadfile->numlumps = numlumps; - wadfile->filecount = filecount; wadfile->foldercount = foldercount; wadfile->lumpinfo = lumpinfo; wadfile->important = important; @@ -1094,7 +1177,7 @@ UINT16 W_InitFolder(const char *path, boolean mainfile, boolean startup) Z_Calloc(numlumps * sizeof (*wadfile->lumpcache), PU_STATIC, &wadfile->lumpcache); Z_Calloc(numlumps * sizeof (*wadfile->patchcache), PU_STATIC, &wadfile->patchcache); - CONS_Printf(M_GetText("Added folder %s (%u files, %u folders)\n"), fn, filecount, foldercount); + CONS_Printf(M_GetText("Added folder %s (%u files, %u folders)\n"), fn, numlumps, foldercount); wadfiles[numwadfiles] = wadfile; numwadfiles++; @@ -1506,12 +1589,24 @@ size_t W_LumpLengthPwad(UINT16 wad, UINT16 lump) l = wadfiles[wad]->lumpinfo + lump; + // Open the external file for this lump, if the WAD is a folder. if (wadfiles[wad]->type == RET_FOLDER) { - INT32 stat = pathisfolder(l->diskpath); + // pathisdirectory calls stat, so if anything wrong has happened, + // this is the time to be aware of it. + INT32 stat = pathisdirectory(l->diskpath); if (stat < 0) - I_Error("W_LumpLengthPwad: could not stat %s", l->diskpath); + { +#ifndef AVOID_ERRNO + if (direrror == ENOENT) + I_Error("W_LumpLengthPwad: file %s doesn't exist", l->diskpath); + else + I_Error("W_LumpLengthPwad: could not stat %s: %s", l->diskpath, strerror(direrror)); +#else + I_Error("W_LumpLengthPwad: could not access %s", l->diskpath); +#endif + } else if (stat == 1) // Path is a folder. return 0; else @@ -1617,7 +1712,7 @@ size_t W_ReadLumpHeaderPwad(UINT16 wad, UINT16 lump, void *dest, size_t size, si lumpinfo_t *l; FILE *handle = NULL; - if (!TestValidLump(wad,lump)) + if (!TestValidLump(wad, lump)) return 0; l = wadfiles[wad]->lumpinfo + lump; @@ -1625,10 +1720,21 @@ size_t W_ReadLumpHeaderPwad(UINT16 wad, UINT16 lump, void *dest, size_t size, si // Open the external file for this lump, if the WAD is a folder. if (wadfiles[wad]->type == RET_FOLDER) { - INT32 stat = pathisfolder(l->diskpath); + // pathisdirectory calls stat, so if anything wrong has happened, + // this is the time to be aware of it. + INT32 stat = pathisdirectory(l->diskpath); if (stat < 0) - I_Error("W_ReadLumpHeaderPwad: could not stat %s", l->diskpath); + { +#ifndef AVOID_ERRNO + if (direrror == ENOENT) + I_Error("W_ReadLumpHeaderPwad: file %s doesn't exist", l->diskpath); + else + I_Error("W_ReadLumpHeaderPwad: could not stat %s: %s", l->diskpath, strerror(direrror)); +#else + I_Error("W_ReadLumpHeaderPwad: could not access %s", l->diskpath); +#endif + } else if (stat == 1) // Path is a folder. return 0; else diff --git a/src/w_wad.h b/src/w_wad.h index 25b4bffa8..3958f7faf 100644 --- a/src/w_wad.h +++ b/src/w_wad.h @@ -122,7 +122,7 @@ typedef struct wadfile_s lumpcache_t *lumpcache; lumpcache_t *patchcache; UINT16 numlumps; // this wad's number of resources - UINT16 filecount, foldercount; // file and folder count + UINT16 foldercount; // folder count FILE *handle; UINT32 filesize; // for network UINT8 md5sum[16]; @@ -152,6 +152,9 @@ void W_InitMultipleFiles(char **filenames); #define W_FileHasFolders(wadfile) ((wadfile)->type == RET_PK3 || (wadfile)->type == RET_FOLDER) +boolean W_IsPathToFolderValid(const char *path); +char *W_GetFullFolderPath(const char *path); + const char *W_CheckNameForNumPwad(UINT16 wad, UINT16 lump); const char *W_CheckNameForNum(lumpnum_t lumpnum); From d5cdb2dc789530ab00a874a56e37f31d7ec4b3f8 Mon Sep 17 00:00:00 2001 From: SMS Alfredo Date: Sun, 12 Sep 2021 22:04:28 +0000 Subject: [PATCH 125/126] Add a missing scale assignment to A_Boss3ShockThink --- src/p_enemy.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/p_enemy.c b/src/p_enemy.c index 6a92c5d33..d148b11e0 100644 --- a/src/p_enemy.c +++ b/src/p_enemy.c @@ -8292,6 +8292,10 @@ void A_Boss3ShockThink(mobj_t *actor) snew->angle = (actor->angle + snext->angle) >> 1; P_SetTarget(&snew->target, actor->target); snew->fuse = actor->fuse; + + P_SetScale(snew, actor->scale); + snew->destscale = actor->destscale; + snew->scalespeed = actor->scalespeed; P_SetTarget(&actor->hnext, snew); P_SetTarget(&snew->hnext, snext); From db92f31f7d41ddd4356acdbf3cd7f4f420d7d34f Mon Sep 17 00:00:00 2001 From: Tatsuru Date: Sun, 12 Sep 2021 18:41:36 -0400 Subject: [PATCH 126/126] Revert "Merge branch 'draw-act-num' into 'next'" This reverts merge request !1532 --- src/lua_hudlib.c | 38 -------------------------------------- 1 file changed, 38 deletions(-) diff --git a/src/lua_hudlib.c b/src/lua_hudlib.c index 0bfcfd9ea..3ce36f4f1 100644 --- a/src/lua_hudlib.c +++ b/src/lua_hudlib.c @@ -919,27 +919,6 @@ static int libd_drawString(lua_State *L) return 0; } -static int libd_drawLevelActNum(lua_State *L) -{ - INT32 x; - INT32 y; - UINT8 num; - INT32 flags; - - HUDONLY - - x = luaL_checkinteger(L, 1); - y = luaL_checkinteger(L, 2); - num = luaL_checkinteger(L, 3); - flags = luaL_optinteger(L, 4, 0); - - flags &= ~V_PARAMMASK; // Don't let crashes happen. - - V_DrawLevelActNum(x, y, flags, num); - return 0; -} - - static int libd_drawNameTag(lua_State *L) { INT32 x; @@ -1044,20 +1023,6 @@ static int libd_stringWidth(lua_State *L) return 1; } -static int libd_levelActNumWidth(lua_State *L) -{ - HUDONLY - lua_pushinteger(L, V_LevelActNumWidth(luaL_checkinteger(L, 1))); - return 1; -} - -static int libd_levelActNumHeight(lua_State *L) -{ - HUDONLY - lua_pushinteger(L, V_LevelActNumHeight(luaL_checkinteger(L, 1))); - return 1; -} - static int libd_nameTagWidth(lua_State *L) { HUDONLY @@ -1287,15 +1252,12 @@ static luaL_Reg lib_draw[] = { {"drawPaddedNum", libd_drawPaddedNum}, {"drawFill", libd_drawFill}, {"drawString", libd_drawString}, - {"drawLevelActNum", libd_drawLevelActNum}, {"drawNameTag", libd_drawNameTag}, {"drawScaledNameTag", libd_drawScaledNameTag}, {"drawLevelTitle", libd_drawLevelTitle}, {"fadeScreen", libd_fadeScreen}, // misc {"stringWidth", libd_stringWidth}, - {"levelActNumWidth", libd_levelActNumWidth}, - {"levelActNumHeight", libd_levelActNumHeight}, {"nameTagWidth", libd_nameTagWidth}, {"levelTitleWidth", libd_levelTitleWidth}, {"levelTitleHeight", libd_levelTitleHeight},