Browse Source

Merge branch 'master' into netsplit

pull/2/head
mittorn 6 years ago committed by GitHub
parent
commit
2b1ed2b20a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 7
      engine/client/cl_custom.c
  2. 15
      engine/client/cl_game.c
  3. 2
      engine/client/cl_gameui.c
  4. 29
      engine/client/cl_main.c
  5. 94
      engine/client/cl_parse.c
  6. 2
      engine/client/client.h
  7. 3
      engine/client/gl_decals.c
  8. 6
      engine/client/gl_local.h
  9. 1
      engine/client/gl_rmisc.c
  10. 1696
      engine/client/gl_rsurf.c
  11. 325
      engine/client/gl_studio.c
  12. 5
      engine/client/keys.c
  13. 4
      engine/client/s_vox.c
  14. 48
      engine/client/vid_common.c
  15. 24
      engine/common/common.c
  16. 1
      engine/common/common.h
  17. 5
      engine/common/dedicated.c
  18. 3
      engine/common/host.c
  19. 2
      engine/common/mod_bmodel.c
  20. 8
      engine/common/model.c
  21. 824
      engine/common/net_ws.c
  22. 7
      engine/common/net_ws.h
  23. 2
      engine/common/protocol.h
  24. 320
      engine/platform/sdl/vid_sdl.c
  25. 21
      engine/server/sv_client.c
  26. 8
      engine/server/sv_frame.c
  27. 15
      engine/server/sv_game.c
  28. 20
      engine/server/sv_init.c
  29. 2
      engine/server/sv_save.c
  30. 2
      mainui

7
engine/client/cl_custom.c

@ -70,6 +70,13 @@ qboolean CL_CheckFile( sizebuf_t *msg, resource_t *pResource )
return true; return true;
} }
if( cl.downloadUrl[0] )
{
HTTP_AddDownload( filepath, pResource->nDownloadSize, true );
host.downloadcount++;
return false;
}
MSG_BeginClientCmd( msg, clc_stringcmd ); MSG_BeginClientCmd( msg, clc_stringcmd );
MSG_WriteString( msg, va( "dlfile %s", filepath )); MSG_WriteString( msg, va( "dlfile %s", filepath ));
host.downloadcount++; host.downloadcount++;

15
engine/client/cl_game.c

@ -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 ) void CL_LinkUserMessage( char *pszName, const int svc_num, int iSize )
{ {
int i; 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].number = svc_num;
clgame.msg[i].size = iSize; clgame.msg[i].size = iSize;
CL_ClearUserMessage( pszName, svc_num );
return; 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 )); Q_strncpy( clgame.msg[i].name, pszName, sizeof( clgame.msg[i].name ));
clgame.msg[i].number = svc_num; clgame.msg[i].number = svc_num;
clgame.msg[i].size = iSize; clgame.msg[i].size = iSize;
CL_ClearUserMessage( pszName, svc_num );
} }
void CL_FreeEntity( cl_entity_t *pEdict ) void CL_FreeEntity( cl_entity_t *pEdict )
@ -1215,7 +1226,7 @@ static qboolean CL_LoadHudSprite( const char *szSpriteName, model_t *m_pSprite,
} }
else 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 ); Mod_UnloadSpriteModel( m_pSprite );
return false; return false;
} }
@ -2341,7 +2352,7 @@ int CL_FindModelIndex( const char *m )
if( lasttimewarn < host.realtime ) if( lasttimewarn < host.realtime )
{ {
// tell user about problem (but don't spam console) // 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; lasttimewarn = host.realtime + 1.0f;
} }

2
engine/client/cl_gameui.c

@ -382,7 +382,7 @@ static HIMAGE pfnPIC_Load( const char *szPicName, const byte *image_buf, int ima
if( !szPicName || !*szPicName ) 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; return 0;
} }

29
engine/client/cl_main.c

@ -1351,6 +1351,7 @@ void CL_ClearState( void )
Cvar_SetValue( "scr_download", -1.0f ); Cvar_SetValue( "scr_download", -1.0f );
Cvar_SetValue( "scr_loading", 0.0f ); Cvar_SetValue( "scr_loading", 0.0f );
host.allow_console = host.allow_console_init; 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" ); Con_Printf( "Scanning for servers on the local network area...\n" );
NET_Config( true ); // allow remote NET_Config( true ); // allow remote
cls.legacyservercount = 0;
// send a broadcast packet // send a broadcast packet
adr.type = NA_BROADCAST; adr.type = NA_BROADCAST;
@ -1561,6 +1563,7 @@ void CL_InternetServers_f( void )
Info_SetValueForKey( info, "gamedir", GI->gamefolder, remaining ); Info_SetValueForKey( info, "gamedir", GI->gamefolder, remaining );
Info_SetValueForKey( info, "clver", XASH_VERSION, remaining ); // let master know about client version Info_SetValueForKey( info, "clver", XASH_VERSION, remaining ); // let master know about client version
// Info_SetValueForKey( info, "nat", cl_nat->string, remaining ); // 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_wait = NET_SendToMasters( NS_CLIENT, sizeof( MS_SCAN_REQUEST ) + Q_strlen( info ), fullquery );
cls.internetservers_pending = true; cls.internetservers_pending = true;
@ -1683,6 +1686,7 @@ void CL_ParseStatusMessage( netadr_t from, sizebuf_t *msg )
{ {
static char infostring[MAX_INFO_STRING+8]; static char infostring[MAX_INFO_STRING+8];
char *s = MSG_ReadString( msg ); char *s = MSG_ReadString( msg );
int i;
CL_FixupColorStringsForInfoString( s, infostring ); 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 ); Netchan_OutOfBandPrint( NS_CLIENT, from, "info %i", PROTOCOL_LEGACY_VERSION );
Con_Printf( "^1Server^7: %s, Info: %s\n", NET_AdrToString( from ), infostring ); Con_Printf( "^1Server^7: %s, Info: %s\n", NET_AdrToString( from ), infostring );
if( cls.legacyservercount < MAX_LEGACY_SERVERS )
cls.legacyservers[cls.legacyservercount++] = from;
return; return;
} }
@ -1699,6 +1705,16 @@ void CL_ParseStatusMessage( netadr_t from, sizebuf_t *msg )
return; // unsupported proto 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 // more info about servers
Con_Printf( "^2Server^7: %s, Game: %s\n", NET_AdrToString( from ), Info_ValueForKey( infostring, "gamedir" )); 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 )); 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; pfilename = filename;
@ -2553,7 +2580,7 @@ qboolean CL_PrecacheResources( void )
{ {
if( FBitSet( pRes->ucFlags, RES_WASMISSING )) 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_precache[pRes->nIndex][0] = 0;
cl.sound_index[pRes->nIndex] = 0; cl.sound_index[pRes->nIndex] = 0;
} }

