mirror of
https://github.com/YGGverse/xash3d-fwgs.git
synced 2025-01-17 10:30:00 +00:00
HTTP download support
This commit is contained in:
parent
7b36796f41
commit
e05c1ed912
@ -70,6 +70,13 @@ qboolean CL_CheckFile( sizebuf_t *msg, resource_t *pResource )
|
||||
return true;
|
||||
}
|
||||
|
||||
if( cl.downloadUrl[0] )
|
||||
{
|
||||
HTTP_AddDownload( filepath, pResource->nDownloadSize, true );
|
||||
host.downloadcount++;
|
||||
return false;
|
||||
}
|
||||
|
||||
MSG_BeginClientCmd( msg, clc_stringcmd );
|
||||
MSG_WriteString( msg, va( "dlfile %s", filepath ));
|
||||
host.downloadcount++;
|
||||
@ -140,4 +147,4 @@ void CL_ClearResourceLists( void )
|
||||
{
|
||||
CL_ClearResourceList( &cl.resourcesneeded );
|
||||
CL_ClearResourceList( &cl.resourcesonhand );
|
||||
}
|
||||
}
|
||||
|
@ -1334,6 +1334,7 @@ void CL_ClearState( void )
|
||||
Cvar_SetValue( "scr_download", -1.0f );
|
||||
Cvar_SetValue( "scr_loading", 0.0f );
|
||||
host.allow_console = host.allow_console_init;
|
||||
HTTP_ClearCustomServers();
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -534,7 +534,11 @@ void CL_BatchResourceRequest( qboolean initialize )
|
||||
|
||||
if( cls.state != ca_disconnected )
|
||||
{
|
||||
if( !MSG_GetNumBytesWritten( &msg ) && CL_PrecacheResources( ))
|
||||
if( !cl.downloadUrl[0] && !MSG_GetNumBytesWritten( &msg ) && CL_PrecacheResources( ))
|
||||
{
|
||||
CL_RegisterResources( &msg );
|
||||
}
|
||||
if( cl.downloadUrl[0] && host.downloadcount == 0 && CL_PrecacheResources( ) )
|
||||
{
|
||||
CL_RegisterResources( &msg );
|
||||
}
|
||||
@ -1702,16 +1706,23 @@ CL_ParseResLocation
|
||||
*/
|
||||
void CL_ParseResLocation( sizebuf_t *msg )
|
||||
{
|
||||
const char *url = MSG_ReadString( msg );
|
||||
const char *data = MSG_ReadString( msg );
|
||||
char token[256];
|
||||
|
||||
if( url && ( !Q_strnicmp( "http://", url, 7 ) || !Q_strnicmp( "https://", url, 8 )))
|
||||
if( Q_strlen( data ) > 256 )
|
||||
{
|
||||
const char *lastSlash = Q_strrchr( url, '/' );
|
||||
Con_Printf( S_ERROR "Resource location too long!\n" );
|
||||
return;
|
||||
}
|
||||
|
||||
if( lastSlash && lastSlash[1] == '\0' )
|
||||
Q_strncpy( cl.downloadUrl, url, sizeof( cl.downloadUrl ));
|
||||
else Q_snprintf( cl.downloadUrl, sizeof( cl.downloadUrl ), "%s/", url );
|
||||
Con_Reportf( "Using %s as primary download location\n", cl.downloadUrl );
|
||||
while( ( data = COM_ParseFile( data, token ) ) )
|
||||
{
|
||||
Con_Reportf( "Adding %s as download location\n", token );
|
||||
|
||||
if( !cl.downloadUrl[0] )
|
||||
Q_strncpy( cl.downloadUrl, token, sizeof( token ) );
|
||||
|
||||
HTTP_AddCustomServer( token );
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1053,6 +1053,7 @@ qboolean CL_IsBackgroundDemo( void );
|
||||
qboolean CL_IsBackgroundMap( void );
|
||||
qboolean SV_Initialized( void );
|
||||
qboolean CL_LoadProgs( const char *name );
|
||||
void CL_ProcessFile( qboolean successfully_received, const char *filename );
|
||||
int SV_GetSaveComment( const char *savename, char *comment );
|
||||
qboolean SV_NewGame( const char *mapName, qboolean loadGame );
|
||||
void SV_ClipPMoveToEntity( struct physent_s *pe, const vec3_t start, vec3_t mins, vec3_t maxs, const vec3_t end, struct pmtrace_s *tr );
|
||||
|
@ -84,6 +84,11 @@ const char *svc_strings[256] =
|
||||
"svc_unused63",
|
||||
};
|
||||
|
||||
void CL_ProcessFile( qboolean successfully_received, const char *filename )
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
int CL_Active( void )
|
||||
{
|
||||
return false;
|
||||
|
@ -542,6 +542,7 @@ void Host_Frame( float time )
|
||||
Host_GetCommands (); // dedicated in
|
||||
Host_ServerFrame (); // server frame
|
||||
Host_ClientFrame (); // client frame
|
||||
HTTP_Run(); // both server and client
|
||||
|
||||
host.framecount++;
|
||||
}
|
||||
@ -978,6 +979,7 @@ int EXPORT Host_Main( int argc, char **argv, const char *progname, int bChangeGa
|
||||
SV_Init();
|
||||
CL_Init();
|
||||
|
||||
HTTP_Init();
|
||||
ID_Init();
|
||||
|
||||
if( Host_IsDedicated() )
|
||||
@ -1060,6 +1062,7 @@ void EXPORT Host_Shutdown( void )
|
||||
|
||||
Mod_Shutdown();
|
||||
NET_Shutdown();
|
||||
HTTP_Shutdown();
|
||||
Host_FreeCommon();
|
||||
#ifdef _WIN32
|
||||
Wcon_DestroyConsole();
|
||||
|
@ -1861,3 +1861,850 @@ void NET_Shutdown( void )
|
||||
#endif
|
||||
net.initialized = false;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
=================================================
|
||||
|
||||
HTTP downloader
|
||||
|
||||
=================================================
|
||||
*/
|
||||
|
||||
typedef struct httpserver_s
|
||||
{
|
||||
char host[256];
|
||||
int port;
|
||||
char path[PATH_MAX];
|
||||
qboolean needfree;
|
||||
struct httpserver_s *next;
|
||||
|
||||
} httpserver_t;
|
||||
|
||||
enum connectionstate
|
||||
{
|
||||
HTTP_QUEUE = 0,
|
||||
HTTP_OPENED,
|
||||
HTTP_SOCKET,
|
||||
HTTP_NS_RESOLVED,
|
||||
HTTP_CONNECTED,
|
||||
HTTP_REQUEST,
|
||||
HTTP_REQUEST_SENT,
|
||||
HTTP_RESPONSE_RECEIVED,
|
||||
HTTP_FREE
|
||||
};
|
||||
|
||||
typedef struct httpfile_s
|
||||
{
|
||||
struct httpfile_s *next;
|
||||
httpserver_t *server;
|
||||
char path[PATH_MAX];
|
||||
file_t *file;
|
||||
int socket;
|
||||
int size;
|
||||
int downloaded;
|
||||
int lastchecksize;
|
||||
float checktime;
|
||||
float blocktime;
|
||||
int id;
|
||||
enum connectionstate state;
|
||||
qboolean process;
|
||||
|
||||
// query or response
|
||||
char buf[BUFSIZ];
|
||||
int header_size, query_length, bytes_sent;
|
||||
} httpfile_t;
|
||||
|
||||
static struct http_static_s
|
||||
{
|
||||
// file and server lists
|
||||
httpfile_t *first_file, *last_file;
|
||||
httpserver_t *first_server, *last_server;
|
||||
} http;
|
||||
|
||||
|
||||
static convar_t *http_useragent;
|
||||
static convar_t *http_autoremove;
|
||||
static convar_t *http_timeout;
|
||||
static convar_t *http_maxconnections;
|
||||
|
||||
#ifdef _WIN32
|
||||
#define ISBLOCK() (pWSAGetLastError() == WSAEWOULDBLOCK )
|
||||
#else
|
||||
#define ISBLOCK() ( errno == EWOULDBLOCK )
|
||||
#endif
|
||||
|
||||
#ifdef _WIN32
|
||||
#define ISINPROGRESS() ( pWSAGetLastError() == WSAEINPROGRESS || pWSAGetLastError() == WSAEWOULDBLOCK )
|
||||
#elif defined(__APPLE__) || defined(__FreeBSD__) || defined __EMSCRIPTEN__
|
||||
#define ISINPROGRESS() ( errno == EINPROGRESS || errno == EWOULDBLOCK )
|
||||
#else
|
||||
#define ISINPROGRESS() ( errno == EINPROGRESS ) // Should give EWOOLDBLOCK if try recv too soon
|
||||
#endif
|
||||
|
||||
#ifdef _WIN32
|
||||
#define ISNOTCONN() ( pWSAGetLastError() == WSAEWOULDBLOCK || pWSAGetLastError() == WSAENOTCONN )
|
||||
#elif defined(__APPLE__) || defined(__FreeBSD__) || defined __EMSCRIPTEN__
|
||||
#define ISNOTCONN() ( errno == EWOULDBLOCK || errno == ENOTCONN )
|
||||
#else
|
||||
#define ISNOTCONN() ( errno == EWOULDBLOCK )
|
||||
#endif
|
||||
|
||||
/*
|
||||
========================
|
||||
HTTP_ClearCustomServers
|
||||
========================
|
||||
*/
|
||||
void HTTP_ClearCustomServers( void )
|
||||
{
|
||||
if( http.first_file )
|
||||
return; // may be referenced
|
||||
|
||||
while( http.first_server && http.first_server->needfree )
|
||||
{
|
||||
httpserver_t *tmp = http.first_server;
|
||||
|
||||
http.first_server = http.first_server->next;
|
||||
Mem_Free( tmp );
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
==============
|
||||
HTTP_FreeFile
|
||||
|
||||
Skip to next server/file
|
||||
==============
|
||||
*/
|
||||
static void HTTP_FreeFile( httpfile_t *file, qboolean error )
|
||||
{
|
||||
char incname[256];
|
||||
|
||||
// Allways close file and socket
|
||||
if( file->file )
|
||||
FS_Close( file->file );
|
||||
|
||||
file->file = NULL;
|
||||
|
||||
if( file->socket != -1 )
|
||||
pCloseSocket( file->socket );
|
||||
|
||||
file->socket = -1;
|
||||
|
||||
Q_snprintf( incname, 256, "%s.incomplete", file->path );
|
||||
if( error )
|
||||
{
|
||||
// Switch to next fastdl server if present
|
||||
if( file->server && ( file->state > HTTP_QUEUE ) && (file->state != HTTP_FREE ) )
|
||||
{
|
||||
file->server = file->server->next;
|
||||
file->state = HTTP_QUEUE; // Reset download state, HTTP_Run() will open file again
|
||||
return;
|
||||
}
|
||||
|
||||
// Called because there was no servers to download, free file now
|
||||
if( http_autoremove->value == 1 ) // remove broken file
|
||||
FS_Delete( incname );
|
||||
else // autoremove disabled, keep file
|
||||
Con_Printf( "cannot download %s from any server. "
|
||||
"You may remove %s now\n", file->path, incname ); // Warn about trash file
|
||||
|
||||
if( file->process )
|
||||
CL_ProcessFile( false, file->path ); // Process file, increase counter
|
||||
}
|
||||
else
|
||||
{
|
||||
// Success, rename and process file
|
||||
char name[256];
|
||||
|
||||
Q_snprintf( name, 256, "%s", file->path );
|
||||
FS_Rename( incname, name );
|
||||
|
||||
if( file->process )
|
||||
CL_ProcessFile( true, name );
|
||||
else
|
||||
Con_Printf( "successfully downloaded %s, processing disabled!\n", name );
|
||||
}
|
||||
|
||||
file->state = HTTP_FREE;
|
||||
}
|
||||
|
||||
/*
|
||||
===================
|
||||
HTTP_AutoClean
|
||||
|
||||
remove files with HTTP_FREE state from list
|
||||
===================
|
||||
*/
|
||||
static void HTTP_AutoClean( void )
|
||||
{
|
||||
httpfile_t *curfile, *prevfile = 0;
|
||||
|
||||
// clean all files marked to free
|
||||
for( curfile = http.first_file; curfile; curfile = curfile->next )
|
||||
{
|
||||
if( curfile->state != HTTP_FREE )
|
||||
{
|
||||
prevfile = curfile;
|
||||
continue;
|
||||
}
|
||||
|
||||
if( curfile == http.first_file )
|
||||
{
|
||||
http.first_file = http.first_file->next;
|
||||
Mem_Free( curfile );
|
||||
curfile = http.first_file;
|
||||
if( !curfile )
|
||||
break;
|
||||
continue;
|
||||
}
|
||||
|
||||
if( prevfile )
|
||||
prevfile->next = curfile->next;
|
||||
Mem_Free( curfile );
|
||||
curfile = prevfile;
|
||||
if( !curfile )
|
||||
break;
|
||||
}
|
||||
http.last_file = prevfile;
|
||||
}
|
||||
|
||||
/*
|
||||
===================
|
||||
HTTP_ProcessStream
|
||||
|
||||
process incoming data
|
||||
===================
|
||||
*/
|
||||
static qboolean HTTP_ProcessStream( httpfile_t *curfile )
|
||||
{
|
||||
char buf[BUFSIZ+1];
|
||||
char *begin = 0;
|
||||
int res;
|
||||
|
||||
while( ( res = pRecv( curfile->socket, buf, BUFSIZ, 0 ) ) > 0) // if we got there, we are receiving data
|
||||
{
|
||||
curfile->blocktime = 0;
|
||||
|
||||
if( curfile->state < HTTP_RESPONSE_RECEIVED ) // Response still not received
|
||||
{
|
||||
buf[res] = 0; // string break to search \r\n\r\n
|
||||
memcpy( curfile->buf + curfile->header_size, buf, res );
|
||||
begin = Q_strstr( curfile->buf, "\r\n\r\n" );
|
||||
|
||||
if( begin ) // Got full header
|
||||
{
|
||||
int cutheadersize = begin - curfile->buf + 4; // after that begin of data
|
||||
char *length;
|
||||
|
||||
Con_Reportf( "HTTP: Got response!\n" );
|
||||
|
||||
if( !Q_strstr( curfile->buf, "200 OK" ) )
|
||||
{
|
||||
*begin = 0; // cut string to print out response
|
||||
begin = Q_strchr( curfile->buf, '\r' );
|
||||
|
||||
if( !begin ) begin = Q_strchr( curfile->buf, '\n' );
|
||||
if( begin )
|
||||
*begin = 0;
|
||||
|
||||
Con_Printf( S_ERROR "bad response: %s\n", curfile->buf );
|
||||
HTTP_FreeFile( curfile, true );
|
||||
return false;
|
||||
}
|
||||
|
||||
// print size
|
||||
length = Q_stristr( curfile->buf, "Content-Length: " );
|
||||
if( length )
|
||||
{
|
||||
int size = Q_atoi( length += 16 );
|
||||
|
||||
Con_Reportf( "HTTP: File size is %d\n", size );
|
||||
|
||||
if( ( curfile->size != -1 ) && ( curfile->size != size ) ) // check size if specified, not used
|
||||
Con_Reportf( S_WARN "Server reports wrong file size!\n" );
|
||||
|
||||
curfile->size = size;
|
||||
}
|
||||
|
||||
if( curfile->size == -1 )
|
||||
{
|
||||
// Usually fastdl's reports file size if link is correct
|
||||
Con_Printf( S_ERROR "file size is unknown, refusing download!\n" );
|
||||
HTTP_FreeFile( curfile, true );
|
||||
return false;
|
||||
}
|
||||
|
||||
curfile->state = HTTP_RESPONSE_RECEIVED; // got response, let's start download
|
||||
begin += 4;
|
||||
|
||||
// Write remaining message part
|
||||
if( res - cutheadersize - curfile->header_size > 0 )
|
||||
{
|
||||
int ret = FS_Write( curfile->file, begin, res - cutheadersize - curfile->header_size );
|
||||
|
||||
if( ret != res - cutheadersize - curfile->header_size ) // could not write file
|
||||
{
|
||||
// close it and go to next
|
||||
Con_Printf( S_ERROR "write failed for %s!\n", curfile->path );
|
||||
HTTP_FreeFile( curfile, true );
|
||||
return false;
|
||||
}
|
||||
curfile->downloaded += ret;
|
||||
}
|
||||
}
|
||||
curfile->header_size += res;
|
||||
}
|
||||
else if( res > 0 )
|
||||
{
|
||||
// data download
|
||||
int ret = FS_Write( curfile->file, buf, res );
|
||||
|
||||
if ( ret != res )
|
||||
{
|
||||
// close it and go to next
|
||||
Con_Printf( S_ERROR "write failed for %s!\n", curfile->path );
|
||||
curfile->state = HTTP_FREE;
|
||||
HTTP_FreeFile( curfile, true );
|
||||
return false;
|
||||
}
|
||||
|
||||
curfile->downloaded += ret;
|
||||
curfile->lastchecksize += ret;
|
||||
|
||||
// as after it will run in same frame
|
||||
if( curfile->checktime > 5 )
|
||||
{
|
||||
curfile->checktime = 0;
|
||||
Con_Reportf( "download speed %f KB/s\n", (float)curfile->lastchecksize / ( 5.0 * 1024 ) );
|
||||
curfile->lastchecksize = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
curfile->checktime += host.frametime;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
==============
|
||||
HTTP_Run
|
||||
|
||||
Download next file block of each active file
|
||||
Call every frame
|
||||
==============
|
||||
*/
|
||||
void HTTP_Run( void )
|
||||
{
|
||||
httpfile_t *curfile;
|
||||
int iActiveCount = 0;
|
||||
int iProgressCount = 0;
|
||||
float flProgress;
|
||||
qboolean fResolving = false;
|
||||
|
||||
for( curfile = http.first_file; curfile; curfile = curfile->next )
|
||||
{
|
||||
int res;
|
||||
struct sockaddr addr;
|
||||
|
||||
if( curfile->state == HTTP_FREE )
|
||||
continue;
|
||||
|
||||
if( curfile->state == HTTP_QUEUE )
|
||||
{
|
||||
char name[PATH_MAX];
|
||||
|
||||
if( iActiveCount > http_maxconnections->value )
|
||||
continue;
|
||||
|
||||
if( !curfile->server )
|
||||
{
|
||||
Con_Printf( S_ERROR "no servers to download %s!\n", curfile->path );
|
||||
HTTP_FreeFile( curfile, true );
|
||||
continue;
|
||||
}
|
||||
|
||||
Con_Reportf( "HTTP: Starting download %s from %s\n", curfile->path, curfile->server->host );
|
||||
Q_snprintf( name, PATH_MAX, "%s.incomplete", curfile->path );
|
||||
|
||||
curfile->file = FS_Open( name, "wb", true );
|
||||
|
||||
if( !curfile->file )
|
||||
{
|
||||
Con_Printf( S_ERROR "cannot open %s!\n", name );
|
||||
HTTP_FreeFile( curfile, true );
|
||||
continue;
|
||||
}
|
||||
|
||||
curfile->state = HTTP_OPENED;
|
||||
curfile->blocktime = 0;
|
||||
curfile->downloaded = 0;
|
||||
curfile->lastchecksize = 0;
|
||||
curfile->checktime = 0;
|
||||
}
|
||||
|
||||
iActiveCount++;
|
||||
|
||||
if( curfile->state < HTTP_SOCKET ) // Socket is not created
|
||||
{
|
||||
dword mode;
|
||||
|
||||
curfile->socket = pSocket( AF_INET, SOCK_STREAM, IPPROTO_TCP );
|
||||
|
||||
// Now set non-blocking mode
|
||||
// You may skip this if not supported by system,
|
||||
// but download will lock engine, maybe you will need to add manual returns
|
||||
#if defined(_WIN32) || defined(__APPLE__) || defined(__FreeBSD__) || defined __EMSCRIPTEN__
|
||||
mode = 1;
|
||||
pIoctlSocket( curfile->socket, FIONBIO, &mode );
|
||||
#else
|
||||
// SOCK_NONBLOCK is not portable, so use fcntl
|
||||
fcntl( curfile->socket, F_SETFL, fcntl( curfile->socket, F_GETFL, 0 ) | O_NONBLOCK );
|
||||
#endif
|
||||
curfile->state = HTTP_SOCKET;
|
||||
}
|
||||
|
||||
if( curfile->state < HTTP_NS_RESOLVED )
|
||||
{
|
||||
if( fResolving )
|
||||
continue;
|
||||
|
||||
res = NET_StringToSockaddr( va( "%s:%d", curfile->server->host, curfile->server->port ), &addr, true );
|
||||
|
||||
if( res == 2 )
|
||||
{
|
||||
fResolving = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
if( !res )
|
||||
{
|
||||
Con_Printf( S_ERROR "failed to resolve server address for %s!\n", curfile->server->host );
|
||||
HTTP_FreeFile( curfile, true ); // Cannot connect
|
||||
continue;
|
||||
}
|
||||
curfile->state = HTTP_NS_RESOLVED;
|
||||
}
|
||||
|
||||
if( curfile->state < HTTP_CONNECTED ) // Connection not enstabilished
|
||||
{
|
||||
res = pConnect( curfile->socket, &addr, sizeof( struct sockaddr ) );
|
||||
|
||||
if( res )
|
||||
{
|
||||
if( ISINPROGRESS() ) // Should give EWOOLDBLOCK if try recv too soon
|
||||
curfile->state = HTTP_CONNECTED;
|
||||
else
|
||||
{
|
||||
Con_Printf( S_ERROR "cannot connect to server: %s\n", NET_ErrorString( ) );
|
||||
HTTP_FreeFile( curfile, true ); // Cannot connect
|
||||
continue;
|
||||
}
|
||||
continue; // skip to next file
|
||||
}
|
||||
curfile->state = HTTP_CONNECTED;
|
||||
}
|
||||
|
||||
if( curfile->state < HTTP_REQUEST ) // Request not formatted
|
||||
{
|
||||
curfile->query_length = Q_snprintf( curfile->buf, sizeof( curfile->buf ),
|
||||
"GET %s%s HTTP/1.0\r\n"
|
||||
"Host: %s\r\n"
|
||||
"User-Agent: %s\r\n\r\n", curfile->server->path,
|
||||
curfile->path, curfile->server->host, http_useragent->string );
|
||||
curfile->header_size = 0;
|
||||
curfile->bytes_sent = 0;
|
||||
curfile->state = HTTP_REQUEST;
|
||||
}
|
||||
|
||||
if( curfile->state < HTTP_REQUEST_SENT ) // Request not sent
|
||||
{
|
||||
qboolean wait = false;
|
||||
|
||||
while( curfile->bytes_sent < curfile->query_length )
|
||||
{
|
||||
res = pSend( curfile->socket, curfile->buf + curfile->bytes_sent, curfile->query_length - curfile->bytes_sent, 0 );
|
||||
|
||||
|
||||
if( res < 0 )
|
||||
{
|
||||
if( !ISNOTCONN() )
|
||||
{
|
||||
Con_Printf( S_ERROR "failed to send request: %s\n", NET_ErrorString() );
|
||||
HTTP_FreeFile( curfile, true );
|
||||
wait = true;
|
||||
break;
|
||||
}
|
||||
// blocking while waiting connection
|
||||
// increase counter when blocking
|
||||
curfile->blocktime += host.frametime;
|
||||
wait = true;
|
||||
|
||||
if( curfile->blocktime > http_timeout->value )
|
||||
{
|
||||
Con_Printf( S_ERROR "timeout on request send:\n%s\n", curfile->buf );
|
||||
HTTP_FreeFile( curfile, true );
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
curfile->bytes_sent += res;
|
||||
curfile->blocktime = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if( wait )
|
||||
continue;
|
||||
|
||||
Con_Reportf( "HTTP: Request sent!\n");
|
||||
memset( curfile->buf, 0, sizeof( curfile->buf ) );
|
||||
curfile->state = HTTP_REQUEST_SENT;
|
||||
}
|
||||
|
||||
if( !HTTP_ProcessStream( curfile ) )
|
||||
continue;
|
||||
|
||||
if( curfile->size > 0 )
|
||||
{
|
||||
flProgress += (float)curfile->downloaded / curfile->size;
|
||||
iProgressCount++;
|
||||
}
|
||||
|
||||
if( curfile->size > 0 && curfile->downloaded >= curfile->size )
|
||||
{
|
||||
HTTP_FreeFile( curfile, false ); // success
|
||||
continue;
|
||||
}
|
||||
else if( !ISBLOCK() )
|
||||
Con_Reportf( "problem downloading %s:\n%s\n", curfile->path, NET_ErrorString() );
|
||||
else
|
||||
curfile->blocktime += host.frametime;
|
||||
|
||||
if( curfile->blocktime > http_timeout->value )
|
||||
{
|
||||
Con_Printf( S_ERROR "timeout on receiving data!\n");
|
||||
HTTP_FreeFile( curfile, true );
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
// update progress
|
||||
Cvar_SetValue( "scr_download", flProgress/iProgressCount * 100 );
|
||||
|
||||
HTTP_AutoClean();
|
||||
}
|
||||
|
||||
/*
|
||||
===================
|
||||
HTTP_AddDownload
|
||||
|
||||
Add new download to end of queue
|
||||
===================
|
||||
*/
|
||||
void HTTP_AddDownload( const char *path, int size, qboolean process )
|
||||
{
|
||||
httpfile_t *httpfile = Z_Calloc( sizeof( httpfile_t ) );
|
||||
|
||||
Con_Reportf( "File %s queued to download\n", path );
|
||||
|
||||
httpfile->size = size;
|
||||
httpfile->downloaded = 0;
|
||||
httpfile->socket = -1;
|
||||
Q_strncpy ( httpfile->path, path, sizeof( httpfile->path ) );
|
||||
|
||||
if( http.last_file )
|
||||
{
|
||||
// Add next to last download
|
||||
httpfile->id = http.last_file->id + 1;
|
||||
http.last_file->next= httpfile;
|
||||
http.last_file = httpfile;
|
||||
}
|
||||
else
|
||||
{
|
||||
// It will be the only download
|
||||
httpfile->id = 0;
|
||||
http.last_file = http.first_file = httpfile;
|
||||
}
|
||||
|
||||
httpfile->file = NULL;
|
||||
httpfile->next = NULL;
|
||||
httpfile->state = HTTP_QUEUE;
|
||||
httpfile->server = http.first_server;
|
||||
httpfile->process = process;
|
||||
}
|
||||
|
||||
/*
|
||||
===============
|
||||
HTTP_Download_f
|
||||
|
||||
Console wrapper
|
||||
===============
|
||||
*/
|
||||
static void HTTP_Download_f( void )
|
||||
{
|
||||
if( Cmd_Argc() < 2 )
|
||||
{
|
||||
Con_Printf( S_USAGE "download <gamedir_path>\n");
|
||||
return;
|
||||
}
|
||||
|
||||
HTTP_AddDownload( Cmd_Argv( 1 ), -1, false );
|
||||
}
|
||||
|
||||
/*
|
||||
==============
|
||||
HTTP_ParseURL
|
||||
==============
|
||||
*/
|
||||
static httpserver_t *HTTP_ParseURL( const char *url )
|
||||
{
|
||||
httpserver_t *server;
|
||||
int i;
|
||||
|
||||
url = Q_strstr( url, "http://" );
|
||||
|
||||
if( !url )
|
||||
return NULL;
|
||||
|
||||
url += 7;
|
||||
server = Z_Calloc( sizeof( httpserver_t ) );
|
||||
i = 0;
|
||||
|
||||
while( *url && ( *url != ':' ) && ( *url != '/' ) && ( *url != '\r' ) && ( *url != '\n' ) )
|
||||
{
|
||||
if( i > sizeof( server->host ) )
|
||||
return NULL;
|
||||
|
||||
server->host[i++] = *url++;
|
||||
}
|
||||
|
||||
server->host[i] = 0;
|
||||
|
||||
if( *url == ':' )
|
||||
{
|
||||
server->port = Q_atoi( ++url );
|
||||
|
||||
while( *url && ( *url != '/' ) && ( *url != '\r' ) && ( *url != '\n' ) )
|
||||
url++;
|
||||
}
|
||||
else
|
||||
server->port = 80;
|
||||
|
||||
i = 0;
|
||||
|
||||
while( *url && ( *url != '\r' ) && ( *url != '\n' ) )
|
||||
{
|
||||
if( i > sizeof( server->path ) )
|
||||
return NULL;
|
||||
|
||||
server->path[i++] = *url++;
|
||||
}
|
||||
|
||||
server->path[i] = 0;
|
||||
server->next = NULL;
|
||||
server->needfree = false;
|
||||
|
||||
return server;
|
||||
}
|
||||
|
||||
/*
|
||||
=======================
|
||||
HTTP_AddCustomServer
|
||||
=======================
|
||||
*/
|
||||
void HTTP_AddCustomServer( const char *url )
|
||||
{
|
||||
httpserver_t *server = HTTP_ParseURL( url );
|
||||
|
||||
if( !server )
|
||||
{
|
||||
Con_Printf( S_ERROR "\"%s\" is not valid url!\n", url );
|
||||
return;
|
||||
}
|
||||
|
||||
server->needfree = true;
|
||||
server->next = http.first_server;
|
||||
http.first_server = server;
|
||||
}
|
||||
|
||||
/*
|
||||
=======================
|
||||
HTTP_AddCustomServer_f
|
||||
=======================
|
||||
*/
|
||||
static void HTTP_AddCustomServer_f( void )
|
||||
{
|
||||
if( Cmd_Argc() == 2 )
|
||||
{
|
||||
HTTP_AddCustomServer( Cmd_Argv( 1 ) );
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
============
|
||||
HTTP_Clear_f
|
||||
|
||||
Clear all queue
|
||||
============
|
||||
*/
|
||||
static void HTTP_Clear_f( void )
|
||||
{
|
||||
http.last_file = NULL;
|
||||
|
||||
while( http.first_file )
|
||||
{
|
||||
httpfile_t *file = http.first_file;
|
||||
|
||||
http.first_file = http.first_file->next;
|
||||
|
||||
if( file->file )
|
||||
FS_Close( file->file );
|
||||
|
||||
if( file->socket != -1 )
|
||||
pCloseSocket ( file->socket );
|
||||
|
||||
Mem_Free( file );
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
==============
|
||||
HTTP_Cancel_f
|
||||
|
||||
Stop current download, skip to next file
|
||||
==============
|
||||
*/
|
||||
static void HTTP_Cancel_f( void )
|
||||
{
|
||||
if( !http.first_file )
|
||||
return;
|
||||
|
||||
http.first_file->state = HTTP_FREE;
|
||||
HTTP_FreeFile( http.first_file, true );
|
||||
}
|
||||
|
||||
/*
|
||||
=============
|
||||
HTTP_Skip_f
|
||||
|
||||
Stop current download, skip to next server
|
||||
=============
|
||||
*/
|
||||
static void HTTP_Skip_f( void )
|
||||
{
|
||||
if( http.first_file )
|
||||
HTTP_FreeFile( http.first_file, true );
|
||||
}
|
||||
|
||||
/*
|
||||
=============
|
||||
HTTP_List_f
|
||||
|
||||
Print all pending downloads to console
|
||||
=============
|
||||
*/
|
||||
static void HTTP_List_f( void )
|
||||
{
|
||||
httpfile_t *file = http.first_file;
|
||||
|
||||
while( file )
|
||||
{
|
||||
if ( file->server )
|
||||
Con_Printf ( "\t%d %d http://%s:%d/%s%s %d\n", file->id, file->state,
|
||||
file->server->host, file->server->port, file->server->path,
|
||||
file->path, file->downloaded );
|
||||
else
|
||||
Con_Printf ( "\t%d %d (no server) %s\n", file->id, file->state, file->path );
|
||||
|
||||
file = file->next;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
================
|
||||
HTTP_ResetProcessState
|
||||
|
||||
When connected to new server, all old files should not increase counter
|
||||
================
|
||||
*/
|
||||
void HTTP_ResetProcessState( void )
|
||||
{
|
||||
httpfile_t *file = http.first_file;
|
||||
|
||||
while( file )
|
||||
{
|
||||
file->process = false;
|
||||
file = file->next;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
=============
|
||||
HTTP_Init
|
||||
=============
|
||||
*/
|
||||
void HTTP_Init( void )
|
||||
{
|
||||
char *serverfile, *line, token[1024];
|
||||
|
||||
http.last_server = NULL;
|
||||
|
||||
http.first_file = http.last_file = NULL;
|
||||
|
||||
Cmd_AddCommand("http_download", &HTTP_Download_f, "add file to download queue");
|
||||
Cmd_AddCommand("http_skip", &HTTP_Skip_f, "skip current download server");
|
||||
Cmd_AddCommand("http_cancel", &HTTP_Cancel_f, "cancel current download");
|
||||
Cmd_AddCommand("http_clear", &HTTP_Clear_f, "cancel all downloads");
|
||||
Cmd_AddCommand("http_list", &HTTP_List_f, "list all queued downloads");
|
||||
Cmd_AddCommand("http_addcustomserver", &HTTP_AddCustomServer_f, "add custom fastdl server");
|
||||
http_useragent = Cvar_Get( "http_useragent", "xash3d", FCVAR_ARCHIVE, "User-Agent string" );
|
||||
http_autoremove = Cvar_Get( "http_autoremove", "1", FCVAR_ARCHIVE, "remove broken files" );
|
||||
http_timeout = Cvar_Get( "http_timeout", "45", FCVAR_ARCHIVE, "timeout for http downloader" );
|
||||
http_maxconnections = Cvar_Get( "http_maxconnections", "4", FCVAR_ARCHIVE, "maximum http connection number" );
|
||||
|
||||
// Read servers from fastdl.txt
|
||||
line = serverfile = (char *)FS_LoadFile( "fastdl.txt", 0, false );
|
||||
|
||||
if( serverfile )
|
||||
{
|
||||
while( ( line = COM_ParseFile( line, token ) ) )
|
||||
{
|
||||
httpserver_t *server = HTTP_ParseURL( token );
|
||||
|
||||
if( !server )
|
||||
continue;
|
||||
|
||||
if( !http.last_server )
|
||||
http.last_server = http.first_server = server;
|
||||
else
|
||||
{
|
||||
http.last_server->next = server;
|
||||
http.last_server = server;
|
||||
}
|
||||
}
|
||||
|
||||
Mem_Free( serverfile );
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
====================
|
||||
HTTP_Shutdown
|
||||
====================
|
||||
*/
|
||||
void HTTP_Shutdown( void )
|
||||
{
|
||||
HTTP_Clear_f();
|
||||
|
||||
while( http.first_server )
|
||||
{
|
||||
httpserver_t *tmp = http.first_server;
|
||||
|
||||
http.first_server = http.first_server->next;
|
||||
Mem_Free( tmp );
|
||||
}
|
||||
|
||||
http.last_server = 0;
|
||||
}
|
||||
|
@ -67,4 +67,11 @@ void NET_ClearLagData( qboolean bClient, qboolean bServer );
|
||||
qboolean CL_LegacyMode( void );
|
||||
#endif
|
||||
|
||||
void HTTP_AddCustomServer( const char *url );
|
||||
void HTTP_AddDownload( const char *path, int size, qboolean process );
|
||||
void HTTP_ClearCustomServers( void );
|
||||
void HTTP_Shutdown( void );
|
||||
void HTTP_Init( void );
|
||||
void HTTP_Run( void );
|
||||
|
||||
#endif//NET_WS_H
|
||||
|
Loading…
x
Reference in New Issue
Block a user