This commit is contained in:
Rim 2025-02-20 06:26:06 -05:00
commit de70cc66bf
51 changed files with 1419 additions and 690 deletions

View File

@ -5,14 +5,16 @@
#include "auth.hpp"
#include "command.hpp"
#include "network.hpp"
#include "console.hpp"
#include "network.hpp"
#include <utils/hook.hpp>
#include <utils/string.hpp>
#include <utils/smbios.hpp>
#include <utils/info_string.hpp>
#include <utils/cryptography.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>
namespace auth
{
@ -31,13 +33,21 @@ namespace auth
std::string get_hw_profile_guid()
{
auto hw_profile_path = (utils::properties::get_appdata_path() / "s1-guid.dat").generic_string();
if (utils::io::file_exists(hw_profile_path))
{
// Migration
utils::io::remove_file(hw_profile_path);
}
HW_PROFILE_INFO info;
if (!GetCurrentHwProfileA(&info))
{
return {};
}
return std::string{info.szHwProfileGuid, sizeof(info.szHwProfileGuid)};
auto hw_profile_info = std::string{ info.szHwProfileGuid, sizeof(info.szHwProfileGuid) };
return hw_profile_info;
}
std::string get_protected_data()
@ -47,7 +57,7 @@ namespace auth
DATA_BLOB data_in{}, data_out{};
data_in.pbData = reinterpret_cast<uint8_t*>(input.data());
data_in.cbData = static_cast<DWORD>(input.size());
if(CryptProtectData(&data_in, nullptr, nullptr, nullptr, nullptr, CRYPTPROTECT_LOCAL_MACHINE, &data_out) != TRUE)
if (CryptProtectData(&data_in, nullptr, nullptr, nullptr, nullptr, CRYPTPROTECT_LOCAL_MACHINE, &data_out) != TRUE)
{
return {};
}
@ -76,9 +86,72 @@ namespace auth
return entropy;
}
utils::cryptography::ecc::key& get_key()
bool load_key(utils::cryptography::ecc::key& key)
{
static auto key = utils::cryptography::ecc::generate_key(512, get_key_entropy());
std::string data{};
auto key_path = (utils::properties::get_appdata_path() / "s1-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() / "s1-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;
}
utils::cryptography::ecc::key load_or_generate_key()
{
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() / "s1-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;
}
@ -86,7 +159,7 @@ namespace auth
{
std::string connect_string(format, len);
game::SV_Cmd_TokenizeString(connect_string.data());
const auto _ = gsl::finally([]()
const auto _0 = gsl::finally([]()
{
game::SV_Cmd_EndTokenizedString();
});
@ -109,7 +182,7 @@ namespace auth
proto::network::connect_info info;
info.set_publickey(get_key().get_public_key());
info.set_signature(sign_message(get_key(), challenge));
info.set_signature(utils::cryptography::ecc::sign_message(get_key(), challenge));
info.set_infostring(connect_string);
network::send(*adr, "connect", info.SerializeAsString());
@ -151,14 +224,14 @@ namespace auth
utils::cryptography::ecc::key key;
key.set(info.publickey());
const auto xuid = strtoull(steam_id.data(), nullptr, 16);
const auto xuid = std::strtoull(steam_id.data(), nullptr, 16);
if (xuid != key.get_hash())
{
network::send(*from, "error", utils::string::va("XUID doesn't match the certificate: %llX != %llX", xuid, key.get_hash()), '\n');
return;
}
if (!key.is_valid() || !verify_message(key, challenge, info.signature()))
if (!key.is_valid() || !utils::cryptography::ecc::verify_message(key, challenge, info.signature()))
{
network::send(*from, "error", "Challenge signature was invalid!", '\n');
return;
@ -218,7 +291,7 @@ namespace auth
utils::hook::call(0x140208C54, send_connect_data_stub);
}
command::add("guid", []
command::add("guid", []() -> void
{
console::info("Your guid: %llX\n", steam::SteamUser()->GetSteamID().bits);
});

View File

@ -30,8 +30,7 @@ namespace binding
if (value && value < get_num_keys())
{
const auto len = sprintf_s(&buffer[bytes_used], (buffer_size_align - bytes_used),
"bind %s \"%s\"\n", key_button, game::command_whitelist[value]);
const auto len = game::Com_sprintf(&buffer[bytes_used], (buffer_size_align - bytes_used), "bind %s \"%s\"\n", key_button, game::command_whitelist[value]);
if (len < 0)
{
@ -45,8 +44,7 @@ namespace binding
value -= get_num_keys();
if (static_cast<size_t>(value) < custom_binds.size() && !custom_binds[value].empty())
{
const auto len = sprintf_s(&buffer[bytes_used], (buffer_size_align - bytes_used),
"bind %s \"%s\"\n", key_button, custom_binds[value].data());
const auto len = game::Com_sprintf(&buffer[bytes_used], (buffer_size_align - bytes_used), "bind %s \"%s\"\n", key_button, custom_binds[value].data());
if (len < 0)
{

View File

@ -62,7 +62,6 @@ namespace bots
return;
}
// SV_BotGetRandomName
const auto* const bot_name = game::SV_BotGetRandomName();
const auto* bot_ent = game::SV_AddBot(bot_name);
if (bot_ent)

View File

@ -33,9 +33,7 @@ namespace bullet
return;
}
dvars::bg_surfacePenetration = game::Dvar_RegisterFloat("bg_surfacePenetration", 0.0f,
0.0f, std::numeric_limits<float>::max(), game::DVAR_FLAG_SAVED,
"Set to a value greater than 0 to override the surface penetration depth");
dvars::bg_surfacePenetration = game::Dvar_RegisterFloat("bg_surfacePenetration", 0.0f, 0.0f, std::numeric_limits<float>::max(), game::DVAR_FLAG_SAVED);
bg_get_surface_penetration_depth_hook.create(0x1401641A0, &bg_get_surface_penetration_depth_stub);
}
};

View File

@ -82,7 +82,16 @@ namespace colors
void com_clean_name_stub(const char* in, char* out, const int out_size)
{
strncpy_s(out, out_size, in, _TRUNCATE);
// Check that the name is at least 3 char without colors
char name[32]{};
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)
@ -92,14 +101,12 @@ namespace colors
return string;
}
size_t get_client_name_stub(const int local_client_num, const int index, char* buf, const int size,
const size_t unk, const size_t unk2)
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)
{
// CL_GetClientName (CL_GetClientNameAndClantag?)
const auto result = reinterpret_cast<size_t(*)(int, int, char*, int, size_t, size_t)>(0x140213E60)(
local_client_num, index, buf, size, unk, unk2);
// CL_GetClientNameAndClanTag -> CL_GetClientNameAndClanTagColorize
const auto result = utils::hook::invoke<int>(0x140213E60, local_client_num, index, name_buf, name_size, clan_tag_buf, clan_tag_size);
utils::string::strip(buf, buf, static_cast<size_t>(size));
utils::string::strip(name_buf, name_buf, name_size);
return result;
}
@ -148,10 +155,11 @@ namespace colors
if (!game::environment::is_sp())
{
// allows colored name in-game
utils::hook::jump(0x1404C9510, com_clean_name_stub);
utils::hook::call(0x1402DBD28, com_clean_name_stub);
utils::hook::call(0x1402DBD6D, com_clean_name_stub);
// don't apply colors to overhead names
utils::hook::call(0x1401891B0, get_client_name_stub);
utils::hook::call(0x1401891B0, cl_get_client_name_and_clan_tag_stub);
// patch I_CleanStr
utils::hook::jump(0x1404C99A0, i_clean_str_stub);

View File

@ -1,14 +1,13 @@
#include <std_include.hpp>
#include "loader/component_loader.hpp"
#include "game/game.hpp"
#include "game/engine/sv_game.hpp"
#include "game/dvars.hpp"
#include "command.hpp"
#include "console.hpp"
#include "game_console.hpp"
#include "scheduler.hpp"
#include "fastfiles.hpp"
#include "game_console.hpp"
#include <utils/hook.hpp>
#include <utils/string.hpp>
@ -236,15 +235,13 @@ namespace command
{
if (!dvars::sv_cheats->current.enabled)
{
game::SV_GameSendServerCommand(ent->s.number, game::SV_CMD_RELIABLE,
"f \"Cheats are not enabled on this server\"");
game::engine::SV_GameSendServerCommand(ent->s.number, game::SV_CMD_RELIABLE, "f \"Cheats are not enabled on this server\"");
return false;
}
if (ent->health < 1)
{
game::SV_GameSendServerCommand(ent->s.number, game::SV_CMD_RELIABLE,
"f \"You must be alive to use this command\"");
game::engine::SV_GameSendServerCommand(ent->s.number, game::SV_CMD_RELIABLE, "f \"You must be alive to use this command\"");
return false;
}
@ -253,7 +250,10 @@ namespace command
void execute(std::string command, const bool sync)
{
command += "\n";
if (!command.ends_with('\n'))
{
command += "\n";
}
if (sync)
{
@ -579,8 +579,7 @@ namespace command
ent->flags ^= game::FL_GODMODE;
game::SV_GameSendServerCommand(ent->s.number, game::SV_CMD_RELIABLE,
utils::string::va("f \"godmode %s\"", (ent->flags & game::FL_GODMODE) ? "^2on" : "^1off"));
game::engine::SV_GameSendServerCommand(ent->s.number, game::SV_CMD_RELIABLE, utils::string::va("f \"godmode %s\"", (ent->flags & game::FL_GODMODE) ? "^2on" : "^1off"));
});
add_sv("demigod", [](game::mp::gentity_s* ent, const params_sv&)
@ -590,8 +589,7 @@ namespace command
ent->flags ^= game::FL_DEMI_GODMODE;
game::SV_GameSendServerCommand(ent->s.number, game::SV_CMD_RELIABLE,
utils::string::va("f \"demigod mode %s\"", (ent->flags & game::FL_DEMI_GODMODE) ? "^2on" : "^1off"));
game::engine::SV_GameSendServerCommand(ent->s.number, game::SV_CMD_RELIABLE, utils::string::va("f \"demigod mode %s\"", (ent->flags & game::FL_DEMI_GODMODE) ? "^2on" : "^1off"));
});
add_sv("notarget", [](game::mp::gentity_s* ent, const params_sv&)
@ -601,8 +599,7 @@ namespace command
ent->flags ^= game::FL_NOTARGET;
game::SV_GameSendServerCommand(ent->s.number, game::SV_CMD_RELIABLE,
utils::string::va("f \"notarget %s\"", (ent->flags & game::FL_NOTARGET) ? "^2on" : "^1off"));
game::engine::SV_GameSendServerCommand(ent->s.number, game::SV_CMD_RELIABLE, utils::string::va("f \"notarget %s\"", (ent->flags & game::FL_NOTARGET) ? "^2on" : "^1off"));
});
add_sv("noclip", [](game::mp::gentity_s* ent, const params_sv&)
@ -612,8 +609,7 @@ namespace command
ent->client->flags ^= 1;
game::SV_GameSendServerCommand(ent->s.number, game::SV_CMD_RELIABLE,
utils::string::va("f \"noclip %s\"", ent->client->flags & 1 ? "^2on" : "^1off"));
game::engine::SV_GameSendServerCommand(ent->s.number, game::SV_CMD_RELIABLE, utils::string::va("f \"noclip %s\"", ent->client->flags & 1 ? "^2on" : "^1off"));
});
add_sv("ufo", [](game::mp::gentity_s* ent, const params_sv&)
@ -623,8 +619,7 @@ namespace command
ent->client->flags ^= 2;
game::SV_GameSendServerCommand(ent->s.number, game::SV_CMD_RELIABLE,
utils::string::va("f \"ufo %s\"", ent->client->flags & 2 ? "^2on" : "^1off"));
game::engine::SV_GameSendServerCommand(ent->s.number, game::SV_CMD_RELIABLE, utils::string::va("f \"ufo %s\"", ent->client->flags & 2 ? "^2on" : "^1off"));
});
add_sv("give", [](game::mp::gentity_s* ent, const params_sv& params)
@ -634,8 +629,7 @@ namespace command
if (params.size() < 2)
{
game::SV_GameSendServerCommand(ent->s.number, game::SV_CMD_RELIABLE,
"f \"You did not specify a weapon name\"");
game::engine::SV_GameSendServerCommand(ent->s.number, game::SV_CMD_RELIABLE, "f \"You did not specify a weapon name\"");
return;
}
@ -658,8 +652,7 @@ namespace command
if (params.size() < 2)
{
game::SV_GameSendServerCommand(ent->s.number, game::SV_CMD_RELIABLE,
"f \"You did not specify a weapon name\"");
game::engine::SV_GameSendServerCommand(ent->s.number, game::SV_CMD_RELIABLE, "f \"You did not specify a weapon name\"");
return;
}

View File

@ -42,7 +42,7 @@ namespace console
{
static thread_local char buffer[0x1000];
const auto count = vsnprintf_s(buffer, _TRUNCATE, message, *ap);
const auto count = vsnprintf(buffer, sizeof(buffer), message, *ap);
if (count < 0) return {};
return {buffer, static_cast<size_t>(count)};
@ -84,15 +84,14 @@ namespace console
void print_stub(const char* fmt, ...)
{
char buffer[4096]{};
va_list ap;
va_start(ap, fmt);
char buffer[4096]{};
const auto res = vsnprintf_s(buffer, _TRUNCATE, fmt, ap);
(void)res;
print_message(buffer);
[[maybe_unused]] const auto res = vsnprintf(buffer, sizeof(buffer), fmt, ap);
va_end(ap);
print_message(buffer);
}
void append_text(const char* text)

View File

@ -1,13 +1,16 @@
#include <std_include.hpp>
#include "loader/component_loader.hpp"
#include "game/game.hpp"
#include "game/engine/sv_game.hpp"
#include "command.hpp"
#include "console.hpp"
#include "dvars.hpp"
#include "network.hpp"
#include "scheduler.hpp"
#include "server_list.hpp"
#include "network.hpp"
#include "command.hpp"
#include "dvars.hpp"
#include "component/gsc/script_extension.hpp"
#include <utils/hook.hpp>
#include <utils/string.hpp>
@ -16,9 +19,6 @@ namespace dedicated
{
namespace
{
utils::hook::detour gscr_set_dynamic_dvar_hook;
utils::hook::detour com_quit_f_hook;
const game::dvar_t* sv_lanOnly;
void init_dedicated_server()
@ -99,8 +99,7 @@ namespace dedicated
for (const auto& command : queue)
{
game::Cbuf_AddText(0, command.data());
game::Cbuf_AddText(0, "\n");
command::execute(command);
}
}
@ -109,49 +108,53 @@ namespace dedicated
std::this_thread::sleep_for(1ms);
}
void gscr_set_dynamic_dvar()
void sv_kill_server_f()
{
auto s = game::Scr_GetString(0);
auto* dvar = game::Dvar_FindVar(s);
if (dvar && !std::strncmp("scr_", dvar->name, 4))
game::Com_Shutdown("EXE_SERVERKILLED");
}
void start_map(const std::string& map_name)
{
if (game::Live_SyncOnlineDataFlags(0) > 32)
{
scheduler::once([map_name]()
{
command::execute(std::format("map {}", map_name), false);
}, scheduler::pipeline::main, 1s);
return;
}
gscr_set_dynamic_dvar_hook.invoke<void>();
}
void kill_server()
{
for (auto i = 0; i < *game::mp::svs_numclients; ++i)
if (!game::SV_MapExists(map_name.c_str()))
{
if (game::mp::svs_clients[i].header.state >= 3)
{
game::SV_GameSendServerCommand(i, game::SV_CMD_CAN_IGNORE,
utils::string::va("r \"%s\"", "EXE_ENDOFGAME"));
}
console::info("Map '%s' doesn't exist.\n", map_name.c_str());
return;
}
com_quit_f_hook.invoke<void>();
auto* current_mapname = game::Dvar_FindVar("mapname");
if (current_mapname && utils::string::to_lower(current_mapname->current.string) == utils::string::to_lower(map_name) && (game::SV_Loaded() && !game::VirtualLobby_Loaded()))
{
console::info("Restarting map: %s\n", map_name.c_str());
command::execute("map_restart", false);
return;
}
console::info("Starting map: %s\n", map_name.c_str());
auto* gametype = game::Dvar_FindVar("g_gametype");
if (gametype && gametype->current.string)
{
command::execute(utils::string::va("ui_gametype %s", gametype->current.string), true);
}
command::execute(utils::string::va("ui_mapname %s", map_name.c_str()), true);
game::SV_StartMapForParty(0, map_name.c_str(), false, false);
}
void sys_error_stub(const char* msg, ...)
void gscr_is_using_match_rules_data_stub()
{
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::main, 3s);
game::Com_Error(game::ERR_DROP, "%s", buffer);
game::Scr_AddInt(0);
}
}
@ -177,9 +180,9 @@ namespace dedicated
return;
}
game::Dvar_RegisterBool("dedicated", true, game::DVAR_FLAG_READ, "Dedicated server");
game::Dvar_RegisterBool("dedicated", true, game::DVAR_FLAG_READ);
sv_lanOnly = game::Dvar_RegisterBool("sv_lanOnly", false, game::DVAR_FLAG_NONE, "Don't send heartbeat");
sv_lanOnly = game::Dvar_RegisterBool("sv_lanOnly", false, game::DVAR_FLAG_NONE);
// Disable VirtualLobby
dvars::override::register_bool("virtualLobbyEnabled", false, game::DVAR_FLAG_NONE | game::DVAR_FLAG_READ);
@ -190,12 +193,12 @@ namespace dedicated
// Don't allow sv_hostname to be changed by the game
dvars::disable::set_string("sv_hostname");
// Stop crashing from sys_errors
utils::hook::jump(0x1404D6260, sys_error_stub);
// Hook R_SyncGpu
utils::hook::jump(0x1405A7630, sync_gpu_stub);
// Make GScr_IsUsingMatchRulesData return 0 so the game doesn't override the cfg
gsc::override_function("isusingmatchrulesdata", gscr_is_using_match_rules_data_stub);
utils::hook::jump(0x14020C6B0, init_dedicated_server);
// delay startup commands until the initialization is done
@ -205,9 +208,6 @@ namespace dedicated
utils::hook::call(0x1403CEC35, execute_console_command);
utils::hook::nop(0x1403CEC4B, 5);
// patch GScr_SetDynamicDvar to behave better
gscr_set_dynamic_dvar_hook.create(0x140312D00, &gscr_set_dynamic_dvar);
utils::hook::nop(0x1404AE6AE, 5); // don't load config file
utils::hook::nop(0x1403AF719, 5); // ^
utils::hook::set<uint8_t>(0x1403D2490, 0xC3); // don't save config file
@ -306,7 +306,7 @@ namespace dedicated
console::info("==================================\n");
// remove disconnect command
game::Cmd_RemoveCommand(reinterpret_cast<const char*>(751));
game::Cmd_RemoveCommand(751);
execute_startup_command_queue();
execute_console_command_queue();
@ -317,8 +317,38 @@ namespace dedicated
command::add("heartbeat", send_heartbeat);
}, scheduler::pipeline::main, 1s);
command::add("killserver", kill_server);
com_quit_f_hook.create(0x1403D08C0, &kill_server);
command::add("killserver", sv_kill_server_f);
command::add("map", [](const command::params& argument)
{
if (argument.size() != 2)
{
return;
}
start_map(argument[1]);
});
command::add("map_restart", []()
{
if (!game::SV_Loaded() || game::VirtualLobby_Loaded())
{
return;
}
*reinterpret_cast<int*>(0x1488692B0) = 1; // sv_map_restart
*reinterpret_cast<int*>(0x1488692B4) = 1; // sv_loadScripts
*reinterpret_cast<int*>(0x1488692B8) = 0; // sv_migrate
reinterpret_cast<void(*)(int)>(0x140437460)(0); // SV_CheckLoadGame
});
command::add("fast_restart", []()
{
if (game::SV_Loaded() && !game::VirtualLobby_Loaded())
{
game::SV_FastRestart(0);
}
});
}
};
}

