mirror of
https://git.do.srb2.org/STJr/SRB2.git
synced 2025-03-21 18:32:08 +00:00
Merge branch 'better-download' into 'next'
Improvements to the in-game file downloader See merge request STJr/SRB2!976
This commit is contained in:
commit
38a8fe2baa
12 changed files with 644 additions and 178 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 (server && luafiletransfers)
|
||||
if (waitingforluafilecommand)
|
||||
{
|
||||
if (FIL_FileOK(luafiletransfers->realfilename))
|
||||
SV_PrepareSendLuaFileToNextNode();
|
||||
else
|
||||
{
|
||||
// Send a net command with 0 as its first byte to indicate the file couldn't be opened
|
||||
success = 0;
|
||||
SendNetXCmd(XD_LUAFILE, &success, 1);
|
||||
}
|
||||
waitingforluafilecommand = false;
|
||||
CL_PrepareDownloadLuaFile();
|
||||
}
|
||||
|
||||
if (server && luafiletransfers)
|
||||
SV_PrepareSendLuaFile();
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -1678,8 +1678,9 @@ static inline void CL_DrawConnectionStatus(void)
|
|||
// 15 pal entries total.
|
||||
const char *cltext;
|
||||
|
||||
for (i = 0; i < 16; ++i)
|
||||
V_DrawFill((BASEVIDWIDTH/2-128) + (i * 16), BASEVIDHEIGHT-16, 16, 8, palstart + ((animtime - i) & 15));
|
||||
if (!(cl_mode == CL_DOWNLOADSAVEGAME && lastfilenum != -1))
|
||||
for (i = 0; i < 16; ++i)
|
||||
V_DrawFill((BASEVIDWIDTH/2-128) + (i * 16), BASEVIDHEIGHT-16, 16, 8, palstart + ((animtime - i) & 15));
|
||||
|
||||
switch (cl_mode)
|
||||
{
|
||||
|
@ -1687,10 +1688,22 @@ 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();
|
||||
|
||||
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",fileneeded[lastfilenum].currentsize>>10));
|
||||
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));
|
||||
}
|
||||
|
@ -2098,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
|
||||
|
@ -2478,7 +2491,7 @@ 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();
|
||||
|
@ -2622,10 +2635,13 @@ static boolean CL_ServerConnectionTicker(boolean viams, const char *tmpsave, tic
|
|||
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
|
||||
|
@ -3764,6 +3780,7 @@ void D_QuitNetGame(void)
|
|||
CloseNetFile();
|
||||
RemoveAllLuaFileTransfers();
|
||||
waitingforluafiletransfer = false;
|
||||
waitingforluafilecommand = false;
|
||||
|
||||
if (server)
|
||||
{
|
||||
|
@ -4437,13 +4454,23 @@ static void HandlePacketFromAwayNode(SINT8 node)
|
|||
break;
|
||||
}
|
||||
SERVERONLY
|
||||
Got_Filetxpak();
|
||||
PT_FileFragment();
|
||||
break;
|
||||
|
||||
case PT_FILEACK:
|
||||
if (server)
|
||||
PT_FileAck();
|
||||
break;
|
||||
|
||||
case PT_FILERECEIVED:
|
||||
if (server)
|
||||
PT_FileReceived();
|
||||
break;
|
||||
|
||||
case PT_REQUESTFILE:
|
||||
if (server)
|
||||
{
|
||||
if (!cv_downloading.value || !Got_RequestFilePak(node))
|
||||
if (!cv_downloading.value || !PT_RequestFile(node))
|
||||
Net_CloseConnection(node); // close connection if one of the requested files could not be sent, or you disabled downloading anyway
|
||||
}
|
||||
else
|
||||
|
@ -4739,11 +4766,7 @@ static void HandlePacketFromPlayer(SINT8 node)
|
|||
break;
|
||||
case PT_ASKLUAFILE:
|
||||
if (server && luafiletransfers && luafiletransfers->nodestatus[node] == LFTNS_ASKED)
|
||||
{
|
||||
char *name = va("%s" PATHSEP "%s", luafiledir, luafiletransfers->filename);
|
||||
boolean textmode = !strchr(luafiletransfers->mode, 'b');
|
||||
SV_SendLuaFile(node, name, textmode);
|
||||
}
|
||||
AddLuaFileToSendQueue(node, luafiletransfers->realfilename);
|
||||
break;
|
||||
case PT_HASLUAFILE:
|
||||
if (server && luafiletransfers && luafiletransfers->nodestatus[node] == LFTNS_SENDING)
|
||||
|
@ -4874,7 +4897,15 @@ static void HandlePacketFromPlayer(SINT8 node)
|
|||
break;
|
||||
}
|
||||
if (client)
|
||||
Got_Filetxpak();
|
||||
PT_FileFragment();
|
||||
break;
|
||||
case PT_FILEACK:
|
||||
if (server)
|
||||
PT_FileAck();
|
||||
break;
|
||||
case PT_FILERECEIVED:
|
||||
if (server)
|
||||
PT_FileReceived();
|
||||
break;
|
||||
case PT_SENDINGLUAFILE:
|
||||
if (client)
|
||||
|
@ -5564,7 +5595,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
|
||||
|
|
|
@ -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();
|
||||
|
||||
|
|
623
src/d_netfil.c
623
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();
|
||||
else
|
||||
{
|
||||
// Send a net command with 0 as its first byte to indicate the file couldn't be opened
|
||||
UINT8 success = 0;
|
||||
SendNetXCmd(XD_LUAFILE, &success, 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
// Only if there is no transfer already going on
|
||||
if (server && filetransfer == luafiletransfers)
|
||||
SV_PrepareSendLuaFile();
|
||||
else
|
||||
filetransfer->ongoing = false;
|
||||
|
||||
// Store the callback so it can be called once everyone has the file
|
||||
filetransfer->id = id;
|
||||
|
@ -526,7 +555,7 @@ void AddLuaFileTransfer(const char *filename, const char *mode)
|
|||
}
|
||||
}
|
||||
|
||||
void SV_PrepareSendLuaFileToNextNode(void)
|
||||
static void SV_PrepareSendLuaFileToNextNode(void)
|
||||
{
|
||||
INT32 i;
|
||||
UINT8 success = 1;
|
||||
|
@ -550,6 +579,45 @@ void SV_PrepareSendLuaFileToNextNode(void)
|
|||
SendNetXCmd(XD_LUAFILE, &success, 1);
|
||||
}
|
||||
|
||||
void SV_PrepareSendLuaFile(void)
|
||||
{
|
||||
char *binfilename;
|
||||
INT32 i;
|
||||
|
||||
luafiletransfers->ongoing = true;
|
||||
|
||||
// Set status to "waiting" for everyone
|
||||
for (i = 0; i < MAXNETNODES; i++)
|
||||
luafiletransfers->nodestatus[i] = LFTNS_WAITING;
|
||||
|
||||
if (FIL_ReadFileOK(luafiletransfers->realfilename))
|
||||
{
|
||||
// If opening in text mode, convert all newlines to LF
|
||||
if (!strchr(luafiletransfers->mode, 'b'))
|
||||
{
|
||||
binfilename = strdup(va("%s" PATHSEP "$$$%d%d.tmp",
|
||||
luafiledir, rand(), rand()));
|
||||
if (!binfilename)
|
||||
I_Error("SV_PrepareSendLuaFile: Out of memory\n");
|
||||
|
||||
if (!FIL_ConvertTextFileToBinary(luafiletransfers->realfilename, binfilename))
|
||||
I_Error("SV_PrepareSendLuaFile: Failed to convert file newlines\n");
|
||||
|
||||
// Use the temporary file instead
|
||||
free(luafiletransfers->realfilename);
|
||||
luafiletransfers->realfilename = binfilename;
|
||||
}
|
||||
|
||||
SV_PrepareSendLuaFileToNextNode();
|
||||
}
|
||||
else
|
||||
{
|
||||
// Send a net command with 0 as its first byte to indicate the file couldn't be opened
|
||||
UINT8 success = 0;
|
||||
SendNetXCmd(XD_LUAFILE, &success, 1);
|
||||
}
|
||||
}
|
||||
|
||||
void SV_HandleLuaFileSent(UINT8 node)
|
||||
{
|
||||
luafiletransfers->nodestatus[node] = LFTNS_SENT;
|
||||
|
@ -560,6 +628,10 @@ void RemoveLuaFileTransfer(void)
|
|||
{
|
||||
luafiletransfer_t *filetransfer = luafiletransfers;
|
||||
|
||||
// If it was a temporary file, delete it
|
||||
if (server && !strchr(filetransfer->mode, 'b'))
|
||||
remove(filetransfer->realfilename);
|
||||
|
||||
RemoveLuaFileCallback(filetransfer->id);
|
||||
|
||||
luafiletransfers = filetransfer->next;
|
||||
|
@ -596,20 +668,28 @@ void CL_PrepareDownloadLuaFile(void)
|
|||
return;
|
||||
}
|
||||
|
||||
if (luafiletransfers->ongoing)
|
||||
{
|
||||
waitingforluafilecommand = true;
|
||||
return;
|
||||
}
|
||||
|
||||
// Tell the server we are ready to receive the file
|
||||
netbuffer->packettype = PT_ASKLUAFILE;
|
||||
HSendPacket(servernode, true, 0, 0);
|
||||
|
||||
fileneedednum = 1;
|
||||
fileneeded[0].status = FS_REQUESTED;
|
||||
fileneeded[0].justdownloaded = false;
|
||||
fileneeded[0].totalsize = UINT32_MAX;
|
||||
fileneeded[0].file = NULL;
|
||||
memset(fileneeded[0].md5sum, 0, 16);
|
||||
strcpy(fileneeded[0].filename, luafiletransfers->realfilename);
|
||||
fileneeded[0].textmode = !strchr(luafiletransfers->mode, 'b');
|
||||
|
||||
// Make sure all directories in the file path exist
|
||||
MakePathDirs(fileneeded[0].filename);
|
||||
|
||||
luafiletransfers->ongoing = true;
|
||||
}
|
||||
|
||||
// Number of files to send
|
||||
|
@ -620,12 +700,12 @@ static INT32 filestosend = 0;
|
|||
*
|
||||
* \param node The node to send the file to
|
||||
* \param filename The file to send
|
||||
* \param fileid ???
|
||||
* \sa SV_SendRam
|
||||
* \sa SV_SendLuaFile
|
||||
* \param fileid The index of the file in the list of added files
|
||||
* \sa AddRamToSendQueue
|
||||
* \sa AddLuaFileToSendQueue
|
||||
*
|
||||
*/
|
||||
static boolean SV_SendFile(INT32 node, const char *filename, UINT8 fileid)
|
||||
static boolean AddFileToSendQueue(INT32 node, const char *filename, UINT8 fileid)
|
||||
{
|
||||
filetx_t **q; // A pointer to the "next" field of the last file in the list
|
||||
filetx_t *p; // The new file request
|
||||
|
@ -643,7 +723,7 @@ static boolean SV_SendFile(INT32 node, const char *filename, UINT8 fileid)
|
|||
// Allocate a file request and append it to the file list
|
||||
p = *q = (filetx_t *)malloc(sizeof (filetx_t));
|
||||
if (!p)
|
||||
I_Error("SV_SendFile: No more memory\n");
|
||||
I_Error("AddFileToSendQueue: No more memory\n");
|
||||
|
||||
// Initialise with zeros
|
||||
memset(p, 0, sizeof (filetx_t));
|
||||
|
@ -651,7 +731,7 @@ static boolean SV_SendFile(INT32 node, const char *filename, UINT8 fileid)
|
|||
// Allocate the file name
|
||||
p->id.filename = (char *)malloc(MAX_WADPATH);
|
||||
if (!p->id.filename)
|
||||
I_Error("SV_SendFile: No more memory\n");
|
||||
I_Error("AddFileToSendQueue: No more memory\n");
|
||||
|
||||
// Set the file name and get rid of the path
|
||||
strlcpy(p->id.filename, filename, MAX_WADPATH);
|
||||
|
@ -711,12 +791,12 @@ static boolean SV_SendFile(INT32 node, const char *filename, UINT8 fileid)
|
|||
* \param data The memory block to send
|
||||
* \param size The size of the block in bytes
|
||||
* \param freemethod How to free the block after it has been sent
|
||||
* \param fileid ???
|
||||
* \sa SV_SendFile
|
||||
* \sa SV_SendLuaFile
|
||||
* \param fileid The index of the file in the list of added files
|
||||
* \sa AddFileToSendQueue
|
||||
* \sa AddLuaFileToSendQueue
|
||||
*
|
||||
*/
|
||||
void SV_SendRam(INT32 node, void *data, size_t size, freemethod_t freemethod, UINT8 fileid)
|
||||
void AddRamToSendQueue(INT32 node, void *data, size_t size, freemethod_t freemethod, UINT8 fileid)
|
||||
{
|
||||
filetx_t **q; // A pointer to the "next" field of the last file in the list
|
||||
filetx_t *p; // The new file request
|
||||
|
@ -729,7 +809,7 @@ void SV_SendRam(INT32 node, void *data, size_t size, freemethod_t freemethod, UI
|
|||
// Allocate a file request and append it to the file list
|
||||
p = *q = (filetx_t *)malloc(sizeof (filetx_t));
|
||||
if (!p)
|
||||
I_Error("SV_SendRam: No more memory\n");
|
||||
I_Error("AddRamToSendQueue: No more memory\n");
|
||||
|
||||
// Initialise with zeros
|
||||
memset(p, 0, sizeof (filetx_t));
|
||||
|
@ -749,11 +829,11 @@ void SV_SendRam(INT32 node, void *data, size_t size, freemethod_t freemethod, UI
|
|||
*
|
||||
* \param node The node to send the file to
|
||||
* \param filename The file to send
|
||||
* \sa SV_SendFile
|
||||
* \sa SV_SendRam
|
||||
* \sa AddFileToSendQueue
|
||||
* \sa AddRamToSendQueue
|
||||
*
|
||||
*/
|
||||
boolean SV_SendLuaFile(INT32 node, const char *filename, boolean textmode)
|
||||
boolean AddLuaFileToSendQueue(INT32 node, const char *filename)
|
||||
{
|
||||
filetx_t **q; // A pointer to the "next" field of the last file in the list
|
||||
filetx_t *p; // The new file request
|
||||
|
@ -770,7 +850,7 @@ boolean SV_SendLuaFile(INT32 node, const char *filename, boolean textmode)
|
|||
// Allocate a file request and append it to the file list
|
||||
p = *q = (filetx_t *)malloc(sizeof (filetx_t));
|
||||
if (!p)
|
||||
I_Error("SV_SendLuaFile: No more memory\n");
|
||||
I_Error("AddLuaFileToSendQueue: No more memory\n");
|
||||
|
||||
// Initialise with zeros
|
||||
memset(p, 0, sizeof (filetx_t));
|
||||
|
@ -778,15 +858,12 @@ boolean SV_SendLuaFile(INT32 node, const char *filename, boolean textmode)
|
|||
// Allocate the file name
|
||||
p->id.filename = (char *)malloc(MAX_WADPATH); // !!!
|
||||
if (!p->id.filename)
|
||||
I_Error("SV_SendLuaFile: No more memory\n");
|
||||
I_Error("AddLuaFileToSendQueue: No more memory\n");
|
||||
|
||||
// Set the file name and get rid of the path
|
||||
strlcpy(p->id.filename, filename, MAX_WADPATH); // !!!
|
||||
//nameonly(p->id.filename);
|
||||
|
||||
// Open in text mode if required by the Lua script
|
||||
p->textmode = textmode;
|
||||
|
||||
DEBFILE(va("Sending Lua file %s to %d\n", filename, node));
|
||||
p->ram = SF_FILE; // It's a file, we need to close it and free its name once we're done sending it
|
||||
p->next = NULL; // End of list
|
||||
|
@ -804,7 +881,8 @@ static void SV_EndFileSend(INT32 node)
|
|||
{
|
||||
filetx_t *p = transfer[node].txlist;
|
||||
|
||||
// Free the file request according to the freemethod parameter used with SV_SendFile/Ram
|
||||
// Free the file request according to the freemethod
|
||||
// parameter used with AddFileToSendQueue/AddRamToSendQueue
|
||||
switch (p->ram)
|
||||
{
|
||||
case SF_FILE: // It's a file, close it and free its filename
|
||||
|
@ -829,43 +907,32 @@ static void SV_EndFileSend(INT32 node)
|
|||
|
||||
// Indicate that the transmission is over
|
||||
transfer[node].currentfile = NULL;
|
||||
if (transfer[node].ackedfragments)
|
||||
free(transfer[node].ackedfragments);
|
||||
transfer[node].ackedfragments = NULL;
|
||||
|
||||
filestosend--;
|
||||
}
|
||||
|
||||
#define PACKETPERTIC net_bandwidth/(TICRATE*software_MAXPACKETLENGTH)
|
||||
#define FILEFRAGMENTSIZE (software_MAXPACKETLENGTH - (FILETXHEADER + BASEPACKETSIZE))
|
||||
|
||||
/** Handles file transmission
|
||||
*
|
||||
* \todo Use an acknowledging method more adapted to file transmission
|
||||
* The current download speed suffers from lack of ack packets,
|
||||
* especially when the one downloading has high latency
|
||||
*
|
||||
*/
|
||||
void SV_FileSendTicker(void)
|
||||
void FileSendTicker(void)
|
||||
{
|
||||
static INT32 currentnode = 0;
|
||||
filetx_pak *p;
|
||||
size_t size;
|
||||
size_t fragmentsize;
|
||||
filetx_t *f;
|
||||
INT32 packetsent, ram, i, j;
|
||||
INT32 maxpacketsent;
|
||||
|
||||
if (!filestosend) // No file to send
|
||||
return;
|
||||
|
||||
if (cv_downloadspeed.value) // New (and experimental) behavior
|
||||
{
|
||||
if (cv_downloadspeed.value) // New behavior
|
||||
packetsent = cv_downloadspeed.value;
|
||||
// Don't send more packets than we have free acks
|
||||
#ifndef NONET
|
||||
maxpacketsent = Net_GetFreeAcks(false) - 5; // Let 5 extra acks just in case
|
||||
#else
|
||||
maxpacketsent = 1;
|
||||
#endif
|
||||
if (packetsent > maxpacketsent && maxpacketsent > 0) // Send at least one packet
|
||||
packetsent = maxpacketsent;
|
||||
}
|
||||
else // Old behavior
|
||||
{
|
||||
packetsent = PACKETPERTIC;
|
||||
|
@ -882,11 +949,12 @@ void SV_FileSendTicker(void)
|
|||
i = (i+1) % MAXNETNODES, j++)
|
||||
{
|
||||
if (transfer[i].txlist)
|
||||
goto found;
|
||||
break;
|
||||
}
|
||||
// no transfer to do
|
||||
I_Error("filestosend=%d but no file to send found\n", filestosend);
|
||||
found:
|
||||
if (j >= MAXNETNODES)
|
||||
I_Error("filestosend=%d but no file to send found\n", filestosend);
|
||||
|
||||
currentnode = (i+1) % MAXNETNODES;
|
||||
f = transfer[i].txlist;
|
||||
ram = f->ram;
|
||||
|
@ -899,7 +967,7 @@ void SV_FileSendTicker(void)
|
|||
long filesize;
|
||||
|
||||
transfer[i].currentfile =
|
||||
fopen(f->id.filename, f->textmode ? "r" : "rb");
|
||||
fopen(f->id.filename, "rb");
|
||||
|
||||
if (!transfer[i].currentfile)
|
||||
I_Error("File %s does not exist",
|
||||
|
@ -920,57 +988,232 @@ void SV_FileSendTicker(void)
|
|||
}
|
||||
else // Sending RAM
|
||||
transfer[i].currentfile = (FILE *)1; // Set currentfile to a non-null value to indicate that it is open
|
||||
|
||||
transfer[i].iteration = 1;
|
||||
transfer[i].ackediteration = 0;
|
||||
transfer[i].position = 0;
|
||||
transfer[i].ackedsize = 0;
|
||||
|
||||
transfer[i].ackedfragments = calloc(f->size / FILEFRAGMENTSIZE + 1, sizeof(*transfer[i].ackedfragments));
|
||||
if (!transfer[i].ackedfragments)
|
||||
I_Error("FileSendTicker: No more memory\n");
|
||||
|
||||
transfer[i].dontsenduntil = 0;
|
||||
}
|
||||
|
||||
// If the client hasn't acknowledged any fragment from the previous iteration,
|
||||
// it is most likely because their acks haven't had enough time to reach the server
|
||||
// yet, due to latency. In that case, we wait a little to avoid useless resend.
|
||||
if (I_GetTime() < transfer[i].dontsenduntil)
|
||||
continue;
|
||||
|
||||
// Find the first non-acknowledged fragment
|
||||
while (transfer[i].ackedfragments[transfer[i].position / FILEFRAGMENTSIZE])
|
||||
{
|
||||
transfer[i].position += FILEFRAGMENTSIZE;
|
||||
if (transfer[i].position >= f->size)
|
||||
{
|
||||
if (transfer[i].ackediteration < transfer[i].iteration)
|
||||
transfer[i].dontsenduntil = I_GetTime() + TICRATE / 2;
|
||||
|
||||
transfer[i].position = 0;
|
||||
transfer[i].iteration++;
|
||||
}
|
||||
}
|
||||
|
||||
// Build a packet containing a file fragment
|
||||
p = &netbuffer->u.filetxpak;
|
||||
size = software_MAXPACKETLENGTH - (FILETXHEADER + BASEPACKETSIZE);
|
||||
if (f->size-transfer[i].position < size)
|
||||
size = f->size-transfer[i].position;
|
||||
fragmentsize = FILEFRAGMENTSIZE;
|
||||
if (f->size-transfer[i].position < fragmentsize)
|
||||
fragmentsize = f->size-transfer[i].position;
|
||||
if (ram)
|
||||
M_Memcpy(p->data, &f->id.ram[transfer[i].position], size);
|
||||
M_Memcpy(p->data, &f->id.ram[transfer[i].position], fragmentsize);
|
||||
else
|
||||
{
|
||||
size_t n = fread(p->data, 1, size, transfer[i].currentfile);
|
||||
if (n != size) // Either an error or Windows turning CR-LF into LF
|
||||
{
|
||||
if (f->textmode && feof(transfer[i].currentfile))
|
||||
size = n;
|
||||
else if (fread(p->data, 1, size, transfer[i].currentfile) != size)
|
||||
I_Error("SV_FileSendTicker: can't read %s byte on %s at %d because %s", sizeu1(size), f->id.filename, transfer[i].position, M_FileError(transfer[i].currentfile));
|
||||
}
|
||||
fseek(transfer[i].currentfile, transfer[i].position, SEEK_SET);
|
||||
|
||||
if (fread(p->data, 1, fragmentsize, transfer[i].currentfile) != fragmentsize)
|
||||
I_Error("FileSendTicker: can't read %s byte on %s at %d because %s", sizeu1(fragmentsize), f->id.filename, transfer[i].position, M_FileError(transfer[i].currentfile));
|
||||
}
|
||||
p->iteration = transfer[i].iteration;
|
||||
p->position = LONG(transfer[i].position);
|
||||
// Put flag so receiver knows the total size
|
||||
if (transfer[i].position + size == f->size || (f->textmode && feof(transfer[i].currentfile)))
|
||||
p->position |= LONG(0x80000000);
|
||||
p->fileid = f->fileid;
|
||||
p->size = SHORT((UINT16)size);
|
||||
p->filesize = LONG(f->size);
|
||||
p->size = SHORT((UINT16)FILEFRAGMENTSIZE);
|
||||
|
||||
// Send the packet
|
||||
if (HSendPacket(i, true, 0, FILETXHEADER + size)) // Reliable SEND
|
||||
if (HSendPacket(i, false, 0, FILETXHEADER + fragmentsize)) // Don't use the default acknowledgement system
|
||||
{ // Success
|
||||
transfer[i].position = (UINT32)(transfer[i].position + size);
|
||||
if (transfer[i].position == f->size || (f->textmode && feof(transfer[i].currentfile))) // Finish?
|
||||
SV_EndFileSend(i);
|
||||
transfer[i].position = (UINT32)(transfer[i].position + fragmentsize);
|
||||
if (transfer[i].position >= f->size)
|
||||
{
|
||||
if (transfer[i].ackediteration < transfer[i].iteration)
|
||||
transfer[i].dontsenduntil = I_GetTime() + TICRATE / 2;
|
||||
|
||||
transfer[i].position = 0;
|
||||
transfer[i].iteration++;
|
||||
}
|
||||
}
|
||||
else
|
||||
{ // Not sent for some odd reason, retry at next call
|
||||
if (!ram)
|
||||
fseek(transfer[i].currentfile,transfer[i].position, SEEK_SET);
|
||||
// Exit the while (can't send this one so why should i send the next?)
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Got_Filetxpak(void)
|
||||
void PT_FileAck(void)
|
||||
{
|
||||
fileack_pak *packet = &netbuffer->u.fileack;
|
||||
INT32 node = doomcom->remotenode;
|
||||
filetran_t *trans = &transfer[node];
|
||||
INT32 i, j;
|
||||
|
||||
// Wrong file id? Ignore it, it's probably a late packet
|
||||
if (!(trans->txlist && packet->fileid == trans->txlist->fileid))
|
||||
return;
|
||||
|
||||
if (packet->numsegments * sizeof(*packet->segments) != doomcom->datalength - BASEPACKETSIZE - sizeof(*packet))
|
||||
{
|
||||
Net_CloseConnection(node);
|
||||
return;
|
||||
}
|
||||
|
||||
if (packet->iteration > trans->ackediteration)
|
||||
{
|
||||
trans->ackediteration = packet->iteration;
|
||||
if (trans->ackediteration >= trans->iteration - 1)
|
||||
trans->dontsenduntil = 0;
|
||||
}
|
||||
|
||||
for (i = 0; i < packet->numsegments; i++)
|
||||
{
|
||||
fileacksegment_t *segment = &packet->segments[i];
|
||||
|
||||
for (j = 0; j < 32; j++)
|
||||
if (LONG(segment->acks) & (1 << j))
|
||||
{
|
||||
if (LONG(segment->start) * FILEFRAGMENTSIZE >= trans->txlist->size)
|
||||
{
|
||||
Net_CloseConnection(node);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!trans->ackedfragments[LONG(segment->start) + j])
|
||||
{
|
||||
trans->ackedfragments[LONG(segment->start) + j] = true;
|
||||
trans->ackedsize += FILEFRAGMENTSIZE;
|
||||
|
||||
// If the last missing fragment was acked, finish!
|
||||
if (trans->ackedsize == trans->txlist->size)
|
||||
{
|
||||
SV_EndFileSend(node);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void PT_FileReceived(void)
|
||||
{
|
||||
filetx_t *trans = transfer[doomcom->remotenode].txlist;
|
||||
|
||||
if (trans && netbuffer->u.filereceived == trans->fileid)
|
||||
SV_EndFileSend(doomcom->remotenode);
|
||||
}
|
||||
|
||||
static void SendAckPacket(fileack_pak *packet, UINT8 fileid)
|
||||
{
|
||||
size_t packetsize;
|
||||
INT32 i;
|
||||
|
||||
packetsize = sizeof(*packet) + packet->numsegments * sizeof(*packet->segments);
|
||||
|
||||
// Finalise the packet
|
||||
packet->fileid = fileid;
|
||||
for (i = 0; i < packet->numsegments; i++)
|
||||
{
|
||||
packet->segments[i].start = LONG(packet->segments[i].start);
|
||||
packet->segments[i].acks = LONG(packet->segments[i].acks);
|
||||
}
|
||||
|
||||
// Send the packet
|
||||
netbuffer->packettype = PT_FILEACK;
|
||||
M_Memcpy(&netbuffer->u.fileack, packet, packetsize);
|
||||
HSendPacket(servernode, false, 0, packetsize);
|
||||
|
||||
// Clear the packet
|
||||
memset(packet, 0, sizeof(*packet) + 512);
|
||||
}
|
||||
|
||||
static void AddFragmentToAckPacket(fileack_pak *packet, UINT8 iteration, UINT32 fragmentpos, UINT8 fileid)
|
||||
{
|
||||
fileacksegment_t *segment = &packet->segments[packet->numsegments - 1];
|
||||
|
||||
packet->iteration = max(packet->iteration, iteration);
|
||||
|
||||
if (packet->numsegments == 0
|
||||
|| fragmentpos < segment->start
|
||||
|| fragmentpos - segment->start >= 32)
|
||||
{
|
||||
// If the packet becomes too big, send it
|
||||
if ((packet->numsegments + 1) * sizeof(*segment) > 512)
|
||||
SendAckPacket(packet, fileid);
|
||||
|
||||
packet->numsegments++;
|
||||
segment = &packet->segments[packet->numsegments - 1];
|
||||
segment->start = fragmentpos;
|
||||
}
|
||||
|
||||
// Set the bit that represents the fragment
|
||||
segment->acks |= 1 << (fragmentpos - segment->start);
|
||||
}
|
||||
|
||||
void FileReceiveTicker(void)
|
||||
{
|
||||
INT32 i;
|
||||
|
||||
for (i = 0; i < fileneedednum; i++)
|
||||
{
|
||||
fileneeded_t *file = &fileneeded[i];
|
||||
|
||||
if (file->status == FS_DOWNLOADING)
|
||||
{
|
||||
if (lasttimeackpacketsent - I_GetTime() > TICRATE / 2)
|
||||
SendAckPacket(file->ackpacket, i);
|
||||
|
||||
// When resuming a tranfer, start with telling
|
||||
// the server what parts we already received
|
||||
if (file->ackresendposition != UINT32_MAX && file->status == FS_DOWNLOADING)
|
||||
{
|
||||
// Acknowledge ~70 MB/s, whichs means the client sends ~18 KB/s
|
||||
INT32 j;
|
||||
for (j = 0; j < 2048; j++)
|
||||
{
|
||||
if (file->receivedfragments[file->ackresendposition])
|
||||
AddFragmentToAckPacket(file->ackpacket, file->iteration, file->ackresendposition, i);
|
||||
|
||||
file->ackresendposition++;
|
||||
if (file->ackresendposition * file->fragmentsize >= file->totalsize)
|
||||
{
|
||||
file->ackresendposition = UINT32_MAX;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void PT_FileFragment(void)
|
||||
{
|
||||
INT32 filenum = netbuffer->u.filetxpak.fileid;
|
||||
fileneeded_t *file = &fileneeded[filenum];
|
||||
UINT32 fragmentpos = LONG(netbuffer->u.filetxpak.position);
|
||||
UINT16 fragmentsize = SHORT(netbuffer->u.filetxpak.size);
|
||||
UINT16 boundedfragmentsize = doomcom->datalength - BASEPACKETSIZE - sizeof(netbuffer->u.filetxpak);
|
||||
char *filename;
|
||||
static INT32 filetime = 0;
|
||||
|
||||
filename = va("%s", file->filename);
|
||||
nameonly(filename);
|
||||
|
@ -995,49 +1238,105 @@ void Got_Filetxpak(void)
|
|||
if (file->status == FS_REQUESTED)
|
||||
{
|
||||
if (file->file)
|
||||
I_Error("Got_Filetxpak: already open file\n");
|
||||
file->file = fopen(filename, file->textmode ? "w" : "wb");
|
||||
if (!file->file)
|
||||
I_Error("Can't create file %s: %s", filename, strerror(errno));
|
||||
CONS_Printf("\r%s...\n",filename);
|
||||
file->currentsize = 0;
|
||||
I_Error("PT_FileFragment: already open file\n");
|
||||
|
||||
file->status = FS_DOWNLOADING;
|
||||
file->fragmentsize = fragmentsize;
|
||||
file->iteration = 0;
|
||||
|
||||
file->ackpacket = calloc(1, sizeof(*file->ackpacket) + 512);
|
||||
if (!file->ackpacket)
|
||||
I_Error("FileSendTicker: No more memory\n");
|
||||
|
||||
if (CL_CanResumeDownload(file))
|
||||
{
|
||||
file->file = fopen(filename, "r+b");
|
||||
if (!file->file)
|
||||
I_Error("Can't reopen file %s: %s", filename, strerror(errno));
|
||||
CONS_Printf("\r%s...\n", filename);
|
||||
|
||||
CONS_Printf("Resuming download...\n");
|
||||
file->currentsize = pauseddownload->currentsize;
|
||||
file->receivedfragments = pauseddownload->receivedfragments;
|
||||
file->ackresendposition = 0;
|
||||
|
||||
free(pauseddownload);
|
||||
pauseddownload = NULL;
|
||||
}
|
||||
else
|
||||
{
|
||||
CL_AbortDownloadResume();
|
||||
|
||||
file->file = fopen(filename, "wb");
|
||||
if (!file->file)
|
||||
I_Error("Can't create file %s: %s", filename, strerror(errno));
|
||||
|
||||
CONS_Printf("\r%s...\n",filename);
|
||||
|
||||
file->currentsize = 0;
|
||||
file->totalsize = LONG(netbuffer->u.filetxpak.filesize);
|
||||
file->ackresendposition = UINT32_MAX; // Only used for resumed downloads
|
||||
|
||||
file->receivedfragments = calloc(file->totalsize / fragmentsize + 1, sizeof(*file->receivedfragments));
|
||||
if (!file->receivedfragments)
|
||||
I_Error("FileSendTicker: No more memory\n");
|
||||
}
|
||||
|
||||
lasttimeackpacketsent = I_GetTime();
|
||||
}
|
||||
|
||||
if (file->status == FS_DOWNLOADING)
|
||||
{
|
||||
UINT32 pos = LONG(netbuffer->u.filetxpak.position);
|
||||
UINT16 size = SHORT(netbuffer->u.filetxpak.size);
|
||||
// Use a special trick to know when the file is complete (not always used)
|
||||
// WARNING: file fragments can arrive out of order so don't stop yet!
|
||||
if (pos & 0x80000000)
|
||||
{
|
||||
pos &= ~0x80000000;
|
||||
file->totalsize = pos + size;
|
||||
}
|
||||
// We can receive packet in the wrong order, anyway all os support gaped file
|
||||
fseek(file->file, pos, SEEK_SET);
|
||||
if (size && fwrite(netbuffer->u.filetxpak.data,size,1,file->file) != 1)
|
||||
I_Error("Can't write to %s: %s\n",filename, M_FileError(file->file));
|
||||
file->currentsize += size;
|
||||
if (fragmentpos >= file->totalsize)
|
||||
I_Error("Invalid file fragment\n");
|
||||
|
||||
// Finished?
|
||||
if (file->currentsize == file->totalsize)
|
||||
file->iteration = max(file->iteration, netbuffer->u.filetxpak.iteration);
|
||||
|
||||
if (!file->receivedfragments[fragmentpos / fragmentsize]) // Not received yet
|
||||
{
|
||||
fclose(file->file);
|
||||
file->file = NULL;
|
||||
file->status = FS_FOUND;
|
||||
CONS_Printf(M_GetText("Downloading %s...(done)\n"),
|
||||
filename);
|
||||
if (luafiletransfers)
|
||||
file->receivedfragments[fragmentpos / fragmentsize] = true;
|
||||
|
||||
// We can receive packets in the wrong order, anyway all OSes support gaped files
|
||||
fseek(file->file, fragmentpos, SEEK_SET);
|
||||
if (fragmentsize && fwrite(netbuffer->u.filetxpak.data, boundedfragmentsize, 1, file->file) != 1)
|
||||
I_Error("Can't write to %s: %s\n",filename, M_FileError(file->file));
|
||||
file->currentsize += boundedfragmentsize;
|
||||
|
||||
AddFragmentToAckPacket(file->ackpacket, file->iteration, fragmentpos / fragmentsize, filenum);
|
||||
|
||||
// Finished?
|
||||
if (file->currentsize == file->totalsize)
|
||||
{
|
||||
fclose(file->file);
|
||||
file->file = NULL;
|
||||
free(file->receivedfragments);
|
||||
free(file->ackpacket);
|
||||
file->status = FS_FOUND;
|
||||
file->justdownloaded = true;
|
||||
CONS_Printf(M_GetText("Downloading %s...(done)\n"),
|
||||
filename);
|
||||
|
||||
// Tell the server we have received the file
|
||||
netbuffer->packettype = PT_HASLUAFILE;
|
||||
HSendPacket(servernode, true, 0, 0);
|
||||
netbuffer->packettype = PT_FILERECEIVED;
|
||||
netbuffer->u.filereceived = filenum;
|
||||
HSendPacket(servernode, true, 0, 1);
|
||||
|
||||
if (luafiletransfers)
|
||||
{
|
||||
// Tell the server we have received the file
|
||||
netbuffer->packettype = PT_HASLUAFILE;
|
||||
HSendPacket(servernode, true, 0, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
else // Already received
|
||||
{
|
||||
// If they are sending us the fragment again, it's probably because
|
||||
// they missed our previous ack, so we must re-acknowledge it
|
||||
AddFragmentToAckPacket(file->ackpacket, file->iteration, fragmentpos / fragmentsize, filenum);
|
||||
}
|
||||
}
|
||||
else
|
||||
else if (!file->justdownloaded)
|
||||
{
|
||||
const char *s;
|
||||
switch(file->status)
|
||||
|
@ -1060,12 +1359,6 @@ void Got_Filetxpak(void)
|
|||
}
|
||||
I_Error("Received a file not requested (file id: %d, file status: %s)\n", filenum, s);
|
||||
}
|
||||
// Send ack back quickly
|
||||
if (++filetime == 3)
|
||||
{
|
||||
Net_SendAcks(servernode);
|
||||
filetime = 0;
|
||||
}
|
||||
|
||||
#ifdef CLIENT_LOADINGSCREEN
|
||||
lastfilenum = filenum;
|
||||
|
@ -1078,7 +1371,7 @@ void Got_Filetxpak(void)
|
|||
* \return True if the node is downloading a file
|
||||
*
|
||||
*/
|
||||
boolean SV_SendingFile(INT32 node)
|
||||
boolean SendingFile(INT32 node)
|
||||
{
|
||||
return transfer[node].txlist != NULL;
|
||||
}
|
||||
|
@ -1107,12 +1400,62 @@ void CloseNetFile(void)
|
|||
if (fileneeded[i].status == FS_DOWNLOADING && fileneeded[i].file)
|
||||
{
|
||||
fclose(fileneeded[i].file);
|
||||
// File is not complete delete it
|
||||
remove(fileneeded[i].filename);
|
||||
}
|
||||
free(fileneeded[i].ackpacket);
|
||||
|
||||
// Remove PT_FILEFRAGMENT from acknowledge list
|
||||
Net_AbortPacketType(PT_FILEFRAGMENT);
|
||||
if (!pauseddownload && i != 0) // 0 is either srb2.srb or the gamestate...
|
||||
{
|
||||
// Don't remove the file, save it for later in case we resume the download
|
||||
pauseddownload = malloc(sizeof(*pauseddownload));
|
||||
if (!pauseddownload)
|
||||
I_Error("CloseNetFile: No more memory\n");
|
||||
|
||||
strcpy(pauseddownload->filename, fileneeded[i].filename);
|
||||
memcpy(pauseddownload->md5sum, fileneeded[i].md5sum, 16);
|
||||
pauseddownload->currentsize = fileneeded[i].currentsize;
|
||||
pauseddownload->receivedfragments = fileneeded[i].receivedfragments;
|
||||
pauseddownload->fragmentsize = fileneeded[i].fragmentsize;
|
||||
}
|
||||
else
|
||||
{
|
||||
free(fileneeded[i].receivedfragments);
|
||||
// File is not complete delete it
|
||||
remove(fileneeded[i].filename);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Command_Downloads_f(void)
|
||||
{
|
||||
INT32 node;
|
||||
|
||||
for (node = 0; node < MAXNETNODES; node++)
|
||||
if (transfer[node].txlist
|
||||
&& transfer[node].txlist->ram == SF_FILE) // Node is downloading a file?
|
||||
{
|
||||
const char *name = transfer[node].txlist->id.filename;
|
||||
UINT32 position = transfer[node].ackedsize;
|
||||
UINT32 size = transfer[node].txlist->size;
|
||||
char ratecolor;
|
||||
|
||||
// Avoid division by zero errors
|
||||
if (!size)
|
||||
size = 1;
|
||||
|
||||
name = &name[strlen(name) - nameonlylength(name)];
|
||||
switch (4 * (position - 1) / size)
|
||||
{
|
||||
case 0: ratecolor = '\x85'; break;
|
||||
case 1: ratecolor = '\x87'; break;
|
||||
case 2: ratecolor = '\x82'; break;
|
||||
case 3: ratecolor = '\x83'; break;
|
||||
default: ratecolor = '\x80';
|
||||
}
|
||||
|
||||
CONS_Printf("%2d %c%s ", node, ratecolor, name); // Node and file name
|
||||
CONS_Printf("\x80%uK\x84/\x80%uK ", position / 1024, size / 1024); // Progress in kB
|
||||
CONS_Printf("\x80(%c%u%%\x80) ", ratecolor, (UINT32)(100.0 * position / size)); // Progress in %
|
||||
CONS_Printf("%s\n", I_GetNodeAddress(node)); // Address and newline
|
||||
}
|
||||
}
|
||||
|
||||
// Functions cut and pasted from Doomatic :)
|
||||
|
|
|
@ -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