1171 lines
43 KiB
Diff
1171 lines
43 KiB
Diff
--- a/src/addons/addons_manager.cpp 2020-08-28 10:16:23.000000000 +0200
|
|
+++ b/src/addons/addons_manager.cpp 2023-12-09 23:08:13.240106458 +0100
|
|
@@ -22,7 +22,6 @@
|
|
|
|
#include "addons/addons_manager.hpp"
|
|
|
|
-#include "addons/news_manager.hpp"
|
|
#include "addons/zip.hpp"
|
|
#include "config/user_config.hpp"
|
|
#include "io/file_manager.hpp"
|
|
@@ -52,21 +51,12 @@
|
|
|
|
// ----------------------------------------------------------------------------
|
|
/** Initialises the non-online component of the addons manager (i.e. handling
|
|
- * the list of already installed addons). The online component is initialised
|
|
- * later from a separate thread started from the news manager (see
|
|
- * NewsManager::init() ).
|
|
+ * the list of already installed addons).
|
|
*/
|
|
AddonsManager::AddonsManager() : m_addons_list(std::vector<Addon>() ),
|
|
m_state(STATE_INIT)
|
|
{
|
|
m_downloaded_icons = false;
|
|
- // Clean .part file which may be left behind
|
|
- std::string addons_part = file_manager->getAddonsFile("addons.xml.part");
|
|
- if (file_manager->fileExists(addons_part))
|
|
- file_manager->removeFile(addons_part);
|
|
-
|
|
- m_file_installed = file_manager->getAddonsFile("addons_installed.xml");
|
|
-
|
|
// Load the addons list (even if internet is disabled)
|
|
m_addons_list.lock();
|
|
// Clear the list in case that a reinit is being done.
|
|
@@ -85,77 +75,11 @@
|
|
} // ~AddonsManager
|
|
|
|
// ----------------------------------------------------------------------------
|
|
-/** This init function is called from a separate thread (started in
|
|
- * news_manager, since the news.xml file contains the address of the
|
|
- * addons.xml URL).
|
|
- * \param xml The news.xml file, which inclues the data about the addons.xml
|
|
- * file (in the 'include' node).
|
|
- * \param force_refresh Download addons.xml, even if the usual waiting period
|
|
- * between downloads hasn't passed yet.
|
|
+/** This init function is called from a separate thread.
|
|
*/
|
|
void AddonsManager::init(const XMLNode *xml,
|
|
bool force_refresh)
|
|
{
|
|
- std::string addon_list_url("");
|
|
- StkTime::TimeType mtime(0);
|
|
- const XMLNode *include = xml->getNode("include");
|
|
- std::string filename=file_manager->getAddonsFile("addons.xml");
|
|
- // Prevent downloading when .part file created, which is already downloaded
|
|
- std::string filename_part=file_manager->getAddonsFile("addons.xml.part");
|
|
- if(!include)
|
|
- {
|
|
- file_manager->removeFile(filename);
|
|
- setErrorState();
|
|
- NewsManager::get()->addNewsMessage(_("Failed to connect to the SuperTuxKart add-ons server."));
|
|
- return;
|
|
- }
|
|
-
|
|
- include->get("file", &addon_list_url);
|
|
- int frequency = 0;
|
|
- include->get("frequency", &frequency);
|
|
-
|
|
- int64_t tmp;
|
|
- include->get("mtime", &tmp);
|
|
- mtime = tmp;
|
|
-
|
|
- bool download =
|
|
- ( mtime > UserConfigParams::m_addons_last_updated +frequency ||
|
|
- force_refresh ||
|
|
- !file_manager->fileExists(filename) )
|
|
- && UserConfigParams::m_internet_status == RequestManager::IPERM_ALLOWED
|
|
- && !file_manager->fileExists(filename_part);
|
|
-
|
|
- if (download)
|
|
- {
|
|
- Log::info("addons", "Downloading updated addons.xml.");
|
|
- auto download_request = std::make_shared<Online::HTTPRequest>("addons.xml");
|
|
- download_request->setURL(addon_list_url);
|
|
- download_request->executeNow();
|
|
- if(download_request->hadDownloadError())
|
|
- {
|
|
- Log::error("addons", "Error on download addons.xml: %s.",
|
|
- download_request->getDownloadErrorMessage());
|
|
- return;
|
|
- }
|
|
- UserConfigParams::m_addons_last_updated=StkTime::getTimeSinceEpoch();
|
|
- }
|
|
- else
|
|
- Log::info("addons", "Using cached addons.xml.");
|
|
-
|
|
- const XMLNode* xml_addons = NULL;
|
|
- try
|
|
- {
|
|
- xml_addons = new XMLNode(filename);
|
|
- }
|
|
- catch (std::exception& e)
|
|
- {
|
|
- Log::error("addons", "Error %s", e.what());
|
|
- }
|
|
- if (!xml_addons)
|
|
- return;
|
|
- addons_manager->initAddons(xml_addons); // will free xml_addons
|
|
- if(UserConfigParams::logAddons())
|
|
- Log::info("addons", "Addons manager list downloaded.");
|
|
} // init
|
|
|
|
// ----------------------------------------------------------------------------
|
|
@@ -168,139 +92,6 @@
|
|
*/
|
|
void AddonsManager::initAddons(const XMLNode *xml)
|
|
{
|
|
- m_addons_list.lock();
|
|
- // Clear the list in case that a reinit is being done.
|
|
- m_addons_list.getData().clear();
|
|
- loadInstalledAddons();
|
|
- m_addons_list.unlock();
|
|
-
|
|
- for(unsigned int i=0; i<xml->getNumNodes(); i++)
|
|
- {
|
|
- const XMLNode *node = xml->getNode(i);
|
|
- const std::string &name = node->getName();
|
|
- // Ignore news/redirect, which is handled by the NewsManager
|
|
- if(name=="include" || name=="message")
|
|
- continue;
|
|
- if(node->getName()=="track" || node->getName()=="kart" ||
|
|
- node->getName()=="arena" )
|
|
- {
|
|
- Addon addon(*node);
|
|
- int index = getAddonIndex(addon.getId());
|
|
-
|
|
- int stk_version=0;
|
|
- node->get("format", &stk_version);
|
|
- int testing=-1;
|
|
- node->get("testing", &testing);
|
|
-
|
|
- bool wrong_version=false;
|
|
-
|
|
- if(addon.getType()=="kart")
|
|
- wrong_version = stk_version <stk_config->m_min_kart_version ||
|
|
- stk_version >stk_config->m_max_kart_version ;
|
|
- else
|
|
- wrong_version = stk_version <stk_config->m_min_track_version ||
|
|
- stk_version >stk_config->m_max_track_version ;
|
|
- // If the add-on is included, behave like it is a wrong version
|
|
- if (addon.testIncluded(addon.getMinIncludeVer(), addon.getMaxIncludeVer()))
|
|
- wrong_version = true;
|
|
-
|
|
- // Check which version to use: only for this stk version,
|
|
- // and not addons that are marked as hidden (testing=0)
|
|
- if(wrong_version|| testing==0)
|
|
- {
|
|
- // If the version is too old (e.g. after an update of stk)
|
|
- // remove a cached icon.
|
|
- std::string full_path =
|
|
- file_manager->getAddonsFile("icons/"
|
|
- +addon.getIconBasename());
|
|
- if(file_manager->fileExists(full_path))
|
|
- {
|
|
- if(UserConfigParams::logAddons())
|
|
- Log::warn("addons", "Removing cached icon '%s'.",
|
|
- addon.getIconBasename().c_str());
|
|
- file_manager->removeFile(full_path);
|
|
- }
|
|
- continue;
|
|
- }
|
|
-
|
|
- m_addons_list.lock();
|
|
- if(index>=0)
|
|
- {
|
|
- Addon& tmplist_addon = m_addons_list.getData()[index];
|
|
-
|
|
- // Only copy the data if a newer revision is found (ignore unapproved
|
|
- // revisions unless player is in the mode to see them)
|
|
- if (tmplist_addon.getRevision() < addon.getRevision() &&
|
|
- (addon.testStatus(Addon::AS_APPROVED) || UserConfigParams::m_artist_debug_mode))
|
|
- {
|
|
- m_addons_list.getData()[index].copyInstallData(addon);
|
|
- }
|
|
- }
|
|
- else
|
|
- {
|
|
- m_addons_list.getData().push_back(addon);
|
|
- index = (int) m_addons_list.getData().size()-1;
|
|
- }
|
|
- // Mark that this addon still exists on the server
|
|
- m_addons_list.getData()[index].setStillExists();
|
|
- m_addons_list.unlock();
|
|
- }
|
|
- else
|
|
- {
|
|
- Log::error("addons", "Found invalid node '%s' while downloading addons.",
|
|
- node->getName().c_str());
|
|
- Log::error("addons", "Ignored.");
|
|
- }
|
|
- } // for i<xml->getNumNodes
|
|
- delete xml;
|
|
-
|
|
- // Now remove all items from the addons-installed list, that are not
|
|
- // on the server anymore (i.e. not in the addons.xml file), and not
|
|
- // installed. If found, remove the icon cached for this addon.
|
|
- // Note that if (due to a bug) an icon is shared (i.e. same icon on
|
|
- // an addon that's still on the server and an invalid entry in the
|
|
- // addons installed file), it will be re-downloaded later.
|
|
- m_addons_list.lock();
|
|
- unsigned int count = (unsigned int) m_addons_list.getData().size();
|
|
-
|
|
- for(unsigned int i=0; i<count;)
|
|
- {
|
|
- if(m_addons_list.getData()[i].getStillExists() ||
|
|
- m_addons_list.getData()[i].isInstalled())
|
|
- {
|
|
- i++;
|
|
- continue;
|
|
- }
|
|
- // This addon is not on the server anymore, and not installed. Remove
|
|
- // it from the list.
|
|
- if(UserConfigParams::logAddons())
|
|
- Log::warn(
|
|
- "addons", "Removing '%s' which is not on the server anymore.",
|
|
- m_addons_list.getData()[i].getId().c_str() );
|
|
- const std::string &icon = m_addons_list.getData()[i].getIconBasename();
|
|
- std::string icon_file =file_manager->getAddonsFile("icons/"+icon);
|
|
- if(file_manager->fileExists(icon_file))
|
|
- {
|
|
- file_manager->removeFile(icon_file);
|
|
- // Ignore errors silently.
|
|
- }
|
|
- m_addons_list.getData()[i] = m_addons_list.getData()[count-1];
|
|
- m_addons_list.getData().pop_back();
|
|
- count--;
|
|
- }
|
|
- m_addons_list.unlock();
|
|
-
|
|
- for (unsigned int i = 0; i < m_addons_list.getData().size(); i++)
|
|
- {
|
|
- Addon& addon = m_addons_list.getData()[i];
|
|
- const std::string& icon = addon.getIconBasename();
|
|
- const std::string& icon_full =
|
|
- file_manager->getAddonsFile("icons/" + icon);
|
|
- if (!addon.iconNeedsUpdate() && file_manager->fileExists(icon_full))
|
|
- addon.setIconReady();
|
|
- } // for i < m_addons_list.size()
|
|
-
|
|
- m_state.setAtomic(STATE_READY);
|
|
} // initAddons
|
|
|
|
// ----------------------------------------------------------------------------
|
|
@@ -309,7 +100,6 @@
|
|
*/
|
|
void AddonsManager::reInit()
|
|
{
|
|
- m_state.setAtomic(STATE_INIT);
|
|
} // reInit
|
|
|
|
// ----------------------------------------------------------------------------
|
|
@@ -320,53 +110,6 @@
|
|
*/
|
|
void AddonsManager::checkInstalledAddons()
|
|
{
|
|
- bool something_was_changed = false;
|
|
-
|
|
- // Lock the whole addons list to make sure a consistent view is
|
|
- // written back to disk. The network thread might still be
|
|
- // downloading icons and modify content
|
|
- m_addons_list.lock();
|
|
-
|
|
- // First karts
|
|
- // -----------
|
|
- for(unsigned int i=0; i<kart_properties_manager->getNumberOfKarts(); i++)
|
|
- {
|
|
- const KartProperties *kp = kart_properties_manager->getKartById(i);
|
|
- const std::string &dir=kp->getKartDir();
|
|
- if(dir.find(file_manager->getAddonsDir())==std::string::npos)
|
|
- continue;
|
|
- int n = getAddonIndex(kp->getIdent());
|
|
- if(n<0) continue;
|
|
- if(!m_addons_list.getData()[n].isInstalled())
|
|
- {
|
|
- Log::info("addons", "Marking '%s' as being installed.",
|
|
- kp->getIdent().c_str());
|
|
- m_addons_list.getData()[n].setInstalled(true);
|
|
- something_was_changed = true;
|
|
- }
|
|
- }
|
|
-
|
|
- // Then tracks
|
|
- // -----------
|
|
- for(unsigned int i=0; i<track_manager->getNumberOfTracks(); i++)
|
|
- {
|
|
- const Track *track = track_manager->getTrack(i);
|
|
- const std::string &dir=track->getFilename();
|
|
- if(dir.find(file_manager->getAddonsDir())==std::string::npos)
|
|
- continue;
|
|
- int n = getAddonIndex(track->getIdent());
|
|
- if(n<0) continue;
|
|
- if(!m_addons_list.getData()[n].isInstalled())
|
|
- {
|
|
- Log::info("addons", "Marking '%s' as being installed.",
|
|
- track->getIdent().c_str());
|
|
- m_addons_list.getData()[n].setInstalled(true);
|
|
- something_was_changed = true;
|
|
- }
|
|
- }
|
|
- if(something_was_changed)
|
|
- saveInstalled();
|
|
- m_addons_list.unlock();
|
|
} // checkInstalledAddons
|
|
|
|
// ----------------------------------------------------------------------------
|
|
@@ -374,83 +117,13 @@
|
|
void AddonsManager::downloadIconForAddon(const std::string& addon_id,
|
|
std::weak_ptr<bool> result)
|
|
{
|
|
- Addon* addon = getAddon(addon_id);
|
|
- if (!addon)
|
|
- return;
|
|
- const std::string &icon = addon->getIconBasename();
|
|
- const std::string &icon_full =
|
|
- file_manager->getAddonsFile("icons/" + icon);
|
|
- if (addon->iconNeedsUpdate() ||
|
|
- !file_manager->fileExists(icon_full))
|
|
- {
|
|
- const std::string& url = addon->getIconURL();
|
|
- const std::string& icon = addon->getIconBasename();
|
|
- if (icon.empty())
|
|
- {
|
|
- if (UserConfigParams::logAddons())
|
|
- {
|
|
- Log::error("addons", "No icon or image specified for '%s'.",
|
|
- addon->getId().c_str());
|
|
- }
|
|
- return;
|
|
- }
|
|
- // A simple class that will notify the addon via a callback
|
|
- class IconRequest : public Online::HTTPRequest
|
|
- {
|
|
- std::weak_ptr<bool> m_result;
|
|
- Addon* m_addon; // stores this addon object
|
|
- void callback()
|
|
- {
|
|
- m_addon->setIconReady();
|
|
- if (std::shared_ptr<bool> result = m_result.lock())
|
|
- *result = true;
|
|
- if (!hadDownloadError())
|
|
- addons_manager->m_downloaded_icons = true;
|
|
- } // callback
|
|
- public:
|
|
- IconRequest(const std::string& filename,
|
|
- const std::string& url,
|
|
- Addon* addon, std::weak_ptr<bool> result)
|
|
- : HTTPRequest(filename,/*priority*/1)
|
|
- {
|
|
- m_addon = addon;
|
|
- m_result = result;
|
|
- setURL(url);
|
|
- } // IconRequest
|
|
- };
|
|
- auto r =
|
|
- std::make_shared<IconRequest>("icons/"+icon, url, addon, result);
|
|
- r->queue();
|
|
- }
|
|
} // downloadIconForAddon
|
|
|
|
// ----------------------------------------------------------------------------
|
|
-/** Loads the installed addons from .../addons/addons_installed.xml.
|
|
+/** Loads the installed addons.
|
|
*/
|
|
void AddonsManager::loadInstalledAddons()
|
|
{
|
|
- /* checking for installed addons */
|
|
- if(UserConfigParams::logAddons())
|
|
- {
|
|
- Log::info("addons", "Loading an xml file for installed addons: %s.",
|
|
- m_file_installed.c_str());
|
|
- }
|
|
- const XMLNode *xml = file_manager->createXMLTree(m_file_installed);
|
|
- if(!xml)
|
|
- return;
|
|
-
|
|
- for(unsigned int i=0; i<xml->getNumNodes(); i++)
|
|
- {
|
|
- const XMLNode *node=xml->getNode(i);
|
|
- if(node->getName()=="kart" || node->getName()=="arena" ||
|
|
- node->getName()=="track" )
|
|
- {
|
|
- Addon addon(*node);
|
|
- m_addons_list.getData().push_back(addon);
|
|
- }
|
|
- } // for i <= xml->getNumNodes()
|
|
-
|
|
- delete xml;
|
|
} // loadInstalledAddons
|
|
|
|
// ----------------------------------------------------------------------------
|
|
@@ -498,70 +171,6 @@
|
|
*/
|
|
bool AddonsManager::install(const Addon &addon)
|
|
{
|
|
-
|
|
- //extract the zip in the addons folder called like the addons name
|
|
- std::string base_name = StringUtils::getBasename(addon.getZipFileName());
|
|
- std::string from = file_manager->getAddonsFile("tmp/"+base_name);
|
|
- std::string to = addon.getDataDir();
|
|
-
|
|
- // Remove old addon first (including non official way to install addons)
|
|
- AddonsPack::uninstallByName(addon.getDirName(), true/*force_clear*/);
|
|
- if (file_manager->isDirectory(to))
|
|
- file_manager->removeDirectory(to);
|
|
-
|
|
- file_manager->checkAndCreateDirForAddons(to);
|
|
-
|
|
- bool success = extract_zip(from, to, true/*recursive*/);
|
|
- if (!success)
|
|
- {
|
|
- // TODO: show a message in the interface
|
|
- Log::error("addons", "Failed to unzip '%s' to '%s'.",
|
|
- from.c_str(), to.c_str());
|
|
- }
|
|
-
|
|
- if(!file_manager->removeFile(from))
|
|
- {
|
|
- Log::error("addons", "Problems removing temporary file '%s'.",
|
|
- from.c_str());
|
|
- }
|
|
- if (!success)
|
|
- return false;
|
|
-
|
|
- int index = getAddonIndex(addon.getId());
|
|
- assert(index>=0 && index < (int)m_addons_list.getData().size());
|
|
- m_addons_list.getData()[index].setInstalled(true);
|
|
-
|
|
- if(addon.getType()=="kart")
|
|
- {
|
|
- // We have to remove the mesh of the kart since otherwise it remains
|
|
- // cashed (if a kart is updated), and will therefore be found again
|
|
- // when reloading the karts. This is important on one hand since we
|
|
- // reload all karts (this function is easily available) and existing
|
|
- // karts will not reload their meshes.
|
|
- const KartProperties *prop =
|
|
- kart_properties_manager->getKart(addon.getId());
|
|
- // If the model already exist, first remove the old kart
|
|
- if(prop)
|
|
- kart_properties_manager->removeKart(addon.getId());
|
|
- kart_properties_manager->loadKart(addon.getDataDir());
|
|
- }
|
|
- else if (addon.getType()=="track" || addon.getType()=="arena")
|
|
- {
|
|
- Track *track = track_manager->getTrack(addon.getId());
|
|
- if(track)
|
|
- track_manager->removeTrack(addon.getId());
|
|
-
|
|
- try
|
|
- {
|
|
- track_manager->loadTrack(addon.getDataDir());
|
|
- }
|
|
- catch (std::exception& e)
|
|
- {
|
|
- Log::error("addons", "Cannot load track '%s' : %s.",
|
|
- addon.getDataDir().c_str(), e.what());
|
|
- }
|
|
- }
|
|
- saveInstalled();
|
|
return true;
|
|
} // install
|
|
|
|
@@ -572,45 +181,7 @@
|
|
*/
|
|
bool AddonsManager::uninstall(const Addon &addon)
|
|
{
|
|
- Log::info("addons", "Uninstalling '%s'.",
|
|
- core::stringc(addon.getName()).c_str());
|
|
-
|
|
- // addon is a const reference, and to avoid removing the const, we
|
|
- // find the proper index again to modify the installed state
|
|
- int index = getAddonIndex(addon.getId());
|
|
- assert(index>=0 && index < (int)m_addons_list.getData().size());
|
|
- m_addons_list.getData()[index].setInstalled(false);
|
|
-
|
|
- //remove the addons directory
|
|
- bool error = false;
|
|
- // if the user deleted the data directory for an add-on with
|
|
- // filesystem tools, removeTrack/removeKart will trigger an assert
|
|
- // because the kart/track was never added in the first place
|
|
- if (file_manager->fileExists(addon.getDataDir()))
|
|
- {
|
|
- error = !file_manager->removeDirectory(addon.getDataDir());
|
|
-
|
|
- // Even if an error happened when removing the data files
|
|
- // still remove the addon, since it is unknown if e.g. only
|
|
- // some files were successfully removed. Since we can not
|
|
- // know if the addon is still functional, better remove it.
|
|
- // On the other hand, in case of a problem the user will have
|
|
- // the option in the GUI to try again. In this case
|
|
- // removeTrack/Kart would not find the addon and assert. So
|
|
- // check first if the track is still known.
|
|
- if(addon.getType()=="kart")
|
|
- {
|
|
- if(kart_properties_manager->getKart(addon.getId()))
|
|
- kart_properties_manager->removeKart(addon.getId());
|
|
- }
|
|
- else if(addon.getType()=="track" || addon.getType()=="arena")
|
|
- {
|
|
- if(track_manager->getTrack(addon.getId()))
|
|
- track_manager->removeTrack(addon.getId());
|
|
- }
|
|
- }
|
|
- saveInstalled();
|
|
- return !error;
|
|
+ return true;
|
|
} // uninstall
|
|
|
|
// ----------------------------------------------------------------------------
|
|
@@ -621,30 +192,6 @@
|
|
*/
|
|
void AddonsManager::saveInstalled()
|
|
{
|
|
- // Put the addons in the xml file
|
|
- // Manually because the irrlicht xml writer doesn't seem finished, FIXME ?
|
|
- std::ofstream xml_installed(
|
|
- FileUtils::getPortableWritingPath(m_file_installed));
|
|
-
|
|
- // Write the header of the xml file
|
|
- xml_installed << "<?xml version=\"1.0\"?>" << std::endl;
|
|
-
|
|
- // Get server address from config
|
|
- const std::string server = stk_config->m_server_addons;
|
|
-
|
|
- // Find the third slash (end of the domain)
|
|
- std::string::size_type index = server.find('/');
|
|
- index = server.find('/', index + 2) + 1; // Omit one slash
|
|
- xml_installed << "<addons xmlns='" << server.substr(0, index) << "'>"
|
|
- << std::endl;
|
|
-
|
|
- for(unsigned int i = 0; i < m_addons_list.getData().size(); i++)
|
|
- {
|
|
- m_addons_list.getData()[i].writeXML(&xml_installed);
|
|
- }
|
|
- xml_installed << "</addons>" << std::endl;
|
|
- xml_installed.close();
|
|
- m_downloaded_icons = false;
|
|
} // saveInstalled
|
|
|
|
#endif
|
|
--- a/src/addons/news_manager.cpp 2020-08-28 10:16:23.000000000 +0200
|
|
+++ b/src/addons/news_manager.cpp 2023-12-09 23:11:32.080108164 +0100
|
|
@@ -46,26 +46,12 @@
|
|
m_error_message.setAtomic("");
|
|
m_force_refresh = false;
|
|
|
|
- // Clean .part file which may be left behind
|
|
- std::string news_part = file_manager->getAddonsFile(m_news_filename + ".part");
|
|
- if (file_manager->fileExists(news_part))
|
|
- file_manager->removeFile(news_part);
|
|
-
|
|
init(false);
|
|
} // NewsManage
|
|
|
|
// ---------------------------------------------------------------------------
|
|
NewsManager::~NewsManager()
|
|
{
|
|
- // If the download thread doesn't finish on time we detach the thread to
|
|
- // avoid exception
|
|
- if (m_download_thread.joinable())
|
|
- {
|
|
- if (!CanBeDeleted::canBeDeletedNow())
|
|
- m_download_thread.detach();
|
|
- else
|
|
- m_download_thread.join();
|
|
- }
|
|
} // ~NewsManager
|
|
|
|
// ---------------------------------------------------------------------------
|
|
@@ -78,21 +64,6 @@
|
|
*/
|
|
void NewsManager::init(bool force_refresh)
|
|
{
|
|
- if (m_download_thread.joinable())
|
|
- return;
|
|
-
|
|
- m_force_refresh = force_refresh;
|
|
-
|
|
- // The rest (which potentially involves downloading m_news_filename) is handled
|
|
- // in a separate thread, so that the GUI remains responsive. It is only
|
|
- // started if internet access is enabled, else nothing is done in the
|
|
- // thread anyway (and the addons menu is disabled as a result).
|
|
- if(UserConfigParams::m_internet_status==RequestManager::IPERM_ALLOWED)
|
|
- {
|
|
- CanBeDeleted::resetCanBeDeleted();
|
|
- m_download_thread = std::thread(std::bind(
|
|
- &NewsManager::downloadNews, this));
|
|
- }
|
|
} //init
|
|
|
|
// ---------------------------------------------------------------------------
|
|
@@ -102,117 +73,6 @@
|
|
*/
|
|
void NewsManager::downloadNews()
|
|
{
|
|
- VS::setThreadName("downloadNews");
|
|
- clearErrorMessage();
|
|
-
|
|
- std::string xml_file = file_manager->getAddonsFile(m_news_filename);
|
|
- // Prevent downloading when .part file created, which is already downloaded
|
|
- std::string xml_file_part = file_manager->getAddonsFile(m_news_filename + ".part");
|
|
- bool news_exists = file_manager->fileExists(xml_file);
|
|
-
|
|
- // The news message must be updated if either it has never been updated,
|
|
- // or if the time of the last update was more than news_frequency ago,
|
|
- // or because a 'refresh' was explicitly requested by the user, or no
|
|
- // m_news_filename file exists.
|
|
- bool download = ( UserConfigParams::m_news_last_updated==0 ||
|
|
- UserConfigParams::m_news_last_updated
|
|
- +UserConfigParams::m_news_frequency
|
|
- < StkTime::getTimeSinceEpoch() ||
|
|
- m_force_refresh ||
|
|
- !news_exists )
|
|
- && UserConfigParams::m_internet_status==RequestManager::IPERM_ALLOWED
|
|
- && !file_manager->fileExists(xml_file_part);
|
|
- const XMLNode *xml = NULL;
|
|
-
|
|
- if(!download && news_exists)
|
|
- {
|
|
- // If (so far) we don't need to download, there should be an existing
|
|
- // file. Try to read this, and do some basic checks
|
|
- xml = new XMLNode(xml_file);
|
|
- // A proper news file has at least a version number, mtime, frequency
|
|
- // and an include node (which contains addon data) defined. If this is
|
|
- // not the case, assume that it is an invalid download, or a corrupt
|
|
- // local file. Try downloading again after resetting the news server
|
|
- // back to the default.
|
|
- int version=-1;
|
|
- if( !xml->get("version", &version) || version!=1 ||
|
|
- !xml->get("mtime", &version) ||
|
|
- !xml->getNode("include") ||
|
|
- !xml->get("frequency", &version) )
|
|
- {
|
|
- delete xml;
|
|
- xml = NULL;
|
|
- download = true;
|
|
- } // if xml not consistemt
|
|
- } // if !download
|
|
-
|
|
- if(download)
|
|
- {
|
|
- core::stringw error_message("");
|
|
-
|
|
- auto download_req = std::make_shared<HTTPRequest>(m_news_filename);
|
|
- download_req->setAddonsURL(m_news_filename);
|
|
-
|
|
- // Initialise the online portion of the addons manager.
|
|
- if(UserConfigParams::logAddons())
|
|
- Log::info("addons", "Downloading news.");
|
|
- download_req->executeNow();
|
|
-
|
|
- if(download_req->hadDownloadError())
|
|
- {
|
|
- // Assume that the server address is wrong. And retry
|
|
- // with the default server address again (just in case
|
|
- // that a redirect went wrong, or a wrong/incorrect
|
|
- // address somehow made its way into the config file.
|
|
- // We need a new object, since the state of the old
|
|
- // download request is now done.
|
|
- download_req = std::make_shared<HTTPRequest>(m_news_filename);
|
|
-
|
|
- // make sure the new server address is actually used
|
|
- download_req->setAddonsURL(m_news_filename);
|
|
- download_req->executeNow();
|
|
-
|
|
- if(download_req->hadDownloadError())
|
|
- {
|
|
- // This message must be translated dynamically in the main menu.
|
|
- // If it would be translated here, it wouldn't be translated
|
|
- // if the language is changed in the menu!
|
|
- error_message = N_("Error downloading news: '%s'.");
|
|
- const char *const curl_error = download_req->getDownloadErrorMessage();
|
|
- error_message = StringUtils::insertValues(error_message, curl_error);
|
|
- addons_manager->setErrorState();
|
|
- setErrorMessage(error_message);
|
|
- Log::error("news", core::stringc(error_message).c_str());
|
|
- } // hadDownloadError
|
|
- } // hadDownloadError
|
|
-
|
|
- if(!download_req->hadDownloadError())
|
|
- UserConfigParams::m_news_last_updated = StkTime::getTimeSinceEpoch();
|
|
-
|
|
- // No download error, update the last_updated time value, and delete
|
|
- // the potentially loaded xml file
|
|
- delete xml;
|
|
- xml = NULL;
|
|
- } // hadDownloadError
|
|
-
|
|
- if(xml) delete xml;
|
|
- xml = NULL;
|
|
-
|
|
- // Process new.xml now.
|
|
- if(file_manager->fileExists(xml_file))
|
|
- {
|
|
- xml = new XMLNode(xml_file);
|
|
- checkRedirect(xml);
|
|
- updateNews(xml, xml_file);
|
|
- if (addons_manager)
|
|
- addons_manager->init(xml, m_force_refresh);
|
|
- delete xml;
|
|
- }
|
|
-
|
|
- // We can't finish stk (esp. delete the file manager) before
|
|
- // this part of the code is reached (since otherwise the file
|
|
- // manager might be access after it was deleted).
|
|
- CanBeDeleted::setCanBeDeleted();
|
|
} // downloadNews
|
|
|
|
// ---------------------------------------------------------------------------
|
|
@@ -222,57 +82,6 @@
|
|
*/
|
|
void NewsManager::checkRedirect(const XMLNode *xml)
|
|
{
|
|
- if (stk_config->m_allow_news_redirects)
|
|
- {
|
|
- // NOTE: Before 0.10 there were just two redirect attributes
|
|
- // "redirect" - addons server (contains /dl/xml/ path)
|
|
- // "hw-report-server" - hardware report server
|
|
-
|
|
- // Redirect for the new addons server
|
|
- std::string new_addons_server;
|
|
- if (xml->get("redirect-server-addons", &new_addons_server) == 1 && !new_addons_server.empty())
|
|
- {
|
|
- if (UserConfigParams::logAddons())
|
|
- {
|
|
- Log::info("[Addons]", "Current addons server: '%s'\n [Addons] New addons server: '%s'",
|
|
- stk_config->m_server_addons.c_str(), new_addons_server.c_str());
|
|
- }
|
|
- stk_config->m_server_addons = new_addons_server;
|
|
- }
|
|
-
|
|
- // Redirect for the API server
|
|
- std::string new_api_server;
|
|
- if (xml->get("redirect-server-api", &new_api_server) == 1 && !new_api_server.empty())
|
|
- {
|
|
- if (UserConfigParams::logAddons())
|
|
- {
|
|
- Log::info("[Addons]", "Current API server: '%s'\n [Addons] New API server: '%s'",
|
|
- stk_config->m_server_api.c_str(), new_api_server.c_str());
|
|
- }
|
|
- stk_config->m_server_api = new_api_server;
|
|
- }
|
|
-
|
|
- // Redirect for the hardware report server
|
|
- std::string new_hardware_report_server;
|
|
- if (xml->get("redirect-server-hardware-report", &new_hardware_report_server) == 1 && !new_hardware_report_server.empty())
|
|
- {
|
|
- Log::info("hw report", "Current hardware report server: '%s'\n [hw report] New hardware report server: '%s'",
|
|
- stk_config->m_server_hardware_report.c_str(), new_hardware_report_server.c_str());
|
|
- stk_config->m_server_hardware_report = new_hardware_report_server;
|
|
- }
|
|
- }
|
|
-
|
|
- // Update menu/game polling interval
|
|
- float polling;
|
|
- if (xml->get("menu-polling-interval", &polling))
|
|
- {
|
|
- RequestManager::get()->setMenuPollingInterval(polling);
|
|
- }
|
|
- if (xml->get("game-polling-interval", &polling))
|
|
- {
|
|
- RequestManager::get()->setGamePollingInterval(polling);
|
|
- }
|
|
-
|
|
} // checkRedirect
|
|
|
|
// ----------------------------------------------------------------------------
|
|
@@ -285,68 +94,6 @@
|
|
*/
|
|
void NewsManager::updateNews(const XMLNode *xml, const std::string &filename)
|
|
{
|
|
-
|
|
- m_all_news_messages = "";
|
|
- const core::stringw message_divider=" +++ ";
|
|
- // This function is also called in case of a reinit, so
|
|
- // we have to delete existing news messages here first.
|
|
- m_news.lock();
|
|
- m_news.getData().clear();
|
|
- m_news.unlock();
|
|
- bool error = true;
|
|
- int frequency=0;
|
|
- if(xml->get("frequency", &frequency))
|
|
- UserConfigParams::m_news_frequency = frequency;
|
|
-
|
|
- for(unsigned int i=0; i<xml->getNumNodes(); i++)
|
|
- {
|
|
- const XMLNode *node = xml->getNode(i);
|
|
- if(node->getName()!="message") continue;
|
|
- core::stringw news;
|
|
- node->get("content", &news);
|
|
- int id=-1;
|
|
- node->get("id", &id);
|
|
- bool important=false;
|
|
- node->get("important", &important);
|
|
-
|
|
- std::string cond;
|
|
- node->get("condition", &cond);
|
|
- if(!conditionFulfilled(cond))
|
|
- continue;
|
|
- m_news.lock();
|
|
- {
|
|
-
|
|
- if(!important)
|
|
- m_all_news_messages += m_all_news_messages.size()>0
|
|
- ? message_divider + news
|
|
- : news;
|
|
- else
|
|
- // Define this if news messages should be removed
|
|
- // after being shown a certain number of times.
|
|
- {
|
|
- NewsMessage n(news, id, important);
|
|
- m_news.getData().push_back(n);
|
|
- }
|
|
- } // m_news.lock()
|
|
- m_news.unlock();
|
|
-
|
|
- error = false;
|
|
- }
|
|
- if(error)
|
|
- {
|
|
- // In case of an error (e.g. the file only contains
|
|
- // an error message from the server), delete the file
|
|
- // so that it is not read again (and this will force
|
|
- // a new read on the next start, instead of waiting
|
|
- // for some time).
|
|
- file_manager->removeFile(filename);
|
|
- NewsMessage n(_("Failed to connect to the SuperTuxKart add-ons server."), -1);
|
|
- m_news.lock();
|
|
- m_news.getData().push_back(n);
|
|
-
|
|
- m_all_news_messages="";
|
|
- m_news.unlock();
|
|
- }
|
|
} // updateNews
|
|
|
|
// ----------------------------------------------------------------------------
|
|
@@ -356,10 +103,6 @@
|
|
*/
|
|
void NewsManager::addNewsMessage(const core::stringw &s)
|
|
{
|
|
- NewsMessage n(s, -1);
|
|
- m_news.lock();
|
|
- m_news.getData().push_back(n);
|
|
- m_news.unlock();
|
|
} // addNewsMessage
|
|
// ----------------------------------------------------------------------------
|
|
/** Returns the important message with the smallest id that has not been
|
|
@@ -368,30 +111,7 @@
|
|
*/
|
|
const core::stringw NewsManager::getImportantMessage()
|
|
{
|
|
- int index = -1;
|
|
- m_news.lock();
|
|
- for(unsigned int i=0; i<m_news.getData().size(); i++)
|
|
- {
|
|
- const NewsMessage &m = m_news.getData()[i];
|
|
- //
|
|
- if(m.isImportant() &&
|
|
- m.getMessageId()>UserConfigParams::m_last_important_message_id &&
|
|
- (index == -1 ||
|
|
- m.getMessageId() < m_news.getData()[index].getMessageId() ) )
|
|
- {
|
|
- index = i;
|
|
- } // if new unshown important message with smaller message id
|
|
- }
|
|
core::stringw message("");
|
|
- if(index>=0)
|
|
- {
|
|
- const NewsMessage &m = m_news.getData()[index];
|
|
- message = m.getNews();
|
|
- UserConfigParams::m_last_important_message_id = m.getMessageId();
|
|
-
|
|
- }
|
|
- m_news.unlock();
|
|
-
|
|
return message;
|
|
} // getImportantMessage
|
|
|
|
@@ -403,35 +123,7 @@
|
|
*/
|
|
const core::stringw NewsManager::getNextNewsMessage()
|
|
{
|
|
- // Only display error message in case of a problem.
|
|
- if(m_error_message.getAtomic().size()>0)
|
|
- return _(m_error_message.getAtomic().c_str());
|
|
-
|
|
- m_news.lock();
|
|
- if(m_all_news_messages.size()>0)
|
|
- {
|
|
- // Copy the news message while it is locked.
|
|
- core::stringw anm = m_all_news_messages;
|
|
- m_news.unlock();
|
|
- return anm;
|
|
- }
|
|
-
|
|
- if(m_news.getData().size()==0)
|
|
- {
|
|
- // Lock
|
|
- m_news.unlock();
|
|
- return "";
|
|
- }
|
|
-
|
|
core::stringw m("");
|
|
- {
|
|
- m_current_news_message++;
|
|
- if(m_current_news_message >= (int)m_news.getData().size())
|
|
- m_current_news_message = 0;
|
|
-
|
|
- m = m_news.getData()[m_current_news_message].getNews();
|
|
- }
|
|
- m_news.unlock();
|
|
return _(m.c_str());
|
|
} // getNextNewsMessage
|
|
|
|
@@ -447,66 +139,6 @@
|
|
*/
|
|
bool NewsManager::conditionFulfilled(const std::string &cond)
|
|
{
|
|
- std::vector<std::string> cond_list;
|
|
- cond_list = StringUtils::split(cond, ';');
|
|
- for(unsigned int i=0; i<cond_list.size(); i++)
|
|
- {
|
|
- std::vector<std::string> cond = StringUtils::split(cond_list[i],' ');
|
|
- if(cond.size()!=3)
|
|
- {
|
|
- Log::warn("NewsManager", "Invalid condition '%s' - assumed to "
|
|
- "be true.", cond_list[i].c_str());
|
|
- continue;
|
|
- }
|
|
- // Check for stkversion comparisons
|
|
- // ================================
|
|
- if(cond[0]=="stkversion")
|
|
- {
|
|
- int news_version = StringUtils::versionToInt(cond[2]);
|
|
- int stk_version = StringUtils::versionToInt(STK_VERSION);
|
|
- if(cond[1]=="=")
|
|
- {
|
|
- if(stk_version!=news_version) return false;
|
|
- continue;
|
|
- }
|
|
- if(cond[1]=="<")
|
|
- {
|
|
- if(stk_version>=news_version) return false;
|
|
- continue;
|
|
- }
|
|
- if(cond[1]==">")
|
|
- {
|
|
- if(stk_version<=news_version) return false;
|
|
- continue;
|
|
- }
|
|
- Log::warn("NewsManager", "Invalid comparison in condition '%s' - "
|
|
- "assumed true.", cond_list[i].c_str());
|
|
- }
|
|
- // Check for addons not installed
|
|
- // ==============================
|
|
- else if(cond[1]=="not" && cond[2]=="installed")
|
|
- {
|
|
- // The addons_manager cannot be accessed, since it's
|
|
- // being initialised after the news manager. So a simple
|
|
- // test is made to see if the directory exists. It is
|
|
- // necessary to check for karts and tracks separately,
|
|
- // since it's not possible to know if the addons is
|
|
- // a kart or a track.
|
|
- const std::string dir=file_manager->getAddonsDir();
|
|
- if(file_manager->fileExists(dir+"/karts/"+cond[0]))
|
|
- return false;
|
|
- if(file_manager->fileExists(dir+"/tracks/"+cond[0]))
|
|
- return false;
|
|
- continue;
|
|
- }
|
|
- else
|
|
- {
|
|
- Log::warn("NewsManager", "Invalid condition '%s' - assumed to "
|
|
- "be true.", cond_list[i].c_str());
|
|
- continue;
|
|
- }
|
|
-
|
|
- } // for i < cond_list
|
|
return true;
|
|
} // conditionFulfilled
|
|
|
|
--- a/src/states_screens/main_menu_screen.cpp 2020-08-28 10:16:23.000000000 +0200
|
|
+++ b/src/states_screens/main_menu_screen.cpp 2023-12-09 23:41:26.040123548 +0100
|
|
@@ -19,7 +19,6 @@
|
|
|
|
#include "states_screens/main_menu_screen.hpp"
|
|
|
|
-#include "addons/news_manager.hpp"
|
|
#include "challenges/story_mode_timer.hpp"
|
|
#include "challenges/unlock_manager.hpp"
|
|
#include "config/player_manager.hpp"
|
|
@@ -41,7 +40,6 @@
|
|
#include "modes/demo_world.hpp"
|
|
#include "network/network_config.hpp"
|
|
#include "online/request_manager.hpp"
|
|
-#include "states_screens/addons_screen.hpp"
|
|
#include "states_screens/credits.hpp"
|
|
#include "states_screens/cutscene_general.hpp"
|
|
#include "states_screens/grand_prix_editor_screen.hpp"
|
|
@@ -81,9 +79,6 @@
|
|
|
|
void MainMenuScreen::loadedFromFile()
|
|
{
|
|
- LabelWidget* w = getWidget<LabelWidget>("info_addons");
|
|
- w->setScrollSpeed(GUIEngine::getFontHeight() / 2);
|
|
-
|
|
RibbonWidget* rw_top = getWidget<RibbonWidget>("menu_toprow");
|
|
assert(rw_top != NULL);
|
|
|
|
@@ -148,18 +143,6 @@
|
|
input_manager->getDeviceManager()->clearLatestUsedDevice();
|
|
|
|
#ifndef SERVER_ONLY
|
|
- if (addons_manager && addons_manager->isLoading())
|
|
- {
|
|
- IconButtonWidget* w = getWidget<IconButtonWidget>("addons");
|
|
- w->setActive(false);
|
|
- w->resetAllBadges();
|
|
- w->setBadge(LOADING_BADGE);
|
|
- }
|
|
-
|
|
- LabelWidget* w = getWidget<LabelWidget>("info_addons");
|
|
- const core::stringw &news_text = NewsManager::get()->getNextNewsMessage();
|
|
- w->setText(news_text, true);
|
|
- w->update(0.01f);
|
|
#endif
|
|
|
|
RibbonWidget* r = getWidget<RibbonWidget>("menu_bottomrow");
|
|
@@ -185,41 +168,6 @@
|
|
void MainMenuScreen::onUpdate(float delta)
|
|
{
|
|
#ifndef SERVER_ONLY
|
|
- NewsManager::get()->joinDownloadThreadIfExit();
|
|
-
|
|
- IconButtonWidget* addons_icon = getWidget<IconButtonWidget>("addons");
|
|
- if (addons_icon != NULL)
|
|
- {
|
|
- if (addons_manager->wasError())
|
|
- {
|
|
- addons_icon->setActive(true);
|
|
- addons_icon->resetAllBadges();
|
|
- addons_icon->setBadge(BAD_BADGE);
|
|
- }
|
|
- else if (addons_manager->isLoading() && UserConfigParams::m_internet_status
|
|
- == Online::RequestManager::IPERM_ALLOWED)
|
|
- {
|
|
- // Addons manager is still initialising/downloading.
|
|
- addons_icon->setActive(false);
|
|
- addons_icon->resetAllBadges();
|
|
- addons_icon->setBadge(LOADING_BADGE);
|
|
- }
|
|
- else
|
|
- {
|
|
- addons_icon->setActive(true);
|
|
- addons_icon->resetAllBadges();
|
|
- }
|
|
- // maybe add a new badge when not allowed to access the net
|
|
- }
|
|
-
|
|
- LabelWidget* w = getWidget<LabelWidget>("info_addons");
|
|
- w->update(delta);
|
|
- if(w->scrolledOff())
|
|
- {
|
|
- const core::stringw &news_text = NewsManager::get()->getNextNewsMessage();
|
|
- w->setText(news_text, true);
|
|
- }
|
|
-
|
|
PlayerProfile *player = PlayerManager::getCurrentPlayer();
|
|
if (!player)
|
|
return;
|
|
@@ -560,31 +508,6 @@
|
|
}
|
|
OnlineScreen::getInstance()->push();
|
|
}
|
|
- else if (selection == "addons")
|
|
- {
|
|
- // Don't go to addons if there is no internet, unless some addons are
|
|
- // already installed (so that you can delete addons without being online).
|
|
- if(UserConfigParams::m_internet_status!=RequestManager::IPERM_ALLOWED)
|
|
- {
|
|
- if (!addons_manager->anyAddonsInstalled())
|
|
- {
|
|
- new MessageDialog(_("You can not download addons without internet access. "
|
|
- "If you want to download addons, go in the options menu, "
|
|
- "and check \"Connect to the Internet\"."));
|
|
- return;
|
|
- }
|
|
- else
|
|
- {
|
|
- AddonsScreen::getInstance()->push();
|
|
- new MessageDialog(_("You can not download addons without internet access. "
|
|
- "If you want to download addons, go in the options menu, "
|
|
- "and check \"Connect to the Internet\".\n\n"
|
|
- "You can however delete already downloaded addons."));
|
|
- return;
|
|
- }
|
|
- }
|
|
- AddonsScreen::getInstance()->push();
|
|
- }
|
|
else if (selection == "gpEditor")
|
|
{
|
|
GrandPrixEditorScreen::getInstance()->push();
|
|
@@ -608,25 +531,6 @@
|
|
void MainMenuScreen::onDisabledItemClicked(const std::string& item)
|
|
{
|
|
#ifndef SERVER_ONLY
|
|
- if (item == "addons")
|
|
- {
|
|
- if (UserConfigParams::m_internet_status != RequestManager::IPERM_ALLOWED)
|
|
- {
|
|
- new MessageDialog( _("The add-ons module is currently disabled in "
|
|
- "the Options screen") );
|
|
- }
|
|
- else if (addons_manager->wasError())
|
|
- {
|
|
- new MessageDialog( _("Sorry, an error occurred while contacting "
|
|
- "the add-ons website. Make sure you are "
|
|
- "connected to the Internet and that "
|
|
- "SuperTuxKart is not blocked by a firewall"));
|
|
- }
|
|
- else if (addons_manager->isLoading())
|
|
- {
|
|
- new MessageDialog( _("Please wait while the add-ons are loading"));
|
|
- }
|
|
- }
|
|
#endif
|
|
} // onDisabledItemClicked
|
|
|
|
--- a/src/io/file_manager.cpp 1990-01-01 01:00:00.000000000 +0100
|
|
+++ b/src/io/file_manager.cpp 2023-12-10 16:28:49.560026623 +0100
|
|
@@ -1090,18 +1090,6 @@
|
|
"falling back to '.'.", m_addons_dir.c_str());
|
|
m_addons_dir = "./";
|
|
}
|
|
-
|
|
- if (!checkAndCreateDirectory(m_addons_dir + "icons/"))
|
|
- {
|
|
- Log::error("FileManager", "Failed to create add-ons icon dir at '%s'.",
|
|
- (m_addons_dir + "icons/").c_str());
|
|
- }
|
|
- if (!checkAndCreateDirectory(m_addons_dir + "tmp/"))
|
|
- {
|
|
- Log::error("FileManager", "Failed to create add-ons tmp dir at '%s'.",
|
|
- (m_addons_dir + "tmp/").c_str());
|
|
- }
|
|
-
|
|
} // checkAndCreateAddonsDir
|
|
|
|
// ----------------------------------------------------------------------------
|
|
|
|
--- a/data/gui/screens/main_menu.stkgui 2020-08-28 10:16:23.000000000 +0200
|
|
+++ b/data/gui/screens/main_menu.stkgui 2023-12-10 00:11:36.830139076 +0100
|
|
@@ -16,9 +16,6 @@
|
|
<icon-button id="online" width="128" height="128"
|
|
icon="gui/icons/menu_online.png" focus_icon="gui/icons/menu_online_focus.png"
|
|
I18N="Main menu button" text="Online" word_wrap="true"/>
|
|
- <icon-button id="addons" width="128" height="128"
|
|
- icon="gui/icons/menu_addons.png" focus_icon="gui/icons/menu_addons_focus.png"
|
|
- I18N="Main menu button" text="Addons" word_wrap="true"/>
|
|
</buttonbar>
|
|
|
|
<spacer width="10" height="6%"/>
|