View File

@ -19,28 +19,28 @@ namespace dedicated_info
scheduler::loop([]()
{
auto* sv_running = game::Dvar_FindVar("sv_running");
const auto* sv_running = game::Dvar_FindVar("sv_running");
if (!sv_running || !sv_running->current.enabled)
{
console::set_title("s1-mod Dedicated Server");
return;
}
auto* const sv_hostname = game::Dvar_FindVar("sv_hostname");
auto* const sv_maxclients = game::Dvar_FindVar("sv_maxclients");
auto* const mapname = game::Dvar_FindVar("mapname");
const auto* sv_hostname = game::Dvar_FindVar("sv_hostname");
const auto* sv_maxclients = game::Dvar_FindVar("sv_maxclients");
const auto* mapname = game::Dvar_FindVar("mapname");
auto bot_count = 0;
auto client_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* self = &game::mp::g_entities[i];
if (client->header.state >= 1 && self && self->client)
{
client_count++;
++client_count;
if (game::SV_BotIsBot(i))
{
++bot_count;

View File

@ -342,21 +342,20 @@ namespace demonware
void bd_logger_stub(const char* const function, const char* const msg, ...)
{
static const auto* bd_logger_enabled = game::Dvar_RegisterBool("bd_logger_enabled", false, game::DVAR_FLAG_NONE, "Enable bdLogger");
static const auto* bd_logger_enabled = game::Dvar_RegisterBool("bd_logger_enabled", false, game::DVAR_FLAG_NONE);
if (!bd_logger_enabled->current.enabled)
{
return;
}
char buffer[2048];
char buffer[2048]{};
va_list ap;
va_start(ap, msg);
vsnprintf_s(buffer, _TRUNCATE, msg, ap);
printf("%s: %s\n", function, buffer);
vsnprintf(buffer, sizeof(buffer), msg, ap);
va_end(ap);
printf("%s: %s\n", function, buffer);
}
void startup_dw()

View File

@ -1,7 +1,7 @@
#include <std_include.hpp>
#include "loader/component_loader.hpp"
#include "game/game.hpp"
#include "game/engine/sv_game.hpp"
#include "game/dvars.hpp"
#include "console.hpp"
@ -130,7 +130,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* command = utils::string::va("q %s \"%s\"", dvar, value);
game::SV_GameSendServerCommand(entity_num, game::SV_CMD_RELIABLE, command);
game::engine::SV_GameSendServerCommand(static_cast<char>(entity_num), game::SV_CMD_RELIABLE, command);
}
const auto player_cmd_set_client_dvar = utils::hook::assemble([](utils::hook::assembler& a)
@ -181,7 +181,7 @@ namespace dvar_cheats
constexpr auto value = false;
#endif
dvars::sv_cheats = game::Dvar_RegisterBool("sv_cheats", value, game::DVAR_FLAG_REPLICATED, "Allow cheat commands and dvars on this server");
dvars::sv_cheats = game::Dvar_RegisterBool("sv_cheats", value, game::DVAR_FLAG_REPLICATED);
}
};
}

View File

@ -209,7 +209,7 @@ namespace dvars
utils::hook::detour dvar_set_int_hook;
utils::hook::detour dvar_set_string_hook;
game::dvar_t* dvar_register_bool_stub(const char* name, bool value, unsigned int flags, const char* description)
game::dvar_t* dvar_register_bool_stub(const char* name, bool value, unsigned int flags)
{
auto* var = find_dvar(override::register_bool_overrides, name);
if (var)
@ -218,11 +218,10 @@ namespace dvars
flags = var->flags;
}
return dvar_register_bool_hook.invoke<game::dvar_t*>(name, value, flags, description);
return dvar_register_bool_hook.invoke<game::dvar_t*>(name, value, flags);
}
game::dvar_t* dvar_register_float_stub(const char* name, float value, float min, float max, unsigned int flags,
const char* description)
game::dvar_t* dvar_register_float_stub(const char* name, float value, float min, float max, unsigned int flags)
{
auto* var = find_dvar(override::register_float_overrides, name);
if (var)
@ -233,11 +232,10 @@ namespace dvars
flags = var->flags;
}
return dvar_register_float_hook.invoke<game::dvar_t*>(name, value, min, max, flags, description);
return dvar_register_float_hook.invoke<game::dvar_t*>(name, value, min, max, flags);
}
game::dvar_t* dvar_register_int_stub(const char* name, int value, int min, int max, unsigned int flags,
const char* description)
game::dvar_t* dvar_register_int_stub(const char* name, int value, int min, int max, unsigned int flags)
{
auto* var = find_dvar(override::register_int_overrides, name);
if (var)
@ -248,10 +246,10 @@ namespace dvars
flags = var->flags;
}
return dvar_register_int_hook.invoke<game::dvar_t*>(name, value, min, max, flags, description);
return dvar_register_int_hook.invoke<game::dvar_t*>(name, value, min, max, flags);
}
game::dvar_t* dvar_register_string_stub(const char* name, const char* value, unsigned int flags, const char* description)
game::dvar_t* dvar_register_string_stub(const char* name, const char* value, unsigned int flags)
{
auto* var = find_dvar(override::register_string_overrides, name);
if (var)
@ -260,11 +258,10 @@ namespace dvars
flags = var->flags;
}
return dvar_register_string_hook.invoke<game::dvar_t*>(name, value, flags, description);
return dvar_register_string_hook.invoke<game::dvar_t*>(name, value, flags);
}
game::dvar_t* dvar_register_vector2_stub(const char* name, float x, float y, float min, float max,
unsigned int flags, const char* description)
game::dvar_t* dvar_register_vector2_stub(const char* name, float x, float y, float min, float max, unsigned int flags)
{
auto* var = find_dvar(override::register_vector2_overrides, name);
if (var)
@ -276,11 +273,10 @@ namespace dvars
flags = var->flags;
}
return dvar_register_vector2_hook.invoke<game::dvar_t*>(name, x, y, min, max, flags, description);
return dvar_register_vector2_hook.invoke<game::dvar_t*>(name, x, y, min, max, flags);
}
game::dvar_t* dvar_register_vector3_stub(const char* name, float x, float y, float z, float min,
float max, unsigned int flags, const char* description)
game::dvar_t* dvar_register_vector3_stub(const char* name, float x, float y, float z, float min, float max, unsigned int flags)
{
auto* var = find_dvar(override::register_vector3_overrides, name);
if (var)
@ -293,7 +289,7 @@ namespace dvars
flags = var->flags;
}
return dvar_register_vector3_hook.invoke<game::dvar_t*>(name, x, y, z, min, max, flags, description);
return dvar_register_vector3_hook.invoke<game::dvar_t*>(name, x, y, z, min, max, flags);
}
void dvar_set_bool_stub(game::dvar_t* dvar, bool boolean)

View File

@ -135,7 +135,7 @@ namespace fastfiles
db_try_load_x_file_internal_hook.create(SELECT_VALUE(0x1401816F0, 0x1402741C0), &db_try_load_x_file_internal);
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);
command::add("loadzone", [](const command::params& params)
{

View File

@ -64,15 +64,22 @@ namespace filesystem
void startup()
{
register_path("s1");
const auto base = std::filesystem::current_path();
register_path(base / "s1");
register_path(get_binary_directory() + "\\data");
if (get_binary_directory() != base)
{
register_path(base / "data");
}
// game's search paths
register_path("devraw");
register_path("devraw_shared");
register_path("raw_shared");
register_path("raw");
register_path("main");
register_path(base / "devraw");
register_path(base / "devraw_shared");
register_path(base / "raw_shared");
register_path(base / "raw");
register_path(base / "main");
}
void check_for_startup()

View File

@ -137,9 +137,9 @@ namespace fps
}
}
const game::dvar_t* cg_draw_fps_register_stub(const char* dvar_name, const char** value_list, const int default_index, unsigned int /*flags*/, const char* description)
const game::dvar_t* cg_draw_fps_register_stub(const char* dvar_name, const char** value_list, const int default_index, unsigned int /*flags*/)
{
cg_drawFPS = game::Dvar_RegisterEnum(dvar_name, value_list, default_index, game::DVAR_FLAG_SAVED, description);
cg_drawFPS = game::Dvar_RegisterEnum(dvar_name, value_list, default_index, game::DVAR_FLAG_SAVED);
return cg_drawFPS;
}
}
@ -173,12 +173,12 @@ namespace fps
scheduler::loop(cg_draw_fps, scheduler::pipeline::renderer);
if (game::environment::is_mp())
{
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);
scheduler::loop(cg_draw_ping, scheduler::pipeline::renderer);
}
game::Dvar_RegisterBool("cg_infobar_ping", false, game::DVAR_FLAG_SAVED, "Show server latency");
game::Dvar_RegisterBool("cg_infobar_fps", false, game::DVAR_FLAG_SAVED, "Show FPS counter");
game::Dvar_RegisterBool("cg_infobar_ping", false, game::DVAR_FLAG_SAVED);
game::Dvar_RegisterBool("cg_infobar_fps", false, game::DVAR_FLAG_SAVED);
}
};
}