94
engine/client/cl_parse.c

@ -534,7 +534,11 @@ void CL_BatchResourceRequest( qboolean initialize )
if( cls.state != ca_disconnected ) 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 ); CL_RegisterResources( &msg );
} }
@ -1702,16 +1706,23 @@ CL_ParseResLocation
*/ */
void CL_ParseResLocation( sizebuf_t *msg ) 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' ) while( ( data = COM_ParseFile( data, token ) ) )
Q_strncpy( cl.downloadUrl, url, sizeof( cl.downloadUrl )); {
else Q_snprintf( cl.downloadUrl, sizeof( cl.downloadUrl ), "%s/", url ); Con_Reportf( "Adding %s as download location\n", token );
Con_Reportf( "Using %s as primary download location\n", cl.downloadUrl );
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 )); 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 CL_ParseLegacyServerMessage
@ -2845,13 +2911,9 @@ void CL_ParseLegacyServerMessage( sizebuf_t *msg, qboolean normal_message )
if( !Q_strnicmp( s, "disconnect", 10 ) && cls.signon != SIGNONS ) if( !Q_strnicmp( s, "disconnect", 10 ) && cls.signon != SIGNONS )
break; // too early break; // too early
#endif #endif
if( !Q_strcmp(s, "cmd getresourcelist\n") )
Cbuf_AddText("cmd continueloading\n"); Con_Reportf( "Stufftext: %s", s );
else Cbuf_AddText( s );
{
Con_Reportf( "Stufftext: %s", s );
Cbuf_AddText( s );
}
break; break;
case svc_setangle: case svc_setangle:
CL_ParseSetAngle( msg ); 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; //cl.frames[cls.netchan.incoming_sequence & CL_UPDATE_MASK].receivedtime = -2.0;
break; break;
case svc_resourcelist: case svc_resourcelist:
CL_ParseResourceList( msg ); CL_LegacyParseResourceList( msg );
break; break;
case svc_deltamovevars: case svc_deltamovevars:
CL_ParseMovevars( msg ); CL_ParseMovevars( msg );

2
engine/client/client.h

@ -663,6 +663,8 @@ typedef struct
qboolean internetservers_pending; // internetservers is waiting for dns request qboolean internetservers_pending; // internetservers is waiting for dns request
qboolean legacymode; // one-way 48 protocol compatibility qboolean legacymode; // one-way 48 protocol compatibility
netadr_t legacyserver; netadr_t legacyserver;
netadr_t legacyservers[MAX_LEGACY_SERVERS];
int legacyservercount;
int extensions; int extensions;
} client_static_t; } client_static_t;

3
engine/client/gl_decals.c

@ -54,7 +54,7 @@ typedef struct
static float g_DecalClipVerts[MAX_DECALCLIPVERT][VERTEXSIZE]; static float g_DecalClipVerts[MAX_DECALCLIPVERT][VERTEXSIZE];
static float g_DecalClipVerts2[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; static int gDecalCount;
void R_ClearDecals( void ) 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 // alloc clipped poly for decal
R_DecalCreatePoly( decalinfo, pdecal, surf ); R_DecalCreatePoly( decalinfo, pdecal, surf );
R_AddDecalVBO( pdecal, surf );
} }
static void R_DecalCreate( decalinfo_t *decalinfo, msurface_t *surf, float x, float y ) static void R_DecalCreate( decalinfo_t *decalinfo, msurface_t *surf, float x, float y )

6
engine/client/gl_local.h

@ -408,6 +408,9 @@ void GL_RebuildLightmaps( void );
void GL_InitRandomTable( void ); void GL_InitRandomTable( void );
void GL_BuildLightmaps( void ); void GL_BuildLightmaps( void );
void GL_ResetFogColor( void ); void GL_ResetFogColor( void );
void R_GenerateVBO();
void R_ClearVBO();
void R_AddDecalVBO( decal_t *pdecal, msurface_t *surf );
// //
// gl_sprite.c // gl_sprite.c
@ -523,6 +526,7 @@ enum
GL_EXT_GPU_SHADER4, // shaders only GL_EXT_GPU_SHADER4, // shaders only
GL_DEPTH_TEXTURE, GL_DEPTH_TEXTURE,
GL_DEBUG_OUTPUT, GL_DEBUG_OUTPUT,
GL_ARB_VERTEX_BUFFER_OBJECT_EXT,
GL_EXTCOUNT, // must be last GL_EXTCOUNT, // must be last
}; };
@ -678,6 +682,8 @@ extern convar_t *r_lockfrustum;
extern convar_t *r_traceglow; extern convar_t *r_traceglow;
extern convar_t *r_dynamic; extern convar_t *r_dynamic;
extern convar_t *r_lightmap; 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_displayfrequency;
extern convar_t *vid_fullscreen; extern convar_t *vid_fullscreen;

1
engine/client/gl_rmisc.c

@ -199,4 +199,5 @@ void R_NewMap( void )
R_SetupSky( clgame.movevars.skyName ); R_SetupSky( clgame.movevars.skyName );
GL_BuildLightmaps (); GL_BuildLightmaps ();
R_GenerateVBO();
} }

