Merge pull request #46 from ineedbots/bot_scr
Bot GSC functions for bots pressing buttons and moving + fix for node system related crash
This commit is contained in:
commit
8d1b06203e
@ -1,12 +1,94 @@
|
|||||||
#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
|
||||||
|
{
|
||||||
|
/* Actions */
|
||||||
|
int buttons;
|
||||||
|
/* Movement */
|
||||||
|
int8 forward;
|
||||||
|
int8 right;
|
||||||
|
/* Weapon */
|
||||||
|
unsigned short weapon;
|
||||||
|
} BotMovementInfo_t;
|
||||||
|
|
||||||
|
static BotMovementInfo_t g_botai[MAX_G_BOTAI_ENTRIES];
|
||||||
|
|
||||||
|
struct BotAction_t
|
||||||
|
{
|
||||||
|
const char* action;
|
||||||
|
int key;
|
||||||
|
};
|
||||||
|
|
||||||
|
static const BotAction_t 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 },
|
||||||
|
};
|
||||||
|
|
||||||
|
unsigned int Bots::GetClientNum(Game::client_s* cl)
|
||||||
|
{
|
||||||
|
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)
|
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 int botId = 0;
|
||||||
|
const char* botName;
|
||||||
|
|
||||||
if (Bots::BotNames.empty())
|
if (Bots::BotNames.empty())
|
||||||
{
|
{
|
||||||
@ -27,15 +109,19 @@ namespace Components
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (Bots::BotNames.empty())
|
|
||||||
{
|
|
||||||
Bots::BotNames.push_back("bot");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
botId %= Bots::BotNames.size();
|
if (!Bots::BotNames.empty())
|
||||||
strncpy_s(buffer, 0x400, Utils::String::VA(connectString, num, Bots::BotNames[botId++].data(), protocol, checksum, statVer, statStuff, port), 0x400);
|
{
|
||||||
|
botId %= Bots::BotNames.size();
|
||||||
|
botName = Bots::BotNames[botId++].data();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
botName = Utils::String::VA("bot%d", ++botId);
|
||||||
|
}
|
||||||
|
|
||||||
|
strncpy_s(buffer, 0x400, Utils::String::VA(connectString, num, botName, protocol, checksum, statVer, statStuff, port), 0x400);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Bots::Spawn(unsigned int count)
|
void Bots::Spawn(unsigned int count)
|
||||||
@ -70,6 +156,241 @@ namespace Components
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Bots::AddMethods()
|
||||||
|
{
|
||||||
|
Script::AddFunction("SetPing", [](Game::scr_entref_t id) // gsc: self SetPing(<int>)
|
||||||
|
{
|
||||||
|
if (Game::Scr_GetNumParam() != 1 || 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
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("^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");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
client->ping = (short)ping;
|
||||||
|
});
|
||||||
|
|
||||||
|
Script::AddFunction("isBot", [](Game::scr_entref_t id) // Usage: <bot> isBot();
|
||||||
|
{
|
||||||
|
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("^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();
|
||||||
|
{
|
||||||
|
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("^1botStop: Need to call on a player entity!\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;
|
||||||
|
});
|
||||||
|
|
||||||
|
Script::AddFunction("botWeapon", [](Game::scr_entref_t id) // Usage: <bot> botWeapon(<str>);
|
||||||
|
{
|
||||||
|
if (Game::Scr_GetNumParam() != 1 || Game::Scr_GetType(0) != Game::VAR_STRING)
|
||||||
|
{
|
||||||
|
Game::Scr_Error("^1botWeapon: Needs one string parameter!\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))
|
||||||
|
{
|
||||||
|
Game::Scr_Error("^1botWeapon: Need to call on a player entity!\n");
|
||||||
|
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;
|
||||||
|
});
|
||||||
|
|
||||||
|
Script::AddFunction("botAction", [](Game::scr_entref_t id) // Usage: <bot> botAction(<str action>);
|
||||||
|
{
|
||||||
|
if (Game::Scr_GetNumParam() != 1 || Game::Scr_GetType(0) != Game::VAR_STRING)
|
||||||
|
{
|
||||||
|
Game::Scr_Error("^1botAction: Needs one string parameter!\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto action = 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))
|
||||||
|
{
|
||||||
|
Game::Scr_Error("^1botAction: Need to call on a player entity!\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");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (size_t i = 0; i < sizeof(BotActions) / sizeof(BotAction_t); ++i)
|
||||||
|
{
|
||||||
|
if (strcmp(&action[1], BotActions[i].action))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (action[0] == '+')
|
||||||
|
g_botai[clientNum].buttons |= BotActions[i].key;
|
||||||
|
else
|
||||||
|
g_botai[clientNum].buttons &= ~(BotActions[i].key);
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Game::Scr_Error("^1botAction: Unknown action.\n");
|
||||||
|
});
|
||||||
|
|
||||||
|
Script::AddFunction("botMovement", [](Game::scr_entref_t id) // Usage: <bot> botMovement(<int>, <int>);
|
||||||
|
{
|
||||||
|
if (Game::Scr_GetNumParam() != 2 || 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);
|
||||||
|
|
||||||
|
if (!Bots::IsValidClientNum(clientNum))
|
||||||
|
{
|
||||||
|
Game::Scr_Error("^1botMovement: Need to call on a player entity!\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (client->state < 3)
|
||||||
|
{
|
||||||
|
Game::Scr_Error("^1botMovement: Needs to be connected.\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
Bots::Bots()
|
Bots::Bots()
|
||||||
{
|
{
|
||||||
// Replace connect string
|
// Replace connect string
|
||||||
@ -78,6 +399,50 @@ 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, 0x4BB9B0, HOOK_CALL).install()->quick();
|
||||||
|
Utils::Hook(0x627241, 0x4BB9B0, HOOK_CALL).install()->quick();
|
||||||
|
|
||||||
|
// zero the bot command array
|
||||||
|
for (int i = 0; i < MAX_G_BOTAI_ENTRIES; i++)
|
||||||
|
{
|
||||||
|
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->outgoingSequence - 1;
|
||||||
|
|
||||||
|
Game::SV_ClientThink(client, &ucmd);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
Command::Add("spawnBot", [](Command::Params* params)
|
Command::Add("spawnBot", [](Command::Params* params)
|
||||||
{
|
{
|
||||||
unsigned int count = 1;
|
unsigned int count = 1;
|
||||||
@ -103,6 +468,8 @@ namespace Components
|
|||||||
|
|
||||||
Bots::Spawn(count);
|
Bots::Spawn(count);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
Bots::AddMethods();
|
||||||
}
|
}
|
||||||
|
|
||||||
Bots::~Bots()
|
Bots::~Bots()
|
||||||
|
@ -7,6 +7,8 @@ namespace Components
|
|||||||
public:
|
public:
|
||||||
Bots();
|
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;
|
||||||
@ -14,5 +16,7 @@ namespace Components
|
|||||||
static void BuildConnectString(char* buffer, const char* connectString, int num, int, int protocol, int checksum, int statVer, int statStuff, int port);
|
static void 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();
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -336,6 +336,12 @@ namespace Components
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Network::NET_DeferPacketToClientStub(Game::netadr_t* from, Game::msg_t* msg)
|
||||||
|
{
|
||||||
|
if (msg->cursize > 0 && msg->cursize <= 1404)
|
||||||
|
Game::NET_DeferPacketToClient(from, msg);
|
||||||
|
}
|
||||||
|
|
||||||
Network::Network()
|
Network::Network()
|
||||||
{
|
{
|
||||||
AssertSize(Game::netadr_t, 20);
|
AssertSize(Game::netadr_t, 20);
|
||||||
@ -372,6 +378,9 @@ namespace Components
|
|||||||
// Install packet deploy hook
|
// Install packet deploy hook
|
||||||
Utils::Hook::RedirectJump(0x5AA713, Network::DeployPacketStub);
|
Utils::Hook::RedirectJump(0x5AA713, Network::DeployPacketStub);
|
||||||
|
|
||||||
|
// Fix packets causing buffer overflow
|
||||||
|
Utils::Hook(0x6267E3, Network::NET_DeferPacketToClientStub, HOOK_CALL).install()->quick();
|
||||||
|
|
||||||
Network::Handle("resolveAddress", [](Address address, const std::string& /*data*/)
|
Network::Handle("resolveAddress", [](Address address, const std::string& /*data*/)
|
||||||
{
|
{
|
||||||
Network::SendRaw(address, address.getString());
|
Network::SendRaw(address, address.getString());
|
||||||
|
@ -58,7 +58,7 @@ namespace Components
|
|||||||
|
|
||||||
static void Handle(const std::string& packet, Utils::Slot<Callback> callback);
|
static void Handle(const std::string& packet, Utils::Slot<Callback> callback);
|
||||||
static void OnStart(Utils::Slot<CallbackRaw> callback);
|
static void OnStart(Utils::Slot<CallbackRaw> callback);
|
||||||
|
|
||||||
// Send quake-styled binary data
|
// Send quake-styled binary data
|
||||||
static void Send(Address target, const std::string& data);
|
static void Send(Address target, const std::string& data);
|
||||||
static void Send(Game::netsrc_t type, Address target, const std::string& data);
|
static void Send(Game::netsrc_t type, Address target, const std::string& data);
|
||||||
@ -88,6 +88,7 @@ namespace Components
|
|||||||
static void NetworkStartStub();
|
static void NetworkStartStub();
|
||||||
|
|
||||||
static void PacketErrorCheck();
|
static void PacketErrorCheck();
|
||||||
|
static void NET_DeferPacketToClientStub(Game::netadr_t* from, Game::msg_t* msg);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -279,25 +279,48 @@ namespace Components
|
|||||||
|
|
||||||
void Node::SendList(Network::Address address)
|
void Node::SendList(Network::Address address)
|
||||||
{
|
{
|
||||||
Proto::Node::List list;
|
|
||||||
list.set_isnode(Dedicated::IsEnabled());
|
|
||||||
list.set_protocol(PROTOCOL);
|
|
||||||
list.set_port(Node::GetPort());
|
|
||||||
|
|
||||||
std::lock_guard<std::recursive_mutex> _(Node::Mutex);
|
std::lock_guard<std::recursive_mutex> _(Node::Mutex);
|
||||||
|
|
||||||
for (auto& node : Node::Nodes)
|
// need to keep the message size below 1404 bytes else recipient will just drop it
|
||||||
{
|
std::vector<std::string> nodeListReponseMessages;
|
||||||
if (node.isValid())
|
|
||||||
{
|
|
||||||
std::string* str = list.add_nodes();
|
|
||||||
|
|
||||||
sockaddr addr = node.address.getSockAddr();
|
for (size_t curNode = 0; curNode < Node::Nodes.size();)
|
||||||
str->append(reinterpret_cast<char*>(&addr), sizeof(addr));
|
{
|
||||||
|
Proto::Node::List list;
|
||||||
|
list.set_isnode(Dedicated::IsEnabled());
|
||||||
|
list.set_protocol(PROTOCOL);
|
||||||
|
list.set_port(Node::GetPort());
|
||||||
|
|
||||||
|
for (size_t i = 0; i < NODE_MAX_NODES_TO_SEND;)
|
||||||
|
{
|
||||||
|
if (curNode >= Node::Nodes.size())
|
||||||
|
break;
|
||||||
|
|
||||||
|
auto node = Node::Nodes.at(curNode++);
|
||||||
|
|
||||||
|
if (node.isValid())
|
||||||
|
{
|
||||||
|
std::string* str = list.add_nodes();
|
||||||
|
|
||||||
|
sockaddr addr = node.address.getSockAddr();
|
||||||
|
str->append(reinterpret_cast<char*>(&addr), sizeof(addr));
|
||||||
|
|
||||||
|
i++;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
nodeListReponseMessages.push_back(list.SerializeAsString());
|
||||||
}
|
}
|
||||||
|
|
||||||
Session::Send(address, "nodeListResponse", list.SerializeAsString());
|
size_t i = 0;
|
||||||
|
for (auto& nodeListData : nodeListReponseMessages)
|
||||||
|
{
|
||||||
|
Scheduler::OnDelay([nodeListData, i, address]()
|
||||||
|
{
|
||||||
|
NODE_LOG("Sending %d nodeListResponse length to %s\n", nodeListData.length(), address.getCString());
|
||||||
|
Session::Send(address, "nodeListResponse", nodeListData);
|
||||||
|
}, NODE_SEND_RATE * i++);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
unsigned short Node::GetPort()
|
unsigned short Node::GetPort()
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#define NODE_HALFLIFE (3 * 60 * 1000) //3min
|
#define NODE_HALFLIFE (3 * 60 * 1000) //3min
|
||||||
|
#define NODE_MAX_NODES_TO_SEND 64
|
||||||
|
#define NODE_SEND_RATE 500ms
|
||||||
|
|
||||||
#ifdef NODE_LOG_MESSAGES
|
#ifdef NODE_LOG_MESSAGES
|
||||||
#define NODE_LOG(x, ...) Logger::Print(x, __VA_ARGS__)
|
#define NODE_LOG(x, ...) Logger::Print(x, __VA_ARGS__)
|
||||||
|
@ -7,6 +7,7 @@ namespace Components
|
|||||||
std::vector<Script::Function> Script::ScriptFunctions;
|
std::vector<Script::Function> Script::ScriptFunctions;
|
||||||
std::vector<std::string> Script::ScriptNameStack;
|
std::vector<std::string> Script::ScriptNameStack;
|
||||||
unsigned short Script::FunctionName;
|
unsigned short Script::FunctionName;
|
||||||
|
std::unordered_map<std::string, std::string> Script::ScriptStorage;
|
||||||
|
|
||||||
Utils::Signal<Scheduler::Callback> Script::VMShutdownSignal;
|
Utils::Signal<Scheduler::Callback> Script::VMShutdownSignal;
|
||||||
|
|
||||||
@ -331,6 +332,127 @@ namespace Components
|
|||||||
return &Game::svs_clients[gentity->number];
|
return &Game::svs_clients[gentity->number];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Script::AddFunctions()
|
||||||
|
{
|
||||||
|
// System time
|
||||||
|
Script::AddFunction("GetSystemTime", [](Game::scr_entref_t) // gsc: GetSystemTime()
|
||||||
|
{
|
||||||
|
SYSTEMTIME time;
|
||||||
|
GetSystemTime(&time);
|
||||||
|
|
||||||
|
Game::Scr_AddInt(time.wSecond);
|
||||||
|
});
|
||||||
|
|
||||||
|
Script::AddFunction("GetSystemMilliseconds", [](Game::scr_entref_t) // gsc: GetSystemMilliseconds()
|
||||||
|
{
|
||||||
|
SYSTEMTIME time;
|
||||||
|
GetSystemTime(&time);
|
||||||
|
|
||||||
|
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() != 1 || 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() != 1 || Game::Scr_GetType(0) != Game::VAR_STRING)
|
||||||
|
{
|
||||||
|
Game::Scr_Error("^1Exec: Needs one string parameter!\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto str = Game::Scr_GetString(0);
|
||||||
|
|
||||||
|
Command::Execute(str, false);
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
// Script Storage Funcs
|
||||||
|
Script::AddFunction("StorageSet", [](Game::scr_entref_t) // gsc: StorageSet(<str key>, <str data>);
|
||||||
|
{
|
||||||
|
if (Game::Scr_GetNumParam() != 2 || Game::Scr_GetType(0) != Game::VAR_STRING || Game::Scr_GetType(1) != Game::VAR_STRING)
|
||||||
|
{
|
||||||
|
Game::Scr_Error("^1StorageSet: Needs two string 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::AddFunction("StorageRemove", [](Game::scr_entref_t) // gsc: StorageRemove(<str key>);
|
||||||
|
{
|
||||||
|
if (Game::Scr_GetNumParam() != 1 || Game::Scr_GetType(0) != Game::VAR_STRING)
|
||||||
|
{
|
||||||
|
Game::Scr_Error("^1StorageRemove: Needs one string parameter!\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string key = Game::Scr_GetString(0);
|
||||||
|
|
||||||
|
if (!Script::ScriptStorage.contains(key))
|
||||||
|
{
|
||||||
|
Game::Scr_Error(Utils::String::VA("^1StorageRemove: Store does not have key '%s'!\n", key.c_str()));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Script::ScriptStorage.erase(key);
|
||||||
|
});
|
||||||
|
|
||||||
|
Script::AddFunction("StorageGet", [](Game::scr_entref_t) // gsc: StorageGet(<str key>);
|
||||||
|
{
|
||||||
|
if (Game::Scr_GetNumParam() != 1 || Game::Scr_GetType(0) != Game::VAR_STRING)
|
||||||
|
{
|
||||||
|
Game::Scr_Error("^1StorageGet: Needs one string parameter!\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string key = Game::Scr_GetString(0);
|
||||||
|
|
||||||
|
if (!Script::ScriptStorage.contains(key))
|
||||||
|
{
|
||||||
|
Game::Scr_Error(Utils::String::VA("^1StorageGet: Store does not have key '%s'!\n", key.c_str()));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto data = Script::ScriptStorage.at(key);
|
||||||
|
Game::Scr_AddString(data.c_str());
|
||||||
|
});
|
||||||
|
|
||||||
|
Script::AddFunction("StorageHas", [](Game::scr_entref_t) // gsc: StorageHas(<str key>);
|
||||||
|
{
|
||||||
|
if (Game::Scr_GetNumParam() != 1 || Game::Scr_GetType(0) != Game::VAR_STRING)
|
||||||
|
{
|
||||||
|
Game::Scr_Error("^1StorageHas: Needs one string parameter!\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string key = Game::Scr_GetString(0);
|
||||||
|
|
||||||
|
Game::Scr_AddInt(Script::ScriptStorage.contains(key));
|
||||||
|
});
|
||||||
|
|
||||||
|
Script::AddFunction("StorageClear", [](Game::scr_entref_t) // gsc: StorageClear();
|
||||||
|
{
|
||||||
|
Script::ScriptStorage.clear();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
Script::Script()
|
Script::Script()
|
||||||
{
|
{
|
||||||
Utils::Hook(0x612DB0, Script::StoreFunctionNameStub, HOOK_JUMP).install()->quick();
|
Utils::Hook(0x612DB0, Script::StoreFunctionNameStub, HOOK_JUMP).install()->quick();
|
||||||
@ -357,6 +479,8 @@ namespace Components
|
|||||||
MessageBoxA(nullptr, Game::Scr_GetString(0), "DEBUG", 0);
|
MessageBoxA(nullptr, Game::Scr_GetString(0), "DEBUG", 0);
|
||||||
}, true);
|
}, true);
|
||||||
|
|
||||||
|
Script::AddFunctions();
|
||||||
|
|
||||||
// Script::AddFunction("playviewmodelfx", [](Game::scr_entref_t /*index*/)
|
// Script::AddFunction("playviewmodelfx", [](Game::scr_entref_t /*index*/)
|
||||||
// {
|
// {
|
||||||
// /*auto Scr_Error = Utils::Hook::Call<void(const char*)>(0x42EF40);
|
// /*auto Scr_Error = Utils::Hook::Call<void(const char*)>(0x42EF40);
|
||||||
@ -391,5 +515,7 @@ namespace Components
|
|||||||
Script::ScriptNameStack.clear();
|
Script::ScriptNameStack.clear();
|
||||||
Script::ScriptFunctions.clear();
|
Script::ScriptFunctions.clear();
|
||||||
Script::VMShutdownSignal.clear();
|
Script::VMShutdownSignal.clear();
|
||||||
|
|
||||||
|
Script::ScriptStorage.clear();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -38,6 +38,7 @@ namespace Components
|
|||||||
static std::vector<Function> ScriptFunctions;
|
static std::vector<Function> ScriptFunctions;
|
||||||
static std::vector<std::string> ScriptNameStack;
|
static std::vector<std::string> ScriptNameStack;
|
||||||
static unsigned short FunctionName;
|
static unsigned short FunctionName;
|
||||||
|
static std::unordered_map<std::string, std::string> ScriptStorage;
|
||||||
|
|
||||||
static Utils::Signal<Scheduler::Callback> VMShutdownSignal;
|
static Utils::Signal<Scheduler::Callback> VMShutdownSignal;
|
||||||
|
|
||||||
@ -62,5 +63,7 @@ namespace Components
|
|||||||
static void ScrShutdownSystemStub(int);
|
static void ScrShutdownSystemStub(int);
|
||||||
|
|
||||||
static int SetExpFogStub();
|
static int SetExpFogStub();
|
||||||
|
|
||||||
|
static void AddFunctions();
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -136,6 +136,7 @@ namespace Game
|
|||||||
FS_BuildPathToFile_t FS_BuildPathToFile = FS_BuildPathToFile_t(0x4702C0);
|
FS_BuildPathToFile_t FS_BuildPathToFile = FS_BuildPathToFile_t(0x4702C0);
|
||||||
FS_IsShippedIWD_t FS_IsShippedIWD = FS_IsShippedIWD_t(0x642440);
|
FS_IsShippedIWD_t FS_IsShippedIWD = FS_IsShippedIWD_t(0x642440);
|
||||||
|
|
||||||
|
G_GetWeaponIndexForName_t G_GetWeaponIndexForName = G_GetWeaponIndexForName_t(0x49E540);
|
||||||
G_SpawnEntitiesFromString_t G_SpawnEntitiesFromString = G_SpawnEntitiesFromString_t(0x4D8840);
|
G_SpawnEntitiesFromString_t G_SpawnEntitiesFromString = G_SpawnEntitiesFromString_t(0x4D8840);
|
||||||
|
|
||||||
GScr_LoadGameTypeScript_t GScr_LoadGameTypeScript = GScr_LoadGameTypeScript_t(0x4ED9A0);
|
GScr_LoadGameTypeScript_t GScr_LoadGameTypeScript = GScr_LoadGameTypeScript_t(0x4ED9A0);
|
||||||
@ -194,6 +195,7 @@ namespace Game
|
|||||||
|
|
||||||
NET_AdrToString_t NET_AdrToString = NET_AdrToString_t(0x469880);
|
NET_AdrToString_t NET_AdrToString = NET_AdrToString_t(0x469880);
|
||||||
NET_CompareAdr_t NET_CompareAdr = NET_CompareAdr_t(0x4D0AA0);
|
NET_CompareAdr_t NET_CompareAdr = NET_CompareAdr_t(0x4D0AA0);
|
||||||
|
NET_DeferPacketToClient_t NET_DeferPacketToClient = NET_DeferPacketToClient_t(0x4C8AA0);
|
||||||
NET_ErrorString_t NET_ErrorString = NET_ErrorString_t(0x4E7720);
|
NET_ErrorString_t NET_ErrorString = NET_ErrorString_t(0x4E7720);
|
||||||
NET_Init_t NET_Init = NET_Init_t(0x491860);
|
NET_Init_t NET_Init = NET_Init_t(0x491860);
|
||||||
NET_IsLocalAddress_t NET_IsLocalAddress = NET_IsLocalAddress_t(0x402BD0);
|
NET_IsLocalAddress_t NET_IsLocalAddress = NET_IsLocalAddress_t(0x402BD0);
|
||||||
@ -253,6 +255,8 @@ namespace Game
|
|||||||
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_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);
|
||||||
|
|
||||||
@ -293,6 +297,7 @@ namespace Game
|
|||||||
SV_DirectConnect_t SV_DirectConnect = SV_DirectConnect_t(0x460480);
|
SV_DirectConnect_t SV_DirectConnect = SV_DirectConnect_t(0x460480);
|
||||||
SV_SetConfigstring_t SV_SetConfigstring = SV_SetConfigstring_t(0x4982E0);
|
SV_SetConfigstring_t SV_SetConfigstring = SV_SetConfigstring_t(0x4982E0);
|
||||||
SV_Loaded_t SV_Loaded = SV_Loaded_t(0x4EE3E0);
|
SV_Loaded_t SV_Loaded = SV_Loaded_t(0x4EE3E0);
|
||||||
|
SV_ClientThink_t SV_ClientThink = SV_ClientThink_t(0x44ADD0);
|
||||||
|
|
||||||
Sys_Error_t Sys_Error = Sys_Error_t(0x4E0200);
|
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);
|
||||||
@ -342,6 +347,7 @@ namespace Game
|
|||||||
source_t **sourceFiles = reinterpret_cast<source_t **>(0x7C4A98);
|
source_t **sourceFiles = reinterpret_cast<source_t **>(0x7C4A98);
|
||||||
keywordHash_t **menuParseKeywordHash = reinterpret_cast<keywordHash_t **>(0x63AE928);
|
keywordHash_t **menuParseKeywordHash = reinterpret_cast<keywordHash_t **>(0x63AE928);
|
||||||
|
|
||||||
|
int* svs_time = reinterpret_cast<int*>(0x31D9384);
|
||||||
int* svs_numclients = reinterpret_cast<int*>(0x31D938C);
|
int* svs_numclients = reinterpret_cast<int*>(0x31D938C);
|
||||||
client_t* svs_clients = reinterpret_cast<client_t*>(0x31D9390);
|
client_t* svs_clients = reinterpret_cast<client_t*>(0x31D9390);
|
||||||
|
|
||||||
|
@ -317,6 +317,9 @@ namespace Game
|
|||||||
typedef iwd_t*(__cdecl * FS_IsShippedIWD_t)(const char* fullpath, const char* iwd);
|
typedef iwd_t*(__cdecl * FS_IsShippedIWD_t)(const char* fullpath, const char* iwd);
|
||||||
extern FS_IsShippedIWD_t FS_IsShippedIWD;
|
extern FS_IsShippedIWD_t FS_IsShippedIWD;
|
||||||
|
|
||||||
|
typedef int(__cdecl* G_GetWeaponIndexForName_t)(char*);
|
||||||
|
extern G_GetWeaponIndexForName_t G_GetWeaponIndexForName;
|
||||||
|
|
||||||
typedef void(__cdecl* G_SpawnEntitiesFromString_t)();
|
typedef void(__cdecl* G_SpawnEntitiesFromString_t)();
|
||||||
extern G_SpawnEntitiesFromString_t G_SpawnEntitiesFromString;
|
extern G_SpawnEntitiesFromString_t G_SpawnEntitiesFromString;
|
||||||
|
|
||||||
@ -462,6 +465,9 @@ namespace Game
|
|||||||
typedef bool(__cdecl * NET_CompareAdr_t)(netadr_t a, netadr_t b);
|
typedef bool(__cdecl * NET_CompareAdr_t)(netadr_t a, netadr_t b);
|
||||||
extern NET_CompareAdr_t NET_CompareAdr;
|
extern NET_CompareAdr_t NET_CompareAdr;
|
||||||
|
|
||||||
|
typedef void(__cdecl * NET_DeferPacketToClient_t)(netadr_t *, msg_t *);
|
||||||
|
extern NET_DeferPacketToClient_t NET_DeferPacketToClient;
|
||||||
|
|
||||||
typedef const char* (__cdecl * NET_ErrorString_t)();
|
typedef const char* (__cdecl * NET_ErrorString_t)();
|
||||||
extern NET_ErrorString_t NET_ErrorString;
|
extern NET_ErrorString_t NET_ErrorString;
|
||||||
|
|
||||||
@ -615,6 +621,12 @@ 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)(int);
|
||||||
|
extern Scr_GetType_t Scr_GetType;
|
||||||
|
|
||||||
|
typedef void(__cdecl* Scr_Error_t)(const char*);
|
||||||
|
extern Scr_Error_t Scr_Error;
|
||||||
|
|
||||||
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;
|
||||||
|
|
||||||
@ -678,6 +690,9 @@ namespace Game
|
|||||||
typedef bool(__cdecl * SV_Loaded_t)();
|
typedef bool(__cdecl * SV_Loaded_t)();
|
||||||
extern SV_Loaded_t SV_Loaded;
|
extern SV_Loaded_t SV_Loaded;
|
||||||
|
|
||||||
|
typedef void(__cdecl* SV_ClientThink_t)(client_s*, usercmd_s*);
|
||||||
|
extern SV_ClientThink_t SV_ClientThink;
|
||||||
|
|
||||||
typedef int(__cdecl * Sys_Error_t)(int, char *, ...);
|
typedef int(__cdecl * Sys_Error_t)(int, char *, ...);
|
||||||
extern Sys_Error_t Sys_Error;
|
extern Sys_Error_t Sys_Error;
|
||||||
|
|
||||||
@ -769,6 +784,7 @@ namespace Game
|
|||||||
|
|
||||||
extern cmd_function_t** cmd_functions;
|
extern cmd_function_t** cmd_functions;
|
||||||
|
|
||||||
|
extern int* svs_time;
|
||||||
extern int* svs_numclients;
|
extern int* svs_numclients;
|
||||||
extern client_t* svs_clients;
|
extern client_t* svs_clients;
|
||||||
|
|
||||||
|
@ -4481,7 +4481,15 @@ namespace Game
|
|||||||
// 0
|
// 0
|
||||||
clientstate_t state;
|
clientstate_t state;
|
||||||
// 4
|
// 4
|
||||||
char pad[36];
|
char _pad[4];
|
||||||
|
// 8
|
||||||
|
int deltaMessage;
|
||||||
|
// 12
|
||||||
|
char __pad[12];
|
||||||
|
// 24
|
||||||
|
int outgoingSequence;
|
||||||
|
// 28
|
||||||
|
char pad[12];
|
||||||
// 40
|
// 40
|
||||||
netadr_t addr;
|
netadr_t addr;
|
||||||
// 60
|
// 60
|
||||||
|
Loading…
Reference in New Issue
Block a user