View File

@ -63,7 +63,7 @@ namespace game_console
void clear()
{
strncpy_s(con.buffer, "", sizeof(con.buffer));
game::I_strncpyz(con.buffer, "", sizeof(con.buffer));
con.cursor = 0;
fixed_input = "";
@ -249,7 +249,7 @@ namespace game_console
dvars::con_inputDvarInactiveValueColor->current.vector, offset);
}
strncpy_s(con.globals.auto_complete_choice, matches[0].data(), 64);
game::I_strncpyz(con.globals.auto_complete_choice, matches[0].data(), 64);
con.globals.may_auto_complete = true;
}
else if (matches.size() > 1)
@ -274,7 +274,7 @@ namespace game_console
}
}
strncpy_s(con.globals.auto_complete_choice, matches[0].data(), 64);
game::I_strncpyz(con.globals.auto_complete_choice, matches[0].data(), 64);
con.globals.may_auto_complete = true;
}
}
@ -365,11 +365,11 @@ namespace game_console
void print_internal(const char* fmt, ...)
{
char va_buffer[0x200]{};
char va_buffer[1024]{};
va_list ap;
va_start(ap, fmt);
vsprintf_s(va_buffer, fmt, ap);
vsnprintf(va_buffer, sizeof(va_buffer), fmt, ap);
va_end(ap);
const auto formatted = std::string(va_buffer);
@ -425,7 +425,7 @@ namespace game_console
con.buffer[1] = '\0';
}
strncat_s(con.buffer, con.globals.auto_complete_choice, 64);
game::I_strncat(con.buffer, sizeof(con.buffer), con.globals.auto_complete_choice);
con.cursor = static_cast<int>(std::string(con.buffer).length());
if (con.cursor != 254)
@ -550,7 +550,7 @@ namespace game_console
if (history_index != -1)
{
strncpy_s(con.buffer, history.at(history_index).c_str(), sizeof(con.buffer));
game::I_strncpyz(con.buffer, history.at(history_index).c_str(), sizeof(con.buffer));
con.cursor = static_cast<int>(strlen(con.buffer));
}
}
@ -565,7 +565,7 @@ namespace game_console
if (history_index != -1)
{
strncpy_s(con.buffer, history.at(history_index).c_str(), sizeof(con.buffer));
game::I_strncpyz(con.buffer, history.at(history_index).c_str(), sizeof(con.buffer));
con.cursor = static_cast<int>(strlen(con.buffer));
}
}
@ -720,7 +720,7 @@ namespace game_console
con.output_visible = false;
con.display_line_offset = 0;
con.line_count = 0;
strncpy_s(con.buffer, "", sizeof(con.buffer));
game::I_strncpyz(con.buffer, "", sizeof(con.buffer));
con.globals.x = 0.0f;
con.globals.y = 0.0f;
@ -728,7 +728,7 @@ namespace game_console
con.globals.font_height = 0.0f;
con.globals.may_auto_complete = false;
con.globals.info_line_count = 0;
strncpy_s(con.globals.auto_complete_choice, "", 64);
game::I_strncpyz(con.globals.auto_complete_choice, "", 64);
// add clear command
command::add("clear", [&]()
@ -746,33 +746,30 @@ namespace game_console
// add our dvars
dvars::con_inputBoxColor = game::Dvar_RegisterVec4("con_inputBoxColor", 0.2f, 0.2f, 0.2f, 0.9f, 0.0f, 1.0f,
game::DVAR_FLAG_SAVED,
"color of console input box");
game::DVAR_FLAG_SAVED);
dvars::con_inputHintBoxColor = game::Dvar_RegisterVec4("con_inputHintBoxColor", 0.3f, 0.3f, 0.3f, 1.0f,
0.0f, 1.0f,
game::DVAR_FLAG_SAVED, "color of console input hint box");
game::DVAR_FLAG_SAVED);
dvars::con_outputBarColor = game::Dvar_RegisterVec4("con_outputBarColor", 0.5f, 0.5f, 0.5f, 0.6f, 0.0f,
1.0f, game::DVAR_FLAG_SAVED,
"color of console output bar");
1.0f, game::DVAR_FLAG_SAVED);
dvars::con_outputSliderColor = game::Dvar_RegisterVec4("con_outputSliderColor", 1.0f, 0.8f, 0.0f, 1.0f,
0.0f, 1.0f,
game::DVAR_FLAG_SAVED, "color of console output slider");
game::DVAR_FLAG_SAVED);
dvars::con_outputWindowColor = game::Dvar_RegisterVec4("con_outputWindowColor", 0.25f, 0.25f, 0.25f, 0.85f,
0.0f,
1.0f, game::DVAR_FLAG_SAVED, "color of console output window");
1.0f, game::DVAR_FLAG_SAVED);
dvars::con_inputDvarMatchColor = game::Dvar_RegisterVec4("con_inputDvarMatchColor", 1.0f, 1.0f, 0.8f, 1.0f,
0.0f,
1.0f, game::DVAR_FLAG_SAVED, "color of console matched dvar");
1.0f, game::DVAR_FLAG_SAVED);
dvars::con_inputDvarValueColor = game::Dvar_RegisterVec4("con_inputDvarValueColor", 1.0f, 1.0f, 0.8f, 1.0f,
0.0f,
1.0f, game::DVAR_FLAG_SAVED, "color of console matched dvar value");
1.0f, game::DVAR_FLAG_SAVED);
dvars::con_inputDvarInactiveValueColor = game::Dvar_RegisterVec4(
"con_inputDvarInactiveValueColor", 0.8f, 0.8f,
0.8f, 1.0f, 0.0f, 1.0f, game::DVAR_FLAG_SAVED,
"color of console inactive dvar value");
0.8f, 1.0f, 0.0f, 1.0f, game::DVAR_FLAG_SAVED);
dvars::con_inputCmdMatchColor = game::Dvar_RegisterVec4("con_inputCmdMatchColor", 0.80f, 0.80f, 1.0f, 1.0f,
0.0f,
1.0f, game::DVAR_FLAG_SAVED, "color of console matched command");
1.0f, game::DVAR_FLAG_SAVED);
}
};
}

View File

