Skip to content

Commit

Permalink
Mod download improvements (#162)
Browse files Browse the repository at this point in the history
This PR adds multiple features relating to mod downloads.
1. With this PR the launcher will convert old style mods (without hash)
to new style mods (with hash). This is so people don't have to
re-download all of their mods after joining a server which was
previously on a version below 3.6.0
2. This PR will save the last used date of mods in a JSON file in the
caching directory. This will allow for smart deletion of cached mods

---

By creating this pull request, I understand that code that is AI
generated or otherwise automatically generated may be rejected without
further discussion.
I declare that I fully understand all code I pushed into this PR, and
wrote all this code myself and own the rights to this code.
  • Loading branch information
WiserTixx authored Jan 18, 2025
2 parents 08a6f9a + d52a791 commit 8373a70
Showing 1 changed file with 86 additions and 1 deletion.
87 changes: 86 additions & 1 deletion src/Network/Resources.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -382,7 +382,7 @@ struct ModInfo {
}
} catch (const std::exception& e) {
debug(std::string("Failed to receive mod list: ") + e.what());
warn("Failed to receive new mod list format! This server may be outdated, but everything should still work as expected.");
debug("Failed to receive new mod list format! This server may be outdated, but everything should still work as expected.");
}
return std::make_pair(success, modInfos);
}
Expand All @@ -392,6 +392,52 @@ struct ModInfo {
std::string HashAlgorithm;
};

nlohmann::json modUsage = {};

void UpdateModUsage(const std::string& fileName) {
try {
fs::path usageFile = fs::path(CachingDirectory) / "mods.json";

if (!fs::exists(usageFile)) {
if (std::ofstream file(usageFile); !file.is_open()) {
error("Failed to create mods.json");
return;
} else {
file.close();
}
}

std::fstream file(usageFile, std::ios::in | std::ios::out);
if (!file.is_open()) {
error("Failed to open or create mods.json");
return;
}

if (modUsage.empty()) {
auto Size = fs::file_size(fs::path(CachingDirectory) / "mods.json");
std::string modsJson(Size, 0);
file.read(&modsJson[0], Size);

if (!modsJson.empty()) {
auto parsedModJson = nlohmann::json::parse(modsJson, nullptr, false);

if (parsedModJson.is_object())
modUsage = parsedModJson;
}
}

modUsage[fileName] = std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::system_clock::now().time_since_epoch()).count();

file.clear();
file.seekp(0, std::ios::beg);
file << modUsage.dump();
file.close();
} catch (std::exception& e) {
error("Failed to update mods.json: " + std::string(e.what()));
}
}


void NewSyncResources(SOCKET Sock, const std::string& Mods, const std::vector<ModInfo> ModInfos) {
if (ModInfos.empty()) {
CoreSend("L");
Expand Down Expand Up @@ -451,6 +497,42 @@ void NewSyncResources(SOCKET Sock, const std::string& Mods, const std::vector<Mo

fs::copy_file(PathToSaveTo, tmp_name, fs::copy_options::overwrite_existing);
fs::rename(tmp_name, name);
UpdateModUsage(FileName);
} catch (std::exception& e) {
error("Failed copy to the mods folder! " + std::string(e.what()));
Terminate = true;
continue;
}
WaitForConfirm();
continue;
} else if (auto OldCachedPath = fs::path(CachingDirectory) / std::filesystem::path(ModInfoIter->FileName).filename();
fs::exists(OldCachedPath) && GetSha256HashReallyFast(OldCachedPath.string()) == ModInfoIter->Hash) {
debug("Mod '" + FileName + "' found in old cache, copying it to the new cache");
std::this_thread::sleep_for(std::chrono::milliseconds(50));
try {
fs::copy_file(OldCachedPath, PathToSaveTo, fs::copy_options::overwrite_existing);

if (!fs::exists(GetGamePath() + "mods/multiplayer")) {
fs::create_directories(GetGamePath() + "mods/multiplayer");
}

auto modname = ModInfoIter->FileName;

#if defined(__linux__)
// Linux version of the game doesnt support uppercase letters in mod names
for (char& c : modname) {
c = ::tolower(c);
}
#endif

debug("Mod name: " + modname);
auto name = std::filesystem::path(GetGamePath()) / "mods/multiplayer" / modname;
std::string tmp_name = name.string();
tmp_name += ".tmp";

fs::copy_file(PathToSaveTo, tmp_name, fs::copy_options::overwrite_existing);
fs::rename(tmp_name, name);
UpdateModUsage(FileName);
} catch (std::exception& e) {
error("Failed copy to the mods folder! " + std::string(e.what()));
Terminate = true;
Expand Down Expand Up @@ -505,6 +587,7 @@ void NewSyncResources(SOCKET Sock, const std::string& Mods, const std::vector<Mo
#endif

fs::copy_file(PathToSaveTo, std::filesystem::path(GetGamePath()) / "mods/multiplayer" / FName, fs::copy_options::overwrite_existing);
UpdateModUsage(FName);
}
WaitForConfirm();
++ModNo;
Expand Down Expand Up @@ -606,6 +689,7 @@ void SyncResources(SOCKET Sock) {
auto tmp_name = name + ".tmp";
fs::copy_file(PathToSaveTo, tmp_name, fs::copy_options::overwrite_existing);
fs::rename(tmp_name, name);
UpdateModUsage(modname);
} catch (std::exception& e) {
error("Failed copy to the mods folder! " + std::string(e.what()));
Terminate = true;
Expand Down Expand Up @@ -661,6 +745,7 @@ void SyncResources(SOCKET Sock) {
#endif

fs::copy_file(PathToSaveTo, GetGamePath() + "mods/multiplayer" + FName, fs::copy_options::overwrite_existing);
UpdateModUsage(FN->substr(pos));
}
WaitForConfirm();
}
Expand Down

0 comments on commit 8373a70

Please sign in to comment.