Merge pull request #162 from diamante0018/refactor-scripts-stuff
Refactor script related funcs/modules
This commit is contained in:
commit
7afd9be6ce
@ -104,8 +104,7 @@ namespace Components
|
||||
Loader::Register(new Movement());
|
||||
Loader::Register(new Elevators());
|
||||
Loader::Register(new ClientCommand());
|
||||
|
||||
Loader::Register(new Client());
|
||||
Loader::Register(new ScriptExtension());
|
||||
|
||||
Loader::Pregame = false;
|
||||
}
|
||||
|
@ -136,4 +136,4 @@ namespace Components
|
||||
#include "Modules/ClientCommand.hpp"
|
||||
|
||||
#include "Modules/Gamepad.hpp"
|
||||
#include "Modules/Client.hpp"
|
||||
#include "Modules/ScriptExtension.hpp"
|
||||
|
@ -520,7 +520,7 @@ namespace Components
|
||||
{
|
||||
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();
|
||||
|
@ -1,104 +1,61 @@
|
||||
#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
|
||||
{
|
||||
std::vector<std::string> Bots::BotNames;
|
||||
|
||||
typedef struct BotMovementInfo_t
|
||||
struct BotMovementInfo
|
||||
{
|
||||
/* Actions */
|
||||
int buttons;
|
||||
/* Movement */
|
||||
int8 forward;
|
||||
int8 right;
|
||||
/* Weapon */
|
||||
unsigned short weapon;
|
||||
} BotMovementInfo_t;
|
||||
int buttons; // Actions
|
||||
int8_t forward;
|
||||
int8_t right;
|
||||
uint16_t weapon;
|
||||
bool active;
|
||||
};
|
||||
|
||||
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;
|
||||
};
|
||||
|
||||
static const BotAction_t BotActions[] =
|
||||
static const BotAction BotActions[] =
|
||||
{
|
||||
{ "gostand", KEY_MASK_JUMP },
|
||||
{ "gocrouch", KEY_MASK_CROUCH },
|
||||
{ "goprone", KEY_MASK_PRONE },
|
||||
{ "fire", KEY_MASK_FIRE },
|
||||
{ "melee", KEY_MASK_MELEE },
|
||||
{ "frag", KEY_MASK_FRAG },
|
||||
{ "smoke", KEY_MASK_SMOKE },
|
||||
{ "reload", KEY_MASK_RELOAD },
|
||||
{ "sprint", KEY_MASK_SPRINT },
|
||||
{ "leanleft", KEY_MASK_LEANLEFT },
|
||||
{ "leanright", KEY_MASK_LEANRIGHT },
|
||||
{ "ads", KEY_MASK_ADS_MODE },
|
||||
{ "holdbreath", KEY_MASK_HOLDBREATH },
|
||||
{ "use", KEY_MASK_USE },
|
||||
{ "0", 8 },
|
||||
{ "1", 32 },
|
||||
{ "2", 65536 },
|
||||
{ "3", 131072 },
|
||||
{ "4", 1048576 },
|
||||
{ "5", 2097152 },
|
||||
{ "6", 4194304 },
|
||||
{ "7", 8388608 },
|
||||
{ "8", 16777216 },
|
||||
{ "9", 33554432 },
|
||||
{ "gostand", Game::usercmdButtonBits::CMD_BUTTON_UP },
|
||||
{ "gocrouch", Game::usercmdButtonBits::CMD_BUTTON_CROUCH },
|
||||
{ "goprone", Game::usercmdButtonBits::CMD_BUTTON_PRONE },
|
||||
{ "fire", Game::usercmdButtonBits::CMD_BUTTON_ATTACK },
|
||||
{ "melee", Game::usercmdButtonBits::CMD_BUTTON_MELEE },
|
||||
{ "frag", Game::usercmdButtonBits::CMD_BUTTON_FRAG },
|
||||
{ "smoke", Game::usercmdButtonBits::CMD_BUTTON_OFFHAND_SECONDARY },
|
||||
{ "reload", Game::usercmdButtonBits::CMD_BUTTON_RELOAD },
|
||||
{ "sprint", Game::usercmdButtonBits::CMD_BUTTON_SPRINT },
|
||||
{ "leanleft", Game::usercmdButtonBits::CMD_BUTTON_LEAN_LEFT },
|
||||
{ "leanright", Game::usercmdButtonBits::CMD_BUTTON_LEAN_RIGHT },
|
||||
{ "ads", Game::usercmdButtonBits::CMD_BUTTON_ADS },
|
||||
{ "holdbreath", Game::usercmdButtonBits::CMD_BUTTON_BREATH },
|
||||
{ "usereload", Game::usercmdButtonBits::CMD_BUTTON_USE_RELOAD },
|
||||
{ "activate", Game::usercmdButtonBits::CMD_BUTTON_ACTIVATE },
|
||||
};
|
||||
|
||||
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;
|
||||
|
||||
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;
|
||||
static size_t botId = 0;
|
||||
static bool loadedNames = false; // Load file only once
|
||||
const char* botName;
|
||||
|
||||
if (Bots::BotNames.empty())
|
||||
if (Bots::BotNames.empty() && !loadedNames)
|
||||
{
|
||||
FileSystem::File bots("bots.txt");
|
||||
loadedNames = true;
|
||||
|
||||
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", "");
|
||||
name = Utils::String::Trim(name);
|
||||
@ -121,276 +78,241 @@ namespace Components
|
||||
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)
|
||||
{
|
||||
for (unsigned int i = 0; i < count; ++i)
|
||||
for (auto i = 0u; i < count; ++i)
|
||||
{
|
||||
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();
|
||||
if (entRef)
|
||||
Game::Scr_AddString("autoassign");
|
||||
Game::Scr_AddString("team_marinesopfor");
|
||||
Game::Scr_Notify(ent, Game::SL_GetString("menuresponse", 0), 2);
|
||||
|
||||
Scheduler::OnDelay([ent]()
|
||||
{
|
||||
Scheduler::OnDelay([entRef]()
|
||||
{
|
||||
Game::Scr_AddString("autoassign");
|
||||
Game::Scr_AddString("team_marinesopfor");
|
||||
Game::Scr_Notify(entRef, Game::SL_GetString("menuresponse", 0), 2);
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
Game::Scr_AddString(Utils::String::VA("class%u", Utils::Cryptography::Rand::GenerateInt() % 5u));
|
||||
Game::Scr_AddString("changeclass");
|
||||
Game::Scr_Notify(ent, Game::SL_GetString("menuresponse", 0), 2);
|
||||
}, 1s);
|
||||
}, 1s);
|
||||
}, 500ms * (i + 1));
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
if (ping < 0 || ping > 999)
|
||||
{
|
||||
Game::Scr_Error("^1SetPing: Ping needs to between 0 and 999!\n");
|
||||
return;
|
||||
}
|
||||
ping = std::clamp(ping, 0, 999);
|
||||
|
||||
Game::gentity_t* gentity = Script::getEntFromEntRef(id);
|
||||
Game::client_t* client = Script::getClientFromEnt(gentity);
|
||||
unsigned int clientNum = GetClientNum(client);
|
||||
const auto* ent = Game::GetPlayerEntity(entref);
|
||||
auto* client = Script::GetClient(ent);
|
||||
|
||||
if (!Bots::IsValidClientNum(clientNum))
|
||||
{
|
||||
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)
|
||||
if (!client->bIsTestClient)
|
||||
{
|
||||
Game::Scr_Error("^1SetPing: Can only call on a bot!\n");
|
||||
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);
|
||||
Game::client_t* client = Script::getClientFromEnt(gentity);
|
||||
unsigned int clientNum = GetClientNum(client);
|
||||
const auto* gentity = Game::GetPlayerEntity(entref);
|
||||
const auto* client = Script::GetClient(gentity);
|
||||
|
||||
if (!Bots::IsValidClientNum(clientNum))
|
||||
{
|
||||
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);
|
||||
Game::Scr_AddBool(client->bIsTestClient == 1);
|
||||
});
|
||||
|
||||
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);
|
||||
Game::client_t* client = Script::getClientFromEnt(gentity);
|
||||
unsigned int clientNum = GetClientNum(client);
|
||||
const auto* ent = Game::GetPlayerEntity(entref);
|
||||
const auto* client = Script::GetClient(ent);
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
if (client->state < 3)
|
||||
{
|
||||
Game::Scr_Error("^1botStop: Needs to be connected.\n");
|
||||
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;
|
||||
g_botai[entref.entnum] = {0};
|
||||
g_botai[entref.entnum].weapon = 1;
|
||||
g_botai[entref.entnum].active = false;
|
||||
});
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
auto weapon = Game::Scr_GetString(0);
|
||||
|
||||
Game::gentity_t* gentity = Script::getEntFromEntRef(id);
|
||||
Game::client_t* client = Script::getClientFromEnt(gentity);
|
||||
unsigned int clientNum = GetClientNum(client);
|
||||
|
||||
if (!Bots::IsValidClientNum(clientNum))
|
||||
if (weapon == nullptr || weapon[0] == '\0')
|
||||
{
|
||||
Game::Scr_Error("^1botWeapon: Need to call on a player entity!\n");
|
||||
g_botai[entref.entnum].weapon = 1;
|
||||
return;
|
||||
}
|
||||
|
||||
if (client->state < 3)
|
||||
{
|
||||
Game::Scr_Error("^1botWeapon: Needs to be connected.\n");
|
||||
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;
|
||||
const auto weapId = Game::G_GetWeaponIndexForName(weapon);
|
||||
g_botai[entref.entnum].weapon = static_cast<uint16_t>(weapId);
|
||||
g_botai[entref.entnum].active = true;
|
||||
});
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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);
|
||||
Game::client_t* client = Script::getClientFromEnt(gentity);
|
||||
unsigned int clientNum = GetClientNum(client);
|
||||
|
||||
if (!Bots::IsValidClientNum(clientNum))
|
||||
if (!client->bIsTestClient)
|
||||
{
|
||||
Game::Scr_Error("^1botAction: Need to call on a player entity!\n");
|
||||
Game::Scr_Error("^1BotAction: Can only call on a bot!\n");
|
||||
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] != '-')
|
||||
{
|
||||
Game::Scr_Error("^1botAction: Sign for action must be '+' or '-'.\n");
|
||||
Game::Scr_ParamError(0, "^1BotAction: Sign for action must be '+' or '-'.\n");
|
||||
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;
|
||||
|
||||
if (action[0] == '+')
|
||||
g_botai[clientNum].buttons |= BotActions[i].key;
|
||||
g_botai[entref.entnum].buttons |= BotActions[i].key;
|
||||
else
|
||||
g_botai[clientNum].buttons &= ~(BotActions[i].key);
|
||||
g_botai[entref.entnum].buttons &= ~BotActions[i].key;
|
||||
|
||||
g_botai[entref.entnum].active = true;
|
||||
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 rightInt = Game::Scr_GetInt(1);
|
||||
|
||||
Game::gentity_t* gentity = Script::getEntFromEntRef(id);
|
||||
Game::client_t* client = Script::getClientFromEnt(gentity);
|
||||
unsigned int clientNum = GetClientNum(client);
|
||||
const auto* ent = Game::GetPlayerEntity(entref);
|
||||
const auto* client = Script::GetClient(ent);
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
if (client->state < 3)
|
||||
{
|
||||
Game::Scr_Error("^1botMovement: Needs to be connected.\n");
|
||||
return;
|
||||
}
|
||||
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());
|
||||
|
||||
if (!client->isBot)
|
||||
{
|
||||
Game::Scr_Error("^1botMovement: Can only call on a bot!\n");
|
||||
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;
|
||||
g_botai[entref.entnum].forward = static_cast<int8_t>(forwardInt);
|
||||
g_botai[entref.entnum].right = static_cast<int8_t>(rightInt);
|
||||
g_botai[entref.entnum].active = true;
|
||||
});
|
||||
}
|
||||
|
||||
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()
|
||||
{
|
||||
// Replace connect string
|
||||
@ -399,61 +321,45 @@ namespace Components
|
||||
// Intercept sprintf for the connect string
|
||||
Utils::Hook(0x48ADAB, Bots::BuildConnectString, HOOK_CALL).install()->quick();
|
||||
|
||||
// Stop default behavour of bots spinning and shooting
|
||||
Utils::Hook(0x627021, 0x4BB9B0, HOOK_CALL).install()->quick();
|
||||
Utils::Hook(0x627241, 0x4BB9B0, HOOK_CALL).install()->quick();
|
||||
Utils::Hook(0x627021, Bots::SV_BotUserMove_Hk, HOOK_CALL).install()->quick();
|
||||
Utils::Hook(0x627241, Bots::SV_BotUserMove_Hk, HOOK_CALL).install()->quick();
|
||||
|
||||
// zero the bot command array
|
||||
for (int i = 0; i < MAX_G_BOTAI_ENTRIES; i++)
|
||||
Utils::Hook(0x441B80, Bots::G_SelectWeaponIndex_Hk, HOOK_JUMP).install()->quick();
|
||||
|
||||
// Zero the bot command array
|
||||
for (auto i = 0u; i < std::extent_v<decltype(g_botai)>; i++)
|
||||
{
|
||||
g_botai[i] = { 0 };
|
||||
g_botai[i].weapon = 1; // prevent the bots from defaulting to the 'none' weapon
|
||||
g_botai[i] = {0};
|
||||
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)
|
||||
{
|
||||
unsigned int count = 1;
|
||||
auto count = 1u;
|
||||
|
||||
if (params->size() > 1)
|
||||
{
|
||||
if (params->get(1) == "all"s) count = static_cast<unsigned int>(-1);
|
||||
else count = atoi(params->get(1));
|
||||
if (params->get(1) == "all"s)
|
||||
{
|
||||
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
|
||||
if (!Game::SV_Loaded())
|
||||
@ -470,10 +376,14 @@ namespace Components
|
||||
});
|
||||
|
||||
Bots::AddMethods();
|
||||
}
|
||||
|
||||
Bots::~Bots()
|
||||
{
|
||||
Bots::BotNames.clear();
|
||||
// In case a loaded mod didn't call "BotStop" before the VM shutdown
|
||||
Script::OnVMShutdown([]
|
||||
{
|
||||
for (auto i = 0u; i < std::extent_v<decltype(g_botai)>; i++)
|
||||
{
|
||||
g_botai[i].active = false;
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -6,17 +6,20 @@ namespace Components
|
||||
{
|
||||
public:
|
||||
Bots();
|
||||
~Bots();
|
||||
static unsigned int GetClientNum(Game::client_s*);
|
||||
static bool IsValidClientNum(unsigned int);
|
||||
|
||||
private:
|
||||
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 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();
|
||||
};
|
||||
}
|
||||
|
@ -11,6 +11,7 @@ namespace Components
|
||||
std::uint8_t padding3[4];
|
||||
std::int32_t tableColumn;
|
||||
};
|
||||
|
||||
struct playercarddata_s
|
||||
{
|
||||
std::uint32_t padding;
|
||||
|
@ -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()
|
||||
{
|
||||
|
||||
}
|
||||
}
|
@ -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();
|
||||
};
|
||||
}
|
@ -17,7 +17,7 @@ namespace Components
|
||||
|
||||
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));
|
||||
return false;
|
||||
}
|
||||
@ -28,10 +28,11 @@ namespace Components
|
||||
bool ClientCommand::CallbackHandler(Game::gentity_s* ent, const char* 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;
|
||||
}
|
||||
|
||||
@ -42,7 +43,7 @@ namespace Components
|
||||
{
|
||||
const auto command = Utils::String::ToLower(name);
|
||||
|
||||
ClientCommand::FunctionMap[command] = callback;
|
||||
ClientCommand::FunctionMap[command] = std::move(callback);
|
||||
}
|
||||
|
||||
void ClientCommand::ClientCommandStub(const int clientNum)
|
||||
@ -75,7 +76,7 @@ namespace Components
|
||||
ent->client->flags ^= Game::PLAYER_FLAG_NOCLIP;
|
||||
|
||||
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,
|
||||
(ent->client->flags & Game::PLAYER_FLAG_NOCLIP) ? "GAME_NOCLIPON" : "GAME_NOCLIPOFF"));
|
||||
@ -89,7 +90,7 @@ namespace Components
|
||||
ent->client->flags ^= Game::PLAYER_FLAG_UFO;
|
||||
|
||||
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,
|
||||
(ent->client->flags & Game::PLAYER_FLAG_UFO) ? "GAME_UFOON" : "GAME_UFOOFF"));
|
||||
@ -103,7 +104,7 @@ namespace Components
|
||||
ent->flags ^= Game::FL_GODMODE;
|
||||
|
||||
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,
|
||||
(ent->flags & Game::FL_GODMODE) ? "GAME_GODMODE_ON" : "GAME_GODMODE_OFF"));
|
||||
@ -117,7 +118,7 @@ namespace Components
|
||||
ent->flags ^= Game::FL_DEMI_GODMODE;
|
||||
|
||||
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,
|
||||
(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;
|
||||
|
||||
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,
|
||||
(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>);
|
||||
{
|
||||
if (entref >= Game::MAX_GENTITIES || Game::g_entities[entref].client == nullptr)
|
||||
{
|
||||
Game::Scr_Error(Utils::String::VA("^1NoClip: entity %u is not a client\n", entref));
|
||||
return;
|
||||
}
|
||||
const auto* ent = Game::GetPlayerEntity(entref);
|
||||
|
||||
if (Game::Scr_GetNumParam() == 1u && Game::Scr_GetType(0) == Game::VAR_INTEGER)
|
||||
if (Game::Scr_GetNumParam() >= 1u)
|
||||
{
|
||||
if (Game::Scr_GetInt(0))
|
||||
{
|
||||
Game::g_entities[entref].client->flags |= Game::PLAYER_FLAG_NOCLIP;
|
||||
ent->client->flags |= Game::PLAYER_FLAG_NOCLIP;
|
||||
}
|
||||
else
|
||||
{
|
||||
Game::g_entities[entref].client->flags &= ~Game::PLAYER_FLAG_NOCLIP;
|
||||
ent->client->flags &= ~Game::PLAYER_FLAG_NOCLIP;
|
||||
}
|
||||
}
|
||||
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>);
|
||||
{
|
||||
if (entref >= Game::MAX_GENTITIES || Game::g_entities[entref].client == nullptr)
|
||||
{
|
||||
Game::Scr_Error(Utils::String::VA("^1Ufo: entity %u is not a client\n", entref));
|
||||
return;
|
||||
}
|
||||
const auto* ent = Game::GetPlayerEntity(entref);
|
||||
|
||||
if (Game::Scr_GetNumParam() == 1u && Game::Scr_GetType(0) == Game::VAR_INTEGER)
|
||||
if (Game::Scr_GetNumParam() >= 1u)
|
||||
{
|
||||
if (Game::Scr_GetInt(0))
|
||||
{
|
||||
Game::g_entities[entref].client->flags |= Game::PLAYER_FLAG_UFO;
|
||||
ent->client->flags |= Game::PLAYER_FLAG_UFO;
|
||||
}
|
||||
else
|
||||
{
|
||||
Game::g_entities[entref].client->flags &= ~Game::PLAYER_FLAG_UFO;
|
||||
ent->client->flags &= ~Game::PLAYER_FLAG_UFO;
|
||||
}
|
||||
}
|
||||
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>);
|
||||
{
|
||||
if (entref >= Game::MAX_GENTITIES)
|
||||
{
|
||||
Game::Scr_Error(Utils::String::VA("^1God: entity %u is out of bounds\n", entref));
|
||||
return;
|
||||
}
|
||||
auto* ent = Game::GetEntity(entref);
|
||||
|
||||
if (Game::Scr_GetNumParam() == 1u && Game::Scr_GetType(0) == Game::VAR_INTEGER)
|
||||
if (Game::Scr_GetNumParam() >= 1u)
|
||||
{
|
||||
if (Game::Scr_GetInt(0))
|
||||
{
|
||||
Game::g_entities[entref].flags |= Game::FL_GODMODE;
|
||||
ent->flags |= Game::FL_GODMODE;
|
||||
}
|
||||
else
|
||||
{
|
||||
Game::g_entities[entref].flags &= ~Game::FL_GODMODE;
|
||||
ent->flags &= ~Game::FL_GODMODE;
|
||||
}
|
||||
}
|
||||
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>);
|
||||
{
|
||||
if (entref >= Game::MAX_GENTITIES)
|
||||
{
|
||||
Game::Scr_Error(Utils::String::VA("^1Demigod: entity %u is out of bounds\n", entref));
|
||||
return;
|
||||
}
|
||||
auto* ent = Game::GetEntity(entref);
|
||||
|
||||
if (Game::Scr_GetNumParam() == 1u && Game::Scr_GetType(0) == Game::VAR_INTEGER)
|
||||
if (Game::Scr_GetNumParam() >= 1u)
|
||||
{
|
||||
if (Game::Scr_GetInt(0))
|
||||
{
|
||||
Game::g_entities[entref].flags |= Game::FL_DEMI_GODMODE;
|
||||
ent->flags |= Game::FL_DEMI_GODMODE;
|
||||
}
|
||||
else
|
||||
{
|
||||
Game::g_entities[entref].flags &= ~Game::FL_DEMI_GODMODE;
|
||||
ent->flags &= ~Game::FL_DEMI_GODMODE;
|
||||
}
|
||||
}
|
||||
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>);
|
||||
{
|
||||
if (entref >= Game::MAX_GENTITIES)
|
||||
{
|
||||
Game::Scr_Error(Utils::String::VA("^1Notarget: entity %u is out of bounds\n", entref));
|
||||
return;
|
||||
}
|
||||
auto* ent = Game::GetEntity(entref);
|
||||
|
||||
if (Game::Scr_GetNumParam() == 1u && Game::Scr_GetType(0) == Game::VAR_INTEGER)
|
||||
if (Game::Scr_GetNumParam() >= 1u)
|
||||
{
|
||||
if (Game::Scr_GetInt(0))
|
||||
{
|
||||
Game::g_entities[entref].flags |= Game::FL_NOTARGET;
|
||||
ent->flags |= Game::FL_NOTARGET;
|
||||
}
|
||||
else
|
||||
{
|
||||
Game::g_entities[entref].flags &= ~Game::FL_NOTARGET;
|
||||
ent->flags &= ~Game::FL_NOTARGET;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Game::g_entities[entref].flags ^= Game::FL_NOTARGET;
|
||||
ent->flags ^= Game::FL_NOTARGET;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
@ -964,13 +964,20 @@ namespace Components
|
||||
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 (Game::Scr_GetNumParam() < 1u) return;
|
||||
if (!Flags::HasFlag("scriptablehttp"))
|
||||
return;
|
||||
|
||||
std::string url = Game::Scr_GetString(0);
|
||||
unsigned int object = Game::AllocObject();
|
||||
const auto* url = Game::Scr_GetString(0);
|
||||
|
||||
if (url == nullptr)
|
||||
{
|
||||
Game::Scr_ParamError(0, "^1HttpGet: Illegal parameter!\n");
|
||||
return;
|
||||
}
|
||||
|
||||
auto object = Game::AllocObject();
|
||||
|
||||
Game::Scr_AddObject(object);
|
||||
|
||||
@ -978,13 +985,13 @@ namespace Components
|
||||
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 (Game::Scr_GetNumParam() < 1u) return;
|
||||
if (!Flags::HasFlag("scriptablehttp"))
|
||||
return;
|
||||
|
||||
unsigned int object = Game::Scr_GetObject(0);
|
||||
for (auto& download : Download::ScriptDownloads)
|
||||
const auto object = Game::Scr_GetObject(0);
|
||||
for (const auto& download : Download::ScriptDownloads)
|
||||
{
|
||||
if (object == download->getObject())
|
||||
{
|
||||
|
@ -788,7 +788,7 @@ namespace Components
|
||||
{
|
||||
int dlc = token.get<int>();
|
||||
|
||||
for (auto pack : Maps::DlcPacks)
|
||||
for (const auto& pack : Maps::DlcPacks)
|
||||
{
|
||||
if (pack.index == dlc)
|
||||
{
|
||||
|
@ -10,48 +10,59 @@ namespace Components
|
||||
{
|
||||
Game::NET_StringToAdr(addrString.data(), &this->address);
|
||||
}
|
||||
|
||||
Network::Address::Address(sockaddr* addr)
|
||||
{
|
||||
Game::SockadrToNetadr(addr, &this->address);
|
||||
}
|
||||
|
||||
bool Network::Address::operator==(const Network::Address& obj) const
|
||||
{
|
||||
return Game::NET_CompareAdr(this->address, obj.address);
|
||||
}
|
||||
|
||||
void Network::Address::setPort(unsigned short port)
|
||||
{
|
||||
this->address.port = htons(port);
|
||||
}
|
||||
|
||||
unsigned short Network::Address::getPort()
|
||||
{
|
||||
return ntohs(this->address.port);
|
||||
}
|
||||
|
||||
void Network::Address::setIP(DWORD ip)
|
||||
{
|
||||
this->address.ip.full = ip;
|
||||
}
|
||||
|
||||
void Network::Address::setIP(Game::netIP_t ip)
|
||||
{
|
||||
this->address.ip = ip;
|
||||
}
|
||||
|
||||
Game::netIP_t Network::Address::getIP()
|
||||
{
|
||||
return this->address.ip;
|
||||
}
|
||||
|
||||
void Network::Address::setType(Game::netadrtype_t type)
|
||||
{
|
||||
this->address.type = type;
|
||||
}
|
||||
|
||||
Game::netadrtype_t Network::Address::getType()
|
||||
{
|
||||
return this->address.type;
|
||||
}
|
||||
|
||||
sockaddr Network::Address::getSockAddr()
|
||||
{
|
||||
sockaddr addr;
|
||||
this->toSockAddr(&addr);
|
||||
return addr;
|
||||
}
|
||||
|
||||
void Network::Address::toSockAddr(sockaddr* addr)
|
||||
{
|
||||
if (addr)
|
||||
@ -59,22 +70,27 @@ namespace Components
|
||||
Game::NetadrToSockadr(&this->address, addr);
|
||||
}
|
||||
}
|
||||
|
||||
void Network::Address::toSockAddr(sockaddr_in* addr)
|
||||
{
|
||||
this->toSockAddr(reinterpret_cast<sockaddr*>(addr));
|
||||
}
|
||||
|
||||
Game::netadr_t* Network::Address::get()
|
||||
{
|
||||
return &this->address;
|
||||
}
|
||||
|
||||
const char* Network::Address::getCString() const
|
||||
{
|
||||
return Game::NET_AdrToString(this->address);
|
||||
}
|
||||
|
||||
std::string Network::Address::getString() const
|
||||
{
|
||||
return this->getCString();
|
||||
}
|
||||
|
||||
bool Network::Address::isLocal()
|
||||
{
|
||||
// According to: https://en.wikipedia.org/wiki/Private_network
|
||||
@ -95,6 +111,7 @@ namespace Components
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Network::Address::isSelf()
|
||||
{
|
||||
if (Game::NET_IsLocalAddress(this->address)) return true; // Loopback
|
||||
@ -110,6 +127,7 @@ namespace Components
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Network::Address::isLoopback()
|
||||
{
|
||||
if (this->getIP().full == 0x100007f) // 127.0.0.1
|
||||
@ -119,10 +137,12 @@ namespace Components
|
||||
|
||||
return Game::NET_IsLocalAddress(this->address);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
void Network::Handle(const std::string& packet, Utils::Slot<Network::Callback> callback)
|
||||
{
|
||||
Network::PacketHandlers[Utils::String::ToLower(packet)] = callback;
|
||||
|
@ -315,7 +315,7 @@ namespace Components
|
||||
{
|
||||
if (Game::svs_clients[i].state >= 3)
|
||||
{
|
||||
if (Game::svs_clients[i].isBot) ++botCount;
|
||||
if (Game::svs_clients[i].bIsTestClient) ++botCount;
|
||||
else ++clientCount;
|
||||
}
|
||||
}
|
||||
|
@ -10,7 +10,7 @@ namespace Components
|
||||
std::unordered_map<std::string, std::string> Script::ScriptStorage;
|
||||
std::unordered_map<int, std::string> Script::ScriptBaseProgramNum;
|
||||
std::unordered_map<const char*, const char*> Script::ReplacedFunctions;
|
||||
const char* Script::ReplacedPos = 0;
|
||||
const char* Script::ReplacedPos = nullptr;
|
||||
int Script::LastFrameTime = -1;
|
||||
|
||||
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)
|
||||
{
|
||||
Script::ScriptNameStack.push_back(Script::ScriptName);
|
||||
@ -156,11 +187,11 @@ namespace Components
|
||||
|
||||
void Script::CompileError(unsigned int offset, const char* message, ...)
|
||||
{
|
||||
char msgbuf[1024] = { 0 };
|
||||
va_list v;
|
||||
va_start(v, message);
|
||||
_vsnprintf_s(msgbuf, sizeof(msgbuf), message, v);
|
||||
va_end(v);
|
||||
char msgbuf[1024] = {0};
|
||||
va_list va;
|
||||
va_start(va, message);
|
||||
_vsnprintf_s(msgbuf, _TRUNCATE, message, va);
|
||||
va_end(va);
|
||||
|
||||
Game::Scr_ShutdownAllocNode();
|
||||
|
||||
@ -180,7 +211,7 @@ namespace Components
|
||||
if (!Game::Scr_LoadScript(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
|
||||
{
|
||||
@ -188,7 +219,7 @@ namespace Components
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
Logger::Print("Script handle %s::%s loaded successfully.\n", script.data(), label.data());
|
||||
@ -201,7 +232,7 @@ namespace Components
|
||||
|
||||
void Script::LoadGameType()
|
||||
{
|
||||
for (auto handle : Script::ScriptHandles)
|
||||
for (const auto& handle : Script::ScriptHandles)
|
||||
{
|
||||
Game::Scr_FreeThread(Game::Scr_ExecThread(handle, 0));
|
||||
}
|
||||
@ -213,7 +244,7 @@ namespace Components
|
||||
{
|
||||
Script::ScriptHandles.clear();
|
||||
|
||||
auto list = FileSystem::GetFileList("scripts/", "gsc");
|
||||
const auto list = FileSystem::GetFileList("scripts/", "gsc");
|
||||
|
||||
for (auto file : list)
|
||||
{
|
||||
@ -224,8 +255,12 @@ namespace Components
|
||||
file = file.substr(0, file.size() - 4);
|
||||
}
|
||||
|
||||
int handle = Script::LoadScriptAndLabel(file, "init");
|
||||
if (handle) Script::ScriptHandles.push_back(handle);
|
||||
auto handle = Script::LoadScriptAndLabel(file, "init");
|
||||
|
||||
if (handle)
|
||||
{
|
||||
Script::ScriptHandles.push_back(handle);
|
||||
}
|
||||
else
|
||||
{
|
||||
handle = Script::LoadScriptAndLabel(file, "main");
|
||||
@ -300,14 +335,12 @@ namespace Components
|
||||
|
||||
void Script::Scr_PrintPrevCodePos(int scriptPos)
|
||||
{
|
||||
int bestCodePos = -1;
|
||||
int nextCodePos = -1;
|
||||
int offset = -1;
|
||||
auto bestCodePos = -1, nextCodePos = -1, offset = -1;
|
||||
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)
|
||||
{
|
||||
@ -322,17 +355,15 @@ namespace Components
|
||||
|
||||
bestCodePos = codePos;
|
||||
|
||||
file = kv.second;
|
||||
file = value;
|
||||
offset = scriptPos - bestCodePos;
|
||||
}
|
||||
|
||||
if (bestCodePos == -1)
|
||||
return;
|
||||
|
||||
float onehundred = 100.0;
|
||||
|
||||
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()
|
||||
@ -373,15 +404,15 @@ namespace Components
|
||||
void Script::OnVMShutdown(Utils::Slot<Scheduler::Callback> callback)
|
||||
{
|
||||
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();
|
||||
|
||||
// Scr_ShutdownSystem
|
||||
Utils::Hook::Call<void(int)>(0x421EE0)(num);
|
||||
Utils::Hook::Call<void(unsigned char)>(0x421EE0)(sys);
|
||||
}
|
||||
|
||||
unsigned int Script::SetExpFogStub()
|
||||
@ -403,7 +434,7 @@ namespace Components
|
||||
{
|
||||
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 "";
|
||||
}
|
||||
|
||||
@ -411,7 +442,7 @@ namespace Components
|
||||
|
||||
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 "";
|
||||
}
|
||||
|
||||
@ -430,7 +461,7 @@ namespace Components
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
@ -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];
|
||||
return gentity;
|
||||
}
|
||||
assert(ent != nullptr);
|
||||
|
||||
Game::client_t* Script::getClientFromEnt(Game::gentity_t* gentity)
|
||||
{
|
||||
if (!gentity->client)
|
||||
if (ent->client == nullptr)
|
||||
{
|
||||
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()
|
||||
@ -535,63 +570,65 @@ namespace Components
|
||||
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
|
||||
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;
|
||||
}
|
||||
|
||||
auto str = Game::Scr_GetString(0);
|
||||
|
||||
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>);
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
std::string key = Game::Scr_GetString(0);
|
||||
std::string data = Game::Scr_GetString(1);
|
||||
|
||||
Script::ScriptStorage.insert_or_assign(key, data);
|
||||
Script::ScriptStorage.insert_or_assign(key, value);
|
||||
});
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
std::string key = Game::Scr_GetString(0);
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
@ -600,41 +637,49 @@ namespace Components
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
std::string key = Game::Scr_GetString(0);
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
auto data = Script::ScriptStorage.at(key);
|
||||
Game::Scr_AddString(data.c_str());
|
||||
const auto& data = Script::ScriptStorage.at(key);
|
||||
Game::Scr_AddString(data.data());
|
||||
});
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
std::string key = Game::Scr_GetString(0);
|
||||
|
||||
Game::Scr_AddInt(Script::ScriptStorage.count(key));
|
||||
Game::Scr_AddBool(static_cast<int>(Script::ScriptStorage.count(key))); // Until C++17
|
||||
});
|
||||
|
||||
Script::AddFunction("StorageClear", [](Game::scr_entref_t) // gsc: StorageClear();
|
||||
{
|
||||
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()
|
||||
@ -645,14 +690,12 @@ namespace Components
|
||||
Utils::Hook(0x426C2D, Script::StoreScriptBaseProgramNumStub, HOOK_JUMP).install()->quick();
|
||||
Utils::Hook(0x42281B, Script::Scr_PrintPrevCodePosStub, HOOK_JUMP).install()->quick();
|
||||
|
||||
// enable scr_error printing if in developer
|
||||
Dvar::OnInit([]()
|
||||
{
|
||||
int developer = Dvar::Var("developer").get<int>();
|
||||
|
||||
if (developer > 0 && Dedicated::IsEnabled())
|
||||
Utils::Hook::Set<BYTE>(0x48D8C7, 0x75);
|
||||
});
|
||||
Utils::Hook(0x61E3AD, Script::RuntimeError, HOOK_CALL).install()->quick();
|
||||
Utils::Hook(0x621976, Script::RuntimeError, HOOK_CALL).install()->quick();
|
||||
Utils::Hook(0x62246E, Script::RuntimeError, HOOK_CALL).install()->quick();
|
||||
// Skip check in GScr_CheckAllowedToSetPersistentData to prevent log spam in RuntimeError.
|
||||
// On IW5 the function is entirely nullsubbed
|
||||
Utils::Hook::Set<BYTE>(0x5F8DBF, 0xEB);
|
||||
|
||||
Utils::Hook(0x612E8D, 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::Nop(0x61E933, 1);
|
||||
|
||||
Utils::Hook(0x47548B, Script::ScrShutdownSystemStub, HOOK_CALL).install()->quick();
|
||||
Utils::Hook(0x4D06BA, 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(); // G_ShutdownGame
|
||||
|
||||
Scheduler::OnFrame([]()
|
||||
{
|
||||
if (!Game::SV_Loaded())
|
||||
return;
|
||||
|
||||
int nowMs = Game::Sys_Milliseconds();
|
||||
const auto nowMs = Game::Sys_Milliseconds();
|
||||
|
||||
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)
|
||||
Logger::Print(23, "Hitch warning: %i msec frame time\n", timeTaken);
|
||||
@ -690,10 +734,19 @@ namespace Components
|
||||
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);
|
||||
#endif
|
||||
|
||||
Script::AddFunctions();
|
||||
|
||||
|
@ -29,8 +29,7 @@ namespace Components
|
||||
|
||||
static void OnVMShutdown(Utils::Slot<Scheduler::Callback> callback);
|
||||
|
||||
static Game::gentity_t* getEntFromEntRef(Game::scr_entref_t entref);
|
||||
static Game::client_t* getClientFromEnt(Game::gentity_t* gentity);
|
||||
static Game::client_t* GetClient(const Game::gentity_t* gentity);
|
||||
|
||||
private:
|
||||
static std::string ScriptName;
|
||||
@ -51,6 +50,7 @@ namespace Components
|
||||
|
||||
static void FunctionError();
|
||||
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 StoreScriptNameStub();
|
||||
@ -64,7 +64,7 @@ namespace Components
|
||||
static Game::scr_function_t GetFunction(void* caller, const char** name, int* isDev);
|
||||
static void GetFunctionStub();
|
||||
|
||||
static void ScrShutdownSystemStub(int);
|
||||
static void ScrShutdownSystemStub(unsigned char sys);
|
||||
static void StoreScriptBaseProgramNumStub();
|
||||
static void StoreScriptBaseProgramNum();
|
||||
static void Scr_PrintPrevCodePosStub();
|
||||
|
185
src/Components/Modules/ScriptExtension.cpp
Normal file
185
src/Components/Modules/ScriptExtension.cpp
Normal 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);
|
||||
}
|
||||
}
|
17
src/Components/Modules/ScriptExtension.hpp
Normal file
17
src/Components/Modules/ScriptExtension.hpp
Normal 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();
|
||||
};
|
||||
}
|
@ -21,7 +21,7 @@ namespace Game
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
AddRefToObject_t AddRefToObject = AddRefToObject_t(0x61C360);
|
||||
AllocObject_t AllocObject = AllocObject_t(0x434320);
|
||||
|
||||
@ -73,6 +73,7 @@ namespace Game
|
||||
Com_MatchToken_t Com_MatchToken = Com_MatchToken_t(0x447130);
|
||||
Com_SetSlowMotion_t Com_SetSlowMotion = Com_SetSlowMotion_t(0x446E20);
|
||||
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_DrawSolidConsole_t Con_DrawSolidConsole = Con_DrawSolidConsole_t(0x5A5040);
|
||||
@ -259,6 +260,8 @@ namespace Game
|
||||
Scr_GetFunctionHandle_t Scr_GetFunctionHandle = Scr_GetFunctionHandle_t(0x4234F0);
|
||||
|
||||
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_GetInt_t Scr_GetInt = Scr_GetInt_t(0x4F31D0);
|
||||
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_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_AddFloat_t Scr_AddFloat = Scr_AddFloat_t(0x61E860);
|
||||
Scr_AddObject_t Scr_AddObject = Scr_AddObject_t(0x430F40);
|
||||
Scr_Notify_t Scr_Notify = Scr_Notify_t(0x4A4750);
|
||||
Scr_NotifyLevel_t Scr_NotifyLevel = Scr_NotifyLevel_t(0x4D9C30);
|
||||
|
||||
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_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_ShutdownAllocNode_t Scr_ShutdownAllocNode = Scr_ShutdownAllocNode_t(0x441650);
|
||||
Scr_IsSystemActive_t Scr_IsSystemActive = Scr_IsSystemActive_t(0x4B24E0);
|
||||
@ -311,9 +322,11 @@ namespace Game
|
||||
Steam_JoinLobby_t Steam_JoinLobby = Steam_JoinLobby_t(0x49CF70);
|
||||
|
||||
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);
|
||||
|
||||
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_GameSendServerCommand_t SV_GameSendServerCommand = SV_GameSendServerCommand_t(0x4BC3A0);
|
||||
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_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_IsDatabaseReady_t Sys_IsDatabaseReady = Sys_IsDatabaseReady_t(0x4CA4A0);
|
||||
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_ListFiles_t Sys_ListFiles = Sys_ListFiles_t(0x45A660);
|
||||
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_TempPriorityAtLeastNormalBegin_t Sys_TempPriorityAtLeastNormalBegin = Sys_TempPriorityAtLeastNormalBegin_t(0x478680);
|
||||
Sys_TempPriorityEnd_t Sys_TempPriorityEnd = Sys_TempPriorityEnd_t(0x4DCF00);
|
||||
@ -436,6 +449,8 @@ namespace Game
|
||||
|
||||
gentity_t* g_entities = reinterpret_cast<gentity_t*>(0x18835D8);
|
||||
|
||||
int* level_scriptPrintChannel = reinterpret_cast<int*>(0x1A860FC);
|
||||
|
||||
netadr_t* connectedHost = reinterpret_cast<netadr_t*>(0xA1E888);
|
||||
|
||||
SOCKET* ip_socket = reinterpret_cast<SOCKET*>(0x64A3008);
|
||||
@ -473,6 +488,7 @@ namespace Game
|
||||
unsigned short* db_hashTable = reinterpret_cast<unsigned short*>(0x12412B0);
|
||||
|
||||
scrVmPub_t* scrVmPub = reinterpret_cast<scrVmPub_t*>(0x2040CF0);
|
||||
scrVarPub_t* scrVarPub = reinterpret_cast<scrVarPub_t*>(0x201A408);
|
||||
|
||||
clientstate_t* clcState = reinterpret_cast<clientstate_t*>(0xB2C540);
|
||||
|
||||
@ -713,14 +729,26 @@ namespace Game
|
||||
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)
|
||||
@ -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*/)
|
||||
{
|
||||
__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)
|
||||
}
|
||||
|
@ -154,6 +154,9 @@ namespace Game
|
||||
typedef void(__cdecl * Com_Quitf_t)();
|
||||
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);
|
||||
extern Con_DrawMiniConsole_t Con_DrawMiniConsole;
|
||||
|
||||
@ -368,7 +371,7 @@ namespace Game
|
||||
typedef int(__cdecl* FS_Delete_t)(const char* fileName);
|
||||
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;
|
||||
|
||||
typedef void(__cdecl* G_SpawnEntitiesFromString_t)();
|
||||
@ -555,7 +558,7 @@ namespace Game
|
||||
typedef bool(__cdecl * NET_IsLocalAddress_t)(netadr_t adr);
|
||||
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;
|
||||
|
||||
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);
|
||||
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;
|
||||
|
||||
typedef void(__cdecl * Scr_AddString_t)(const char* str);
|
||||
typedef void(__cdecl * Scr_AddString_t)(const char* value);
|
||||
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;
|
||||
|
||||
typedef void(__cdecl * Scr_AddFloat_t)(float);
|
||||
typedef void(__cdecl * Scr_AddFloat_t)(float value);
|
||||
extern Scr_AddFloat_t Scr_AddFloat;
|
||||
|
||||
typedef void(__cdecl * Scr_AddObject_t)(unsigned int id);
|
||||
@ -660,22 +666,28 @@ namespace Game
|
||||
typedef int(__cdecl * Scr_LoadScript_t)(const char*);
|
||||
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;
|
||||
|
||||
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;
|
||||
|
||||
typedef int(__cdecl * Scr_GetInt_t)(int);
|
||||
typedef int(__cdecl * Scr_GetInt_t)(unsigned int index);
|
||||
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;
|
||||
|
||||
typedef unsigned int(__cdecl * Scr_GetNumParam_t)();
|
||||
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;
|
||||
|
||||
typedef int(__cdecl * Scr_ExecThread_t)(int, int);
|
||||
@ -699,12 +711,24 @@ namespace Game
|
||||
typedef bool(__cdecl * Scr_IsSystemActive_t)();
|
||||
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;
|
||||
|
||||
typedef void(__cdecl* Scr_Error_t)(const char*);
|
||||
typedef void(__cdecl * Scr_Error_t)(const char*);
|
||||
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);
|
||||
extern Script_Alloc_t Script_Alloc;
|
||||
|
||||
@ -741,15 +765,21 @@ namespace Game
|
||||
typedef void(__cdecl * Steam_JoinLobby_t)(SteamID, char);
|
||||
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;
|
||||
|
||||
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);
|
||||
extern StringTable_HashString_t StringTable_HashString;
|
||||
|
||||
typedef gentity_t*(__cdecl* SV_AddTestClient_t)();
|
||||
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);
|
||||
extern SV_GameClientNum_Score_t SV_GameClientNum_Score;
|
||||
|
||||
@ -783,7 +813,7 @@ namespace Game
|
||||
typedef client_t*(__cdecl * SV_GetPlayerByNum_t)();
|
||||
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;
|
||||
|
||||
typedef void(__cdecl * Sys_FreeFileList_t)(char** list);
|
||||
@ -976,6 +1006,8 @@ namespace Game
|
||||
constexpr auto ENTITYNUM_NONE = MAX_GENTITIES - 1;
|
||||
extern gentity_t* g_entities;
|
||||
|
||||
extern int* level_scriptPrintChannel;
|
||||
|
||||
extern netadr_t* connectedHost;
|
||||
extern SOCKET* ip_socket;
|
||||
|
||||
@ -1012,6 +1044,7 @@ namespace Game
|
||||
extern unsigned short* db_hashTable;
|
||||
|
||||
extern scrVmPub_t* scrVmPub;
|
||||
extern scrVarPub_t* scrVarPub;
|
||||
|
||||
extern clientstate_t* clcState;
|
||||
|
||||
@ -1080,10 +1113,13 @@ namespace Game
|
||||
|
||||
void SV_KickClient(client_t* client, const char* 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_AddBool(int value);
|
||||
|
||||
void IN_KeyUp(kbutton_t* button);
|
||||
void IN_KeyDown(kbutton_t* button);
|
||||
|
@ -20,7 +20,12 @@ namespace Game
|
||||
typedef vec_t vec3_t[3];
|
||||
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);
|
||||
|
||||
enum XAssetType
|
||||
@ -1451,21 +1456,23 @@ namespace Game
|
||||
|
||||
enum usercmdButtonBits
|
||||
{
|
||||
CMD_BUTTON_ATTACK = 0x1,
|
||||
CMD_BUTTON_SPRINT = 0x2,
|
||||
CMD_BUTTON_MELEE = 0x4,
|
||||
CMD_BUTTON_ACTIVATE = 0x8,
|
||||
CMD_BUTTON_RELOAD = 0x10,
|
||||
CMD_BUTTON_USE_RELOAD = 0x20,
|
||||
CMD_BUTTON_PRONE = 0x100,
|
||||
CMD_BUTTON_CROUCH = 0x200,
|
||||
CMD_BUTTON_UP = 0x400,
|
||||
CMD_BUTTON_ADS = 0x800,
|
||||
CMD_BUTTON_DOWN = 0x1000,
|
||||
CMD_BUTTON_BREATH = 0x2000,
|
||||
CMD_BUTTON_FRAG = 0x4000,
|
||||
CMD_BUTTON_OFFHAND_SECONDARY = 0x8000,
|
||||
CMD_BUTTON_THROW = 0x80000,
|
||||
CMD_BUTTON_ATTACK = 0x1,
|
||||
CMD_BUTTON_SPRINT = 0x2,
|
||||
CMD_BUTTON_MELEE = 0x4,
|
||||
CMD_BUTTON_ACTIVATE = 0x8,
|
||||
CMD_BUTTON_RELOAD = 0x10,
|
||||
CMD_BUTTON_USE_RELOAD = 0x20,
|
||||
CMD_BUTTON_LEAN_LEFT = 0x40,
|
||||
CMD_BUTTON_LEAN_RIGHT = 0x80,
|
||||
CMD_BUTTON_PRONE = 0x100,
|
||||
CMD_BUTTON_CROUCH = 0x200,
|
||||
CMD_BUTTON_UP = 0x400,
|
||||
CMD_BUTTON_ADS = 0x800,
|
||||
CMD_BUTTON_DOWN = 0x1000,
|
||||
CMD_BUTTON_BREATH = 0x2000,
|
||||
CMD_BUTTON_FRAG = 0x4000,
|
||||
CMD_BUTTON_OFFHAND_SECONDARY = 0x8000,
|
||||
CMD_BUTTON_THROW = 0x80000
|
||||
};
|
||||
|
||||
#pragma pack(push, 4)
|
||||
@ -4999,6 +5006,21 @@ namespace Game
|
||||
int dataCount;
|
||||
} 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
|
||||
{
|
||||
const char *pos;
|
||||
@ -5094,6 +5116,40 @@ namespace Game
|
||||
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
|
||||
{
|
||||
UILOCALVAR_INT = 0x0,
|
||||
@ -5661,7 +5717,7 @@ namespace Game
|
||||
int pureAuthentic; // 135896
|
||||
char __pad7[133138]; // 135900
|
||||
short scriptID; // 269038
|
||||
int isBot; // 269040
|
||||
int bIsTestClient; // 269040
|
||||
int serverID; // 269044
|
||||
char __pad8[9224]; // 269048
|
||||
unsigned __int64 steamID; // 278272
|
||||
|
@ -81,6 +81,7 @@ namespace Utils
|
||||
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);
|
||||
bool StartsWith(const std::string& haystack, const std::string& needle);
|
||||
|
||||
std::string& LTrim(std::string& str);
|
||||
std::string& RTrim(std::string& str);
|
||||
std::string& Trim(std::string& str);
|
||||
|
Loading…
x
Reference in New Issue
Block a user