@ -10,7 +10,6 @@
#include "gsc/script_extension.hpp"
#include <utils/hook.hpp>
#include <utils/io.hpp>
#include <utils/string.hpp>
@ -23,7 +22,7 @@ namespace game_log
char buf[1024]{};
std::size_t out_chars = 0;
for (auto i = 0u; i < game::Scr_GetNumParam(); ++i)
for (std::uint32_t i = 0; i < game::Scr_GetNumParam(); ++i)
{
const auto* value = game::Scr_GetString(i);
const auto len = std::strlen(value);
@ -34,7 +33,7 @@ namespace game_log
break;
}
strncat_s(buf, value, _TRUNCATE);
game::I_strncat(buf, sizeof(buf), value);
}
g_log_printf("%s", buf);
@ -53,18 +52,11 @@ namespace game_log
va_list ap;
va_start(ap, fmt);
vsnprintf_s(buffer, _TRUNCATE, fmt, ap);
vsnprintf(buffer, sizeof(buffer), fmt, ap);
va_end(ap);
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
@ -81,7 +73,7 @@ namespace game_log
scheduler::once([]
{
dvars::g_log = game::Dvar_RegisterString("g_log", "logs/games_mp.log", game::DVAR_FLAG_NONE, "Log file name");
dvars::g_log = game::Dvar_RegisterString("g_log", "logs/games_mp.log", game::DVAR_FLAG_NONE);
}, scheduler::pipeline::main);
scripting::on_init([]
@ -102,9 +94,9 @@ namespace game_log
g_log_printf("InitGame\n");
});
scripting::on_shutdown([](int free_scripts)
scripting::on_shutdown([](const int clear_scripts) -> void
{
console::info("==== ShutdownGame (%d) ====\n", free_scripts);
console::info("==== ShutdownGame (%d) ====\n", clear_scripts);
g_log_printf("ShutdownGame:\n");
g_log_printf("------------------------------------------------------------\n");

View File

@ -144,54 +144,45 @@ namespace gameplay
public:
void post_unpack() override
{
dvars::player_sustainAmmo = game::Dvar_RegisterBool("player_sustainAmmo", false,
game::DVAR_FLAG_REPLICATED, "Firing weapon will not decrease clip ammo");
dvars::player_sustainAmmo = game::Dvar_RegisterBool("player_sustainAmmo", false, game::DVAR_FLAG_REPLICATED);
pm_weapon_use_ammo_hook.create(SELECT_VALUE(0x1403DD050, 0x140162B20), &pm_weapon_use_ammo_stub);
if (game::environment::is_sp()) return;
// 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);
utils::hook::call(0x1402D5E4A, stuck_in_client_stub);
// Implement player collision dvar
dvars::g_playerCollision = game::Dvar_RegisterBool("g_playerCollision", true, game::DVAR_FLAG_REPLICATED,
"Flag whether player collision is on or off");
dvars::g_playerCollision = game::Dvar_RegisterBool("g_playerCollision", true, game::DVAR_FLAG_REPLICATED);
utils::hook::call(0x1404563DA, cm_transformed_capsule_trace_stub); // SV_ClipMoveToEntity
utils::hook::call(0x1401F7F8F, cm_transformed_capsule_trace_stub); // CG_ClipMoveToEntity
// Implement bouncing dvar
utils::hook::jump(0x14014DF91, pm_bouncing_stub_mp, true);
dvars::pm_bouncing = game::Dvar_RegisterBool("pm_bouncing", false,
game::DVAR_FLAG_REPLICATED, "Enable bouncing");
dvars::pm_bouncing = game::Dvar_RegisterBool("pm_bouncing", false, game::DVAR_FLAG_REPLICATED);
// Change jump_slowdownEnable dvar flags to just "replicated"
utils::hook::set<uint8_t>(0x140135992, game::DVAR_FLAG_REPLICATED);
// Choosing the following min/max because the game would truncate larger values
dvars::g_gravity = game::Dvar_RegisterInt("g_gravity", 800, std::numeric_limits<short>::min(),
std::numeric_limits<short>::max(), game::DVAR_FLAG_REPLICATED, "Gravity in inches per second per second");
dvars::g_gravity = game::Dvar_RegisterInt("g_gravity", 800, std::numeric_limits<short>::min(), std::numeric_limits<short>::max(), game::DVAR_FLAG_REPLICATED);
utils::hook::jump(0x1402D5A5D, client_end_frame_stub, true);
utils::hook::nop(0x1402D5A69, 1); // Nop skipped opcode
// Choosing the following min/max because the game would truncate larger values
dvars::g_speed = game::Dvar_RegisterInt("g_speed", 190,
std::numeric_limits<short>::min(), std::numeric_limits<short>::max(), game::DVAR_FLAG_REPLICATED, "Player speed");
dvars::g_speed = game::Dvar_RegisterInt("g_speed", 190, std::numeric_limits<short>::min(), std::numeric_limits<short>::max(), game::DVAR_FLAG_REPLICATED);
utils::hook::jump(0x1402D6A8C, client_think_real_stub, true);
utils::hook::nop(0x1402D6A98, 4); // Nop skipped opcodes
dvars::jump_height = game::Dvar_RegisterFloat("jump_height", 39.0f,
0.0f, 1000.0f, game::DVAR_FLAG_REPLICATED, "The maximum height of a player's jump");
dvars::jump_height = game::Dvar_RegisterFloat("jump_height", 39.0f, 0.0f, 1000.0f, game::DVAR_FLAG_REPLICATED);
utils::hook::call(0x1401352FF, jump_start_stub);
dvars::jump_ladderPushVel = game::Dvar_RegisterFloat("jump_ladderPushVel", 128.0f,
0.0f, 1024.0f, game::DVAR_FLAG_REPLICATED, "The velocity of a jump off of a ladder");
dvars::jump_ladderPushVel = game::Dvar_RegisterFloat("jump_ladderPushVel", 128.0f, 0.0f, 1024.0f, game::DVAR_FLAG_REPLICATED);
utils::hook::jump(0x1401358B3, jump_push_off_ladder, true);
utils::hook::nop(0x1401358BF, 4); // Nop skipped opcodes
dvars::g_elevators = game::Dvar_RegisterBool("g_elevators", false,
game::DVAR_FLAG_REPLICATED, "Enable elevators");
dvars::g_elevators = game::Dvar_RegisterBool("g_elevators", false, game::DVAR_FLAG_REPLICATED);
utils::hook::call(0x140146134, pm_player_trace_stub);
utils::hook::call(0x14014619B, pm_player_trace_stub);

View File

@ -186,100 +186,6 @@ namespace gsc
}
}
}
void scr_get_vector(unsigned int index, float* vector_value)
{
if (index < game::scr_VmPub->outparamcount)
{
auto* value = game::scr_VmPub->top - index;
if (value->type == game::VAR_VECTOR)
{
std::memcpy(vector_value, value->u.vectorValue, sizeof(std::float_t[3]));
return;
}
scr_error(va("Type %s is not a vector", var_typename[value->type]));
}
scr_error(va("Parameter %u does not exist", index + 1));
}
int scr_get_int(unsigned int index)
{
if (index < game::scr_VmPub->outparamcount)
{
auto* value = game::scr_VmPub->top - index;
if (value->type == game::VAR_INTEGER)
{
return value->u.intValue;
}
scr_error(va("Type %s is not an int", var_typename[value->type]));
}
scr_error(va("Parameter %u does not exist", index + 1));
return 0;
}
float scr_get_float(unsigned int index)
{
if (index < game::scr_VmPub->outparamcount)
{
auto* value = game::scr_VmPub->top - index;
if (value->type == game::VAR_FLOAT)
{
return value->u.floatValue;
}
if (value->type == game::VAR_INTEGER)
{
return static_cast<float>(value->u.intValue);
}
scr_error(va("Type %s is not a float", var_typename[value->type]));
}
scr_error(va("Parameter %u does not exist", index + 1));
return 0.0f;
}
int scr_get_pointer_type(unsigned int index)
{
if (index < game::scr_VmPub->outparamcount)
{
if ((game::scr_VmPub->top - index)->type == game::VAR_POINTER)
{
return static_cast<int>(game::GetObjectType((game::scr_VmPub->top - index)->u.uintValue));
}
scr_error(va("Type %s is not an object", var_typename[(game::scr_VmPub->top - index)->type]));
}
scr_error(va("Parameter %u does not exist", index + 1));
return 0;
}
int scr_get_type(unsigned int index)
{
if (index < game::scr_VmPub->outparamcount)
{
return (game::scr_VmPub->top - index)->type;
}
scr_error(va("Parameter %u does not exist", index + 1));
return 0;
}
const char* scr_get_type_name(unsigned int index)
{
if (index < game::scr_VmPub->outparamcount)
{
return var_typename[(game::scr_VmPub->top - index)->type];
}
scr_error(va("Parameter %u does not exist", index + 1));
return nullptr;
}
}
std::optional<std::pair<std::string, std::string>> find_function(const char* pos)
@ -299,34 +205,130 @@ namespace gsc
return {};
}
void scr_get_vector(unsigned int index, float* vector_value)
{
if (index < game::scr_VmPub->outparamcount)
{
auto* value = game::scr_VmPub->top - index;
if (value->type == game::VAR_VECTOR)
{
std::memcpy(vector_value, value->u.vectorValue, sizeof(std::float_t[3]));
return;
}
scr_error(va("Type %s is not a vector", var_typename[value->type]));
}
scr_error(va("Parameter %u does not exist", index + 1));
}
int scr_get_int(unsigned int index)
{
if (index < game::scr_VmPub->outparamcount)
{
auto* value = game::scr_VmPub->top - index;
if (value->type == game::VAR_INTEGER)
{
return value->u.intValue;
}
scr_error(va("Type %s is not an int", var_typename[value->type]));
}
scr_error(va("Parameter %u does not exist", index + 1));
return 0;
}
float scr_get_float(unsigned int index)
{
if (index < game::scr_VmPub->outparamcount)
{
auto* value = game::scr_VmPub->top - index;
if (value->type == game::VAR_FLOAT)
{
return value->u.floatValue;
}
if (value->type == game::VAR_INTEGER)
{
return static_cast<float>(value->u.intValue);
}
scr_error(va("Type %s is not a float", var_typename[value->type]));
}
scr_error(va("Parameter %u does not exist", index + 1));
return 0.0f;
}
int scr_get_pointer_type(unsigned int index)
{
if (index < game::scr_VmPub->outparamcount)
{
if ((game::scr_VmPub->top - index)->type == game::VAR_POINTER)
{
return static_cast<int>(game::GetObjectType((game::scr_VmPub->top - index)->u.uintValue));
}
scr_error(va("Type %s is not an object", var_typename[(game::scr_VmPub->top - index)->type]));
}
scr_error(va("Parameter %u does not exist", index + 1));
return 0;
}
int scr_get_type(unsigned int index)
{
if (index < game::scr_VmPub->outparamcount)
{
return (game::scr_VmPub->top - index)->type;
}
scr_error(va("Parameter %u does not exist", index + 1));
return 0;
}
const char* scr_get_type_name(unsigned int index)
{
if (index < game::scr_VmPub->outparamcount)
{
return var_typename[(game::scr_VmPub->top - index)->type];
}
scr_error(va("Parameter %u does not exist", index + 1));
return nullptr;
}
class error final : public component_interface
{
public:
void post_unpack() override
{
scr_emit_function_hook.create(SELECT_VALUE(0x1403113D0, 0x1403ED900), &scr_emit_function_stub);
utils::hook::call(SELECT_VALUE(0x140311364, 0x1403ED894), compile_error_stub); // LinkFile
utils::hook::call(SELECT_VALUE(0x1403113B8, 0x1403ED8E8), compile_error_stub); // LinkFile
utils::hook::call(SELECT_VALUE(0x1403114AA, 0x1403ED9DB), find_variable_stub); // Scr_EmitFunction
// Restore basic error messages for commonly used scr functions
utils::hook::jump(SELECT_VALUE(0x14031BD80, 0x1403F8510), scr_get_const_string);
utils::hook::jump(game::Scr_GetInt, scr_get_int);
utils::hook::jump(game::Scr_GetFloat, scr_get_float);
utils::hook::jump(game::Scr_GetVector, scr_get_vector);
utils::hook::jump(SELECT_VALUE(0x14031C690, 0x1403F8D70), scr_get_type);
utils::hook::jump(SELECT_VALUE(0x14031C700, 0x1403F8DE0), scr_get_type_name);
if (game::environment::is_sp())
{
return;
}
scr_emit_function_hook.create(0x1403ED900, &scr_emit_function_stub);
utils::hook::call(0x1403ED894, compile_error_stub); // LinkFile
utils::hook::call(0x1403ED8E8, compile_error_stub); // LinkFile
utils::hook::call(0x1403ED9DB, find_variable_stub); // Scr_EmitFunction
// Restore basic error messages for commonly used scr functions
utils::hook::jump(0x1403F8990, scr_get_object);
utils::hook::jump(0x1403F8510, scr_get_const_string);
utils::hook::jump(0x1403F82F0, scr_get_const_istring);
utils::hook::jump(0x140327870, scr_validate_localized_string_ref);
utils::hook::jump(0x1403F8EC0, scr_get_vector);
utils::hook::jump(0x1403F88D0, scr_get_int);
utils::hook::jump(0x1403F8820, scr_get_float);
utils::hook::jump(0x1403F8BA0, scr_get_pointer_type);
utils::hook::jump(0x1403F8D70, scr_get_type);
utils::hook::jump(0x1403F8DE0, scr_get_type_name);
}
void pre_destroy() override

View File

@ -3,4 +3,11 @@
namespace gsc
{
std::optional<std::pair<std::string, std::string>> find_function(const char* pos);
void scr_get_vector(unsigned int index, float* vector_value);
int scr_get_int(unsigned int index);
float scr_get_float(unsigned int index);
int scr_get_pointer_type(unsigned int index);
int scr_get_type(unsigned int index);
const char* scr_get_type_name(unsigned int index);
}

View File

@ -5,15 +5,15 @@
#include "game/scripting/functions.hpp"
#include <utils/hook.hpp>
#include "component/console.hpp"
#include "component/command.hpp"
#include "component/console.hpp"
#include "component/notifies.hpp"
#include "script_error.hpp"
#include "script_extension.hpp"
#include "script_loading.hpp"
#include <utils/hook.hpp>
#include <utils/string.hpp>
namespace gsc
@ -39,9 +39,60 @@ namespace gsc
utils::hook::detour scr_register_function_hook;
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)
{
const auto result = utils::hook::invoke<unsigned int>(0x1403318B0, p_name, type);
const auto result = game::Scr_GetFunction(p_name, type);
for (const auto& [name, func] : functions)
{
@ -122,27 +173,36 @@ namespace gsc
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 char* location;
if (function.has_value())
{
console::warn("\tat function \"%s\" in file \"%s.gsc\"\n", function.value().first.data(), function.value().second.data());
location = utils::string::va("function \"%s\" in file \"%s\"", function.value().first.data(), function.value().second.data());
}
else
{
console::warn("\tat unknown location %p\n", pos);
location = utils::string::va("unknown location %p", 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(int mark_pos)
void vm_error_stub(unsigned __int64 mark_pos)
{
if (!dvars::com_developer_script->current.enabled && !force_error_print)
{
utils::hook::invoke<void>(0x1404B6790, mark_pos);
game::LargeLocalResetToMark(mark_pos);
return;
}
console::warn("******* script runtime error ********\n");
const auto opcode_id = *reinterpret_cast<std::uint8_t*>(0x148806768);
const auto opcode_id = *reinterpret_cast<std::uint8_t*>(SELECT_VALUE(0x14A19DE68, 0x148806768));
const std::string error = gsc_error_msg.has_value() ? std::format(": {}", gsc_error_msg.value()) : std::string();
@ -168,7 +228,7 @@ namespace gsc
print_callstack();
console::warn("************************************\n");
utils::hook::invoke<void>(0x1404B6790, mark_pos);
game::LargeLocalResetToMark(mark_pos);
}
void scr_register_function_stub(void* func, int type, unsigned int name)
@ -219,6 +279,46 @@ namespace gsc
{
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());
}
const char* get_code_pos(const int index)
{
if (static_cast<unsigned int>(index) >= game::scr_VmPub->outparamcount)
{
scr_error("Scr_GetCodePos: index is out of range");
return "";
}
const auto* value = &game::scr_VmPub->top[-index];
if (value->type != game::VAR_FUNCTION)
{
scr_error("Scr_GetCodePos requires a function as parameter");
return "";
}
return value->u.codePosValue;
}
}
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 scr_error(const char* error)
@ -252,33 +352,40 @@ namespace gsc
override_function("print", &scr_print);
override_function("println", &scr_print_ln);
utils::hook::set<std::uint32_t>(SELECT_VALUE(0x1403115BC, 0x1403EDAEC), 0x1000); // Scr_RegisterFunction
utils::hook::set<std::uint32_t>(SELECT_VALUE(0x1403115C2 + 4, 0x1403EDAF2 + 4), RVA(&func_table)); // Scr_RegisterFunction
utils::hook::set<std::uint32_t>(SELECT_VALUE(0x14031F097 + 3, 0x1403FB7F7 + 3), RVA(&func_table)); // VM_Execute_0
utils::hook::inject(SELECT_VALUE(0x140311964 + 3, 0x1403EDEE4 + 3), &func_table); // Scr_BeginLoadScripts
if (game::environment::is_sp())
{
return;
}
utils::hook::nop(0x1403FB7F7 + 5, 2);
utils::hook::call(0x1403FB7F7, vm_call_builtin_function);
utils::hook::call(0x1403FCAB0, vm_error_stub); // LargeLocalResetToMark
utils::hook::call(0x1403EDF1F, scr_get_function_stub);
override_function("assert", &assert_cmd);
override_function("assertex", &assert_ex_cmd);
override_function("assertmsg", &assert_msg_cmd);
add_function("executecommand", []
override_function("isdedicatedserver", &scr_cmd_is_dedicated_server);
add_function("replacefunc", []
{
if (scr_get_type(0) != game::VAR_FUNCTION || scr_get_type(1) != game::VAR_FUNCTION)
{
throw gsc_error("Parameter must be a function");
}
notifies::set_gsc_hook(get_code_pos(0), get_code_pos(1));
});
add_function("executecommand", []() -> void
{
const auto* cmd = game::Scr_GetString(0);
command::execute(cmd);
});
utils::hook::set<std::uint32_t>(SELECT_VALUE(0x1403115BC, 0x1403EDAEC), 0x1000); // Scr_RegisterFunction
utils::hook::set<std::uint32_t>(SELECT_VALUE(0x1403115C2 + 4, 0x1403EDAF2 + 4), RVA(&func_table)); // Scr_RegisterFunction
// utils::hook::set<std::uint32_t>(SELECT_VALUE(0x14031F097 + 3, 0x1403FB7F7 + 3), RVA(&func_table)); // VM_Execute_0
utils::hook::inject(SELECT_VALUE(0x140311964 + 3, 0x1403EDEE4 + 3), &func_table); // Scr_BeginLoadScripts
utils::hook::nop(SELECT_VALUE(0x14031F097 + 5, 0x1403FB7F7 + 5), 2);
utils::hook::call(SELECT_VALUE(0x14031F097, 0x1403FB7F7), vm_call_builtin_function);
utils::hook::call(SELECT_VALUE(0x14032034F, 0x1403FCAB0), vm_error_stub); // LargeLocalResetToMark
utils::hook::call(SELECT_VALUE(0x14031199F, 0x1403EDF1F), scr_get_function_stub);
}
};
}

View File

