Many style changes and code improvements to addons management.

The addons icon is now greyed out in case of an error (and till
the list of addons is downloaded, though usually that is done by
the time the main gui is shown). Moved synchronisations/threading
from addons_manager to network_http. Added 'Synchron' template
to hide usage of mutex to provide synchronised accesses to a variable.


git-svn-id: svn+ssh://svn.code.sf.net/p/supertuxkart/code/main/trunk@7177 178a84e3-b1eb-0310-8ba1-8eac791a3b58
This commit is contained in:
hikerstk 2010-12-28 22:48:44 +00:00
parent 1c57310b41
commit ce986d623d
15 changed files with 546 additions and 331 deletions

View File

@ -28,94 +28,96 @@
#include <string.h>
#include <vector>
#include "irrXML.h"
#include "addons/network_http.hpp"
#include "addons/zip.hpp"
#include "io/file_manager.hpp"
#include "io/xml_node.hpp"
#include "karts/kart_properties_manager.hpp"
#include "states_screens/kart_selection.hpp"
#include "tracks/track_manager.hpp"
using namespace irr; /* irrXML which is used to read (not write) xml file,
is located in the namespace irr::io.*/
using namespace io;
AddonsManager* addons_manager = 0;
// ----------------------------------------------------------------------------
AddonsManager::AddonsManager()
// ----------------------------------------------------------------------------
/** 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 in network_http (once network_http is setup).
*/
AddonsManager::AddonsManager() : m_state(STATE_INIT)
{
m_index = -1;
int download_state = 0;
m_download_state = download_state;
pthread_mutex_init(&m_str_mutex, NULL);
// FIXME: It is _very_ dirty to save the list as a locale file since we have a
// function to load it directly in a string.
const bool success = download("list");
if (!success)
{
fprintf(stderr, "Downloading 'list' failed\n");
}
std::string xml_file = file_manager->getConfigDir() + "/" + "list";
std::cout << "[Addons] Using file '" << xml_file << "'\n";
IrrXMLReader* xml = createIrrXMLReader(xml_file.c_str());
// strings for storing the data we want to get out of the file
std::string attribute_name;
// parse the file until end reached
while(xml && xml->read())
{
/*only if it is a node*/
if(xml->getNodeType() == EXN_ELEMENT)
{
if (!strcmp("kart", xml->getNodeName()) || !strcmp("track", xml->getNodeName()))
{
addons_prop addons;
//the unsigned is to remove the compiler warnings, maybe it is a bad idea ?
for(unsigned int i = 0; i < xml->getAttributeCount(); i++)
{
attribute_name = xml->getAttributeName(i);
if(attribute_name == "name")
{
addons.name = xml->getAttributeValue("name");
}
if(attribute_name == "version")
{
addons.version = atoi(xml->getAttributeValue("version"));
}
if(attribute_name == "file")
{
addons.file = xml->getAttributeValue("file");
}
if(attribute_name == "description")
{
addons.description = xml->getAttributeValue("description");
}
if(attribute_name == "icon")
{
addons.icon = xml->getAttributeValue("icon");
}
if(attribute_name == "id")
{
addons.id = xml->getAttributeValue("id");
}
}
addons.type = xml->getNodeName();
addons.installed = false;
m_addons_list.push_back(addons);
}
}
}
delete xml;
m_file_installed = file_manager->getConfigDir()
+ "/" + "addons_installed.xml";
getInstalledAddons();
}
} // AddonsManager
// ----------------------------------------------------------------------------
/** This initialises the online portion of the addons manager. It downloads
* the list of available addons. This is called by network_http before it
* goes into command-receiving mode, so we can't use any asynchronous calls
* here (though this is being called from a separate thread anyway, so the
* main GUI is not blocked). This function will update the state variable
*
*/
void AddonsManager::initOnline()
{
if(UserConfigParams::m_verbosity>=3)
printf("[addons] Init online addons manager\n");
int download_state = 0;
m_download_state = download_state;
// FIXME: It is _very_ dirty to save the list as a locale file
// since we have a function to load it directly in a string.
if(UserConfigParams::m_verbosity>=3)
printf("[addons] Addons manager downloading list\n");
if(!network_http->downloadFileSynchron("list"))
{
m_state.set(STATE_ERROR);
return;
}
if(UserConfigParams::m_verbosity>=3)
printf("[addons] Addons manager list downloaded\n");
std::string xml_file = file_manager->getAddonsFile("list");
const XMLNode *xml = new XMLNode(xml_file);
for(unsigned int i=0; i<xml->getNumNodes(); i++)
{
const XMLNode *node = xml->getNode(i);
if(node->getName()=="track")
{
AddonsProp addons(*node);
m_addons_list.push_back(addons);
}
else if(node->getName()=="kart")
{
AddonsProp addons(*node);
m_addons_list.push_back(addons);
}
else
{
fprintf(stderr,
"Found invalid node '%s' while downloading addons.\n",
node->getName().c_str());
fprintf(stderr, "Ignored.\n");
}
} // for i<xml->getNumNodes
delete xml;
m_state.set(STATE_READY);
} // initOnline
// ----------------------------------------------------------------------------
/** Returns true if the list of online addons has been downloaded. This is
* used to grey out the 'addons' entry till a network connections could be
* established.
*/
bool AddonsManager::onlineReady()
{
return m_state.get()==STATE_READY;
} // onlineReady
// ----------------------------------------------------------------------------
void AddonsManager::resetIndex()
{
@ -131,7 +133,7 @@ void AddonsManager::getInstalledAddons()
std::cout << "[Addons] Loading an xml file for installed addons: ";
std::cout << m_file_installed << std::endl;
IrrXMLReader* xml = createIrrXMLReader(m_file_installed.c_str());
io::IrrXMLReader* xml = io::createIrrXMLReader(m_file_installed.c_str());
// parse the file until end reached
@ -142,12 +144,14 @@ void AddonsManager::getInstalledAddons()
int version = 0;
switch(xml->getNodeType())
{
case EXN_ELEMENT:
case io::EXN_ELEMENT:
{
if (!strcmp("kart", xml->getNodeName()) || !strcmp("track", xml->getNodeName()))
if (!strcmp("kart", xml->getNodeName()) ||
!strcmp("track", xml->getNodeName()) )
{
std::cout << xml->getAttributeCount() << std::endl;
//the unsigned is to remove the compiler warnings, maybe it is a bad idea ?
//the unsigned is to remove the compiler warnings,
// maybe it is a bad idea ?
for(unsigned int i = 0; i < xml->getAttributeCount(); i++)
{
attribute_name = xml->getAttributeName(i);
@ -166,18 +170,19 @@ void AddonsManager::getInstalledAddons()
}
if(selectId(id))
{
m_addons_list[m_index].installed = true;
m_addons_list[m_index].installed_version = version;
std::cout << "[Addons] An addon is already installed: " << id << std::endl;
m_addons_list[m_index].m_installed = true;
m_addons_list[m_index].m_installed_version = version;
std::cout << "[Addons] An addon is already installed: "
<< id << std::endl;
}
else
{
addons_prop addons;
addons.type = xml->getNodeName();
addons.name = name;
addons.installed_version = version;
addons.version = version;
addons.installed = true;
AddonsProp addons;
addons.m_type = xml->getNodeName();
addons.m_name = name;
addons.m_installed_version = version;
addons.m_version = version;
addons.m_installed = true;
m_addons_list.push_back(addons);
}
}
@ -208,12 +213,12 @@ bool AddonsManager::nextType(std::string type)
{
while(next())
{
if(m_addons_list[m_index].type == type)
if(m_addons_list[m_index].m_type == type)
return true;
}
while(next())
{
if(m_addons_list[m_index].type == type)
if(m_addons_list[m_index].m_type == type)
return false;
}
return false;
@ -236,12 +241,12 @@ bool AddonsManager::previousType(std::string type)
{
while(previous())
{
if(m_addons_list[m_index].type == type)
if(m_addons_list[m_index].m_type == type)
return true;
}
while(previous())
{
if(m_addons_list[m_index].type == type)
if(m_addons_list[m_index].m_type == type)
return false;
}
return false;
@ -253,7 +258,7 @@ bool AddonsManager::select(std::string name)
//the unsigned is to remove the compiler warnings, maybe it is a bad idea ?
for(unsigned int i = 0; i < m_addons_list.size(); i++)
{
if(m_addons_list[i].name == name)
if(m_addons_list[i].m_name == name)
{
m_index = i;
return true;
@ -265,21 +270,20 @@ bool AddonsManager::select(std::string name)
// ----------------------------------------------------------------------------
bool AddonsManager::selectId(std::string id)
{
//the unsigned is to remove the compiler warnings, maybe it is a bad idea ?
for(unsigned int i = 0; i < m_addons_list.size(); i++)
{
if(m_addons_list[i].m_id == id)
{
if(m_addons_list[i].id == id)
{
m_index = i;
return true;
}
m_index = i;
return true;
}
}
return false;
} // selectId
// ----------------------------------------------------------------------------
/* FIXME : remove this function */
addons_prop AddonsManager::getAddons()
const AddonsManager::AddonsProp& AddonsManager::getAddons() const
{
return m_addons_list[m_index];
} // getAddons
@ -288,7 +292,7 @@ addons_prop AddonsManager::getAddons()
std::string AddonsManager::getVersionAsStr() const
{
std::ostringstream os;
os << m_addons_list[m_index].version;
os << m_addons_list[m_index].m_version;
return os.str();
} // getVersionAsStr
@ -296,25 +300,25 @@ std::string AddonsManager::getVersionAsStr() const
std::string AddonsManager::getIdAsStr() const
{
std::ostringstream os;
os << m_addons_list[m_index].id;
os << m_addons_list[m_index].m_id;
return os.str();
} // getIdAsStr
// ----------------------------------------------------------------------------
int AddonsManager::getInstalledVersion() const
{
if(m_addons_list[m_index].installed)
return m_addons_list[m_index].installed_version;
if(m_addons_list[m_index].m_installed)
return m_addons_list[m_index].m_installed_version;
return 0;
} // getInstalledVersion
// ----------------------------------------------------------------------------
std::string AddonsManager::getInstalledVersionAsStr() const
{
if(m_addons_list[m_index].installed)
if(m_addons_list[m_index].m_installed)
{
std::ostringstream os;
os << m_addons_list[m_index].installed_version;
os << m_addons_list[m_index].m_installed_version;
return os.str();
}
return "";
@ -325,14 +329,12 @@ void AddonsManager::install()
{
//download of the addons file
pthread_mutex_lock(&m_str_mutex);
m_str_state = "Downloading...";
pthread_mutex_unlock(&m_str_mutex);
std::string file = "file/" + m_addons_list[m_index].file;
bool success = download(file,
m_addons_list[m_index].name, &m_download_state);
std::string file = "file/" + m_addons_list[m_index].m_file;
network_http->downloadFileAsynchron(file, m_addons_list[m_index].m_name);
//FIXME , &m_download_state);
bool success=true;
if (!success)
{
// TODO: show a message in the interface
@ -340,34 +342,32 @@ void AddonsManager::install()
return;
}
file_manager->checkAndCreateDirForAddons(m_addons_list[m_index].name,
m_addons_list[m_index].type + "s/");
file_manager->checkAndCreateDirForAddons(m_addons_list[m_index].m_name,
m_addons_list[m_index].m_type + "s/");
//extract the zip in the addons folder called like the addons name
std::string dest_file =file_manager->getAddonsDir() + "/" + "data" + "/" +
m_addons_list[m_index].type + "s/" +
m_addons_list[m_index].name + "/" ;
std::string from = file_manager->getConfigDir() + "/" + m_addons_list[m_index].name;
m_addons_list[m_index].m_type + "s/" +
m_addons_list[m_index].m_name + "/" ;
std::string from = file_manager->getConfigDir() + "/"
+ m_addons_list[m_index].m_name;
std::string to = dest_file;
pthread_mutex_lock(&m_str_mutex);
m_str_state = "Unzip the addons...";
pthread_mutex_unlock(&m_str_mutex);
success = extract_zip(from, to);
if (!success)
{
// TODO: show a message in the interface
std::cerr << "[Addons] Failed to unzip '" << from << "' to '" << to << "'\n";
std::cerr << "[Addons] Failed to unzip '" << from << "' to '"
<< to << "'\n";
return;
}
m_addons_list[m_index].installed = true;
m_addons_list[m_index].installed_version = m_addons_list[m_index].version;
m_addons_list[m_index].m_installed = true;
m_addons_list[m_index].m_installed_version = m_addons_list[m_index].m_version;
pthread_mutex_lock(&m_str_mutex);
m_str_state = "Reloading kart list...";
pthread_mutex_unlock(&m_str_mutex);
saveInstalled();
} // install
@ -385,15 +385,15 @@ void AddonsManager::saveInstalled()
for(unsigned int i = 0; i < m_addons_list.size(); i++)
{
if(m_addons_list[i].installed)
if(m_addons_list[i].m_installed)
{
std::ostringstream os;
os << m_addons_list[i].installed_version;
os << m_addons_list[i].m_installed_version;
//transform the version (int) in string
xml_installed << "<"+ m_addons_list[i].type +" name=\"" +
m_addons_list[i].name + "\" id=\"" +
m_addons_list[i].id + "\"";
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;
}
}
@ -406,13 +406,14 @@ void AddonsManager::saveInstalled()
// ----------------------------------------------------------------------------
void AddonsManager::uninstall()
{
std::cout << "[Addons] Uninstalling <" << m_addons_list[m_index].name << ">\n";
std::cout << "[Addons] Uninstalling <"
<< m_addons_list[m_index].m_name << ">\n";
m_addons_list[m_index].installed = false;
m_addons_list[m_index].m_installed = false;
//write the xml file with the informations about installed karts
std::string dest_file = file_manager->getAddonsDir() + "/" + "data" + "/" +
m_addons_list[m_index].type + "s/" +
m_addons_list[m_index].name + "/";
m_addons_list[m_index].m_type + "s/" +
m_addons_list[m_index].m_name + "/";
//remove the addons directory
file_manager->removeDirectory(dest_file.c_str());
@ -423,20 +424,15 @@ void AddonsManager::uninstall()
// ----------------------------------------------------------------------------
int AddonsManager::getDownloadState()
{
pthread_mutex_lock(&download_mutex);
int value = m_download_state;
pthread_mutex_unlock(&download_mutex);
return value;
}
// ----------------------------------------------------------------------------
std::string AddonsManager::getDownloadStateAsStr() const
const std::string& AddonsManager::getDownloadStateAsStr() const
{
pthread_mutex_lock(&m_str_mutex);
std::string value = m_str_state;
pthread_mutex_unlock(&m_str_mutex);
return value;
return m_str_state;
} // getDownloadStateAsStr
// ----------------------------------------------------------------------------

View File

@ -22,43 +22,70 @@
#include <string>
#include <map>
#include <pthread.h>
#include <vector>
struct addons_prop
{
std::string name;
int version;
int installed_version;
std::string description;
std::string icon;
std::string file;
std::string id;
bool installed;
std::string type;
};
#include "io/xml_node.hpp"
#include "utils/synchronised.hpp"
class AddonsManager
{
class AddonsProp
{
public:
std::string m_name;
int m_version;
int m_installed_version;
std::string m_description;
std::string m_icon;
std::string m_file;
std::string m_id;
bool m_installed;
std::string m_type;
AddonsProp() {};
AddonsProp(const XMLNode &xml)
{
m_installed = false;
m_installed_version = 0;
m_type = xml.getName();
m_name = ""; xml.get("name", &m_name );
m_version = 0 ; xml.get("version", &m_version );
m_file = ""; xml.get("file", &m_file );
m_description = ""; xml.get("description", &m_description);
m_icon = ""; xml.get("icon", &m_icon );
m_id = ""; xml.get("id", &m_id );
}; // AddonsProp(const XML&)
};
private:
std::vector<addons_prop> m_addons_list;
std::vector<AddonsProp> m_addons_list;
int m_index;
std::string m_file_installed;
void saveInstalled();
void getInstalledAddons();
std::string m_type;
int m_download_state;
mutable pthread_mutex_t m_str_mutex;
std::string m_str_state;
public:
AddonsManager();
std::string m_str_state;
/** Which state the addons manager is:
* INIT: Waiting to download the list of addons.
* READY: List is downloaded, manager is ready.
* ERROR: Error downloading the list, no addons available. */
enum STATE_TYPE {STATE_INIT, STATE_READY, STATE_ERROR};
// Synchronise the state between threads (e.g. GUI and update thread)
Synchronised<STATE_TYPE> m_state;
public:
AddonsManager();
void initOnline();
bool onlineReady();
/** Select the next addons in the addons list. */
bool next();
/** Select the next addons in the addons list. */
bool previous();
/** Get all the selected addon parameters. */
addons_prop getAddons();
const AddonsProp &getAddons() const;
/** Select an addon with it name. */
bool select(std::string);
@ -68,14 +95,14 @@ class AddonsManager
/** Get the name of the selected addon. */
const std::string &getName() const
{ return m_addons_list[m_index].name; };
{ return m_addons_list[m_index].m_name; };
/** Get the version of the selected addon. */
int getVersion() const { return m_addons_list[m_index].version; };
int getVersion() const { return m_addons_list[m_index].m_version; };
/** Get the path of the addon icon. */
const std::string &getIcon() const
{ return m_addons_list[m_index].icon; };
{ return m_addons_list[m_index].m_icon; };
/** Get the version of the selected addon as a string. */
std::string getVersionAsStr() const;
@ -92,10 +119,10 @@ class AddonsManager
/** Get the description of the selected addons. */
const std::string &getDescription() const
{ return m_addons_list[m_index].description; };
{ return m_addons_list[m_index].m_description; };
const std::string &getType() const
{ return m_addons_list[m_index].type; };
{ return m_addons_list[m_index].m_type; };
/** Install or upgrade the selected addon. */
void install();
@ -106,15 +133,15 @@ class AddonsManager
void resetIndex();
/** Get the state of the addon: if it is installed or not.*/
bool isInstalledAsBool() const
{ return m_addons_list[m_index].installed; };
bool isInstalled() const
{ return m_addons_list[m_index].m_installed; };
bool nextType(std::string type);
bool previousType(std::string type);
int getDownloadState();
/** Get the install state (if it is the download, unzip...)*/
std::string getDownloadStateAsStr() const;
const std::string& getDownloadStateAsStr() const;
};
extern AddonsManager *addons_manager;

View File

@ -42,22 +42,21 @@
# include <unistd.h>
#endif
pthread_mutex_t download_mutex;
NetworkHttp * network_http = 0;
NetworkHttp *network_http;
// ----------------------------------------------------------------------------
/** Create a thread that handles all network functions independent of the
* main program.
* main program. NetworkHttp supports only a single thread (i.e. it's not
* possible to download two addons at the same time), which makes handling
* and synchronisation a lot easier (otherwise all objects using this object
* would need an additional handle to get the right data back).
* This separate thread is running in NetworkHttp::mainLoop, and is being
* waken up if a command is issued (e.g. using downloadFileAsynchronous).
*/
NetworkHttp::NetworkHttp()
NetworkHttp::NetworkHttp() : m_news(""), m_progress(-1.0f)
{
pthread_mutex_init(&m_mutex_news, NULL);
m_news_message = "";
pthread_mutex_init(&m_mutex_command, NULL);
pthread_cond_init(&m_cond_command, NULL);
m_command = HC_SLEEP;
m_abort = false;
pthread_attr_t attr;
@ -76,16 +75,21 @@ NetworkHttp::NetworkHttp()
void *NetworkHttp::mainLoop(void *obj)
{
NetworkHttp *me=(NetworkHttp*)obj;
// FIXME: this needs better error handling!
me->checkNewServer();
me->updateNews();
// Allow this thread to be cancelled anytime
// FIXME: this mechanism will later not be necessary anymore!
// Initialise the online portion of the addons manager.
addons_manager->initOnline();
pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL);
pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, NULL);
// Wait in the main loop till a command is received
// (atm only QUIT is used).
pthread_mutex_lock(&me->m_mutex_command);
me->m_command = HC_SLEEP;
while(1)
{
pthread_cond_wait(&me->m_cond_command, &me->m_mutex_command);
@ -99,8 +103,12 @@ void *NetworkHttp::mainLoop(void *obj)
case HC_NEWS:
me->updateNews();
break;
case HC_DOWNLOAD_FILE:
me->downloadFileInternal();
} // switch(m_command)
me->m_command = HC_SLEEP;
} // while !m_abort
pthread_mutex_unlock(&me->m_mutex_command);
return NULL;
} // mainLoop
@ -121,7 +129,6 @@ NetworkHttp::~NetworkHttp()
void *result;
pthread_join(m_thread_id, &result);
pthread_mutex_destroy(&m_mutex_news);
pthread_mutex_destroy(&m_mutex_command);
pthread_cond_destroy(&m_cond_command);
} // ~NetworkHttp
@ -132,7 +139,7 @@ NetworkHttp::~NetworkHttp()
*/
void NetworkHttp::checkNewServer()
{
std::string newserver = downloadToStr("redirect");
std::string newserver = downloadToStrInternal("redirect");
if (newserver != "")
{
newserver.replace(newserver.find("\n"), 1, "");
@ -158,14 +165,10 @@ void NetworkHttp::checkNewServer()
*/
void NetworkHttp::updateNews()
{
const std::string tmp_str = downloadToStr("news");
// Only lock the actual assignment, not the downloading!
const std::string tmp_str = downloadToStrInternal("news");
m_news.set(tmp_str);
pthread_mutex_lock(&m_mutex_news);
{
// Only lock the actual assignment, not the downloading!
m_news_message = tmp_str;
}
pthread_mutex_unlock(&m_mutex_news);
} // updateNews
// ----------------------------------------------------------------------------
@ -174,30 +177,28 @@ void NetworkHttp::updateNews()
*/
const std::string NetworkHttp::getNewsMessage() const
{
pthread_mutex_lock(&m_mutex_news);
const std::string tmp_str = m_news_message;
pthread_mutex_unlock(&m_mutex_news);
return tmp_str;
return m_news.get();
} // getNewsMessage
// ----------------------------------------------------------------------------
size_t NetworkHttp::writeStr(char ptr [], size_t size, size_t nb_char, std::string * stream)
size_t NetworkHttp::writeStr(char ptr [], size_t size, size_t nb_char,
std::string * stream)
{
static std::string str = std::string(ptr);
*stream = str;
*stream = std::string(ptr);
//needed, otherwise, the download failed
return nb_char;
}
} // writeStr
// ------------------------------------------------------------------------------------------------------
// ----------------------------------------------------------------------------
std::string NetworkHttp::downloadToStr(std::string url)
std::string NetworkHttp::downloadToStrInternal(std::string url)
{
CURL *session = curl_easy_init();
std::string full_url = (std::string)UserConfigParams::m_server_addons + "/" + url;
std::string full_url = (std::string)UserConfigParams::m_server_addons
+ "/" + url;
curl_easy_setopt(session, CURLOPT_URL, full_url.c_str());
std::string fout;
@ -213,39 +214,33 @@ std::string NetworkHttp::downloadToStr(std::string url)
if (success == 0) return fout;
else return "";
}
} // downloadToStrInternal
// ----------------------------------------------------------------------------
/** Download a file. The file name isn't absolute, the server in the config
* will be added to file.
* \param progress_data is used to have the state of the download (in %)
*/
bool download(std::string file, const std::string &save, int * progress_data)
bool NetworkHttp::downloadFileInternal()
{
m_progress.set(0.0f);
CURL *session = curl_easy_init();
std::string full_url = (std::string)UserConfigParams::m_server_addons + "/" + file;
std::string full_url = (std::string)UserConfigParams::m_server_addons
+ "/" + m_file;
curl_easy_setopt(session, CURLOPT_URL, full_url.c_str());
FILE * fout;
if(save != "")
fout = fopen((file_manager->getAddonsDir() + "/" + save).c_str(), "w");
else
fout = fopen((file_manager->getAddonsDir() + "/" + file).c_str(), "w");
FILE * fout = fopen(file_manager->getAddonsFile(m_save_filename).c_str(),
"w");
//from and out
curl_easy_setopt(session, CURLOPT_WRITEDATA, fout);
curl_easy_setopt(session, CURLOPT_WRITEDATA, fout );
curl_easy_setopt(session, CURLOPT_WRITEFUNCTION, fwrite);
//init the mutex for the progress function
pthread_mutex_init(&download_mutex, NULL);
curl_easy_setopt(session, CURLOPT_PROGRESSFUNCTION, &progressDownload);
//needed, else, the progress function doesn't work
curl_easy_setopt(session, CURLOPT_PROGRESSFUNCTION,
&NetworkHttp::progressDownload);
// Necessary, oyherwise the progress function doesn't work
curl_easy_setopt(session, CURLOPT_NOPROGRESS, 0);
//to update the progress bar
curl_easy_setopt(session, CURLOPT_PROGRESSDATA, progress_data);
int success = curl_easy_perform(session);
//close the file where we downloaded the content
@ -253,30 +248,79 @@ bool download(std::string file, const std::string &save, int * progress_data)
//stop curl
curl_easy_cleanup(session);
return (success == 0);
}
// ------------------------------------------------------------------------------------------------------
m_progress.set( (success==CURLE_OK) ? 1.0f : -1.0f );
return success==CURLE_OK;
} // downloadFileInternal
//FIXME : this way is a bit ugly but the simplest at the moment
int time_last_print = 0;
int progressDownload (void *clientp, float dltotal, float dlnow,
float ultotal, float ulnow)
// ----------------------------------------------------------------------------
/** External interface to download a file synchronously, i.e. it will only
* return once the download is complete.
* \param file The file from the server to download.
* \param save The name to save the downloaded file under. Defaults to
* the name given in file.
*/
bool NetworkHttp::downloadFileSynchron(const std::string &file,
const std::string &save)
{
int progress = (int)(dlnow/dltotal*100);
if(isnan(dlnow/dltotal*100))
progress = 0;
pthread_mutex_lock(&download_mutex);
if(clientp != NULL)
m_file = file;
m_save_filename = (save!="") ? save : file;
return downloadFileInternal();
} // downloadFileSynchron
// ----------------------------------------------------------------------------
/** External interface to download a file asynchronously. This will wake up
* the thread and schedule it to download the file. The calling program has
* to poll using getProgress() to find out if the download has finished.
* \param file The file from the server to download.
* \param save The name to save the downloaded file under. Defaults to
* the name given in file.
*/
void NetworkHttp::downloadFileAsynchron(const std::string &file,
const std::string &save)
{
m_progress.set(0.0f);
m_file = file;
m_save_filename = (save!="") ? save : file;
// Wake up the network http thread
pthread_mutex_lock(&m_mutex_command);
{
int * progress_data = (int*)clientp;
*progress_data = progress;
m_command = HC_DOWNLOAD_FILE;
pthread_cond_signal(&m_cond_command);
}
pthread_mutex_unlock(&download_mutex);
pthread_mutex_unlock(&m_mutex_command);
} // downloadFileAsynchron
// ----------------------------------------------------------------------------
/** Callback function from curl: inform about progress.
* \param clientp
* \param download_total Total size of data to download.
* \param download_now How much has been downloaded so far.
* \param upload_total Total amount of upload.
* \param upload_now How muc has been uploaded so far.
*/
int NetworkHttp::progressDownload(void *clientp,
float download_total, float download_now,
float upload_total, float upload_now)
{
float f;
if(download_now < download_total)
{
f = download_now / download_total;
// In case of floating point rouding errors make sure that
// 1.0 is only reached when downloadFileInternal is finished
if (f>=1.0f) f=0.99f;
}
else
f=1.0f;
network_http->m_progress.set(f);
static int time_last_print=0;
if(time_last_print > 10)
{
std::cout << "Download progress: " << progress << "%" << std::endl;
std::cout << "Download progress: " << f << "%" << std::endl;
time_last_print = 0;
}
else
@ -284,5 +328,15 @@ int progressDownload (void *clientp, float dltotal, float dlnow,
time_last_print += 1;
}
return 0;
}
} // progressDownload
// ----------------------------------------------------------------------------
/** Returns the progress of a download that has been started.
* \return <0 in case of an error, between 0 and smaller than 1 while the
* download is in progress, and 1.0f if the download has finished.
*/
float NetworkHttp::getProgress() const
{
return m_progress.get();
} // getProgress
#endif

View File

@ -23,33 +23,44 @@
#include <pthread.h>
#include <string>
#include "utils/synchronised.hpp"
class NetworkHttp
{
public:
/** List of 'http commands' for this object:
* HC_SLEEP: No command, sleep
* HC_INIT: Object is being initialised
* HC_DOWNLOAD_FILE : download a file
* HC_QUIT: Stop loop and terminate thread.
* HC_NEWS: Update the news
*/
enum HttpCommands {HC_SLEEP,
HC_QUIT,
HC_INIT,
HC_DOWNLOAD_FILE,
HC_NEWS } ;
private:
/** The news message from the server. This is guarded by m_mutex_news. */
std::string m_news_message;
/** A mutex for accessing m_news_message. Exclude this so that
* getter can be declared const. */
mutable pthread_mutex_t m_mutex_news;
Synchronised<std::string> m_news;
/** Which command to execute next. Access to this variable is guarded
* by m_mutex_command and m_cond_command. */
HttpCommands m_command;
HttpCommands m_command;
/** A mutex for accessing m_commands. */
pthread_mutex_t m_mutex_command;
/** A conditional variable to wake up the main loop. */
pthread_cond_t m_cond_command;
pthread_cond_t m_cond_command;
/** The file to download when a file download is triggered. */
std::string m_file;
/** The name and path under which to save the downloaded file. */
std::string m_save_filename;
/** Progress of a download in percent. It is guaranteed that
* this value only becomes 1.0f, if the download is completed.*/
Synchronised<float> m_progress;
/** Thread id of the thread running in this object. */
pthread_t m_thread_id;
@ -61,26 +72,27 @@ private:
void checkNewServer();
void updateNews();
std::string downloadToStrInternal(std::string url);
bool downloadFileInternal();
static int progressDownload(void *clientp, float dltotal, float dlnow,
float ultotal, float ulnow);
public:
NetworkHttp();
~NetworkHttp();
static size_t writeStr(char str [], size_t size, size_t nb_char,
std::string * stream);
std::string downloadToStr(std::string url);
void downloadFileAsynchron(const std::string &file,
const std::string &save = "");
bool downloadFileSynchron(const std::string &file,
const std::string &save = "");
const std::string
getNewsMessage() const;
float getProgress() const;
};
extern NetworkHttp *network_http;
bool download(std::string file, const std::string &save = "",
int *progress_data = 0);
int progressDownload (void *clientp, float dltotal, float dlnow,
float ultotal, float ulnow);
extern pthread_mutex_t download_mutex;
#endif
#endif

View File

@ -862,6 +862,10 @@
RelativePath="..\..\io\xml_node.cpp"
>
</File>
<File
RelativePath="..\..\io\xml_writer.cpp"
>
</File>
</Filter>
<Filter
Name="input"
@ -1360,6 +1364,10 @@
RelativePath="..\..\utils\string_utils.hpp"
>
</File>
<File
RelativePath="..\..\utils\synchronised.hpp"
>
</File>
<File
RelativePath="..\..\utils\translation.hpp"
>
@ -1764,6 +1772,10 @@
RelativePath="..\..\io\xml_node.hpp"
>
</File>
<File
RelativePath="..\..\io\xml_writer.hpp"
>
</File>
</Filter>
<Filter
Name="input"

View File

@ -568,13 +568,21 @@ void FileManager::checkAndCreateAddonsDir()
} // checkAndCreateAddonsDir
//-----------------------------------------------------------------------------
std::string FileManager::getAddonsDir() const
/** Returns the directory for addon files. */
const std::string &FileManager::getAddonsDir() const
{
return m_addons_dir;
} // getADdonsDir
/* see l450: to avoid the compilation of unused methods. */
#endif
//-----------------------------------------------------------------------------
/** Returns a filename in the addons directory.
* \param name Name of the file.
*/
std::string FileManager::getAddonsFile(const std::string &name)
{
return getAddonsDir()+"/"+name;
}
//-----------------------------------------------------------------------------
std::string FileManager::getConfigDir() const
{
@ -666,10 +674,10 @@ void FileManager::listFiles(std::set<std::string>& result, const std::string& di
void FileManager::checkAndCreateDirForAddons(std::string addons_name,
std::string addons_type)
{
bool success = checkAndCreateDirectory(getAddonsDir() + "/data/" + addons_type);
bool success = checkAndCreateDirectory(getAddonsDir() + addons_type);
if(!success)
std::cout << "There is a problem with the addons dir." << std::endl;
checkAndCreateDirectory(getAddonsDir() + "/data/" + addons_type + addons_name);
checkAndCreateDirectory(getAddonsDir() + addons_type + addons_name);
}
bool FileManager::removeDirectory(char const *name)

View File

@ -42,46 +42,48 @@ class FileManager : public NoCopy
{
private:
/** Handle to irrlicht's file systems. */
io::IFileSystem *m_file_system;
io::IFileSystem *m_file_system;
/** Pointer to the irrlicht device. This is necessary before reInit is
* called to store the NULL device initially created. See Constructor
* for details. */
IrrlichtDevice *m_device;
IrrlichtDevice *m_device;
bool m_is_full_path;
bool m_is_full_path;
/** Directory where user config files are stored. */
std::string m_config_dir;
std::string m_config_dir;
/** Directory where addons are stored. */
std::string m_addons_dir;
std::string m_addons_dir;
/** Root data directory. */
std::string m_root_dir;
std::vector<std::string> m_texture_search_path,
m_model_search_path,
m_music_search_path;
bool findFile (std::string& full_path,
const std::string& fname,
const std::vector<std::string>& search_path)
const;
void makePath (std::string& path, const std::string& dir,
const std::string& fname) const;
bool checkAndCreateDirectory(const std::string &path);
io::path createAbsoluteFilename(const std::string &f);
void checkAndCreateConfigDir();
std::string m_root_dir;
std::vector<std::string>
m_texture_search_path,
m_model_search_path,
m_music_search_path;
bool findFile(std::string& full_path,
const std::string& fname,
const std::vector<std::string>& search_path)
const;
void makePath(std::string& path, const std::string& dir,
const std::string& fname) const;
bool checkAndCreateDirectory(const std::string &path);
io::path createAbsoluteFilename(const std::string &f);
void checkAndCreateConfigDir();
#ifdef ADDONS_MANAGER
void checkAndCreateAddonsDir();
void checkAndCreateAddonsDir();
#endif
public:
FileManager(char *argv[]);
~FileManager();
void setDevice(IrrlichtDevice *device);
void dropFileSystem();
io::IXMLReader *createXMLReader(const std::string &filename);
XMLNode *createXMLTree(const std::string &filename);
FileManager(char *argv[]);
~FileManager();
void setDevice(IrrlichtDevice *device);
void dropFileSystem();
io::IXMLReader *createXMLReader(const std::string &filename);
XMLNode *createXMLTree(const std::string &filename);
std::string getConfigDir () const;
bool checkAndCreateDirectoryP(const std::string &path);
std::string getConfigDir() const;
bool checkAndCreateDirectoryP(const std::string &path);
#ifdef ADDONS_MANAGER
std::string getAddonsDir () const;
const std::string &getAddonsDir() const;
std::string getAddonsFile(const std::string &name);
void checkAndCreateDirForAddons(std::string addons_name,
std::string addons_type);
bool removeDirectory(char const *name);

View File

@ -214,7 +214,7 @@ int handleCmdLinePreliminary(int argc, char **argv)
exit(EXIT_FAILURE);
}
}
else if( !strcmp(argv[i], "--version") || !strcmp(argv[i], "-v") )
else if( !strcmp(argv[i], "--version") || !strcmp(argv[i], "-V") )
{
printf("==============================\n");
#ifdef VERSION
@ -561,6 +561,13 @@ void initRest()
video::IVideoDriver* driver = device->getVideoDriver();
GUIEngine::init(device, driver, StateManager::get());
#ifdef ADDONS_MANAGER
// This only initialises the non-network part of the addons manager. The
// online section of the addons manager will be initialised from a
// separate thread running in network http.
addons_manager = new AddonsManager();
network_http = new NetworkHttp();
#endif
music_manager = new MusicManager();
sfx_manager = new SFXManager();
// The order here can be important, e.g. KartPropertiesManager needs
@ -576,16 +583,16 @@ void initRest()
highscore_manager = new HighscoreManager ();
network_manager = new NetworkManager ();
#ifdef ADDONS_MANAGER
network_http = new NetworkHttp ();
addons_manager = new AddonsManager ();
KartPropertiesManager::addKartSearchDir(file_manager->getAddonsDir() + "/data/karts/");
track_manager->addTrackSearchDir(file_manager->getAddonsDir() + "/data/tracks/");
KartPropertiesManager::addKartSearchDir(
file_manager->getAddonsFile("data/karts/") );
track_manager->addTrackSearchDir(
file_manager->getAddonsFile("/data/tracks/"));
#endif
track_manager->loadTrackList();
music_manager->addMusicToTracks();
GUIEngine::addLoadingIcon( irr_driver->getTexture(file_manager->getTextureFile("notes.png")) );
GUIEngine::addLoadingIcon(
irr_driver->getTexture(file_manager->getTextureFile("notes.png")) );
grand_prix_manager = new GrandPrixManager ();
// Consistency check for challenges, and enable all challenges

View File

@ -72,13 +72,13 @@ void AddonsScreen::loadList()
while(addons_manager->nextType(m_type))
{
std::cout << addons_manager->getName() << std::endl;
if(addons_manager->isInstalledAsBool() &&
if(addons_manager->isInstalled() &&
addons_manager->getInstalledVersion() < addons_manager->getVersion())
{
w_list->addItem(addons_manager->getIdAsStr().c_str(),
addons_manager->getName().c_str(), 2 /* icon installed */);
}
else if(addons_manager->isInstalledAsBool())
else if(addons_manager->isInstalled())
{
w_list->addItem(addons_manager->getIdAsStr().c_str(),
addons_manager->getName().c_str(), 0 /* icon installed */);

View File

@ -66,7 +66,8 @@ void AddonsUpdateScreen::eventCallback(GUIEngine::Widget* widget,
}
else if (name == "category")
{
std::string selection = ((GUIEngine::RibbonWidget*)widget)->getSelectionIDString(PLAYER_ID_GAME_MASTER).c_str();
std::string selection =
((GUIEngine::RibbonWidget*)widget)->getSelectionIDString(PLAYER_ID_GAME_MASTER);
if (selection == "tab_track")
{
@ -96,12 +97,12 @@ void AddonsUpdateScreen::init()
//w_list->addItem("kart", _("Karts:"), -1 /* no icon */);
while(addons_manager->next())
{
if(addons_manager->isInstalledAsBool() &&
if(addons_manager->isInstalled() &&
addons_manager->getInstalledVersion() < addons_manager->getVersion())
{
std::cout << addons_manager->getName() << std::endl;
w_list->addItem(addons_manager->getIdAsStr().c_str(),
addons_manager->getName().c_str(), 0);
w_list->addItem(addons_manager->getIdAsStr(),
addons_manager->getName().c_str(), 0);
}
}
}

View File

@ -30,7 +30,8 @@ namespace GUIEngine { class Widget; }
* \brief Help screen, part 1
* \ingroup states_screens
*/
class AddonsUpdateScreen : public GUIEngine::Screen, public GUIEngine::ScreenSingleton<AddonsUpdateScreen>
class AddonsUpdateScreen : public GUIEngine::Screen,
public GUIEngine::ScreenSingleton<AddonsUpdateScreen>
{
friend class GUIEngine::ScreenSingleton<AddonsUpdateScreen>;
AddonsUpdateScreen();
@ -45,7 +46,8 @@ public:
virtual void loadedFromFile();
/** \brief implement callback from parent class GUIEngine::Screen */
virtual void eventCallback(GUIEngine::Widget* widget, const std::string& name, const int playerID);
virtual void eventCallback(GUIEngine::Widget* widget,
const std::string& name, const int playerID);
/** \brief implement callback from parent class GUIEngine::Screen */
virtual void init();

View File

@ -37,25 +37,22 @@ using namespace irr::gui;
// ------------------------------------------------------------------------------------------------------
AddonsLoading::AddonsLoading(const float w, const float h) :
ModalDialog(w, h)
AddonsLoading::AddonsLoading(const float w, const float h)
: ModalDialog(w, h)
{
loadFromFile("addons_view_dialog.stkgui");
m_can_install = false;
m_can_load_icon = false;
m_can_install = false;
m_can_load_icon = false;
m_percent_update = false;
pthread_mutex_init(&m_mutex_can_install, NULL);
/*Init the icon here to be able to load a single image*/
icon = getWidget<IconButtonWidget>("icon");
name = getWidget<LabelWidget>("name");
description = getWidget<LabelWidget>("description");
m_icon = getWidget<IconButtonWidget>("icon");
m_name = getWidget<LabelWidget>("name");
m_description = getWidget<LabelWidget>("description");
m_version = getWidget<LabelWidget>("version");
version = getWidget<LabelWidget>("version");
if(addons_manager->isInstalledAsBool())
if(addons_manager->isInstalled())
{
if(addons_manager->getInstalledVersion() < addons_manager->getVersion())
getWidget<ButtonWidget>("install")->setLabel(_("Update"));
@ -67,11 +64,11 @@ AddonsLoading::AddonsLoading(const float w, const float h) :
}
void AddonsLoading::loadInfo()
{
name->setText(StringUtils::insertValues(_("Name: %i"),
m_name->setText(StringUtils::insertValues(_("Name: %i"),
addons_manager->getName().c_str()));
description->setText(StringUtils::insertValues(_("Description: %i"),
m_description->setText(StringUtils::insertValues(_("Description: %i"),
addons_manager->getDescription().c_str()));
version->setText(StringUtils::insertValues(_("Version: %i"),
m_version->setText(StringUtils::insertValues(_("Version: %i"),
addons_manager->getVersionAsStr().c_str()));
pthread_t thread;
pthread_create(&thread, NULL, &AddonsLoading::downloadIcon, this);
@ -83,13 +80,14 @@ void * AddonsLoading::downloadIcon( void * pthis)
AddonsLoading * pt = (AddonsLoading*)pthis;
std::string iconPath = "icon/" + addons_manager->getIcon();
if (download(iconPath, addons_manager->getName() + ".png"))
network_http->downloadFileAsynchron(iconPath, addons_manager->getName() + ".png");
// FIXME if (network_http->downloadFile(iconPath, addons_manager->getName() + ".png"))
{
pthread_mutex_lock(&(pt->m_mutex_can_install));
pt->m_can_load_icon = true;
pthread_mutex_unlock(&(pt->m_mutex_can_install));
}
else
// else
{
fprintf(stderr, "[Addons] Download icon '%s' failed\n", iconPath.c_str());
}
@ -170,7 +168,7 @@ void AddonsLoading::onUpdate(float delta)
}
if(m_can_load_icon)
{
icon->setImage( (file_manager->getConfigDir() + "/"
m_icon->setImage( (file_manager->getConfigDir() + "/"
+ addons_manager->getName() + ".png").c_str(),
IconButtonWidget::ICON_PATH_TYPE_ABSOLUTE);
}
@ -192,7 +190,7 @@ void AddonsLoading::close()
void * AddonsLoading::startInstall(void* pthis)
{
AddonsLoading * obj = (AddonsLoading*)pthis;
if(!addons_manager->isInstalledAsBool() || addons_manager->needUpdate())
if(!addons_manager->isInstalled() || addons_manager->needUpdate())
{
addons_manager->install();
}

View File

@ -21,26 +21,27 @@
#ifndef HEADER_ADDONS_LOADING_HPP
#define HEADER_ADDONS_LOADING_HPP
#include "guiengine/modaldialog.hpp"
#include "addons/addons_manager.hpp"
#include "guiengine/widgets.hpp"
#include "guiengine/modaldialog.hpp"
#include <pthread.h>
class AddonsLoading : public GUIEngine::ModalDialog
{
//virtual void escapePressed() {};
private:
GUIEngine::LabelWidget * name;
GUIEngine::LabelWidget * description;
GUIEngine::LabelWidget * version;
GUIEngine::LabelWidget * author;
GUIEngine::LabelWidget * m_state;
GUIEngine::ProgressBarWidget * m_progress;
GUIEngine::ButtonWidget * m_back_button;
GUIEngine::ButtonWidget * install_button;
GUIEngine::IconButtonWidget * icon;
GUIEngine::IconButtonWidget * m_next;
GUIEngine::IconButtonWidget * m_previous;
GUIEngine::LabelWidget *m_name;
GUIEngine::LabelWidget *m_description;
GUIEngine::LabelWidget *m_version;
GUIEngine::LabelWidget *m_author;
GUIEngine::LabelWidget *m_state;
GUIEngine::ProgressBarWidget *m_progress;
GUIEngine::ButtonWidget *m_back_button;
GUIEngine::ButtonWidget *m_install_button;
GUIEngine::IconButtonWidget *m_icon;
GUIEngine::IconButtonWidget *m_next;
GUIEngine::IconButtonWidget *m_previous;
/**
* This function is called when the user click on 'Install', 'Uninstall', or

View File

@ -26,12 +26,12 @@
#include "io/file_manager.hpp"
#include "karts/kart_properties_manager.hpp"
#include "main_loop.hpp"
#include "states_screens/addons_screen.hpp"
#include "states_screens/challenges.hpp"
#include "states_screens/credits.hpp"
#include "states_screens/kart_selection.hpp"
#include "states_screens/help_screen_1.hpp"
#include "states_screens/kart_selection.hpp"
#include "states_screens/options_screen_video.hpp"
#include "states_screens/addons_screen.hpp"
#include "states_screens/state_manager.hpp"
//FIXME : remove, temporary tutorial test
@ -90,13 +90,26 @@ void MainMenuScreen::init()
// To avoid this, we will clean the last used device, making
// the key bindings for the first player the default again.
input_manager->getDeviceList()->clearLatestUsedDevice();
if(!addons_manager->onlineReady())
{
IconButtonWidget* w = this->getWidget<IconButtonWidget>("addons");
w->setDeactivated();
}
}
#ifdef ADDONS_MANAGER
// ------------------------------------------------------------------------------------------------------
void MainMenuScreen::onUpdate(float delta, irr::video::IVideoDriver* driver)
{
IconButtonWidget* icon = this->getWidget<IconButtonWidget>("addons");
if(!addons_manager->onlineReady())
icon->setDeactivated();
else
icon->setActivated();
LabelWidget* w = this->getWidget<LabelWidget>("info_addons");
const std::string &news_text = network_http->getNewsMessage();
if(news_text == "")

View File

@ -0,0 +1,82 @@
// $Id$
//
// SuperTuxKart - a fun racing game with go-kart
// Copyright (C) 2010 Joerg Henrichs
//
// 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_SYNCHRONISED_HPP
#define HEADER_SYNCHRONISED_HPP
#include <pthread.h>
template<typename TYPE>
/** A variable that is automatically synchronised using pthreads mutex.
*/
class Synchronised
{
private:
/** The mutex to protect this variable with. */
mutable pthread_mutex_t m_mutex;
/** The actual data to be used. */
TYPE m_data;
public:
/** Initialise the data and the mutex. */
Synchronised(const TYPE &v)
{
m_data = v;
pthread_mutex_init(&m_mutex, NULL);
} // Synchronised
// ------------------------------------------------------------------------
/** Destroy this mutex.
*/
~Synchronised()
{
pthread_mutex_destroy(&m_mutex);
} // ~Synchronised
// ------------------------------------------------------------------------
/** Sets the value of this variable using a mutex.
* \param v Value to be set.
*/
void set(const TYPE &v)
{
pthread_mutex_lock(&m_mutex);
m_data = v;
pthread_mutex_unlock(&m_mutex);
} // set
// ------------------------------------------------------------------------
/** Returns a copy of this variable.
*/
TYPE get() const
{
TYPE v;
pthread_mutex_lock(&m_mutex);
v = m_data;
pthread_mutex_unlock(&m_mutex);
return v;
} // get
private:
// Make sure that no actual copying is taking place
// ------------------------------------------------------------------------
void operator=(const Synchronised<TYPE>& v) {}
};
#endif