static void SV_CheckStartMatch (void) { client_t *cl; if (!sv->spawned || sv->started) return; if (sv_maxclients->integer > 1) { cl = NULL; while ((cl = SV_GetNextClient(cl)) != NULL) { /* all players must have their actors spawned */ if (cl->state != cs_spawned) return; } } else if (SV_GetClient(0)->state != cs_spawned) { /* in single player mode we must have received the 'spawnsoldiers' */ return; } sv->started = true; cl = NULL; while ((cl = SV_GetNextClient(cl)) != NULL) if (cl->state != cs_free) SV_ClientCommand(cl, "startmatch\n"); }
/* =================== SV_ExecuteClientMessage Parse a client packet =================== */ void SV_ExecuteClientMessage( client_t *cl, msg_t *msg ) { int c; while( 1 ) { if ( msg->readcount > msg->cursize ) { SV_DropClient (cl, "had a badread"); return; } c = MSG_ReadByte( msg ); if ( c == -1 ) { break; } switch( c ) { default: SV_DropClient( cl,"had an unknown command char" ); return; case clc_nop: break; case clc_move: SV_UserMove( cl, msg ); break; case clc_clientCommand: SV_ClientCommand( cl, msg ); if (cl->state == CS_ZOMBIE) { return; // disconnect command } break; } } }
/** * @brief If all connected clients have set their ready flag the server will spawn the clients * and that change the client state. * @sa SV_Spawn_f */ static void SV_CheckSpawnSoldiers (void) { client_t *cl; /* already started? */ if (sv->spawned) return; if (sv_maxclients->integer > 1) { cl = NULL; while ((cl = SV_GetNextClient(cl)) != NULL) { /* all players must be connected and all of them must have set * the ready flag */ if (cl->state != cs_began || !cl->player->isReady) return; } } else if (SV_GetClient(0)->state != cs_began) { /* in single player mode we must have received the 'begin' */ return; } sv->spawned = qtrue; cl = NULL; while ((cl = SV_GetNextClient(cl)) != NULL) if (cl->state != cs_free) SV_ClientCommand(cl, "spawnsoldiers\n"); }
/** * @brief If all connected clients have set their ready flag the server will spawn the clients * and that change the client state. * @sa SV_Spawn_f */ static void SV_CheckSpawnSoldiers (void) { /* already started? */ if (sv->spawned) return; client_t* cl = nullptr; while ((cl = SV_GetNextClient(cl)) != nullptr) { /* all players must be connected and all of them must have set * the ready flag */ if (cl->state != cs_began || !cl->player->isReady()) return; } sv->spawned = true; cl = nullptr; while ((cl = SV_GetNextClient(cl)) != nullptr) if (cl->state != cs_free) SV_ClientCommand(cl, CL_SPAWNSOLDIERS "\n"); }
/** * @brief If all connected clients have set their ready flag the server will spawn the clients * and that change the client state. * @sa SV_Spawn_f */ static void SV_CheckSpawnSoldiers (void) { client_t *cl; /* already started? */ if (sv->spawned) return; cl = NULL; while ((cl = SV_GetNextClient(cl)) != NULL) { /* all players must be connected and all of them must have set * the ready flag */ if (cl->state != cs_began || !cl->player->isReady) return; } sv->spawned = true; cl = NULL; while ((cl = SV_GetNextClient(cl)) != NULL) if (cl->state != cs_free) SV_ClientCommand(cl, "spawnsoldiers\n"); }
/** * @brief Sends the first message from the server to a connected client. * This will be sent on the initial connection and upon each server load. * Client reads via CL_ParseServerData in cl_parse.c * @sa CL_Reconnect_f * @sa CL_ConnectionlessPacket */ static void SV_New_f (client_t *cl) { Com_DPrintf(DEBUG_SERVER, "New() from %s\n", cl->name); if (cl->state != cs_connected) { if (cl->state == cs_spawning) { /* client typed 'reconnect/new' while connecting. */ Com_Printf("SV_New_f: client typed 'reconnect/new' while connecting\n"); SV_ClientCommand(cl, "\ndisconnect\nreconnect\n"); SV_DropClient(cl, ""); } else Com_DPrintf(DEBUG_SERVER, "WARNING: Illegal 'new' from %s, client state %d. This shouldn't happen...\n", cl->name, cl->state); return; } /* client state to prevent multiple new from causing high cpu / overflows. */ SV_SetClientState(cl, cs_spawning); /* serverdata needs to go over for all types of servers * to make sure the protocol is right, and to set the gamedir */ /* send the serverdata */ { const int playernum = cl - SV_GetClient(0); struct dbuffer *msg = new_dbuffer(); NET_WriteByte(msg, svc_serverdata); NET_WriteLong(msg, PROTOCOL_VERSION); NET_WriteShort(msg, playernum); /* send full levelname */ NET_WriteString(msg, SV_GetConfigString(CS_NAME)); NET_WriteMsg(cl->stream, msg); } /* game server */ if (Com_ServerState() == ss_game) { int i; for (i = 0; i < MAX_CONFIGSTRINGS; i++) { const char *configString; /* CS_TILES and CS_POSITIONS can stretch over multiple configstrings, * so don't send the middle parts again. */ if (i > CS_TILES && i < CS_POSITIONS) continue; if (i > CS_POSITIONS && i < CS_MODELS) continue; configString = SV_GetConfigString(i); if (configString[0] != '\0') { struct dbuffer *msg = new_dbuffer(); Com_DPrintf(DEBUG_SERVER, "sending configstring %d: %s\n", i, configString); NET_WriteByte(msg, svc_configstring); NET_WriteShort(msg, i); NET_WriteString(msg, configString); /* enqueue and free msg */ NET_WriteMsg(cl->stream, msg); } } } SV_ClientCommand(cl, "precache\n"); }
/* =================== 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 // // 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 Log::Debug( "%s^7: ignoring pre map_restart / outdated client message", 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 ) { Log::Debug( "%s^7: dropped gamestate, resending", cl->name ); SV_SendClientGameState( cl ); } // read optional clientCommand strings do { c = MSG_ReadByte( msg ); if ( c == clc_EOF ) { break; } if ( c != clc_clientCommand ) { break; } if ( !SV_ClientCommand( cl, msg, true ) ) { return; // we couldn't execute it because of the flood protection } if ( cl->state == clientState_t::CS_ZOMBIE ) { return; // disconnect command } } while ( 1 ); return; } // read optional clientCommand strings do { c = MSG_ReadByte( msg ); if ( c == clc_EOF ) { break; } if ( c != clc_clientCommand ) { break; } if ( !SV_ClientCommand( cl, msg, false ) ) { return; // we couldn't execute it because of the flood protection } if ( cl->state == clientState_t::CS_ZOMBIE ) { return; // disconnect command } } while ( 1 ); // read the usercmd_t if ( c == clc_move ) { SV_UserMove( cl, msg, true ); } else if ( c == clc_moveNoDelta ) { SV_UserMove( cl, msg, false ); } else if ( c != clc_EOF ) { Log::Warn( "bad command byte for client %i\n", ( int )( cl - svs.clients ) ); } SV_ParseBinaryMessage( cl, msg ); // if ( msg->readcount != msg->cursize ) { // Log::Notice( "WARNING: Junk at end of packet for client %i\n", cl - svs.clients ); // } }
/* =================== 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 //SV_DropClient( cl, "illegible client message" ); 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 //SV_DropClient( cl, "illegible client message" ); 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 if ( serverId != sv.serverId && !*cl->downloadName ) { if ( serverId == sv.restartedServerId ) { // they just haven't caught the map_restart yet 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; } // read optional clientCommand strings do { c = MSG_ReadByte( msg ); 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_EOF ) { Com_Printf( "WARNING: bad command byte for client %i\n", cl - svs.clients ); } // if ( msg->readcount != msg->cursize ) { // Com_Printf( "WARNING: Junk at end of packet for client %i\n", cl - svs.clients ); // } }
/* =================== 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 ); // } }