@ -1,9 +1,36 @@
#pragma once
#include <xsk/gsc/engine/s1_pc.hpp>
namespace gsc
{
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 scr_error(const char* error);
void override_function(const std::string& name, game::BuiltinFunction func);
void add_function(const std::string& name, game::BuiltinFunction function);

View File

@ -12,6 +12,7 @@
#include "component/console.hpp"
#include "component/scripting.hpp"
#include "script_extension.hpp"
#include "script_loading.hpp"
namespace gsc
@ -32,6 +33,7 @@ namespace gsc
init_handles.clear();
loaded_scripts.clear();
script_allocator.clear();
clear_devmap();
}
bool read_raw_script_file(const std::string& name, std::string* data)
@ -115,6 +117,12 @@ namespace gsc
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;
}
catch (const std::exception& ex)
@ -181,13 +189,10 @@ namespace gsc
}
}
void load_scripts(const std::filesystem::path& root_dir)
void load_scripts_from_folder(const std::filesystem::path& root_dir, const std::filesystem::path& script_dir)
{
const std::filesystem::path script_dir = root_dir / "scripts";
if (!utils::io::directory_exists(script_dir.generic_string()))
{
return;
}
console::info("Scanning directory '%s' for custom GSC scripts...\n", script_dir.generic_string().data());
const auto scripts = utils::io::list_files(script_dir.generic_string());
for (const auto& script : scripts)
@ -205,6 +210,44 @@ 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");
if (game::environment::is_sp())
{
const std::filesystem::path game_folder = "sp";
load(base_dir / game_folder);
load(base_dir / game_folder / map_name->current.string);
}
else
{
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)
{
if (loaded_scripts.contains(name))
@ -224,8 +267,6 @@ namespace gsc
return;
}
clear();
for (const auto& path : filesystem::get_search_paths())
{
load_scripts(path);
@ -259,7 +300,38 @@ namespace gsc
utils::hook::invoke<void>(0x1403380D0);
}
void scr_load_level_stub()
int g_scr_set_level_script_stub(game::ScriptFunctions* functions)
{
const auto result = utils::hook::invoke<int>(0x140262F60, functions);
for (const auto& path : filesystem::get_search_paths())
{
load_scripts(path);
}
return result;
}
void scr_load_level_singleplayer_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>(0x140257720);
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>(0x140325B90);
@ -347,7 +419,7 @@ namespace gsc
public:
void post_load() override
{
gsc_ctx = std::make_unique<xsk::gsc::s1_pc::context>();
gsc_ctx = std::make_unique<xsk::gsc::s1_pc::context>(xsk::gsc::instance::server);
}
void post_unpack() override
@ -358,28 +430,32 @@ namespace gsc
utils::hook::call(SELECT_VALUE(0x1403309E9, 0x1403309E9), scr_begin_load_scripts_stub); // GScr_LoadScripts
utils::hook::call(SELECT_VALUE(0x14023DA84, 0x140330B9C), scr_end_load_scripts_stub); // GScr_LoadScripts
dvars::com_developer = game::Dvar_RegisterInt("developer", 0, 0, 2, game::DVAR_FLAG_NONE, "Enable development options");
dvars::com_developer_script = game::Dvar_RegisterBool("developer_script", false, game::DVAR_FLAG_NONE, "Enable developer script comments");
// ProcessScript
utils::hook::call(SELECT_VALUE(0x14031AB47, 0x1403F7317), find_script);
utils::hook::call(SELECT_VALUE(0x14031AB57, 0x1403F7327), db_is_x_asset_default);
dvars::com_developer = game::Dvar_RegisterInt("developer", 0, 0, 2, game::DVAR_FLAG_NONE);
dvars::com_developer_script = game::Dvar_RegisterBool("developer_script", false, game::DVAR_FLAG_NONE);
if (game::environment::is_sp())
{
return;
utils::hook::call(0x1402632A5, g_scr_set_level_script_stub);
utils::hook::call(0x140226931, scr_load_level_singleplayer_stub);
}
else
{
// GScr_LoadScripts
utils::hook::call(0x140330B97, gscr_post_load_scripts_stub);
// Exec script handles
utils::hook::call(0x1402F71AE, g_load_structs_stub);
utils::hook::call(0x1402F71C7, scr_load_level_multiplayer_stub);
}
// ProcessScript
utils::hook::call(0x1403F7317, find_script);
utils::hook::call(0x1403F7327, db_is_x_asset_default);
// GScr_LoadScripts
utils::hook::call(0x140330B97, gscr_post_load_scripts_stub);
// Exec script handles
utils::hook::call(0x1402F71AE, g_load_structs_stub);
utils::hook::call(0x1402F71C7, scr_load_level_stub);
scripting::on_shutdown([](int free_scripts)
scripting::on_shutdown([](const int clear_scripts) -> void
{
if (free_scripts)
if (clear_scripts)
{
clear();
}

View File

@ -2,7 +2,6 @@
#include "loader/component_loader.hpp"
#include "game/game.hpp"
#include "party.hpp"
#include "console.hpp"
#include <utils/hook.hpp>
@ -11,57 +10,15 @@ namespace logger
{
namespace
{
utils::hook::detour com_error_hook;
game::dvar_t* logger_dev = nullptr;
void print_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("%s", buffer);
}
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);
party::clear_sv_motd(); // clear sv_motd on error if it exists
com_error_hook.invoke<void>(error, "%s", buffer);
}
void print_warning(const char* msg, ...)
{
char buffer[2048]{};
va_list ap;
va_start(ap, msg);
vsnprintf_s(buffer, _TRUNCATE, msg, ap);
vsnprintf(buffer, sizeof(buffer), msg, ap);
va_end(ap);
console::warn("%s", buffer);
@ -73,7 +30,7 @@ namespace logger
va_list ap;
va_start(ap, msg);
vsnprintf_s(buffer, _TRUNCATE, msg, ap);
vsnprintf(buffer, sizeof(buffer), msg, ap);
va_end(ap);
console::info("%s", buffer);
@ -90,7 +47,7 @@ namespace logger
va_list ap;
va_start(ap, msg);
vsnprintf_s(buffer, _TRUNCATE, msg, ap);
vsnprintf(buffer, sizeof(buffer), msg, ap);
va_end(ap);
console::info("%s", buffer);
@ -157,14 +114,7 @@ namespace logger
utils::hook::jump(0x140701A1C, print);
}
if (!game::environment::is_sp())
{
utils::hook::call(0x1404D8543, print_com_error);
}
com_error_hook.create(game::Com_Error, com_error_stub);
logger_dev = game::Dvar_RegisterBool("logger_dev", false, game::DVAR_FLAG_SAVED, "Print dev stuff");
logger_dev = game::Dvar_RegisterBool("logger_dev", false, game::DVAR_FLAG_SAVED);
}
};
}

View File

@ -29,7 +29,7 @@ namespace lui
{
if (params.size() <= 1)
{
console::info("usage: lui_open <name>\n");
console::info("USAGE: %s <name>\n", params.get(0));
return;
}
@ -40,7 +40,7 @@ namespace lui
{
if (params.size() <= 1)
{
console::info("usage: lui_open_popup <name>\n");
console::info("USAGE: %s <name>\n", params.get(0));
return;
}

View File

@ -113,7 +113,7 @@ namespace map_rotation
console::error("%s: %s contains invalid data!\n", ex.what(), sv_map_rotation->name);
}
#ifdef _DEBUG
console::info("dedicated_rotation size after parsing is '%zu'", dedicated_rotation.get_entries_size());
console::info("dedicated_rotation size after parsing is '%zu'\n", dedicated_rotation.get_entries_size());
#endif
}
@ -289,11 +289,11 @@ namespace map_rotation
scheduler::once([]
{
sv_map_rotation = game::Dvar_RegisterString("sv_mapRotation", "", game::DVAR_FLAG_NONE, "");
sv_map_rotation_current = game::Dvar_RegisterString("sv_mapRotationCurrent", "", game::DVAR_FLAG_NONE, "");
sv_map_rotation = game::Dvar_RegisterString("sv_mapRotation", "", game::DVAR_FLAG_NONE);
sv_map_rotation_current = game::Dvar_RegisterString("sv_mapRotationCurrent", "", game::DVAR_FLAG_NONE);
}, scheduler::pipeline::main);
sv_random_map_rotation = game::Dvar_RegisterBool("sv_randomMapRotation", false, game::DVAR_FLAG_NONE, "Randomize map rotation");
sv_random_map_rotation = game::Dvar_RegisterBool("sv_randomMapRotation", false, game::DVAR_FLAG_NONE);
command::add("map_rotate", &perform_map_rotation);

View File

@ -179,7 +179,7 @@ namespace network
game::dvar_t* register_netport_stub(const char* dvarName, int value, int min, int max, unsigned int flags,
const char* description)
{
auto dvar = game::Dvar_RegisterInt("net_port", 27016, 0, 0xFFFFu, game::DVAR_FLAG_LATCHED, "Network port");
auto dvar = game::Dvar_RegisterInt("net_port", 27016, 0, 0xFFFFu, game::DVAR_FLAG_LATCHED);
// read net_port from command line
command::read_startup_variable("net_port");

View File

@ -28,8 +28,6 @@ namespace notifies
char empty_function[2] = {0x32, 0x34}; // CHECK_CLEAR_PARAMS, END
const char* target_function = nullptr;
void client_command_stub(const int client_num)
{
if (game::mp::g_entities[client_num].client == nullptr)
@ -137,7 +135,7 @@ namespace notifies
a.lea(eax, dword_ptr(r15, -0x17));
a.mov(dword_ptr(rbp, 0x68), r15d);
a.jmp(0x1403FA143);
a.jmp(SELECT_VALUE(0x14031D9E3, 0x1403FA143));
a.bind(replace);
@ -185,22 +183,22 @@ namespace notifies
public:
void post_unpack() override
{
utils::hook::jump(SELECT_VALUE(0x14031D9D4, 0x1403FA134), utils::hook::assemble(vm_execute_stub), true);
scripting::on_shutdown([](const int clear_scripts) -> void
{
if (clear_scripts)
{
vm_execute_hooks.clear();
}
});
if (game::environment::is_sp())
{
return;
}
utils::hook::call(0x14043A9AD, client_command_stub);
utils::hook::jump(0x1403FA134, utils::hook::assemble(vm_execute_stub), true);
scripting::on_shutdown([](bool free_scripts)
{
if (free_scripts)
{
vm_execute_hooks.clear();
}
});
}
};
}

View File

