mirror of
https://git.do.srb2.org/STJr/SRB2.git
synced 2024-11-15 17:22:12 +00:00
Merge branch 'next' of https://git.magicalgirl.moe/STJr/SRB2 into gamequit-hook
This commit is contained in:
commit
08e9efee76
13 changed files with 1182 additions and 196 deletions
|
@ -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 (waitingforluafilecommand)
|
||||
{
|
||||
waitingforluafilecommand = false;
|
||||
CL_PrepareDownloadLuaFile();
|
||||
}
|
||||
|
||||
if (server && luafiletransfers)
|
||||
{
|
||||
if (FIL_FileOK(luafiletransfers->realfilename))
|
||||
SV_PrepareSendLuaFileToNextNode();
|
||||
else
|
||||
{
|
||||
// Send a net command with 0 as its first byte to indicate the file couldn't be opened
|
||||
success = 0;
|
||||
SendNetXCmd(XD_LUAFILE, &success, 1);
|
||||
}
|
||||
}
|
||||
SV_PrepareSendLuaFile();
|
||||
}
|
||||
|
||||
|
||||
|
|
606
src/d_clisrv.c
606
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;
|
||||
|
||||
if (!(cl_mode == CL_DOWNLOADSAVEGAME && lastfilenum != -1))
|
||||
for (i = 0; i < 16; ++i)
|
||||
V_DrawFill((BASEVIDWIDTH/2-128) + (i * 16), BASEVIDHEIGHT-24, 16, 8, palstart + ((animtime - i) & 15));
|
||||
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)
|
||||
{
|
||||
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();
|
||||
|
@ -3249,6 +3782,7 @@ void D_QuitNetGame(void)
|
|||
CloseNetFile();
|
||||
RemoveAllLuaFileTransfers();
|
||||
waitingforluafiletransfer = false;
|
||||
waitingforluafilecommand = false;
|
||||
|
||||
if (server)
|
||||
{
|
||||
|
@ -3926,13 +4460,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
|
||||
|
@ -4228,11 +4772,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)
|
||||
|
@ -4363,7 +4903,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)
|
||||
|
@ -5053,7 +5601,7 @@ void NetUpdate(void)
|
|||
CON_Ticker();
|
||||
}
|
||||
|
||||
SV_FileSendTicker();
|
||||
FileSendTicker();
|
||||
}
|
||||
|
||||
/** Returns the number of players playing.
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -1190,6 +1190,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());
|
||||
|
|
|
@ -806,6 +806,9 @@ static const char *packettypename[NUMPACKETTYPE] =
|
|||
"HASLUAFILE",
|
||||
|
||||
"FILEFRAGMENT",
|
||||
"FILEACK",
|
||||
"FILERECEIVED",
|
||||
|
||||
"TEXTCMD",
|
||||
"TEXTCMD2",
|
||||
"CLIENTJOIN",
|
||||
|
|
|
@ -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();
|
||||
|
||||
|
|
583
src/d_netfil.c
583
src/d_netfil.c
|
@ -56,7 +56,7 @@
|
|||
#include <errno.h>
|
||||
|
||||
// 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();
|
||||
// Only if there is no transfer already going on
|
||||
if (server && filetransfer == luafiletransfers)
|
||||
SV_PrepareSendLuaFile();
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
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
|
||||
if (j >= MAXNETNODES)
|
||||
I_Error("filestosend=%d but no file to send found\n", filestosend);
|
||||
found:
|
||||
|
||||
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,40 +1238,89 @@ 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");
|
||||
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->status = FS_DOWNLOADING;
|
||||
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)
|
||||
if (fragmentpos >= file->totalsize)
|
||||
I_Error("Invalid file fragment\n");
|
||||
|
||||
file->iteration = max(file->iteration, netbuffer->u.filetxpak.iteration);
|
||||
|
||||
if (!file->receivedfragments[fragmentpos / fragmentsize]) // Not received yet
|
||||
{
|
||||
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)
|
||||
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 += size;
|
||||
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_FILERECEIVED;
|
||||
netbuffer->u.filereceived = filenum;
|
||||
HSendPacket(servernode, true, 0, 1);
|
||||
|
||||
if (luafiletransfers)
|
||||
{
|
||||
// Tell the server we have received the file
|
||||
|
@ -1037,7 +1329,14 @@ void Got_Filetxpak(void)
|
|||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
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 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);
|
||||
free(fileneeded[i].ackpacket);
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Remove PT_FILEFRAGMENT from acknowledge list
|
||||
Net_AbortPacketType(PT_FILEFRAGMENT);
|
||||
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 :)
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
38
src/m_misc.c
38
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.
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -295,6 +295,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
|
||||
|
@ -2295,6 +2296,7 @@ void I_Quit(void)
|
|||
G_StopMetalRecording(false);
|
||||
|
||||
D_QuitNetGame();
|
||||
CL_AbortDownloadResume();
|
||||
M_FreePlayerSetupColors();
|
||||
I_ShutdownMusic();
|
||||
I_ShutdownSound();
|
||||
|
@ -2412,6 +2414,7 @@ void I_Error(const char *error, ...)
|
|||
G_StopMetalRecording(false);
|
||||
|
||||
D_QuitNetGame();
|
||||
CL_AbortDownloadResume();
|
||||
M_FreePlayerSetupColors();
|
||||
I_ShutdownMusic();
|
||||
I_ShutdownSound();
|
||||
|
|
|
@ -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();
|
||||
|
||||
|
|
Loading…
Reference in a new issue