2023-03-08 16:00:41 -05:00
|
|
|
#include <std_include.hpp>
|
|
|
|
#include "loader/component_loader.hpp"
|
|
|
|
#include "workshop.hpp"
|
|
|
|
|
|
|
|
#include "game/game.hpp"
|
|
|
|
|
|
|
|
#include <utils/hook.hpp>
|
2023-04-12 09:47:38 -04:00
|
|
|
#include <utils/string.hpp>
|
|
|
|
#include <utils/io.hpp>
|
2023-03-08 16:00:41 -05:00
|
|
|
|
|
|
|
namespace workshop
|
|
|
|
{
|
2023-04-12 09:47:38 -04:00
|
|
|
namespace
|
2023-03-08 16:00:41 -05:00
|
|
|
{
|
2023-04-12 09:47:38 -04:00
|
|
|
utils::hook::detour setup_server_map_hook;
|
2023-03-08 16:00:41 -05:00
|
|
|
|
2023-04-12 09:47:38 -04:00
|
|
|
bool has_mod(const std::string& pub_id)
|
2023-03-08 16:00:41 -05:00
|
|
|
{
|
2023-04-12 09:47:38 -04:00
|
|
|
const auto total_mods = *reinterpret_cast<unsigned int*>(0x15678D170_g);
|
|
|
|
|
|
|
|
for (unsigned int i = 0; i < total_mods; ++i)
|
2023-03-08 16:00:41 -05:00
|
|
|
{
|
2023-04-12 09:47:38 -04:00
|
|
|
const auto mod_data = reinterpret_cast<game::workshop_data*>(0x15678D178_g + (sizeof(game::workshop_data) * i));
|
|
|
|
if (mod_data->publisherId == pub_id)
|
|
|
|
{
|
|
|
|
return true;
|
|
|
|
}
|
2023-03-08 16:00:41 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2023-04-12 09:47:38 -04:00
|
|
|
void load_usermap_mod_if_needed(const std::string& publisher_id)
|
2023-03-08 16:00:41 -05:00
|
|
|
{
|
2023-04-12 09:47:38 -04:00
|
|
|
if (!game::isModLoaded() && !publisher_id.empty())
|
|
|
|
{
|
|
|
|
game::loadMod(0, "usermaps", true);
|
|
|
|
}
|
2023-03-08 16:00:41 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
void setup_server_map_stub(int localClientNum, const char* mapname, const char* gametype)
|
|
|
|
{
|
|
|
|
const auto publisher_id = get_usermap_publisher_id(mapname);
|
|
|
|
load_usermap_mod_if_needed(publisher_id);
|
|
|
|
|
|
|
|
setup_server_map_hook.invoke(localClientNum, mapname, gametype);
|
|
|
|
}
|
|
|
|
|
|
|
|
bool has_workshop_item_stub(int type, const char* mapname, int a3)
|
|
|
|
{
|
|
|
|
const auto publisher_id = get_usermap_publisher_id(mapname);
|
|
|
|
const auto name = publisher_id.empty() ? mapname : publisher_id.data();
|
|
|
|
|
|
|
|
return utils::hook::invoke<bool>(0x1420D6380_g, type, name, a3);
|
|
|
|
}
|
|
|
|
|
|
|
|
game::workshop_data* load_usermap_stub(const char* mapname)
|
|
|
|
{
|
|
|
|
const auto publisher_id = get_usermap_publisher_id(mapname);
|
|
|
|
const auto name = publisher_id.empty() ? mapname : publisher_id.data();
|
|
|
|
|
|
|
|
return utils::hook::invoke<game::workshop_data*>(0x1420D5700_g, name);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-04-13 05:35:06 -04:00
|
|
|
std::string get_mod_name(const std::string& mod_id)
|
2023-04-12 09:47:38 -04:00
|
|
|
{
|
|
|
|
if (mod_id == "usermaps")
|
|
|
|
{
|
|
|
|
return mod_id;
|
|
|
|
}
|
|
|
|
|
|
|
|
const utils::nt::library host{};
|
|
|
|
std::string path;
|
|
|
|
|
|
|
|
if (game::is_server())
|
|
|
|
{
|
|
|
|
const auto base_path = host.get_folder().generic_string();
|
|
|
|
path = utils::string::va("%s/mods/%s/zone/workshop.json", base_path.data(), mod_id.data());
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
return mod_id;
|
|
|
|
}
|
|
|
|
|
|
|
|
std::string json_str;
|
|
|
|
utils::io::read_file(path, &json_str);
|
|
|
|
|
|
|
|
if (json_str.empty())
|
|
|
|
{
|
2023-04-13 07:40:19 -04:00
|
|
|
printf("[ Workshop ] workshop.json has not been found in mod folder: %s\n", mod_id.data());
|
2023-04-12 09:47:38 -04:00
|
|
|
return mod_id;
|
|
|
|
}
|
|
|
|
|
|
|
|
rapidjson::Document doc;
|
2023-04-13 07:40:19 -04:00
|
|
|
const rapidjson::ParseResult parse_result = doc.Parse(json_str);
|
|
|
|
|
|
|
|
if (parse_result.IsError() || !doc.IsObject())
|
|
|
|
{
|
|
|
|
printf("[ Workshop ] Unable to parse workshop.json\n");
|
|
|
|
return mod_id;
|
|
|
|
}
|
2023-04-12 09:47:38 -04:00
|
|
|
|
|
|
|
if (doc.HasMember("Title"))
|
|
|
|
{
|
|
|
|
std::string title = doc["Title"].GetString();
|
|
|
|
|
|
|
|
if (title.size() > 31)
|
|
|
|
{
|
|
|
|
title.resize(31);
|
|
|
|
}
|
|
|
|
|
|
|
|
return title;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2023-04-13 07:40:19 -04:00
|
|
|
printf("[ Workshop ] workshop.json has no \"Title\" member.\n");
|
2023-04-12 09:47:38 -04:00
|
|
|
return mod_id;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-04-13 05:35:06 -04:00
|
|
|
std::string get_usermap_publisher_id(const std::string& mapname)
|
2023-04-12 09:47:38 -04:00
|
|
|
{
|
|
|
|
const auto total_usermaps = *reinterpret_cast<unsigned int*>(0x1567B3580_g);
|
|
|
|
|
|
|
|
for (unsigned int i = 0; i < total_usermaps; ++i)
|
|
|
|
{
|
|
|
|
const auto usermap_data = reinterpret_cast<game::workshop_data*>(0x1567B3588_g + (sizeof(game::workshop_data) * i));
|
|
|
|
if (usermap_data->folderName == mapname)
|
|
|
|
{
|
|
|
|
return usermap_data->publisherId;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return {};
|
|
|
|
}
|
|
|
|
|
|
|
|
bool check_valid_usermap_id(const std::string& mapname, const std::string& pub_id)
|
|
|
|
{
|
|
|
|
if (!game::DB_FileExists(mapname.data(), 0) && pub_id.empty())
|
|
|
|
{
|
|
|
|
game::Com_Error(0, "Can't find usermap: %s!\nMake sure you're subscribed to the workshop item.", mapname.data());
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool check_valid_mod_id(const std::string& mod)
|
|
|
|
{
|
|
|
|
if (mod.empty() || mod == "usermaps")
|
|
|
|
{
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!has_mod(mod))
|
|
|
|
{
|
|
|
|
game::Com_Error(0, "Can't find mod with publisher id: %s!\nMake sure you're subscribed to the workshop item.", mod.data());
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
void load_mod_if_needed(const std::string& usermap, const std::string& mod)
|
|
|
|
{
|
|
|
|
if (!usermap.empty() || mod != "usermaps")
|
|
|
|
{
|
|
|
|
game::loadMod(0, mod.data(), true);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-03-08 16:00:41 -05:00
|
|
|
class component final : public client_component
|
|
|
|
{
|
|
|
|
public:
|
|
|
|
void post_unpack() override
|
|
|
|
{
|
|
|
|
setup_server_map_hook.create(0x14135CD20_g, setup_server_map_stub);
|
|
|
|
|
|
|
|
// Allow client to switch maps if server sends zone name instead of publisher id
|
|
|
|
utils::hook::call(0x14135CD84_g, has_workshop_item_stub);
|
|
|
|
utils::hook::call(0x14135CE48_g, load_usermap_stub);
|
|
|
|
}
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
REGISTER_COMPONENT(workshop::component)
|