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"
|
||||
|
||||
#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
|
||||
{
|
||||
/* 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)
|
||||
{
|
||||
static int botId = 0;
|
||||
const char* botName;
|
||||
|
||||
if (Bots::BotNames.empty())
|
||||
{
|
||||
@ -27,15 +109,19 @@ namespace Components
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (Bots::BotNames.empty())
|
||||
if (!Bots::BotNames.empty())
|
||||
{
|
||||
Bots::BotNames.push_back("bot");
|
||||
botId %= Bots::BotNames.size();
|
||||
botName = Bots::BotNames[botId++].data();
|
||||
}
|
||||
else
|
||||
{
|
||||
botName = Utils::String::VA("bot%d", ++botId);
|
||||
}
|
||||
|
||||
botId %= Bots::BotNames.size();
|
||||
strncpy_s(buffer, 0x400, Utils::String::VA(connectString, num, Bots::BotNames[botId++].data(), protocol, checksum, statVer, statStuff, port), 0x400);
|
||||
strncpy_s(buffer, 0x400, Utils::String::VA(connectString, num, botName, protocol, checksum, statVer, statStuff, port), 0x400);
|
||||
}
|
||||
|
||||
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()
|
||||
{
|
||||
// Replace connect string
|
||||
@ -78,6 +399,50 @@ 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();
|
||||
|
||||
// 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)
|
||||
{
|
||||
unsigned int count = 1;
|
||||
@ -103,6 +468,8 @@ namespace Components
|
||||
|
||||
Bots::Spawn(count);
|
||||
});
|
||||
|
||||
Bots::AddMethods();
|
||||
}
|
||||
|
||||
Bots::~Bots()
|
||||
|
@ -7,6 +7,8 @@ namespace Components
|
||||
public:
|
||||
Bots();
|
||||
~Bots();
|
||||
static unsigned int GetClientNum(Game::client_s*);
|
||||
static bool IsValidClientNum(unsigned int);
|
||||
|
||||
private:
|
||||
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 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()
|
||||
{
|
||||
AssertSize(Game::netadr_t, 20);
|
||||
@ -372,6 +378,9 @@ namespace Components
|
||||
// Install packet deploy hook
|
||||
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::SendRaw(address, address.getString());
|
||||
|
@ -88,6 +88,7 @@ namespace Components
|
||||
static void NetworkStartStub();
|
||||
|
||||
static void PacketErrorCheck();
|
||||
static void NET_DeferPacketToClientStub(Game::netadr_t* from, Game::msg_t* msg);
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -278,26 +278,49 @@ namespace Components
|
||||
}
|
||||
|
||||
void Node::SendList(Network::Address address)
|
||||
{
|
||||
std::lock_guard<std::recursive_mutex> _(Node::Mutex);
|
||||
|
||||
// need to keep the message size below 1404 bytes else recipient will just drop it
|
||||
std::vector<std::string> nodeListReponseMessages;
|
||||
|
||||
for (size_t curNode = 0; curNode < Node::Nodes.size();)
|
||||
{
|
||||
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);
|
||||
|
||||
for (auto& node : Node::Nodes)
|
||||
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++;
|
||||
}
|
||||
}
|
||||
|
||||
Session::Send(address, "nodeListResponse", list.SerializeAsString());
|
||||
nodeListReponseMessages.push_back(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()
|
||||
|
@ -1,6 +1,8 @@
|
||||
#pragma once
|
||||
|
||||
#define NODE_HALFLIFE (3 * 60 * 1000) //3min
|
||||
#define NODE_MAX_NODES_TO_SEND 64
|
||||
#define NODE_SEND_RATE 500ms
|
||||
|
||||
#ifdef NODE_LOG_MESSAGES
|
||||
#define NODE_LOG(x, ...) Logger::Print(x, __VA_ARGS__)
|
||||
|
@ -7,6 +7,7 @@ namespace Components
|
||||
std::vector<Script::Function> Script::ScriptFunctions;
|
||||
std::vector<std::string> Script::ScriptNameStack;
|
||||
unsigned short Script::FunctionName;
|
||||
std::unordered_map<std::string, std::string> Script::ScriptStorage;
|
||||
|
||||
Utils::Signal<Scheduler::Callback> Script::VMShutdownSignal;
|
||||
|
||||
@ -331,6 +332,127 @@ namespace Components
|
||||
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()
|
||||
{
|
||||
Utils::Hook(0x612DB0, Script::StoreFunctionNameStub, HOOK_JUMP).install()->quick();
|
||||
@ -357,6 +479,8 @@ namespace Components
|
||||
MessageBoxA(nullptr, Game::Scr_GetString(0), "DEBUG", 0);
|
||||
}, true);
|
||||
|
||||
Script::AddFunctions();
|
||||
|
||||
// Script::AddFunction("playviewmodelfx", [](Game::scr_entref_t /*index*/)
|
||||
// {
|
||||
// /*auto Scr_Error = Utils::Hook::Call<void(const char*)>(0x42EF40);
|
||||
@ -391,5 +515,7 @@ namespace Components
|
||||
Script::ScriptNameStack.clear();
|
||||
Script::ScriptFunctions.clear();
|
||||
Script::VMShutdownSignal.clear();
|
||||
|
||||
Script::ScriptStorage.clear();
|
||||
}
|
||||
}
|
||||
|
@ -38,6 +38,7 @@ namespace Components
|
||||
static std::vector<Function> ScriptFunctions;
|
||||
static std::vector<std::string> ScriptNameStack;
|
||||
static unsigned short FunctionName;
|
||||
static std::unordered_map<std::string, std::string> ScriptStorage;
|
||||
|
||||
static Utils::Signal<Scheduler::Callback> VMShutdownSignal;
|
||||
|
||||
@ -62,5 +63,7 @@ namespace Components
|
||||
static void ScrShutdownSystemStub(int);
|
||||
|
||||
static int SetExpFogStub();
|
||||
|
||||
static void AddFunctions();
|
||||
};
|
||||
}
|
||||
|
@ -136,6 +136,7 @@ namespace Game
|
||||
FS_BuildPathToFile_t FS_BuildPathToFile = FS_BuildPathToFile_t(0x4702C0);
|
||||
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);
|
||||
|
||||
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_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_Init_t NET_Init = NET_Init_t(0x491860);
|
||||
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_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_GetType_t Scr_GetType = Scr_GetType_t(0x422900);
|
||||
|
||||
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_SetConfigstring_t SV_SetConfigstring = SV_SetConfigstring_t(0x4982E0);
|
||||
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_FreeFileList_t Sys_FreeFileList = Sys_FreeFileList_t(0x4D8580);
|
||||
@ -342,6 +347,7 @@ namespace Game
|
||||
source_t **sourceFiles = reinterpret_cast<source_t **>(0x7C4A98);
|
||||
keywordHash_t **menuParseKeywordHash = reinterpret_cast<keywordHash_t **>(0x63AE928);
|
||||
|
||||
int* svs_time = reinterpret_cast<int*>(0x31D9384);
|
||||
int* svs_numclients = reinterpret_cast<int*>(0x31D938C);
|
||||
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);
|
||||
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)();
|
||||
extern G_SpawnEntitiesFromString_t G_SpawnEntitiesFromString;
|
||||
|
||||
@ -462,6 +465,9 @@ namespace Game
|
||||
typedef bool(__cdecl * NET_CompareAdr_t)(netadr_t a, netadr_t b);
|
||||
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)();
|
||||
extern NET_ErrorString_t NET_ErrorString;
|
||||
|
||||
@ -615,6 +621,12 @@ namespace Game
|
||||
typedef bool(__cdecl * Scr_IsSystemActive_t)();
|
||||
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);
|
||||
extern Script_Alloc_t Script_Alloc;
|
||||
|
||||
@ -678,6 +690,9 @@ namespace Game
|
||||
typedef bool(__cdecl * SV_Loaded_t)();
|
||||
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 *, ...);
|
||||
extern Sys_Error_t Sys_Error;
|
||||
|
||||
@ -769,6 +784,7 @@ namespace Game
|
||||
|
||||
extern cmd_function_t** cmd_functions;
|
||||
|
||||
extern int* svs_time;
|
||||
extern int* svs_numclients;
|
||||
extern client_t* svs_clients;
|
||||
|
||||
|
@ -4481,7 +4481,15 @@ namespace Game
|
||||
// 0
|
||||
clientstate_t state;
|
||||
// 4
|
||||
char pad[36];
|
||||
char _pad[4];
|
||||
// 8
|
||||
int deltaMessage;
|
||||
// 12
|
||||
char __pad[12];
|
||||
// 24
|
||||
int outgoingSequence;
|
||||
// 28
|
||||
char pad[12];
|
||||
// 40
|
||||
netadr_t addr;
|
||||
// 60
|
||||
|
Loading…
Reference in New Issue
Block a user