diff --git a/src/blua/lbaselib.c b/src/blua/lbaselib.c index 3c919cb64..644565c28 100644 --- a/src/blua/lbaselib.c +++ b/src/blua/lbaselib.c @@ -11,6 +11,10 @@ #include #include +#include "../doomdef.h" +#include "../lua_script.h" +#include "../w_wad.h" + #define lbaselib_c #define LUA_LIB @@ -263,6 +267,27 @@ static int luaB_ipairs (lua_State *L) { } +// Edited to load PK3 entries instead +static int luaB_dofile (lua_State *L) { + const char *filename = luaL_checkstring(L, 1); + char fullfilename[256]; + UINT16 lumpnum; + int n = lua_gettop(L); + + if (wadfiles[numwadfiles - 1]->type != RET_PK3) + luaL_error(L, "dofile() only works with PK3 files"); + + snprintf(fullfilename, sizeof(fullfilename), "Lua/%s", filename); + lumpnum = W_CheckNumForFullNamePK3(fullfilename, numwadfiles - 1, 0); + if (lumpnum == INT16_MAX) + luaL_error(L, "can't find script " LUA_QS, fullfilename); + + LUA_LoadLump(numwadfiles - 1, lumpnum, false); + + return lua_gettop(L) - n; +} + + static int luaB_assert (lua_State *L) { luaL_checkany(L, 1); if (!lua_toboolean(L, 1)) @@ -380,6 +405,7 @@ static const luaL_Reg base_funcs[] = { {"assert", luaB_assert}, {"collectgarbage", luaB_collectgarbage}, {"error", luaB_error}, + {"dofile", luaB_dofile}, {"gcinfo", luaB_gcinfo}, {"getfenv", luaB_getfenv}, {"getmetatable", luaB_getmetatable}, diff --git a/src/blua/liolib.c b/src/blua/liolib.c index b43052194..a055aad3f 100644 --- a/src/blua/liolib.c +++ b/src/blua/liolib.c @@ -284,8 +284,16 @@ void Got_LuaFile(UINT8 **cp, INT32 playernum) // Push the first argument (file handle or nil) on the stack if (success) { + char mode[4]; + + // Ensure we are opening in binary mode + // (if it's a text file, newlines have been converted already) + strcpy(mode, luafiletransfers->mode); + if (!strchr(mode, 'b')) + strcat(mode, "b"); + pf = newfile(gL); // Create and push the file handle - *pf = fopen(luafiletransfers->realfilename, luafiletransfers->mode); // Open the file + *pf = fopen(luafiletransfers->realfilename, mode); // Open the file if (!*pf) I_Error("Can't open file \"%s\"\n", luafiletransfers->realfilename); // The file SHOULD exist } @@ -313,17 +321,14 @@ void Got_LuaFile(UINT8 **cp, INT32 playernum) RemoveLuaFileTransfer(); - if (server && luafiletransfers) + if (waitingforluafilecommand) { - if (FIL_FileOK(luafiletransfers->realfilename)) - SV_PrepareSendLuaFileToNextNode(); - else - { - // Send a net command with 0 as its first byte to indicate the file couldn't be opened - success = 0; - SendNetXCmd(XD_LUAFILE, &success, 1); - } + waitingforluafilecommand = false; + CL_PrepareDownloadLuaFile(); } + + if (server && luafiletransfers) + SV_PrepareSendLuaFile(); } diff --git a/src/d_clisrv.c b/src/d_clisrv.c index 7ff836c6e..95927710a 100644 --- a/src/d_clisrv.c +++ b/src/d_clisrv.c @@ -1163,6 +1163,497 @@ static void CV_LoadPlayerNames(UINT8 **p) } #ifdef CLIENT_LOADINGSCREEN +#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 *x, UINT8 *y, UINT8 headx, UINT8 heady) +{ + 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)); +} + +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 + 1 < 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 // @@ -1177,8 +1668,8 @@ static inline void CL_DrawConnectionStatus(void) V_DrawFadeScreen(0xFF00, 16); // force default // Draw the bottom box. - M_DrawTextBox(BASEVIDWIDTH/2-128-8, BASEVIDHEIGHT-24-8, 32, 1); - V_DrawCenteredString(BASEVIDWIDTH/2, BASEVIDHEIGHT-24-24, V_YELLOWMAP, "Press ESC to abort"); + 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) { @@ -1187,8 +1678,9 @@ static inline void CL_DrawConnectionStatus(void) // 15 pal entries total. const char *cltext; - for (i = 0; i < 16; ++i) - V_DrawFill((BASEVIDWIDTH/2-128) + (i * 16), BASEVIDHEIGHT-24, 16, 8, palstart + ((animtime - i) & 15)); + 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) { @@ -1196,11 +1688,23 @@ static inline void CL_DrawConnectionStatus(void) 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(); - V_DrawString(BASEVIDWIDTH/2-128, BASEVIDHEIGHT-24, V_20TRANS|V_MONOSPACE, - va(" %4uK",fileneeded[lastfilenum].currentsize>>10)); - V_DrawRightAlignedString(BASEVIDWIDTH/2+128, BASEVIDHEIGHT-24, V_20TRANS|V_MONOSPACE, + + 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 @@ -1215,7 +1719,7 @@ static inline void CL_DrawConnectionStatus(void) cltext = M_GetText("Connecting to server..."); break; } - V_DrawCenteredString(BASEVIDWIDTH/2, BASEVIDHEIGHT-24-32, V_YELLOWMAP, cltext); + V_DrawCenteredString(BASEVIDWIDTH/2, BASEVIDHEIGHT-16-24, V_YELLOWMAP, cltext); } else { @@ -1226,12 +1730,14 @@ static inline void CL_DrawConnectionStatus(void) 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-24, 256, 8, 111); - V_DrawFill(BASEVIDWIDTH/2-128, BASEVIDHEIGHT-24, dldlength, 8, 96); + 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 @@ -1249,15 +1755,15 @@ static inline void CL_DrawConnectionStatus(void) strncpy(tempname, filename, sizeof(tempname)-1); } - V_DrawCenteredString(BASEVIDWIDTH/2, BASEVIDHEIGHT-24-32, V_YELLOWMAP, + V_DrawCenteredString(BASEVIDWIDTH/2, BASEVIDHEIGHT-16-24, V_YELLOWMAP, va(M_GetText("Downloading \"%s\""), tempname)); - V_DrawString(BASEVIDWIDTH/2-128, BASEVIDHEIGHT-24, V_20TRANS|V_MONOSPACE, + 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-24, V_20TRANS|V_MONOSPACE, + V_DrawRightAlignedString(BASEVIDWIDTH/2+128, BASEVIDHEIGHT-16, V_20TRANS|V_MONOSPACE, va("%3.1fK/s ", ((double)getbps)/1024)); } else - V_DrawCenteredString(BASEVIDWIDTH/2, BASEVIDHEIGHT-24-32, V_YELLOWMAP, + V_DrawCenteredString(BASEVIDWIDTH/2, BASEVIDHEIGHT-16-24, V_YELLOWMAP, M_GetText("Waiting to download files...")); } } @@ -1605,7 +2111,7 @@ static void SV_SendSaveGame(INT32 node) WRITEUINT32(savebuffer, 0); } - SV_SendRam(node, buffertosend, length, SF_RAM, 0); + AddRamToSendQueue(node, buffertosend, length, SF_RAM, 0); save_p = NULL; // Remember when we started sending the savegame so we can handle timeouts @@ -1985,8 +2491,11 @@ static boolean CL_ServerConnectionSearchTicker(boolean viams, tic_t *asksent) return false; } // no problem if can't send packet, we will retry later - if (CL_SendRequestFile()) + if (CL_SendFileRequest()) + { cl_mode = CL_DOWNLOADFILES; + Snake_Initialise(); + } } } else @@ -2050,6 +2559,12 @@ static boolean CL_ServerConnectionTicker(boolean viams, const char *tmpsave, tic if (waitmore) break; // exit the case + if (snake) + { + free(snake); + snake = NULL; + } + cl_mode = CL_ASKJOIN; // don't break case continue to cljoin request now /* FALLTHRU */ @@ -2096,36 +2611,54 @@ static boolean CL_ServerConnectionTicker(boolean viams, const char *tmpsave, tic // Call it only once by tic if (*oldtic != I_GetTime()) { - INT32 key; - I_OsPolling(); - key = I_GetKey(); - if (key == KEY_ESCAPE || key == KEY_JOY1+1) + 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); + + if (snake) + { + free(snake); + snake = NULL; + } + D_QuitNetGame(); CL_Reset(); D_StartTitle(); + memset(gamekeydown, 0, NUMKEYS); return false; } + else if (cl_mode == CL_DOWNLOADFILES && snake) + Snake_Handle(); + + 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; - //SV_FileSendTicker(); + //FileSendTicker(); *oldtic = I_GetTime(); #ifdef CLIENT_LOADINGSCREEN if (client && cl_mode != CL_CONNECTED && cl_mode != CL_ABORTED) { - F_MenuPresTicker(true); // title sky - F_TitleScreenTicker(true); - F_TitleScreenDrawer(); + 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(); @@ -3247,6 +3780,7 @@ void D_QuitNetGame(void) CloseNetFile(); RemoveAllLuaFileTransfers(); waitingforluafiletransfer = false; + waitingforluafilecommand = false; if (server) { @@ -3920,13 +4454,23 @@ static void HandlePacketFromAwayNode(SINT8 node) break; } SERVERONLY - Got_Filetxpak(); + 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 || !Got_RequestFilePak(node)) + 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 @@ -4222,11 +4766,7 @@ static void HandlePacketFromPlayer(SINT8 node) break; case PT_ASKLUAFILE: if (server && luafiletransfers && luafiletransfers->nodestatus[node] == LFTNS_ASKED) - { - char *name = va("%s" PATHSEP "%s", luafiledir, luafiletransfers->filename); - boolean textmode = !strchr(luafiletransfers->mode, 'b'); - SV_SendLuaFile(node, name, textmode); - } + AddLuaFileToSendQueue(node, luafiletransfers->realfilename); break; case PT_HASLUAFILE: if (server && luafiletransfers && luafiletransfers->nodestatus[node] == LFTNS_SENDING) @@ -4357,7 +4897,15 @@ static void HandlePacketFromPlayer(SINT8 node) break; } if (client) - Got_Filetxpak(); + PT_FileFragment(); + break; + case PT_FILEACK: + if (server) + PT_FileAck(); + break; + case PT_FILERECEIVED: + if (server) + PT_FileReceived(); break; case PT_SENDINGLUAFILE: if (client) @@ -5047,7 +5595,7 @@ void NetUpdate(void) CON_Ticker(); } - SV_FileSendTicker(); + FileSendTicker(); } /** Returns the number of players playing. diff --git a/src/d_clisrv.h b/src/d_clisrv.h index 1135b043c..6b06764f9 100644 --- a/src/d_clisrv.h +++ b/src/d_clisrv.h @@ -76,6 +76,8 @@ typedef enum // In addition, this packet can't occupy all the available slots. PT_FILEFRAGMENT = PT_CANFAIL, // A part of a file. + PT_FILEACK, + PT_FILERECEIVED, PT_TEXTCMD, // Extra text commands from the client. PT_TEXTCMD2, // Splitscreen text commands. @@ -324,13 +326,30 @@ typedef struct UINT8 varlengthinputs[0]; // Playernames and netvars } ATTRPACK serverconfig_pak; -typedef struct { +typedef struct +{ UINT8 fileid; + UINT32 filesize; + UINT8 iteration; UINT32 position; UINT16 size; UINT8 data[0]; // Size is variable using hardware_MAXPACKETLENGTH } ATTRPACK filetx_pak; +typedef struct +{ + UINT32 start; + UINT32 acks; +} ATTRPACK fileacksegment_t; + +typedef struct +{ + UINT8 fileid; + UINT8 iteration; + UINT8 numsegments; + fileacksegment_t segments[0]; +} ATTRPACK fileack_pak; + #ifdef _MSC_VER #pragma warning(default : 4200) #endif @@ -446,6 +465,8 @@ typedef struct UINT8 resynchgot; // UINT8 textcmd[MAXTEXTCMD+1]; // 66049 bytes (wut??? 64k??? More like 257 bytes...) filetx_pak filetxpak; // 139 bytes + fileack_pak fileack; + UINT8 filereceived; clientconfig_pak clientcfg; // 136 bytes UINT8 md5sum[16]; serverinfo_pak serverinfo; // 1024 bytes diff --git a/src/d_main.c b/src/d_main.c index e4978205c..6057e65a7 100644 --- a/src/d_main.c +++ b/src/d_main.c @@ -1266,6 +1266,9 @@ void D_SRB2Main(void) // rand() needs seeded regardless of password srand((unsigned int)time(NULL)); + rand(); + rand(); + rand(); if (M_CheckParm("-password") && M_IsNextParm()) D_SetPassword(M_GetNextParm()); diff --git a/src/d_net.c b/src/d_net.c index 8e62b8d25..2823ce219 100644 --- a/src/d_net.c +++ b/src/d_net.c @@ -806,6 +806,9 @@ static const char *packettypename[NUMPACKETTYPE] = "HASLUAFILE", "FILEFRAGMENT", + "FILEACK", + "FILERECEIVED", + "TEXTCMD", "TEXTCMD2", "CLIENTJOIN", diff --git a/src/d_netcmd.c b/src/d_netcmd.c index 84070135a..8b75741ae 100644 --- a/src/d_netcmd.c +++ b/src/d_netcmd.c @@ -502,6 +502,8 @@ void D_RegisterServerCommands(void) COM_AddCommand("archivetest", Command_Archivetest_f); #endif + COM_AddCommand("downloads", Command_Downloads_f); + // for master server connection AddMServCommands(); diff --git a/src/d_netfil.c b/src/d_netfil.c index 6d3ac7f9d..7b99fddfb 100644 --- a/src/d_netfil.c +++ b/src/d_netfil.c @@ -56,7 +56,7 @@ #include // Prototypes -static boolean SV_SendFile(INT32 node, const char *filename, UINT8 fileid); +static boolean AddFileToSendQueue(INT32 node, const char *filename, UINT8 fileid); // Sender structure typedef struct filetx_s @@ -69,7 +69,6 @@ typedef struct filetx_s UINT32 size; // Size of the file UINT8 fileid; INT32 node; // Destination - boolean textmode; // For files requested by Lua without the "b" option struct filetx_s *next; // Next file in the list } filetx_t; @@ -77,8 +76,13 @@ typedef struct filetx_s typedef struct filetran_s { filetx_t *txlist; // Linked list of all files for the node + UINT8 iteration; + UINT8 ackediteration; UINT32 position; // The current position in the file + boolean *ackedfragments; + UINT32 ackedsize; FILE *currentfile; // The file currently being sent/received + tic_t dontsenduntil; } filetran_t; static filetran_t transfer[MAXNETNODES]; @@ -88,8 +92,20 @@ static filetran_t transfer[MAXNETNODES]; // Receiver structure INT32 fileneedednum; // Number of files needed to join the server fileneeded_t fileneeded[MAX_WADFILES]; // List of needed files +static tic_t lasttimeackpacketsent = 0; char downloaddir[512] = "DOWNLOAD"; +// For resuming failed downloads +typedef struct +{ + char filename[MAX_WADPATH]; + UINT8 md5sum[16]; + boolean *receivedfragments; + UINT32 fragmentsize; + UINT32 currentsize; +} pauseddownload_t; +static pauseddownload_t *pauseddownload = NULL; + #ifdef CLIENT_LOADINGSCREEN // for cl loading screen INT32 lastfilenum = -1; @@ -97,6 +113,7 @@ INT32 lastfilenum = -1; luafiletransfer_t *luafiletransfers = NULL; boolean waitingforluafiletransfer = false; +boolean waitingforluafilecommand = false; char luafiledir[256 + 16] = "luafiles"; @@ -159,25 +176,29 @@ void D_ParseFileneeded(INT32 fileneedednum_parm, UINT8 *fileneededstr) for (i = 0; i < fileneedednum; i++) { 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].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 READSTRINGN(p, fileneeded[i].filename, MAX_WADPATH); // The next bytes are the file name READMEM(p, fileneeded[i].md5sum, 16); // The last 16 bytes are the file checksum - fileneeded[i].textmode = false; } } void CL_PrepareDownloadSaveGame(const char *tmpsave) { +#ifdef CLIENT_LOADINGSCREEN + lastfilenum = -1; +#endif + fileneedednum = 1; fileneeded[0].status = FS_REQUESTED; + fileneeded[0].justdownloaded = false; fileneeded[0].totalsize = UINT32_MAX; fileneeded[0].file = NULL; memset(fileneeded[0].md5sum, 0, 16); strcpy(fileneeded[0].filename, tmpsave); - fileneeded[0].textmode = false; } /** Checks the server to see if we CAN download all the files, @@ -246,6 +267,31 @@ boolean CL_CheckDownloadable(void) return false; } +/** Returns true if a needed file transfer can be resumed + * + * \param file The needed file to resume the transfer for + * \return True if the transfer can be resumed + * + */ +static boolean CL_CanResumeDownload(fileneeded_t *file) +{ + return pauseddownload + && !strcmp(pauseddownload->filename, file->filename) // Same name + && !memcmp(pauseddownload->md5sum, file->md5sum, 16) // Same checksum + && pauseddownload->fragmentsize == file->fragmentsize; // Same fragment size +} + +void CL_AbortDownloadResume(void) +{ + if (!pauseddownload) + return; + + free(pauseddownload->receivedfragments); + remove(pauseddownload->filename); + free(pauseddownload); + pauseddownload = NULL; +} + /** Sends requests for files in the ::fileneeded table with a status of * ::FS_NOTFOUND. * @@ -253,7 +299,7 @@ boolean CL_CheckDownloadable(void) * \note Sends a PT_REQUESTFILE packet * */ -boolean CL_SendRequestFile(void) +boolean CL_SendFileRequest(void) { char *p; INT32 i; @@ -298,7 +344,7 @@ boolean CL_SendRequestFile(void) // get request filepak and put it on the send queue // returns false if a requested file was not found or cannot be sent -boolean Got_RequestFilePak(INT32 node) +boolean PT_RequestFile(INT32 node) { char wad[MAX_WADPATH+1]; UINT8 *p = netbuffer->u.textcmd; @@ -309,7 +355,7 @@ boolean Got_RequestFilePak(INT32 node) if (id == 0xFF) break; READSTRINGN(p, wad, MAX_WADPATH); - if (!SV_SendFile(node, wad, id)) + if (!AddFileToSendQueue(node, wad, id)) { SV_AbortSendFiles(node); return false; // don't read the rest of the files @@ -462,8 +508,6 @@ void AddLuaFileTransfer(const char *filename, const char *mode) luafiletransfer_t *filetransfer; static INT32 id; - //CONS_Printf("AddLuaFileTransfer \"%s\"\n", filename); - // Find the last transfer in the list and set a pointer to its "next" field prevnext = &luafiletransfers; while (*prevnext) @@ -493,26 +537,11 @@ void AddLuaFileTransfer(const char *filename, const char *mode) strlcpy(filetransfer->mode, mode, sizeof(filetransfer->mode)); - if (server) - { - INT32 i; - - // Set status to "waiting" for everyone - for (i = 0; i < MAXNETNODES; i++) - filetransfer->nodestatus[i] = LFTNS_WAITING; - - if (!luafiletransfers->next) // Only if there is no transfer already going on - { - if (FIL_ReadFileOK(filetransfer->realfilename)) - SV_PrepareSendLuaFileToNextNode(); - else - { - // Send a net command with 0 as its first byte to indicate the file couldn't be opened - UINT8 success = 0; - SendNetXCmd(XD_LUAFILE, &success, 1); - } - } - } + // Only if there is no transfer already going on + if (server && filetransfer == luafiletransfers) + SV_PrepareSendLuaFile(); + else + filetransfer->ongoing = false; // Store the callback so it can be called once everyone has the file filetransfer->id = id; @@ -526,7 +555,7 @@ void AddLuaFileTransfer(const char *filename, const char *mode) } } -void SV_PrepareSendLuaFileToNextNode(void) +static void SV_PrepareSendLuaFileToNextNode(void) { INT32 i; UINT8 success = 1; @@ -550,6 +579,45 @@ void SV_PrepareSendLuaFileToNextNode(void) SendNetXCmd(XD_LUAFILE, &success, 1); } +void SV_PrepareSendLuaFile(void) +{ + char *binfilename; + INT32 i; + + luafiletransfers->ongoing = true; + + // Set status to "waiting" for everyone + for (i = 0; i < MAXNETNODES; i++) + luafiletransfers->nodestatus[i] = LFTNS_WAITING; + + if (FIL_ReadFileOK(luafiletransfers->realfilename)) + { + // If opening in text mode, convert all newlines to LF + if (!strchr(luafiletransfers->mode, 'b')) + { + binfilename = strdup(va("%s" PATHSEP "$$$%d%d.tmp", + luafiledir, rand(), rand())); + if (!binfilename) + I_Error("SV_PrepareSendLuaFile: Out of memory\n"); + + if (!FIL_ConvertTextFileToBinary(luafiletransfers->realfilename, binfilename)) + I_Error("SV_PrepareSendLuaFile: Failed to convert file newlines\n"); + + // Use the temporary file instead + free(luafiletransfers->realfilename); + luafiletransfers->realfilename = binfilename; + } + + SV_PrepareSendLuaFileToNextNode(); + } + else + { + // Send a net command with 0 as its first byte to indicate the file couldn't be opened + UINT8 success = 0; + SendNetXCmd(XD_LUAFILE, &success, 1); + } +} + void SV_HandleLuaFileSent(UINT8 node) { luafiletransfers->nodestatus[node] = LFTNS_SENT; @@ -560,6 +628,10 @@ void RemoveLuaFileTransfer(void) { luafiletransfer_t *filetransfer = luafiletransfers; + // If it was a temporary file, delete it + if (server && !strchr(filetransfer->mode, 'b')) + remove(filetransfer->realfilename); + RemoveLuaFileCallback(filetransfer->id); luafiletransfers = filetransfer->next; @@ -596,20 +668,28 @@ void CL_PrepareDownloadLuaFile(void) return; } + if (luafiletransfers->ongoing) + { + waitingforluafilecommand = true; + return; + } + // Tell the server we are ready to receive the file netbuffer->packettype = PT_ASKLUAFILE; HSendPacket(servernode, true, 0, 0); fileneedednum = 1; fileneeded[0].status = FS_REQUESTED; + fileneeded[0].justdownloaded = false; fileneeded[0].totalsize = UINT32_MAX; fileneeded[0].file = NULL; memset(fileneeded[0].md5sum, 0, 16); strcpy(fileneeded[0].filename, luafiletransfers->realfilename); - fileneeded[0].textmode = !strchr(luafiletransfers->mode, 'b'); // Make sure all directories in the file path exist MakePathDirs(fileneeded[0].filename); + + luafiletransfers->ongoing = true; } // Number of files to send @@ -620,12 +700,12 @@ static INT32 filestosend = 0; * * \param node The node to send the file to * \param filename The file to send - * \param fileid ??? - * \sa SV_SendRam - * \sa SV_SendLuaFile + * \param fileid The index of the file in the list of added files + * \sa AddRamToSendQueue + * \sa AddLuaFileToSendQueue * */ -static boolean SV_SendFile(INT32 node, const char *filename, UINT8 fileid) +static boolean AddFileToSendQueue(INT32 node, const char *filename, UINT8 fileid) { filetx_t **q; // A pointer to the "next" field of the last file in the list filetx_t *p; // The new file request @@ -643,7 +723,7 @@ static boolean SV_SendFile(INT32 node, const char *filename, UINT8 fileid) // Allocate a file request and append it to the file list p = *q = (filetx_t *)malloc(sizeof (filetx_t)); if (!p) - I_Error("SV_SendFile: No more memory\n"); + I_Error("AddFileToSendQueue: No more memory\n"); // Initialise with zeros memset(p, 0, sizeof (filetx_t)); @@ -651,7 +731,7 @@ static boolean SV_SendFile(INT32 node, const char *filename, UINT8 fileid) // Allocate the file name p->id.filename = (char *)malloc(MAX_WADPATH); if (!p->id.filename) - I_Error("SV_SendFile: No more memory\n"); + I_Error("AddFileToSendQueue: No more memory\n"); // Set the file name and get rid of the path strlcpy(p->id.filename, filename, MAX_WADPATH); @@ -711,12 +791,12 @@ static boolean SV_SendFile(INT32 node, const char *filename, UINT8 fileid) * \param data The memory block to send * \param size The size of the block in bytes * \param freemethod How to free the block after it has been sent - * \param fileid ??? - * \sa SV_SendFile - * \sa SV_SendLuaFile + * \param fileid The index of the file in the list of added files + * \sa AddFileToSendQueue + * \sa AddLuaFileToSendQueue * */ -void SV_SendRam(INT32 node, void *data, size_t size, freemethod_t freemethod, UINT8 fileid) +void AddRamToSendQueue(INT32 node, void *data, size_t size, freemethod_t freemethod, UINT8 fileid) { filetx_t **q; // A pointer to the "next" field of the last file in the list filetx_t *p; // The new file request @@ -729,7 +809,7 @@ void SV_SendRam(INT32 node, void *data, size_t size, freemethod_t freemethod, UI // Allocate a file request and append it to the file list p = *q = (filetx_t *)malloc(sizeof (filetx_t)); if (!p) - I_Error("SV_SendRam: No more memory\n"); + I_Error("AddRamToSendQueue: No more memory\n"); // Initialise with zeros memset(p, 0, sizeof (filetx_t)); @@ -749,11 +829,11 @@ void SV_SendRam(INT32 node, void *data, size_t size, freemethod_t freemethod, UI * * \param node The node to send the file to * \param filename The file to send - * \sa SV_SendFile - * \sa SV_SendRam + * \sa AddFileToSendQueue + * \sa AddRamToSendQueue * */ -boolean SV_SendLuaFile(INT32 node, const char *filename, boolean textmode) +boolean AddLuaFileToSendQueue(INT32 node, const char *filename) { filetx_t **q; // A pointer to the "next" field of the last file in the list filetx_t *p; // The new file request @@ -770,7 +850,7 @@ boolean SV_SendLuaFile(INT32 node, const char *filename, boolean textmode) // Allocate a file request and append it to the file list p = *q = (filetx_t *)malloc(sizeof (filetx_t)); if (!p) - I_Error("SV_SendLuaFile: No more memory\n"); + I_Error("AddLuaFileToSendQueue: No more memory\n"); // Initialise with zeros memset(p, 0, sizeof (filetx_t)); @@ -778,15 +858,12 @@ boolean SV_SendLuaFile(INT32 node, const char *filename, boolean textmode) // Allocate the file name p->id.filename = (char *)malloc(MAX_WADPATH); // !!! if (!p->id.filename) - I_Error("SV_SendLuaFile: No more memory\n"); + I_Error("AddLuaFileToSendQueue: No more memory\n"); // Set the file name and get rid of the path strlcpy(p->id.filename, filename, MAX_WADPATH); // !!! //nameonly(p->id.filename); - // Open in text mode if required by the Lua script - p->textmode = textmode; - DEBFILE(va("Sending Lua file %s to %d\n", filename, node)); p->ram = SF_FILE; // It's a file, we need to close it and free its name once we're done sending it p->next = NULL; // End of list @@ -804,7 +881,8 @@ static void SV_EndFileSend(INT32 node) { filetx_t *p = transfer[node].txlist; - // Free the file request according to the freemethod parameter used with SV_SendFile/Ram + // Free the file request according to the freemethod + // parameter used with AddFileToSendQueue/AddRamToSendQueue switch (p->ram) { case SF_FILE: // It's a file, close it and free its filename @@ -829,43 +907,32 @@ static void SV_EndFileSend(INT32 node) // Indicate that the transmission is over transfer[node].currentfile = NULL; + if (transfer[node].ackedfragments) + free(transfer[node].ackedfragments); + transfer[node].ackedfragments = NULL; filestosend--; } #define PACKETPERTIC net_bandwidth/(TICRATE*software_MAXPACKETLENGTH) +#define FILEFRAGMENTSIZE (software_MAXPACKETLENGTH - (FILETXHEADER + BASEPACKETSIZE)) /** Handles file transmission - * - * \todo Use an acknowledging method more adapted to file transmission - * The current download speed suffers from lack of ack packets, - * especially when the one downloading has high latency * */ -void SV_FileSendTicker(void) +void FileSendTicker(void) { static INT32 currentnode = 0; filetx_pak *p; - size_t size; + size_t fragmentsize; filetx_t *f; INT32 packetsent, ram, i, j; - INT32 maxpacketsent; if (!filestosend) // No file to send return; - if (cv_downloadspeed.value) // New (and experimental) behavior - { + if (cv_downloadspeed.value) // New behavior packetsent = cv_downloadspeed.value; - // Don't send more packets than we have free acks -#ifndef NONET - maxpacketsent = Net_GetFreeAcks(false) - 5; // Let 5 extra acks just in case -#else - maxpacketsent = 1; -#endif - if (packetsent > maxpacketsent && maxpacketsent > 0) // Send at least one packet - packetsent = maxpacketsent; - } else // Old behavior { packetsent = PACKETPERTIC; @@ -882,11 +949,12 @@ void SV_FileSendTicker(void) i = (i+1) % MAXNETNODES, j++) { if (transfer[i].txlist) - goto found; + break; } // no transfer to do - I_Error("filestosend=%d but no file to send found\n", filestosend); - found: + if (j >= MAXNETNODES) + I_Error("filestosend=%d but no file to send found\n", filestosend); + currentnode = (i+1) % MAXNETNODES; f = transfer[i].txlist; ram = f->ram; @@ -899,7 +967,7 @@ void SV_FileSendTicker(void) long filesize; transfer[i].currentfile = - fopen(f->id.filename, f->textmode ? "r" : "rb"); + fopen(f->id.filename, "rb"); if (!transfer[i].currentfile) I_Error("File %s does not exist", @@ -920,57 +988,232 @@ void SV_FileSendTicker(void) } else // Sending RAM transfer[i].currentfile = (FILE *)1; // Set currentfile to a non-null value to indicate that it is open + + transfer[i].iteration = 1; + transfer[i].ackediteration = 0; transfer[i].position = 0; + transfer[i].ackedsize = 0; + + transfer[i].ackedfragments = calloc(f->size / FILEFRAGMENTSIZE + 1, sizeof(*transfer[i].ackedfragments)); + if (!transfer[i].ackedfragments) + I_Error("FileSendTicker: No more memory\n"); + + transfer[i].dontsenduntil = 0; + } + + // If the client hasn't acknowledged any fragment from the previous iteration, + // it is most likely because their acks haven't had enough time to reach the server + // yet, due to latency. In that case, we wait a little to avoid useless resend. + if (I_GetTime() < transfer[i].dontsenduntil) + continue; + + // Find the first non-acknowledged fragment + while (transfer[i].ackedfragments[transfer[i].position / FILEFRAGMENTSIZE]) + { + transfer[i].position += FILEFRAGMENTSIZE; + if (transfer[i].position >= f->size) + { + if (transfer[i].ackediteration < transfer[i].iteration) + transfer[i].dontsenduntil = I_GetTime() + TICRATE / 2; + + transfer[i].position = 0; + transfer[i].iteration++; + } } // Build a packet containing a file fragment p = &netbuffer->u.filetxpak; - size = software_MAXPACKETLENGTH - (FILETXHEADER + BASEPACKETSIZE); - if (f->size-transfer[i].position < size) - size = f->size-transfer[i].position; + fragmentsize = FILEFRAGMENTSIZE; + if (f->size-transfer[i].position < fragmentsize) + fragmentsize = f->size-transfer[i].position; if (ram) - M_Memcpy(p->data, &f->id.ram[transfer[i].position], size); + M_Memcpy(p->data, &f->id.ram[transfer[i].position], fragmentsize); else { - size_t n = fread(p->data, 1, size, transfer[i].currentfile); - if (n != size) // Either an error or Windows turning CR-LF into LF - { - if (f->textmode && feof(transfer[i].currentfile)) - size = n; - else if (fread(p->data, 1, size, transfer[i].currentfile) != size) - I_Error("SV_FileSendTicker: can't read %s byte on %s at %d because %s", sizeu1(size), f->id.filename, transfer[i].position, M_FileError(transfer[i].currentfile)); - } + fseek(transfer[i].currentfile, transfer[i].position, SEEK_SET); + + if (fread(p->data, 1, fragmentsize, transfer[i].currentfile) != fragmentsize) + I_Error("FileSendTicker: can't read %s byte on %s at %d because %s", sizeu1(fragmentsize), f->id.filename, transfer[i].position, M_FileError(transfer[i].currentfile)); } + p->iteration = transfer[i].iteration; p->position = LONG(transfer[i].position); - // Put flag so receiver knows the total size - if (transfer[i].position + size == f->size || (f->textmode && feof(transfer[i].currentfile))) - p->position |= LONG(0x80000000); p->fileid = f->fileid; - p->size = SHORT((UINT16)size); + p->filesize = LONG(f->size); + p->size = SHORT((UINT16)FILEFRAGMENTSIZE); // Send the packet - if (HSendPacket(i, true, 0, FILETXHEADER + size)) // Reliable SEND + if (HSendPacket(i, false, 0, FILETXHEADER + fragmentsize)) // Don't use the default acknowledgement system { // Success - transfer[i].position = (UINT32)(transfer[i].position + size); - if (transfer[i].position == f->size || (f->textmode && feof(transfer[i].currentfile))) // Finish? - SV_EndFileSend(i); + transfer[i].position = (UINT32)(transfer[i].position + fragmentsize); + if (transfer[i].position >= f->size) + { + if (transfer[i].ackediteration < transfer[i].iteration) + transfer[i].dontsenduntil = I_GetTime() + TICRATE / 2; + + transfer[i].position = 0; + transfer[i].iteration++; + } } else { // Not sent for some odd reason, retry at next call - if (!ram) - fseek(transfer[i].currentfile,transfer[i].position, SEEK_SET); // Exit the while (can't send this one so why should i send the next?) break; } } } -void Got_Filetxpak(void) +void PT_FileAck(void) +{ + fileack_pak *packet = &netbuffer->u.fileack; + INT32 node = doomcom->remotenode; + filetran_t *trans = &transfer[node]; + INT32 i, j; + + // Wrong file id? Ignore it, it's probably a late packet + if (!(trans->txlist && packet->fileid == trans->txlist->fileid)) + return; + + if (packet->numsegments * sizeof(*packet->segments) != doomcom->datalength - BASEPACKETSIZE - sizeof(*packet)) + { + Net_CloseConnection(node); + return; + } + + if (packet->iteration > trans->ackediteration) + { + trans->ackediteration = packet->iteration; + if (trans->ackediteration >= trans->iteration - 1) + trans->dontsenduntil = 0; + } + + for (i = 0; i < packet->numsegments; i++) + { + fileacksegment_t *segment = &packet->segments[i]; + + for (j = 0; j < 32; j++) + if (LONG(segment->acks) & (1 << j)) + { + if (LONG(segment->start) * FILEFRAGMENTSIZE >= trans->txlist->size) + { + Net_CloseConnection(node); + return; + } + + if (!trans->ackedfragments[LONG(segment->start) + j]) + { + trans->ackedfragments[LONG(segment->start) + j] = true; + trans->ackedsize += FILEFRAGMENTSIZE; + + // If the last missing fragment was acked, finish! + if (trans->ackedsize == trans->txlist->size) + { + SV_EndFileSend(node); + return; + } + } + } + } +} + +void PT_FileReceived(void) +{ + filetx_t *trans = transfer[doomcom->remotenode].txlist; + + if (trans && netbuffer->u.filereceived == trans->fileid) + SV_EndFileSend(doomcom->remotenode); +} + +static void SendAckPacket(fileack_pak *packet, UINT8 fileid) +{ + size_t packetsize; + INT32 i; + + packetsize = sizeof(*packet) + packet->numsegments * sizeof(*packet->segments); + + // Finalise the packet + packet->fileid = fileid; + for (i = 0; i < packet->numsegments; i++) + { + packet->segments[i].start = LONG(packet->segments[i].start); + packet->segments[i].acks = LONG(packet->segments[i].acks); + } + + // Send the packet + netbuffer->packettype = PT_FILEACK; + M_Memcpy(&netbuffer->u.fileack, packet, packetsize); + HSendPacket(servernode, false, 0, packetsize); + + // Clear the packet + memset(packet, 0, sizeof(*packet) + 512); +} + +static void AddFragmentToAckPacket(fileack_pak *packet, UINT8 iteration, UINT32 fragmentpos, UINT8 fileid) +{ + fileacksegment_t *segment = &packet->segments[packet->numsegments - 1]; + + packet->iteration = max(packet->iteration, iteration); + + if (packet->numsegments == 0 + || fragmentpos < segment->start + || fragmentpos - segment->start >= 32) + { + // If the packet becomes too big, send it + if ((packet->numsegments + 1) * sizeof(*segment) > 512) + SendAckPacket(packet, fileid); + + packet->numsegments++; + segment = &packet->segments[packet->numsegments - 1]; + segment->start = fragmentpos; + } + + // Set the bit that represents the fragment + segment->acks |= 1 << (fragmentpos - segment->start); +} + +void FileReceiveTicker(void) +{ + INT32 i; + + for (i = 0; i < fileneedednum; i++) + { + fileneeded_t *file = &fileneeded[i]; + + if (file->status == FS_DOWNLOADING) + { + if (lasttimeackpacketsent - I_GetTime() > TICRATE / 2) + SendAckPacket(file->ackpacket, i); + + // When resuming a tranfer, start with telling + // the server what parts we already received + if (file->ackresendposition != UINT32_MAX && file->status == FS_DOWNLOADING) + { + // Acknowledge ~70 MB/s, whichs means the client sends ~18 KB/s + INT32 j; + for (j = 0; j < 2048; j++) + { + if (file->receivedfragments[file->ackresendposition]) + AddFragmentToAckPacket(file->ackpacket, file->iteration, file->ackresendposition, i); + + file->ackresendposition++; + if (file->ackresendposition * file->fragmentsize >= file->totalsize) + { + file->ackresendposition = UINT32_MAX; + break; + } + } + } + } + } +} + +void PT_FileFragment(void) { INT32 filenum = netbuffer->u.filetxpak.fileid; fileneeded_t *file = &fileneeded[filenum]; + UINT32 fragmentpos = LONG(netbuffer->u.filetxpak.position); + UINT16 fragmentsize = SHORT(netbuffer->u.filetxpak.size); + UINT16 boundedfragmentsize = doomcom->datalength - BASEPACKETSIZE - sizeof(netbuffer->u.filetxpak); char *filename; - static INT32 filetime = 0; filename = va("%s", file->filename); nameonly(filename); @@ -995,49 +1238,105 @@ void Got_Filetxpak(void) if (file->status == FS_REQUESTED) { if (file->file) - I_Error("Got_Filetxpak: already open file\n"); - file->file = fopen(filename, file->textmode ? "w" : "wb"); - if (!file->file) - I_Error("Can't create file %s: %s", filename, strerror(errno)); - CONS_Printf("\r%s...\n",filename); - file->currentsize = 0; + I_Error("PT_FileFragment: already open file\n"); + file->status = FS_DOWNLOADING; + file->fragmentsize = fragmentsize; + file->iteration = 0; + + file->ackpacket = calloc(1, sizeof(*file->ackpacket) + 512); + if (!file->ackpacket) + I_Error("FileSendTicker: No more memory\n"); + + if (CL_CanResumeDownload(file)) + { + file->file = fopen(filename, "r+b"); + if (!file->file) + I_Error("Can't reopen file %s: %s", filename, strerror(errno)); + CONS_Printf("\r%s...\n", filename); + + CONS_Printf("Resuming download...\n"); + file->currentsize = pauseddownload->currentsize; + file->receivedfragments = pauseddownload->receivedfragments; + file->ackresendposition = 0; + + free(pauseddownload); + pauseddownload = NULL; + } + else + { + CL_AbortDownloadResume(); + + file->file = fopen(filename, "wb"); + if (!file->file) + I_Error("Can't create file %s: %s", filename, strerror(errno)); + + CONS_Printf("\r%s...\n",filename); + + file->currentsize = 0; + file->totalsize = LONG(netbuffer->u.filetxpak.filesize); + file->ackresendposition = UINT32_MAX; // Only used for resumed downloads + + file->receivedfragments = calloc(file->totalsize / fragmentsize + 1, sizeof(*file->receivedfragments)); + if (!file->receivedfragments) + I_Error("FileSendTicker: No more memory\n"); + } + + lasttimeackpacketsent = I_GetTime(); } if (file->status == FS_DOWNLOADING) { - UINT32 pos = LONG(netbuffer->u.filetxpak.position); - UINT16 size = SHORT(netbuffer->u.filetxpak.size); - // Use a special trick to know when the file is complete (not always used) - // WARNING: file fragments can arrive out of order so don't stop yet! - if (pos & 0x80000000) - { - pos &= ~0x80000000; - file->totalsize = pos + size; - } - // We can receive packet in the wrong order, anyway all os support gaped file - fseek(file->file, pos, SEEK_SET); - if (size && fwrite(netbuffer->u.filetxpak.data,size,1,file->file) != 1) - I_Error("Can't write to %s: %s\n",filename, M_FileError(file->file)); - file->currentsize += size; + if (fragmentpos >= file->totalsize) + I_Error("Invalid file fragment\n"); - // Finished? - if (file->currentsize == file->totalsize) + file->iteration = max(file->iteration, netbuffer->u.filetxpak.iteration); + + if (!file->receivedfragments[fragmentpos / fragmentsize]) // Not received yet { - fclose(file->file); - file->file = NULL; - file->status = FS_FOUND; - CONS_Printf(M_GetText("Downloading %s...(done)\n"), - filename); - if (luafiletransfers) + file->receivedfragments[fragmentpos / fragmentsize] = true; + + // We can receive packets in the wrong order, anyway all OSes support gaped files + fseek(file->file, fragmentpos, SEEK_SET); + if (fragmentsize && fwrite(netbuffer->u.filetxpak.data, boundedfragmentsize, 1, file->file) != 1) + I_Error("Can't write to %s: %s\n",filename, M_FileError(file->file)); + file->currentsize += boundedfragmentsize; + + AddFragmentToAckPacket(file->ackpacket, file->iteration, fragmentpos / fragmentsize, filenum); + + // Finished? + if (file->currentsize == file->totalsize) { + fclose(file->file); + file->file = NULL; + free(file->receivedfragments); + free(file->ackpacket); + file->status = FS_FOUND; + file->justdownloaded = true; + CONS_Printf(M_GetText("Downloading %s...(done)\n"), + filename); + // Tell the server we have received the file - netbuffer->packettype = PT_HASLUAFILE; - HSendPacket(servernode, true, 0, 0); + netbuffer->packettype = PT_FILERECEIVED; + netbuffer->u.filereceived = filenum; + HSendPacket(servernode, true, 0, 1); + + if (luafiletransfers) + { + // Tell the server we have received the file + netbuffer->packettype = PT_HASLUAFILE; + HSendPacket(servernode, true, 0, 0); + } } } + else // Already received + { + // If they are sending us the fragment again, it's probably because + // they missed our previous ack, so we must re-acknowledge it + AddFragmentToAckPacket(file->ackpacket, file->iteration, fragmentpos / fragmentsize, filenum); + } } - else + else if (!file->justdownloaded) { const char *s; switch(file->status) @@ -1060,12 +1359,6 @@ void Got_Filetxpak(void) } I_Error("Received a file not requested (file id: %d, file status: %s)\n", filenum, s); } - // Send ack back quickly - if (++filetime == 3) - { - Net_SendAcks(servernode); - filetime = 0; - } #ifdef CLIENT_LOADINGSCREEN lastfilenum = filenum; @@ -1078,7 +1371,7 @@ void Got_Filetxpak(void) * \return True if the node is downloading a file * */ -boolean SV_SendingFile(INT32 node) +boolean SendingFile(INT32 node) { return transfer[node].txlist != NULL; } @@ -1107,12 +1400,62 @@ void CloseNetFile(void) if (fileneeded[i].status == FS_DOWNLOADING && fileneeded[i].file) { fclose(fileneeded[i].file); - // File is not complete delete it - remove(fileneeded[i].filename); - } + free(fileneeded[i].ackpacket); - // Remove PT_FILEFRAGMENT from acknowledge list - Net_AbortPacketType(PT_FILEFRAGMENT); + if (!pauseddownload && i != 0) // 0 is either srb2.srb or the gamestate... + { + // Don't remove the file, save it for later in case we resume the download + pauseddownload = malloc(sizeof(*pauseddownload)); + if (!pauseddownload) + I_Error("CloseNetFile: No more memory\n"); + + strcpy(pauseddownload->filename, fileneeded[i].filename); + memcpy(pauseddownload->md5sum, fileneeded[i].md5sum, 16); + pauseddownload->currentsize = fileneeded[i].currentsize; + pauseddownload->receivedfragments = fileneeded[i].receivedfragments; + pauseddownload->fragmentsize = fileneeded[i].fragmentsize; + } + else + { + free(fileneeded[i].receivedfragments); + // File is not complete delete it + remove(fileneeded[i].filename); + } + } +} + +void Command_Downloads_f(void) +{ + INT32 node; + + for (node = 0; node < MAXNETNODES; node++) + if (transfer[node].txlist + && transfer[node].txlist->ram == SF_FILE) // Node is downloading a file? + { + const char *name = transfer[node].txlist->id.filename; + UINT32 position = transfer[node].ackedsize; + UINT32 size = transfer[node].txlist->size; + char ratecolor; + + // Avoid division by zero errors + if (!size) + size = 1; + + name = &name[strlen(name) - nameonlylength(name)]; + switch (4 * (position - 1) / size) + { + case 0: ratecolor = '\x85'; break; + case 1: ratecolor = '\x87'; break; + case 2: ratecolor = '\x82'; break; + case 3: ratecolor = '\x83'; break; + default: ratecolor = '\x80'; + } + + CONS_Printf("%2d %c%s ", node, ratecolor, name); // Node and file name + CONS_Printf("\x80%uK\x84/\x80%uK ", position / 1024, size / 1024); // Progress in kB + CONS_Printf("\x80(%c%u%%\x80) ", ratecolor, (UINT32)(100.0 * position / size)); // Progress in % + CONS_Printf("%s\n", I_GetNodeAddress(node)); // Address and newline + } } // Functions cut and pasted from Doomatic :) diff --git a/src/d_netfil.h b/src/d_netfil.h index 7d6efada0..2225157cb 100644 --- a/src/d_netfil.h +++ b/src/d_netfil.h @@ -14,6 +14,7 @@ #define __D_NETFIL__ #include "d_net.h" +#include "d_clisrv.h" #include "w_wad.h" typedef enum @@ -39,12 +40,18 @@ typedef struct UINT8 willsend; // Is the server willing to send it? char filename[MAX_WADPATH]; UINT8 md5sum[16]; + filestatus_t status; // The value returned by recsearch + boolean justdownloaded; // To prevent late fragments from causing an I_Error + // Used only for download FILE *file; + boolean *receivedfragments; + UINT32 fragmentsize; + UINT8 iteration; + fileack_pak *ackpacket; UINT32 currentsize; UINT32 totalsize; - filestatus_t status; // The value returned by recsearch - boolean textmode; // For files requested by Lua without the "b" option + UINT32 ackresendposition; // Used when resuming downloads } fileneeded_t; extern INT32 fileneedednum; @@ -61,16 +68,20 @@ void CL_PrepareDownloadSaveGame(const char *tmpsave); INT32 CL_CheckFiles(void); void CL_LoadServerFiles(void); -void SV_SendRam(INT32 node, void *data, size_t size, freemethod_t freemethod, +void AddRamToSendQueue(INT32 node, void *data, size_t size, freemethod_t freemethod, UINT8 fileid); -void SV_FileSendTicker(void); -void Got_Filetxpak(void); -boolean SV_SendingFile(INT32 node); +void FileSendTicker(void); +void PT_FileAck(void); +void PT_FileReceived(void); +boolean SendingFile(INT32 node); + +void FileReceiveTicker(void); +void PT_FileFragment(void); boolean CL_CheckDownloadable(void); -boolean CL_SendRequestFile(void); -boolean Got_RequestFilePak(INT32 node); +boolean CL_SendFileRequest(void); +boolean PT_RequestFile(INT32 node); typedef enum { @@ -86,18 +97,19 @@ typedef struct luafiletransfer_s char *realfilename; char mode[4]; // rb+/wb+/ab+ + null character INT32 id; // Callback ID + boolean ongoing; luafiletransfernodestatus_t nodestatus[MAXNETNODES]; struct luafiletransfer_s *next; } luafiletransfer_t; extern luafiletransfer_t *luafiletransfers; extern boolean waitingforluafiletransfer; +extern boolean waitingforluafilecommand; extern char luafiledir[256 + 16]; void AddLuaFileTransfer(const char *filename, const char *mode); -void SV_PrepareSendLuaFileToNextNode(void); -boolean SV_SendLuaFile(INT32 node, const char *filename, boolean textmode); -void SV_PrepareSendLuaFile(const char *filename); +void SV_PrepareSendLuaFile(void); +boolean AddLuaFileToSendQueue(INT32 node, const char *filename); void SV_HandleLuaFileSent(UINT8 node); void RemoveLuaFileTransfer(void); void RemoveAllLuaFileTransfers(void); @@ -110,6 +122,9 @@ void MakePathDirs(char *path); void SV_AbortSendFiles(INT32 node); void CloseNetFile(void); +void CL_AbortDownloadResume(void); + +void Command_Downloads_f(void); boolean fileexist(char *filename, time_t ptime); diff --git a/src/dehacked.c b/src/dehacked.c index bd8a07b90..88aaeac54 100644 --- a/src/dehacked.c +++ b/src/dehacked.c @@ -769,7 +769,8 @@ static void readthing(MYFILE *f, INT32 num) static void readskincolor(MYFILE *f, INT32 num) { char *s = Z_Malloc(MAXLINELEN, PU_STATIC, NULL); - char *word, *word2, *word3; + char *word = s; + char *word2; char *tmp; Color_cons_t[num].value = num; @@ -781,32 +782,31 @@ static void readskincolor(MYFILE *f, INT32 num) if (s[0] == '\n') break; + // First remove trailing newline, if there is one + tmp = strchr(s, '\n'); + if (tmp) + *tmp = '\0'; + tmp = strchr(s, '#'); if (tmp) *tmp = '\0'; if (s == tmp) continue; // Skip comment lines, but don't break. - word = strtok(s, " "); - if (word) - strupr(word); + // Get the part before the " = " + tmp = strchr(s, '='); + if (tmp) + *(tmp-1) = '\0'; else break; + strupr(word); - word2 = strtok(NULL, " = "); - if (word2) { - word3 = Z_StrDup(word2); - strupr(word2); - } else - break; - if (word2[strlen(word2)-1] == '\n') - word2[strlen(word2)-1] = '\0'; - if (word3[strlen(word3)-1] == '\n') - word3[strlen(word3)-1] = '\0'; + // Now get the part after + word2 = tmp += 2; if (fastcmp(word, "NAME")) { - deh_strlcpy(skincolors[num].name, word3, + deh_strlcpy(skincolors[num].name, word2, sizeof (skincolors[num].name), va("Skincolor %d: name", num)); } else if (fastcmp(word, "RAMP")) diff --git a/src/hardware/r_opengl/r_opengl.c b/src/hardware/r_opengl/r_opengl.c index f08fae105..6fe9f7a36 100644 --- a/src/hardware/r_opengl/r_opengl.c +++ b/src/hardware/r_opengl/r_opengl.c @@ -2610,7 +2610,7 @@ EXPORT void HWRAPI(CreateModelVBOs) (model_t *model) } } -#define BUFFER_OFFSET(i) ((char*)(i)) +#define BUFFER_OFFSET(i) ((void*)(i)) static void DrawModelEx(model_t *model, INT32 frameIndex, INT32 duration, INT32 tics, INT32 nextFrameIndex, FTransform *pos, float scale, UINT8 flipped, FSurfaceInfo *Surface) { diff --git a/src/i_tcp.c b/src/i_tcp.c index 373ea1bd0..5180869a5 100644 --- a/src/i_tcp.c +++ b/src/i_tcp.c @@ -487,7 +487,7 @@ static void cleanupnodes(void) // Why can't I start at zero? for (j = 1; j < MAXNETNODES; j++) - if (!(nodeingame[j] || SV_SendingFile(j))) + if (!(nodeingame[j] || SendingFile(j))) nodeconnected[j] = false; } diff --git a/src/lua_script.c b/src/lua_script.c index 06ea18b0e..374294d6e 100644 --- a/src/lua_script.c +++ b/src/lua_script.c @@ -447,11 +447,13 @@ void LUA_ClearExtVars(void) // Use this variable to prevent certain functions from running // if they were not called on lump load // (i.e. they were called in hooks or coroutines etc) -boolean lua_lumploading = false; +INT32 lua_lumploading = 0; // Load a script from a MYFILE -static inline void LUA_LoadFile(MYFILE *f, char *name) +static inline void LUA_LoadFile(MYFILE *f, char *name, boolean noresults) { + int errorhandlerindex; + if (!name) name = wadfiles[f->wad]->filename; CONS_Printf("Loading Lua script from %s\n", name); @@ -460,21 +462,22 @@ static inline void LUA_LoadFile(MYFILE *f, char *name) lua_pushinteger(gL, f->wad); lua_setfield(gL, LUA_REGISTRYINDEX, "WAD"); - lua_lumploading = true; // turn on loading flag + lua_lumploading++; // turn on loading flag lua_pushcfunction(gL, LUA_GetErrorMessage); - if (luaL_loadbuffer(gL, f->data, f->size, va("@%s",name)) || lua_pcall(gL, 0, 0, lua_gettop(gL) - 1)) { + errorhandlerindex = lua_gettop(gL); + if (luaL_loadbuffer(gL, f->data, f->size, va("@%s",name)) || lua_pcall(gL, 0, noresults ? 0 : LUA_MULTRET, lua_gettop(gL) - 1)) { CONS_Alert(CONS_WARNING,"%s\n",lua_tostring(gL,-1)); lua_pop(gL,1); } lua_gc(gL, LUA_GCCOLLECT, 0); - lua_pop(gL, 1); // Pop error handler + lua_remove(gL, errorhandlerindex); - lua_lumploading = false; // turn off again + lua_lumploading--; // turn off again } // Load a script from a lump -void LUA_LoadLump(UINT16 wad, UINT16 lump) +void LUA_LoadLump(UINT16 wad, UINT16 lump, boolean noresults) { MYFILE f; char *name; @@ -501,7 +504,7 @@ void LUA_LoadLump(UINT16 wad, UINT16 lump) name[len] = '\0'; } - LUA_LoadFile(&f, name); // actually load file! + LUA_LoadFile(&f, name, noresults); // actually load file! free(name); Z_Free(f.data); diff --git a/src/lua_script.h b/src/lua_script.h index 9568503e1..5a3520d11 100644 --- a/src/lua_script.h +++ b/src/lua_script.h @@ -37,10 +37,10 @@ void LUA_ClearExtVars(void); #endif -extern boolean lua_lumploading; // is LUA_LoadLump being called? +extern INT32 lua_lumploading; // is LUA_LoadLump being called? int LUA_GetErrorMessage(lua_State *L); -void LUA_LoadLump(UINT16 wad, UINT16 lump); +void LUA_LoadLump(UINT16 wad, UINT16 lump, boolean noresults); #ifdef LUA_ALLOW_BYTECODE void LUA_DumpFile(const char *filename); #endif diff --git a/src/m_misc.c b/src/m_misc.c index 920a13198..c527d2296 100644 --- a/src/m_misc.c +++ b/src/m_misc.c @@ -297,6 +297,44 @@ size_t FIL_ReadFileTag(char const *name, UINT8 **buffer, INT32 tag) return length; } +/** Makes a copy of a text file with all newlines converted into LF newlines. + * + * \param textfilename The name of the source file + * \param binfilename The name of the destination file + */ +boolean FIL_ConvertTextFileToBinary(const char *textfilename, const char *binfilename) +{ + FILE *textfile; + FILE *binfile; + UINT8 buffer[1024]; + size_t count; + boolean success; + + textfile = fopen(textfilename, "r"); + if (!textfile) + return false; + + binfile = fopen(binfilename, "wb"); + if (!binfile) + { + fclose(textfile); + return false; + } + + do + { + count = fread(buffer, 1, sizeof(buffer), textfile); + fwrite(buffer, 1, count, binfile); + } while (count); + + success = !(ferror(textfile) || ferror(binfile)); + + fclose(textfile); + fclose(binfile); + + return success; +} + /** Check if the filename exists * * \param name Filename to check. diff --git a/src/m_misc.h b/src/m_misc.h index d64faea59..dbded37d0 100644 --- a/src/m_misc.h +++ b/src/m_misc.h @@ -48,6 +48,8 @@ boolean FIL_WriteFile(char const *name, const void *source, size_t length); size_t FIL_ReadFileTag(char const *name, UINT8 **buffer, INT32 tag); #define FIL_ReadFile(n, b) FIL_ReadFileTag(n, b, PU_STATIC) +boolean FIL_ConvertTextFileToBinary(const char *textfilename, const char *binfilename); + boolean FIL_FileExists(const char *name); boolean FIL_WriteFileOK(char const *name); boolean FIL_ReadFileOK(char const *name); diff --git a/src/p_enemy.c b/src/p_enemy.c index 75d32c506..578874574 100644 --- a/src/p_enemy.c +++ b/src/p_enemy.c @@ -5106,9 +5106,11 @@ void A_SignPlayer(mobj_t *actor) INT32 locvar2 = var2; skin_t *skin = NULL; mobj_t *ov; - UINT16 facecolor, signcolor = (UINT16)locvar2; + UINT16 facecolor, signcolor = 0; UINT32 signframe = states[actor->info->raisestate].frame; + facecolor = signcolor = (UINT16)locvar2; + if (LUA_CallAction("A_SignPlayer", actor)) return; @@ -5213,7 +5215,7 @@ void A_SignPlayer(mobj_t *actor) actor->tracer->color = signcolor; if (signcolor && signcolor < numskincolors) - signframe += (15 - skincolors[signcolor].invshade); + signframe += (15 - skincolors[facecolor].invshade); actor->tracer->frame = signframe; } diff --git a/src/sdl/i_system.c b/src/sdl/i_system.c index 191268011..27e015ef7 100644 --- a/src/sdl/i_system.c +++ b/src/sdl/i_system.c @@ -301,6 +301,7 @@ static void I_ReportSignal(int num, int coredumped) FUNCNORETURN static ATTRNORETURN void signal_handler(INT32 num) { D_QuitNetGame(); // Fix server freezes + CL_AbortDownloadResume(); I_ReportSignal(num, 0); I_ShutdownSystem(); signal(num, SIG_DFL); //default signal action @@ -2323,6 +2324,7 @@ void I_Quit(void) G_StopMetalRecording(false); D_QuitNetGame(); + CL_AbortDownloadResume(); M_FreePlayerSetupColors(); I_ShutdownMusic(); I_ShutdownSound(); @@ -2440,6 +2442,7 @@ void I_Error(const char *error, ...) G_StopMetalRecording(false); D_QuitNetGame(); + CL_AbortDownloadResume(); M_FreePlayerSetupColors(); I_ShutdownMusic(); I_ShutdownSound(); diff --git a/src/sdl/mixer_sound.c b/src/sdl/mixer_sound.c index 9a713608c..5a086880a 100644 --- a/src/sdl/mixer_sound.c +++ b/src/sdl/mixer_sound.c @@ -880,13 +880,18 @@ boolean I_SetSongSpeed(float speed) #ifdef HAVE_OPENMPT if (openmpt_mhandle) { - char modspd[13]; - if (speed > 4.0f) speed = 4.0f; // Limit this to 4x to prevent crashing, stupid fix but... ~SteelT 27/9/19 - - sprintf(modspd, "%g", speed); - openmpt_module_ctl_set(openmpt_mhandle, "play.tempo_factor", modspd); +#if OPENMPT_API_VERSION_MAJOR < 1 && OPENMPT_API_VERSION_MINOR < 5 + { + // deprecated in 0.5.0 + char modspd[13]; + sprintf(modspd, "%g", speed); + openmpt_module_ctl_set(openmpt_mhandle, "play.tempo_factor", modspd); + } +#else + openmpt_module_ctl_set_floatingpoint(openmpt_mhandle, "play.tempo_factor", (double)speed); +#endif return true; } #else diff --git a/src/w_wad.c b/src/w_wad.c index c01780ec2..9af661b57 100644 --- a/src/w_wad.c +++ b/src/w_wad.c @@ -199,12 +199,20 @@ static inline void W_LoadDehackedLumpsPK3(UINT16 wadnum, boolean mainfile) { UINT16 posStart, posEnd; - posStart = W_CheckNumForFolderStartPK3("Lua/", wadnum, 0); + posStart = W_CheckNumForFullNamePK3("Init.lua", wadnum, 0); if (posStart != INT16_MAX) { - posEnd = W_CheckNumForFolderEndPK3("Lua/", wadnum, posStart); - for (; posStart < posEnd; posStart++) - LUA_LoadLump(wadnum, posStart); + LUA_LoadLump(wadnum, posStart, true); + } + else + { + posStart = W_CheckNumForFolderStartPK3("Lua/", wadnum, 0); + if (posStart != INT16_MAX) + { + posEnd = W_CheckNumForFolderEndPK3("Lua/", wadnum, posStart); + for (; posStart < posEnd; posStart++) + LUA_LoadLump(wadnum, posStart, true); + } } posStart = W_CheckNumForFolderStartPK3("SOC/", wadnum, 0); @@ -236,7 +244,7 @@ static inline void W_LoadDehackedLumps(UINT16 wadnum, boolean mainfile) lumpinfo_t *lump_p = wadfiles[wadnum]->lumpinfo; for (lump = 0; lump < wadfiles[wadnum]->numlumps; lump++, lump_p++) if (memcmp(lump_p->name,"LUA_",4)==0) - LUA_LoadLump(wadnum, lump); + LUA_LoadLump(wadnum, lump, true); } { @@ -863,7 +871,7 @@ UINT16 W_InitFile(const char *filename, boolean mainfile, boolean startup) DEH_LoadDehackedLumpPwad(numwadfiles - 1, 0, mainfile); break; case RET_LUA: - LUA_LoadLump(numwadfiles - 1, 0); + LUA_LoadLump(numwadfiles - 1, 0, true); break; default: break; diff --git a/src/win32/win_sys.c b/src/win32/win_sys.c index 6c4ff4484..a374a2587 100644 --- a/src/win32/win_sys.c +++ b/src/win32/win_sys.c @@ -467,6 +467,7 @@ static void signal_handler(int num) char sigdef[64]; D_QuitNetGame(); // Fix server freezes + CL_AbortDownloadResume(); I_ShutdownSystem(); switch (num) @@ -652,6 +653,7 @@ void I_Error(const char *error, ...) G_StopMetalRecording(false); D_QuitNetGame(); + CL_AbortDownloadResume(); M_FreePlayerSetupColors(); // shutdown everything that was started @@ -748,6 +750,7 @@ void I_Quit(void) // or something else that will be finished by I_ShutdownSystem(), // so do it before. D_QuitNetGame(); + CL_AbortDownloadResume(); M_FreePlayerSetupColors();