mirror of
https://github.com/FWGS/xash3d-fwgs
synced 2024-12-26 02:36:08 +01:00
Merge branch 'master' into netsplit
This commit is contained in:
commit
2b1ed2b20a
@ -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 );
|
||||
}
|
||||
}
|
||||
|
@ -1049,6 +1049,15 @@ void CL_DrawHUD( int state )
|
||||
}
|
||||
}
|
||||
|
||||
static void CL_ClearUserMessage( char *pszName, int svc_num )
|
||||
{
|
||||
int i;
|
||||
|
||||
for( i = 0; i < MAX_USER_MESSAGES && clgame.msg[i].name[0]; i++ )
|
||||
if( ( clgame.msg[i].number == svc_num ) && Q_strcmp( clgame.msg[i].name, pszName ) )
|
||||
clgame.msg[i].number = 0;
|
||||
}
|
||||
|
||||
void CL_LinkUserMessage( char *pszName, const int svc_num, int iSize )
|
||||
{
|
||||
int i;
|
||||
@ -1067,6 +1076,7 @@ void CL_LinkUserMessage( char *pszName, const int svc_num, int iSize )
|
||||
{
|
||||
clgame.msg[i].number = svc_num;
|
||||
clgame.msg[i].size = iSize;
|
||||
CL_ClearUserMessage( pszName, svc_num );
|
||||
return;
|
||||
}
|
||||
}
|
||||
@ -1081,6 +1091,7 @@ void CL_LinkUserMessage( char *pszName, const int svc_num, int iSize )
|
||||
Q_strncpy( clgame.msg[i].name, pszName, sizeof( clgame.msg[i].name ));
|
||||
clgame.msg[i].number = svc_num;
|
||||
clgame.msg[i].size = iSize;
|
||||
CL_ClearUserMessage( pszName, svc_num );
|
||||
}
|
||||
|
||||
void CL_FreeEntity( cl_entity_t *pEdict )
|
||||
@ -1215,7 +1226,7 @@ static qboolean CL_LoadHudSprite( const char *szSpriteName, model_t *m_pSprite,
|
||||
}
|
||||
else
|
||||
{
|
||||
Con_Reportf( S_ERROR "%s couldn't load\n", szSpriteName );
|
||||
Con_Reportf( S_ERROR "Could not load HUD sprite %s\n", szSpriteName );
|
||||
Mod_UnloadSpriteModel( m_pSprite );
|
||||
return false;
|
||||
}
|
||||
@ -2341,7 +2352,7 @@ int CL_FindModelIndex( const char *m )
|
||||
if( lasttimewarn < host.realtime )
|
||||
{
|
||||
// tell user about problem (but don't spam console)
|
||||
Con_Printf( S_ERROR "%s not precached\n", filepath );
|
||||
Con_Printf( S_ERROR "Could not find index for model %s: not precached\n", filepath );
|
||||
lasttimewarn = host.realtime + 1.0f;
|
||||
}
|
||||
|
||||
|
@ -382,7 +382,7 @@ static HIMAGE pfnPIC_Load( const char *szPicName, const byte *image_buf, int ima
|
||||
|
||||
if( !szPicName || !*szPicName )
|
||||
{
|
||||
Con_Reportf( S_ERROR "CL_LoadImage: bad name!\n" );
|
||||
Con_Reportf( S_ERROR "CL_LoadImage: refusing to load image with empty name\n" );
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -1351,6 +1351,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();
|
||||
}
|
||||
|
||||
/*
|
||||
@ -1534,6 +1535,7 @@ void CL_LocalServers_f( void )
|
||||
|
||||
Con_Printf( "Scanning for servers on the local network area...\n" );
|
||||
NET_Config( true ); // allow remote
|
||||
cls.legacyservercount = 0;
|
||||
|
||||
// send a broadcast packet
|
||||
adr.type = NA_BROADCAST;
|
||||
@ -1561,6 +1563,7 @@ void CL_InternetServers_f( void )
|
||||
Info_SetValueForKey( info, "gamedir", GI->gamefolder, remaining );
|
||||
Info_SetValueForKey( info, "clver", XASH_VERSION, remaining ); // let master know about client version
|
||||
// Info_SetValueForKey( info, "nat", cl_nat->string, remaining );
|
||||
cls.legacyservercount = 0;
|
||||
|
||||
cls.internetservers_wait = NET_SendToMasters( NS_CLIENT, sizeof( MS_SCAN_REQUEST ) + Q_strlen( info ), fullquery );
|
||||
cls.internetservers_pending = true;
|
||||
@ -1683,6 +1686,7 @@ void CL_ParseStatusMessage( netadr_t from, sizebuf_t *msg )
|
||||
{
|
||||
static char infostring[MAX_INFO_STRING+8];
|
||||
char *s = MSG_ReadString( msg );
|
||||
int i;
|
||||
|
||||
CL_FixupColorStringsForInfoString( s, infostring );
|
||||
|
||||
@ -1690,6 +1694,8 @@ void CL_ParseStatusMessage( netadr_t from, sizebuf_t *msg )
|
||||
{
|
||||
Netchan_OutOfBandPrint( NS_CLIENT, from, "info %i", PROTOCOL_LEGACY_VERSION );
|
||||
Con_Printf( "^1Server^7: %s, Info: %s\n", NET_AdrToString( from ), infostring );
|
||||
if( cls.legacyservercount < MAX_LEGACY_SERVERS )
|
||||
cls.legacyservers[cls.legacyservercount++] = from;
|
||||
return;
|
||||
}
|
||||
|
||||
@ -1699,6 +1705,16 @@ void CL_ParseStatusMessage( netadr_t from, sizebuf_t *msg )
|
||||
return; // unsupported proto
|
||||
}
|
||||
|
||||
for( i = 0; i < cls.legacyservercount; i++ )
|
||||
{
|
||||
if( NET_CompareAdr( cls.legacyservers[i], from ) )
|
||||
{
|
||||
Info_SetValueForKey( infostring, "legacy", "1", sizeof( infostring ) );
|
||||
Con_Print("Legacy: ");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// more info about servers
|
||||
Con_Printf( "^2Server^7: %s, Game: %s\n", NET_AdrToString( from ), Info_ValueForKey( infostring, "gamedir" ));
|
||||
|
||||
@ -2309,6 +2325,17 @@ void CL_ProcessFile( qboolean successfully_received, const char *filename )
|
||||
{
|
||||
Con_Printf( S_ERROR "server failed to transmit file '%s'\n", CL_CleanFileName( filename ));
|
||||
}
|
||||
if( cls.legacymode )
|
||||
{
|
||||
if( host.downloadcount > 0 )
|
||||
host.downloadcount--;
|
||||
if( !host.downloadcount )
|
||||
{
|
||||
MSG_WriteByte( &cls.netchan.message, clc_stringcmd );
|
||||
MSG_WriteString( &cls.netchan.message, "continueloading" );
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
pfilename = filename;
|
||||
|
||||
@ -2358,7 +2385,7 @@ void CL_ProcessFile( qboolean successfully_received, const char *filename )
|
||||
}
|
||||
else
|
||||
{
|
||||
Con_Printf( "Downloaded %i bytes for purported %i byte file, ignoring download\n",
|
||||
Con_Printf( "Downloaded %i bytes for purported %i byte file, ignoring download\n",
|
||||
cls.netchan.tempbuffersize, p->nDownloadSize );
|
||||
}
|
||||
|
||||
@ -2553,7 +2580,7 @@ qboolean CL_PrecacheResources( void )
|
||||
{
|
||||
if( FBitSet( pRes->ucFlags, RES_WASMISSING ))
|
||||
{
|
||||
Con_Printf( S_ERROR "%s%s couldn't load\n", DEFAULT_SOUNDPATH, pRes->szFileName );
|
||||
Con_Printf( S_ERROR "Could not load sound %s%s\n", DEFAULT_SOUNDPATH, pRes->szFileName );
|
||||
cl.sound_precache[pRes->nIndex][0] = 0;
|
||||
cl.sound_index[pRes->nIndex] = 0;
|
||||
}
|
||||
|
@ -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 );
|
||||
}
|
||||
}
|
||||
|
||||
@ -2716,6 +2727,61 @@ void CL_LegacyUpdateUserinfo( sizebuf_t *msg )
|
||||
else memset( player, 0, sizeof( *player ));
|
||||
}
|
||||
|
||||
/*
|
||||
==============
|
||||
CL_ParseResourceList
|
||||
|
||||
==============
|
||||
*/
|
||||
void CL_LegacyParseResourceList( sizebuf_t *msg )
|
||||
{
|
||||
int i = 0;
|
||||
|
||||
static struct
|
||||
{
|
||||
int rescount;
|
||||
int restype[MAX_RESOURCES];
|
||||
char resnames[MAX_RESOURCES][CS_SIZE];
|
||||
} reslist;
|
||||
memset( &reslist, 0, sizeof( reslist ));
|
||||
|
||||
reslist.rescount = MSG_ReadWord( msg ) - 1;
|
||||
|
||||
for( i = 0; i < reslist.rescount; i++ )
|
||||
{
|
||||
reslist.restype[i] = MSG_ReadWord( msg );
|
||||
Q_strncpy( reslist.resnames[i], MSG_ReadString( msg ), CS_SIZE );
|
||||
}
|
||||
|
||||
if( CL_IsPlaybackDemo() )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
host.downloadcount = 0;
|
||||
|
||||
for( i = 0; i < reslist.rescount; i++ )
|
||||
{
|
||||
const char *path;
|
||||
|
||||
if( reslist.restype[i] == t_sound )
|
||||
path = va( "sound/%s", reslist.resnames[i] );
|
||||
else path = reslist.resnames[i];
|
||||
|
||||
if( FS_FileExists( path, false ))
|
||||
continue; // already exists
|
||||
|
||||
host.downloadcount++;
|
||||
HTTP_AddDownload( path, -1, true );
|
||||
}
|
||||
|
||||
if( !host.downloadcount )
|
||||
{
|
||||
MSG_WriteByte( &cls.netchan.message, clc_stringcmd );
|
||||
MSG_WriteString( &cls.netchan.message, "continueloading" );
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
=====================
|
||||
CL_ParseLegacyServerMessage
|
||||
@ -2845,13 +2911,9 @@ void CL_ParseLegacyServerMessage( sizebuf_t *msg, qboolean normal_message )
|
||||
if( !Q_strnicmp( s, "disconnect", 10 ) && cls.signon != SIGNONS )
|
||||
break; // too early
|
||||
#endif
|
||||
if( !Q_strcmp(s, "cmd getresourcelist\n") )
|
||||
Cbuf_AddText("cmd continueloading\n");
|
||||
else
|
||||
{
|
||||
Con_Reportf( "Stufftext: %s", s );
|
||||
Cbuf_AddText( s );
|
||||
}
|
||||
|
||||
Con_Reportf( "Stufftext: %s", s );
|
||||
Cbuf_AddText( s );
|
||||
break;
|
||||
case svc_setangle:
|
||||
CL_ParseSetAngle( msg );
|
||||
@ -2981,7 +3043,7 @@ void CL_ParseLegacyServerMessage( sizebuf_t *msg, qboolean normal_message )
|
||||
//cl.frames[cls.netchan.incoming_sequence & CL_UPDATE_MASK].receivedtime = -2.0;
|
||||
break;
|
||||
case svc_resourcelist:
|
||||
CL_ParseResourceList( msg );
|
||||
CL_LegacyParseResourceList( msg );
|
||||
break;
|
||||
case svc_deltamovevars:
|
||||
CL_ParseMovevars( msg );
|
||||
|
@ -663,6 +663,8 @@ typedef struct
|
||||
qboolean internetservers_pending; // internetservers is waiting for dns request
|
||||
qboolean legacymode; // one-way 48 protocol compatibility
|
||||
netadr_t legacyserver;
|
||||
netadr_t legacyservers[MAX_LEGACY_SERVERS];
|
||||
int legacyservercount;
|
||||
int extensions;
|
||||
} client_static_t;
|
||||
|
||||
|
@ -54,7 +54,7 @@ typedef struct
|
||||
static float g_DecalClipVerts[MAX_DECALCLIPVERT][VERTEXSIZE];
|
||||
static float g_DecalClipVerts2[MAX_DECALCLIPVERT][VERTEXSIZE];
|
||||
|
||||
static decal_t gDecalPool[MAX_RENDER_DECALS];
|
||||
decal_t gDecalPool[MAX_RENDER_DECALS];
|
||||
static int gDecalCount;
|
||||
|
||||
void R_ClearDecals( void )
|
||||
@ -570,6 +570,7 @@ static void R_AddDecalToSurface( decal_t *pdecal, msurface_t *surf, decalinfo_t
|
||||
|
||||
// alloc clipped poly for decal
|
||||
R_DecalCreatePoly( decalinfo, pdecal, surf );
|
||||
R_AddDecalVBO( pdecal, surf );
|
||||
}
|
||||
|
||||
static void R_DecalCreate( decalinfo_t *decalinfo, msurface_t *surf, float x, float y )
|
||||
@ -1282,4 +1283,4 @@ void R_ClearAllDecals( void )
|
||||
{
|
||||
clgame.drawFuncs.R_ClearStudioDecals();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -408,6 +408,9 @@ void GL_RebuildLightmaps( void );
|
||||
void GL_InitRandomTable( void );
|
||||
void GL_BuildLightmaps( void );
|
||||
void GL_ResetFogColor( void );
|
||||
void R_GenerateVBO();
|
||||
void R_ClearVBO();
|
||||
void R_AddDecalVBO( decal_t *pdecal, msurface_t *surf );
|
||||
|
||||
//
|
||||
// gl_sprite.c
|
||||
@ -523,6 +526,7 @@ enum
|
||||
GL_EXT_GPU_SHADER4, // shaders only
|
||||
GL_DEPTH_TEXTURE,
|
||||
GL_DEBUG_OUTPUT,
|
||||
GL_ARB_VERTEX_BUFFER_OBJECT_EXT,
|
||||
GL_EXTCOUNT, // must be last
|
||||
};
|
||||
|
||||
@ -678,6 +682,8 @@ extern convar_t *r_lockfrustum;
|
||||
extern convar_t *r_traceglow;
|
||||
extern convar_t *r_dynamic;
|
||||
extern convar_t *r_lightmap;
|
||||
extern convar_t *r_vbo;
|
||||
extern convar_t *r_vbo_dlightmode;
|
||||
|
||||
extern convar_t *vid_displayfrequency;
|
||||
extern convar_t *vid_fullscreen;
|
||||
|
@ -199,4 +199,5 @@ void R_NewMap( void )
|
||||
R_SetupSky( clgame.movevars.skyName );
|
||||
|
||||
GL_BuildLightmaps ();
|
||||
}
|
||||
R_GenerateVBO();
|
||||
}
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -102,13 +102,22 @@ typedef struct
|
||||
vec4_t lightpos[MAXSTUDIOVERTS][MAX_LOCALLIGHTS];
|
||||
vec3_t lightbonepos[MAXSTUDIOBONES][MAX_LOCALLIGHTS];
|
||||
float locallightR2[MAX_LOCALLIGHTS];
|
||||
|
||||
// drawelements renderer
|
||||
vec3_t arrayverts[MAXSTUDIOVERTS];
|
||||
vec2_t arraycoord[MAXSTUDIOVERTS];
|
||||
unsigned short arrayelems[MAXSTUDIOVERTS*6];
|
||||
GLubyte arraycolor[MAXSTUDIOVERTS][4];
|
||||
uint numverts;
|
||||
uint numelems;
|
||||
} studio_draw_state_t;
|
||||
|
||||
// studio-related cvars
|
||||
convar_t *r_studio_sort_textures;
|
||||
convar_t *r_drawviewmodel;
|
||||
static convar_t *r_studio_sort_textures;
|
||||
static convar_t *r_drawviewmodel;
|
||||
convar_t *cl_righthand = NULL;
|
||||
convar_t *cl_himodels;
|
||||
static convar_t *cl_himodels;
|
||||
static convar_t *r_studio_drawelements;
|
||||
|
||||
static r_studio_interface_t *pStudioDraw;
|
||||
static studio_draw_state_t g_studio; // global studio state
|
||||
@ -135,6 +144,7 @@ void R_StudioInit( void )
|
||||
cl_himodels = Cvar_Get( "cl_himodels", "1", FCVAR_ARCHIVE, "draw high-resolution player models in multiplayer" );
|
||||
r_studio_sort_textures = Cvar_Get( "r_studio_sort_textures", "0", FCVAR_ARCHIVE, "change draw order for additive meshes" );
|
||||
r_drawviewmodel = Cvar_Get( "r_drawviewmodel", "1", 0, "draw firstperson weapon model" );
|
||||
r_studio_drawelements = Cvar_Get( "r_studio_drawelements", "1", FCVAR_ARCHIVE, "use glDrawElements for studiomodels" );
|
||||
|
||||
Matrix3x4_LoadIdentity( g_studio.rotationmatrix );
|
||||
Cvar_RegisterVariable( &r_glowshellfreq );
|
||||
@ -1723,7 +1733,7 @@ R_LightLambert
|
||||
|
||||
====================
|
||||
*/
|
||||
void R_LightLambert( vec4_t light[MAX_LOCALLIGHTS], vec3_t normal, vec3_t color )
|
||||
void R_LightLambert( vec4_t light[MAX_LOCALLIGHTS], vec3_t normal, vec3_t color, byte *out )
|
||||
{
|
||||
vec3_t finalLight;
|
||||
vec3_t localLight;
|
||||
@ -1761,7 +1771,53 @@ void R_LightLambert( vec4_t light[MAX_LOCALLIGHTS], vec3_t normal, vec3_t color
|
||||
}
|
||||
}
|
||||
|
||||
pglColor4f( finalLight[0], finalLight[1], finalLight[2], tr.blend );
|
||||
out[0] = finalLight[0] * 255;
|
||||
out[1] = finalLight[1] * 255;
|
||||
out[2] = finalLight[2] * 255;
|
||||
}
|
||||
|
||||
static void R_StudioSetColorBegin(short *ptricmds, vec3_t *pstudionorms )
|
||||
{
|
||||
float *lv = (float *)g_studio.lightvalues[ptricmds[1]];
|
||||
rgba_t color;
|
||||
|
||||
if( g_studio.numlocallights )
|
||||
{
|
||||
color[3] = tr.blend * 255;
|
||||
R_LightLambert( g_studio.lightpos[ptricmds[0]], pstudionorms[ptricmds[1]], lv, color );
|
||||
pglColor4ubv( color );
|
||||
}
|
||||
else
|
||||
{
|
||||
if( RI.currententity->curstate.rendermode == kRenderTransColor )
|
||||
{
|
||||
color[3] = tr.blend * 255;
|
||||
VectorCopy( (byte*)&RI.currententity->curstate.rendercolor, color );
|
||||
pglColor4ubv( color );
|
||||
}
|
||||
else pglColor4f( lv[0], lv[1], lv[2], tr.blend );
|
||||
}
|
||||
}
|
||||
|
||||
static void R_StudioSetColorArray(short *ptricmds, vec3_t *pstudionorms, byte *color )
|
||||
{
|
||||
float *lv = (float *)g_studio.lightvalues[ptricmds[1]];
|
||||
|
||||
color[3] = tr.blend * 255;
|
||||
|
||||
if( g_studio.numlocallights )
|
||||
R_LightLambert( g_studio.lightpos[ptricmds[0]], pstudionorms[ptricmds[1]], lv, color );
|
||||
else
|
||||
{
|
||||
if( RI.currententity->curstate.rendermode == kRenderTransColor )
|
||||
VectorCopy( (byte*)&RI.currententity->curstate.rendercolor, color );
|
||||
else
|
||||
{
|
||||
color[0] = lv[0] * 255;
|
||||
color[1] = lv[1] * 255;
|
||||
color[2] = lv[2] * 255;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
@ -1934,11 +1990,8 @@ _inline void R_StudioDrawNormalMesh( short *ptricmds, vec3_t *pstudionorms, floa
|
||||
|
||||
for( ; i > 0; i--, ptricmds += 4 )
|
||||
{
|
||||
lv = (float *)g_studio.lightvalues[ptricmds[1]];
|
||||
R_StudioSetColorBegin( ptricmds, pstudionorms );
|
||||
|
||||
if( g_studio.numlocallights )
|
||||
R_LightLambert( g_studio.lightpos[ptricmds[0]], pstudionorms[ptricmds[1]], lv );
|
||||
else pglColor4f( lv[0], lv[1], lv[2], tr.blend );
|
||||
pglTexCoord2f( ptricmds[2] * s, ptricmds[3] * t );
|
||||
pglVertex3fv( g_studio.verts[ptricmds[0]] );
|
||||
}
|
||||
@ -1970,10 +2023,7 @@ _inline void R_StudioDrawFloatMesh( short *ptricmds, vec3_t *pstudionorms )
|
||||
|
||||
for( ; i > 0; i--, ptricmds += 4 )
|
||||
{
|
||||
lv = (float *)g_studio.lightvalues[ptricmds[1]];
|
||||
if( g_studio.numlocallights )
|
||||
R_LightLambert( g_studio.lightpos[ptricmds[0]], pstudionorms[ptricmds[1]], lv );
|
||||
else pglColor4f( lv[0], lv[1], lv[2], tr.blend );
|
||||
R_StudioSetColorBegin( ptricmds, pstudionorms );
|
||||
pglTexCoord2f( HalfToFloat( ptricmds[2] ), HalfToFloat( ptricmds[3] ));
|
||||
pglVertex3fv( g_studio.verts[ptricmds[0]] );
|
||||
}
|
||||
@ -2009,10 +2059,13 @@ _inline void R_StudioDrawChromeMesh( short *ptricmds, vec3_t *pstudionorms, floa
|
||||
{
|
||||
if( glowShell )
|
||||
{
|
||||
color24 *clr = &RI.currententity->curstate.rendercolor;
|
||||
|
||||
idx = g_studio.normaltable[ptricmds[0]];
|
||||
av = g_studio.verts[ptricmds[0]];
|
||||
lv = g_studio.norms[ptricmds[0]];
|
||||
VectorMA( av, scale, lv, vert );
|
||||
pglColor4ub( clr->r, clr->g, clr->b, 255 );
|
||||
pglTexCoord2f( g_studio.chrome[idx][0] * s, g_studio.chrome[idx][1] * t );
|
||||
pglVertex3fv( vert );
|
||||
}
|
||||
@ -2020,9 +2073,7 @@ _inline void R_StudioDrawChromeMesh( short *ptricmds, vec3_t *pstudionorms, floa
|
||||
{
|
||||
idx = ptricmds[1];
|
||||
lv = (float *)g_studio.lightvalues[ptricmds[1]];
|
||||
if( g_studio.numlocallights )
|
||||
R_LightLambert( g_studio.lightpos[ptricmds[0]], pstudionorms[ptricmds[1]], lv );
|
||||
else pglColor4f( lv[0], lv[1], lv[2], tr.blend );
|
||||
R_StudioSetColorBegin( ptricmds, pstudionorms );
|
||||
pglTexCoord2f( g_studio.chrome[idx][0] * s, g_studio.chrome[idx][1] * t );
|
||||
pglVertex3fv( g_studio.verts[ptricmds[0]] );
|
||||
}
|
||||
@ -2032,6 +2083,220 @@ _inline void R_StudioDrawChromeMesh( short *ptricmds, vec3_t *pstudionorms, floa
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
_inline int R_StudioBuildIndices( qboolean tri_strip, int vertexState )
|
||||
{
|
||||
// build in indices
|
||||
if( vertexState++ < 3 )
|
||||
{
|
||||
g_studio.arrayelems[g_studio.numelems++] = g_studio.numverts;
|
||||
}
|
||||
else if( tri_strip )
|
||||
{
|
||||
// flip triangles between clockwise and counter clockwise
|
||||
if( vertexState & 1 )
|
||||
{
|
||||
// draw triangle [n-2 n-1 n]
|
||||
g_studio.arrayelems[g_studio.numelems++] = g_studio.numverts - 2;
|
||||
g_studio.arrayelems[g_studio.numelems++] = g_studio.numverts - 1;
|
||||
g_studio.arrayelems[g_studio.numelems++] = g_studio.numverts;
|
||||
}
|
||||
else
|
||||
{
|
||||
// draw triangle [n-1 n-2 n]
|
||||
g_studio.arrayelems[g_studio.numelems++] = g_studio.numverts - 1;
|
||||
g_studio.arrayelems[g_studio.numelems++] = g_studio.numverts - 2;
|
||||
g_studio.arrayelems[g_studio.numelems++] = g_studio.numverts;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// draw triangle fan [0 n-1 n]
|
||||
g_studio.arrayelems[g_studio.numelems++] = g_studio.numverts - ( vertexState - 1 );
|
||||
g_studio.arrayelems[g_studio.numelems++] = g_studio.numverts - 1;
|
||||
g_studio.arrayelems[g_studio.numelems++] = g_studio.numverts;
|
||||
}
|
||||
|
||||
return vertexState;
|
||||
}
|
||||
|
||||
/*
|
||||
===============
|
||||
R_StudioDrawNormalMesh
|
||||
|
||||
generic path
|
||||
===============
|
||||
*/
|
||||
_inline void R_StudioBuildArrayNormalMesh( short *ptricmds, vec3_t *pstudionorms, float s, float t )
|
||||
{
|
||||
float *lv;
|
||||
int i;
|
||||
float alpha = tr.blend;
|
||||
|
||||
while( i = *( ptricmds++ ))
|
||||
{
|
||||
int vertexState = 0;
|
||||
qboolean tri_strip = true;
|
||||
|
||||
if( i < 0 )
|
||||
{
|
||||
tri_strip = false;
|
||||
i = -i;
|
||||
}
|
||||
|
||||
for( ; i > 0; i--, ptricmds += 4 )
|
||||
{
|
||||
GLubyte *cl;
|
||||
cl = g_studio.arraycolor[g_studio.numverts];
|
||||
lv = (float *)g_studio.lightvalues[ptricmds[1]];
|
||||
|
||||
vertexState = R_StudioBuildIndices( tri_strip, vertexState );
|
||||
|
||||
R_StudioSetColorArray( ptricmds, pstudionorms, cl );
|
||||
|
||||
g_studio.arraycoord[g_studio.numverts][0] = ptricmds[2] * s;
|
||||
g_studio.arraycoord[g_studio.numverts][1] = ptricmds[3] * t;
|
||||
|
||||
VectorCopy( g_studio.verts[ptricmds[0]], g_studio.arrayverts[g_studio.numverts] );
|
||||
g_studio.numverts++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
===============
|
||||
R_StudioDrawNormalMesh
|
||||
|
||||
generic path
|
||||
===============
|
||||
*/
|
||||
_inline void R_StudioBuildArrayFloatMesh( short *ptricmds, vec3_t *pstudionorms )
|
||||
{
|
||||
float *lv;
|
||||
int i;
|
||||
float alpha = tr.blend;
|
||||
|
||||
while( i = *( ptricmds++ ))
|
||||
{
|
||||
int vertexState = 0;
|
||||
qboolean tri_strip = true;
|
||||
|
||||
if( i < 0 )
|
||||
{
|
||||
tri_strip = false;
|
||||
i = -i;
|
||||
}
|
||||
|
||||
for( ; i > 0; i--, ptricmds += 4 )
|
||||
{
|
||||
GLubyte *cl;
|
||||
cl = g_studio.arraycolor[g_studio.numverts];
|
||||
lv = (float *)g_studio.lightvalues[ptricmds[1]];
|
||||
|
||||
vertexState = R_StudioBuildIndices( tri_strip, vertexState );
|
||||
|
||||
R_StudioSetColorArray( ptricmds, pstudionorms, cl );
|
||||
|
||||
g_studio.arraycoord[g_studio.numverts][0] = HalfToFloat( ptricmds[2] );
|
||||
g_studio.arraycoord[g_studio.numverts][1] = HalfToFloat( ptricmds[3] );
|
||||
|
||||
VectorCopy( g_studio.verts[ptricmds[0]], g_studio.arrayverts[g_studio.numverts] );
|
||||
g_studio.numverts++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
===============
|
||||
R_StudioDrawNormalMesh
|
||||
|
||||
generic path
|
||||
===============
|
||||
*/
|
||||
_inline void R_StudioBuildArrayChromeMesh( short *ptricmds, vec3_t *pstudionorms, float s, float t, float scale )
|
||||
{
|
||||
float *lv, *av;
|
||||
int i, idx;
|
||||
qboolean glowShell = (scale > 0.0f) ? true : false;
|
||||
vec3_t vert;
|
||||
float alpha = tr.blend;
|
||||
|
||||
while( i = *( ptricmds++ ))
|
||||
{
|
||||
int vertexState = 0;
|
||||
qboolean tri_strip = true;
|
||||
|
||||
if( i < 0 )
|
||||
{
|
||||
tri_strip = false;
|
||||
i = -i;
|
||||
}
|
||||
|
||||
for( ; i > 0; i--, ptricmds += 4 )
|
||||
{
|
||||
GLubyte *cl;
|
||||
cl = g_studio.arraycolor[g_studio.numverts];
|
||||
lv = (float *)g_studio.lightvalues[ptricmds[1]];
|
||||
|
||||
vertexState = R_StudioBuildIndices( tri_strip, vertexState );
|
||||
|
||||
if( glowShell )
|
||||
{
|
||||
idx = g_studio.normaltable[ptricmds[0]];
|
||||
av = g_studio.verts[ptricmds[0]];
|
||||
lv = g_studio.norms[ptricmds[0]];
|
||||
|
||||
cl[0] = RI.currententity->curstate.rendercolor.r;
|
||||
cl[1] = RI.currententity->curstate.rendercolor.g;
|
||||
cl[2] = RI.currententity->curstate.rendercolor.b;
|
||||
cl[3] = 255;
|
||||
|
||||
VectorMA( av, scale, lv, vert );
|
||||
VectorCopy( vert, g_studio.arrayverts[g_studio.numverts] );
|
||||
}
|
||||
else
|
||||
{
|
||||
idx = ptricmds[1];
|
||||
R_StudioSetColorArray( ptricmds, pstudionorms, cl );
|
||||
|
||||
VectorCopy( g_studio.verts[ptricmds[0]], g_studio.arrayverts[g_studio.numverts] );
|
||||
}
|
||||
|
||||
g_studio.arraycoord[g_studio.numverts][0] = g_studio.chrome[idx][0] * s;
|
||||
g_studio.arraycoord[g_studio.numverts][1] = g_studio.chrome[idx][1] * t;
|
||||
|
||||
g_studio.numverts++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
_inline void R_StudioDrawArrays( uint startverts, uint startelems )
|
||||
{
|
||||
pglEnableClientState( GL_VERTEX_ARRAY );
|
||||
pglVertexPointer( 3, GL_FLOAT, 12, g_studio.arrayverts );
|
||||
|
||||
pglEnableClientState( GL_TEXTURE_COORD_ARRAY );
|
||||
pglTexCoordPointer( 2, GL_FLOAT, 0, g_studio.arraycoord );
|
||||
|
||||
if( !( g_nForceFaceFlags & STUDIO_NF_CHROME ) )
|
||||
{
|
||||
pglEnableClientState( GL_COLOR_ARRAY );
|
||||
pglColorPointer( 4, GL_UNSIGNED_BYTE, 0, g_studio.arraycolor );
|
||||
}
|
||||
|
||||
#if !defined XASH_NANOGL || defined XASH_WES && defined __EMSCRIPTEN__ // WebGL need to know array sizes
|
||||
if( pglDrawRangeElements )
|
||||
pglDrawRangeElements( GL_TRIANGLES, startverts, g_studio.numverts,
|
||||
g_studio.numelems - startelems, GL_UNSIGNED_SHORT, &g_studio.arrayelems[startelems] );
|
||||
else
|
||||
#endif
|
||||
pglDrawElements( GL_TRIANGLES, g_studio.numelems - startelems, GL_UNSIGNED_SHORT, &g_studio.arrayelems[startelems] );
|
||||
pglDisableClientState( GL_VERTEX_ARRAY );
|
||||
pglDisableClientState( GL_TEXTURE_COORD_ARRAY );
|
||||
if( !( g_nForceFaceFlags & STUDIO_NF_CHROME ) )
|
||||
pglDisableClientState( GL_COLOR_ARRAY );
|
||||
}
|
||||
|
||||
/*
|
||||
===============
|
||||
R_StudioDrawPoints
|
||||
@ -2054,6 +2319,9 @@ static void R_StudioDrawPoints( void )
|
||||
|
||||
if( !m_pStudioHeader ) return;
|
||||
|
||||
|
||||
g_studio.numverts = g_studio.numelems = 0;
|
||||
|
||||
// safety bounding the skinnum
|
||||
m_skinnum = bound( 0, RI.currententity->curstate.skin, ( m_pStudioHeader->numskinfamilies - 1 ));
|
||||
ptexture = (mstudiotexture_t *)((byte *)m_pStudioHeader + m_pStudioHeader->textureindex);
|
||||
@ -2151,6 +2419,8 @@ static void R_StudioDrawPoints( void )
|
||||
for( j = 0; j < m_pSubModel->nummesh; j++ )
|
||||
{
|
||||
float oldblend = tr.blend;
|
||||
uint startArrayVerts = g_studio.numverts;
|
||||
uint startArrayElems = g_studio.numelems;
|
||||
short *ptricmds;
|
||||
float s, t;
|
||||
|
||||
@ -2184,11 +2454,24 @@ static void R_StudioDrawPoints( void )
|
||||
|
||||
R_StudioSetupSkin( m_pStudioHeader, pskinref[pmesh->skinref] );
|
||||
|
||||
if( FBitSet( g_nFaceFlags, STUDIO_NF_CHROME ))
|
||||
R_StudioDrawChromeMesh( ptricmds, pstudionorms, s, t, shellscale );
|
||||
else if( FBitSet( g_nFaceFlags, STUDIO_NF_UV_COORDS ))
|
||||
R_StudioDrawFloatMesh( ptricmds, pstudionorms );
|
||||
else R_StudioDrawNormalMesh( ptricmds, pstudionorms, s, t );
|
||||
if( CVAR_TO_BOOL(r_studio_drawelements) )
|
||||
{
|
||||
if( FBitSet( g_nFaceFlags, STUDIO_NF_CHROME ))
|
||||
R_StudioBuildArrayChromeMesh( ptricmds, pstudionorms, s, t, shellscale );
|
||||
else if( FBitSet( g_nFaceFlags, STUDIO_NF_UV_COORDS ))
|
||||
R_StudioBuildArrayFloatMesh( ptricmds, pstudionorms );
|
||||
else R_StudioBuildArrayNormalMesh( ptricmds, pstudionorms, s, t );
|
||||
|
||||
R_StudioDrawArrays( startArrayVerts, startArrayElems );
|
||||
}
|
||||
else
|
||||
{
|
||||
if( FBitSet( g_nFaceFlags, STUDIO_NF_CHROME ))
|
||||
R_StudioDrawChromeMesh( ptricmds, pstudionorms, s, t, shellscale );
|
||||
else if( FBitSet( g_nFaceFlags, STUDIO_NF_UV_COORDS ))
|
||||
R_StudioDrawFloatMesh( ptricmds, pstudionorms );
|
||||
else R_StudioDrawNormalMesh( ptricmds, pstudionorms, s, t );
|
||||
}
|
||||
|
||||
if( FBitSet( g_nFaceFlags, STUDIO_NF_MASKED ))
|
||||
{
|
||||
|
@ -527,8 +527,11 @@ Key_IsAllowedAutoRepeat
|
||||
List of keys that allows auto-repeat
|
||||
===================
|
||||
*/
|
||||
qboolean Key_IsAllowedAutoRepeat( int key )
|
||||
static qboolean Key_IsAllowedAutoRepeat( int key )
|
||||
{
|
||||
if( cls.key_dest != key_game )
|
||||
return true;
|
||||
|
||||
switch( key )
|
||||
{
|
||||
case K_BACKSPACE:
|
||||
|
@ -545,7 +545,7 @@ void VOX_ParseLineCommands( char *pSentenceData, int sentenceIndex )
|
||||
length = pNext - pSentenceData;
|
||||
if( tempBufferPos + length > sizeof( tempBuffer ))
|
||||
{
|
||||
Con_Printf( S_ERROR "sentence too long!\n" );
|
||||
Con_Printf( S_ERROR "Sentence too long (max length %d characters)\n", sizeof(tempBuffer) - 1 );
|
||||
return;
|
||||
}
|
||||
|
||||
@ -619,7 +619,7 @@ void VOX_ReadSentenceFile( const char *psentenceFileName )
|
||||
{
|
||||
if( g_numSentences >= MAX_SENTENCES )
|
||||
{
|
||||
Con_Printf( S_ERROR "VOX_Init: too many sentences specified\n" );
|
||||
Con_Printf( S_ERROR "VOX_Init: too many sentences specified, max is %d\n", MAX_SENTENCES );
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -65,6 +65,8 @@ convar_t *r_traceglow;
|
||||
convar_t *r_dynamic;
|
||||
convar_t *r_lightmap;
|
||||
convar_t *gl_round_down;
|
||||
convar_t *r_vbo;
|
||||
convar_t *r_vbo_dlightmode;
|
||||
|
||||
convar_t *vid_displayfrequency;
|
||||
convar_t *vid_fullscreen;
|
||||
@ -546,6 +548,51 @@ static void SetFullscreenModeFromCommandLine( )
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
===============
|
||||
R_CheckVBO
|
||||
|
||||
register VBO cvars and get default value
|
||||
===============
|
||||
*/
|
||||
static void R_CheckVBO( void )
|
||||
{
|
||||
const char *def = "1";
|
||||
const char *dlightmode = "1";
|
||||
int flags = FCVAR_ARCHIVE;
|
||||
qboolean disable = false;
|
||||
|
||||
// some bad GLES1 implementations breaks dlights completely
|
||||
if( glConfig.max_texture_units < 3 )
|
||||
disable = true;
|
||||
|
||||
#ifdef XASH_MOBILE_PLATFORM
|
||||
// VideoCore4 drivers have a problem with mixing VBO and client arrays
|
||||
// Disable it, as there is no suitable workaround here
|
||||
if( Q_stristr( glConfig.renderer_string, "VideoCore IV" ) || Q_stristr( glConfig.renderer_string, "vc4" ) )
|
||||
disable = true;
|
||||
|
||||
// dlightmode 1 is not too much tested on android
|
||||
// so better to left it off
|
||||
dlightmode = "0";
|
||||
#endif
|
||||
|
||||
if( disable )
|
||||
{
|
||||
// do not keep in config unless dev > 3 and enabled
|
||||
flags = 0;
|
||||
def = "0";
|
||||
}
|
||||
|
||||
r_vbo = Cvar_Get( "r_vbo", def, flags, "draw world using VBO" );
|
||||
r_vbo_dlightmode = Cvar_Get( "r_vbo_dlightmode", dlightmode, FCVAR_ARCHIVE, "vbo dlight rendering mode(0-1)" );
|
||||
|
||||
// check if enabled manually
|
||||
if( CVAR_TO_BOOL(r_vbo) )
|
||||
r_vbo->flags |= FCVAR_ARCHIVE;
|
||||
}
|
||||
|
||||
/*
|
||||
===============
|
||||
R_Init
|
||||
@ -580,6 +627,7 @@ qboolean R_Init( void )
|
||||
r_temppool = Mem_AllocPool( "Render Zone" );
|
||||
|
||||
GL_SetDefaults();
|
||||
R_CheckVBO();
|
||||
R_InitImages();
|
||||
R_SpriteInit();
|
||||
R_StudioInit();
|
||||
|
@ -20,18 +20,18 @@ GNU General Public License for more details.
|
||||
#include "client.h"
|
||||
#include "library.h"
|
||||
|
||||
const char *file_exts[10] =
|
||||
static const char *file_exts[] =
|
||||
{
|
||||
".cfg",
|
||||
".lst",
|
||||
".exe",
|
||||
".vbs",
|
||||
".com",
|
||||
".bat",
|
||||
".dll",
|
||||
".ini",
|
||||
".log",
|
||||
".sys",
|
||||
"cfg",
|
||||
"lst",
|
||||
"exe",
|
||||
"vbs",
|
||||
"com",
|
||||
"bat",
|
||||
"dll",
|
||||
"ini",
|
||||
"log",
|
||||
"sys",
|
||||
};
|
||||
|
||||
#ifdef _DEBUG
|
||||
|
@ -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();
|
||||
|
@ -309,7 +309,7 @@ static void Mod_LoadLump( const byte *in, mlumpinfo_t *info, mlumpstat_t *stat,
|
||||
if( l->filelen % real_entrysize )
|
||||
{
|
||||
if( !FBitSet( flags, LUMP_SILENT ))
|
||||
Con_DPrintf( S_ERROR "Mod_Load%s: funny lump size\n", msg2 );
|
||||
Con_DPrintf( S_ERROR "Mod_Load%s: Lump size %d was not a multiple of %u bytes\n", msg2, l->filelen, real_entrysize );
|
||||
loadstat.numerrors++;
|
||||
return;
|
||||
}
|
||||
|
@ -288,8 +288,8 @@ model_t *Mod_LoadModel( model_t *mod, qboolean crash )
|
||||
{
|
||||
memset( mod, 0, sizeof( model_t ));
|
||||
|
||||
if( crash ) Host_Error( "%s couldn't load\n", tempname );
|
||||
else Con_Printf( S_ERROR "%s couldn't load\n", tempname );
|
||||
if( crash ) Host_Error( "Could not load model %s from disk\n", tempname );
|
||||
else Con_Printf( S_ERROR "Could not load model %s from disk\n", tempname );
|
||||
|
||||
return NULL;
|
||||
}
|
||||
@ -331,8 +331,8 @@ model_t *Mod_LoadModel( model_t *mod, qboolean crash )
|
||||
Mod_FreeModel( mod );
|
||||
Mem_Free( buf );
|
||||
|
||||
if( crash ) Host_Error( "%s couldn't load\n", tempname );
|
||||
else Con_Printf( S_ERROR "%s couldn't load\n", tempname );
|
||||
if( crash ) Host_Error( "Could not load model %s\n", tempname );
|
||||
else Con_Printf( S_ERROR "Could not load model %s\n", tempname );
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
@ -1877,3 +1877,827 @@ 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;
|
||||
|
||||
/*
|
||||
========================
|
||||
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 = 0;
|
||||
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
|
||||
mode = 1;
|
||||
pIoctlSocket( curfile->socket, FIONBIO, &mode );
|
||||
#ifdef __linux__
|
||||
// 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( pWSAGetLastError() == WSAEINPROGRESS || pWSAGetLastError() == WSAEWOULDBLOCK ) // 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( pWSAGetLastError() != WSAEWOULDBLOCK && pWSAGetLastError() != WSAENOTCONN )
|
||||
{
|
||||
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( pWSAGetLastError() != WSAEWOULDBLOCK )
|
||||
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;
|
||||
}
|
||||
|
@ -69,4 +69,11 @@ qboolean CL_LegacyMode( void );
|
||||
int CL_GetSplitSize( 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
|
||||
|
@ -262,4 +262,6 @@ extern const char *clc_strings[clc_lastmsg+1];
|
||||
#define MAX_LEGACY_ENTITY_BITS 12
|
||||
#define MAX_LEGACY_WEAPON_BITS 5
|
||||
#define MAX_LEGACY_MODEL_BITS 11
|
||||
#define MAX_LEGACY_SERVERS 32
|
||||
|
||||
#endif//NET_PROTOCOL_H
|
||||
|
@ -25,176 +25,189 @@ GNU General Public License for more details.
|
||||
static vidmode_t *vidmodes = NULL;
|
||||
static int num_vidmodes = 0;
|
||||
static int context_flags = 0;
|
||||
|
||||
#define GL_CALL( x ) #x, (void**)&p##x
|
||||
static dllfunc_t opengl_110funcs[] =
|
||||
{
|
||||
{ "glClearColor" , (void **)&pglClearColor },
|
||||
{ "glClear" , (void **)&pglClear },
|
||||
{ "glAlphaFunc" , (void **)&pglAlphaFunc },
|
||||
{ "glBlendFunc" , (void **)&pglBlendFunc },
|
||||
{ "glCullFace" , (void **)&pglCullFace },
|
||||
{ "glDrawBuffer" , (void **)&pglDrawBuffer },
|
||||
{ "glReadBuffer" , (void **)&pglReadBuffer },
|
||||
{ "glAccum" , (void **)&pglAccum },
|
||||
{ "glEnable" , (void **)&pglEnable },
|
||||
{ "glDisable" , (void **)&pglDisable },
|
||||
{ "glEnableClientState" , (void **)&pglEnableClientState },
|
||||
{ "glDisableClientState" , (void **)&pglDisableClientState },
|
||||
{ "glGetBooleanv" , (void **)&pglGetBooleanv },
|
||||
{ "glGetDoublev" , (void **)&pglGetDoublev },
|
||||
{ "glGetFloatv" , (void **)&pglGetFloatv },
|
||||
{ "glGetIntegerv" , (void **)&pglGetIntegerv },
|
||||
{ "glGetError" , (void **)&pglGetError },
|
||||
{ "glGetString" , (void **)&pglGetString },
|
||||
{ "glFinish" , (void **)&pglFinish },
|
||||
{ "glFlush" , (void **)&pglFlush },
|
||||
{ "glClearDepth" , (void **)&pglClearDepth },
|
||||
{ "glDepthFunc" , (void **)&pglDepthFunc },
|
||||
{ "glDepthMask" , (void **)&pglDepthMask },
|
||||
{ "glDepthRange" , (void **)&pglDepthRange },
|
||||
{ "glFrontFace" , (void **)&pglFrontFace },
|
||||
{ "glDrawElements" , (void **)&pglDrawElements },
|
||||
{ "glDrawArrays" , (void **)&pglDrawArrays },
|
||||
{ "glColorMask" , (void **)&pglColorMask },
|
||||
{ "glIndexPointer" , (void **)&pglIndexPointer },
|
||||
{ "glVertexPointer" , (void **)&pglVertexPointer },
|
||||
{ "glNormalPointer" , (void **)&pglNormalPointer },
|
||||
{ "glColorPointer" , (void **)&pglColorPointer },
|
||||
{ "glTexCoordPointer" , (void **)&pglTexCoordPointer },
|
||||
{ "glArrayElement" , (void **)&pglArrayElement },
|
||||
{ "glColor3f" , (void **)&pglColor3f },
|
||||
{ "glColor3fv" , (void **)&pglColor3fv },
|
||||
{ "glColor4f" , (void **)&pglColor4f },
|
||||
{ "glColor4fv" , (void **)&pglColor4fv },
|
||||
{ "glColor3ub" , (void **)&pglColor3ub },
|
||||
{ "glColor4ub" , (void **)&pglColor4ub },
|
||||
{ "glColor4ubv" , (void **)&pglColor4ubv },
|
||||
{ "glTexCoord1f" , (void **)&pglTexCoord1f },
|
||||
{ "glTexCoord2f" , (void **)&pglTexCoord2f },
|
||||
{ "glTexCoord3f" , (void **)&pglTexCoord3f },
|
||||
{ "glTexCoord4f" , (void **)&pglTexCoord4f },
|
||||
{ "glTexCoord1fv" , (void **)&pglTexCoord1fv },
|
||||
{ "glTexCoord2fv" , (void **)&pglTexCoord2fv },
|
||||
{ "glTexCoord3fv" , (void **)&pglTexCoord3fv },
|
||||
{ "glTexCoord4fv" , (void **)&pglTexCoord4fv },
|
||||
{ "glTexGenf" , (void **)&pglTexGenf },
|
||||
{ "glTexGenfv" , (void **)&pglTexGenfv },
|
||||
{ "glTexGeni" , (void **)&pglTexGeni },
|
||||
{ "glVertex2f" , (void **)&pglVertex2f },
|
||||
{ "glVertex3f" , (void **)&pglVertex3f },
|
||||
{ "glVertex3fv" , (void **)&pglVertex3fv },
|
||||
{ "glNormal3f" , (void **)&pglNormal3f },
|
||||
{ "glNormal3fv" , (void **)&pglNormal3fv },
|
||||
{ "glBegin" , (void **)&pglBegin },
|
||||
{ "glEnd" , (void **)&pglEnd },
|
||||
{ "glLineWidth" , (void**)&pglLineWidth },
|
||||
{ "glPointSize" , (void**)&pglPointSize },
|
||||
{ "glMatrixMode" , (void **)&pglMatrixMode },
|
||||
{ "glOrtho" , (void **)&pglOrtho },
|
||||
{ "glRasterPos2f" , (void **) &pglRasterPos2f },
|
||||
{ "glFrustum" , (void **)&pglFrustum },
|
||||
{ "glViewport" , (void **)&pglViewport },
|
||||
{ "glPushMatrix" , (void **)&pglPushMatrix },
|
||||
{ "glPopMatrix" , (void **)&pglPopMatrix },
|
||||
{ "glPushAttrib" , (void **)&pglPushAttrib },
|
||||
{ "glPopAttrib" , (void **)&pglPopAttrib },
|
||||
{ "glLoadIdentity" , (void **)&pglLoadIdentity },
|
||||
{ "glLoadMatrixd" , (void **)&pglLoadMatrixd },
|
||||
{ "glLoadMatrixf" , (void **)&pglLoadMatrixf },
|
||||
{ "glMultMatrixd" , (void **)&pglMultMatrixd },
|
||||
{ "glMultMatrixf" , (void **)&pglMultMatrixf },
|
||||
{ "glRotated" , (void **)&pglRotated },
|
||||
{ "glRotatef" , (void **)&pglRotatef },
|
||||
{ "glScaled" , (void **)&pglScaled },
|
||||
{ "glScalef" , (void **)&pglScalef },
|
||||
{ "glTranslated" , (void **)&pglTranslated },
|
||||
{ "glTranslatef" , (void **)&pglTranslatef },
|
||||
{ "glReadPixels" , (void **)&pglReadPixels },
|
||||
{ "glDrawPixels" , (void **)&pglDrawPixels },
|
||||
{ "glStencilFunc" , (void **)&pglStencilFunc },
|
||||
{ "glStencilMask" , (void **)&pglStencilMask },
|
||||
{ "glStencilOp" , (void **)&pglStencilOp },
|
||||
{ "glClearStencil" , (void **)&pglClearStencil },
|
||||
{ "glIsEnabled" , (void **)&pglIsEnabled },
|
||||
{ "glIsList" , (void **)&pglIsList },
|
||||
{ "glIsTexture" , (void **)&pglIsTexture },
|
||||
{ "glTexEnvf" , (void **)&pglTexEnvf },
|
||||
{ "glTexEnvfv" , (void **)&pglTexEnvfv },
|
||||
{ "glTexEnvi" , (void **)&pglTexEnvi },
|
||||
{ "glTexParameterf" , (void **)&pglTexParameterf },
|
||||
{ "glTexParameterfv" , (void **)&pglTexParameterfv },
|
||||
{ "glTexParameteri" , (void **)&pglTexParameteri },
|
||||
{ "glHint" , (void **)&pglHint },
|
||||
{ "glPixelStoref" , (void **)&pglPixelStoref },
|
||||
{ "glPixelStorei" , (void **)&pglPixelStorei },
|
||||
{ "glGenTextures" , (void **)&pglGenTextures },
|
||||
{ "glDeleteTextures" , (void **)&pglDeleteTextures },
|
||||
{ "glBindTexture" , (void **)&pglBindTexture },
|
||||
{ "glTexImage1D" , (void **)&pglTexImage1D },
|
||||
{ "glTexImage2D" , (void **)&pglTexImage2D },
|
||||
{ "glTexSubImage1D" , (void **)&pglTexSubImage1D },
|
||||
{ "glTexSubImage2D" , (void **)&pglTexSubImage2D },
|
||||
{ "glCopyTexImage1D" , (void **)&pglCopyTexImage1D },
|
||||
{ "glCopyTexImage2D" , (void **)&pglCopyTexImage2D },
|
||||
{ "glCopyTexSubImage1D" , (void **)&pglCopyTexSubImage1D },
|
||||
{ "glCopyTexSubImage2D" , (void **)&pglCopyTexSubImage2D },
|
||||
{ "glScissor" , (void **)&pglScissor },
|
||||
{ "glGetTexImage" , (void **)&pglGetTexImage },
|
||||
{ "glGetTexEnviv" , (void **)&pglGetTexEnviv },
|
||||
{ "glPolygonOffset" , (void **)&pglPolygonOffset },
|
||||
{ "glPolygonMode" , (void **)&pglPolygonMode },
|
||||
{ "glPolygonStipple" , (void **)&pglPolygonStipple },
|
||||
{ "glClipPlane" , (void **)&pglClipPlane },
|
||||
{ "glGetClipPlane" , (void **)&pglGetClipPlane },
|
||||
{ "glShadeModel" , (void **)&pglShadeModel },
|
||||
{ "glGetTexLevelParameteriv" , (void **)&pglGetTexLevelParameteriv },
|
||||
{ "glGetTexLevelParameterfv" , (void **)&pglGetTexLevelParameterfv },
|
||||
{ "glFogfv" , (void **)&pglFogfv },
|
||||
{ "glFogf" , (void **)&pglFogf },
|
||||
{ "glFogi" , (void **)&pglFogi },
|
||||
{ NULL , NULL }
|
||||
{ GL_CALL( glClearColor ) },
|
||||
{ GL_CALL( glClear ) },
|
||||
{ GL_CALL( glAlphaFunc ) },
|
||||
{ GL_CALL( glBlendFunc ) },
|
||||
{ GL_CALL( glCullFace ) },
|
||||
{ GL_CALL( glDrawBuffer ) },
|
||||
{ GL_CALL( glReadBuffer ) },
|
||||
{ GL_CALL( glAccum ) },
|
||||
{ GL_CALL( glEnable ) },
|
||||
{ GL_CALL( glDisable ) },
|
||||
{ GL_CALL( glEnableClientState ) },
|
||||
{ GL_CALL( glDisableClientState ) },
|
||||
{ GL_CALL( glGetBooleanv ) },
|
||||
{ GL_CALL( glGetDoublev ) },
|
||||
{ GL_CALL( glGetFloatv ) },
|
||||
{ GL_CALL( glGetIntegerv ) },
|
||||
{ GL_CALL( glGetError ) },
|
||||
{ GL_CALL( glGetString ) },
|
||||
{ GL_CALL( glFinish ) },
|
||||
{ GL_CALL( glFlush ) },
|
||||
{ GL_CALL( glClearDepth ) },
|
||||
{ GL_CALL( glDepthFunc ) },
|
||||
{ GL_CALL( glDepthMask ) },
|
||||
{ GL_CALL( glDepthRange ) },
|
||||
{ GL_CALL( glFrontFace ) },
|
||||
{ GL_CALL( glDrawElements ) },
|
||||
{ GL_CALL( glDrawArrays ) },
|
||||
{ GL_CALL( glColorMask ) },
|
||||
{ GL_CALL( glIndexPointer ) },
|
||||
{ GL_CALL( glVertexPointer ) },
|
||||
{ GL_CALL( glNormalPointer ) },
|
||||
{ GL_CALL( glColorPointer ) },
|
||||
{ GL_CALL( glTexCoordPointer ) },
|
||||
{ GL_CALL( glArrayElement ) },
|
||||
{ GL_CALL( glColor3f ) },
|
||||
{ GL_CALL( glColor3fv ) },
|
||||
{ GL_CALL( glColor4f ) },
|
||||
{ GL_CALL( glColor4fv ) },
|
||||
{ GL_CALL( glColor3ub ) },
|
||||
{ GL_CALL( glColor4ub ) },
|
||||
{ GL_CALL( glColor4ubv ) },
|
||||
{ GL_CALL( glTexCoord1f ) },
|
||||
{ GL_CALL( glTexCoord2f ) },
|
||||
{ GL_CALL( glTexCoord3f ) },
|
||||
{ GL_CALL( glTexCoord4f ) },
|
||||
{ GL_CALL( glTexCoord1fv ) },
|
||||
{ GL_CALL( glTexCoord2fv ) },
|
||||
{ GL_CALL( glTexCoord3fv ) },
|
||||
{ GL_CALL( glTexCoord4fv ) },
|
||||
{ GL_CALL( glTexGenf ) },
|
||||
{ GL_CALL( glTexGenfv ) },
|
||||
{ GL_CALL( glTexGeni ) },
|
||||
{ GL_CALL( glVertex2f ) },
|
||||
{ GL_CALL( glVertex3f ) },
|
||||
{ GL_CALL( glVertex3fv ) },
|
||||
{ GL_CALL( glNormal3f ) },
|
||||
{ GL_CALL( glNormal3fv ) },
|
||||
{ GL_CALL( glBegin ) },
|
||||
{ GL_CALL( glEnd ) },
|
||||
{ GL_CALL( glLineWidth ) },
|
||||
{ GL_CALL( glPointSize ) },
|
||||
{ GL_CALL( glMatrixMode ) },
|
||||
{ GL_CALL( glOrtho ) },
|
||||
{ GL_CALL( glRasterPos2f ) },
|
||||
{ GL_CALL( glFrustum ) },
|
||||
{ GL_CALL( glViewport ) },
|
||||
{ GL_CALL( glPushMatrix ) },
|
||||
{ GL_CALL( glPopMatrix ) },
|
||||
{ GL_CALL( glPushAttrib ) },
|
||||
{ GL_CALL( glPopAttrib ) },
|
||||
{ GL_CALL( glLoadIdentity ) },
|
||||
{ GL_CALL( glLoadMatrixd ) },
|
||||
{ GL_CALL( glLoadMatrixf ) },
|
||||
{ GL_CALL( glMultMatrixd ) },
|
||||
{ GL_CALL( glMultMatrixf ) },
|
||||
{ GL_CALL( glRotated ) },
|
||||
{ GL_CALL( glRotatef ) },
|
||||
{ GL_CALL( glScaled ) },
|
||||
{ GL_CALL( glScalef ) },
|
||||
{ GL_CALL( glTranslated ) },
|
||||
{ GL_CALL( glTranslatef ) },
|
||||
{ GL_CALL( glReadPixels ) },
|
||||
{ GL_CALL( glDrawPixels ) },
|
||||
{ GL_CALL( glStencilFunc ) },
|
||||
{ GL_CALL( glStencilMask ) },
|
||||
{ GL_CALL( glStencilOp ) },
|
||||
{ GL_CALL( glClearStencil ) },
|
||||
{ GL_CALL( glIsEnabled ) },
|
||||
{ GL_CALL( glIsList ) },
|
||||
{ GL_CALL( glIsTexture ) },
|
||||
{ GL_CALL( glTexEnvf ) },
|
||||
{ GL_CALL( glTexEnvfv ) },
|
||||
{ GL_CALL( glTexEnvi ) },
|
||||
{ GL_CALL( glTexParameterf ) },
|
||||
{ GL_CALL( glTexParameterfv ) },
|
||||
{ GL_CALL( glTexParameteri ) },
|
||||
{ GL_CALL( glHint ) },
|
||||
{ GL_CALL( glPixelStoref ) },
|
||||
{ GL_CALL( glPixelStorei ) },
|
||||
{ GL_CALL( glGenTextures ) },
|
||||
{ GL_CALL( glDeleteTextures ) },
|
||||
{ GL_CALL( glBindTexture ) },
|
||||
{ GL_CALL( glTexImage1D ) },
|
||||
{ GL_CALL( glTexImage2D ) },
|
||||
{ GL_CALL( glTexSubImage1D ) },
|
||||
{ GL_CALL( glTexSubImage2D ) },
|
||||
{ GL_CALL( glCopyTexImage1D ) },
|
||||
{ GL_CALL( glCopyTexImage2D ) },
|
||||
{ GL_CALL( glCopyTexSubImage1D ) },
|
||||
{ GL_CALL( glCopyTexSubImage2D ) },
|
||||
{ GL_CALL( glScissor ) },
|
||||
{ GL_CALL( glGetTexImage ) },
|
||||
{ GL_CALL( glGetTexEnviv ) },
|
||||
{ GL_CALL( glPolygonOffset ) },
|
||||
{ GL_CALL( glPolygonMode ) },
|
||||
{ GL_CALL( glPolygonStipple ) },
|
||||
{ GL_CALL( glClipPlane ) },
|
||||
{ GL_CALL( glGetClipPlane ) },
|
||||
{ GL_CALL( glShadeModel ) },
|
||||
{ GL_CALL( glGetTexLevelParameteriv ) },
|
||||
{ GL_CALL( glGetTexLevelParameterfv ) },
|
||||
{ GL_CALL( glFogfv ) },
|
||||
{ GL_CALL( glFogf ) },
|
||||
{ GL_CALL( glFogi ) },
|
||||
{ NULL , NULL }
|
||||
};
|
||||
|
||||
static dllfunc_t debugoutputfuncs[] =
|
||||
{
|
||||
{ "glDebugMessageControlARB" , (void **)&pglDebugMessageControlARB },
|
||||
{ "glDebugMessageInsertARB" , (void **)&pglDebugMessageInsertARB },
|
||||
{ "glDebugMessageCallbackARB" , (void **)&pglDebugMessageCallbackARB },
|
||||
{ "glGetDebugMessageLogARB" , (void **)&pglGetDebugMessageLogARB },
|
||||
{ NULL , NULL }
|
||||
{ GL_CALL( glDebugMessageControlARB ) },
|
||||
{ GL_CALL( glDebugMessageInsertARB ) },
|
||||
{ GL_CALL( glDebugMessageCallbackARB ) },
|
||||
{ GL_CALL( glGetDebugMessageLogARB ) },
|
||||
{ NULL , NULL }
|
||||
};
|
||||
|
||||
static dllfunc_t multitexturefuncs[] =
|
||||
{
|
||||
{ "glMultiTexCoord1fARB" , (void **)&pglMultiTexCoord1f },
|
||||
{ "glMultiTexCoord2fARB" , (void **)&pglMultiTexCoord2f },
|
||||
{ "glMultiTexCoord3fARB" , (void **)&pglMultiTexCoord3f },
|
||||
{ "glMultiTexCoord4fARB" , (void **)&pglMultiTexCoord4f },
|
||||
{ "glActiveTextureARB" , (void **)&pglActiveTexture },
|
||||
{ "glActiveTextureARB" , (void **)&pglActiveTextureARB },
|
||||
{ "glClientActiveTextureARB" , (void **)&pglClientActiveTexture },
|
||||
{ "glClientActiveTextureARB" , (void **)&pglClientActiveTextureARB },
|
||||
{ NULL , NULL }
|
||||
{ GL_CALL( glMultiTexCoord1f ) },
|
||||
{ GL_CALL( glMultiTexCoord2f ) },
|
||||
{ GL_CALL( glMultiTexCoord3f ) },
|
||||
{ GL_CALL( glMultiTexCoord4f ) },
|
||||
{ GL_CALL( glActiveTexture ) },
|
||||
{ GL_CALL( glActiveTextureARB ) },
|
||||
{ GL_CALL( glClientActiveTexture ) },
|
||||
{ GL_CALL( glClientActiveTextureARB ) },
|
||||
{ NULL , NULL }
|
||||
};
|
||||
|
||||
static dllfunc_t texture3dextfuncs[] =
|
||||
{
|
||||
{ "glTexImage3DEXT" , (void **)&pglTexImage3D },
|
||||
{ "glTexSubImage3DEXT" , (void **)&pglTexSubImage3D },
|
||||
{ "glCopyTexSubImage3DEXT" , (void **)&pglCopyTexSubImage3D },
|
||||
{ NULL , NULL }
|
||||
{ GL_CALL( glTexImage3D ) },
|
||||
{ GL_CALL( glTexSubImage3D ) },
|
||||
{ GL_CALL( glCopyTexSubImage3D ) },
|
||||
{ NULL , NULL }
|
||||
};
|
||||
|
||||
static dllfunc_t texturecompressionfuncs[] =
|
||||
{
|
||||
{ "glCompressedTexImage3DARB" , (void **)&pglCompressedTexImage3DARB },
|
||||
{ "glCompressedTexImage2DARB" , (void **)&pglCompressedTexImage2DARB },
|
||||
{ "glCompressedTexImage1DARB" , (void **)&pglCompressedTexImage1DARB },
|
||||
{ "glCompressedTexSubImage3DARB" , (void **)&pglCompressedTexSubImage3DARB },
|
||||
{ "glCompressedTexSubImage2DARB" , (void **)&pglCompressedTexSubImage2DARB },
|
||||
{ "glCompressedTexSubImage1DARB" , (void **)&pglCompressedTexSubImage1DARB },
|
||||
{ "glGetCompressedTexImageARB" , (void **)&pglGetCompressedTexImage },
|
||||
{ NULL , NULL }
|
||||
{ GL_CALL( glCompressedTexImage3DARB ) },
|
||||
{ GL_CALL( glCompressedTexImage2DARB ) },
|
||||
{ GL_CALL( glCompressedTexImage1DARB ) },
|
||||
{ GL_CALL( glCompressedTexSubImage3DARB ) },
|
||||
{ GL_CALL( glCompressedTexSubImage2DARB ) },
|
||||
{ GL_CALL( glCompressedTexSubImage1DARB ) },
|
||||
{ GL_CALL( glGetCompressedTexImage ) },
|
||||
{ NULL , NULL }
|
||||
};
|
||||
|
||||
static dllfunc_t vbofuncs[] =
|
||||
{
|
||||
{ GL_CALL( glBindBufferARB ) },
|
||||
{ GL_CALL( glDeleteBuffersARB ) },
|
||||
{ GL_CALL( glGenBuffersARB ) },
|
||||
{ GL_CALL( glIsBufferARB ) },
|
||||
{ GL_CALL( glMapBufferARB ) },
|
||||
{ GL_CALL( glUnmapBufferARB ) }, // ,
|
||||
{ GL_CALL( glBufferDataARB ) },
|
||||
{ GL_CALL( glBufferSubDataARB ) },
|
||||
{ NULL, NULL}
|
||||
};
|
||||
|
||||
static void GL_SetupAttributes( void );
|
||||
@ -884,7 +897,6 @@ qboolean R_Init_Video( void )
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
#ifdef XASH_GLES
|
||||
void GL_InitExtensionsGLES( void )
|
||||
{
|
||||
@ -1039,6 +1051,8 @@ void GL_InitExtensionsBigGL()
|
||||
GL_CheckExtension( "GL_ARB_depth_buffer_float", NULL, "gl_arb_depth_float", GL_ARB_DEPTH_FLOAT_EXT );
|
||||
GL_CheckExtension( "GL_EXT_gpu_shader4", NULL, NULL, GL_EXT_GPU_SHADER4 ); // don't confuse users
|
||||
GL_CheckExtension( "GL_ARB_shading_language_100", NULL, "gl_glslprogram", GL_SHADER_GLSL100_EXT );
|
||||
GL_CheckExtension( "GL_ARB_vertex_buffer_object", vbofuncs, "gl_vertex_buffer_object", GL_ARB_VERTEX_BUFFER_OBJECT_EXT );
|
||||
|
||||
// rectangle textures support
|
||||
GL_CheckExtension( "GL_ARB_texture_rectangle", NULL, "gl_texture_rectangle", GL_TEXTURE_2D_RECT_EXT );
|
||||
|
||||
|
@ -375,6 +375,7 @@ void SV_ConnectClient( netadr_t from )
|
||||
sv.current_client = newcl;
|
||||
newcl->edict = EDICT_NUM( (newcl - svs.clients) + 1 );
|
||||
newcl->challenge = challenge; // save challenge for checksumming
|
||||
if( newcl->frames ) Mem_Free( newcl->frames );
|
||||
newcl->frames = (client_frame_t *)Z_Calloc( sizeof( client_frame_t ) * SV_UPDATE_BACKUP );
|
||||
newcl->userid = g_userid++; // create unique userid
|
||||
newcl->state = cs_connected;
|
||||
@ -1875,6 +1876,26 @@ static qboolean SV_DownloadFile_f( sv_client_t *cl )
|
||||
{
|
||||
if( sv_send_resources.value )
|
||||
{
|
||||
int i;
|
||||
|
||||
// security: allow download only precached resources
|
||||
for( i = 0; i < sv.num_resources; i++ )
|
||||
{
|
||||
const char *cmpname = name;
|
||||
|
||||
if( sv.resources[i].type == t_sound )
|
||||
cmpname += sizeof( DEFAULT_SOUNDPATH ) - 1; // cut "sound/" off
|
||||
|
||||
if( !Q_strncmp( sv.resources[i].szFileName, cmpname, 64 ) )
|
||||
break;
|
||||
}
|
||||
|
||||
if( i == sv.num_resources )
|
||||
{
|
||||
SV_FailDownload( cl, name );
|
||||
return true;
|
||||
}
|
||||
|
||||
// also check the model textures
|
||||
if( !Q_stricmp( COM_FileExtension( name ), "mdl" ))
|
||||
{
|
||||
|
@ -998,9 +998,15 @@ void SV_InactivateClients( void )
|
||||
if( !cl->state || !cl->edict )
|
||||
continue;
|
||||
|
||||
if( !cl->edict || FBitSet( cl->edict->v.flags, FL_FAKECLIENT ))
|
||||
if( !cl->edict )
|
||||
continue;
|
||||
|
||||
if( FBitSet( cl->edict->v.flags, FL_FAKECLIENT ))
|
||||
{
|
||||
SV_DropClient( cl, false );
|
||||
continue;
|
||||
}
|
||||
|
||||
if( cl->state > cs_connected )
|
||||
cl->state = cs_connected;
|
||||
|
||||
@ -1015,4 +1021,4 @@ void SV_InactivateClients( void )
|
||||
MSG_Clear( &cl->netchan.message );
|
||||
MSG_Clear( &cl->datagram );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1294,16 +1294,21 @@ void pfnSetModel( edict_t *e, const char *m )
|
||||
|
||||
if( COM_CheckString( name ))
|
||||
{
|
||||
qboolean notfound = true;
|
||||
|
||||
// check to see if model was properly precached
|
||||
for( i = 1; i < MAX_MODELS && sv.model_precache[i][0]; i++ )
|
||||
{
|
||||
if( !Q_stricmp( sv.model_precache[i], name ))
|
||||
{
|
||||
notfound = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if( i == MAX_MODELS )
|
||||
if( notfound )
|
||||
{
|
||||
Con_Printf( S_ERROR "no precache: %s\n", name );
|
||||
Con_Printf( S_ERROR "Failed to set model %s: was not precached\n", name );
|
||||
return;
|
||||
}
|
||||
}
|
||||
@ -1311,7 +1316,7 @@ void pfnSetModel( edict_t *e, const char *m )
|
||||
if( e == svgame.edicts )
|
||||
{
|
||||
if( sv.state == ss_active )
|
||||
Con_Printf( S_ERROR "world model can't be changed\n" );
|
||||
Con_Printf( S_ERROR "Failed to set model %s: world model cannot be changed\n", name );
|
||||
return;
|
||||
}
|
||||
|
||||
@ -1358,8 +1363,8 @@ int pfnModelIndex( const char *m )
|
||||
return i;
|
||||
}
|
||||
|
||||
Con_Printf( S_ERROR "no precache: %s\n", name );
|
||||
return 0;
|
||||
Con_Printf( S_ERROR "Cannot get index for model %s: not precached\n", name );
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
@ -4410,7 +4415,7 @@ void pfnForceUnmodified( FORCE_TYPE type, float *mins, float *maxs, const char *
|
||||
if( !Q_strcmp( filename, pc->filename ))
|
||||
return;
|
||||
}
|
||||
Con_Printf( S_ERROR "no precache: %s\n", filename );
|
||||
Con_Printf( S_ERROR "Failed to enforce consistency for %s: was not precached\n", filename );
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -274,15 +274,11 @@ model_t *SV_ModelHandle( int modelindex )
|
||||
return sv.models[modelindex];
|
||||
}
|
||||
|
||||
void SV_CreateGenericResources( void )
|
||||
void SV_ReadResourceList( const char *filename )
|
||||
{
|
||||
string filename, token;
|
||||
string token;
|
||||
char *afile, *pfile;
|
||||
|
||||
Q_strncpy( filename, sv.model_precache[1], sizeof( filename ));
|
||||
COM_ReplaceExtension( filename, ".res" );
|
||||
COM_FixSlashes( filename );
|
||||
|
||||
afile = FS_LoadFile( filename, NULL, false );
|
||||
if( !afile ) return;
|
||||
|
||||
@ -304,6 +300,18 @@ void SV_CreateGenericResources( void )
|
||||
Mem_Free( afile );
|
||||
}
|
||||
|
||||
void SV_CreateGenericResources( void )
|
||||
{
|
||||
string filename;
|
||||
|
||||
Q_strncpy( filename, sv.model_precache[1], sizeof( filename ));
|
||||
COM_ReplaceExtension( filename, ".res" );
|
||||
COM_FixSlashes( filename );
|
||||
|
||||
SV_ReadResourceList( filename );
|
||||
SV_ReadResourceList( "reslist.txt" );
|
||||
}
|
||||
|
||||
void SV_CreateResourceList( void )
|
||||
{
|
||||
qboolean ffirstsent = false;
|
||||
|
@ -842,7 +842,7 @@ static SAVERESTOREDATA *LoadSaveData( const char *level )
|
||||
|
||||
if(( pFile = FS_Open( name, "rb", true )) == NULL )
|
||||
{
|
||||
Con_Printf( S_ERROR "couldn't open.\n" );
|
||||
Con_Printf( S_ERROR "Couldn't open save data file %s.\n", name );
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
2
mainui
2
mainui
@ -1 +1 @@
|
||||
Subproject commit 995ed39b9a88a20c650df624c309f7cee3ecfdee
|
||||
Subproject commit 33e6e585e875d91ae44b22d5425f1c38f2413741
|
Loading…
Reference in New Issue
Block a user