/* ===================== CL_ParseServerMessage ===================== */ void CL_ParseServerMessage( msg_t *msg ) { int cmd; // msg_t msgback; // msgback = *msg; if ( cl_shownet->integer == 1 ) { Com_Printf("%i ", msg->cursize ); } else if ( cl_shownet->integer >= 2 ) { Com_Printf( "------------------\n" ); } MSG_Bitstream( msg ); // get the reliable sequence acknowledge number clc.reliableAcknowledge = MSG_ReadLong( msg ); // if ( clc.reliableAcknowledge < clc.reliableSequence - MAX_RELIABLE_COMMANDS ) { clc.reliableAcknowledge = clc.reliableSequence; } // // parse the message // while ( 1 ) { if ( msg->readcount > msg->cursize ) { Com_Error( ERR_DROP, "CL_ParseServerMessage: read past end of server message" ); } cmd = MSG_ReadByte( msg ); // 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_extension ) ) { 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_EOF ) { SHOWNET( msg, "END OF MESSAGE" ); break; } if ( cl_shownet->integer >= 2 ) { if ( !svc_strings[ cmd ] ) { Com_Printf( "%3i:BAD CMD %i\n", msg->readcount - 1, cmd ); } else { SHOWNET( msg, svc_strings[ cmd ] ); } } // other commands switch ( cmd ) { default: Com_Error( ERR_DROP, "CL_ParseServerMessage: Illegible server message %d", cmd ); case svc_nop: break; case svc_serverCommand: CL_ParseCommandString( msg ); break; case svc_gamestate: CL_ParseGamestate( msg ); break; case svc_snapshot: CL_ParseSnapshot( msg ); break; case svc_download: CL_ParseDownload( msg ); break; case svc_voip: #ifdef USE_VOIP CL_ParseVoip( msg ); #endif break; } } }
/* =================== SV_ExecuteClientMessage Parse a client packet =================== */ void SV_ExecuteClientMessage( client_t *cl, msg_t *msg ) { int c; int serverId; MSG_Bitstream(msg); serverId = MSG_ReadLong( msg ); cl->messageAcknowledge = MSG_ReadLong( msg ); if (cl->messageAcknowledge < 0) { // usually only hackers create messages like this // it is more annoying for them to let them hanging #ifndef NDEBUG SV_DropClient( cl, "DEBUG: illegible client message" ); #endif return; } cl->reliableAcknowledge = MSG_ReadLong( msg ); // NOTE: when the client message is fux0red the acknowledgement numbers // can be out of range, this could cause the server to send thousands of server // commands which the server thinks are not yet acknowledged in SV_UpdateServerCommandsToClient if (cl->reliableAcknowledge < cl->reliableSequence - MAX_RELIABLE_COMMANDS) { // usually only hackers create messages like this // it is more annoying for them to let them hanging #ifndef NDEBUG SV_DropClient( cl, "DEBUG: illegible client message" ); #endif cl->reliableAcknowledge = cl->reliableSequence; return; } // if this is a usercmd from a previous gamestate, // ignore it or retransmit the current gamestate // // if the client was downloading, let it stay at whatever serverId and // gamestate it was at. This allows it to keep downloading even when // the gamestate changes. After the download is finished, we'll // notice and send it a new game state // // https://zerowing.idsoftware.com/bugzilla/show_bug.cgi?id=536 // don't drop as long as previous command was a nextdl, after a dl is done, downloadName is set back to "" // but we still need to read the next message to move to next download or send gamestate // I don't like this hack though, it must have been working fine at some point, suspecting the fix is somewhere else if ( serverId != sv.serverId && !*cl->downloadName && !strstr(cl->lastClientCommandString, "nextdl") ) { if ( serverId >= sv.restartedServerId && serverId < sv.serverId ) { // TTimo - use a comparison here to catch multiple map_restart // they just haven't caught the map_restart yet Com_DPrintf("%s : ignoring pre map_restart / outdated client message\n", cl->name); return; } // if we can tell that the client has dropped the last // gamestate we sent them, resend it if ( cl->messageAcknowledge > cl->gamestateMessageNum ) { Com_DPrintf( "%s : dropped gamestate, resending\n", cl->name ); SV_SendClientGameState( cl ); } return; } // this client has acknowledged the new gamestate so it's // safe to start sending it the real time again if( cl->oldServerTime && serverId == sv.serverId ){ Com_DPrintf( "%s acknowledged gamestate\n", cl->name ); cl->oldServerTime = 0; } // read optional clientCommand strings do { c = MSG_ReadByte( msg ); // 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_extension)) { 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_EOF ) { break; } if ( c != clc_clientCommand ) { break; } if ( !SV_ClientCommand( cl, msg ) ) { return; // we couldn't execute it because of the flood protection } if (cl->state == CS_ZOMBIE) { return; // disconnect command } } while ( 1 ); // read the usercmd_t if ( c == clc_move ) { SV_UserMove( cl, msg, qtrue ); } else if ( c == clc_moveNoDelta ) { SV_UserMove( cl, msg, qfalse ); } else if ( c == clc_voip ) { #ifdef USE_VOIP SV_UserVoip( cl, msg ); #endif } else if ( c != clc_EOF ) { Com_Printf( "WARNING: bad command byte for client %i\n", (int) (cl - svs.clients) ); } // if ( msg->readcount != msg->cursize ) { // Com_Printf( "WARNING: Junk at end of packet for client %i\n", cl - svs.clients ); // } }