/*! \brief Called when a player asks to select a kart.
 *  \param event : Event providing the information.
 *
 *  Format of the data :
 *  Byte 0          1                      2            
 *       ----------------------------------------------
 *  Size |    1     |           1         |     N     |
 *  Data |player id |  N (kart name size) | kart name |
 *       ----------------------------------------------
 */
void ServerLobbyRoomProtocol::kartSelectionRequested(Event* event)
{
    if(m_state!=SELECTING)
    {
        Log::warn("Server", "Received kart selection while in state %d.",
                  m_state);
        return;
    }

    if (!checkDataSize(event, 1)) return;

    const NetworkString &data = event->data();
    STKPeer* peer = event->getPeer();

    uint8_t player_id = data.getUInt8();
    std::string kart_name;
    data.decodeString(&kart_name);
    // check if selection is possible
    if (!m_selection_enabled)
    {
        NetworkString *answer = getNetworkString(2);
        // selection still not started
        answer->addUInt8(LE_KART_SELECTION_REFUSED).addUInt8(2);
        peer->sendPacket(answer);
        delete answer;
        return;
    }
    // check if somebody picked that kart
    if (!m_setup->isKartAvailable(kart_name))
    {
        NetworkString *answer = getNetworkString(2);
        // kart is already taken
        answer->addUInt8(LE_KART_SELECTION_REFUSED).addUInt8(0);
        peer->sendPacket(answer);
        delete answer;
        return;
    }
    // check if this kart is authorized
    if (!m_setup->isKartAllowed(kart_name))
    {
        NetworkString *answer = getNetworkString(2);
        // kart is not authorized
        answer->addUInt8(LE_KART_SELECTION_REFUSED).addUInt8(1);
        peer->sendPacket(answer);
        delete answer;
        return;
    }

    // send a kart update to everyone
    NetworkString *answer = getNetworkString(3+kart_name.size());
    // This message must be handled synchronously on the client.
    answer->setSynchronous(true);
    // kart update (3), 1, race id
    answer->addUInt8(LE_KART_SELECTION_UPDATE).addUInt8(player_id)
          .encodeString(kart_name);
    sendMessageToPeersChangingToken(answer);
    delete answer;
    m_setup->setPlayerKart(player_id, kart_name);
}   // kartSelectionRequested
/** Sends a vote for the number of tracks from a client to the server. Note
 *  that even this client will only store the vote when it is received back
 *  from the server.
 *  \param player_id The global player id of the voting player.
 *  \param count NUmber of tracks to play.
 */
void ClientLobbyRoomProtocol::voteRaceCount(uint8_t player_id, uint8_t count)
{
    NetworkString *request = getNetworkString(3);
    request->addUInt8(LE_VOTE_RACE_COUNT).addUInt8(player_id).addUInt8(count);
    sendToServer(request, true);
    delete request;
}   // voteRaceCount
/** Called from the gui when a client clicked on 'continue' on the race result
 *  screen. It notifies the server that this client has exited the screen and
 *  is back at the lobby.
 */
void ClientLobbyRoomProtocol::doneWithResults()
{
    NetworkString *done = getNetworkString(1);
    done->addUInt8(LE_RACE_FINISHED_ACK);
    sendToServer(done, /*reliable*/true);
    delete done;
}   // doneWithResults
/** Sends a vote for the minor game mode from a client to the server. Note that
 *  even this client will only store the vote when it is received back from the
 *  server.
 *  \param player_id The global player id of the voting player.
 *  \param minor Voted minor mode.
 */
void ClientLobbyRoomProtocol::voteMinor(uint8_t player_id, uint32_t minor)
{
    NetworkString *request = getNetworkString(6);
    request->addUInt8(LE_VOTE_MINOR).addUInt8(player_id).addUInt32(minor);
    sendToServer(request, true);
    delete request;
}   // voteMinor
// ----------------------------------------------------------------------------
void GameEventsProtocol::sendStartupBoost(uint8_t kart_id)
{
    NetworkString *ns = getNetworkString();
    ns->setSynchronous(true);
    ns->addUInt8(GE_STARTUP_BOOST).addUInt8(kart_id);
    sendToServer(ns, /*reliable*/true);
    delete ns;
}   // sendStartupBoost
/** This function is called from the server when a kart finishes a race. It
 *  sends a notification to all clients about this event.
 *  \param kart The kart that finished the race.
 *  \param time The time at which the kart finished.
 */
