/*! \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
/** 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
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
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); } }
/** 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