/** Sends a vote for a major vote 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 major Major mode voted for. */ void ClientLobbyRoomProtocol::voteMajor(uint8_t player_id, uint32_t major) { NetworkString *request = getNetworkString(6); request->addUInt8(LE_VOTE_MAJOR).addUInt8(player_id) .addUInt32(major); sendToServer(request, true); delete request; } // voteMajor
// ---------------------------------------------------------------------------- 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
/** 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
/** 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
/** 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
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
/** Sends a packet to this host. * \param data The data to send. * \param reliable If the data is sent reliable or not. */ void STKPeer::sendPacket(NetworkString const& data, bool reliable) { TransportAddress a(m_enet_peer->address); Log::verbose("STKPeer", "sending packet of size %d to %s", data.size(), a.toString().c_str()); ENetPacket* packet = enet_packet_create(data.getBytes(), data.size() + 1, (reliable ? ENET_PACKET_FLAG_RELIABLE : ENET_PACKET_FLAG_UNSEQUENCED)); enet_peer_send(m_enet_peer, 0, packet); } // sendPacket
/** 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 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
void ServerLobbyRoomProtocol::startSelection() { std::vector<STKPeer*> peers = NetworkManager::getInstance()->getPeers(); for (unsigned int i = 0; i < peers.size(); i++) { NetworkString ns; ns.ai8(0x05).ai8(4).ai32(peers[i]->getClientServerToken()); // start selection m_listener->sendMessage(this, peers[i], ns, true); // reliably } m_selection_enabled = true; }
void ServerLobbyRoomProtocol::startGame() { std::vector<STKPeer*> peers = NetworkManager::getInstance()->getPeers(); for (unsigned int i = 0; i < peers.size(); i++) { NetworkString ns; ns.ai8(0x04).ai8(4).ai32(peers[i]->getClientServerToken()); // start game m_listener->sendMessage(this, peers[i], ns, true); // reliably } m_listener->requestStart(new StartGameProtocol(m_setup)); m_in_race = true; }
void SynchronizationProtocol::asynchronousUpdate() { static double timer = StkTime::getRealTime(); double current_time = StkTime::getRealTime(); if (m_countdown_activated) { m_countdown -= (current_time - m_last_countdown_update); m_last_countdown_update = current_time; Log::debug("SynchronizationProtocol", "Update! Countdown remaining : %f", m_countdown); if (m_countdown < 0.0 && !m_has_quit) { m_has_quit = true; Log::info("SynchronizationProtocol", "Countdown finished. Starting now."); m_listener->requestStart(new KartUpdateProtocol()); m_listener->requestStart(new ControllerEventsProtocol()); m_listener->requestStart(new GameEventsProtocol()); m_listener->requestTerminate(this); return; } static int seconds = -1; if (seconds == -1) { seconds = (int)(ceil(m_countdown)); } else if (seconds != (int)(ceil(m_countdown))) { seconds = (int)(ceil(m_countdown)); Log::info("SynchronizationProtocol", "Starting in %d seconds.", seconds); } } if (current_time > timer+0.1) { std::vector<STKPeer*> peers = NetworkManager::getInstance()->getPeers(); for (unsigned int i = 0; i < peers.size(); i++) { NetworkString ns; ns.ai8(i).addUInt32(peers[i]->getClientServerToken()).addUInt8(1).addUInt32(m_pings[i].size()); // now add the countdown if necessary if (m_countdown_activated && m_listener->isServer()) { ns.addUInt32((int)(m_countdown*1000.0)); Log::debug("SynchronizationProtocol", "CNTActivated: Countdown value : %f", m_countdown); } Log::verbose("SynchronizationProtocol", "Added sequence number %u for peer %d", m_pings[i].size(), i); timer = current_time; m_pings[i].insert(std::pair<int,double>(m_pings_count[i], timer)); m_listener->sendMessage(this, peers[i], ns, false); m_pings_count[i]++; } } }
/** Saves a state on the client. Used to save an initial state at t=0 for each * client in case that we receive an event from another client (which will * trigger a rewind) before a state from the server. */ void RewindManager::saveLocalState() { int ticks = World::getWorld()->getTimeTicks(); saveState(/*local_state*/true); NetworkString *state = GameProtocol::lock()->getState(); // Copy the data to a new string, making the buffer in // GameProtocol availble for again. BareNetworkString *bns = new BareNetworkString(state->getCurrentData(), state->size() ); m_rewind_queue.addLocalState(bns, /*confirmed*/true, ticks); } // saveLocalState
/*! \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
void ServerLobbyRoomProtocol::kartDisconnected(Event* event) { STKPeer* peer = *(event->peer); if (peer->getPlayerProfile() != NULL) // others knew him { NetworkString msg; msg.ai8(0x02).ai8(1).ai8(peer->getPlayerProfile()->race_id); m_listener->sendMessage(this, msg); Log::info("ServerLobbyRoomProtocol", "Player disconnected : id %d", peer->getPlayerProfile()->race_id); m_setup->removePlayer(peer->getPlayerProfile()->race_id); NetworkManager::getInstance()->removePeer(peer); } else Log::info("ServerLobbyRoomProtocol", "The DC peer wasn't registered."); }
/*! \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
void StartGameProtocol::ready() // on clients, means the loading is finished { if (!m_listener->isServer()) // if we're a client { assert(NetworkManager::getInstance()->getPeerCount() == 1); NetworkString ns; ns.ai32(NetworkManager::getInstance()->getPeers()[0]->getClientServerToken()).ai8(1); Log::info("StartGameProtocol", "Player ready, notifying server."); m_listener->sendMessage(this, ns, true); m_state = READY; m_ready = true; return; } else // on the server { } }
/** 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
void STKPeer::sendPacket(NetworkString const& data, bool reliable) { TransportAddress a(m_peer->address); Log::verbose("STKPeer", "sending packet of size %d to %s", a.toString().c_str()); ENetPacket* packet = enet_packet_create(data.getBytes(), data.size() + 1, (reliable ? ENET_PACKET_FLAG_RELIABLE : ENET_PACKET_FLAG_UNSEQUENCED)); /* to debug the packet output printf("STKPeer: "); for (unsigned int i = 0; i < data.size(); i++) { printf("%d ", (uint8_t)(data[i])); } printf("\n"); */ enet_peer_send(m_peer, 0, packet); }
/** This function is called on a client when it receives a kartFinishedRace * event from the server. It updates the game with this information. * \param ns The message from the server. */ void GameEventsProtocol::kartFinishedRace(const NetworkString &ns) { if (ns.size() < 5) { Log::warn("GameEventsProtocol", "kartFinisheRace: Too short message."); return; } uint8_t kart_id = ns.getUInt8(); float time = ns.getFloat(); if (race_manager->modeHasLaps()) { World::getWorld()->getKart(kart_id) ->setPosition(m_last_finished_position++); } World::getWorld()->getKart(kart_id)->finishedRace(time, /*from_server*/true); } // kartFinishedRace
bool StartGameProtocol::notifyEventAsynchronous(Event* event) { NetworkString data = event->data(); if (data.size() < 5) { Log::error("StartGameProtocol", "Too short message."); return true; } uint32_t token = data.gui32(); uint8_t ready = data.gui8(4); STKPeer* peer = (*(event->peer)); if (peer->getClientServerToken() != token) { Log::error("StartGameProtocol", "Bad token received."); return true; } if (m_listener->isServer() && ready) // on server, player is ready { Log::info("StartGameProtocol", "One of the players is ready."); m_player_states[peer->getPlayerProfile()] = READY; m_ready_count++; if (m_ready_count == m_game_setup->getPlayerCount()) { // everybody ready, synchronize SynchronizationProtocol* protocol = static_cast<SynchronizationProtocol*>(m_listener->getProtocol(PROTOCOL_SYNCHRONIZATION)); if (protocol) { protocol->startCountdown(5000); // 5 seconds countdown Log::info("StartGameProtocol", "All players ready, starting countdown."); m_ready = true; return true; } else Log::error("StartGameProtocol", "The Synchronization protocol hasn't been started."); } } else // on the client, we shouldn't even receive messages. { Log::error("StartGameProtocol", "Received a message with bad format."); } return true; }
/*! \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
bool ServerLobbyRoomProtocol::notifyEventAsynchronous(Event* event) { assert(m_setup); // assert that the setup exists if (event->type == EVENT_TYPE_MESSAGE) { NetworkString data = event->data(); assert(data.size()); // message not empty uint8_t message_type; message_type = data[0]; event->removeFront(1); Log::info("ServerLobbyRoomProtocol", "Message received with type %d.", message_type); if (message_type == 0x01) // player requesting connection connectionRequested(event); else if (message_type == 0x02) // player requesting kart selection kartSelectionRequested(event); else if (message_type == 0xc0) // vote for major mode playerMajorVote(event); else if (message_type == 0xc1) // vote for race count playerRaceCountVote(event); else if (message_type == 0xc2) // vote for minor mode playerMinorVote(event); else if (message_type == 0xc3) // vote for track playerTrackVote(event); else if (message_type == 0xc4) // vote for reversed mode playerReversedVote(event); else if (message_type == 0xc5) // vote for laps playerLapsVote(event); } // if (event->type == EVENT_TYPE_MESSAGE) else if (event->type == EVENT_TYPE_CONNECTED) { } // if (event->type == EVENT_TYPE_CONNECTED) else if (event->type == EVENT_TYPE_DISCONNECTED) { kartDisconnected(event); } // if (event->type == EVENT_TYPE_DISCONNECTED) return true; }
/** \brief Log packets into a file * \param ns : The data in the packet * \param incoming : True if the packet comes from a peer. * False if it's sent to a peer. */ void Network::logPacket(const NetworkString &ns, bool incoming) { if (m_log_file.getData() == NULL) // read only access, no need to lock return; const char *arrow = incoming ? "<--" : "-->"; m_log_file.lock(); fprintf(m_log_file.getData(), "[%d\t] %s ", (int)(StkTime::getRealTime()), arrow); for (int i = 0; i < ns.size(); i++) { fprintf(m_log_file.getData(), "%d.", ns[i]); } fprintf(m_log_file.getData(), "\n"); m_log_file.unlock(); } // logPacket
// ---------------------------------------------------------------------------- void STKHost::handleLANRequests() { const int LEN=2048; char buffer[LEN]; TransportAddress sender; int len = m_lan_network->receiveRawPacket(buffer, LEN, &sender, 1); if(len<=0) return; if (std::string(buffer, len) == "stk-server") { Log::verbose("STKHost", "Received LAN server query"); std::string name = StringUtils::wideToUtf8(NetworkConfig::get()->getServerName()); // Avoid buffer overflows if (name.size() > 255) name = name.substr(0, 255); // Send the answer, consisting of server name, max players, // current players, and the client's ip address and port // number (which solves the problem which network interface // might be the right one if there is more than one). NetworkString s; s.encodeString(name); s.addUInt8(NetworkConfig::get()->getMaxPlayers()); s.addUInt8(0); // FIXME: current number of connected players s.addUInt32(sender.getIP()); s.addUInt16(sender.getPort()); m_lan_network->sendRawPacket(s.getBytes(), s.size(), sender); } // if message is server-requested else if (std::string(buffer, len) == "connection-request") { Protocol *c = new ConnectToPeer(sender); c->requestStart(); } else Log::info("STKHost", "Received unknown command '%s'", std::string(buffer, len).c_str()); } // handleLANRequests