1) Added proper handling and detection of installed addons

2) Moved addon related data files into a separate 'addons' directory.
3) Directory names of addons are now lower case.
(Still addons are not stable enough to be used!)


git-svn-id: svn+ssh://svn.code.sf.net/p/supertuxkart/code/main/trunk@7252 178a84e3-b1eb-0310-8ba1-8eac791a3b58
This commit is contained in:
hikerstk 2011-01-04 21:49:17 +00:00
parent 470832cd19
commit 3c729b4b0f
10 changed files with 126 additions and 72 deletions

View File

@ -20,8 +20,13 @@
#ifdef ADDONS_MANAGER #ifdef ADDONS_MANAGER
#include "addons/addon.hpp" #include "addons/addon.hpp"
#include <fstream>
//#include <iostream>
#include "io/file_manager.hpp" #include "io/file_manager.hpp"
#include "io/xml_node.hpp" #include "io/xml_node.hpp"
#include "utils/string_utils.hpp"
Addon::Addon(const XMLNode &xml, bool installed) Addon::Addon(const XMLNode &xml, bool installed)
{ {
@ -29,17 +34,53 @@ Addon::Addon(const XMLNode &xml, bool installed)
m_installed_version = 0; m_installed_version = 0;
m_name = ""; m_name = "";
m_version = 0 ; m_version = 0 ;
m_file = ""; m_zip_file = "";
m_description = ""; m_description = "";
m_icon = ""; m_icon = "";
m_id = ""; m_id = "";
m_type = xml.getName(); m_type = xml.getName();
xml.get("name", &m_name ); xml.get("name", &m_name);
xml.get("version", &m_version ); if(m_installed)
xml.get("file", &m_file ); {
xml.get("description", &m_description); xml.get("installed-version", &m_installed_version);
xml.get("icon", &m_icon ); xml.get("id", &m_id );
xml.get("id", &m_id ); }
else // not installed
{
xml.get("file", &m_zip_file );
xml.get("description", &m_description);
xml.get("icon", &m_icon );
xml.get("version", &m_version );
// The online list has a numeric id, which is not used.
// So ignore it.
m_id = StringUtils::toLowerCase(m_name);
} // if installed
}; // Addon(const XML&) }; // Addon(const XML&)
// ----------------------------------------------------------------------------
/** Copies the installation data (like description, version, icon) from the
* downloaded online list to this entry.
*/
void Addon::copyInstallData(const Addon &addon)
{
m_description = addon.m_description;
m_version = addon.m_version;
m_zip_file = addon.m_zip_file;
m_icon = addon.m_icon;
} // copyInstallData
// ----------------------------------------------------------------------------
/** Writes information about an installed addon (it is only called for
* installed addons).
* \param out_stream Output stream to write to.
*/
void Addon::writeXML(std::ofstream *out_stream)
{
(*out_stream) << " <" << m_type << " name=\"" << m_name
<< "\" id=\"" << m_id << "\" installed-version=\""
<< m_installed_version << "\"/>\n";
} // writeXML
#endif #endif

View File

@ -29,27 +29,42 @@ class XMLNode;
class Addon class Addon
{ {
public: public:
/** The name to be displayed. */
std::string m_name; std::string m_name;
int m_version; /** Internal id for this addon, which is the name in lower case.
int m_installed_version; * This is used to create a subdirectory for this addon. */
std::string m_description;
std::string m_icon;
std::string m_file;
std::string m_id; std::string m_id;
/** The (highest) version available online. */
int m_version;
/** The currently installed version. */
int m_installed_version;
/** A description of this addon. */
std::string m_description;
/** Name of the icon to use. */
std::string m_icon;
/** The name of the zip file on the addon server. */
std::string m_zip_file;
/** True if the addon is installed. */
bool m_installed; bool m_installed;
/** Type, must be 'kart' or 'track'. */
std::string m_type; std::string m_type;
Addon() {}; Addon() {};
/** Initialises the object from an XML node. */ /** Initialises the object from an XML node. */
Addon(const XMLNode &xml, bool installed=false); Addon(const XMLNode &xml, bool installed=false);
// ------------------------------------------------------------------------ // ------------------------------------------------------------------------
void writeXML(std::ofstream *out_stram);
// ------------------------------------------------------------------------
void copyInstallData(const Addon &addon);
// ------------------------------------------------------------------------
/** Returns the name of the addon. */ /** Returns the name of the addon. */
const std::string& getName() const {return m_name; } const std::string& getName() const {return m_name; }
// ------------------------------------------------------------------------ // ------------------------------------------------------------------------
/** Returns the type of the addon. */ /** Returns the type of the addon. */
const std::string& getType() const {return m_type; } const std::string& getType() const {return m_type; }
// ------------------------------------------------------------------------ // ------------------------------------------------------------------------
/** Returns the filename of the addon. */ /** Returns the filename of the zip file with the addon. */
const std::string& getFile() const {return m_file; } const std::string& getZipFileName() const {return m_zip_file; }
// ------------------------------------------------------------------------ // ------------------------------------------------------------------------
/** Returns the name of the icon of this addon. */ /** Returns the name of the icon of this addon. */
const std::string& getIcon() const {return m_icon; } const std::string& getIcon() const {return m_icon; }

View File

@ -46,8 +46,7 @@ AddonsManager* addons_manager = 0;
*/ */
AddonsManager::AddonsManager() : m_state(STATE_INIT) AddonsManager::AddonsManager() : m_state(STATE_INIT)
{ {
m_file_installed = file_manager->getConfigDir() m_file_installed = file_manager->getAddonsFile("addons_installed.xml");
+ "/" + "addons_installed.xml";
loadInstalledAddons(); loadInstalledAddons();
} // AddonsManager } // AddonsManager
@ -84,15 +83,14 @@ void AddonsManager::initOnline()
for(unsigned int i=0; i<xml->getNumNodes(); i++) for(unsigned int i=0; i<xml->getNumNodes(); i++)
{ {
const XMLNode *node = xml->getNode(i); const XMLNode *node = xml->getNode(i);
if(node->getName()=="track") if(node->getName()=="track" || node->getName()=="kart")
{ {
Addon addon(*node); Addon addon(*node);
m_addons_list.push_back(addon); int index = getAddonIndex(addon.getId());
} if(index>=0)
else if(node->getName()=="kart") m_addons_list[index].copyInstallData(addon);
{ else
Addon addon(*node); m_addons_list.push_back(addon);
m_addons_list.push_back(addon);
} }
else else
{ {
@ -118,6 +116,10 @@ bool AddonsManager::onlineReady()
} // onlineReady } // onlineReady
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
/** Loads the installed addons from .../addons/addons_installed.xml. This is
* called before network_http is constructed (so no need to protect
* m_addons_list with a mutex).
*/
void AddonsManager::loadInstalledAddons() void AddonsManager::loadInstalledAddons()
{ {
/* checking for installed addons */ /* checking for installed addons */
@ -133,25 +135,8 @@ void AddonsManager::loadInstalledAddons()
if(node->getName()=="kart" || if(node->getName()=="kart" ||
node->getName()=="track" ) node->getName()=="track" )
{ {
std::string name=""; Addon addon(*node, /*installed*/ true);
std::string id=""; m_addons_list.push_back(addon);
int version = 0;
node->get("id", &id );
node->get("name", &name );
node->get("version", &version);
int index = getAddonIndex(id);
if(index>0)
{
m_addons_list[index].m_installed = true;
m_addons_list[index].m_installed_version = version;
std::cout << "[Addons] An addon is already installed: "
<< id << std::endl;
}
else
{
Addon addon(*xml, /* installed= */ true);
m_addons_list.push_back(addon);
}
} }
} // for i <= xml->getNumNodes() } // for i <= xml->getNumNodes()
@ -186,14 +171,13 @@ int AddonsManager::getAddonIndex(const std::string &id) const
void AddonsManager::install(const Addon &addon) void AddonsManager::install(const Addon &addon)
{ {
bool success=true; bool success=true;
std::string id = StringUtils::toLowerCase(addon.getName());
file_manager->checkAndCreateDirForAddons(addon.getName(), file_manager->checkAndCreateDirForAddons(id, addon.getType()+ "s/");
addon.getType()+ "s/");
//extract the zip in the addons folder called like the addons name //extract the zip in the addons folder called like the addons name
std::string dest_file = file_manager->getAddonsDir() + "/" std::string dest_file = file_manager->getAddonsDir() + "/"
+ addon.getType()+ "s/" + addon.getName() + "/" ; + addon.getType()+ "s/" + addon.getName() + "/" ;
std::string base_name = StringUtils::getBasename(addon.getFile()); std::string base_name = StringUtils::getBasename(addon.getZipFileName());
std::string from = file_manager->getAddonsFile(base_name); std::string from = file_manager->getAddonsFile(base_name);
std::string to = dest_file; std::string to = dest_file;
@ -232,14 +216,7 @@ void AddonsManager::saveInstalled()
{ {
if(m_addons_list[i].m_installed) if(m_addons_list[i].m_installed)
{ {
std::ostringstream os; m_addons_list[i].writeXML(&xml_installed);
os << m_addons_list[i].m_installed_version;
//transform the version (int) in string
xml_installed << "<"+ m_addons_list[i].m_type +" name=\"" +
m_addons_list[i].m_name + "\" id=\"" +
m_addons_list[i].m_id + "\"";
xml_installed << " version=\"" + os.str() + "\" />" << std::endl;
} }
} }
xml_installed << "</addons>" << std::endl; xml_installed << "</addons>" << std::endl;

View File

@ -490,7 +490,7 @@ void FileManager::checkAndCreateConfigDir()
void FileManager::checkAndCreateAddonsDir() void FileManager::checkAndCreateAddonsDir()
{ {
#if defined(WIN32) #if defined(WIN32)
m_addons_dir = m_config_dir; m_addons_dir = m_config_dir+"/addons";
#elif defined(__APPLE__) #elif defined(__APPLE__)
m_addons_dir = getenv("HOME"); m_addons_dir = getenv("HOME");
m_addons_dir += "/Library/Application Support/SuperTuxKart"; m_addons_dir += "/Library/Application Support/SuperTuxKart";

View File

@ -75,7 +75,8 @@ void AddonsScreen::init()
getWidget<GUIEngine::LabelWidget>("update_status") getWidget<GUIEngine::LabelWidget>("update_status")
->setText(_("Updating the list...")); ->setText(_("Updating the list..."));
loadList("kart"); m_type = "kart";
loadList();
} // init } // init
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
@ -83,7 +84,7 @@ void AddonsScreen::init()
* updated. * updated.
* \param type Must be 'kart' or 'track'. * \param type Must be 'kart' or 'track'.
*/ */
void AddonsScreen::loadList(const std::string &type) void AddonsScreen::loadList()
{ {
GUIEngine::ListWidget* w_list = GUIEngine::ListWidget* w_list =
getWidget<GUIEngine::ListWidget>("list_addons"); getWidget<GUIEngine::ListWidget>("list_addons");
@ -92,11 +93,11 @@ void AddonsScreen::loadList(const std::string &type)
{ {
const Addon &addon = addons_manager->getAddon(i); const Addon &addon = addons_manager->getAddon(i);
// Ignore addons of a different type // Ignore addons of a different type
if(addon.getType()!=type) continue; if(addon.getType()!=m_type) continue;
// Get the right icon to display // Get the right icon to display
int icon; int icon;
if(addon.isInstalled() && addon.needsUpdate()) if(addon.isInstalled())
icon = addon.needsUpdate() ? m_icon_needs_update icon = addon.needsUpdate() ? m_icon_needs_update
: m_icon_installed; : m_icon_installed;
else else
@ -109,10 +110,10 @@ void AddonsScreen::loadList(const std::string &type)
m_can_load_list = false; m_can_load_list = false;
getWidget<GUIEngine::RibbonWidget>("category")->setActivated(); getWidget<GUIEngine::RibbonWidget>("category")->setActivated();
getWidget<GUIEngine::LabelWidget>("update_status")->setText(""); getWidget<GUIEngine::LabelWidget>("update_status")->setText("");
if(type == "kart") if(m_type == "kart")
getWidget<GUIEngine::RibbonWidget>("category")->select("tab_kart", getWidget<GUIEngine::RibbonWidget>("category")->select("tab_kart",
PLAYER_ID_GAME_MASTER); PLAYER_ID_GAME_MASTER);
else if(type == "track") else if(m_type == "track")
getWidget<GUIEngine::RibbonWidget>("category")->select("tab_track", getWidget<GUIEngine::RibbonWidget>("category")->select("tab_track",
PLAYER_ID_GAME_MASTER); PLAYER_ID_GAME_MASTER);
else else
@ -146,11 +147,13 @@ void AddonsScreen::eventCallback(GUIEngine::Widget* widget,
StateManager::get()->replaceTopMostScreen(AddonsUpdateScreen::getInstance()); StateManager::get()->replaceTopMostScreen(AddonsUpdateScreen::getInstance());
else if (selection == "tab_track") else if (selection == "tab_track")
{ {
loadList("track"); m_type = "track";
loadList();
} }
else if (selection == "tab_kart") else if (selection == "tab_kart")
{ {
loadList("kart"); m_type = "kart";
loadList();
} }
} }
} // eventCallback } // eventCallback

View File

@ -40,7 +40,7 @@ class AddonsScreen : public GUIEngine::Screen,
public GUIEngine::ScreenSingleton<AddonsScreen> public GUIEngine::ScreenSingleton<AddonsScreen>
{ {
friend class GUIEngine::ScreenSingleton<AddonsScreen>; friend class GUIEngine::ScreenSingleton<AddonsScreen>;
private:
AddonsScreen(); AddonsScreen();
AddonsManager *m_addons; AddonsManager *m_addons;
AddonsLoading *m_load; AddonsLoading *m_load;
@ -55,13 +55,15 @@ class AddonsScreen : public GUIEngine::Screen,
*m_icon_bank; *m_icon_bank;
GUIEngine::LabelWidget GUIEngine::LabelWidget
*m_update_status; *m_update_status;
/** Currently selected type. */
std::string m_type;
public: public:
bool m_can_load_list; bool m_can_load_list;
/** Load the addons into the main list.*/ /** Load the addons into the main list.*/
void loadList(const std::string &type); void loadList();
/** \brief implement callback from parent class GUIEngine::Screen */ /** \brief implement callback from parent class GUIEngine::Screen */
virtual void loadedFromFile(); virtual void loadedFromFile();

View File

@ -71,15 +71,15 @@ void AddonsUpdateScreen::eventCallback(GUIEngine::Widget* widget,
if (selection == "tab_track") if (selection == "tab_track")
{ {
StateManager::get()->replaceTopMostScreen(AddonsScreen::getInstance()); StateManager::get()->replaceTopMostScreen(AddonsScreen::getInstance());
AddonsScreen::getInstance()->loadList("track"); AddonsScreen::getInstance()->loadList();
} }
else if (selection == "tab_kart") else if (selection == "tab_kart")
{ {
StateManager::get()->replaceTopMostScreen(AddonsScreen::getInstance()); StateManager::get()->replaceTopMostScreen(AddonsScreen::getInstance());
AddonsScreen::getInstance()->loadList("kart"); AddonsScreen::getInstance()->loadList();
} }
} }
} } // eventCallback
// ------------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------------

View File

@ -167,7 +167,7 @@ void AddonsLoading::onUpdate(float delta)
{ {
// TODO: show a message in the interface // TODO: show a message in the interface
fprintf(stderr, "[Addons] Failed to download '%s'\n", fprintf(stderr, "[Addons] Failed to download '%s'\n",
m_addon.getFile().c_str()); m_addon.getZipFileName().c_str());
dismiss(); dismiss();
return; return;
} }
@ -218,12 +218,14 @@ void AddonsLoading::close()
**/ **/
void AddonsLoading::startInstall() void AddonsLoading::startInstall()
{ {
std::string file = "file/" + m_addon.getFile(); std::string file = "file/" + m_addon.getZipFileName();
std::string save = StringUtils::getBasename(m_addon.getFile()); std::string save = StringUtils::getBasename(m_addon.getZipFileName());
network_http->downloadFileAsynchron(file, save); network_http->downloadFileAsynchron(file, save);
} // startInstall } // startInstall
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
/** Called when the asynchronous download of the addon finished.
*/
void AddonsLoading::endInstall() void AddonsLoading::endInstall()
{ {
if(!m_addon.isInstalled() || m_addon.needsUpdate()) if(!m_addon.isInstalled() || m_addon.needsUpdate())
@ -234,5 +236,8 @@ void AddonsLoading::endInstall()
{ {
addons_manager->uninstall(m_addon); addons_manager->uninstall(m_addon);
} }
// The list of the addon screen needs to be updated to correctly
// display the newly (un)installed addon.
AddonsScreen::getInstance()->loadList();
} // endInstall } // endInstall
#endif #endif

View File

@ -112,6 +112,16 @@ namespace StringUtils
return name; return name;
} // toUpperCase } // toUpperCase
//-------------------------------------------------------------------------
/** Returns a string converted to lower case.
*/
std::string toLowerCase(const std::string& str)
{
std::string name = str;
std::transform(name.begin(), name.end(), name.begin(), ::tolower);
return name;
} // toLowerCase
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
/** Splits a string into substrings separated by a certain character, and /** Splits a string into substrings separated by a certain character, and
* returns a std::vector of all those substring. E.g.: * returns a std::vector of all those substring. E.g.:

View File

@ -76,6 +76,7 @@ namespace StringUtils
} }
std::string toUpperCase(const std::string&); std::string toUpperCase(const std::string&);
std::string toLowerCase(const std::string&);
std::vector<std::string> split(const std::string& s, char c, std::vector<std::string> split(const std::string& s, char c,
bool keepSplitChar=false); bool keepSplitChar=false);
std::vector<irr::core::stringw> split(const irr::core::stringw& s, std::vector<irr::core::stringw> split(const irr::core::stringw& s,