Skip to content
Snippets Groups Projects
Commit db85c62c authored by LJ Sonic's avatar LJ Sonic
Browse files

Allow resuming the most recent file transfer

parent 66ecfb74
No related branches found
No related tags found
No related merge requests found
...@@ -93,6 +93,17 @@ fileneeded_t fileneeded[MAX_WADFILES]; // List of needed files ...@@ -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) ...@@ -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; ...@@ -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 ...@@ -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,14 +1119,37 @@ void FileReceiveTicker(void) ...@@ -1083,14 +1119,37 @@ 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)
{ {
SendAckPacket(fileneeded[i].ackpacket, 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->ackresendposition, i);
file->ackresendposition++;
if (file->ackresendposition * file->fragmentsize >= file->totalsize)
{
file->ackresendposition = UINT32_MAX;
break; break;
} }
} }
}
}
}
}
void PT_FileFragment(void) void PT_FileFragment(void)
{ {
...@@ -1126,20 +1185,47 @@ 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->status = FS_DOWNLOADING;
file->fragmentsize = fragmentsize;
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, 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"); file->file = fopen(filename, file->textmode ? "w" : "wb");
if (!file->file) if (!file->file)
I_Error("Can't create file %s: %s", filename, strerror(errno)); I_Error("Can't create file %s: %s", filename, strerror(errno));
CONS_Printf("\r%s...\n",filename); CONS_Printf("\r%s...\n",filename);
file->currentsize = 0;
file->status = FS_DOWNLOADING;
file->currentsize = 0;
file->totalsize = LONG(netbuffer->u.filetxpak.filesize); 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)); file->receivedfragments = calloc(file->totalsize / fragmentsize + 1, sizeof(*file->receivedfragments));
file->ackpacket = calloc(1, sizeof(*file->ackpacket) + 512); if (!file->receivedfragments)
if (!(file->receivedfragments && file->ackpacket))
I_Error("FileSendTicker: No more memory\n"); I_Error("FileSendTicker: No more memory\n");
}
lasttimeackpacketsent = I_GetTime(); lasttimeackpacketsent = I_GetTime();
} }
...@@ -1256,14 +1342,28 @@ void CloseNetFile(void) ...@@ -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);
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 // File is not complete delete it
remove(fileneeded[i].filename); remove(fileneeded[i].filename);
} }
}
// Remove PT_FILEFRAGMENT from acknowledge list
Net_AbortPacketType(PT_FILEFRAGMENT);
} }
// Functions cut and pasted from Doomatic :) // Functions cut and pasted from Doomatic :)
......
...@@ -43,10 +43,11 @@ typedef struct ...@@ -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); ...@@ -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) ...@@ -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) ...@@ -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, ...) ...@@ -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) ...@@ -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, ...) ...@@ -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) ...@@ -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();
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment