summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
author/dev/humancontroller <devhc@example.com>2015-02-08 13:55:15 +0100
committer/dev/humancontroller <devhc@example.com>2017-03-09 13:51:18 +0100
commitb392b0d97f3ea048478059873ed6dec8afd9634b (patch)
treee3fb4a9ef70d89bfb70676fe33f9b493da7051ec /src
parent65bcf419d4b612b7d626447924fa0fe2079c18c2 (diff)
implement part 1 of the multi-protocol functionality: protocols
this contains support for connecting via, and serving simultaneously via, any of the three protocols: latest, GPP and 1.1 alternate-1 means protocol 70 (GPP), alternate-2 means protocol 69 (1.1) relevant cvars: - net_alternateProtocols - net_alt{1|2}port[6] - sv_alt{1|2}master{1|...|5} - sv_clAltProto{0|..|63}
Diffstat (limited to 'src')
-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
-rw-r--r--src/qcommon/common.c2
-rw-r--r--src/qcommon/msg.c373
-rw-r--r--src/qcommon/net_chan.c22
-rw-r--r--src/qcommon/net_ip.c197
-rw-r--r--src/qcommon/q_shared.h1
-rw-r--r--src/qcommon/qcommon.h24
-rw-r--r--src/qcommon/vm.c4
-rw-r--r--src/sdl/sdl_input.c6
-rw-r--r--src/server/server.h2
-rw-r--r--src/server/sv_ccmds.c6
-rw-r--r--src/server/sv_client.c90
-rw-r--r--src/server/sv_init.c10
-rw-r--r--src/server/sv_main.c78
-rw-r--r--src/server/sv_net_chan.c108
-rw-r--r--src/server/sv_snapshot.c24
26 files changed, 1540 insertions, 254 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
diff --git a/src/qcommon/common.c b/src/qcommon/common.c
index cf8b4d83..2644a729 100644
--- a/src/qcommon/common.c
+++ b/src/qcommon/common.c
@@ -33,7 +33,7 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
#endif
int demo_protocols[] =
-{ PROTOCOL_VERSION, 0 };
+{ PROTOCOL_VERSION, 70, 69, 0 };
#define MAX_NUM_ARGVS 50
diff --git a/src/qcommon/msg.c b/src/qcommon/msg.c
index 6e0b88c4..8258abdd 100644
--- a/src/qcommon/msg.c
+++ b/src/qcommon/msg.c
@@ -484,12 +484,12 @@ void MSG_ReadData( msg_t *msg, void *data, int len ) {
// a string hasher which gives the same hash value even if the
// string is later modified via the legacy MSG read/write code
-int MSG_HashKey(const char *string, int maxlen) {
+int MSG_HashKey(int alternateProtocol, const char *string, int maxlen) {
int hash, i;
hash = 0;
for (i = 0; i < maxlen && string[i] != '\0'; i++) {
- if (string[i] & 0x80)
+ if (string[i] & 0x80 || (alternateProtocol == 2 && string[i] == '%'))
hash += '.' * (119 + i);
else
hash += string[i] * (119 + i);
@@ -795,7 +795,7 @@ If force is not set, then nothing at all will be generated if the entity is
identical, under the assumption that the in-order delta code will catch it.
==================
*/
-void MSG_WriteDeltaEntity( msg_t *msg, struct entityState_s *from, struct entityState_s *to,
+void MSG_WriteDeltaEntity( int alternateProtocol, msg_t *msg, struct entityState_s *from, struct entityState_s *to,
qboolean force ) {
int i, lc;
int numFields;
@@ -829,6 +829,9 @@ void MSG_WriteDeltaEntity( msg_t *msg, struct entityState_s *from, struct entity
lc = 0;
// build the change vector as bytes so it is endien independent
for ( i = 0, field = entityStateFields ; i < numFields ; i++, field++ ) {
+ if ( alternateProtocol == 2 && i == 13 ) {
+ continue;
+ }
fromF = (int *)( (byte *)from + field->offset );
toF = (int *)( (byte *)to + field->offset );
if ( *fromF != *toF ) {
@@ -852,11 +855,19 @@ void MSG_WriteDeltaEntity( msg_t *msg, struct entityState_s *from, struct entity
MSG_WriteBits( msg, 0, 1 ); // not removed
MSG_WriteBits( msg, 1, 1 ); // we have a delta
- MSG_WriteByte( msg, lc ); // # of changes
+ if ( alternateProtocol == 2 && lc - 1 > 13 ) {
+ MSG_WriteByte( msg, lc - 1 ); // # of changes
+ } else {
+ MSG_WriteByte( msg, lc ); // # of changes
+ }
oldsize += numFields;
for ( i = 0, field = entityStateFields ; i < lc ; i++, field++ ) {
+ if ( alternateProtocol == 2 && i == 13 ) {
+ continue;
+ }
+
fromF = (int *)( (byte *)from + field->offset );
toF = (int *)( (byte *)to + field->offset );
@@ -894,7 +905,11 @@ void MSG_WriteDeltaEntity( msg_t *msg, struct entityState_s *from, struct entity
} else {
MSG_WriteBits( msg, 1, 1 );
// integer
- MSG_WriteBits( msg, *toF, field->bits );
+ if ( alternateProtocol == 2 && i == 33 ) {
+ MSG_WriteBits( msg, *toF, 8 );
+ } else {
+ MSG_WriteBits( msg, *toF, field->bits );
+ }
}
}
}
@@ -912,7 +927,7 @@ If the delta removes the entity, entityState_t->number will be set to MAX_GENTIT
Can go from either a baseline or a previous packet_entity
==================
*/
-void MSG_ReadDeltaEntity( msg_t *msg, entityState_t *from, entityState_t *to,
+void MSG_ReadDeltaEntity( int alternateProtocol, msg_t *msg, entityState_t *from, entityState_t *to,
int number) {
int i, lc;
int numFields;
@@ -951,6 +966,9 @@ void MSG_ReadDeltaEntity( msg_t *msg, entityState_t *from, entityState_t *to,
numFields = ARRAY_LEN( entityStateFields );
lc = MSG_ReadByte(msg);
+ if ( alternateProtocol == 2 && lc - 1 >= 13 ) {
+ ++lc;
+ }
if ( lc > numFields || lc < 0 ) {
Com_Error( ERR_DROP, "invalid entityState field count" );
@@ -970,6 +988,10 @@ void MSG_ReadDeltaEntity( msg_t *msg, entityState_t *from, entityState_t *to,
for ( i = 0, field = entityStateFields ; i < lc ; i++, field++ ) {
fromF = (int *)( (byte *)from + field->offset );
toF = (int *)( (byte *)to + field->offset );
+ if ( alternateProtocol == 2 && i == 13 ) {
+ *toF = 0;
+ continue;
+ }
if ( ! MSG_ReadBits( msg, 1 ) ) {
// no change
@@ -1002,7 +1024,11 @@ void MSG_ReadDeltaEntity( msg_t *msg, entityState_t *from, entityState_t *to,
*toF = 0;
} else {
// integer
- *toF = MSG_ReadBits( msg, field->bits );
+ if ( alternateProtocol == 2 && i == 33 ) {
+ *toF = MSG_ReadBits( msg, 8 );
+ } else {
+ *toF = MSG_ReadBits( msg, field->bits );
+ }
if ( print ) {
Com_Printf( "%s:%i ", field->name, *toF );
}
@@ -1096,17 +1122,143 @@ netField_t playerStateFields[] =
{ PSF(loopSound), 16 }
};
+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;
+
+#define APSF(x) #x,(size_t)&((alternatePlayerState_t*)0)->x
+
+netField_t alternatePlayerStateFields[] =
+{
+{ APSF(commandTime), 32 },
+{ APSF(origin[0]), 0 },
+{ APSF(origin[1]), 0 },
+{ APSF(bobCycle), 8 },
+{ APSF(velocity[0]), 0 },
+{ APSF(velocity[1]), 0 },
+{ APSF(viewangles[1]), 0 },
+{ APSF(viewangles[0]), 0 },
+{ APSF(weaponTime), -16 },
+{ APSF(origin[2]), 0 },
+{ APSF(velocity[2]), 0 },
+{ APSF(legsTimer), 8 },
+{ APSF(pm_time), -16 },
+{ APSF(eventSequence), 16 },
+{ APSF(torsoAnim), 8 },
+{ APSF(movementDir), 4 },
+{ APSF(events[0]), 8 },
+{ APSF(legsAnim), 8 },
+{ APSF(events[1]), 8 },
+{ APSF(pm_flags), 16 },
+{ APSF(groundEntityNum), GENTITYNUM_BITS },
+{ APSF(weaponstate), 4 },
+{ APSF(eFlags), 16 },
+{ APSF(externalEvent), 10 },
+{ APSF(gravity), -16 },
+{ APSF(speed), -16 },
+{ APSF(delta_angles[1]), 16 },
+{ APSF(externalEventParm), 8 },
+{ APSF(viewheight), -8 },
+{ APSF(damageEvent), 8 },
+{ APSF(damageYaw), 8 },
+{ APSF(damagePitch), 8 },
+{ APSF(damageCount), 8 },
+{ APSF(generic1), 8 },
+{ APSF(pm_type), 8 },
+{ APSF(delta_angles[0]), 16 },
+{ APSF(delta_angles[2]), 16 },
+{ APSF(torsoTimer), 12 },
+{ APSF(eventParms[0]), 8 },
+{ APSF(eventParms[1]), 8 },
+{ APSF(clientNum), 8 },
+{ APSF(weapon), 5 },
+{ APSF(viewangles[2]), 0 },
+{ APSF(grapplePoint[0]), 0 },
+{ APSF(grapplePoint[1]), 0 },
+{ APSF(grapplePoint[2]), 0 },
+{ APSF(otherEntityNum), GENTITYNUM_BITS },
+{ APSF(loopSound), 16 }
+};
+
/*
=============
MSG_WriteDeltaPlayerstate
=============
*/
-void MSG_WriteDeltaPlayerstate( msg_t *msg, struct playerState_s *from, struct playerState_s *to ) {
+void MSG_WriteDeltaPlayerstate( int alternateProtocol, msg_t *msg, struct playerState_s *from, struct playerState_s *to ) {
int i;
playerState_t dummy;
int statsbits;
int persistantbits;
+ int altFromAmmo[3];
+ int altToAmmo[3];
+ int ammobits;
int miscbits;
int numFields;
netField_t *field;
@@ -1123,6 +1275,9 @@ void MSG_WriteDeltaPlayerstate( msg_t *msg, struct playerState_s *from, struct p
lc = 0;
for ( i = 0, field = playerStateFields ; i < numFields ; i++, field++ ) {
+ if ( alternateProtocol == 2 && ( i == 15 || i == 34 || i == 35 || i == 41 ) ) {
+ continue;
+ }
fromF = (int *)( (byte *)from + field->offset );
toF = (int *)( (byte *)to + field->offset );
if ( *fromF != *toF ) {
@@ -1130,11 +1285,29 @@ void MSG_WriteDeltaPlayerstate( msg_t *msg, struct playerState_s *from, struct p
}
}
- MSG_WriteByte( msg, lc ); // # of changes
+ if ( alternateProtocol == 2 ) {
+ if ( lc - 1 > 41 ) {
+ MSG_WriteByte( msg, lc - 4 ); // # of changes
+ } else if ( lc - 1 > 35 ) {
+ MSG_WriteByte( msg, lc - 3 ); // # of changes
+ } else if ( lc - 1 > 34 ) {
+ MSG_WriteByte( msg, lc - 2 ); // # of changes
+ } else if ( lc - 1 > 15 ) {
+ MSG_WriteByte( msg, lc - 1 ); // # of changes
+ } else {
+ MSG_WriteByte( msg, lc ); // # of changes
+ }
+ } else {
+ MSG_WriteByte( msg, lc ); // # of changes
+ }
oldsize += numFields - lc;
for ( i = 0, field = playerStateFields ; i < lc ; i++, field++ ) {
+ if ( alternateProtocol == 2 && ( i == 15 || i == 34 || i == 35 || i == 41 ) ) {
+ continue;
+ }
+
fromF = (int *)( (byte *)from + field->offset );
toF = (int *)( (byte *)to + field->offset );
@@ -1163,7 +1336,17 @@ void MSG_WriteDeltaPlayerstate( msg_t *msg, struct playerState_s *from, struct p
}
} else {
// integer
- MSG_WriteBits( msg, *toF, field->bits );
+ if ( alternateProtocol == 2 ) {
+ if ( i == 20 ) {
+ MSG_WriteBits( msg, *toF, 16 );
+ } else if ( i == 36 ) {
+ MSG_WriteBits( msg, *toF, 8 );
+ } else {
+ MSG_WriteBits( msg, *toF, field->bits );
+ }
+ } else {
+ MSG_WriteBits( msg, *toF, field->bits );
+ }
}
}
@@ -1183,6 +1366,22 @@ void MSG_WriteDeltaPlayerstate( msg_t *msg, struct playerState_s *from, struct p
persistantbits |= 1<<i;
}
}
+ if ( alternateProtocol == 2 ) {
+ altFromAmmo[0] = ( from->weaponAnim & 0xFF ) | ( ( from->pm_flags >> 8 ) & 0xFF00 );
+ altFromAmmo[1] = ( from->ammo & 0xFFF ) | ( ( from->clips << 12 ) & 0xF000 );
+ altFromAmmo[2] = ( from->tauntTimer & 0xFFF ) | ( ( from->generic1 << 4 ) & 0x3000 );
+ altToAmmo[0] = ( to->weaponAnim & 0xFF ) | ( ( to->pm_flags >> 8 ) & 0xFF00 );
+ altToAmmo[1] = ( to->ammo & 0xFFF ) | ( ( to->clips << 12 ) & 0xF000 );
+ altToAmmo[2] = ( to->tauntTimer & 0xFFF ) | ( ( to->generic1 << 4 ) & 0x3000 );
+ ammobits = 0;
+ for (i=0 ; i<3 ; i++) {
+ if (altToAmmo[i] != altFromAmmo[i]) {
+ ammobits |= 1<<i;
+ }
+ }
+ } else {
+ ammobits = 0;
+ }
miscbits = 0;
for (i=0 ; i<MAX_MISC ; i++) {
if (to->misc[i] != from->misc[i]) {
@@ -1190,7 +1389,7 @@ void MSG_WriteDeltaPlayerstate( msg_t *msg, struct playerState_s *from, struct p
}
}
- if (!statsbits && !persistantbits && !miscbits) {
+ if (!statsbits && !persistantbits && !ammobits && !miscbits) {
MSG_WriteBits( msg, 0, 1 ); // no change
oldsize += 4;
return;
@@ -1219,6 +1418,19 @@ void MSG_WriteDeltaPlayerstate( msg_t *msg, struct playerState_s *from, struct p
}
+ if ( alternateProtocol == 2 ) {
+ if ( ammobits ) {
+ MSG_WriteBits( msg, 1, 1 ); // changed
+ MSG_WriteBits( msg, ammobits, 16 );
+ for (i=0 ; i<3 ; i++)
+ if (ammobits & (1<<i) )
+ MSG_WriteShort (msg, altToAmmo[i]);
+ } else {
+ MSG_WriteBits( msg, 0, 1 ); // no change
+ }
+ }
+
+
if ( miscbits ) {
MSG_WriteBits( msg, 1, 1 ); // changed
MSG_WriteBits( msg, miscbits, MAX_MISC );
@@ -1364,6 +1576,145 @@ void MSG_ReadDeltaPlayerstate (msg_t *msg, playerState_t *from, playerState_t *t
}
}
+void MSG_ReadDeltaAlternatePlayerstate (msg_t *msg, alternatePlayerState_t *from, alternatePlayerState_t *to ) {
+ int i, lc;
+ int bits;
+ netField_t *field;
+ int numFields;
+ int startBit, endBit;
+ int print;
+ int *fromF, *toF;
+ int trunc;
+ alternatePlayerState_t dummy;
+
+ if ( !from ) {
+ from = &dummy;
+ Com_Memset( &dummy, 0, sizeof( dummy ) );
+ }
+ *to = *from;
+
+ if ( msg->bit == 0 ) {
+ startBit = msg->readcount * 8 - GENTITYNUM_BITS;
+ } else {
+ startBit = ( msg->readcount - 1 ) * 8 + msg->bit - GENTITYNUM_BITS;
+ }
+
+ // shownet 2/3 will interleave with other printed info, -2 will
+ // just print the delta records
+ if ( cl_shownet && ( cl_shownet->integer >= 2 || cl_shownet->integer == -2 ) ) {
+ print = 1;
+ Com_Printf( "%3i: playerstate ", msg->readcount );
+ } else {
+ print = 0;
+ }
+
+ numFields = ARRAY_LEN( alternatePlayerStateFields );
+ lc = MSG_ReadByte(msg);
+
+ if ( lc > numFields || lc < 0 ) {
+ Com_Error( ERR_DROP, "invalid playerState field count" );
+ }
+
+ for ( i = 0, field = alternatePlayerStateFields ; i < lc ; i++, field++ ) {
+ fromF = (int *)( (byte *)from + field->offset );
+ toF = (int *)( (byte *)to + field->offset );
+
+ if ( ! MSG_ReadBits( msg, 1 ) ) {
+ // no change
+ *toF = *fromF;
+ } else {
+ if ( field->bits == 0 ) {
+ // float
+ if ( MSG_ReadBits( msg, 1 ) == 0 ) {
+ // integral float
+ trunc = MSG_ReadBits( msg, FLOAT_INT_BITS );
+ // bias to allow equal parts positive and negative
+ trunc -= FLOAT_INT_BIAS;
+ *(float *)toF = trunc;
+ if ( print ) {
+ Com_Printf( "%s:%i ", field->name, trunc );
+ }
+ } else {
+ // full floating point value
+ *toF = MSG_ReadBits( msg, 32 );
+ if ( print ) {
+ Com_Printf( "%s:%f ", field->name, *(float *)toF );
+ }
+ }
+ } else {
+ // integer
+ *toF = MSG_ReadBits( msg, field->bits );
+ if ( print ) {
+ Com_Printf( "%s:%i ", field->name, *toF );
+ }
+ }
+ }
+ }
+ for ( i=lc,field = &alternatePlayerStateFields[lc];i<numFields; i++, field++) {
+ fromF = (int *)( (byte *)from + field->offset );
+ toF = (int *)( (byte *)to + field->offset );
+ // no change
+ *toF = *fromF;
+ }
+
+
+ // read the arrays
+ if (MSG_ReadBits( msg, 1 ) ) {
+ // parse stats
+ if ( MSG_ReadBits( msg, 1 ) ) {
+ LOG("PS_STATS");
+ bits = MSG_ReadBits (msg, MAX_STATS);
+ for (i=0 ; i<MAX_STATS ; i++) {
+ if (bits & (1<<i) ) {
+ to->stats[i] = MSG_ReadShort(msg);
+ }
+ }
+ }
+
+ // parse persistant stats
+ if ( MSG_ReadBits( msg, 1 ) ) {
+ LOG("PS_PERSISTANT");
+ bits = MSG_ReadBits (msg, MAX_PERSISTANT);
+ for (i=0 ; i<MAX_PERSISTANT ; i++) {
+ if (bits & (1<<i) ) {
+ to->persistant[i] = MSG_ReadShort(msg);
+ }
+ }
+ }
+
+ // parse ammo
+ if ( MSG_ReadBits( msg, 1 ) ) {
+ LOG("PS_AMMO");
+ bits = MSG_ReadBits (msg, MAX_WEAPONS);
+ for (i=0 ; i<MAX_WEAPONS ; i++) {
+ if (bits & (1<<i) ) {
+ to->ammo[i] = MSG_ReadShort(msg);
+ }
+ }
+ }
+
+ // parse misc data
+ if ( MSG_ReadBits( msg, 1 ) ) {
+ LOG("PS_MISC");
+ bits = MSG_ReadBits (msg, MAX_MISC);
+ for (i=0 ; i<MAX_MISC ; i++) {
+ if (bits & (1<<i) ) {
+ to->misc[i] = MSG_ReadLong(msg);
+ }
+ }
+ }
+ }
+
+ if ( print ) {
+ if ( msg->bit == 0 ) {
+ endBit = msg->readcount * 8 - GENTITYNUM_BITS;
+ } else {
+ endBit = ( msg->readcount - 1 ) * 8 + msg->bit - GENTITYNUM_BITS;
+ }
+ Com_Printf( " (%i bits)\n", endBit - startBit );
+ }
+}
+
int msg_hData[256] = {
250315, // 0
41193, // 1
diff --git a/src/qcommon/net_chan.c b/src/qcommon/net_chan.c
index 58a45b50..422f64fc 100644
--- a/src/qcommon/net_chan.c
+++ b/src/qcommon/net_chan.c
@@ -84,7 +84,7 @@ Netchan_Setup
called to open a channel to a remote system
==============
*/
-void Netchan_Setup(netsrc_t sock, netchan_t *chan, netadr_t adr, int qport, int challenge)
+void Netchan_Setup(int alternateProtocol, netsrc_t sock, netchan_t *chan, netadr_t adr, int qport, int challenge)
{
Com_Memset (chan, 0, sizeof(*chan));
@@ -94,6 +94,7 @@ void Netchan_Setup(netsrc_t sock, netchan_t *chan, netadr_t adr, int qport, int
chan->incomingSequence = 0;
chan->outgoingSequence = 1;
chan->challenge = challenge;
+ chan->alternateProtocol = alternateProtocol;
}
/*
@@ -120,7 +121,8 @@ void Netchan_TransmitNextFragment( netchan_t *chan ) {
MSG_WriteShort( &send, qport->integer );
}
- MSG_WriteLong(&send, NETCHAN_GENCHECKSUM(chan->challenge, chan->outgoingSequence));
+ if ( chan->alternateProtocol == 0 )
+ MSG_WriteLong(&send, NETCHAN_GENCHECKSUM(chan->challenge, chan->outgoingSequence));
// copy the reliable message to the packet first
fragmentLength = FRAGMENT_SIZE;
@@ -198,7 +200,8 @@ void Netchan_Transmit( netchan_t *chan, int length, const byte *data ) {
if(chan->sock == NS_CLIENT)
MSG_WriteShort(&send, qport->integer);
- MSG_WriteLong(&send, NETCHAN_GENCHECKSUM(chan->challenge, chan->outgoingSequence));
+ if ( chan->alternateProtocol == 0 )
+ MSG_WriteLong(&send, NETCHAN_GENCHECKSUM(chan->challenge, chan->outgoingSequence));
chan->outgoingSequence++;
@@ -258,11 +261,14 @@ qboolean Netchan_Process( netchan_t *chan, msg_t *msg ) {
MSG_ReadShort( msg );
}
- checksum = MSG_ReadLong(msg);
+ if ( chan->alternateProtocol == 0 )
+ {
+ checksum = MSG_ReadLong(msg);
- // UDP spoofing protection
- if(NETCHAN_GENCHECKSUM(chan->challenge, sequence) != checksum)
- return qfalse;
+ // UDP spoofing protection
+ if(NETCHAN_GENCHECKSUM(chan->challenge, sequence) != checksum)
+ return qfalse;
+ }
// read the fragment information
if ( fragmented ) {
@@ -656,6 +662,8 @@ int NET_StringToAdr( const char *s, netadr_t *a, netadrtype_t family )
search = base;
}
+ a->alternateProtocol = 0;
+
if(!Sys_StringToAdr(search, a, family))
{
a->type = NA_BAD;
diff --git a/src/qcommon/net_ip.c b/src/qcommon/net_ip.c
index 882562e9..82a1ebe0 100644
--- a/src/qcommon/net_ip.c
+++ b/src/qcommon/net_ip.c
@@ -96,6 +96,7 @@ static qboolean usingSocks = qfalse;
static int networkingEnabled = 0;
static cvar_t *net_enabled;
+static cvar_t *net_alternateProtocols;
static cvar_t *net_socksEnabled;
static cvar_t *net_socksServer;
@@ -105,8 +106,8 @@ static cvar_t *net_socksPassword;
static cvar_t *net_ip;
static cvar_t *net_ip6;
-static cvar_t *net_port;
-static cvar_t *net_port6;
+static cvar_t *net_ports[3];
+static cvar_t *net_port6s[3];
static cvar_t *net_mcast6addr;
static cvar_t *net_mcast6iface;
@@ -114,10 +115,13 @@ static cvar_t *net_dropsim;
static struct sockaddr socksRelayAddr;
-static SOCKET ip_socket = INVALID_SOCKET;
-static SOCKET ip6_socket = INVALID_SOCKET;
+static SOCKET ip_sockets[3] = { INVALID_SOCKET, INVALID_SOCKET, INVALID_SOCKET };
+static SOCKET ip6_sockets[3] = { INVALID_SOCKET, INVALID_SOCKET, INVALID_SOCKET };
+/*
+TODO: accommodate
static SOCKET socks_socket = INVALID_SOCKET;
static SOCKET multicast6_socket = INVALID_SOCKET;
+*/
// Keep track of currently joined multicast group.
static struct ipv6_mreq curgroup;
@@ -253,6 +257,7 @@ static void SockadrToNetadr( struct sockaddr *s, netadr_t *a ) {
a->port = ((struct sockaddr_in6 *)s)->sin6_port;
a->scope_id = ((struct sockaddr_in6 *)s)->sin6_scope_id;
}
+ a->alternateProtocol = 0;
}
@@ -396,7 +401,10 @@ qboolean NET_CompareBaseAdrMask(netadr_t a, netadr_t b, int netmask)
{
byte cmpmask, *addra, *addrb;
int curbyte;
-
+
+ if (a.alternateProtocol != b.alternateProtocol)
+ return qfalse;
+
if (a.type != b.type)
return qfalse;
@@ -523,15 +531,19 @@ Receive one packet
*/
qboolean NET_GetPacket(netadr_t *net_from, msg_t *net_message, fd_set *fdr)
{
+ int a;
int ret;
struct sockaddr_storage from;
socklen_t fromlen;
int err;
- if(ip_socket != INVALID_SOCKET && FD_ISSET(ip_socket, fdr))
+ for(a = 0; a < 3; ++a)
+ {
+ // indent
+ if(ip_sockets[a] != INVALID_SOCKET && FD_ISSET(ip_sockets[a], fdr))
{
fromlen = sizeof(from);
- ret = recvfrom( ip_socket, (void *)net_message->data, net_message->maxsize, 0, (struct sockaddr *) &from, &fromlen );
+ ret = recvfrom( ip_sockets[a], (void *)net_message->data, net_message->maxsize, 0, (struct sockaddr *) &from, &fromlen );
if (ret == SOCKET_ERROR)
{
@@ -561,6 +573,8 @@ qboolean NET_GetPacket(netadr_t *net_from, msg_t *net_message, fd_set *fdr)
SockadrToNetadr( (struct sockaddr *) &from, net_from );
net_message->readcount = 0;
}
+
+ net_from->alternateProtocol = a;
if( ret >= net_message->maxsize ) {
Com_Printf( "Oversize packet from %s\n", NET_AdrToString (*net_from) );
@@ -572,10 +586,10 @@ qboolean NET_GetPacket(netadr_t *net_from, msg_t *net_message, fd_set *fdr)
}
}
- if(ip6_socket != INVALID_SOCKET && FD_ISSET(ip6_socket, fdr))
+ if(ip6_sockets[a] != INVALID_SOCKET && FD_ISSET(ip6_sockets[a], fdr))
{
fromlen = sizeof(from);
- ret = recvfrom(ip6_socket, (void *)net_message->data, net_message->maxsize, 0, (struct sockaddr *) &from, &fromlen);
+ ret = recvfrom(ip6_sockets[a], (void *)net_message->data, net_message->maxsize, 0, (struct sockaddr *) &from, &fromlen);
if (ret == SOCKET_ERROR)
{
@@ -589,6 +603,8 @@ qboolean NET_GetPacket(netadr_t *net_from, msg_t *net_message, fd_set *fdr)
SockadrToNetadr((struct sockaddr *) &from, net_from);
net_message->readcount = 0;
+ net_from->alternateProtocol = a;
+
if(ret >= net_message->maxsize)
{
Com_Printf( "Oversize packet from %s\n", NET_AdrToString (*net_from) );
@@ -600,6 +616,8 @@ qboolean NET_GetPacket(netadr_t *net_from, msg_t *net_message, fd_set *fdr)
}
}
+ /*
+ TODO: accommodate
if(multicast6_socket != INVALID_SOCKET && multicast6_socket != ip6_socket && FD_ISSET(multicast6_socket, fdr))
{
fromlen = sizeof(from);
@@ -627,6 +645,9 @@ qboolean NET_GetPacket(netadr_t *net_from, msg_t *net_message, fd_set *fdr)
return qtrue;
}
}
+ */
+ // outdent
+ }
return qfalse;
@@ -651,10 +672,10 @@ void Sys_SendPacket( int length, const void *data, netadr_t to ) {
return;
}
- if( (ip_socket == INVALID_SOCKET && to.type == NA_IP) ||
- (ip_socket == INVALID_SOCKET && to.type == NA_BROADCAST) ||
- (ip6_socket == INVALID_SOCKET && to.type == NA_IP6) ||
- (ip6_socket == INVALID_SOCKET && to.type == NA_MULTICAST6) )
+ if( (ip_sockets[to.alternateProtocol] == INVALID_SOCKET && to.type == NA_IP) ||
+ (ip_sockets[to.alternateProtocol] == INVALID_SOCKET && to.type == NA_BROADCAST) ||
+ (ip6_sockets[to.alternateProtocol] == INVALID_SOCKET && to.type == NA_IP6) ||
+ (/* TODO: accommodate ip6_socket == INVALID_SOCKET && */to.type == NA_MULTICAST6) )
return;
if(to.type == NA_MULTICAST6 && (net_enabled->integer & NET_DISABLEMCAST))
@@ -671,13 +692,13 @@ void Sys_SendPacket( int length, const void *data, netadr_t to ) {
*(int *)&socksBuf[4] = ((struct sockaddr_in *)&addr)->sin_addr.s_addr;
*(short *)&socksBuf[8] = ((struct sockaddr_in *)&addr)->sin_port;
memcpy( &socksBuf[10], data, length );
- ret = sendto( ip_socket, socksBuf, length+10, 0, &socksRelayAddr, sizeof(socksRelayAddr) );
+ ret = sendto( ip_sockets[to.alternateProtocol], socksBuf, length+10, 0, &socksRelayAddr, sizeof(socksRelayAddr) );
}
else {
if(addr.ss_family == AF_INET)
- ret = sendto( ip_socket, data, length, 0, (struct sockaddr *) &addr, sizeof(struct sockaddr_in) );
+ ret = sendto( ip_sockets[to.alternateProtocol], data, length, 0, (struct sockaddr *) &addr, sizeof(struct sockaddr_in) );
else if(addr.ss_family == AF_INET6)
- ret = sendto( ip6_socket, data, length, 0, (struct sockaddr *) &addr, sizeof(struct sockaddr_in6) );
+ ret = sendto( ip6_sockets[to.alternateProtocol], data, length, 0, (struct sockaddr *) &addr, sizeof(struct sockaddr_in6) );
}
if( ret == SOCKET_ERROR ) {
int err = socketError;
@@ -811,7 +832,7 @@ void Sys_ShowIP(void) {
NET_IPSocket
====================
*/
-SOCKET NET_IPSocket( char *net_interface, int port, int *err ) {
+SOCKET NET_IPSocket( int alternateProtocol, char *net_interface, int port, int *err ) {
SOCKET newsocket;
struct sockaddr_in address;
ioctlarg_t _true = 1;
@@ -820,10 +841,12 @@ SOCKET NET_IPSocket( char *net_interface, int port, int *err ) {
*err = 0;
if( net_interface ) {
- Com_Printf( "Opening IP socket: %s:%i\n", net_interface, port );
+ Com_Printf( "Opening%s IP socket: %s:%i\n",
+ ( alternateProtocol == 2 ? " alternate-2" : alternateProtocol == 1 ? " alternate-1" : "" ), net_interface, port );
}
else {
- Com_Printf( "Opening IP socket: 0.0.0.0:%i\n", port );
+ Com_Printf( "Opening%s IP socket: 0.0.0.0:%i\n",
+ ( alternateProtocol == 2 ? " alternate-2" : alternateProtocol == 1 ? " alternate-1" : "" ), port );
}
if( ( newsocket = socket( PF_INET, SOCK_DGRAM, IPPROTO_UDP ) ) == INVALID_SOCKET ) {
@@ -879,7 +902,7 @@ SOCKET NET_IPSocket( char *net_interface, int port, int *err ) {
NET_IP6Socket
====================
*/
-SOCKET NET_IP6Socket( char *net_interface, int port, struct sockaddr_in6 *bindto, int *err ) {
+SOCKET NET_IP6Socket( int alternateProtocol, char *net_interface, int port, struct sockaddr_in6 *bindto, int *err ) {
SOCKET newsocket;
struct sockaddr_in6 address;
ioctlarg_t _true = 1;
@@ -890,12 +913,15 @@ SOCKET NET_IP6Socket( char *net_interface, int port, struct sockaddr_in6 *bindto
{
// Print the name in brackets if there is a colon:
if(Q_CountChar(net_interface, ':'))
- Com_Printf( "Opening IP6 socket: [%s]:%i\n", net_interface, port );
+ Com_Printf( "Opening%s IP6 socket: [%s]:%i\n",
+ ( alternateProtocol == 2 ? " alternate-2" : alternateProtocol == 1 ? " alternate-1" : "" ), net_interface, port );
else
- Com_Printf( "Opening IP6 socket: %s:%i\n", net_interface, port );
+ Com_Printf( "Opening%s IP6 socket: %s:%i\n",
+ ( alternateProtocol == 2 ? " alternate-2" : alternateProtocol == 1 ? " alternate-1" : "" ), net_interface, port );
}
else
- Com_Printf( "Opening IP6 socket: [::]:%i\n", port );
+ Com_Printf( "Opening%s IP6 socket: [::]:%i\n",
+ ( alternateProtocol == 2 ? " alternate-2" : alternateProtocol == 1 ? " alternate-1" : "" ), port );
if( ( newsocket = socket( PF_INET6, SOCK_DGRAM, IPPROTO_UDP ) ) == INVALID_SOCKET ) {
*err = socketError;
@@ -999,6 +1025,8 @@ Join an ipv6 multicast group
*/
void NET_JoinMulticast6(void)
{
+ /*
+ TODO: accommodate
int err;
if(ip6_socket == INVALID_SOCKET || multicast6_socket != INVALID_SOCKET || (net_enabled->integer & NET_DISABLEMCAST))
@@ -1045,10 +1073,13 @@ void NET_JoinMulticast6(void)
return;
}
}
+ */
}
void NET_LeaveMulticast6()
{
+ /*
+ TODO: accommodate
if(multicast6_socket != INVALID_SOCKET)
{
if(multicast6_socket != ip6_socket)
@@ -1058,6 +1089,7 @@ void NET_LeaveMulticast6()
multicast6_socket = INVALID_SOCKET;
}
+ */
}
/*
@@ -1066,6 +1098,8 @@ NET_OpenSocks
====================
*/
void NET_OpenSocks( int port ) {
+ /*
+ TODO: accommodate
struct sockaddr_in address;
struct hostent *h;
int len;
@@ -1224,6 +1258,7 @@ void NET_OpenSocks( int port ) {
memset( ((struct sockaddr_in *)&socksRelayAddr)->sin_zero, 0, 8 );
usingSocks = qtrue;
+ */
}
@@ -1347,16 +1382,30 @@ NET_OpenIP
====================
*/
void NET_OpenIP( void ) {
+ int a;
int i;
int err;
- int port;
- int port6;
+ int ports[3];
+ int port6s[3];
- port = net_port->integer;
- port6 = net_port6->integer;
+ for( a = 0; a < 3; ++a )
+ {
+ ports[a] = net_ports[a]->integer;
+ port6s[a] = net_port6s[a]->integer;
+ }
NET_GetLocalAddress();
+ for( a = 0; a < 3; ++a )
+ {
+ // indent
+ if( a == 0 && ( net_alternateProtocols->integer & NET_DISABLEPRIMPROTO ) )
+ continue;
+ if( a == 1 && !( net_alternateProtocols->integer & NET_ENABLEALT1PROTO ) )
+ continue;
+ if( a == 2 && !( net_alternateProtocols->integer & NET_ENABLEALT2PROTO ) )
+ continue;
+
// automatically scan for a valid port, so multiple
// dedicated servers can be started without requiring
// a different net_port for each one
@@ -1365,10 +1414,10 @@ void NET_OpenIP( void ) {
{
for( i = 0 ; i < 10 ; i++ )
{
- ip6_socket = NET_IP6Socket(net_ip6->string, port6 + i, &boundto, &err);
- if (ip6_socket != INVALID_SOCKET)
+ ip6_sockets[a] = NET_IP6Socket( a, net_ip6->string, port6s[a] + i, &boundto, &err );
+ if (ip6_sockets[a] != INVALID_SOCKET)
{
- Cvar_SetValue( "net_port6", port6 + i );
+ Cvar_SetValue( ( a == 2 ? "net_alt2port6" : a == 1 ? "net_alt1port6" : "net_port6" ), port6s[a] + i );
break;
}
else
@@ -1377,19 +1426,20 @@ void NET_OpenIP( void ) {
break;
}
}
- if(ip6_socket == INVALID_SOCKET)
- Com_Printf( "WARNING: Couldn't bind to a v6 ip address.\n");
+ if(ip6_sockets[a] == INVALID_SOCKET)
+ Com_Printf( "WARNING: Couldn't bind to a%s v6 ip address.\n",
+ ( a == 2 ? "n alternate-2" : a == 1 ? "n alternate-1" : "" ) );
}
if(net_enabled->integer & NET_ENABLEV4)
{
for( i = 0 ; i < 10 ; i++ ) {
- ip_socket = NET_IPSocket( net_ip->string, port + i, &err );
- if (ip_socket != INVALID_SOCKET) {
- Cvar_SetValue( "net_port", port + i );
+ ip_sockets[a] = NET_IPSocket( a, net_ip->string, ports[a] + i, &err );
+ if (ip_sockets[a] != INVALID_SOCKET) {
+ Cvar_SetValue( ( a == 2 ? "net_alt2port" : a == 1 ? "net_alt1port" : "net_port" ), ports[a] + i );
if (net_socksEnabled->integer)
- NET_OpenSocks( port + i );
+ NET_OpenSocks( ports[a] + i );
break;
}
@@ -1400,8 +1450,11 @@ void NET_OpenIP( void ) {
}
}
- if(ip_socket == INVALID_SOCKET)
- Com_Printf( "WARNING: Couldn't bind to a v4 ip address.\n");
+ if(ip_sockets[a] == INVALID_SOCKET)
+ Com_Printf( "WARNING: Couldn't bind to a%s v4 ip address.\n",
+ ( a == 2 ? "n alternate-2" : a == 1 ? "n alternate-1" : "" ) );
+ }
+ // outdent
}
}
@@ -1416,6 +1469,7 @@ NET_GetCvars
*/
static qboolean NET_GetCvars( void ) {
int modified;
+ int a;
#ifdef DEDICATED
// I want server owners to explicitly turn on ipv6 support.
@@ -1428,6 +1482,10 @@ static qboolean NET_GetCvars( void ) {
modified = net_enabled->modified;
net_enabled->modified = qfalse;
+ net_alternateProtocols = Cvar_Get( "net_alternateProtocols", "3", CVAR_LATCH | CVAR_ARCHIVE );
+ modified += net_alternateProtocols->modified;
+ net_alternateProtocols->modified = qfalse;
+
net_ip = Cvar_Get( "net_ip", "0.0.0.0", CVAR_LATCH );
modified += net_ip->modified;
net_ip->modified = qfalse;
@@ -1436,13 +1494,18 @@ static qboolean NET_GetCvars( void ) {
modified += net_ip6->modified;
net_ip6->modified = qfalse;
- net_port = Cvar_Get( "net_port", va( "%i", PORT_SERVER ), CVAR_LATCH );
- modified += net_port->modified;
- net_port->modified = qfalse;
+ for( a = 0; a < 3; ++a )
+ {
+ net_ports[a] = Cvar_Get( ( a == 2 ? "net_alt2port" : a == 1 ? "net_alt1port" : "net_port" ),
+ ( a == 2 ? XSTRING( ALT2PORT_SERVER ) : a == 1 ? XSTRING( ALT1PORT_SERVER ) : XSTRING( PORT_SERVER ) ), CVAR_LATCH );
+ modified += net_ports[a]->modified;
+ net_ports[a]->modified = qfalse;
- net_port6 = Cvar_Get( "net_port6", va( "%i", PORT_SERVER ), CVAR_LATCH );
- modified += net_port6->modified;
- net_port6->modified = qfalse;
+ net_port6s[a] = Cvar_Get( ( a == 2 ? "net_alt2port6" : a == 1 ? "net_alt1port6" : "net_port6" ),
+ ( a == 2 ? XSTRING( ALT2PORT_SERVER ) : a == 1 ? XSTRING( ALT1PORT_SERVER ) : XSTRING( PORT_SERVER ) ), CVAR_LATCH );
+ modified += net_port6s[a]->modified;
+ net_port6s[a]->modified = qfalse;
+ }
// Some cvars for configuring multicast options which facilitates scanning for servers on local subnets.
net_mcast6addr = Cvar_Get( "net_mcast6addr", NET_MULTICAST_IP6, CVAR_LATCH | CVAR_ARCHIVE );
@@ -1492,6 +1555,7 @@ void NET_Config( qboolean enableNetworking ) {
qboolean modified;
qboolean stop;
qboolean start;
+ int a;
// get any latched changes to cvars
modified = NET_GetCvars();
@@ -1528,11 +1592,21 @@ void NET_Config( qboolean enableNetworking ) {
}
if( stop ) {
- if ( ip_socket != INVALID_SOCKET ) {
- closesocket( ip_socket );
- ip_socket = INVALID_SOCKET;
+ for( a = 0; a < 3; ++a )
+ {
+ if ( ip_sockets[a] != INVALID_SOCKET ) {
+ closesocket( ip_sockets[a] );
+ ip_sockets[a] = INVALID_SOCKET;
+ }
+
+ if ( ip6_sockets[a] != INVALID_SOCKET ) {
+ closesocket( ip6_sockets[a] );
+ ip6_sockets[a] = INVALID_SOCKET;
+ }
}
+ /*
+ TODO: accommodate
if(multicast6_socket != INVALID_SOCKET)
{
if(multicast6_socket != ip6_socket)
@@ -1541,15 +1615,11 @@ void NET_Config( qboolean enableNetworking ) {
multicast6_socket = INVALID_SOCKET;
}
- if ( ip6_socket != INVALID_SOCKET ) {
- closesocket( ip6_socket );
- ip6_socket = INVALID_SOCKET;
- }
-
if ( socks_socket != INVALID_SOCKET ) {
closesocket( socks_socket );
socks_socket = INVALID_SOCKET;
}
+ */
}
@@ -1656,6 +1726,7 @@ void NET_Sleep(int msec)
struct timeval timeout;
fd_set fdr;
int retval;
+ int a;
SOCKET highestfd = INVALID_SOCKET;
if(msec < 0)
@@ -1663,18 +1734,22 @@ void NET_Sleep(int msec)
FD_ZERO(&fdr);
- if(ip_socket != INVALID_SOCKET)
+ for(a = 0; a < 3; ++a)
{
- FD_SET(ip_socket, &fdr);
+ if(ip_sockets[a] != INVALID_SOCKET)
+ {
+ FD_SET(ip_sockets[a], &fdr);
- highestfd = ip_socket;
- }
- if(ip6_socket != INVALID_SOCKET)
- {
- FD_SET(ip6_socket, &fdr);
+ if(highestfd == INVALID_SOCKET || ip_sockets[a] > highestfd)
+ highestfd = ip_sockets[a];
+ }
+ if(ip6_sockets[a] != INVALID_SOCKET)
+ {
+ FD_SET(ip6_sockets[a], &fdr);
- if(highestfd == INVALID_SOCKET || ip6_socket > highestfd)
- highestfd = ip6_socket;
+ if(highestfd == INVALID_SOCKET || ip6_sockets[a] > highestfd)
+ highestfd = ip6_sockets[a];
+ }
}
#ifdef _WIN32
diff --git a/src/qcommon/q_shared.h b/src/qcommon/q_shared.h
index 331dcfcc..6d6aa461 100644
--- a/src/qcommon/q_shared.h
+++ b/src/qcommon/q_shared.h
@@ -1103,6 +1103,7 @@ typedef struct {
// if none of the catchers are active, bound key strings will be executed
#define KEYCATCH_CONSOLE 0x0001
#define KEYCATCH_UI 0x0002
+#define KEYCATCH_MESSAGE 0x0004
#define KEYCATCH_CGAME 0x0008
diff --git a/src/qcommon/qcommon.h b/src/qcommon/qcommon.h
index b494773b..895c01d4 100644
--- a/src/qcommon/qcommon.h
+++ b/src/qcommon/qcommon.h
@@ -75,7 +75,7 @@ void MSG_WriteFloat (msg_t *sb, float f);
void MSG_WriteString (msg_t *sb, const char *s);
void MSG_WriteBigString (msg_t *sb, const char *s);
void MSG_WriteAngle16 (msg_t *sb, float f);
-int MSG_HashKey(const char *string, int maxlen);
+int MSG_HashKey(int alternateProtocol, const char *string, int maxlen);
void MSG_BeginReading (msg_t *sb);
void MSG_BeginReadingOOB(msg_t *sb);
@@ -97,13 +97,15 @@ int MSG_LookaheadByte (msg_t *msg);
void MSG_WriteDeltaUsercmdKey( msg_t *msg, int key, usercmd_t *from, usercmd_t *to );
void MSG_ReadDeltaUsercmdKey( msg_t *msg, int key, usercmd_t *from, usercmd_t *to );
-void MSG_WriteDeltaEntity( msg_t *msg, struct entityState_s *from, struct entityState_s *to
+void MSG_WriteDeltaEntity( int alternateProtocol, msg_t *msg, struct entityState_s *from, struct entityState_s *to
, qboolean force );
-void MSG_ReadDeltaEntity( msg_t *msg, entityState_t *from, entityState_t *to,
+void MSG_ReadDeltaEntity( int alternateProtocol, msg_t *msg, entityState_t *from, entityState_t *to,
int number );
-void MSG_WriteDeltaPlayerstate( msg_t *msg, struct playerState_s *from, struct playerState_s *to );
+void MSG_WriteDeltaPlayerstate( int alternateProtocol, msg_t *msg, struct playerState_s *from, struct playerState_s *to );
void MSG_ReadDeltaPlayerstate( msg_t *msg, struct playerState_s *from, struct playerState_s *to );
+struct alternatePlayerState_s;
+void MSG_ReadDeltaAlternatePlayerstate( msg_t *msg, struct alternatePlayerState_s *from, struct alternatePlayerState_s *to );
void MSG_ReportChangeVectors_f( void );
@@ -125,6 +127,10 @@ NET
// disables ipv6 multicast support if set.
#define NET_DISABLEMCAST 0x08
+#define NET_ENABLEALT1PROTO 0x01
+#define NET_ENABLEALT2PROTO 0x02
+#define NET_DISABLEPRIMPROTO 0x04
+
#define PACKET_BACKUP 32 // number of old messages that must be kept on client and
// server for delta comrpession and ping estimation
@@ -162,6 +168,8 @@ typedef struct {
unsigned short port;
unsigned long scope_id; // Needed for IPv6 link-local addresses
+
+ int alternateProtocol;
} netadr_t;
void NET_Init( void );
@@ -204,6 +212,7 @@ typedef struct {
int dropped; // between last packet and previous
+ int alternateProtocol;
netadr_t remoteAddress;
int qport; // qport value to write when transmitting
@@ -229,7 +238,7 @@ typedef struct {
} netchan_t;
void Netchan_Init( int qport );
-void Netchan_Setup(netsrc_t sock, netchan_t *chan, netadr_t adr, int qport, int challenge);
+void Netchan_Setup(int alternateProtocol, netsrc_t sock, netchan_t *chan, netadr_t adr, int qport, int challenge);
void Netchan_Transmit( netchan_t *chan, int length, const byte *data );
void Netchan_TransmitNextFragment( netchan_t *chan );
@@ -258,6 +267,10 @@ extern int demo_protocols[];
#define PORT_MASTER 30700
#define PORT_SERVER 30720
+#define ALT1PORT_MASTER 30700
+#define ALT1PORT_SERVER 30721
+#define ALT2PORT_MASTER 30710
+#define ALT2PORT_SERVER 30722
#define NUM_SERVER_PORTS 4 // broadcast scan this many ports after
// PORT_SERVER so a single machine can
// run multiple servers
@@ -343,6 +356,7 @@ void VM_Free( vm_t *vm );
void VM_Clear(void);
void VM_Forced_Unload_Start(void);
void VM_Forced_Unload_Done(void);
+void VM_ClearCallLevel(vm_t *vm);
vm_t *VM_Restart(vm_t *vm, qboolean unpure);
intptr_t QDECL VM_Call( vm_t *vm, int callNum, ... );
diff --git a/src/qcommon/vm.c b/src/qcommon/vm.c
index 18be5331..aa77f605 100644
--- a/src/qcommon/vm.c
+++ b/src/qcommon/vm.c
@@ -744,6 +744,10 @@ void VM_Forced_Unload_Done(void) {
forced_unload = 0;
}
+void VM_ClearCallLevel(vm_t *vm) {
+ vm->callLevel = 0;
+}
+
void *VM_ArgPtr( intptr_t intValue ) {
if ( !intValue ) {
return NULL;
diff --git a/src/sdl/sdl_input.c b/src/sdl/sdl_input.c
index 2cbdfe9a..e3ae9efc 100644
--- a/src/sdl/sdl_input.c
+++ b/src/sdl/sdl_input.c
@@ -416,7 +416,7 @@ static void IN_DeactivateMouse( void )
SDL_SetRelativeMouseMode( SDL_FALSE );
// Don't warp the mouse unless the cursor is within the window
- if( SDL_GetWindowFlags( SDL_window ) & SDL_WINDOW_MOUSE_FOCUS )
+ if( SDL_GetWindowFlags( SDL_window ) & SDL_WINDOW_MOUSE_FOCUS && uiInterface != 2 )
{
int x, y;
IN_GetUIMousePosition( &x, &y );
@@ -956,7 +956,7 @@ void IN_Frame( void )
// Loading in windowed mode
IN_DeactivateMouse( );
}
- else if( !cls.glconfig.isFullscreen && cursorShowing )
+ else if( !cls.glconfig.isFullscreen && cursorShowing && uiInterface != 2 )
{
// Use WM cursor when not fullscreen
IN_DeactivateMouse( );
@@ -969,7 +969,7 @@ void IN_Frame( void )
else
IN_ActivateMouse( );
- if( !mouseActive )
+ if( !mouseActive && uiInterface != 2 )
{
SDL_GetMouseState( &x, &y );
IN_SetUIMousePosition( x, y );
diff --git a/src/server/server.h b/src/server/server.h
index 0d56ea11..591af854 100644
--- a/src/server/server.h
+++ b/src/server/server.h
@@ -259,7 +259,7 @@ extern cvar_t *sv_maxclients;
extern cvar_t *sv_privateClients;
extern cvar_t *sv_hostname;
-extern cvar_t *sv_master[MAX_MASTER_SERVERS];
+extern cvar_t *sv_masters[3][MAX_MASTER_SERVERS];
extern cvar_t *sv_reconnectlimit;
extern cvar_t *sv_showloss;
extern cvar_t *sv_padPackets;
diff --git a/src/server/sv_ccmds.c b/src/server/sv_ccmds.c
index 48b1eb65..56c73aa8 100644
--- a/src/server/sv_ccmds.c
+++ b/src/server/sv_ccmds.c
@@ -45,6 +45,7 @@ static void SV_Map_f( void ) {
qboolean killBots, cheat;
char expanded[MAX_QPATH];
char mapname[MAX_QPATH];
+ int a;
int i;
map = Cmd_Argv(1);
@@ -88,8 +89,9 @@ static void SV_Map_f( void ) {
// This forces the local master server IP address cache
// to be updated on sending the next heartbeat
- for( i = 0; i < MAX_MASTER_SERVERS; i++ )
- sv_master[ i ]->modified = qtrue;
+ for( a = 0; a < 3; ++a )
+ for( i = 0; i < MAX_MASTER_SERVERS; i++ )
+ sv_masters[ a ][ i ]->modified = qtrue;
}
/*
diff --git a/src/server/sv_client.c b/src/server/sv_client.c
index 5a60a271..91743f8e 100644
--- a/src/server/sv_client.c
+++ b/src/server/sv_client.c
@@ -60,8 +60,6 @@ void SV_GetChallenge(netadr_t from)
int clientChallenge;
challenge_t *challenge;
qboolean wasfound = qfalse;
- char *gameName;
- qboolean gameMismatch;
// Prevent using getchallenge as an amplifier
if ( SVC_RateLimitAddress( from, 10, 1000 ) ) {
@@ -77,18 +75,6 @@ void SV_GetChallenge(netadr_t from)
return;
}
- gameName = Cmd_Argv(2);
-
- gameMismatch = !*gameName || strcmp(gameName, com_gamename->string) != 0;
-
- // reject client if the gamename string sent by the client doesn't match ours
- if (gameMismatch)
- {
- NET_OutOfBandPrint(NS_SERVER, from, "print\nGame mismatch: This is a %s server\n",
- com_gamename->string);
- return;
- }
-
oldest = 0;
oldestClientTime = oldestTime = 0x7fffffff;
@@ -166,8 +152,8 @@ void SV_DirectConnect( netadr_t from ) {
Q_strncpyz( userinfo, Cmd_Argv(1), sizeof(userinfo) );
version = atoi( Info_ValueForKey( userinfo, "protocol" ) );
- if ( version != PROTOCOL_VERSION ) {
- NET_OutOfBandPrint(NS_SERVER, from, "print\nServer uses protocol version %i "
+ if ( version != PROTOCOL_VERSION && version != 70 && version != 69 ) {
+ NET_OutOfBandPrint(NS_SERVER, from, "print\nServer uses either protocol version %i, 70 or 69 "
"(yours is %i).\n", PROTOCOL_VERSION, version);
Com_DPrintf(" rejected connect from version %i\n", version);
return;
@@ -333,11 +319,13 @@ gotnewcl:
ent = SV_GentityNum( clientNum );
newcl->gentity = ent;
+ Cvar_Set( va( "sv_clAltProto%i", clientNum ), ( version == 69 ? "2" : version == 70 ? "1" : "0" ) );
+
// save the challenge
newcl->challenge = challenge;
// save the address
- Netchan_Setup(NS_SERVER, &newcl->netchan, from, qport, challenge);
+ Netchan_Setup((version == 69 ? 2 : version == 70 ? 1 : 0), NS_SERVER, &newcl->netchan, from, qport, challenge);
// init the netchan queue
newcl->netchan_end_queue = &newcl->netchan_start_queue;
@@ -532,7 +520,7 @@ static void SV_SendClientGameState( client_t *client ) {
continue;
}
MSG_WriteByte( &msg, svc_baseline );
- MSG_WriteDeltaEntity( &msg, &nullstate, base, qtrue );
+ MSG_WriteDeltaEntity( client->netchan.alternateProtocol, &msg, &nullstate, base, qtrue );
}
MSG_WriteByte( &msg, svc_EOF );
@@ -1200,8 +1188,16 @@ void SV_UserinfoChanged( client_t *cl ) {
}
#ifdef USE_VOIP
- val = Info_ValueForKey(cl->userinfo, "cl_voipProtocol");
- cl->hasVoip = !Q_stricmp( val, "opus" );
+ if( cl->netchan.alternateProtocol == 0 )
+ {
+ val = Info_ValueForKey(cl->userinfo, "cl_voipProtocol");
+ cl->hasVoip = !Q_stricmp( val, "opus" );
+ }
+ else
+ {
+ val = Info_ValueForKey(cl->userinfo, "cl_voip");
+ cl->hasVoip = atoi(val);
+ }
#endif
// TTimo
@@ -1446,7 +1442,7 @@ static void SV_UserMove( client_t *cl, msg_t *msg, qboolean delta ) {
// also use the message acknowledge
key ^= cl->messageAcknowledge;
// also use the last acknowledged server command in the key
- key ^= MSG_HashKey(cl->reliableCommands[ cl->reliableAcknowledge & (MAX_RELIABLE_COMMANDS-1) ], 32);
+ key ^= MSG_HashKey(cl->netchan.alternateProtocol, cl->reliableCommands[ cl->reliableAcknowledge & (MAX_RELIABLE_COMMANDS-1) ], 32);
Com_Memset( &nullcmd, 0, sizeof(nullcmd) );
oldcmd = &nullcmd;
@@ -1539,7 +1535,8 @@ void SV_UserVoip(client_t *cl, msg_t *msg, qboolean ignoreData)
{
int sender, generation, sequence, frames, packetsize;
uint8_t recips[(MAX_CLIENTS + 7) / 8];
- int flags;
+ int recip1 = 0, recip2 = 0, recip3 = 0; // silence warning
+ int flags = 0;
byte encoded[sizeof(cl->voipPacket[0]->data)];
client_t *client = NULL;
voipServerPacket_t *packet = NULL;
@@ -1549,8 +1546,14 @@ void SV_UserVoip(client_t *cl, msg_t *msg, qboolean ignoreData)
generation = MSG_ReadByte(msg);
sequence = MSG_ReadLong(msg);
frames = MSG_ReadByte(msg);
- MSG_ReadData(msg, recips, sizeof(recips));
- flags = MSG_ReadByte(msg);
+ if (cl->netchan.alternateProtocol == 0) {
+ MSG_ReadData(msg, recips, sizeof(recips));
+ flags = MSG_ReadByte(msg);
+ } else {
+ recip1 = MSG_ReadLong(msg);
+ recip2 = MSG_ReadLong(msg);
+ recip3 = MSG_ReadLong(msg);
+ }
packetsize = MSG_ReadShort(msg);
if (msg->readcount > msg->cursize)
@@ -1593,10 +1596,21 @@ void SV_UserVoip(client_t *cl, msg_t *msg, qboolean ignoreData)
else if (*cl->downloadName) // !!! FIXME: possible to DoS?
continue; // no VoIP allowed if downloading, to save bandwidth.
- if(Com_IsVoipTarget(recips, sizeof(recips), i))
+ if (cl->netchan.alternateProtocol == 0) {
+ if(Com_IsVoipTarget(recips, sizeof(recips), i))
+ flags |= VOIP_DIRECT;
+ else
+ flags &= ~VOIP_DIRECT;
+ } else {
+ if (i < 31 && (recip1 & (1 << (i - 0))) == 0)
+ continue; // not addressed to this player.
+ else if (i >= 31 && i < 62 && (recip2 & (1 << (i - 31))) == 0)
+ continue; // not addressed to this player.
+ else if (i >= 62 && (recip3 & (1 << (i - 62))) == 0)
+ continue; // not addressed to this player.
+
flags |= VOIP_DIRECT;
- else
- flags &= ~VOIP_DIRECT;
+ }
if (!(flags & (VOIP_SPATIAL | VOIP_DIRECT)))
continue; // not addressed to this player.
@@ -1709,6 +1723,28 @@ void SV_ExecuteClientMessage( client_t *cl, msg_t *msg ) {
do {
c = MSG_ReadByte( msg );
+ if ( cl->netchan.alternateProtocol != 0 ) {
+ // See if this is an extension command after the EOF, which means we
+ // got data that a legacy server should ignore.
+ if ( c == clc_EOF && MSG_LookaheadByte( msg ) == clc_voipSpeex ) {
+ MSG_ReadByte( msg ); // throw the clc_extension byte away.
+ c = MSG_ReadByte( msg ); // something legacy servers can't do!
+ // sometimes you get a clc_extension at end of stream...dangling
+ // bits in the huffman decoder giving a bogus value?
+ if ( c == -1 ) {
+ c = clc_EOF;
+ }
+ }
+
+ if ( c == clc_voipOpus ) {
+ c = clc_voipSpeex;
+ } else if ( c == clc_voipOpus + 1 ) {
+ c = clc_voipOpus;
+ } else if ( c == clc_voipSpeex ) {
+ c = clc_voipOpus + 1;
+ }
+ }
+
if ( c == clc_EOF ) {
break;
}
diff --git a/src/server/sv_init.c b/src/server/sv_init.c
index 380a117e..16726b40 100644
--- a/src/server/sv_init.c
+++ b/src/server/sv_init.c
@@ -626,6 +626,7 @@ Only called at main exe startup, not for each game
*/
void SV_Init (void)
{
+ int a;
int index;
SV_AddOperatorCommands ();
@@ -668,9 +669,12 @@ void SV_Init (void)
sv_allowDownload = Cvar_Get ("sv_allowDownload", "0", CVAR_SERVERINFO);
Cvar_Get ("sv_dlURL", "", CVAR_SERVERINFO | CVAR_ARCHIVE);
- sv_master[0] = Cvar_Get("sv_master1", MASTER_SERVER_NAME, 0);
- for(index = 1; index < MAX_MASTER_SERVERS; index++)
- sv_master[index] = Cvar_Get(va("sv_master%d", index + 1), "", CVAR_ARCHIVE);
+ for (a = 0; a < 3; ++a)
+ {
+ sv_masters[a][0] = Cvar_Get(va("sv_%smaster1", (a == 2 ? "alt2" : a == 1 ? "alt1" : "")), MASTER_SERVER_NAME, 0);
+ for(index = 1; index < MAX_MASTER_SERVERS; index++)
+ sv_masters[a][index] = Cvar_Get(va("sv_%smaster%d", (a == 2 ? "alt2" : a == 1 ? "alt1" : ""), index + 1), "", CVAR_ARCHIVE);
+ }
sv_reconnectlimit = Cvar_Get ("sv_reconnectlimit", "3", 0);
sv_showloss = Cvar_Get ("sv_showloss", "0", 0);
diff --git a/src/server/sv_main.c b/src/server/sv_main.c
index e5b260e5..19ec5cea 100644
--- a/src/server/sv_main.c
+++ b/src/server/sv_main.c
@@ -42,7 +42,7 @@ cvar_t *sv_maxclients;
cvar_t *sv_privateClients; // number of clients reserved for password
cvar_t *sv_hostname;
-cvar_t *sv_master[MAX_MASTER_SERVERS]; // master server ip address
+cvar_t *sv_masters[3][MAX_MASTER_SERVERS]; // master server IP addresses
cvar_t *sv_reconnectlimit; // minimum seconds between connect messages
cvar_t *sv_showloss; // report when usercmds are lost
cvar_t *sv_padPackets; // add nop bytes to messages
@@ -225,12 +225,15 @@ but not on every player enter or exit.
#define HEARTBEAT_MSEC 300*1000
void SV_MasterHeartbeat(const char *message)
{
- static netadr_t adr[MAX_MASTER_SERVERS][2]; // [2] for v4 and v6 address for the same address string.
+ static netadr_t adrs[3][MAX_MASTER_SERVERS][2]; // [2] for v4 and v6 address for the same address string.
+ int a;
int i;
int res;
int netenabled;
+ int netAlternateProtocols;
netenabled = Cvar_VariableIntegerValue("net_enabled");
+ netAlternateProtocols = Cvar_VariableIntegerValue("net_alternateProtocols");
// "dedicated 1" is for lan play, "dedicated 2" is for inet public play
if (!com_dedicated || com_dedicated->integer != 2 || !(netenabled & (NET_ENABLEV4 | NET_ENABLEV6)))
@@ -242,72 +245,86 @@ void SV_MasterHeartbeat(const char *message)
svs.nextHeartbeatTime = svs.time + HEARTBEAT_MSEC;
+ 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;
+
// send to group masters
for (i = 0; i < MAX_MASTER_SERVERS; i++)
{
- if(!sv_master[i]->string[0])
+ if(!sv_masters[a][i]->string[0])
continue;
// see if we haven't already resolved the name
// resolving usually causes hitches on win95, so only
// do it when needed
- if(sv_master[i]->modified || (adr[i][0].type == NA_BAD && adr[i][1].type == NA_BAD))
+ if(sv_masters[a][i]->modified || (adrs[a][i][0].type == NA_BAD && adrs[a][i][1].type == NA_BAD))
{
- sv_master[i]->modified = qfalse;
+ sv_masters[a][i]->modified = qfalse;
if(netenabled & NET_ENABLEV4)
{
- Com_Printf("Resolving %s (IPv4)\n", sv_master[i]->string);
- res = NET_StringToAdr(sv_master[i]->string, &adr[i][0], NA_IP);
+ Com_Printf("Resolving %s (IPv4)\n", sv_masters[a][i]->string);
+ res = NET_StringToAdr(sv_masters[a][i]->string, &adrs[a][i][0], NA_IP);
+ adrs[a][i][0].alternateProtocol = a;
if(res == 2)
{
// if no port was specified, use the default master port
- adr[i][0].port = BigShort(PORT_MASTER);
+ adrs[a][i][0].port = BigShort(a == 2 ? ALT2PORT_MASTER : a == 1 ? ALT1PORT_MASTER : PORT_MASTER);
}
if(res)
- Com_Printf( "%s resolved to %s\n", sv_master[i]->string, NET_AdrToStringwPort(adr[i][0]));
+ Com_Printf( "%s resolved to %s\n", sv_masters[a][i]->string, NET_AdrToStringwPort(adrs[a][i][0]));
else
- Com_Printf( "%s has no IPv4 address.\n", sv_master[i]->string);
+ Com_Printf( "%s has no IPv4 address.\n", sv_masters[a][i]->string);
}
if(netenabled & NET_ENABLEV6)
{
- Com_Printf("Resolving %s (IPv6)\n", sv_master[i]->string);
- res = NET_StringToAdr(sv_master[i]->string, &adr[i][1], NA_IP6);
+ Com_Printf("Resolving %s (IPv6)\n", sv_masters[a][i]->string);
+ res = NET_StringToAdr(sv_masters[a][i]->string, &adrs[a][i][1], NA_IP6);
+ adrs[a][i][1].alternateProtocol = a;
if(res == 2)
{
// if no port was specified, use the default master port
- adr[i][1].port = BigShort(PORT_MASTER);
+ adrs[a][i][1].port = BigShort(a == 2 ? ALT2PORT_MASTER : a == 1 ? ALT1PORT_MASTER : PORT_MASTER);
}
if(res)
- Com_Printf( "%s resolved to %s\n", sv_master[i]->string, NET_AdrToStringwPort(adr[i][1]));
+ Com_Printf( "%s resolved to %s\n", sv_masters[a][i]->string, NET_AdrToStringwPort(adrs[a][i][1]));
else
- Com_Printf( "%s has no IPv6 address.\n", sv_master[i]->string);
+ Com_Printf( "%s has no IPv6 address.\n", sv_masters[a][i]->string);
}
- if(adr[i][0].type == NA_BAD && adr[i][1].type == NA_BAD)
+ if(adrs[a][i][0].type == NA_BAD && adrs[a][i][1].type == NA_BAD)
{
- Com_Printf("Couldn't resolve address: %s\n", sv_master[i]->string);
- Cvar_Set(sv_master[i]->name, "");
- sv_master[i]->modified = qfalse;
+ Com_Printf("Couldn't resolve address: %s\n", sv_masters[a][i]->string);
+ Cvar_Set(sv_masters[a][i]->name, "");
+ sv_masters[a][i]->modified = qfalse;
continue;
}
}
- Com_Printf ("Sending heartbeat to %s\n", sv_master[i]->string );
+ Com_Printf ("Sending%s heartbeat to %s\n", (a == 2 ? " alternate-2" : a == 1 ? " alternate-1" : ""), sv_masters[a][i]->string );
// this command should be changed if the server info / status format
// ever incompatably changes
- if(adr[i][0].type != NA_BAD)
- NET_OutOfBandPrint( NS_SERVER, adr[i][0], "heartbeat %s\n", message);
- if(adr[i][1].type != NA_BAD)
- NET_OutOfBandPrint( NS_SERVER, adr[i][1], "heartbeat %s\n", message);
+ if(adrs[a][i][0].type != NA_BAD)
+ NET_OutOfBandPrint( NS_SERVER, adrs[a][i][0], "heartbeat %s\n", message);
+ if(adrs[a][i][1].type != NA_BAD)
+ NET_OutOfBandPrint( NS_SERVER, adrs[a][i][1], "heartbeat %s\n", message);
+ }
+ // outdent
}
}
@@ -542,6 +559,9 @@ static void SVC_Status( netadr_t from ) {
// to prevent timed spoofed reply packets that add ghost servers
Info_SetValueForKey( infostring, "challenge", Cmd_Argv(1) );
+ if ( from.alternateProtocol != 0 )
+ Info_SetValueForKey( infostring, "protocol", from.alternateProtocol == 2 ? "69" : "70" );
+
status[0] = 0;
statusLength = 0;
@@ -612,7 +632,7 @@ void SVC_Info( netadr_t from ) {
// to prevent timed spoofed reply packets that add ghost servers
Info_SetValueForKey( infostring, "challenge", Cmd_Argv(1) );
- Info_SetValueForKey( infostring, "protocol", va("%i", PROTOCOL_VERSION) );
+ Info_SetValueForKey( infostring, "protocol", va("%i", from.alternateProtocol == 2 ? 69 : from.alternateProtocol == 1 ? 70 : PROTOCOL_VERSION) );
Info_SetValueForKey( infostring, "gamename", com_gamename->string );
Info_SetValueForKey( infostring, "hostname", sv_hostname->string );
Info_SetValueForKey( infostring, "mapname", sv_mapname->string );
@@ -622,8 +642,12 @@ void SVC_Info( netadr_t from ) {
Info_SetValueForKey( infostring, "pure", va("%i", sv_pure->integer ) );
#ifdef USE_VOIP
- if (sv_voipProtocol->string && *sv_voipProtocol->string) {
- Info_SetValueForKey( infostring, "voip", sv_voipProtocol->string );
+ if (from.alternateProtocol == 0) {
+ if (sv_voipProtocol->string && *sv_voipProtocol->string) {
+ Info_SetValueForKey( infostring, "voip", sv_voipProtocol->string );
+ }
+ } else {
+ Info_SetValueForKey( infostring, "voip", va("%i", sv_voip->integer) );
}
#endif
diff --git a/src/server/sv_net_chan.c b/src/server/sv_net_chan.c
index d7ac326c..7ec27cf9 100644
--- a/src/server/sv_net_chan.c
+++ b/src/server/sv_net_chan.c
@@ -26,6 +26,110 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
#include "server.h"
/*
+==============
+SV_Netchan_Encode
+
+ // first four bytes of the data are always:
+ long reliableAcknowledge;
+
+==============
+*/
+static void SV_Netchan_Encode( client_t *client, msg_t *msg ) {
+ long i, index;
+ byte key, *string;
+ int srdc, sbit;
+ qboolean soob;
+
+ if ( msg->cursize < SV_ENCODE_START ) {
+ return;
+ }
+
+ srdc = msg->readcount;
+ sbit = msg->bit;
+ soob = msg->oob;
+
+ msg->bit = 0;
+ msg->readcount = 0;
+ msg->oob = qfalse;
+
+ /* reliableAcknowledge = */ MSG_ReadLong(msg);
+
+ msg->oob = soob;
+ msg->bit = sbit;
+ msg->readcount = srdc;
+
+ string = (byte *)client->lastClientCommandString;
+ index = 0;
+ // xor the client challenge with the netchan sequence number
+ key = client->challenge ^ client->netchan.outgoingSequence;
+ for (i = SV_ENCODE_START; i < msg->cursize; i++) {
+ // modify the key with the last received and with this message acknowledged client command
+ if (!string[index])
+ index = 0;
+ if (string[index] > 127 || (client->netchan.alternateProtocol == 2 && string[index] == '%')) {
+ key ^= '.' << (i & 1);
+ }
+ else {
+ key ^= string[index] << (i & 1);
+ }
+ index++;
+ // encode the data with this key
+ *(msg->data + i) = *(msg->data + i) ^ key;
+ }
+}
+
+/*
+==============
+SV_Netchan_Decode
+
+ // first 12 bytes of the data are always:
+ long serverId;
+ long messageAcknowledge;
+ long reliableAcknowledge;
+
+==============
+*/
+static void SV_Netchan_Decode( client_t *client, msg_t *msg ) {
+ int serverId, messageAcknowledge, reliableAcknowledge;
+ int i, index, srdc, sbit;
+ qboolean soob;
+ byte key, *string;
+
+ srdc = msg->readcount;
+ sbit = msg->bit;
+ soob = msg->oob;
+
+ msg->oob = qfalse;
+
+ serverId = MSG_ReadLong(msg);
+ messageAcknowledge = MSG_ReadLong(msg);
+ reliableAcknowledge = MSG_ReadLong(msg);
+
+ msg->oob = soob;
+ msg->bit = sbit;
+ msg->readcount = srdc;
+
+ string = (byte *)client->reliableCommands[ reliableAcknowledge & (MAX_RELIABLE_COMMANDS-1) ];
+ index = 0;
+
+ key = client->challenge ^ serverId ^ messageAcknowledge;
+ for (i = msg->readcount + SV_DECODE_START; i < msg->cursize; i++) {
+ // modify the key with the last sent and acknowledged server command
+ if (!string[index])
+ index = 0;
+ if (string[index] > 127 || (client->netchan.alternateProtocol == 2 && string[index] == '%')) {
+ key ^= '.' << (i & 1);
+ }
+ else {
+ key ^= string[index] << (i & 1);
+ }
+ index++;
+ // decode the data with this key
+ *(msg->data + i) = *(msg->data + i) ^ key;
+ }
+}
+
+/*
=================
SV_Netchan_FreeQueue
=================
@@ -126,6 +230,8 @@ void SV_Netchan_Transmit( client_t *client, msg_t *msg)
}
else
{
+ if (client->netchan.alternateProtocol != 0)
+ SV_Netchan_Encode( client, msg );
Netchan_Transmit( &client->netchan, msg->cursize, msg->data );
}
}
@@ -140,6 +246,8 @@ qboolean SV_Netchan_Process( client_t *client, msg_t *msg ) {
ret = Netchan_Process( &client->netchan, msg );
if (!ret)
return qfalse;
+ if (client->netchan.alternateProtocol != 0)
+ SV_Netchan_Decode( client, msg );
return qtrue;
}
diff --git a/src/server/sv_snapshot.c b/src/server/sv_snapshot.c
index e73e6138..09633eb7 100644
--- a/src/server/sv_snapshot.c
+++ b/src/server/sv_snapshot.c
@@ -53,7 +53,7 @@ SV_EmitPacketEntities
Writes a delta update of an entityState_t list to the message.
=============
*/
-static void SV_EmitPacketEntities( clientSnapshot_t *from, clientSnapshot_t *to, msg_t *msg ) {
+static void SV_EmitPacketEntities( int alternateProtocol, clientSnapshot_t *from, clientSnapshot_t *to, msg_t *msg ) {
entityState_t *oldent, *newent;
int oldindex, newindex;
int oldnum, newnum;
@@ -89,7 +89,7 @@ static void SV_EmitPacketEntities( clientSnapshot_t *from, clientSnapshot_t *to,
// delta update from old position
// because the force parm is qfalse, this will not result
// in any bytes being emited if the entity has not changed at all
- MSG_WriteDeltaEntity (msg, oldent, newent, qfalse );
+ MSG_WriteDeltaEntity (alternateProtocol, msg, oldent, newent, qfalse );
oldindex++;
newindex++;
continue;
@@ -97,14 +97,14 @@ static void SV_EmitPacketEntities( clientSnapshot_t *from, clientSnapshot_t *to,
if ( newnum < oldnum ) {
// this is a new entity, send it from the baseline
- MSG_WriteDeltaEntity (msg, &sv.svEntities[newnum].baseline, newent, qtrue );
+ MSG_WriteDeltaEntity (alternateProtocol, msg, &sv.svEntities[newnum].baseline, newent, qtrue );
newindex++;
continue;
}
if ( newnum > oldnum ) {
// the old entity isn't present in the new message
- MSG_WriteDeltaEntity (msg, oldent, NULL, qtrue );
+ MSG_WriteDeltaEntity (alternateProtocol, msg, oldent, NULL, qtrue );
oldindex++;
continue;
}
@@ -192,13 +192,13 @@ static void SV_WriteSnapshotToClient( client_t *client, msg_t *msg ) {
// delta encode the playerstate
if ( oldframe ) {
- MSG_WriteDeltaPlayerstate( msg, &oldframe->ps, &frame->ps );
+ MSG_WriteDeltaPlayerstate( client->netchan.alternateProtocol, msg, &oldframe->ps, &frame->ps );
} else {
- MSG_WriteDeltaPlayerstate( msg, NULL, &frame->ps );
+ MSG_WriteDeltaPlayerstate( client->netchan.alternateProtocol, msg, NULL, &frame->ps );
}
// delta encode the entities
- SV_EmitPacketEntities (oldframe, frame, msg);
+ SV_EmitPacketEntities (client->netchan.alternateProtocol, oldframe, frame, msg);
// padding for rate debugging
if ( sv_padPackets->integer ) {
@@ -551,13 +551,19 @@ static void SV_WriteVoipToClient(client_t *cl, msg_t *msg)
if (totalbytes > (msg->maxsize - msg->cursize) / 2)
break;
- MSG_WriteByte(msg, svc_voipOpus);
+ if(cl->netchan.alternateProtocol != 0)
+ {
+ MSG_WriteByte(msg, svc_EOF);
+ MSG_WriteByte(msg, svc_voipSpeex);
+ }
+ MSG_WriteByte(msg, svc_voipOpus);
MSG_WriteShort(msg, packet->sender);
MSG_WriteByte(msg, (byte) packet->generation);
MSG_WriteLong(msg, packet->sequence);
MSG_WriteByte(msg, packet->frames);
MSG_WriteShort(msg, packet->len);
- MSG_WriteBits(msg, packet->flags, VOIP_FLAGCNT);
+ if(cl->netchan.alternateProtocol == 0)
+ MSG_WriteBits(msg, packet->flags, VOIP_FLAGCNT);
MSG_WriteData(msg, packet->data, packet->len);
}