int SV_ReplacePendingServerCommands( client_t *client, const char *cmd ) { int i, index, csnum1, csnum2; for ( i = client->reliableSent + 1; i <= client->reliableSequence; i++ ) { index = i & ( MAX_RELIABLE_COMMANDS - 1 ); // //if ( !Q_strncmp(cmd, client->reliableCommands[ index ], strlen("cs")) ) { if ( !Q_strncmp( cmd, SV_GetReliableCommand( client, index ), strlen( "cs" ) ) ) { sscanf( cmd, "cs %i", &csnum1 ); //sscanf(client->reliableCommands[ index ], "cs %i", &csnum2); sscanf( SV_GetReliableCommand( client, index ), "cs %i", &csnum2 ); if ( csnum1 == csnum2 ) { //Q_strncpyz( client->reliableCommands[ index ], cmd, sizeof( client->reliableCommands[ index ] ) ); /* if ( client->netchan.remoteAddress.type != NA_BOT ) { Com_Printf( "WARNING: client %i removed double pending config string %i: %s\n", client-svs.clients, csnum1, cmd ); } */ return qtrue; } } } return qfalse; }
/* ====================== SV_AddServerCommand The given command will be transmitted to the client, and is guaranteed to not have future snapshot_t executed before it is executed ====================== */ void SV_AddServerCommand( client_t *client, const char *cmd ) { int index, i; client->reliableSequence++; // if we would be losing an old command that hasn't been acknowledged, // we must drop the connection // we check == instead of >= so a broadcast print added by SV_DropClient() // doesn't cause a recursive drop client if ( client->reliableSequence - client->reliableAcknowledge == MAX_RELIABLE_COMMANDS + 1 ) { Com_Printf( "===== pending server commands =====\n" ); for ( i = client->reliableAcknowledge + 1 ; i <= client->reliableSequence ; i++ ) { #if defined RTCW_SP //Com_Printf( "cmd %5d: %s\n", i, client->reliableCommands[ i & (MAX_RELIABLE_COMMANDS-1) ] ); Com_Printf( "cmd %5d: %s\n", i, SV_GetReliableCommand( client, i & ( MAX_RELIABLE_COMMANDS - 1 ) ) ); #else Com_Printf( "cmd %5d: %s\n", i, client->reliableCommands[ i & ( MAX_RELIABLE_COMMANDS - 1 ) ] ); #endif // RTCW_XX } Com_Printf( "cmd %5d: %s\n", i, cmd ); SV_DropClient( client, "Server command overflow" ); return; } index = client->reliableSequence & ( MAX_RELIABLE_COMMANDS - 1 ); #if defined RTCW_SP //Q_strncpyz( client->reliableCommands[ index ], cmd, sizeof( client->reliableCommands[ index ] ) ); SV_AddReliableCommand( client, index, cmd ); #else Q_strncpyz( client->reliableCommands[ index ], cmd, sizeof( client->reliableCommands[ index ] ) ); #endif // RTCW_XX }
/* ================== SV_UpdateServerCommandsToClient (re)send all server commands the client hasn't acknowledged yet ================== */ void SV_UpdateServerCommandsToClient( client_t *client, msg_t *msg ) { int i; // write any unacknowledged serverCommands for ( i = client->reliableAcknowledge + 1 ; i <= client->reliableSequence ; i++ ) { MSG_WriteByte( msg, svc_serverCommand ); MSG_WriteLong( msg, i ); //MSG_WriteString( msg, client->reliableCommands[ i & (MAX_RELIABLE_COMMANDS-1) ] ); MSG_WriteString( msg, SV_GetReliableCommand( client, i & ( MAX_RELIABLE_COMMANDS - 1 ) ) ); } client->reliableSent = client->reliableSequence; }
/* ============== 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, soob; byte key, *string; srdc = msg->readcount; sbit = msg->bit; soob = msg->oob; 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 *)SV_GetReliableCommand( client, 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 || 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_UserMove The message usually contains all the movement commands that were in the last three packets, so that the information in dropped packets can be recovered. On very fast clients, there may be multiple usercmd packed into each of the backup packets. ================== */ static void SV_UserMove(client_t *cl, msg_t *msg, qboolean delta) { int i, key; int cmdCount; usercmd_t nullcmd; usercmd_t cmds[MAX_PACKET_USERCMDS]; usercmd_t *cmd, *oldcmd; if(delta) { cl->deltaMessage = cl->messageAcknowledge; } else { cl->deltaMessage = -1; } cmdCount = MSG_ReadByte(msg); if(cmdCount < 1) { Com_Printf("cmdCount < 1\n"); return; } if(cmdCount > MAX_PACKET_USERCMDS) { Com_Printf("cmdCount > MAX_PACKET_USERCMDS\n"); return; } // use the checksum feed in the key key = sv.checksumFeed; // also use the message acknowledge key ^= cl->messageAcknowledge; // also use the last acknowledged server command in the key //key ^= Com_HashKey(cl->reliableCommands[ cl->reliableAcknowledge & (MAX_RELIABLE_COMMANDS-1) ], 32); key ^= Com_HashKey(SV_GetReliableCommand(cl, cl->reliableAcknowledge & (MAX_RELIABLE_COMMANDS - 1)), 32); memset(&nullcmd, 0, sizeof(nullcmd)); oldcmd = &nullcmd; for(i = 0 ; i < cmdCount ; i++) { cmd = &cmds[i]; MSG_ReadDeltaUsercmdKey(msg, key, oldcmd, cmd); // MSG_ReadDeltaUsercmd( msg, oldcmd, cmd ); oldcmd = cmd; } // save time for ping calculation cl->frames[ cl->messageAcknowledge & PACKET_MASK ].messageAcked = svs.time; // if this is the first usercmd we have received // this gamestate, put the client into the world if(cl->state == CS_PRIMED) { SV_ClientEnterWorld(cl, &cmds[0]); // the moves can be processed normaly } // if(sv_pure->integer != 0 && cl->pureAuthentic == 0) { SV_DropClient(cl, "Cannot validate pure client!"); return; } if(cl->state != CS_ACTIVE) { cl->deltaMessage = -1; return; } // usually, the first couple commands will be duplicates // of ones we have previously received, but the servertimes // in the commands will cause them to be immediately discarded for(i = 0 ; i < cmdCount ; i++) { // if this is a cmd from before a map_restart ignore it if(cmds[i].serverTime > cmds[cmdCount - 1].serverTime) { continue; } // extremely lagged or cmd from before a map_restart //if ( cmds[i].serverTime > svs.time + 3000 ) { // continue; //} if(sv_gametype->integer != GT_SINGLE_PLAYER) // RF, we need to allow this in single player, where loadgame's can cause the player to freeze after reloading if we do this check { // don't execute if this is an old cmd which is already executed // these old cmds are included when cl_packetdup > 0 if(cmds[i].serverTime <= cl->lastUsercmd.serverTime) // Q3_MISSIONPACK { // if ( cmds[i].serverTime > cmds[cmdCount-1].serverTime ) { continue; // from just before a map_restart } } SV_ClientThink(cl, &cmds[ i ]); } }