Merge branch 'next' of https://git.magicalgirl.moe/STJr/SRB2 into sal-oglshaderport

# Conflicts:
#	src/hardware/r_opengl/r_opengl.c
This commit is contained in:
Steel Titanium 2020-06-17 18:15:07 -04:00
commit 3946309ece
21 changed files with 1265 additions and 235 deletions

View File

@ -11,6 +11,10 @@
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
#include "../doomdef.h"
#include "../lua_script.h"
#include "../w_wad.h"
#define lbaselib_c #define lbaselib_c
#define LUA_LIB #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) { static int luaB_assert (lua_State *L) {
luaL_checkany(L, 1); luaL_checkany(L, 1);
if (!lua_toboolean(L, 1)) if (!lua_toboolean(L, 1))
@ -380,6 +405,7 @@ static const luaL_Reg base_funcs[] = {
{"assert", luaB_assert}, {"assert", luaB_assert},
{"collectgarbage", luaB_collectgarbage}, {"collectgarbage", luaB_collectgarbage},
{"error", luaB_error}, {"error", luaB_error},
{"dofile", luaB_dofile},
{"gcinfo", luaB_gcinfo}, {"gcinfo", luaB_gcinfo},
{"getfenv", luaB_getfenv}, {"getfenv", luaB_getfenv},
{"getmetatable", luaB_getmetatable}, {"getmetatable", luaB_getmetatable},

View File

@ -284,8 +284,16 @@ void Got_LuaFile(UINT8 **cp, INT32 playernum)
// Push the first argument (file handle or nil) on the stack // Push the first argument (file handle or nil) on the stack
if (success) 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 = 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) if (!*pf)
I_Error("Can't open file \"%s\"\n", luafiletransfers->realfilename); // The file SHOULD exist 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(); RemoveLuaFileTransfer();
if (server && luafiletransfers) if (waitingforluafilecommand)
{ {
if (FIL_FileOK(luafiletransfers->realfilename)) waitingforluafilecommand = false;
SV_PrepareSendLuaFileToNextNode(); CL_PrepareDownloadLuaFile();
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);
}
} }
if (server && luafiletransfers)
SV_PrepareSendLuaFile();
} }

View File

