Implement background download for addons pack
This commit is contained in:
@@ -2583,6 +2583,7 @@ int main(int argc, char *argv[])
|
||||
|
||||
if (STKHost::existHost())
|
||||
STKHost::get()->shutdown();
|
||||
ClientLobby::destroyBackgroundDownload();
|
||||
|
||||
cleanSuperTuxKart();
|
||||
NetworkConfig::destroy();
|
||||
|
||||
@@ -55,9 +55,12 @@
|
||||
#include "network/server_config.hpp"
|
||||
#include "network/stk_host.hpp"
|
||||
#include "network/stk_peer.hpp"
|
||||
#include "race/grand_prix_manager.hpp"
|
||||
#include "replay/replay_play.hpp"
|
||||
#include "online/online_profile.hpp"
|
||||
#include "online/xml_request.hpp"
|
||||
#include "states_screens/dialogs/addons_pack.hpp"
|
||||
#include "states_screens/dialogs/message_dialog.hpp"
|
||||
#include "states_screens/online/networking_lobby.hpp"
|
||||
#include "states_screens/online/network_kart_selection.hpp"
|
||||
#include "states_screens/online/tracks_screen.hpp"
|
||||
@@ -77,6 +80,21 @@ extern "C"
|
||||
}
|
||||
#endif
|
||||
|
||||
// ============================================================================
|
||||
std::thread ClientLobby::m_background_download;
|
||||
std::shared_ptr<Online::HTTPRequest> ClientLobby::m_download_request;
|
||||
//-----------------------------------------------------------------------------
|
||||
void ClientLobby::destroyBackgroundDownload()
|
||||
{
|
||||
if (m_download_request)
|
||||
{
|
||||
m_download_request->cancel();
|
||||
m_download_request = nullptr;
|
||||
}
|
||||
if (m_background_download.joinable())
|
||||
m_background_download.join();
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
/** The protocol that manages starting a race with the server. It uses a
|
||||
* finite state machine:
|
||||
@@ -483,6 +501,17 @@ void ClientLobby::update(int ticks)
|
||||
start.addUInt8(LobbyProtocol::LE_REQUEST_BEGIN);
|
||||
STKHost::get()->sendToServer(&start, true);
|
||||
}
|
||||
if (m_background_download.joinable())
|
||||
{
|
||||
if (m_download_request && (m_download_request->isCancelled() ||
|
||||
m_download_request->isDone()))
|
||||
{
|
||||
m_background_download.join();
|
||||
if (m_download_request->isDone())
|
||||
doInstallAddonsPack();
|
||||
m_download_request = nullptr;
|
||||
}
|
||||
}
|
||||
case SELECTING_ASSETS:
|
||||
case RACING:
|
||||
case EXITING:
|
||||
@@ -1611,7 +1640,26 @@ void ClientLobby::handleClientCommand(const std::string& cmd)
|
||||
auto argv = StringUtils::split(cmd, ' ');
|
||||
if (argv.size() == 0)
|
||||
return;
|
||||
if (argv[0] == "installaddon" && argv.size() == 2)
|
||||
if (argv[0] == "addondownloadprogress")
|
||||
{
|
||||
float progress = 0.0f;
|
||||
if (m_download_request)
|
||||
progress = m_download_request->getProgress();
|
||||
core::stringw msg = L"Background download progress: ";
|
||||
msg += progress;
|
||||
NetworkingLobby::getInstance()->addMoreServerInfo(msg);
|
||||
}
|
||||
else if (argv[0] == "stopaddondownload")
|
||||
{
|
||||
if (m_download_request)
|
||||
{
|
||||
m_download_request->cancel();
|
||||
m_download_request = nullptr;
|
||||
}
|
||||
if (m_background_download.joinable())
|
||||
m_background_download.join();
|
||||
}
|
||||
else if (argv[0] == "installaddon" && argv.size() == 2)
|
||||
AddonsPack::install(argv[1]);
|
||||
else if (argv[0] == "uninstalladdon" && argv.size() == 2)
|
||||
AddonsPack::uninstall(argv[1]);
|
||||
@@ -1822,3 +1870,116 @@ void ClientLobby::updateAssetsToServer()
|
||||
sendToServer(ns, /*reliable*/true);
|
||||
delete ns;
|
||||
} // updateAssetsToServer
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
void ClientLobby::downloadAddonsPack(std::shared_ptr<Online::HTTPRequest> r)
|
||||
{
|
||||
if (startedDownloadAddonsPack())
|
||||
return;
|
||||
m_download_request = r;
|
||||
m_background_download = std::thread([r](){ r->executeNow(); });
|
||||
} // downloadAddonsPack
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
/** Called when the asynchronous download of the addon finished.
|
||||
*/
|
||||
void ClientLobby::doInstallAddonsPack()
|
||||
{
|
||||
#ifndef SERVER_ONLY
|
||||
core::stringw msg;
|
||||
if (!m_download_request->isCancelled() &&
|
||||
m_download_request->hadDownloadError())
|
||||
{
|
||||
// Reset the download buttons so user can redownload if needed
|
||||
// I18N: Shown when there is download error for assets download
|
||||
// in the first run
|
||||
msg = _("Failed to download assets, check your storage space or "
|
||||
"internet connection and try again later.");
|
||||
}
|
||||
|
||||
if (!msg.empty())
|
||||
{
|
||||
new MessageDialog(msg);
|
||||
}
|
||||
else
|
||||
{
|
||||
std::set<std::string> result;
|
||||
std::string tmp_extract = file_manager->getAddonsFile("tmp_extract");
|
||||
file_manager->listFiles(result, tmp_extract);
|
||||
tmp_extract += "/";
|
||||
bool addon_kart_installed = false;
|
||||
bool addon_track_installed = false;
|
||||
bool track_installed = false;
|
||||
for (auto& r : result)
|
||||
{
|
||||
if (r == ".." || r == ".")
|
||||
continue;
|
||||
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"))
|
||||
{
|
||||
std::string skins = file_manager->getAddonsFile("skins");
|
||||
file_manager->checkAndCreateDirectoryP(skins);
|
||||
if (file_manager->isDirectory(skins + "/" + r))
|
||||
file_manager->removeDirectory(skins + "/" + r);
|
||||
file_manager->moveDirectoryInto(tmp_extract + r, skins);
|
||||
// Skin is not supported in stk-addons atm
|
||||
}
|
||||
else if (file_manager->fileExists(tmp_extract + r + "/kart.xml"))
|
||||
{
|
||||
std::string karts = file_manager->getAddonsFile("karts");
|
||||
file_manager->checkAndCreateDirectoryP(karts);
|
||||
if (file_manager->isDirectory(karts + "/" + r))
|
||||
{
|
||||
const KartProperties* prop =
|
||||
kart_properties_manager->getKart(addon_id);
|
||||
// If the model already exist, first remove the old kart
|
||||
if (prop)
|
||||
kart_properties_manager->removeKart(addon_id);
|
||||
file_manager->removeDirectory(karts + "/" + r);
|
||||
}
|
||||
if (file_manager->moveDirectoryInto(tmp_extract + r, karts))
|
||||
{
|
||||
kart_properties_manager->loadKart(karts + "/" + r);
|
||||
Addon* addon = addons_manager->getAddon(addon_id);
|
||||
if (addon)
|
||||
{
|
||||
addon_kart_installed = true;
|
||||
addon->setInstalled(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (file_manager->fileExists(tmp_extract + r + "/track.xml"))
|
||||
{
|
||||
track_installed = true;
|
||||
std::string tracks = file_manager->getAddonsFile("tracks");
|
||||
file_manager->checkAndCreateDirectoryP(tracks);
|
||||
if (file_manager->isDirectory(tracks + "/" + r))
|
||||
file_manager->removeDirectory(tracks + "/" + r);
|
||||
if (file_manager->moveDirectoryInto(tmp_extract + r, tracks))
|
||||
{
|
||||
Addon* addon = addons_manager->getAddon(addon_id);
|
||||
if (addon)
|
||||
{
|
||||
addon_track_installed = true;
|
||||
addon->setInstalled(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (addon_kart_installed || addon_track_installed)
|
||||
addons_manager->saveInstalled();
|
||||
if (track_installed)
|
||||
{
|
||||
track_manager->loadTrackList();
|
||||
// Update the replay file list to use latest track pointer
|
||||
ReplayPlay::get()->loadAllReplayFile();
|
||||
delete grand_prix_manager;
|
||||
grand_prix_manager = new GrandPrixManager();
|
||||
grand_prix_manager->checkConsistency();
|
||||
}
|
||||
updateAssetsToServer();
|
||||
}
|
||||
#endif
|
||||
} // doInstall
|
||||
|
||||
@@ -37,6 +37,11 @@ enum HandicapLevel : uint8_t;
|
||||
class BareNetworkString;
|
||||
class Server;
|
||||
|
||||
namespace Online
|
||||
{
|
||||
class HTTPRequest;
|
||||
}
|
||||
|
||||
struct LobbyPlayer
|
||||
{
|
||||
irr::core::stringw m_user_name;
|
||||
@@ -132,6 +137,10 @@ private:
|
||||
|
||||
irr::core::stringw m_total_players;
|
||||
|
||||
static std::thread m_background_download;
|
||||
|
||||
static std::shared_ptr<Online::HTTPRequest> m_download_request;
|
||||
|
||||
void liveJoinAcknowledged(Event* event);
|
||||
void handleKartInfo(Event* event);
|
||||
void finishLiveJoin();
|
||||
@@ -140,6 +149,7 @@ private:
|
||||
std::shared_ptr<STKPeer> peer = nullptr,
|
||||
bool* is_spectator = NULL) const;
|
||||
void getKartsTracksNetworkString(BareNetworkString* ns);
|
||||
void doInstallAddonsPack();
|
||||
public:
|
||||
ClientLobby(std::shared_ptr<Server> s);
|
||||
virtual ~ClientLobby();
|
||||
@@ -184,9 +194,13 @@ public:
|
||||
const std::vector<float>& getRankingChanges() const
|
||||
{ return m_ranking_changes; }
|
||||
void handleClientCommand(const std::string& cmd);
|
||||
void updateAssetsToServer();
|
||||
ClientState getCurrentState() const { return m_state.load(); }
|
||||
std::shared_ptr<Server> getJoinedServer() const { return m_server; }
|
||||
static bool startedDownloadAddonsPack()
|
||||
{ return m_background_download.joinable() || m_download_request; }
|
||||
static void downloadAddonsPack(std::shared_ptr<Online::HTTPRequest> r);
|
||||
static void destroyBackgroundDownload();
|
||||
void updateAssetsToServer();
|
||||
};
|
||||
|
||||
#endif // CLIENT_LOBBY_HPP
|
||||
|
||||
@@ -112,7 +112,7 @@ namespace Online
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
/** Returns true if there was an error downloading the file. */
|
||||
bool hadDownloadError() const { return m_curl_code != CURLE_OK; }
|
||||
virtual bool hadDownloadError() const { return m_curl_code != CURLE_OK; }
|
||||
// ------------------------------------------------------------------------
|
||||
void setDownloadAssetsRequest(bool val)
|
||||
{ m_download_assets_request = val; }
|
||||
|
||||
@@ -25,10 +25,9 @@
|
||||
#include "io/file_manager.hpp"
|
||||
#include "karts/kart_properties.hpp"
|
||||
#include "karts/kart_properties_manager.hpp"
|
||||
#include "guiengine/message_queue.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"
|
||||
@@ -47,7 +46,8 @@ class AddonsPackRequest : public HTTPRequest
|
||||
{
|
||||
private:
|
||||
bool m_extraction_error;
|
||||
virtual void afterOperation()
|
||||
bool m_background_download;
|
||||
virtual void afterOperation() OVERRIDE
|
||||
{
|
||||
Online::HTTPRequest::afterOperation();
|
||||
if (isCancelled())
|
||||
@@ -61,12 +61,18 @@ private:
|
||||
file_manager->checkAndCreateDirectory(tmp_extract);
|
||||
m_extraction_error =
|
||||
!extract_zip(getFileName(), tmp_extract, true/*recursive*/);
|
||||
if (!m_extraction_error && m_background_download)
|
||||
{
|
||||
core::stringw msg = _("Background download completed.");
|
||||
MessageQueue::add(MessageQueue::MT_GENERIC, msg);
|
||||
}
|
||||
}
|
||||
public:
|
||||
AddonsPackRequest(const std::string& url)
|
||||
: HTTPRequest(StringUtils::getBasename(url), /*priority*/5)
|
||||
{
|
||||
m_extraction_error = true;
|
||||
m_background_download = false;
|
||||
if (url.find("https://") != std::string::npos ||
|
||||
url.find("http://") != std::string::npos)
|
||||
{
|
||||
@@ -87,7 +93,9 @@ public:
|
||||
file_manager->removeDirectory(
|
||||
file_manager->getAddonsFile("tmp_extract"));
|
||||
}
|
||||
bool hadError() const { return hadDownloadError() || m_extraction_error; }
|
||||
void backgroundDownload() { m_background_download = true; }
|
||||
virtual bool hadDownloadError() const OVERRIDE
|
||||
{ return HTTPRequest::hadDownloadError() || m_extraction_error; }
|
||||
}; // DownloadAssetsRequest
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
@@ -113,14 +121,26 @@ AddonsPack::AddonsPack(const std::string& url) : ModalDialog(0.8f, 0.8f)
|
||||
getWidget<GUIEngine::RibbonWidget>("actions");
|
||||
actions_ribbon->setFocusForPlayer(PLAYER_ID_GAME_MASTER);
|
||||
actions_ribbon->select("back", PLAYER_ID_GAME_MASTER);
|
||||
getWidget("install")->setVisible(false);
|
||||
icon = getWidget<IconButtonWidget>("install");
|
||||
icon->setImage(file_manager->getAsset(FileManager::GUI_ICON,
|
||||
"blue_plus.png"), IconButtonWidget::ICON_PATH_TYPE_ABSOLUTE);
|
||||
icon->setLabel(_("Background download"));
|
||||
icon->setVisible(true);
|
||||
getWidget("uninstall")->setVisible(false);
|
||||
icon = getWidget<IconButtonWidget>("back");
|
||||
icon->setImage(file_manager->getAsset(FileManager::GUI_ICON, "remove.png"),
|
||||
IconButtonWidget::ICON_PATH_TYPE_ABSOLUTE);
|
||||
icon->setLabel(_("Cancel"));
|
||||
m_download_request = std::make_shared<AddonsPackRequest>(url);
|
||||
m_download_request->queue();
|
||||
if (ClientLobby::startedDownloadAddonsPack())
|
||||
{
|
||||
core::stringw msg = _("Background download has already started.");
|
||||
MessageQueue::add(MessageQueue::MT_ERROR, msg);
|
||||
}
|
||||
else
|
||||
{
|
||||
m_download_request = std::make_shared<AddonsPackRequest>(url);
|
||||
ClientLobby::downloadAddonsPack(m_download_request);
|
||||
}
|
||||
} // AddonsPack
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
@@ -158,7 +178,15 @@ GUIEngine::EventPropagation AddonsPack::processEvent(const std::string& event_so
|
||||
{
|
||||
const std::string& selection =
|
||||
actions_ribbon->getSelectionIDString(PLAYER_ID_GAME_MASTER);
|
||||
if (selection == "back")
|
||||
if (selection == "install")
|
||||
{
|
||||
if (m_download_request)
|
||||
m_download_request->backgroundDownload();
|
||||
m_download_request = nullptr;
|
||||
dismiss();
|
||||
return GUIEngine::EVENT_BLOCK;
|
||||
}
|
||||
else if (selection == "back")
|
||||
{
|
||||
dismiss();
|
||||
return GUIEngine::EVENT_BLOCK;
|
||||
@@ -192,14 +220,17 @@ void AddonsPack::onUpdate(float delta)
|
||||
new MessageDialog(_("Sorry, downloading the add-on failed"));
|
||||
return;
|
||||
}
|
||||
else if (m_download_request->isDone())
|
||||
else if (m_download_request->isDone() ||
|
||||
m_download_request->isCancelled())
|
||||
{
|
||||
// No sense to update state text, since it all
|
||||
// happens before the GUI is refrehsed.
|
||||
doInstall();
|
||||
dismiss();
|
||||
return;
|
||||
}
|
||||
} // if (m_progress->isVisible())
|
||||
}
|
||||
else
|
||||
dismiss();
|
||||
} // onUpdate
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
@@ -212,120 +243,12 @@ void AddonsPack::stopDownload()
|
||||
// (and not uninstalling an installed one):
|
||||
if (m_download_request)
|
||||
{
|
||||
m_download_request->cancel();
|
||||
if (!m_download_request->isDone())
|
||||
m_download_request->cancel();
|
||||
m_download_request = nullptr;
|
||||
}
|
||||
} // startDownload
|
||||
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
/** Called when the asynchronous download of the addon finished.
|
||||
*/
|
||||
void AddonsPack::doInstall()
|
||||
{
|
||||
core::stringw msg;
|
||||
if (m_download_request->hadError())
|
||||
{
|
||||
// Reset the download buttons so user can redownload if needed
|
||||
// I18N: Shown when there is download error for assets download
|
||||
// in the first run
|
||||
msg = _("Failed to download assets, check your storage space or internet connection and try again later.");
|
||||
}
|
||||
|
||||
if (!msg.empty())
|
||||
{
|
||||
dismiss();
|
||||
new MessageDialog(msg);
|
||||
}
|
||||
else
|
||||
{
|
||||
std::shared_ptr<AddonsPackRequest> request = m_download_request;
|
||||
dismiss();
|
||||
std::set<std::string> result;
|
||||
std::string tmp_extract = file_manager->getAddonsFile("tmp_extract");
|
||||
file_manager->listFiles(result, tmp_extract);
|
||||
tmp_extract += "/";
|
||||
bool addon_kart_installed = false;
|
||||
bool addon_track_installed = false;
|
||||
bool track_installed = false;
|
||||
for (auto& r : result)
|
||||
{
|
||||
if (r == ".." || r == ".")
|
||||
continue;
|
||||
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"))
|
||||
{
|
||||
std::string skins = file_manager->getAddonsFile("skins");
|
||||
file_manager->checkAndCreateDirectoryP(skins);
|
||||
if (file_manager->isDirectory(skins + "/" + r))
|
||||
file_manager->removeDirectory(skins + "/" + r);
|
||||
file_manager->moveDirectoryInto(tmp_extract + r, skins);
|
||||
// Skin is not supported in stk-addons atm
|
||||
}
|
||||
else if (file_manager->fileExists(tmp_extract + r + "/kart.xml"))
|
||||
{
|
||||
std::string karts = file_manager->getAddonsFile("karts");
|
||||
file_manager->checkAndCreateDirectoryP(karts);
|
||||
if (file_manager->isDirectory(karts + "/" + r))
|
||||
{
|
||||
const KartProperties* prop =
|
||||
kart_properties_manager->getKart(addon_id);
|
||||
// If the model already exist, first remove the old kart
|
||||
if (prop)
|
||||
kart_properties_manager->removeKart(addon_id);
|
||||
file_manager->removeDirectory(karts + "/" + r);
|
||||
}
|
||||
if (file_manager->moveDirectoryInto(tmp_extract + r, karts))
|
||||
{
|
||||
kart_properties_manager->loadKart(karts + "/" + r);
|
||||
Addon* addon = addons_manager->getAddon(addon_id);
|
||||
if (addon)
|
||||
{
|
||||
addon_kart_installed = true;
|
||||
addon->setInstalled(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (file_manager->fileExists(tmp_extract + r + "/track.xml"))
|
||||
{
|
||||
track_installed = true;
|
||||
std::string tracks = file_manager->getAddonsFile("tracks");
|
||||
file_manager->checkAndCreateDirectoryP(tracks);
|
||||
if (file_manager->isDirectory(tracks + "/" + r))
|
||||
file_manager->removeDirectory(tracks + "/" + r);
|
||||
if (file_manager->moveDirectoryInto(tmp_extract + r, tracks))
|
||||
{
|
||||
Addon* addon = addons_manager->getAddon(addon_id);
|
||||
if (addon)
|
||||
{
|
||||
addon_track_installed = true;
|
||||
addon->setInstalled(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (addon_kart_installed || addon_track_installed)
|
||||
addons_manager->saveInstalled();
|
||||
if (track_installed)
|
||||
{
|
||||
track_manager->loadTrackList();
|
||||
// Update the replay file list to use latest track pointer
|
||||
ReplayPlay::get()->loadAllReplayFile();
|
||||
delete grand_prix_manager;
|
||||
grand_prix_manager = new GrandPrixManager();
|
||||
grand_prix_manager->checkConsistency();
|
||||
}
|
||||
AddonsScreen* as = dynamic_cast<AddonsScreen*>(
|
||||
GUIEngine::getCurrentScreen());
|
||||
if (as)
|
||||
as->loadList();
|
||||
if (auto cl = LobbyProtocol::get<ClientLobby>())
|
||||
cl->updateAssetsToServer();
|
||||
}
|
||||
} // doInstall
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
void AddonsPack::install(const std::string& name)
|
||||
{
|
||||
|
||||
@@ -35,7 +35,6 @@ private:
|
||||
GUIEngine::LabelWidget* m_size;
|
||||
|
||||
void stopDownload();
|
||||
void doInstall();
|
||||
|
||||
/** A pointer to the download request, which gives access
|
||||
* to the progress of a download. */
|
||||
|
||||
Reference in New Issue
Block a user