void GameEventsProtocol::kartFinishedRace(AbstractKart *kart, float time)
{
    NetworkString *ns = getNetworkString(20);
    ns->setSynchronous(true);
    ns->addUInt8(GE_KART_FINISHED_RACE).addUInt8(kart->getWorldKartId())
       .addFloat(time);
    sendMessageToPeers(ns, /*reliable*/true);
    delete ns;
}   // kartFinishedRace
/** Sends the selection of a kart from this client to the server.
 *  \param player_id The global player id of the voting player.
 *  \param kart_name Name of the selected kart.
 */
void ClientLobbyRoomProtocol::requestKartSelection(uint8_t player_id,
                                                   const std::string &kart_name)
{
    NetworkString *request = getNetworkString(3+kart_name.size());
    request->addUInt8(LE_KART_SELECTION).addUInt8(player_id)
            .encodeString(kart_name);
    sendToServer(request, /*reliable*/ true);
    delete request;
}   // requestKartSelection
/** Vote for the number of laps of the specified track. Note that even this
 *  client will only store the vote when it is received back from the server.
 *  \param player_id Global player id of the voting player.
 *  \param laps Number of laps for the specified track.
 *  \param track_nb Index of the track in the list of all tracks.
 */
void ClientLobbyRoomProtocol::voteLaps(uint8_t player_id, uint8_t laps,
                                       uint8_t track_nb)
{
    NetworkString *request = getNetworkString(10);
    request->addUInt8(LE_VOTE_LAPS).addUInt8(player_id).addUInt8(laps)
            .addUInt8(track_nb);
    sendToServer(request, true);
    delete request;
}   // voteLaps
/** Sends a vote if a track at a specified place in the list of all tracks
 *  should be played in reverse or not. Note that even this client will only
 *  store the vote when it is received back from the server.
 *  \param player_id Global player id of the voting player.
 *  \param reversed True if the track should be played in reverse.
 *  \param track_nb Index for the track to be voted on in the list of all
 *         tracks.
 */
void ClientLobbyRoomProtocol::voteReversed(uint8_t player_id, bool reversed, 
                                           uint8_t track_nb)
{
    NetworkString *request = getNetworkString(9);
    request->addUInt8(LE_VOTE_REVERSE).addUInt8(player_id).addUInt8(reversed)
            .addUInt8(track_nb);
    sendToServer(request, true);
    delete request;
}   // voteReversed
void ClientLobbyRoomProtocol::update(float dt)
{
    switch (m_state)
    {
    case NONE:
        if (STKHost::get()->isConnectedTo(m_server_address))
        {
            m_state = LINKED;
        }
        break;
    case LINKED:
    {
        core::stringw name;
        if(PlayerManager::getCurrentOnlineState()==PlayerProfile::OS_SIGNED_IN)
            name = PlayerManager::getCurrentOnlineUserName();
        else
            name = PlayerManager::getCurrentPlayer()->getName();

        std::string name_u8 = StringUtils::wideToUtf8(name);
        const std::string &password = NetworkConfig::get()->getPassword();
        NetworkString *ns = getNetworkString(6+1+name_u8.size()
                                                 +1+password.size());
        // 4 (size of id), global id
        ns->addUInt8(LE_CONNECTION_REQUESTED).encodeString(name)
          .encodeString(NetworkConfig::get()->getPassword());
        sendToServer(ns);
        delete ns;
        m_state = REQUESTING_CONNECTION;
    }
    break;
    case REQUESTING_CONNECTION:
        break;
    case CONNECTED:
        break;
    case KART_SELECTION:
    {
        NetworkKartSelectionScreen* screen =
                                     NetworkKartSelectionScreen::getInstance();
        screen->push();
        m_state = SELECTING_KARTS;
    }
    break;
    case SELECTING_KARTS:
        break;
    case PLAYING:
        break;
    case RACE_FINISHED:
        break;
    case DONE:
        m_state = EXITING;
        ProtocolManager::getInstance()->requestTerminate(this);
        break;
    case EXITING:
        break;
    }
}   // update
/** Sends the vote about which track to play at which place in the list of
 *  tracks (like a custom GP definition). Note that even this client will only
 *  store the vote when it is received back from the server.
 *  \param player_id The global player id of the voting player.
 *  \param track Name of the track.
 *  \param At which place in the list of tracks this track should be played.
 */