@ -1163,6 +1163,497 @@ static void CV_LoadPlayerNames(UINT8 **p)
} }
#ifdef CLIENT_LOADINGSCREEN #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 // CL_DrawConnectionStatus
// //
@ -1177,8 +1668,8 @@ static inline void CL_DrawConnectionStatus(void)
V_DrawFadeScreen(0xFF00, 16); // force default V_DrawFadeScreen(0xFF00, 16); // force default
// Draw the bottom box. // Draw the bottom box.
M_DrawTextBox(BASEVIDWIDTH/2-128-8, BASEVIDHEIGHT-24-8, 32, 1); M_DrawTextBox(BASEVIDWIDTH/2-128-8, BASEVIDHEIGHT-16-8, 32, 1);
V_DrawCenteredString(BASEVIDWIDTH/2, BASEVIDHEIGHT-24-24, V_YELLOWMAP, "Press ESC to abort"); V_DrawCenteredString(BASEVIDWIDTH/2, BASEVIDHEIGHT-16-16, V_YELLOWMAP, "Press ESC to abort");
if (cl_mode != CL_DOWNLOADFILES) if (cl_mode != CL_DOWNLOADFILES)
{ {
@ -1187,8 +1678,9 @@ static inline void CL_DrawConnectionStatus(void)
// 15 pal entries total. // 15 pal entries total.
const char *cltext; const char *cltext;
for (i = 0; i < 16; ++i) if (!(cl_mode == CL_DOWNLOADSAVEGAME && lastfilenum != -1))
V_DrawFill((BASEVIDWIDTH/2-128) + (i * 16), BASEVIDHEIGHT-24, 16, 8, palstart + ((animtime - i) & 15)); for (i = 0; i < 16; ++i)
V_DrawFill((BASEVIDWIDTH/2-128) + (i * 16), BASEVIDHEIGHT-16, 16, 8, palstart + ((animtime - i) & 15));
switch (cl_mode) switch (cl_mode)
{ {
@ -1196,11 +1688,23 @@ static inline void CL_DrawConnectionStatus(void)
case CL_DOWNLOADSAVEGAME: case CL_DOWNLOADSAVEGAME:
if (lastfilenum != -1) if (lastfilenum != -1)
{ {
UINT32 currentsize = fileneeded[lastfilenum].currentsize;
UINT32 totalsize = fileneeded[lastfilenum].totalsize;
INT32 dldlength;
cltext = M_GetText("Downloading game state..."); cltext = M_GetText("Downloading game state...");
Net_GetNetStat(); Net_GetNetStat();
V_DrawString(BASEVIDWIDTH/2-128, BASEVIDHEIGHT-24, V_20TRANS|V_MONOSPACE,
va(" %4uK",fileneeded[lastfilenum].currentsize>>10)); dldlength = (INT32)((currentsize/(double)totalsize) * 256);
V_DrawRightAlignedString(BASEVIDWIDTH/2+128, BASEVIDHEIGHT-24, V_20TRANS|V_MONOSPACE, 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)); va("%3.1fK/s ", ((double)getbps)/1024));
} }
else else
@ -1215,7 +1719,7 @@ static inline void CL_DrawConnectionStatus(void)
cltext = M_GetText("Connecting to server..."); cltext = M_GetText("Connecting to server...");
break; break;
} }
V_DrawCenteredString(BASEVIDWIDTH/2, BASEVIDHEIGHT-24-32, V_YELLOWMAP, cltext); V_DrawCenteredString(BASEVIDWIDTH/2, BASEVIDHEIGHT-16-24, V_YELLOWMAP, cltext);
} }
else else
{ {
@ -1226,12 +1730,14 @@ static inline void CL_DrawConnectionStatus(void)
fileneeded_t *file = &fileneeded[lastfilenum]; fileneeded_t *file = &fileneeded[lastfilenum];
char *filename = file->filename; char *filename = file->filename;
Snake_Draw();
Net_GetNetStat(); Net_GetNetStat();
dldlength = (INT32)((file->currentsize/(double)file->totalsize) * 256); dldlength = (INT32)((file->currentsize/(double)file->totalsize) * 256);
if (dldlength > 256) if (dldlength > 256)
dldlength = 256; dldlength = 256;
V_DrawFill(BASEVIDWIDTH/2-128, BASEVIDHEIGHT-24, 256, 8, 111); V_DrawFill(BASEVIDWIDTH/2-128, BASEVIDHEIGHT-16, 256, 8, 111);
V_DrawFill(BASEVIDWIDTH/2-128, BASEVIDHEIGHT-24, dldlength, 8, 96); V_DrawFill(BASEVIDWIDTH/2-128, BASEVIDHEIGHT-16, dldlength, 8, 96);
memset(tempname, 0, sizeof(tempname)); memset(tempname, 0, sizeof(tempname));
// offset filename to just the name only part // offset filename to just the name only part
@ -1249,15 +1755,15 @@ static inline void CL_DrawConnectionStatus(void)
strncpy(tempname, filename, sizeof(tempname)-1); 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)); 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)); 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)); va("%3.1fK/s ", ((double)getbps)/1024));
} }
else 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...")); M_GetText("Waiting to download files..."));
} }
} }
@ -1605,7 +2111,7 @@ static void SV_SendSaveGame(INT32 node)
WRITEUINT32(savebuffer, 0); WRITEUINT32(savebuffer, 0);
} }
SV_SendRam(node, buffertosend, length, SF_RAM, 0); AddRamToSendQueue(node, buffertosend, length, SF_RAM, 0);
save_p = NULL; save_p = NULL;
// Remember when we started sending the savegame so we can handle timeouts // 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; return false;
} }
// no problem if can't send packet, we will retry later // no problem if can't send packet, we will retry later
if (CL_SendRequestFile()) if (CL_SendFileRequest())
{
cl_mode = CL_DOWNLOADFILES; cl_mode = CL_DOWNLOADFILES;
Snake_Initialise();
}
} }
} }
else else
@ -2050,6 +2559,12 @@ static boolean CL_ServerConnectionTicker(boolean viams, const char *tmpsave, tic
if (waitmore) if (waitmore)
break; // exit the case break; // exit the case
if (snake)
{
free(snake);
snake = NULL;
}
cl_mode = CL_ASKJOIN; // don't break case continue to cljoin request now cl_mode = CL_ASKJOIN; // don't break case continue to cljoin request now
/* FALLTHRU */ /* FALLTHRU */
@ -2096,36 +2611,54 @@ static boolean CL_ServerConnectionTicker(boolean viams, const char *tmpsave, tic
// Call it only once by tic // Call it only once by tic
if (*oldtic != I_GetTime()) if (*oldtic != I_GetTime())
{ {
INT32 key;
I_OsPolling(); I_OsPolling();
key = I_GetKey(); for (; eventtail != eventhead; eventtail = (eventtail+1) & (MAXEVENTS-1))
if (key == KEY_ESCAPE || key == KEY_JOY1+1) G_MapEventsToControls(&events[eventtail]);
if (gamekeydown[KEY_ESCAPE] || gamekeydown[KEY_JOY1+1])
{ {
CONS_Printf(M_GetText("Network game synchronization aborted.\n")); CONS_Printf(M_GetText("Network game synchronization aborted.\n"));
// M_StartMessage(M_GetText("Network game synchronization aborted.\n\nPress ESC\n"), NULL, MM_NOTHING); // M_StartMessage(M_GetText("Network game synchronization aborted.\n\nPress ESC\n"), NULL, MM_NOTHING);
if (snake)
{
free(snake);
snake = NULL;
}
D_QuitNetGame(); D_QuitNetGame();
CL_Reset(); CL_Reset();
D_StartTitle(); D_StartTitle();
memset(gamekeydown, 0, NUMKEYS);
return false; 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 // why are these here? this is for servers, we're a client
//if (key == 's' && server) //if (key == 's' && server)
// doomcom->numnodes = (INT16)pnumnodes; // doomcom->numnodes = (INT16)pnumnodes;
//SV_FileSendTicker(); //FileSendTicker();
*oldtic = I_GetTime(); *oldtic = I_GetTime();
#ifdef CLIENT_LOADINGSCREEN #ifdef CLIENT_LOADINGSCREEN
if (client && cl_mode != CL_CONNECTED && cl_mode != CL_ABORTED) if (client && cl_mode != CL_CONNECTED && cl_mode != CL_ABORTED)
{ {
F_MenuPresTicker(true); // title sky if (cl_mode != CL_DOWNLOADFILES && cl_mode != CL_DOWNLOADSAVEGAME)
F_TitleScreenTicker(true); {
F_TitleScreenDrawer(); F_MenuPresTicker(true); // title sky
F_TitleScreenTicker(true);
F_TitleScreenDrawer();
}
CL_DrawConnectionStatus(); CL_DrawConnectionStatus();
I_UpdateNoVsync(); // page flip or blit buffer I_UpdateNoVsync(); // page flip or blit buffer
if (moviemode) if (moviemode)
M_SaveFrame(); M_SaveFrame();
S_UpdateSounds();
S_UpdateClosedCaptions();
} }
#else #else
CON_Drawer(); CON_Drawer();
@ -3247,6 +3780,7 @@ void D_QuitNetGame(void)
CloseNetFile(); CloseNetFile();
RemoveAllLuaFileTransfers(); RemoveAllLuaFileTransfers();
waitingforluafiletransfer = false; waitingforluafiletransfer = false;
waitingforluafilecommand = false;
if (server) if (server)
{ {
@ -3920,13 +4454,23 @@ static void HandlePacketFromAwayNode(SINT8 node)
break; break;
} }
SERVERONLY SERVERONLY
Got_Filetxpak(); PT_FileFragment();
break;
case PT_FILEACK:
if (server)
PT_FileAck();
break;
case PT_FILERECEIVED:
if (server)
PT_FileReceived();
break; break;
case PT_REQUESTFILE: case PT_REQUESTFILE:
if (server) 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 Net_CloseConnection(node); // close connection if one of the requested files could not be sent, or you disabled downloading anyway
} }
else else
@ -4222,11 +4766,7 @@ static void HandlePacketFromPlayer(SINT8 node)
break; break;
case PT_ASKLUAFILE: case PT_ASKLUAFILE:
if (server && luafiletransfers && luafiletransfers->nodestatus[node] == LFTNS_ASKED) if (server && luafiletransfers && luafiletransfers->nodestatus[node] == LFTNS_ASKED)
{ AddLuaFileToSendQueue(node, luafiletransfers->realfilename);
char *name = va("%s" PATHSEP "%s", luafiledir, luafiletransfers->filename);
boolean textmode = !strchr(luafiletransfers->mode, 'b');
SV_SendLuaFile(node, name, textmode);
}
break; break;
case PT_HASLUAFILE: case PT_HASLUAFILE:
if (server && luafiletransfers && luafiletransfers->nodestatus[node] == LFTNS_SENDING) if (server && luafiletransfers && luafiletransfers->nodestatus[node] == LFTNS_SENDING)
@ -4357,7 +4897,15 @@ static void HandlePacketFromPlayer(SINT8 node)
break; break;
} }
if (client) if (client)
Got_Filetxpak(); PT_FileFragment();
break;
case PT_FILEACK:
if (server)
PT_FileAck();
break;
case PT_FILERECEIVED:
if (server)
PT_FileReceived();
break; break;
case PT_SENDINGLUAFILE: case PT_SENDINGLUAFILE:
if (client) if (client)
@ -5047,7 +5595,7 @@ void NetUpdate(void)
CON_Ticker(); CON_Ticker();
} }
SV_FileSendTicker(); FileSendTicker();
} }
/** Returns the number of players playing. /** Returns the number of players playing.

View File

@ -76,6 +76,8 @@ typedef enum
// In addition, this packet can't occupy all the available slots. // In addition, this packet can't occupy all the available slots.
PT_FILEFRAGMENT = PT_CANFAIL, // A part of a file. PT_FILEFRAGMENT = PT_CANFAIL, // A part of a file.
PT_FILEACK,
PT_FILERECEIVED,
PT_TEXTCMD, // Extra text commands from the client. PT_TEXTCMD, // Extra text commands from the client.
PT_TEXTCMD2, // Splitscreen text commands. PT_TEXTCMD2, // Splitscreen text commands.
@ -324,13 +326,30 @@ typedef struct
UINT8 varlengthinputs[0]; // Playernames and netvars UINT8 varlengthinputs[0]; // Playernames and netvars
} ATTRPACK serverconfig_pak; } ATTRPACK serverconfig_pak;
typedef struct { typedef struct
{
UINT8 fileid; UINT8 fileid;
UINT32 filesize;
UINT8 iteration;
UINT32 position; UINT32 position;
UINT16 size; UINT16 size;
UINT8 data[0]; // Size is variable using hardware_MAXPACKETLENGTH UINT8 data[0]; // Size is variable using hardware_MAXPACKETLENGTH
} ATTRPACK filetx_pak; } 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 #ifdef _MSC_VER
#pragma warning(default : 4200) #pragma warning(default : 4200)
#endif #endif
@ -446,6 +465,8 @@ typedef struct
UINT8 resynchgot; // UINT8 resynchgot; //
UINT8 textcmd[MAXTEXTCMD+1]; // 66049 bytes (wut??? 64k??? More like 257 bytes...) UINT8 textcmd[MAXTEXTCMD+1]; // 66049 bytes (wut??? 64k??? More like 257 bytes...)
filetx_pak filetxpak; // 139 bytes filetx_pak filetxpak; // 139 bytes
fileack_pak fileack;
UINT8 filereceived;
clientconfig_pak clientcfg; // 136 bytes clientconfig_pak clientcfg; // 136 bytes
UINT8 md5sum[16]; UINT8 md5sum[16];
serverinfo_pak serverinfo; // 1024 bytes serverinfo_pak serverinfo; // 1024 bytes

View File

@ -1266,6 +1266,9 @@ void D_SRB2Main(void)
// rand() needs seeded regardless of password // rand() needs seeded regardless of password
srand((unsigned int)time(NULL)); srand((unsigned int)time(NULL));
rand();
rand();
rand();
if (M_CheckParm("-password") && M_IsNextParm()) if (M_CheckParm("-password") && M_IsNextParm())
D_SetPassword(M_GetNextParm()); D_SetPassword(M_GetNextParm());

View File

@ -806,6 +806,9 @@ static const char *packettypename[NUMPACKETTYPE] =
"HASLUAFILE", "HASLUAFILE",
"FILEFRAGMENT", "FILEFRAGMENT",
"FILEACK",
"FILERECEIVED",
"TEXTCMD", "TEXTCMD",
"TEXTCMD2", "TEXTCMD2",
"CLIENTJOIN", "CLIENTJOIN",

View File

@ -502,6 +502,8 @@ void D_RegisterServerCommands(void)
COM_AddCommand("archivetest", Command_Archivetest_f); COM_AddCommand("archivetest", Command_Archivetest_f);
#endif #endif
COM_AddCommand("downloads", Command_Downloads_f);
// for master server connection // for master server connection
AddMServCommands(); AddMServCommands();

View File

@ -56,7 +56,7 @@
#include <errno.h> #include <errno.h>
// Prototypes // Prototypes
static boolean SV_SendFile(INT32 node, const char *filename, UINT8 fileid); static boolean AddFileToSendQueue(INT32 node, const char *filename, UINT8 fileid);
// Sender structure // Sender structure
typedef struct filetx_s typedef struct filetx_s
@ -69,7 +69,6 @@ typedef struct filetx_s
UINT32 size; // Size of the file UINT32 size; // Size of the file
UINT8 fileid; UINT8 fileid;
INT32 node; // Destination INT32 node; // Destination
boolean textmode; // For files requested by Lua without the "b" option
struct filetx_s *next; // Next file in the list struct filetx_s *next; // Next file in the list
} filetx_t; } filetx_t;
@ -77,8 +76,13 @@ typedef struct filetx_s
typedef struct filetran_s typedef struct filetran_s
{ {
filetx_t *txlist; // Linked list of all files for the node filetx_t *txlist; // Linked list of all files for the node
UINT8 iteration;
UINT8 ackediteration;
UINT32 position; // The current position in the file UINT32 position; // The current position in the file
boolean *ackedfragments;
UINT32 ackedsize;
FILE *currentfile; // The file currently being sent/received FILE *currentfile; // The file currently being sent/received
tic_t dontsenduntil;
} filetran_t; } filetran_t;
static filetran_t transfer[MAXNETNODES]; static filetran_t transfer[MAXNETNODES];
@ -88,8 +92,20 @@ static filetran_t transfer[MAXNETNODES];
// Receiver structure // Receiver structure
INT32 fileneedednum; // Number of files needed to join the server INT32 fileneedednum; // Number of files needed to join the server
fileneeded_t fileneeded[MAX_WADFILES]; // List of needed files fileneeded_t fileneeded[MAX_WADFILES]; // List of needed files
static tic_t lasttimeackpacketsent = 0;
char downloaddir[512] = "DOWNLOAD"; 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 #ifdef CLIENT_LOADINGSCREEN
// for cl loading screen // for cl loading screen
INT32 lastfilenum = -1; INT32 lastfilenum = -1;
@ -97,6 +113,7 @@ INT32 lastfilenum = -1;
luafiletransfer_t *luafiletransfers = NULL; luafiletransfer_t *luafiletransfers = NULL;
boolean waitingforluafiletransfer = false; boolean waitingforluafiletransfer = false;
boolean waitingforluafilecommand = false;
char luafiledir[256 + 16] = "luafiles"; char luafiledir[256 + 16] = "luafiles";
@ -159,25 +176,29 @@ void D_ParseFileneeded(INT32 fileneedednum_parm, UINT8 *fileneededstr)
for (i = 0; i < fileneedednum; i++) for (i = 0; i < fileneedednum; i++)
{ {
fileneeded[i].status = FS_NOTFOUND; // We haven't even started looking for the file yet 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 filestatus = READUINT8(p); // The first byte is the file status
fileneeded[i].willsend = (UINT8)(filestatus >> 4); fileneeded[i].willsend = (UINT8)(filestatus >> 4);
fileneeded[i].totalsize = READUINT32(p); // The four next bytes are the file size fileneeded[i].totalsize = READUINT32(p); // The four next bytes are the file size
fileneeded[i].file = NULL; // The file isn't open yet fileneeded[i].file = NULL; // The file isn't open yet
READSTRINGN(p, fileneeded[i].filename, MAX_WADPATH); // The next bytes are the file name 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 READMEM(p, fileneeded[i].md5sum, 16); // The last 16 bytes are the file checksum
fileneeded[i].textmode = false;
} }
} }
void CL_PrepareDownloadSaveGame(const char *tmpsave) void CL_PrepareDownloadSaveGame(const char *tmpsave)
{ {
#ifdef CLIENT_LOADINGSCREEN
lastfilenum = -1;
#endif
fileneedednum = 1; fileneedednum = 1;
fileneeded[0].status = FS_REQUESTED; fileneeded[0].status = FS_REQUESTED;
fileneeded[0].justdownloaded = false;
fileneeded[0].totalsize = UINT32_MAX; fileneeded[0].totalsize = UINT32_MAX;
fileneeded[0].file = NULL; fileneeded[0].file = NULL;
memset(fileneeded[0].md5sum, 0, 16); memset(fileneeded[0].md5sum, 0, 16);
strcpy(fileneeded[0].filename, tmpsave); strcpy(fileneeded[0].filename, tmpsave);
fileneeded[0].textmode = false;
} }
/** Checks the server to see if we CAN download all the files, /** Checks the server to see if we CAN download all the files,
@ -246,6 +267,31 @@ boolean CL_CheckDownloadable(void)
return false; 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 /** Sends requests for files in the ::fileneeded table with a status of
* ::FS_NOTFOUND. * ::FS_NOTFOUND.
* *
@ -253,7 +299,7 @@ boolean CL_CheckDownloadable(void)
* \note Sends a PT_REQUESTFILE packet * \note Sends a PT_REQUESTFILE packet
* *
*/ */
boolean CL_SendRequestFile(void) boolean CL_SendFileRequest(void)
{ {
char *p; char *p;
INT32 i; INT32 i;
@ -298,7 +344,7 @@ boolean CL_SendRequestFile(void)
// get request filepak and put it on the send queue // get request filepak and put it on the send queue
// returns false if a requested file was not found or cannot be sent // 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]; char wad[MAX_WADPATH+1];
UINT8 *p = netbuffer->u.textcmd; UINT8 *p = netbuffer->u.textcmd;
@ -309,7 +355,7 @@ boolean Got_RequestFilePak(INT32 node)
if (id == 0xFF) if (id == 0xFF)
break; break;
READSTRINGN(p, wad, MAX_WADPATH); READSTRINGN(p, wad, MAX_WADPATH);
if (!SV_SendFile(node, wad, id)) if (!AddFileToSendQueue(node, wad, id))
{ {
SV_AbortSendFiles(node); SV_AbortSendFiles(node);
return false; // don't read the rest of the files 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; luafiletransfer_t *filetransfer;
static INT32 id; static INT32 id;
//CONS_Printf("AddLuaFileTransfer \"%s\"\n", filename);
// Find the last transfer in the list and set a pointer to its "next" field // Find the last transfer in the list and set a pointer to its "next" field
prevnext = &luafiletransfers; prevnext = &luafiletransfers;
while (*prevnext) while (*prevnext)
@ -493,26 +537,11 @@ void AddLuaFileTransfer(const char *filename, const char *mode)
strlcpy(filetransfer->mode, mode, sizeof(filetransfer->mode)); strlcpy(filetransfer->mode, mode, sizeof(filetransfer->mode));
if (server) // Only if there is no transfer already going on
{ if (server && filetransfer == luafiletransfers)
INT32 i; SV_PrepareSendLuaFile();
else
// Set status to "waiting" for everyone filetransfer->ongoing = false;
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);
}
}
}
// Store the callback so it can be called once everyone has the file // Store the callback so it can be called once everyone has the file
filetransfer->id = id; 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; INT32 i;
UINT8 success = 1; UINT8 success = 1;
@ -550,6 +579,45 @@ void SV_PrepareSendLuaFileToNextNode(void)
SendNetXCmd(XD_LUAFILE, &success, 1); 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) void SV_HandleLuaFileSent(UINT8 node)
{ {
luafiletransfers->nodestatus[node] = LFTNS_SENT; luafiletransfers->nodestatus[node] = LFTNS_SENT;
@ -560,6 +628,10 @@ void RemoveLuaFileTransfer(void)
{ {
luafiletransfer_t *filetransfer = luafiletransfers; luafiletransfer_t *filetransfer = luafiletransfers;
// If it was a temporary file, delete it
if (server && !strchr(filetransfer->mode, 'b'))
remove(filetransfer->realfilename);
RemoveLuaFileCallback(filetransfer->id); RemoveLuaFileCallback(filetransfer->id);
luafiletransfers = filetransfer->next; luafiletransfers = filetransfer->next;
@ -596,20 +668,28 @@ void CL_PrepareDownloadLuaFile(void)
return; return;
} }
if (luafiletransfers->ongoing)
{
waitingforluafilecommand = true;
return;
}
// Tell the server we are ready to receive the file // Tell the server we are ready to receive the file
netbuffer->packettype = PT_ASKLUAFILE; netbuffer->packettype = PT_ASKLUAFILE;
HSendPacket(servernode, true, 0, 0); HSendPacket(servernode, true, 0, 0);
fileneedednum = 1; fileneedednum = 1;
fileneeded[0].status = FS_REQUESTED; fileneeded[0].status = FS_REQUESTED;
fileneeded[0].justdownloaded = false;
fileneeded[0].totalsize = UINT32_MAX; fileneeded[0].totalsize = UINT32_MAX;
fileneeded[0].file = NULL; fileneeded[0].file = NULL;
memset(fileneeded[0].md5sum, 0, 16); memset(fileneeded[0].md5sum, 0, 16);
strcpy(fileneeded[0].filename, luafiletransfers->realfilename); strcpy(fileneeded[0].filename, luafiletransfers->realfilename);
fileneeded[0].textmode = !strchr(luafiletransfers->mode, 'b');
// Make sure all directories in the file path exist // Make sure all directories in the file path exist
MakePathDirs(fileneeded[0].filename); MakePathDirs(fileneeded[0].filename);
luafiletransfers->ongoing = true;
} }
// Number of files to send // Number of files to send
@ -620,12 +700,12 @@ static INT32 filestosend = 0;
* *
* \param node The node to send the file to * \param node The node to send the file to
* \param filename The file to send * \param filename The file to send
* \param fileid ??? * \param fileid The index of the file in the list of added files
* \sa SV_SendRam * \sa AddRamToSendQueue
* \sa SV_SendLuaFile * \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 **q; // A pointer to the "next" field of the last file in the list
filetx_t *p; // The new file request 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 // Allocate a file request and append it to the file list
p = *q = (filetx_t *)malloc(sizeof (filetx_t)); p = *q = (filetx_t *)malloc(sizeof (filetx_t));
if (!p) if (!p)
I_Error("SV_SendFile: No more memory\n"); I_Error("AddFileToSendQueue: No more memory\n");
// Initialise with zeros // Initialise with zeros
memset(p, 0, sizeof (filetx_t)); 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 // Allocate the file name
p->id.filename = (char *)malloc(MAX_WADPATH); p->id.filename = (char *)malloc(MAX_WADPATH);
if (!p->id.filename) 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 // Set the file name and get rid of the path
strlcpy(p->id.filename, filename, MAX_WADPATH); 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 data The memory block to send
* \param size The size of the block in bytes * \param size The size of the block in bytes
* \param freemethod How to free the block after it has been sent * \param freemethod How to free the block after it has been sent
* \param fileid ??? * \param fileid The index of the file in the list of added files
* \sa SV_SendFile * \sa AddFileToSendQueue
* \sa SV_SendLuaFile * \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 **q; // A pointer to the "next" field of the last file in the list
filetx_t *p; // The new file request 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 // Allocate a file request and append it to the file list
p = *q = (filetx_t *)malloc(sizeof (filetx_t)); p = *q = (filetx_t *)malloc(sizeof (filetx_t));
if (!p) if (!p)
I_Error("SV_SendRam: No more memory\n"); I_Error("AddRamToSendQueue: No more memory\n");
// Initialise with zeros // Initialise with zeros
memset(p, 0, sizeof (filetx_t)); 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 node The node to send the file to
* \param filename The file to send * \param filename The file to send
* \sa SV_SendFile * \sa AddFileToSendQueue
* \sa SV_SendRam * \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 **q; // A pointer to the "next" field of the last file in the list
filetx_t *p; // The new file request 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 // Allocate a file request and append it to the file list
p = *q = (filetx_t *)malloc(sizeof (filetx_t)); p = *q = (filetx_t *)malloc(sizeof (filetx_t));
if (!p) if (!p)
I_Error("SV_SendLuaFile: No more memory\n"); I_Error("AddLuaFileToSendQueue: No more memory\n");
// Initialise with zeros // Initialise with zeros
memset(p, 0, sizeof (filetx_t)); memset(p, 0, sizeof (filetx_t));
@ -778,15 +858,12 @@ boolean SV_SendLuaFile(INT32 node, const char *filename, boolean textmode)
// Allocate the file name // Allocate the file name
p->id.filename = (char *)malloc(MAX_WADPATH); // !!! p->id.filename = (char *)malloc(MAX_WADPATH); // !!!
if (!p->id.filename) 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 // Set the file name and get rid of the path
strlcpy(p->id.filename, filename, MAX_WADPATH); // !!! strlcpy(p->id.filename, filename, MAX_WADPATH); // !!!
//nameonly(p->id.filename); //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)); 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->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 p->next = NULL; // End of list
@ -804,7 +881,8 @@ static void SV_EndFileSend(INT32 node)
{ {
filetx_t *p = transfer[node].txlist; 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) switch (p->ram)
{ {
case SF_FILE: // It's a file, close it and free its filename 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 // Indicate that the transmission is over
transfer[node].currentfile = NULL; transfer[node].currentfile = NULL;
if (transfer[node].ackedfragments)
free(transfer[node].ackedfragments);
transfer[node].ackedfragments = NULL;
filestosend--; filestosend--;
} }
#define PACKETPERTIC net_bandwidth/(TICRATE*software_MAXPACKETLENGTH) #define PACKETPERTIC net_bandwidth/(TICRATE*software_MAXPACKETLENGTH)
#define FILEFRAGMENTSIZE (software_MAXPACKETLENGTH - (FILETXHEADER + BASEPACKETSIZE))
/** Handles file transmission /** 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; static INT32 currentnode = 0;
filetx_pak *p; filetx_pak *p;
size_t size; size_t fragmentsize;
filetx_t *f; filetx_t *f;
INT32 packetsent, ram, i, j; INT32 packetsent, ram, i, j;
INT32 maxpacketsent;
if (!filestosend) // No file to send if (!filestosend) // No file to send
return; return;
if (cv_downloadspeed.value) // New (and experimental) behavior if (cv_downloadspeed.value) // New behavior
{
packetsent = cv_downloadspeed.value; 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 else // Old behavior
{ {
packetsent = PACKETPERTIC; packetsent = PACKETPERTIC;
@ -882,11 +949,12 @@ void SV_FileSendTicker(void)
i = (i+1) % MAXNETNODES, j++) i = (i+1) % MAXNETNODES, j++)
{ {
if (transfer[i].txlist) if (transfer[i].txlist)
goto found; break;
} }
// no transfer to do // no transfer to do
I_Error("filestosend=%d but no file to send found\n", filestosend); if (j >= MAXNETNODES)
found: I_Error("filestosend=%d but no file to send found\n", filestosend);
currentnode = (i+1) % MAXNETNODES; currentnode = (i+1) % MAXNETNODES;
f = transfer[i].txlist; f = transfer[i].txlist;
ram = f->ram; ram = f->ram;
@ -899,7 +967,7 @@ void SV_FileSendTicker(void)
long filesize; long filesize;
transfer[i].currentfile = transfer[i].currentfile =
fopen(f->id.filename, f->textmode ? "r" : "rb"); fopen(f->id.filename, "rb");
if (!transfer[i].currentfile) if (!transfer[i].currentfile)
I_Error("File %s does not exist", I_Error("File %s does not exist",
@ -920,57 +988,232 @@ void SV_FileSendTicker(void)
} }
else // Sending RAM else // Sending RAM
transfer[i].currentfile = (FILE *)1; // Set currentfile to a non-null value to indicate that it is open 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].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 // Build a packet containing a file fragment
p = &netbuffer->u.filetxpak; p = &netbuffer->u.filetxpak;
size = software_MAXPACKETLENGTH - (FILETXHEADER + BASEPACKETSIZE); fragmentsize = FILEFRAGMENTSIZE;
if (f->size-transfer[i].position < size) if (f->size-transfer[i].position < fragmentsize)
size = f->size-transfer[i].position; fragmentsize = f->size-transfer[i].position;
if (ram) 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 else
{ {
size_t n = fread(p->data, 1, size, transfer[i].currentfile); fseek(transfer[i].currentfile, transfer[i].position, SEEK_SET);
if (n != size) // Either an error or Windows turning CR-LF into LF
{ if (fread(p->data, 1, fragmentsize, transfer[i].currentfile) != fragmentsize)
if (f->textmode && feof(transfer[i].currentfile)) 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));
size = n;
else if (fread(p->data, 1, size, transfer[i].currentfile) != size)
I_Error("SV_FileSendTicker: can't read %s byte on %s at %d because %s", sizeu1(size), f->id.filename, transfer[i].position, M_FileError(transfer[i].currentfile));
}
} }
p->iteration = transfer[i].iteration;
p->position = LONG(transfer[i].position); 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->fileid = f->fileid;
p->size = SHORT((UINT16)size); p->filesize = LONG(f->size);
p->size = SHORT((UINT16)FILEFRAGMENTSIZE);
// Send the packet // 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 { // Success
transfer[i].position = (UINT32)(transfer[i].position + size); transfer[i].position = (UINT32)(transfer[i].position + fragmentsize);
if (transfer[i].position == f->size || (f->textmode && feof(transfer[i].currentfile))) // Finish? if (transfer[i].position >= f->size)
SV_EndFileSend(i); {
if (transfer[i].ackediteration < transfer[i].iteration)
transfer[i].dontsenduntil = I_GetTime() + TICRATE / 2;
transfer[i].position = 0;
transfer[i].iteration++;
}
} }
else else
{ // Not sent for some odd reason, retry at next call { // 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?) // Exit the while (can't send this one so why should i send the next?)
break; 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; INT32 filenum = netbuffer->u.filetxpak.fileid;
fileneeded_t *file = &fileneeded[filenum]; 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; char *filename;
static INT32 filetime = 0;
filename = va("%s", file->filename); filename = va("%s", file->filename);
nameonly(filename); nameonly(filename);
@ -995,49 +1238,105 @@ void Got_Filetxpak(void)
if (file->status == FS_REQUESTED) if (file->status == FS_REQUESTED)
{ {
if (file->file) if (file->file)
I_Error("Got_Filetxpak: already open file\n"); I_Error("PT_FileFragment: 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;
file->status = FS_DOWNLOADING; 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) if (file->status == FS_DOWNLOADING)
{ {
UINT32 pos = LONG(netbuffer->u.filetxpak.position); if (fragmentpos >= file->totalsize)
UINT16 size = SHORT(netbuffer->u.filetxpak.size); I_Error("Invalid file fragment\n");
// 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;
// Finished? file->iteration = max(file->iteration, netbuffer->u.filetxpak.iteration);
if (file->currentsize == file->totalsize)
if (!file->receivedfragments[fragmentpos / fragmentsize]) // Not received yet
{ {
fclose(file->file); file->receivedfragments[fragmentpos / fragmentsize] = true;
file->file = NULL;
file->status = FS_FOUND; // We can receive packets in the wrong order, anyway all OSes support gaped files
CONS_Printf(M_GetText("Downloading %s...(done)\n"), fseek(file->file, fragmentpos, SEEK_SET);
filename); if (fragmentsize && fwrite(netbuffer->u.filetxpak.data, boundedfragmentsize, 1, file->file) != 1)
if (luafiletransfers) 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 // Tell the server we have received the file
netbuffer->packettype = PT_HASLUAFILE; netbuffer->packettype = PT_FILERECEIVED;
HSendPacket(servernode, true, 0, 0); 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; const char *s;
switch(file->status) 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); 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 #ifdef CLIENT_LOADINGSCREEN
lastfilenum = filenum; lastfilenum = filenum;
@ -1078,7 +1371,7 @@ void Got_Filetxpak(void)
* \return True if the node is downloading a file * \return True if the node is downloading a file
* *
*/ */
boolean SV_SendingFile(INT32 node) boolean SendingFile(INT32 node)
{ {
return transfer[node].txlist != NULL; return transfer[node].txlist != NULL;
} }
@ -1107,12 +1400,62 @@ void CloseNetFile(void)
if (fileneeded[i].status == FS_DOWNLOADING && fileneeded[i].file) if (fileneeded[i].status == FS_DOWNLOADING && fileneeded[i].file)
{ {
fclose(fileneeded[i].file); fclose(fileneeded[i].file);
// File is not complete delete it free(fileneeded[i].ackpacket);
remove(fileneeded[i].filename);
}
// Remove PT_FILEFRAGMENT from acknowledge list if (!pauseddownload && i != 0) // 0 is either srb2.srb or the gamestate...
Net_AbortPacketType(PT_FILEFRAGMENT); {
// 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 :) // Functions cut and pasted from Doomatic :)

View File

@ -14,6 +14,7 @@
#define __D_NETFIL__ #define __D_NETFIL__
#include "d_net.h" #include "d_net.h"
#include "d_clisrv.h"
#include "w_wad.h" #include "w_wad.h"
typedef enum typedef enum
@ -39,12 +40,18 @@ typedef struct
UINT8 willsend; // Is the server willing to send it? UINT8 willsend; // Is the server willing to send it?
char filename[MAX_WADPATH]; char filename[MAX_WADPATH];
UINT8 md5sum[16]; 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 // Used only for download
FILE *file; FILE *file;
boolean *receivedfragments;
UINT32 fragmentsize;
UINT8 iteration;
fileack_pak *ackpacket;
UINT32 currentsize; UINT32 currentsize;
UINT32 totalsize; UINT32 totalsize;
filestatus_t status; // The value returned by recsearch UINT32 ackresendposition; // Used when resuming downloads
boolean textmode; // For files requested by Lua without the "b" option
} fileneeded_t; } fileneeded_t;
extern INT32 fileneedednum; extern INT32 fileneedednum;
@ -61,16 +68,20 @@ void CL_PrepareDownloadSaveGame(const char *tmpsave);
INT32 CL_CheckFiles(void); INT32 CL_CheckFiles(void);
void CL_LoadServerFiles(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); UINT8 fileid);
void SV_FileSendTicker(void); void FileSendTicker(void);
void Got_Filetxpak(void); void PT_FileAck(void);
boolean SV_SendingFile(INT32 node); void PT_FileReceived(void);
boolean SendingFile(INT32 node);
void FileReceiveTicker(void);
void PT_FileFragment(void);
boolean CL_CheckDownloadable(void); boolean CL_CheckDownloadable(void);
boolean CL_SendRequestFile(void); boolean CL_SendFileRequest(void);
boolean Got_RequestFilePak(INT32 node); boolean PT_RequestFile(INT32 node);
typedef enum typedef enum
{ {
@ -86,18 +97,19 @@ typedef struct luafiletransfer_s
char *realfilename; char *realfilename;
char mode[4]; // rb+/wb+/ab+ + null character char mode[4]; // rb+/wb+/ab+ + null character
INT32 id; // Callback ID INT32 id; // Callback ID
boolean ongoing;
luafiletransfernodestatus_t nodestatus[MAXNETNODES]; luafiletransfernodestatus_t nodestatus[MAXNETNODES];
struct luafiletransfer_s *next; struct luafiletransfer_s *next;
} luafiletransfer_t; } luafiletransfer_t;
extern luafiletransfer_t *luafiletransfers; extern luafiletransfer_t *luafiletransfers;
extern boolean waitingforluafiletransfer; extern boolean waitingforluafiletransfer;
extern boolean waitingforluafilecommand;
extern char luafiledir[256 + 16]; extern char luafiledir[256 + 16];
void AddLuaFileTransfer(const char *filename, const char *mode); void AddLuaFileTransfer(const char *filename, const char *mode);
void SV_PrepareSendLuaFileToNextNode(void); void SV_PrepareSendLuaFile(void);
boolean SV_SendLuaFile(INT32 node, const char *filename, boolean textmode); boolean AddLuaFileToSendQueue(INT32 node, const char *filename);
void SV_PrepareSendLuaFile(const char *filename);
void SV_HandleLuaFileSent(UINT8 node); void SV_HandleLuaFileSent(UINT8 node);
void RemoveLuaFileTransfer(void); void RemoveLuaFileTransfer(void);
void RemoveAllLuaFileTransfers(void); void RemoveAllLuaFileTransfers(void);
@ -110,6 +122,9 @@ void MakePathDirs(char *path);
void SV_AbortSendFiles(INT32 node); void SV_AbortSendFiles(INT32 node);
void CloseNetFile(void); void CloseNetFile(void);
void CL_AbortDownloadResume(void);
void Command_Downloads_f(void);
boolean fileexist(char *filename, time_t ptime); boolean fileexist(char *filename, time_t ptime);