1696
engine/client/gl_rsurf.c

File diff suppressed because it is too large Load Diff

325
engine/client/gl_studio.c

@ -102,13 +102,22 @@ typedef struct
vec4_t lightpos[MAXSTUDIOVERTS][MAX_LOCALLIGHTS]; vec4_t lightpos[MAXSTUDIOVERTS][MAX_LOCALLIGHTS];
vec3_t lightbonepos[MAXSTUDIOBONES][MAX_LOCALLIGHTS]; vec3_t lightbonepos[MAXSTUDIOBONES][MAX_LOCALLIGHTS];
float locallightR2[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_draw_state_t;
// studio-related cvars // studio-related cvars
convar_t *r_studio_sort_textures; static convar_t *r_studio_sort_textures;
convar_t *r_drawviewmodel; static convar_t *r_drawviewmodel;
convar_t *cl_righthand = NULL; 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 r_studio_interface_t *pStudioDraw;
static studio_draw_state_t g_studio; // global studio state 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" ); 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_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_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 ); Matrix3x4_LoadIdentity( g_studio.rotationmatrix );
Cvar_RegisterVariable( &r_glowshellfreq ); 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 finalLight;
vec3_t localLight; 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 ) 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 ); pglTexCoord2f( ptricmds[2] * s, ptricmds[3] * t );
pglVertex3fv( g_studio.verts[ptricmds[0]] ); 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 ) 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( HalfToFloat( ptricmds[2] ), HalfToFloat( ptricmds[3] )); pglTexCoord2f( HalfToFloat( ptricmds[2] ), HalfToFloat( ptricmds[3] ));
pglVertex3fv( g_studio.verts[ptricmds[0]] ); pglVertex3fv( g_studio.verts[ptricmds[0]] );
} }
@ -2009,10 +2059,13 @@ _inline void R_StudioDrawChromeMesh( short *ptricmds, vec3_t *pstudionorms, floa
{ {
if( glowShell ) if( glowShell )
{ {
color24 *clr = &RI.currententity->curstate.rendercolor;
idx = g_studio.normaltable[ptricmds[0]]; idx = g_studio.normaltable[ptricmds[0]];
av = g_studio.verts[ptricmds[0]]; av = g_studio.verts[ptricmds[0]];
lv = g_studio.norms[ptricmds[0]]; lv = g_studio.norms[ptricmds[0]];
VectorMA( av, scale, lv, vert ); 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 ); pglTexCoord2f( g_studio.chrome[idx][0] * s, g_studio.chrome[idx][1] * t );
pglVertex3fv( vert ); pglVertex3fv( vert );
} }
@ -2020,9 +2073,7 @@ _inline void R_StudioDrawChromeMesh( short *ptricmds, vec3_t *pstudionorms, floa
{ {
idx = ptricmds[1]; idx = ptricmds[1];
lv = (float *)g_studio.lightvalues[ptricmds[1]]; lv = (float *)g_studio.lightvalues[ptricmds[1]];
if( g_studio.numlocallights ) R_StudioSetColorBegin( ptricmds, pstudionorms );
R_LightLambert( g_studio.lightpos[ptricmds[0]], pstudionorms[ptricmds[1]], lv );
else pglColor4f( lv[0], lv[1], lv[2], tr.blend );
pglTexCoord2f( g_studio.chrome[idx][0] * s, g_studio.chrome[idx][1] * t ); pglTexCoord2f( g_studio.chrome[idx][0] * s, g_studio.chrome[idx][1] * t );
pglVertex3fv( g_studio.verts[ptricmds[0]] ); 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 R_StudioDrawPoints
@ -2054,6 +2319,9 @@ static void R_StudioDrawPoints( void )
if( !m_pStudioHeader ) return; if( !m_pStudioHeader ) return;
g_studio.numverts = g_studio.numelems = 0;
// safety bounding the skinnum // safety bounding the skinnum
m_skinnum = bound( 0, RI.currententity->curstate.skin, ( m_pStudioHeader->numskinfamilies - 1 )); m_skinnum = bound( 0, RI.currententity->curstate.skin, ( m_pStudioHeader->numskinfamilies - 1 ));
ptexture = (mstudiotexture_t *)((byte *)m_pStudioHeader + m_pStudioHeader->textureindex); 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++ ) for( j = 0; j < m_pSubModel->nummesh; j++ )
{ {
float oldblend = tr.blend; float oldblend = tr.blend;
uint startArrayVerts = g_studio.numverts;
uint startArrayElems = g_studio.numelems;
short *ptricmds; short *ptricmds;
float s, t; float s, t;
@ -2184,11 +2454,24 @@ static void R_StudioDrawPoints( void )
R_StudioSetupSkin( m_pStudioHeader, pskinref[pmesh->skinref] ); R_StudioSetupSkin( m_pStudioHeader, pskinref[pmesh->skinref] );
if( FBitSet( g_nFaceFlags, STUDIO_NF_CHROME )) if( CVAR_TO_BOOL(r_studio_drawelements) )
R_StudioDrawChromeMesh( ptricmds, pstudionorms, s, t, shellscale ); {
else if( FBitSet( g_nFaceFlags, STUDIO_NF_UV_COORDS )) if( FBitSet( g_nFaceFlags, STUDIO_NF_CHROME ))
R_StudioDrawFloatMesh( ptricmds, pstudionorms ); R_StudioBuildArrayChromeMesh( ptricmds, pstudionorms, s, t, shellscale );
else R_StudioDrawNormalMesh( ptricmds, pstudionorms, s, t ); 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 )) if( FBitSet( g_nFaceFlags, STUDIO_NF_MASKED ))
{ {

5
engine/client/keys.c

@ -527,8 +527,11 @@ Key_IsAllowedAutoRepeat
List of keys that allows auto-repeat 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 ) switch( key )
{ {
case K_BACKSPACE: case K_BACKSPACE:

4
engine/client/s_vox.c

@ -545,7 +545,7 @@ void VOX_ParseLineCommands( char *pSentenceData, int sentenceIndex )
length = pNext - pSentenceData; length = pNext - pSentenceData;
if( tempBufferPos + length > sizeof( tempBuffer )) 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; return;
} }
@ -619,7 +619,7 @@ void VOX_ReadSentenceFile( const char *psentenceFileName )
{ {
if( g_numSentences >= MAX_SENTENCES ) 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; break;
} }

48
engine/client/vid_common.c

@ -65,6 +65,8 @@ convar_t *r_traceglow;
convar_t *r_dynamic; convar_t *r_dynamic;
convar_t *r_lightmap; convar_t *r_lightmap;
convar_t *gl_round_down; convar_t *gl_round_down;
convar_t *r_vbo;
convar_t *r_vbo_dlightmode;
convar_t *vid_displayfrequency; convar_t *vid_displayfrequency;
convar_t *vid_fullscreen; convar_t *vid_fullscreen;
@ -546,6 +548,51 @@ static void SetFullscreenModeFromCommandLine( )
#endif #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 R_Init
@ -580,6 +627,7 @@ qboolean R_Init( void )
r_temppool = Mem_AllocPool( "Render Zone" ); r_temppool = Mem_AllocPool( "Render Zone" );
GL_SetDefaults(); GL_SetDefaults();
R_CheckVBO();
R_InitImages(); R_InitImages();
R_SpriteInit(); R_SpriteInit();
R_StudioInit(); R_StudioInit();

24
engine/common/common.c

@ -20,18 +20,18 @@ GNU General Public License for more details.
#include "client.h" #include "client.h"
#include "library.h" #include "library.h"
const char *file_exts[10] = static const char *file_exts[] =
{ {
".cfg", "cfg",
".lst", "lst",
".exe", "exe",
".vbs", "vbs",
".com", "com",
".bat", "bat",
".dll", "dll",
".ini", "ini",
".log", "log",
".sys", "sys",
}; };
#ifdef _DEBUG #ifdef _DEBUG

1
engine/common/common.h

@ -1053,6 +1053,7 @@ qboolean CL_IsBackgroundDemo( void );
qboolean CL_IsBackgroundMap( void ); qboolean CL_IsBackgroundMap( void );
qboolean SV_Initialized( void ); qboolean SV_Initialized( void );
qboolean CL_LoadProgs( const char *name ); qboolean CL_LoadProgs( const char *name );
void CL_ProcessFile( qboolean successfully_received, const char *filename );
int SV_GetSaveComment( const char *savename, char *comment ); int SV_GetSaveComment( const char *savename, char *comment );
qboolean SV_NewGame( const char *mapName, qboolean loadGame ); 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 ); 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 );

5
engine/common/dedicated.c

@ -84,6 +84,11 @@ const char *svc_strings[256] =
"svc_unused63", "svc_unused63",
}; };
void CL_ProcessFile( qboolean successfully_received, const char *filename )
{
}
int CL_Active( void ) int CL_Active( void )
{ {
return false; return false;

3
engine/common/host.c

@ -542,6 +542,7 @@ void Host_Frame( float time )
Host_GetCommands (); // dedicated in Host_GetCommands (); // dedicated in
Host_ServerFrame (); // server frame Host_ServerFrame (); // server frame
Host_ClientFrame (); // client frame Host_ClientFrame (); // client frame
HTTP_Run(); // both server and client
host.framecount++; host.framecount++;
} }
@ -978,6 +979,7 @@ int EXPORT Host_Main( int argc, char **argv, const char *progname, int bChangeGa
SV_Init(); SV_Init();
CL_Init(); CL_Init();
HTTP_Init();
ID_Init(); ID_Init();
if( Host_IsDedicated() ) if( Host_IsDedicated() )
@ -1060,6 +1062,7 @@ void EXPORT Host_Shutdown( void )
Mod_Shutdown(); Mod_Shutdown();
NET_Shutdown(); NET_Shutdown();
HTTP_Shutdown();
Host_FreeCommon(); Host_FreeCommon();
#ifdef _WIN32 #ifdef _WIN32
Wcon_DestroyConsole(); Wcon_DestroyConsole();

2
engine/common/mod_bmodel.c

@ -309,7 +309,7 @@ static void Mod_LoadLump( const byte *in, mlumpinfo_t *info, mlumpstat_t *stat,
if( l->filelen % real_entrysize ) if( l->filelen % real_entrysize )
{ {
if( !FBitSet( flags, LUMP_SILENT )) 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++; loadstat.numerrors++;
return; return;
} }

8
engine/common/model.c

@ -288,8 +288,8 @@ model_t *Mod_LoadModel( model_t *mod, qboolean crash )
{ {
memset( mod, 0, sizeof( model_t )); memset( mod, 0, sizeof( model_t ));
if( crash ) Host_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 "%s couldn't load\n", tempname ); else Con_Printf( S_ERROR "Could not load model %s from disk\n", tempname );
return NULL; return NULL;
} }
@ -331,8 +331,8 @@ model_t *Mod_LoadModel( model_t *mod, qboolean crash )
Mod_FreeModel( mod ); Mod_FreeModel( mod );
Mem_Free( buf ); Mem_Free( buf );
if( crash ) Host_Error( "%s couldn't load\n", tempname ); if( crash ) Host_Error( "Could not load model %s\n", tempname );
else Con_Printf( S_ERROR "%s couldn't load\n", tempname ); else Con_Printf( S_ERROR "Could not load model %s\n", tempname );
return NULL; return NULL;
} }

824
engine/common/net_ws.c

@ -1877,3 +1877,827 @@ void NET_Shutdown( void )
#endif #endif
net.initialized = false; 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;
}