void ClientLobbyRoomProtocol::voteTrack(uint8_t player_id,
                                        const std::string &track,
                                        uint8_t track_nb)
{
    NetworkString *request = getNetworkString(2+1+track.size());
    request->addUInt8(LE_VOTE_TRACK).addUInt8(player_id).addUInt8(track_nb)
            .encodeString(track);
    sendToServer(request, true);
    delete request;
}   // voteTrack
/** This function informs each client to start the race, and then starts the
 *  StartGameProtocol.
 */
void ServerLobbyRoomProtocol::startGame()
{
    const std::vector<STKPeer*> &peers = STKHost::get()->getPeers();
    NetworkString *ns = getNetworkString(1);
    ns->addUInt8(LE_START_RACE);
    sendMessageToPeersChangingToken(ns, /*reliable*/true);
    delete ns;
    Protocol *p = new StartGameProtocol(m_setup);
    p->requestStart();
    m_state = RACING;
}   // startGame
/*! \brief Called when a player votes for a minor race mode.
 *  \param event : Event providing the information.
 *
 *  Format of the data :
 *  Byte 0           1
 *       -------------------------------
 *  Size |      1    |         4       |
 *  Data | player-id | minor mode vote |
 *       -------------------------------
 */
void ServerLobbyRoomProtocol::playerMinorVote(Event* event)
{
    if (!checkDataSize(event, 1)) return;
    NetworkString &data = event->data();
    uint8_t player_id   = data.getUInt8();
    uint32_t minor      = data.getUInt32();
    m_setup->getRaceConfig()->setPlayerMinorVote(player_id, minor);

    // Send the vote to everybody (including the sender)
    NetworkString *other = getNetworkString(3);
    other->addUInt8(LE_VOTE_MINOR).addUInt8(player_id).addUInt8(minor); 
    sendMessageToPeersChangingToken(other);
    delete other;
}   // playerMinorVote
/*! \brief Called when a player votes for a major race mode.
 *  \param event : Event providing the information.
 *
 *  Format of the data :
 *  Byte 0           1      2 
 *       ----------------------------------------
 *  Size |     1     |   1  |       1           |
 *  Data | player id | laps | track number (gp) |
 *       ----------------------------------------
 */
void ServerLobbyRoomProtocol::playerLapsVote(Event* event)
{
    if (!checkDataSize(event, 2)) return;
    NetworkString &data = event->data();
    uint8_t player_id   = data.getUInt8();
    uint8_t lap_count   = data.getUInt8();
    uint8_t track_nb    = data.getUInt8();
    m_setup->getRaceConfig()->setPlayerLapsVote(player_id, lap_count,
                                                track_nb);
    NetworkString *other = getNetworkString(4);
    other->addUInt8(LE_VOTE_LAPS).addUInt8(player_id).addUInt8(lap_count)
          .addUInt8(track_nb);
    sendMessageToPeersChangingToken(other);
    delete other;
}   // playerLapsVote
/** Checks if the race is finished, and if so informs the clients and switches
 *  to state RESULT_DISPLAY, during which the race result gui is shown and all
 *  clients can click on 'continue'.
 */
 void ServerLobbyRoomProtocol::checkRaceFinished()
{
    assert(RaceEventManager::getInstance()->isRunning());
    assert(World::getWorld());
    if(!RaceEventManager::getInstance()->isRaceOver()) return;

    m_player_ready_counter = 0;
    // Set the delay before the server forces all clients to exit the race
    // result screen and go back to the lobby
    m_timeout = (float)(StkTime::getRealTime()+15.0f);
    m_state = RESULT_DISPLAY;

    // calculate karts ranks :
    int num_karts = race_manager->getNumberOfKarts();
    std::vector<int> karts_results;
    std::vector<float> karts_times;
    for (int j = 0; j < num_karts; j++)
    {
        float kart_time = race_manager->getKartRaceTime(j);
        for (unsigned int i = 0; i < karts_times.size(); i++)
        {
            if (kart_time < karts_times[i])
            {
                karts_times.insert(karts_times.begin() + i, kart_time);
                karts_results.insert(karts_results.begin() + i, j);
                break;
            }
        }
    }

    const std::vector<STKPeer*> &peers = STKHost::get()->getPeers();

    NetworkString *total = getNetworkString(1 + karts_results.size());
    total->setSynchronous(true);
    total->addUInt8(LE_RACE_FINISHED);
    for (unsigned int i = 0; i < karts_results.size(); i++)
    {
        total->addUInt8(karts_results[i]); // kart pos = i+1
        Log::info("ServerLobbyRoomProtocol", "Kart %d finished #%d",
            karts_results[i], i + 1);
    }
    sendMessageToPeersChangingToken(total, /*reliable*/ true);
    delete total;
    Log::info("ServerLobbyRoomProtocol", "End of game message sent");
        
}   // checkRaceFinished
/*! \brief Called when a player votes for the reverse mode of a race
 *  \param event : Event providing the information.
 *
 *  Format of the data :
 *  Byte 0           1          2
 *       --------------------------------------------
 *  Size |     1     |     1    |       1           |
 *  Data | player id | reversed | track number (gp) |
 *       --------------------------------------------
 */