View File

@ -769,7 +769,8 @@ static void readthing(MYFILE *f, INT32 num)
static void readskincolor(MYFILE *f, INT32 num) static void readskincolor(MYFILE *f, INT32 num)
{ {
char *s = Z_Malloc(MAXLINELEN, PU_STATIC, NULL); char *s = Z_Malloc(MAXLINELEN, PU_STATIC, NULL);
char *word, *word2, *word3; char *word = s;
char *word2;
char *tmp; char *tmp;
Color_cons_t[num].value = num; Color_cons_t[num].value = num;
@ -781,32 +782,31 @@ static void readskincolor(MYFILE *f, INT32 num)
if (s[0] == '\n') if (s[0] == '\n')
break; break;
// First remove trailing newline, if there is one
tmp = strchr(s, '\n');
if (tmp)
*tmp = '\0';
tmp = strchr(s, '#'); tmp = strchr(s, '#');
if (tmp) if (tmp)
*tmp = '\0'; *tmp = '\0';
if (s == tmp) if (s == tmp)
continue; // Skip comment lines, but don't break. continue; // Skip comment lines, but don't break.
word = strtok(s, " "); // Get the part before the " = "
if (word) tmp = strchr(s, '=');
strupr(word); if (tmp)
*(tmp-1) = '\0';
else else
break; break;
strupr(word);
word2 = strtok(NULL, " = "); // Now get the part after
if (word2) { word2 = tmp += 2;
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';
if (fastcmp(word, "NAME")) 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)); sizeof (skincolors[num].name), va("Skincolor %d: name", num));
} }
else if (fastcmp(word, "RAMP")) else if (fastcmp(word, "RAMP"))

