Add imagefiles to mod download
This commit is contained in:
parent
3d5eb1c629
commit
ee2daaf0e9
@ -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);
|
||||||
|
|
||||||
|
@ -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:
|
||||||
|
7
src/client/component/imagefiles.hpp
Normal file
7
src/client/component/imagefiles.hpp
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
namespace imagefiles
|
||||||
|
{
|
||||||
|
void close_custom_handles();
|
||||||
|
void close_handle(const std::string& fastfile);
|
||||||
|
}
|
@ -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');
|
||||||
|
Loading…
Reference in New Issue
Block a user