summaryrefslogtreecommitdiff
path: root/src/client
diff options
context:
space:
mode:
Diffstat (limited to 'src/client')
-rw-r--r--src/client/cl_cgame.c74
-rw-r--r--src/client/cl_cin.c2
-rw-r--r--src/client/cl_console.c20
-rw-r--r--src/client/cl_input.c25
-rw-r--r--src/client/cl_keys.c55
-rw-r--r--src/client/cl_main.c277
-rw-r--r--src/client/cl_net_chan.c106
-rw-r--r--src/client/cl_parse.c57
-rw-r--r--src/client/cl_scrn.c10
-rw-r--r--src/client/cl_ui.c84
-rw-r--r--src/client/client.h137
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