void ServerLobbyRoomProtocol::playerReversedVote(Event* event)
{
    if (!checkDataSize(event, 3)) return;

    NetworkString &data = event->data();
    uint8_t player_id   = data.getUInt8();
    uint8_t reverse     = data.getUInt8();
    uint8_t nb_track    = data.getUInt8();
    m_setup->getRaceConfig()->setPlayerReversedVote(player_id,
                                                    reverse!=0, nb_track);
    // Send the vote to everybody (including the sender)
    NetworkString *other = getNetworkString(4);
    other->addUInt8(LE_VOTE_REVERSE).addUInt8(player_id).addUInt8(reverse)
          .addUInt8(nb_track);
    sendMessageToPeersChangingToken(other);
    delete other;
}   // playerReversedVote
Beispiel #17
0
/** Waits for the countdown to be started. On the server the start of the
 *  countdown is triggered by ServerLobby::finishedLoadingWorld(),
 *  which is called once all clients have confirmed that they are ready to
 *  start. The server will send a ping request to each client once a second,
 *  and include the information if the countdown has started (and its current
 *  value). On the client the countdown is started in notifyEvenAsynchronous()
 *  when a server ping is received that indicates that the countdown has
 *  started. The measured times can be used later to estimate the latency
 *  between server and client.
 */
void LatencyProtocol::asynchronousUpdate()
{
    float current_time = float(StkTime::getRealTime());
    if (NetworkConfig::get()->isServer() &&  current_time > m_last_time+1)
    {
        const std::vector<STKPeer*> &peers = STKHost::get()->getPeers();
        for (unsigned int i = 0; i < peers.size(); i++)
        {
            NetworkString *ping_request = 
                            getNetworkString(5);
            ping_request->addUInt8(1).addUInt32(m_pings[i].size());
            m_pings[i] [ m_pings_count ] = current_time;
            peers[i]->sendPacket(ping_request, false);
            delete ping_request;
        }   // for i M peers
        m_last_time = current_time;
        m_pings_count++;
    }   // if current_time > m_last_time + 0.1
}   // asynchronousUpdate
Beispiel #18
0
void update_welcome(int socket, Message *m)
{
	int nNetworkStrings = m->sum;
	int nStringsGotten = 0;
	int i;
	NetworkString strings[nNetworkStrings];

	mlog("client.log", "nNetworkStrings = %d", nNetworkStrings);

	for (i=0; i<nNetworkStrings; i++) {
		if (getNetworkString(socket, &(strings[i])) != 0) {
			mlog("client.log", "Could not get network string: %s\n",
					strerror(errno));
		} else {
			nStringsGotten++;
		}
	}

	mlog("client.log", "got %d strings", nStringsGotten);

	/* i starts at zero */
	if (nStringsGotten != 2) {
		mlog("client.log", "upgrade protocol error: should receive "
				"2 network strings when updating welcome, got "
				"%d.", i);
		return;
	} else {
		char *lang_code = strings[0].string;
		char *newmsg = strings[1].string;
		if (update_welcome_db(lang_code, newmsg) != 0) {
			mlog("client.log", "could not update database.");
		} else {
			mlog("client.log", "updated welcome");
		}

	}

	for (i=0; i<nNetworkStrings; i++) {
		free(strings[i].string);
	}
}
/*! \brief Called when a player votes for a track.
 *  \param event : Event providing the information.
 *
 *  Format of the data :
 *  Byte 0           1                    2  3
 *       --------------------------------------------------
 *  Size |     1     |        1          | 1 |      N     |
 *  Data | player id | track number (gp) | N | track name |
 *       --------------------------------------------------
 */
