24 Commits

Author SHA1 Message Date
14d0f401fb vs2022 build 2022-03-18 01:17:28 +01:00
df23604b1e Update pointers 2022-03-17 19:23:52 +01:00
fed
e2960bc895 Merge pull request #14 from diamante0018/main
Update Premake5, fix git modules.
2022-02-26 12:13:25 +01:00
db90d361a9 tabs 2022-02-26 11:12:55 +00:00
6ec0c5e23c .git 2022-02-26 11:00:11 +00:00
c84d681a3a Re add unfricked json 2022-02-26 10:58:51 +00:00
62d1c8671b Re add unfricked json 2022-02-26 10:58:41 +00:00
877adfa4b6 Fix json 2022-02-26 10:54:43 +00:00
12fd44d0da New gsc func + premake5 update 2022-02-26 10:51:20 +00:00
fed
12562eb8fd Merge pull request #13 from diamante0018/main
Clean up gsc functions with additional checks
2022-02-25 12:04:16 +01:00
286481cbcd Clean up gsc functions with additional checks 2022-02-24 19:25:58 +00:00
e9a877bd74 Update dllmain.cpp 2022-02-11 19:35:46 +01:00
3060df1f05 Update pointers 2022-02-07 02:00:15 +01:00
fed
aa1ae8c574 Merge pull request #12 from diamante0018/main 2022-02-02 15:25:52 +01:00
ccd8176c25 Remove assert 2022-02-02 15:24:20 +01:00
c230a1d916 Remove incorrect offset comments 2022-02-02 12:47:46 +01:00
2785a60dc0 Add function drop all bots 2022-02-02 12:45:07 +01:00
1b65bda323 Update pointers 2022-01-29 16:37:04 +01:00
9ca07aca55 Update pointers 2022-01-23 22:09:40 +01:00
fed
10b45027d0 Merge pull request #10 from diamante0018/main
Add isBot function
2022-01-06 22:08:45 +01:00
d5c0063fee Add isBot function 2022-01-06 22:04:19 +01:00
31920b0692 Some fixes 2021-12-27 21:03:29 +01:00
fed
89e4edc00b Fix 2021-12-14 10:03:59 +01:00
fed
379a21ff7e Small fix 2021-12-13 08:06:24 +01:00
17 changed files with 264 additions and 71 deletions

7
.gitmodules vendored
View File

@ -4,12 +4,7 @@
[submodule "deps/GSL"] [submodule "deps/GSL"]
path = deps/GSL path = deps/GSL
url = https://github.com/microsoft/GSL.git url = https://github.com/microsoft/GSL.git
[submodule "deps/sol2"]
path = deps/sol2
url = https://github.com/ThePhD/sol2.git
[submodule "deps/lua"]
path = deps/lua
url = https://github.com/lua/lua.git
[submodule "deps/json"] [submodule "deps/json"]
path = deps/json path = deps/json
url = https://github.com/nlohmann/json.git url = https://github.com/nlohmann/json.git
branch = develop

2
deps/json vendored

Submodule deps/json updated: e10a3fac8a...e4643d1f1b

View File

@ -1,2 +1,3 @@
@echo off @echo off
tools\windows\premake5.exe vs2019 call git submodule update --init --recursive
tools\windows\premake5.exe vs2022

View File

@ -36,32 +36,35 @@ workspace "iw5-gsc-utils"
targetdir "%{wks.location}/bin/%{cfg.buildcfg}" targetdir "%{wks.location}/bin/%{cfg.buildcfg}"
targetname "%{prj.name}" targetname "%{prj.name}"
configurations { "Debug", "Release", }
language "C++" language "C++"
cppdialect "C++20"
architecture "x86" architecture "x86"
buildoptions "/std:c++latest"
systemversion "latest" systemversion "latest"
symbols "On"
staticruntime "On"
editandcontinue "Off"
warnings "Extra"
characterset "ASCII"
flags flags
{ {
"NoIncrementalLink", "NoIncrementalLink",
"MultiProcessorCompile", "MultiProcessorCompile",
} }
configurations { "Debug", "Release", }
symbols "On"
configuration "Release" filter "configurations:Release"
optimize "Full" optimize "Full"
defines { "NDEBUG" } defines { "NDEBUG" }
configuration{} filter {}
configuration "Debug" filter "configurations:Debug"
optimize "Debug" optimize "Debug"
defines { "DEBUG", "_DEBUG" } defines { "DEBUG", "_DEBUG" }
configuration {} filter {}
startproject "iw5-gsc-utils" startproject "iw5-gsc-utils"