@ -1,6 +1,8 @@
#include <std_include.hpp>
#include "loader/component_loader.hpp"
#include "game/game.hpp"
#include "game/engine/sv_game.hpp"
#include "game/dvars.hpp"
#include "party.hpp"
#include "console.hpp"
@ -33,6 +35,29 @@ namespace party
int sv_maxclients;
utils::info_string get_info()
{
utils::info_string info;
info.set("gamename", "S1");
info.set("hostname", dvars::get_dvar_string("sv_hostname"));
info.set("gametype", dvars::get_dvar_string("g_gametype"));
info.set("sv_motd", dvars::get_dvar_string("sv_motd"));
info.set("xuid", utils::string::va("%llX", steam::SteamUser()->GetSteamID().bits));
info.set("mapname", dvars::get_dvar_string("mapname"));
info.set("isPrivate", dvars::get_dvar_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::Dvar_FindVar("sv_maxclients")->current.integer));
info.set("protocol", std::to_string(PROTOCOL));
info.set("shortversion", SHORTVERSION);
info.set("playmode", utils::string::va("%i", game::Com_GetCurrentCoDPlayMode()));
info.set("sv_running", std::to_string(dvars::get_dvar_bool("sv_running")));
info.set("dedicated", std::to_string(dvars::get_dvar_bool("dedicated")));
return info;
}
void perform_game_initialization()
{
command::execute("onlinegame 1", true);
@ -79,30 +104,7 @@ namespace party
// CL_ConnectFromParty
char session_info[0x100] = {};
reinterpret_cast<void(*)(int, char*, const game::netadr_s*, const char*, const char*)>(0x140209360)(
0, session_info, &target, mapname.data(), gametype.data());
}
std::string get_dvar_string(const std::string& dvar)
{
const auto* dvar_value = game::Dvar_FindVar(dvar.data());
if (dvar_value && dvar_value->current.string)
{
return {dvar_value->current.string};
}
return {};
}
bool get_dvar_bool(const std::string& dvar)
{
const auto* dvar_value = game::Dvar_FindVar(dvar.data());
if (dvar_value && dvar_value->current.enabled)
{
return dvar_value->current.enabled;
}
return false;
reinterpret_cast<void(*)(int, char*, const game::netadr_s*, const char*, const char*)>(0x140209360)(0, session_info, &target, mapname.data(), gametype.data());
}
void didyouknow_stub(const char* dvar_name, const char* string)
@ -158,7 +160,7 @@ namespace party
if (game::mp::g_entities[i].client)
{
char client_name[16] = {0};
strncpy_s(client_name, game::mp::g_entities[i].client->name, sizeof(client_name));
game::I_strncpyz(client_name, game::mp::g_entities[i].client->name, sizeof(client_name));
game::I_CleanStr(client_name);
if (client_name == name)
@ -225,64 +227,6 @@ namespace party
network::send(target, "getInfo", connect_state.challenge);
}
void start_map(const std::string& mapname)
{
if (game::Live_SyncOnlineDataFlags(0) > 32)
{
scheduler::once([=]()
{
command::execute("map " + mapname, false);
}, scheduler::pipeline::main, 1s);
}
else
{
if (!game::SV_MapExists(mapname.data()))
{
console::info("Map '%s' doesn't exist.\n", mapname.data());
return;
}
auto* current_mapname = game::Dvar_FindVar("mapname");
if (current_mapname && utils::string::to_lower(current_mapname->current.string) ==
utils::string::to_lower(mapname) && (game::SV_Loaded() && !game::VirtualLobby_Loaded()))
{
console::info("Restarting map: %s\n", mapname.data());
command::execute("map_restart", false);
return;
}
if (!game::environment::is_dedi())
{
if (game::SV_Loaded())
{
const auto* args = "Leave";
game::UI_RunMenuScript(0, &args);
}
perform_game_initialization();
}
console::info("Starting map: %s\n", mapname.data());
auto* gametype = game::Dvar_FindVar("g_gametype");
if (gametype && gametype->current.string)
{
command::execute(utils::string::va("ui_gametype %s", gametype->current.string), true);
}
command::execute(utils::string::va("ui_mapname %s", mapname.data()), true);
/*auto* maxclients = game::Dvar_FindVar("sv_maxclients");
if (maxclients)
{
command::execute(utils::string::va("ui_maxclients %i", maxclients->current.integer), true);
command::execute(utils::string::va("party_maxplayers %i", maxclients->current.integer), true);
}*/
const auto* args = "StartServer";
game::UI_RunMenuScript(0, &args);
}
}
int server_client_count()
{
return sv_maxclients;
@ -313,36 +257,6 @@ namespace party
// enable custom kick reason in GScr_KickPlayer
utils::hook::set<uint8_t>(0x14032ED80, 0xEB);
command::add("map", [](const command::params& argument)
{
if (argument.size() != 2)
{
return;
}
start_map(argument[1]);
});
command::add("map_restart", []()
{
if (!game::SV_Loaded() || game::VirtualLobby_Loaded())
{
return;
}
*reinterpret_cast<int*>(0x1488692B0) = 1; // sv_map_restart
*reinterpret_cast<int*>(0x1488692B4) = 1; // sv_loadScripts
*reinterpret_cast<int*>(0x1488692B8) = 0; // sv_migrate
reinterpret_cast<void(*)(int)>(0x140437460)(0); // SV_CheckLoadGame
});
command::add("fast_restart", []()
{
if (game::SV_Loaded() && !game::VirtualLobby_Loaded())
{
game::SV_FastRestart(0);
}
});
command::add("reconnect", [](const command::params& argument)
{
if (!connect_state.hostDefined)
@ -441,7 +355,7 @@ namespace party
{
scheduler::once([i, reason]
{
game::SV_KickClientNum(i, reason.data());
game::SV_KickClientNum(i, reason.c_str());
}, scheduler::pipeline::server);
}
return;
@ -455,14 +369,13 @@ namespace party
scheduler::once([client_num, reason]()
{
game::SV_KickClientNum(client_num, reason.data());
game::SV_KickClientNum(client_num, reason.c_str());
}, scheduler::pipeline::server);
});
scheduler::once([]()
{
game::Dvar_RegisterString("sv_sayName", "console", game::DvarFlags::DVAR_FLAG_NONE,
"The name to pose as for 'say' commands");
game::Dvar_RegisterString("sv_sayName", "console", game::DVAR_FLAG_NONE);
}, scheduler::pipeline::main);
command::add("tell", [](const command::params& params)
@ -476,9 +389,8 @@ namespace party
const auto message = params.join(2);
const auto* const name = game::Dvar_FindVar("sv_sayName")->current.string;
game::SV_GameSendServerCommand(client_num, game::SV_CMD_CAN_IGNORE,
utils::string::va("%c \"%s: %s\"", 84, name, message.data()));
printf("%s -> %i: %s\n", name, client_num, message.data());
game::engine::SV_GameSendServerCommand(static_cast<char>(client_num), game::SV_CMD_CAN_IGNORE, utils::string::va("%c \"%s: %s\"", 84, name, message.c_str()));
printf("%s -> %i: %s\n", name, client_num, message.c_str());
});
command::add("tellraw", [](const command::params& params)
@ -491,9 +403,8 @@ namespace party
const auto client_num = atoi(params.get(1));
const auto message = params.join(2);
game::SV_GameSendServerCommand(client_num, game::SV_CMD_CAN_IGNORE,
utils::string::va("%c \"%s\"", 84, message.data()));
printf("%i: %s\n", client_num, message.data());
game::engine::SV_GameSendServerCommand(static_cast<char>(client_num), game::SV_CMD_CAN_IGNORE, utils::string::va("%c \"%s\"", 84, message.c_str()));
printf("%i: %s\n", client_num, message.c_str());
});
command::add("say", [](const command::params& params)
@ -506,9 +417,8 @@ namespace party
const auto message = params.join(1);
const auto* const name = game::Dvar_FindVar("sv_sayName")->current.string;
game::SV_GameSendServerCommand(
-1, game::SV_CMD_CAN_IGNORE, utils::string::va("%c \"%s: %s\"", 84, name, message.data()));
printf("%s: %s\n", name, message.data());
game::engine::SV_GameSendServerCommand(-1, game::SV_CMD_CAN_IGNORE, utils::string::va("%c \"%s: %s\"", 84, name, message.c_str()));
printf("%s: %s\n", name, message.c_str());
});
command::add("sayraw", [](const command::params& params)
@ -520,8 +430,7 @@ namespace party
const auto message = params.join(1);
game::SV_GameSendServerCommand(-1, game::SV_CMD_CAN_IGNORE,
utils::string::va("%c \"%s\"", 84, message.data()));
game::engine::SV_GameSendServerCommand(-1, game::SV_CMD_CAN_IGNORE, utils::string::va("%c \"%s\"", 84, message.c_str()));
printf("%s\n", message.data());
});
@ -529,27 +438,55 @@ namespace party
network::on("getInfo", [](const game::netadr_s& target, const std::string& data)
{
utils::info_string info{};
utils::info_string info = get_info();
info.set("challenge", data);
info.set("gamename", "S1");
info.set("hostname", get_dvar_string("sv_hostname"));
info.set("gametype", get_dvar_string("g_gametype"));
info.set("sv_motd", get_dvar_string("sv_motd"));
info.set("xuid", utils::string::va("%llX", steam::SteamUser()->GetSteamID().bits));
info.set("mapname", get_dvar_string("mapname"));
info.set("isPrivate", get_dvar_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_numclients));
info.set("protocol", std::to_string(PROTOCOL));
info.set("playmode", utils::string::va("%i", game::Com_GetCurrentCoDPlayMode()));
info.set("sv_running", std::to_string(get_dvar_bool("sv_running")));
info.set("dedicated", std::to_string(get_dvar_bool("dedicated")));
info.set("shortversion", SHORTVERSION);
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 < 5)
{
continue;
}
if (!self || !self->client)
{
continue;
}
if (game::SV_BotIsBot(i))
{
continue;
}
const auto score = game::G_GetClientScore(i);
const auto ping = game::SV_GetClientPing(i);
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())
{
return;

View File

@ -5,7 +5,6 @@ namespace party
void reset_connect_state();
void connect(const game::netadr_s& target);
void start_map(const std::string& mapname);
void clear_sv_motd();
int server_client_count();

View File

@ -1,6 +1,7 @@
#include <std_include.hpp>
#include "loader/component_loader.hpp"
#include "game/game.hpp"
#include "game/engine/sv_game.hpp"
#include "game/dvars.hpp"
#include "command.hpp"
@ -19,6 +20,8 @@ namespace patches
{
namespace
{
utils::hook::detour com_quit_f_hook;
utils::hook::detour sv_shutdown_hook;
utils::hook::detour live_get_local_client_name_hook;
const char* live_get_local_client_name()
@ -58,28 +61,28 @@ namespace patches
if (game::environment::is_mp())
{
// Make name save
game::Dvar_RegisterString("name", get_login_username().data(), game::DVAR_FLAG_SAVED, "Player name.");
game::Dvar_RegisterString("name", get_login_username().data(), game::DVAR_FLAG_SAVED);
// Disable data validation error popup
game::Dvar_RegisterInt("data_validation_allow_drop", 0, 0, 0, game::DVAR_FLAG_NONE, "");
game::Dvar_RegisterInt("data_validation_allow_drop", 0, 0, 0, game::DVAR_FLAG_NONE);
}
return com_register_dvars_hook.invoke<void>();
}
game::dvar_t* register_com_maxfps_stub(const char* name, int /*value*/, int /*min*/, int /*max*/, const unsigned int /*flags*/, const char* description)
game::dvar_t* register_com_maxfps_stub(const char* name, int /*value*/, int /*min*/, int /*max*/, const unsigned int /*flags*/)
{
return game::Dvar_RegisterInt(name, 0, 0, 1000, game::DVAR_FLAG_SAVED, description);
return game::Dvar_RegisterInt(name, 0, 0, 1000, game::DVAR_FLAG_SAVED);
}
game::dvar_t* register_cg_fov_stub(const char* name, float value, float min, float /*max*/, const unsigned int /*flags*/, const char* description)
game::dvar_t* register_cg_fov_stub(const char* name, float value, float min, float /*max*/, const unsigned int /*flags*/)
{
return game::Dvar_RegisterFloat(name, value, min, 160, game::DVAR_FLAG_SAVED, description);
return game::Dvar_RegisterFloat(name, value, min, 160, game::DVAR_FLAG_SAVED);
}
game::dvar_t* register_fovscale_stub(const char* name, float /*value*/, float /*min*/, float /*max*/, unsigned int /*flags*/, const char* desc)
game::dvar_t* register_fovscale_stub(const char* name, float /*value*/, float /*min*/, float /*max*/, unsigned int /*flags*/)
{
return game::Dvar_RegisterFloat(name, 1.0f, 0.2f, 5.0f, game::DVAR_FLAG_SAVED, desc);
return game::Dvar_RegisterFloat(name, 1.0f, 0.2f, 5.0f, game::DVAR_FLAG_SAVED);
}
int dvar_command_patch() // game makes this return an int and compares with eax instead of al -_-
@ -115,7 +118,7 @@ namespace patches
char* db_read_raw_file_stub(const char* filename, char* buf, const int size)
{
std::string file_name = filename;
if (file_name.find(".cfg") == std::string::npos)
if (!file_name.ends_with(".cfg"))
{
file_name.append(".cfg");
}
@ -124,6 +127,7 @@ namespace patches
if (file.exists())
{
snprintf(buf, size, "%s\n", file.get_buffer().data());
buf[size - 1] = '\0';
return buf;
}
@ -178,13 +182,27 @@ namespace patches
const auto client = &game::mp::svs_clients[ent->s.number];
// 22 => "end_game"
if (menu_id == 22 && client->header.remoteAddress.type != game::NA_LOOPBACK)
if (menu_id == 22 && client->header.netchan.remoteAddress.type != game::NA_LOOPBACK)
{
return;
}
cmd_lui_notify_server_hook.invoke<void>(ent);
}
void sv_shutdown_stub(const char* finalmsg)
{
console::info("----- Server Shutdown -----\n");
sv_shutdown_hook.invoke<void>(finalmsg);
}
void com_quit_f_stub()
{
console::info("quitting...\n");
com_quit_f_hook.invoke<void>();
}
}
class component final : public component_interface
@ -239,6 +257,10 @@ namespace patches
static void patch_mp()
{
// Bypass Arxan function
utils::hook::nop(0x14043E120, 9);
utils::hook::jump(0x14043E120, game::engine::SV_GameSendServerCommand);
// Use name dvar
live_get_local_client_name_hook.create(0x1404D47F0, &live_get_local_client_name);
@ -252,7 +274,7 @@ namespace patches
utils::hook::call(0x14026E63B, bsp_sys_error_stub);
// client side aim assist dvar
dvars::aimassist_enabled = game::Dvar_RegisterBool("aimassist_enabled", true, game::DVAR_FLAG_SAVED, "Enables aim assist for controllers");
dvars::aimassist_enabled = game::Dvar_RegisterBool("aimassist_enabled", true, game::DVAR_FLAG_SAVED);
utils::hook::call(0x140003609, aim_assist_add_to_target_list);
// isProfanity
@ -304,10 +326,16 @@ namespace patches
dvars::override::register_int("cl_connectTimeout", 120, 120, 1800, game::DVAR_FLAG_NONE); // Seems unused
dvars::override::register_int("sv_connectTimeout", 120, 120, 1800, game::DVAR_FLAG_NONE); // 60 - 0 - 1800
game::Dvar_RegisterInt("scr_game_spectatetype", 1, 0, 99, game::DVAR_FLAG_REPLICATED, "");
game::Dvar_RegisterInt("scr_game_spectatetype", 1, 0, 99, game::DVAR_FLAG_REPLICATED);
// Disable Com_Error in NET_SendPacket
utils::hook::nop(0x1404D8543, 5);
// Prevent clients from ending the game as non host by sending 'end_game' lui notification
cmd_lui_notify_server_hook.create(0x1402E9390, cmd_lui_notify_server_stub);
com_quit_f_hook.create(0x1403D08C0, com_quit_f_stub);
sv_shutdown_hook.create(0x140440170, sv_shutdown_stub);
}
static void patch_sp()

View File

@ -31,7 +31,7 @@ namespace ranked
dvars::override::register_bool("xblive_privatematch", false, game::DVAR_FLAG_REPLICATED | game::DVAR_FLAG_WRITE);
// Some dvar used in gsc
game::Dvar_RegisterBool("force_ranking", true, game::DVAR_FLAG_WRITE, "Force ranking");
game::Dvar_RegisterBool("force_ranking", true, game::DVAR_FLAG_WRITE);
}
// Always run bots, even if xblive_privatematch is 0

View File

@ -80,23 +80,25 @@ namespace rcon
{
const auto client = &game::mp::svs_clients[i];
if (client->header.state == game::CA_DISCONNECTED)
{
continue;
}
char clean_name[32]{};
strncpy_s(clean_name, client->name, _TRUNCATE);
game::I_strncpyz(clean_name, client->name, sizeof(clean_name));
game::I_CleanStr(clean_name);
if (client->header.state > game::CA_DISCONNECTED)
{
buffer.append(utils::string::va("%3i %5i %3s %s %32s %16s %21s %5i\n",
i,
game::G_GetClientScore(i),
game::SV_BotIsBot(i) ? "Yes" : "No",
(client->header.state == 2) ? "CNCT" : (client->header.state == 1) ? "ZMBI" : utils::string::va("%4i", game::SV_GetClientPing(i)),
game::SV_GetGuid(i),
clean_name,
network::net_adr_to_string(client->header.remoteAddress),
client->header.remoteAddress.port)
);
}
buffer.append(utils::string::va("%3i %5i %3s %s %32s %16s %21s %5i\n",
i,
game::G_GetClientScore(i),
game::SV_BotIsBot(i) ? "Yes" : "No",
(client->header.state == 2) ? "CNCT" : (client->header.state == 1) ? "ZMBI" : utils::string::va("%4i", game::SV_GetClientPing(i)),
game::SV_GetGuid(i),
clean_name,
network::net_adr_to_string(client->header.netchan.remoteAddress),
client->header.netchan.remoteAddress.port)
);
}
return buffer;
@ -128,8 +130,7 @@ namespace rcon
scheduler::once([]()
{
game::Dvar_RegisterString("rcon_password", "", game::DvarFlags::DVAR_FLAG_NONE,
"The password for remote console");
game::Dvar_RegisterString("rcon_password", "", game::DVAR_FLAG_NONE);
}, scheduler::pipeline::main);
command::add("status", []()

View File

@ -62,7 +62,7 @@ namespace renderer
return;
}
dvars::r_fullbright = game::Dvar_RegisterInt("r_fullbright", 0, 0, 3, game::DVAR_FLAG_SAVED, "Toggles rendering without lighting");
dvars::r_fullbright = game::Dvar_RegisterInt("r_fullbright", 0, 0, 3, game::DVAR_FLAG_SAVED);
r_init_draw_method_hook.create(SELECT_VALUE(0x14046C150, 0x140588B00), &r_init_draw_method_stub);
r_update_front_end_dvar_options_hook.create(SELECT_VALUE(0x1404A5330, 0x1405C3AE0), &r_update_front_end_dvar_options_stub);

View File

@ -49,9 +49,9 @@ namespace scripting
}
}
void g_shutdown_game_stub(const int free_scripts)
void g_shutdown_game_stub(const int clear_scripts)
{
if (free_scripts)
if (clear_scripts)
{
script_function_table_sort.clear();
script_function_table.clear();
@ -61,10 +61,10 @@ namespace scripting
for (const auto& callback : shutdown_callbacks)
{
callback(free_scripts);
callback(clear_scripts);
}
return g_shutdown_game_hook.invoke<void>(free_scripts);
return g_shutdown_game_hook.invoke<void>(clear_scripts);
}
void process_script_stub(const char* filename)
@ -182,10 +182,6 @@ namespace scripting
scr_set_thread_position_hook.create(SELECT_VALUE(0x1403115E0, 0x1403EDB10), &scr_set_thread_position_stub);
process_script_hook.create(SELECT_VALUE(0x14031AB30, 0x1403F7300), &process_script_stub);
sl_get_canonical_string_hook.create(game::SL_GetCanonicalString, &sl_get_canonical_string_stub);
scheduler::loop([]
{
}, scheduler::pipeline::server);
}
};
}

View File