7
engine/common/net_ws.h

@ -69,4 +69,11 @@ qboolean CL_LegacyMode( void );
int CL_GetSplitSize( void ); int CL_GetSplitSize( void );
#endif #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 #endif//NET_WS_H

2
engine/common/protocol.h

@ -262,4 +262,6 @@ extern const char *clc_strings[clc_lastmsg+1];
#define MAX_LEGACY_ENTITY_BITS 12 #define MAX_LEGACY_ENTITY_BITS 12
#define MAX_LEGACY_WEAPON_BITS 5 #define MAX_LEGACY_WEAPON_BITS 5
#define MAX_LEGACY_MODEL_BITS 11 #define MAX_LEGACY_MODEL_BITS 11
#define MAX_LEGACY_SERVERS 32
#endif//NET_PROTOCOL_H #endif//NET_PROTOCOL_H

320
engine/platform/sdl/vid_sdl.c

@ -25,176 +25,189 @@ GNU General Public License for more details.
static vidmode_t *vidmodes = NULL; static vidmode_t *vidmodes = NULL;
static int num_vidmodes = 0; static int num_vidmodes = 0;
static int context_flags = 0; static int context_flags = 0;
#define GL_CALL( x ) #x, (void**)&p##x
static dllfunc_t opengl_110funcs[] = static dllfunc_t opengl_110funcs[] =
{ {
{ "glClearColor" , (void **)&pglClearColor }, { GL_CALL( glClearColor ) },
{ "glClear" , (void **)&pglClear }, { GL_CALL( glClear ) },
{ "glAlphaFunc" , (void **)&pglAlphaFunc }, { GL_CALL( glAlphaFunc ) },
{ "glBlendFunc" , (void **)&pglBlendFunc }, { GL_CALL( glBlendFunc ) },
{ "glCullFace" , (void **)&pglCullFace }, { GL_CALL( glCullFace ) },
{ "glDrawBuffer" , (void **)&pglDrawBuffer }, { GL_CALL( glDrawBuffer ) },
{ "glReadBuffer" , (void **)&pglReadBuffer }, { GL_CALL( glReadBuffer ) },
{ "glAccum" , (void **)&pglAccum }, { GL_CALL( glAccum ) },
{ "glEnable" , (void **)&pglEnable }, { GL_CALL( glEnable ) },
{ "glDisable" , (void **)&pglDisable }, { GL_CALL( glDisable ) },
{ "glEnableClientState" , (void **)&pglEnableClientState }, { GL_CALL( glEnableClientState ) },
{ "glDisableClientState" , (void **)&pglDisableClientState }, { GL_CALL( glDisableClientState ) },
{ "glGetBooleanv" , (void **)&pglGetBooleanv }, { GL_CALL( glGetBooleanv ) },
{ "glGetDoublev" , (void **)&pglGetDoublev }, { GL_CALL( glGetDoublev ) },
{ "glGetFloatv" , (void **)&pglGetFloatv }, { GL_CALL( glGetFloatv ) },
{ "glGetIntegerv" , (void **)&pglGetIntegerv }, { GL_CALL( glGetIntegerv ) },
{ "glGetError" , (void **)&pglGetError }, { GL_CALL( glGetError ) },
{ "glGetString" , (void **)&pglGetString }, { GL_CALL( glGetString ) },
{ "glFinish" , (void **)&pglFinish }, { GL_CALL( glFinish ) },
{ "glFlush" , (void **)&pglFlush }, { GL_CALL( glFlush ) },
{ "glClearDepth" , (void **)&pglClearDepth }, { GL_CALL( glClearDepth ) },
{ "glDepthFunc" , (void **)&pglDepthFunc }, { GL_CALL( glDepthFunc ) },
{ "glDepthMask" , (void **)&pglDepthMask }, { GL_CALL( glDepthMask ) },
{ "glDepthRange" , (void **)&pglDepthRange }, { GL_CALL( glDepthRange ) },
{ "glFrontFace" , (void **)&pglFrontFace }, { GL_CALL( glFrontFace ) },
{ "glDrawElements" , (void **)&pglDrawElements }, { GL_CALL( glDrawElements ) },
{ "glDrawArrays" , (void **)&pglDrawArrays }, { GL_CALL( glDrawArrays ) },
{ "glColorMask" , (void **)&pglColorMask }, { GL_CALL( glColorMask ) },
{ "glIndexPointer" , (void **)&pglIndexPointer }, { GL_CALL( glIndexPointer ) },
{ "glVertexPointer" , (void **)&pglVertexPointer }, { GL_CALL( glVertexPointer ) },
{ "glNormalPointer" , (void **)&pglNormalPointer }, { GL_CALL( glNormalPointer ) },
{ "glColorPointer" , (void **)&pglColorPointer }, { GL_CALL( glColorPointer ) },
{ "glTexCoordPointer" , (void **)&pglTexCoordPointer }, { GL_CALL( glTexCoordPointer ) },
{ "glArrayElement" , (void **)&pglArrayElement }, { GL_CALL( glArrayElement ) },
{ "glColor3f" , (void **)&pglColor3f }, { GL_CALL( glColor3f ) },
{ "glColor3fv" , (void **)&pglColor3fv }, { GL_CALL( glColor3fv ) },
{ "glColor4f" , (void **)&pglColor4f }, { GL_CALL( glColor4f ) },
{ "glColor4fv" , (void **)&pglColor4fv }, { GL_CALL( glColor4fv ) },
{ "glColor3ub" , (void **)&pglColor3ub }, { GL_CALL( glColor3ub ) },
{ "glColor4ub" , (void **)&pglColor4ub }, { GL_CALL( glColor4ub ) },
{ "glColor4ubv" , (void **)&pglColor4ubv }, { GL_CALL( glColor4ubv ) },
{ "glTexCoord1f" , (void **)&pglTexCoord1f }, { GL_CALL( glTexCoord1f ) },
{ "glTexCoord2f" , (void **)&pglTexCoord2f }, { GL_CALL( glTexCoord2f ) },
{ "glTexCoord3f" , (void **)&pglTexCoord3f }, { GL_CALL( glTexCoord3f ) },
{ "glTexCoord4f" , (void **)&pglTexCoord4f }, { GL_CALL( glTexCoord4f ) },
{ "glTexCoord1fv" , (void **)&pglTexCoord1fv }, { GL_CALL( glTexCoord1fv ) },
{ "glTexCoord2fv" , (void **)&pglTexCoord2fv }, { GL_CALL( glTexCoord2fv ) },
{ "glTexCoord3fv" , (void **)&pglTexCoord3fv }, { GL_CALL( glTexCoord3fv ) },
{ "glTexCoord4fv" , (void **)&pglTexCoord4fv }, { GL_CALL( glTexCoord4fv ) },
{ "glTexGenf" , (void **)&pglTexGenf }, { GL_CALL( glTexGenf ) },
{ "glTexGenfv" , (void **)&pglTexGenfv }, { GL_CALL( glTexGenfv ) },
{ "glTexGeni" , (void **)&pglTexGeni }, { GL_CALL( glTexGeni ) },
{ "glVertex2f" , (void **)&pglVertex2f }, { GL_CALL( glVertex2f ) },
{ "glVertex3f" , (void **)&pglVertex3f }, { GL_CALL( glVertex3f ) },
{ "glVertex3fv" , (void **)&pglVertex3fv }, { GL_CALL( glVertex3fv ) },
{ "glNormal3f" , (void **)&pglNormal3f }, { GL_CALL( glNormal3f ) },
{ "glNormal3fv" , (void **)&pglNormal3fv }, { GL_CALL( glNormal3fv ) },
{ "glBegin" , (void **)&pglBegin }, { GL_CALL( glBegin ) },
{ "glEnd" , (void **)&pglEnd }, { GL_CALL( glEnd ) },
{ "glLineWidth" , (void**)&pglLineWidth }, { GL_CALL( glLineWidth ) },
{ "glPointSize" , (void**)&pglPointSize }, { GL_CALL( glPointSize ) },
{ "glMatrixMode" , (void **)&pglMatrixMode }, { GL_CALL( glMatrixMode ) },
{ "glOrtho" , (void **)&pglOrtho }, { GL_CALL( glOrtho ) },
{ "glRasterPos2f" , (void **) &pglRasterPos2f }, { GL_CALL( glRasterPos2f ) },
{ "glFrustum" , (void **)&pglFrustum }, { GL_CALL( glFrustum ) },
{ "glViewport" , (void **)&pglViewport }, { GL_CALL( glViewport ) },
{ "glPushMatrix" , (void **)&pglPushMatrix }, { GL_CALL( glPushMatrix ) },
{ "glPopMatrix" , (void **)&pglPopMatrix }, { GL_CALL( glPopMatrix ) },
{ "glPushAttrib" , (void **)&pglPushAttrib }, { GL_CALL( glPushAttrib ) },
{ "glPopAttrib" , (void **)&pglPopAttrib }, { GL_CALL( glPopAttrib ) },
{ "glLoadIdentity" , (void **)&pglLoadIdentity }, { GL_CALL( glLoadIdentity ) },
{ "glLoadMatrixd" , (void **)&pglLoadMatrixd }, { GL_CALL( glLoadMatrixd ) },
{ "glLoadMatrixf" , (void **)&pglLoadMatrixf }, { GL_CALL( glLoadMatrixf ) },
{ "glMultMatrixd" , (void **)&pglMultMatrixd }, { GL_CALL( glMultMatrixd ) },
{ "glMultMatrixf" , (void **)&pglMultMatrixf }, { GL_CALL( glMultMatrixf ) },
{ "glRotated" , (void **)&pglRotated }, { GL_CALL( glRotated ) },
{ "glRotatef" , (void **)&pglRotatef }, { GL_CALL( glRotatef ) },
{ "glScaled" , (void **)&pglScaled }, { GL_CALL( glScaled ) },
{ "glScalef" , (void **)&pglScalef }, { GL_CALL( glScalef ) },
{ "glTranslated" , (void **)&pglTranslated }, { GL_CALL( glTranslated ) },
{ "glTranslatef" , (void **)&pglTranslatef }, { GL_CALL( glTranslatef ) },
{ "glReadPixels" , (void **)&pglReadPixels }, { GL_CALL( glReadPixels ) },
{ "glDrawPixels" , (void **)&pglDrawPixels }, { GL_CALL( glDrawPixels ) },
{ "glStencilFunc" , (void **)&pglStencilFunc }, { GL_CALL( glStencilFunc ) },
{ "glStencilMask" , (void **)&pglStencilMask }, { GL_CALL( glStencilMask ) },
{ "glStencilOp" , (void **)&pglStencilOp }, { GL_CALL( glStencilOp ) },
{ "glClearStencil" , (void **)&pglClearStencil }, { GL_CALL( glClearStencil ) },
{ "glIsEnabled" , (void **)&pglIsEnabled }, { GL_CALL( glIsEnabled ) },
{ "glIsList" , (void **)&pglIsList }, { GL_CALL( glIsList ) },
{ "glIsTexture" , (void **)&pglIsTexture }, { GL_CALL( glIsTexture ) },
{ "glTexEnvf" , (void **)&pglTexEnvf }, { GL_CALL( glTexEnvf ) },
{ "glTexEnvfv" , (void **)&pglTexEnvfv }, { GL_CALL( glTexEnvfv ) },
{ "glTexEnvi" , (void **)&pglTexEnvi }, { GL_CALL( glTexEnvi ) },
{ "glTexParameterf" , (void **)&pglTexParameterf }, { GL_CALL( glTexParameterf ) },
{ "glTexParameterfv" , (void **)&pglTexParameterfv }, { GL_CALL( glTexParameterfv ) },
{ "glTexParameteri" , (void **)&pglTexParameteri }, { GL_CALL( glTexParameteri ) },
{ "glHint" , (void **)&pglHint }, { GL_CALL( glHint ) },
{ "glPixelStoref" , (void **)&pglPixelStoref }, { GL_CALL( glPixelStoref ) },
{ "glPixelStorei" , (void **)&pglPixelStorei }, { GL_CALL( glPixelStorei ) },
{ "glGenTextures" , (void **)&pglGenTextures }, { GL_CALL( glGenTextures ) },
{ "glDeleteTextures" , (void **)&pglDeleteTextures }, { GL_CALL( glDeleteTextures ) },
{ "glBindTexture" , (void **)&pglBindTexture }, { GL_CALL( glBindTexture ) },
{ "glTexImage1D" , (void **)&pglTexImage1D }, { GL_CALL( glTexImage1D ) },
{ "glTexImage2D" , (void **)&pglTexImage2D }, { GL_CALL( glTexImage2D ) },
{ "glTexSubImage1D" , (void **)&pglTexSubImage1D }, { GL_CALL( glTexSubImage1D ) },
{ "glTexSubImage2D" , (void **)&pglTexSubImage2D }, { GL_CALL( glTexSubImage2D ) },
{ "glCopyTexImage1D" , (void **)&pglCopyTexImage1D }, { GL_CALL( glCopyTexImage1D ) },
{ "glCopyTexImage2D" , (void **)&pglCopyTexImage2D }, { GL_CALL( glCopyTexImage2D ) },
{ "glCopyTexSubImage1D" , (void **)&pglCopyTexSubImage1D }, { GL_CALL( glCopyTexSubImage1D ) },
{ "glCopyTexSubImage2D" , (void **)&pglCopyTexSubImage2D }, { GL_CALL( glCopyTexSubImage2D ) },
{ "glScissor" , (void **)&pglScissor }, { GL_CALL( glScissor ) },
{ "glGetTexImage" , (void **)&pglGetTexImage }, { GL_CALL( glGetTexImage ) },
{ "glGetTexEnviv" , (void **)&pglGetTexEnviv }, { GL_CALL( glGetTexEnviv ) },
{ "glPolygonOffset" , (void **)&pglPolygonOffset }, { GL_CALL( glPolygonOffset ) },
{ "glPolygonMode" , (void **)&pglPolygonMode }, { GL_CALL( glPolygonMode ) },
{ "glPolygonStipple" , (void **)&pglPolygonStipple }, { GL_CALL( glPolygonStipple ) },
{ "glClipPlane" , (void **)&pglClipPlane }, { GL_CALL( glClipPlane ) },
{ "glGetClipPlane" , (void **)&pglGetClipPlane }, { GL_CALL( glGetClipPlane ) },
{ "glShadeModel" , (void **)&pglShadeModel }, { GL_CALL( glShadeModel ) },
{ "glGetTexLevelParameteriv" , (void **)&pglGetTexLevelParameteriv }, { GL_CALL( glGetTexLevelParameteriv ) },
{ "glGetTexLevelParameterfv" , (void **)&pglGetTexLevelParameterfv }, { GL_CALL( glGetTexLevelParameterfv ) },
{ "glFogfv" , (void **)&pglFogfv }, { GL_CALL( glFogfv ) },
{ "glFogf" , (void **)&pglFogf }, { GL_CALL( glFogf ) },
{ "glFogi" , (void **)&pglFogi }, { GL_CALL( glFogi ) },
{ NULL , NULL } { NULL , NULL }
}; };
static dllfunc_t debugoutputfuncs[] = static dllfunc_t debugoutputfuncs[] =
{ {
{ "glDebugMessageControlARB" , (void **)&pglDebugMessageControlARB }, { GL_CALL( glDebugMessageControlARB ) },
{ "glDebugMessageInsertARB" , (void **)&pglDebugMessageInsertARB }, { GL_CALL( glDebugMessageInsertARB ) },
{ "glDebugMessageCallbackARB" , (void **)&pglDebugMessageCallbackARB }, { GL_CALL( glDebugMessageCallbackARB ) },
{ "glGetDebugMessageLogARB" , (void **)&pglGetDebugMessageLogARB }, { GL_CALL( glGetDebugMessageLogARB ) },
{ NULL , NULL } { NULL , NULL }
}; };
static dllfunc_t multitexturefuncs[] = static dllfunc_t multitexturefuncs[] =
{ {
{ "glMultiTexCoord1fARB" , (void **)&pglMultiTexCoord1f }, { GL_CALL( glMultiTexCoord1f ) },
{ "glMultiTexCoord2fARB" , (void **)&pglMultiTexCoord2f }, { GL_CALL( glMultiTexCoord2f ) },
{ "glMultiTexCoord3fARB" , (void **)&pglMultiTexCoord3f }, { GL_CALL( glMultiTexCoord3f ) },
{ "glMultiTexCoord4fARB" , (void **)&pglMultiTexCoord4f }, { GL_CALL( glMultiTexCoord4f ) },
{ "glActiveTextureARB" , (void **)&pglActiveTexture }, { GL_CALL( glActiveTexture ) },
{ "glActiveTextureARB" , (void **)&pglActiveTextureARB }, { GL_CALL( glActiveTextureARB ) },
{ "glClientActiveTextureARB" , (void **)&pglClientActiveTexture }, { GL_CALL( glClientActiveTexture ) },
{ "glClientActiveTextureARB" , (void **)&pglClientActiveTextureARB }, { GL_CALL( glClientActiveTextureARB ) },
{ NULL , NULL } { NULL , NULL }
}; };
static dllfunc_t texture3dextfuncs[] = static dllfunc_t texture3dextfuncs[] =
{ {
{ "glTexImage3DEXT" , (void **)&pglTexImage3D }, { GL_CALL( glTexImage3D ) },
{ "glTexSubImage3DEXT" , (void **)&pglTexSubImage3D }, { GL_CALL( glTexSubImage3D ) },
{ "glCopyTexSubImage3DEXT" , (void **)&pglCopyTexSubImage3D }, { GL_CALL( glCopyTexSubImage3D ) },
{ NULL , NULL } { NULL , NULL }
}; };
static dllfunc_t texturecompressionfuncs[] = static dllfunc_t texturecompressionfuncs[] =
{ {
{ "glCompressedTexImage3DARB" , (void **)&pglCompressedTexImage3DARB }, { GL_CALL( glCompressedTexImage3DARB ) },
{ "glCompressedTexImage2DARB" , (void **)&pglCompressedTexImage2DARB }, { GL_CALL( glCompressedTexImage2DARB ) },
{ "glCompressedTexImage1DARB" , (void **)&pglCompressedTexImage1DARB }, { GL_CALL( glCompressedTexImage1DARB ) },
{ "glCompressedTexSubImage3DARB" , (void **)&pglCompressedTexSubImage3DARB }, { GL_CALL( glCompressedTexSubImage3DARB ) },
{ "glCompressedTexSubImage2DARB" , (void **)&pglCompressedTexSubImage2DARB }, { GL_CALL( glCompressedTexSubImage2DARB ) },
{ "glCompressedTexSubImage1DARB" , (void **)&pglCompressedTexSubImage1DARB }, { GL_CALL( glCompressedTexSubImage1DARB ) },
{ "glGetCompressedTexImageARB" , (void **)&pglGetCompressedTexImage }, { GL_CALL( glGetCompressedTexImage ) },
{ NULL , NULL } { 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 ); static void GL_SetupAttributes( void );
@ -884,7 +897,6 @@ qboolean R_Init_Video( void )
return true; return true;
} }
#ifdef XASH_GLES #ifdef XASH_GLES
void GL_InitExtensionsGLES( void ) 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_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_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_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 // rectangle textures support
GL_CheckExtension( "GL_ARB_texture_rectangle", NULL, "gl_texture_rectangle", GL_TEXTURE_2D_RECT_EXT ); GL_CheckExtension( "GL_ARB_texture_rectangle", NULL, "gl_texture_rectangle", GL_TEXTURE_2D_RECT_EXT );

