Merge pull request #162 from diamante0018/refactor-scripts-stuff

Refactor script related funcs/modules
This commit is contained in:
Jan 2022-04-09 19:48:42 +02:00 committed by GitHub
commit 7afd9be6ce
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
21 changed files with 847 additions and 707 deletions

View File

@ -104,8 +104,7 @@ namespace Components
Loader::Register(new Movement()); Loader::Register(new Movement());
Loader::Register(new Elevators()); Loader::Register(new Elevators());
Loader::Register(new ClientCommand()); Loader::Register(new ClientCommand());
Loader::Register(new ScriptExtension());
Loader::Register(new Client());
Loader::Pregame = false; Loader::Pregame = false;
} }

View File

@ -136,4 +136,4 @@ namespace Components
#include "Modules/ClientCommand.hpp" #include "Modules/ClientCommand.hpp"
#include "Modules/Gamepad.hpp" #include "Modules/Gamepad.hpp"
#include "Modules/Client.hpp" #include "Modules/ScriptExtension.hpp"

View File

@ -520,7 +520,7 @@ namespace Components
{ {
for (auto& asset : AssetHandler::EmptyAssets) for (auto& asset : AssetHandler::EmptyAssets)
{ {
Game::Sys_Error(25, reinterpret_cast<char*>(0x724428), Game::DB_GetXAssetTypeName(asset.first), asset.second.data()); Game::Com_PrintWarning(25, reinterpret_cast<const char*>(0x724428), Game::DB_GetXAssetTypeName(asset.first), asset.second.data());
} }
AssetHandler::EmptyAssets.clear(); AssetHandler::EmptyAssets.clear();

View File

@ -1,104 +1,61 @@
#include <STDInclude.hpp> #include <STDInclude.hpp>
#define KEY_MASK_FIRE 1
#define KEY_MASK_SPRINT 2
#define KEY_MASK_MELEE 4
#define KEY_MASK_RELOAD 16
#define KEY_MASK_LEANLEFT 64
#define KEY_MASK_LEANRIGHT 128
#define KEY_MASK_PRONE 256
#define KEY_MASK_CROUCH 512
#define KEY_MASK_JUMP 1024
#define KEY_MASK_ADS_MODE 2048
#define KEY_MASK_TEMP_ACTION 4096
#define KEY_MASK_HOLDBREATH 8192
#define KEY_MASK_FRAG 16384
#define KEY_MASK_SMOKE 32768
#define KEY_MASK_NIGHTVISION 262144
#define KEY_MASK_ADS 524288
#define KEY_MASK_USE 0x28
#define MAX_G_BOTAI_ENTRIES 18
namespace Components namespace Components
{ {
std::vector<std::string> Bots::BotNames; std::vector<std::string> Bots::BotNames;
typedef struct BotMovementInfo_t struct BotMovementInfo
{ {
/* Actions */ int buttons; // Actions
int buttons; int8_t forward;
/* Movement */ int8_t right;
int8 forward; uint16_t weapon;
int8 right; bool active;
/* Weapon */ };
unsigned short weapon;
} BotMovementInfo_t;
static BotMovementInfo_t g_botai[MAX_G_BOTAI_ENTRIES]; static BotMovementInfo g_botai[18];
struct BotAction_t struct BotAction
{ {
const char* action; std::string action;
int key; int key;
}; };
static const BotAction_t BotActions[] = static const BotAction BotActions[] =
{ {
{ "gostand", KEY_MASK_JUMP }, { "gostand", Game::usercmdButtonBits::CMD_BUTTON_UP },
{ "gocrouch", KEY_MASK_CROUCH }, { "gocrouch", Game::usercmdButtonBits::CMD_BUTTON_CROUCH },
{ "goprone", KEY_MASK_PRONE }, { "goprone", Game::usercmdButtonBits::CMD_BUTTON_PRONE },
{ "fire", KEY_MASK_FIRE }, { "fire", Game::usercmdButtonBits::CMD_BUTTON_ATTACK },
{ "melee", KEY_MASK_MELEE }, { "melee", Game::usercmdButtonBits::CMD_BUTTON_MELEE },
{ "frag", KEY_MASK_FRAG }, { "frag", Game::usercmdButtonBits::CMD_BUTTON_FRAG },
{ "smoke", KEY_MASK_SMOKE }, { "smoke", Game::usercmdButtonBits::CMD_BUTTON_OFFHAND_SECONDARY },
{ "reload", KEY_MASK_RELOAD }, { "reload", Game::usercmdButtonBits::CMD_BUTTON_RELOAD },
{ "sprint", KEY_MASK_SPRINT }, { "sprint", Game::usercmdButtonBits::CMD_BUTTON_SPRINT },
{ "leanleft", KEY_MASK_LEANLEFT }, { "leanleft", Game::usercmdButtonBits::CMD_BUTTON_LEAN_LEFT },
{ "leanright", KEY_MASK_LEANRIGHT }, { "leanright", Game::usercmdButtonBits::CMD_BUTTON_LEAN_RIGHT },
{ "ads", KEY_MASK_ADS_MODE }, { "ads", Game::usercmdButtonBits::CMD_BUTTON_ADS },
{ "holdbreath", KEY_MASK_HOLDBREATH }, { "holdbreath", Game::usercmdButtonBits::CMD_BUTTON_BREATH },
{ "use", KEY_MASK_USE }, { "usereload", Game::usercmdButtonBits::CMD_BUTTON_USE_RELOAD },
{ "0", 8 }, { "activate", Game::usercmdButtonBits::CMD_BUTTON_ACTIVATE },
{ "1", 32 },
{ "2", 65536 },
{ "3", 131072 },
{ "4", 1048576 },
{ "5", 2097152 },
{ "6", 4194304 },
{ "7", 8388608 },
{ "8", 16777216 },
{ "9", 33554432 },
}; };
unsigned int Bots::GetClientNum(Game::client_s* cl) int Bots::BuildConnectString(char* buffer, const char* connectString, int num, int, int protocol, int checksum, int statVer, int statStuff, int port)
{ {
unsigned int num; static size_t botId = 0;
static bool loadedNames = false; // Load file only once
num = ((byte*)cl - (byte*)Game::svs_clients) / sizeof(Game::client_s);
return num;
}
bool Bots::IsValidClientNum(unsigned int cNum)
{
return (cNum >= 0) && (cNum < (unsigned int)*Game::svs_numclients);
}
void Bots::BuildConnectString(char* buffer, const char* connectString, int num, int, int protocol, int checksum, int statVer, int statStuff, int port)
{
static int botId = 0;
const char* botName; const char* botName;
if (Bots::BotNames.empty()) if (Bots::BotNames.empty() && !loadedNames)
{ {
FileSystem::File bots("bots.txt"); FileSystem::File bots("bots.txt");
loadedNames = true;
if (bots.exists()) if (bots.exists())
{ {
std::vector<std::string> names = Utils::String::Split(bots.getBuffer(), '\n'); auto names = Utils::String::Split(bots.getBuffer(), '\n');
for (auto name : names) for (auto& name : names)
{ {
Utils::String::Replace(name, "\r", ""); Utils::String::Replace(name, "\r", "");
name = Utils::String::Trim(name); name = Utils::String::Trim(name);
@ -121,276 +78,241 @@ namespace Components
botName = Utils::String::VA("bot%d", ++botId); botName = Utils::String::VA("bot%d", ++botId);
} }
strncpy_s(buffer, 0x400, Utils::String::VA(connectString, num, botName, protocol, checksum, statVer, statStuff, port), 0x400); return _snprintf_s(buffer, 0x400, _TRUNCATE, connectString, num, botName, protocol, checksum, statVer, statStuff, port);
} }
void Bots::Spawn(unsigned int count) void Bots::Spawn(unsigned int count)
{ {
for (unsigned int i = 0; i < count; ++i) for (auto i = 0u; i < count; ++i)
{ {
Scheduler::OnDelay([]() Scheduler::OnDelay([]()
{ {
for (int i = 0; i < 3; ++i) auto* ent = Game::SV_AddTestClient();
if (ent == nullptr)
return;
Scheduler::OnDelay([ent]()
{ {
Game::gentity_t* entRef = Game::SV_AddTestClient(); Game::Scr_AddString("autoassign");
if (entRef) Game::Scr_AddString("team_marinesopfor");
Game::Scr_Notify(ent, Game::SL_GetString("menuresponse", 0), 2);
Scheduler::OnDelay([ent]()
{ {
Scheduler::OnDelay([entRef]() Game::Scr_AddString(Utils::String::VA("class%u", Utils::Cryptography::Rand::GenerateInt() % 5u));
{ Game::Scr_AddString("changeclass");
Game::Scr_AddString("autoassign"); Game::Scr_Notify(ent, Game::SL_GetString("menuresponse", 0), 2);
Game::Scr_AddString("team_marinesopfor"); }, 1s);
Game::Scr_Notify(entRef, Game::SL_GetString("menuresponse", 0), 2); }, 1s);
Scheduler::OnDelay([entRef]()
{
Game::Scr_AddString(Utils::String::VA("class%d", Utils::Cryptography::Rand::GenerateInt() % 5));
Game::Scr_AddString("changeclass");
Game::Scr_Notify(entRef, Game::SL_GetString("menuresponse", 0), 2);
}, 1s);
}, 1s);
break;
}
}
}, 500ms * (i + 1)); }, 500ms * (i + 1));
} }
} }
void Bots::AddMethods() void Bots::AddMethods()
{ {
Script::AddFunction("SetPing", [](Game::scr_entref_t id) // gsc: self SetPing(<int>) Script::AddFunction("SetPing", [](Game::scr_entref_t entref) // gsc: self SetPing(<int>)
{ {
if (Game::Scr_GetNumParam() != 1u || Game::Scr_GetType(0) != Game::VAR_INTEGER)
{
Game::Scr_Error("^1SetPing: Needs one integer parameter!\n");
return;
}
auto ping = Game::Scr_GetInt(0); auto ping = Game::Scr_GetInt(0);
if (ping < 0 || ping > 999) ping = std::clamp(ping, 0, 999);
{
Game::Scr_Error("^1SetPing: Ping needs to between 0 and 999!\n");
return;
}
Game::gentity_t* gentity = Script::getEntFromEntRef(id); const auto* ent = Game::GetPlayerEntity(entref);
Game::client_t* client = Script::getClientFromEnt(gentity); auto* client = Script::GetClient(ent);
unsigned int clientNum = GetClientNum(client);
if (!Bots::IsValidClientNum(clientNum)) if (!client->bIsTestClient)
{
Game::Scr_Error("^1SetPing: Need to call on a player entity!\n");
return;
}
if (client->state < 3)
{
Game::Scr_Error("^1SetPing: Need to call on a connected player!\n");
return;
}
if (!client->isBot)
{ {
Game::Scr_Error("^1SetPing: Can only call on a bot!\n"); Game::Scr_Error("^1SetPing: Can only call on a bot!\n");
return; return;
} }
client->ping = (short)ping; client->ping = static_cast<int16_t>(ping);
}); });
Script::AddFunction("isBot", [](Game::scr_entref_t id) // Usage: <bot> isBot(); Script::AddFunction("IsTestClient", [](Game::scr_entref_t entref) // Usage: <bot> IsTestClient();
{ {
Game::gentity_t* gentity = Script::getEntFromEntRef(id); const auto* gentity = Game::GetPlayerEntity(entref);
Game::client_t* client = Script::getClientFromEnt(gentity); const auto* client = Script::GetClient(gentity);
unsigned int clientNum = GetClientNum(client);
if (!Bots::IsValidClientNum(clientNum)) Game::Scr_AddBool(client->bIsTestClient == 1);
{
Game::Scr_Error("^1isBot: Need to call on a player entity!\n");
return;
}
if (client->state < 3)
{
Game::Scr_Error("^1isBot: Needs to be connected.\n");
return;
}
Game::Scr_AddInt(client->isBot);
}); });
Script::AddFunction("botStop", [](Game::scr_entref_t id) // Usage: <bot> botStop(); Script::AddFunction("BotStop", [](Game::scr_entref_t entref) // Usage: <bot> BotStop();
{ {
Game::gentity_t* gentity = Script::getEntFromEntRef(id); const auto* ent = Game::GetPlayerEntity(entref);
Game::client_t* client = Script::getClientFromEnt(gentity); const auto* client = Script::GetClient(ent);
unsigned int clientNum = GetClientNum(client);
if (!Bots::IsValidClientNum(clientNum)) if (!client->bIsTestClient)
{ {
Game::Scr_Error("^1botStop: Need to call on a player entity!\n"); Game::Scr_Error("^1BotStop: Can only call on a bot!\n");
return; return;
} }
if (client->state < 3) g_botai[entref.entnum] = {0};
{ g_botai[entref.entnum].weapon = 1;
Game::Scr_Error("^1botStop: Needs to be connected.\n"); g_botai[entref.entnum].active = false;
return;
}
if (!client->isBot)
{
Game::Scr_Error("^1botStop: Can only call on a bot!\n");
return;
}
g_botai[clientNum] = { 0 };
g_botai[clientNum].weapon = 1;
}); });
Script::AddFunction("botWeapon", [](Game::scr_entref_t id) // Usage: <bot> botWeapon(<str>); Script::AddFunction("BotWeapon", [](Game::scr_entref_t entref) // Usage: <bot> BotWeapon(<str>);
{ {
if (Game::Scr_GetNumParam() != 1u || Game::Scr_GetType(0) != Game::VAR_STRING) const auto* weapon = Game::Scr_GetString(0);
const auto* ent = Game::GetPlayerEntity(entref);
const auto* client = Script::GetClient(ent);
if (!client->bIsTestClient)
{ {
Game::Scr_Error("^1botWeapon: Needs one string parameter!\n"); Game::Scr_Error("^1BotWeapon: Can only call on a bot!\n");
return; return;
} }
auto weapon = Game::Scr_GetString(0); if (weapon == nullptr || weapon[0] == '\0')
Game::gentity_t* gentity = Script::getEntFromEntRef(id);
Game::client_t* client = Script::getClientFromEnt(gentity);
unsigned int clientNum = GetClientNum(client);
if (!Bots::IsValidClientNum(clientNum))
{ {
Game::Scr_Error("^1botWeapon: Need to call on a player entity!\n"); g_botai[entref.entnum].weapon = 1;
return; return;
} }
if (client->state < 3) const auto weapId = Game::G_GetWeaponIndexForName(weapon);
{ g_botai[entref.entnum].weapon = static_cast<uint16_t>(weapId);
Game::Scr_Error("^1botWeapon: Needs to be connected.\n"); g_botai[entref.entnum].active = true;
return;
}
if (!client->isBot)
{
Game::Scr_Error("^1botWeapon: Can only call on a bot!\n");
return;
}
if (weapon == ""s)
{
g_botai[clientNum].weapon = 1;
return;
}
int weapId = Game::G_GetWeaponIndexForName(weapon);
g_botai[clientNum].weapon = (unsigned short)weapId;
}); });
Script::AddFunction("botAction", [](Game::scr_entref_t id) // Usage: <bot> botAction(<str action>); Script::AddFunction("BotAction", [](Game::scr_entref_t entref) // Usage: <bot> BotAction(<str action>);
{ {
if (Game::Scr_GetNumParam() != 1u || Game::Scr_GetType(0) != Game::VAR_STRING) const auto* action = Game::Scr_GetString(0);
if (action == nullptr)
{ {
Game::Scr_Error("^1botAction: Needs one string parameter!\n"); Game::Scr_ParamError(0, "^1BotAction: Illegal parameter!\n");
return; return;
} }
auto action = Game::Scr_GetString(0); const auto* ent = Game::GetPlayerEntity(entref);
const auto* client = Script::GetClient(ent);
Game::gentity_t* gentity = Script::getEntFromEntRef(id); if (!client->bIsTestClient)
Game::client_t* client = Script::getClientFromEnt(gentity);
unsigned int clientNum = GetClientNum(client);
if (!Bots::IsValidClientNum(clientNum))
{ {
Game::Scr_Error("^1botAction: Need to call on a player entity!\n"); Game::Scr_Error("^1BotAction: Can only call on a bot!\n");
return; return;
} }
if (client->state < 3)
{
Game::Scr_Error("^1botAction: Needs to be connected.\n");
return;
}
if (!client->isBot)
{
Game::Scr_Error("^1botAction: Can only call on a bot!\n");
return;
}
if (action[0] != '+' && action[0] != '-') if (action[0] != '+' && action[0] != '-')
{ {
Game::Scr_Error("^1botAction: Sign for action must be '+' or '-'.\n"); Game::Scr_ParamError(0, "^1BotAction: Sign for action must be '+' or '-'.\n");
return; return;
} }
for (size_t i = 0; i < sizeof(BotActions) / sizeof(BotAction_t); ++i) for (auto i = 0u; i < std::extent_v<decltype(BotActions)>; ++i)
{ {
if (strcmp(&action[1], BotActions[i].action)) if (Utils::String::ToLower(&action[1]) != BotActions[i].action)
continue; continue;
if (action[0] == '+') if (action[0] == '+')
g_botai[clientNum].buttons |= BotActions[i].key; g_botai[entref.entnum].buttons |= BotActions[i].key;
else else
g_botai[clientNum].buttons &= ~(BotActions[i].key); g_botai[entref.entnum].buttons &= ~BotActions[i].key;
g_botai[entref.entnum].active = true;
return; return;
} }
Game::Scr_Error("^1botAction: Unknown action.\n"); Game::Scr_ParamError(0, "^1BotAction: Unknown action.\n");
}); });
Script::AddFunction("botMovement", [](Game::scr_entref_t id) // Usage: <bot> botMovement(<int>, <int>); Script::AddFunction("BotMovement", [](Game::scr_entref_t entref) // Usage: <bot> BotMovement(<int>, <int>);
{ {
if (Game::Scr_GetNumParam() != 2u || Game::Scr_GetType(0) != Game::VAR_INTEGER || Game::Scr_GetType(1) != Game::VAR_INTEGER)
{
Game::Scr_Error("^1botMovement: Needs two integer parameters!\n");
return;
}
auto forwardInt = Game::Scr_GetInt(0); auto forwardInt = Game::Scr_GetInt(0);
auto rightInt = Game::Scr_GetInt(1); auto rightInt = Game::Scr_GetInt(1);
Game::gentity_t* gentity = Script::getEntFromEntRef(id); const auto* ent = Game::GetPlayerEntity(entref);
Game::client_t* client = Script::getClientFromEnt(gentity); const auto* client = Script::GetClient(ent);
unsigned int clientNum = GetClientNum(client);
if (!Bots::IsValidClientNum(clientNum)) if (!client->bIsTestClient)
{ {
Game::Scr_Error("^1botMovement: Need to call on a player entity!\n"); Game::Scr_Error("^1BotMovement: Can only call on a bot!\n");
return; return;
} }
if (client->state < 3) forwardInt = std::clamp<int>(forwardInt, std::numeric_limits<char>::min(), std::numeric_limits<char>::max());
{ rightInt = std::clamp<int>(rightInt, std::numeric_limits<char>::min(), std::numeric_limits<char>::max());
Game::Scr_Error("^1botMovement: Needs to be connected.\n");
return;
}
if (!client->isBot) g_botai[entref.entnum].forward = static_cast<int8_t>(forwardInt);
{ g_botai[entref.entnum].right = static_cast<int8_t>(rightInt);
Game::Scr_Error("^1botMovement: Can only call on a bot!\n"); g_botai[entref.entnum].active = true;
return;
}
if (forwardInt > 127)
forwardInt = 127;
if (forwardInt < -127)
forwardInt = -127;
if (rightInt > 127)
rightInt = 127;
if (rightInt < -127)
rightInt = -127;
g_botai[clientNum].forward = (int8)forwardInt;
g_botai[clientNum].right = (int8)rightInt;
}); });
} }
void Bots::BotAiAction(Game::client_t* cl)
{
if (cl->gentity == nullptr)
return;
const auto entnum = cl->gentity->s.number;
// Keep test client functionality
if (!g_botai[entnum].active)
{
Game::SV_BotUserMove(cl);
return;
}
Game::usercmd_s userCmd = {0};
userCmd.serverTime = *Game::svs_time;
userCmd.buttons = g_botai[entnum].buttons;
userCmd.forwardmove = g_botai[entnum].forward;
userCmd.rightmove = g_botai[entnum].right;
userCmd.weapon = g_botai[entnum].weapon;
Game::SV_ClientThink(cl, &userCmd);
}
constexpr auto SV_BotUserMove = 0x626E50;
__declspec(naked) void Bots::SV_BotUserMove_Hk()
{
__asm
{
pushad
push edi
call Bots::BotAiAction
add esp, 4
popad
ret
}
}
void Bots::G_SelectWeaponIndex(int clientNum, int iWeaponIndex)
{
if (g_botai[clientNum].active)
{
g_botai[clientNum].weapon = static_cast<uint16_t>(iWeaponIndex);
}
}
__declspec(naked) void Bots::G_SelectWeaponIndex_Hk()
{
__asm
{
pushad
push [esp + 0x20 + 0x8]
push [esp + 0x20 + 0x8]
call Bots::G_SelectWeaponIndex
add esp, 0x8
popad
// Code skipped by hook
mov eax, [esp + 0x8]
push eax
push 0x441B85
retn
}
}
Bots::Bots() Bots::Bots()
{ {
// Replace connect string // Replace connect string
@ -399,61 +321,45 @@ namespace Components
// Intercept sprintf for the connect string // Intercept sprintf for the connect string
Utils::Hook(0x48ADAB, Bots::BuildConnectString, HOOK_CALL).install()->quick(); Utils::Hook(0x48ADAB, Bots::BuildConnectString, HOOK_CALL).install()->quick();
// Stop default behavour of bots spinning and shooting Utils::Hook(0x627021, Bots::SV_BotUserMove_Hk, HOOK_CALL).install()->quick();
Utils::Hook(0x627021, 0x4BB9B0, HOOK_CALL).install()->quick(); Utils::Hook(0x627241, Bots::SV_BotUserMove_Hk, HOOK_CALL).install()->quick();
Utils::Hook(0x627241, 0x4BB9B0, HOOK_CALL).install()->quick();
// zero the bot command array Utils::Hook(0x441B80, Bots::G_SelectWeaponIndex_Hk, HOOK_JUMP).install()->quick();
for (int i = 0; i < MAX_G_BOTAI_ENTRIES; i++)
// Zero the bot command array
for (auto i = 0u; i < std::extent_v<decltype(g_botai)>; i++)
{ {
g_botai[i] = { 0 }; g_botai[i] = {0};
g_botai[i].weapon = 1; // prevent the bots from defaulting to the 'none' weapon g_botai[i].weapon = 1; // Prevent the bots from defaulting to the 'none' weapon
} }
// have the bots perform the command every server frame
Scheduler::OnFrame([]()
{
if (!Game::SV_Loaded())
return;
int time = *Game::svs_time;
int numClients = *Game::svs_numclients;
for (int i = 0; i < numClients; ++i)
{
Game::client_t* client = &Game::svs_clients[i];
if (client->state < 3)
continue;
if (!client->isBot)
continue;
Game::usercmd_s ucmd = { 0 };
ucmd.serverTime = time;
ucmd.buttons = g_botai[i].buttons;
ucmd.forwardmove = g_botai[i].forward;
ucmd.rightmove = g_botai[i].right;
ucmd.weapon = g_botai[i].weapon;
client->deltaMessage = client->netchan.outgoingSequence - 1;
Game::SV_ClientThink(client, &ucmd);
}
});
Command::Add("spawnBot", [](Command::Params* params) Command::Add("spawnBot", [](Command::Params* params)
{ {
unsigned int count = 1; auto count = 1u;
if (params->size() > 1) if (params->size() > 1)
{ {
if (params->get(1) == "all"s) count = static_cast<unsigned int>(-1); if (params->get(1) == "all"s)
else count = atoi(params->get(1)); {
count = *Game::svs_numclients;
}
else
{
char* endptr;
const auto* input = params->get(1);
count = std::strtoul(input, &endptr, 10);
if (input == endptr)
{
Logger::Print("Warning: %s is not a valid input\n"
"Usage: %s optional <number of bots> or optional <\"all\">\n",
input, params->get(0));
return;
}
}
} }
count = std::min(18u, count); count = std::min(static_cast<unsigned int>(*Game::svs_numclients), count);
// Check if ingame and host // Check if ingame and host
if (!Game::SV_Loaded()) if (!Game::SV_Loaded())
@ -470,10 +376,14 @@ namespace Components
}); });
Bots::AddMethods(); Bots::AddMethods();
}
Bots::~Bots() // In case a loaded mod didn't call "BotStop" before the VM shutdown
{ Script::OnVMShutdown([]
Bots::BotNames.clear(); {
for (auto i = 0u; i < std::extent_v<decltype(g_botai)>; i++)
{
g_botai[i].active = false;
}
});
} }
} }

