Allow resuming the most recent file transfer
This commit is contained in:
parent
66ecfb741a
commit
db85c62c6f
146
src/d_netfil.c
146
src/d_netfil.c
|
@ -93,6 +93,17 @@ fileneeded_t fileneeded[MAX_WADFILES]; // List of needed files
|
||||||
static tic_t lasttimeackpacketsent = 0;
|
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;
|
||||||
|
@ -255,6 +266,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.
|
||||||
*
|
*
|
||||||
|
@ -630,7 +666,7 @@ 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 AddRamToSendQueue
|
* \sa AddRamToSendQueue
|
||||||
* \sa AddLuaFileToSendQueue
|
* \sa AddLuaFileToSendQueue
|
||||||
*
|
*
|
||||||
|
@ -721,7 +757,7 @@ static boolean AddFileToSendQueue(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 AddFileToSendQueue
|
* \sa AddFileToSendQueue
|
||||||
* \sa AddLuaFileToSendQueue
|
* \sa AddLuaFileToSendQueue
|
||||||
*
|
*
|
||||||
|
@ -1083,13 +1119,36 @@ void FileReceiveTicker(void)
|
||||||
{
|
{
|
||||||
INT32 i;
|
INT32 i;
|
||||||
|
|
||||||
if (lasttimeackpacketsent - I_GetTime() > TICRATE / 2)
|
for (i = 0; i < fileneedednum; i++)
|
||||||
for (i = 0; i < fileneedednum; i++)
|
{
|
||||||
if (fileneeded[i].status == FS_DOWNLOADING)
|
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)
|
||||||
{
|
{
|
||||||
SendAckPacket(fileneeded[i].ackpacket, i);
|
// Acknowledge ~70 MB/s, whichs means the client sends ~18 KB/s
|
||||||
break;
|
INT32 j;
|
||||||
|
for (j = 0; j < 2048; j++)
|
||||||
|
{
|
||||||
|
if (file->receivedfragments[file->ackresendposition])
|
||||||
|
AddFragmentToAckPacket(file->ackpacket, file->ackresendposition, i);
|
||||||
|
|
||||||
|
file->ackresendposition++;
|
||||||
|
if (file->ackresendposition * file->fragmentsize >= file->totalsize)
|
||||||
|
{
|
||||||
|
file->ackresendposition = UINT32_MAX;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void PT_FileFragment(void)
|
void PT_FileFragment(void)
|
||||||
|
@ -1126,20 +1185,47 @@ void PT_FileFragment(void)
|
||||||
if (file->file)
|
if (file->file)
|
||||||
I_Error("PT_FileFragment: 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->totalsize = LONG(netbuffer->u.filetxpak.filesize);
|
|
||||||
|
|
||||||
file->receivedfragments = calloc(file->totalsize / fragmentsize + 1, sizeof(*file->receivedfragments));
|
|
||||||
file->ackpacket = calloc(1, sizeof(*file->ackpacket) + 512);
|
file->ackpacket = calloc(1, sizeof(*file->ackpacket) + 512);
|
||||||
if (!(file->receivedfragments && file->ackpacket))
|
if (!file->ackpacket)
|
||||||
I_Error("FileSendTicker: No more memory\n");
|
I_Error("FileSendTicker: No more memory\n");
|
||||||
|
|
||||||
|
if (CL_CanResumeDownload(file))
|
||||||
|
{
|
||||||
|
file->file = fopen(filename, file->textmode ? "r+" : "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, 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->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();
|
lasttimeackpacketsent = I_GetTime();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1256,14 +1342,28 @@ 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);
|
||||||
free(fileneeded[i].receivedfragments);
|
|
||||||
free(fileneeded[i].ackpacket);
|
free(fileneeded[i].ackpacket);
|
||||||
// File is not complete delete it
|
|
||||||
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Functions cut and pasted from Doomatic :)
|
// Functions cut and pasted from Doomatic :)
|
||||||
|
|
|
@ -43,10 +43,11 @@ typedef struct
|
||||||
// Used only for download
|
// Used only for download
|
||||||
FILE *file;
|
FILE *file;
|
||||||
boolean *receivedfragments;
|
boolean *receivedfragments;
|
||||||
|
UINT32 fragmentsize;
|
||||||
fileack_pak *ackpacket;
|
fileack_pak *ackpacket;
|
||||||
tic_t lasttimeackpacketsent;
|
|
||||||
UINT32 currentsize;
|
UINT32 currentsize;
|
||||||
UINT32 totalsize;
|
UINT32 totalsize;
|
||||||
|
UINT32 ackresendposition; // Used when resuming downloads
|
||||||
filestatus_t status; // The value returned by recsearch
|
filestatus_t status; // The value returned by recsearch
|
||||||
boolean justdownloaded; // To prevent late fragments from causing an I_Error
|
boolean justdownloaded; // To prevent late fragments from causing an I_Error
|
||||||
boolean textmode; // For files requested by Lua without the "b" option
|
boolean textmode; // For files requested by Lua without the "b" option
|
||||||
|
@ -119,6 +120,7 @@ void MakePathDirs(char *path);
|
||||||
|
|
||||||
void SV_AbortSendFiles(INT32 node);
|
void SV_AbortSendFiles(INT32 node);
|
||||||
void CloseNetFile(void);
|
void CloseNetFile(void);
|
||||||
|
void CL_AbortDownloadResume(void);
|
||||||
|
|
||||||
boolean fileexist(char *filename, time_t ptime);
|
boolean fileexist(char *filename, time_t ptime);
|
||||||
|
|
||||||
|
|
|
@ -293,6 +293,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
|
||||||
|
@ -2293,6 +2294,7 @@ void I_Quit(void)
|
||||||
G_StopMetalRecording(false);
|
G_StopMetalRecording(false);
|
||||||
|
|
||||||
D_QuitNetGame();
|
D_QuitNetGame();
|
||||||
|
CL_AbortDownloadResume();
|
||||||
I_ShutdownMusic();
|
I_ShutdownMusic();
|
||||||
I_ShutdownSound();
|
I_ShutdownSound();
|
||||||
I_ShutdownCD();
|
I_ShutdownCD();
|
||||||
|
@ -2409,6 +2411,7 @@ void I_Error(const char *error, ...)
|
||||||
G_StopMetalRecording(false);
|
G_StopMetalRecording(false);
|
||||||
|
|
||||||
D_QuitNetGame();
|
D_QuitNetGame();
|
||||||
|
CL_AbortDownloadResume();
|
||||||
I_ShutdownMusic();
|
I_ShutdownMusic();
|
||||||
I_ShutdownSound();
|
I_ShutdownSound();
|
||||||
I_ShutdownCD();
|
I_ShutdownCD();
|
||||||
|
|
|
@ -465,6 +465,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)
|
||||||
|
@ -650,6 +651,7 @@ void I_Error(const char *error, ...)
|
||||||
G_StopMetalRecording(false);
|
G_StopMetalRecording(false);
|
||||||
|
|
||||||
D_QuitNetGame();
|
D_QuitNetGame();
|
||||||
|
CL_AbortDownloadResume();
|
||||||
|
|
||||||
// shutdown everything that was started
|
// shutdown everything that was started
|
||||||
I_ShutdownSystem();
|
I_ShutdownSystem();
|
||||||
|
@ -745,6 +747,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();
|
||||||
|
|
||||||
// shutdown everything that was started
|
// shutdown everything that was started
|
||||||
I_ShutdownSystem();
|
I_ShutdownSystem();
|
||||||
|
|
Loading…
Reference in a new issue