View File

@ -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) static void DrawModelEx(model_t *model, INT32 frameIndex, INT32 duration, INT32 tics, INT32 nextFrameIndex, FTransform *pos, float scale, UINT8 flipped, FSurfaceInfo *Surface)
{ {

View File

@ -487,7 +487,7 @@ static void cleanupnodes(void)
// Why can't I start at zero? // Why can't I start at zero?
for (j = 1; j < MAXNETNODES; j++) for (j = 1; j < MAXNETNODES; j++)
if (!(nodeingame[j] || SV_SendingFile(j))) if (!(nodeingame[j] || SendingFile(j)))
nodeconnected[j] = false; nodeconnected[j] = false;
} }

View File

@ -447,11 +447,13 @@ void LUA_ClearExtVars(void)
// Use this variable to prevent certain functions from running // Use this variable to prevent certain functions from running
// if they were not called on lump load // if they were not called on lump load
// (i.e. they were called in hooks or coroutines etc) // (i.e. they were called in hooks or coroutines etc)
boolean lua_lumploading = false; INT32 lua_lumploading = 0;
// Load a script from a MYFILE // 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) if (!name)
name = wadfiles[f->wad]->filename; name = wadfiles[f->wad]->filename;
CONS_Printf("Loading Lua script from %s\n", name); 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_pushinteger(gL, f->wad);
lua_setfield(gL, LUA_REGISTRYINDEX, "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); 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)); CONS_Alert(CONS_WARNING,"%s\n",lua_tostring(gL,-1));
lua_pop(gL,1); lua_pop(gL,1);
} }
lua_gc(gL, LUA_GCCOLLECT, 0); 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 // Load a script from a lump
void LUA_LoadLump(UINT16 wad, UINT16 lump) void LUA_LoadLump(UINT16 wad, UINT16 lump, boolean noresults)
{ {
MYFILE f; MYFILE f;
char *name; char *name;
@ -501,7 +504,7 @@ void LUA_LoadLump(UINT16 wad, UINT16 lump)
name[len] = '\0'; name[len] = '\0';
} }
LUA_LoadFile(&f, name); // actually load file! LUA_LoadFile(&f, name, noresults); // actually load file!
free(name); free(name);
Z_Free(f.data); Z_Free(f.data);

