diff options
Diffstat (limited to 'src/client')
-rw-r--r-- | src/client/cl_cgame.c | 74 | ||||
-rw-r--r-- | src/client/cl_cin.c | 2 | ||||
-rw-r--r-- | src/client/cl_console.c | 20 | ||||
-rw-r--r-- | src/client/cl_input.c | 25 | ||||
-rw-r--r-- | src/client/cl_keys.c | 55 | ||||
-rw-r--r-- | src/client/cl_main.c | 277 | ||||
-rw-r--r-- | src/client/cl_net_chan.c | 106 | ||||
-rw-r--r-- | src/client/cl_parse.c | 57 | ||||
-rw-r--r-- | src/client/cl_scrn.c | 10 | ||||
-rw-r--r-- | src/client/cl_ui.c | 84 | ||||
-rw-r--r-- | src/client/client.h | 137 |
11 files changed, 750 insertions, 97 deletions
diff --git a/src/client/cl_cgame.c b/src/client/cl_cgame.c index da15e4e6..ab46e298 100644 --- a/src/client/cl_cgame.c +++ b/src/client/cl_cgame.c @@ -23,6 +23,7 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA // cl_cgame.c -- client system interaction with client game +#include <setjmp.h> #include "client.h" #ifdef USE_MUMBLE @@ -33,6 +34,8 @@ extern qboolean loadCamera(const char *name); extern void startCamera(int time); extern qboolean getCameraInfo(int time, vec3_t *origin, vec3_t *angles); +int cgInterface; + /* ==================== CL_GetGameState @@ -98,7 +101,13 @@ qboolean CL_GetParseEntityState( int parseEntityNumber, entityState_t *state ) { return qfalse; } - *state = cl.parseEntities[ parseEntityNumber & ( MAX_PARSE_ENTITIES - 1 ) ]; + if ( cgInterface == 2 ) { + entityState_t *es = &cl.parseEntities[ parseEntityNumber & ( MAX_PARSE_ENTITIES - 1 ) ]; + memcpy( state, es, (size_t)&((entityState_t *)0)->weaponAnim ); + state->weaponAnim = es->generic1; + } else { + *state = cl.parseEntities[ parseEntityNumber & ( MAX_PARSE_ENTITIES - 1 ) ]; + } return qtrue; } @@ -144,20 +153,32 @@ qboolean CL_GetSnapshot( int snapshotNumber, snapshot_t *snapshot ) { // write the snapshot snapshot->snapFlags = clSnap->snapFlags; - snapshot->serverCommandSequence = clSnap->serverCommandNum; snapshot->ping = clSnap->ping; snapshot->serverTime = clSnap->serverTime; Com_Memcpy( snapshot->areamask, clSnap->areamask, sizeof( snapshot->areamask ) ); - snapshot->ps = clSnap->ps; count = clSnap->numEntities; if ( count > MAX_ENTITIES_IN_SNAPSHOT ) { Com_DPrintf( "CL_GetSnapshot: truncated %i entities to %i\n", count, MAX_ENTITIES_IN_SNAPSHOT ); count = MAX_ENTITIES_IN_SNAPSHOT; } - snapshot->numEntities = count; - for ( i = 0 ; i < count ; i++ ) { - snapshot->entities[i] = - cl.parseEntities[ ( clSnap->parseEntitiesNum + i ) & (MAX_PARSE_ENTITIES-1) ]; + if ( cgInterface == 2 ) { + alternateSnapshot_t *altSnapshot = (alternateSnapshot_t *)snapshot; + altSnapshot->ps = clSnap->alternatePs; + altSnapshot->serverCommandSequence = clSnap->serverCommandNum; + altSnapshot->numEntities = count; + for ( i = 0 ; i < count ; i++ ) { + entityState_t *es = &cl.parseEntities[ ( clSnap->parseEntitiesNum + i ) & (MAX_PARSE_ENTITIES-1) ]; + memcpy( &altSnapshot->entities[i], es, (size_t)&((entityState_t *)0)->weaponAnim ); + altSnapshot->entities[i].generic1 = es->generic1; + } + } else { + snapshot->ps = clSnap->ps; + snapshot->serverCommandSequence = clSnap->serverCommandNum; + snapshot->numEntities = count; + for ( i = 0 ; i < count ; i++ ) { + snapshot->entities[i] = + cl.parseEntities[ ( clSnap->parseEntitiesNum + i ) & (MAX_PARSE_ENTITIES-1) ]; + } } // FIXME: configstring changes and server commands!!! @@ -379,6 +400,9 @@ static int FloatAsInt( float f ) { return fi.i; } +static jmp_buf cgProbingJB; +static qboolean probingCG = qfalse; + /* ==================== CL_CgameSystemCalls @@ -387,11 +411,26 @@ The cgame module is making a system call ==================== */ intptr_t CL_CgameSystemCalls( intptr_t *args ) { + if( cgInterface == 2 && args[0] >= CG_R_SETCLIPREGION && args[0] < CG_MEMSET ) { + if( args[0] < CG_S_STOPBACKGROUNDTRACK - 1 ) { + args[0] += 1; + } else if( args[0] < CG_S_STOPBACKGROUNDTRACK + 4 ) { + args[0] += CG_PARSE_ADD_GLOBAL_DEFINE - CG_S_STOPBACKGROUNDTRACK + 1; + } else if( args[0] < CG_PARSE_ADD_GLOBAL_DEFINE + 4 ) { + args[0] -= 4; + } else if( args[0] >= CG_PARSE_SOURCE_FILE_AND_LINE && args[0] <= CG_S_SOUNDDURATION ) { + args[0] = CG_PARSE_SOURCE_FILE_AND_LINE - 1337 - args[0] ; + } + } + switch( args[0] ) { case CG_PRINT: Com_Printf( "%s", (const char*)VMA(1) ); return 0; case CG_ERROR: + if( probingCG ) { + longjmp( cgProbingJB, 1 ); + } Com_Error( ERR_DROP, "%s", (const char*)VMA(1) ); return 0; case CG_MILLISECONDS: @@ -732,6 +771,7 @@ void CL_InitCGame( void ) { const char *info; const char *mapname; int t1, t2; + char backup[ MAX_STRING_CHARS ]; vmInterpret_t interpret; t1 = Sys_Milliseconds(); @@ -756,6 +796,26 @@ void CL_InitCGame( void ) { } clc.state = CA_LOADING; + Cvar_VariableStringBuffer( "cl_voipSendTarget", backup, sizeof( backup ) ); + Cvar_Set( "cl_voipSendTarget", "" ); + cgInterface = 0; + probingCG = qtrue; + if ( setjmp( cgProbingJB ) == 0 ) { + VM_Call( cgvm, CG_VOIP_STRING ); + } else { + VM_ClearCallLevel( cgvm ); + cgInterface = 2; + } + probingCG = qfalse; + Cvar_Set( "cl_voipSendTarget", backup ); + + if ( ( clc.netchan.alternateProtocol == 2 ) != ( cgInterface == 2 ) ) { + Com_Error( ERR_DROP, "%s protocol %i, but a cgame module using the %s interface was found", + ( clc.demoplaying ? "Demo was recorded using" : "Server uses" ), + ( clc.netchan.alternateProtocol == 0 ? PROTOCOL_VERSION : clc.netchan.alternateProtocol == 1 ? 70 : 69 ), + ( cgInterface == 2 ? "1.1" : "non-1.1" ) ); + } + // init for this gamestate // use the lastExecutedServerCommand instead of the serverCommandSequence // otherwise server commands sent just before a gamestate are dropped diff --git a/src/client/cl_cin.c b/src/client/cl_cin.c index 72176f42..01d9f892 100644 --- a/src/client/cl_cin.c +++ b/src/client/cl_cin.c @@ -1447,7 +1447,7 @@ int CIN_PlayCinematic( const char *arg, int x, int y, int w, int h, int systemBi if (cinTable[currentHandle].alterGameState) { // close the menu if ( uivm ) { - VM_Call( uivm, UI_SET_ACTIVE_MENU, UIMENU_NONE ); + VM_Call( uivm, UI_SET_ACTIVE_MENU - ( uiInterface == 2 ? 2 : 0 ), UIMENU_NONE ); } } else { cinTable[currentHandle].playonwalls = cl_inGameVideo->integer; diff --git a/src/client/cl_console.c b/src/client/cl_console.c index 01e71ce0..0b58d540 100644 --- a/src/client/cl_console.c +++ b/src/client/cl_console.c @@ -588,6 +588,26 @@ void Con_DrawConsole( void ) { if( Key_GetCatcher( ) & ( KEYCATCH_UI | KEYCATCH_CGAME ) ) return; + + // draw the chat line + if( clc.state >= CA_CONNECTING && clc.serverAddress.alternateProtocol == 2 && ( Key_GetCatcher( ) & KEYCATCH_MESSAGE ) ) + { + int skip; + + if( chat_team ) + { + SCR_DrawBigString( 8, 232, "Team Say:", 1.0f, qfalse ); + skip = 11; + } + else + { + SCR_DrawBigString( 8, 232, "Say:", 1.0f, qfalse ); + skip = 5; + } + + Field_BigDraw( &chatField, skip * BIGCHAR_WIDTH, 232, + SCREEN_WIDTH - ( skip + 1 ) * BIGCHAR_WIDTH, qfalse, qtrue ); + } } //================================================================ diff --git a/src/client/cl_input.c b/src/client/cl_input.c index 7a917df2..7d0a692e 100644 --- a/src/client/cl_input.c +++ b/src/client/cl_input.c @@ -269,7 +269,7 @@ void IN_Button15Down(void) {IN_KeyDown(&in_buttons[15]);} void IN_Button15Up(void) {IN_KeyUp(&in_buttons[15]);} void IN_CenterView (void) { - cl.viewangles[PITCH] = -SHORT2ANGLE(cl.snap.ps.delta_angles[PITCH]); + cl.viewangles[PITCH] = -SHORT2ANGLE((clc.netchan.alternateProtocol == 2 ? cl.snap.alternatePs.delta_angles : cl.snap.ps.delta_angles)[PITCH]); } @@ -795,12 +795,25 @@ void CL_WritePacket( void ) { { if((clc.voipFlags & VOIP_SPATIAL) || Com_IsVoipTarget(clc.voipTargets, sizeof(clc.voipTargets), -1)) { + if ( clc.netchan.alternateProtocol != 0 ) { + MSG_WriteByte (&buf, clc_EOF); + MSG_WriteByte (&buf, clc_voipSpeex); + } MSG_WriteByte (&buf, clc_voipOpus); MSG_WriteByte (&buf, clc.voipOutgoingGeneration); MSG_WriteLong (&buf, clc.voipOutgoingSequence); MSG_WriteByte (&buf, clc.voipOutgoingDataFrames); - MSG_WriteData (&buf, clc.voipTargets, sizeof(clc.voipTargets)); - MSG_WriteByte(&buf, clc.voipFlags); + if ( clc.netchan.alternateProtocol == 0 ) { + MSG_WriteData (&buf, clc.voipTargets, sizeof(clc.voipTargets)); + MSG_WriteByte(&buf, clc.voipFlags); + } else { + MSG_WriteLong (&buf, clc.voipTargets[0] | (clc.voipTargets[1] << 8) | + (clc.voipTargets[2] << 16) | ((clc.voipTargets[3] & 0x7F) << 24)); + MSG_WriteLong (&buf, (clc.voipTargets[3] >> 7) | + (clc.voipTargets[4] << 1) | (clc.voipTargets[5] << 9) | + (clc.voipTargets[6] << 17) | ((clc.voipTargets[7] & 0x3F) << 25)); + MSG_WriteLong (&buf, clc.voipTargets[7] >> 6); + } MSG_WriteShort (&buf, clc.voipOutgoingDataSize); MSG_WriteData (&buf, clc.voipOutgoingData, clc.voipOutgoingDataSize); @@ -822,7 +835,9 @@ void CL_WritePacket( void ) { MSG_WriteLong (&fakemsg, clc.voipOutgoingSequence); MSG_WriteByte (&fakemsg, clc.voipOutgoingDataFrames); MSG_WriteShort (&fakemsg, clc.voipOutgoingDataSize ); - MSG_WriteBits (&fakemsg, clc.voipFlags, VOIP_FLAGCNT); + if ( clc.netchan.alternateProtocol == 0 ) { + MSG_WriteBits (&fakemsg, clc.voipFlags, VOIP_FLAGCNT); + } MSG_WriteData (&fakemsg, clc.voipOutgoingData, voipSize); MSG_WriteByte (&fakemsg, svc_EOF); CL_WriteDemoMessage (&fakemsg, 0); @@ -862,7 +877,7 @@ void CL_WritePacket( void ) { // also use the message acknowledge key ^= clc.serverMessageSequence; // also use the last acknowledged server command in the key - key ^= MSG_HashKey(clc.serverCommands[ clc.serverCommandSequence & (MAX_RELIABLE_COMMANDS-1) ], 32); + key ^= MSG_HashKey(clc.netchan.alternateProtocol, clc.serverCommands[ clc.serverCommandSequence & (MAX_RELIABLE_COMMANDS-1) ], 32); // write all the commands, including the predicted command for ( i = 0 ; i < count ; i++ ) { diff --git a/src/client/cl_keys.c b/src/client/cl_keys.c index 52a5f7a2..08c6c79b 100644 --- a/src/client/cl_keys.c +++ b/src/client/cl_keys.c @@ -35,6 +35,9 @@ int historyLine; // the line being displayed from history buffer // will be <= nextHistoryLine field_t g_consoleField; +field_t chatField; +qboolean chat_team; +int chat_playerNum; qboolean key_overstrikeMode; @@ -706,6 +709,42 @@ void Console_Key (int key) { } +/* +================ +Message_Key + +In game talk message +================ +*/ +void Message_Key( int key ) { + char buffer[MAX_STRING_CHARS]; + + if ( key == K_ESCAPE ) { + Key_SetCatcher( Key_GetCatcher( ) & ~KEYCATCH_MESSAGE ); + Field_Clear( &chatField ); + return; + } + + if ( key == K_ENTER || key == K_KP_ENTER ) { + if ( chatField.buffer[ 0 ] && clc.state == CA_ACTIVE ) { + if ( chat_playerNum != -1 ) { + Com_sprintf( buffer, sizeof( buffer ), "tell %i \"%s\"\n", chat_playerNum, chatField.buffer ); + } else if ( chat_team ) { + Com_sprintf( buffer, sizeof( buffer ), "say_team \"%s\"\n", chatField.buffer ); + } else { + Com_sprintf( buffer, sizeof( buffer ), "say \"%s\"\n", chatField.buffer ); + } + + CL_AddReliableCommand( buffer, qfalse ); + } + Key_SetCatcher( Key_GetCatcher( ) & ~KEYCATCH_MESSAGE ); + Field_Clear( &chatField ); + return; + } + + Field_KeyDownEvent( &chatField, key ); +} + //============================================================================ @@ -1190,6 +1229,12 @@ void CL_KeyDownEvent( int key, unsigned time ) // escape is always handled special if ( key == K_ESCAPE ) { + if ( clc.state >= CA_CONNECTING && clc.serverAddress.alternateProtocol == 2 && ( Key_GetCatcher( ) & KEYCATCH_MESSAGE ) ) { + // clear message mode + Message_Key( key ); + return; + } + // escape always gets out of CGAME stuff if (Key_GetCatcher( ) & KEYCATCH_CGAME) { Key_SetCatcher( Key_GetCatcher( ) & ~KEYCATCH_CGAME ); @@ -1199,12 +1244,12 @@ void CL_KeyDownEvent( int key, unsigned time ) if ( !( Key_GetCatcher( ) & KEYCATCH_UI ) ) { if ( clc.state == CA_ACTIVE && !clc.demoplaying ) { - VM_Call( uivm, UI_SET_ACTIVE_MENU, UIMENU_INGAME ); + VM_Call( uivm, UI_SET_ACTIVE_MENU - ( uiInterface == 2 ? 2 : 0 ), UIMENU_INGAME ); } else if ( clc.state != CA_DISCONNECTED ) { CL_Disconnect_f(); S_StopAllSounds(); - VM_Call( uivm, UI_SET_ACTIVE_MENU, UIMENU_MAIN ); + VM_Call( uivm, UI_SET_ACTIVE_MENU - ( uiInterface == 2 ? 2 : 0 ), UIMENU_MAIN ); } return; } @@ -1227,6 +1272,8 @@ void CL_KeyDownEvent( int key, unsigned time ) if ( cgvm ) { VM_Call( cgvm, CG_KEY_EVENT, key, qtrue ); } + } else if ( clc.state >= CA_CONNECTING && clc.serverAddress.alternateProtocol == 2 && ( Key_GetCatcher( ) & KEYCATCH_MESSAGE ) ) { + Message_Key( key ); } else if ( clc.state == CA_DISCONNECTED ) { Console_Key( key ); } @@ -1305,6 +1352,10 @@ void CL_CharEvent( int key ) { { VM_Call( uivm, UI_KEY_EVENT, key | K_CHAR_FLAG, qtrue ); } + else if ( clc.state >= CA_CONNECTING && clc.serverAddress.alternateProtocol == 2 && ( Key_GetCatcher( ) & KEYCATCH_MESSAGE ) ) + { + Field_CharEvent( &chatField, key ); + } else if ( clc.state == CA_DISCONNECTED ) { Field_CharEvent( &g_consoleField, key ); diff --git a/src/client/cl_main.c b/src/client/cl_main.c index 203e90d5..91ce5d81 100644 --- a/src/client/cl_main.c +++ b/src/client/cl_main.c @@ -186,11 +186,19 @@ void CL_UpdateMumble(void) return; // !!! FIXME: not sure if this is even close to correct. - AngleVectors( cl.snap.ps.viewangles, forward, NULL, up); + if(clc.netchan.alternateProtocol == 2) { + AngleVectors( cl.snap.alternatePs.viewangles, forward, NULL, up); - pos[0] = cl.snap.ps.origin[0] * scale; - pos[1] = cl.snap.ps.origin[2] * scale; - pos[2] = cl.snap.ps.origin[1] * scale; + pos[0] = cl.snap.alternatePs.origin[0] * scale; + pos[1] = cl.snap.alternatePs.origin[2] * scale; + pos[2] = cl.snap.alternatePs.origin[1] * scale; + } else { + AngleVectors( cl.snap.ps.viewangles, forward, NULL, up); + + pos[0] = cl.snap.ps.origin[0] * scale; + pos[1] = cl.snap.ps.origin[2] * scale; + pos[2] = cl.snap.ps.origin[1] * scale; + } tmp = forward[1]; forward[1] = forward[2]; @@ -783,7 +791,7 @@ void CL_Record_f( void ) { continue; } MSG_WriteByte (&buf, svc_baseline); - MSG_WriteDeltaEntity (&buf, &nullstate, ent, qtrue ); + MSG_WriteDeltaEntity (clc.netchan.alternateProtocol, &buf, &nullstate, ent, qtrue); } MSG_WriteByte( &buf, svc_EOF ); @@ -1097,6 +1105,7 @@ void CL_PlayDemo_f( void ) { clc.state = CA_CONNECTED; clc.demoplaying = qtrue; Q_strncpyz( clc.servername, arg, sizeof( clc.servername ) ); + clc.netchan.alternateProtocol = ( protocol == 69 ? 2 : protocol == 70 ? 1 : 0 ); // read demo messages until connected while ( clc.state >= CA_CONNECTED && clc.state < CA_PRIMED ) { @@ -1195,6 +1204,66 @@ void CL_DemoName( char *buffer, int size ) { //====================================================================== /* +================ +Con_MessageMode_f +================ +*/ +void Con_MessageMode_f (void) { + chat_playerNum = -1; + chat_team = qfalse; + Field_Clear( &chatField ); + chatField.widthInChars = 30; + Key_SetCatcher( Key_GetCatcher( ) ^ KEYCATCH_MESSAGE ); +} + +/* +================ +Con_MessageMode2_f +================ +*/ +void Con_MessageMode2_f (void) { + chat_playerNum = -1; + chat_team = qtrue; + Field_Clear( &chatField ); + chatField.widthInChars = 25; + Key_SetCatcher( Key_GetCatcher( ) ^ KEYCATCH_MESSAGE ); +} + +/* +================ +Con_MessageMode3_f +================ +*/ +void Con_MessageMode3_f (void) { + chat_playerNum = VM_Call( cgvm, CG_CROSSHAIR_PLAYER ); + if ( chat_playerNum < 0 || chat_playerNum >= MAX_CLIENTS ) { + chat_playerNum = -1; + return; + } + chat_team = qfalse; + Field_Clear( &chatField ); + chatField.widthInChars = 30; + Key_SetCatcher( Key_GetCatcher( ) ^ KEYCATCH_MESSAGE ); +} + +/* +================ +Con_MessageMode4_f +================ +*/ +void Con_MessageMode4_f (void) { + chat_playerNum = VM_Call( cgvm, CG_LAST_ATTACKER ); + if ( chat_playerNum < 0 || chat_playerNum >= MAX_CLIENTS ) { + chat_playerNum = -1; + return; + } + chat_team = qfalse; + Field_Clear( &chatField ); + chatField.widthInChars = 30; + Key_SetCatcher( Key_GetCatcher( ) ^ KEYCATCH_MESSAGE ); +} + +/* ===================== CL_ShutdownAll ===================== @@ -1375,6 +1444,14 @@ void CL_Disconnect( qboolean showMainMenu ) { return; } + if ( clc.state >= CA_CONNECTING && clc.serverAddress.alternateProtocol == 2 ) { + Field_Clear( &chatField ); + Cmd_RemoveCommand( "messagemode" ); + Cmd_RemoveCommand( "messagemode2" ); + Cmd_RemoveCommand( "messagemode3" ); + Cmd_RemoveCommand( "messagemode4" ); + } + // shutting down the client so enter full screen ui mode Cvar_Set("r_uiFullScreen", "1"); @@ -1422,7 +1499,7 @@ void CL_Disconnect( qboolean showMainMenu ) { } if ( uivm && showMainMenu ) { - VM_Call( uivm, UI_SET_ACTIVE_MENU, UIMENU_NONE ); + VM_Call( uivm, UI_SET_ACTIVE_MENU - ( uiInterface == 2 ? 2 : 0 ), UIMENU_NONE ); } SCR_StopCinematic (); @@ -1606,15 +1683,28 @@ CL_Connect_f */ void CL_Connect_f( void ) { char *server; + int alternateProtocol; const char *serverString; int argc = Cmd_Argc(); netadrtype_t family = NA_UNSPEC; - if ( argc != 2 && argc != 3 ) { - Com_Printf( "usage: connect [-4|-6] server\n"); + if ( argc < 2 || argc > 4 ) { + Com_Printf( "usage: connect [-4|-6] server [-g|-1]\n"); return; } + alternateProtocol = 0; + if ( argc == 2 ) { + } else if ( !strcmp( Cmd_Argv( argc - 1 ), "-g" ) ) { + alternateProtocol = 1; + --argc; + } else if ( !strcmp( Cmd_Argv( argc - 1 ), "-1" ) ) { + alternateProtocol = 2; + --argc; + } else if ( argc == 4 ) { + --argc; + } + if(argc == 2) server = Cmd_Argv(1); else @@ -1659,6 +1749,7 @@ void CL_Connect_f( void ) { if (clc.serverAddress.port == 0) { clc.serverAddress.port = BigShort( PORT_SERVER ); } + clc.serverAddress.alternateProtocol = alternateProtocol; serverString = NET_AdrToStringwPort(clc.serverAddress); @@ -1681,6 +1772,14 @@ void CL_Connect_f( void ) { clc.challenge = ((rand() << 16) ^ rand()) ^ Com_Milliseconds(); } + if( alternateProtocol == 2 ) + { + Cmd_AddCommand( "messagemode", Con_MessageMode_f ); + Cmd_AddCommand( "messagemode2", Con_MessageMode2_f ); + Cmd_AddCommand( "messagemode3", Con_MessageMode3_f ); + Cmd_AddCommand( "messagemode4", Con_MessageMode4_f ); + } + Key_SetCatcher( Key_GetCatcher() & KEYCATCH_CONSOLE ); clc.connectTime = -99999; // CL_CheckForResend() will fire immediately clc.connectPacketCount = 0; @@ -2368,7 +2467,14 @@ void CL_CheckForResend( void ) { // The challenge request shall be followed by a client challenge so no malicious server can hijack this connection. // Add the gamename so the server knows we're running the correct game or can reject the client // with a meaningful message - Com_sprintf(data, sizeof(data), "getchallenge %d %s", clc.challenge, com_gamename->string); + if ( clc.serverAddress.alternateProtocol == 2 ) { + Com_sprintf(data, sizeof(data), "getchallenge"); + } + else if ( clc.serverAddress.alternateProtocol == 1 ) { + Com_sprintf(data, sizeof(data), "getchallenge %d", clc.challenge); + } + else + Com_sprintf(data, sizeof(data), "getchallenge %d %s", clc.challenge, com_gamename->string); NET_OutOfBandPrint(NS_CLIENT, clc.serverAddress, "%s", data); break; @@ -2378,7 +2484,12 @@ void CL_CheckForResend( void ) { port = Cvar_VariableValue ("net_qport"); Q_strncpyz( info, Cvar_InfoString( CVAR_USERINFO ), sizeof( info ) ); - Info_SetValueForKey( info, "protocol", va("%i", PROTOCOL_VERSION ) ); + Info_SetValueForKey( info, "protocol", va("%i", ( clc.serverAddress.alternateProtocol == 0 ? PROTOCOL_VERSION : clc.serverAddress.alternateProtocol == 1 ? 70 : 69 ) ) ); + if( clc.serverAddress.alternateProtocol != 0 ) + { + Info_SetValueForKey( info, "cl_voipProtocol", "" ); + Info_SetValueForKey( info, "cl_voip", va("%i", cl_voip->integer) ); + } Info_SetValueForKey( info, "qport", va("%i", port ) ); Info_SetValueForKey( info, "challenge", va("%i", clc.challenge ) ); @@ -2460,7 +2571,7 @@ The sequencing information isn't terribly useful at present (we can skip duplicate packets, but we don't bother to make sure we've got all of them). =================== */ -int CL_GSRSequenceInformation( byte **data ) +int CL_GSRSequenceInformation( int alternateProtocol, byte **data ) { char *p = (char *)*data, *e; int ind, num; @@ -2479,16 +2590,17 @@ int CL_GSRSequenceInformation( byte **data ) if( num <= 0 || ind <= 0 || ind > num ) return -1; // nonsensical response - if( cls.numMasterPackets > 0 && num != cls.numMasterPackets ) + if( cls.numAlternateMasterPackets[alternateProtocol] > 0 && num != cls.numAlternateMasterPackets[alternateProtocol] ) { // Assume we sent two getservers and somehow they changed in // between - only use the results that arrive later - Com_DPrintf( "Master changed its mind about packet count!\n" ); - cls.receivedMasterPackets = 0; - cls.numglobalservers = 0; - cls.numGlobalServerAddresses = 0; + Com_DPrintf( "Master changed its mind about%s packet count!\n", + ( alternateProtocol == 0 ? "" : alternateProtocol == 1 ? " alternate-1" : " alternate-2" ) ); + cls.receivedAlternateMasterPackets[alternateProtocol] = 0; + //cls.numglobalservers = 0; + //cls.numGlobalServerAddresses = 0; } - cls.numMasterPackets = num; + cls.numAlternateMasterPackets[alternateProtocol] = num; // successfully parsed *data = (byte *)p; @@ -2548,8 +2660,10 @@ void CL_ServersResponsePacket( const netadr_t* from, msg_t *msg, qboolean extend // state to detect lack of servers or lack of response cls.numglobalservers = 0; cls.numGlobalServerAddresses = 0; - cls.numMasterPackets = 0; - cls.receivedMasterPackets = 0; + for (i = 0; i < 3; ++i) { + cls.numAlternateMasterPackets[i] = 0; + cls.receivedAlternateMasterPackets[i] = 0; + } } // parse through server response string @@ -2573,12 +2687,12 @@ void CL_ServersResponsePacket( const netadr_t* from, msg_t *msg, qboolean extend if( *buffptr == '\0' ) { - int ind = CL_GSRSequenceInformation( &buffptr ); + int ind = CL_GSRSequenceInformation( from->alternateProtocol, &buffptr ); if( ind >= 0 ) { // this denotes the start of new-syntax stuff // have we already received this packet? - if( cls.receivedMasterPackets & ( 1 << ( ind - 1 ) ) ) + if( cls.receivedAlternateMasterPackets[from->alternateProtocol] & ( 1 << ( ind - 1 ) ) ) { Com_DPrintf( "CL_ServersResponsePacket: " "received packet %d again, ignoring\n", @@ -2587,9 +2701,10 @@ void CL_ServersResponsePacket( const netadr_t* from, msg_t *msg, qboolean extend } // TODO: detect dropped packets and make another // request - Com_DPrintf( "CL_ServersResponsePacket: packet " - "%d of %d\n", ind, cls.numMasterPackets ); - cls.receivedMasterPackets |= ( 1 << ( ind - 1 ) ); + Com_DPrintf( "CL_ServersResponsePacket:%s packet " + "%d of %d\n", ( from->alternateProtocol == 0 ? "" : from->alternateProtocol == 1 ? " alternate-1" : " alternate-2" ), + ind, cls.numAlternateMasterPackets[from->alternateProtocol] ); + cls.receivedAlternateMasterPackets[from->alternateProtocol] |= ( 1 << ( ind - 1 ) ); CL_GSRFeaturedLabel( &buffptr, label, sizeof( label ) ); } @@ -2640,6 +2755,8 @@ void CL_ServersResponsePacket( const netadr_t* from, msg_t *msg, qboolean extend if (*buffptr != '\\' && *buffptr != '/') break; + addresses[numservers].alternateProtocol = from->alternateProtocol; + numservers++; if (numservers >= MAX_SERVERSPERPACKET) break; @@ -2709,10 +2826,6 @@ void CL_ConnectionlessPacket( netadr_t from, msg_t *msg ) { return; } - c = Cmd_Argv(2); - if(*c) - challenge = atoi(c); - strver = Cmd_Argv(3); if(*strver) { @@ -2724,10 +2837,17 @@ void CL_ConnectionlessPacket( netadr_t from, msg_t *msg ) { "Trying anyways.\n", ver, PROTOCOL_VERSION); } } - if(!*c || challenge != clc.challenge) + if ( clc.serverAddress.alternateProtocol == 0 ) { - Com_Printf("Bad challenge for challengeResponse. Ignored.\n"); - return; + c = Cmd_Argv(2); + if(*c) + challenge = atoi(c); + + if(!*c || challenge != clc.challenge) + { + Com_Printf("Bad challenge for challengeResponse. Ignored.\n"); + return; + } } // start sending challenge response instead of challenge request packets @@ -2758,23 +2878,26 @@ void CL_ConnectionlessPacket( netadr_t from, msg_t *msg ) { return; } - c = Cmd_Argv(1); - - if(*c) - challenge = atoi(c); - else - { - Com_Printf("Bad connectResponse received. Ignored.\n"); - return; - } - - if(challenge != clc.challenge) + if ( clc.serverAddress.alternateProtocol == 0 ) { - Com_Printf("ConnectResponse with bad challenge received. Ignored.\n"); - return; + c = Cmd_Argv(1); + + if(*c) + challenge = atoi(c); + else + { + Com_Printf("Bad connectResponse received. Ignored.\n"); + return; + } + + if(challenge != clc.challenge) + { + Com_Printf("ConnectResponse with bad challenge received. Ignored.\n"); + return; + } } - Netchan_Setup(NS_CLIENT, &clc.netchan, from, Cvar_VariableValue("net_qport"), + Netchan_Setup(clc.serverAddress.alternateProtocol, NS_CLIENT, &clc.netchan, from, Cvar_VariableValue("net_qport"), clc.challenge); clc.state = CA_CONNECTED; @@ -2957,8 +3080,14 @@ void CL_CheckUserinfo( void ) { // send a reliable userinfo update if needed if(cvar_modifiedFlags & CVAR_USERINFO) { + char *s = Cvar_InfoString(CVAR_USERINFO); cvar_modifiedFlags &= ~CVAR_USERINFO; - CL_AddReliableCommand(va("userinfo \"%s\"", Cvar_InfoString( CVAR_USERINFO ) ), qfalse); + if(clc.netchan.alternateProtocol != 0) + { + Info_SetValueForKey(s, "cl_voipProtocol", ""); + Info_SetValueForKey(s, "cl_voip", va("%i", cl_voip->integer)); + } + CL_AddReliableCommand(va("userinfo \"%s\"", s), qfalse); } } @@ -3019,7 +3148,7 @@ void CL_Frame ( int msec ) { && !com_sv_running->integer && uivm ) { // if disconnected, bring up the menu S_StopAllSounds(); - VM_Call( uivm, UI_SET_ACTIVE_MENU, UIMENU_MAIN ); + VM_Call( uivm, UI_SET_ACTIVE_MENU - ( uiInterface == 2 ? 2 : 0 ), UIMENU_MAIN ); } // if recording an avi, lock to a fixed fps @@ -3880,20 +4009,23 @@ void CL_ServerInfoPacket( netadr_t from, msg_t *msg ) { infoString = MSG_ReadString( msg ); - // if this isn't the correct gamename, ignore it - gamename = Info_ValueForKey( infoString, "gamename" ); + if ( from.alternateProtocol == 0 ) + { + // if this isn't the correct gamename, ignore it + gamename = Info_ValueForKey( infoString, "gamename" ); - gameMismatch = !*gamename || strcmp(gamename, com_gamename->string) != 0; + gameMismatch = !*gamename || strcmp(gamename, com_gamename->string) != 0; - if (gameMismatch) - { - Com_DPrintf( "Game mismatch in info packet: %s\n", infoString ); - return; + if (gameMismatch) + { + Com_DPrintf( "Game mismatch in info packet: %s\n", infoString ); + return; + } } // if this isn't the correct protocol version, ignore it prot = atoi( Info_ValueForKey( infoString, "protocol" ) ); - if ( prot != PROTOCOL_VERSION ) { + if ( prot != ( from.alternateProtocol == 0 ? PROTOCOL_VERSION : from.alternateProtocol == 1 ? 70 : 69 ) ) { Com_DPrintf( "Different protocol info packet: %s\n", infoString ); return; } @@ -4202,6 +4334,7 @@ CL_GlobalServers_f ================== */ void CL_GlobalServers_f( void ) { + int netAlternateProtocols, a; netadr_t to; int count, i, masterNum; char command[1024], *masteraddress; @@ -4212,13 +4345,26 @@ void CL_GlobalServers_f( void ) { return; } - sprintf(command, "sv_master%d", masterNum + 1); + netAlternateProtocols = Cvar_VariableIntegerValue("net_alternateProtocols"); + + for (a = 0; a < 3; ++a) + { + // indent + if(a == 0 && (netAlternateProtocols & NET_DISABLEPRIMPROTO)) + continue; + if(a == 1 && !(netAlternateProtocols & NET_ENABLEALT1PROTO)) + continue; + if(a == 2 && !(netAlternateProtocols & NET_ENABLEALT2PROTO)) + continue; + + sprintf(command, "sv_%smaster%d", (a == 0 ? "" : a == 1 ? "alt1" : "alt2"), masterNum + 1); masteraddress = Cvar_VariableString(command); if(!*masteraddress) { - Com_Printf( "CL_GlobalServers_f: Error: No master server address given.\n"); - return; + Com_Printf("CL_GlobalServers_f: Error: No%s master server address given.\n", + (a == 0 ? "" : a == 1 ? " alternate-1" : " alternate-2")); + continue; } // reset the list, waiting for response @@ -4228,19 +4374,22 @@ void CL_GlobalServers_f( void ) { if(!i) { - Com_Printf( "CL_GlobalServers_f: Error: could not resolve address of master %s\n", masteraddress); - return; + Com_Printf("CL_GlobalServers_f: Error: could not resolve address of%s master %s\n", + (a == 0 ? "" : a == 1 ? " alternate-1" : " alternate-2"), masteraddress); + continue; } else if(i == 2) - to.port = BigShort(PORT_MASTER); + to.port = BigShort(a == 0 ? PORT_MASTER : a == 1 ? ALT1PORT_MASTER : ALT2PORT_MASTER); + to.alternateProtocol = a; - Com_Printf("Requesting servers from master %s...\n", masteraddress); + Com_Printf("Requesting servers from%s master %s...\n", + (a == 0 ? "" : a == 1 ? " alternate-1" : " alternate-2"), masteraddress); cls.numglobalservers = -1; cls.pingUpdateSource = AS_GLOBAL; Com_sprintf(command, sizeof(command), "getserversExt %s %i%s", - com_gamename->string, PROTOCOL_VERSION, + com_gamename->string, (a == 0 ? PROTOCOL_VERSION : a == 1 ? 70 : 69), (Cvar_VariableIntegerValue("net_enabled") & NET_ENABLEV4 ? "" : " ipv6")); for (i=3; i < count; i++) @@ -4250,6 +4399,8 @@ void CL_GlobalServers_f( void ) { } NET_OutOfBandPrint( NS_SERVER, to, "%s", command ); + // outdent + } CL_RequestMotd(); } diff --git a/src/client/cl_net_chan.c b/src/client/cl_net_chan.c index 395c2d75..45dc56be 100644 --- a/src/client/cl_net_chan.c +++ b/src/client/cl_net_chan.c @@ -26,6 +26,108 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA #include "client.h" /* +============== +CL_Netchan_Encode + + // first 12 bytes of the data are always: + long serverId; + long messageAcknowledge; + long reliableAcknowledge; + +============== +*/ +static void CL_Netchan_Encode( msg_t *msg ) { + int serverId, messageAcknowledge, reliableAcknowledge; + int i, index, srdc, sbit, soob; + byte key, *string; + + if ( msg->cursize <= CL_ENCODE_START ) { + return; + } + + srdc = msg->readcount; + sbit = msg->bit; + soob = msg->oob; + + msg->bit = 0; + msg->readcount = 0; + msg->oob = 0; + + serverId = MSG_ReadLong(msg); + messageAcknowledge = MSG_ReadLong(msg); + reliableAcknowledge = MSG_ReadLong(msg); + + msg->oob = soob; + msg->bit = sbit; + msg->readcount = srdc; + + string = (byte *)clc.serverCommands[ reliableAcknowledge & (MAX_RELIABLE_COMMANDS-1) ]; + index = 0; + // + key = clc.challenge ^ serverId ^ messageAcknowledge; + for (i = CL_ENCODE_START; i < msg->cursize; i++) { + // modify the key with the last received now acknowledged server command + if (!string[index]) + index = 0; + if (string[index] > 127 || (string[index] == '%' && clc.netchan.alternateProtocol == 2)) { + key ^= '.' << (i & 1); + } + else { + key ^= string[index] << (i & 1); + } + index++; + // encode the data with this key + *(msg->data + i) = (*(msg->data + i)) ^ key; + } +} + +/* +============== +CL_Netchan_Decode + + // first four bytes of the data are always: + long reliableAcknowledge; + +============== +*/ +static void CL_Netchan_Decode( msg_t *msg ) { + long reliableAcknowledge, i, index; + byte key, *string; + int srdc, sbit, soob; + + srdc = msg->readcount; + sbit = msg->bit; + soob = msg->oob; + + msg->oob = 0; + + reliableAcknowledge = MSG_ReadLong(msg); + + msg->oob = soob; + msg->bit = sbit; + msg->readcount = srdc; + + string = (byte *) clc.reliableCommands[ reliableAcknowledge & (MAX_RELIABLE_COMMANDS-1) ]; + index = 0; + // xor the client challenge with the netchan sequence number (need something that changes every message) + key = clc.challenge ^ LittleLong( *(unsigned *)msg->data ); + for (i = msg->readcount + CL_DECODE_START; i < msg->cursize; i++) { + // modify the key with the last sent and with this message acknowledged client command + if (!string[index]) + index = 0; + if (string[index] > 127 || (string[index] == '%' && clc.netchan.alternateProtocol == 2)) { + key ^= '.' << (i & 1); + } + else { + key ^= string[index] << (i & 1); + } + index++; + // decode the data with this key + *(msg->data + i) = *(msg->data + i) ^ key; + } +} + +/* ================= CL_Netchan_TransmitNextFragment ================= @@ -49,6 +151,8 @@ CL_Netchan_Transmit void CL_Netchan_Transmit( netchan_t *chan, msg_t* msg ) { MSG_WriteByte( msg, clc_EOF ); + if(chan->alternateProtocol != 0) + CL_Netchan_Encode( msg ); Netchan_Transmit(chan, msg->cursize, msg->data); // Transmit all fragments without delay @@ -69,6 +173,8 @@ qboolean CL_Netchan_Process( netchan_t *chan, msg_t *msg ) { ret = Netchan_Process( chan, msg ); if (!ret) return qfalse; + if(chan->alternateProtocol != 0) + CL_Netchan_Decode( msg ); return qtrue; } diff --git a/src/client/cl_parse.c b/src/client/cl_parse.c index 5e481f92..a4810411 100644 --- a/src/client/cl_parse.c +++ b/src/client/cl_parse.c @@ -73,7 +73,7 @@ void CL_DeltaEntity (msg_t *msg, clSnapshot_t *frame, int newnum, entityState_t if ( unchanged ) { *state = *old; } else { - MSG_ReadDeltaEntity( msg, old, state, newnum ); + MSG_ReadDeltaEntity( clc.netchan.alternateProtocol, msg, old, state, newnum ); } if ( state->number == (MAX_GENTITIES-1) ) { @@ -274,10 +274,18 @@ void CL_ParseSnapshot( msg_t *msg ) { // read playerinfo SHOWNET( msg, "playerstate" ); - if ( old ) { - MSG_ReadDeltaPlayerstate( msg, &old->ps, &newSnap.ps ); + if ( clc.netchan.alternateProtocol == 2 ) { + if ( old ) { + MSG_ReadDeltaAlternatePlayerstate( msg, &old->alternatePs, &newSnap.alternatePs ); + } else { + MSG_ReadDeltaAlternatePlayerstate( msg, NULL, &newSnap.alternatePs ); + } } else { - MSG_ReadDeltaPlayerstate( msg, NULL, &newSnap.ps ); + if ( old ) { + MSG_ReadDeltaPlayerstate( msg, &old->ps, &newSnap.ps ); + } else { + MSG_ReadDeltaPlayerstate( msg, NULL, &newSnap.ps ); + } } // read packet entities @@ -309,7 +317,7 @@ void CL_ParseSnapshot( msg_t *msg ) { // calculate ping time for ( i = 0 ; i < PACKET_BACKUP ; i++ ) { packetNum = ( clc.netchan.outgoingSequence - 1 - i ) & PACKET_MASK; - if ( cl.snap.ps.commandTime >= cl.outPackets[ packetNum ].p_serverTime ) { + if ( ( clc.netchan.alternateProtocol == 2 ? cl.snap.alternatePs.commandTime : cl.snap.ps.commandTime ) >= cl.outPackets[ packetNum ].p_serverTime ) { cl.snap.ping = cls.realtime - cl.outPackets[ packetNum ].p_realtime; break; } @@ -355,8 +363,13 @@ void CL_SystemInfoChanged( void ) { cl.serverId = atoi( Info_ValueForKey( systemInfo, "sv_serverid" ) ); #ifdef USE_VOIP - s = Info_ValueForKey( systemInfo, "sv_voipProtocol" ); - clc.voipEnabled = !Q_stricmp(s, "opus"); + if( clc.netchan.alternateProtocol == 0 ) { + s = Info_ValueForKey( systemInfo, "sv_voipProtocol" ); + clc.voipEnabled = !Q_stricmp(s, "opus"); + } else { + s = Info_ValueForKey( systemInfo, "sv_voip" ); + clc.voipEnabled = !!atoi(s); + } #endif // don't set any vars when playing a demo @@ -502,7 +515,7 @@ void CL_ParseGamestate( msg_t *msg ) { } Com_Memset (&nullstate, 0, sizeof(nullstate)); es = &cl.entityBaselines[ newnum ]; - MSG_ReadDeltaEntity( msg, &nullstate, es, newnum ); + MSG_ReadDeltaEntity( clc.netchan.alternateProtocol, msg, &nullstate, es, newnum ); } else { Com_Error( ERR_DROP, "CL_ParseGamestate: bad command byte" ); } @@ -698,7 +711,9 @@ void CL_ParseVoip ( msg_t *msg, qboolean ignoreData ) { const int sequence = MSG_ReadLong(msg); const int frames = MSG_ReadByte(msg); const int packetsize = MSG_ReadShort(msg); - const int flags = MSG_ReadBits(msg, VOIP_FLAGCNT); + int flags = VOIP_DIRECT; + if (clc.netchan.alternateProtocol == 0) + flags = MSG_ReadBits(msg, VOIP_FLAGCNT); unsigned char encoded[4000]; int numSamples; int seqdiff; @@ -874,6 +889,30 @@ void CL_ParseServerMessage( msg_t *msg ) { cmd = MSG_ReadByte( msg ); + if ( clc.netchan.alternateProtocol != 0 ) + { + // See if this is an extension command after the EOF, which means we + // got data that a legacy client should ignore. + if ( cmd == svc_EOF && MSG_LookaheadByte( msg ) == svc_voipSpeex ) { + SHOWNET( msg, "EXTENSION" ); + MSG_ReadByte( msg ); // throw the svc_extension byte away. + cmd = MSG_ReadByte( msg ); // something legacy clients can't do! + // sometimes you get a svc_extension at end of stream...dangling + // bits in the huffman decoder giving a bogus value? + if ( cmd == -1 ) { + cmd = svc_EOF; + } + } + + if ( cmd == svc_voipOpus ) { + cmd = svc_voipSpeex; + } else if ( cmd == svc_voipOpus + 1 ) { + cmd = svc_voipOpus; + } else if ( cmd == svc_voipSpeex ) { + cmd = svc_voipOpus + 1; + } + } + if (cmd == svc_EOF) { SHOWNET( msg, "END OF MESSAGE" ); break; diff --git a/src/client/cl_scrn.c b/src/client/cl_scrn.c index 1f2f60bf..0c3a1187 100644 --- a/src/client/cl_scrn.c +++ b/src/client/cl_scrn.c @@ -458,7 +458,7 @@ void SCR_DrawScreenField( stereoFrame_t stereoFrame ) { re.BeginFrame( stereoFrame ); - uiFullscreen = (uivm && VM_Call( uivm, UI_IS_FULLSCREEN )); + uiFullscreen = (uivm && VM_Call( uivm, UI_IS_FULLSCREEN - ( uiInterface == 2 ? 2 : 0 ) )); // wide aspect ratio screens need to have the sides cleared // unless they are displaying game renderings @@ -483,15 +483,15 @@ void SCR_DrawScreenField( stereoFrame_t stereoFrame ) { case CA_DISCONNECTED: // force menu up S_StopAllSounds(); - VM_Call( uivm, UI_SET_ACTIVE_MENU, UIMENU_MAIN ); + VM_Call( uivm, UI_SET_ACTIVE_MENU - ( uiInterface == 2 ? 2 : 0 ), UIMENU_MAIN ); break; case CA_CONNECTING: case CA_CHALLENGING: case CA_CONNECTED: // connecting clients will only show the connection dialog // refresh to update the time - VM_Call( uivm, UI_REFRESH, cls.realtime ); - VM_Call( uivm, UI_DRAW_CONNECT_SCREEN, qfalse ); + VM_Call( uivm, UI_REFRESH - ( uiInterface == 2 ? 2 : 0 ), cls.realtime ); + VM_Call( uivm, UI_DRAW_CONNECT_SCREEN - ( uiInterface == 2 ? 2 : 0 ), qfalse ); break; case CA_LOADING: case CA_PRIMED: @@ -510,7 +510,7 @@ void SCR_DrawScreenField( stereoFrame_t stereoFrame ) { // the menu draws next if ( Key_GetCatcher( ) & KEYCATCH_UI && uivm ) { - VM_Call( uivm, UI_REFRESH, cls.realtime ); + VM_Call( uivm, UI_REFRESH - ( uiInterface == 2 ? 2 : 0 ), cls.realtime ); } // console draws next diff --git a/src/client/cl_ui.c b/src/client/cl_ui.c index 37aed08d..c4f7e0ba 100644 --- a/src/client/cl_ui.c +++ b/src/client/cl_ui.c @@ -21,9 +21,11 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA =========================================================================== */ +#include <setjmp.h> #include "client.h" vm_t *uivm; +int uiInterface; /* ==================== @@ -36,7 +38,7 @@ static void GetClientState( uiClientState_t *state ) { Q_strncpyz( state->servername, clc.servername, sizeof( state->servername ) ); Q_strncpyz( state->updateInfoString, cls.updateInfoString, sizeof( state->updateInfoString ) ); Q_strncpyz( state->messageString, clc.serverMessage, sizeof( state->messageString ) ); - state->clientNum = cl.snap.ps.clientNum; + state->clientNum = ( clc.netchan.alternateProtocol == 2 ? cl.snap.alternatePs.clientNum : cl.snap.ps.clientNum ); } /* @@ -280,6 +282,8 @@ static void LAN_GetServerAddressString( int source, int n, char *buf, int buflen case AS_LOCAL : if (n >= 0 && n < MAX_OTHER_SERVERS) { Q_strncpyz(buf, NET_AdrToStringwPort( cls.localServers[n].adr) , buflen ); + if (cls.localServers[n].adr.alternateProtocol != 0) + Q_strncpyz(buf + (int)strlen(buf), (cls.localServers[n].adr.alternateProtocol == 1 ? " -g" : " -1"), buflen - (int)strlen(buf)); return; } break; @@ -287,12 +291,16 @@ static void LAN_GetServerAddressString( int source, int n, char *buf, int buflen case AS_GLOBAL : if (n >= 0 && n < MAX_GLOBAL_SERVERS) { Q_strncpyz(buf, NET_AdrToStringwPort( cls.globalServers[n].adr) , buflen ); + if (cls.globalServers[n].adr.alternateProtocol != 0) + Q_strncpyz(buf + (int)strlen(buf), (cls.globalServers[n].adr.alternateProtocol == 1 ? " -g" : " -1"), buflen - (int)strlen(buf)); return; } break; case AS_FAVORITES : if (n >= 0 && n < MAX_OTHER_SERVERS) { Q_strncpyz(buf, NET_AdrToStringwPort( cls.favoriteServers[n].adr) , buflen ); + if (cls.favoriteServers[n].adr.alternateProtocol != 0) + Q_strncpyz(buf + (int)strlen(buf), (cls.favoriteServers[n].adr.alternateProtocol == 1 ? " -g" : " -1"), buflen - (int)strlen(buf)); return; } break; @@ -329,7 +337,14 @@ static void LAN_GetServerInfo( int source, int n, char *buf, int buflen ) { } if (server && buf) { buf[0] = '\0'; - Info_SetValueForKey( info, "hostname", server->hostName); + if (server->adr.alternateProtocol != 0) { + char hn[ MAX_HOSTNAME_LENGTH ]; + Q_strncpyz(hn, server->hostName, sizeof(hn)); + Q_strcat(hn, sizeof(hn), (server->adr.alternateProtocol == 1 ? S_COLOR_WHITE " [GPP]" : S_COLOR_WHITE " [1.1]")); + Info_SetValueForKey( info, "hostname", hn); + } else { + Info_SetValueForKey( info, "hostname", server->hostName); + } Info_SetValueForKey( info, "mapname", server->mapName); Info_SetValueForKey( info, "label", server->label); Info_SetValueForKey( info, "clients", va("%i",server->clients)); @@ -675,6 +690,9 @@ static int FloatAsInt( float f ) { return fi.i; } +static jmp_buf uiProbingJB; +static qboolean probingUI = qfalse; + /* ==================== CL_UISystemCalls @@ -683,8 +701,46 @@ The ui module is making a system call ==================== */ intptr_t CL_UISystemCalls( intptr_t *args ) { + if( uiInterface == 2 ) { + if( args[0] >= UI_R_SETCLIPREGION && args[0] < UI_MEMSET ) { + if( args[0] < UI_S_STOPBACKGROUNDTRACK - 1 ) { + args[0] += 1; + } else if( args[0] < UI_S_STOPBACKGROUNDTRACK + 4 ) { + args[0] += UI_PARSE_ADD_GLOBAL_DEFINE - UI_S_STOPBACKGROUNDTRACK + 1; + } else if( args[0] >= UI_PARSE_ADD_GLOBAL_DEFINE + 4 ) { + args[0] += UI_GETNEWS - UI_PARSE_ADD_GLOBAL_DEFINE - 5; + if( args[0] == UI_PARSE_SOURCE_FILE_AND_LINE || args[0] == UI_GETNEWS ) + args[0] = UI_PARSE_SOURCE_FILE_AND_LINE - 1337 - args[0]; + } else { + args[0] -= 4; + } + } + + switch( args[0] ) { + case UI_LAN_GETSERVERCOUNT: + case UI_LAN_GETSERVERADDRESSSTRING: + case UI_LAN_GETSERVERINFO: + case UI_LAN_MARKSERVERVISIBLE: + case UI_LAN_UPDATEVISIBLEPINGS: + case UI_LAN_RESETPINGS: + case UI_LAN_ADDSERVER: + case UI_LAN_REMOVESERVER: + case UI_LAN_GETSERVERPING: + case UI_LAN_SERVERISVISIBLE: + case UI_LAN_COMPARESERVERS: + if( args[1] == AS_GLOBAL ) { + args[1] = AS_LOCAL; + } else if( args[1] == AS_LOCAL ) { + args[1] = AS_GLOBAL; + } + } + } + switch( args[0] ) { case UI_ERROR: + if( probingUI ) { + longjmp( uiProbingJB, 1 ); + } Com_Error( ERR_DROP, "%s", (const char*)VMA(1) ); return 0; @@ -1089,6 +1145,28 @@ void CL_InitUI( void ) { Com_Error( ERR_DROP, "User Interface is version %d, expected %d", v, UI_API_VERSION ); } + Cmd_TokenizeString( "" ); + uiInterface = 0; + probingUI = qtrue; + if ( setjmp( uiProbingJB ) == 0 ) { + if ( VM_Call( uivm, UI_CONSOLE_COMMAND, 0 ) < 0 ) { + uiInterface = 2; + } + } else { + uiInterface = 2; + VM_ClearCallLevel( uivm ); + } + probingUI = qfalse; + + if ( clc.state >= CA_CONNECTED && clc.state <= CA_ACTIVE && + ( clc.netchan.alternateProtocol == 2 ) != ( uiInterface == 2 ) ) + { + Com_Printf( S_COLOR_YELLOW "WARNING: %s protocol %i, but a ui module using the %s interface was found\n", + ( clc.demoplaying ? "Demo was recorded using" : "Server uses" ), + ( clc.netchan.alternateProtocol == 0 ? PROTOCOL_VERSION : clc.netchan.alternateProtocol == 1 ? 70 : 69 ), + ( uiInterface == 2 ? "1.1" : "non-1.1" ) ); + } + // init for this gamestate VM_Call( uivm, UI_INIT, ( clc.state >= CA_AUTHORIZING && clc.state < CA_ACTIVE ) ); @@ -1110,5 +1188,5 @@ qboolean UI_GameCommand( void ) { return qfalse; } - return VM_Call( uivm, UI_CONSOLE_COMMAND, cls.realtime ); + return VM_Call( uivm, UI_CONSOLE_COMMAND - ( uiInterface == 2 ? 2 : 0 ), cls.realtime ); } diff --git a/src/client/client.h b/src/client/client.h index b4016467..695d758f 100644 --- a/src/client/client.h +++ b/src/client/client.h @@ -39,6 +39,137 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA #include <opus.h> #endif +typedef struct alternatePlayerState_s { + int commandTime; // cmd->serverTime of last executed command + int pm_type; + int bobCycle; // for view bobbing and footstep generation + int pm_flags; // ducked, jump_held, etc + int pm_time; + + vec3_t origin; + vec3_t velocity; + int weaponTime; + int gravity; + int speed; + int delta_angles[3]; // add to command angles to get view direction + // changed by spawns, rotating objects, and teleporters + + int groundEntityNum;// ENTITYNUM_NONE = in air + + int legsTimer; // don't change low priority animations until this runs out + int legsAnim; // mask off ANIM_TOGGLEBIT + + int torsoTimer; // don't change low priority animations until this runs out + int torsoAnim; // mask off ANIM_TOGGLEBIT + + int movementDir; // a number 0 to 7 that represents the relative angle + // of movement to the view angle (axial and diagonals) + // when at rest, the value will remain unchanged + // used to twist the legs during strafing + + vec3_t grapplePoint; // location of grapple to pull towards if PMF_GRAPPLE_PULL + + int eFlags; // copied to entityState_t->eFlags + + int eventSequence; // pmove generated events + int events[MAX_PS_EVENTS]; + int eventParms[MAX_PS_EVENTS]; + + int externalEvent; // events set on player from another source + int externalEventParm; + int externalEventTime; + + int clientNum; // ranges from 0 to MAX_CLIENTS-1 + int weapon; // copied to entityState_t->weapon + int weaponstate; + + vec3_t viewangles; // for fixed views + int viewheight; + + // damage feedback + int damageEvent; // when it changes, latch the other parms + int damageYaw; + int damagePitch; + int damageCount; + + int stats[MAX_STATS]; + int persistant[MAX_PERSISTANT]; // stats that aren't cleared on death + int misc[MAX_MISC]; // misc data + int ammo[MAX_WEAPONS]; + + int generic1; + int loopSound; + int otherEntityNum; + + // not communicated over the net at all + int ping; // server to game info for scoreboard + int pmove_framecount; + int jumppad_frame; + int entityEventSequence; +} alternatePlayerState_t; + +typedef struct alternateEntityState_s { + int number; // entity index + int eType; // entityType_t + int eFlags; + + trajectory_t pos; // for calculating position + trajectory_t apos; // for calculating angles + + int time; + int time2; + + vec3_t origin; + vec3_t origin2; + + vec3_t angles; + vec3_t angles2; + + int otherEntityNum; // shotgun sources, etc + int otherEntityNum2; + + int groundEntityNum; // ENTITYNUM_NONE = in air + + int constantLight; // r + (g<<8) + (b<<16) + (intensity<<24) + int loopSound; // constantly loop this sound + + int modelindex; + int modelindex2; + int clientNum; // 0 to (MAX_CLIENTS - 1), for players and corpses + int frame; + + int solid; // for client side prediction, trap_linkentity sets this properly + + int event; // impulse events -- muzzle flashes, footsteps, etc + int eventParm; + + // for players + int misc; // bit flags + int weapon; // determines weapon and flash model, etc + int legsAnim; // mask off ANIM_TOGGLEBIT + int torsoAnim; // mask off ANIM_TOGGLEBIT + + int generic1; +} alternateEntityState_t; + +typedef struct +{ + int snapFlags; // SNAPFLAG_RATE_DELAYED, etc + int ping; + + int serverTime; // server time the message is valid for (in msec) + + byte areamask[ MAX_MAP_AREA_BYTES ]; // portalarea visibility bits + + alternatePlayerState_t ps; // complete information about the current player at this time + + int numEntities; // all of the entities that need to be presented + alternateEntityState_t entities[ MAX_ENTITIES_IN_SNAPSHOT ]; // at the time of this snapshot + + int numServerCommands; // text based server commands to execute when this + int serverCommandSequence; // snapshot becomes current +} alternateSnapshot_t; + // file full of random crap that gets used to create cl_guid #define QKEY_FILE "qkey" #define QKEY_SIZE 2048 @@ -59,6 +190,7 @@ typedef struct { int cmdNum; // the next cmdNum the server is expecting playerState_t ps; // complete information about the current player at this time + alternatePlayerState_t alternatePs; // complete information about the current player at this time int numEntities; // all of the entities that need to be presented int parseEntitiesNum; // at the time of this snapshot @@ -322,8 +454,8 @@ typedef struct { int realFrametime; // ignoring pause, so console always works // master server sequence information - int numMasterPackets; - unsigned int receivedMasterPackets; // bitfield + int numAlternateMasterPackets[3]; + unsigned int receivedAlternateMasterPackets[3]; // bitfield int numlocalservers; serverInfo_t localServers[MAX_OTHER_SERVERS]; @@ -362,6 +494,7 @@ extern qboolean cl_oldGameSet; extern vm_t *cgvm; // interface to cgame dll or vm extern vm_t *uivm; // interface to ui dll or vm +extern int uiInterface; extern refexport_t re; // interface to refresh .dll |