115 changed files with 2851 additions and 1982 deletions
+3
View File
@@ -29,6 +29,9 @@
[submodule "deps/udis86"] [submodule "deps/udis86"]
path = deps/udis86 path = deps/udis86
url = https://github.com/vmt/udis86.git url = https://github.com/vmt/udis86.git
[submodule "deps/WinToast"]
path = deps/WinToast
url = https://github.com/mohabouje/WinToast.git
[submodule "deps/zlib"] [submodule "deps/zlib"]
path = deps/zlib path = deps/zlib
url = https://github.com/madler/zlib.git url = https://github.com/madler/zlib.git
Vendored
+1 -1
Submodule deps/GSL updated: 543d0dd3fe...f1a494cfd2
Vendored Submodule
+1
Submodule deps/WinToast added at 821c4818ad
Vendored
+1 -1
+1 -1
+1 -1
+32
View File
@@ -0,0 +1,32 @@
wintoast = {
source = path.join(dependencies.basePath, "WinToast"),
}
function wintoast.import()
links { "WinToast" }
wintoast.includes()
end
function wintoast.includes()
includedirs {
path.join(wintoast.source, "include"),
}
end
function wintoast.project()
project "WinToast"
language "C++"
wintoast.includes()
rapidjson.import();
files {
path.join(wintoast.source, "include/wintoastlib.h"),
path.join(wintoast.source, "src/wintoastlib.cpp"),
}
warnings "Off"
kind "StaticLib"
end
table.insert(dependencies, wintoast)
+1 -1
Vendored
+1 -1
Submodule deps/zlib updated: 5a82f71ed1...9f0f2d4f9f
+2 -2
View File
@@ -257,7 +257,7 @@ if os.getenv("CI") then
defines {"CI"} defines {"CI"}
end end
multiprocessorcompile "On" flags {"NoIncrementalLink", "NoMinimalRebuild", "MultiProcessorCompile", "No64BitChecks"}
filter "platforms:x64" filter "platforms:x64"
defines {"_WINDOWS", "WIN32"} defines {"_WINDOWS", "WIN32"}
@@ -268,7 +268,7 @@ filter "configurations:Release"
buildoptions {"/GL"} buildoptions {"/GL"}
linkoptions {"/IGNORE:4702", "/LTCG"} linkoptions {"/IGNORE:4702", "/LTCG"}
defines {"NDEBUG"} defines {"NDEBUG"}
fatalwarnings {"All"} flags {"FatalCompileWarnings"}
filter {} filter {}
filter "configurations:Debug" filter "configurations:Debug"
+5 -1
View File
@@ -103,7 +103,9 @@ namespace arxan
BOOL WINAPI set_thread_context_stub(const HANDLE thread, CONTEXT* context) BOOL WINAPI set_thread_context_stub(const HANDLE thread, CONTEXT* context)
{ {
if (game::dwGetLogOnStatus(0) == game::DW_LIVE_CONNECTED && context->ContextFlags == CONTEXT_DEBUG_REGISTERS) if (!game::environment::is_sp()
&& game::dwGetLogOnStatus(0) == game::DW_LIVE_CONNECTED
&& context->ContextFlags == CONTEXT_DEBUG_REGISTERS)
{ {
return TRUE; return TRUE;
} }
@@ -140,6 +142,8 @@ namespace arxan
void post_unpack() override void post_unpack() override
{ {
if (game::environment::is_sp()) return;
utils::hook::jump(0x1404FE1E0, 0x1404FE2D0); // idk utils::hook::jump(0x1404FE1E0, 0x1404FE2D0); // idk
utils::hook::jump(0x140558C20, 0x140558CB0); // dwNetPump utils::hook::jump(0x140558C20, 0x140558CB0); // dwNetPump
utils::hook::jump(0x140591850, 0x1405918E0); // dwLobbyPump utils::hook::jump(0x140591850, 0x1405918E0); // dwLobbyPump
-35
View File
@@ -1,35 +0,0 @@
#include <std_include.hpp>
#include <loader/component_loader.hpp>
#include "game/game.hpp"
#include <utils/memory.hpp>
namespace asset_restrict
{
namespace
{
void reallocate_asset_pool(game::XAssetType type, const int size)
{
const auto asset_size = game::DB_GetXAssetTypeSize(type);
const auto new_size = size * game::g_poolSize[type];
const auto new_allocation_size = static_cast<std::size_t>(new_size * asset_size);
const game::XAssetHeader pool_entry = { .data = utils::memory::allocate(new_allocation_size) };
game::DB_XAssetPool[type] = pool_entry.data;
game::g_poolSize[type] = new_size;
}
}
class component final : public component_interface
{
public:
void post_unpack() override
{
reallocate_asset_pool(game::ASSET_TYPE_XANIMPARTS, 2);
reallocate_asset_pool(game::ASSET_TYPE_ATTACHMENT, 2);
}
};
}
REGISTER_COMPONENT(asset_restrict::component)
-59
View File
@@ -1,59 +0,0 @@
#include <std_include.hpp>
#include "loader/component_loader.hpp"
#include "game/game.hpp"
#include "component/console.hpp"
#include <utils/hook.hpp>
namespace weapons
{
namespace
{
void g_setup_level_weapon_def_stub()
{
game::G_SetupLevelWeaponDef();
// The count on most maps is well below 200
std::array<game::WeaponCompleteDef*, 256> weapons{};
const auto count = game::DB_GetAllXAssetOfType_FastFile(game::ASSET_TYPE_WEAPON, (void**)weapons.data(), static_cast<int>(weapons.max_size()));
std::sort(weapons.begin(), weapons.begin() + count, [](game::WeaponCompleteDef* weapon1, game::WeaponCompleteDef* weapon2)
{
assert(weapon1->szInternalName);
assert(weapon2->szInternalName);
return std::strcmp(weapon1->szInternalName, weapon2->szInternalName) < 0;
});
#ifdef _DEBUG
console::info("Found %i weapons to precache\n", count);
#endif
for (auto i = 0; i < count; ++i)
{
#ifdef _DEBUG
console::info("Precaching weapon \"%s\"\n", weapons[i]->szInternalName);
#endif
(void)game::G_GetWeaponForName(weapons[i]->szInternalName);
}
}
}
class component final : public component_interface
{
public:
void post_unpack() override
{
// Kill Scr_PrecacheItem (We are going to do this from code)
utils::hook::nop(0x1403BDB10, 4);
utils::hook::set<std::uint8_t>(0x1403BDB10, 0xC3);
// Load weapons from the DB
utils::hook::call(0x14039F382, g_setup_level_weapon_def_stub);
utils::hook::call(0x1403AE31A, g_setup_level_weapon_def_stub);
}
};
}
REGISTER_COMPONENT(weapons::component)
+23 -87
View File
@@ -8,13 +8,11 @@
#include "console.hpp" #include "console.hpp"
#include "network.hpp" #include "network.hpp"
#include <utils/cryptography.hpp>
#include <utils/hook.hpp> #include <utils/hook.hpp>
#include <utils/info_string.hpp>
#include <utils/io.hpp>
#include <utils/properties.hpp>
#include <utils/smbios.hpp>
#include <utils/string.hpp> #include <utils/string.hpp>
#include <utils/smbios.hpp>
#include <utils/info_string.hpp>
#include <utils/cryptography.hpp>
namespace auth namespace auth
{ {
@@ -33,21 +31,13 @@ namespace auth
std::string get_hw_profile_guid() std::string get_hw_profile_guid()
{ {
auto hw_profile_path = (utils::properties::get_appdata_path() / "iw6-guid.dat").generic_string();
if (utils::io::file_exists(hw_profile_path))
{
// Migration
utils::io::remove_file(hw_profile_path);
}
HW_PROFILE_INFO info; HW_PROFILE_INFO info;
if (!GetCurrentHwProfileA(&info)) if (!GetCurrentHwProfileA(&info))
{ {
return {}; return {};
} }
auto hw_profile_info = std::string{ info.szHwProfileGuid, sizeof(info.szHwProfileGuid) }; return std::string{ info.szHwProfileGuid, sizeof(info.szHwProfileGuid) };
return hw_profile_info;
} }
std::string get_protected_data() std::string get_protected_data()
@@ -62,7 +52,7 @@ namespace auth
return {}; return {};
} }
const auto size = std::min<DWORD>(data_out.cbData, 52); const auto size = std::min(data_out.cbData, 52ul);
std::string result{ reinterpret_cast<char*>(data_out.pbData), size }; std::string result{ reinterpret_cast<char*>(data_out.pbData), size };
LocalFree(data_out.pbData); LocalFree(data_out.pbData);
@@ -86,80 +76,17 @@ namespace auth
return entropy; return entropy;
} }
bool load_key(utils::cryptography::ecc::key& key) utils::cryptography::ecc::key& get_key()
{ {
std::string data{}; static auto key = utils::cryptography::ecc::generate_key(512, get_key_entropy());
auto key_path = (utils::properties::get_appdata_path() / "iw6-private.key").generic_string();
if (!utils::io::read_file(key_path, &data))
{
return false;
}
key.deserialize(data);
if (!key.is_valid())
{
console::error("Loaded key is invalid!\n");
return false;
}
return true;
}
utils::cryptography::ecc::key generate_key()
{
auto key = utils::cryptography::ecc::generate_key(512, get_key_entropy());
if (!key.is_valid())
{
throw std::runtime_error("Failed to generate cryptographic key!");
}
auto key_path = (utils::properties::get_appdata_path() / "iw6-private.key").generic_string();
if (!utils::io::write_file(key_path, key.serialize()))
{
console::error("Failed to write cryptographic key!\n");
}
console::info("Generated cryptographic key: %llX\n", key.get_hash());
return key; return key;
} }
utils::cryptography::ecc::key load_or_generate_key() int send_connect_data_stub(game::netsrc_t sock, game::netadr_t* adr, const char* format, const int len)
{
utils::cryptography::ecc::key key{};
if (load_key(key))
{
console::info("Loaded cryptographic key: %llX\n", key.get_hash());
return key;
}
return generate_key();
}
utils::cryptography::ecc::key get_key_internal()
{
auto key = load_or_generate_key();
auto key_path = (utils::properties::get_appdata_path() / "iw6-public.key").generic_string();
if (!utils::io::write_file(key_path, key.get_public_key()))
{
console::error("Failed to write public key!\n");
}
return key;
}
const utils::cryptography::ecc::key& get_key()
{
static auto key = get_key_internal();
return key;
}
int send_connect_data_stub(game::netsrc_t sock, game::netadr_s* adr, const char* format, const int len)
{ {
std::string connect_string(format, len); std::string connect_string(format, len);
game::SV_Cmd_TokenizeString(connect_string.data()); game::SV_Cmd_TokenizeString(connect_string.data());
const auto _0 = gsl::finally([]() const auto _ = gsl::finally([]()
{ {
game::SV_Cmd_EndTokenizedString(); game::SV_Cmd_EndTokenizedString();
}); });
@@ -182,14 +109,14 @@ namespace auth
proto::network::connect_info info; proto::network::connect_info info;
info.set_publickey(get_key().get_public_key()); info.set_publickey(get_key().get_public_key());
info.set_signature(utils::cryptography::ecc::sign_message(get_key(), challenge)); info.set_signature(sign_message(get_key(), challenge));
info.set_infostring(connect_string); info.set_infostring(connect_string);
network::send(*adr, "connect", info.SerializeAsString()); network::send(*adr, "connect", info.SerializeAsString());
return true; return true;
} }
void direct_connect(game::netadr_s* from, game::msg_t* msg) void direct_connect(game::netadr_t* from, game::msg_t* msg)
{ {
const auto offset = sizeof("connect") + 4; const auto offset = sizeof("connect") + 4;
@@ -223,14 +150,14 @@ namespace auth
utils::cryptography::ecc::key key; utils::cryptography::ecc::key key;
key.set(info.publickey()); key.set(info.publickey());
const auto xuid = std::strtoull(steam_id.data(), nullptr, 16); const auto xuid = strtoull(steam_id.data(), nullptr, 16);
if (xuid != key.get_hash()) if (xuid != key.get_hash())
{ {
network::send(*from, "error", "XUID doesn't match the certificate!", '\n'); network::send(*from, "error", "XUID doesn't match the certificate!", '\n');
return; return;
} }
if (!key.is_valid() || !utils::cryptography::ecc::verify_message(key, challenge, info.signature())) if (!key.is_valid() || !verify_message(key, challenge, info.signature()))
{ {
network::send(*from, "error", "Challenge signature was invalid!", '\n'); network::send(*from, "error", "Challenge signature was invalid!", '\n');
return; return;
@@ -272,6 +199,14 @@ namespace auth
void post_unpack() override void post_unpack() override
{ {
// Patch steam id bit check // Patch steam id bit check
if (game::environment::is_sp())
{
utils::hook::jump(0x1404BF3F0, 0x1404BF446);
utils::hook::jump(0x1404C02AF, 0x1404C02F0);
utils::hook::jump(0x1404C07F4, 0x1404C0842);
}
else
{
utils::hook::jump(0x140585410, 0x140585466); utils::hook::jump(0x140585410, 0x140585466);
utils::hook::jump(0x140142252, 0x140142293); utils::hook::jump(0x140142252, 0x140142293);
utils::hook::jump(0x140142334, 0x140142389); utils::hook::jump(0x140142334, 0x140142389);
@@ -280,8 +215,9 @@ namespace auth
utils::hook::jump(0x140479636, get_direct_connect_stub(), true); utils::hook::jump(0x140479636, get_direct_connect_stub(), true);
utils::hook::call(0x1402C4F8E, send_connect_data_stub); utils::hook::call(0x1402C4F8E, send_connect_data_stub);
}
command::add("guid", []() -> void command::add("guid", []
{ {
console::info("Your guid: %llX\n", steam::SteamUser()->GetSteamID().bits); console::info("Your guid: %llX\n", steam::SteamUser()->GetSteamID().bits);
}); });
+9 -5
View File
@@ -25,7 +25,9 @@ namespace binding
if (value && value < 100) if (value && value < 100)
{ {
const auto len = game::Com_sprintf(&buffer[bytes_used], (buffer_size_align - bytes_used), "bind %s \"%s\"\n", key_button, game::command_whitelist[value]); const auto len = sprintf_s(&buffer[bytes_used], (buffer_size_align - bytes_used),
"bind %s \"%s\"\n", key_button, game::command_whitelist[value]);
if (len < 0) if (len < 0)
{ {
return bytes_used; return bytes_used;
@@ -38,7 +40,9 @@ namespace binding
value -= 100; value -= 100;
if (static_cast<size_t>(value) < custom_binds.size() && !custom_binds[value].empty()) if (static_cast<size_t>(value) < custom_binds.size() && !custom_binds[value].empty())
{ {
const auto len = game::Com_sprintf(&buffer[bytes_used], (buffer_size_align - bytes_used), "bind %s \"%s\"\n", key_button, custom_binds[value].data()); const auto len = sprintf_s(&buffer[bytes_used], (buffer_size_align - bytes_used),
"bind %s \"%s\"\n", key_button, custom_binds[value].data());
if (len < 0) if (len < 0)
{ {
return bytes_used; return bytes_used;
@@ -115,13 +119,13 @@ namespace binding
} }
// write all bindings to config file // write all bindings to config file
utils::hook::call(0x1402C465B, key_write_bindings_to_buffer_stub); utils::hook::call(SELECT_VALUE(0x14023DF2B, 0x1402C465B), key_write_bindings_to_buffer_stub);
// links a custom command to an index // links a custom command to an index
utils::hook::jump(0x1402C3E50, key_get_binding_for_cmd_stub); utils::hook::jump(SELECT_VALUE(0x14023D6E0, 0x1402C3E50), key_get_binding_for_cmd_stub);
// execute custom binds // execute custom binds
cl_execute_key_hook.create(0x1402BF0E0, &cl_execute_key_stub); cl_execute_key_hook.create(SELECT_VALUE(0x140239970, 0x1402BF0E0), &cl_execute_key_stub);
} }
}; };
} }
+8 -5
View File
@@ -13,8 +13,6 @@
#include <utils/hook.hpp> #include <utils/hook.hpp>
#include <utils/string.hpp> #include <utils/string.hpp>
#define ALLOW_CUSTOM_BOT_NAMES
namespace bots namespace bots
{ {
namespace namespace
@@ -146,7 +144,7 @@ namespace bots
{ {
bot_names_received = false; bot_names_received = false;
game::netadr_s master{}; game::netadr_t master{};
if (server_list::get_master_server(master)) if (server_list::get_master_server(master))
{ {
console::info("Getting bots...\n"); console::info("Getting bots...\n");
@@ -160,6 +158,11 @@ namespace bots
public: public:
void post_unpack() override void post_unpack() override
{ {
if (game::environment::is_sp())
{
return;
}
get_bot_name_hook.create(game::SV_BotGetRandomName, get_random_bot_name); get_bot_name_hook.create(game::SV_BotGetRandomName, get_random_bot_name);
command::add("spawnBot", [](const command::params& params) command::add("spawnBot", [](const command::params& params)
@@ -195,9 +198,9 @@ namespace bots
parse_bot_names_from_file(); parse_bot_names_from_file();
} }
network::on("getbotsResponse", [](const game::netadr_s& target, const std::string& data) network::on("getbotsResponse", [](const game::netadr_t& target, const std::string& data)
{ {
game::netadr_s master{}; game::netadr_t master{};
if (server_list::get_master_server(master) && !bot_names_received && target == master) if (server_list::get_master_server(master) && !bot_names_received && target == master)
{ {
bot_names = utils::string::split(data, '\n'); bot_names = utils::string::split(data, '\n');
+3 -2
View File
@@ -40,8 +40,9 @@ namespace branding
localized_strings::override("LUA_MENU_LEGAL_COPYRIGHT", "iw6-mod: " VERSION " by AlterWare.\n"); localized_strings::override("LUA_MENU_LEGAL_COPYRIGHT", "iw6-mod: " VERSION " by AlterWare.\n");
utils::hook::call(0x140414424, dvar_set_string_stub); utils::hook::call(SELECT_VALUE(0x1403BDABA, 0x140414424), dvar_set_string_stub);
ui_get_formatted_build_number_hook.create(0x1404D7C00, ui_get_formatted_build_number_stub); ui_get_formatted_build_number_hook.create(
SELECT_VALUE(0x140415FD0, 0x1404D7C00), ui_get_formatted_build_number_stub);
scheduler::loop([]() scheduler::loop([]()
{ {
+5
View File
@@ -28,6 +28,11 @@ namespace bullet
public: public:
void post_unpack() override void post_unpack() override
{ {
if (game::environment::is_sp())
{
return;
}
dvars::bg_surfacePenetration = game::Dvar_RegisterFloat("bg_surfacePenetration", 0.0f, dvars::bg_surfacePenetration = game::Dvar_RegisterFloat("bg_surfacePenetration", 0.0f,
0.0f, std::numeric_limits<float>::max(), game::DVAR_FLAG_SAVED, 0.0f, std::numeric_limits<float>::max(), game::DVAR_FLAG_SAVED,
"Set to a value greater than 0 to override the surface penetration depth"); "Set to a value greater than 0 to override the surface penetration depth");
+22 -33
View File
@@ -76,16 +76,7 @@ namespace colors
void com_clean_name_stub(const char* in, char* out, const int out_size) void com_clean_name_stub(const char* in, char* out, const int out_size)
{ {
// Check that the name is at least 3 char without colors strncpy_s(out, out_size, in, _TRUNCATE);
char name[16]{};
game::I_strncpyz(out, in, std::min<int>(out_size, sizeof(name)));
utils::string::strip(out, name, std::strlen(out) + 1);
if (std::strlen(name) < 3)
{
game::I_strncpyz(out, "UnnamedPlayer", std::min<int>(out_size, sizeof(name)));
}
} }
char* i_clean_str_stub(char* string) char* i_clean_str_stub(char* string)
@@ -95,29 +86,26 @@ namespace colors
return string; return string;
} }
int cl_get_client_name_and_clan_tag_stub(const int local_client_num, const int index, char* name_buf, const int name_size, char* clan_tag_buf, int clan_tag_size) size_t get_client_name_stub(const int local_client_num, const int index, char* name_buf,
const int max_name_size, char* clan_buf, const int max_clan_size)
{ {
// CL_GetClientNameAndClanTag -> CL_GetClientNameAndClanTagColorize // CL_GetClientNameAndClanTag (wrapper for CL_GetClientNameAndClanTagColorize)
const auto result = utils::hook::invoke<int>(0x1402CF790, local_client_num, index, name_buf, name_size, clan_tag_buf, clan_tag_size); const auto result = reinterpret_cast<size_t(*)(int, int, char*, int, char*, int)>(0x1402CF790)(
local_client_num, index, name_buf, max_name_size, clan_buf, max_clan_size);
utils::string::strip(name_buf, name_buf, name_size); utils::string::strip(name_buf, name_buf, static_cast<size_t>(max_name_size));
return result; return result;
} }
int com_sprintf_stub(char* dest, int size, const char* fmt, const char* name) int clean_agent_name_stub(char* out, int max_size, const char* fmt, const char* in)
{ {
const auto len = game::Com_sprintf(dest, size, fmt, name); // format agent overhead name like [%s]
if (len < 0) const auto length = sprintf_s(out, max_size, fmt, in);
{
game::I_strncpyz(dest, "UnnamedAgent", size);
return len;
}
// This should inherit the name of the owner (a player) which already passed the length check in Com_CleanName utils::string::strip(in, out, std::min(static_cast<size_t>(length), static_cast<size_t>(max_size)));
utils::string::strip(dest, dest, len + 1);
return len; return length;
} }
void rb_lookup_color_stub(const char index, DWORD* color) void rb_lookup_color_stub(const char index, DWORD* color)
@@ -126,11 +114,11 @@ namespace colors
if (index == '8') if (index == '8')
{ {
*color = *reinterpret_cast<DWORD*>(0x1480E85BC); *color = *reinterpret_cast<DWORD*>(SELECT_VALUE(0x145FFD958, 0x1480E85BC));
} }
else if (index == '9') else if (index == '9')
{ {
*color = *reinterpret_cast<DWORD*>(0x1480E85C0); *color = *reinterpret_cast<DWORD*>(SELECT_VALUE(0x145FFD95C, 0x1480E85C0));
} }
else if (index == ':') else if (index == ':')
{ {
@@ -138,7 +126,7 @@ namespace colors
} }
else if (index == ';') else if (index == ';')
{ {
*color = *reinterpret_cast<DWORD*>(0x1480E85C8); *color = *reinterpret_cast<DWORD*>(SELECT_VALUE(0x145FFD964, 0x1480E85C8));
} }
else else
{ {
@@ -157,22 +145,23 @@ namespace colors
return; return;
} }
if (!game::environment::is_sp())
{
// allows colored name in-game // allows colored name in-game
utils::hook::call(0x1403881CF, com_clean_name_stub); utils::hook::jump(0x1404F5FC0, com_clean_name_stub);
utils::hook::call(0x140388224, com_clean_name_stub);
// don't apply colors to overhead names // don't apply colors to overhead names
utils::hook::call(0x14025CE79, cl_get_client_name_and_clan_tag_stub); utils::hook::call(0x14025CE79, get_client_name_stub);
// don't apply colors to overhead names of agents (like dogs or juggernauts) // don't apply colors to overhead names of agents (like dogs or juggernauts)
// hook Com_sprintf in CL_GetAgentName utils::hook::call(0x1402CF760, clean_agent_name_stub);
utils::hook::call(0x1402CF760, com_sprintf_stub);
// patch I_CleanStr // patch I_CleanStr
utils::hook::jump(0x1404F63C0, i_clean_str_stub); utils::hook::jump(0x1404F63C0, i_clean_str_stub);
}
// force new colors // force new colors
utils::hook::jump(0x14062AE80, rb_lookup_color_stub); utils::hook::jump(SELECT_VALUE(0x14055DCC0, 0x14062AE80), rb_lookup_color_stub);
// add colors // add colors
add(0, 0, 0); // 0 - Black add(0, 0, 0); // 0 - Black
+140 -25
View File
@@ -2,7 +2,6 @@
#include "loader/component_loader.hpp" #include "loader/component_loader.hpp"
#include "game/game.hpp" #include "game/game.hpp"
#include "game/dvars.hpp" #include "game/dvars.hpp"
#include "game/engine/sv_game.hpp"
#include "command.hpp" #include "command.hpp"
#include "console.hpp" #include "console.hpp"
@@ -195,7 +194,7 @@ namespace command
void add_raw(const char* name, void (*callback)()) void add_raw(const char* name, void (*callback)())
{ {
game::Cmd_AddCommandInternal(name, callback, utils::memory::get_allocator()->allocate<game::cmd_function_s>()); game::Cmd_AddCommandInternal(name, callback, utils::memory::get_allocator()->allocate<game::cmd_function_t>());
} }
void add(const char* name, const std::function<void(const params&)>& callback) void add(const char* name, const std::function<void(const params&)>& callback)
@@ -232,11 +231,8 @@ namespace command
} }
void execute(std::string command, const bool sync) void execute(std::string command, const bool sync)
{
if (!command.ends_with('\n'))
{ {
command += "\n"; command += "\n";
}
if (sync) if (sync)
{ {
@@ -252,10 +248,17 @@ namespace command
{ {
public: public:
void post_unpack() override void post_unpack() override
{
if (game::environment::is_sp())
{
add_sp_commands();
}
else
{ {
utils::hook::call(0x14041213C, &parse_commandline_stub); utils::hook::call(0x14041213C, &parse_commandline_stub);
add_mp_commands(); add_mp_commands();
}
add_commands_generic(); add_commands_generic();
} }
@@ -315,7 +318,7 @@ namespace command
} }
console::info("================================ COMMAND DUMP =====================================\n"); console::info("================================ COMMAND DUMP =====================================\n");
game::cmd_function_s* cmd = *game::cmd_functions; game::cmd_function_t* cmd = *game::cmd_functions;
auto i = 0; auto i = 0;
while (cmd) while (cmd)
{ {
@@ -443,6 +446,105 @@ namespace command
}); });
} }
void add_sp_commands()
{
add("god", []
{
if (!game::SV_Loaded())
{
return;
}
game::sp::g_entities[0].flags ^= game::FL_GODMODE;
game::CG_GameMessage(0, utils::string::va("godmode %s",
game::sp::g_entities[0].flags & game::FL_GODMODE
? "^2on"
: "^1off"));
});
add("notarget", []
{
if (!game::SV_Loaded())
{
return;
}
game::sp::g_entities[0].flags ^= game::FL_NOTARGET;
game::CG_GameMessage(0, utils::string::va("notarget %s",
game::sp::g_entities[0].flags & game::FL_NOTARGET
? "^2on"
: "^1off"));
});
add("noclip", []
{
if (!game::SV_Loaded())
{
return;
}
game::sp::g_entities[0].client->flags ^= 1;
game::CG_GameMessage(0, utils::string::va("noclip %s",
game::sp::g_entities[0].client->flags & 1
? "^2on"
: "^1off"));
});
add("ufo", []
{
if (!game::SV_Loaded())
{
return;
}
game::sp::g_entities[0].client->flags ^= 2;
game::CG_GameMessage(0, utils::string::va("ufo %s",
game::sp::g_entities[0].client->flags & 2
? "^2on"
: "^1off"));
});
add("give", [](const params& params)
{
if (!game::SV_Loaded())
{
return;
}
if (params.size() < 2)
{
game::CG_GameMessage(0, "You did not specify a weapon name");
return;
}
auto ps = game::SV_GetPlayerstateForClientNum(0);
auto wp = game::G_GetWeaponForName(params.get(1));
if (game::G_GivePlayerWeapon(ps, wp, 0, 0, 0))
{
game::G_InitializeAmmo(ps, wp, 0);
game::G_SelectWeapon(0, wp);
}
});
add("take", [](const params& params)
{
if (!game::SV_Loaded())
{
return;
}
if (params.size() < 2)
{
game::CG_GameMessage(0, "You did not specify a weapon name");
return;
}
auto ps = game::SV_GetPlayerstateForClientNum(0);
auto wp = game::G_GetWeaponForName(params.get(1));
game::G_TakePlayerWeapon(ps, wp);
});
}
static void add_mp_commands() static void add_mp_commands()
{ {
client_command_hook.create(0x1403929B0, &client_command); client_command_hook.create(0x1403929B0, &client_command);
@@ -451,64 +553,77 @@ namespace command
{ {
if (!dvars::sv_cheats->current.enabled) if (!dvars::sv_cheats->current.enabled)
{ {
game::engine::SV_GameSendServerCommand(client_num, game::SV_CMD_RELIABLE, "f \"Cheats are not enabled on this server\""); game::SV_GameSendServerCommand(client_num, 1, "f \"Cheats are not enabled on this server\"");
return; return;
} }
game::mp::g_entities[client_num].flags ^= game::FL_GODMODE; game::mp::g_entities[client_num].flags ^= game::FL_GODMODE;
game::engine::SV_GameSendServerCommand(client_num, game::SV_CMD_RELIABLE, utils::string::va("f \"godmode %s\"", game::mp::g_entities[client_num].flags & game::FL_GODMODE ? "^2on" : "^1off")); game::SV_GameSendServerCommand(client_num, 1,
utils::string::va("f \"godmode %s\"",
game::mp::g_entities[client_num].flags & game::FL_GODMODE
? "^2on"
: "^1off"));
}); });
add_sv("notarget", [](const int client_num, const params_sv&) add_sv("notarget", [](const int client_num, const params_sv&)
{ {
if (!dvars::sv_cheats->current.enabled) if (!dvars::sv_cheats->current.enabled)
{ {
game::engine::SV_GameSendServerCommand(client_num, game::SV_CMD_RELIABLE, "f \"Cheats are not enabled on this server\""); game::SV_GameSendServerCommand(client_num, 1, "f \"Cheats are not enabled on this server\"");
return; return;
} }
game::mp::g_entities[client_num].flags ^= game::FL_NOTARGET; game::mp::g_entities[client_num].flags ^= game::FL_NOTARGET;
game::engine::SV_GameSendServerCommand(client_num, game::SV_CMD_RELIABLE, game::SV_GameSendServerCommand(client_num, 1,
utils::string::va("f \"notarget %s\"", game::mp::g_entities[client_num].flags & game::FL_NOTARGET ? "^2on" : "^1off")); utils::string::va("f \"notarget %s\"",
game::mp::g_entities[client_num].flags & game::FL_NOTARGET
? "^2on"
: "^1off"));
}); });
add_sv("noclip", [](const int client_num, const params_sv&) add_sv("noclip", [](const int client_num, const params_sv&)
{ {
if (!dvars::sv_cheats->current.enabled) if (!dvars::sv_cheats->current.enabled)
{ {
game::engine::SV_GameSendServerCommand(client_num, game::SV_CMD_RELIABLE, "f \"Cheats are not enabled on this server\""); game::SV_GameSendServerCommand(client_num, 1, "f \"Cheats are not enabled on this server\"");
return; return;
} }
game::mp::g_entities[client_num].client->flags ^= 1; game::mp::g_entities[client_num].client->flags ^= 1;
game::engine::SV_GameSendServerCommand(client_num, game::SV_CMD_RELIABLE, game::SV_GameSendServerCommand(client_num, 1,
utils::string::va("f \"noclip %s\"", game::mp::g_entities[client_num].client->flags & 1 ? "^2on" : "^1off")); utils::string::va("f \"noclip %s\"",
game::mp::g_entities[client_num].client->flags & 1
? "^2on"
: "^1off"));
}); });
add_sv("ufo", [](const int client_num, const params_sv&) add_sv("ufo", [](const int client_num, const params_sv&)
{ {
if (!dvars::sv_cheats->current.enabled) if (!dvars::sv_cheats->current.enabled)
{ {
game::engine::SV_GameSendServerCommand(client_num, game::SV_CMD_RELIABLE, "f \"Cheats are not enabled on this server\""); game::SV_GameSendServerCommand(client_num, 1, "f \"Cheats are not enabled on this server\"");
return; return;
} }
game::mp::g_entities[client_num].client->flags ^= 2; game::mp::g_entities[client_num].client->flags ^= 2;
game::engine::SV_GameSendServerCommand(client_num, game::SV_CMD_RELIABLE, game::SV_GameSendServerCommand(client_num, 1,
utils::string::va("f \"ufo %s\"", game::mp::g_entities[client_num].client->flags & 2 ? "^2on" : "^1off")); utils::string::va("f \"ufo %s\"",
game::mp::g_entities[client_num].client->flags & 2
? "^2on"
: "^1off"));
}); });
add_sv("setviewpos", [](const int client_num, const params_sv& params) add_sv("setviewpos", [](const int client_num, const params_sv& params)
{ {
if (!dvars::sv_cheats->current.enabled) if (!dvars::sv_cheats->current.enabled)
{ {
game::engine::SV_GameSendServerCommand(client_num, game::SV_CMD_RELIABLE, "f \"Cheats are not enabled on this server\""); game::SV_GameSendServerCommand(client_num, 1, "f \"Cheats are not enabled on this server\"");
return; return;
} }
if (params.size() < 4) if (params.size() < 4)
{ {
game::engine::SV_GameSendServerCommand(client_num, game::SV_CMD_RELIABLE, game::SV_GameSendServerCommand(client_num, 1,
"f \"You did not specify the correct number of coordinates\""); "f \"You did not specify the correct number of coordinates\"");
return; return;
} }
@@ -522,13 +637,13 @@ namespace command
{ {
if (!dvars::sv_cheats->current.enabled) if (!dvars::sv_cheats->current.enabled)
{ {
game::engine::SV_GameSendServerCommand(client_num, game::SV_CMD_RELIABLE, "f \"Cheats are not enabled on this server\""); game::SV_GameSendServerCommand(client_num, 1, "f \"Cheats are not enabled on this server\"");
return; return;
} }
if (params.size() < 4) if (params.size() < 4)
{ {
game::engine::SV_GameSendServerCommand(client_num, game::SV_CMD_RELIABLE, game::SV_GameSendServerCommand(client_num, 1,
"f \"You did not specify the correct number of coordinates\""); "f \"You did not specify the correct number of coordinates\"");
return; return;
} }
@@ -542,13 +657,13 @@ namespace command
{ {
if (!dvars::sv_cheats->current.enabled) if (!dvars::sv_cheats->current.enabled)
{ {
game::engine::SV_GameSendServerCommand(client_num, game::SV_CMD_RELIABLE, "f \"Cheats are not enabled on this server\""); game::SV_GameSendServerCommand(client_num, 1, "f \"Cheats are not enabled on this server\"");
return; return;
} }
if (params.size() < 2) if (params.size() < 2)
{ {
game::engine::SV_GameSendServerCommand(client_num, game::SV_CMD_RELIABLE, "f \"You did not specify a weapon name\""); game::SV_GameSendServerCommand(client_num, 1, "f \"You did not specify a weapon name\"");
return; return;
} }
@@ -565,13 +680,13 @@ namespace command
{ {
if (!dvars::sv_cheats->current.enabled) if (!dvars::sv_cheats->current.enabled)
{ {
game::engine::SV_GameSendServerCommand(client_num, game::SV_CMD_RELIABLE, "f \"Cheats are not enabled on this server\""); game::SV_GameSendServerCommand(client_num, 1, "f \"Cheats are not enabled on this server\"");
return; return;
} }
if (params.size() < 2) if (params.size() < 2)
{ {
game::engine::SV_GameSendServerCommand(client_num, game::SV_CMD_RELIABLE, "f \"You did not specify a weapon name\""); game::SV_GameSendServerCommand(client_num, 1, "f \"You did not specify a weapon name\"");
return; return;
} }
+8 -7
View File
@@ -84,14 +84,15 @@ namespace console
void print_stub(const char* fmt, ...) void print_stub(const char* fmt, ...)
{ {
char buffer[4096]{};
va_list ap; va_list ap;
va_start(ap, fmt); va_start(ap, fmt);
[[maybe_unused]] const auto len = vsnprintf(buffer, sizeof(buffer), fmt, ap);
va_end(ap);
char buffer[4096]{};
const auto res = vsnprintf_s(buffer, _TRUNCATE, fmt, ap);
(void)res;
print_message(buffer); print_message(buffer);
va_end(ap);
} }
void append_text(const char* text) void append_text(const char* text)
@@ -125,7 +126,7 @@ namespace console
void post_unpack() override void post_unpack() override
{ {
// Redirect input (]command) // Redirect input (]command)
utils::hook::jump(0x140502A80, append_text); utils::hook::jump(SELECT_VALUE(0x14043DFA0, 0x140502A80), append_text);
utils::hook::jump(printf, print_stub); utils::hook::jump(printf, print_stub);
@@ -219,7 +220,7 @@ namespace console
HWND get_window() HWND get_window()
{ {
return *reinterpret_cast<HWND*>(0x147AD1DB0); return *reinterpret_cast<HWND*>((SELECT_VALUE(0x145A7B490, 0x147AD1DB0)));
} }
void set_title(std::string title) void set_title(std::string title)
@@ -241,7 +242,7 @@ namespace console
SetWindowPos(get_window(), nullptr, rect.left, rect.top, width, height, 0); SetWindowPos(get_window(), nullptr, rect.left, rect.top, width, height, 0);
auto* const logo_window = *reinterpret_cast<HWND*>(0x147AD1DC0); auto* const logo_window = *reinterpret_cast<HWND*>(SELECT_VALUE(0x145A7B4A0, 0x147AD1DC0));
SetWindowPos(logo_window, nullptr, 5, 5, width - 25, 60, 0); SetWindowPos(logo_window, nullptr, 5, 5, width - 25, 60, 0);
} }
+1 -1
View File
@@ -49,7 +49,7 @@ namespace cursor
{ {
// This is the legacy cursor // This is the legacy cursor
// TODO: Find the new cursor drawing... // TODO: Find the new cursor drawing...
utils::hook::call(0x1404BB119, draw_cursor_stub); utils::hook::call(SELECT_VALUE(0x14040117E, 0x1404BB119), draw_cursor_stub);
scheduler::loop([]() scheduler::loop([]()
{ {
+23 -1
View File
@@ -110,7 +110,7 @@ namespace dedicated
return; return;
} }
game::netadr_s target{}; game::netadr_t target{};
if (server_list::get_master_server(target)) if (server_list::get_master_server(target))
{ {
network::send(target, "heartbeat", "IW6"); network::send(target, "heartbeat", "IW6");
@@ -198,6 +198,25 @@ namespace dedicated
return hwnd; return hwnd;
} }
void sys_error_stub(const char* msg, ...)
{
char buffer[2048]{};
va_list ap;
va_start(ap, msg);
vsnprintf_s(buffer, _TRUNCATE, msg, ap);
va_end(ap);
scheduler::once([]()
{
command::execute("map_rotate");
}, scheduler::pipeline::main, 3s);
game::Com_Error(game::ERR_DROP, "%s", buffer);
}
void add_commands() void add_commands()
{ {
command::add("map", [](const command::params& params) command::add("map", [](const command::params& params)
@@ -345,6 +364,9 @@ namespace dedicated
utils::hook::nop(0x1404F8BE1, 2); // ^ utils::hook::nop(0x1404F8BE1, 2); // ^
utils::hook::set<uint8_t>(0x140328660, 0xC3); // Disable image pak file loading utils::hook::set<uint8_t>(0x140328660, 0xC3); // Disable image pak file loading
// Stop crashing from sys_errors
utils::hook::jump(0x1404FF510, sys_error_stub);
// Reduce min required memory // Reduce min required memory
utils::hook::set<uint64_t>(0x1404FA6BD, 0x80000000); utils::hook::set<uint64_t>(0x1404FA6BD, 0x80000000);
utils::hook::set<uint64_t>(0x1404FA76F, 0x80000000); utils::hook::set<uint64_t>(0x1404FA76F, 0x80000000);
+8 -7
View File
@@ -25,28 +25,28 @@ namespace dedicated_info
scheduler::loop([]() scheduler::loop([]()
{ {
const auto* sv_running = game::Dvar_FindVar("sv_running"); auto* sv_running = game::Dvar_FindVar("sv_running");
if (!sv_running || !sv_running->current.enabled) if (!sv_running || !sv_running->current.enabled)
{ {
console::set_title("iw6-mod Dedicated Server"); console::set_title("iw6-mod Dedicated Server");
return; return;
} }
const auto* sv_hostname = game::Dvar_FindVar("sv_hostname"); auto* const sv_hostname = game::Dvar_FindVar("sv_hostname");
const auto* sv_maxclients = game::Dvar_FindVar("sv_maxclients"); auto* const sv_maxclients = game::Dvar_FindVar("sv_maxclients");
const auto* mapname = game::Dvar_FindVar("mapname"); auto* const mapname = game::Dvar_FindVar("mapname");
auto client_count = 0; auto client_count = 0;
auto bot_count = 0; auto bot_count = 0;
for (auto i = 0; i < sv_maxclients->current.integer; ++i) for (auto i = 0; i < sv_maxclients->current.integer; i++)
{ {
auto* client = &game::mp::svs_clients[i]; auto* client = &game::mp::svs_clients[i];
auto* self = &game::mp::g_entities[i]; auto* self = &game::mp::g_entities[i];
if (client->header.state > game::CS_FREE && self && self->client) if (client->header.state > game::CS_FREE && self && self->client)
{ {
++client_count; client_count++;
if (game::SV_BotIsBot(i)) if (game::SV_BotIsBot(i))
{ {
++bot_count; ++bot_count;
@@ -56,7 +56,8 @@ namespace dedicated_info
std::string cleaned_hostname = sv_hostname->current.string; std::string cleaned_hostname = sv_hostname->current.string;
utils::string::strip(sv_hostname->current.string, cleaned_hostname.data(), cleaned_hostname.size() + 1); utils::string::strip(sv_hostname->current.string, cleaned_hostname.data(),
cleaned_hostname.size() + 1);
console::set_title(utils::string::va("%s on %s [%d/%d] (%d)", cleaned_hostname.data(), console::set_title(utils::string::va("%s on %s [%d/%d] (%d)", cleaned_hostname.data(),
mapname->current.string, client_count, mapname->current.string, client_count,
+2 -2
View File
@@ -222,7 +222,7 @@ namespace demonware
va_list ap; va_list ap;
va_start(ap, msg); va_start(ap, msg);
vsnprintf(buffer, sizeof(buffer), msg, ap); vsnprintf_s(buffer, _TRUNCATE, msg, ap);
printf("%s: %s\n", function, buffer); printf("%s: %s\n", function, buffer);
va_end(ap); va_end(ap);
@@ -442,7 +442,7 @@ namespace demonware
void post_unpack() override void post_unpack() override
{ {
utils::hook::jump(0x1406F54D0, bd_logger_stub); utils::hook::jump(SELECT_VALUE(0x140602230, 0x1406F54D0), bd_logger_stub);
} }
void pre_destroy() override void pre_destroy() override
+4 -2
View File
@@ -37,7 +37,7 @@ namespace discord
if (!game::CL_IsCgameInitialized()) if (!game::CL_IsCgameInitialized())
{ {
discord_presence.details = "Multiplayer"; discord_presence.details = game::environment::is_sp() ? "Singleplayer" : "Multiplayer";
discord_presence.state = "Main Menu"; discord_presence.state = "Main Menu";
discord_presence.partySize = 0; discord_presence.partySize = 0;
@@ -47,6 +47,8 @@ namespace discord
} }
else else
{ {
if (game::environment::is_sp()) return;
const auto* gametype = game::UI_LocalizeGametype(game::Dvar_FindVar("ui_gametype")->current.string); const auto* gametype = game::UI_LocalizeGametype(game::Dvar_FindVar("ui_gametype")->current.string);
const auto* map = game::UI_LocalizeMapname(game::Dvar_FindVar("ui_mapname")->current.string); const auto* map = game::UI_LocalizeMapname(game::Dvar_FindVar("ui_mapname")->current.string);
@@ -69,7 +71,7 @@ namespace discord
discord_presence.state = host_name; discord_presence.state = host_name;
discord_presence.partyMax = party::server_client_count(); discord_presence.partyMax = party::server_client_count();
std::hash<game::netadr_s> hash_fn; std::hash<game::netadr_t> hash_fn;
static const auto nonce = utils::cryptography::random::get_integer(); static const auto nonce = utils::cryptography::random::get_integer();
const auto& address = party::get_target(); const auto& address = party::get_target();
+18 -17
View File
@@ -1,8 +1,8 @@
#include <std_include.hpp> #include <std_include.hpp>
#include "loader/component_loader.hpp" #include "loader/component_loader.hpp"
#include "game/game.hpp" #include "game/game.hpp"
#include "game/dvars.hpp" #include "game/dvars.hpp"
#include "game/engine/sv_game.hpp"
#include "console.hpp" #include "console.hpp"
@@ -11,7 +11,7 @@
namespace dvar_cheats namespace dvar_cheats
{ {
void apply_sv_cheats(const game::dvar_t* dvar, const game::DvarSetSource source, game::DvarValue* value) void apply_sv_cheats(const game::dvar_t* dvar, const game::DvarSetSource source, game::dvar_value* value)
{ {
if (dvar && dvar->name == "sv_cheats"s) if (dvar && dvar->name == "sv_cheats"s)
{ {
@@ -110,26 +110,25 @@ namespace dvar_cheats
a.jmp(0x1404F0D74); a.jmp(0x1404F0D74);
}); });
void cg_set_client_dvar_from_server(const int local_client_num, game::mp::cg_s* cg, const char* dvar_id, const char* value) void cg_set_client_dvar_from_server(const int local_client_num, game::mp::cg_t* cg, const char* dvar_id, const char* value)
{ {
const auto* dvar = game::Dvar_FindVar(dvar_id); if (dvar_id == "cg_fov"s || dvar_id == "com_maxfps"s)
{
return;
}
if (!dvar) const auto* dvar = game::Dvar_FindVar(dvar_id);
if (dvar)
{
// If we send as string, it can't be set with source SERVERCMD because the game only allows that source on real server cmd dvars.
// Just use external instead as if it was being set by the console
game::Dvar_SetFromStringByNameFromSource(dvar_id, value, game::DvarSetSource::DVAR_SOURCE_EXTERNAL);
}
else
{ {
// Not a dvar name, assume it is an id and the game will handle normally // Not a dvar name, assume it is an id and the game will handle normally
game::CG_SetClientDvarFromServer(local_client_num, cg, dvar_id, value); game::CG_SetClientDvarFromServer(local_client_num, cg, dvar_id, value);
return;
} }
if (dvar && ((dvar->flags & game::DVAR_FLAG_SAVED) != 0))
{
console::info("Not allowing server to override archive dvar '%s'\n", dvar->name);
return;
}
// If we send as string, it can't be set with source SERVERCMD because the game only allows that source on real server cmd dvars.
// Just use external instead as if it was being set by the console
game::Dvar_SetFromStringByNameFromSource(dvar_id, value, game::DVAR_SOURCE_EXTERNAL);
} }
void set_client_dvar_by_string(const int entity_num, const char* value) void set_client_dvar_by_string(const int entity_num, const char* value)
@@ -137,7 +136,7 @@ namespace dvar_cheats
const auto* dvar = game::Scr_GetString(0); // grab the original dvar again since it's never stored on stack const auto* dvar = game::Scr_GetString(0); // grab the original dvar again since it's never stored on stack
const auto* command = utils::string::va("q %s \"%s\"", dvar, value); const auto* command = utils::string::va("q %s \"%s\"", dvar, value);
game::engine::SV_GameSendServerCommand(entity_num, game::SV_CMD_RELIABLE, command); game::SV_GameSendServerCommand(entity_num, 1, command);
} }
const auto player_cmd_set_client_dvar = utils::hook::assemble([](utils::hook::assembler& a) const auto player_cmd_set_client_dvar = utils::hook::assemble([](utils::hook::assembler& a)
@@ -171,6 +170,8 @@ namespace dvar_cheats
public: public:
void post_unpack() override void post_unpack() override
{ {
if (game::environment::is_sp()) return;
utils::hook::nop(0x1404F0D13, 4); // let our stub handle zero-source sets utils::hook::nop(0x1404F0D13, 4); // let our stub handle zero-source sets
utils::hook::jump(0x1404F0D1A, dvar_flag_checks_stub, true); // check extra dvar flags when setting values utils::hook::jump(0x1404F0D1A, dvar_flag_checks_stub, true); // check extra dvar flags when setting values
+28 -55
View File
@@ -1,16 +1,16 @@
#include <std_include.hpp> #include <std_include.hpp>
#include "loader/component_loader.hpp" #include "loader/component_loader.hpp"
#include "game/game.hpp" #include "game/game.hpp"
#include "game/dvars.hpp" #include "game/dvars.hpp"
#include "fastfiles.hpp"
#include "command.hpp" #include "command.hpp"
#include "console.hpp" #include "console.hpp"
#include "fastfiles.hpp"
#include <utils/hook.hpp> #include <utils/hook.hpp>
#include <utils/io.hpp>
#include <utils/memory.hpp> #include <utils/memory.hpp>
#include <utils/string.hpp> #include <utils/io.hpp>
namespace fastfiles namespace fastfiles
{ {
@@ -22,7 +22,7 @@ namespace fastfiles
void db_try_load_x_file_internal(const char* zone_name, const int zone_flags, const int is_base_map) void db_try_load_x_file_internal(const char* zone_name, const int zone_flags, const int is_base_map)
{ {
console::info("Loading fastfile %s\n", zone_name); console::info("Loading fastfile %s\n", zone_name);
db_try_load_x_file_internal_hook.invoke<void>(zone_name, zone_flags, is_base_map); return db_try_load_x_file_internal_hook.invoke<void>(zone_name, zone_flags, is_base_map);
} }
void dump_gsc_script(const std::string& name, game::XAssetHeader header) void dump_gsc_script(const std::string& name, game::XAssetHeader header)
@@ -32,12 +32,6 @@ namespace fastfiles
return; return;
} }
const auto out_name = std::format("gsc_dump/{}.gscbin", name);
if (utils::io::file_exists(out_name))
{
return;
}
std::string buffer; std::string buffer;
buffer.append(header.scriptfile->name, std::strlen(header.scriptfile->name) + 1); buffer.append(header.scriptfile->name, std::strlen(header.scriptfile->name) + 1);
buffer.append(reinterpret_cast<char*>(&header.scriptfile->compressedLen), 4); buffer.append(reinterpret_cast<char*>(&header.scriptfile->compressedLen), 4);
@@ -46,38 +40,12 @@ namespace fastfiles
buffer.append(header.scriptfile->buffer, header.scriptfile->compressedLen); buffer.append(header.scriptfile->buffer, header.scriptfile->compressedLen);
buffer.append(reinterpret_cast<char*>(header.scriptfile->bytecode), header.scriptfile->bytecodeLen); buffer.append(reinterpret_cast<char*>(header.scriptfile->bytecode), header.scriptfile->bytecodeLen);
const auto out_name = std::format("gsc_dump/{}.gscbin", name);
utils::io::write_file(out_name, buffer); utils::io::write_file(out_name, buffer);
console::info("Dumped %s\n", out_name.c_str()); console::info("Dumped %s\n", out_name.data());
} }
void dump_csv_table(const std::string& name, game::XAssetHeader header)
{
if (!dvars::g_dump_string_tables->current.enabled)
{
return;
}
const auto out_name = std::format("csv_dump/{}.csv", name);
if (utils::io::file_exists(out_name))
{
return;
}
std::string buffer;
for (auto row = 0; row < header.stringTable->rowCount; row++)
{
for (auto column = 0; column < header.stringTable->columnCount; column++)
{
const auto* string = header.stringTable->values[(row * header.stringTable->columnCount) + column].string;
buffer.append(utils::string::va("%s%s", string ? string : "", (column == header.stringTable->columnCount - 1) ? "\n" : ","));
}
}
utils::io::write_file(out_name, buffer);
console::info("Dumped %s\n", out_name.c_str());
}
game::XAssetHeader db_find_x_asset_header_stub(game::XAssetType type, const char* name, int allow_create_default) game::XAssetHeader db_find_x_asset_header_stub(game::XAssetType type, const char* name, int allow_create_default)
{ {
@@ -90,11 +58,6 @@ namespace fastfiles
dump_gsc_script(name, result); dump_gsc_script(name, result);
} }
if (type == game::ASSET_TYPE_STRINGTABLE)
{
dump_csv_table(name, result);
}
if (diff > 100) if (diff > 100)
{ {
console::print( console::print(
@@ -143,16 +106,30 @@ namespace fastfiles
public: public:
void post_unpack() override void post_unpack() override
{ {
db_try_load_x_file_internal_hook.create(0x1403237F0, &db_try_load_x_file_internal); db_try_load_x_file_internal_hook.create(SELECT_VALUE(0x140275850, 0x1403237F0), &db_try_load_x_file_internal);
db_find_x_asset_header_hook.create(game::DB_FindXAssetHeader, db_find_x_asset_header_stub); db_find_x_asset_header_hook.create(game::DB_FindXAssetHeader, db_find_x_asset_header_stub);
dvars::g_dump_scripts = game::Dvar_RegisterBool("g_dumpScripts", false, game::DVAR_FLAG_NONE, "Dump GSC scripts to binary format"); dvars::g_dump_scripts = game::Dvar_RegisterBool("g_dumpScripts", false, game::DVAR_FLAG_NONE, "Dump GSC scripts to binary format");
dvars::g_dump_string_tables = game::Dvar_RegisterBool("g_dumpStringTables", false, game::DVAR_FLAG_NONE, "Dump CSV files");
utils::hook::call(0x140156350, p_mem_free_stub); utils::hook::call(SELECT_VALUE(0x1402752DF, 0x140156350), p_mem_free_stub);
utils::hook::call(0x140324259, p_mem_free_stub); utils::hook::call(SELECT_VALUE(0x140276004, 0x140324259), p_mem_free_stub);
command::add("loadzone", [](const command::params& params)
{
if (params.size() < 2)
{
console::info("usage: loadzone <zone>\n");
return;
}
game::XZoneInfo info;
info.name = params.get(1);
info.allocFlags = 1;
info.freeFlags = 0;
game::DB_LoadXAssets(&info, 1, game::DBSyncMode::DB_LOAD_SYNC);
});
// who asked for this this ?
command::add("materiallist", [](const command::params& params) command::add("materiallist", [](const command::params& params)
{ {
game::DB_EnumXAssets_FastFile(game::ASSET_TYPE_MATERIAL, [](const game::XAssetHeader header, void*) game::DB_EnumXAssets_FastFile(game::ASSET_TYPE_MATERIAL, [](const game::XAssetHeader header, void*)
@@ -164,14 +141,10 @@ namespace fastfiles
}, nullptr, false); }, nullptr, false);
}); });
if (!game::environment::is_sp())
{
reallocate_asset_pool(game::ASSET_TYPE_WEAPON, 320); reallocate_asset_pool(game::ASSET_TYPE_WEAPON, 320);
}
// Fixes ("Exceeded limit of 16 'font' assets.") new steam files use a 32 font pool (2026)
reallocate_asset_pool(game::ASSET_TYPE_FONT, 32);
// Allow loading of unsigned fastfiles
utils::hook::set<uint8_t>(0x1402FBF23, 0xEB); // DB_LoadXFile
utils::hook::nop(0x1402FC445, 2); // DB_SetFileLoadCompressor
} }
}; };
} }
+19 -15
View File
@@ -83,22 +83,15 @@ namespace filesystem
void startup() void startup()
{ {
const auto base = std::filesystem::current_path(); register_path("iw6");
register_path(base / "iw6");
register_path(get_binary_directory() + "\\data"); register_path(get_binary_directory() + "\\data");
if (get_binary_directory() != base)
{
register_path(base / "data");
}
// game's search paths // game's search paths
register_path(base / "devraw"); register_path("devraw");
register_path(base / "devraw_shared"); register_path("devraw_shared");
register_path(base / "raw_shared"); register_path("raw_shared");
register_path(base / "raw"); register_path("raw");
register_path(base / "main"); register_path("main");
} }
void check_for_startup() void check_for_startup()
@@ -275,12 +268,22 @@ namespace filesystem
void post_unpack() override void post_unpack() override
{ {
// Set fs_basegame // Set fs_basegame
utils::hook::inject(0x1404DDA13, "iw6"); utils::hook::inject(SELECT_VALUE(0x14041C053, 0x1404DDA13), "iw6");
#ifndef INJECT_HOST_AS_LIB #ifndef INJECT_HOST_AS_LIB
utils::hook::jump(0x1404F96C0, sys_default_install_path_stub); utils::hook::jump(SELECT_VALUE(0x1404351B0, 0x1404F96C0), sys_default_install_path_stub);
#endif #endif
if (game::environment::is_sp())
{
utils::hook::call(0x14041B744, fs_startup_stub);
utils::hook::call(0x14041CD00, register_custom_path_stub);
utils::hook::call(0x14041CD20, register_custom_path_stub);
utils::hook::call(0x14041CD5F, register_custom_path_stub);
}
else
{
utils::hook::call(0x1404DD704, fs_startup_stub); utils::hook::call(0x1404DD704, fs_startup_stub);
utils::hook::call(0x1404DDB43, fs_startup_stub); utils::hook::call(0x1404DDB43, fs_startup_stub);
@@ -288,6 +291,7 @@ namespace filesystem
utils::hook::call(0x1404DE570, register_custom_path_stub); utils::hook::call(0x1404DE570, register_custom_path_stub);
utils::hook::call(0x1404DE5AF, register_custom_path_stub); utils::hook::call(0x1404DE5AF, register_custom_path_stub);
} }
}
}; };
} }
+3 -3
View File
@@ -85,7 +85,7 @@ namespace fps
perf_calc_fps(&cg_perf, cg_perf.frame_ms); perf_calc_fps(&cg_perf, cg_perf.frame_ms);
utils::hook::invoke<void>(0x140658E30); utils::hook::invoke<void>(SELECT_VALUE(0x1405806E0, 0x140658E30));
} }
void cg_draw_fps() void cg_draw_fps()
@@ -167,10 +167,10 @@ namespace fps
// fps setup // fps setup
cg_perf.perf_start = std::chrono::high_resolution_clock::now(); cg_perf.perf_start = std::chrono::high_resolution_clock::now();
utils::hook::call(0x1402CF457, &perf_update); utils::hook::call(SELECT_VALUE(0x140242C11, 0x1402CF457), &perf_update);
// change cg_drawfps flags to saved // change cg_drawfps flags to saved
utils::hook::call(0x140272B98, &cg_draw_fps_register_stub); utils::hook::call(SELECT_VALUE(0x1401F400A, 0x140272B98), &cg_draw_fps_register_stub);
cg_drawPing = game::Dvar_RegisterInt("cg_drawPing", 0, 0, 1, game::DVAR_FLAG_SAVED, "Draw ping"); cg_drawPing = game::Dvar_RegisterInt("cg_drawPing", 0, 0, 1, game::DVAR_FLAG_SAVED, "Draw ping");
+11 -11
View File
@@ -63,7 +63,7 @@ namespace game_console
void clear() void clear()
{ {
game::I_strncpyz(con.buffer, "", sizeof(con.buffer)); strncpy_s(con.buffer, "", sizeof(con.buffer));
con.cursor = 0; con.cursor = 0;
fixed_input = ""; fixed_input = "";
@@ -249,7 +249,7 @@ namespace game_console
dvars::con_inputDvarInactiveValueColor->current.vector, offset); dvars::con_inputDvarInactiveValueColor->current.vector, offset);
} }
game::I_strncpyz(con.globals.auto_complete_choice, matches[0].data(), sizeof(con.globals.auto_complete_choice)); strncpy_s(con.globals.auto_complete_choice, matches[0].data(), sizeof(con.globals.auto_complete_choice));
con.globals.may_auto_complete = true; con.globals.may_auto_complete = true;
} }
else if (matches.size() > 1) else if (matches.size() > 1)
@@ -274,7 +274,7 @@ namespace game_console
} }
} }
game::I_strncpyz(con.globals.auto_complete_choice, matches[0].data(), sizeof(con.globals.auto_complete_choice)); strncpy_s(con.globals.auto_complete_choice, matches[0].data(), sizeof(con.globals.auto_complete_choice));
con.globals.may_auto_complete = true; con.globals.may_auto_complete = true;
} }
} }
@@ -364,11 +364,11 @@ namespace game_console
void print_internal(const char* fmt, ...) void print_internal(const char* fmt, ...)
{ {
char va_buffer[1024]{}; char va_buffer[0x200] = { 0 };
va_list ap; va_list ap;
va_start(ap, fmt); va_start(ap, fmt);
vsnprintf(va_buffer, sizeof(va_buffer), fmt, ap); vsprintf_s(va_buffer, fmt, ap);
va_end(ap); va_end(ap);
const auto formatted = std::string(va_buffer); const auto formatted = std::string(va_buffer);
@@ -424,7 +424,7 @@ namespace game_console
con.buffer[1] = '\0'; con.buffer[1] = '\0';
} }
game::I_strncat(con.buffer, sizeof(con.buffer), con.globals.auto_complete_choice); strncat_s(con.buffer, con.globals.auto_complete_choice, 64);
con.cursor = static_cast<int>(std::string(con.buffer).length()); con.cursor = static_cast<int>(std::string(con.buffer).length());
if (con.cursor != 254) if (con.cursor != 254)
@@ -549,7 +549,7 @@ namespace game_console
if (history_index != -1) if (history_index != -1)
{ {
game::I_strncpyz(con.buffer, history.at(history_index).c_str(), sizeof(con.buffer)); strncpy_s(con.buffer, history.at(history_index).c_str(), sizeof(con.buffer));
con.cursor = static_cast<int>(strlen(con.buffer)); con.cursor = static_cast<int>(strlen(con.buffer));
} }
} }
@@ -564,7 +564,7 @@ namespace game_console
if (history_index != -1) if (history_index != -1)
{ {
game::I_strncpyz(con.buffer, history.at(history_index).c_str(), sizeof(con.buffer)); strncpy_s(con.buffer, history.at(history_index).c_str(), sizeof(con.buffer));
con.cursor = static_cast<int>(strlen(con.buffer)); con.cursor = static_cast<int>(strlen(con.buffer));
} }
} }
@@ -674,7 +674,7 @@ namespace game_console
} }
} }
game::cmd_function_s* cmd = (*game::cmd_functions); game::cmd_function_t* cmd = (*game::cmd_functions);
while (cmd) while (cmd)
{ {
if (cmd->name) if (cmd->name)
@@ -721,7 +721,7 @@ namespace game_console
con.output_visible = false; con.output_visible = false;
con.display_line_offset = 0; con.display_line_offset = 0;
con.line_count = 0; con.line_count = 0;
game::I_strncpyz(con.buffer, "", sizeof(con.buffer)); strncpy_s(con.buffer, "", sizeof(con.buffer));
con.globals.x = 0.0f; con.globals.x = 0.0f;
con.globals.y = 0.0f; con.globals.y = 0.0f;
@@ -729,7 +729,7 @@ namespace game_console
con.globals.font_height = 0.0f; con.globals.font_height = 0.0f;
con.globals.may_auto_complete = false; con.globals.may_auto_complete = false;
con.globals.info_line_count = 0; con.globals.info_line_count = 0;
game::I_strncpyz(con.globals.auto_complete_choice, "", sizeof(con.globals.auto_complete_choice)); strncpy_s(con.globals.auto_complete_choice, "", sizeof(con.globals.auto_complete_choice));
// add clear command // add clear command
command::add("clear", [&]() command::add("clear", [&]()
+23 -14
View File
@@ -3,10 +3,12 @@
#include "game/game.hpp" #include "game/game.hpp"
#include "game/dvars.hpp" #include "game/dvars.hpp"
#include "console.hpp"
#include "game_log.hpp"
#include "scheduler.hpp" #include "scheduler.hpp"
#include "scripting.hpp" #include "scripting.hpp"
#include "console.hpp"
#include "game_log.hpp"
#include "gsc/script_extension.hpp"
#include <utils/hook.hpp> #include <utils/hook.hpp>
#include <utils/io.hpp> #include <utils/io.hpp>
@@ -21,7 +23,7 @@ namespace game_log
char buf[1024]{}; char buf[1024]{};
std::size_t out_chars = 0; std::size_t out_chars = 0;
for (std::uint32_t i = 0; i < game::Scr_GetNumParam(); ++i) for (auto i = 0u; i < game::Scr_GetNumParam(); ++i)
{ {
const auto* value = game::Scr_GetString(i); const auto* value = game::Scr_GetString(i);
const auto len = std::strlen(value); const auto len = std::strlen(value);
@@ -32,7 +34,7 @@ namespace game_log
break; break;
} }
game::I_strncat(buf, sizeof(buf), value); strncat_s(buf, value, _TRUNCATE);
} }
g_log_printf("%s", buf); g_log_printf("%s", buf);
@@ -47,15 +49,22 @@ namespace game_log
return; return;
} }
char buffer[1024]{}; char buffer[0x400]{};
va_list ap; va_list ap;
va_start(ap, fmt); va_start(ap, fmt);
vsnprintf(buffer, sizeof(buffer), fmt, ap);
vsprintf_s(buffer, fmt, ap);
va_end(ap); va_end(ap);
const auto time = *game::level_time / 1000; const auto time = *game::level_time / 1000;
utils::io::write_file(log, utils::string::va("%3i:%i%i %s", time / 60, time % 60 / 10, time % 60 % 10, buffer), true); utils::io::write_file(log, utils::string::va("%3i:%i%i %s",
time / 60,
time % 60 / 10,
time % 60 % 10,
buffer
), true);
} }
class component final : public component_interface class component final : public component_interface
@@ -63,6 +72,11 @@ namespace game_log
public: public:
void post_load() override void post_load() override
{ {
if (game::environment::is_sp())
{
return;
}
utils::hook::set<game::BuiltinFunction>(0x1409E8A20, gscr_log_print); utils::hook::set<game::BuiltinFunction>(0x1409E8A20, gscr_log_print);
scheduler::once([] scheduler::once([]
@@ -88,14 +102,9 @@ namespace game_log
g_log_printf("InitGame\n"); g_log_printf("InitGame\n");
}); });
scripting::on_shutdown([](const int clear_scripts, const int post_shutdown) -> void scripting::on_shutdown([](int free_scripts)
{ {
if (post_shutdown) console::info("==== ShutdownGame (%d) ====\n", free_scripts);
{
return;
}
console::info("==== ShutdownGame (%d) ====\n", clear_scripts);
g_log_printf("ShutdownGame:\n"); g_log_printf("ShutdownGame:\n");
g_log_printf("------------------------------------------------------------\n"); g_log_printf("------------------------------------------------------------\n");
+46 -14
View File
@@ -65,6 +65,30 @@ namespace gameplay
a.jmp(0x140383796); a.jmp(0x140383796);
}); });
const auto pm_bouncing_stub_sp = utils::hook::assemble([](utils::hook::assembler& a)
{
const auto no_bounce = a.newLabel();
const auto loc_14046ED26 = a.newLabel();
a.push(rax);
a.mov(rax, qword_ptr(reinterpret_cast<int64_t>(&dvars::pm_bouncing)));
a.mov(al, byte_ptr(rax, 0x10));
a.cmp(ptr(rbp, -0x40), al);
a.pop(rax);
a.jz(no_bounce);
a.jmp(0x14046EC7E);
a.bind(no_bounce);
a.cmp(ptr(rbp, -0x80), r13d);
a.jnz(loc_14046ED26);
a.jmp(0x14046EC6C);
a.bind(loc_14046ED26);
a.jmp(0x14046ED26);
});
const auto pm_bouncing_stub_mp = utils::hook::assemble([](utils::hook::assembler& a) const auto pm_bouncing_stub_mp = utils::hook::assemble([](utils::hook::assembler& a)
{ {
const auto no_bounce = a.newLabel(); const auto no_bounce = a.newLabel();
@@ -109,7 +133,7 @@ namespace gameplay
return jump_height; return jump_height;
} }
void jump_apply_slowdown_stub(game::mp::playerState_s* ps) void jump_apply_slowdown_stub(game::mp::playerState_t* ps)
{ {
assert(ps->pm_flags & game::PMF_JUMPING); assert(ps->pm_flags & game::PMF_JUMPING);
@@ -139,7 +163,7 @@ namespace gameplay
} }
} }
float jump_get_slowdown_friction(game::mp::playerState_s* ps) float jump_get_slowdown_friction(game::mp::playerState_t* ps)
{ {
assert(ps->pm_flags & game::PMF_JUMPING); assert(ps->pm_flags & game::PMF_JUMPING);
assert(ps->pm_time <= game::JUMP_LAND_SLOWDOWN_TIME); assert(ps->pm_time <= game::JUMP_LAND_SLOWDOWN_TIME);
@@ -157,7 +181,7 @@ namespace gameplay
return 2.5f; return 2.5f;
} }
float jump_reduce_friction_stub(game::mp::playerState_s* ps) float jump_reduce_friction_stub(game::mp::playerState_t* ps)
{ {
float control; float control;
@@ -175,7 +199,7 @@ namespace gameplay
return control; return control;
} }
float jump_get_land_factor(game::mp::playerState_s* ps) float jump_get_land_factor(game::mp::playerState_t* ps)
{ {
assert(ps->pm_flags & game::PMF_JUMPING); assert(ps->pm_flags & game::PMF_JUMPING);
assert(ps->pm_time <= game::JUMP_LAND_SLOWDOWN_TIME); assert(ps->pm_time <= game::JUMP_LAND_SLOWDOWN_TIME);
@@ -195,9 +219,9 @@ namespace gameplay
void jump_start_stub(game::pmove_t* pm, game::pml_t* pml, float height) void jump_start_stub(game::pmove_t* pm, game::pml_t* pml, float height)
{ {
static_assert(offsetof(game::mp::playerState_s, groundEntityNum) == 0x70); static_assert(offsetof(game::mp::playerState_t, groundEntityNum) == 0x70);
static_assert(offsetof(game::mp::playerState_s, pm_time) == 0x8); static_assert(offsetof(game::mp::playerState_t, pm_time) == 0x8);
static_assert(offsetof(game::mp::playerState_s, sprintState.sprintButtonUpRequired) == 0x240); static_assert(offsetof(game::mp::playerState_t, sprintState.sprintButtonUpRequired) == 0x240);
static_assert(offsetof(game::pml_t, frametime) == 0x24); static_assert(offsetof(game::pml_t, frametime) == 0x24);
static_assert(offsetof(game::pml_t, walking) == 0x2C); static_assert(offsetof(game::pml_t, walking) == 0x2C);
static_assert(offsetof(game::pml_t, groundPlane) == 0x30); static_assert(offsetof(game::pml_t, groundPlane) == 0x30);
@@ -205,9 +229,9 @@ namespace gameplay
float factor; float factor;
float velocity_sqrd; float velocity_sqrd;
game::mp::playerState_s* ps; game::mp::playerState_t* ps;
ps = static_cast<game::mp::playerState_s*>(pm->ps); ps = static_cast<game::mp::playerState_t*>(pm->ps);
assert(ps); assert(ps);
@@ -271,7 +295,7 @@ namespace gameplay
} }
} }
void pm_weapon_use_ammo_stub(game::playerState_s* ps, game::Weapon weapon, void pm_weapon_use_ammo_stub(game::playerState_t* ps, game::Weapon weapon,
bool is_alternate, int amount, game::PlayerHandIndex hand) bool is_alternate, int amount, game::PlayerHandIndex hand)
{ {
if (!dvars::player_sustainAmmo->current.enabled) if (!dvars::player_sustainAmmo->current.enabled)
@@ -280,10 +304,10 @@ namespace gameplay
} }
} }
game::mp::gentity_s* weapon_rocket_launcher_fire_stub(game::mp::gentity_s* ent, game::Weapon weapon, float spread, game::weaponParms* wp, game::mp::gentity_t* weapon_rocket_launcher_fire_stub(game::mp::gentity_t* ent, game::Weapon weapon, float spread, game::weaponParms* wp,
const float* gun_vel, game::mp::missileFireParms* fire_parms, bool magic_bullet) const float* gun_vel, game::mp::missileFireParms* fire_parms, bool magic_bullet)
{ {
auto* result = utils::hook::invoke<game::mp::gentity_s*>(0x1403DB8A0, ent, weapon, spread, wp, gun_vel, fire_parms, magic_bullet); auto* result = utils::hook::invoke<game::mp::gentity_t*>(0x1403DB8A0, ent, weapon, spread, wp, gun_vel, fire_parms, magic_bullet);
if (ent->client != nullptr && wp->weapDef->inventoryType != game::WEAPINVENTORY_EXCLUSIVE) if (ent->client != nullptr && wp->weapDef->inventoryType != game::WEAPINVENTORY_EXCLUSIVE)
{ {
@@ -303,13 +327,21 @@ namespace gameplay
void post_unpack() override void post_unpack() override
{ {
// Implement bouncing dvar // Implement bouncing dvar
utils::hook::jump(0x140228FFF, pm_bouncing_stub_mp, true); if (game::environment::is_sp())
{
utils::hook::nop(0x14046EC5C, 16);
}
utils::hook::jump(
SELECT_VALUE(0x14046EC5C, 0x140228FFF), SELECT_VALUE(pm_bouncing_stub_sp, pm_bouncing_stub_mp), true);
dvars::pm_bouncing = game::Dvar_RegisterBool("pm_bouncing", false, dvars::pm_bouncing = game::Dvar_RegisterBool("pm_bouncing", false,
game::DVAR_FLAG_REPLICATED, "Enable bouncing"); game::DVAR_FLAG_REPLICATED, "Enable bouncing");
dvars::player_sustainAmmo = game::Dvar_RegisterBool("player_sustainAmmo", false, dvars::player_sustainAmmo = game::Dvar_RegisterBool("player_sustainAmmo", false,
game::DVAR_FLAG_REPLICATED, "Firing weapon will not decrease clip ammo."); game::DVAR_FLAG_REPLICATED, "Firing weapon will not decrease clip ammo.");
pm_weapon_use_ammo_hook.create(0x140238A90, &pm_weapon_use_ammo_stub); pm_weapon_use_ammo_hook.create(SELECT_VALUE(0x140479640, 0x140238A90), &pm_weapon_use_ammo_stub);
if (game::environment::is_sp()) return;
// Implement player ejection dvar // Implement player ejection dvar
dvars::g_playerEjection = game::Dvar_RegisterBool("g_playerEjection", true, game::DVAR_FLAG_REPLICATED, "Flag whether player ejection is on or off"); dvars::g_playerEjection = game::Dvar_RegisterBool("g_playerEjection", true, game::DVAR_FLAG_REPLICATED, "Flag whether player ejection is on or off");
+11 -29
View File
@@ -114,25 +114,6 @@ namespace gsc
return res; return res;
} }
bool player_cmd_set_client_dvar_is_valid_name(const char* dvar_name)
{
if (!game::Dvar_IsValidName(dvar_name))
{
scr_error(va("%s is an invalid dvar name", dvar_name));
}
return true;
}
void player_cmd_set_client_dvar_server_dvar()
{
scr_error("server dvar cannot be set as a client dvar");
}
void player_cmd_set_client_dvar_non_writable_dvar()
{
scr_error("non-writable dvar cannot be set as a client dvar");
}
} }
unsigned int scr_get_object(unsigned int index) unsigned int scr_get_object(unsigned int index)
@@ -303,6 +284,11 @@ namespace gsc
public: public:
void post_unpack() override void post_unpack() override
{ {
if (game::environment::is_sp())
{
return;
}
scr_emit_function_hook.create(0x14042E150, &scr_emit_function_stub); scr_emit_function_hook.create(0x14042E150, &scr_emit_function_stub);
utils::hook::call(0x14042E0E4, compile_error_stub); // LinkFile utils::hook::call(0x14042E0E4, compile_error_stub); // LinkFile
@@ -310,20 +296,16 @@ namespace gsc
utils::hook::call(0x14042E22B, find_variable_stub); // Scr_EmitFunction utils::hook::call(0x14042E22B, find_variable_stub); // Scr_EmitFunction
// Restore basic error messages to scr functions // Restore basic error messages to scr functions
utils::hook::jump(game::Scr_GetObject, scr_get_object); utils::hook::jump(0x140438ED0, scr_get_object);
utils::hook::jump(game::Scr_GetConstString, scr_get_const_string); utils::hook::jump(0x140438AD0, scr_get_const_string);
utils::hook::jump(game::Scr_GetConstIString, scr_get_const_istring); utils::hook::jump(0x1404388B0, scr_get_const_istring);
utils::hook::jump(game::Scr_GetVector, scr_get_vector); utils::hook::jump(0x1404393D0, scr_get_vector);
utils::hook::jump(game::Scr_GetInt, scr_get_int); utils::hook::jump(0x140438E10, scr_get_int);
utils::hook::jump(game::Scr_GetFloat, scr_get_float); utils::hook::jump(0x140438D60, scr_get_float);
utils::hook::jump(0x1404390B0, scr_get_pointer_type); utils::hook::jump(0x1404390B0, scr_get_pointer_type);
utils::hook::jump(0x140439280, scr_get_type); utils::hook::jump(0x140439280, scr_get_type);
utils::hook::jump(0x1404392F0, scr_get_type_name); utils::hook::jump(0x1404392F0, scr_get_type_name);
utils::hook::call(0x14038A4F7, player_cmd_set_client_dvar_is_valid_name);
utils::hook::call(0x14038A52F, player_cmd_set_client_dvar_server_dvar);
utils::hook::call(0x14038A53A, player_cmd_set_client_dvar_non_writable_dvar);
} }
void pre_destroy() override void pre_destroy() override
+26 -113
View File
@@ -37,60 +37,9 @@ namespace gsc
bool force_error_print = false; bool force_error_print = false;
std::optional<std::string> gsc_error_msg; std::optional<std::string> gsc_error_msg;
std::vector<devmap_entry> devmap_entries{};
std::optional<devmap_entry> get_devmap_entry(const std::uint8_t* codepos)
{
const auto itr = std::ranges::find_if(devmap_entries, [codepos](const devmap_entry& entry) -> bool
{
return codepos >= entry.bytecode && codepos < entry.bytecode + entry.size;
});
if (itr != devmap_entries.end())
{
return *itr;
}
return {};
}
std::optional<std::pair<std::uint16_t, std::uint16_t>> get_line_and_col_for_codepos(const std::uint8_t* codepos)
{
const auto entry = get_devmap_entry(codepos);
if (!entry.has_value())
{
return {};
}
std::optional<std::pair<std::uint16_t, std::uint16_t>> best_line_info{};
std::uint32_t best_codepos = 0;
assert(codepos >= entry->bytecode);
const std::uint32_t codepos_offset = static_cast<std::uint32_t>(codepos - entry->bytecode);
for (const auto& instruction : entry->devmap)
{
if (instruction.codepos > codepos_offset)
{
continue;
}
if (best_line_info.has_value() && codepos_offset - instruction.codepos > codepos_offset - best_codepos)
{
continue;
}
best_line_info = { { instruction.line, instruction.col } };
best_codepos = instruction.codepos;
}
return best_line_info;
}
unsigned int scr_get_function_stub(const char** p_name, int* type) unsigned int scr_get_function_stub(const char** p_name, int* type)
{ {
const auto result = game::Scr_GetFunction(p_name, type); const auto result = utils::hook::invoke<unsigned int>(0x1403CD9F0, p_name, type);
for (const auto& [name, func] : functions) for (const auto& [name, func] : functions)
{ {
@@ -171,31 +120,22 @@ namespace gsc
const auto pos = frame == game::scr_VmPub->function_frame ? game::scr_function_stack->pos : frame->fs.pos; const auto pos = frame == game::scr_VmPub->function_frame ? game::scr_function_stack->pos : frame->fs.pos;
const auto function = find_function(frame->fs.pos); const auto function = find_function(frame->fs.pos);
const char* location;
if (function.has_value()) if (function.has_value())
{ {
location = utils::string::va("function \"%s\" in file \"%s\"", function.value().first.data(), function.value().second.data()); console::warn("\tat function \"%s\" in file \"%s.gsc\"\n", function.value().first.data(), function.value().second.data());
} }
else else
{ {
location = utils::string::va("unknown location %p", pos); console::warn("\tat unknown location %p\n", pos);
} }
const auto line_info = get_line_and_col_for_codepos(reinterpret_cast<const std::uint8_t*>(pos));
if (line_info.has_value())
{
location = utils::string::va("%s line \"%d\" column \"%d\"", location, line_info->first, line_info->second);
}
console::warn("\tat %s\n", location);
} }
} }
void vm_error_stub(const unsigned __int64 mark_pos) void vm_error_stub(int mark_pos)
{ {
if (!dvars::com_developer_script->current.enabled && !force_error_print) if (!dvars::com_developer_script->current.enabled && !force_error_print)
{ {
game::LargeLocalResetToMark(mark_pos); utils::hook::invoke<void>(0x1404E4D00, mark_pos);
return; return;
} }
@@ -226,7 +166,7 @@ namespace gsc
print_callstack(); print_callstack();
console::warn("************************************\n"); console::warn("************************************\n");
game::LargeLocalResetToMark(mark_pos); utils::hook::invoke<void>(0x1404E4D00, mark_pos);
} }
void inc_in_param() void inc_in_param()
@@ -288,22 +228,6 @@ namespace gsc
scr_error(utils::string::va("Assert fail: %s", game::Scr_GetString(0))); scr_error(utils::string::va("Assert fail: %s", game::Scr_GetString(0)));
} }
void scr_cmd_is_dedicated_server()
{
game::Scr_AddInt(game::environment::is_dedi());
}
void scr_bot_auto_connect_enabled()
{
if (game::environment::is_dedi() && dvars::sv_botsAutoJoin->current.enabled)
{
game::Scr_AddInt(1); // 2 seems to be unused (incomplete GSC/game mode)
return;
}
game::Scr_AddInt(game::BG_BotsConnectType());
}
const char* get_code_pos(const int index) const char* get_code_pos(const int index)
{ {
if (static_cast<unsigned int>(index) >= game::scr_VmPub->outparamcount) if (static_cast<unsigned int>(index) >= game::scr_VmPub->outparamcount)
@@ -324,22 +248,6 @@ namespace gsc
} }
} }
void add_devmap_entry(std::uint8_t* codepos, std::size_t size, const std::string& name, xsk::gsc::buffer devmap_buf)
{
std::vector<dev_map_instruction> devmap{};
const auto* devmap_ptr = reinterpret_cast<const dev_map*>(devmap_buf.data);
devmap.resize(devmap_ptr->num_instructions);
std::memcpy(devmap.data(), devmap_ptr->instructions, sizeof(dev_map_instruction) * devmap_ptr->num_instructions);
devmap_entries.emplace_back(codepos, size, name, std::move(devmap));
}
void clear_devmap()
{
devmap_entries.clear();
}
void add_function(const std::string& name, game::BuiltinFunction function) void add_function(const std::string& name, game::BuiltinFunction function)
{ {
++function_id_start; ++function_id_start;
@@ -360,21 +268,30 @@ namespace gsc
public: public:
void post_unpack() override void post_unpack() override
{ {
utils::hook::set<game::BuiltinFunction>(0x1409E6CE8, scr_print); utils::hook::set<game::BuiltinFunction>(SELECT_VALUE(0x14086F468, 0x1409E6CE8), scr_print);
utils::hook::set<game::BuiltinFunction>(0x1409E6D00, scr_print_ln); utils::hook::set<game::BuiltinFunction>(SELECT_VALUE(0x14086F480, 0x1409E6D00), scr_print_ln);
utils::hook::set<std::uint32_t>(0x14042E33B + 1, 0x1000); // Scr_RegisterFunction utils::hook::set<std::uint32_t>(SELECT_VALUE(0x1403D353C, 0x14042E33C), 0x1000); // Scr_RegisterFunction
utils::hook::set<std::uint32_t>(0x14042E342 + 4, RVA(&func_table)); // Scr_RegisterFunction utils::hook::set<std::uint32_t>(SELECT_VALUE(0x1403D3542 + 4, 0x14042E342 + 4), RVA(&func_table)); // Scr_RegisterFunction
// utils::hook::set<std::uint32_t>(0x14043BBBE + 3, RVA(&func_table)); // VM_Execute_0 utils::hook::set<std::uint32_t>(SELECT_VALUE(0x1403E0BDD + 3, 0x14043BBBE + 3), RVA(&func_table)); // VM_Execute_0
utils::hook::inject(0x14042E734 + 3, &func_table); // Scr_BeginLoadScripts utils::hook::inject(SELECT_VALUE(0x1403D38E4 + 3, 0x14042E734 + 3), &func_table); // Scr_BeginLoadScripts
if (game::environment::is_sp())
{
return;
}
utils::hook::nop(0x14043BBBE + 5, 2); utils::hook::nop(0x14043BBBE + 5, 2);
utils::hook::call(0x14043BBBE, vm_call_builtin_function); utils::hook::call(0x14043BBBE, vm_call_builtin_function);
utils::hook::call(0x14043CEB1, vm_error_stub);
utils::hook::call(0x14042E76F, scr_get_function_stub); utils::hook::call(0x14042E76F, scr_get_function_stub);
utils::hook::call(0x14043CEB1, vm_error_stub); utils::hook::set<game::BuiltinFunction>(0x1409E6E38, assert_ex_cmd);
utils::hook::set<game::BuiltinFunction>(0x1409E6E50, assert_msg_cmd);
utils::hook::set<game::BuiltinFunction>(0x1409E6E20, assert_cmd);
add_function("replacefunc", [] add_function("replacefunc", []
{ {
@@ -392,14 +309,10 @@ namespace gsc
command::execute(cmd); command::execute(cmd);
}); });
utils::hook::set<game::BuiltinFunction>(0x1409E6E38, assert_ex_cmd); add_function("isdedicated", []
utils::hook::set<game::BuiltinFunction>(0x1409E6E50, assert_msg_cmd); {
utils::hook::set<game::BuiltinFunction>(0x1409E6E20, assert_cmd); game::Scr_AddInt(game::environment::is_dedi());
});
utils::hook::set<game::BuiltinFunction>(0x1409E94D0, scr_cmd_is_dedicated_server);
dvars::sv_botsAutoJoin = game::Dvar_RegisterBool("sv_botsAutoJoin", false, game::DVAR_FLAG_NONE, "");
utils::hook::set<game::BuiltinFunction>(0x1409E92F0, scr_bot_auto_connect_enabled);
} }
}; };
} }
@@ -1,36 +1,9 @@
#pragma once #pragma once
#include <xsk/gsc/engine/iw6_pc.hpp>
namespace gsc namespace gsc
{ {
extern void* func_table[0x1000]; extern void* func_table[0x1000];
#pragma pack(push, 1)
struct dev_map_instruction
{
std::uint32_t codepos;
std::uint16_t line;
std::uint16_t col;
};
struct dev_map
{
std::uint32_t num_instructions;
dev_map_instruction instructions[1];
};
#pragma pack(pop)
struct devmap_entry
{
const std::uint8_t* bytecode;
std::size_t size;
std::string script_name;
std::vector<dev_map_instruction> devmap;
};
void add_devmap_entry(std::uint8_t*, std::size_t, const std::string&, xsk::gsc::buffer);
void clear_devmap();
void add_function(const std::string& name, game::BuiltinFunction function); void add_function(const std::string& name, game::BuiltinFunction function);
void scr_error(const char* error); void scr_error(const char* error);
+35 -90
View File
@@ -3,16 +3,15 @@
#include "game/game.hpp" #include "game/game.hpp"
#include "game/dvars.hpp" #include "game/dvars.hpp"
#include <utils/compression.hpp>
#include <utils/hook.hpp>
#include <utils/io.hpp> #include <utils/io.hpp>
#include <utils/hook.hpp>
#include <utils/memory.hpp> #include <utils/memory.hpp>
#include <utils/compression.hpp>
#include "component/console.hpp"
#include "component/filesystem.hpp" #include "component/filesystem.hpp"
#include "component/console.hpp"
#include "component/scripting.hpp" #include "component/scripting.hpp"
#include "script_extension.hpp"
#include "script_loading.hpp" #include "script_loading.hpp"
namespace gsc namespace gsc
@@ -33,7 +32,6 @@ namespace gsc
init_handles.clear(); init_handles.clear();
loaded_scripts.clear(); loaded_scripts.clear();
script_allocator.clear(); script_allocator.clear();
clear_devmap();
} }
bool read_raw_script_file(const std::string& name, std::string* data) bool read_raw_script_file(const std::string& name, std::string* data)
@@ -116,12 +114,6 @@ namespace gsc
loaded_scripts[real_name] = script_file_ptr; loaded_scripts[real_name] = script_file_ptr;
const auto devmap = std::get<2>(output_script);
if (devmap.size > 0 && (gsc_ctx->build() & xsk::gsc::build::dev_maps) != xsk::gsc::build::prod)
{
add_devmap_entry(script_file_ptr->bytecode, byte_code_size, real_name, devmap);
}
return script_file_ptr; return script_file_ptr;
} }
catch (const std::exception& ex) catch (const std::exception& ex)
@@ -188,9 +180,13 @@ namespace gsc
} }
} }
void load_scripts_from_folder(const std::filesystem::path& root_dir, const std::filesystem::path& script_dir) void load_scripts(const std::filesystem::path& root_dir)
{ {
console::info("Scanning directory '%s' for custom GSC scripts...\n", script_dir.generic_string().data()); const std::filesystem::path script_dir = root_dir / "scripts";
if (!utils::io::directory_exists(script_dir.generic_string()))
{
return;
}
const auto scripts = utils::io::list_files(script_dir.generic_string()); const auto scripts = utils::io::list_files(script_dir.generic_string());
for (const auto& script : scripts) for (const auto& script : scripts)
@@ -208,33 +204,6 @@ namespace gsc
} }
} }
void load_scripts(const std::filesystem::path& root_dir)
{
const auto load = [&root_dir](const std::filesystem::path& folder) -> void
{
const std::filesystem::path script_dir = root_dir / folder;
if (utils::io::directory_exists(script_dir.generic_string()))
{
load_scripts_from_folder(root_dir, script_dir);
}
};
const std::filesystem::path base_dir = "scripts";
load(base_dir);
const auto* map_name = game::Dvar_FindVar("mapname");
const std::filesystem::path game_folder = "mp";
load(base_dir / game_folder);
load(base_dir / game_folder / map_name->current.string);
const auto* game_type = game::Dvar_FindVar("g_gametype");
load(base_dir / game_folder / game_type->current.string);
}
int db_is_x_asset_default(game::XAssetType type, const char* name) int db_is_x_asset_default(game::XAssetType type, const char* name)
{ {
if (loaded_scripts.contains(name)) if (loaded_scripts.contains(name))
@@ -249,22 +218,12 @@ namespace gsc
{ {
utils::hook::invoke<void>(0x1403CCB10); utils::hook::invoke<void>(0x1403CCB10);
for (const auto& path : filesystem::get_search_paths()) clear();
{
load_scripts(path);
}
}
int g_scr_load_script_and_label_stub(game::ScriptFunctions* functions)
{
const auto result = utils::hook::invoke<int>(0x140349790, functions);
for (const auto& path : filesystem::get_search_paths()) for (const auto& path : filesystem::get_search_paths())
{ {
load_scripts(path); load_scripts(path);
} }
return result;
} }
void db_get_raw_buffer_stub(const game::RawFile* rawfile, char* buf, const int size) void db_get_raw_buffer_stub(const game::RawFile* rawfile, char* buf, const int size)
@@ -291,26 +250,7 @@ namespace gsc
utils::hook::invoke<void>(0x1403D2CA0); utils::hook::invoke<void>(0x1403D2CA0);
} }
void scr_load_level_singleplayer_stub() void scr_load_level_stub()
{
for (auto& function_handle : main_handles)
{
console::info("Executing '%s::main'\n", function_handle.first.data());
const auto thread = game::Scr_ExecThread(static_cast<int>(function_handle.second), 0);
game::RemoveRefToObject(thread);
}
utils::hook::invoke<void>(0x1403401B0);
for (auto& function_handle : init_handles)
{
console::info("Executing '%s::init'\n", function_handle.first.data());
const auto thread = game::Scr_ExecThread(static_cast<int>(function_handle.second), 0);
game::RemoveRefToObject(thread);
}
}
void scr_load_level_multiplayer_stub()
{ {
utils::hook::invoke<void>(0x1403CDC60); utils::hook::invoke<void>(0x1403CDC60);
@@ -336,7 +276,7 @@ namespace gsc
build = static_cast<xsk::gsc::build>(static_cast<unsigned int>(build) | static_cast<unsigned int>(xsk::gsc::build::dev_blocks)); build = static_cast<xsk::gsc::build>(static_cast<unsigned int>(build) | static_cast<unsigned int>(xsk::gsc::build::dev_blocks));
} }
gsc_ctx->init(build, []([[maybe_unused]] const auto* ctx, const auto& included_path) -> std::pair<xsk::gsc::buffer, std::vector<std::uint8_t>> gsc_ctx->init(build, []([[maybe_unused]] auto const* ctx, const auto& included_path) -> std::pair<xsk::gsc::buffer, std::vector<std::uint8_t>>
{ {
const auto script_name = std::filesystem::path(included_path).replace_extension().string(); const auto script_name = std::filesystem::path(included_path).replace_extension().string();
@@ -358,14 +298,14 @@ namespace gsc
return {{}, script_data}; return {{}, script_data};
}); });
utils::hook::invoke<void>(0x14042E6B0); utils::hook::invoke<void>(SELECT_VALUE(0x1403D3860, 0x14042E6B0));
} }
void scr_end_load_scripts_stub() void scr_end_load_scripts_stub()
{ {
gsc_ctx->cleanup(); gsc_ctx->cleanup();
utils::hook::invoke<void>(0x14042E7E0); utils::hook::invoke<void>(SELECT_VALUE(0x140603C00, 0x14042E7E0));
} }
} }
@@ -392,39 +332,44 @@ namespace gsc
public: public:
loading() loading()
{ {
gsc_ctx = std::make_unique<xsk::gsc::iw6_pc::context>(xsk::gsc::instance::server); gsc_ctx = std::make_unique<xsk::gsc::iw6_pc::context>();
} }
void post_unpack() override void post_unpack() override
{ {
dvars::com_developer = reinterpret_cast<game::dvar_t**>(0x1419A9700); dvars::com_developer = reinterpret_cast<game::dvar_t**>(SELECT_VALUE(0x141603850, 0x1419A9700));
// Load our scripts with an uncompressed stack // Load our scripts with an uncompressed stack
utils::hook::call(0x140437940, db_get_raw_buffer_stub); utils::hook::call(SELECT_VALUE(0x1403DC8F0, 0x140437940), db_get_raw_buffer_stub);
utils::hook::call(0x1403CCED9, scr_begin_load_scripts_stub); // GScr_LoadScripts utils::hook::call(SELECT_VALUE(0x14032D1E0, 0x1403CCED9), scr_begin_load_scripts_stub); // GScr_LoadScripts
utils::hook::call(0x1403CD08D, scr_end_load_scripts_stub); // GScr_LoadScripts utils::hook::call(SELECT_VALUE(0x14032D345, 0x1403CD08D), scr_end_load_scripts_stub); // GScr_LoadScripts
dvars::com_developer_script = game::Dvar_RegisterBool("developer_script", false, game::DVAR_FLAG_NONE, "Enable developer script comments");
if (game::environment::is_sp())
{
return;
}
// ProcessScript // ProcessScript
utils::hook::call(0x1404378D7, find_script); utils::hook::call(0x1404378D7, find_script);
utils::hook::call(0x1404378E7, db_is_x_asset_default); utils::hook::call(0x1404378E7, db_is_x_asset_default);
dvars::com_developer_script = game::Dvar_RegisterBool("developer_script", false, game::DVAR_FLAG_NONE, "Enable developer script comments");
scripting::on_shutdown([](const int clear_scripts, const int post_shutdown) -> void
{
if (clear_scripts && post_shutdown)
{
clear();
}
});
// GScr_LoadScripts // GScr_LoadScripts
utils::hook::call(0x1403CD009, gscr_load_game_type_script_stub); utils::hook::call(0x1403CD009, gscr_load_game_type_script_stub);
// Exec script handles // Exec script handles
utils::hook::call(0x14039F64E, g_load_structs_stub); utils::hook::call(0x14039F64E, g_load_structs_stub);
utils::hook::call(0x14039F653, scr_load_level_multiplayer_stub); utils::hook::call(0x14039F653, scr_load_level_stub);
scripting::on_shutdown([](int free_scripts)
{
if (free_scripts)
{
clear();
}
});
} }
}; };
} }
+2 -2
View File
@@ -50,8 +50,8 @@ namespace input
return; return;
} }
cl_char_event_hook.create(0x1402C2AE0, cl_char_event_stub); cl_char_event_hook.create(SELECT_VALUE(0x14023CE50, 0x1402C2AE0), cl_char_event_stub);
cl_key_event_hook.create(0x1402C2CE0, cl_key_event_stub); cl_key_event_hook.create(SELECT_VALUE(0x14023D070, 0x1402C2CE0), cl_key_event_stub);
} }
}; };
} }
+1 -1
View File
@@ -58,7 +58,7 @@ namespace localized_strings
void post_unpack() override void post_unpack() override
{ {
// Change some localized strings // Change some localized strings
seh_string_ed_get_string_hook.create(0x1404A5F60, &seh_string_ed_get_string); seh_string_ed_get_string_hook.create(SELECT_VALUE(0x1403F42D0, 0x1404A5F60), &seh_string_ed_get_string);
} }
}; };
} }
+41 -6
View File
@@ -10,15 +10,43 @@ namespace logger
{ {
namespace namespace
{ {
utils::hook::detour com_error_hook;
const game::dvar_t* logger_dev = nullptr; const game::dvar_t* logger_dev = nullptr;
void print_com_error(int, const char* msg, ...)
{
char buffer[2048]{};
va_list ap;
va_start(ap, msg);
vsnprintf_s(buffer, _TRUNCATE, msg, ap);
va_end(ap);
console::error("%s", buffer);
}
void com_error_stub(const int error, const char* msg, ...)
{
char buffer[2048]{};
va_list ap;
va_start(ap, msg);
vsnprintf_s(buffer, _TRUNCATE, msg, ap);
va_end(ap);
console::error("Error: %s\n", buffer);
com_error_hook.invoke<void>(error, "%s", buffer);
}
void print_warning(const char* msg, ...) void print_warning(const char* msg, ...)
{ {
char buffer[2048]{}; char buffer[2048]{};
va_list ap; va_list ap;
va_start(ap, msg); va_start(ap, msg);
vsnprintf(buffer, sizeof(buffer), msg, ap); vsnprintf_s(buffer, _TRUNCATE, msg, ap);
va_end(ap); va_end(ap);
console::warn("%s", buffer); console::warn("%s", buffer);
@@ -30,7 +58,7 @@ namespace logger
va_list ap; va_list ap;
va_start(ap, msg); va_start(ap, msg);
vsnprintf(buffer, sizeof(buffer), msg, ap); vsnprintf_s(buffer, _TRUNCATE, msg, ap);
va_end(ap); va_end(ap);
console::info("%s", buffer); console::info("%s", buffer);
@@ -47,7 +75,7 @@ namespace logger
va_list ap; va_list ap;
va_start(ap, msg); va_start(ap, msg);
vsnprintf(buffer, sizeof(buffer), msg, ap); vsnprintf_s(buffer, _TRUNCATE, msg, ap);
va_end(ap); va_end(ap);
console::info("%s", buffer); console::info("%s", buffer);
@@ -113,10 +141,17 @@ namespace logger
sub_1401DAA40(); sub_1401DAA40();
} }
// Make havok script's print function actually print if (!game::environment::is_sp())
utils::hook::jump(0x140732184, print); {
utils::hook::call(0x140501AE3, print_com_error);
}
logger_dev = game::Dvar_RegisterBool("logger_dev", false, game::DVAR_FLAG_NONE, "Print dev stuff"); com_error_hook.create(game::Com_Error, com_error_stub);
// Make havok script's print function actually print
utils::hook::jump(SELECT_VALUE(0x1406283A4, 0x140732184), print);
logger_dev = game::Dvar_RegisterBool("logger_dev", false, game::DVAR_FLAG_SAVED, "Print dev stuff");
} }
}; };
} }
+1 -1
View File
@@ -99,7 +99,7 @@ namespace map_rotation
console::error("%s: %s contains invalid data!\n", ex.what(), sv_map_rotation->name); console::error("%s: %s contains invalid data!\n", ex.what(), sv_map_rotation->name);
} }
#ifdef _DEBUG #ifdef _DEBUG
console::info("dedicated_rotation size after parsing is '%llu'\n", dedicated_rotation.get_entries_size()); console::info("dedicated_rotation size after parsing is '%llu'", dedicated_rotation.get_entries_size());
#endif #endif
} }
-229
View File
@@ -1,229 +0,0 @@
#include <std_include.hpp>
#include "loader/component_loader.hpp"
#include "game/game.hpp"
#include "game/dvars.hpp"
#include "command.hpp"
#include "console.hpp"
#include "mods.hpp"
#include <utils/hook.hpp>
#include <utils/string.hpp>
namespace mods
{
namespace
{
utils::hook::detour sys_create_file_hook;
void db_build_os_path_from_source(const char* zone_name, const game::FF_DIR source, const int size, char* filename)
{
char user_map[MAX_PATH]{};
switch (source)
{
case game::FFD_DEFAULT:
(void)game::Com_sprintf(filename, size, "%s\\%s.ff", std::filesystem::current_path().string().c_str(), zone_name);
break;
case game::FFD_MOD_DIR:
assert(mods::is_using_mods());
(void)game::Com_sprintf(filename, size, "%s\\%s\\%s.ff", std::filesystem::current_path().string().c_str(), (*dvars::fs_gameDirVar)->current.string, zone_name);
break;
case game::FFD_USER_MAP:
game::I_strncpyz(user_map, zone_name, sizeof(user_map));
(void)game::Com_sprintf(filename, size, "%s\\%s\\%s\\%s.ff", std::filesystem::current_path().string().c_str(), "usermaps", user_map, zone_name);
break;
default:
assert(false && "inconceivable");
break;
}
}
game::Sys_File sys_create_file_stub(game::Sys_Folder folder, const char* base_filename)
{
auto result = sys_create_file_hook.invoke<game::Sys_File>(folder, base_filename);
if (result.handle != INVALID_HANDLE_VALUE)
{
return result;
}
if (!is_using_mods())
{
return result;
}
// .ff extension was added previously
if (!std::strcmp(base_filename, "mod.ff") && mods::db_mod_file_exists())
{
char file_path[MAX_PATH]{};
db_build_os_path_from_source("mod", game::FFD_MOD_DIR, sizeof(file_path), file_path);
result.handle = game::Sys_OpenFileReliable(file_path);
}
return result;
}
void db_load_x_assets_stub(game::XZoneInfo* zone_info, unsigned int zone_count, game::DBSyncMode sync_mode)
{
std::vector<game::XZoneInfo> zones(zone_info, zone_info + zone_count);
if (db_mod_file_exists())
{
zones.emplace_back("mod", game::DB_ZONE_COMMON | game::DB_ZONE_CUSTOM, 0);
}
game::DB_LoadXAssets(zones.data(), static_cast<unsigned int>(zones.size()), sync_mode);
}
const auto skip_extra_zones_stub = utils::hook::assemble([](utils::hook::assembler& a)
{
const auto skip = a.newLabel();
const auto original = a.newLabel();
a.pushad64();
a.test(ebp, game::DB_ZONE_CUSTOM); // allocFlags
a.jnz(skip);
a.bind(original);
a.popad64();
a.mov(rdx, 0x140835F28);
a.mov(rcx, rsi);
a.call_aligned(strcmp);
a.jmp(0x1403217C0);
a.bind(skip);
a.popad64();
a.mov(r15d, 0x80);
a.not_(r15d);
a.and_(ebp, r15d);
a.jmp(0x1403217F6);
});
bool fs_game_dir_domain_func(game::dvar_t* dvar, game::DvarValue new_value)
{
if (*new_value.string == '\0')
{
return true;
}
if (game::I_strnicmp(new_value.string, "mods", 4) != 0)
{
game::LiveStorage_StatsWriteNotNeeded(game::CONTROLLER_INDEX_0);
console::error("ERROR: Invalid server value '%s' for '%s'\n", new_value.string, dvar->name);
return false;
}
if (5 < std::strlen(new_value.string) && (new_value.string[4] == '\\' || new_value.string[4] == '/'))
{
const auto* s1 = std::strstr(new_value.string, "..");
const auto* s2 = std::strstr(new_value.string, "::");
if (s1 == nullptr && s2 == nullptr)
{
return true;
}
game::LiveStorage_StatsWriteNotNeeded(game::CONTROLLER_INDEX_0);
console::error("ERROR: Invalid server value '%s' for '%s'\n", new_value.string, dvar->name);
return false;
}
// Invalid path specified
game::LiveStorage_StatsWriteNotNeeded(game::CONTROLLER_INDEX_0);
console::error("ERROR: Invalid server value '%s' for '%s'\n", new_value.string, dvar->name);
return false;
}
}
bool is_using_mods()
{
return (*dvars::fs_gameDirVar) && *(*dvars::fs_gameDirVar)->current.string;
}
bool db_mod_file_exists()
{
if (!*(*dvars::fs_gameDirVar)->current.string)
{
return false;
}
char filename[MAX_PATH]{};
db_build_os_path_from_source("mod", game::FFD_MOD_DIR, sizeof(filename), filename);
if (auto zone_file = game::Sys_OpenFileReliable(filename); zone_file != INVALID_HANDLE_VALUE)
{
::CloseHandle(zone_file);
return true;
}
return false;
}
class component final : public component_interface
{
public:
static_assert(sizeof(game::Sys_File) == 8);
void post_unpack() override
{
dvars::fs_gameDirVar = reinterpret_cast<game::dvar_t**>(0x147876000);
// Remove DVAR_INIT from fs_game
utils::hook::set<std::uint32_t>(0x1404DDA45 + 2, game::DVAR_FLAG_SERVERINFO);
utils::hook::inject(0x1404DDA57 + 3, &fs_game_dir_domain_func);
// Don't load eng_ + patch_ with loadzone
utils::hook::nop(0x1403217B1, 15);
utils::hook::jump(0x1403217B1, skip_extra_zones_stub, true);
// Add custom zone paths
sys_create_file_hook.create(game::Sys_CreateFile, sys_create_file_stub);
// Load mod.ff
utils::hook::call(0x1405E7113, db_load_x_assets_stub); // R_LoadGraphicsAssets According to myself but I don't remember where I got it from
command::add("loadmod", [](const command::params& params) -> void
{
if (params.size() != 2)
{
console::info("USAGE: %s \"mods/<mod name>\"", params.get(0));
return;
}
std::string mod_name = utils::string::to_lower(params.get(1));
if (!mod_name.empty() && !mod_name.starts_with("mods/"))
{
mod_name = "mods/" + mod_name;
}
// change fs_game if needed
if (mod_name != (*dvars::fs_gameDirVar)->current.string)
{
game::Dvar_SetString((*dvars::fs_gameDirVar), mod_name.c_str());
command::execute("vid_restart\n");
}
});
command::add("unloadmod", [](const command::params& params) -> void
{
if (*dvars::fs_gameDirVar == nullptr || *(*dvars::fs_gameDirVar)->current.string == '\0')
{
return;
}
game::Dvar_SetString(*dvars::fs_gameDirVar, "");
command::execute("vid_restart\n");
});
// TODO: without a way to monitor all the ways fs_game can be changed there is no way to detect when we
// should unregister the path from the internal filesystem we use
// HINT: It could be done in fs_game_dir_domain_func, but I haven't tested if that's the best place to monitor for changes and register/unregister the mods folder
}
};
}
REGISTER_COMPONENT(mods::component)
-7
View File
@@ -1,7 +0,0 @@
#pragma once
namespace mods
{
bool is_using_mods();
bool db_mod_file_exists();
}
+15 -10
View File
@@ -20,7 +20,7 @@ namespace network
return callbacks; return callbacks;
} }
bool handle_command(game::netadr_s* address, const char* command, game::msg_t* message) bool handle_command(game::netadr_t* address, const char* command, game::msg_t* message)
{ {
const auto cmd_string = utils::string::to_lower(command); const auto cmd_string = utils::string::to_lower(command);
auto& callbacks = get_callbacks(); auto& callbacks = get_callbacks();
@@ -61,7 +61,7 @@ namespace network
a.jmp(0x1402C64EE); a.jmp(0x1402C64EE);
} }
int net_compare_base_address(const game::netadr_s* a1, const game::netadr_s* a2) int net_compare_base_address(const game::netadr_t* a1, const game::netadr_t* a2)
{ {
if (a1->type == a2->type) if (a1->type == a2->type)
{ {
@@ -83,12 +83,12 @@ namespace network
return false; return false;
} }
int net_compare_address(const game::netadr_s* a1, const game::netadr_s* a2) int net_compare_address(const game::netadr_t* a1, const game::netadr_t* a2)
{ {
return net_compare_base_address(a1, a2) && a1->port == a2->port; return net_compare_base_address(a1, a2) && a1->port == a2->port;
} }
void reconnect_migrated_client(game::mp::client_t*, game::netadr_s* from, const int, const int, const char*, const char*, bool) void reconnect_migrated_client(game::mp::client_t*, game::netadr_t* from, const int, const int, const char*, const char*, bool)
{ {
// This happens when a client tries to rejoin after being recently disconnected, OR by a duplicated guid // This happens when a client tries to rejoin after being recently disconnected, OR by a duplicated guid
// We don't want this to do anything. It decides to crash seemingly randomly // We don't want this to do anything. It decides to crash seemingly randomly
@@ -107,14 +107,14 @@ namespace network
get_callbacks()[utils::string::to_lower(command)] = callback; get_callbacks()[utils::string::to_lower(command)] = callback;
} }
int dw_send_to_stub(const int size, const char* src, game::netadr_s* addr) int dw_send_to_stub(const int size, const char* src, game::netadr_t* addr)
{ {
sockaddr s = {}; sockaddr s = {};
game::NetadrToSockadr(addr, &s); game::NetadrToSockadr(addr, &s);
return sendto(*game::query_socket, src, size, 0, &s, 16) >= 0; return sendto(*game::query_socket, src, size, 0, &s, 16) >= 0;
} }
void send(const game::netadr_s& address, const std::string& command, const std::string& data, const char separator) void send(const game::netadr_t& address, const std::string& command, const std::string& data, const char separator)
{ {
std::string packet = "\xFF\xFF\xFF\xFF"; std::string packet = "\xFF\xFF\xFF\xFF";
packet.append(command); packet.append(command);
@@ -124,7 +124,7 @@ namespace network
send_data(address, packet); send_data(address, packet);
} }
void send_data(const game::netadr_s& address, const std::string& data) void send_data(const game::netadr_t& address, const std::string& data)
{ {
if (address.type == game::NA_LOOPBACK) if (address.type == game::NA_LOOPBACK)
{ {
@@ -136,12 +136,12 @@ namespace network
} }
} }
bool are_addresses_equal(const game::netadr_s& a, const game::netadr_s& b) bool are_addresses_equal(const game::netadr_t& a, const game::netadr_t& b)
{ {
return net_compare_address(&a, &b); return net_compare_address(&a, &b);
} }
const char* net_adr_to_string(const game::netadr_s& a) const char* net_adr_to_string(const game::netadr_t& a)
{ {
if (a.type == game::NA_LOOPBACK) if (a.type == game::NA_LOOPBACK)
{ {
@@ -210,6 +210,11 @@ namespace network
void post_unpack() override void post_unpack() override
{ {
{ {
if (game::environment::is_sp())
{
return;
}
// redirect dw_sendto to raw socket // redirect dw_sendto to raw socket
utils::hook::jump(0x140501A00, dw_send_to_stub); utils::hook::jump(0x140501A00, dw_send_to_stub);
@@ -281,7 +286,7 @@ namespace network
if (!game::environment::is_dedi()) if (!game::environment::is_dedi())
{ {
// we need this on the client for RCon // we need this on the client for RCon
on("print", [](const game::netadr_s& address, const std::string& message) on("print", [](const game::netadr_t& address, const std::string& message)
{ {
if (address != party::get_target()) if (address != party::get_target())
{ {
+11 -11
View File
@@ -2,23 +2,23 @@
namespace network namespace network
{ {
using callback = std::function<void(const game::netadr_s&, const std::string&)>; using callback = std::function<void(const game::netadr_t&, const std::string&)>;
void on(const std::string& command, const callback& callback); void on(const std::string& command, const callback& callback);
void send(const game::netadr_s& address, const std::string& command, const std::string& data = {}, char separator = ' '); void send(const game::netadr_t& address, const std::string& command, const std::string& data = {}, char separator = ' ');
void send_data(const game::netadr_s& address, const std::string& data); void send_data(const game::netadr_t& address, const std::string& data);
bool are_addresses_equal(const game::netadr_s& a, const game::netadr_s& b); bool are_addresses_equal(const game::netadr_t& a, const game::netadr_t& b);
const char* net_adr_to_string(const game::netadr_s& a); const char* net_adr_to_string(const game::netadr_t& a);
} }
inline bool operator==(const game::netadr_s& a, const game::netadr_s& b) inline bool operator==(const game::netadr_t& a, const game::netadr_t& b)
{ {
return network::are_addresses_equal(a, b); // return network::are_addresses_equal(a, b); //
} }
inline bool operator!=(const game::netadr_s& a, const game::netadr_s& b) inline bool operator!=(const game::netadr_t& a, const game::netadr_t& b)
{ {
return !(a == b); // return !(a == b); //
} }
@@ -26,20 +26,20 @@ inline bool operator!=(const game::netadr_s& a, const game::netadr_s& b)
namespace std namespace std
{ {
template <> template <>
struct equal_to<game::netadr_s> struct equal_to<game::netadr_t>
{ {
using result_type = bool; using result_type = bool;
bool operator()(const game::netadr_s& lhs, const game::netadr_s& rhs) const bool operator()(const game::netadr_t& lhs, const game::netadr_t& rhs) const
{ {
return network::are_addresses_equal(lhs, rhs); return network::are_addresses_equal(lhs, rhs);
} }
}; };
template <> template <>
struct hash<game::netadr_s> struct hash<game::netadr_t>
{ {
size_t operator()(const game::netadr_s& x) const noexcept size_t operator()(const game::netadr_t& x) const noexcept
{ {
return hash<uint32_t>()(*reinterpret_cast<const uint32_t*>(&x.ip[0])) ^ hash<uint16_t>()(x.port); return hash<uint32_t>()(*reinterpret_cast<const uint32_t*>(&x.ip[0])) ^ hash<uint16_t>()(x.port);
} }
+13 -6
View File
@@ -63,10 +63,12 @@ namespace notifies
const auto* guid = game::SV_GetGuid(client_num); const auto* guid = game::SV_GetGuid(client_num);
const auto* name = game::mp::svs_clients[client_num].name; const auto* name = game::mp::svs_clients[client_num].name;
game_log::g_log_printf("%s;%s;%i;%s;%s\n", params.get(0), guid, game_log::g_log_printf("%s;%s;%i;%s;%s\n",
params.get(0),
guid,
client_num, client_num,
name, name,
message.c_str() message.data()
); );
}, scheduler::pipeline::server); }, scheduler::pipeline::server);
@@ -172,17 +174,22 @@ namespace notifies
public: public:
void post_unpack() override void post_unpack() override
{ {
if (game::environment::is_sp())
{
return;
}
utils::hook::call(0x1404724DD, client_command_stub);
utils::hook::jump(0x14043A584, utils::hook::assemble(vm_execute_stub), true); utils::hook::jump(0x14043A584, utils::hook::assemble(vm_execute_stub), true);
scripting::on_shutdown([](const int clear_scripts, const int post_shutdown) -> void scripting::on_shutdown([](const bool free_scripts)
{ {
if (clear_scripts && post_shutdown) if (free_scripts)
{ {
vm_execute_hooks.clear(); vm_execute_hooks.clear();
} }
}); });
utils::hook::call(0x1404724DD, client_command_stub);
} }
}; };
} }
+44 -84
View File
@@ -2,7 +2,6 @@
#include "loader/component_loader.hpp" #include "loader/component_loader.hpp"
#include "game/game.hpp" #include "game/game.hpp"
#include "game/dvars.hpp" #include "game/dvars.hpp"
#include "game/engine/sv_game.hpp"
#include "command.hpp" #include "command.hpp"
#include "console.hpp" #include "console.hpp"
@@ -27,7 +26,7 @@ namespace party
{ {
struct struct
{ {
game::netadr_s host{}; game::netadr_t host{};
std::string challenge{}; std::string challenge{};
bool hostDefined{false}; bool hostDefined{false};
} connect_state; } connect_state;
@@ -38,8 +37,13 @@ namespace party
int sv_maxclients; int sv_maxclients;
void connect_to_party(const game::netadr_s& target, const std::string& mapname, const std::string& gametype) void connect_to_party(const game::netadr_t& target, const std::string& mapname, const std::string& gametype)
{ {
if (game::environment::is_sp())
{
return;
}
if (game::Live_SyncOnlineDataFlags(0)) if (game::Live_SyncOnlineDataFlags(0))
{ {
scheduler::once([=]() scheduler::once([=]()
@@ -54,7 +58,7 @@ namespace party
// CL_ConnectFromParty // CL_ConnectFromParty
char session_info[0x100] = {}; char session_info[0x100] = {};
reinterpret_cast<void(*)(int, char*, const game::netadr_s*, const char*, const char*)>(0x1402C5700)( reinterpret_cast<void(*)(int, char*, const game::netadr_t*, const char*, const char*)>(0x1402C5700)(
0, session_info, &target, mapname.data(), gametype.data()); 0, session_info, &target, mapname.data(), gametype.data());
} }
@@ -77,26 +81,6 @@ namespace party
a.mov(ecx, 2); a.mov(ecx, 2);
a.jmp(0x1402C617D); a.jmp(0x1402C617D);
}); });
utils::info_string get_info()
{
utils::info_string info;
info.set("gamename", "IW6");
info.set("hostname", dvars::get_string("sv_hostname"));
info.set("gametype", dvars::get_string("g_gametype"));
info.set("sv_motd", dvars::get_string("sv_motd"));
info.set("xuid", utils::string::va("%llX", steam::SteamUser()->GetSteamID().bits));
info.set("mapname", dvars::get_string("mapname"));
info.set("isPrivate", dvars::get_string("g_password").empty() ? "0" : "1");
info.set("clients", std::to_string(get_client_count()));
info.set("bots", std::to_string(get_bot_count()));
info.set("sv_maxclients", std::to_string(*game::mp::svs_clientCount));
info.set("protocol", std::to_string(PROTOCOL));
info.set("shortversion", SHORTVERSION);
return info;
}
} }
void switch_gamemode_if_necessary(const std::string& gametype) void switch_gamemode_if_necessary(const std::string& gametype)
@@ -162,7 +146,7 @@ namespace party
return count; return count;
} }
game::netadr_s& get_target() game::netadr_t& get_target()
{ {
return connect_state.host; return connect_state.host;
} }
@@ -176,13 +160,10 @@ namespace party
{ {
for (auto i = 0; !name.empty() && i < *game::mp::svs_clientCount; ++i) for (auto i = 0; !name.empty() && i < *game::mp::svs_clientCount; ++i)
{ {
if (!game::mp::g_entities[i].client) if (game::mp::g_entities[i].client)
{ {
continue; char client_name[16] = {0};
} strncpy_s(client_name, game::mp::g_entities[i].client->sess.cs.name, sizeof(client_name));
char client_name[16]{};
game::I_strncpyz(client_name, game::mp::g_entities[i].client->sess.cs.name, sizeof(client_name));
game::I_CleanStr(client_name); game::I_CleanStr(client_name);
if (client_name == name) if (client_name == name)
@@ -190,12 +171,17 @@ namespace party
return i; return i;
} }
} }
}
return -1; return -1;
} }
void connect(const game::netadr_s& target) void connect(const game::netadr_t& target)
{ {
if (game::environment::is_sp())
{
return;
}
command::execute("lui_open popup_acceptinginvite", false); command::execute("lui_open popup_acceptinginvite", false);
connect_state.host = target; connect_state.host = target;
@@ -220,6 +206,11 @@ namespace party
public: public:
void post_unpack() override void post_unpack() override
{ {
if (game::environment::is_sp())
{
return;
}
// hook disconnect command function // hook disconnect command function
utils::hook::jump(0x1402C6370, disconnect_stub); utils::hook::jump(0x1402C6370, disconnect_stub);
@@ -261,7 +252,7 @@ namespace party
return; return;
} }
game::netadr_s target{}; game::netadr_t target{};
if (game::NET_StringToAdr(params[1], &target)) if (game::NET_StringToAdr(params[1], &target))
{ {
connect(target); connect(target);
@@ -369,7 +360,7 @@ namespace party
const auto message = params.join(2); const auto message = params.join(2);
const auto* const name = game::Dvar_FindVar("sv_sayName")->current.string; const auto* const name = game::Dvar_FindVar("sv_sayName")->current.string;
game::engine::SV_GameSendServerCommand(client_num, game::SV_CMD_CAN_IGNORE, utils::string::va("%c \"%s: %s\"", 84, name, message.data())); game::SV_GameSendServerCommand(client_num, 0, utils::string::va("%c \"%s: %s\"", 84, name, message.data()));
console::info("%s -> %i: %s\n", name, client_num, message.data()); console::info("%s -> %i: %s\n", name, client_num, message.data());
}); });
@@ -383,7 +374,7 @@ namespace party
const auto client_num = atoi(params.get(1)); const auto client_num = atoi(params.get(1));
const auto message = params.join(2); const auto message = params.join(2);
game::engine::SV_GameSendServerCommand(client_num, game::SV_CMD_CAN_IGNORE, utils::string::va("%c \"%s\"", 84, message.data())); game::SV_GameSendServerCommand(client_num, 0, utils::string::va("%c \"%s\"", 84, message.data()));
console::info("%i: %s\n", client_num, message.data()); console::info("%i: %s\n", client_num, message.data());
}); });
@@ -397,7 +388,7 @@ namespace party
const auto message = params.join(1); const auto message = params.join(1);
const auto* const name = game::Dvar_FindVar("sv_sayName")->current.string; const auto* const name = game::Dvar_FindVar("sv_sayName")->current.string;
game::engine::SV_GameSendServerCommand(-1, game::SV_CMD_CAN_IGNORE, utils::string::va("%c \"%s: %s\"", 84, name, message.data())); game::SV_GameSendServerCommand(-1, 0, utils::string::va("%c \"%s: %s\"", 84, name, message.data()));
console::info("%s: %s\n", name, message.data()); console::info("%s: %s\n", name, message.data());
}); });
@@ -410,67 +401,36 @@ namespace party
const auto message = params.join(1); const auto message = params.join(1);
game::engine::SV_GameSendServerCommand(-1, game::SV_CMD_CAN_IGNORE, utils::string::va("%c \"%s\"", 84, message.data())); game::SV_GameSendServerCommand(-1, 0, utils::string::va("%c \"%s\"", 84, message.data()));
console::info("%s\n", message.data()); console::info("%s\n", message.data());
}); });
network::on("getInfo", [](const game::netadr_s& target, const std::string& data) network::on("getInfo", [](const game::netadr_t& target, const std::string& data)
{ {
utils::info_string info = get_info(); utils::info_string info;
info.set("challenge", data); info.set("challenge", data);
info.set("gamename", "IW6");
info.set("hostname", dvars::get_string("sv_hostname"));
info.set("gametype", dvars::get_string("g_gametype"));
info.set("sv_motd", dvars::get_string("sv_motd"));
info.set("xuid", utils::string::va("%llX", steam::SteamUser()->GetSteamID().bits));
info.set("mapname", dvars::get_string("mapname"));
info.set("isPrivate", dvars::get_string("g_password").empty() ? "0" : "1");
info.set("clients", std::to_string(get_client_count()));
info.set("bots", std::to_string(get_bot_count()));
info.set("sv_maxclients", std::to_string(*game::mp::svs_clientCount));
info.set("protocol", std::to_string(PROTOCOL));
info.set("shortversion", SHORTVERSION);
network::send(target, "infoResponse", info.build(), '\n'); network::send(target, "infoResponse", info.build(), '\n');
}); });
network::on("getStatus", [](const game::netadr_s& target, const std::string& data)
{
std::string player_list;
utils::info_string info = get_info();
info.set("challenge", data);
const auto* sv_running = game::Dvar_FindVar("sv_running");
if (!sv_running || !sv_running->current.enabled)
{
return;
}
for (auto i = 0; i < game::Dvar_FindVar("sv_maxclients")->current.integer; ++i)
{
auto* client = &game::mp::svs_clients[i];
auto* self = &game::mp::g_entities[i];
if (client->header.state < game::CS_ACTIVE)
{
continue;
}
if (!self || !self->client)
{
continue;
}
if (game::SV_BotIsBot(i))
{
continue;
}
const auto score = game::G_GetClientScore(i);
const auto ping = game::mp::svs_clients[i].ping;
const std::string name = game::mp::svs_clients[i].name;
player_list.append(std::format("{} {} \"{}\"\n", score, ping, name));
}
network::send(target, "statusResponse", info.build() + "\n"s + player_list + "\n"s, '\n');
});
if (game::environment::is_dedi()) if (game::environment::is_dedi())
{ {
return; return;
} }
network::on("infoResponse", [](const game::netadr_s& target, const std::string& data) network::on("infoResponse", [](const game::netadr_t& target, const std::string& data)
{ {
const utils::info_string info(data); const utils::info_string info(data);
server_list::handle_info_response(target, info); server_list::handle_info_response(target, info);
+2 -2
View File
@@ -7,7 +7,7 @@ namespace party
void reset_connect_state(); void reset_connect_state();
void connect(const game::netadr_s& target); void connect(const game::netadr_t& target);
void map_restart(); void map_restart();
[[nodiscard]] int server_client_count(); [[nodiscard]] int server_client_count();
@@ -15,5 +15,5 @@ namespace party
[[nodiscard]] int get_client_count(); [[nodiscard]] int get_client_count();
[[nodiscard]] int get_bot_count(); [[nodiscard]] int get_bot_count();
[[nodiscard]] game::netadr_s& get_target(); [[nodiscard]] game::netadr_t& get_target();
} }
+51 -34
View File
@@ -2,7 +2,6 @@
#include "loader/component_loader.hpp" #include "loader/component_loader.hpp"
#include "game/game.hpp" #include "game/game.hpp"
#include "game/dvars.hpp" #include "game/dvars.hpp"
#include "game/engine/sv_game.hpp"
#include "command.hpp" #include "command.hpp"
#include "console.hpp" #include "console.hpp"
@@ -33,13 +32,12 @@ namespace patches
{ {
return; return;
} }
return sv_kick_client_num_hook.invoke<void>(clientNum, reason); return sv_kick_client_num_hook.invoke<void>(clientNum, reason);
} }
std::string get_login_username() std::string get_login_username()
{ {
char username[UNLEN + 1]{}; char username[UNLEN + 1];
DWORD username_len = UNLEN + 1; DWORD username_len = UNLEN + 1;
if (!GetUserNameA(username, &username_len)) if (!GetUserNameA(username, &username_len))
{ {
@@ -56,7 +54,7 @@ namespace patches
if (game::environment::is_mp()) if (game::environment::is_mp())
{ {
// Make name save // Make name save
game::Dvar_RegisterString("name", get_login_username().c_str(), game::DVAR_FLAG_SAVED, "Player name."); game::Dvar_RegisterString("name", get_login_username().data(), game::DVAR_FLAG_SAVED, "Player name.");
} }
return com_register_dvars_hook.invoke<void>(); return com_register_dvars_hook.invoke<void>();
@@ -74,10 +72,8 @@ namespace patches
if (exec_params.size() == 2) if (exec_params.size() == 2)
{ {
std::string file_name = exec_params.get(1); std::string file_name = exec_params.get(1);
if (!file_name.ends_with(".cfg")) if (file_name.find(".cfg") == std::string::npos)
{
file_name.append(".cfg"); file_name.append(".cfg");
}
const auto file = filesystem::file(file_name); const auto file = filesystem::file(file_name);
if (file.exists()) if (file.exists())
@@ -107,6 +103,23 @@ namespace patches
a.jmp(0x1403F7574); a.jmp(0x1403F7574);
}); });
auto cmd_exec_stub_sp = utils::hook::assemble([](utils::hook::assembler& a)
{
const auto success = a.newLabel();
a.pushad64();
a.call_aligned(cmd_exec_patch);
a.test(al, al);
a.popad64();
a.jz(success);
a.mov(edx, 0x18000);
a.jmp(0x1403B39C0);
a.bind(success);
a.jmp(0x1403B3A04);
});
int dvar_command_patch() // game makes this return an int and compares with eax instead of al -_- int dvar_command_patch() // game makes this return an int and compares with eax instead of al -_-
{ {
const command::params args{}; const command::params args{};
@@ -137,7 +150,7 @@ namespace patches
return 0; return 0;
} }
game::Font_s* get_chat_font_handle() game::Font_t* get_chat_font_handle()
{ {
return game::R_RegisterFont("fonts/bigFont"); return game::R_RegisterFont("fonts/bigFont");
} }
@@ -150,11 +163,11 @@ namespace patches
game::AimAssist_AddToTargetList(a1, a2); game::AimAssist_AddToTargetList(a1, a2);
} }
game::dvar_t* register_cg_fov_stub(const char* name, const float value, const float min, [[maybe_unused]] const float max, game::dvar_t* register_cg_fov_stub(const char* name, float value, float min, float /*max*/,
const unsigned int flags, const unsigned int flags,
const char* description) const char* description)
{ {
return game::Dvar_RegisterFloat(name, value, min, 160, flags, description); return game::Dvar_RegisterFloat(name, value, min, 160, flags | 1, description);
} }
void bsp_sys_error_stub(const char* error, const char* arg1) void bsp_sys_error_stub(const char* error, const char* arg1)
@@ -174,7 +187,7 @@ namespace patches
} }
utils::hook::detour cmd_lui_notify_server_hook; utils::hook::detour cmd_lui_notify_server_hook;
void cmd_lui_notify_server_stub(game::mp::gentity_s* ent) void cmd_lui_notify_server_stub(game::mp::gentity_t* ent)
{ {
command::params_sv params{}; command::params_sv params{};
const auto menu_id = atoi(params.get(1)); const auto menu_id = atoi(params.get(1));
@@ -219,10 +232,14 @@ namespace patches
LoadLibraryA("PhysXUpdateLoader64.dll"); LoadLibraryA("PhysXUpdateLoader64.dll");
// Unlock fps in main menu // Unlock fps in main menu
utils::hook::set<std::uint8_t>(0x1402CF58B, 0xEB); utils::hook::set<std::uint8_t>(SELECT_VALUE(0x140242DDB, 0x1402CF58B), 0xEB);
// Unlock cg_fov // Unlock cg_fov
utils::hook::call(0x14027273C, register_cg_fov_stub); utils::hook::call(SELECT_VALUE(0x1401F3E96, 0x14027273C), register_cg_fov_stub);
if (game::environment::is_sp())
{
utils::hook::call(0x1401F3EC7, register_cg_fov_stub);
}
// changed max value from 85 -> 1000 // changed max value from 85 -> 1000
if (!game::environment::is_dedi()) if (!game::environment::is_dedi())
@@ -230,8 +247,11 @@ namespace patches
dvars::override::register_int("com_maxfps", 85, 0, 1000, game::DVAR_FLAG_SAVED); dvars::override::register_int("com_maxfps", 85, 0, 1000, game::DVAR_FLAG_SAVED);
} }
if (!game::environment::is_sp())
{
//increased max limit for sv_network_fps, the lower limit is the default one. Original range is from 20 to 200 times a second. //increased max limit for sv_network_fps, the lower limit is the default one. Original range is from 20 to 200 times a second.
utils::hook::call(0x140476F4F, register_network_fps_stub); utils::hook::call(0x140476F4F, register_network_fps_stub);
}
// register cg_gun_ dvars with new values and flags // register cg_gun_ dvars with new values and flags
// maybe _x can stay usable within a reasonable range? it can make scoped weapons DRASTICALLY better on high FOVs // maybe _x can stay usable within a reasonable range? it can make scoped weapons DRASTICALLY better on high FOVs
@@ -243,33 +263,33 @@ namespace patches
dvars::override::register_float("cg_fovScale", 1.0f, 0.1f, 5.0f, game::DVAR_FLAG_SAVED); dvars::override::register_float("cg_fovScale", 1.0f, 0.1f, 5.0f, game::DVAR_FLAG_SAVED);
// Patch Dvar_Command to print out values how CoD4 does it // Patch Dvar_Command to print out values how CoD4 does it
utils::hook::jump(0x140416A60, dvar_command_patch); utils::hook::jump(SELECT_VALUE(0x1403BFCB0, 0x140416A60), dvar_command_patch);
// Allow executing custom cfg files with the "exec" command // Allow executing custom cfg files with the "exec" command
utils::hook::jump(0x1403F752B, 0x1403F7582); utils::hook::jump(SELECT_VALUE(0x1403B39BB, 0x1403F752B), SELECT_VALUE(0x1403B3A12, 0x1403F7582));
//Use a relative jump to empty memory first //Use a relative jump to empty memory first
utils::hook::jump(0x1403F7582, cmd_exec_stub_mp, true); utils::hook::jump(SELECT_VALUE(0x1403B3A12, 0x1403F7582), SELECT_VALUE(cmd_exec_stub_sp, cmd_exec_stub_mp), true);
// Use empty memory to go to our stub first (can't do close jump, so need space for 12 bytes) // Use empty memory to go to our stub first (can't do close jump, so need space for 12 bytes)
// nop call to SetThreadExecutionState for good luck (???) // Fix mouse lag
utils::hook::nop(0x140504A2B, 6); utils::hook::nop(SELECT_VALUE(0x14043E6CB, 0x140504A2B), 6);
// They are lying, this cannot actually fix a black screen on startup :(
scheduler::loop([]() scheduler::loop([]()
{ {
SetThreadExecutionState(ES_DISPLAY_REQUIRED); SetThreadExecutionState(ES_DISPLAY_REQUIRED);
}, scheduler::pipeline::main); }, scheduler::pipeline::main);
if (game::environment::is_sp())
{
patch_sp();
}
else
{
patch_mp(); patch_mp();
} }
}
static void patch_mp() static void patch_mp()
{ {
// Bypass Arxan function
utils::hook::nop(0x1404758C0, 16);
utils::hook::jump(0x1404758C0, game::engine::SV_GameSendServerCommand, true);
utils::hook::call(0x140477399, game::engine::SV_SendServerCommand);
// Register dvars // Register dvars
com_register_dvars_hook.create(0x140413A90, &com_register_dvars_stub); com_register_dvars_hook.create(0x140413A90, &com_register_dvars_stub);
@@ -306,13 +326,6 @@ namespace patches
dvars::override::register_int("igs_s1", 1, 0, 1, 0); dvars::override::register_int("igs_s1", 1, 0, 1, 0);
dvars::override::register_int("igs_crossgame", 1, 0, 1, 0); dvars::override::register_int("igs_crossgame", 1, 0, 1, 0);
// Required by UI scripts. Missing when joining a dedi and causes crashes
game::Dvar_RegisterInt("scr_gun_winlimit", 1, 0, 10, game::DVAR_FLAG_REPLICATED, "Win limit for Gun Game");
game::Dvar_RegisterInt("scr_gun_scorelimit", 18, 1, 1000, game::DVAR_FLAG_REPLICATED, "Score limit for Gun Game");
// Remove cheat protection from r_hudOutlinePostMode
utils::hook::set<uint8_t>(0x1405DDF23 + 2, 0x40);
// Patch game chat on resolutions higher than 1080p to use the right font // Patch game chat on resolutions higher than 1080p to use the right font
utils::hook::call(0x14025C825, get_chat_font_handle); utils::hook::call(0x14025C825, get_chat_font_handle);
utils::hook::call(0x1402BC42F, get_chat_font_handle); utils::hook::call(0x1402BC42F, get_chat_font_handle);
@@ -344,9 +357,13 @@ namespace patches
utils::hook::nop(0x1403A1A0F, 1); utils::hook::nop(0x1403A1A0F, 1);
// ^^ // ^^
utils::hook::nop(0x1403A072F, 5); // LiveStorage_RecordMovementInMatchdata utils::hook::nop(0x1403A072F, 5); // LiveStorage_RecordMovementInMatchdata
}
// Disable Com_Error in NET_SendPacket static void patch_sp()
utils::hook::nop(0x140501AE3, 5); {
// SP doesn't initialize WSA
WSADATA wsa_data;
WSAStartup(MAKEWORD(2, 2), &wsa_data);
} }
}; };
} }
+5
View File
@@ -74,6 +74,11 @@ namespace ranked
public: public:
void post_unpack() override void post_unpack() override
{ {
if (game::environment::is_sp())
{
return;
}
if (game::environment::is_mp()) if (game::environment::is_mp())
{ {
// This must be registered as 'true' to avoid crash when starting a private match // This must be registered as 'true' to avoid crash when starting a private match
+7 -6
View File
@@ -181,15 +181,16 @@ namespace raw_mouse
return; return;
} }
utils::hook::call(0x1404FC3FB, &in_mouse_move); utils::hook::call(SELECT_VALUE(0x14043845F, 0x1404FC3FB), &in_mouse_move);
utils::hook::call(0x1405E69A1, &create_window_ex_a_stub); utils::hook::call(SELECT_VALUE(0x140519F71, 0x1405E69A1), &create_window_ex_a_stub);
utils::hook::set<std::uint8_t>(0x1405E69A6, 0x90); utils::hook::set<std::uint8_t>(SELECT_VALUE(0x140519F76, 0x1405E69A6), 0x90);
utils::hook::call(0x140500824, &register_class_ex_a_stub); utils::hook::call(SELECT_VALUE(0x14043BF04, 0x140500824), &register_class_ex_a_stub);
utils::hook::set<std::uint8_t>(0x140500829, 0x90); utils::hook::set<std::uint8_t>(SELECT_VALUE(0x14043BF09, 0x140500829), 0x90);
cl_rawInput = game::Dvar_RegisterBool("cl_rawInput", true, game::DVAR_FLAG_SAVED, "Use Raw Input for mouse. This fixes mouse acceleration issue"); cl_rawInput = game::Dvar_RegisterBool("cl_rawInput", true,
game::DVAR_FLAG_SAVED, "Use Raw Input for mouse input. This fixes mouse acceleration issue");
} }
}; };
} }
+10 -10
View File
@@ -14,11 +14,11 @@ namespace rcon
namespace namespace
{ {
bool is_redirecting_ = false; bool is_redirecting_ = false;
game::netadr_s redirect_target_ = {}; game::netadr_t redirect_target_ = {};
std::string redirect_buffer = {}; std::string redirect_buffer = {};
std::recursive_mutex redirect_lock; std::recursive_mutex redirect_lock;
void setup_redirect(const game::netadr_s& target) void setup_redirect(const game::netadr_t& target)
{ {
std::lock_guard<std::recursive_mutex> $(redirect_lock); std::lock_guard<std::recursive_mutex> $(redirect_lock);
@@ -53,15 +53,12 @@ namespace rcon
const auto client = &game::mp::svs_clients[i]; const auto client = &game::mp::svs_clients[i];
auto self = &game::mp::g_entities[i]; auto self = &game::mp::g_entities[i];
if (client->header.state == game::CS_FREE || !self || !self->client)
{
continue;
}
char clean_name[32]{}; char clean_name[32]{};
game::I_strncpyz(clean_name, self->client->sess.cs.name, sizeof(clean_name)); strncpy_s(clean_name, self->client->sess.cs.name, sizeof(clean_name));
game::I_CleanStr(clean_name); game::I_CleanStr(clean_name);
if (client->header.state > game::CS_FREE && self && self->client)
{
buffer.append(utils::string::va("%3i %5i %3s %s %32s %16s %21s %5i\n", buffer.append(utils::string::va("%3i %5i %3s %s %32s %16s %21s %5i\n",
i, i,
self->client->sess.scores.score, self->client->sess.scores.score,
@@ -73,6 +70,7 @@ namespace rcon
client->header.netchan.remoteAddress.port) client->header.netchan.remoteAddress.port)
); );
} }
}
return buffer; return buffer;
} }
@@ -94,7 +92,7 @@ namespace rcon
if (*reinterpret_cast<std::int32_t*>(0x1419E1AE0) >= 5) //clientUIActive.connectionState >= CA_CONNECTED if (*reinterpret_cast<std::int32_t*>(0x1419E1AE0) >= 5) //clientUIActive.connectionState >= CA_CONNECTED
{ {
const auto target = *reinterpret_cast<game::netadr_s*>(0x141CB535C); const auto target = *reinterpret_cast<game::netadr_t*>(0x141CB535C);
const auto buffer = password + " " + data; const auto buffer = password + " " + data;
network::send(target, "rcon", buffer); network::send(target, "rcon", buffer);
} }
@@ -123,6 +121,8 @@ namespace rcon
public: public:
void post_unpack() override void post_unpack() override
{ {
if (game::environment::is_sp()) return;
scheduler::once([]() scheduler::once([]()
{ {
game::Dvar_RegisterString("rcon_password", "", game::DvarFlags::DVAR_FLAG_NONE, game::Dvar_RegisterString("rcon_password", "", game::DvarFlags::DVAR_FLAG_NONE,
@@ -170,7 +170,7 @@ namespace rcon
} }
else else
{ {
network::on("rcon", [](const game::netadr_s& addr, const std::string& data) network::on("rcon", [](const game::netadr_t& addr, const std::string& data)
{ {
const auto pos = data.find_first_of(" "); const auto pos = data.find_first_of(" ");
if (pos == std::string::npos) if (pos == std::string::npos)
+4 -3
View File
@@ -46,11 +46,12 @@ namespace renderer
return; return;
} }
dvars::r_fullbright = game::Dvar_RegisterBool("r_fullbright", false, game::DVAR_FLAG_CHEAT, dvars::r_fullbright = game::Dvar_RegisterBool("r_fullbright", false, game::DvarFlags::DVAR_FLAG_SAVED,
"Toggles rendering without lighting"); "Toggles rendering without lighting");
r_init_draw_method_hook.create(0x1405CB470, &r_init_draw_method_stub); r_init_draw_method_hook.create(SELECT_VALUE(0x1404FF600, 0x1405CB470), &r_init_draw_method_stub);
r_update_front_end_dvar_options_hook.create(0x140603240, &r_update_front_end_dvar_options_stub); r_update_front_end_dvar_options_hook.create(
SELECT_VALUE(0x140535FF0, 0x140603240), &r_update_front_end_dvar_options_stub);
} }
}; };
} }
+4 -4
View File
@@ -149,7 +149,7 @@ namespace scheduler
{ {
schedule([=]() schedule([=]()
{ {
const auto dw_init = game::Live_SyncOnlineDataFlags(0) == 0; const auto dw_init = game::environment::is_sp() || game::Live_SyncOnlineDataFlags(0) == 0;
if (dw_init && game::Sys_IsDatabaseReady2()) if (dw_init && game::Sys_IsDatabaseReady2())
{ {
once(callback, type, delay); once(callback, type, delay);
@@ -177,10 +177,10 @@ namespace scheduler
void post_unpack() override void post_unpack() override
{ {
r_end_frame_hook.create(0x140601AA0, scheduler::r_end_frame_stub); r_end_frame_hook.create(SELECT_VALUE(0x140534860, 0x140601AA0), scheduler::r_end_frame_stub);
utils::hook::call(0x140413142, scheduler::main_frame_stub); utils::hook::call(SELECT_VALUE(0x1403BC922, 0x140413142), scheduler::main_frame_stub);
utils::hook::call(0x1403A0AF9, scheduler::server_frame_stub); utils::hook::call(SELECT_VALUE(0x1403185FD, 0x1403A0AF9), scheduler::server_frame_stub);
} }
void pre_destroy() override void pre_destroy() override
+14 -15
View File
@@ -34,7 +34,7 @@ namespace scripting
std::unordered_map<unsigned int, std::string> canonical_string_table; std::unordered_map<unsigned int, std::string> canonical_string_table;
std::vector<std::function<void(int, int)>> shutdown_callbacks; std::vector<std::function<void(int)>> shutdown_callbacks;
std::vector<std::function<void()>> init_callbacks; std::vector<std::function<void()>> init_callbacks;
void scr_load_level_stub() void scr_load_level_stub()
@@ -47,9 +47,9 @@ namespace scripting
} }
} }
void g_shutdown_game_stub(const int clear_scripts) void g_shutdown_game_stub(const int free_scripts)
{ {
if (clear_scripts) if (free_scripts)
{ {
script_function_table_sort.clear(); script_function_table_sort.clear();
script_function_table.clear(); script_function_table.clear();
@@ -59,15 +59,10 @@ namespace scripting
for (const auto& callback : shutdown_callbacks) for (const auto& callback : shutdown_callbacks)
{ {
callback(clear_scripts ,false); callback(free_scripts);
} }
g_shutdown_game_hook.invoke<void>(clear_scripts); return g_shutdown_game_hook.invoke<void>(free_scripts);
for (const auto& callback : shutdown_callbacks)
{
callback(clear_scripts, true);
}
} }
void process_script_stub(const char* filename) void process_script_stub(const char* filename)
@@ -170,7 +165,7 @@ namespace scripting
return find_token(id); return find_token(id);
} }
void on_shutdown(const std::function<void(int, int)>& callback) void on_shutdown(const std::function<void(int)>& callback)
{ {
shutdown_callbacks.push_back(callback); shutdown_callbacks.push_back(callback);
} }
@@ -195,13 +190,16 @@ namespace scripting
public: public:
void post_unpack() override void post_unpack() override
{ {
scr_load_level_hook.create(0x1403C4E60, &scr_load_level_stub); // SP address is wrong, but should be ok
g_shutdown_game_hook.create(0x1403A0DF0, &g_shutdown_game_stub); scr_load_level_hook.create(SELECT_VALUE(0x14013D5D0, 0x1403C4E60), &scr_load_level_stub);
g_shutdown_game_hook.create(SELECT_VALUE(0x140318C10, 0x1403A0DF0), &g_shutdown_game_stub);
scr_set_thread_position_hook.create(0x14042E360, &scr_set_thread_position_stub); scr_set_thread_position_hook.create(SELECT_VALUE(0x1403D3560, 0x14042E360), &scr_set_thread_position_stub);
process_script_hook.create(0x1404378C0, &process_script_stub); process_script_hook.create(SELECT_VALUE(0x1403DC870, 0x1404378C0), &process_script_stub);
sl_get_canonical_string_hook.create(game::SL_GetCanonicalString, &sl_get_canonical_string_stub); sl_get_canonical_string_hook.create(game::SL_GetCanonicalString, &sl_get_canonical_string_stub);
if (!game::environment::is_sp())
{
// Make some room for pre_main hook // Make some room for pre_main hook
utils::hook::jump(0x1402084A0, has_config_string_index); utils::hook::jump(0x1402084A0, has_config_string_index);
@@ -210,6 +208,7 @@ namespace scripting
utils::hook::set<uint16_t>(0x1402084D0, 0xD3EB); // jump to 0x1402084A5 utils::hook::set<uint16_t>(0x1402084D0, 0xD3EB); // jump to 0x1402084A5
g_find_config_string_index.create(0x140161F90, &g_find_config_string_index_stub); g_find_config_string_index.create(0x140161F90, &g_find_config_string_index_stub);
} }
}
}; };
} }
+1 -1
View File
@@ -8,7 +8,7 @@ namespace scripting
extern std::string current_file; extern std::string current_file;
void on_shutdown(const std::function<void(int, int)>& callback); void on_shutdown(const std::function<void(int)>& callback);
void on_init(const std::function<void()>& callback); void on_init(const std::function<void()>& callback);
std::optional<std::string> get_canonical_string(unsigned int id); std::optional<std::string> get_canonical_string(unsigned int id);
+1 -75
View File
@@ -10,8 +10,6 @@ namespace security
{ {
namespace namespace
{ {
utils::hook::detour ui_replace_directive_hook;
void set_cached_playerdata_stub(const int localclient, const int index1, const int index2) void set_cached_playerdata_stub(const int localclient, const int index1, const int index2)
{ {
if (index1 >= 0 && index1 < 18 && index2 >= 0 && index2 < 42) if (index1 >= 0 && index1 < 18 && index2 >= 0 && index2 < 42)
@@ -32,71 +30,6 @@ namespace security
utils::hook::invoke<void>(0x140472500, client, msg); utils::hook::invoke<void>(0x140472500, client, msg);
} }
void ui_replace_directive_stub(const int local_client_num, const char* src_string, char* dst_string, const int dst_buffer_size)
{
assert(src_string);
if (!src_string)
{
return;
}
assert(dst_string);
if (!dst_string)
{
return;
}
assert(dst_buffer_size > 0);
if (dst_buffer_size <= 0)
{
return;
}
constexpr std::size_t MAX_HUDELEM_TEXT_LEN = 0x100;
if (std::strlen(src_string) > MAX_HUDELEM_TEXT_LEN)
{
return;
}
ui_replace_directive_hook.invoke<void>(local_client_num, src_string, dst_string, dst_buffer_size);
}
int hud_elem_set_enum_string_stub(char* string, const char* format, ...)
{
va_list ap;
va_start(ap, format);
const auto len = vsnprintf(string, 0x800, format, ap);
va_end(ap);
string[0x800 - 1] = '\0';
return len;
}
int sv_add_bot_stub(char* string, const char* format, ...)
{
va_list ap;
va_start(ap, format);
const auto len = vsnprintf(string, 0x400, format, ap);
va_end(ap);
string[0x400 - 1] = '\0';
return len;
}
int sv_add_test_client_stub(char* string, const char* format, ...)
{
va_list ap;
va_start(ap, format);
const auto len = vsnprintf(string, 0x400, format, ap);
va_end(ap);
string[0x400 - 1] = '\0';
return len;
}
} }
class component final : public component_interface class component final : public component_interface
@@ -104,20 +37,13 @@ namespace security
public: public:
void post_unpack() override void post_unpack() override
{ {
// sprinf if (game::environment::is_sp()) return;
utils::hook::call(0x140399B0F, hud_elem_set_enum_string_stub);
// Patch vulnerability in PlayerCards_SetCachedPlayerData // Patch vulnerability in PlayerCards_SetCachedPlayerData
utils::hook::call(0x140287C5C, set_cached_playerdata_stub); utils::hook::call(0x140287C5C, set_cached_playerdata_stub);
// sprinf
utils::hook::call(0x140470A88, sv_add_bot_stub);
utils::hook::call(0x140470F68, sv_add_test_client_stub);
// It is possible to make the server hang if left unchecked // It is possible to make the server hang if left unchecked
utils::hook::call(0x14047A29A, sv_execute_client_message_stub); utils::hook::call(0x14047A29A, sv_execute_client_message_stub);
ui_replace_directive_hook.create(0x1404D8A00, ui_replace_directive_stub);
} }
}; };
} }
+7 -7
View File
@@ -33,14 +33,14 @@ namespace server_list
std::string map_name; std::string map_name;
std::string game_type; std::string game_type;
char in_game; char in_game;
game::netadr_s address; game::netadr_t address;
}; };
struct struct
{ {
game::netadr_s address{}; game::netadr_t address{};
volatile bool requesting = false; volatile bool requesting = false;
std::unordered_map<game::netadr_s, int> queued_servers{}; std::unordered_map<game::netadr_t, int> queued_servers{};
} master_state; } master_state;
volatile bool update_server_list = false; volatile bool update_server_list = false;
@@ -307,12 +307,12 @@ namespace server_list
} }
} }
bool get_master_server(game::netadr_s& address) bool get_master_server(game::netadr_t& address)
{ {
return game::NET_StringToAdr("server.alterware.dev:20810", &address); return game::NET_StringToAdr("server.alterware.dev:20810", &address);
} }
void handle_info_response(const game::netadr_s& address, const utils::info_string& info) void handle_info_response(const game::netadr_t& address, const utils::info_string& info)
{ {
int start_time{}; int start_time{};
const auto now = game::Sys_Milliseconds(); const auto now = game::Sys_Milliseconds();
@@ -436,7 +436,7 @@ namespace server_list
scheduler::loop(do_frame_work, scheduler::pipeline::main); scheduler::loop(do_frame_work, scheduler::pipeline::main);
network::on("getServersResponse", [](const game::netadr_s& target, const std::string& data) network::on("getServersResponse", [](const game::netadr_t& target, const std::string& data)
{ {
{ {
std::lock_guard<std::mutex> _(mutex); std::lock_guard<std::mutex> _(mutex);
@@ -469,7 +469,7 @@ namespace server_list
break; break;
} }
game::netadr_s address{}; game::netadr_t address{};
address.type = game::NA_IP; address.type = game::NA_IP;
address.localNetID = game::NS_CLIENT1; address.localNetID = game::NS_CLIENT1;
std::memcpy(&address.ip[0], data.data() + i + 0, 4); std::memcpy(&address.ip[0], data.data() + i + 0, 4);
+2 -2
View File
@@ -3,8 +3,8 @@
namespace server_list namespace server_list
{ {
bool get_master_server(game::netadr_s& address); bool get_master_server(game::netadr_t& address);
void handle_info_response(const game::netadr_s& address, const utils::info_string& info); void handle_info_response(const game::netadr_t& address, const utils::info_string& info);
bool sl_key_event(int key, int down); bool sl_key_event(int key, int down);
} }
+1 -28
View File
@@ -2,9 +2,6 @@
#include "loader/component_loader.hpp" #include "loader/component_loader.hpp"
#include "game/game.hpp" #include "game/game.hpp"
#include "command.hpp"
#include "console.hpp"
#include <utils/hook.hpp> #include <utils/hook.hpp>
#include <utils/string.hpp> #include <utils/string.hpp>
@@ -57,25 +54,6 @@ namespace slowmotion
client->nextSnapshotTime = *game::mp::serverTime - 1; client->nextSnapshotTime = *game::mp::serverTime - 1;
} }
} }
void set_code_time_scale(float timescale)
{
assert(timescale > 0.0f);
*game::com_codeTimeScale = timescale;
}
void com_timescale_f(const command::params& params)
{
if (params.size() != 2)
{
console::info("timescale <rate>\n");
return;
}
const auto timescale = static_cast<float>(std::atof(params.get(1)));
console::info("timescale set to %f\n", timescale);
set_code_time_scale(timescale);
}
} }
class component final : public component_interface class component final : public component_interface
@@ -83,12 +61,7 @@ namespace slowmotion
public: public:
void post_unpack() override void post_unpack() override
{ {
command::add("timescale", com_timescale_f); if (!game::environment::is_dedi()) return;
if (!game::environment::is_dedi())
{
return;
}
utils::hook::jump(0x1403B4A10, scr_cmd_set_slow_motion); utils::hook::jump(0x1403B4A10, scr_cmd_set_slow_motion);
+3 -3
View File
@@ -30,9 +30,9 @@ namespace splash
void post_unpack() override void post_unpack() override
{ {
// Disable native splash screen // Disable native splash screen
utils::hook::nop(0x140500ADB, 5); utils::hook::nop(SELECT_VALUE(0x14043C15B, 0x140500ADB), 5);
utils::hook::jump(0x140502000, destroy_stub); utils::hook::jump(SELECT_VALUE(0x14043D5A0, 0x140502000), destroy_stub);
utils::hook::jump(0x140502040, destroy_stub); utils::hook::jump(SELECT_VALUE(0x14043D5E0, 0x140502040), destroy_stub);
} }
void pre_destroy() override void pre_destroy() override
+4 -34
View File
@@ -26,8 +26,6 @@ namespace steam_proxy
ownership_state state_; ownership_state state_;
utils::binary_resource runner_file(RUNNER, "runner.exe"); utils::binary_resource runner_file(RUNNER, "runner.exe");
bool is_disabled() { return true; }
} }
class component final : public component_interface class component final : public component_interface
@@ -35,7 +33,7 @@ namespace steam_proxy
public: public:
void post_load() override void post_load() override
{ {
if (game::environment::is_dedi() || is_disabled()) if (game::environment::is_dedi())
{ {
return; return;
} }
@@ -46,27 +44,16 @@ namespace steam_proxy
#ifndef DEV_BUILD #ifndef DEV_BUILD
try try
{ {
const std::string game_type = "multiplayer"; const std::string game_type = game::environment::is_sp() ? "singleplayer" : "multiplayer";
state_ = this->start_mod("\xF0\x9F\x92\x8E" " iw6-mod: " + (game_type), 209170); state_ = this->start_mod("\xF0\x9F\x92\x8E" " iw6-mod: " + (game_type), game::environment::is_sp() ? 209160 : 209170);
} }
catch (const std::exception& ex) catch (const std::exception& ex)
{ {
state_ = ownership_state::error; state_ = ownership_state::error;
printf("Steam: %s\n", ex.what()); printf("Steam: %s\n", ex.what());
} }
#ifdef STEAM_OWNERSHIP_CHECK
if (utils::nt::is_wine())
{
state_ = ownership_state::success;
return;
}
#endif
#ifdef STEAM_OWNERSHIP_CHECK
evaluate_ownership_state(state_);
#endif
#endif #endif
(void)state_;
} }
void pre_destroy() override void pre_destroy() override
@@ -223,23 +210,6 @@ namespace steam_proxy
return scheduler::cond_end; return scheduler::cond_end;
}); });
} }
#ifdef STEAM_OWNERSHIP_CHECK
void evaluate_ownership_state(const ownership_state state)
{
switch (state)
{
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;
}
}
#endif
}; };
} }
+3 -2
View File
@@ -51,12 +51,13 @@ namespace system_check
{ {
static std::unordered_map<std::string, std::string> mp_zone_hashes = static std::unordered_map<std::string, std::string> mp_zone_hashes =
{ {
{"patch_common_mp.ff", "1731093802719CFD4F35FE1D16373D9F97BBB4E75E72D1AE4A84996CF51C6870"}, {"patch_common_mp.ff", "F1F08BFD03D0496199FAFD49CAA3E5786B70084A4C3C8841ACC4A7B7616D226C"},
}; };
static std::unordered_map<std::string, std::string> sp_zone_hashes = static std::unordered_map<std::string, std::string> sp_zone_hashes =
{ {
{"patch_common_alien_mp.ff", "22F0CE99DD26376F44AE896FF8307CB38E14B65C805548E00A6251465CB6666A"}, {"patch_common.ff", "883DB33A1E386420EC6EF19F25C0D8081D01C1945BA2BCAD9FBD5460A201D6AA"},
{"patch_common_alien_mp.ff", "78B00BFF961F69F9A45446D40638A5A7F5C3462F1AF05A833066772C62FB5DB2"},
}; };
return verify_hashes(mp_zone_hashes) && (game::environment::is_dedi() || verify_hashes(sp_zone_hashes)); return verify_hashes(mp_zone_hashes) && (game::environment::is_dedi() || verify_hashes(sp_zone_hashes));
+22 -41
View File
@@ -32,13 +32,15 @@ namespace ui_scripting
std::string root; std::string root;
}; };
struct struct globals
{ {
std::string in_require_script; std::string in_require_script;
std::vector<script> loaded_scripts; std::vector<script> loaded_scripts;
bool load_raw_script{}; bool load_raw_script{};
std::string raw_script_name{}; std::string raw_script_name{};
} globals; };
globals globals;
bool is_loaded_script(const std::string& name) bool is_loaded_script(const std::string& name)
{ {
@@ -154,7 +156,7 @@ namespace ui_scripting
game_type["issingleplayer"] = [](const game&) game_type["issingleplayer"] = [](const game&)
{ {
return false; return ::game::environment::is_sp();
}; };
game_type["ismultiplayer"] = [](const game&) game_type["ismultiplayer"] = [](const game&)
@@ -193,27 +195,6 @@ namespace ui_scripting
setup_functions(); setup_functions();
lua["print"] = function(reinterpret_cast<game::hks::lua_function>(0x14017B120)); // hks::base_print lua["print"] = function(reinterpret_cast<game::hks::lua_function>(0x14017B120)); // hks::base_print
lua["directoryexists"] = [](const std::string& string)
{
return utils::io::directory_exists(string);
};
lua["listfiles"] = [](const std::string& string)
{
return utils::io::list_files(string);
};
lua["directoryisempty"] = [](const std::string& string)
{
return utils::io::directory_is_empty(string);
};
lua["fileexists"] = [](const std::string& string)
{
return utils::io::file_exists(string);
};
lua["table"]["unpack"] = lua["unpack"]; lua["table"]["unpack"] = lua["unpack"];
lua["luiglobals"] = lua; lua["luiglobals"] = lua;
@@ -295,7 +276,7 @@ namespace ui_scripting
void* lua_atpanic_stub(void* l, [[maybe_unused]] void* panicf) void* lua_atpanic_stub(void* l, [[maybe_unused]] void* panicf)
{ {
return utils::hook::invoke<void*>(0x1401A4730, l, 0x140197BC0); return utils::hook::invoke<void*>(SELECT_VALUE(0x1401851F0, 0x1401A4730), l, SELECT_VALUE(0x1401789A0, 0x140197BC0));
} }
int luaopen_stub([[maybe_unused]] void* l) int luaopen_stub([[maybe_unused]] void* l)
@@ -360,31 +341,31 @@ namespace ui_scripting
void post_unpack() override void post_unpack() override
{ {
// Disable debug breakpoints in the assembly of hksDefaultPanic // Disable debug breakpoints in the assembly of hksDefaultPanic
utils::hook::set<std::uint8_t>(0x140197E7E, 0x90); utils::hook::set<std::uint8_t>(SELECT_VALUE(0x140178C5E, 0x140197E7E), 0x90);
// Restore hksDefaultPanic for complete error messages // Restore hksDefaultPanic for complete error messages
utils::hook::call(0x1401CE6C8, lua_atpanic_stub); utils::hook::call(SELECT_VALUE(0x1401B0028, 0x1401CE6C8), lua_atpanic_stub);
// Do not allow the HKS vm to open LUA's libraries // Do not allow the HKS vm to open LUA's libraries
utils::hook::jump(0x14017E040, luaopen_stub); // io utils::hook::jump(SELECT_VALUE(0x14015BD10, 0x14017E040), luaopen_stub); // io
utils::hook::jump(0x14017E700, luaopen_stub); // os utils::hook::jump(SELECT_VALUE(0x14015C3D0, 0x14017E700), luaopen_stub); // os
utils::hook::jump(0x14017F730, luaopen_stub); // serialize utils::hook::jump(SELECT_VALUE(0x14015D400, 0x14017F730), luaopen_stub); // serialize
utils::hook::jump(0x14017F700, luaopen_stub); // havokscript utils::hook::jump(SELECT_VALUE(0x14015D3D0, 0x14017F700), luaopen_stub); // havokscript
utils::hook::jump(0x14017EC60, luaopen_stub); // debug utils::hook::jump(SELECT_VALUE(0x14015C930, 0x14017EC60), luaopen_stub); // debug
utils::hook::nop(0x14017D969, 5); // coroutine utils::hook::nop(SELECT_VALUE(0x14015B639, 0x14017D969), 5); // coroutine
// Disable unsafe functions // Disable unsafe functions
utils::hook::jump(0x14017AF50, hks_base_stub); // base_loadfile utils::hook::jump(SELECT_VALUE(0x140158C20, 0x14017AF50), hks_base_stub); // base_loadfile
utils::hook::jump(0x14017AE40, hks_base_stub); // base_dofile utils::hook::jump(SELECT_VALUE(0x140158B10, 0x14017AE40), hks_base_stub); // base_dofile
utils::hook::jump(0x14017FB10, hks_base_stub); // base_load utils::hook::jump(SELECT_VALUE(0x14015D7E0, 0x14017FB10), hks_base_stub); // base_load
utils::hook::jump(0x140178CF0, hks_base_stub); // package_loadlib utils::hook::jump(SELECT_VALUE(0x1401569D0, 0x140178CF0), hks_base_stub); // package_loadlib
utils::hook::jump(0x140177E00, hks_base_stub); // package_c_loader utils::hook::jump(SELECT_VALUE(0x140163CD0, 0x140177E00), hks_base_stub); // package_c_loader
utils::hook::jump(0x140177F10, hks_base_stub); // package_all_in_one_loader utils::hook::jump(SELECT_VALUE(0x140163DE0, 0x140177F10), hks_base_stub); // package_all_in_one_loader
utils::hook::jump(0x14017A0D0, hks_base_stub); // string_dump utils::hook::jump(SELECT_VALUE(0x140157DA0, 0x14017A0D0), hks_base_stub); // string_dump
utils::hook::jump(0x1401747D0, hks_base_stub); // os_execute utils::hook::jump(SELECT_VALUE(0x1401606A0, 0x1401747D0), hks_base_stub); // os_execute
if (!game::environment::is_mp()) if (!game::environment::is_mp())
{ {
+90 -16
View File
@@ -59,6 +59,19 @@ namespace ultrawide
} }
} }
// general aspect ratio
auto ultrawide_spawn_window_stub_sp = utils::hook::assemble([](utils::hook::assembler& a)
{
a.call_aligned(0x140519430); // og - setup hwnd struct
a.lea(rcx, ptr(rsp, 0x0A0));
a.call_aligned(ultrawide_patch);
a.mov(edi, ptr(rsp, 0x0B0)); // mov edi, [rsp+0B0h]
a.jmp(0x140519ED3);
});
auto ultrawide_spawn_window_stub_mp = utils::hook::assemble([](utils::hook::assembler& a) auto ultrawide_spawn_window_stub_mp = utils::hook::assemble([](utils::hook::assembler& a)
{ {
a.call_aligned(0x1405E5E70); // og - setup hwnd struct a.call_aligned(0x1405E5E70); // og - setup hwnd struct
@@ -71,6 +84,27 @@ namespace ultrawide
a.jmp(0x1405E6903); a.jmp(0x1405E6903);
}); });
// menu aspect ratio + left offset
auto ultrawide_menu_stub_01_sp = utils::hook::assemble([](utils::hook::assembler& a)
{
a.push(eax);
a.mov(eax, dword_ptr(reinterpret_cast<int64_t>(&hud_aspect_ratio)));
a.movd(xmm5, eax);
a.divss(xmm0, xmm5); // divss xmm0, cs:FLOAT_1_77778 // -> custom aspect ratio
a.subss(xmm1, xmm0); // subss xmm1, xmm0
a.movaps(xmm0, xmm3); // movaps xmm0, xmm3
a.mov(eax, dword_ptr(reinterpret_cast<int64_t>(&hud_aspect_ratio_inv)));
a.movd(xmm5, eax);
a.mulss(xmm0, xmm5); // mulss xmm0, cs:dword_14083D47C // -1.77778 // -> left menu offset
a.pop(eax);
a.jmp(0x1401CD772);
});
auto ultrawide_menu_stub_01_mp = utils::hook::assemble([](utils::hook::assembler& a) auto ultrawide_menu_stub_01_mp = utils::hook::assemble([](utils::hook::assembler& a)
{ {
a.push(eax); a.push(eax);
@@ -90,6 +124,22 @@ namespace ultrawide
a.jmp(0x1402008D2); a.jmp(0x1402008D2);
}); });
// menu aspect ratio right offset
auto ultrawide_menu_stub_02_sp = utils::hook::assemble([](utils::hook::assembler& a)
{
a.push(eax);
a.mov(eax, dword_ptr(reinterpret_cast<int64_t>(&hud_aspect_ratio)));
a.movd(xmm5, eax);
a.mulss(xmm0, xmm5); // mulss xmm0, cs:FLOAT_1_77778 // -> custom aspect ratio
a.mulss(xmm0, xmm1); // mulss xmm0, xmm1
a.pop(eax);
a.jmp(0x1401CD79F);
});
auto ultrawide_menu_stub_02_mp = utils::hook::assemble([](utils::hook::assembler& a) auto ultrawide_menu_stub_02_mp = utils::hook::assemble([](utils::hook::assembler& a)
{ {
a.push(eax); a.push(eax);
@@ -104,6 +154,22 @@ namespace ultrawide
a.jmp(0x1402008FF); a.jmp(0x1402008FF);
}); });
// fix loadscreen and in-game hud aspect ratio
auto ultrawide_hud_stub_sp = utils::hook::assemble([](utils::hook::assembler& a)
{
a.push(eax);
a.mov(eax, dword_ptr(reinterpret_cast<int64_t>(&hud_aspect_ratio)));
a.movd(xmm5, eax);
a.divss(xmm6, xmm5); // divss xmm6, cs:FLOAT_1_77778
a.mulss(xmm6, xmm3); // mulss xmm6, xmm3
a.pop(eax);
a.jmp(0x14024D32F);
});
auto ultrawide_hud_stub_mp = utils::hook::assemble([](utils::hook::assembler& a) auto ultrawide_hud_stub_mp = utils::hook::assemble([](utils::hook::assembler& a)
{ {
a.push(eax); a.push(eax);
@@ -125,37 +191,45 @@ namespace ultrawide
void post_unpack() override void post_unpack() override
{ {
// ultrawide patches // ultrawide patches
utils::hook::call(0x1405DCF58, dvar_register_aspect_ratio); utils::hook::call(SELECT_VALUE(0x140510D0E, 0x1405DCF58), dvar_register_aspect_ratio);
// register r_aspectRatioCustom // register r_aspectRatioCustom
utils::hook::jump(0x1405E68F7, ultrawide_spawn_window_stub_mp, true); utils::hook::jump(
SELECT_VALUE(0x140519EC7, 0x1405E68F7),
SELECT_VALUE(ultrawide_spawn_window_stub_sp, ultrawide_spawn_window_stub_mp), true);
// apply general aspect ratio // apply general aspect ratio
utils::hook::jump(0x1402008BB, ultrawide_menu_stub_01_mp, true); utils::hook::jump(
SELECT_VALUE(0x1401CD75B, 0x1402008BB),
SELECT_VALUE(ultrawide_menu_stub_01_sp, ultrawide_menu_stub_01_mp), true);
// fix menu aspect ratio and left offset // fix menu aspect ratio and left offset
utils::hook::jump(0x1402008F3, ultrawide_menu_stub_02_mp, true); utils::hook::jump(
SELECT_VALUE(0x1401CD793, 0x1402008F3),
SELECT_VALUE(ultrawide_menu_stub_02_sp, ultrawide_menu_stub_02_mp), true);
// fix menu aspect ratio right offset // fix menu aspect ratio right offset
utils::hook::jump(0x1402F7043, ultrawide_hud_stub_mp, true); // fix aspect ratio for loadscreen and general in-game hud utils::hook::jump(
SELECT_VALUE(0x14024D323, 0x1402F7043), SELECT_VALUE(ultrawide_hud_stub_sp, ultrawide_hud_stub_mp),
true); // fix aspect ratio for loadscreen and general in-game hud
// safeArea_adjusted dvars :: disable resets (in-game hud padding) // safeArea_adjusted dvars :: disable resets (in-game hud padding)
utils::hook::nop(0x1402D4FB2, 5); // safeArea_adjusted_horizontal utils::hook::nop(SELECT_VALUE(0x14024AEF2, 0x1402D4FB2), 5); // safeArea_adjusted_horizontal
utils::hook::nop(0x1402F7375, 5); // safeArea_adjusted_horizontal utils::hook::nop(SELECT_VALUE(0x14024D655, 0x1402F7375), 5); // safeArea_adjusted_horizontal
utils::hook::nop(0x1402D4FD9, 5); utils::hook::nop(SELECT_VALUE(0x14024AF19, 0x1402D4FD9), 5);
utils::hook::set<uint8_t>(0x1402D4FD9, 0xC3); utils::hook::set<BYTE>(SELECT_VALUE(0x14024AF19, 0x1402D4FD9), 0xC3);
// safeArea_adjusted_vertical :: return instead of jumping to Dvar_SetFloat // safeArea_adjusted_vertical :: return instead of jumping to Dvar_SetFloat
utils::hook::nop(0x1402F7384, 5); // safeArea_adjusted_vertical utils::hook::nop(SELECT_VALUE(0x14024D664, 0x1402F7384), 5); // safeArea_adjusted_vertical
// safeArea_ dvars :: remove cheat protection + add saved flag // safeArea_ dvars :: remove cheat protection + add saved flag
utils::hook::set<uint8_t>(0x1402F6EBF + 4, 0x1); utils::hook::set<BYTE>(SELECT_VALUE(0x14024D19F + 4, 0x1402F6EBF + 4), 0x1);
// register safeArea_horizontal (mov dword ptr [rsp+20h], 4) // register safeArea_horizontal (mov dword ptr [rsp+20h], 4)
utils::hook::set<uint8_t>(0x1402F6EF7 + 4, 0x1); utils::hook::set<BYTE>(SELECT_VALUE(0x14024D1D7 + 4, 0x1402F6EF7 + 4), 0x1);
// register safeArea_vertical (mov dword ptr [rsp+20h], 4) // register safeArea_vertical (mov dword ptr [rsp+20h], 4)
utils::hook::set<uint8_t>(0x1402F6F27 + 5, 0x0); utils::hook::set<BYTE>(SELECT_VALUE(0x14024D207 + 5, 0x1402F6F27 + 5), 0x0);
utils::hook::set<uint8_t>(0x1402F6F27 + 4, 0x1); utils::hook::set<BYTE>(SELECT_VALUE(0x14024D207 + 4, 0x1402F6F27 + 4), 0x1);
// register safeArea_adjusted_horizontal (mov dword ptr [rsp+20h], 2000h) // register safeArea_adjusted_horizontal (mov dword ptr [rsp+20h], 2000h)
utils::hook::set<uint8_t>(0x1402F6F57 + 5, 0x0); utils::hook::set<BYTE>(SELECT_VALUE(0x14024D237 + 5, 0x1402F6F57 + 5), 0x0);
utils::hook::set<uint8_t>(0x1402F6F57 + 4, 0x1); utils::hook::set<BYTE>(SELECT_VALUE(0x14024D237 + 4, 0x1402F6F57 + 4), 0x1);
// register safeArea_adjusted_vertical (mov dword ptr [rsp+20h], 2000h) // register safeArea_adjusted_vertical (mov dword ptr [rsp+20h], 2000h)
} }
}; };
+3 -3
View File
@@ -30,7 +30,7 @@ namespace demonware
uint64_t file_id; uint64_t file_id;
uint32_t create_time; uint32_t create_time;
uint32_t modified_time; uint32_t modified_time;
bool visibility; bool priv;
uint64_t owner_id; uint64_t owner_id;
std::string filename; std::string filename;
uint32_t file_size; uint32_t file_size;
@@ -41,7 +41,7 @@ namespace demonware
buffer->write_uint64(this->file_id); buffer->write_uint64(this->file_id);
buffer->write_uint32(this->create_time); buffer->write_uint32(this->create_time);
buffer->write_uint32(this->modified_time); buffer->write_uint32(this->modified_time);
buffer->write_bool(this->visibility); buffer->write_bool(this->priv);
buffer->write_uint64(this->owner_id); buffer->write_uint64(this->owner_id);
buffer->write_string(this->filename); buffer->write_string(this->filename);
} }
@@ -52,7 +52,7 @@ namespace demonware
buffer->read_uint64(&this->file_id); buffer->read_uint64(&this->file_id);
buffer->read_uint32(&this->create_time); buffer->read_uint32(&this->create_time);
buffer->read_uint32(&this->modified_time); buffer->read_uint32(&this->modified_time);
buffer->read_bool(&this->visibility); buffer->read_bool(&this->priv);
buffer->read_uint64(&this->owner_id); buffer->read_uint64(&this->owner_id);
buffer->read_string(&this->filename); buffer->read_string(&this->filename);
} }
+4 -20
View File
@@ -10,35 +10,19 @@ namespace demonware
this->register_service(4, &bdGroup::get_groups); this->register_service(4, &bdGroup::get_groups);
} }
void bdGroup::set_groups(i_server* server, byte_buffer* buffer) void bdGroup::set_groups(i_server* server, byte_buffer* /*buffer*/) const
{ {
uint32_t entries_count{}; //uint32_t groupCount;
buffer->read_array_header(game::BD_BB_UNSIGNED_INTEGER32_TYPE, &entries_count); // TODO: Implement array reading
auto reply = server->create_reply(this->get_sub_type()); auto reply = server->create_reply(this->get_sub_type());
buffer->set_use_data_types(false);
for (uint32_t i = 0; i < entries_count; ++i)
{
uint32_t group_id{};
buffer->read_uint32(&group_id);
if (group_id < ARRAYSIZE(this->groups))
{
this->groups[group_id] = 999;
}
}
buffer->set_use_data_types(true);
reply->send(); reply->send();
} }
void bdGroup::get_groups(i_server* server, byte_buffer* buffer) void bdGroup::get_groups(i_server* server, byte_buffer* buffer)
{ {
uint32_t group_count; uint32_t group_count;
buffer->read_array_header(game::BD_BB_UNSIGNED_INTEGER32_TYPE, &group_count); buffer->read_array_header(8, &group_count);
auto reply = server->create_reply(this->get_sub_type()); auto reply = server->create_reply(this->get_sub_type());
@@ -9,7 +9,7 @@ namespace demonware
bdGroup(); bdGroup();
private: private:
void set_groups(i_server* server, byte_buffer* buffer); void set_groups(i_server* server, byte_buffer* buffer) const;
void get_groups(i_server* server, byte_buffer* buffer); void get_groups(i_server* server, byte_buffer* buffer);
uint32_t groups[512]{}; uint32_t groups[512]{};
@@ -17,7 +17,7 @@ namespace demonware
{ {
std::string get_motd_text() std::string get_motd_text()
{ {
return "Welcome to iw6-mod. Did you know aiming down sights increases accuracy? Online Interactions Not Rated by the ESRB."; return "This is not a copy & pasted client";
} }
} }
@@ -119,11 +119,11 @@ namespace demonware
void bdStorage::set_legacy_user_file(i_server* server, byte_buffer* buffer) const void bdStorage::set_legacy_user_file(i_server* server, byte_buffer* buffer) const
{ {
bool visibility; bool priv;
std::string filename, data; std::string filename, data;
buffer->read_string(&filename); buffer->read_string(&filename);
buffer->read_bool(&visibility); buffer->read_bool(&priv);
buffer->read_blob(&data); buffer->read_blob(&data);
const auto id = *reinterpret_cast<const uint64_t*>(utils::cryptography::sha1::compute(filename).data()); const auto id = *reinterpret_cast<const uint64_t*>(utils::cryptography::sha1::compute(filename).data());
@@ -139,11 +139,11 @@ namespace demonware
info->file_id = id; info->file_id = id;
info->filename = filename; info->filename = filename;
info->create_time = static_cast<uint32_t>(std::time(nullptr)); info->create_time = uint32_t(time(nullptr));
info->modified_time = info->create_time; info->modified_time = info->create_time;
info->file_size = static_cast<uint32_t>(data.size()); info->file_size = uint32_t(data.size());
info->owner_id = 0; info->owner_id = 0;
info->visibility = visibility; info->priv = priv;
auto reply = server->create_reply(this->get_sub_type()); auto reply = server->create_reply(this->get_sub_type());
reply->add(info); reply->add(info);
@@ -170,11 +170,11 @@ namespace demonware
info->file_id = id; info->file_id = id;
info->filename = "<>"; info->filename = "<>";
info->create_time = static_cast<uint32_t>(std::time(nullptr)); info->create_time = uint32_t(time(nullptr));
info->modified_time = info->create_time; info->modified_time = info->create_time;
info->file_size = static_cast<uint32_t>(data.size()); info->file_size = uint32_t(data.size());
info->owner_id = 0; info->owner_id = 0;
info->visibility = false; info->priv = false;
auto reply = server->create_reply(this->get_sub_type()); auto reply = server->create_reply(this->get_sub_type());
reply->add(info); reply->add(info);
@@ -207,12 +207,12 @@ namespace demonware
void bdStorage::list_legacy_user_files(i_server* server, byte_buffer* buffer) const void bdStorage::list_legacy_user_files(i_server* server, byte_buffer* buffer) const
{ {
uint64_t owner; uint64_t unk;
uint32_t date; uint32_t date;
uint16_t num_results, offset; uint16_t num_results, offset;
std::string filename, data; std::string filename, data;
buffer->read_uint64(&owner); buffer->read_uint64(&unk);
buffer->read_uint32(&date); buffer->read_uint32(&date);
buffer->read_uint16(&num_results); buffer->read_uint16(&num_results);
buffer->read_uint16(&offset); buffer->read_uint16(&offset);
@@ -229,9 +229,9 @@ namespace demonware
info->filename = filename; info->filename = filename;
info->create_time = 0; info->create_time = 0;
info->modified_time = info->create_time; info->modified_time = info->create_time;
info->file_size = static_cast<uint32_t>(data.size()); info->file_size = uint32_t(data.size());
info->owner_id = owner; info->owner_id = 0;
info->visibility = false; info->priv = false;
reply->add(info); reply->add(info);
} }
@@ -260,9 +260,9 @@ namespace demonware
info->filename = filename; info->filename = filename;
info->create_time = 0; info->create_time = 0;
info->modified_time = info->create_time; info->modified_time = info->create_time;
info->file_size = static_cast<uint32_t>(data.size()); info->file_size = uint32_t(data.size());
info->owner_id = 0; info->owner_id = 0;
info->visibility = false; info->priv = false;
reply->add(info); reply->add(info);
} }
@@ -312,13 +312,13 @@ namespace demonware
void bdStorage::set_user_file(i_server* server, byte_buffer* buffer) const void bdStorage::set_user_file(i_server* server, byte_buffer* buffer) const
{ {
bool visibility; bool priv;
uint64_t owner; uint64_t owner;
std::string game, filename, data; std::string game, filename, data;
buffer->read_string(&game); buffer->read_string(&game);
buffer->read_string(&filename); buffer->read_string(&filename);
buffer->read_bool(&visibility); buffer->read_bool(&priv);
buffer->read_blob(&data); buffer->read_blob(&data);
buffer->read_uint64(&owner); buffer->read_uint64(&owner);
@@ -329,11 +329,11 @@ namespace demonware
info->file_id = *reinterpret_cast<const uint64_t*>(utils::cryptography::sha1::compute(filename).data()); info->file_id = *reinterpret_cast<const uint64_t*>(utils::cryptography::sha1::compute(filename).data());
info->filename = filename; info->filename = filename;
info->create_time = static_cast<uint32_t>(std::time(nullptr)); info->create_time = uint32_t(time(nullptr));
info->modified_time = info->create_time; info->modified_time = info->create_time;
info->file_size = static_cast<uint32_t>(data.size()); info->file_size = uint32_t(data.size());
info->owner_id = owner; info->owner_id = owner;
info->visibility = visibility; info->priv = priv;
auto reply = server->create_reply(this->get_sub_type()); auto reply = server->create_reply(this->get_sub_type());
reply->add(info); reply->add(info);
+1 -5
View File
@@ -16,7 +16,6 @@ namespace dvars
game::dvar_t* con_inputCmdMatchColor = nullptr; game::dvar_t* con_inputCmdMatchColor = nullptr;
game::dvar_t* sv_cheats = nullptr; game::dvar_t* sv_cheats = nullptr;
game::dvar_t* sv_botsAutoJoin = nullptr;
game::dvar_t* g_playerEjection = nullptr; game::dvar_t* g_playerEjection = nullptr;
game::dvar_t* g_playerCollision = nullptr; game::dvar_t* g_playerCollision = nullptr;
@@ -25,7 +24,6 @@ namespace dvars
game::dvar_t* g_rocketPushbackScale = nullptr; game::dvar_t* g_rocketPushbackScale = nullptr;
game::dvar_t* g_enableElevators = nullptr; game::dvar_t* g_enableElevators = nullptr;
game::dvar_t* g_dump_scripts = nullptr; game::dvar_t* g_dump_scripts = nullptr;
game::dvar_t* g_dump_string_tables = nullptr;
game::dvar_t* g_log = nullptr; game::dvar_t* g_log = nullptr;
game::dvar_t* bg_surfacePenetration = nullptr; game::dvar_t* bg_surfacePenetration = nullptr;
@@ -48,9 +46,7 @@ namespace dvars
game::dvar_t* cg_legacyCrashHandling = nullptr; game::dvar_t* cg_legacyCrashHandling = nullptr;
game::dvar_t* com_developer_script = nullptr; game::dvar_t* com_developer_script = nullptr;
game::dvar_t** com_developer = nullptr; game::dvar_t** com_developer;
game::dvar_t** fs_gameDirVar = nullptr;
std::string dvar_get_vector_domain(const int components, const game::dvar_limits& domain) std::string dvar_get_vector_domain(const int components, const game::dvar_limits& domain)
{ {
-4
View File
@@ -15,7 +15,6 @@ namespace dvars
extern game::dvar_t* con_inputCmdMatchColor; extern game::dvar_t* con_inputCmdMatchColor;
extern game::dvar_t* sv_cheats; extern game::dvar_t* sv_cheats;
extern game::dvar_t* sv_botsAutoJoin;
extern game::dvar_t* g_playerCollision; extern game::dvar_t* g_playerCollision;
extern game::dvar_t* g_playerEjection; extern game::dvar_t* g_playerEjection;
@@ -24,7 +23,6 @@ namespace dvars
extern game::dvar_t* g_rocketPushbackScale; extern game::dvar_t* g_rocketPushbackScale;
extern game::dvar_t* g_enableElevators; extern game::dvar_t* g_enableElevators;
extern game::dvar_t* g_dump_scripts; extern game::dvar_t* g_dump_scripts;
extern game::dvar_t* g_dump_string_tables;
extern game::dvar_t* g_log; extern game::dvar_t* g_log;
extern game::dvar_t* bg_surfacePenetration; extern game::dvar_t* bg_surfacePenetration;
@@ -49,8 +47,6 @@ namespace dvars
extern game::dvar_t* com_developer_script; extern game::dvar_t* com_developer_script;
extern game::dvar_t** com_developer; extern game::dvar_t** com_developer;
extern game::dvar_t** fs_gameDirVar;
std::string dvar_get_vector_domain(int components, const game::dvar_limits& domain); std::string dvar_get_vector_domain(int components, const game::dvar_limits& domain);
std::string dvar_get_domain(game::dvar_type type, const game::dvar_limits& domain); std::string dvar_get_domain(game::dvar_type type, const game::dvar_limits& domain);
} }
-206
View File
@@ -1,206 +0,0 @@
#include <std_include.hpp>
#include <game/game.hpp>
#include "sv_game.hpp"
#include <component/console.hpp>
#include <utils/string.hpp>
namespace game::engine
{
char* SV_ExpandNewlines(char* in)
{
static char string[1024];
unsigned int l = 0;
while (*in && l < sizeof(string) - 3)
{
if (*in == '\n')
{
string[l++] = '\\';
string[l++] = 'n';
}
else
{
if (*in != '\x14' && *in != '\x15')
{
string[l++] = *in;
}
}
++in;
}
string[l] = '\0';
return string;
}
void SV_CullIgnorableServerCommands(mp::client_t* client)
{
int to = client->reliableSent + 1;
for (int from = to; from <= client->reliableSequence; ++from)
{
int from_index = from & 0x7F;
assert(client->netBuf.reliableCommandInfo[from_index].time >= 0);
if (client->netBuf.reliableCommandInfo[from_index].type)
{
int to_index = to & 0x7F;
if (to_index != from_index)
{
client->netBuf.reliableCommandInfo[to_index] = client->netBuf.reliableCommandInfo[from_index];
}
++to;
}
}
client->reliableSequence = to - 1;
}
void SV_DelayDropClient(mp::client_t* drop, const char* reason)
{
assert(drop);
assert(reason);
assert(drop->header.state != CS_FREE);
if (drop->header.state == CS_ZOMBIE)
{
#ifdef _DEBUG
console::info("(drop->dropReason) = %s", drop->dropReason);
#endif
}
else if (!drop->dropReason)
{
drop->dropReason = reason;
}
}
void SV_AddServerCommand(mp::client_t* client, svscmd_type type, const char* cmd)
{
static_assert(offsetof(mp::client_t, netBuf.reliableCommandInfo[0].cmd) == 0xC44);
if (client->testClient == TC_BOT)
{
return;
}
if (client->reliableSequence - client->reliableAcknowledge < 64 && client->header.state == CS_ACTIVE || (SV_CullIgnorableServerCommands(client), type))
{
int len = static_cast<int>(std::strlen(cmd)) + 1;
int to = SV_CanReplaceServerCommand(client, reinterpret_cast<const unsigned char*>(cmd), len);
if (to < 0)
{
++client->reliableSequence;
}
else
{
int from = to + 1;
while (from <= client->reliableSequence)
{
client->netBuf.reliableCommandInfo[to & 0x7F] = client->netBuf.reliableCommandInfo[from & 0x7F];
++from;
++to;
}
}
if (client->reliableSequence - client->reliableAcknowledge == 129)
{
#ifdef _DEBUG
console::info("===== pending server commands =====\n");
int i = 0;
for (i = client->reliableAcknowledge + 1; i <= client->reliableSequence; ++i)
{
console::info("cmd %5d: %8d: %s\n", i, client->netBuf.reliableCommandInfo[i & 0x7F].time, client->netBuf.reliableCommandInfo[i & 0x7F].cmd);
}
console::info("cmd %5d: %8d: %s\n", i, *game::mp::serverTime, cmd);
#endif
NET_OutOfBandPrint(NS_SERVER, &client->header.netchan.remoteAddress, "disconnect");
SV_DelayDropClient(client, "EXE_SERVERCOMMANDOVERFLOW");
type = SV_CMD_RELIABLE;
cmd = utils::string::va("%c \"EXE_SERVERCOMMANDOVERFLOW\"", 'r');
}
int index = client->reliableSequence & 0x7F;
MSG_WriteReliableCommandToBuffer(cmd, client->netBuf.reliableCommandInfo[index].cmd, sizeof(client->netBuf.reliableCommandInfo[index].cmd));
client->netBuf.reliableCommandInfo[index].time = *game::mp::serverTime;
client->netBuf.reliableCommandInfo[index].type = type;
}
}
void SV_SendServerCommand(mp::client_t* cl, svscmd_type type, const char* fmt, ...)
{
mp::client_t* client;
int j, len;
va_list va;
const auto server_command_buf_large = std::make_unique<char[]>(0x20000);
va_start(va, fmt);
len = vsnprintf(server_command_buf_large.get(), 0x20000, fmt, va);
va_end(va);
assert(len >= 0);
if (cl)
{
SV_AddServerCommand(cl, type, server_command_buf_large.get());
return;
}
if (environment::is_dedi() && !std::strncmp(server_command_buf_large.get(), "print", 5))
{
console::info("broadcast: %s\n", SV_ExpandNewlines(server_command_buf_large.get()));
}
const auto* sv_maxclients = Dvar_FindVar("sv_maxclients");
for (j = 0, client = mp::svs_clients; j < sv_maxclients->current.integer; j++, client++)
{
if (client->header.state < CS_CLIENTLOADING)
{
continue;
}
SV_AddServerCommand(client, type, server_command_buf_large.get());
}
}
void SV_GameSendServerCommand(int clientNum, svscmd_type type, const char* text)
{
[[maybe_unused]] const auto* sv_maxclients = Dvar_FindVar("sv_maxclients");
if (clientNum == -1)
{
SV_SendServerCommand(nullptr, type, "%s", text);
return;
}
assert(sv_maxclients->current.integer >= 1 && sv_maxclients->current.integer <= 18);
assert(static_cast<unsigned>(clientNum) < sv_maxclients->current.unsignedInt);
SV_SendServerCommand(&mp::svs_clients[clientNum], type, "%s", text);
}
void SV_ReconnectClients(int savepersist)
{
const auto* sv_maxclients = Dvar_FindVar("sv_maxclients");
for (int i = 0; i < sv_maxclients->current.integer; ++i)
{
mp::client_t* client = &mp::svs_clients[i];
if (client->header.state < CS_CONNECTED)
{
continue;
}
SV_AddServerCommand(client, SV_CMD_RELIABLE, utils::string::va("%c", savepersist != 0 ? 107 : 118));
const char* denied = ClientConnect(i, client->scriptId);
if (denied)
{
SV_DropClient(client, denied, true);
console::info("SV_MapRestart_f: dropped client %i - denied!\n", i);
}
else if (client->header.state == CS_ACTIVE)
{
SV_ClientEnterWorld(client, &client->lastUsercmd);
}
}
}
}
-8
View File
@@ -1,8 +0,0 @@
#pragma once
namespace game::engine
{
void SV_SendServerCommand(mp::client_t* cl, svscmd_type type, const char* fmt, ...);
void SV_GameSendServerCommand(int clientNum, svscmd_type type, const char* text);
void SV_ReconnectClients(int savepersist);
}
+8 -7
View File
@@ -25,13 +25,6 @@ namespace game
return sv_cmd_args->argv[sv_cmd_args->nesting][index]; return sv_cmd_args->argv[sv_cmd_args->nesting][index];
} }
HANDLE Sys_OpenFileReliable(const char* filename)
{
return ::CreateFileA(filename, GENERIC_READ, FILE_SHARE_READ, nullptr,
OPEN_EXISTING,
FILE_FLAG_OVERLAPPED | FILE_FLAG_NO_BUFFERING, nullptr);
}
namespace environment namespace environment
{ {
launcher::mode mode = launcher::mode::none; launcher::mode mode = launcher::mode::none;
@@ -56,6 +49,11 @@ namespace game
return get_mode() == launcher::mode::multiplayer; return get_mode() == launcher::mode::multiplayer;
} }
bool is_sp()
{
return get_mode() == launcher::mode::singleplayer;
}
void set_mode(const launcher::mode _mode) void set_mode(const launcher::mode _mode)
{ {
mode = _mode; mode = _mode;
@@ -72,6 +70,9 @@ namespace game
case launcher::mode::multiplayer: case launcher::mode::multiplayer:
return "Multiplayer"; return "Multiplayer";
case launcher::mode::singleplayer:
return "Multiplayer";
case launcher::mode::none: case launcher::mode::none:
return "None"; return "None";
+8 -11
View File
@@ -1,16 +1,9 @@
#pragma once #pragma once
#include "structs.hpp" #include "structs.hpp"
#include "launcher/launcher.hpp"
namespace launcher #define SELECT_VALUE(sp, mp) (game::environment::is_sp() ? (sp) : (mp))
{
enum class mode
{
none,
multiplayer,
server,
};
}
#define SERVER_CD_KEY "iw6-mod-CD-Key" #define SERVER_CD_KEY "iw6-mod-CD-Key"
@@ -21,6 +14,7 @@ namespace game
launcher::mode get_mode(); launcher::mode get_mode();
bool is_mp(); bool is_mp();
bool is_sp();
bool is_dedi(); bool is_dedi();
void set_mode(launcher::mode mode); void set_mode(launcher::mode mode);
@@ -40,6 +34,11 @@ namespace game
T* get() const T* get() const
{ {
if (environment::is_sp())
{
return sp_object_;
}
return mp_object_; return mp_object_;
} }
@@ -64,8 +63,6 @@ namespace game
[[nodiscard]] int SV_Cmd_Argc(); [[nodiscard]] int SV_Cmd_Argc();
[[nodiscard]] const char* SV_Cmd_Argv(int index); [[nodiscard]] const char* SV_Cmd_Argv(int index);
[[nodiscard]] HANDLE Sys_OpenFileReliable(const char* filename);
[[nodiscard]] bool is_headless(); [[nodiscard]] bool is_headless();
void show_error(const std::string& text, const std::string& title = "Error"); void show_error(const std::string& text, const std::string& title = "Error");
+1 -1
View File
@@ -70,7 +70,7 @@ namespace scripting
script_function get_function_by_index(const std::uint32_t index) script_function get_function_by_index(const std::uint32_t index)
{ {
static const auto function_table = &gsc::func_table; static const auto function_table = &gsc::func_table;
static const auto method_table = 0x1446B8A90; static const auto method_table = SELECT_VALUE(0x144E1F9E0, 0x1446B8A90);
if (index <= 0x1000) if (index <= 0x1000)
{ {
+51 -183
View File
@@ -10,14 +10,6 @@ namespace game
typedef vec_t vec3_t[3]; typedef vec_t vec3_t[3];