View File

@ -37,10 +37,10 @@
void LUA_ClearExtVars(void); void LUA_ClearExtVars(void);
#endif #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); 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 #ifdef LUA_ALLOW_BYTECODE
void LUA_DumpFile(const char *filename); void LUA_DumpFile(const char *filename);
#endif #endif

View File

@ -297,6 +297,44 @@ size_t FIL_ReadFileTag(char const *name, UINT8 **buffer, INT32 tag)
return length; 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 /** Check if the filename exists
* *
* \param name Filename to check. * \param name Filename to check.

View File

@ -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); size_t FIL_ReadFileTag(char const *name, UINT8 **buffer, INT32 tag);
#define FIL_ReadFile(n, b) FIL_ReadFileTag(n, b, PU_STATIC) #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_FileExists(const char *name);
boolean FIL_WriteFileOK(char const *name); boolean FIL_WriteFileOK(char const *name);
boolean FIL_ReadFileOK(char const *name); boolean FIL_ReadFileOK(char const *name);

View File

@ -5106,9 +5106,11 @@ void A_SignPlayer(mobj_t *actor)
INT32 locvar2 = var2; INT32 locvar2 = var2;
skin_t *skin = NULL; skin_t *skin = NULL;
mobj_t *ov; mobj_t *ov;
UINT16 facecolor, signcolor = (UINT16)locvar2; UINT16 facecolor, signcolor = 0;
UINT32 signframe = states[actor->info->raisestate].frame; UINT32 signframe = states[actor->info->raisestate].frame;
facecolor = signcolor = (UINT16)locvar2;
if (LUA_CallAction("A_SignPlayer", actor)) if (LUA_CallAction("A_SignPlayer", actor))
return; return;
@ -5213,7 +5215,7 @@ void A_SignPlayer(mobj_t *actor)
actor->tracer->color = signcolor; actor->tracer->color = signcolor;
if (signcolor && signcolor < numskincolors) if (signcolor && signcolor < numskincolors)
signframe += (15 - skincolors[signcolor].invshade); signframe += (15 - skincolors[facecolor].invshade);
actor->tracer->frame = signframe; actor->tracer->frame = signframe;
} }

View File

@ -301,6 +301,7 @@ static void I_ReportSignal(int num, int coredumped)
FUNCNORETURN static ATTRNORETURN void signal_handler(INT32 num) FUNCNORETURN static ATTRNORETURN void signal_handler(INT32 num)
{ {
D_QuitNetGame(); // Fix server freezes D_QuitNetGame(); // Fix server freezes
CL_AbortDownloadResume();
I_ReportSignal(num, 0); I_ReportSignal(num, 0);
I_ShutdownSystem(); I_ShutdownSystem();
signal(num, SIG_DFL); //default signal action signal(num, SIG_DFL); //default signal action
@ -2323,6 +2324,7 @@ void I_Quit(void)
G_StopMetalRecording(false); G_StopMetalRecording(false);
D_QuitNetGame(); D_QuitNetGame();
CL_AbortDownloadResume();
M_FreePlayerSetupColors(); M_FreePlayerSetupColors();
I_ShutdownMusic(); I_ShutdownMusic();
I_ShutdownSound(); I_ShutdownSound();
@ -2440,6 +2442,7 @@ void I_Error(const char *error, ...)
G_StopMetalRecording(false); G_StopMetalRecording(false);
D_QuitNetGame(); D_QuitNetGame();
CL_AbortDownloadResume();
M_FreePlayerSetupColors(); M_FreePlayerSetupColors();
I_ShutdownMusic(); I_ShutdownMusic();
I_ShutdownSound(); I_ShutdownSound();

View File

@ -880,13 +880,18 @@ boolean I_SetSongSpeed(float speed)
#ifdef HAVE_OPENMPT #ifdef HAVE_OPENMPT
if (openmpt_mhandle) if (openmpt_mhandle)
{ {
char modspd[13];
if (speed > 4.0f) if (speed > 4.0f)
speed = 4.0f; // Limit this to 4x to prevent crashing, stupid fix but... ~SteelT 27/9/19 speed = 4.0f; // Limit this to 4x to prevent crashing, stupid fix but... ~SteelT 27/9/19
#if OPENMPT_API_VERSION_MAJOR < 1 && OPENMPT_API_VERSION_MINOR < 5
sprintf(modspd, "%g", speed); {
openmpt_module_ctl_set(openmpt_mhandle, "play.tempo_factor", modspd); // 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; return true;
} }
#else #else

View File

@ -199,12 +199,20 @@ static inline void W_LoadDehackedLumpsPK3(UINT16 wadnum, boolean mainfile)
{ {
UINT16 posStart, posEnd; UINT16 posStart, posEnd;
posStart = W_CheckNumForFolderStartPK3("Lua/", wadnum, 0); posStart = W_CheckNumForFullNamePK3("Init.lua", wadnum, 0);
if (posStart != INT16_MAX) if (posStart != INT16_MAX)
{ {
posEnd = W_CheckNumForFolderEndPK3("Lua/", wadnum, posStart); LUA_LoadLump(wadnum, posStart, true);
for (; posStart < posEnd; posStart++) }
LUA_LoadLump(wadnum, posStart); 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); 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; lumpinfo_t *lump_p = wadfiles[wadnum]->lumpinfo;
for (lump = 0; lump < wadfiles[wadnum]->numlumps; lump++, lump_p++) for (lump = 0; lump < wadfiles[wadnum]->numlumps; lump++, lump_p++)
if (memcmp(lump_p->name,"LUA_",4)==0) 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); DEH_LoadDehackedLumpPwad(numwadfiles - 1, 0, mainfile);
break; break;
case RET_LUA: case RET_LUA:
LUA_LoadLump(numwadfiles - 1, 0); LUA_LoadLump(numwadfiles - 1, 0, true);
break; break;
default: default:
break; break;

View File

@ -467,6 +467,7 @@ static void signal_handler(int num)
char sigdef[64]; char sigdef[64];
D_QuitNetGame(); // Fix server freezes D_QuitNetGame(); // Fix server freezes
CL_AbortDownloadResume();
I_ShutdownSystem(); I_ShutdownSystem();
switch (num) switch (num)
@ -652,6 +653,7 @@ void I_Error(const char *error, ...)
G_StopMetalRecording(false); G_StopMetalRecording(false);
D_QuitNetGame(); D_QuitNetGame();
CL_AbortDownloadResume();
M_FreePlayerSetupColors(); M_FreePlayerSetupColors();
// shutdown everything that was started // shutdown everything that was started
@ -748,6 +750,7 @@ void I_Quit(void)
// or something else that will be finished by I_ShutdownSystem(), // or something else that will be finished by I_ShutdownSystem(),
// so do it before. // so do it before.
D_QuitNetGame(); D_QuitNetGame();
CL_AbortDownloadResume();
M_FreePlayerSetupColors(); M_FreePlayerSetupColors();