void ServerLobbyRoomProtocol::playerTrackVote(Event* event)
{
    if (!checkDataSize(event, 3)) return;
    NetworkString &data  = event->data();
    uint8_t player_id    = data.getUInt8();
    // As which track this track should be used, e.g. 1st track: Sandtrack
    // 2nd track Mathclass, ...
    uint8_t track_number = data.getUInt8();
    std::string track_name;
    int N = data.decodeString(&track_name);
    m_setup->getRaceConfig()->setPlayerTrackVote(player_id, track_name,
                                                 track_number);
    // Send the vote to everybody (including the sender)
    NetworkString *other = getNetworkString(3+1+data.size());
    other->addUInt8(LE_VOTE_TRACK).addUInt8(player_id).addUInt8(track_number)
          .encodeString(track_name);
    sendMessageToPeersChangingToken(other);
    delete other;
    if(m_setup->getRaceConfig()->getNumTrackVotes()==m_setup->getPlayerCount())
        startGame();
}   // playerTrackVote
/** Instructs all clients to start the kart selection. If event is not NULL,
 *  the command comes from a client (which needs to be authorised).
 */
void ServerLobbyRoomProtocol::startSelection(const Event *event)
{
    if(event && !event->getPeer()->isAuthorised())
    {
        Log::warn("ServerLobby", 
                  "Client %lx is not authorised to start selection.",
                  event->getPeer());
        return;
    }
    const std::vector<STKPeer*> &peers = STKHost::get()->getPeers();
    NetworkString *ns = getNetworkString(1);
    // start selection
    ns->addUInt8(LE_START_SELECTION);
    sendMessageToPeersChangingToken(ns, /*reliable*/true);
    delete ns;

    m_selection_enabled = true;

    m_state = SELECTING;
    WaitingForOthersScreen::getInstance()->push();
}   // startSelection
/** Called when a client disconnects.
 *  \param event The disconnect event.
 */
void ServerLobbyRoomProtocol::clientDisconnected(Event* event)
{
    std::vector<NetworkPlayerProfile*> players_on_host = 
                                      event->getPeer()->getAllPlayerProfiles();

    NetworkString *msg = getNetworkString(2);
    msg->addUInt8(LE_PLAYER_DISCONNECTED);

    for(unsigned int i=0; i<players_on_host.size(); i++)
    {
        msg->addUInt8(players_on_host[i]->getGlobalPlayerId());
        Log::info("ServerLobbyRoomProtocol", "Player disconnected : id %d",
                  players_on_host[i]->getGlobalPlayerId());
        m_setup->removePlayer(players_on_host[i]);
    }

    sendMessageToPeersChangingToken(msg, /*reliable*/true);
    // Remove the profile from the peer (to avoid double free)
    STKHost::get()->removePeer(event->getPeer());
    delete msg;
    
}   // clientDisconnected
Beispiel #22
0
void add_language(int socket, Message *m)
{
	int nNetworkStrings = m->sum;
	int i;
	NetworkString strings[nNetworkStrings];

	for (i=0; i<nNetworkStrings; i++) {
		if (getNetworkString(socket, &(strings[i])) != 0) {
			mlog("client.log", "Could not get network string: %s\n",
					strerror(errno));
		}
	}

	char *lang_strings[nNetworkStrings];
	for (i=0; i<nNetworkStrings; i++) {
		lang_strings[i] = strings[i].string;
	}

	create_language(lang_strings, nNetworkStrings);

	for (i=0; i<nNetworkStrings; i++) {
		free(strings[i].string);
	}
}
Beispiel #23
0
/** Called when receiving a message. On the client side the message is a ping
 *  from the server, which is answered back. On the server the received message
 *  is a reply to a previous ping request. The server will keep track of
 *  average latency.
 */
