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 "fastfiles.hpp"
#include "filesystem.hpp"
#include "imagefiles.hpp"
#include "game/dvars.hpp"
@ -232,8 +233,9 @@ namespace fastfiles
const auto& usermap_value = usermap.value();
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_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",
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);
}
@ -337,6 +339,8 @@ namespace fastfiles
void load_pre_gfx_zones(game::XZoneInfo* zoneInfo, unsigned int zoneCount, game::DBSyncMode syncMode)
{
imagefiles::close_custom_handles();
std::vector<game::XZoneInfo> data;
merge(&data, zoneInfo, zoneCount);

View File

@ -6,6 +6,7 @@
#include "filesystem.hpp"
#include "fastfiles.hpp"
#include "scheduler.hpp"
#include "imagefiles.hpp"
#include "game/game.hpp"
@ -25,6 +26,7 @@ namespace imagefiles
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, game::DB_IFileSysFile*> image_file_handles;
@ -44,7 +46,7 @@ namespace imagefiles
const auto name = get_image_file_name();
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;
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
{
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;
};
// snake case these names before release
std::vector<usermap_file> usermap_files =
{
{".ff", "usermaphash", false},
{"_load.ff", "usermaploadhash", true},
{".arena", "usermaparenahash", true},
{".pak", "usermappakhash", true},
};
std::vector<usermap_file> mod_files =
{
{".ff", "modHash", false},
{".pak", "modpakhash", true},
};
struct
@ -226,15 +234,16 @@ namespace party
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 auto source_hash = info.get(name);
const std::string filename = utils::string::va("usermaps/%s/%s%s",
mapname.data(), mapname.data(), file.extension.data());
const auto source_hash = info.get(file.name);
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;
@ -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,18 +285,27 @@ namespace party
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");
if (source_hash.empty())
auto needs_restart = false;
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;
}
const auto mod_path = server_fs_game + "/mod.ff";
auto has_to_download = !utils::io::file_exists(mod_path);
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(mod_path);
const auto data = utils::io::read_file(file_path);
const auto hash = utils::cryptography::sha1::compute(data, true);
has_to_download = source_hash != hash;
@ -295,16 +313,25 @@ namespace party
if (has_to_download)
{
files.emplace_back(mod_path, source_hash);
return false;
// 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);
return true;
needs_restart = true;
}
}
return false;
return needs_restart;
}
void close_joining_popups()
@ -431,16 +458,16 @@ namespace party
fastfiles::set_usermap(mapname);
for (const auto& [ext, key, opt] : usermap_files)
for (const auto& file : usermap_files)
{
char buffer[0x100] = {0};
const std::string source_hash = game::MSG_ReadStringLine(msg,
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);
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");
scheduler::once([]
@ -499,19 +526,14 @@ namespace party
line(current_sv_mapname);
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);
for (const auto& [ext, key, opt] : usermap_files)
for (const auto& file : usermap_files)
{
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
{
@ -998,16 +1020,11 @@ namespace party
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);
info.set(name, hash);
};
for (const auto& [ext, name, opt] : usermap_files)
{
add_hash(ext, name);
info.set(file.name, hash);
}
}
@ -1016,8 +1033,12 @@ namespace party
if (!fs_game.empty())
{
const auto hash = get_file_hash(utils::string::va("%s/mod.ff", fs_game.data()));
info.set("modHash", hash);
for (const auto& file : mod_files)
{
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');