/*! \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
/** 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
/*! \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
/** 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 number of races in a GP. * \param event : Event providing the information. * * Format of the data : * Byte 0 1 5 6 7 * ------------------------------------ * Size | 1 | 4 | 1 | 1 | * Data | 4 | priv token | 1 | races count | * ------------------------------------ */ void ServerLobbyRoomProtocol::playerRaceCountVote(Event* event) { NetworkString &data = event->data(); STKPeer* peer = event->getPeer(); if (!checkDataSizeAndToken(event, 7)) return; if (!isByteCorrect(event, 5, 1)) return; uint8_t player_id = peer->getPlayerProfile()->getGlobalPlayerId(); m_setup->getRaceConfig()->setPlayerRaceCountVote(player_id, data[6]); // Send the vote to everybody (including the sender) data.removeFront(5); // remove the token NetworkString other(2+data.size()); other.ai8(1).ai8(player_id); // add the player id other += data; // add the data sendMessageToPeersChangingToken(LE_VOTE_RACE_COUNT, other); } // playerRaceCountVote
/*! \brief Called when a player votes for a minor race mode. * \param event : Event providing the information. * * Format of the data : * Byte 0 1 5 6 7 * ---------------------------------------- * Size | 1 | 4 | 1 | 1 | * Data | 4 | priv token | 1 | minor mode vote | * ---------------------------------------- */ void ServerLobbyRoomProtocol::playerMinorVote(Event* event) { NetworkString data = event->data(); STKPeer* peer = *(event->peer); if (!checkDataSizeAndToken(event, 7)) return; if (!isByteCorrect(event, 5, 1)) return; uint8_t player_id = peer->getPlayerProfile()->race_id; m_setup->getRaceConfig()->setPlayerMinorVote(player_id, data[6]); // Send the vote to everybody (including the sender) NetworkString other; other.ai8(1).ai8(player_id); // add the player id data.removeFront(5); // remove the token other += data; // add the data NetworkString prefix; prefix.ai8(0xc2); // prefix the token with the ype sendMessageToPeersChangingToken(prefix, other); }
/*! \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
/*! \brief Called when a player votes for a track. * \param event : Event providing the information. * * Format of the data : * Byte 0 1 5 6 N+6 N+7 N+8 * ----------------------------------------------------------- * Size | 1 | 4 | 1 | N | 1 | 1 | * Data | 4 | priv token | N | track name | 1 | track number (gp) | * ----------------------------------------------------------- */ void ServerLobbyRoomProtocol::playerTrackVote(Event* event) { NetworkString &data = event->data(); STKPeer* peer = event->getPeer(); if (!checkDataSizeAndToken(event, 8)) return; std::string track_name; int N = data.decodeString(5, &track_name); if (!isByteCorrect(event, N+5, 1)) return; uint8_t player_id = peer->getPlayerProfile()->getGlobalPlayerId(); m_setup->getRaceConfig()->setPlayerTrackVote(player_id, track_name, data[N+6]); // Send the vote to everybody (including the sender) data.removeFront(5); // remove the token NetworkString other(2+data.size()); other.ai8(1).ai8(player_id); // add the player id other += data; // add the data sendMessageToPeersChangingToken(LE_VOTE_TRACK, other); if(m_setup->getRaceConfig()->getNumTrackVotes()==m_setup->getPlayerCount()) startGame(); } // playerTrackVote
/** 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
/** 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