@ -10,6 +10,8 @@ namespace security
{
namespace
{
utils::hook::detour ui_replace_directive_hook;
void set_cached_playerdata_stub(const int localclient, const int index1, const int index2)
{
if (index1 >= 0 && index1 < 18 && index2 >= 0 && index2 < 42)
@ -53,6 +55,65 @@ namespace security
utils::hook::invoke<void>(0x14043AA90, 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);
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);
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);
return len;
}
}
class component final : public component_interface
@ -60,16 +121,25 @@ namespace security
public:
void post_unpack() override
{
// sprinf
utils::hook::call(SELECT_VALUE(0x1402203DF, 0x1402F0BAF), hud_elem_set_enum_string_stub);
if (game::environment::is_sp()) return;
// Patch vulnerability in PlayerCards_SetCachedPlayerData
utils::hook::call(0x1401BB909, set_cached_playerdata_stub);
// sprinf
utils::hook::call(0x140439075, sv_add_bot_stub);
utils::hook::call(0x14043932A, sv_add_test_client_stub);
// Patch entity overflow
utils::hook::jump(0x14044DE3A, assemble(remap_cached_entities_stub), true);
// It is possible to make the server hang if left unchecked
utils::hook::call(0x140443051, sv_execute_client_message_stub);
ui_replace_directive_hook.create(0x1404A9640, ui_replace_directive_stub);
}
};
}

View File

@ -2,6 +2,9 @@
#include "loader/component_loader.hpp"
#include "game/game.hpp"
#include "command.hpp"
#include "console.hpp"
#include <utils/hook.hpp>
#include <utils/string.hpp>
@ -33,6 +36,25 @@ namespace slowmotion
game::SV_SetConfigstring(10, utils::string::va("%i %i %g %g", *game::mp::gameTime, duration, start, end));
game::Com_SetSlowMotion(start, end, duration);
}
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
@ -40,6 +62,13 @@ namespace slowmotion
public:
void post_unpack() override
{
if (game::environment::is_sp())
{
return;
}
command::add("timescale", com_timescale_f);
if (!game::environment::is_dedi())
{
return;

View File

@ -75,9 +75,9 @@ namespace stats
else
{
// unlock all
cg_unlock_all_items = game::Dvar_RegisterBool("cg_unlockall_items", false, game::DVAR_FLAG_SAVED, "Unlock items that are level-locked by the player's stats.");
cg_unlock_all_loot = game::Dvar_RegisterBool("cg_unlockall_loot", false, game::DVAR_FLAG_SAVED, "Unlock supply drop loot.");
game::Dvar_RegisterBool("cg_unlockall_classes", false, game::DVAR_FLAG_SAVED, "Unlock extra class slots.");
cg_unlock_all_items = game::Dvar_RegisterBool("cg_unlockall_items", false, game::DVAR_FLAG_SAVED);
cg_unlock_all_loot = game::Dvar_RegisterBool("cg_unlockall_loot", false, game::DVAR_FLAG_SAVED);
game::Dvar_RegisterBool("cg_unlockall_classes", false, game::DVAR_FLAG_SAVED);
is_item_locked_hook1.create(0x1403BD790, is_item_locked_stub1); // LiveStorage_IsItemUnlockedFromTable_LocalClient
is_item_locked_hook2.create(0x1403BD290, is_item_locked_stub2); // LiveStorage_IsItemUnlockedFromTable

View File

@ -206,6 +206,27 @@ namespace ui_scripting
setup_functions();
lua["print"] = function(reinterpret_cast<game::hks::lua_function>(0x14007CB70));
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["luiglobals"] = lua;

View File

@ -82,8 +82,8 @@ namespace virtuallobby
return;
}
virtualLobby_fov = game::Dvar_RegisterFloat("virtualLobby_fov", 40.0f, 1.0f, 170.0f, game::DVAR_FLAG_SAVED, "Field of view for the virtual lobby");
virtualLobby_fovscale = game::Dvar_RegisterFloat("virtualLobby_fovScale", 1.0f, 0.0f, 2.0f, game::DVAR_FLAG_SAVED, "Field of view scaled for the virtual lobby");
virtualLobby_fov = game::Dvar_RegisterFloat("virtualLobby_fov", 40.0f, 1.0f, 170.0f, game::DVAR_FLAG_SAVED);
virtualLobby_fovscale = game::Dvar_RegisterFloat("virtualLobby_fovScale", 1.0f, 0.0f, 2.0f, game::DVAR_FLAG_SAVED);
utils::hook::nop(0x1401D5AFB, 16);
utils::hook::jump(0x1401D5AFB, get_fov_stub, true);

View File

@ -43,6 +43,28 @@ namespace dvars
game::dvar_t* com_developer = nullptr;
game::dvar_t* com_developer_script = nullptr;
std::string get_dvar_string(const std::string& dvar)
{
const auto* dvar_value = game::Dvar_FindVar(dvar.data());
if (dvar_value && dvar_value->current.string)
{
return { dvar_value->current.string };
}
return {};
}
bool get_dvar_bool(const std::string& dvar)
{
const auto* dvar_value = game::Dvar_FindVar(dvar.data());
if (dvar_value && dvar_value->current.enabled)
{
return dvar_value->current.enabled;
}
return false;
}
std::string dvar_get_vector_domain(const int components, const game::dvar_limits& domain)
{
if (domain.vector.min == -FLT_MAX)
@ -51,22 +73,16 @@ namespace dvars
{
return utils::string::va("Domain is any %iD vector", components);
}
else
{
return utils::string::va("Domain is any %iD vector with components %g or smaller", components,
domain.vector.max);
}
return utils::string::va("Domain is any %iD vector with components %g or smaller", components, domain.vector.max);
}
else if (domain.vector.max == FLT_MAX)
if (domain.vector.max == FLT_MAX)
{
return utils::string::va("Domain is any %iD vector with components %g or bigger", components,
domain.vector.min);
}
else
{
return utils::string::va("Domain is any %iD vector with components from %g to %g", components,
domain.vector.min, domain.vector.max);
return utils::string::va("Domain is any %iD vector with components %g or bigger", components, domain.vector.min);
}
return utils::string::va("Domain is any %iD vector with components from %g to %g", components, domain.vector.min, domain.vector.max);
}
std::string dvar_get_domain(const game::dvar_type type, const game::dvar_limits& domain)

View File

@ -42,6 +42,9 @@ namespace dvars
extern game::dvar_t* com_developer;
extern game::dvar_t* com_developer_script;
std::string get_dvar_string(const std::string& dvar);
bool get_dvar_bool(const std::string& dvar);
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);
}

View File

@ -0,0 +1,181 @@
#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 != mp::CS_FREE);
if (drop->header.state == mp::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 == mp::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 < mp::CS_CLIENTLOADING)
{
continue;
}
SV_AddServerCommand(client, type, server_command_buf_large.get());
}
}
void SV_GameSendServerCommand(char 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);
}
}

View File

@ -0,0 +1,7 @@
#pragma once
namespace game::engine
{
void SV_SendServerCommand(mp::client_t* cl, svscmd_type type, const char* fmt, ...);
void SV_GameSendServerCommand(char clientNum, svscmd_type type, const char* text);
}

View File

