t7x/src/client/component/steam_proxy.cpp

316 lines
7.5 KiB
C++
Raw Normal View History

2022-09-26 12:39:01 -04:00
#include <std_include.hpp>
#include "loader/component_loader.hpp"
#include "steam_proxy.hpp"
#include "scheduler.hpp"
#include <utils/nt.hpp>
#include <utils/flags.hpp>
#include <utils/string.hpp>
2022-09-30 13:19:58 -04:00
#include <utils/finally.hpp>
#include <utils/concurrency.hpp>
2022-09-26 12:39:01 -04:00
#include <utils/binary_resource.hpp>
#include "resource.hpp"
#include "steam/interface.hpp"
#include "steam/steam.hpp"
namespace steam_proxy
{
namespace
{
utils::binary_resource runner_file(RUNNER, "boiii-runner.exe");
2022-10-01 05:08:42 -04:00
utils::nt::library steam_client_module{};
utils::nt::library steam_overlay_module{};
void* steam_pipe = nullptr;
void* global_user = nullptr;
steam::interface client_engine{};
steam::interface client_user{};
steam::interface client_utils{};
steam::interface client_friends{};
steam::interface client_ugc{};
utils::concurrency::container<subscribed_item_map> subscribed_items;
2022-09-26 12:39:01 -04:00
enum class ownership_state
{
success,
unowned,
nosteam,
error,
};
bool is_disabled()
{
static const auto disabled = utils::flags::has_flag("nosteam");
return disabled;
}
2022-09-30 13:19:58 -04:00
2022-10-01 05:08:42 -04:00
void* load_client_engine()
2022-09-30 13:19:58 -04:00
{
2022-10-01 05:08:42 -04:00
if (!steam_client_module) return nullptr;
2022-09-26 12:39:01 -04:00
for (auto i = 1; i > 0; ++i)
{
std::string name = utils::string::va("CLIENTENGINE_INTERFACE_VERSION%03i", i);
2022-10-01 05:08:42 -04:00
auto* const temp_client_engine = steam_client_module
.invoke<void*>("CreateInterface", name.data(), nullptr);
if (temp_client_engine) return temp_client_engine;
2022-09-26 12:39:01 -04:00
}
return nullptr;
}
void load_client()
{
const std::filesystem::path steam_path = steam::SteamAPI_GetSteamInstallPath();
if (steam_path.empty()) return;
utils::nt::library::load(steam_path / "tier0_s64.dll");
utils::nt::library::load(steam_path / "vstdlib_s64.dll");
2022-10-01 05:08:42 -04:00
steam_overlay_module = utils::nt::library::load(steam_path / "gameoverlayrenderer64.dll");
steam_client_module = utils::nt::library::load(steam_path / "steamclient64.dll");
if (!steam_client_module) return;
client_engine = load_client_engine();
if (!client_engine) return;
steam_pipe = steam_client_module.invoke<void*>("Steam_CreateSteamPipe");
global_user = steam_client_module.invoke<void*>(
"Steam_ConnectToGlobalUser", steam_pipe);
client_user = client_engine.invoke<void*>(8, global_user, steam_pipe);
client_utils = client_engine.invoke<void*>(14, steam_pipe);
client_friends = client_engine.invoke<void*>(13, global_user, steam_pipe);
client_ugc = client_engine.invoke<void*>(61, global_user, steam_pipe);
2022-09-26 12:39:01 -04:00
}
2022-10-01 05:08:42 -04:00
void do_cleanup()
2022-09-26 12:39:01 -04:00
{
2022-10-01 05:08:42 -04:00
client_engine = nullptr;
client_user = nullptr;
client_utils = nullptr;
2022-10-01 12:52:15 -04:00
client_friends = nullptr;
client_ugc = nullptr;
2022-10-01 05:08:42 -04:00
steam_pipe = nullptr;
global_user = nullptr;
steam_client_module = utils::nt::library{nullptr};
}
2022-10-01 12:52:15 -04:00
bool perform_cleanup_if_needed()
{
if (steam_client_module
&& steam_pipe
&& global_user
&& steam_client_module.invoke<bool>("Steam_BConnected", global_user,
steam_pipe)
&& steam_client_module.invoke<bool>("Steam_BLoggedOn", global_user, steam_pipe)
)
{
return false;
}
do_cleanup();
return true;
}
2022-10-01 05:08:42 -04:00
void clean_up_on_error()
{
scheduler::schedule([]
2022-09-26 12:39:01 -04:00
{
2022-10-01 12:52:15 -04:00
if (perform_cleanup_if_needed())
2022-10-01 05:08:42 -04:00
{
2022-10-01 12:52:15 -04:00
return scheduler::cond_end;
2022-10-01 05:08:42 -04:00
}
2022-10-01 12:52:15 -04:00
return scheduler::cond_continue;
}, scheduler::main);
2022-09-26 12:39:01 -04:00
}
ownership_state start_mod_unsafe(const std::string& title, size_t app_id)
{
2022-10-01 05:08:42 -04:00
if (!client_utils || !client_user)
2022-09-26 12:39:01 -04:00
{
return ownership_state::nosteam;
}
2022-10-01 05:08:42 -04:00
if (!client_user.invoke<bool>("BIsSubscribedApp", app_id))
2022-09-26 12:39:01 -04:00
{
//app_id = 480; // Spacewar
return ownership_state::unowned;
}
2022-09-26 13:00:22 -04:00
if (is_disabled())
{
return ownership_state::success;
}
2022-10-01 05:08:42 -04:00
client_utils.invoke<void>("SetAppIDForCurrentPipe", app_id, false);
2022-09-26 12:39:01 -04:00
char our_directory[MAX_PATH] = {0};
GetCurrentDirectoryA(sizeof(our_directory), our_directory);
const auto path = runner_file.get_extracted_file();
const std::string cmdline = utils::string::va("\"%s\" -proc %d", path.data(), GetCurrentProcessId());
steam::game_id game_id;
game_id.raw.type = 1; // k_EGameIDTypeGameMod
game_id.raw.app_id = app_id & 0xFFFFFF;
const auto* mod_id = "bo3";
game_id.raw.mod_id = *reinterpret_cast<const unsigned int*>(mod_id) | 0x80000000;
2022-10-01 05:08:42 -04:00
client_user.invoke<bool>("SpawnProcess", path.data(), cmdline.data(), our_directory,
&game_id.bits, title.data(), 0, 0, 0);
2022-09-26 12:39:01 -04:00
return ownership_state::success;
}
2022-10-01 05:08:42 -04:00
ownership_state start_mod(const std::string& title, const size_t app_id)
{
__try
{
return start_mod_unsafe(title, app_id);
}
__except (EXCEPTION_EXECUTE_HANDLER)
{
do_cleanup();
return ownership_state::error;
}
}
}
class component final : public component_interface
{
public:
void pre_start() override
2022-09-26 12:39:01 -04:00
{
2022-10-01 05:08:42 -04:00
load_client();
clean_up_on_error();
}
2022-09-26 12:39:01 -04:00
2022-10-01 05:08:42 -04:00
void post_unpack() override
{
try
{
const auto res = start_mod("\xE2\x98\x84\xEF\xB8\x8F" " BOIII"s, steam::SteamUtils()->GetAppID());
2022-09-26 12:39:01 -04:00
2022-10-01 05:08:42 -04:00
switch (res)
{
case ownership_state::nosteam:
throw std::runtime_error("Steam must be running to play this game!");
case ownership_state::unowned:
throw std::runtime_error("You must own the game on steam to play this mod!");
case ownership_state::error:
throw std::runtime_error("Failed to verify ownership of the game!");
case ownership_state::success:
break;
}
}
catch (std::exception& e)
{
printf("Steam: %s\n", e.what());
MessageBoxA(GetForegroundWindow(), e.what(), "BOIII Error", MB_ICONERROR);
TerminateProcess(GetCurrentProcess(), 1234);
}
2022-09-26 12:39:01 -04:00
}
2022-10-01 05:08:42 -04:00
void pre_destroy() override
2022-09-26 12:39:01 -04:00
{
2022-10-01 05:08:42 -04:00
if (steam_client_module && steam_pipe)
2022-09-26 12:39:01 -04:00
{
2022-10-01 05:08:42 -04:00
if (global_user)
2022-09-26 12:39:01 -04:00
{
2022-10-01 05:08:42 -04:00
steam_client_module.invoke<void>("Steam_ReleaseUser", steam_pipe,
global_user);
2022-09-26 12:39:01 -04:00
}
2022-10-01 05:08:42 -04:00
steam_client_module.invoke<bool>("Steam_BReleaseSteamPipe", steam_pipe);
}
2022-09-26 12:39:01 -04:00
}
};
const utils::nt::library& get_overlay_module()
{
2022-10-01 05:08:42 -04:00
return steam_overlay_module;
2022-09-26 12:39:01 -04:00
}
2022-09-29 12:15:40 -04:00
const char* get_player_name()
{
2022-10-01 05:08:42 -04:00
if (client_friends)
{
return client_friends.invoke<const char*>("GetPersonaName");
}
return "boiii";
2022-09-29 12:15:40 -04:00
}
2022-09-30 13:19:58 -04:00
void update_subscribed_items()
{
2022-10-01 05:08:42 -04:00
subscribed_item_map map{};
const auto _ = utils::finally([&]
{
subscribed_items.access([&](subscribed_item_map& items)
{
items = std::move(map);
});
});
if (!client_ugc)
{
return;
}
try
{
const auto app_id = steam::SteamUtils()->GetAppID();
const auto num_items = client_ugc.invoke<uint32_t>("GetNumSubscribedItems", app_id);
if (!num_items)
{
return;
}
std::vector<uint64_t> ids;
ids.resize(num_items);
auto result = client_ugc.invoke<uint32_t>("GetSubscribedItems", app_id, ids.data(),
num_items);
result = std::min(num_items, result);
for (uint32_t i = 0; i < result; ++i)
{
char buffer[0x1000] = {0};
subscribed_item item{};
item.state = client_ugc.invoke<uint32_t>("GetItemState", app_id, ids[i]);
item.available = client_ugc.invoke<bool>("GetItemInstallInfo", app_id, ids[i],
&item.size_on_disk,
buffer,
sizeof(buffer), &item.time_stamp);
item.path = buffer;
map[ids[i]] = std::move(item);
}
}
catch (...)
{
client_ugc = {};
}
2022-09-30 13:19:58 -04:00
}
2022-10-01 05:08:42 -04:00
void access_subscribed_items(
const std::function<void(const subscribed_item_map&)>& callback)
2022-09-30 13:19:58 -04:00
{
2022-10-01 05:08:42 -04:00
subscribed_items.access(callback);
2022-09-30 13:19:58 -04:00
}
2022-09-26 12:39:01 -04:00
}
REGISTER_COMPONENT(steam_proxy::component)