diff --git a/engine/client/cl_cmds.c b/engine/client/cl_cmds.c index de83c522..691574c2 100644 --- a/engine/client/cl_cmds.c +++ b/engine/client/cl_cmds.c @@ -146,7 +146,7 @@ void CL_PlayCDTrack_f( void ) if( paused ) Con_Printf( "Paused %s track %u\n", looped ? "looping" : "playing", track ); else Con_Printf( "Currently %s track %u\n", looped ? "looping" : "playing", track ); } - Con_Printf( "Volume is %f\n", Cvar_VariableValue( "MP3Volume" )); + Con_Printf( "Volume is %f\n", s_musicvolume.value ); return; } else Con_Printf( "%s: unknown command %s\n", Cmd_Argv( 0 ), command ); diff --git a/engine/client/cl_demo.c b/engine/client/cl_demo.c index 3a8790b0..c8a7eee2 100644 --- a/engine/client/cl_demo.c +++ b/engine/client/cl_demo.c @@ -285,7 +285,7 @@ void CL_WriteDemoUserCmd( int cmdnumber ) // write usercmd_t MSG_Init( &buf, "UserCmd", data, sizeof( data )); - CL_WriteUsercmd( &buf, -1, cmdnumber ); // always no delta + CL_WriteUsercmd( PROTO_CURRENT, &buf, -1, cmdnumber ); // always no delta, always in current protocol bytes = MSG_GetNumBytesWritten( &buf ); @@ -708,7 +708,7 @@ static void CL_DemoStartPlayback( int mode ) demo.starttime = CL_GetDemoPlaybackClock(); // for determining whether to read another message - Netchan_Setup( NS_CLIENT, &cls.netchan, net_from, Cvar_VariableInteger( "net_qport" ), NULL, CL_GetFragmentSize, 0 ); + CL_SetupNetchanForProtocol( cls.legacymode ); memset( demo.cmds, 0, sizeof( demo.cmds )); demo.angle_position = 1; diff --git a/engine/client/cl_main.c b/engine/client/cl_main.c index 70297bd2..2537185f 100644 --- a/engine/client/cl_main.c +++ b/engine/client/cl_main.c @@ -202,23 +202,23 @@ static void CL_CheckClientState( void ) } } -int CL_GetFragmentSize( void *unused, fragsize_t mode ) +static int CL_GetGoldSrcFragmentSize( void *unused, fragsize_t mode ) { - if( cls.legacymode == PROTO_GOLDSRC ) + switch( mode ) { - switch( mode ) - { - case FRAGSIZE_SPLIT: - return 1200; // MAX_RELIABLE_PAYLOAD - case FRAGSIZE_UNRELIABLE: - return 1400; // MAX_ROUTABLE_PACKET - case FRAGSIZE_FRAG: - if( cls.state == ca_active ) - return bound( 16, cl_dlmax.value, 1024 ); - return 128; - } + case FRAGSIZE_SPLIT: + return 1200; // MAX_RELIABLE_PAYLOAD + case FRAGSIZE_UNRELIABLE: + return 1400; // MAX_ROUTABLE_PACKET + default: + if( cls.state == ca_active ) + return bound( 16, cl_dlmax.value, 1024 ); + return 128; } +} +static int CL_GetFragmentSize( void *unused, fragsize_t mode ) +{ switch( mode ) { case FRAGSIZE_SPLIT: @@ -226,12 +226,10 @@ int CL_GetFragmentSize( void *unused, fragsize_t mode ) case FRAGSIZE_UNRELIABLE: return NET_MAX_MESSAGE; default: - break; + if( Netchan_IsLocal( &cls.netchan )) + return FRAGMENT_LOCAL_SIZE; + return cl_upmax.value; } - - if( Netchan_IsLocal( &cls.netchan )) - return FRAGMENT_LOCAL_SIZE; - return cl_upmax.value; } /* @@ -696,7 +694,7 @@ static void CL_CreateCmd( void ) CL_PredictMovement( false ); } -void CL_WriteUsercmd( sizebuf_t *msg, int from, int to ) +void CL_WriteUsercmd( connprotocol_t proto, sizebuf_t *msg, int from, int to ) { const usercmd_t nullcmd = { 0 }; const usercmd_t *f; @@ -705,15 +703,11 @@ void CL_WriteUsercmd( sizebuf_t *msg, int from, int to ) Assert( from == -1 || ( from >= 0 && from < MULTIPLAYER_BACKUP )); Assert( to >= 0 && to < MULTIPLAYER_BACKUP ); - if( from == -1 ) - f = &nullcmd; - else - f = &cl.commands[from].cmd; - + f = from == -1 ? &nullcmd : &cl.commands[from].cmd; t = &cl.commands[to].cmd; // write it into the buffer - if( cls.legacymode == PROTO_GOLDSRC ) + if( proto == PROTO_GOLDSRC ) { MSG_StartBitWriting( msg ); Delta_WriteGSFields( msg, DT_USERCMD_T, f, t, 0.0f ); @@ -740,6 +734,7 @@ static void CL_WritePacket( void ) int numcmds, maxcmds; int newcmds; int cmdnumber; + const connprotocol_t proto = cls.legacymode; // don't send anything if playing back a demo if( cls.demoplayback || cls.state < ca_connected || cls.state == ca_cinematic ) @@ -757,7 +752,7 @@ static void CL_WritePacket( void ) MSG_Init( &buf, "ClientData", data, sizeof( data )); // Determine number of backup commands to send along - switch( cls.legacymode ) + switch( proto ) { case PROTO_GOLDSRC: maxbackup = MAX_GOLDSRC_BACKUP_CMDS; @@ -835,7 +830,7 @@ static void CL_WritePacket( void ) // begin a client move command MSG_BeginClientCmd( &buf, clc_move ); - if( cls.legacymode == PROTO_GOLDSRC ) + if( proto == PROTO_GOLDSRC ) MSG_WriteByte( &buf, 0 ); // length // save the position for a checksum byte @@ -868,7 +863,7 @@ static void CL_WritePacket( void ) cmdnumber = ( cls.netchan.outgoing_sequence - i ) & CL_UPDATE_MASK; to = cmdnumber; - CL_WriteUsercmd( &buf, from, to ); + CL_WriteUsercmd( proto, &buf, from, to ); from = to; if( MSG_CheckOverflow( &buf )) @@ -876,7 +871,7 @@ static void CL_WritePacket( void ) } // calculate a checksum over the move commands - if( cls.legacymode == PROTO_GOLDSRC ) + if( proto == PROTO_GOLDSRC ) { size = MSG_GetRealBytesWritten( &buf ) - key - 1; @@ -1093,7 +1088,7 @@ We have gotten a challenge from the server, so try and connect. ====================== */ -static void CL_SendConnectPacket( void ) +static void CL_SendConnectPacket( connprotocol_t proto, int challenge ) { char protinfo[MAX_INFO_STRING]; const char *key = ID_GetMD5(); @@ -1116,7 +1111,7 @@ static void CL_SendConnectPacket( void ) // GoldSrc doesn't need sv_cheats set to 0, it's handled by svc_goldsrc_sendextrainfo // it also doesn't need useragent string - if( adr.type != NA_LOOPBACK && cls.legacymode != PROTO_GOLDSRC ) + if( adr.type != NA_LOOPBACK && proto != PROTO_GOLDSRC ) { Cvar_SetCheatState(); Cvar_FullSet( "sv_cheats", "0", FCVAR_READ_ONLY | FCVAR_SERVER ); @@ -1128,7 +1123,7 @@ static void CL_SendConnectPacket( void ) Info_SetValueForKey( protinfo, "a", Q_buildarch(), sizeof( protinfo ) ); } - if( cls.legacymode == PROTO_GOLDSRC ) + if( proto == PROTO_GOLDSRC ) { const char *name; sizebuf_t send; @@ -1150,7 +1145,7 @@ static void CL_SendConnectPacket( void ) MSG_Init( &send, "GoldSrcConnect", send_buf, sizeof( send_buf )); MSG_WriteLong( &send, NET_HEADER_OUTOFBANDPACKET ); MSG_WriteStringf( &send, "connect %i %i \"%s\" \"%s\"\n", - PROTOCOL_GOLDSRC_VERSION, cls.challenge, protinfo, cls.userinfo ); + PROTOCOL_GOLDSRC_VERSION, challenge, protinfo, cls.userinfo ); MSG_SeekToBit( &send, -8, SEEK_CUR ); // rewrite null terminator CL_WriteSteamTicket( &send ); @@ -1160,7 +1155,7 @@ static void CL_SendConnectPacket( void ) NET_SendPacket( NS_CLIENT, MSG_GetNumBytesWritten( &send ), MSG_GetData( &send ), adr ); Con_Printf( "Trying to connect with GoldSrc 48 protocol\n" ); } - else if( cls.legacymode == PROTO_LEGACY ) + else if( proto == PROTO_LEGACY ) { const char *dlmax; int qport = Cvar_VariableInteger( "net_qport" ); @@ -1178,7 +1173,7 @@ static void CL_SendConnectPacket( void ) Info_SetValueForKey( protinfo, "i", key, sizeof( protinfo )); Netchan_OutOfBandPrint( NS_CLIENT, adr, "connect %i %i %i \"%s\" %d \"%s\"\n", - PROTOCOL_LEGACY_VERSION, qport, cls.challenge, cls.userinfo, NET_LEGACY_EXT_SPLIT, protinfo ); + PROTOCOL_LEGACY_VERSION, qport, challenge, cls.userinfo, NET_LEGACY_EXT_SPLIT, protinfo ); Con_Printf( "Trying to connect with legacy protocol\n" ); } else @@ -1200,7 +1195,7 @@ static void CL_SendConnectPacket( void ) Info_SetValueForKey( protinfo, "qport", qport, sizeof( protinfo )); Info_SetValueForKeyf( protinfo, "ext", sizeof( protinfo ), "%d", extensions); - Netchan_OutOfBandPrint( NS_CLIENT, adr, "connect %i %i \"%s\" \"%s\"\n", PROTOCOL_VERSION, cls.challenge, protinfo, cls.userinfo ); + Netchan_OutOfBandPrint( NS_CLIENT, adr, "connect %i %i \"%s\" \"%s\"\n", PROTOCOL_VERSION, challenge, protinfo, cls.userinfo ); Con_Printf( "Trying to connect with modern protocol\n" ); } @@ -1223,12 +1218,12 @@ static int CL_GetTestFragmentSize( void ) return FRAGMENT_MIN_SIZE; } -static void CL_SendGetChallenge( netadr_t to, connprotocol_t proto ) +static void CL_SendGetChallenge( netadr_t to ) { - if( proto == PROTO_GOLDSRC ) - Netchan_OutOfBandPrint( NS_CLIENT, to, "getchallenge steam\n" ); - else - Netchan_OutOfBandPrint( NS_CLIENT, to, "getchallenge\n" ); + // always send GoldSrc-styled getchallenge message + // Xash servers will ignore it but for GoldSrc it will help + // in auto-detection + Netchan_OutOfBandPrint( NS_CLIENT, to, "getchallenge steam\n" ); } /* @@ -1255,9 +1250,10 @@ static void CL_CheckForResend( void ) cls.state = ca_connecting; Q_strncpy( cls.servername, "localhost", sizeof( cls.servername )); cls.serveradr.type = NA_LOOPBACK; + cls.legacymode = PROTO_CURRENT; // we don't need a challenge on the localhost - CL_SendConnectPacket(); + CL_SendConnectPacket( PROTO_CURRENT, 0 ); return; } @@ -1305,7 +1301,7 @@ static void CL_CheckForResend( void ) // too many fails use default connection method Con_Printf( "Bandwidth test failed, fallback to default connecting method\n" ); Con_Printf( "Connecting to %s... (retry #%i)\n", cls.servername, cls.connect_retry + 1 ); - CL_SendGetChallenge( adr, cls.legacymode ); + CL_SendGetChallenge( adr ); Cvar_SetValue( "cl_dlmax", FRAGMENT_MIN_SIZE ); cls.connect_time = host.realtime; cls.connect_retry++; @@ -1325,7 +1321,7 @@ static void CL_CheckForResend( void ) if( bandwidthTest ) Netchan_OutOfBandPrint( NS_CLIENT, adr, "bandwidth %i %i\n", PROTOCOL_VERSION, cls.max_fragment_size ); else - CL_SendGetChallenge( adr, cls.legacymode ); + CL_SendGetChallenge( adr ); } static resource_t *CL_AddResource( resourcetype_t type, const char *name, int size, qboolean bFatalIfMissing, int index ) @@ -1598,6 +1594,35 @@ int CL_GetSplitSize( void ) return (int)cl_dlmax.value; } +void CL_SetupNetchanForProtocol( connprotocol_t proto ) +{ + int (*pfnBlockSize)( void *, fragsize_t ) = CL_GetFragmentSize; + uint flags = 0; + + switch( proto ) + { + case PROTO_GOLDSRC: + SetBits( flags, NETCHAN_USE_MUNGE | NETCHAN_USE_BZIP2 | NETCHAN_GOLDSRC ); + pfnBlockSize = CL_GetGoldSrcFragmentSize; + break; + case PROTO_LEGACY: + if( FBitSet( Q_atoi( Cmd_Argv( 1 )), NET_LEGACY_EXT_SPLIT )) + { + SetBits( flags, NETCHAN_USE_LEGACY_SPLIT ); + Con_Reportf( "^2NET_EXT_SPLIT enabled^7 (packet sizes is %d/%d)\n", (int)cl_dlmax.value, 65536 ); + } + break; + default: + cls.extensions = Q_atoi( Info_ValueForKey( Cmd_Argv( 1 ), "ext" )); + + if( FBitSet( cls.extensions, NET_EXT_SPLITSIZE )) + Con_Reportf( "^2NET_EXT_SPLITSIZE enabled^7 (packet size is %d)\n", (int)cl_dlmax.value ); + break; + } + + Netchan_Setup( NS_CLIENT, &cls.netchan, net_from, Cvar_VariableInteger( "net_qport" ), NULL, pfnBlockSize, flags ); +} + /* ===================== CL_Reconnect @@ -1609,29 +1634,7 @@ static void CL_Reconnect( qboolean setup_netchan ) { if( setup_netchan ) { - uint flags = 0; - - switch( cls.legacymode ) - { - case PROTO_GOLDSRC: - SetBits( flags, NETCHAN_USE_MUNGE | NETCHAN_USE_BZIP2 | NETCHAN_GOLDSRC ); - break; - case PROTO_LEGACY: - if( FBitSet( Q_atoi( Cmd_Argv( 1 )), NET_LEGACY_EXT_SPLIT )) - { - SetBits( flags, NETCHAN_USE_LEGACY_SPLIT ); - Con_Reportf( "^2NET_EXT_SPLIT enabled^7 (packet sizes is %d/%d)\n", (int)cl_dlmax.value, 65536 ); - } - break; - default: - cls.extensions = Q_atoi( Info_ValueForKey( Cmd_Argv( 1 ), "ext" )); - - if( FBitSet( cls.extensions, NET_EXT_SPLITSIZE )) - Con_Reportf( "^2NET_EXT_SPLITSIZE enabled^7 (packet size is %d)\n", (int)cl_dlmax.value ); - break; - } - - Netchan_Setup( NS_CLIENT, &cls.netchan, net_from, Cvar_VariableInteger( "net_qport" ), NULL, CL_GetFragmentSize, flags ); + CL_SetupNetchanForProtocol( cls.legacymode ); } else { @@ -2189,6 +2192,12 @@ static void CL_ConnectionlessPacket( netadr_t from, sizebuf_t *msg ) return; } + if( cls.legacymode != PROTO_GOLDSRC && !Q_strcmp( c, S2C_CONNECTION )) + { + Con_DPrintf( S_ERROR "GoldSrc client connect received but wasn't expected, ignored\n"); + return; + } + CL_Reconnect( true ); UI_SetActiveMenu( cl.background ); } @@ -2259,7 +2268,7 @@ static void CL_ConnectionlessPacket( netadr_t from, sizebuf_t *msg ) { // too many fails use default connection method Con_Printf( "hi-speed connection is failed, use default method\n" ); - CL_SendGetChallenge( from, cls.legacymode ); + CL_SendGetChallenge( from ); Cvar_SetValue( "cl_dlmax", FRAGMENT_DEFAULT_SIZE ); cls.connect_time = host.realtime; return; @@ -2281,7 +2290,7 @@ static void CL_ConnectionlessPacket( netadr_t from, sizebuf_t *msg ) // packet was sucessfully delivered, adjust the fragment size and get challenge Con_DPrintf( "CRC %x is matched, get challenge, fragment size %d\n", crcValue, cls.max_fragment_size ); - CL_SendGetChallenge( from, cls.legacymode ); + CL_SendGetChallenge( from ); Cvar_SetValue( "cl_dlmax", cls.max_fragment_size ); cls.connect_time = host.realtime; } @@ -2291,7 +2300,7 @@ static void CL_ConnectionlessPacket( netadr_t from, sizebuf_t *msg ) { // too many fails use default connection method Con_Printf( "hi-speed connection is failed, use default method\n" ); - CL_SendGetChallenge( from, cls.legacymode ); + CL_SendGetChallenge( from ); Cvar_SetValue( "cl_dlmax", FRAGMENT_MIN_SIZE ); cls.connect_time = host.realtime; return; @@ -2318,9 +2327,12 @@ static void CL_ConnectionlessPacket( netadr_t from, sizebuf_t *msg ) if( !CL_IsFromConnectingServer( from )) return; + // try to autodetect protocol by challenge response + if( !Q_strcmp( c, S2C_CHALLENGE )) + cls.legacymode = PROTO_GOLDSRC; + // challenge from the server we are connecting to - cls.challenge = Q_atoi( Cmd_Argv( 1 )); - CL_SendConnectPacket(); + CL_SendConnectPacket( cls.legacymode, Q_atoi( Cmd_Argv( 1 ))); return; } else if( !Q_strcmp( c, "echo" )) @@ -2511,9 +2523,6 @@ static void CL_ReadNetMessage( void ) switch( cls.legacymode ) { - case PROTO_CURRENT: - parsefn = CL_ParseServerMessage; - break; case PROTO_LEGACY: parsefn = CL_ParseLegacyServerMessage; break; @@ -2524,8 +2533,8 @@ static void CL_ReadNetMessage( void ) parsefn = CL_ParseGoldSrcServerMessage; break; default: - ASSERT( 0 ); - return; + parsefn = CL_ParseServerMessage; + break; } while( CL_GetMessage( net_message_buffer, &curSize )) diff --git a/engine/client/cl_parse.c b/engine/client/cl_parse.c index 83bab0ac..46317ad3 100644 --- a/engine/client/cl_parse.c +++ b/engine/client/cl_parse.c @@ -925,9 +925,13 @@ void CL_ParseServerData( sizebuf_t *msg, connprotocol_t proto ) clgame.maxEntities = bound( MIN_LEGACY_EDICTS, clgame.maxEntities, MAX_GOLDSRC_EDICTS ); clgame.maxModels = 512; // ??? Q_strncpy( clgame.maptitle, clgame.mapname, sizeof( clgame.maptitle )); + + Host_ValidateEngineFeatures( 0, 0 ); } else { + uint32_t mask; + cl.playernum = MSG_ReadByte( msg ); cl.maxclients = MSG_ReadByte( msg ); clgame.maxEntities = MSG_ReadWord( msg ); @@ -935,17 +939,19 @@ void CL_ParseServerData( sizebuf_t *msg, connprotocol_t proto ) { clgame.maxEntities = bound( MIN_LEGACY_EDICTS, clgame.maxEntities, MAX_LEGACY_EDICTS ); clgame.maxModels = 512; // ??? + mask = ENGINE_LEGACY_FEATURES_MASK; } else { clgame.maxEntities = bound( MIN_EDICTS, clgame.maxEntities, MAX_EDICTS ); clgame.maxModels = MSG_ReadWord( msg ); + mask = ENGINE_FEATURES_MASK; } Q_strncpy( clgame.mapname, MSG_ReadString( msg ), sizeof( clgame.mapname )); Q_strncpy( clgame.maptitle, MSG_ReadString( msg ), sizeof( clgame.maptitle )); background = MSG_ReadOneBit( msg ); Q_strncpy( gamefolder, MSG_ReadString( msg ), sizeof( gamefolder )); - Host_ValidateEngineFeatures( MSG_ReadDword( msg )); + Host_ValidateEngineFeatures( 0, MSG_ReadDword( msg )); if( proto != PROTO_LEGACY ) { diff --git a/engine/client/client.h b/engine/client/client.h index 3cb97c91..c31380f7 100644 --- a/engine/client/client.h +++ b/engine/client/client.h @@ -560,7 +560,6 @@ typedef struct byte datagram_buf[MAX_DATAGRAM]; netchan_t netchan; - int challenge; // from the server to use for connecting float packet_loss; double packet_loss_recalc_time; @@ -748,8 +747,8 @@ void CL_Particle( const vec3_t org, int color, float life, int zpos, int zvel ); void CL_Init( void ); void CL_Disconnect_f( void ); void CL_ProcessFile( qboolean successfully_received, const char *filename ); -void CL_WriteUsercmd( sizebuf_t *msg, int from, int to ); -int CL_GetFragmentSize( void *unused , fragsize_t mode ); +void CL_WriteUsercmd( connprotocol_t proto, sizebuf_t *msg, int from, int to ); +void CL_SetupNetchanForProtocol( connprotocol_t proto ); qboolean CL_PrecacheResources( void ); void CL_SetupOverviewParams( void ); void CL_UpdateFrameLerp( void ); diff --git a/engine/common/common.h b/engine/common/common.h index 39363195..30150992 100644 --- a/engine/common/common.h +++ b/engine/common/common.h @@ -537,7 +537,7 @@ void Host_WriteVideoConfig( void ); void Host_WriteConfig( void ); void Host_ShutdownServer( void ); void Host_Error( const char *error, ... ) _format( 1 ); -void Host_ValidateEngineFeatures( uint32_t features ); +void Host_ValidateEngineFeatures( uint32_t mask, uint32_t features ); void Host_Frame( double time ); void Host_Credits( void ); diff --git a/engine/common/host.c b/engine/common/host.c index 794321c5..ea7ae2b4 100644 --- a/engine/common/host.c +++ b/engine/common/host.c @@ -228,27 +228,8 @@ Host_ValidateEngineFeatures validate features bits and set host.features ============== */ -void Host_ValidateEngineFeatures( uint32_t features ) +void Host_ValidateEngineFeatures( uint32_t mask, uint32_t features ) { - uint32_t mask = ENGINE_FEATURES_MASK; - -#if !XASH_DEDICATED - if( !Host_IsDedicated( )) - { - switch( cls.legacymode ) - { - case PROTO_CURRENT: - break; - case PROTO_LEGACY: - mask = ENGINE_LEGACY_FEATURES_MASK; - break; - default: - mask = 0; - break; - } - } -#endif - // don't allow unsupported bits features &= mask; diff --git a/engine/server/sv_phys.c b/engine/server/sv_phys.c index 83e1353d..c47887a8 100644 --- a/engine/server/sv_phys.c +++ b/engine/server/sv_phys.c @@ -2146,18 +2146,18 @@ qboolean SV_InitPhysicsAPI( void ) if( svgame.physFuncs.SV_CheckFeatures != NULL ) { // grab common engine features (it will be shared across the network) - Host_ValidateEngineFeatures( svgame.physFuncs.SV_CheckFeatures( )); + Host_ValidateEngineFeatures( ENGINE_FEATURES_MASK, svgame.physFuncs.SV_CheckFeatures( )); } return true; } // make sure what physic functions is cleared memset( &svgame.physFuncs, 0, sizeof( svgame.physFuncs )); - Host_ValidateEngineFeatures( 0 ); + Host_ValidateEngineFeatures( ENGINE_FEATURES_MASK, 0 ); return false; // just tell user about problems } // physic interface is missed - Host_ValidateEngineFeatures( 0 ); + Host_ValidateEngineFeatures( ENGINE_FEATURES_MASK, 0 ); return true; }