@ -60,6 +60,13 @@ namespace game
VAR_TOTAL_COUNT = 0x1B,
};
struct ScriptFunctions
{
int maxSize;
int count;
int* address;
};
struct VariableStackBuffer
{
const char* pos;
@ -214,7 +221,6 @@ namespace game
unsigned __int16 childVariableBucket[65536];
ChildVariableValue childVariableValue[384000];
};
// *
enum Sys_Folder
{
@ -849,10 +855,11 @@ namespace game
int readcount;
int bit;
int lastEntityRef;
netsrc_t targetLocalNetID;
int useZlib;
};
static_assert(sizeof(msg_t) == 0x38);
enum errorParm
{
ERR_FATAL = 0,
@ -1410,8 +1417,44 @@ namespace game
static_assert(sizeof(pml_t) == 0x130);
struct netProfilePacket_t
{
int iTime;
int iSize;
int bFragment;
};
struct netProfileStream_t
{
netProfilePacket_t packets[60];
int iCurrPacket;
int iBytesPerSecond;
int iLastBPSCalcTime;
int iCountedPackets;
int iCountedFragments;
int iFragmentPercentage;
int iLargestPacket;
int iSmallestPacket;
};
struct netProfileInfo_t
{
netProfileStream_t send;
netProfileStream_t recieve;
};
namespace mp
{
enum
{
CS_FREE = 0x0,
CS_ZOMBIE = 0x1,
CS_RECONNECTING = 0x2,
CS_CONNECTED = 0x3,
CS_CLIENTLOADING = 0x4,
CS_ACTIVE = 0x5,
};
struct cachedSnapshot_t
{
int archivedFrame;
@ -1464,30 +1507,144 @@ namespace game
{
};
struct netchan_t
{
int outgoingSequence;
netsrc_t sock;
int dropped;
int incomingSequence;
netadr_s remoteAddress;
int fragmentSequence;
int fragmentLength;
char* fragmentBuffer;
int fragmentBufferSize;
int unsentFragments;
int unsentFragmentStart;
int unsentLength;
char* unsentBuffer;
int unsentBufferSize;
netProfileInfo_t prof;
};
struct clientHeader_t
{
int state;
char __pad0[36];
netadr_s remoteAddress;
}; // size = ?
int sendAsActive;
int deltaMessage;
int rateDelayed;
int hasAckedBaselineData;
int hugeSnapshotSent;
netchan_t netchan;
float predictedOrigin[3];
int predictedOriginServerTime;
int migrationState;
unsigned int predictedVehicleSplineId;
int predictedVehicleTargetEntity;
float predictedVehicleOrigin[3];
int predictedVehicleServerTime;
int ackedMessage[32];
unsigned int ackedMessageCount;
int sentMessage[32];
int wasKillcam[32];
unsigned int sendMessageCount;
bool overrideDeltaMessage;
int overrideSequenceNumber;
int sequenceResume;
int isInKillcam;
};
struct svscmd_info_t
{
int time;
int type;
char cmd[1024];
};
static_assert(sizeof(svscmd_info_t) == 0x408);
struct client_net_buffers_t
{
svscmd_info_t reliableCommandInfo[128];
char netchanOutgoingBuffer[131072];
char netchanIncomingBuffer[2048];
};
struct usercmd_s
{
int serverTime;
unsigned int buttons;
int angles[3];
Weapon weapon;
Weapon offHand;
char forwardmove;
char rightmove;
unsigned __int16 airburstMarkDistance;
__int16 meleeChargeEnt;
unsigned __int8 meleeChargeDist;
char selectedLoc[2];
unsigned __int8 selectedLocAngle;
char remoteControlAngles[2];
char remoteControlMove[3];
unsigned int sightedClientsMask;
unsigned __int16 spawnTraceEntIndex;
unsigned int sightedSpawnsMask[2];
unsigned int partialSightedSpawnsMask[2];
};
static_assert(sizeof(usercmd_s) == 0x44);
struct client_t
{
clientHeader_t header;
char __pad0[3044];
const char* dropReason;
char userinfo[0x400];
int reliableSequence;
int reliableAcknowledge;
char __pad1[265864];
int reliableSent;
int messageAcknowledge;
int largeCommandSequence;
int gamestateMessageNum;
int challenge;
client_net_buffers_t netBuf;
int cumulThinkTime;
int beginCmdIndex;
int currCmdIndex;
usercmd_s lastUsercmd;
usercmd_s cmds[8];
int lastClientCommand;
gentity_s* gentity; // 268976
char name[32]; // 268984
char __pad2[8];
char name[32];
int lastPacketTime;
int lastConnectTime;
int nextSnapshotTime; // 269024
char __pad3[544];
LiveClientDropType liveDropRequest; //269572
char __pad3[532];
int pureAuthentic;
unsigned int streamSyncWaitBits;
unsigned int streamSyncWaitTimeout;
LiveClientDropType liveDropRequest; // 269572
char __pad4[24];
TestClientType testClient; // 269600
char __pad5[391700];
}; // size = 661304
static_assert(offsetof(client_t, header.netchan.unsentFragments) == 0x54);
static_assert(offsetof(client_t, header.migrationState) == 0x660);
static_assert(offsetof(client_t, userinfo) == 0x820);
static_assert(offsetof(client_t, reliableSequence) == 0xC20);
static_assert(offsetof(client_t, reliableAcknowledge) == 0xC24);
static_assert(offsetof(client_t, reliableSent) == 0xC28);
static_assert(offsetof(client_t, messageAcknowledge) == 0xC2C);
static_assert(offsetof(client_t, largeCommandSequence) == 0xC30);
static_assert(offsetof(client_t, lastUsercmd) == 0x41848);
static_assert(offsetof(client_t, lastClientCommand) == 0x41AAC);
static_assert(offsetof(client_t, gentity) == 0x41AB0);
static_assert(offsetof(client_t, name) == 0x41AB8);
static_assert(offsetof(client_t, lastPacketTime) == 0x41AD8);
static_assert(offsetof(client_t, nextSnapshotTime) == 0x41AE0);
static_assert(offsetof(client_t, pureAuthentic) == 0x41CF8);
static_assert(offsetof(client_t, liveDropRequest) == 0x41D04);
static_assert(offsetof(client_t, testClient) == 0x41D20);
static_assert(sizeof(client_t) == 0xA1738);
}
namespace sp
@ -1496,7 +1653,9 @@ namespace game
{
char __pad[56135];
int flags; // 56136
};
}; // Incomplete
static_assert(offsetof(gclient_s, flags) == 56136);
struct gentity_s
{

View File

@ -12,7 +12,7 @@ namespace game
WEAK symbol<void(unsigned int id)> AddRefToObject{0, 0x1403F1F10};
WEAK symbol<unsigned int(unsigned int id)> AllocThread{0, 0x1403F2270};
WEAK symbol<void(int type, VariableUnion u)> RemoveRefToValue{0x140317340, 0x1403F3A50};
WEAK symbol<void(unsigned int id)> RemoveRefToObject{0, 0x1403F3940};
WEAK symbol<void(unsigned int id)> RemoveRefToObject{0x140317230, 0x1403F3940};
WEAK symbol<void(void*, void*)> AimAssist_AddToTargetList{0, 0x140001730};
@ -23,12 +23,14 @@ namespace game
WEAK symbol<CodPlayMode()> Com_GetCurrentCoDPlayMode{0, 0x1404C9690};
WEAK symbol<void(float, float, int)> Com_SetSlowMotion{0, 0x1403D19B0};
WEAK symbol<void()> Com_Quit_f{0x1402F9390, 0x1403D08C0};
WEAK symbol<void(const char* finalmsg)> Com_Shutdown{0x0, 0x1403D1BF0};
WEAK symbol<void(const char* cmdName, void (), cmd_function_s* allocedCmd)> Cmd_AddCommandInternal{0x1402EDDB0, 0x1403AF2C0};
WEAK symbol<void(int localClientNum, int controllerIndex, const char* text)> Cmd_ExecuteSingleCommand{0x1402EE350, 0x1403AF900};
WEAK symbol<void(const char*)> Cmd_RemoveCommand{0x1402EE910, 0x1403AFEF0};
WEAK symbol<void(int index)> Cmd_RemoveCommand{0x1402EE910, 0x1403AFEF0};
WEAK symbol<void(const char* text_in)> Cmd_TokenizeString{0x1402EEA30, 0x1403B0020};
WEAK symbol<void()> Cmd_EndTokenizeString{0x1402EE000, 0x1403AF5B0};
WEAK symbol<int(char* dest, int size, const char* fmt, ...)> Com_sprintf{0x140378E30, 0x1404C97B0};
WEAK symbol<void(const char* message)> Conbuf_AppendText{0x14038F220, 0x1404D9040};
@ -70,22 +72,14 @@ namespace game
WEAK symbol<void(const char*, const char*, DvarSetSource)> Dvar_SetFromStringByNameFromSource{0x1403737D0, 0x1404C2E40};
WEAK symbol<const char*(dvar_t* dvar, dvar_value value)> Dvar_ValueToString{0x140374E10, 0x1404C47B0};
WEAK symbol<dvar_t*(const char* dvarName, bool value, unsigned int flags, const char* description)>
Dvar_RegisterBool{0x140371850, 0x1404C0BE0};
WEAK symbol<dvar_t*(const char* dvarName, const char** valueList, int defaultIndex, unsigned int flags,
const char* description)> Dvar_RegisterEnum{0x140371B30, 0x1404C0EC0};
WEAK symbol<dvar_t*(const char* dvarName, float value, float min, float max, unsigned int flags,
const char* description)> Dvar_RegisterFloat{0x140371C20, 0x1404C0FB0};
WEAK symbol<dvar_t*(const char* dvarName, int value, int min, int max, unsigned int flags, const char* desc)>
Dvar_RegisterInt{0x140371CF0, 0x1404C1080};
WEAK symbol<dvar_t*(const char* dvarName, const char* value, unsigned int flags, const char* description)>
Dvar_RegisterString{0x140372050, 0x1404C1450};
WEAK symbol<dvar_t* (const char* dvarName, float x, float y, float min, float max,
unsigned int flags, const char* description)> Dvar_RegisterVec2{0x140372120, 0x1404C1520};
WEAK symbol<dvar_t* (const char* dvarName, float x, float y, float z, float min, float max,
unsigned int flags, const char* description)> Dvar_RegisterVec3{0x140372230, 0x1404C1600};
WEAK symbol<dvar_t*(const char* dvarName, float x, float y, float z, float w, float min, float max,
unsigned int flags, const char* description)> Dvar_RegisterVec4{0x140372430, 0x1404C1800};
WEAK symbol<dvar_t*(const char* dvarName, bool value, unsigned int flags)> Dvar_RegisterBool{0x140371850, 0x1404C0BE0};
WEAK symbol<dvar_t*(const char* dvarName, const char** valueList, int defaultIndex, unsigned int flags)> Dvar_RegisterEnum{0x140371B30, 0x1404C0EC0};
WEAK symbol<dvar_t*(const char* dvarName, float value, float min, float max, unsigned int flags)> Dvar_RegisterFloat{0x140371C20, 0x1404C0FB0};
WEAK symbol<dvar_t*(const char* dvarName, int value, int min, int max, unsigned int flags)> Dvar_RegisterInt{0x140371CF0, 0x1404C1080};
WEAK symbol<dvar_t*(const char* dvarName, const char* value, unsigned int flags)> Dvar_RegisterString{0x140372050, 0x1404C1450};
WEAK symbol<dvar_t*(const char* dvarName, float x, float y, float min, float max, unsigned int flags)> Dvar_RegisterVec2{0x140372120, 0x1404C1520};
WEAK symbol<dvar_t*(const char* dvarName, float x, float y, float z, float min, float max, unsigned int flags)> Dvar_RegisterVec3{0x140372230, 0x1404C1600};
WEAK symbol<dvar_t*(const char* dvarName, float x, float y, float z, float w, float min, float max, unsigned int flags)> Dvar_RegisterVec4{0x140372430, 0x1404C1800};
WEAK symbol<DWOnlineStatus()> dwGetLogOnStatus{0, 0x14053CCB0};
@ -96,7 +90,7 @@ namespace game
WEAK symbol<void (const char *path, const char *dir)> FS_AddLocalizedGameDirectory{0x14035F5C0, 0x1404AD170};
WEAK symbol<void()> GScr_LoadConsts{0x140283970, 0x1403479C0};
WEAK symbol<unsigned int(unsigned int parentId, unsigned int name)> FindVariable{0x1403165D0, 0x1403F2DC0};
WEAK symbol<unsigned int(unsigned int parentId, unsigned int name)> FindVariable{0x1403166D0, 0x1403F2DC0};
WEAK symbol<unsigned int(int entnum, unsigned int classnum)> FindEntityId{0x1403166D0, 0x1403F2CC0};
WEAK symbol<scr_string_t(unsigned int parentId, unsigned int id)> GetVariableName{0x1403170E0, 0x1403F37F0};
WEAK symbol<void(VariableValue* result, unsigned int classnum, int entnum, int offset)> GetEntityFieldValue{0x14031AAD0, 0x1403F72A0};
@ -152,21 +146,23 @@ namespace game
WEAK symbol<void(int value)> Scr_AddInt{0x0, 0x1403F7B30};
WEAK symbol<int(unsigned int index)> Scr_GetInt{0x14031C1F0, 0x1403F88D0};
WEAK symbol<float(unsigned int index)> Scr_GetFloat{0x14031C090, 0x1403F8820};
WEAK symbol<void(unsigned int index, float* vectorValue)> Scr_GetVector{0x14031C7E0, 0x1403F8EC0};
WEAK symbol<unsigned int()> Scr_GetNumParam{0x14031C2A0, 0x1403F8980};
WEAK symbol<void()> Scr_ClearOutParams{0x14031B7C0, 0x1403F8040};
WEAK symbol<scr_entref_t(unsigned int entId)> Scr_GetEntityIdRef{0x14031A0D0, 0x1403F68A0};
WEAK symbol<int(unsigned int classnum, int entnum, int offset)> Scr_SetObjectField{0x14026B620, 0x140339450};
WEAK symbol<void(void* ent, unsigned int stringValue, unsigned int paramcount)> Scr_Notify{0x0, 0x140339120};
WEAK symbol<void(unsigned int id, unsigned int stringValue, unsigned int paramcount)> Scr_NotifyId{0x14031CB80, 0x1403F92D0};
WEAK symbol<bool(VariableValue* value)> Scr_CastString{0x0, 0x1403F4500};
WEAK symbol<bool(VariableValue* value)> Scr_CastString{0x140317EA0, 0x1403F4500};
WEAK symbol<unsigned __int16(int handle, unsigned int paramcount)> Scr_ExecThread{0x0, 0x1403F8120};
WEAK symbol<unsigned int(const char* name)> Scr_LoadScript{0x0, 0x1403EE250};
WEAK symbol<unsigned int(const char* script, unsigned int name)> Scr_GetFunctionHandle{0x0, 0x1403EE0D0};
WEAK symbol<unsigned __int16(int handle, unsigned int paramcount)> Scr_ExecThread{0x14031B8C0, 0x1403F8120};
WEAK symbol<unsigned int(const char* name)> Scr_LoadScript{0x140311CD0, 0x1403EE250};
WEAK symbol<unsigned int(const char* filename, unsigned int name)> Scr_GetFunctionHandle{0x140311B50, 0x1403EE0D0};
WEAK symbol<unsigned int(void* func, int type, unsigned int name)> Scr_RegisterFunction{0x1403115B0, 0x1403EDAE0};
WEAK symbol<unsigned int(const char** pName, int* type)> Scr_GetFunction{0x140263E90, 0x1403318B0};
WEAK symbol<unsigned int(unsigned int localId, const char* pos, unsigned int paramcount)> VM_Execute{0x0, 0x1403F9E40};
WEAK symbol<void()> Scr_ErrorInternal{0x0, 0x1403F80A0};
WEAK symbol<void()> Scr_ErrorInternal{0x14031B840, 0x1403F80A0};
WEAK symbol<const char*(scr_string_t stringValue)> SL_ConvertToString{0x140314850, 0x1403F0F10};
WEAK symbol<scr_string_t(const char* str)> SL_FindString{0x140314AF0, 0x1403F11C0};
@ -190,7 +186,7 @@ namespace game
WEAK symbol<void(mp::client_t* client)> SV_DropClient{0, 0x140438A30};
WEAK symbol<void(mp::client_t*, const char*, int)> SV_ExecuteClientCommand{0, 0x15121D8E6};
WEAK symbol<void(int localClientNum)> SV_FastRestart{0, 0x1404374E0};
WEAK symbol<void(int clientNum, svscmd_type type, const char* text)> SV_GameSendServerCommand{0x1403F3A70, 0x14043E120};
WEAK symbol<int(mp::client_t* client, const unsigned char* cmd, int cmdSize)> SV_CanReplaceServerCommand{0x0, 0x140441A00};
WEAK symbol<const char*(int clientNum)> SV_GetGuid{0, 0x14043E1E0};
WEAK symbol<int(int clientNum)> SV_GetClientPing{0, 0x14043E1C0};
WEAK symbol<playerState_s*(int num)> SV_GetPlayerstateForClientNum{0x1403F3AB0, 0x14043E260};
@ -202,8 +198,7 @@ namespace game
WEAK symbol<void(int index, const char* string)> SV_SetConfigstring{0, 0x14043FCA0};
WEAK symbol<void(char* path, int pathSize, Sys_Folder folder, const char* filename, const char* ext)>
Sys_BuildAbsPath{0x14037BBE0, 0x1404CC7E0};
WEAK symbol<void(char* path, int pathSize, Sys_Folder folder, const char* filename, const char* ext)> Sys_BuildAbsPath{0x14037BBE0, 0x1404CC7E0};
WEAK symbol<HANDLE(int folder, const char* baseFileName)> Sys_CreateFile{0x14037BCA0, 0x1404CC8A0};
WEAK symbol<void(const char* error, ...)> Sys_Error{0x14038C770, 0x1404D6260};
WEAK symbol<bool(const char* path)> Sys_FileExists{0x14038C810, 0x1404D6310};
@ -224,6 +219,13 @@ namespace game
WEAK symbol<void*(unsigned int size, unsigned int alignment, unsigned int type, int source)> PMem_AllocFromSource_NoDebug{0x1403775F0, 0x1404C7BA0};
WEAK symbol<void*(unsigned int size)> Hunk_AllocateTempMemoryHighInternal{0x140369D60, 0x1404B68B0};
WEAK symbol<void(unsigned __int64 markPos)> LargeLocalResetToMark{0x140369C40, 0x1404B6790};
WEAK symbol<void(char* dest, const char* src, int destsize)> I_strncpyz{0x1403793B0, 0x1404C9E60};
WEAK symbol<void(char* dest, int size, const char* src)> I_strncat{0x1403792E0, 0x1404C9D90};
WEAK symbol<void(const char* pszCommand, char* pszBuffer, int iBufferSize)> MSG_WriteReliableCommandToBuffer{0x0, 0x1403E1090};
WEAK symbol<void*(jmp_buf* Buf, int Value)> longjmp{0x14059C5C0, 0x1406FD930};
WEAK symbol<int(jmp_buf* Buf)> _setjmp{0x14059CD00, 0x1406FE070};
@ -246,6 +248,8 @@ namespace game
WEAK symbol<jmp_buf> g_script_error{0x14A1917B0, 0x1487FA0C0};
WEAK symbol<scr_classStruct_t> g_classMap{0x14080A840, 0x1409BE1B0};
WEAK symbol<float> com_codeTimeScale{0x0, 0x147B754EC};
WEAK symbol<scrVarGlob_t> scr_VarGlob{0x149B1D680, 0x148185F80};
WEAK symbol<scrVmPub_t> scr_VmPub{0x14A1938C0, 0x1487FC1C0};
WEAK symbol<function_stack_t> scr_function_stack{0x14A19DE40, 0x148806740};

View File

@ -64,9 +64,7 @@ LONG WINAPI exception_handler(PEXCEPTION_POINTERS exception_info)
&exception_information, nullptr, nullptr))
{
char buf[4096]{};
sprintf_s(buf, "An exception 0x%08X occurred at location 0x%p\n",
exception_info->ExceptionRecord->ExceptionCode,
exception_info->ExceptionRecord->ExceptionAddress);
sprintf_s(buf, "An exception 0x%08X occurred at location 0x%p\n", exception_info->ExceptionRecord->ExceptionCode, exception_info->ExceptionRecord->ExceptionAddress);
game::show_error(buf);
}

View File

@ -54,6 +54,7 @@
#include <functional>
#include <iostream>
#include <map>
#include <memory>
#include <mutex>
#include <optional>
#include <queue>

View File

@ -0,0 +1,25 @@
#include "properties.hpp"
#include <gsl/gsl>
#include <ShlObj.h>
namespace utils::properties
{
std::filesystem::path get_appdata_path()
{
PWSTR path;
if (!SUCCEEDED(SHGetKnownFolderPath(FOLDERID_LocalAppData, 0, nullptr, &path)))
{
throw std::runtime_error("Failed to read APPDATA path!");
}
auto _ = gsl::finally([&path]
{
CoTaskMemFree(path);
});
static auto appdata = std::filesystem::path(path) / "alterware";
return appdata;
}
}

View File

@ -0,0 +1,7 @@
#pragma once
#include <filesystem>
namespace utils::properties
{
std::filesystem::path get_appdata_path();
}

View File

@ -109,13 +109,13 @@ namespace utils::string
void strip(const char* in, char* out, size_t max)
{
if (!in || !out) return;
if (!in || !out || !max) return;
max--;
auto current = 0u;
while (*in != 0 && current < max)
{
const auto color_index = (*(in + 1) - 48) >= 0xC ? 7 : (*(in + 1) - 48);
const auto color_index = (static_cast<size_t>(*(in + 1) - 48)) >= 0xC ? 7 : (*(in + 1) - 48);
if (*in == '^' && (color_index != 7 || *(in + 1) == '7'))
{