bool LatencyProtocol::notifyEventAsynchronous(Event* event)
{
    if (event->getType() != EVENT_TYPE_MESSAGE)
        return true;
    if(!checkDataSize(event, 5)) return true;

    const NetworkString &data = event->data();
    uint32_t request  = data.getUInt8();
    uint32_t sequence = data.getUInt32();

    const std::vector<STKPeer*> &peers = STKHost::get()->getPeers();
    assert(peers.size() > 0);

    // Find the right peer id. The host id (i.e. each host sendings its
    // host id) can not be used here, since host ids can have gaps (if a
    // host should disconnect)
    uint8_t peer_id = -1;
    for (unsigned int i = 0; i < peers.size(); i++)
    {
        if (peers[i]->isSamePeer(event->getPeer()))
        {
            peer_id = i;
            break;
        }
    }

    if (request)
    {
        // Only a client should receive a request for a ping response
        assert(NetworkConfig::get()->isClient());
        NetworkString *response = getNetworkString(5);
        // The '0' indicates a response to a ping request
        response->addUInt8(0).addUInt32(sequence);
        event->getPeer()->sendPacket(response, false);
        delete response;
    }
    else // receive response to a ping request
    {
        // Only a server should receive this kind of message
        assert(NetworkConfig::get()->isServer());
        if (sequence >= m_pings[peer_id].size())
        {
            Log::warn("LatencyProtocol",
                      "The sequence# %u isn't known.", sequence);
            return true;
        }
        double current_time = StkTime::getRealTime();
        m_total_diff[peer_id] += current_time - m_pings[peer_id][sequence];
        m_successed_pings[peer_id]++;
        m_average_ping[peer_id] =
            (int)((m_total_diff[peer_id]/m_successed_pings[peer_id])*1000.0);

        Log::debug("LatencyProtocol",
            "Peer %d sequence %d ping %u average %u at %lf",
            peer_id, sequence,
            (unsigned int)((current_time - m_pings[peer_id][sequence])*1000),
            m_average_ping[peer_id],
            StkTime::getRealTime());
    }
    return true;
}   // notifyEventAsynchronous
/*! \brief Called when a player asks for a connection.
 *  \param event : Event providing the information.
 *
 *  Format of the data :
 *  Byte 0   1                
 *       ---------------------
 *  Size | 1 |1|             |
 *  Data | 4 |n| player name |
 *       ---------------------
 */