21
engine/server/sv_client.c

@ -375,6 +375,7 @@ void SV_ConnectClient( netadr_t from )
sv.current_client = newcl; sv.current_client = newcl;
newcl->edict = EDICT_NUM( (newcl - svs.clients) + 1 ); newcl->edict = EDICT_NUM( (newcl - svs.clients) + 1 );
newcl->challenge = challenge; // save challenge for checksumming 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->frames = (client_frame_t *)Z_Calloc( sizeof( client_frame_t ) * SV_UPDATE_BACKUP );
newcl->userid = g_userid++; // create unique userid newcl->userid = g_userid++; // create unique userid
newcl->state = cs_connected; newcl->state = cs_connected;
@ -1875,6 +1876,26 @@ static qboolean SV_DownloadFile_f( sv_client_t *cl )
{ {
if( sv_send_resources.value ) 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 // also check the model textures
if( !Q_stricmp( COM_FileExtension( name ), "mdl" )) if( !Q_stricmp( COM_FileExtension( name ), "mdl" ))
{ {

8
engine/server/sv_frame.c

@ -998,9 +998,15 @@ void SV_InactivateClients( void )
if( !cl->state || !cl->edict ) if( !cl->state || !cl->edict )
continue; continue;
if( !cl->edict || FBitSet( cl->edict->v.flags, FL_FAKECLIENT )) if( !cl->edict )
continue; continue;
if( FBitSet( cl->edict->v.flags, FL_FAKECLIENT ))
{
SV_DropClient( cl, false );
continue;
}
if( cl->state > cs_connected ) if( cl->state > cs_connected )
cl->state = cs_connected; cl->state = cs_connected;

15
engine/server/sv_game.c

@ -1294,16 +1294,21 @@ void pfnSetModel( edict_t *e, const char *m )
if( COM_CheckString( name )) if( COM_CheckString( name ))
{ {
qboolean notfound = true;
// check to see if model was properly precached // check to see if model was properly precached
for( i = 1; i < MAX_MODELS && sv.model_precache[i][0]; i++ ) for( i = 1; i < MAX_MODELS && sv.model_precache[i][0]; i++ )
{ {
if( !Q_stricmp( sv.model_precache[i], name )) if( !Q_stricmp( sv.model_precache[i], name ))
{
notfound = false;
break; 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; return;
} }
} }
@ -1311,7 +1316,7 @@ void pfnSetModel( edict_t *e, const char *m )
if( e == svgame.edicts ) if( e == svgame.edicts )
{ {
if( sv.state == ss_active ) 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; return;
} }
@ -1358,7 +1363,7 @@ int pfnModelIndex( const char *m )
return i; return i;
} }
Con_Printf( S_ERROR "no precache: %s\n", name ); Con_Printf( S_ERROR "Cannot get index for model %s: not precached\n", name );
return 0; return 0;
} }
@ -4410,7 +4415,7 @@ void pfnForceUnmodified( FORCE_TYPE type, float *mins, float *maxs, const char *
if( !Q_strcmp( filename, pc->filename )) if( !Q_strcmp( filename, pc->filename ))
return; 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 );
} }
} }

20
engine/server/sv_init.c

@ -274,15 +274,11 @@ model_t *SV_ModelHandle( int modelindex )
return sv.models[modelindex]; return sv.models[modelindex];
} }
void SV_CreateGenericResources( void ) void SV_ReadResourceList( const char *filename )
{ {
string filename, token; string token;
char *afile, *pfile; 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 ); afile = FS_LoadFile( filename, NULL, false );
if( !afile ) return; if( !afile ) return;
@ -304,6 +300,18 @@ void SV_CreateGenericResources( void )
Mem_Free( afile ); 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 ) void SV_CreateResourceList( void )
{ {
qboolean ffirstsent = false; qboolean ffirstsent = false;

2
engine/server/sv_save.c

@ -842,7 +842,7 @@ static SAVERESTOREDATA *LoadSaveData( const char *level )
if(( pFile = FS_Open( name, "rb", true )) == NULL ) 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; return NULL;
} }

2
mainui

@ -1 +1 @@
Subproject commit 995ed39b9a88a20c650df624c309f7cee3ecfdee Subproject commit 33e6e585e875d91ae44b22d5425f1c38f2413741
Loading…
Cancel
Save