Add imagefiles to mod download

This commit is contained in:
fed 2023-01-15 03:35:24 +01:00
parent 3d5eb1c629
commit ee2daaf0e9
4 changed files with 127 additions and 58 deletions

View File

@ -5,6 +5,7 @@
#include "console.hpp" #include "console.hpp"
#include "fastfiles.hpp" #include "fastfiles.hpp"
#include "filesystem.hpp" #include "filesystem.hpp"
#include "imagefiles.hpp"
#include "game/dvars.hpp" #include "game/dvars.hpp"
@ -232,8 +233,9 @@ namespace fastfiles
const auto& usermap_value = usermap.value(); const auto& usermap_value = usermap.value();
const std::string usermap_file = utils::string::va("%s.ff", usermap_value.data()); const std::string usermap_file = utils::string::va("%s.ff", usermap_value.data());
const std::string usermap_load_file = utils::string::va("%s_load.ff", usermap_value.data()); const std::string usermap_load_file = utils::string::va("%s_load.ff", usermap_value.data());
const std::string usermap_pak_file = utils::string::va("%s.pak", usermap_value.data());
if (mapname == usermap_file || mapname == usermap_load_file) if (mapname == usermap_file || mapname == usermap_load_file || mapname == usermap_pak_file)
{ {
const auto path = utils::string::va("usermaps\\%s\\%s", const auto path = utils::string::va("usermaps\\%s\\%s",
usermap_value.data(), mapname.data()); usermap_value.data(), mapname.data());
@ -289,7 +291,7 @@ namespace fastfiles
} }
} }
if (name.ends_with(".ff")) if (name.ends_with(".ff") || name.ends_with(".pak"))
{ {
handle = find_fastfile(name, true); handle = find_fastfile(name, true);
} }
@ -337,6 +339,8 @@ namespace fastfiles
void load_pre_gfx_zones(game::XZoneInfo* zoneInfo, unsigned int zoneCount, game::DBSyncMode syncMode) void load_pre_gfx_zones(game::XZoneInfo* zoneInfo, unsigned int zoneCount, game::DBSyncMode syncMode)
{ {
imagefiles::close_custom_handles();
std::vector<game::XZoneInfo> data; std::vector<game::XZoneInfo> data;
merge(&data, zoneInfo, zoneCount); merge(&data, zoneInfo, zoneCount);

View File

@ -6,6 +6,7 @@
#include "filesystem.hpp" #include "filesystem.hpp"
#include "fastfiles.hpp" #include "fastfiles.hpp"
#include "scheduler.hpp" #include "scheduler.hpp"
#include "imagefiles.hpp"
#include "game/game.hpp" #include "game/game.hpp"
@ -25,6 +26,7 @@ namespace imagefiles
char __pad0[120]; char __pad0[120];
}; };
utils::memory::allocator image_file_allocator;
std::unordered_map<std::string, image_file_unk*> image_file_unk_map; std::unordered_map<std::string, image_file_unk*> image_file_unk_map;
std::unordered_map<std::string, game::DB_IFileSysFile*> image_file_handles; std::unordered_map<std::string, game::DB_IFileSysFile*> image_file_handles;
@ -44,7 +46,7 @@ namespace imagefiles
const auto name = get_image_file_name(); const auto name = get_image_file_name();
if (image_file_unk_map.find(name) == image_file_unk_map.end()) if (image_file_unk_map.find(name) == image_file_unk_map.end())
{ {
const auto unk = utils::memory::get_allocator()->allocate<image_file_unk>(); const auto unk = image_file_allocator.allocate<image_file_unk>();
image_file_unk_map[name] = unk; image_file_unk_map[name] = unk;
return unk; return unk;
} }
@ -135,6 +137,41 @@ namespace imagefiles
} }
} }
void close_custom_handles()
{
const auto db_fs = *game::db_fs;
for (const auto& handle : image_file_handles)
{
if (handle.second != nullptr)
{
db_fs->vftbl->Close(db_fs, handle.second);
}
}
image_file_handles.clear();
image_file_unk_map.clear();
image_file_allocator.clear();
}
void close_handle(const std::string& fastfile)
{
if (!image_file_handles.contains(fastfile))
{
return;
}
const auto db_fs = *game::db_fs;
const auto handle = image_file_handles[fastfile];
if (handle != nullptr)
{
db_fs->vftbl->Close(db_fs, handle);
}
image_file_handles.erase(fastfile);
image_file_allocator.free(image_file_unk_map[fastfile]);
image_file_unk_map.erase(fastfile);
}
class component final : public component_interface class component final : public component_interface
{ {
public: public:

View File

@ -0,0 +1,7 @@
#pragma once
namespace imagefiles
{
void close_custom_handles();
void close_handle(const std::string& fastfile);
}

View File

@ -45,11 +45,19 @@ namespace party
bool optional; bool optional;
}; };
// snake case these names before release
std::vector<usermap_file> usermap_files = std::vector<usermap_file> usermap_files =
{ {
{".ff", "usermaphash", false}, {".ff", "usermaphash", false},
{"_load.ff", "usermaploadhash", true}, {"_load.ff", "usermaploadhash", true},
{".arena", "usermaparenahash", true}, {".arena", "usermaparenahash", true},
{".pak", "usermappakhash", true},
};
std::vector<usermap_file> mod_files =
{
{".ff", "modHash", false},
{".pak", "modpakhash", true},
}; };
struct struct
@ -226,15 +234,16 @@ namespace party
throw std::runtime_error(utils::string::va("Invalid server mapname value %s\n", mapname.data())); throw std::runtime_error(utils::string::va("Invalid server mapname value %s\n", mapname.data()));
} }
const auto check_file = [&](const std::string& ext, const std::string& name, bool optional) const auto check_file = [&](const usermap_file& file)
{ {
const std::string filename = utils::string::va("usermaps/%s/%s%s", mapname.data(), mapname.data(), ext.data()); const std::string filename = utils::string::va("usermaps/%s/%s%s",
const auto source_hash = info.get(name); mapname.data(), mapname.data(), file.extension.data());
const auto source_hash = info.get(file.name);
if (source_hash.empty()) if (source_hash.empty())
{ {
if (!optional) if (!file.optional)
{ {
throw std::runtime_error(utils::string::va("Server %s is empty", name.data())); throw std::runtime_error(utils::string::va("Server %s is empty", file.name.data()));
} }
return; return;
@ -248,9 +257,9 @@ namespace party
} }
}; };
for (const auto& [ext, name, opt] : usermap_files) for (const auto& file : usermap_files)
{ {
check_file(ext, name, opt); check_file(file);
} }
} }
@ -276,35 +285,53 @@ namespace party
throw std::runtime_error(utils::string::va("Invalid server fs_game value %s\n", server_fs_game.data())); throw std::runtime_error(utils::string::va("Invalid server fs_game value %s\n", server_fs_game.data()));
} }
const auto source_hash = info.get("modHash"); auto needs_restart = false;
if (source_hash.empty()) for (const auto& file : mod_files)
{ {
throw std::runtime_error("Connection failed: Server mod hash is empty."); const auto source_hash = info.get(file.name);
if (source_hash.empty() && !file.optional)
{
if (file.optional)
{
continue;
}
throw std::runtime_error(
utils::string::va("Connection failed: Server %s is empty.", file.name.data()));
}
const auto file_path = server_fs_game + "/mod" + file.extension;
auto has_to_download = !utils::io::file_exists(file_path);
if (!has_to_download)
{
const auto data = utils::io::read_file(file_path);
const auto hash = utils::cryptography::sha1::compute(data, true);
has_to_download = source_hash != hash;
}
if (has_to_download)
{
// unload mod before downloading it
if (client_fs_game == server_fs_game)
{
mods::set_mod("", true);
return true;
}
else
{
files.emplace_back(file_path, source_hash);
}
}
else if (client_fs_game != server_fs_game)
{
mods::set_mod(server_fs_game);
needs_restart = true;
}
} }
const auto mod_path = server_fs_game + "/mod.ff"; return needs_restart;
auto has_to_download = !utils::io::file_exists(mod_path);
if (!has_to_download)
{
const auto data = utils::io::read_file(mod_path);
const auto hash = utils::cryptography::sha1::compute(data, true);
has_to_download = source_hash != hash;
}
if (has_to_download)
{
files.emplace_back(mod_path, source_hash);
return false;
}
else if (client_fs_game != server_fs_game)
{
mods::set_mod(server_fs_game);
return true;
}
return false;
} }
void close_joining_popups() void close_joining_popups()
@ -431,16 +458,16 @@ namespace party
fastfiles::set_usermap(mapname); fastfiles::set_usermap(mapname);
for (const auto& [ext, key, opt] : usermap_files) for (const auto& file : usermap_files)
{ {
char buffer[0x100] = {0}; char buffer[0x100] = {0};
const std::string source_hash = game::MSG_ReadStringLine(msg, const std::string source_hash = game::MSG_ReadStringLine(msg,
buffer, static_cast<unsigned int>(sizeof(buffer))); buffer, static_cast<unsigned int>(sizeof(buffer)));
const auto path = get_usermap_file_path(mapname, ext); const auto path = get_usermap_file_path(mapname, file.extension);
const auto hash = get_file_hash(path); const auto hash = get_file_hash(path);
if ((!source_hash.empty() && hash != source_hash) || (source_hash.empty() && !opt)) if ((!source_hash.empty() && hash != source_hash) || (source_hash.empty() && !file.optional))
{ {
command::execute("disconnect"); command::execute("disconnect");
scheduler::once([] scheduler::once([]
@ -499,19 +526,14 @@ namespace party
line(current_sv_mapname); line(current_sv_mapname);
line(sv_gametype->current.string); line(sv_gametype->current.string);
const auto add_hash = [&](const std::string extension)
{
const auto filename = get_usermap_file_path(current_sv_mapname, extension);
const auto hash = get_file_hash(filename);
line(hash);
};
const auto is_usermap = fastfiles::usermap_exists(current_sv_mapname); const auto is_usermap = fastfiles::usermap_exists(current_sv_mapname);
for (const auto& [ext, key, opt] : usermap_files) for (const auto& file : usermap_files)
{ {
if (is_usermap) if (is_usermap)
{ {
add_hash(ext); const auto filename = get_usermap_file_path(current_sv_mapname, file.extension);
const auto hash = get_file_hash(filename);
line(hash);
} }
else else
{ {
@ -998,16 +1020,11 @@ namespace party
if (!fastfiles::is_stock_map(mapname)) if (!fastfiles::is_stock_map(mapname))
{ {
const auto add_hash = [&](const std::string& extension, const std::string& name) for (const auto& file : usermap_files)
{ {
const auto path = get_usermap_file_path(mapname, extension); const auto path = get_usermap_file_path(mapname, file.extension);
const auto hash = get_file_hash(path); const auto hash = get_file_hash(path);
info.set(name, hash); info.set(file.name, hash);
};
for (const auto& [ext, name, opt] : usermap_files)
{
add_hash(ext, name);
} }
} }
@ -1016,8 +1033,12 @@ namespace party
if (!fs_game.empty()) if (!fs_game.empty())
{ {
const auto hash = get_file_hash(utils::string::va("%s/mod.ff", fs_game.data())); for (const auto& file : mod_files)
info.set("modHash", hash); {
const auto hash = get_file_hash(utils::string::va("%s/mod%s",
fs_game.data(), file.extension.data()));
info.set(file.name, hash);
}
} }
network::send(target, "infoResponse", info.build(), '\n'); network::send(target, "infoResponse", info.build(), '\n');