Add support for installing addons pack
This commit is contained in:
parent
07a43e0ca3
commit
19d008d0f8
@ -1,5 +1,5 @@
|
||||
# Modify this file to change the last-modified date when you add/remove a file.
|
||||
# This will then trigger a new cmake run automatically.
|
||||
# This will then trigger a new cmake run automatically.
|
||||
file(GLOB_RECURSE STK_HEADERS RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} "src/*.hpp")
|
||||
file(GLOB_RECURSE STK_SOURCES RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} "src/*.cpp")
|
||||
file(GLOB_RECURSE STK_SHADERS RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} "data/shaders/*")
|
||||
|
@ -447,7 +447,7 @@ void AddonsManager::loadInstalledAddons()
|
||||
* found!
|
||||
* \param id The id to search for.
|
||||
*/
|
||||
const Addon* AddonsManager::getAddon(const std::string &id) const
|
||||
Addon* AddonsManager::getAddon(const std::string &id)
|
||||
{
|
||||
int i = getAddonIndex(id);
|
||||
return (i<0) ? NULL : &(m_addons_list.getData()[i]);
|
||||
|
@ -63,7 +63,7 @@ public:
|
||||
void init(const XMLNode *xml, bool force_refresh);
|
||||
void initAddons(const XMLNode *xml);
|
||||
void checkInstalledAddons();
|
||||
const Addon* getAddon(const std::string &id) const;
|
||||
Addon* getAddon(const std::string &id);
|
||||
int getAddonIndex(const std::string &id) const;
|
||||
bool install(const Addon &addon);
|
||||
bool uninstall(const Addon &addon);
|
||||
|
@ -57,9 +57,6 @@ namespace Online
|
||||
/** The POST parameters that will be send with the request. */
|
||||
std::string m_parameters;
|
||||
|
||||
/** Contains a filename if the data should be saved into a file
|
||||
* instead of being kept in in memory. Otherwise this is "". */
|
||||
std::string m_filename;
|
||||
|
||||
/** Pointer to the curl data structure for this request. */
|
||||
CURL *m_curl_session = NULL;
|
||||
@ -72,6 +69,10 @@ namespace Online
|
||||
|
||||
struct curl_slist* m_http_header = NULL;
|
||||
protected:
|
||||
/** Contains a filename if the data should be saved into a file
|
||||
* instead of being kept in in memory. Otherwise this is "". */
|
||||
std::string m_filename;
|
||||
|
||||
bool m_disable_sending_log;
|
||||
/* If true, it will not call curl_easy_setopt CURLOPT_POSTFIELDS so
|
||||
* it's just a GET request. */
|
||||
|
308
src/states_screens/dialogs/addons_pack.cpp
Normal file
308
src/states_screens/dialogs/addons_pack.cpp
Normal file
@ -0,0 +1,308 @@
|
||||
// SuperTuxKart - a fun racing game with go-kart
|
||||
// Copyright (C) 2019 SuperTuxKart-Team
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or
|
||||
// modify it under the terms of the GNU General Public License
|
||||
// as published by the Free Software Foundation; either version 3
|
||||
// of the License, or (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||
|
||||
#ifndef SERVER_ONLY
|
||||
|
||||
#include "states_screens/dialogs/addons_pack.hpp"
|
||||
|
||||
#include "addons/addons_manager.hpp"
|
||||
#include "addons/zip.hpp"
|
||||
#include "io/file_manager.hpp"
|
||||
#include "karts/kart_properties.hpp"
|
||||
#include "karts/kart_properties_manager.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/message_dialog.hpp"
|
||||
#include "states_screens/state_manager.hpp"
|
||||
#include "tracks/track_manager.hpp"
|
||||
#include "utils/string_utils.hpp"
|
||||
#include "utils/translation.hpp"
|
||||
|
||||
using namespace GUIEngine;
|
||||
using namespace Online;
|
||||
using namespace irr::gui;
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
class AddonsPackRequest : public HTTPRequest
|
||||
{
|
||||
private:
|
||||
bool m_extraction_error;
|
||||
virtual void afterOperation()
|
||||
{
|
||||
Online::HTTPRequest::afterOperation();
|
||||
if (isCancelled())
|
||||
return;
|
||||
m_extraction_error = !file_manager->fileExists(getFileName());
|
||||
if (m_extraction_error)
|
||||
return;
|
||||
|
||||
std::string tmp_extract = file_manager->getAddonsFile("tmp_extract");
|
||||
file_manager->removeDirectory(tmp_extract);
|
||||
file_manager->checkAndCreateDirectory(tmp_extract);
|
||||
m_extraction_error =
|
||||
!extract_zip(getFileName(), tmp_extract, true/*recursive*/);
|
||||
file_manager->removeFile(getFileName());
|
||||
}
|
||||
public:
|
||||
AddonsPackRequest(const std::string& url)
|
||||
: HTTPRequest(StringUtils::getBasename(url), /*manage mem*/false,
|
||||
/*priority*/5)
|
||||
{
|
||||
m_extraction_error = true;
|
||||
if (url.find("https://") != std::string::npos ||
|
||||
url.find("http://") != std::string::npos)
|
||||
{
|
||||
setURL(url);
|
||||
setDownloadAssetsRequest(true);
|
||||
}
|
||||
else
|
||||
m_filename.clear();
|
||||
}
|
||||
~AddonsPackRequest()
|
||||
{
|
||||
if (isCancelled())
|
||||
{
|
||||
const std::string& zip = getFileName();
|
||||
const std::string zip_part = zip + ".part";
|
||||
if (file_manager->fileExists(zip))
|
||||
file_manager->removeFile(zip);
|
||||
if (file_manager->fileExists(zip_part))
|
||||
file_manager->removeFile(zip_part);
|
||||
}
|
||||
file_manager->removeDirectory(
|
||||
file_manager->getAddonsFile("tmp_extract"));
|
||||
}
|
||||
bool hadError() const { return hadDownloadError() || m_extraction_error; }
|
||||
}; // DownloadAssetsRequest
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
/** Creates a modal dialog with given percentage of screen width and height
|
||||
*/
|
||||
AddonsPack::AddonsPack(const std::string& url) : ModalDialog(0.8f, 0.8f)
|
||||
{
|
||||
loadFromFile("addons_loading.stkgui");
|
||||
getWidget<IconButtonWidget>("install")->setVisible(false);
|
||||
getWidget<LabelWidget>("size")->setVisible(false);
|
||||
getWidget<BubbleWidget>("description")->setText(
|
||||
StringUtils::utf8ToWide(url));
|
||||
|
||||
IconButtonWidget* icon = getWidget<IconButtonWidget>("icon");
|
||||
icon->setImage(file_manager->getAsset(FileManager::GUI_ICON, "logo.png"),
|
||||
IconButtonWidget::ICON_PATH_TYPE_ABSOLUTE);
|
||||
|
||||
m_progress = getWidget<ProgressBarWidget>("progress");
|
||||
m_progress->setValue(0);
|
||||
m_progress->setVisible(true);
|
||||
GUIEngine::RibbonWidget* actions_ribbon =
|
||||
getWidget<GUIEngine::RibbonWidget>("actions");
|
||||
actions_ribbon->setVisible(false);
|
||||
m_download_request = new AddonsPackRequest(url);
|
||||
m_download_request->queue();
|
||||
} // AddonsPack
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
void AddonsPack::beforeAddingWidgets()
|
||||
{
|
||||
getWidget("uninstall")->setVisible(false);
|
||||
} // beforeAddingWidgets
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
void AddonsPack::init()
|
||||
{
|
||||
getWidget("rating")->setVisible(false);
|
||||
} // init
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
bool AddonsPack::onEscapePressed()
|
||||
{
|
||||
stopDownload();
|
||||
ModalDialog::dismiss();
|
||||
return true;
|
||||
} // onEscapePressed
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
GUIEngine::EventPropagation AddonsPack::processEvent(const std::string& event_source)
|
||||
{
|
||||
GUIEngine::RibbonWidget* actions_ribbon =
|
||||
getWidget<GUIEngine::RibbonWidget>("actions");
|
||||
|
||||
if (event_source == "actions")
|
||||
{
|
||||
const std::string& selection =
|
||||
actions_ribbon->getSelectionIDString(PLAYER_ID_GAME_MASTER);
|
||||
if (selection == "back")
|
||||
{
|
||||
stopDownload();
|
||||
dismiss();
|
||||
return GUIEngine::EVENT_BLOCK;
|
||||
}
|
||||
}
|
||||
return GUIEngine::EVENT_LET;
|
||||
} // processEvent
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
void AddonsPack::onUpdate(float delta)
|
||||
{
|
||||
if (m_download_request)
|
||||
{
|
||||
float progress = m_download_request->getProgress();
|
||||
// Last 1% for unzipping
|
||||
m_progress->setValue(progress * 99.0f);
|
||||
if (progress < 0)
|
||||
{
|
||||
// Avoid displaying '-100%' in case of an error.
|
||||
m_progress->setVisible(false);
|
||||
m_download_request->setManageMemory(true);
|
||||
dismiss();
|
||||
new MessageDialog(_("Sorry, downloading the add-on failed"));
|
||||
return;
|
||||
}
|
||||
else if (m_download_request->isDone())
|
||||
{
|
||||
// No sense to update state text, since it all
|
||||
// happens before the GUI is refrehsed.
|
||||
doInstall();
|
||||
return;
|
||||
}
|
||||
} // if (m_progress->isVisible())
|
||||
} // onUpdate
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
/** This function is called when the user click on 'Back', 'Cancel' or press
|
||||
* escape.
|
||||
**/
|
||||
void AddonsPack::stopDownload()
|
||||
{
|
||||
// Cancel a download only if we are installing/upgrading one
|
||||
// (and not uninstalling an installed one):
|
||||
if (m_download_request)
|
||||
{
|
||||
// In case of a cancel we can't free the memory, since the
|
||||
// request manager thread is potentially working on this request. So
|
||||
// in order to avoid a memory leak, we let the request manager
|
||||
// free the data. This is thread safe since freeing the data is done
|
||||
// when the request manager handles the result queue - and this is
|
||||
// done by the main thread (i.e. this thread).
|
||||
m_download_request->setManageMemory(true);
|
||||
m_download_request->cancel();
|
||||
m_download_request = NULL;
|
||||
}
|
||||
} // 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())
|
||||
{
|
||||
delete m_download_request;
|
||||
dismiss();
|
||||
new MessageDialog(msg);
|
||||
}
|
||||
else
|
||||
{
|
||||
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 track_installed = false;
|
||||
for (auto& r : result)
|
||||
{
|
||||
if (r == ".." || r == ".")
|
||||
continue;
|
||||
std::string addon_id = "addon_";
|
||||
addon_id += 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->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->setInstalled(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
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();
|
||||
delete request;
|
||||
}
|
||||
} // doInstall
|
||||
|
||||
#endif
|
52
src/states_screens/dialogs/addons_pack.hpp
Normal file
52
src/states_screens/dialogs/addons_pack.hpp
Normal file
@ -0,0 +1,52 @@
|
||||
// SuperTuxKart - a fun racing game with go-kart
|
||||
// Copyright (C) 2019 SuperTuxKart-Team
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or
|
||||
// modify it under the terms of the GNU General Public License
|
||||
// as published by the Free Software Foundation; either version 3
|
||||
// of the License, or (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||
|
||||
#ifndef HEADER_ADDONS_PACK_HPP
|
||||
#define HEADER_ADDONS_PACK_HPP
|
||||
|
||||
#include "guiengine/widgets.hpp"
|
||||
#include "guiengine/modaldialog.hpp"
|
||||
#include "utils/cpp2011.hpp"
|
||||
|
||||
class AddonsPackRequest;
|
||||
|
||||
/**
|
||||
* \ingroup states_screens
|
||||
*/
|
||||
class AddonsPack : public GUIEngine::ModalDialog
|
||||
{
|
||||
private:
|
||||
GUIEngine::ProgressBarWidget *m_progress;
|
||||
|
||||
void stopDownload();
|
||||
void doInstall();
|
||||
|
||||
/** 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);
|
||||
|
||||
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;
|
||||
}; // DownloadAssets
|
||||
|
||||
#endif
|
Loading…
x
Reference in New Issue
Block a user