Skip to content

Commit

Permalink
Incremental addon updates (client side)
Browse files Browse the repository at this point in the history
  • Loading branch information
kabachuha authored and irydacea committed Oct 15, 2020
1 parent b0c33d3 commit 5a77701
Show file tree
Hide file tree
Showing 3 changed files with 124 additions and 39 deletions.
133 changes: 94 additions & 39 deletions src/addon/client.cpp
Expand Up @@ -243,64 +243,119 @@ bool addons_client::download_addon(config& archive_cfg, const std::string& id, c
return !this->update_last_error(archive_cfg);
}

static inline std::string write_info_contents(const addon_info& info)
{
LOG_ADDONS << "generating version info for add-on '" << info.id << "'\n";

std::ostringstream info_contents;
config wml;

info_contents <<
"#\n"
"# File automatically generated by Wesnoth to keep track\n"
"# of version information on installed add-ons. DO NOT EDIT!\n"
"#\n";

info.write_minimal(wml.add_child("info"));
write(info_contents, wml);

return info_contents.str();
}

bool addons_client::install_addon(config& archive_cfg, const addon_info& info)
{
const cursor::setter cursor_setter(cursor::WAIT);

utils::string_map i18n_symbols;
i18n_symbols["addon_title"] = font::escape_text(info.title);

if(!check_names_legal(archive_cfg)) {
gui2::show_error_message(
VGETTEXT("The add-on <i>$addon_title</i> has an invalid file or directory "
"name and cannot be installed.", i18n_symbols));
return false;
}
if(!check_case_insensitive_duplicates(archive_cfg)){
gui2::show_error_message(
VGETTEXT("The add-on <i>$addon_title</i> has file or directory names "
"with case conflicts. This may cause problems.", i18n_symbols));
}
if(archive_cfg.has_child("removelist") || archive_cfg.has_child("addlist")) {
LOG_ADDONS << "Received an updatepack for the addon '" << info.id << "'\n";

// Update the version tracking file
config& info_remove = archive_cfg.add_child("removelist");
config& remove_maindir = info_remove.add_child("dir");
remove_maindir["name"] = info.id;
config file;
file["name"] = "_info.cfg";
remove_maindir.add_child("file", file);

config& info_add = archive_cfg.add_child("addlist");
config& add_maindir = info_add.add_child("dir");
add_maindir["name"] = info.id;
file["contents"] = write_info_contents(info);
add_maindir.add_child("file", file);

// A consistency check
config::all_children_itors itors = archive_cfg.all_children_range();
auto iter = itors.begin();
while(iter != itors.end()) {
if(iter->key == "removelist" || iter->key == "addlist") {
if(!check_names_legal(iter->cfg)) {
gui2::show_error_message(VGETTEXT("The add-on <i>$addon_title</i> has an invalid file or directory "
"name and cannot be installed.", i18n_symbols));
return false;
}
if(!check_case_insensitive_duplicates(iter->cfg)) {
gui2::show_error_message(VGETTEXT("The add-on <i>$addon_title</i> has file or directory names "
"with case conflicts. This may cause problems.", i18n_symbols));
}
}
iter++;
}

// Add local version information before unpacking
iter = itors.begin();
while(iter != itors.end()) {
if(iter->key == "removelist") {
purge_addon(iter->cfg);
} else if(iter->key == "addlist") {
unarchive_addon(iter->cfg);
}
iter++;
}

config* maindir = &archive_cfg.find_child("dir", "name", info.id);
if(!*maindir) {
LOG_ADDONS << "downloaded add-on '" << info.id << "' is missing its directory in the archive; creating it\n";
maindir = &archive_cfg.add_child("dir");
(*maindir)["name"] = info.id;
}
LOG_ADDONS << "Update completed.\n";

LOG_ADDONS << "generating version info for add-on '" << info.id << "'\n";
//#TODO: hash verification ???
} else {
LOG_ADDONS << "Received a full pack for the addon '" << info.id << "'\n";

std::ostringstream info_contents;
config wml;
if(!check_names_legal(archive_cfg)) {
gui2::show_error_message(VGETTEXT("The add-on <i>$addon_title</i> has an invalid file or directory "
"name and cannot be installed.", i18n_symbols));
return false;
}
if(!check_case_insensitive_duplicates(archive_cfg)) {
gui2::show_error_message(VGETTEXT("The add-on <i>$addon_title</i> has file or directory names "
"with case conflicts. This may cause problems.", i18n_symbols));
}

info_contents <<
"#\n"
"# File automatically generated by Wesnoth to keep track\n"
"# of version information on installed add-ons. DO NOT EDIT!\n"
"#\n";
// Add local version information before unpacking
config* maindir = &archive_cfg.find_child("dir", "name", info.id);
if(!*maindir) {
LOG_ADDONS << "downloaded add-on '" << info.id
<< "' is missing its directory in the archive; creating it\n";
maindir = &archive_cfg.add_child("dir");
(*maindir)["name"] = info.id;
}

info.write_minimal(wml.add_child("info"));
write(info_contents, wml);
config file;
file["name"] = "_info.cfg";
file["contents"] = write_info_contents(info);

config file;
file["name"] = "_info.cfg";
file["contents"] = info_contents.str();
maindir->add_child("file", file);

maindir->add_child("file", file);
LOG_ADDONS << "unpacking " << info.id << '\n';

LOG_ADDONS << "unpacking " << info.id << '\n';
// Remove any previously installed versions
if(!remove_local_addon(info.id)) {
WRN_ADDONS << "failed to uninstall previous version of " << info.id << "; the add-on may not work properly!\n";
}

// Remove any previously installed versions
if(!remove_local_addon(info.id)) {
WRN_ADDONS << "failed to uninstall previous version of " << info.id << "; the add-on may not work properly!" << std::endl;
unarchive_addon(archive_cfg);
LOG_ADDONS << "unpacking finished\n";
}

unarchive_addon(archive_cfg);
LOG_ADDONS << "unpacking finished\n";

return true;
}

Expand Down
27 changes: 27 additions & 0 deletions src/addon/manager.cpp
Expand Up @@ -299,6 +299,33 @@ void unarchive_addon(const config& cfg)
unarchive_dir(parentd, cfg);
}

static void purge_dir(const std::string& path, const config& removelist)
{
std::string dir;
if(removelist["name"].empty())
dir = path;
else
dir = path + '/' + removelist["name"].str();

for(const config& d : removelist.child_range("dir")) {
purge_dir(dir, d);
}

for(const config& f : removelist.child_range("file")) {
filesystem::delete_file(path + '/' + f["name"].str());
}

if(filesystem::dir_size(dir) < 1) {
filesystem::delete_directory(dir);
}
}

void purge_addon(const config& removelist)
{
const std::string parentd = filesystem::get_addons_dir();
purge_dir(parentd, removelist);
}

namespace {
std::map< std::string, version_info > version_info_cache;
} // end unnamed namespace 5
Expand Down
3 changes: 3 additions & 0 deletions src/addon/manager.hpp
Expand Up @@ -126,6 +126,9 @@ void archive_addon(const std::string& addon_name, class config& cfg);
/** Unarchives an add-on from campaignd's retrieved config object. */
void unarchive_addon(const class config& cfg);

/** Removes the listed files from the addon. */
void purge_addon(const config& removelist);

/** Refreshes the per-session cache of add-on's version information structs. */
void refresh_addon_version_info_cache();

Expand Down

0 comments on commit 5a77701

Please sign in to comment.