void ServerLobbyRoomProtocol::connectionRequested(Event* event)
{
    STKPeer* peer = event->getPeer();
    const NetworkString &data = event->data();

    // can we add the player ?
    if (m_setup->getPlayerCount() >= NetworkConfig::get()->getMaxPlayers() ||
        m_state!=ACCEPTING_CLIENTS                                           )
    {
        NetworkString *message = getNetworkString(2);
        // Len, error code: 2 = busy, 0 = too many players
        message->addUInt8(LE_CONNECTION_REFUSED)
                .addUInt8(m_state!=ACCEPTING_CLIENTS ? 2 : 0);

        // send only to the peer that made the request
        peer->sendPacket(message);
        delete message;
        Log::verbose("ServerLobbyRoomProtocol", "Player refused");
        return;
    }

    // Connection accepted.
    // ====================
    std::string name_u8;
    int len = data.decodeString(&name_u8);
    core::stringw name = StringUtils::utf8ToWide(name_u8);
    std::string password;
    data.decodeString(&password);
    bool is_authorised = (password==NetworkConfig::get()->getPassword());

    // Get the unique global ID for this player.
    m_next_player_id.lock();
    m_next_player_id.getData()++;
    int new_player_id = m_next_player_id.getData();
    m_next_player_id.unlock();
    if(m_setup->getLocalMasterID()==0)
        m_setup->setLocalMaster(new_player_id);

    // The host id has already been incremented when the peer
    // was added, so it is the right id now.
    int new_host_id = STKHost::get()->getNextHostId();

    // Notify everybody that there is a new player
    // -------------------------------------------
    NetworkString *message = getNetworkString(3+1+name_u8.size());
    // size of id -- id -- size of local id -- local id;
    message->addUInt8(LE_NEW_PLAYER_CONNECTED).addUInt8(new_player_id)
            .addUInt8(new_host_id).encodeString(name_u8);
    STKHost::get()->sendPacketExcept(peer, message);
    delete message;

    // Now answer to the peer that just connected
    // ------------------------------------------
    RandomGenerator token_generator;
    // use 4 random numbers because rand_max is probably 2^15-1.
    uint32_t token = (uint32_t)((token_generator.get(RAND_MAX) & 0xff) << 24 |
                                (token_generator.get(RAND_MAX) & 0xff) << 16 |
                                (token_generator.get(RAND_MAX) & 0xff) <<  8 |
                                (token_generator.get(RAND_MAX) & 0xff));

    peer->setClientServerToken(token);
    peer->setAuthorised(is_authorised);
    peer->setHostId(new_host_id);

    const std::vector<NetworkPlayerProfile*> &players = m_setup->getPlayers();
    // send a message to the one that asked to connect
    // Estimate 10 as average name length
    NetworkString *message_ack = getNetworkString(4 + players.size() * (2+10));
    // connection success -- size of token -- token
    message_ack->addUInt8(LE_CONNECTION_ACCEPTED).addUInt8(new_player_id)
                .addUInt8(new_host_id).addUInt8(is_authorised);
    // Add all players so that this user knows (this new player is only added
    // to the list of players later, so the new player's info is not included)
    for (unsigned int i = 0; i < players.size(); i++)
    {
        message_ack->addUInt8(players[i]->getGlobalPlayerId())
                    .addUInt8(players[i]->getHostId())
                    .encodeString(players[i]->getName());
    }
    peer->sendPacket(message_ack);
    delete message_ack;

    NetworkPlayerProfile* profile = 
        new NetworkPlayerProfile(name, new_player_id, new_host_id);
    m_setup->addPlayer(profile);
    NetworkingLobby::getInstance()->addPlayer(profile);

    Log::verbose("ServerLobbyRoomProtocol", "New player.");

}   // connectionRequested
// ----------------------------------------------------------------------------
bool GameEventsProtocol::notifyEvent(Event* event)
{
    // Avoid crash in case that we still receive race events when
    // the race is actually over.
    if (event->getType() != EVENT_TYPE_MESSAGE || !World::getWorld())
        return true;
    NetworkString &data = event->data();
    if (data.size() < 1) // for type
    {
        Log::warn("GameEventsProtocol", "Too short message.");
        return true;
    }
    uint8_t type = data.getUInt8();
    CaptureTheFlag* ctf = dynamic_cast<CaptureTheFlag*>(World::getWorld());
    FreeForAll* ffa = dynamic_cast<FreeForAll*>(World::getWorld());
    SoccerWorld* sw = dynamic_cast<SoccerWorld*>(World::getWorld());
    LinearWorld* lw = dynamic_cast<LinearWorld*>(World::getWorld());
    switch (type)
    {
    case GE_KART_FINISHED_RACE:
        kartFinishedRace(data);     break;
    case GE_RESET_BALL:
    {
        if (!sw)
            throw std::invalid_argument("No soccer world");
        sw->handleResetBallFromServer(data);
        break;
    }
    case GE_PLAYER_GOAL:
    {
        if (!sw)
            throw std::invalid_argument("No soccer world");
        sw->handlePlayerGoalFromServer(data);
        break;
    }
    case GE_BATTLE_KART_SCORE:
    {
        if (!ffa)
            throw std::invalid_argument("No free-for-all world");
        ffa->setKartScoreFromServer(data);
        break;
    }
    case GE_CTF_SCORED:
    {
        if (!ctf)
            throw std::invalid_argument("No CTF world");
        uint8_t kart_id = data.getUInt8();
        bool red_team_scored = data.getUInt8() == 1;
        int16_t new_kart_scores = data.getUInt16();
        int new_red_scores = data.getUInt8();
        int new_blue_scores = data.getUInt8();
        ctf->ctfScored(kart_id, red_team_scored, new_kart_scores,
            new_red_scores, new_blue_scores);
        break;
    }
    case GE_STARTUP_BOOST:
    {
        if (NetworkConfig::get()->isServer())
        {
            uint8_t kart_id = data.getUInt8();
            if (!event->getPeer()->availableKartID(kart_id))
            {
                Log::warn("GameProtocol", "Wrong kart id %d from %s.",
                    kart_id, event->getPeer()->getAddress().toString().c_str());
                return true;
            }
            float f = LobbyProtocol::get<ServerLobby>()
                ->getStartupBoostOrPenaltyForKart(
                event->getPeer()->getAveragePing(), kart_id);
            NetworkString *ns = getNetworkString();
            ns->setSynchronous(true);
            ns->addUInt8(GE_STARTUP_BOOST).addUInt8(kart_id).addFloat(f);
            sendMessageToPeers(ns, true);
            delete ns;
        }
        else
        {
            uint8_t kart_id = data.getUInt8();
            float boost = data.getFloat();
            AbstractKart* k = World::getWorld()->getKart(kart_id);
            if (boost < 0.0f)
            {
                PlayerController* pc =
                    dynamic_cast<PlayerController*>(k->getController());
                pc->displayPenaltyWarning();
            }
            else
                k->setStartupBoost(boost);
        }
        break;
    }
    case GE_CHECK_LINE:
    {
        if (!lw)
            throw std::invalid_argument("No linear world");
        if (NetworkConfig::get()->isClient())
            lw->updateCheckLinesClient(data);
        break;
    }
    default:
        Log::warn("GameEventsProtocol", "Unkown message type.");
        break;
    }
    return true;
}   // notifyEvent
/** Simple finite state machine. First get the public ip address. Once this
 *  is known, register the server and its address with the stk server so that
 *  client can find it.
 */
