/* ====================== 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++) { Com_Printf("cmd %5d: %s\n", i, SV_GetServerCommand( client, i ) ); } Com_Printf("cmd %5d: %s\n", i, cmd); SV_DropClient(client, "Server command overflow"); return; } index = client->reliableSequence & (MAX_RELIABLE_COMMANDS - 1); client->reliableCommands[ index ] = Q_strcpy_ringbuffer( client->reliableCommandBuffer, sizeof( client->reliableCommandBuffer ), client->reliableCommands[ (client->reliableAcknowledge) & ( MAX_RELIABLE_COMMANDS - 1 ) ], client->reliableCommands[ (client->reliableSequence - 1) & ( MAX_RELIABLE_COMMANDS - 1 ) ], cmd ); if ( !client->reliableCommands[ index ] ) { SV_DropClient( client, "Server command buffer overflow" ); } }
/* ================== 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, SV_GetServerCommand( client, i ) ); } client->reliableSent = client->reliableSequence; }
/* ================== 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( SV_GetServerCommand( cl, cl->reliableAcknowledge ), 32); Com_Memset( &nullcmd, 0, sizeof(nullcmd) ); oldcmd = &nullcmd; for ( i = 0 ; i < cmdCount ; i++ ) { cmd = &cmds[i]; MSG_ReadDeltaUsercmdKey( msg, key, oldcmd, cmd ); oldcmd = cmd; } // save time for ping calculation cl->frames[ cl->messageAcknowledge & PACKET_MASK ].messageAcked = svs.time; // TTimo // catch the no-cp-yet situation before SV_ClientEnterWorld // if CS_ACTIVE, then it's time to trigger a new gamestate emission // if not, then we are getting remaining parasite usermove commands, which we should ignore if (sv_pure->integer != 0 && cl->pureAuthentic == 0 && !cl->gotCP) { if (cl->state == CS_ACTIVE) { // we didn't get a cp yet, don't assume anything and just send the gamestate all over again Com_DPrintf( "%s: didn't get cp command, resending gamestate\n", cl->name, cl->state ); SV_SendClientGameState( cl ); } return; } // 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 } // a bad cp command was sent, drop the client 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; //} // 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 ) { continue; } SV_ClientThink (cl, &cmds[ i ]); } }