View File

@ -49,6 +49,7 @@ namespace command
if (i > index) result.append(" "); if (i > index) result.append(" ");
result.append(this->get(i)); result.append(this->get(i));
} }
return result; return result;
} }
@ -96,7 +97,6 @@ namespace command
public: public:
void post_unpack() override void post_unpack() override
{ {
} }
}; };
} }

View File

@ -53,8 +53,6 @@ namespace gsc
{ {
std::vector<scripting::script_value> args; std::vector<scripting::script_value> args;
const auto top = game::scr_VmPub->top;
for (auto i = 0; i < game::scr_VmPub->outparamcount; i++) for (auto i = 0; i < game::scr_VmPub->outparamcount; i++)
{ {
const auto value = game::scr_VmPub->top[-i]; const auto value = game::scr_VmPub->top[-i];
@ -380,6 +378,20 @@ namespace gsc
return {}; return {};
}); });
function::add("dropallbots", [](const function_args&) -> scripting::script_value
{
for (auto i = 0; i < *game::svs_clientCount; i++)
{
if (game::svs_clients[i].header.state != game::CS_FREE
&& game::svs_clients[i].header.netchan.remoteAddress.type == game::NA_BOT)
{
game::SV_GameDropClient(i, "GAME_GET_TO_COVER");
}
}
return {};
});
method::add("tell", [](const game::scr_entref_t ent, const function_args& args) -> scripting::script_value method::add("tell", [](const game::scr_entref_t ent, const function_args& args) -> scripting::script_value
{ {
if (ent.classnum != 0) if (ent.classnum != 0)
@ -388,8 +400,13 @@ namespace gsc
} }
const auto client = ent.entnum; const auto client = ent.entnum;
const auto message = args[0].as<std::string>();
if (game::g_entities[client].client == nullptr)
{
throw std::runtime_error("Not a player entity");
}
const auto message = args[0].as<std::string>();
game::SV_GameSendServerCommand(client, 0, utils::string::va("%c \"%s\"", 84, message.data())); game::SV_GameSendServerCommand(client, 0, utils::string::va("%c \"%s\"", 84, message.data()));
return {}; return {};
@ -402,20 +419,56 @@ namespace gsc
throw std::runtime_error("Invalid entity"); throw std::runtime_error("Invalid entity");
} }
const auto num = ent.entnum; const auto client = ent.entnum;
if (game::g_entities[client].client == nullptr)
{
throw std::runtime_error("Not a player entity");
}
const auto toggle = args[0].as<int>(); const auto toggle = args[0].as<int>();
auto flags = game::g_entities[client].client->ps.perks[0];
auto g_client = game::g_entities[num].client; game::g_entities[client].client->ps.perks[0] = toggle
auto playerState = &g_client->ps; ? flags | 0x4000u : flags & ~0x4000u;
auto flags = playerState->perks[0];
playerState->perks[0] = toggle
? flags | 0x4000u
: flags & ~0x4000u;
return {}; return {};
}); });
method::add("isbot", [](const game::scr_entref_t ent, const function_args&) -> scripting::script_value
{
if (ent.classnum != 0)
{
throw std::runtime_error("Invalid entity");
}
const auto client = ent.entnum;
if (game::g_entities[client].client == nullptr)
{
throw std::runtime_error("Not a player entity");
}
return game::svs_clients[client].bIsTestClient;
});
method::add("arecontrolsfrozen", [](const game::scr_entref_t ent, const function_args&) -> scripting::script_value
{
if (ent.classnum != 0)
{
throw std::runtime_error("Invalid entity");
}
const auto client = ent.entnum;
if (game::g_entities[client].client == nullptr)
{
throw std::runtime_error("Not a player entity");
}
return {(game::g_entities[client].client->flags & 4) != 0};
});
utils::hook::jump(0x56C8EB, call_builtin_stub); utils::hook::jump(0x56C8EB, call_builtin_stub);
utils::hook::jump(0x56CBDC, call_builtin_method_stub); utils::hook::jump(0x56CBDC, call_builtin_method_stub);
} }