void ServerLobbyRoomProtocol::update(float dt)
{
    switch (m_state)
    {
    case NONE:
        // Start the protocol to find the public ip address.
        m_current_protocol = new GetPublicAddress(this);
        m_current_protocol->requestStart();
        m_state = GETTING_PUBLIC_ADDRESS;
        // The callback from GetPublicAddress will wake this protocol up
        requestPause();
        break;
    case GETTING_PUBLIC_ADDRESS:
        {
            Log::debug("ServerLobbyRoomProtocol", "Public address known.");
            // Free GetPublicAddress protocol
            delete m_current_protocol;

            // Register this server with the STK server. This will block
            // this thread, but there is no need for the protocol manager
            // to react to any requests before the server is registered.
            registerServer();
            Log::info("ServerLobbyRoomProtocol", "Server registered.");
            m_state = ACCEPTING_CLIENTS;
        }
        break;
    case ACCEPTING_CLIENTS:
    {
        // Only poll the STK server if this is a WAN server.
        if(NetworkConfig::get()->isWAN())
            checkIncomingConnectionRequests();
        break;
    }
    case SELECTING:
        break;   // Nothing to do, this is event based
    case RACING:
        if (World::getWorld() &&
            RaceEventManager::getInstance<RaceEventManager>()->isRunning())
        {
            checkRaceFinished();
        }
        break;
    case RESULT_DISPLAY:
        if(StkTime::getRealTime() > m_timeout)
        {
            // Send a notification to all clients to exit 
            // the race result screen
            NetworkString *exit_result_screen = getNetworkString(1);
            exit_result_screen->setSynchronous(true);
            exit_result_screen->addUInt8(LE_EXIT_RESULT);
            sendMessageToPeersChangingToken(exit_result_screen,
                                            /*reliable*/true);
            delete exit_result_screen;
            m_state = ACCEPTING_CLIENTS;
            RaceResultGUI::getInstance()->backToLobby();
            // notify the network world that it is stopped
            RaceEventManager::getInstance()->stop();
            // stop race protocols
            findAndTerminateProtocol(PROTOCOL_CONTROLLER_EVENTS);
            findAndTerminateProtocol(PROTOCOL_KART_UPDATE);
            findAndTerminateProtocol(PROTOCOL_GAME_EVENTS);
        }
        break;
    case DONE:
        m_state = EXITING;
        requestTerminate();
        break;
    case EXITING:
        break;
    }
}   // update