/** This function checks if the information in the installed addons file is * consistent with what is actually available. This avoids e.g. that an * addon is installed, but not marked here (and therefore shows up as * not installed in the addons GUI), see bug #455. */ 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()) { printf("[addons] Marking '%s' as being installed.\n", 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()) { printf("[addons] Marking '%s' as being installed.\n", 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
/** Removes all files froma login. * \param addon The addon to be removed. * \return True if uninstallation was successful. */ bool AddonsManager::uninstall(const Addon &addon) { std::cout << "[addons] Uninstalling <" << core::stringc(addon.getName()).c_str() << ">\n"; // 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()); if(addon.getType()=="kart") { kart_properties_manager->removeKart(addon.getId()); } else if(addon.getType()=="track" || addon.getType()=="arena") { track_manager->removeTrack(addon.getId()); } } saveInstalled(); return !error; } // uninstall
/** Installs or updates (i.e. = install on top of an existing installation) an * addon. It checks for the directories and then unzips the file (which must * already have been downloaded). * \param addon Addon data for the addon to install. * \return true if installation was successful. */ bool AddonsManager::install(const Addon &addon) { bool success=true; const std::string &id = addon.getId(); file_manager->checkAndCreateDirForAddons(id, addon.getTypeDirectory()); //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(); 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] Zip file will not be removed.\n"; return false; } if(!file_manager->removeFile(from)) { std::cerr << "[addons] Problems removing temporary file '" << from << "'.\n"; } 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()); track_manager->loadTrack(addon.getDataDir()); } saveInstalled(); return true; } // install
/** Removes all files froma login. * \param addon The addon to be removed. * \return True if uninstallation was successful. */ bool AddonsManager::uninstall(const Addon &addon) { std::cout << "[addons] Uninstalling <" << core::stringc(addon.getName()).c_str() << ">\n"; // 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 = !file_manager->removeDirectory(addon.getDataDir()); if(addon.getType()=="kart") { kart_properties_manager->removeKart(addon.getId()); } else if(addon.getType()=="track" || addon.getType()=="arena") { track_manager->removeTrack(addon.getId()); } saveInstalled(); return !error; } // uninstall
/** This initialises the online portion of the addons manager. It uses the * downloaded 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 , so the * main GUI is not blocked anyway). This function will update the state * variable */ void AddonsManager::initOnline(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 network_http 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()) printf("[addons] Removing cached icon '%s'.\n", 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 = 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 { fprintf(stderr, "[addons] Found invalid node '%s' while downloading addons.\n", node->getName().c_str()); fprintf(stderr, "[addons] Ignored.\n"); } } // 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 = 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()) printf( "[addons] Removing '%s' which is not on the server anymore.\n", m_addons_list.getData()[i].getId().c_str() ); 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(); m_state.setAtomic(STATE_READY); if (UserConfigParams::m_internet_status == INetworkHttp::IPERM_ALLOWED) downloadIcons(); } // initOnline
/** Returns an addon with a given id. Raises an assertion if the id is not * found! * \param id The id to search for. */ const Addon* AddonsManager::getAddon(const std::string &id) const { int i = getAddonIndex(id); return (i<0) ? NULL : &(m_addons_list.getData()[i]); } // getAddon