From e9889b21ca41cfee56199ac55a27c5dafac9c45a Mon Sep 17 00:00:00 2001 From: g-cont Date: Sun, 14 Aug 2016 00:00:00 +0300 Subject: [PATCH] 14 Aug 2016 --- engine/client/cl_events.c | 18 +- engine/client/cl_frame.c | 78 +++++-- engine/client/cl_game.c | 106 +-------- engine/client/cl_main.c | 69 +++--- engine/client/cl_pmove.c | 353 ++++++++++++++++++++++++++--- engine/client/cl_tent.c | 6 +- engine/client/cl_view.c | 23 +- engine/client/client.h | 40 +++- engine/client/gl_decals.c | 6 +- engine/client/gl_export.h | 1 + engine/client/gl_local.h | 2 + engine/client/gl_rmain.c | 10 +- engine/client/gl_rpart.c | 58 ++++- engine/client/gl_studio.c | 6 - engine/client/gl_vidnt.c | 31 ++- engine/client/s_dsp.c | 2 +- engine/client/s_main.c | 14 +- engine/client/vgui/vgui_draw.h | 2 +- engine/client/vgui/vgui_input.cpp | 2 +- engine/common/build.c | 2 +- engine/common/con_utils.c | 70 +++++- engine/common/console.c | 6 - engine/common/imagelib/img_utils.c | 6 +- engine/common/input.c | 2 +- engine/common/input.h | 2 +- engine/common/netchan.h | 2 +- engine/common/soundlib/snd_wav.c | 3 +- engine/common/zone.c | 11 +- engine/server/server.h | 1 + engine/server/sv_client.c | 141 +++++++----- engine/server/sv_frame.c | 15 +- engine/server/sv_game.c | 19 +- engine/server/sv_init.c | 5 +- engine/server/sv_main.c | 60 ++++- engine/server/sv_world.c | 2 +- game_launch/game.cpp | 5 + 36 files changed, 846 insertions(+), 333 deletions(-) diff --git a/engine/client/cl_events.c b/engine/client/cl_events.c index af697990..92f4202f 100644 --- a/engine/client/cl_events.c +++ b/engine/client/cl_events.c @@ -476,12 +476,22 @@ void CL_PlaybackEvent( int flags, const edict_t *pInvoker, word eventindex, floa else VectorCopy( angles, args.angles ); if( !origin || VectorIsNull( origin )) - VectorCopy( cl.frame.client.origin, args.origin ); + { + if( CL_IsPredicted( )) VectorCopy( cl.predicted.origin, args.origin ); + else VectorCopy( cl.frame.client.origin, args.origin ); + } else VectorCopy( origin, args.origin ); - VectorCopy( cl.frame.client.velocity, args.velocity ); - args.ducking = (cl.frame.playerstate[cl.playernum].usehull == 1); -// args.ducking = cl.frame.client.bInDuck; + if( CL_IsPredicted( )) + { + VectorCopy( cl.predicted.velocity, args.velocity ); + args.ducking = (cl.predicted.usehull == 1); + } + else + { + VectorCopy( cl.frame.client.velocity, args.velocity ); + args.ducking = cl.frame.client.bInDuck; + } args.fparam1 = fparam1; args.fparam2 = fparam2; diff --git a/engine/client/cl_frame.c b/engine/client/cl_frame.c index 2d916f62..7b72854d 100644 --- a/engine/client/cl_frame.c +++ b/engine/client/cl_frame.c @@ -123,7 +123,7 @@ qboolean CL_FindInterpolationUpdates( cl_entity_t *ent, float targettime, positi int CL_InterpolateModel( cl_entity_t *e ) { - position_history_t *ph0, *ph1; + position_history_t *ph0 = NULL, *ph1 = NULL; vec3_t origin, angles, delta; float t, t1, t2, frac; int i; @@ -131,13 +131,15 @@ int CL_InterpolateModel( cl_entity_t *e ) VectorCopy( e->curstate.origin, e->origin ); VectorCopy( e->curstate.angles, e->angles ); - if( e->model == NULL || cl.maxclients <= 1 ) + if( !e->model || ( e->model->name[0] == '*' && !cl_bmodelinterp->integer ) || RP_LOCALCLIENT( e ) || cl.maxclients <= 1 ) + return 1; + + if( cl.predicted.moving && cl.predicted.onground == e->index ) return 1; t = cl.time - cl_interp->value; - if( !CL_FindInterpolationUpdates( e, t, &ph0, &ph1, NULL )) - return 0; + CL_FindInterpolationUpdates( e, t, &ph0, &ph1, NULL ); if( ph0 == NULL || ph1 == NULL ) return 0; @@ -193,6 +195,36 @@ int CL_InterpolateModel( cl_entity_t *e ) return 1; } +void CL_InterpolateMovingEntity( cl_entity_t *ent ) +{ + float d, f = 0.0f; + int i; + + // don't do it if the goalstarttime hasn't updated in a while. + // NOTE: Because we need to interpolate multiplayer characters, the interpolation time limit + // was increased to 1.0 s., which is 2x the max lag we are accounting for. + if(( cl.time < ent->curstate.animtime + 1.0f ) && ( ent->curstate.animtime != ent->latched.prevanimtime )) + f = ( cl.time - ent->curstate.animtime ) / ( ent->curstate.animtime - ent->latched.prevanimtime ); + + f = f - 1.0f; + + ent->origin[0] += ( ent->origin[0] - ent->latched.prevorigin[0] ) * f; + ent->origin[1] += ( ent->origin[1] - ent->latched.prevorigin[1] ) * f; + ent->origin[2] += ( ent->origin[2] - ent->latched.prevorigin[2] ) * f; + + for( i = 0; i < 3; i++ ) + { + float ang1, ang2; + + ang1 = ent->angles[i]; + ang2 = ent->latched.prevangles[i]; + d = ang1 - ang2; + if( d > 180.0f ) d -= 360.0f; + else if( d < -180.0f ) d += 360.0f; + ent->angles[i] += d * f; + } +} + void CL_UpdateEntityFields( cl_entity_t *ent ) { // parametric rockets code @@ -218,7 +250,11 @@ void CL_UpdateEntityFields( cl_entity_t *ent ) ent->angles[PITCH] = -ent->angles[PITCH] / 3.0f; // make me lerp - if( ent->model && ent->model->type == mod_brush && ent->curstate.animtime != 0.0f ) + if( ent->index == cl.predicted.onground && cl.predicted.moving ) + { + CL_InterpolateMovingEntity( ent ); + } + else if( ent->model && ent->model->type == mod_brush && ent->curstate.animtime != 0.0f) { float d, f = 0.0f; int i; @@ -324,7 +360,7 @@ void CL_UpdateEntityFields( cl_entity_t *ent ) } } - // move code from StudioSetupTransform here + // moved code from StudioSetupTransform here if( host.features & ENGINE_COMPUTE_STUDIO_LERP ) { ent->origin[0] += ( ent->curstate.origin[0] - ent->latched.prevorigin[0] ) * f; @@ -516,10 +552,6 @@ void CL_WeaponAnim( int iAnim, int body ) cl.weaponstarttime = 0; cl.weaponsequence = iAnim; - if( Host_IsLocalClient() || cl_predict->value || !cl_lw->value ) - view->curstate.modelindex = cl.frame.client.viewmodel; - else view->curstate.modelindex = cl.predicted_viewmodel; - // anim is changed. update latchedvars if( iAnim != view->curstate.sequence ) { @@ -749,6 +781,8 @@ void CL_DeltaEntity( sizebuf_t *msg, frame_t *frame, int newnum, entity_state_t /* ================= CL_FlushEntityPacket + +Read and ignore whole entity packet. ================= */ void CL_FlushEntityPacket( sizebuf_t *msg ) @@ -756,7 +790,6 @@ void CL_FlushEntityPacket( sizebuf_t *msg ) int newnum; entity_state_t from, to; - MsgDev( D_INFO, "FlushEntityPacket()\n" ); Q_memset( &from, 0, sizeof( from )); cl.frames[cl.parsecountmod].valid = false; @@ -1003,23 +1036,34 @@ CL_SetIdealPitch void CL_SetIdealPitch( void ) { float angleval, sinval, cosval; - vec3_t top, bottom; - float z[MAX_FORWARD]; + float z[MAX_FORWARD], view_z; + vec3_t top, bottom, origin; int i, j; int step, dir, steps; pmtrace_t tr; if( !( cl.frame.client.flags & FL_ONGROUND )) return; - + + if( CL_IsPredicted( )) + { + VectorCopy( cl.predicted.origin, origin ); + view_z = cl.predicted.viewofs[2]; + } + else + { + VectorCopy( cl.frame.client.origin, origin ); + view_z = cl.frame.client.view_ofs[2]; + } + angleval = cl.frame.playerstate[cl.playernum].angles[YAW] * M_PI2 / 360.0f; SinCos( angleval, &sinval, &cosval ); for( i = 0; i < MAX_FORWARD; i++ ) { - top[0] = cl.frame.client.origin[0] + cosval * (i + 3.0f) * 12.0f; - top[1] = cl.frame.client.origin[1] + sinval * (i + 3.0f) * 12.0f; - top[2] = cl.frame.client.origin[2] + cl.frame.client.view_ofs[2]; + top[0] = origin[0] + cosval * (i + 3.0f) * 12.0f; + top[1] = origin[1] + sinval * (i + 3.0f) * 12.0f; + top[2] = origin[2] + view_z; bottom[0] = top[0]; bottom[1] = top[1]; diff --git a/engine/client/cl_game.c b/engine/client/cl_game.c index f2dfeeb6..641e79b3 100644 --- a/engine/client/cl_game.c +++ b/engine/client/cl_game.c @@ -1226,7 +1226,8 @@ HSPRITE pfnSPR_LoadExt( const char *szPicName, uint texFlags ) // load new model if( CL_LoadHudSprite( name, &clgame.sprites[i], false, texFlags )) { - clgame.sprites[i].needload = clgame.load_sequence; + if( i < ( MAX_IMAGES - 1 )) + clgame.sprites[i].needload = clgame.load_sequence; return i; } return 0; @@ -2217,6 +2218,8 @@ pfnLocalPlayerDucking */ int pfnLocalPlayerDucking( void ) { + if( CL_IsPredicted( )) + return (cl.predicted.usehull == 1); return cl.frame.client.bInDuck; } @@ -2232,7 +2235,7 @@ void pfnLocalPlayerViewheight( float *view_ofs ) if( !view_ofs ) return; if( CL_IsPredicted( )) - VectorCopy( cl.predicted_viewofs, view_ofs ); + VectorCopy( cl.predicted.viewofs, view_ofs ); else VectorCopy( cl.frame.client.view_ofs, view_ofs ); } @@ -2283,94 +2286,6 @@ physent_t *pfnGetPhysent( int idx ) return NULL; } -/* -============= -pfnSetUpPlayerPrediction - -FIXME: finalize -============= -*/ -void pfnSetUpPlayerPrediction( int dopred, int bIncludeLocalClient ) -{ -#if 0 - entity_state_t *playerstate = cl.frames[cl.parsecountmod].playerstate; - predicted_player_t *player = cls.predicted_players; - cl_entity_t *clent; - int j, v12; - - for( j = 0; j < MAX_CLIENTS; j++, player++, playerstate++ ) - { - player->active = false; - - if( playerstate->messagenum != cl.parsecount ) - continue; // not present this frame - - if( !playerstate->modelindex ) - continue; - - // special for EF_NODRAW and local client? - if(( playerstate->effects & EF_NODRAW ) && !bIncludeLocalClient ) - { - // don't include local player? - if( cl.playernum != j ) - { - player->active = true; - player->movetype = playerstate->movetype; - player->solid = playerstate->solid; - player->usehull = playerstate->usehull; - - clent = CL_EDICT_NUM( j + 1 ); -// CL_ComputePlayerOrigin( v9 ); - VectorCopy( clent->origin, player->origin ); - VectorCopy( clent->angles, player->angles ); - } - else continue; - } - else - { - if( cl.playernum == j ) - continue; - - player->active = true; - player->movetype = playerstate->movetype; - player->solid = playerstate->solid; - player->usehull = playerstate->usehull; - - v12 = 17080 * cl.parsecountmod + 340 * j; - player->origin[0] = cl.frames[0].playerstate[0].origin[0] + v12; - player->origin[1] = cl.frames[0].playerstate[0].origin[1] + v12; - player->origin[2] = cl.frames[0].playerstate[0].origin[2] + v12; - - player->angles[0] = cl.frames[0].playerstate[0].angles[0] + v12; - player->angles[1] = cl.frames[0].playerstate[0].angles[1] + v12; - player->angles[2] = cl.frames[0].playerstate[0].angles[2] + v12; - } - } -#endif -} - -/* -============= -pfnPushPMStates - -============= -*/ -void pfnPushPMStates( void ) -{ - clgame.oldcount = clgame.pmove->numphysent; -} - -/* -============= -pfnPopPMStates - -============= -*/ -void pfnPopPMStates( void ) -{ - clgame.pmove->numphysent = clgame.oldcount; -} - /* ============= pfnSetTraceHull @@ -2612,7 +2527,7 @@ const char *PlayerInfo_ValueForKey( int playerNum, const char *key ) if(( playerNum > cl.maxclients ) || ( playerNum < 1 )) return NULL; - if(( cl.players[playerNum-1].name == NULL ) || (*(cl.players[playerNum-1].name) == 0 )) + if( !cl.players[playerNum-1].name[0] ) return NULL; return Info_ValueForKey( cl.players[playerNum-1].userinfo, key ); @@ -3351,7 +3266,8 @@ TriForParams */ void TriFogParams( float flDensity, int iFogSkybox ) { - // TODO: implement + RI.fogDensity = flDensity; + RI.fogCustom = iFogSkybox; } /* @@ -3787,9 +3703,9 @@ static event_api_t gEventApi = pfnLocalPlayerBounds, pfnIndexFromTrace, pfnGetPhysent, - pfnSetUpPlayerPrediction, - pfnPushPMStates, - pfnPopPMStates, + CL_SetUpPlayerPrediction, + CL_PushPMStates, + CL_PopPMStates, CL_SetSolidPlayers, CL_SetTraceHull, CL_PlayerTrace, diff --git a/engine/client/cl_main.c b/engine/client/cl_main.c index 94d7a986..80f28f27 100644 --- a/engine/client/cl_main.c +++ b/engine/client/cl_main.c @@ -30,7 +30,8 @@ GNU General Public License for more details. convar_t *rcon_client_password; convar_t *rcon_address; -convar_t *cl_smooth; +convar_t *cl_nosmooth; +convar_t *cl_smoothtime; convar_t *cl_timeout; convar_t *cl_predict; convar_t *cl_showfps; @@ -38,6 +39,7 @@ convar_t *cl_nodelta; convar_t *cl_crosshair; convar_t *cl_cmdbackup; convar_t *cl_showerror; +convar_t *cl_bmodelinterp; convar_t *cl_draw_particles; convar_t *cl_lightstyle_lerping; convar_t *cl_idealpitchscale; @@ -305,27 +307,22 @@ void CL_CreateCmd( void ) Q_memset( &cmd, 0, sizeof( cmd )); // build list of all solid entities per next frame (exclude clients) - CL_SetSolidEntities (); - CL_SetSolidPlayers ( cl.playernum ); + CL_SetSolidEntities(); + CL_PushPMStates(); + CL_SetSolidPlayers( cl.playernum ); VectorCopy( cl.refdef.cl_viewangles, angles ); VectorCopy( cl.frame.client.origin, cl.data.origin ); VectorCopy( cl.refdef.cl_viewangles, cl.data.viewangles ); cl.data.iWeaponBits = cl.frame.client.weapons; - - if( cl.scr_fov < 1.0f || cl.scr_fov > 179.0f ) - cl.scr_fov = 90.0f; // reset to default cl.data.fov = cl.scr_fov; - clgame.dllFuncs.pfnUpdateClientData( &cl.data, cl.time ); - - // grab changes - VectorCopy( cl.data.viewangles, cl.refdef.cl_viewangles ); - cl.frame.client.weapons = cl.data.iWeaponBits; - cl.scr_fov = cl.data.fov; - - if( cl.scr_fov < 1.0f || cl.scr_fov > 179.0f ) - cl.scr_fov = 90.0f; // reset to default + if( clgame.dllFuncs.pfnUpdateClientData( &cl.data, cl.time )) + { + // grab changes if successful + VectorCopy( cl.data.viewangles, cl.refdef.cl_viewangles ); + cl.scr_fov = cl.data.fov; + } // allways dump the first ten messages, // because it may contain leftover inputs @@ -337,6 +334,8 @@ void CL_CreateCmd( void ) cl.refdef.cmd = &cl.commands[cls.netchan.outgoing_sequence & CL_UPDATE_MASK].cmd; *cl.refdef.cmd = cmd; } + + CL_PopPMStates(); return; } @@ -354,6 +353,7 @@ void CL_CreateCmd( void ) active = ( cls.state == ca_active && !cl.refdef.paused && !cls.demoplayback ); clgame.dllFuncs.CL_CreateMove( cl.time - cl.oldtime, &pcmd->cmd, active ); + CL_PopPMStates(); R_LightForPoint( cl.frame.client.origin, &color, false, false, 128.0f ); pcmd->cmd.lightlevel = (color.r + color.g + color.b) / 3; @@ -523,7 +523,6 @@ void CL_WritePacket( void ) for( i = numcmds - 1; i >= 0; i-- ) { cmdnumber = ( cls.netchan.outgoing_sequence - i ) & CL_UPDATE_MASK; - if( i == 0 ) cl.commands[cmdnumber].processedfuncs = true; // only last cmd allow to run funcs to = cmdnumber; CL_WriteUsercmd( &buf, from, to ); @@ -722,7 +721,7 @@ void CL_Connect_f( void ) return; } - Q_strncpy( server, Cmd_Argv( 1 ), sizeof( cls.servername )); + Q_strncpy( server, Cmd_Argv( 1 ), sizeof( server )); if( Host_ServerState()) { @@ -950,7 +949,7 @@ CL_InternetServers_f void CL_InternetServers_f( void ) { netadr_t adr; - char fullquery[512] = "\x31\xFF" "0.0.0.0:0\0" "\\gamedir\\"; + char fullquery[512] = "1\xFF" "0.0.0.0:0\0" "\\gamedir\\"; MsgDev( D_INFO, "Scanning for servers on the internet area...\n" ); NET_Config( true ); // allow remote @@ -958,9 +957,9 @@ void CL_InternetServers_f( void ) if( !NET_StringToAdr( MASTERSERVER_ADR, &adr ) ) MsgDev( D_INFO, "Can't resolve adr: %s\n", MASTERSERVER_ADR ); - Q_strcpy( &fullquery[21], GI->gamedir ); + Q_strcpy( &fullquery[22], GI->gamedir ); - NET_SendPacket( NS_CLIENT, Q_strlen( GI->gamedir ) + 22, fullquery, adr ); + NET_SendPacket( NS_CLIENT, Q_strlen( GI->gamedir ) + 23, fullquery, adr ); } /* @@ -1074,9 +1073,10 @@ Handle a reply from a info */ void CL_ParseStatusMessage( netadr_t from, sizebuf_t *msg ) { - char *s; + char *s = BF_ReadString( msg ); - s = BF_ReadString( msg ); + // more info about servers + MsgDev( D_INFO, "Server: %s, Game: %s\n", NET_AdrToString( from ), Info_ValueForKey( s, "gamedir" )); UI_AddServerToList( from, s ); } @@ -1395,26 +1395,21 @@ void CL_ConnectionlessPacket( netadr_t from, sizebuf_t *msg ) // dropped the connection but it is still getting packets from us CL_Disconnect(); } - else if( msg->pData[0] == 0xFF && msg->pData[1] == 0xFF && msg->pData[2] == 0xFF && msg->pData[3] == 0xFF && msg->pData[4] == 0x66 && msg->pData[5] == 0x0A ) + else if( !Q_strcmp( c, "f" )) { - dataoffset = 6; - - while( 1 ) + // serverlist got from masterserver + while( !msg->bOverflow ) { servadr.type = NA_IP; - Q_memcpy( servadr.ip, &msg->pData[dataoffset], sizeof(servadr.ip)); - servadr.port = *(word *)&msg->pData[dataoffset + 4]; + // 4 bytes for IP + BF_ReadBytes( msg, servadr.ip, sizeof( servadr.ip )); + // 2 bytes for Port + servadr.port = BF_ReadShort( msg ); - if( !servadr.port ) - break; - - MsgDev( D_INFO, "Found server: %s\n", NET_AdrToString( servadr )); + if( !servadr.port ) break; NET_Config( true ); // allow remote - Netchan_OutOfBandPrint( NS_CLIENT, servadr, "info %i", PROTOCOL_VERSION ); - - dataoffset += 6; } } else if( clgame.dllFuncs.pfnConnectionlessPacket( &from, args, buf, &len )) @@ -1670,13 +1665,15 @@ void CL_InitLocal( void ) rate = Cvar_Get( "rate", "25000", CVAR_USERINFO|CVAR_ARCHIVE, "player network rate" ); hltv = Cvar_Get( "hltv", "0", CVAR_USERINFO|CVAR_LATCH, "HLTV mode" ); cl_showfps = Cvar_Get( "cl_showfps", "1", CVAR_ARCHIVE, "show client fps" ); - cl_smooth = Cvar_Get ("cl_smooth", "0", CVAR_ARCHIVE, "smooth up stair climbing and interpolate position in multiplayer" ); + cl_nosmooth = Cvar_Get( "cl_nosmooth", "0", CVAR_ARCHIVE, "disable smooth up stair climbing and interpolate position in multiplayer" ); + cl_smoothtime = Cvar_Get( "cl_smoothtime", "0.1", CVAR_ARCHIVE, "time to smooth up" ); cl_cmdbackup = Cvar_Get( "cl_cmdbackup", "10", CVAR_ARCHIVE, "how many additional history commands are sent" ); cl_cmdrate = Cvar_Get( "cl_cmdrate", "30", CVAR_ARCHIVE, "Max number of command packets sent to server per second" ); cl_draw_particles = Cvar_Get( "cl_draw_particles", "1", CVAR_ARCHIVE, "Disable any particle effects" ); cl_draw_beams = Cvar_Get( "cl_draw_beams", "1", CVAR_ARCHIVE, "Disable view beams" ); cl_lightstyle_lerping = Cvar_Get( "cl_lightstyle_lerping", "0", CVAR_ARCHIVE, "enables animated light lerping (perfomance option)" ); cl_showerror = Cvar_Get( "cl_showerror", "0", CVAR_ARCHIVE, "show prediction error" ); + cl_bmodelinterp = Cvar_Get( "cl_bmodelinterp", "1", CVAR_ARCHIVE, "enable bmodel interpolation" ); Cvar_Get( "hud_scale", "0", CVAR_ARCHIVE|CVAR_LATCH, "scale hud at current resolution" ); Cvar_Get( "skin", "", CVAR_USERINFO, "player skin" ); // XDM 3.3 want this cvar diff --git a/engine/client/cl_pmove.c b/engine/client/cl_pmove.c index a08df177..9ef703ff 100644 --- a/engine/client/cl_pmove.c +++ b/engine/client/cl_pmove.c @@ -29,6 +29,120 @@ void CL_ClearPhysEnts( void ) clgame.pmove->numphysent = 0; } +/* +============= +CL_PushPMStates + +============= +*/ +void CL_PushPMStates( void ) +{ + if( clgame.pushed ) + { + MsgDev( D_ERROR, "PushPMStates called with pushed stack\n"); + } + else + { + clgame.oldphyscount = clgame.pmove->numphysent; + clgame.oldviscount = clgame.pmove->numvisent; + clgame.pushed = true; + } + +} + +/* +============= +CL_PopPMStates + +============= +*/ +void CL_PopPMStates( void ) +{ + if( clgame.pushed ) + { + clgame.pmove->numphysent = clgame.oldphyscount; + clgame.pmove->numvisent = clgame.oldviscount; + clgame.pushed = false; + } + else + { + MsgDev( D_ERROR, "PopPMStates called without stack\n"); + } +} + +/* +============= +CL_ComputePlayerOrigin + +FIXME: implement +============= +*/ +void CL_ComputePlayerOrigin( cl_entity_t *clent ) +{ +} + +/* +============= +CL_SetUpPlayerPrediction + +============= +*/ +void CL_SetUpPlayerPrediction( int dopred, int bIncludeLocalClient ) +{ + entity_state_t *state; + predicted_player_t *player; + cl_entity_t *clent; + int i; + + for( i = 0; i < MAX_CLIENTS; i++ ) + { + state = &cl.frames[cl.parsecountmod].playerstate[i]; + player = &cls.predicted_players[i]; + + player->active = false; + + if( state->messagenum != cl.parsecount ) + continue; // not present this frame + + if( !state->modelindex ) + continue; + + clent = CL_GetEntityByIndex( i + 1 ); + + // special for EF_NODRAW and local client? + if(( state->effects & EF_NODRAW ) && !bIncludeLocalClient ) + { + // don't include local player? + if( cl.playernum == i ) + continue; + + player->active = true; + player->movetype = state->movetype; + player->solid = state->solid; + player->usehull = state->usehull; + + CL_ComputePlayerOrigin( clent ); + + VectorCopy( clent->origin, player->origin ); + VectorCopy( clent->angles, player->angles ); + } + else + { + player->active = true; + player->movetype = state->movetype; + player->solid = state->solid; + player->usehull = state->usehull; + + // don't rewrite origin and angles of local client + if( cl.playernum == i ) + continue; + + VectorCopy( state->origin, player->origin ); + VectorCopy( state->angles, player->angles ); + } + } +} + void CL_ClipPMoveToEntity( physent_t *pe, const vec3_t start, vec3_t mins, vec3_t maxs, const vec3_t end, pmtrace_t *tr ) { ASSERT( tr != NULL ); @@ -211,25 +325,32 @@ pmove must be setup with world and solid entity hulls before calling */ void CL_SetSolidPlayers( int playernum ) { - int j; - extern vec3_t player_mins; - extern vec3_t player_maxs; + entity_state_t *state; cl_entity_t *ent; physent_t *pe; + int i; if( !cl_solid_players->integer ) return; - for( j = 0; j < cl.maxclients; j++ ) + for( i = 0; i < cl.maxclients; i++ ) { // the player object never gets added - if( j == playernum ) continue; + if( i == playernum ) continue; - ent = CL_GetEntityByIndex( j + 1 ); + ent = CL_GetEntityByIndex( i + 1 ); if( !ent || !ent->player ) continue; // not present this frame + state = cl.frames[cl.parsecountmod].playerstate + i; + + if( state->effects & EF_NODRAW ) + continue; // skip invisible + + if( !state->solid ) + continue; // not solid + pe = &clgame.pmove->physents[clgame.pmove->numphysent]; if( CL_CopyEntityToPhysEnt( pe, ent )) clgame.pmove->numphysent++; @@ -858,6 +979,10 @@ void CL_RunUsercmd( local_state_t *from, local_state_t *to, usercmd_t *u, qboole // copy results back to client CL_FinishPMove( clgame.pmove, to ); + cl.predicted.lastground = clgame.pmove->onground; + if( cl.predicted.lastground > 0 && cl.predicted.lastground < clgame.pmove->numphysent ) + cl.predicted.lastground = clgame.pmove->physents[cl.predicted.lastground].info; + clgame.dllFuncs.pfnPostRunCmd( from, to, &cmd, runfuncs, *time, random_seed ); *time += (double)cmd.msec / 1000.0; } @@ -879,26 +1004,30 @@ void CL_CheckPredictionError( void ) frame = ( cls.netchan.incoming_acknowledged ) & CL_UPDATE_MASK; // compare what the server returned with what we had predicted it to be - VectorSubtract( cl.frame.playerstate[cl.playernum].origin, cl.predicted_origins[frame], delta ); + VectorSubtract( cl.frame.playerstate[cl.playernum].origin, cl.predicted.origins[frame], delta ); maxspd = ( clgame.movevars.maxvelocity * host.frametime ); len = VectorLength( delta ); // save the prediction error for interpolation - if(( cl.frame.client.flags & EF_NOINTERP ) || len > maxspd ) +// if(( cl.frame.client.flags & EF_NOINTERP ) || len > maxspd ) + if( len > 64.0f ) { // a teleport or something or gamepaused - VectorClear( cl.prediction_error ); + VectorClear( cl.predicted.error ); } else { if( cl_showerror->value && len > 0.5f ) MsgDev( D_ERROR, "prediction error on %i: %g\n", cl.parsecount, len ); - VectorCopy( cl.frame.playerstate[cl.playernum].origin, cl.predicted_origins[frame] ); + VectorCopy( cl.frame.playerstate[cl.playernum].origin, cl.predicted.origins[frame] ); - // save for error itnerpolation - VectorCopy( delta, cl.prediction_error ); + // save for error interpolation + VectorCopy( delta, cl.predicted.error ); + + if(( len > 0.25f ) && ( cl.maxclients > 1 )) + cl.predicted.correction_time = cl_smoothtime->value; } } @@ -913,7 +1042,9 @@ void CL_PostRunCmd( usercmd_t *ucmd, int random_seed ) { local_state_t from, to; - memcpy( from.weapondata, cl.frame.weapondata, sizeof( from.weapondata )); + Q_memset( &from, 0, sizeof( local_state_t )); + Q_memset( &to, 0, sizeof( local_state_t )); + Q_memcpy( from.weapondata, cl.frame.weapondata, sizeof( from.weapondata )); from.playerstate = cl.frame.playerstate[cl.playernum]; from.client = cl.frame.client; to = from; @@ -921,6 +1052,35 @@ void CL_PostRunCmd( usercmd_t *ucmd, int random_seed ) clgame.dllFuncs.pfnPostRunCmd( &from, &to, ucmd, true, cl.time, random_seed ); } +/* +================= +CL_FakeUsercmd + +Runs client weapons prediction code +================= +*/ +void CL_FakeUsercmd( local_state_t *from, local_state_t *to, usercmd_t *u, qboolean runfuncs, double *pfElapsed, unsigned int random_seed ) +{ + usercmd_t cmd; + local_state_t temp; + usercmd_t split; + + while( u->msec > 50 ) + { + split = *u; + split.msec /= 2; + CL_FakeUsercmd( from, &temp, &split, runfuncs, pfElapsed, random_seed ); + from = &temp; + u = &split; + } + + cmd = *u; + *to = *from; + + clgame.dllFuncs.pfnPostRunCmd( from, to, &cmd, runfuncs, *pfElapsed, random_seed ); + *pfElapsed += cmd.msec / 1000.0; +} + /* ================= CL_PredictMovement @@ -949,15 +1109,62 @@ void CL_PredictMovement( void ) if( !CL_IsInGame( )) return; + CL_SetUpPlayerPrediction( false, false ); + // unpredicted pure angled values converted into axis AngleVectors( cl.refdef.cl_viewangles, cl.refdef.forward, cl.refdef.right, cl.refdef.up ); ASSERT( cl.refdef.cmd != NULL ); if( !CL_IsPredicted( )) - { - // run commands even if client predicting is disabled - client expected it - CL_PostRunCmd( cl.refdef.cmd, cls.lastoutgoingcommand ); + { + // fake prediction code + // we need to perform cl_lw prediction while cl_predict is disabled + // because cl_lw is enabled by default in Half-Life + if( !cl_lw->integer ) + { + cl.predicted.viewmodel = cl.frame.client.viewmodel; + return; + } + + ack = cls.netchan.incoming_acknowledged; + outgoing_command = cls.netchan.outgoing_sequence; + + from = &cl.predict[cl.parsecountmod]; + from->playerstate = cl.frame.playerstate[cl.playernum]; + from->client = cl.frame.client; + Q_memcpy( from->weapondata, cl.frame.weapondata, sizeof( from->weapondata )); + + time = cl.frame.time; + + while( 1 ) + { + // we've run too far forward + if( frame >= ( CL_UPDATE_BACKUP - 1 )) + break; + + // Incoming_acknowledged is the last usercmd the server acknowledged having acted upon + current_command = ack + frame; + current_command_mod = current_command & CL_UPDATE_MASK; + + // we've caught up to the current command. + if( current_command >= outgoing_command ) + break; + + to = &cl.predict[( cl.parsecountmod + frame ) & CL_UPDATE_MASK]; + + CL_FakeUsercmd( from, to, &cl.commands[current_command_mod].cmd, + !cl.commands[current_command_mod].processedfuncs, + &time, cls.netchan.incoming_acknowledged + frame ); + + cl.commands[current_command_mod].processedfuncs = true; + + from = to; + frame++; + } + + if( to ) + cl.predicted.viewmodel = to->client.viewmodel; return; } @@ -965,7 +1172,7 @@ void CL_PredictMovement( void ) outgoing_command = cls.netchan.outgoing_sequence; from = &cl.predict[cl.parsecountmod]; - memcpy( from->weapondata, cl.frame.weapondata, sizeof( from->weapondata )); + Q_memcpy( from->weapondata, cl.frame.weapondata, sizeof( from->weapondata )); from->playerstate = cl.frame.playerstate[cl.playernum]; from->client = cl.frame.client; @@ -996,7 +1203,7 @@ void CL_PredictMovement( void ) cl.commands[current_command_mod].processedfuncs = true; // save for debug checking - VectorCopy( to->playerstate.origin, cl.predicted_origins[current_command_mod] ); + VectorCopy( to->playerstate.origin, cl.predicted.origins[current_command_mod] ); from = to; frame++; @@ -1004,15 +1211,109 @@ void CL_PredictMovement( void ) if( to ) { - VectorCopy( to->playerstate.origin, cl.predicted_origin ); - VectorCopy( to->client.velocity, cl.predicted_velocity ); - VectorCopy( to->client.view_ofs, cl.predicted_viewofs ); - VectorCopy( to->client.punchangle, cl.predicted_punchangle ); + float t0 = cl.commands[( cl.parsecountmod + frame - 1) & CL_UPDATE_MASK].senttime; + float t1 = cl.commands[( cl.parsecountmod + frame ) & CL_UPDATE_MASK].senttime; + float t; - cl.predicted_viewmodel = to->client.viewmodel; - cl.scr_fov = to->client.fov; + if( t0 == t1 ) + { + t = 0.0f; + } + else + { + t = (host.realtime - t0) / (t1 - t0); + t = bound( 0.0f, t, 1.0f ); + } - if( cl.scr_fov < 1.0f || cl.scr_fov > 179.0f ) - cl.scr_fov = 90.0f; + // was teleported + if( fabs( to->playerstate.origin[0] - from->playerstate.origin[0] ) > 128.0f || + fabs( to->playerstate.origin[1] - from->playerstate.origin[1] ) > 128.0f || + fabs( to->playerstate.origin[2] - from->playerstate.origin[2] ) > 128.0f ) + { + VectorCopy( to->playerstate.origin, cl.predicted.origin ); + VectorCopy( to->client.velocity, cl.predicted.velocity ); + VectorCopy( to->client.punchangle, cl.predicted.punchangle ); + VectorCopy( to->client.view_ofs, cl.predicted.viewofs ); + } + else + { + vec3_t delta_origin, delta_punch, delta_vel; + + VectorSubtract( to->playerstate.origin, from->playerstate.origin, delta_origin ); + VectorSubtract( to->client.velocity, from->client.velocity, delta_vel ); + VectorSubtract( to->client.punchangle, from->client.punchangle, delta_punch ); + + VectorMA( from->playerstate.origin, t, delta_origin, cl.predicted.origin ); + VectorMA( from->client.velocity, t, delta_vel, cl.predicted.velocity ); + VectorMA( from->client.punchangle, t, delta_punch, cl.predicted.punchangle ); + + if( from->playerstate.usehull == to->playerstate.usehull ) + { + vec3_t delta_viewofs; + + VectorSubtract( to->client.view_ofs, from->client.view_ofs, delta_viewofs ); + VectorMA( from->client.view_ofs, t, delta_viewofs, cl.predicted.viewofs ); + } + } + + cl.predicted.waterlevel = to->client.waterlevel; + cl.predicted.viewmodel = to->client.viewmodel; + cl.predicted.usehull = to->playerstate.usehull; + + if( to->client.flags & FL_ONGROUND ) + { + cl_entity_t *ent = CL_GetEntityByIndex( cl.predicted.lastground ); + + cl.predicted.onground = cl.predicted.lastground; + cl.predicted.moving = 0; + + if( ent ) + { + vec3_t delta; + delta[0] = ent->curstate.origin[0] - ent->prevstate.origin[0]; + delta[1] = ent->curstate.origin[1] - ent->prevstate.origin[1]; + delta[2] = 0.0f; + + if( VectorLength( delta ) > 0.0f ) + { + cl.predicted.correction_time = 0; + cl.predicted.moving = 1; + } + } + } + else + { + cl.predicted.onground = -1; + cl.predicted.moving = 0; + } + + if ( cl.predicted.correction_time > 0.0 && !cl_nosmooth->value && cl_smoothtime->value ) + { + float d; + int i; + + cl.predicted.correction_time = cl.predicted.correction_time - host.frametime; + + if( cl_smoothtime->value <= 0 ) + Cvar_SetFloat( "cl_smoothtime", 0.1 ); + + if( cl.predicted.correction_time < 0 ) + cl.predicted.correction_time = 0; + + if( cl_smoothtime->value <= cl.predicted.correction_time ) + cl.predicted.correction_time = cl_smoothtime->value; + + d = cl.predicted.correction_time / cl_smoothtime->value; + + for( i = 0; i < 3; i++ ) + { + cl.predicted.origin[i] = cl.predicted.lastorigin[i] + ( cl.predicted.origin[i] - cl.predicted.lastorigin[i] ) * (1.0 - d); + } + } + + VectorCopy( cl.predicted.origin, cl.predicted.lastorigin ); + CL_SetIdealPitch(); } + + CL_CheckPredictionError(); } \ No newline at end of file diff --git a/engine/client/cl_tent.c b/engine/client/cl_tent.c index 2721545c..ea9b1f1f 100644 --- a/engine/client/cl_tent.c +++ b/engine/client/cl_tent.c @@ -527,7 +527,7 @@ void CL_Bubbles( const vec3_t mins, const vec3_t maxs, float height, int modelIn pTemp->x = origin[0]; pTemp->y = origin[1]; - angle = Com_RandomLong( -M_PI, M_PI ); + angle = Com_RandomFloat( -M_PI, M_PI ); SinCos( angle, &sine, &cosine ); zspeed = Com_RandomLong( 80, 140 ); @@ -572,7 +572,7 @@ void CL_BubbleTrail( const vec3_t start, const vec3_t end, float flWaterZ, int m pTemp->x = origin[0]; pTemp->y = origin[1]; - angle = Com_RandomLong( -M_PI, M_PI ); + angle = Com_RandomFloat( -M_PI, M_PI ); zspeed = Com_RandomLong( 80, 140 ); VectorSet( pTemp->entity.baseline.origin, speed * cos( angle ), speed * sin( angle ), zspeed ); @@ -2653,7 +2653,7 @@ int CL_DecalIndexFromName( const char *name ) return 0; // look through the loaded sprite name list for SpriteName - for( i = 0; i < MAX_DECALS && host.draw_decals[i+1][0]; i++ ) + for( i = 0; i < (MAX_DECALS - 1) && host.draw_decals[i+1][0]; i++ ) { if( !Q_stricmp( name, host.draw_decals[i+1] )) return i+1; diff --git a/engine/client/cl_view.c b/engine/client/cl_view.c index cef90e3a..ee85b7b3 100644 --- a/engine/client/cl_view.c +++ b/engine/client/cl_view.c @@ -36,14 +36,14 @@ void V_SetupRefDef( void ) clent = CL_GetLocalPlayer (); clgame.entities->curstate.scale = clgame.movevars.waveHeight; - clgame.viewent.curstate.modelindex = cl.frame.client.viewmodel; + if( cl_lw->value ) clgame.viewent.curstate.modelindex = cl.predicted.viewmodel; + else clgame.viewent.curstate.modelindex = cl.frame.client.viewmodel; clgame.viewent.model = Mod_Handle( clgame.viewent.curstate.modelindex ); clgame.viewent.curstate.number = cl.playernum + 1; clgame.viewent.curstate.entityType = ET_NORMAL; clgame.viewent.index = cl.playernum + 1; cl.refdef.movevars = &clgame.movevars; - cl.refdef.onground = ( cl.frame.client.flags & FL_ONGROUND ) ? 1 : 0; cl.refdef.health = cl.frame.client.health; cl.refdef.playernum = cl.playernum; cl.refdef.max_entities = clgame.maxEntities; @@ -51,12 +51,11 @@ void V_SetupRefDef( void ) cl.refdef.time = cl.time; cl.refdef.frametime = cl.time - cl.oldtime; cl.refdef.demoplayback = cls.demoplayback; - cl.refdef.smoothing = cl_smooth->integer; cl.refdef.viewsize = scr_viewsize->integer; - cl.refdef.waterlevel = cl.frame.client.waterlevel; cl.refdef.onlyClientDraw = 0; // reset clientdraw cl.refdef.hardware = true; // always true cl.refdef.spectator = (clent->curstate.spectator != 0); + cl.refdef.smoothing = false; // old stuff to smooth multiplayer view cl.refdef.nextView = 0; SCR_AddDirtyPoint( 0, 0 ); @@ -81,8 +80,7 @@ void V_SetupRefDef( void ) cl.refdef.viewport[0] = (scr_width->integer - cl.refdef.viewport[2]) / 2; cl.refdef.viewport[1] = (scr_height->integer - sb_lines - cl.refdef.viewport[3]) / 2; - if( cl.scr_fov < 1.0f || cl.scr_fov > 179.0f ) - cl.scr_fov = 90.0f; + cl.scr_fov = bound( 1.0f, cl.scr_fov, 179.0f ); // calc FOV cl.refdef.fov_x = cl.scr_fov; // this is a final fov value @@ -94,11 +92,12 @@ void V_SetupRefDef( void ) if( CL_IsPredicted( ) && !cl.refdef.demoplayback ) { - VectorMA( cl.predicted_origin, -cl.lerpBack, cl.prediction_error, cl.refdef.simorg ); - VectorCopy( cl.predicted_origin, cl.refdef.simorg ); - VectorCopy( cl.predicted_velocity, cl.refdef.simvel ); - VectorCopy( cl.predicted_viewofs, cl.refdef.viewheight ); - VectorCopy( cl.predicted_punchangle, cl.refdef.punchangle ); + VectorCopy( cl.predicted.origin, cl.refdef.simorg ); + VectorCopy( cl.predicted.velocity, cl.refdef.simvel ); + VectorCopy( cl.predicted.viewofs, cl.refdef.viewheight ); + VectorCopy( cl.predicted.punchangle, cl.refdef.punchangle ); + cl.refdef.onground = ( cl.predicted.onground == -1 ) ? false : true; + cl.refdef.waterlevel = cl.predicted.waterlevel; } else { @@ -106,6 +105,8 @@ void V_SetupRefDef( void ) VectorCopy( cl.frame.client.view_ofs, cl.refdef.viewheight ); VectorCopy( cl.frame.client.velocity, cl.refdef.simvel ); VectorCopy( cl.frame.client.punchangle, cl.refdef.punchangle ); + cl.refdef.onground = (cl.frame.client.flags & FL_ONGROUND) ? 1 : 0; + cl.refdef.waterlevel = cl.frame.client.waterlevel; } } diff --git a/engine/client/client.h b/engine/client/client.h index 617e9aad..072f1821 100644 --- a/engine/client/client.h +++ b/engine/client/client.h @@ -87,6 +87,24 @@ extern int CL_UPDATE_BACKUP; #define INVALID_HANDLE 0xFFFF // for XashXT cache system +typedef struct +{ + vec3_t origin; // generated by CL_PredictMovement + vec3_t viewofs; + vec3_t velocity; + vec3_t punchangle; + vec3_t origins[CMD_BACKUP]; + vec3_t error; + vec3_t lastorigin; + double correction_time; + int viewmodel; + int onground; + int waterlevel; + int usehull; + int moving; + int lastground; +} cl_predicted_data_t; // data we got from prediction system + // the client_t structure is wiped completely at every // server map change typedef struct @@ -140,12 +158,7 @@ typedef struct event_state_t events; // predicting stuff - vec3_t predicted_origin; // generated by CL_PredictMovement - vec3_t predicted_viewofs; - vec3_t predicted_velocity; - vec3_t predicted_punchangle; - vec3_t predicted_origins[CMD_BACKUP]; - vec3_t prediction_error; + cl_predicted_data_t predicted; // generated from CL_PredictMovement // server state information int playernum; @@ -166,7 +179,6 @@ typedef struct // weapon predict stuff float scr_fov; - int predicted_viewmodel; float weaponstarttime; int weaponsequence; } client_t; @@ -357,8 +369,10 @@ typedef struct movevars_t oldmovevars; playermove_t *pmove; // pmove state - int old_trace_hull; // used by PM_Push\Pop state - int oldcount; // used by PM_Push\Pop state + int old_trace_hull; // used by PM_Push\Pop state + qboolean pushed; // used by PM_Push\Pop state + int oldviscount; // used by PM_Push\Pop state + int oldphyscount; // used by PM_Push\Pop state vec3_t player_mins[MAX_MAP_HULLS]; // 4 hulls allowed vec3_t player_maxs[MAX_MAP_HULLS]; // 4 hulls allowed @@ -518,13 +532,14 @@ extern menu_static_t menu; // cvars // extern convar_t *cl_predict; -extern convar_t *cl_smooth; extern convar_t *cl_showfps; extern convar_t *cl_envshot_size; extern convar_t *cl_timeout; extern convar_t *cl_nodelta; extern convar_t *cl_interp; extern convar_t *cl_showerror; +extern convar_t *cl_nosmooth; +extern convar_t *cl_smoothtime; extern convar_t *cl_crosshair; extern convar_t *cl_testlights; extern convar_t *cl_solid_players; @@ -534,6 +549,7 @@ extern convar_t *cl_lightstyle_lerping; extern convar_t *cl_draw_particles; extern convar_t *cl_levelshot_name; extern convar_t *cl_draw_beams; +extern convar_t *cl_bmodelinterp; extern convar_t *cl_lw; // local weapons extern convar_t *scr_centertime; extern convar_t *scr_viewsize; @@ -712,6 +728,9 @@ cl_entity_t *CL_GetWaterEntity( const float *rgflPos ); void CL_SetupPMove( playermove_t *pmove, local_state_t *from, usercmd_t *ucmd, qboolean runfuncs, double time ); pmtrace_t CL_TraceLine( vec3_t start, vec3_t end, int flags ); void CL_ClearPhysEnts( void ); +void CL_PushPMStates( void ); +void CL_PopPMStates( void ); +void CL_SetUpPlayerPrediction( int dopred, int bIncludeLocalClient ); // // cl_studio.c @@ -727,6 +746,7 @@ void CL_UpdateStudioVars( cl_entity_t *ent, entity_state_t *newstate, qboolean n qboolean CL_GetEntitySpatialization( int entnum, vec3_t origin, float *pradius ); void CL_UpdateEntityFields( cl_entity_t *ent ); qboolean CL_IsPlayerIndex( int idx ); +void CL_SetIdealPitch( void ); // // cl_remap.c diff --git a/engine/client/gl_decals.c b/engine/client/gl_decals.c index 170a5acf..1c548a08 100644 --- a/engine/client/gl_decals.c +++ b/engine/client/gl_decals.c @@ -1232,7 +1232,11 @@ void R_DecalRemoveAll( int textureIndex ) { pdecal = &gDecalPool[i]; - if( !textureIndex || pdecal->texture == textureIndex ) + // don't remove permanent decals + if( pdecal->flags & FDECAL_PERMANENT ) + continue; + + if( !textureIndex || ( pdecal->texture == textureIndex )) R_DecalUnlink( pdecal ); } } diff --git a/engine/client/gl_export.h b/engine/client/gl_export.h index 97f103d1..8464ff3b 100644 --- a/engine/client/gl_export.h +++ b/engine/client/gl_export.h @@ -1191,5 +1191,6 @@ BOOL ( WINAPI * pwglRealizeLayerPalette)(HDC, int, BOOL); BOOL ( WINAPI * pwglSwapLayerBuffers)(HDC, UINT); BOOL ( WINAPI * pwglSwapIntervalEXT)( int interval ); HGLRC ( WINAPI * pwglCreateContextAttribsARB)( HDC hDC, HGLRC hShareContext, const int *attribList ); +const char *( WINAPI * pwglGetExtensionsStringEXT)( void ); #endif//GL_EXPORT_H \ No newline at end of file diff --git a/engine/client/gl_local.h b/engine/client/gl_local.h index 1deed5b4..033e1440 100644 --- a/engine/client/gl_local.h +++ b/engine/client/gl_local.h @@ -492,6 +492,7 @@ void R_NewMap( void ); enum { GL_OPENGL_110 = 0, // base + GL_WGL_EXTENSIONS, GL_WGL_SWAPCONTROL, GL_WGL_PROCADDRESS, GL_HARDWARE_GAMMA_CONTROL, @@ -548,6 +549,7 @@ typedef struct // list of supported extensions const char *extensions_string; + const char *wgl_extensions_string; byte extension[GL_EXTCOUNT]; int max_texture_units; diff --git a/engine/client/gl_rmain.c b/engine/client/gl_rmain.c index f107ac8f..9e209550 100644 --- a/engine/client/gl_rmain.c +++ b/engine/client/gl_rmain.c @@ -370,16 +370,16 @@ int R_ComputeFxBlend( cl_entity_t *e ) break; } - if( e->model->type != mod_brush ) + if( e->model && e->model->type != mod_brush ) { // NOTE: never pass sprites with rendercolor '0 0 0' it's a stupid Valve Hammer Editor bug if( !e->curstate.rendercolor.r && !e->curstate.rendercolor.g && !e->curstate.rendercolor.b ) e->curstate.rendercolor.r = e->curstate.rendercolor.g = e->curstate.rendercolor.b = 255; - } - // apply scale to studiomodels and sprites only - if( e->model && e->model->type != mod_brush && !e->curstate.scale ) - e->curstate.scale = 1.0f; + // apply scale to studiomodels and sprites only + if( !e->curstate.scale ) + e->curstate.scale = 1.0f; + } blend = bound( 0, blend, 255 ); diff --git a/engine/client/gl_rpart.c b/engine/client/gl_rpart.c index 92e0a213..372302c8 100644 --- a/engine/client/gl_rpart.c +++ b/engine/client/gl_rpart.c @@ -843,20 +843,64 @@ void CL_BloodStream( const vec3_t org, const vec3_t dir, int pcolor, int speed ) { particle_t *p; int i, j; + float arc; - for( i = 0; i < speed * 20; i++ ) + for( arc = 0.05f, i = 0; i < 100; i++, arc -= 0.005f ) { + p = CL_AllocParticle( NULL ); + + if( !p ) return; + + p->die += 2.0f; + p->type = pt_vox_grav; + p->color = pcolor + Com_RandomLong( 0, 9 ); + + VectorCopy( org, p->org ); + VectorCopy( dir, p->vel ); + + p->vel[2] -= arc; + arc -= 0.005f; + + VectorScale( p->vel, speed, p->vel ); + } + + for( arc = 0.075f, i = 0; i < speed / 2; i++, arc -= 0.005f ) + { + float num; + p = CL_AllocParticle( NULL ); if( !p ) return; - p->die += Com_RandomFloat( 0.2f, 0.8f ); - p->type = pt_vox_grav; - p->color = pcolor; + p->die += 3.0f; + p->color = pcolor + Com_RandomLong( 0, 9 ); + p->type = pt_vox_slowgrav; - for( j = 0; j < 3; j++ ) + VectorCopy( org, p->org ); + VectorCopy( dir, p->vel ); + + p->vel[2] -= arc; + + num = Com_RandomFloat( 0.0f, 1.0f ); + num = 1.7f * num * (int)(num * speed); + VectorScale( p->vel, num, p->vel ); + + for( j = 0; j < 2; j++ ) { - p->org[j] = org[j]; - p->vel[j] = dir[j] * speed; + p = CL_AllocParticle( NULL ); + if( !p ) return; + + p->die += 3.0f; + p->color = pcolor + Com_RandomLong( 0, 9 ); + p->type = pt_vox_slowgrav; + + p->org[0] = org[0] + Com_RandomFloat( -1.0f, 1.0f ); + p->org[1] = org[1] + Com_RandomFloat( -1.0f, 1.0f ); + p->org[2] = org[2] + Com_RandomFloat( -1.0f, 1.0f ); + + VectorCopy( dir, p->vel ); + p->vel[2] -= arc; + + VectorScale( p->vel, num, p->vel ); } } } diff --git a/engine/client/gl_studio.c b/engine/client/gl_studio.c index f49b13ca..e5c32b48 100644 --- a/engine/client/gl_studio.c +++ b/engine/client/gl_studio.c @@ -3302,9 +3302,6 @@ void R_RunViewmodelEvents( void ) if( !Mod_Extradata( clgame.viewent.model )) return; - if( cl_lw->value && cl.frame.client.viewmodel != cl.predicted_viewmodel ) - return; - RI.currententity = &clgame.viewent; RI.currentmodel = RI.currententity->model; if( !RI.currentmodel ) return; @@ -3339,9 +3336,6 @@ void R_DrawViewModel( void ) if( !Mod_Extradata( clgame.viewent.model )) return; - if( cl_lw->value && cl.frame.client.viewmodel != cl.predicted_viewmodel ) - return; - RI.currententity = &clgame.viewent; RI.currentmodel = RI.currententity->model; if( !RI.currentmodel ) return; diff --git a/engine/client/gl_vidnt.c b/engine/client/gl_vidnt.c index 34c6cc7e..9509ee6f 100644 --- a/engine/client/gl_vidnt.c +++ b/engine/client/gl_vidnt.c @@ -27,11 +27,6 @@ GNU General Public License for more details. #define WINDOW_EX_STYLE (0) #define WINDOW_NAME "Xash Window" // Half-Life -#ifdef WIN32 -// Enable NVIDIA High Performance Graphics while using Integrated Graphics. -__declspec(dllexport) DWORD NvOptimusEnablement = 0x00000001; -#endif - convar_t *renderinfo; convar_t *gl_allow_software; convar_t *gl_extensions; @@ -465,6 +460,12 @@ static dllfunc_t wglswapintervalfuncs[] = { NULL, NULL } }; +static dllfunc_t wglgetextensionsstring[] = +{ +{ "wglGetExtensionsStringEXT" , (void **)&pwglGetExtensionsStringEXT }, +{ NULL, NULL } +}; + dll_info_t opengl_dll = { "opengl32.dll", wgl_funcs, true }; /* @@ -530,6 +531,7 @@ void GL_CheckExtension( const char *name, const dllfunc_t *funcs, const char *cv { const dllfunc_t *func; convar_t *parm; + const char *extensions_string; MsgDev( D_NOTE, "GL_CheckExtension: %s ", name ); @@ -547,7 +549,12 @@ void GL_CheckExtension( const char *name, const dllfunc_t *funcs, const char *cv GL_SetExtension( r_ext, 1 ); } - if(( name[2] == '_' || name[3] == '_' ) && !Q_strstr( glConfig.extensions_string, name )) + extensions_string = glConfig.extensions_string; + + if( name[0] == 'W' && name[1] == 'G' && name[2] == 'L' && glConfig.wgl_extensions_string != NULL ) + extensions_string = glConfig.wgl_extensions_string; + + if(( name[2] == '_' || name[3] == '_' ) && !Q_strstr( extensions_string, name )) { GL_SetExtension( r_ext, false ); // update render info MsgDev( D_NOTE, "- ^1failed\n" ); @@ -1577,8 +1584,13 @@ void R_RenderInfo_f( void ) // don't spam about extensions if( host.developer >= 4 ) + { Msg( "GL_EXTENSIONS: %s\n", glConfig.extensions_string ); + if( glConfig.wgl_extensions_string != NULL ) + Msg( "\nWGL_EXTENSIONS: %s\n", glConfig.wgl_extensions_string ); + } + Msg( "GL_MAX_TEXTURE_SIZE: %i\n", glConfig.max_2d_texture_size ); if( GL_Support( GL_ARB_MULTITEXTURE )) @@ -1705,6 +1717,13 @@ void GL_InitExtensions( void ) glConfig.extensions_string = pglGetString( GL_EXTENSIONS ); MsgDev( D_INFO, "Video: %s\n", glConfig.renderer_string ); + // windows-specific extensions + GL_CheckExtension( "WGL Extensions String", wglgetextensionsstring, NULL, GL_WGL_EXTENSIONS ); + + if( pwglGetExtensionsStringEXT != NULL ) + glConfig.wgl_extensions_string = pwglGetExtensionsStringEXT(); + else glConfig.wgl_extensions_string = NULL; + // initalize until base opengl functions loaded GL_CheckExtension( "WGL_EXT_swap_control", wglswapintervalfuncs, NULL, GL_WGL_SWAPCONTROL ); diff --git a/engine/client/s_dsp.c b/engine/client/s_dsp.c index b616b82e..cad404e4 100644 --- a/engine/client/s_dsp.c +++ b/engine/client/s_dsp.c @@ -828,7 +828,7 @@ void CheckNewDspPresets( void ) if( dsp_off->value != 0.0f ) return; - if( cl.frame.client.waterlevel > 2 ) + if( s_listener.waterlevel > 2 ) idsp_room = roomwater_type->value; else idsp_room = room_type->value; diff --git a/engine/client/s_main.c b/engine/client/s_main.c index 38339dcf..cedf4f25 100644 --- a/engine/client/s_main.c +++ b/engine/client/s_main.c @@ -355,20 +355,8 @@ already playing. channel_t *SND_PickStaticChannel( int entnum, sfx_t *sfx, const vec3_t pos ) { channel_t *ch = NULL; - int i, dupe = 0; -#if 0 - // TODO: remove this code when predicting is will be done - // check for duplicate sounds - for( i = 0; i < total_channels; i++ ) - { - if( channels[i].sfx == sfx && VectorCompare( channels[i].origin, pos )) - dupe++; - } + int i; - // check for duplicated static channels (same origin and same sfx) - if( dupe > MAX_DUPLICATED_CHANNELS ) - return NULL; -#endif // check for replacement sound, or find the best one to replace for( i = MAX_DYNAMIC_CHANNELS; i < total_channels; i++ ) { diff --git a/engine/client/vgui/vgui_draw.h b/engine/client/vgui/vgui_draw.h index ceb2ce91..d8126cfb 100644 --- a/engine/client/vgui/vgui_draw.h +++ b/engine/client/vgui/vgui_draw.h @@ -46,7 +46,7 @@ void VGUI_EnableTexture( qboolean enable ); void VGUI_CreateTexture( int id, int width, int height ); void VGUI_UploadTexture( int id, const char *buffer, int width, int height ); void VGUI_UploadTextureBlock( int id, int drawX, int drawY, const byte *rgba, int blockWidth, int blockHeight ); -long VGUI_SurfaceWndProc( HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam ); +LONG VGUI_SurfaceWndProc( HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam ); void VGUI_DrawQuad( const vpoint_t *ul, const vpoint_t *lr ); void VGUI_GetTextureSizes( int *width, int *height ); int VGUI_GenerateTexture( void ); diff --git a/engine/client/vgui/vgui_input.cpp b/engine/client/vgui/vgui_input.cpp index 4d175c71..f1d1255d 100644 --- a/engine/client/vgui/vgui_input.cpp +++ b/engine/client/vgui/vgui_input.cpp @@ -228,7 +228,7 @@ KeyCode VGUI_MapKey( int keyCode ) } } -long VGUI_SurfaceWndProc( HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam ) +LONG VGUI_SurfaceWndProc( HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam ) { SurfaceBase *surface = NULL; CEnginePanel *panel = NULL; diff --git a/engine/common/build.c b/engine/common/build.c index d158a082..1a8ca7a3 100644 --- a/engine/common/build.c +++ b/engine/common/build.c @@ -48,6 +48,6 @@ int Q_buildnum( void ) return b; #else - return 3387; + return 3477; #endif } \ No newline at end of file diff --git a/engine/common/con_utils.c b/engine/common/con_utils.c index 4d15b768..61973edb 100644 --- a/engine/common/con_utils.c +++ b/engine/common/con_utils.c @@ -304,8 +304,8 @@ qboolean Cmd_GetMusicList( const char *s, char *completedname, int length ) { const char *ext = FS_FileExtension( t->filenames[i] ); - if( !Q_stricmp( ext, "wav" ) || !Q_stricmp( ext, "mp3" )); - else continue; + if( Q_stricmp( ext, "wav" ) && Q_stricmp( ext, "mp3" )) + continue; FS_FileBase( t->filenames[i], matchbuf ); Msg( "%16s\n", matchbuf ); @@ -568,7 +568,7 @@ Cmd_GetTexturemodes Prints or complete sound filename ===================================== */ -qboolean Cmd_GetTexturemodes( const char *s, char *completedname, int length ) +qboolean Cmd_GetTextureModes( const char *s, char *completedname, int length ) { int i, numtexturemodes; string texturemodes[6]; // keep an actual ( sizeof( gl_texturemode) / sizeof( gl_texturemode[0] )) @@ -584,7 +584,7 @@ qboolean Cmd_GetTexturemodes( const char *s, char *completedname, int length ) "GL_NEAREST_MIPMAP_NEAREST", }; - // compare gamelist with current keyword + // compare texture filtering mode list with current keyword for( i = 0, numtexturemodes = 0; i < 6; i++ ) { if(( *s == '*' ) || !Q_strnicmp( gl_texturemode[i], s, Q_strlen( s ))) @@ -592,7 +592,7 @@ qboolean Cmd_GetTexturemodes( const char *s, char *completedname, int length ) } if( !numtexturemodes ) return false; - Q_strncpy( matchbuf, gl_texturemode[0], MAX_STRING ); + Q_strncpy( matchbuf, texturemodes[0], MAX_STRING ); if( completedname && length ) Q_strncpy( completedname, matchbuf, length ); if( numtexturemodes == 1 ) return true; @@ -665,6 +665,63 @@ qboolean Cmd_GetGamesList( const char *s, char *completedname, int length ) return true; } +/* +===================================== +Cmd_GetCDList + +Prints or complete CD command name +===================================== +*/ +qboolean Cmd_GetCDList( const char *s, char *completedname, int length ) +{ + int i, numcdcommands; + string cdcommands[8]; + string matchbuf; + + const char *cd_command[] = + { + "info", + "loop", + "off", + "on", + "pause", + "play", + "resume", + "stop", + }; + + // compare CD command list with current keyword + for( i = 0, numcdcommands = 0; i < 8; i++ ) + { + if(( *s == '*' ) || !Q_strnicmp( cd_command[i], s, Q_strlen( s ))) + Q_strcpy( cdcommands[numcdcommands++], cd_command[i] ); + } + + if( !numcdcommands ) return false; + Q_strncpy( matchbuf, cdcommands[0], MAX_STRING ); + if( completedname && length ) Q_strncpy( completedname, matchbuf, length ); + if( numcdcommands == 1 ) return true; + + for( i = 0; i < numcdcommands; i++ ) + { + Q_strncpy( matchbuf, cdcommands[i], MAX_STRING ); + Msg( "%16s\n", matchbuf ); + } + + Msg( "\n^3 %i commands found.\n", numcdcommands ); + + // cut shortestMatch to the amount common with s + if( completedname && length ) + { + for( i = 0; matchbuf[i]; i++ ) + { + if( Q_tolower( completedname[i] ) != Q_tolower( matchbuf[i] )) + completedname[i] = 0; + } + } + return true; +} + qboolean Cmd_CheckMapsList_R( qboolean fRefresh, qboolean onlyingamedir ) { byte buf[MAX_SYSPATH]; @@ -815,7 +872,7 @@ qboolean Cmd_CheckMapsList( qboolean fRefresh ) autocomplete_list_t cmd_list[] = { -{ "gl_texturemode", Cmd_GetTexturemodes }, +{ "gl_texturemode", Cmd_GetTextureModes }, { "map_background", Cmd_GetMapList }, { "changelevel", Cmd_GetMapList }, { "playdemo", Cmd_GetDemoList, }, @@ -832,6 +889,7 @@ autocomplete_list_t cmd_list[] = { "load", Cmd_GetSavesList }, { "play", Cmd_GetSoundList }, { "map", Cmd_GetMapList }, +{ "cd", Cmd_GetCDList }, { NULL }, // termiantor }; diff --git a/engine/common/console.c b/engine/common/console.c index e4f45245..59ebf1fe 100644 --- a/engine/common/console.c +++ b/engine/common/console.c @@ -755,12 +755,6 @@ void Con_Print( const char *txt ) if( txt[l] <= ' ') break; } -#if 0 - // g-cont. experiment from SDLash3D - // word wrap - if( l != con.linewidth && ( con.x + l >= con.linewidth )) - Con_Linefeed(); -#endif txt++; switch( c ) diff --git a/engine/common/imagelib/img_utils.c b/engine/common/imagelib/img_utils.c index 0c29fb4d..06bfe325 100644 --- a/engine/common/imagelib/img_utils.c +++ b/engine/common/imagelib/img_utils.c @@ -462,6 +462,8 @@ void Image_PaletteHueReplace( byte *palSrc, int newHue, int start, int end ) maxcol = max( max( r, g ), b ) / 255.0f; mincol = min( min( r, g ), b ) / 255.0f; + + if( maxcol == 0 ) continue; val = maxcol; sat = (maxcol - mincol) / maxcol; @@ -567,7 +569,7 @@ qboolean Image_Copy8bitRGBA( const byte *in, byte *out, int pixels ) // check for color for( i = 0; i < 256; i++ ) { - col = (rgba_t *)image.d_currentpal[i]; + col = (rgba_t *)&image.d_currentpal[i]; if( col[0] != col[1] || col[1] != col[2] ) { image.flags |= IMAGE_HAS_COLOR; @@ -1114,7 +1116,7 @@ byte *Image_FloodInternal( const byte *indata, int inwidth, int inheight, int ou { if( x < inwidth ) *out++ = *in++; - else *out++; + else out++; } } } diff --git a/engine/common/input.c b/engine/common/input.c index 7b1794d6..34bda414 100644 --- a/engine/common/input.c +++ b/engine/common/input.c @@ -435,7 +435,7 @@ IN_WndProc main window procedure ==================== */ -long IN_WndProc( void *hWnd, uint uMsg, uint wParam, long lParam ) +LONG IN_WndProc( HWND hWnd, UINT uMsg, UINT wParam, LONG lParam ) { int i, temp = 0; qboolean fActivate; diff --git a/engine/common/input.h b/engine/common/input.h index e2e7067d..333bc7c5 100644 --- a/engine/common/input.h +++ b/engine/common/input.h @@ -45,7 +45,7 @@ void IN_MouseEvent( int mstate ); void IN_ActivateMouse( qboolean force ); void IN_DeactivateMouse( void ); void IN_ToggleClientMouse( int newstate, int oldstate ); -long IN_WndProc( void *hWnd, uint uMsg, uint wParam, long lParam ); +LONG IN_WndProc( HWND hWnd, UINT uMsg, UINT wParam, LONG lParam ); void IN_SetCursor( HICON hCursor ); #endif//INPUT_H \ No newline at end of file diff --git a/engine/common/netchan.h b/engine/common/netchan.h index dad19a10..d5d97a48 100644 --- a/engine/common/netchan.h +++ b/engine/common/netchan.h @@ -63,7 +63,7 @@ GNU General Public License for more details. #define PORT_MASTER 27010 #define PORT_CLIENT 27005 #define PORT_SERVER 27015 -#define MULTIPLAYER_BACKUP 64 // how many data slots to use when in multiplayer (must be power of 2) +#define MULTIPLAYER_BACKUP 128 // how many data slots to use when in multiplayer (must be power of 2) #define SINGLEPLAYER_BACKUP 16 // same for single player /* diff --git a/engine/common/soundlib/snd_wav.c b/engine/common/soundlib/snd_wav.c index a82065ad..6616f555 100644 --- a/engine/common/soundlib/snd_wav.c +++ b/engine/common/soundlib/snd_wav.c @@ -76,7 +76,8 @@ static void FindNextChunk( const char *name ) iff_dataPtr += 4; iff_chunkLen = GetLittleLong(); - if( iff_chunkLen < 0 ) + // limit chunk size to 1 mb + if( iff_chunkLen < 0 || iff_chunkLen > ( 1024 * 1024 )) { iff_dataPtr = NULL; return; diff --git a/engine/common/zone.c b/engine/common/zone.c index 5b8e639a..4e753387 100644 --- a/engine/common/zone.c +++ b/engine/common/zone.c @@ -165,9 +165,16 @@ static const char *Mem_CheckFilename( const char *filename ) int i; if( !out ) return dummy; + for( i = 0; i < 128; i++, out++ ) - if( out == '\0' ) break; // valid name - if( i == 128 ) return dummy; + { + if( *out == '\0' ) + break; // valid name + } + + if( i == 128 ) + return dummy; + return filename; } diff --git a/engine/server/server.h b/engine/server/server.h index 6b13388b..df6736e9 100644 --- a/engine/server/server.h +++ b/engine/server/server.h @@ -447,6 +447,7 @@ void SV_RemoteCommand( netadr_t from, sizebuf_t *msg ); void SV_PrepWorldFrame( void ); void SV_ProcessFile( sv_client_t *cl, char *filename ); void SV_SendResourceList( sv_client_t *cl ); +void SV_AddToMaster( netadr_t from, sizebuf_t *msg ); void Master_Add( void ); void Master_Heartbeat( void ); void Master_Packet( void ); diff --git a/engine/server/sv_client.c b/engine/server/sv_client.c index 53842976..2fc915f2 100644 --- a/engine/server/sv_client.c +++ b/engine/server/sv_client.c @@ -548,7 +548,7 @@ char *SV_StatusString( void ) cl = &svs.clients[i]; if( cl->state == cs_connected || cl->state == cs_spawned ) { - Q_sprintf( player, "%i %i \"%s\"\n", (int)cl->edict->v.frags, cl->ping, cl->name ); + Q_sprintf( player, "%i %i \"%s\"\n", (int)cl->edict->v.frags, (int)cl->ping, cl->name ); playerLength = Q_strlen( player ); if( statusLength + playerLength >= sizeof( status )) break; // can't hold any more @@ -642,7 +642,7 @@ void SV_Info( netadr_t from ) int version; // ignore in single player - if( sv_maxclients->integer == 1 ) + if( sv_maxclients->integer == 1 || !svs.initialized ) return; version = Q_atoi( Cmd_Argv( 1 )); @@ -660,9 +660,9 @@ void SV_Info( netadr_t from ) Info_SetValueForKey( string, "host", hostname->string ); Info_SetValueForKey( string, "map", sv.name ); - Info_SetValueForKey( string, "dm", va( "%i", svgame.globals->deathmatch )); - Info_SetValueForKey( string, "team", va( "%i", svgame.globals->teamplay )); - Info_SetValueForKey( string, "coop", va( "%i", svgame.globals->coop )); + Info_SetValueForKey( string, "dm", va( "%i", (int)svgame.globals->deathmatch )); + Info_SetValueForKey( string, "team", va( "%i", (int)svgame.globals->teamplay )); + Info_SetValueForKey( string, "coop", va( "%i", (int)svgame.globals->coop )); Info_SetValueForKey( string, "numcl", va( "%i", count )); Info_SetValueForKey( string, "maxcl", va( "%i", sv_maxclients->integer )); Info_SetValueForKey( string, "gamedir", GI->gamefolder ); @@ -685,7 +685,7 @@ void SV_BuildNetAnswer( netadr_t from ) int i, count = 0; // ignore in single player - if( sv_maxclients->integer == 1 ) + if( sv_maxclients->integer == 1 || !svs.initialized ) return; version = Q_atoi( Cmd_Argv( 1 )); @@ -716,7 +716,7 @@ void SV_BuildNetAnswer( netadr_t from ) { edict_t *ed = svs.clients[i].edict; float time = host.realtime - svs.clients[i].lastconnect; - Q_strncat( string, va( "%c\\%s\\%i\\%f\\", count, svs.clients[i].name, ed->v.frags, time ), sizeof( string )); + Q_strncat( string, va( "%c\\%s\\%i\\%f\\", count, svs.clients[i].name, (int)ed->v.frags, time ), sizeof( string )); count++; } } @@ -1802,6 +1802,14 @@ void SV_UserinfoChanged( sv_client_t *cl, const char *userinfo ) val = Info_ValueForKey( cl->userinfo, "name" ); } + if( !Q_strlen( temp1 ) ) + { + Info_SetValueForKey( cl->userinfo, "name", "unnamed" ); + val = Info_ValueForKey( cl->userinfo, "name" ); + Q_strncpy( temp2, "unnamed", sizeof( temp2 )); + Q_strncpy( temp1, "unnamed", sizeof( temp1 )); + } + // check to see if another user by the same name exists while( 1 ) { @@ -1871,6 +1879,14 @@ void SV_UserinfoChanged( sv_client_t *cl, const char *userinfo ) } else cl->modelindex = 0; + // force reset player model to "player" + if( cl->modelindex == 0 ) + { + Info_SetValueForKey( cl->userinfo, "model", "player" ); + Mod_RegisterModel( "models/player.mdl", SV_ModelIndex( "models/player.mdl" )); + SV_SetModel( ent, "models/player.mdl" ); + } + // call prog code to allow overrides svgame.dllFuncs.pfnClientUserInfoChanged( cl->edict, cl->userinfo ); ent->v.netname = MAKE_STRING( cl->name ); @@ -2038,6 +2054,70 @@ void SV_ExecuteClientCommand( sv_client_t *cl, char *s ) } } +/* +================== +SV_TSourceEngineQuery +================== +*/ +void SV_TSourceEngineQuery( netadr_t from ) +{ + // A2S_INFO + char answer[1024] = ""; + int count = 0, bots = 0, index; + sizebuf_t buf; + + if( svs.clients ) + { + for( index = 0; index < sv_maxclients->integer; index++ ) + { + if( svs.clients[index].state >= cs_connected ) + { + if( svs.clients[index].fakeclient ) + bots++; + else count++; + } + } + } + + BF_Init( &buf, "TSourceEngineQuery", answer, sizeof( answer )); + + BF_WriteByte( &buf, 'm' ); + BF_WriteString( &buf, NET_AdrToString( net_local ) ); + BF_WriteString( &buf, hostname->string ); + BF_WriteString( &buf, sv.name ); + BF_WriteString( &buf, GI->gamefolder ); + BF_WriteString( &buf, GI->title ); + BF_WriteByte( &buf, count ); + BF_WriteByte( &buf, sv_maxclients->integer ); + BF_WriteByte( &buf, PROTOCOL_VERSION ); + BF_WriteByte( &buf, host.type == HOST_DEDICATED ? 'D' : 'L'); + BF_WriteByte( &buf, 'W' ); + + if( Q_stricmp( GI->gamedir, "valve" )) + { + BF_WriteByte( &buf, 1 ); // mod + BF_WriteString( &buf, GI->game_url ); + BF_WriteString( &buf, GI->update_url ); + BF_WriteByte( &buf, 0 ); + BF_WriteLong( &buf, (long)GI->version ); + BF_WriteLong( &buf, GI->size ); + + if( GI->gamemode == 2 ) + BF_WriteByte( &buf, 1 ); // multiplayer_only + else BF_WriteByte( &buf, 0 ); + + if( Q_strstr( GI->game_dll, "hl." )) + BF_WriteByte( &buf, 0 ); // Half-Life DLL + else BF_WriteByte( &buf, 1 ); // Own DLL + } + else BF_WriteByte( &buf, 0 ); // Half-Life + + BF_WriteByte( &buf, GI->secure ); // unsecure + BF_WriteByte( &buf, bots ); + + NET_SendPacket( NS_SERVER, BF_GetNumBytesWritten( &buf ), BF_GetData( &buf ), from ); +} + /* ================= SV_ConnectionlessPacket @@ -2053,9 +2133,6 @@ void SV_ConnectionlessPacket( netadr_t from, sizebuf_t *msg ) char *args; char *c, buf[MAX_SYSPATH]; int len = sizeof( buf ); - uint challenge; - int index, count = 0; - char query[512], ostype = 'w'; BF_Clear( msg ); BF_ReadLong( msg );// skip the -1 marker @@ -2074,46 +2151,9 @@ void SV_ConnectionlessPacket( netadr_t from, sizebuf_t *msg ) else if( !Q_strcmp( c, "connect" )) SV_DirectConnect( from ); else if( !Q_strcmp( c, "rcon" )) SV_RemoteCommand( from, msg ); else if( !Q_strcmp( c, "netinfo" )) SV_BuildNetAnswer( from ); - else if( msg->pData[0] == 0xFF && msg->pData[1] == 0xFF && msg->pData[2] == 0xFF && msg->pData[3] == 0xFF && msg->pData[4] == 0x73 && msg->pData[5] == 0x0A ) - { - Q_memcpy(&challenge, &msg->pData[6], sizeof(int)); - - for( index = 0; index < sv_maxclients->integer; index++ ) - { - if( svs.clients[index].state >= cs_connected ) - count++; - } - - Q_snprintf( query, sizeof( query ), - "0\n" - "\\protocol\\%d" // protocol version - "\\challenge\\%u" // challenge number that got after FF FF FF FF 73 0A - "\\players\\%d" // current player number - "\\max\\%d" // max_players - "\\bots\\0" // bot number? - "\\gamedir\\%s" // gamedir. _xash appended, because Xash3D is not compatible with GS in multiplayer - "\\map\\%s" // current map - "\\type\\d" // server type - "\\password\\0" // is password set - "\\os\\%c" // server OS? - "\\secure\\0" // server anti-cheat? VAC? - "\\lan\\0" // is LAN server? - "\\version\\%f" // server version - "\\region\\255" // server region - "\\product\\%s\n", // product? Where is the difference with gamedir? - PROTOCOL_VERSION, - challenge, - count, - sv_maxclients->integer, - GI->gamefolder, - sv.name, - ostype, - XASH_VERSION, - GI->gamefolder - ); - - NET_SendPacket( NS_SERVER, Q_strlen( query ), query, from ); - } + else if( !Q_strcmp( c, "s")) SV_AddToMaster( from, msg ); + else if( !Q_strcmp( c, "T" "Source" )) SV_TSourceEngineQuery( from ); + else if( !Q_strcmp( c, "i" )) NET_SendPacket( NS_SERVER, 5, "\xFF\xFF\xFF\xFFj", from ); // A2A_PING else if( svgame.dllFuncs.pfnConnectionlessPacket( &from, args, buf, &len )) { // user out of band message (must be handled in CL_ConnectionlessPacket) @@ -2143,7 +2183,6 @@ static void SV_ParseClientMove( sv_client_t *cl, sizebuf_t *msg ) usercmd_t cmds[32], *to; edict_t *player; - numbackup = 2; player = cl->edict; frame = &cl->frames[cl->netchan.incoming_acknowledged & SV_UPDATE_MASK]; diff --git a/engine/server/sv_frame.c b/engine/server/sv_frame.c index 9f22b968..04d000e9 100644 --- a/engine/server/sv_frame.c +++ b/engine/server/sv_frame.c @@ -561,6 +561,16 @@ void SV_WriteEntitiesToClient( sv_client_t *cl, sizebuf_t *msg ) // copy the entity states out frame->num_entities = 0; + + // It will break all connected clients, but it takes more than one week to overflow it + if(( (uint)svs.next_client_entities ) + frame_ents.num_entities >= 0x7FFFFFFE ) + { + // just reset counter + svs.next_client_entities = 0; + // delta is broken now, cannot keep connected clients + SV_FinalMessage( "Server is running to long, reconnecting!", true ); + } + frame->first_entity = svs.next_client_entities; for( i = 0; i < frame_ents.num_entities; i++ ) @@ -569,10 +579,6 @@ void SV_WriteEntitiesToClient( sv_client_t *cl, sizebuf_t *msg ) state = &svs.packet_entities[svs.next_client_entities % svs.num_client_entities]; *state = frame_ents.entities[i]; svs.next_client_entities++; - - // this should never hit, map should always be restarted first in SV_Frame - if( svs.next_client_entities >= 0x7FFFFFFE ) - Host_Error( "svs.next_client_entities wrapped\n" ); frame->num_entities++; } @@ -601,6 +607,7 @@ void SV_SendClientDatagram( sv_client_t *cl ) svs.currentPlayer = cl; svs.currentPlayerNum = (cl - svs.clients); + Q_memset( msg_buf, 0, NET_MAX_PAYLOAD ); BF_Init( &msg, "Datagram", msg_buf, sizeof( msg_buf )); // always send servertime at new frame diff --git a/engine/server/sv_game.c b/engine/server/sv_game.c index 79be26c2..6f3786e2 100644 --- a/engine/server/sv_game.c +++ b/engine/server/sv_game.c @@ -3185,7 +3185,7 @@ void pfnClientPrintf( edict_t* pEdict, PRINT_TYPE ptype, const char *szMsg ) if( sv.state != ss_active ) { // send message into console during loading - MsgDev( D_INFO, szMsg ); + MsgDev( D_INFO, "%s\n", szMsg ); return; } @@ -3198,7 +3198,7 @@ void pfnClientPrintf( edict_t* pEdict, PRINT_TYPE ptype, const char *szMsg ) switch( ptype ) { case print_console: - if( client->fakeclient ) MsgDev( D_INFO, szMsg ); + if( client->fakeclient ) MsgDev( D_INFO, "%s", szMsg ); else SV_ClientPrintf( client, PRINT_HIGH, "%s", szMsg ); break; case print_chat: @@ -3222,7 +3222,7 @@ pfnServerPrint void pfnServerPrint( const char *szMsg ) { // while loading in-progress we can sending message only for local client - if( sv.state != ss_active ) MsgDev( D_INFO, szMsg ); + if( sv.state != ss_active ) MsgDev( D_INFO, "%s", szMsg ); else SV_BroadcastPrintf( PRINT_HIGH, "%s", szMsg ); } @@ -4362,15 +4362,16 @@ pfnCheckParm */ static int pfnCheckParm( char *parm, char **ppnext ) { - static char str[64]; + int i = Sys_CheckParm( parm ); - if( Sys_GetParmFromCmdLine( parm, str )) + if( ppnext != NULL ) { - // get the pointer on cmdline param - if( ppnext ) *ppnext = str; - return 1; + if( i > 0 && i < host.argc - 1 ) + *ppnext = (char*)host.argv[i + 1]; + else *ppnext = NULL; } - return 0; + + return i; } // engine callbacks diff --git a/engine/server/sv_init.c b/engine/server/sv_init.c index 4eefc9ed..53719a1f 100644 --- a/engine/server/sv_init.c +++ b/engine/server/sv_init.c @@ -403,6 +403,9 @@ void SV_DeactivateServer( void ) svgame.dllFuncs.pfnServerDeactivate(); + if( sv_maxclients->integer > 32 ) + Cvar_SetFloat( "maxplayers", 32.0f ); + for( i = 0; i < sv_maxclients->integer; i++ ) { // release client frames @@ -677,7 +680,7 @@ void SV_InitGame( void ) // copy gamemode into svgame.globals svgame.globals->deathmatch = Cvar_VariableInteger( "deathmatch" ); svgame.globals->teamplay = Cvar_VariableInteger( "teamplay" ); - svgame.globals->coop = Cvar_VariableInteger( "coop" ); + svgame.globals->coop = ( sv_maxclients->integer > 1 ) ? Cvar_VariableInteger( "coop" ) : 0; // heartbeats will always be sent to the id master svs.last_heartbeat = MAX_HEARTBEAT; // send immediately diff --git a/engine/server/sv_main.c b/engine/server/sv_main.c index a502894f..4dfe5b56 100644 --- a/engine/server/sv_main.c +++ b/engine/server/sv_main.c @@ -320,7 +320,8 @@ SV_ReadPackets void SV_ReadPackets( void ) { sv_client_t *cl; - int i, qport, curSize; + int i, qport; + size_t curSize; while( NET_GetPacket( NS_SERVER, &net_from, net_message_buffer, &curSize )) { @@ -548,7 +549,12 @@ Host_ServerFrame void Host_ServerFrame( void ) { // if server is not active, do nothing - if( !svs.initialized ) return; + if( !svs.initialized ) + { + // but allow rcon + SV_ReadPackets (); + return; + } svgame.globals->frametime = host.frametime; @@ -599,7 +605,7 @@ void Master_Add( void ) if( !NET_StringToAdr( MASTERSERVER_ADR, &adr )) MsgDev( D_INFO, "Can't resolve adr: %s\n", MASTERSERVER_ADR ); - NET_SendPacket( NS_SERVER, 1, "q", adr ); + NET_SendPacket( NS_SERVER, 2, "q\xFF", adr ); } /* @@ -646,6 +652,54 @@ void Master_Shutdown( void ) NET_SendPacket( NS_SERVER, 2, "\x62\x0A", adr ); } +/* +================= +SV_AddToMaster + +A server info answer to master server. +Master will validate challenge and this server to public list +================= +*/ +void SV_AddToMaster( netadr_t from, sizebuf_t *msg ) +{ + uint challenge; + char s[MAX_INFO_STRING] = "0\n"; // skip 2 bytes of header + int clients = 0, bots = 0, index; + + if( svs.clients ) + { + for( index = 0; index < sv_maxclients->integer; index++ ) + { + if( svs.clients[index].state >= cs_connected ) + { + if( svs.clients[index].fakeclient ) + bots++; + else clients++; + } + } + } + + challenge = BF_ReadUBitLong( msg, sizeof( uint ) << 3 ); + + Info_SetValueForKey( s, "protocol", va( "%d", PROTOCOL_VERSION ) ); // protocol version + Info_SetValueForKey( s, "challenge", va( "%u", challenge ) ); // challenge number + Info_SetValueForKey( s, "players", va( "%d", clients ) ); // current player number, without bots + Info_SetValueForKey( s, "max", sv_maxclients->string ); // max_players + Info_SetValueForKey( s, "bots", va( "%d", bots ) ); // bot count + Info_SetValueForKey( s, "gamedir", GI->gamedir ); // gamedir + Info_SetValueForKey( s, "map", sv.name ); // current map + Info_SetValueForKey( s, "type", (host.type == HOST_DEDICATED) ? "d" : "l" ); // dedicated or local + Info_SetValueForKey( s, "password", "0" ); // is password set + Info_SetValueForKey( s, "os", "w" ); // Windows + Info_SetValueForKey( s, "secure", "0" ); // server anti-cheat + Info_SetValueForKey( s, "lan", "0" ); // LAN servers doesn't send info to master + Info_SetValueForKey( s, "version", va( "%g", XASH_VERSION )); // server region. 255 -- all regions + Info_SetValueForKey( s, "region", "255" ); // server region. 255 -- all regions + Info_SetValueForKey( s, "product", GI->gamefolder ); // product? Where is the difference with gamedir? + + NET_SendPacket( NS_SERVER, Q_strlen( s ), s, from ); +} + //============================================================================ /* diff --git a/engine/server/sv_world.c b/engine/server/sv_world.c index 25afaa47..1654c9c1 100644 --- a/engine/server/sv_world.c +++ b/engine/server/sv_world.c @@ -540,7 +540,7 @@ void SV_FindTouchedLeafs( edict_t *ent, mnode_t *node, int *headnode ) int sides, leafnum; mleaf_t *leaf; - if( node->contents == CONTENTS_SOLID ) + if( !node || node->contents == CONTENTS_SOLID ) return; // add an efrag if the node is a leaf diff --git a/game_launch/game.cpp b/game_launch/game.cpp index 2337aa75..a80b3132 100644 --- a/game_launch/game.cpp +++ b/game_launch/game.cpp @@ -17,6 +17,11 @@ GNU General Public License for more details. #define GAME_PATH "valve" // default dir to start from +#ifdef WIN32 +// enable NVIDIA High Performance Graphics while using Integrated Graphics. +__declspec(dllexport) DWORD NvOptimusEnablement = 0x00000001; +#endif + typedef void (*pfnChangeGame)( const char *progname ); typedef int (*pfnInit)( const char *progname, int bChangeGame, pfnChangeGame func ); typedef void (*pfnShutdown)( void );