View File

@ -6,17 +6,20 @@ namespace Components
{ {
public: public:
Bots(); Bots();
~Bots();
static unsigned int GetClientNum(Game::client_s*);
static bool IsValidClientNum(unsigned int);
private: private:
static std::vector<std::string> BotNames; static std::vector<std::string> BotNames;
static void BuildConnectString(char* buffer, const char* connectString, int num, int, int protocol, int checksum, int statVer, int statStuff, int port); static int BuildConnectString(char* buffer, const char* connectString, int num, int, int protocol, int checksum, int statVer, int statStuff, int port);
static void Spawn(unsigned int count); static void Spawn(unsigned int count);
static void AddMethods(); static void AddMethods();
static void BotAiAction(Game::client_t* cl);
static void SV_BotUserMove_Hk();
static void G_SelectWeaponIndex(int clientNum, int iWeaponIndex);
static void G_SelectWeaponIndex_Hk();
}; };
} }

View File

@ -11,6 +11,7 @@ namespace Components
std::uint8_t padding3[4]; std::uint8_t padding3[4];
std::int32_t tableColumn; std::int32_t tableColumn;
}; };
struct playercarddata_s struct playercarddata_s
{ {
std::uint32_t padding; std::uint32_t padding;

View File

@ -1,176 +0,0 @@
#include <STDInclude.hpp>
namespace Components
{
void Client::AddFunctions()
{
//File functions
Script::AddFunction("fileWrite", [](Game::scr_entref_t) // gsc: fileWrite(<filepath>, <string>, <mode>)
{
std::string path = Game::Scr_GetString(0);
auto text = Game::Scr_GetString(1);
auto mode = Game::Scr_GetString(2);
if (path.empty())
{
Game::Com_Printf(0, "^1fileWrite: filepath not defined!\n");
return;
}
std::vector<const char*> queryStrings = { R"(..)", R"(../)", R"(..\)" };
for (auto i = 0u; i < queryStrings.size(); i++)
{
if (path.find(queryStrings[i]) != std::string::npos)
{
Game::Com_Printf(0, "^1fileWrite: directory traversal is not allowed!\n");
return;
}
}
if (mode != "append"s && mode != "write"s)
{
Game::Com_Printf(0, "^3fileWrite: mode not defined or was wrong, defaulting to 'write'\n");
mode = const_cast<char*>("write");
}
if (mode == "write"s)
{
FileSystem::FileWriter(path).write(text);
}
else if (mode == "append"s)
{
FileSystem::FileWriter(path, true).write(text);
}
});
Script::AddFunction("fileRead", [](Game::scr_entref_t) // gsc: fileRead(<filepath>)
{
std::string path = Game::Scr_GetString(0);
if (path.empty())
{
Game::Com_Printf(0, "^1fileRead: filepath not defined!\n");
return;
}
std::vector<const char*> queryStrings = { R"(..)", R"(../)", R"(..\)" };
for (auto i = 0u; i < queryStrings.size(); i++)
{
if (path.find(queryStrings[i]) != std::string::npos)
{
Game::Com_Printf(0, "^1fileRead: directory traversal is not allowed!\n");
return;
}
}
if (!FileSystem::FileReader(path).exists())
{
Game::Com_Printf(0, "^1fileRead: file not found!\n");
return;
}
Game::Scr_AddString(FileSystem::FileReader(path).getBuffer().data());
});
Script::AddFunction("fileExists", [](Game::scr_entref_t) // gsc: fileExists(<filepath>)
{
std::string path = Game::Scr_GetString(0);
if (path.empty())
{
Game::Com_Printf(0, "^1fileExists: filepath not defined!\n");
return;
}
std::vector<const char*> queryStrings = { R"(..)", R"(../)", R"(..\)" };
for (auto i = 0u; i < queryStrings.size(); i++)
{
if (path.find(queryStrings[i]) != std::string::npos)
{
Game::Com_Printf(0, "^1fileExists: directory traversal is not allowed!\n");
return;
}
}
Game::Scr_AddInt(FileSystem::FileReader(path).exists());
});
Script::AddFunction("fileRemove", [](Game::scr_entref_t) // gsc: fileRemove(<filepath>)
{
std::string path = Game::Scr_GetString(0);
if (path.empty())
{
Game::Com_Printf(0, "^1fileRemove: filepath not defined!\n");
return;
}
std::vector<const char*> queryStrings = { R"(..)", R"(../)", R"(..\)" };
for (auto i = 0u; i < queryStrings.size(); i++)
{
if (path.find(queryStrings[i]) != std::string::npos)
{
Game::Com_Printf(0, "^1fileRemove: directory traversal is not allowed!\n");
return;
}
}
auto p = std::filesystem::path(path);
std::string folder = p.parent_path().string();
std::string file = p.filename().string();
Game::Scr_AddInt(FileSystem::DeleteFile(folder, file));
});
}
void Client::AddMethods()
{
// Client methods
Script::AddFunction("getIp", [](Game::scr_entref_t id) // gsc: self getIp()
{
Game::gentity_t* gentity = Script::getEntFromEntRef(id);
Game::client_t* client = Script::getClientFromEnt(gentity);
if (client->state >= 3)
{
std::string ip = Game::NET_AdrToString(client->netchan.remoteAddress);
if (ip.find_first_of(":") != std::string::npos)
ip.erase(ip.begin() + ip.find_first_of(":"), ip.end()); // erase port
Game::Scr_AddString(ip.data());
}
});
Script::AddFunction("getPing", [](Game::scr_entref_t id) // gsc: self getPing()
{
Game::gentity_t* gentity = Script::getEntFromEntRef(id);
Game::client_t* client = Script::getClientFromEnt(gentity);
if (client->state >= 3)
{
int ping = (int)client->ping;
Game::Scr_AddInt(ping);
}
});
}
void Client::AddCommands()
{
Command::Add("NULL", [](Command::Params*)
{
return NULL;
});
}
Client::Client()
{
Client::AddFunctions();
Client::AddMethods();
Client::AddCommands();
}
Client::~Client()
{
}
}

View File

@ -1,17 +0,0 @@
#pragma once
namespace Components
{
class Client : public Component
{
public:
Client();
~Client();
private:
static void AddFunctions();
static void AddMethods();
static void AddCommands();
};
}

View File

@ -17,7 +17,7 @@ namespace Components
if (ent->health < 1) if (ent->health < 1)
{ {
Logger::Print("CheatsOk: entity %u must be alive to use this command!\n", entNum); Logger::Print("CheatsOk: entity %i must be alive to use this command!\n", entNum);
Game::SV_GameSendServerCommand(entNum, 0, Utils::String::VA("%c \"GAME_MUSTBEALIVECOMMAND\"", 0x65)); Game::SV_GameSendServerCommand(entNum, 0, Utils::String::VA("%c \"GAME_MUSTBEALIVECOMMAND\"", 0x65));
return false; return false;
} }
@ -28,10 +28,11 @@ namespace Components
bool ClientCommand::CallbackHandler(Game::gentity_s* ent, const char* cmd) bool ClientCommand::CallbackHandler(Game::gentity_s* ent, const char* cmd)
{ {
const auto command = Utils::String::ToLower(cmd); const auto command = Utils::String::ToLower(cmd);
const auto got = ClientCommand::FunctionMap.find(command);
if (ClientCommand::FunctionMap.find(command) != ClientCommand::FunctionMap.end()) if (got != ClientCommand::FunctionMap.end())
{ {
ClientCommand::FunctionMap[command](ent); got->second(ent);
return true; return true;
} }
@ -42,7 +43,7 @@ namespace Components
{ {
const auto command = Utils::String::ToLower(name); const auto command = Utils::String::ToLower(name);
ClientCommand::FunctionMap[command] = callback; ClientCommand::FunctionMap[command] = std::move(callback);
} }
void ClientCommand::ClientCommandStub(const int clientNum) void ClientCommand::ClientCommandStub(const int clientNum)
@ -75,7 +76,7 @@ namespace Components
ent->client->flags ^= Game::PLAYER_FLAG_NOCLIP; ent->client->flags ^= Game::PLAYER_FLAG_NOCLIP;
const auto entNum = ent->s.number; const auto entNum = ent->s.number;
Logger::Print("Noclip toggled for entity %u\n", entNum); Logger::Print("Noclip toggled for entity %i\n", entNum);
Game::SV_GameSendServerCommand(entNum, 0, Utils::String::VA("%c \"%s\"", 0x65, Game::SV_GameSendServerCommand(entNum, 0, Utils::String::VA("%c \"%s\"", 0x65,
(ent->client->flags & Game::PLAYER_FLAG_NOCLIP) ? "GAME_NOCLIPON" : "GAME_NOCLIPOFF")); (ent->client->flags & Game::PLAYER_FLAG_NOCLIP) ? "GAME_NOCLIPON" : "GAME_NOCLIPOFF"));
@ -89,7 +90,7 @@ namespace Components
ent->client->flags ^= Game::PLAYER_FLAG_UFO; ent->client->flags ^= Game::PLAYER_FLAG_UFO;
const auto entNum = ent->s.number; const auto entNum = ent->s.number;
Logger::Print("UFO toggled for entity %u\n", entNum); Logger::Print("UFO toggled for entity %i\n", entNum);
Game::SV_GameSendServerCommand(entNum, 0, Utils::String::VA("%c \"%s\"", 0x65, Game::SV_GameSendServerCommand(entNum, 0, Utils::String::VA("%c \"%s\"", 0x65,
(ent->client->flags & Game::PLAYER_FLAG_UFO) ? "GAME_UFOON" : "GAME_UFOOFF")); (ent->client->flags & Game::PLAYER_FLAG_UFO) ? "GAME_UFOON" : "GAME_UFOOFF"));
@ -103,7 +104,7 @@ namespace Components
ent->flags ^= Game::FL_GODMODE; ent->flags ^= Game::FL_GODMODE;
const auto entNum = ent->s.number; const auto entNum = ent->s.number;
Logger::Print("God toggled for entity %u\n", entNum); Logger::Print("God toggled for entity %i\n", entNum);
Game::SV_GameSendServerCommand(entNum, 0, Utils::String::VA("%c \"%s\"", 0x65, Game::SV_GameSendServerCommand(entNum, 0, Utils::String::VA("%c \"%s\"", 0x65,
(ent->flags & Game::FL_GODMODE) ? "GAME_GODMODE_ON" : "GAME_GODMODE_OFF")); (ent->flags & Game::FL_GODMODE) ? "GAME_GODMODE_ON" : "GAME_GODMODE_OFF"));
@ -117,7 +118,7 @@ namespace Components
ent->flags ^= Game::FL_DEMI_GODMODE; ent->flags ^= Game::FL_DEMI_GODMODE;
const auto entNum = ent->s.number; const auto entNum = ent->s.number;
Logger::Print("Demigod toggled for entity %u\n", entNum); Logger::Print("Demigod toggled for entity %i\n", entNum);
Game::SV_GameSendServerCommand(entNum, 0, Utils::String::VA("%c \"%s\"", 0x65, Game::SV_GameSendServerCommand(entNum, 0, Utils::String::VA("%c \"%s\"", 0x65,
(ent->flags & Game::FL_DEMI_GODMODE) ? "GAME_DEMI_GODMODE_ON" : "GAME_DEMI_GODMODE_OFF")); (ent->flags & Game::FL_DEMI_GODMODE) ? "GAME_DEMI_GODMODE_ON" : "GAME_DEMI_GODMODE_OFF"));
@ -131,7 +132,7 @@ namespace Components
ent->flags ^= Game::FL_NOTARGET; ent->flags ^= Game::FL_NOTARGET;
const auto entNum = ent->s.number; const auto entNum = ent->s.number;
Logger::Print("Notarget toggled for entity %u\n", entNum); Logger::Print("Notarget toggled for entity %i\n", entNum);
Game::SV_GameSendServerCommand(entNum, 0, Utils::String::VA("%c \"%s\"", 0x65, Game::SV_GameSendServerCommand(entNum, 0, Utils::String::VA("%c \"%s\"", 0x65,
(ent->flags & Game::FL_NOTARGET) ? "GAME_NOTARGETON" : "GAME_NOTARGETOFF")); (ent->flags & Game::FL_NOTARGET) ? "GAME_NOTARGETON" : "GAME_NOTARGETOFF"));
@ -177,126 +178,106 @@ namespace Components
{ {
Script::AddFunction("Noclip", [](Game::scr_entref_t entref) // gsc: Noclip(<optional int toggle>); Script::AddFunction("Noclip", [](Game::scr_entref_t entref) // gsc: Noclip(<optional int toggle>);
{ {
if (entref >= Game::MAX_GENTITIES || Game::g_entities[entref].client == nullptr) const auto* ent = Game::GetPlayerEntity(entref);
{
Game::Scr_Error(Utils::String::VA("^1NoClip: entity %u is not a client\n", entref));
return;
}
if (Game::Scr_GetNumParam() == 1u && Game::Scr_GetType(0) == Game::VAR_INTEGER) if (Game::Scr_GetNumParam() >= 1u)
{ {
if (Game::Scr_GetInt(0)) if (Game::Scr_GetInt(0))
{ {
Game::g_entities[entref].client->flags |= Game::PLAYER_FLAG_NOCLIP; ent->client->flags |= Game::PLAYER_FLAG_NOCLIP;
} }
else else
{ {
Game::g_entities[entref].client->flags &= ~Game::PLAYER_FLAG_NOCLIP; ent->client->flags &= ~Game::PLAYER_FLAG_NOCLIP;
} }
} }
else else
{ {
Game::g_entities[entref].client->flags ^= Game::PLAYER_FLAG_NOCLIP; ent->client->flags ^= Game::PLAYER_FLAG_NOCLIP;
} }
}); });
Script::AddFunction("Ufo", [](Game::scr_entref_t entref) // gsc: Ufo(<optional int toggle>); Script::AddFunction("Ufo", [](Game::scr_entref_t entref) // gsc: Ufo(<optional int toggle>);
{ {
if (entref >= Game::MAX_GENTITIES || Game::g_entities[entref].client == nullptr) const auto* ent = Game::GetPlayerEntity(entref);
{
Game::Scr_Error(Utils::String::VA("^1Ufo: entity %u is not a client\n", entref));
return;
}
if (Game::Scr_GetNumParam() == 1u && Game::Scr_GetType(0) == Game::VAR_INTEGER) if (Game::Scr_GetNumParam() >= 1u)
{ {
if (Game::Scr_GetInt(0)) if (Game::Scr_GetInt(0))
{ {
Game::g_entities[entref].client->flags |= Game::PLAYER_FLAG_UFO; ent->client->flags |= Game::PLAYER_FLAG_UFO;
} }
else else
{ {
Game::g_entities[entref].client->flags &= ~Game::PLAYER_FLAG_UFO; ent->client->flags &= ~Game::PLAYER_FLAG_UFO;
} }
} }
else else
{ {
Game::g_entities[entref].client->flags ^= Game::PLAYER_FLAG_UFO; ent->client->flags ^= Game::PLAYER_FLAG_UFO;
} }
}); });
Script::AddFunction("God", [](Game::scr_entref_t entref) // gsc: God(<optional int toggle>); Script::AddFunction("God", [](Game::scr_entref_t entref) // gsc: God(<optional int toggle>);
{ {
if (entref >= Game::MAX_GENTITIES) auto* ent = Game::GetEntity(entref);
{
Game::Scr_Error(Utils::String::VA("^1God: entity %u is out of bounds\n", entref));
return;
}
if (Game::Scr_GetNumParam() == 1u && Game::Scr_GetType(0) == Game::VAR_INTEGER) if (Game::Scr_GetNumParam() >= 1u)
{ {
if (Game::Scr_GetInt(0)) if (Game::Scr_GetInt(0))
{ {
Game::g_entities[entref].flags |= Game::FL_GODMODE; ent->flags |= Game::FL_GODMODE;
} }
else else
{ {
Game::g_entities[entref].flags &= ~Game::FL_GODMODE; ent->flags &= ~Game::FL_GODMODE;
} }
} }
else else
{ {
Game::g_entities[entref].flags ^= Game::FL_GODMODE; ent->flags ^= Game::FL_GODMODE;
} }
}); });
Script::AddFunction("Demigod", [](Game::scr_entref_t entref) // gsc: Demigod(<optional int toggle>); Script::AddFunction("Demigod", [](Game::scr_entref_t entref) // gsc: Demigod(<optional int toggle>);
{ {
if (entref >= Game::MAX_GENTITIES) auto* ent = Game::GetEntity(entref);
{
Game::Scr_Error(Utils::String::VA("^1Demigod: entity %u is out of bounds\n", entref));
return;
}
if (Game::Scr_GetNumParam() == 1u && Game::Scr_GetType(0) == Game::VAR_INTEGER) if (Game::Scr_GetNumParam() >= 1u)
{ {
if (Game::Scr_GetInt(0)) if (Game::Scr_GetInt(0))
{ {
Game::g_entities[entref].flags |= Game::FL_DEMI_GODMODE; ent->flags |= Game::FL_DEMI_GODMODE;
} }
else else
{ {
Game::g_entities[entref].flags &= ~Game::FL_DEMI_GODMODE; ent->flags &= ~Game::FL_DEMI_GODMODE;
} }
} }
else else
{ {
Game::g_entities[entref].flags ^= Game::FL_DEMI_GODMODE; ent->flags ^= Game::FL_DEMI_GODMODE;
} }
}); });
Script::AddFunction("Notarget", [](Game::scr_entref_t entref) // gsc: Notarget(<optional int toggle>); Script::AddFunction("Notarget", [](Game::scr_entref_t entref) // gsc: Notarget(<optional int toggle>);
{ {
if (entref >= Game::MAX_GENTITIES) auto* ent = Game::GetEntity(entref);
{
Game::Scr_Error(Utils::String::VA("^1Notarget: entity %u is out of bounds\n", entref));
return;
}
if (Game::Scr_GetNumParam() == 1u && Game::Scr_GetType(0) == Game::VAR_INTEGER) if (Game::Scr_GetNumParam() >= 1u)
{ {
if (Game::Scr_GetInt(0)) if (Game::Scr_GetInt(0))
{ {
Game::g_entities[entref].flags |= Game::FL_NOTARGET; ent->flags |= Game::FL_NOTARGET;
} }
else else
{ {
Game::g_entities[entref].flags &= ~Game::FL_NOTARGET; ent->flags &= ~Game::FL_NOTARGET;
} }
} }
else else
{ {
Game::g_entities[entref].flags ^= Game::FL_NOTARGET; ent->flags ^= Game::FL_NOTARGET;
} }
}); });
} }

