diff --git a/src/network/protocols/client_lobby.cpp b/src/network/protocols/client_lobby.cpp index 3895f5df5..27cd2a47b 100644 --- a/src/network/protocols/client_lobby.cpp +++ b/src/network/protocols/client_lobby.cpp @@ -52,6 +52,7 @@ #include "network/stk_peer.hpp" #include "online/online_profile.hpp" #include "online/xml_request.hpp" +#include "states_screens/dialogs/addons_pack.hpp" #include "states_screens/online/networking_lobby.hpp" #include "states_screens/online/network_kart_selection.hpp" #include "states_screens/race_result_gui.hpp" @@ -372,21 +373,7 @@ void ClientLobby::update(int ticks) for (const std::string& cap : stk_config->m_network_capabilities) ns->encodeString(cap); - auto all_k = kart_properties_manager->getAllAvailableKarts(); - auto all_t = track_manager->getAllTrackIdentifiers(); - if (all_k.size() >= 65536) - all_k.resize(65535); - if (all_t.size() >= 65536) - all_t.resize(65535); - ns->addUInt16((uint16_t)all_k.size()).addUInt16((uint16_t)all_t.size()); - for (const std::string& kart : all_k) - { - ns->encodeString(kart); - } - for (const std::string& track : all_t) - { - ns->encodeString(track); - } + getKartsTracksNetworkString(ns); assert(!NetworkConfig::get()->isAddingNetworkPlayers()); const uint8_t player_count = (uint8_t)NetworkConfig::get()->getNetworkPlayers().size(); @@ -1511,3 +1498,55 @@ void ClientLobby::reportSuccess(Event* event) MessageQueue::add(MessageQueue::MT_GENERIC, msg); } } // reportSuccess + +// ---------------------------------------------------------------------------- +void ClientLobby::handleClientCommand(const std::string& cmd) +{ +#ifndef SERVER_ONLY + auto argv = StringUtils::split(cmd, ' '); + if (argv.size() == 0) + return; + if (argv[0] == "installaddon" && argv.size() == 2) + AddonsPack::install(argv[1]); + else if (argv[0] == "uninstalladdon" && argv.size() == 2) + AddonsPack::uninstall(argv[1]); + else + { + // Send for server command + NetworkString* cmd_ns = getNetworkString(1); + cmd_ns->addUInt8(LE_COMMAND).encodeString(cmd); + sendToServer(cmd_ns, /*reliable*/true); + delete cmd_ns; + } +#endif +} // handleClientCommand + +// ---------------------------------------------------------------------------- +void ClientLobby::getKartsTracksNetworkString(BareNetworkString* ns) +{ + auto all_k = kart_properties_manager->getAllAvailableKarts(); + auto all_t = track_manager->getAllTrackIdentifiers(); + if (all_k.size() >= 65536) + all_k.resize(65535); + if (all_t.size() >= 65536) + all_t.resize(65535); + ns->addUInt16((uint16_t)all_k.size()).addUInt16((uint16_t)all_t.size()); + for (const std::string& kart : all_k) + { + ns->encodeString(kart); + } + for (const std::string& track : all_t) + { + ns->encodeString(track); + } +} // getKartsTracksNetworkString + +// ---------------------------------------------------------------------------- +void ClientLobby::updateAssetsToServer() +{ + NetworkString* ns = getNetworkString(1); + ns->addUInt8(LE_ASSETS_UPDATE); + getKartsTracksNetworkString(ns); + sendToServer(ns, /*reliable*/true); + delete ns; +} // updateAssetsToServer diff --git a/src/network/protocols/client_lobby.hpp b/src/network/protocols/client_lobby.hpp index 952f73564..7bd1224c3 100644 --- a/src/network/protocols/client_lobby.hpp +++ b/src/network/protocols/client_lobby.hpp @@ -139,6 +139,7 @@ private: decodePlayers(const BareNetworkString& data, std::shared_ptr peer = nullptr, bool* is_spectator = NULL) const; + void getKartsTracksNetworkString(BareNetworkString* ns); public: ClientLobby(const TransportAddress& a, std::shared_ptr s); virtual ~ClientLobby(); @@ -182,6 +183,8 @@ public: { return m_server_enabled_report_player; } const std::vector& getRankingChanges() const { return m_ranking_changes; } + void handleClientCommand(const std::string& cmd); + void updateAssetsToServer(); }; #endif // CLIENT_LOBBY_HPP diff --git a/src/network/protocols/lobby_protocol.hpp b/src/network/protocols/lobby_protocol.hpp index 3a2140441..7a0793b24 100644 --- a/src/network/protocols/lobby_protocol.hpp +++ b/src/network/protocols/lobby_protocol.hpp @@ -74,8 +74,10 @@ public: LE_LIVE_JOIN_ACK, // Server tell client live join or spectate succeed LE_KART_INFO, // Client or server exchange new kart info LE_CLIENT_BACK_LOBBY, // Client tell server to go back lobby - LE_REPORT_PLAYER // Client report some player in server + LE_REPORT_PLAYER, // Client report some player in server // (like abusive behaviour) + LE_ASSETS_UPDATE, // Client tell server with updated assets + LE_COMMAND, // Command }; enum RejectReason : uint8_t diff --git a/src/network/protocols/server_lobby.cpp b/src/network/protocols/server_lobby.cpp index 8d87d5d37..8f6c2c6c9 100644 --- a/src/network/protocols/server_lobby.cpp +++ b/src/network/protocols/server_lobby.cpp @@ -755,6 +755,8 @@ bool ServerLobby::notifyEventAsynchronous(Event* event) case LE_CLIENT_BACK_LOBBY: clientSelectingAssetsWantsToBackLobby(event); break; case LE_REPORT_PLAYER: writePlayerReport(event); break; + case LE_ASSETS_UPDATE: + handleAssets(event->data(), event->getPeer()); break; default: break; } // switch } // if (event->getType() == EVENT_TYPE_MESSAGE) @@ -2914,72 +2916,21 @@ void ServerLobby::saveIPBanTable(const TransportAddress& addr) } // saveIPBanTable //----------------------------------------------------------------------------- -void ServerLobby::connectionRequested(Event* event) +bool ServerLobby::handleAssets(const NetworkString& ns, STKPeer* peer) const { - std::shared_ptr peer = event->getPeerSP(); - NetworkString& data = event->data(); - if (!checkDataSize(event, 14)) return; - - peer->cleanPlayerProfiles(); - - // can we add the player ? - if (!allowJoinedPlayersWaiting() && - (m_state.load() != WAITING_FOR_START_GAME || - m_game_setup->isGrandPrixStarted())) - { - NetworkString *message = getNetworkString(2); - message->setSynchronous(true); - message->addUInt8(LE_CONNECTION_REFUSED).addUInt8(RR_BUSY); - // send only to the peer that made the request and disconnect it now - peer->sendPacket(message, true/*reliable*/, false/*encrypted*/); - peer->reset(); - delete message; - Log::verbose("ServerLobby", "Player refused: selection started"); - return; - } - - // Check server version - int version = data.getUInt32(); - if (version < stk_config->m_min_server_version || - version > stk_config->m_max_server_version) - { - NetworkString *message = getNetworkString(2); - message->setSynchronous(true); - message->addUInt8(LE_CONNECTION_REFUSED) - .addUInt8(RR_INCOMPATIBLE_DATA); - peer->sendPacket(message, true/*reliable*/, false/*encrypted*/); - peer->reset(); - delete message; - Log::verbose("ServerLobby", "Player refused: wrong server version"); - return; - } - std::string user_version; - data.decodeString(&user_version); - event->getPeer()->setUserVersion(user_version); - - unsigned list_caps = data.getUInt16(); - std::set caps; - for (unsigned i = 0; i < list_caps; i++) - { - std::string cap; - data.decodeString(&cap); - caps.insert(cap); - } - event->getPeer()->setClientCapabilities(caps); - std::set client_karts, client_tracks; - const unsigned kart_num = data.getUInt16(); - const unsigned track_num = data.getUInt16(); + const unsigned kart_num = ns.getUInt16(); + const unsigned track_num = ns.getUInt16(); for (unsigned i = 0; i < kart_num; i++) { std::string kart; - data.decodeString(&kart); + ns.decodeString(&kart); client_karts.insert(kart); } for (unsigned i = 0; i < track_num; i++) { std::string track; - data.decodeString(&track); + ns.decodeString(&track); client_tracks.insert(track); } @@ -3032,12 +2983,70 @@ void ServerLobby::connectionRequested(Event* event) peer->reset(); delete message; Log::verbose("ServerLobby", "Player has incompatible karts / tracks."); - return; + return false; } // Save available karts and tracks from clients in STKPeer so if this peer // disconnects later in lobby it won't affect current players peer->setAvailableKartsTracks(client_karts, client_tracks); + return true; +} // handleAssets + +//----------------------------------------------------------------------------- +void ServerLobby::connectionRequested(Event* event) +{ + std::shared_ptr peer = event->getPeerSP(); + NetworkString& data = event->data(); + if (!checkDataSize(event, 14)) return; + + peer->cleanPlayerProfiles(); + + // can we add the player ? + if (!allowJoinedPlayersWaiting() && + (m_state.load() != WAITING_FOR_START_GAME || + m_game_setup->isGrandPrixStarted())) + { + NetworkString *message = getNetworkString(2); + message->setSynchronous(true); + message->addUInt8(LE_CONNECTION_REFUSED).addUInt8(RR_BUSY); + // send only to the peer that made the request and disconnect it now + peer->sendPacket(message, true/*reliable*/, false/*encrypted*/); + peer->reset(); + delete message; + Log::verbose("ServerLobby", "Player refused: selection started"); + return; + } + + // Check server version + int version = data.getUInt32(); + if (version < stk_config->m_min_server_version || + version > stk_config->m_max_server_version) + { + NetworkString *message = getNetworkString(2); + message->setSynchronous(true); + message->addUInt8(LE_CONNECTION_REFUSED) + .addUInt8(RR_INCOMPATIBLE_DATA); + peer->sendPacket(message, true/*reliable*/, false/*encrypted*/); + peer->reset(); + delete message; + Log::verbose("ServerLobby", "Player refused: wrong server version"); + return; + } + std::string user_version; + data.decodeString(&user_version); + event->getPeer()->setUserVersion(user_version); + + unsigned list_caps = data.getUInt16(); + std::set caps; + for (unsigned i = 0; i < list_caps; i++) + { + std::string cap; + data.decodeString(&cap); + caps.insert(cap); + } + event->getPeer()->setClientCapabilities(caps); + if (!handleAssets(data, event->getPeer())) + return; unsigned player_count = data.getUInt8(); uint32_t online_id = 0; diff --git a/src/network/protocols/server_lobby.hpp b/src/network/protocols/server_lobby.hpp index f339d777b..929f0246d 100644 --- a/src/network/protocols/server_lobby.hpp +++ b/src/network/protocols/server_lobby.hpp @@ -316,6 +316,7 @@ private: std::vector >& players) const; std::vector > getLivePlayers() const; void setPlayerKarts(const NetworkString& ns, STKPeer* peer) const; + bool handleAssets(const NetworkString& ns, STKPeer* peer) const; void liveJoinRequest(Event* event); void rejectLiveJoin(STKPeer* peer, BackLobbyReason blr); bool canLiveJoinNow() const; diff --git a/src/states_screens/dialogs/addons_loading.cpp b/src/states_screens/dialogs/addons_loading.cpp index cf120244c..13e8caf35 100644 --- a/src/states_screens/dialogs/addons_loading.cpp +++ b/src/states_screens/dialogs/addons_loading.cpp @@ -28,6 +28,7 @@ #include "guiengine/widgets.hpp" #include "input/input_manager.hpp" #include "io/file_manager.hpp" +#include "network/protocols/client_lobby.hpp" #include "online/request_manager.hpp" #include "online/xml_request.hpp" #include "race/grand_prix_manager.hpp" @@ -81,7 +82,12 @@ AddonsLoading::~AddonsLoading() { // Select the last selected item in the addons_screen, so that // users can keep on installing from the last selected item. - AddonsScreen::getInstance()->setLastSelected(); + // This dialog can be called in network lobby screen atm for live addon + // install + AddonsScreen* as = dynamic_cast( + GUIEngine::getCurrentScreen()); + if (as) + as->setLastSelected(); } // AddonsLoading // ---------------------------------------------------------------------------- @@ -210,7 +216,25 @@ bool AddonsLoading::onEscapePressed() } // onEscapePressed // ---------------------------------------------------------------------------- +void AddonsLoading::tryInstall() +{ +#ifndef SERVER_ONLY + // Only display the progress bar etc. if we are not uninstalling an addon. + if (!m_addon.isInstalled() || m_addon.needsUpdate()) + { + m_progress->setValue(0); + m_progress->setVisible(true); + // Change the 'back' button into a 'cancel' button. + m_back_button->setLabel(_("Cancel")); + GUIEngine::RibbonWidget* actions_ribbon = + getWidget("actions"); + actions_ribbon->setVisible(false); + startDownload(); + } +#endif +} // tryInstall +// ---------------------------------------------------------------------------- GUIEngine::EventPropagation AddonsLoading::processEvent(const std::string& event_source) { #ifndef SERVER_ONLY @@ -230,19 +254,7 @@ GUIEngine::EventPropagation AddonsLoading::processEvent(const std::string& event } else if(selection == "install") { - // Only display the progress bar etc. if we are - // not uninstalling an addon. - if(!m_addon.isInstalled() || m_addon.needsUpdate()) - { - m_progress->setValue(0); - m_progress->setVisible(true); - // Change the 'back' button into a 'cancel' button. - m_back_button->setLabel(_("Cancel")); - - actions_ribbon->setVisible(false); - - startDownload(); - } + tryInstall(); return GUIEngine::EVENT_BLOCK; } else if (selection == "uninstall") @@ -395,7 +407,10 @@ void AddonsLoading::doInstall() { // The list of the addon screen needs to be updated to correctly // display the newly (un)installed addon. - AddonsScreen::getInstance()->loadList(); + AddonsScreen* as = dynamic_cast( + GUIEngine::getCurrentScreen()); + if (as) + as->loadList(); dismiss(); } @@ -405,6 +420,9 @@ void AddonsLoading::doInstall() delete grand_prix_manager; grand_prix_manager = new GrandPrixManager(); grand_prix_manager->checkConsistency(); + + if (auto cl = LobbyProtocol::get()) + cl->updateAssetsToServer(); #endif } // doInstall @@ -439,7 +457,10 @@ void AddonsLoading::doUninstall() { // The list of the addon screen needs to be updated to correctly // display the newly (un)installed addon. - AddonsScreen::getInstance()->loadList(); + AddonsScreen* as = dynamic_cast( + GUIEngine::getCurrentScreen()); + if (as) + as->loadList(); dismiss(); } // Update the replay file list to use latest track pointer @@ -447,5 +468,8 @@ void AddonsLoading::doUninstall() delete grand_prix_manager; grand_prix_manager = new GrandPrixManager(); grand_prix_manager->checkConsistency(); + + if (auto cl = LobbyProtocol::get()) + cl->updateAssetsToServer(); #endif } // doUninstall diff --git a/src/states_screens/dialogs/addons_loading.hpp b/src/states_screens/dialogs/addons_loading.hpp index 1c217641a..05a3b8653 100644 --- a/src/states_screens/dialogs/addons_loading.hpp +++ b/src/states_screens/dialogs/addons_loading.hpp @@ -71,6 +71,7 @@ public: * */ void onUpdate(float delta) OVERRIDE; void voteClicked(); + void tryInstall(); virtual bool onEscapePressed() OVERRIDE; }; // AddonsLoading diff --git a/src/states_screens/dialogs/addons_pack.cpp b/src/states_screens/dialogs/addons_pack.cpp index 5628aa08c..9cfc26b92 100644 --- a/src/states_screens/dialogs/addons_pack.cpp +++ b/src/states_screens/dialogs/addons_pack.cpp @@ -24,10 +24,12 @@ #include "io/file_manager.hpp" #include "karts/kart_properties.hpp" #include "karts/kart_properties_manager.hpp" +#include "network/protocols/client_lobby.hpp" #include "online/http_request.hpp" #include "race/grand_prix_manager.hpp" #include "replay/replay_play.hpp" #include "states_screens/addons_screen.hpp" +#include "states_screens/dialogs/addons_loading.hpp" #include "states_screens/dialogs/message_dialog.hpp" #include "states_screens/state_manager.hpp" #include "tracks/track_manager.hpp" @@ -239,8 +241,7 @@ void AddonsPack::doInstall() { if (r == ".." || r == ".") continue; - std::string addon_id = "addon_"; - addon_id += r; + std::string addon_id = Addon::createAddonId(r); // We assume the addons pack the user downloaded use the latest // revision from the stk-addons (if exists) if (file_manager->fileExists(tmp_extract + r + "/stkskin.xml")) @@ -301,8 +302,44 @@ void AddonsPack::doInstall() GUIEngine::getCurrentScreen()); if (as) as->loadList(); + if (auto cl = LobbyProtocol::get()) + cl->updateAssetsToServer(); delete request; } } // doInstall +// ---------------------------------------------------------------------------- +void AddonsPack::install(const std::string& name) +{ + // Only install addon live in menu + if (StateManager::get()->getGameState() != GUIEngine::MENU && + !ModalDialog::isADialogActive()) + return; + Addon* addon = addons_manager->getAddon(Addon::createAddonId(name)); + if (addon) + { + AddonsLoading* al = new AddonsLoading(addon->getId()); + al->tryInstall(); + } + else + { + // Assume it's addon pack url + new AddonsPack(name); + } +} // install + +// ---------------------------------------------------------------------------- +void AddonsPack::uninstall(const std::string& name) +{ + // Only uninstall addon live in menu + if (StateManager::get()->getGameState() != GUIEngine::MENU) + return; + Addon* addon = addons_manager->getAddon(Addon::createAddonId(name)); + if (addon && addons_manager->uninstall(*addon)) + { + if (auto cl = LobbyProtocol::get()) + cl->updateAssetsToServer(); + } +} // uninstall + #endif diff --git a/src/states_screens/dialogs/addons_pack.hpp b/src/states_screens/dialogs/addons_pack.hpp index 688e7f16e..223d39a29 100644 --- a/src/states_screens/dialogs/addons_pack.hpp +++ b/src/states_screens/dialogs/addons_pack.hpp @@ -38,15 +38,15 @@ private: /** A pointer to the download request, which gives access * to the progress of a download. */ AddonsPackRequest* m_download_request; - -public: AddonsPack(const std::string& url); - +public: virtual GUIEngine::EventPropagation processEvent(const std::string& event_source) OVERRIDE; virtual void beforeAddingWidgets() OVERRIDE; virtual void init() OVERRIDE; void onUpdate(float delta) OVERRIDE; virtual bool onEscapePressed() OVERRIDE; + static void install(const std::string& name); + static void uninstall(const std::string& name); }; // DownloadAssets #endif diff --git a/src/states_screens/dialogs/race_paused_dialog.cpp b/src/states_screens/dialogs/race_paused_dialog.cpp index b07ca88be..97709b991 100644 --- a/src/states_screens/dialogs/race_paused_dialog.cpp +++ b/src/states_screens/dialogs/race_paused_dialog.cpp @@ -337,7 +337,18 @@ void RacePausedDialog::beforeAddingWidgets() bool RacePausedDialog::onEnterPressed(const irr::core::stringw& text) { if (auto cl = LobbyProtocol::get()) - cl->sendChat(text); + { + if (!text.empty()) + { + if (text[0] == L'/' && text.size() > 1) + { + std::string cmd = StringUtils::wideToUtf8(text); + cl->handleClientCommand(cmd.erase(0, 1)); + } + else + cl->sendChat(text); + } + } m_self_destroy = true; return true; } // onEnterPressed diff --git a/src/states_screens/online/networking_lobby.cpp b/src/states_screens/online/networking_lobby.cpp index a79023b11..f43e25b14 100644 --- a/src/states_screens/online/networking_lobby.cpp +++ b/src/states_screens/online/networking_lobby.cpp @@ -568,7 +568,18 @@ void NetworkingLobby::updatePlayerPings() bool NetworkingLobby::onEnterPressed(const irr::core::stringw& text) { if (auto cl = LobbyProtocol::get()) - cl->sendChat(text); + { + if (!text.empty()) + { + if (text[0] == L'/' && text.size() > 1) + { + std::string cmd = StringUtils::wideToUtf8(text); + cl->handleClientCommand(cmd.erase(0, 1)); + } + else + cl->sendChat(text); + } + } return true; } // onEnterPressed @@ -601,8 +612,7 @@ void NetworkingLobby::eventCallback(Widget* widget, const std::string& name, } // click on a user else if (name == m_send_button->m_properties[PROP_ID]) { - if (auto cl = LobbyProtocol::get()) - cl->sendChat(m_chat_box->getText()); + onEnterPressed(m_chat_box->getText()); m_chat_box->setText(""); } // send chat message else if (name == m_emoji_button->m_properties[PROP_ID] &&