View File

@ -14,7 +14,13 @@ namespace notifies
void client_command_stub(int clientNum) void client_command_stub(int clientNum)
{ {
char cmd[1024] = { 0 }; char cmd[1024] = {0};
const auto* entity = &game::g_entities[clientNum];
if (entity->client == nullptr)
{
return; // Client is not fully in game yet
}
game::SV_Cmd_ArgvBuffer(0, cmd, 1024); game::SV_Cmd_ArgvBuffer(0, cmd, 1024);
@ -29,7 +35,7 @@ namespace notifies
const auto entity_id = game::Scr_GetEntityId(clientNum, 0); const auto entity_id = game::Scr_GetEntityId(clientNum, 0);
const auto result = callback(entity_id, {message, cmd == "say_team"s}); const auto result = callback(entity_id, {message, cmd == "say_team"s});
if (result.is<int>()) if (result.is<int>() && !hidden)
{ {
hidden = result.as<int>() == 0; hidden = result.as<int>() == 0;
} }

View File

@ -85,7 +85,6 @@ namespace scripting
callback(); callback();
} }
shutdown_callbacks = {};
g_shutdown_game_hook.invoke<void>(free_scripts); g_shutdown_game_hook.invoke<void>(free_scripts);
} }

View File

@ -13,12 +13,17 @@ namespace userinfo
{ {
utils::hook::detour sv_getuserinfo_hook; utils::hook::detour sv_getuserinfo_hook;
userinfo_map userinfo_to_map(const std::string& userinfo) userinfo_map userinfo_to_map(std::string userinfo)
{ {
userinfo_map map; userinfo_map map{};
const auto args = utils::string::split(userinfo, '\\');
for (auto i = 1; i < args.size() - 1; i += 2) if (userinfo[0] == '\\')
{
userinfo = userinfo.substr(1);
}
const auto args = utils::string::split(userinfo, '\\');
for (size_t i = 0; !args.empty() && i < (args.size() - 1); i += 2)
{ {
map[args[i]] = args[i + 1]; map[args[i]] = args[i + 1];
} }
@ -28,7 +33,7 @@ namespace userinfo
std::string map_to_userinfo(const userinfo_map& map) std::string map_to_userinfo(const userinfo_map& map)
{ {
std::string buffer; std::string buffer{};
for (const auto& value : map) for (const auto& value : map)
{ {
@ -43,9 +48,8 @@ namespace userinfo
void sv_getuserinfo_stub(int index, char* buffer, int bufferSize) void sv_getuserinfo_stub(int index, char* buffer, int bufferSize)
{ {
char _buffer[1024]; sv_getuserinfo_hook.invoke<void>(index, buffer, bufferSize);
sv_getuserinfo_hook.invoke<void>(index, _buffer, 1024); auto map = userinfo_to_map(buffer);
auto map = userinfo_to_map(_buffer);
if (userinfo_overrides.find(index) == userinfo_overrides.end()) if (userinfo_overrides.find(index) == userinfo_overrides.end())
{ {
@ -88,26 +92,36 @@ namespace userinfo
gsc::method::add("setname", [](const game::scr_entref_t ent, const gsc::function_args& args) -> scripting::script_value gsc::method::add("setname", [](const game::scr_entref_t ent, const gsc::function_args& args) -> scripting::script_value
{ {
const auto name = args[0].as<std::string>(); if (ent.classnum != 0)
if (ent.classnum != 0 || ent.entnum > 17)
{ {
throw std::runtime_error("Invalid entity"); throw std::runtime_error("Invalid entity");
} }
if (game::g_entities[ent.entnum].client == nullptr)
{
throw std::runtime_error("Not a player entity");
}
const auto name = args[0].as<std::string>();
userinfo_overrides[ent.entnum]["name"] = name; userinfo_overrides[ent.entnum]["name"] = name;
game::ClientUserinfoChanged(ent.entnum); game::ClientUserinfoChanged(ent.entnum);
return {}; return {};
}); });
gsc::method::add("resetname", [](const game::scr_entref_t ent, const gsc::function_args& args) -> scripting::script_value gsc::method::add("resetname", [](const game::scr_entref_t ent, const gsc::function_args&) -> scripting::script_value
{ {
if (ent.classnum != 0 || ent.entnum > 17) if (ent.classnum != 0)
{ {
throw std::runtime_error("Invalid entity"); throw std::runtime_error("Invalid entity");
} }
if (game::g_entities[ent.entnum].client == nullptr)
{
throw std::runtime_error("Not a player entity");
}
userinfo_overrides[ent.entnum].erase("name"); userinfo_overrides[ent.entnum].erase("name");
game::ClientUserinfoChanged(ent.entnum); game::ClientUserinfoChanged(ent.entnum);
@ -116,13 +130,18 @@ namespace userinfo
gsc::method::add("setclantag", [](const game::scr_entref_t ent, const gsc::function_args& args) -> scripting::script_value gsc::method::add("setclantag", [](const game::scr_entref_t ent, const gsc::function_args& args) -> scripting::script_value
{ {
const auto name = args[0].as<std::string>(); if (ent.classnum != 0)
if (ent.classnum != 0 || ent.entnum > 17)
{ {
throw std::runtime_error("Invalid entity"); throw std::runtime_error("Invalid entity");
} }
if (game::g_entities[ent.entnum].client == nullptr)
{
throw std::runtime_error("Not a player entity");
}
const auto name = args[0].as<std::string>();
userinfo_overrides[ent.entnum]["clantag"] = name; userinfo_overrides[ent.entnum]["clantag"] = name;
userinfo_overrides[ent.entnum]["ec_TagText"] = name; userinfo_overrides[ent.entnum]["ec_TagText"] = name;
userinfo_overrides[ent.entnum]["ec_usingTag"] = "1"; userinfo_overrides[ent.entnum]["ec_usingTag"] = "1";
@ -131,13 +150,18 @@ namespace userinfo
return {}; return {};
}); });
gsc::method::add("resetclantag", [](const game::scr_entref_t ent, const gsc::function_args& args) -> scripting::script_value gsc::method::add("resetclantag", [](const game::scr_entref_t ent, const gsc::function_args&) -> scripting::script_value
{ {
if (ent.classnum != 0 || ent.entnum > 17) if (ent.classnum != 0)
{ {
throw std::runtime_error("Invalid entity"); throw std::runtime_error("Invalid entity");
} }
if (game::g_entities[ent.entnum].client == nullptr)
{
throw std::runtime_error("Not a player entity");
}
userinfo_overrides[ent.entnum].erase("clantag"); userinfo_overrides[ent.entnum].erase("clantag");
userinfo_overrides[ent.entnum].erase("ec_TagText"); userinfo_overrides[ent.entnum].erase("ec_TagText");
userinfo_overrides[ent.entnum].erase("ec_usingTag"); userinfo_overrides[ent.entnum].erase("ec_usingTag");
@ -146,13 +170,18 @@ namespace userinfo
return {}; return {};
}); });
gsc::method::add("removeclantag", [](const game::scr_entref_t ent, const gsc::function_args& args) -> scripting::script_value gsc::method::add("removeclantag", [](const game::scr_entref_t ent, const gsc::function_args&) -> scripting::script_value
{ {
if (ent.classnum != 0 || ent.entnum > 17) if (ent.classnum != 0)
{ {
throw std::runtime_error("Invalid entity"); throw std::runtime_error("Invalid entity");
} }
if (game::g_entities[ent.entnum].client == nullptr)
{
throw std::runtime_error("Not a player entity");
}
userinfo_overrides[ent.entnum]["clantag"] = ""; userinfo_overrides[ent.entnum]["clantag"] = "";
userinfo_overrides[ent.entnum]["ec_TagText"] = ""; userinfo_overrides[ent.entnum]["ec_TagText"] = "";
userinfo_overrides[ent.entnum]["ec_usingTag"] = "0"; userinfo_overrides[ent.entnum]["ec_usingTag"] = "0";

View File

@ -1,17 +1,17 @@
#include <stdinc.hpp> #include <stdinc.hpp>
#include "loader/component_loader.hpp" #include "loader/component_loader.hpp"
BOOL APIENTRY DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved) BOOL APIENTRY DllMain(HMODULE /*hModule*/, DWORD ul_reason_for_call, LPVOID /*lpReserved*/)
{ {
if (ul_reason_for_call == DLL_PROCESS_ATTACH) if (ul_reason_for_call == DLL_PROCESS_ATTACH)
{ {
const auto value = *reinterpret_cast<DWORD*>(0x20900000); const auto value = *reinterpret_cast<DWORD*>(0x21B00000);
if (value != 0xF0681B6A) if (value != 0x64AA1902)
{ {
MessageBoxA(NULL, MessageBoxA(NULL,
"This version of iw5-gsc-utils is outdated.\n" \ "This version of iw5-gsc-utils is outdated.\n" \
"Download the latest dll from here: https://github.com/fedddddd/iw5-gsc-utils/releases" \ "Download the latest dll from here: https://github.com/fedddddd/iw5-gsc-utils/releases",
, "ERROR", MB_ICONERROR); "ERROR", MB_ICONERROR);
return FALSE; return FALSE;
} }

View File

@ -2,5 +2,11 @@
namespace game namespace game
{ {
void SV_GameDropClient(int clientNum, const char* reason)
{
if (clientNum >= 0 && clientNum < sv_maxclients->current.integer)
{
SV_DropClient(&svs_clients[clientNum], reason, true);
}
}
} }

View File

@ -29,6 +29,8 @@ namespace game
private: private:
T* dedi_; T* dedi_;
}; };
void SV_GameDropClient(int clientNum, const char* reason);
} }
#include "symbols.hpp" #include "symbols.hpp"

View File

@ -60,13 +60,11 @@ namespace scripting
array::array() array::array()
{ {
this->id_ = make_array(); this->id_ = make_array();
this->add();
} }
array::array(std::vector<script_value> values) array::array(std::vector<script_value> values)
{ {
this->id_ = make_array(); this->id_ = make_array();
this->add();
for (const auto& value : values) for (const auto& value : values)
{ {
@ -77,7 +75,6 @@ namespace scripting
array::array(std::unordered_map<std::string, script_value> values) array::array(std::unordered_map<std::string, script_value> values)
{ {
this->id_ = make_array(); this->id_ = make_array();
this->add();
for (const auto& value : values) for (const auto& value : values)
{ {

View File

@ -269,8 +269,6 @@ namespace scripting
{ {
return get_custom_field(entity, field); return get_custom_field(entity, field);
} }
return {};
} }
unsigned int make_array() unsigned int make_array()

View File

@ -26,20 +26,35 @@ namespace game
const char** argv[8]; const char** argv[8];
}; };
enum netsrc_t
{
NS_CLIENT1 = 0x0,
NS_CLIENT2 = 0x1,
NS_CLIENT3 = 0x2,
NS_CLIENT4 = 0x3,
NS_MAXCLIENTS = 0x4,
NS_SERVER = 0x4,
NS_PACKET = 0x5,
NS_INVALID_NETSRC = 0x6
};
struct msg_t struct msg_t
{ {
int overflowed; int overflowed;
int readOnly; int readOnly;
char* data; unsigned char* data;
char* splitData; unsigned char* splitData;
int maxsize; int maxsize;
int cursize; int cursize;
int splitSize; int splitSize;
int readcount; int readcount;
int bit; int bit;
int lastEntityRef; int lastEntityRef;
netsrc_t targetLocalNetID;
}; };
static_assert(sizeof(msg_t) == 0x2C);
struct XZoneInfo struct XZoneInfo
{ {
const char* name; const char* name;
@ -327,4 +342,88 @@ namespace game
int flags; int flags;
char __pad2[0xEC]; char __pad2[0xEC];
}; };
enum netadrtype_t
{
NA_BOT = 0x0,
NA_BAD = 0x1,
NA_LOOPBACK = 0x2,
NA_BROADCAST = 0x3,
NA_IP = 0x4
};
struct netadr_s
{
netadrtype_t type;
unsigned char ip[4];
unsigned __int16 port;
unsigned char ipx[10];
unsigned int addrHandleIndex;
};
static_assert(sizeof(netadr_s) == 24);
struct netchan_t
{
int outgoingSequence;
netsrc_t sock;
int dropped;
int incomingSequence;
netadr_s remoteAddress;
int qport;
int fragmentSequence;
int fragmentLength;
unsigned char* fragmentBuffer;
int fragmentBufferSize;
int unsentFragments;
int unsentFragmentStart;
int unsentLength;
unsigned char* unsentBuffer;
int unsentBufferSize;
unsigned char __pad0[0x5E0];
};
static_assert(sizeof(netchan_t) == 0x630);
enum clientState_t
{
CS_FREE = 0,
CS_ZOMBIE = 1,
CS_RECONNECTING = 2,
CS_CONNECTED = 3,
CS_CLIENTLOADING = 4,
CS_ACTIVE = 5
};
struct clientHeader_t
{
clientState_t state; // 0
int sendAsActive; // 4
int deltaMessage; // 8
int rateDealyed; // 12
int hasAckedBaselineData; // 16
int hugeSnapshotSent; // 20
netchan_t netchan; // 24
vec3_t predictedOrigin;
int predictedOriginServerTime;
int migrationState;
vec3_t predictedVehicleOrigin;
int predictedVehicleServerTime;
};
static_assert(sizeof(clientHeader_t) == 0x66C);
struct client_s
{
clientHeader_t header;
const char* dropReason;
char userinfo[1024];
char __pad0[0x41242];
unsigned __int16 scriptId; // 269490
int bIsTestClient; // 269492
int serverId; // 269496
char __pad1[0x369DC];
};
static_assert(sizeof(client_s) == 0x78698);
} }

View File

@ -57,6 +57,7 @@ namespace game
WEAK symbol<void(int clientNum, int type, const char* command)> SV_GameSendServerCommand{0x573220}; WEAK symbol<void(int clientNum, int type, const char* command)> SV_GameSendServerCommand{0x573220};
WEAK symbol<void(int arg, char* buffer, int bufferLength)> SV_Cmd_ArgvBuffer{0x5459F0}; WEAK symbol<void(int arg, char* buffer, int bufferLength)> SV_Cmd_ArgvBuffer{0x5459F0};
WEAK symbol<void(client_s* drop, const char* reason, bool tellThem)> SV_DropClient{0x570980};
WEAK symbol<void(unsigned int notifyListOwnerId, unsigned int stringValue, VariableValue* top)> VM_Notify{0x569720}; WEAK symbol<void(unsigned int notifyListOwnerId, unsigned int stringValue, VariableValue* top)> VM_Notify{0x569720};
WEAK symbol<unsigned int(unsigned int localId, const char* pos, unsigned int paramcount)> VM_Execute{0x56DFE0}; WEAK symbol<unsigned int(unsigned int localId, const char* pos, unsigned int paramcount)> VM_Execute{0x56DFE0};
@ -79,13 +80,17 @@ namespace game
WEAK symbol<gentity_s> g_entities{0x1A66E28}; WEAK symbol<gentity_s> g_entities{0x1A66E28};
WEAK symbol<unsigned int> levelEntityId{0x208E1A4}; WEAK symbol<unsigned int> levelEntityId{0x208E1A4};
WEAK symbol<dvar_t> sv_maxclients{0x1BA0E4C};
WEAK symbol<int> svs_clientCount{0x4B5CF8C};
WEAK symbol<client_s> svs_clients{0x4B5CF90};
namespace plutonium namespace plutonium
{ {
WEAK symbol<std::unordered_map<std::string, std::uint16_t>> function_map_rev{0x20693038}; WEAK symbol<std::unordered_map<std::string, std::uint16_t>> function_map_rev{0x20802D34};
WEAK symbol<std::unordered_map<std::string, std::uint16_t>> method_map_rev{0x20693058}; WEAK symbol<std::unordered_map<std::string, std::uint16_t>> method_map_rev{0x20802D54};
WEAK symbol<std::unordered_map<std::string, std::uint16_t>> token_map_rev{0x20693098}; WEAK symbol<std::unordered_map<std::string, std::uint16_t>> token_map_rev{0x20802D94};
WEAK symbol<int(const char* fmt, ...)> printf{0x208879B0}; WEAK symbol<int(const char* fmt, ...)> printf{0x209F30F0};
WEAK symbol<void*> function_table{0x2068BCF0}; WEAK symbol<void*> function_table{0x20762008};
WEAK symbol<void*> method_table{0x2068C4C0}; WEAK symbol<void*> method_table{0x207627D8};
} }
} }

Binary file not shown.