View File

@ -964,13 +964,20 @@ namespace Components
Download::ScriptDownloads.clear(); Download::ScriptDownloads.clear();
}); });
Script::AddFunction("httpGet", [](Game::scr_entref_t) Script::AddFunction("HttpGet", [](Game::scr_entref_t)
{ {
if (!Dedicated::IsEnabled() && !Flags::HasFlag("scriptablehttp")) return; if (!Flags::HasFlag("scriptablehttp"))
if (Game::Scr_GetNumParam() < 1u) return; return;
std::string url = Game::Scr_GetString(0); const auto* url = Game::Scr_GetString(0);
unsigned int object = Game::AllocObject();
if (url == nullptr)
{
Game::Scr_ParamError(0, "^1HttpGet: Illegal parameter!\n");
return;
}
auto object = Game::AllocObject();
Game::Scr_AddObject(object); Game::Scr_AddObject(object);
@ -978,13 +985,13 @@ namespace Components
Game::RemoveRefToObject(object); Game::RemoveRefToObject(object);
}); });
Script::AddFunction("httpCancel", [](Game::scr_entref_t) Script::AddFunction("HttpCancel", [](Game::scr_entref_t)
{ {
if (!Dedicated::IsEnabled() && !Flags::HasFlag("scriptablehttp")) return; if (!Flags::HasFlag("scriptablehttp"))
if (Game::Scr_GetNumParam() < 1u) return; return;
unsigned int object = Game::Scr_GetObject(0); const auto object = Game::Scr_GetObject(0);
for (auto& download : Download::ScriptDownloads) for (const auto& download : Download::ScriptDownloads)
{ {
if (object == download->getObject()) if (object == download->getObject())
{ {

View File

@ -788,7 +788,7 @@ namespace Components
{ {
int dlc = token.get<int>(); int dlc = token.get<int>();
for (auto pack : Maps::DlcPacks) for (const auto& pack : Maps::DlcPacks)
{ {
if (pack.index == dlc) if (pack.index == dlc)
{ {

View File

@ -10,48 +10,59 @@ namespace Components
{ {
Game::NET_StringToAdr(addrString.data(), &this->address); Game::NET_StringToAdr(addrString.data(), &this->address);
} }
Network::Address::Address(sockaddr* addr) Network::Address::Address(sockaddr* addr)
{ {
Game::SockadrToNetadr(addr, &this->address); Game::SockadrToNetadr(addr, &this->address);
} }
bool Network::Address::operator==(const Network::Address& obj) const bool Network::Address::operator==(const Network::Address& obj) const
{ {
return Game::NET_CompareAdr(this->address, obj.address); return Game::NET_CompareAdr(this->address, obj.address);
} }
void Network::Address::setPort(unsigned short port) void Network::Address::setPort(unsigned short port)
{ {
this->address.port = htons(port); this->address.port = htons(port);
} }
unsigned short Network::Address::getPort() unsigned short Network::Address::getPort()
{ {
return ntohs(this->address.port); return ntohs(this->address.port);
} }
void Network::Address::setIP(DWORD ip) void Network::Address::setIP(DWORD ip)
{ {
this->address.ip.full = ip; this->address.ip.full = ip;
} }
void Network::Address::setIP(Game::netIP_t ip) void Network::Address::setIP(Game::netIP_t ip)
{ {
this->address.ip = ip; this->address.ip = ip;
} }
Game::netIP_t Network::Address::getIP() Game::netIP_t Network::Address::getIP()
{ {
return this->address.ip; return this->address.ip;
} }
void Network::Address::setType(Game::netadrtype_t type) void Network::Address::setType(Game::netadrtype_t type)
{ {
this->address.type = type; this->address.type = type;
} }
Game::netadrtype_t Network::Address::getType() Game::netadrtype_t Network::Address::getType()
{ {
return this->address.type; return this->address.type;
} }
sockaddr Network::Address::getSockAddr() sockaddr Network::Address::getSockAddr()
{ {
sockaddr addr; sockaddr addr;
this->toSockAddr(&addr); this->toSockAddr(&addr);
return addr; return addr;
} }
void Network::Address::toSockAddr(sockaddr* addr) void Network::Address::toSockAddr(sockaddr* addr)
{ {
if (addr) if (addr)
@ -59,22 +70,27 @@ namespace Components
Game::NetadrToSockadr(&this->address, addr); Game::NetadrToSockadr(&this->address, addr);
} }
} }
void Network::Address::toSockAddr(sockaddr_in* addr) void Network::Address::toSockAddr(sockaddr_in* addr)
{ {
this->toSockAddr(reinterpret_cast<sockaddr*>(addr)); this->toSockAddr(reinterpret_cast<sockaddr*>(addr));
} }
Game::netadr_t* Network::Address::get() Game::netadr_t* Network::Address::get()
{ {
return &this->address; return &this->address;
} }
const char* Network::Address::getCString() const const char* Network::Address::getCString() const
{ {
return Game::NET_AdrToString(this->address); return Game::NET_AdrToString(this->address);
} }
std::string Network::Address::getString() const std::string Network::Address::getString() const
{ {
return this->getCString(); return this->getCString();
} }
bool Network::Address::isLocal() bool Network::Address::isLocal()
{ {
// According to: https://en.wikipedia.org/wiki/Private_network // According to: https://en.wikipedia.org/wiki/Private_network
@ -95,6 +111,7 @@ namespace Components
return false; return false;
} }
bool Network::Address::isSelf() bool Network::Address::isSelf()
{ {
if (Game::NET_IsLocalAddress(this->address)) return true; // Loopback if (Game::NET_IsLocalAddress(this->address)) return true; // Loopback
@ -110,6 +127,7 @@ namespace Components
return false; return false;
} }
bool Network::Address::isLoopback() bool Network::Address::isLoopback()
{ {
if (this->getIP().full == 0x100007f) // 127.0.0.1 if (this->getIP().full == 0x100007f) // 127.0.0.1
@ -119,10 +137,12 @@ namespace Components
return Game::NET_IsLocalAddress(this->address); return Game::NET_IsLocalAddress(this->address);
} }
bool Network::Address::isValid() bool Network::Address::isValid()
{ {
return (this->getType() != Game::netadrtype_t::NA_BAD && this->getType() >= Game::netadrtype_t::NA_BOT && this->getType() <= Game::netadrtype_t::NA_IP); return (this->getType() != Game::netadrtype_t::NA_BAD && this->getType() >= Game::netadrtype_t::NA_BOT && this->getType() <= Game::netadrtype_t::NA_IP);
} }
void Network::Handle(const std::string& packet, Utils::Slot<Network::Callback> callback) void Network::Handle(const std::string& packet, Utils::Slot<Network::Callback> callback)
{ {
Network::PacketHandlers[Utils::String::ToLower(packet)] = callback; Network::PacketHandlers[Utils::String::ToLower(packet)] = callback;

View File

@ -315,7 +315,7 @@ namespace Components
{ {
if (Game::svs_clients[i].state >= 3) if (Game::svs_clients[i].state >= 3)
{ {
if (Game::svs_clients[i].isBot) ++botCount; if (Game::svs_clients[i].bIsTestClient) ++botCount;
else ++clientCount; else ++clientCount;
} }
} }

View File

@ -10,7 +10,7 @@ namespace Components
std::unordered_map<std::string, std::string> Script::ScriptStorage; std::unordered_map<std::string, std::string> Script::ScriptStorage;
std::unordered_map<int, std::string> Script::ScriptBaseProgramNum; std::unordered_map<int, std::string> Script::ScriptBaseProgramNum;
std::unordered_map<const char*, const char*> Script::ReplacedFunctions; std::unordered_map<const char*, const char*> Script::ReplacedFunctions;
const char* Script::ReplacedPos = 0; const char* Script::ReplacedPos = nullptr;
int Script::LastFrameTime = -1; int Script::LastFrameTime = -1;
Utils::Signal<Scheduler::Callback> Script::VMShutdownSignal; Utils::Signal<Scheduler::Callback> Script::VMShutdownSignal;
@ -45,6 +45,37 @@ namespace Components
} }
} }
void Script::RuntimeError(const char* codePos, unsigned int index, const char* msg, const char* dialogMessage)
{
const auto developer = Dvar::Var("developer").get<int>();
// Allow error messages to be printed if developer mode is on
// Should check scrVarPub.developer but it's absent
// in this version of the game so let's check the dvar
if (!Game::scrVmPub->terminal_error && !developer)
return;
// If were are developing let's call RuntimeErrorInternal
// scrVmPub.debugCode seems to be always false
if (Game::scrVmPub->debugCode || Game::scrVarPub->developer_script)
{
Game::RuntimeErrorInternal(23, codePos, index, msg);
}
else
{
Logger::Print(23, "%s\n", msg);
}
// Let's not throw error unless we have to
if (Game::scrVmPub->terminal_error)
{
if (dialogMessage == nullptr)
dialogMessage = "";
Logger::Error(Game::ERR_SCRIPT_DROP, "\x15script runtime error\n(see console for details)\n%s\n%s", msg, dialogMessage);
}
}
void Script::StoreScriptName(const char* name) void Script::StoreScriptName(const char* name)
{ {
Script::ScriptNameStack.push_back(Script::ScriptName); Script::ScriptNameStack.push_back(Script::ScriptName);
@ -156,11 +187,11 @@ namespace Components
void Script::CompileError(unsigned int offset, const char* message, ...) void Script::CompileError(unsigned int offset, const char* message, ...)
{ {
char msgbuf[1024] = { 0 }; char msgbuf[1024] = {0};
va_list v; va_list va;
va_start(v, message); va_start(va, message);
_vsnprintf_s(msgbuf, sizeof(msgbuf), message, v); _vsnprintf_s(msgbuf, _TRUNCATE, message, va);
va_end(v); va_end(va);
Game::Scr_ShutdownAllocNode(); Game::Scr_ShutdownAllocNode();
@ -180,7 +211,7 @@ namespace Components
if (!Game::Scr_LoadScript(script.data())) if (!Game::Scr_LoadScript(script.data()))
{ {
Logger::Print("Script %s encountered an error while loading. (doesn't exist?)", script.data()); Logger::Print("Script %s encountered an error while loading. (doesn't exist?)", script.data());
Logger::Error(Game::ERR_DROP, reinterpret_cast<char*>(0x70B810), script.data()); Logger::Error(Game::ERR_DROP, reinterpret_cast<const char*>(0x70B810), script.data());
} }
else else
{ {
@ -188,7 +219,7 @@ namespace Components
} }
Logger::Print("Finding script handle %s::%s...\n", script.data(), label.data()); Logger::Print("Finding script handle %s::%s...\n", script.data(), label.data());
int handle = Game::Scr_GetFunctionHandle(script.data(), label.data()); const auto handle = Game::Scr_GetFunctionHandle(script.data(), label.data());
if (handle) if (handle)
{ {
Logger::Print("Script handle %s::%s loaded successfully.\n", script.data(), label.data()); Logger::Print("Script handle %s::%s loaded successfully.\n", script.data(), label.data());
@ -201,7 +232,7 @@ namespace Components
void Script::LoadGameType() void Script::LoadGameType()
{ {
for (auto handle : Script::ScriptHandles) for (const auto& handle : Script::ScriptHandles)
{ {
Game::Scr_FreeThread(Game::Scr_ExecThread(handle, 0)); Game::Scr_FreeThread(Game::Scr_ExecThread(handle, 0));
} }
@ -213,7 +244,7 @@ namespace Components
{ {
Script::ScriptHandles.clear(); Script::ScriptHandles.clear();
auto list = FileSystem::GetFileList("scripts/", "gsc"); const auto list = FileSystem::GetFileList("scripts/", "gsc");
for (auto file : list) for (auto file : list)
{ {
@ -224,8 +255,12 @@ namespace Components
file = file.substr(0, file.size() - 4); file = file.substr(0, file.size() - 4);
} }
int handle = Script::LoadScriptAndLabel(file, "init"); auto handle = Script::LoadScriptAndLabel(file, "init");
if (handle) Script::ScriptHandles.push_back(handle);
if (handle)
{
Script::ScriptHandles.push_back(handle);
}
else else
{ {
handle = Script::LoadScriptAndLabel(file, "main"); handle = Script::LoadScriptAndLabel(file, "main");
@ -300,14 +335,12 @@ namespace Components
void Script::Scr_PrintPrevCodePos(int scriptPos) void Script::Scr_PrintPrevCodePos(int scriptPos)
{ {
int bestCodePos = -1; auto bestCodePos = -1, nextCodePos = -1, offset = -1;
int nextCodePos = -1;
int offset = -1;
std::string file; std::string file;
for (auto kv : Script::ScriptBaseProgramNum) for (const auto& [key, value] : Script::ScriptBaseProgramNum)
{ {
int codePos = kv.first; const auto codePos = key;
if (codePos > scriptPos) if (codePos > scriptPos)
{ {
@ -322,17 +355,15 @@ namespace Components
bestCodePos = codePos; bestCodePos = codePos;
file = kv.second; file = value;
offset = scriptPos - bestCodePos; offset = scriptPos - bestCodePos;
} }
if (bestCodePos == -1) if (bestCodePos == -1)
return; return;
float onehundred = 100.0;
Logger::Print(23, "\n@ %d (%d - %d)\n", scriptPos, bestCodePos, nextCodePos); Logger::Print(23, "\n@ %d (%d - %d)\n", scriptPos, bestCodePos, nextCodePos);
Logger::Print(23, "in %s (%.1f%% through the source)\n\n", file.c_str(), ((offset * onehundred) / (nextCodePos - bestCodePos))); Logger::Print(23, "in %s (%.1f%% through the source)\n\n", file.data(), ((offset * 100.0f) / (nextCodePos - bestCodePos)));
} }
__declspec(naked) void Script::Scr_PrintPrevCodePosStub() __declspec(naked) void Script::Scr_PrintPrevCodePosStub()
@ -373,15 +404,15 @@ namespace Components
void Script::OnVMShutdown(Utils::Slot<Scheduler::Callback> callback) void Script::OnVMShutdown(Utils::Slot<Scheduler::Callback> callback)
{ {
Script::ScriptBaseProgramNum.clear(); Script::ScriptBaseProgramNum.clear();
Script::VMShutdownSignal.connect(callback); Script::VMShutdownSignal.connect(std::move(callback));
} }
void Script::ScrShutdownSystemStub(int num) void Script::ScrShutdownSystemStub(unsigned char sys)
{ {
Script::VMShutdownSignal(); Script::VMShutdownSignal();
// Scr_ShutdownSystem // Scr_ShutdownSystem
Utils::Hook::Call<void(int)>(0x421EE0)(num); Utils::Hook::Call<void(unsigned char)>(0x421EE0)(sys);
} }
unsigned int Script::SetExpFogStub() unsigned int Script::SetExpFogStub()
@ -403,7 +434,7 @@ namespace Components
{ {
if (static_cast<unsigned int>(index) >= Game::scrVmPub->outparamcount) if (static_cast<unsigned int>(index) >= Game::scrVmPub->outparamcount)
{ {
Game::Scr_Error("^1GetCodePosForParam: Index is out of range!\n"); Game::Scr_ParamError(static_cast<unsigned int>(index), "^1GetCodePosForParam: Index is out of range!\n");
return ""; return "";
} }
@ -411,7 +442,7 @@ namespace Components
if (value->type != Game::VAR_FUNCTION) if (value->type != Game::VAR_FUNCTION)
{ {
Game::Scr_Error("^1GetCodePosForParam: Expects a function as parameter!\n"); Game::Scr_ParamError(static_cast<unsigned int>(index), "^1GetCodePosForParam: Expects a function as parameter!\n");
return ""; return "";
} }
@ -430,7 +461,7 @@ namespace Components
{ {
if (what[0] == '\0' || with[0] == '\0') if (what[0] == '\0' || with[0] == '\0')
{ {
Logger::Print("Warning: Invalid paramters passed to ReplacedFunctions\n"); Logger::Print("Warning: Invalid parameters passed to ReplacedFunctions\n");
return; return;
} }
@ -487,19 +518,23 @@ namespace Components
} }
} }
Game::gentity_t* Script::getEntFromEntRef(Game::scr_entref_t entref) Game::client_t* Script::GetClient(const Game::gentity_t* ent)
{ {
Game::gentity_t* gentity = &Game::g_entities[entref]; assert(ent != nullptr);
return gentity;
}
Game::client_t* Script::getClientFromEnt(Game::gentity_t* gentity) if (ent->client == nullptr)
{
if (!gentity->client)
{ {
Logger::Error(Game::ERR_SCRIPT_DROP, "Entity: %i is not a client", gentity); Game::Scr_ObjectError(Utils::String::VA("Entity %i is not a player", ent->s.number));
return nullptr;
} }
return &Game::svs_clients[gentity->s.number];
if (ent->s.number >= *Game::svs_numclients)
{
Game::Scr_ObjectError(Utils::String::VA("Entity %i is out of bounds", ent->s.number));
return nullptr;
}
return &Game::svs_clients[ent->s.number];
} }
void Script::AddFunctions() void Script::AddFunctions()
@ -535,63 +570,65 @@ namespace Components
Game::Scr_AddInt(time.wMilliseconds); Game::Scr_AddInt(time.wMilliseconds);
}); });
// Print to console, even without being in 'developer 1'.
Script::AddFunction("PrintConsole", [](Game::scr_entref_t) // gsc: PrintConsole(<string>)
{
if (Game::Scr_GetNumParam() != 1u || Game::Scr_GetType(0) != Game::VAR_STRING)
{
Game::Scr_Error("^1PrintConsole: Needs one string parameter!\n");
return;
}
auto str = Game::Scr_GetString(0);
Game::Com_Printf(0, str);
});
// Executes command to the console // Executes command to the console
Script::AddFunction("Exec", [](Game::scr_entref_t) // gsc: Exec(<string>) Script::AddFunction("Exec", [](Game::scr_entref_t) // gsc: Exec(<string>)
{ {
if (Game::Scr_GetNumParam() != 1u || Game::Scr_GetType(0) != Game::VAR_STRING) const auto str = Game::Scr_GetString(0);
if (str == nullptr)
{ {
Game::Scr_Error("^1Exec: Needs one string parameter!\n"); Game::Scr_ParamError(0, "^1Exec: Illegal parameter!\n");
return; return;
} }
auto str = Game::Scr_GetString(0);
Command::Execute(str, false); Command::Execute(str, false);
}); });
// Allow printing to the console even when developer is 0
Script::AddFunction("PrintConsole", [](Game::scr_entref_t) // gsc: PrintConsole(<string>)
{
for (auto i = 0u; i < Game::Scr_GetNumParam(); i++)
{
const auto str = Game::Scr_GetString(i);
// Script Storage Funcs if (str == nullptr)
{
Game::Scr_ParamError(i, "^1PrintConsole: Illegal parameter!\n");
return;
}
Logger::Print(*Game::level_scriptPrintChannel, "%s", str);
}
});
// Script Storage Functions
Script::AddFunction("StorageSet", [](Game::scr_entref_t) // gsc: StorageSet(<str key>, <str data>); Script::AddFunction("StorageSet", [](Game::scr_entref_t) // gsc: StorageSet(<str key>, <str data>);
{ {
if (Game::Scr_GetNumParam() != 2u || Game::Scr_GetType(0) != Game::VAR_STRING || Game::Scr_GetType(1) != Game::VAR_STRING) const auto* key = Game::Scr_GetString(0);
const auto* value = Game::Scr_GetString(1);
if (key == nullptr || value == nullptr)
{ {
Game::Scr_Error("^1StorageSet: Needs two string parameters!\n"); Game::Scr_Error("^1StorageSet: Illegal parameters!\n");
return; return;
} }
std::string key = Game::Scr_GetString(0); Script::ScriptStorage.insert_or_assign(key, value);
std::string data = Game::Scr_GetString(1);
Script::ScriptStorage.insert_or_assign(key, data);
}); });
Script::AddFunction("StorageRemove", [](Game::scr_entref_t) // gsc: StorageRemove(<str key>); Script::AddFunction("StorageRemove", [](Game::scr_entref_t) // gsc: StorageRemove(<str key>);
{ {
if (Game::Scr_GetNumParam() != 1u || Game::Scr_GetType(0) != Game::VAR_STRING) const auto* key = Game::Scr_GetString(0);
if (key == nullptr)
{ {
Game::Scr_Error("^1StorageRemove: Needs one string parameter!\n"); Game::Scr_Error("^1StorageRemove: Illegal parameter!\n");
return; return;
} }
std::string key = Game::Scr_GetString(0);
if (!Script::ScriptStorage.count(key)) if (!Script::ScriptStorage.count(key))
{ {
Game::Scr_Error(Utils::String::VA("^1StorageRemove: Store does not have key '%s'!\n", key.c_str())); Game::Scr_Error(Utils::String::VA("^1StorageRemove: Store does not have key '%s'!\n", key));
return; return;
} }
@ -600,41 +637,49 @@ namespace Components
Script::AddFunction("StorageGet", [](Game::scr_entref_t) // gsc: StorageGet(<str key>); Script::AddFunction("StorageGet", [](Game::scr_entref_t) // gsc: StorageGet(<str key>);
{ {
if (Game::Scr_GetNumParam() != 1u || Game::Scr_GetType(0) != Game::VAR_STRING) const auto* key = Game::Scr_GetString(0);
if (key == nullptr)
{ {
Game::Scr_Error("^1StorageGet: Needs one string parameter!\n"); Game::Scr_Error("^1StorageGet: Illegal parameter!\n");
return; return;
} }
std::string key = Game::Scr_GetString(0);
if (!Script::ScriptStorage.count(key)) if (!Script::ScriptStorage.count(key))
{ {
Game::Scr_Error(Utils::String::VA("^1StorageGet: Store does not have key '%s'!\n", key.c_str())); Game::Scr_Error(Utils::String::VA("^1StorageGet: Store does not have key '%s'!\n", key));
return; return;
} }
auto data = Script::ScriptStorage.at(key); const auto& data = Script::ScriptStorage.at(key);
Game::Scr_AddString(data.c_str()); Game::Scr_AddString(data.data());
}); });
Script::AddFunction("StorageHas", [](Game::scr_entref_t) // gsc: StorageHas(<str key>); Script::AddFunction("StorageHas", [](Game::scr_entref_t) // gsc: StorageHas(<str key>);
{ {
if (Game::Scr_GetNumParam() != 1u || Game::Scr_GetType(0) != Game::VAR_STRING) const auto* key = Game::Scr_GetString(0);
if (key == nullptr)
{ {
Game::Scr_Error("^1StorageHas: Needs one string parameter!\n"); Game::Scr_Error("^1StorageHas: Illegal parameter!\n");
return; return;
} }
std::string key = Game::Scr_GetString(0); Game::Scr_AddBool(static_cast<int>(Script::ScriptStorage.count(key))); // Until C++17
Game::Scr_AddInt(Script::ScriptStorage.count(key));
}); });
Script::AddFunction("StorageClear", [](Game::scr_entref_t) // gsc: StorageClear(); Script::AddFunction("StorageClear", [](Game::scr_entref_t) // gsc: StorageClear();
{ {
Script::ScriptStorage.clear(); Script::ScriptStorage.clear();
}); });
// PlayerCmd_AreControlsFrozen GSC function from Black Ops 2
Script::AddFunction("AreControlsFrozen", [](Game::scr_entref_t entref) // Usage: self AreControlsFrozen();
{
const auto* ent = Game::GetPlayerEntity(entref);
Game::Scr_AddBool((ent->client->flags & Game::PLAYER_FLAG_FROZEN) != 0);
});
} }
Script::Script() Script::Script()
@ -645,14 +690,12 @@ namespace Components
Utils::Hook(0x426C2D, Script::StoreScriptBaseProgramNumStub, HOOK_JUMP).install()->quick(); Utils::Hook(0x426C2D, Script::StoreScriptBaseProgramNumStub, HOOK_JUMP).install()->quick();
Utils::Hook(0x42281B, Script::Scr_PrintPrevCodePosStub, HOOK_JUMP).install()->quick(); Utils::Hook(0x42281B, Script::Scr_PrintPrevCodePosStub, HOOK_JUMP).install()->quick();
// enable scr_error printing if in developer Utils::Hook(0x61E3AD, Script::RuntimeError, HOOK_CALL).install()->quick();
Dvar::OnInit([]() Utils::Hook(0x621976, Script::RuntimeError, HOOK_CALL).install()->quick();
{ Utils::Hook(0x62246E, Script::RuntimeError, HOOK_CALL).install()->quick();
int developer = Dvar::Var("developer").get<int>(); // Skip check in GScr_CheckAllowedToSetPersistentData to prevent log spam in RuntimeError.
// On IW5 the function is entirely nullsubbed
if (developer > 0 && Dedicated::IsEnabled()) Utils::Hook::Set<BYTE>(0x5F8DBF, 0xEB);
Utils::Hook::Set<BYTE>(0x48D8C7, 0x75);
});
Utils::Hook(0x612E8D, Script::FunctionError, HOOK_CALL).install()->quick(); Utils::Hook(0x612E8D, Script::FunctionError, HOOK_CALL).install()->quick();
Utils::Hook(0x612EA2, Script::FunctionError, HOOK_CALL).install()->quick(); Utils::Hook(0x612EA2, Script::FunctionError, HOOK_CALL).install()->quick();
@ -669,19 +712,20 @@ namespace Components
Utils::Hook(0x61E92E, Script::VMExecuteInternalStub, HOOK_JUMP).install()->quick(); Utils::Hook(0x61E92E, Script::VMExecuteInternalStub, HOOK_JUMP).install()->quick();
Utils::Hook::Nop(0x61E933, 1); Utils::Hook::Nop(0x61E933, 1);
Utils::Hook(0x47548B, Script::ScrShutdownSystemStub, HOOK_CALL).install()->quick(); Utils::Hook(0x47548B, Script::ScrShutdownSystemStub, HOOK_CALL).install()->quick(); // G_LoadGame
Utils::Hook(0x4D06BA, Script::ScrShutdownSystemStub, HOOK_CALL).install()->quick(); Utils::Hook(0x4D06BA, Script::ScrShutdownSystemStub, HOOK_CALL).install()->quick(); // G_ShutdownGame
Scheduler::OnFrame([]() Scheduler::OnFrame([]()
{ {
if (!Game::SV_Loaded()) if (!Game::SV_Loaded())
return; return;
int nowMs = Game::Sys_Milliseconds(); const auto nowMs = Game::Sys_Milliseconds();
if (Script::LastFrameTime != -1) if (Script::LastFrameTime != -1)
{ {
int timeTaken = static_cast<int>((nowMs - Script::LastFrameTime) * Dvar::Var("timescale").get<float>()); const auto timeScale = Dvar::Var("timescale").get<float>();
const auto timeTaken = static_cast<int>((nowMs - Script::LastFrameTime) * timeScale);
if (timeTaken >= 500) if (timeTaken >= 500)
Logger::Print(23, "Hitch warning: %i msec frame time\n", timeTaken); Logger::Print(23, "Hitch warning: %i msec frame time\n", timeTaken);
@ -690,10 +734,19 @@ namespace Components
Script::LastFrameTime = nowMs; Script::LastFrameTime = nowMs;
}); });
Script::AddFunction("debugBox", [](Game::scr_entref_t) #ifdef _DEBUG
Script::AddFunction("DebugBox", [](Game::scr_entref_t)
{ {
MessageBoxA(nullptr, Game::Scr_GetString(0), "DEBUG", 0); const auto* message = Game::Scr_GetString(0);
if (message == nullptr)
{
Game::Scr_Error("^1DebugBox: Illegal parameter!\n");
}
MessageBoxA(nullptr, message, "DEBUG", MB_OK);
}, true); }, true);
#endif
Script::AddFunctions(); Script::AddFunctions();

View File

@ -29,8 +29,7 @@ namespace Components
static void OnVMShutdown(Utils::Slot<Scheduler::Callback> callback); static void OnVMShutdown(Utils::Slot<Scheduler::Callback> callback);
static Game::gentity_t* getEntFromEntRef(Game::scr_entref_t entref); static Game::client_t* GetClient(const Game::gentity_t* gentity);
static Game::client_t* getClientFromEnt(Game::gentity_t* gentity);
private: private:
static std::string ScriptName; static std::string ScriptName;
@ -51,6 +50,7 @@ namespace Components
static void FunctionError(); static void FunctionError();
static void StoreFunctionNameStub(); static void StoreFunctionNameStub();
static void RuntimeError(const char* codePos, unsigned int index, const char* msg, const char* dialogMessage);
static void StoreScriptName(const char* name); static void StoreScriptName(const char* name);
static void StoreScriptNameStub(); static void StoreScriptNameStub();
@ -64,7 +64,7 @@ namespace Components
static Game::scr_function_t GetFunction(void* caller, const char** name, int* isDev); static Game::scr_function_t GetFunction(void* caller, const char** name, int* isDev);
static void GetFunctionStub(); static void GetFunctionStub();
static void ScrShutdownSystemStub(int); static void ScrShutdownSystemStub(unsigned char sys);
static void StoreScriptBaseProgramNumStub(); static void StoreScriptBaseProgramNumStub();
static void StoreScriptBaseProgramNum(); static void StoreScriptBaseProgramNum();
static void Scr_PrintPrevCodePosStub(); static void Scr_PrintPrevCodePosStub();

View File

@ -0,0 +1,185 @@
#include <STDInclude.hpp>
namespace Components
{
const char* ScriptExtension::QueryStrings[] = { R"(..)", R"(../)", R"(..\)" };
void ScriptExtension::AddFunctions()
{
//File functions
Script::AddFunction("FileWrite", [](Game::scr_entref_t) // gsc: FileWrite(<filepath>, <string>, <mode>)
{
const auto* path = Game::Scr_GetString(0);
auto* text = Game::Scr_GetString(1);
auto* mode = Game::Scr_GetString(2);
if (path == nullptr)
{
Game::Scr_ParamError(0, "^1FileWrite: filepath is not defined!\n");
return;
}
if (text == nullptr || mode == nullptr)
{
Game::Scr_Error("^1FileWrite: Illegal parameters!\n");
return;
}
for (auto i = 0u; i < ARRAYSIZE(ScriptExtension::QueryStrings); ++i)
{
if (std::strstr(path, ScriptExtension::QueryStrings[i]) != nullptr)
{
Logger::Print("^1FileWrite: directory traversal is not allowed!\n");
return;
}
}
if (mode != "append"s && mode != "write"s)
{
Logger::Print("^3FileWrite: mode not defined or was wrong, defaulting to 'write'\n");
mode = "write";
}
if (mode == "write"s)
{
FileSystem::FileWriter(path).write(text);
}
else if (mode == "append"s)
{
FileSystem::FileWriter(path, true).write(text);
}
});
Script::AddFunction("FileRead", [](Game::scr_entref_t) // gsc: FileRead(<filepath>)
{
const auto* path = Game::Scr_GetString(0);
if (path == nullptr)
{
Game::Scr_ParamError(0, "^1FileRead: filepath is not defined!\n");
return;
}
for (auto i = 0u; i < ARRAYSIZE(ScriptExtension::QueryStrings); ++i)
{
if (std::strstr(path, ScriptExtension::QueryStrings[i]) != nullptr)
{
Logger::Print("^1FileRead: directory traversal is not allowed!\n");
return;
}
}
if (!FileSystem::FileReader(path).exists())
{
Logger::Print("^1FileRead: file not found!\n");
return;
}
Game::Scr_AddString(FileSystem::FileReader(path).getBuffer().data());
});
Script::AddFunction("FileExists", [](Game::scr_entref_t) // gsc: FileExists(<filepath>)
{
const auto* path = Game::Scr_GetString(0);
if (path == nullptr)
{
Game::Scr_ParamError(0, "^1FileExists: filepath is not defined!\n");
return;
}
for (auto i = 0u; i < ARRAYSIZE(ScriptExtension::QueryStrings); ++i)
{
if (std::strstr(path, ScriptExtension::QueryStrings[i]) != nullptr)
{
Logger::Print("^1FileExists: directory traversal is not allowed!\n");
return;
}
}
Game::Scr_AddInt(FileSystem::FileReader(path).exists());
});
Script::AddFunction("FileRemove", [](Game::scr_entref_t) // gsc: FileRemove(<filepath>)
{
const auto* path = Game::Scr_GetString(0);
if (path == nullptr)
{
Game::Scr_ParamError(0, "^1FileRemove: filepath is not defined!\n");
return;
}
for (auto i = 0u; i < ARRAYSIZE(ScriptExtension::QueryStrings); ++i)
{
if (std::strstr(path, ScriptExtension::QueryStrings[i]) != nullptr)
{
Logger::Print("^1FileRemove: directory traversal is not allowed!\n");
return;
}
}
const auto p = std::filesystem::path(path);
const auto& folder = p.parent_path().string();
const auto& file = p.filename().string();
Game::Scr_AddInt(FileSystem::DeleteFile(folder, file));
});
}
void ScriptExtension::AddMethods()
{
// ScriptExtension methods
Script::AddFunction("GetIp", [](Game::scr_entref_t entref) // gsc: self GetIp()
{
const auto* ent = Game::GetPlayerEntity(entref);
const auto* client = Script::GetClient(ent);
std::string ip = Game::NET_AdrToString(client->netchan.remoteAddress);
if (const auto pos = ip.find_first_of(":"); pos != std::string::npos)
ip.erase(ip.begin() + pos, ip.end()); // Erase port
Game::Scr_AddString(ip.data());
});
Script::AddFunction("GetPing", [](Game::scr_entref_t entref) // gsc: self GetPing()
{
const auto* ent = Game::GetPlayerEntity(entref);
const auto* client = Script::GetClient(ent);
Game::Scr_AddInt(client->ping);
});
}
void ScriptExtension::Scr_TableLookupIStringByRow()
{
if (Game::Scr_GetNumParam() < 3)
{
Game::Scr_Error("USAGE: tableLookupIStringByRow( filename, rowNum, returnValueColumnNum )\n");
return;
}
const auto* fileName = Game::Scr_GetString(0);
const auto rowNum = Game::Scr_GetInt(1);
const auto returnValueColumnNum = Game::Scr_GetInt(2);
const auto* table = Game::DB_FindXAssetHeader(Game::ASSET_TYPE_STRINGTABLE, fileName).stringTable;
if (table == nullptr)
{
Game::Scr_ParamError(0, Utils::String::VA("%s does not exist\n", fileName));
return;
}
const auto* value = Game::StringTable_GetColumnValueForRow(table, rowNum, returnValueColumnNum);
Game::Scr_AddIString(value);
}
ScriptExtension::ScriptExtension()
{
ScriptExtension::AddFunctions();
ScriptExtension::AddMethods();
// Correct builtin function pointer
Utils::Hook::Set<void(*)()>(0x79A90C, ScriptExtension::Scr_TableLookupIStringByRow);
}
}

View File

@ -0,0 +1,17 @@
#pragma once
namespace Components
{
class ScriptExtension : public Component
{
public:
ScriptExtension();
private:
static const char* QueryStrings[];
static void AddFunctions();
static void AddMethods();
static void Scr_TableLookupIStringByRow();
};
}

View File

@ -21,7 +21,7 @@ namespace Game
return result; return result;
} }
AddRefToObject_t AddRefToObject = AddRefToObject_t(0x61C360); AddRefToObject_t AddRefToObject = AddRefToObject_t(0x61C360);
AllocObject_t AllocObject = AllocObject_t(0x434320); AllocObject_t AllocObject = AllocObject_t(0x434320);
@ -73,6 +73,7 @@ namespace Game
Com_MatchToken_t Com_MatchToken = Com_MatchToken_t(0x447130); Com_MatchToken_t Com_MatchToken = Com_MatchToken_t(0x447130);
Com_SetSlowMotion_t Com_SetSlowMotion = Com_SetSlowMotion_t(0x446E20); Com_SetSlowMotion_t Com_SetSlowMotion = Com_SetSlowMotion_t(0x446E20);
Com_Quitf_t Com_Quit_f = Com_Quitf_t(0x4D4000); Com_Quitf_t Com_Quit_f = Com_Quitf_t(0x4D4000);
Com_PrintWarning_t Com_PrintWarning = Com_PrintWarning_t(0x4E0200);
Con_DrawMiniConsole_t Con_DrawMiniConsole = Con_DrawMiniConsole_t(0x464F30); Con_DrawMiniConsole_t Con_DrawMiniConsole = Con_DrawMiniConsole_t(0x464F30);
Con_DrawSolidConsole_t Con_DrawSolidConsole = Con_DrawSolidConsole_t(0x5A5040); Con_DrawSolidConsole_t Con_DrawSolidConsole = Con_DrawSolidConsole_t(0x5A5040);
@ -259,6 +260,8 @@ namespace Game
Scr_GetFunctionHandle_t Scr_GetFunctionHandle = Scr_GetFunctionHandle_t(0x4234F0); Scr_GetFunctionHandle_t Scr_GetFunctionHandle = Scr_GetFunctionHandle_t(0x4234F0);
Scr_GetString_t Scr_GetString = Scr_GetString_t(0x425900); Scr_GetString_t Scr_GetString = Scr_GetString_t(0x425900);
Scr_GetConstString_t Scr_GetConstString = Scr_GetConstString_t(0x494830);
Scr_GetDebugString_t Scr_GetDebugString = Scr_GetDebugString_t(0x4EBF50);
Scr_GetFloat_t Scr_GetFloat = Scr_GetFloat_t(0x443140); Scr_GetFloat_t Scr_GetFloat = Scr_GetFloat_t(0x443140);
Scr_GetInt_t Scr_GetInt = Scr_GetInt_t(0x4F31D0); Scr_GetInt_t Scr_GetInt = Scr_GetInt_t(0x4F31D0);
Scr_GetObject_t Scr_GetObject = Scr_GetObject_t(0x462100); Scr_GetObject_t Scr_GetObject = Scr_GetObject_t(0x462100);
@ -269,16 +272,24 @@ namespace Game
Scr_AddEntity_t Scr_AddEntity = Scr_AddEntity_t(0x4BFB40); Scr_AddEntity_t Scr_AddEntity = Scr_AddEntity_t(0x4BFB40);
Scr_AddString_t Scr_AddString = Scr_AddString_t(0x412310); Scr_AddString_t Scr_AddString = Scr_AddString_t(0x412310);
Scr_AddIString_t Scr_AddIString = Scr_AddIString_t(0x455F20);
Scr_AddInt_t Scr_AddInt = Scr_AddInt_t(0x41D7D0); Scr_AddInt_t Scr_AddInt = Scr_AddInt_t(0x41D7D0);
Scr_AddFloat_t Scr_AddFloat = Scr_AddFloat_t(0x61E860); Scr_AddFloat_t Scr_AddFloat = Scr_AddFloat_t(0x61E860);
Scr_AddObject_t Scr_AddObject = Scr_AddObject_t(0x430F40); Scr_AddObject_t Scr_AddObject = Scr_AddObject_t(0x430F40);
Scr_Notify_t Scr_Notify = Scr_Notify_t(0x4A4750); Scr_Notify_t Scr_Notify = Scr_Notify_t(0x4A4750);
Scr_NotifyLevel_t Scr_NotifyLevel = Scr_NotifyLevel_t(0x4D9C30); Scr_NotifyLevel_t Scr_NotifyLevel = Scr_NotifyLevel_t(0x4D9C30);
Scr_Error_t Scr_Error = Scr_Error_t(0x61E8B0); Scr_Error_t Scr_Error = Scr_Error_t(0x61E8B0);
Scr_ObjectError_t Scr_ObjectError = Scr_ObjectError_t(0x42EF40);
Scr_ParamError_t Scr_ParamError = Scr_ParamError_t(0x4FBC70);
Scr_GetType_t Scr_GetType = Scr_GetType_t(0x422900); Scr_GetType_t Scr_GetType = Scr_GetType_t(0x422900);
Scr_ClearOutParams_t Scr_ClearOutParams = Scr_ClearOutParams_t(0x4386E0); Scr_ClearOutParams_t Scr_ClearOutParams = Scr_ClearOutParams_t(0x4386E0);
GetEntity_t GetEntity = GetEntity_t(0x4BC270);
GetPlayerEntity_t GetPlayerEntity = GetPlayerEntity_t(0x49C4A0);
Scr_RegisterFunction_t Scr_RegisterFunction = Scr_RegisterFunction_t(0x492D50); Scr_RegisterFunction_t Scr_RegisterFunction = Scr_RegisterFunction_t(0x492D50);
Scr_ShutdownAllocNode_t Scr_ShutdownAllocNode = Scr_ShutdownAllocNode_t(0x441650); Scr_ShutdownAllocNode_t Scr_ShutdownAllocNode = Scr_ShutdownAllocNode_t(0x441650);
Scr_IsSystemActive_t Scr_IsSystemActive = Scr_IsSystemActive_t(0x4B24E0); Scr_IsSystemActive_t Scr_IsSystemActive = Scr_IsSystemActive_t(0x4B24E0);
@ -311,9 +322,11 @@ namespace Game
Steam_JoinLobby_t Steam_JoinLobby = Steam_JoinLobby_t(0x49CF70); Steam_JoinLobby_t Steam_JoinLobby = Steam_JoinLobby_t(0x49CF70);
StringTable_Lookup_t StringTable_Lookup = StringTable_Lookup_t(0x42F0E0); StringTable_Lookup_t StringTable_Lookup = StringTable_Lookup_t(0x42F0E0);
StringTable_GetColumnValueForRow_t StringTable_GetColumnValueForRow = StringTable_GetColumnValueForRow_t(0x4F2C80);
StringTable_HashString_t StringTable_HashString = StringTable_HashString_t(0x475EB0); StringTable_HashString_t StringTable_HashString = StringTable_HashString_t(0x475EB0);
SV_AddTestClient_t SV_AddTestClient = SV_AddTestClient_t(0x48AD30); SV_AddTestClient_t SV_AddTestClient = SV_AddTestClient_t(0x48AD30);
SV_IsTestClient_t SV_IsTestClient = SV_IsTestClient_t(0x4D6E40);
SV_GameClientNum_Score_t SV_GameClientNum_Score = SV_GameClientNum_Score_t(0x469AC0); SV_GameClientNum_Score_t SV_GameClientNum_Score = SV_GameClientNum_Score_t(0x469AC0);
SV_GameSendServerCommand_t SV_GameSendServerCommand = SV_GameSendServerCommand_t(0x4BC3A0); SV_GameSendServerCommand_t SV_GameSendServerCommand = SV_GameSendServerCommand_t(0x4BC3A0);
SV_Cmd_TokenizeString_t SV_Cmd_TokenizeString = SV_Cmd_TokenizeString_t(0x4B5780); SV_Cmd_TokenizeString_t SV_Cmd_TokenizeString = SV_Cmd_TokenizeString_t(0x4B5780);
@ -326,7 +339,6 @@ namespace Game
SV_GetPlayerByName_t SV_GetPlayerByName = SV_GetPlayerByName_t(0x6242B0); SV_GetPlayerByName_t SV_GetPlayerByName = SV_GetPlayerByName_t(0x6242B0);
SV_GetPlayerByNum_t SV_GetPlayerByNum = SV_GetPlayerByNum_t(0x624390); SV_GetPlayerByNum_t SV_GetPlayerByNum = SV_GetPlayerByNum_t(0x624390);
Sys_Error_t Sys_Error = Sys_Error_t(0x4E0200);
Sys_FreeFileList_t Sys_FreeFileList = Sys_FreeFileList_t(0x4D8580); Sys_FreeFileList_t Sys_FreeFileList = Sys_FreeFileList_t(0x4D8580);
Sys_IsDatabaseReady_t Sys_IsDatabaseReady = Sys_IsDatabaseReady_t(0x4CA4A0); Sys_IsDatabaseReady_t Sys_IsDatabaseReady = Sys_IsDatabaseReady_t(0x4CA4A0);
Sys_IsDatabaseReady2_t Sys_IsDatabaseReady2 = Sys_IsDatabaseReady2_t(0x441280); Sys_IsDatabaseReady2_t Sys_IsDatabaseReady2 = Sys_IsDatabaseReady2_t(0x441280);
@ -339,6 +351,7 @@ namespace Game
Sys_SuspendOtherThreads_t Sys_SuspendOtherThreads = Sys_SuspendOtherThreads_t(0x45A190); Sys_SuspendOtherThreads_t Sys_SuspendOtherThreads = Sys_SuspendOtherThreads_t(0x45A190);
Sys_ListFiles_t Sys_ListFiles = Sys_ListFiles_t(0x45A660); Sys_ListFiles_t Sys_ListFiles = Sys_ListFiles_t(0x45A660);
Sys_Milliseconds_t Sys_Milliseconds = Sys_Milliseconds_t(0x42A660); Sys_Milliseconds_t Sys_Milliseconds = Sys_Milliseconds_t(0x42A660);
Sys_Error_t Sys_Error = Sys_Error_t(0x43D570);
Sys_LockWrite_t Sys_LockWrite = Sys_LockWrite_t(0x435880); Sys_LockWrite_t Sys_LockWrite = Sys_LockWrite_t(0x435880);
Sys_TempPriorityAtLeastNormalBegin_t Sys_TempPriorityAtLeastNormalBegin = Sys_TempPriorityAtLeastNormalBegin_t(0x478680); Sys_TempPriorityAtLeastNormalBegin_t Sys_TempPriorityAtLeastNormalBegin = Sys_TempPriorityAtLeastNormalBegin_t(0x478680);
Sys_TempPriorityEnd_t Sys_TempPriorityEnd = Sys_TempPriorityEnd_t(0x4DCF00); Sys_TempPriorityEnd_t Sys_TempPriorityEnd = Sys_TempPriorityEnd_t(0x4DCF00);
@ -436,6 +449,8 @@ namespace Game
gentity_t* g_entities = reinterpret_cast<gentity_t*>(0x18835D8); gentity_t* g_entities = reinterpret_cast<gentity_t*>(0x18835D8);
int* level_scriptPrintChannel = reinterpret_cast<int*>(0x1A860FC);
netadr_t* connectedHost = reinterpret_cast<netadr_t*>(0xA1E888); netadr_t* connectedHost = reinterpret_cast<netadr_t*>(0xA1E888);
SOCKET* ip_socket = reinterpret_cast<SOCKET*>(0x64A3008); SOCKET* ip_socket = reinterpret_cast<SOCKET*>(0x64A3008);
@ -473,6 +488,7 @@ namespace Game
unsigned short* db_hashTable = reinterpret_cast<unsigned short*>(0x12412B0); unsigned short* db_hashTable = reinterpret_cast<unsigned short*>(0x12412B0);
scrVmPub_t* scrVmPub = reinterpret_cast<scrVmPub_t*>(0x2040CF0); scrVmPub_t* scrVmPub = reinterpret_cast<scrVmPub_t*>(0x2040CF0);
scrVarPub_t* scrVarPub = reinterpret_cast<scrVarPub_t*>(0x201A408);
clientstate_t* clcState = reinterpret_cast<clientstate_t*>(0xB2C540); clientstate_t* clcState = reinterpret_cast<clientstate_t*>(0xB2C540);
@ -713,14 +729,26 @@ namespace Game
SV_KickClient(client, reason.data()); SV_KickClient(client, reason.data());
} }
void Scr_iPrintLn(int clientNum, const std::string& message) void IncInParam()
{ {
Game::SV_GameSendServerCommand(clientNum, 0, Utils::String::VA("%c \"%s\"", 0x66, message.data())); Scr_ClearOutParams();
if (scrVmPub->top == scrVmPub->maxStack)
{
Sys_Error("Internal script stack overflow");
}
scrVmPub->top++;
scrVmPub->inparamcount++;
} }
void Scr_iPrintLnBold(int clientNum, const std::string& message) void Scr_AddBool(int value)
{ {
Game::SV_GameSendServerCommand(clientNum, 0, Utils::String::VA("%c \"%s\"", 0x67, message.data())); assert(value == 0 || value == 1);
IncInParam();
scrVmPub->top->type = VAR_INTEGER;
scrVmPub->top->u.intValue = value;
} }
int FS_FOpenFileReadCurrentThread(const char* file, int* fh) int FS_FOpenFileReadCurrentThread(const char* file, int* fh)
@ -1207,6 +1235,27 @@ namespace Game
} }
} }
__declspec(naked) void RuntimeErrorInternal(int /*channel*/, const char* /*codePos*/, unsigned int /*index*/, const char* /*msg*/)
{
__asm
{
pushad
mov eax, [esp + 0x10 + 0x20] // msg
mov edi, [esp + 0x4 + 0x20] // channel
push [esp + 0xC + 0x20] // index
push [esp + 0xC + 0x20] // codePos
mov edx, 0x61ABE0
call edx
add esp, 0x8
popad
ret
}
}
__declspec(naked) void IN_KeyUp(kbutton_t* /*button*/) __declspec(naked) void IN_KeyUp(kbutton_t* /*button*/)
{ {
__asm __asm
@ -1540,5 +1589,20 @@ namespace Game
} }
} }
constexpr auto SV_BotUserMove_Addr = 0x626E50;
__declspec(naked) void SV_BotUserMove(client_t* /*client*/)
{
__asm
{
pushad
mov edi, [esp + 0x20 + 0x4]
call SV_BotUserMove_Addr
popad
ret
}
}
#pragma optimize("", on) #pragma optimize("", on)
} }

View File

@ -154,6 +154,9 @@ namespace Game
typedef void(__cdecl * Com_Quitf_t)(); typedef void(__cdecl * Com_Quitf_t)();
extern Com_Quitf_t Com_Quit_f; extern Com_Quitf_t Com_Quit_f;
typedef void(__cdecl * Com_PrintWarning_t)(int, const char*, ...);
extern Com_PrintWarning_t Com_PrintWarning;
typedef char* (__cdecl * Con_DrawMiniConsole_t)(int localClientNum, int xPos, int yPos, float alpha); typedef char* (__cdecl * Con_DrawMiniConsole_t)(int localClientNum, int xPos, int yPos, float alpha);
extern Con_DrawMiniConsole_t Con_DrawMiniConsole; extern Con_DrawMiniConsole_t Con_DrawMiniConsole;
@ -368,7 +371,7 @@ namespace Game
typedef int(__cdecl* FS_Delete_t)(const char* fileName); typedef int(__cdecl* FS_Delete_t)(const char* fileName);
extern FS_Delete_t FS_Delete; extern FS_Delete_t FS_Delete;
typedef int(__cdecl* G_GetWeaponIndexForName_t)(char*); typedef unsigned int(__cdecl * G_GetWeaponIndexForName_t)(const char*);
extern G_GetWeaponIndexForName_t G_GetWeaponIndexForName; extern G_GetWeaponIndexForName_t G_GetWeaponIndexForName;
typedef void(__cdecl* G_SpawnEntitiesFromString_t)(); typedef void(__cdecl* G_SpawnEntitiesFromString_t)();
@ -555,7 +558,7 @@ namespace Game
typedef bool(__cdecl * NET_IsLocalAddress_t)(netadr_t adr); typedef bool(__cdecl * NET_IsLocalAddress_t)(netadr_t adr);
extern NET_IsLocalAddress_t NET_IsLocalAddress; extern NET_IsLocalAddress_t NET_IsLocalAddress;
typedef bool(__cdecl * NET_StringToAdr_t)(const char *s, netadr_t *a); typedef int(__cdecl * NET_StringToAdr_t)(const char *s, netadr_t *a);
extern NET_StringToAdr_t NET_StringToAdr; extern NET_StringToAdr_t NET_StringToAdr;
typedef void(__cdecl * NET_OutOfBandPrint_t)(netsrc_t sock, netadr_t adr, const char *data); typedef void(__cdecl * NET_OutOfBandPrint_t)(netsrc_t sock, netadr_t adr, const char *data);
@ -636,16 +639,19 @@ namespace Game
typedef void(__cdecl * RemoveRefToObject_t)(unsigned int id); typedef void(__cdecl * RemoveRefToObject_t)(unsigned int id);
extern RemoveRefToObject_t RemoveRefToObject; extern RemoveRefToObject_t RemoveRefToObject;
typedef void(__cdecl * Scr_AddEntity_t)(gentity_s const*); typedef void(__cdecl * Scr_AddEntity_t)(const gentity_s* ent);
extern Scr_AddEntity_t Scr_AddEntity; extern Scr_AddEntity_t Scr_AddEntity;
typedef void(__cdecl * Scr_AddString_t)(const char* str); typedef void(__cdecl * Scr_AddString_t)(const char* value);
extern Scr_AddString_t Scr_AddString; extern Scr_AddString_t Scr_AddString;
typedef void(__cdecl * Scr_AddInt_t)(int num); typedef void(__cdecl * Scr_AddIString_t)(const char* value);
extern Scr_AddIString_t Scr_AddIString;
typedef void(__cdecl * Scr_AddInt_t)(int value);
extern Scr_AddInt_t Scr_AddInt; extern Scr_AddInt_t Scr_AddInt;
typedef void(__cdecl * Scr_AddFloat_t)(float); typedef void(__cdecl * Scr_AddFloat_t)(float value);
extern Scr_AddFloat_t Scr_AddFloat; extern Scr_AddFloat_t Scr_AddFloat;
typedef void(__cdecl * Scr_AddObject_t)(unsigned int id); typedef void(__cdecl * Scr_AddObject_t)(unsigned int id);
@ -660,22 +666,28 @@ namespace Game
typedef int(__cdecl * Scr_LoadScript_t)(const char*); typedef int(__cdecl * Scr_LoadScript_t)(const char*);
extern Scr_LoadScript_t Scr_LoadScript; extern Scr_LoadScript_t Scr_LoadScript;
typedef char* (__cdecl * Scr_GetString_t)(int); typedef const char*(__cdecl * Scr_GetString_t)(unsigned int index);
extern Scr_GetString_t Scr_GetString; extern Scr_GetString_t Scr_GetString;
typedef float(__cdecl * Scr_GetFloat_t)(int); typedef unsigned int(__cdecl * Scr_GetConstString_t)(unsigned int index);
extern Scr_GetConstString_t Scr_GetConstString;
typedef const char*(__cdecl * Scr_GetDebugString_t)(unsigned int index);
extern Scr_GetDebugString_t Scr_GetDebugString;
typedef float(__cdecl * Scr_GetFloat_t)(unsigned int index);
extern Scr_GetFloat_t Scr_GetFloat; extern Scr_GetFloat_t Scr_GetFloat;
typedef int(__cdecl * Scr_GetInt_t)(int); typedef int(__cdecl * Scr_GetInt_t)(unsigned int index);
extern Scr_GetInt_t Scr_GetInt; extern Scr_GetInt_t Scr_GetInt;
typedef unsigned int(__cdecl * Scr_GetObject_t)(int); typedef unsigned int(__cdecl * Scr_GetObject_t)(unsigned int index);
extern Scr_GetObject_t Scr_GetObject; extern Scr_GetObject_t Scr_GetObject;
typedef unsigned int(__cdecl * Scr_GetNumParam_t)(); typedef unsigned int(__cdecl * Scr_GetNumParam_t)();
extern Scr_GetNumParam_t Scr_GetNumParam; extern Scr_GetNumParam_t Scr_GetNumParam;
typedef int(__cdecl * Scr_GetFunctionHandle_t)(const char*, const char*); typedef int(__cdecl * Scr_GetFunctionHandle_t)(const char* filename, const char* name);
extern Scr_GetFunctionHandle_t Scr_GetFunctionHandle; extern Scr_GetFunctionHandle_t Scr_GetFunctionHandle;
typedef int(__cdecl * Scr_ExecThread_t)(int, int); typedef int(__cdecl * Scr_ExecThread_t)(int, int);
@ -699,12 +711,24 @@ namespace Game
typedef bool(__cdecl * Scr_IsSystemActive_t)(); typedef bool(__cdecl * Scr_IsSystemActive_t)();
extern Scr_IsSystemActive_t Scr_IsSystemActive; extern Scr_IsSystemActive_t Scr_IsSystemActive;
typedef int(__cdecl* Scr_GetType_t)(unsigned int); typedef int(__cdecl * Scr_GetType_t)(unsigned int);
extern Scr_GetType_t Scr_GetType; extern Scr_GetType_t Scr_GetType;
typedef void(__cdecl* Scr_Error_t)(const char*); typedef void(__cdecl * Scr_Error_t)(const char*);
extern Scr_Error_t Scr_Error; extern Scr_Error_t Scr_Error;
typedef void(__cdecl * Scr_ObjectError_t)(const char*);
extern Scr_ObjectError_t Scr_ObjectError;
typedef void(__cdecl * Scr_ParamError_t)(unsigned int paramIndex, const char*);
extern Scr_ParamError_t Scr_ParamError;
typedef gentity_s*(__cdecl * GetPlayerEntity_t)(scr_entref_t entref);
extern GetPlayerEntity_t GetPlayerEntity;
typedef gentity_s*(__cdecl * GetEntity_t)(scr_entref_t entref);
extern GetEntity_t GetEntity;
typedef script_t* (__cdecl * Script_Alloc_t)(int length); typedef script_t* (__cdecl * Script_Alloc_t)(int length);
extern Script_Alloc_t Script_Alloc; extern Script_Alloc_t Script_Alloc;
@ -741,15 +765,21 @@ namespace Game
typedef void(__cdecl * Steam_JoinLobby_t)(SteamID, char); typedef void(__cdecl * Steam_JoinLobby_t)(SteamID, char);
extern Steam_JoinLobby_t Steam_JoinLobby; extern Steam_JoinLobby_t Steam_JoinLobby;
typedef const char*(__cdecl * StringTable_Lookup_t)(StringTable *table, const int comparisonColumn, const char *value, const int valueColumn); typedef const char*(__cdecl * StringTable_Lookup_t)(const StringTable *table, const int comparisonColumn, const char *value, const int valueColumn);
extern StringTable_Lookup_t StringTable_Lookup; extern StringTable_Lookup_t StringTable_Lookup;
typedef const char* (__cdecl * StringTable_GetColumnValueForRow_t)(const StringTable* table, int, int column);
extern StringTable_GetColumnValueForRow_t StringTable_GetColumnValueForRow;
typedef int(__cdecl * StringTable_HashString_t)(const char* string); typedef int(__cdecl * StringTable_HashString_t)(const char* string);
extern StringTable_HashString_t StringTable_HashString; extern StringTable_HashString_t StringTable_HashString;
typedef gentity_t*(__cdecl* SV_AddTestClient_t)(); typedef gentity_t*(__cdecl* SV_AddTestClient_t)();
extern SV_AddTestClient_t SV_AddTestClient; extern SV_AddTestClient_t SV_AddTestClient;
typedef int(__cdecl * SV_IsTestClient_t)(int clientNum);
extern SV_IsTestClient_t SV_IsTestClient;
typedef int(__cdecl* SV_GameClientNum_Score_t)(int clientID); typedef int(__cdecl* SV_GameClientNum_Score_t)(int clientID);
extern SV_GameClientNum_Score_t SV_GameClientNum_Score; extern SV_GameClientNum_Score_t SV_GameClientNum_Score;
@ -783,7 +813,7 @@ namespace Game
typedef client_t*(__cdecl * SV_GetPlayerByNum_t)(); typedef client_t*(__cdecl * SV_GetPlayerByNum_t)();
extern SV_GetPlayerByNum_t SV_GetPlayerByNum; extern SV_GetPlayerByNum_t SV_GetPlayerByNum;
typedef int(__cdecl * Sys_Error_t)(int, char *, ...); typedef void(__cdecl * Sys_Error_t)(const char* error, ...);
extern Sys_Error_t Sys_Error; extern Sys_Error_t Sys_Error;
typedef void(__cdecl * Sys_FreeFileList_t)(char** list); typedef void(__cdecl * Sys_FreeFileList_t)(char** list);
@ -976,6 +1006,8 @@ namespace Game
constexpr auto ENTITYNUM_NONE = MAX_GENTITIES - 1; constexpr auto ENTITYNUM_NONE = MAX_GENTITIES - 1;
extern gentity_t* g_entities; extern gentity_t* g_entities;
extern int* level_scriptPrintChannel;
extern netadr_t* connectedHost; extern netadr_t* connectedHost;
extern SOCKET* ip_socket; extern SOCKET* ip_socket;
@ -1012,6 +1044,7 @@ namespace Game
extern unsigned short* db_hashTable; extern unsigned short* db_hashTable;
extern scrVmPub_t* scrVmPub; extern scrVmPub_t* scrVmPub;
extern scrVarPub_t* scrVarPub;
extern clientstate_t* clcState; extern clientstate_t* clcState;
@ -1080,10 +1113,13 @@ namespace Game
void SV_KickClient(client_t* client, const char* reason); void SV_KickClient(client_t* client, const char* reason);
void SV_KickClientError(client_t* client, const std::string& reason); void SV_KickClientError(client_t* client, const std::string& reason);
void SV_BotUserMove(client_t* client);
void RuntimeErrorInternal(int channel, const char* codePos, unsigned int index, const char* msg);
void IncInParam();
void Scr_iPrintLn(int clientNum, const std::string& message);
void Scr_iPrintLnBold(int clientNum, const std::string& message);
void Scr_NotifyId(unsigned int id, unsigned __int16 stringValue, unsigned int paramcount); void Scr_NotifyId(unsigned int id, unsigned __int16 stringValue, unsigned int paramcount);
void Scr_AddBool(int value);
void IN_KeyUp(kbutton_t* button); void IN_KeyUp(kbutton_t* button);
void IN_KeyDown(kbutton_t* button); void IN_KeyDown(kbutton_t* button);

View File

@ -20,7 +20,12 @@ namespace Game
typedef vec_t vec3_t[3]; typedef vec_t vec3_t[3];
typedef vec_t vec4_t[4]; typedef vec_t vec4_t[4];
typedef unsigned int scr_entref_t; struct scr_entref_t
{
unsigned __int16 entnum;
unsigned __int16 classnum;
};
typedef void(__cdecl * scr_function_t)(scr_entref_t); typedef void(__cdecl * scr_function_t)(scr_entref_t);
enum XAssetType enum XAssetType
@ -1451,21 +1456,23 @@ namespace Game
enum usercmdButtonBits enum usercmdButtonBits
{ {
CMD_BUTTON_ATTACK = 0x1, CMD_BUTTON_ATTACK = 0x1,
CMD_BUTTON_SPRINT = 0x2, CMD_BUTTON_SPRINT = 0x2,
CMD_BUTTON_MELEE = 0x4, CMD_BUTTON_MELEE = 0x4,
CMD_BUTTON_ACTIVATE = 0x8, CMD_BUTTON_ACTIVATE = 0x8,
CMD_BUTTON_RELOAD = 0x10, CMD_BUTTON_RELOAD = 0x10,
CMD_BUTTON_USE_RELOAD = 0x20, CMD_BUTTON_USE_RELOAD = 0x20,
CMD_BUTTON_PRONE = 0x100, CMD_BUTTON_LEAN_LEFT = 0x40,
CMD_BUTTON_CROUCH = 0x200, CMD_BUTTON_LEAN_RIGHT = 0x80,
CMD_BUTTON_UP = 0x400, CMD_BUTTON_PRONE = 0x100,
CMD_BUTTON_ADS = 0x800, CMD_BUTTON_CROUCH = 0x200,
CMD_BUTTON_DOWN = 0x1000, CMD_BUTTON_UP = 0x400,
CMD_BUTTON_BREATH = 0x2000, CMD_BUTTON_ADS = 0x800,
CMD_BUTTON_FRAG = 0x4000, CMD_BUTTON_DOWN = 0x1000,
CMD_BUTTON_OFFHAND_SECONDARY = 0x8000, CMD_BUTTON_BREATH = 0x2000,
CMD_BUTTON_THROW = 0x80000, CMD_BUTTON_FRAG = 0x4000,
CMD_BUTTON_OFFHAND_SECONDARY = 0x8000,
CMD_BUTTON_THROW = 0x80000
}; };
#pragma pack(push, 4) #pragma pack(push, 4)
@ -4999,6 +5006,21 @@ namespace Game
int dataCount; int dataCount;
} gameState; } gameState;
struct HunkUser
{
HunkUser* current;
HunkUser* next;
int maxSize;
int end;
int pos;
const char* name;
bool fixed;
int type;
char buf[1];
};
static_assert(sizeof(HunkUser) == 36);
struct VariableStackBuffer struct VariableStackBuffer
{ {
const char *pos; const char *pos;
@ -5094,6 +5116,40 @@ namespace Game
VariableValue stack[2048]; VariableValue stack[2048];
}; };
struct scrVarPub_t
{
const char* fieldBuffer;
unsigned __int16 canonicalStrCount;
bool developer_script;
bool evaluate;
const char* error_message;
int error_index;
int time;
int timeArrayId;
int pauseArrayId;
int notifyArrayId;
int objectStackId;
int levelId;
int gameId;
int animId;
int freeEntList;
int tempVariable;
int numScriptValues[2];
bool bInited;
unsigned __int16 savecount;
unsigned __int16 savecountMark;
int checksum;
int entId;
int entFieldName;
HunkUser* programHunkUser;
const char* programBuffer;
const char* endScriptBuffer;
unsigned __int16 saveIdMap[36864];
unsigned __int16 saveIdMapRev[36864];
};
static_assert(sizeof(scrVarPub_t) == 0x24060);
enum UILocalVarType enum UILocalVarType
{ {
UILOCALVAR_INT = 0x0, UILOCALVAR_INT = 0x0,
@ -5661,7 +5717,7 @@ namespace Game
int pureAuthentic; // 135896 int pureAuthentic; // 135896
char __pad7[133138]; // 135900 char __pad7[133138]; // 135900
short scriptID; // 269038 short scriptID; // 269038
int isBot; // 269040 int bIsTestClient; // 269040
int serverID; // 269044 int serverID; // 269044
char __pad8[9224]; // 269048 char __pad8[9224]; // 269048
unsigned __int64 steamID; // 278272 unsigned __int64 steamID; // 278272

View File

@ -81,6 +81,7 @@ namespace Utils
std::vector<std::string> Split(const std::string& str, const char delim); std::vector<std::string> Split(const std::string& str, const char delim);
void Replace(std::string& string, const std::string& find, const std::string& replace); void Replace(std::string& string, const std::string& find, const std::string& replace);
bool StartsWith(const std::string& haystack, const std::string& needle); bool StartsWith(const std::string& haystack, const std::string& needle);
std::string& LTrim(std::string& str); std::string& LTrim(std::string& str);
std::string& RTrim(std::string& str); std::string& RTrim(std::string& str);
std::string& Trim(std::string& str); std::string& Trim(std::string& str);