diff --git a/csgo2/csgo2.vcxproj b/csgo2/csgo2.vcxproj index 5558845..285337a 100644 --- a/csgo2/csgo2.vcxproj +++ b/csgo2/csgo2.vcxproj @@ -205,6 +205,7 @@ + @@ -224,6 +225,7 @@ + @@ -280,6 +282,8 @@ Create + + diff --git a/csgo2/csgo2.vcxproj.filters b/csgo2/csgo2.vcxproj.filters index 9951e33..3257372 100644 --- a/csgo2/csgo2.vcxproj.filters +++ b/csgo2/csgo2.vcxproj.filters @@ -285,6 +285,12 @@ 头文件\lua + + 头文件\script_engine + + + 头文件\script_engine + @@ -452,6 +458,12 @@ 头文件\lua + + 源文件\script_engine + + + 源文件\script_engine + diff --git a/csgo2/events.cpp b/csgo2/events.cpp index 8b0a85e..49917c6 100644 --- a/csgo2/events.cpp +++ b/csgo2/events.cpp @@ -4,24 +4,59 @@ namespace events { auto OnPlayerDeathEvent(IGameEvent* event) -> void { UnkGameEventStruct_t userIdNameParams{"userid"}; UnkGameEventStruct_t attackerNameParams{"attacker"}; - - const auto victim = reinterpret_cast( + UnkGameEventStruct_t headshotNameParams{ 0 }; + static const auto headShotStr = "headshot"; + headshotNameParams.m_Unk = Offset::FnServerHashFunction(headShotStr, sizeof headShotStr, SERVER_HASH_FUCNTION_KEY); + headshotNameParams.m_Key = headShotStr; + const auto victimPawn = reinterpret_cast( event->GetPlayerPawn(&userIdNameParams)); - const auto attacker = reinterpret_cast( + const auto attackerPawn = reinterpret_cast( event->GetPlayerPawn(&attackerNameParams)); - - //printf("player[%p] %s kill[%p] %llu\n", attacker, &attacker->m_iszPlayerName(), victim, &victim->m_steamID()); + const auto isHeadShot = event->GetBool(&headshotNameParams); + if (victimPawn == nullptr || attackerPawn == nullptr) { + return; + } + if (victimPawn->IsBasePlayerController() == false || + attackerPawn->IsBasePlayerController() == false) { + return; + } + const auto victim = victimPawn->GetPlayerController(); + const auto attacker = attackerPawn->GetPlayerController(); + if (victim == nullptr || attacker == nullptr) { + return; + } + const auto victimIndex = victim->GetRefEHandle().m_Index; + const auto attackerIndex = attacker->GetRefEHandle().m_Index; + LOG("is head shot: %d \n", isHeadShot); + ScriptCallBacks::luaCall_onPlayerDeath(victimIndex, attackerIndex); + // printf("player[%p] %s kill[%p] %llu\n", attacker, + // &attacker->m_iszPlayerName(), victim, &victim->m_steamID()); } auto OnPlayerChat(CCSPlayerController* player, std::string message) -> bool { - auto [procesChatSuccess, chatType, chatCtx] = SdkTools::ProcessChatString(message); + auto [procesChatSuccess, chatType, chatCtx] = + SdkTools::ProcessChatString(message); if (procesChatSuccess == false) { return false; } - - LOG("player %s say[%d]: %s steamid: %llu\n", &player->m_iszPlayerName(), chatType ,chatCtx.c_str(), player->m_steamID()); if (chatCtx.at(0) == '/' || chatCtx.at(0) == '!') { return true; } return false; } +auto OnPlayerConnect(int slot, const char* pszName, uint64_t xuid, + const char* pszNetworkID, const char* pszAddress, + bool bFakePlayer) -> void { + const auto PlayerIndex = PlayerSlot_to_EntityIndex(slot); + ScriptCallBacks::luaCall_onPlayerConnect(PlayerIndex, slot, pszName, xuid, + pszNetworkID, pszAddress, + bFakePlayer); +} +auto OnPlayerDisconnect(int slot, const char* pszName, uint64_t xuid, + const char* pszNetworkID, const char* pszAddress, + bool bFakePlayer) -> void { + const auto PlayerIndex = PlayerSlot_to_EntityIndex(slot); + ScriptCallBacks::luaCall_onPlayerDisconnect(PlayerIndex, slot, pszName, + xuid, pszNetworkID, pszAddress, + bFakePlayer); +} } // namespace events diff --git a/csgo2/events.h b/csgo2/events.h index 56bcc37..9c6f845 100644 --- a/csgo2/events.h +++ b/csgo2/events.h @@ -2,6 +2,12 @@ #include "head.h" class CCSPlayerController; namespace events { - auto OnPlayerDeathEvent(IGameEvent* event) -> void; - auto OnPlayerChat(CCSPlayerController* player, std::string message) -> bool; -} +auto OnPlayerDeathEvent(IGameEvent* event) -> void; +auto OnPlayerChat(CCSPlayerController* player, std::string message) -> bool; +auto OnPlayerConnect(int slot, const char* pszName, uint64_t xuid, + const char* pszNetworkID, const char* pszAddress, + bool bFakePlayer) -> void; +auto OnPlayerDisconnect(int slot, const char* pszName, uint64_t xuid, + const char* pszNetworkID, const char* pszAddress, + bool bFakePlayer) -> void; +} // namespace events diff --git a/csgo2/head.h b/csgo2/head.h index 887837c..ae87c81 100644 --- a/csgo2/head.h +++ b/csgo2/head.h @@ -8,28 +8,30 @@ #include #include #include +#include + #include "framework.h" #include "stb.hh" // #define LOG DebugPrintA static void DebugPrintA(const char* format, ...) { std::string temp; - va_list marker = { 0 }; + va_list marker = {0}; va_start(marker, format); size_t num_of_chars = _vscprintf(format, marker); if (num_of_chars > temp.capacity()) { temp.reserve(num_of_chars + 1); } vsprintf_s(const_cast(temp.c_str()), num_of_chars + 1, format, - marker); + marker); OutputDebugStringA(temp.c_str()); } #define LOG(...) printf(__VA_ARGS__) #define THE_GAME_SIG(sig) \ stb::simple_conversion::build::value -// +// #include "./MinHook/include/MinHook.h" #include "hash_fnv1a_constexpr.h" -//߿ +// ߿ #include "vmt.h" #include "memory.h" @@ -38,7 +40,7 @@ static void DebugPrintA(const char* format, ...) { #include "sdk/public/eiface.h" #include "sdk/player/playerslot.h" -//sdk +// sdk #include "sdk/sdk.h" #include "sdk/tier1/UtlString.hpp" #include "sdk/interfaces/interfaces.h" @@ -63,4 +65,6 @@ static void DebugPrintA(const char* format, ...) { #include "lua/lua.hpp" #include "tools.h" -#include "script_engine.h" \ No newline at end of file +#include "script_engine.h" +#include "script_apis.h" +#include "script_callbacks.h" diff --git a/csgo2/hooks.cpp b/csgo2/hooks.cpp index ca07641..8cddb34 100644 --- a/csgo2/hooks.cpp +++ b/csgo2/hooks.cpp @@ -11,10 +11,14 @@ VMTHook* VMT_IServerGameClient; void __fastcall hook_ClientDisconnect(void* rcx, CPlayerSlot slot, int reason, const char* pszName, uint64_t xuid, const char* pszNetworkID) { + bool isFakePlayer = true; if (pszNetworkID != NULL && *pszNetworkID == '[') { ExtendPlayerManager::RemovePlayerSlotBySteamId( ExtendPlayerManager::SteamIDStringToUInt64(pszNetworkID)); + isFakePlayer = false; } + events::OnPlayerDisconnect(slot.Get(), pszName, xuid, pszNetworkID, + pszNetworkID, isFakePlayer); return original_OnClientDisconnect(rcx, slot, reason, pszName, xuid, pszNetworkID); } @@ -28,7 +32,8 @@ void __fastcall hook_OnClientConnected(void* rcx, CPlayerSlot slot, ExtendPlayerManager::SteamIDStringToUInt64(pszNetworkID), slot.Get()); } - + events::OnPlayerConnect(slot.Get(), pszName, xuid, pszNetworkID, pszAddress, + bFakePlayer); return original_OnClientConnected(rcx, slot, pszName, xuid, pszNetworkID, pszAddress, bFakePlayer); } @@ -42,7 +47,7 @@ void __fastcall hook_Host_Say(void* pEntity, void* args, bool teamonly, do { if (theArgs == nullptr || theEntity == nullptr) { break; - } + } const auto message = std::string(theArgs->GetCommandString()); if (events::OnPlayerChat(theEntity, message) == true) { @@ -57,8 +62,9 @@ void __fastcall hook_Host_Say(void* pEntity, void* args, bool teamonly, if (*pMessage == '/') return; */ - if (blockMsg) { return; } - else { + if (blockMsg) { + return; + } else { return original_Host_Say(pEntity, args, teamonly, unk1, unk2); } } @@ -77,6 +83,7 @@ bool __fastcall hook_FireEventServerSide(CGameEventManager* rcx, static constexpr auto player_death = hash_32_fnv1a_const("player_death"); static constexpr auto player_chat = hash_32_fnv1a_const("player_chat"); + switch (hash_32_fnv1a_const(eventName)) { case player_death: events::OnPlayerDeathEvent(event); diff --git a/csgo2/offset.cpp b/csgo2/offset.cpp index cbe2718..2234980 100644 --- a/csgo2/offset.cpp +++ b/csgo2/offset.cpp @@ -8,7 +8,7 @@ uint64_t CGameEventManagerPtr; uint64_t Host_SayPtr; uint64_t Module_tier0; uint64_t MaxPlayerNumsPtr; - +HashFunction_t FnServerHashFunction; namespace InterFaces { CSchemaSystem* SchemaSystem; IGameEventManager2* GameEventManager; @@ -16,6 +16,7 @@ CGameEventManager* CGameEventManger; CGameResourceService* GameResourceServiceServer; IServerGameClients* IServerGameClient; IVEngineServer2* IVEngineServer; +ISource2Server* ISource2ServerInterFace; }; // namespace InterFaces auto Init() -> bool { CModule server("server.dll"); @@ -32,13 +33,16 @@ auto Init() -> bool { .ToAbsolute(3, 0) .Get(CGameEventManagerPtr); server.FindPattern(pattern_fnHost_SayPtr).Get(Host_SayPtr); + server.FindPattern(pattern_ServerHashFunctionPtr).Get(FnServerHashFunction); // schemasystem InterFaces::SchemaSystem = reinterpret_cast( schemasystem.FindInterface("SchemaSystem_001").Get()); + // engine.dll - InterFaces::GameEventManager = reinterpret_cast( - engine.FindInterface("GameEventSystemServerV001").Get()); + //InterFaces::GameEventManager = reinterpret_cast( + // engine.FindInterface("GameEventSystemServerV001").Get()); + //InterFaces::GameEventManager = reinterpret_cast(engine.FindInterface("GameEventSystemServerV001").Get()); InterFaces::GameResourceServiceServer = reinterpret_cast( engine.FindInterface("GameResourceServiceServerV001").Get()); @@ -48,17 +52,22 @@ auto Init() -> bool { // server.dll InterFaces::IServerGameClient = reinterpret_cast( server.FindInterface("Source2GameClients001").Get()); - - // only init in console server + InterFaces::ISource2ServerInterFace = reinterpret_cast( + server.FindInterface("Source2Server001").Get()); + if (InterFaces::ISource2ServerInterFace) { + InterFaces::GameEventManager = (IGameEventManager2*)(CALL_VIRTUAL(uintptr_t, 91, InterFaces::ISource2ServerInterFace) - 8); + } InterFaces::CGameEventManger = reinterpret_cast(CGameEventManagerPtr); //global::MaxPlayers = *(int*)((char*)MaxPlayerNumsPtr + 2); // client.FindPattern(pattern_FireEventServerSide).Get(FireEventServerSidePtr); + global::MaxPlayers = 64; + LOG("[huoji]FireEventServerSidePtr : %llx \n", FireEventServerSidePtr); LOG("[huoji]NetworkStateChangedPtr : %llx \n", NetworkStateChangedPtr); LOG("[huoji]Host_SayPtr : %llx \n", Host_SayPtr); - LOG("[huoji]Host_SayPtr : %llx \n", MaxPlayerNumsPtr); + LOG("[huoji]FnServerHashFunction : %llx \n", FnServerHashFunction); LOG("[huoji]MaxGlobals : %d \n", global::MaxPlayers); LOG("[huoji]InterFaces::SchemaSystem : %llx \n", InterFaces::SchemaSystem); @@ -72,10 +81,13 @@ auto Init() -> bool { InterFaces::IServerGameClient); LOG("[huoji]InterFaces::IVEngineServer : %llx \n", InterFaces::IVEngineServer); + LOG("[huoji]InterFaces::ISource2ServerInterFace : %llx \n", + InterFaces::ISource2ServerInterFace); // GetOffsets(); LOG("init offset success !\n"); - return Host_SayPtr && InterFaces::IVEngineServer && + LOG("FnServerHashFunction: %llx \n", FnServerHashFunction("here", sizeof("here") - 1, 0x31415926)); + return FnServerHashFunction && Host_SayPtr && InterFaces::IVEngineServer && InterFaces::GameResourceServiceServer && InterFaces::IServerGameClient && InterFaces::GameEventManager && InterFaces::SchemaSystem && FireEventServerSidePtr && diff --git a/csgo2/offset.h b/csgo2/offset.h index 190b1a5..576adaa 100644 --- a/csgo2/offset.h +++ b/csgo2/offset.h @@ -1,5 +1,7 @@ #pragma once #include "head.h" +#define SERVER_HASH_FUCNTION_KEY 0x31415926 +typedef uint64_t(__fastcall* HashFunction_t)(const char*, unsigned int, unsigned int); class CSchemaSystem; class CGameResourceService; @@ -28,6 +30,8 @@ static const auto pattern_fnGetLocalPlayerController = //"\"Console<0>\" say \"%s\"\n" static const auto pattern_fnHost_SayPtr = THE_GAME_SIG("44 89 4C 24 ?? 44 88 44 24 ?? 55 53 56 57 41 54 41 55"); +static const auto pattern_ServerHashFunctionPtr = + THE_GAME_SIG("48 89 ?? ?? ?? 57 48 ?? ?? ?? ?? ?? ?? 33 C0 8B DA 41 8B F8 48 89 ?? ?? ?? 4C 8B C1 C7 44 ?? ?? ?? ?? ?? ?? 44 8B CA 89 44 ?? ?? 48 8D ?? ?? ?? 88 44 ?? ?? 33 D2"); static const auto pattern_MaxPlayerNumsPtr = THE_GAME_SIG("41 3B 87 ?? ?? ?? ?? 0F 8E ?? ?? ?? ?? 8B 0D ?? ?? ?? ??"); extern uint64_t GameResourceServicePtr; @@ -36,5 +40,6 @@ extern uint64_t Module_tier0; extern uint64_t NetworkStateChangedPtr; extern uint64_t Host_SayPtr; extern uint64_t MaxPlayerNumsPtr; +extern HashFunction_t FnServerHashFunction; auto Init() -> bool; }; // namespace Offset diff --git a/csgo2/player_manager.cpp b/csgo2/player_manager.cpp index 90ff9fa..0e98ce1 100644 --- a/csgo2/player_manager.cpp +++ b/csgo2/player_manager.cpp @@ -24,13 +24,13 @@ auto SteamIDStringToUInt64(const std::string& steamID) -> uint64_t { auto AddSteamIdToPlayerSteamIdWithNameTable(uint64_t SteamId, uint64_t PlayerSlot) -> void { - std::unique_lock lock(mutex_Table_PlayerSteamIdPlayerSlot); - LOG("steamid: %llu playername: %ld \n", SteamId, PlayerSlot); + std::unique_lock lock( + mutex_Table_PlayerSteamIdPlayerSlot); Table_PlayerSteamIdPlayerSlot.insert(std::make_pair(SteamId, PlayerSlot)); } auto GetPlayerSlotBySteamId(uint64_t SteamId) -> uint64_t { - std::shared_lock lock(mutex_Table_PlayerSteamIdPlayerSlot); - LOG("steamid: %llu \n", SteamId); + std::shared_lock lock( + mutex_Table_PlayerSteamIdPlayerSlot); auto it = Table_PlayerSteamIdPlayerSlot.find(SteamId); if (it != Table_PlayerSteamIdPlayerSlot.end()) { @@ -38,12 +38,43 @@ auto GetPlayerSlotBySteamId(uint64_t SteamId) -> uint64_t { } return -1; } +auto GetPlayerSteamIdByPlayerSlot(uint64_t playerSlot) -> uint64_t { + std::shared_lock lock( + mutex_Table_PlayerSteamIdPlayerSlot); + for (auto& [SteamId, PlayerSlot] : Table_PlayerSteamIdPlayerSlot) { + if (PlayerSlot == playerSlot) { + return SteamId; + } + } + return -1; +} auto RemovePlayerSlotBySteamId(uint64_t SteamId) -> void { - std::unique_lock lock(mutex_Table_PlayerSteamIdPlayerSlot); - LOG("steamid: %llu \n", SteamId); + std::unique_lock lock( + mutex_Table_PlayerSteamIdPlayerSlot); if (Table_PlayerSteamIdPlayerSlot.find(SteamId) != Table_PlayerSteamIdPlayerSlot.end()) { Table_PlayerSteamIdPlayerSlot.erase(SteamId); } } +auto GetPlayerByPlayerSlot(uint64_t playerSlot) -> CCSPlayerController* { + auto PlayerSteamId = GetPlayerSteamIdByPlayerSlot(playerSlot); + if (PlayerSteamId == -1) { + return nullptr; + } + CGameEntitySystem* pEntitySystem = CGameEntitySystem::GetInstance(); + if (!pEntitySystem) { + return nullptr; + } + for (int i = 1; i <= global::MaxPlayers; ++i) { + CBaseEntity* pEntity = pEntitySystem->GetBaseEntity(i); + if (!pEntity) continue; + if (pEntity->IsBasePlayerController()) { + const auto player = reinterpret_cast(pEntity); + if (player->m_steamID() == PlayerSteamId) { + return player; + } + } + } + return nullptr; +} }; // namespace ExtendPlayerManager diff --git a/csgo2/player_manager.h b/csgo2/player_manager.h index 9e7b19d..0d43ae3 100644 --- a/csgo2/player_manager.h +++ b/csgo2/player_manager.h @@ -6,4 +6,5 @@ auto AddSteamIdToPlayerSteamIdWithNameTable(uint64_t SteamId, auto GetPlayerSlotBySteamId(uint64_t SteamId) -> uint64_t; auto SteamIDStringToUInt64(const std::string& steamID) -> uint64_t; auto RemovePlayerSlotBySteamId(uint64_t SteamId) -> void; +auto GetPlayerByPlayerSlot(uint64_t playerSlot) -> CCSPlayerController*; }; // namespace ExtendPlayerManager diff --git a/csgo2/script_apis.cpp b/csgo2/script_apis.cpp new file mode 100644 index 0000000..ca0440c --- /dev/null +++ b/csgo2/script_apis.cpp @@ -0,0 +1,36 @@ +#include "script_apis.h" +namespace ScriptApis { +// Ƿֵ,ֵҪԼpushstack +auto luaApi_ListenToGameEvent(lua_State* luaVm) -> int { + const auto eventName = lua_tostring(luaVm, 1); + do { + if (eventName == nullptr) { + LOG("luaApi_ListenToGameEvent eventName == nullptr || callbackName " + "== " + "nullptr\n"); + break; + } + if (!lua_isfunction(luaVm, 2)) { + LOG("luaApi_ListenToGameEvent callback is not a function\n"); + break; + } + auto callbackType = ScriptCallBacks::CallBackNameToEnum(eventName); + if (callbackType == ScriptCallBacks::_CallbackNames::kError) { + LOG("luaApi_ListenToGameEvent unknown event name: %s\n", eventName); + break; + } + // صӵӳ + std::unique_lock lock(ScriptCallBacks::mutex_callbackList); + ScriptCallBacks::callbackList[luaVm][callbackType] = luaL_ref(luaVm, LUA_REGISTRYINDEX); + + LOG("luaApi_ListenToGameEvent eventName:%s callback added\n", + eventName); + } while (false); + + lua_pop(luaVm, 2); // ջ + return 0; +} +auto initFunciton(lua_State* luaVm) -> void { + lua_register(luaVm, "ListenToGameEvent", luaApi_ListenToGameEvent); +} +}; // namespace ScriptApis diff --git a/csgo2/script_apis.h b/csgo2/script_apis.h new file mode 100644 index 0000000..ba4d353 --- /dev/null +++ b/csgo2/script_apis.h @@ -0,0 +1,5 @@ +#pragma once +#include "head.h" +namespace ScriptApis { +auto initFunciton(lua_State* luaVm) -> void; +}; diff --git a/csgo2/script_callbacks.cpp b/csgo2/script_callbacks.cpp new file mode 100644 index 0000000..fe5ba7e --- /dev/null +++ b/csgo2/script_callbacks.cpp @@ -0,0 +1,105 @@ +#include "script_callbacks.h" +#include +namespace ScriptCallBacks { +std::unordered_map> + callbackList; +std::shared_mutex mutex_callbackList; +std::unordered_map callbackNameWithEnumMap{ + {hash_32_fnv1a_const("player_connect"), _CallbackNames::kOnPlayerConnect}, + {hash_32_fnv1a_const("player_disconnect"), + _CallbackNames::kOnPlayerDisconnect}, + {hash_32_fnv1a_const("player_death"), _CallbackNames::kOnPlayerDeath}}; +auto CallBackNameToEnum(const char* name) -> _CallbackNames { + if (name == nullptr) { + return _CallbackNames::kError; + } + auto hash = hash_32_fnv1a_const(name); + auto it = callbackNameWithEnumMap.find(hash); + if (it == callbackNameWithEnumMap.end()) { + return _CallbackNames::kError; + } + return it->second; +} +auto ExcuteCallbackInAllLuaVm(_CallbackNames cbType, + std::function cb) -> void { + std::shared_lock lock(mutex_callbackList); + + for (auto& [pluginName, luaVm] : ScriptEngine::pluginEnvs) { + // find lua in callbackList + if (callbackList.find(luaVm) == callbackList.end()) { + continue; + } + const auto luaVMCallbackLists = callbackList[luaVm]; + if (luaVMCallbackLists.find(cbType) == luaVMCallbackLists.end()) { + continue; + } + LOG("excute callback %d in %s \n", cbType, pluginName.c_str()); + const auto luaRefIndex = luaVMCallbackLists.at(cbType); + cb(luaVm, luaRefIndex); + } +} +auto luaCall_onPlayerConnect(int player, int playerSlot, const char* playerName, + uint64_t xuid, const char* SteamId, + const char* IpAddress, bool isBot) -> void { + ExcuteCallbackInAllLuaVm(_CallbackNames::kOnPlayerConnect, + [&](lua_State* luaVm, int refIndex) -> void { + lua_rawgeti(luaVm, LUA_REGISTRYINDEX, + refIndex); + if (lua_isfunction(luaVm, -1)) { + lua_pushinteger(luaVm, player); + lua_pushinteger(luaVm, playerSlot); + lua_pushstring(luaVm, playerName); + lua_pushinteger(luaVm, xuid); + lua_pushstring(luaVm, SteamId); + lua_pushstring(luaVm, IpAddress); + lua_pushboolean(luaVm, isBot); + if (lua_pcall(luaVm, 7, 0, 0) != LUA_OK) { + LOG("Error calling Lua callback: %s\n", + lua_tostring(luaVm, -1)); + lua_pop(luaVm, 1); + } + } + // lua_pop(luaVm, 1); + }); +} +auto luaCall_onPlayerDisconnect(int player, int slot, const char* pszName, + uint64_t xuid, const char* pszNetworkID, + const char* pszAddress, bool bFakePlayer) + -> void { + ExcuteCallbackInAllLuaVm(_CallbackNames::kOnPlayerDisconnect, + [&](lua_State* luaVm, int refIndex) -> void { + lua_rawgeti(luaVm, LUA_REGISTRYINDEX, + refIndex); + if (lua_isfunction(luaVm, -1)) { + lua_pushinteger(luaVm, player); + lua_pushinteger(luaVm, slot); + lua_pushstring(luaVm, pszName); + lua_pushinteger(luaVm, xuid); + lua_pushstring(luaVm, pszNetworkID); + lua_pushstring(luaVm, pszAddress); + lua_pushboolean(luaVm, bFakePlayer); + if (lua_pcall(luaVm, 7, 0, 0) != LUA_OK) { + LOG("Error calling Lua callback: %s\n", + lua_tostring(luaVm, -1)); + lua_pop(luaVm, 1); + } + } + }); +} +auto luaCall_onPlayerDeath(int victim, int killer) -> void { + ExcuteCallbackInAllLuaVm(_CallbackNames::kOnPlayerDeath, + [&](lua_State* luaVm, int refIndex) -> void { + lua_rawgeti(luaVm, LUA_REGISTRYINDEX, + refIndex); + if (lua_isfunction(luaVm, -1)) { + lua_pushinteger(luaVm, victim); + lua_pushinteger(luaVm, killer); + if (lua_pcall(luaVm, 2, 0, 0) != LUA_OK) { + LOG("Error calling Lua callback: %s\n", + lua_tostring(luaVm, -1)); + lua_pop(luaVm, 1); + } + } + }); +} +} // namespace ScriptCallBacks diff --git a/csgo2/script_callbacks.h b/csgo2/script_callbacks.h new file mode 100644 index 0000000..e24cfd1 --- /dev/null +++ b/csgo2/script_callbacks.h @@ -0,0 +1,22 @@ +#pragma once +#include "head.h" +namespace ScriptCallBacks { +extern std::shared_mutex mutex_callbackList; +enum class _CallbackNames { + kError, + kOnPlayerConnect, + kOnPlayerDisconnect, + kOnPlayerDeath +}; +extern std::unordered_map> + callbackList; +auto CallBackNameToEnum(const char* name) -> ScriptCallBacks::_CallbackNames; +auto luaCall_onPlayerConnect(int player, int playerSlot, const char* playerName, + uint64_t xuid, const char* SteamId, + const char* IpAddress, bool isBot) -> void; +auto luaCall_onPlayerDisconnect(int player, int slot, const char* pszName, + uint64_t xuid, const char* pszNetworkID, + const char* pszAddress, bool bFakePlayer) + -> void; +auto luaCall_onPlayerDeath(int victim, int killer) -> void; +} // namespace ScriptCallBacks diff --git a/csgo2/script_engine.cpp b/csgo2/script_engine.cpp index dd92f03..2231086 100644 --- a/csgo2/script_engine.cpp +++ b/csgo2/script_engine.cpp @@ -1,44 +1,81 @@ #include "script_engine.h" namespace ScriptEngine { -lua_State* luaVm; std::string luaPath; -auto callFunction(const char* funcName) -> int { +std::map pluginEnvs; +std::shared_mutex mutex_pluginEnvs; + +auto callFunction(lua_State* luaVm, const char* funcName) -> int { lua_getglobal(luaVm, funcName); - _ASSERT(lua_isfunction(luaVm, -1)); - if (lua_pcall(luaVm, 0, 1, 0)) { - printf("lua_pcall_err:%s\n\n", lua_tostring(luaVm, -1)); - } - const auto result = lua_toboolean(luaVm, 1); + auto result = 0; + do { + if (lua_type(luaVm, -1) == LUA_TNIL) { + printf("lua_getglobal :%s\n\n", lua_tostring(luaVm, -1)); + result = 0; + break; + } + if (!lua_isfunction(luaVm, -1)) { + printf("lua_isfunction_err:%s\n\n", lua_tostring(luaVm, -1)); + result = 0; + break; + } + if (lua_pcall(luaVm, 0, 1, 0)) { + printf("lua_pcall_err:%s\n\n", lua_tostring(luaVm, -1)); + result = 0; + break; + } + const auto result = lua_toboolean(luaVm, 1); + } while (false); + lua_pop(luaVm, 1); return result; } -auto initFunciton() -> void { -} + auto initLuaScripts() -> void { - std::vector files; - Tools::GetFiles(luaPath, files); - if (files.size() == 0) { + auto [dirPaths, dirNames] = Tools::GetDirs(luaPath); + if (dirPaths.size() == 0) { LOG("no lua file in %s\n", luaPath.c_str()); - exit(0); + return; } - for (size_t i = 0; i < files.size(); i++) { - std::string file = files[i]; - if (file.find(".lua") != std::string::npos) { - LOG("excute: %s\n", file.c_str()); + std::unique_lock lock(mutex_pluginEnvs); + for (size_t i = 0; i < dirPaths.size(); i++) { + std::string dirPath = dirPaths[i]; + std::string dirName = dirNames[i]; - if (luaL_dofile(luaVm, file.c_str())) { - LOG("dofile_err:%s\n\n", lua_tostring(luaVm, -1)); - continue; - } - callFunction("main"); + lua_State* L = luaL_newstate(); + ScriptApis::initFunciton(L); + + luaL_openlibs(L); + pluginEnvs[dirName] = L; + + std::string file = dirPath + "\\main.lua"; + if (std::filesystem::exists(file) == false) { + continue; } - } + LOG("execute: %s\n", file.c_str()); + if (luaL_dofile(L, file.c_str())) { + LOG("dofile_err:%s\n\n", lua_tostring(L, -1)); + continue; + } + callFunction(L, "Main"); + } } -auto Init() -> void { - luaVm = luaL_newstate(); - luaPath = Tools::GetExePath() + "\\huoji_scripts\\"; - initFunciton(); + +auto releaseLuaScripts() -> void { + std::unique_lock lock(mutex_pluginEnvs); + for (auto& pair : pluginEnvs) { + lua_close(pair.second); + } + pluginEnvs.clear(); +} + +auto reloadLuaScripts() -> void { + releaseLuaScripts(); initLuaScripts(); } +auto Init() -> void { + // luaPath = Tools::GetExePath() + "\\huoji_scripts\\"; + luaPath = "F:\\source2\\huoji_scripts\\"; + initLuaScripts(); } +} // namespace ScriptEngine diff --git a/csgo2/script_engine.h b/csgo2/script_engine.h index 9a9c9f9..672f5e3 100644 --- a/csgo2/script_engine.h +++ b/csgo2/script_engine.h @@ -1,6 +1,11 @@ #pragma once #include "head.h" namespace ScriptEngine { - auto Init() -> void; - auto callFunction(const char* funcName) -> int; -} \ No newline at end of file +extern std::map pluginEnvs; +extern std::shared_mutex mutex_pluginEnvs; +extern std::shared_mutex mutex_pluginEnvs; +auto Init() -> void; +auto callFunction(const char* funcName) -> int; +auto releaseLuaScripts() -> void; +auto reloadLuaScripts() -> void; +} // namespace ScriptEngine diff --git a/csgo2/sdk/gameevent/IGameEvent.h b/csgo2/sdk/gameevent/IGameEvent.h index 5df198c..2491c27 100644 --- a/csgo2/sdk/gameevent/IGameEvent.h +++ b/csgo2/sdk/gameevent/IGameEvent.h @@ -10,7 +10,6 @@ class IToolGameEventAPI { virtual void unk001(void*) = 0; }; - struct UnkGameEventStruct_t { UnkGameEventStruct_t(const char* keyName) { m_Unk = 0; @@ -24,6 +23,7 @@ struct UnkGameEventStruct_t { class IGameEvent { public: + // 0 virtual ~IGameEvent() {}; virtual const char* GetName() const = 0; // get event name virtual int GetID() const = 0; @@ -32,8 +32,8 @@ public: virtual bool IsLocal() const = 0; // if event is never networked virtual bool IsEmpty(const char* keyName = NULL) = 0; // check if data field exists - // Data access - virtual bool GetBool(const char* keyName = NULL, bool defaultValue = false) = 0; + // Data access index 6 + virtual bool GetBool(UnkGameEventStruct_t* keyName = NULL, bool defaultValue = false) = 0; virtual int GetInt(const char* keyName = NULL, int defaultValue = 0) = 0; virtual uint64_t GetUint64(const char* keyName = NULL, uint64_t defaultValue = 0) = 0; virtual float GetFloat(const char* keyName = NULL, float defaultValue = 0.0f) = 0; diff --git a/csgo2/sdk/public/eiface.h b/csgo2/sdk/public/eiface.h index 3cff241..7ff2882 100644 --- a/csgo2/sdk/public/eiface.h +++ b/csgo2/sdk/public/eiface.h @@ -21,6 +21,11 @@ class CGlobalVars; class CSharedEdictChangeInfo; class IAchievementMgr; class CCommandContext; +class EconControlPointInfo_t; +struct EconItemInfo_t { + +}; +class bf_write; typedef uint32_t SpawnGroupHandle_t; typedef uint32_t SwapChainHandle_t; struct CEntityIndex @@ -78,7 +83,98 @@ public: virtual void* UnknownFunc1(const char* pszFilename, void* pUnknown1, void* pUnknown2, void* pUnknown3) = 0; virtual void UnknownFunc2() = 0; }; +class ISource2Server : public IAppSystem +{ +public: + virtual bool Unknown0() const = 0; + virtual void SetGlobals(CGlobalVars* pGlobals) = 0; + + // Let the game .dll allocate it's own network/shared string tables + virtual void GameCreateNetworkStringTables(void) = 0; + + virtual void WriteSignonMessages(const bf_write& buf) = 0; + + virtual void PreWorldUpdate(bool simulating) = 0; + + virtual void* GetEntity2Networkables(void) const = 0; + + virtual void* GetEntityInfo() = 0; + + // Called to apply lobby settings to a dedicated server + virtual void ApplyGameSettings(KeyValues* pKV) = 0; + + // The server should run physics/think on all edicts + // One of these bools is 'simulating'... probably + virtual void GameFrame(bool simulating, bool bFirstTick, bool bLastTick) = 0; + + // Returns true if the game DLL wants the server not to be made public. + // Used by commentary system to hide multiplayer commentary servers from the master. + virtual bool ShouldHideFromMasterServer(bool bServerHasPassword) = 0; + + virtual void GetMatchmakingTags(char* buf, size_t bufSize) = 0; + + virtual void ServerHibernationUpdate(bool bHibernating) = 0; + + virtual void* GetServerGCLobby() = 0; + + virtual void GetMatchmakingGameData(CBufferString& buf) = 0; + + // return true to disconnect client due to timeout (used to do stricter timeouts when the game is sure the client isn't loading a map) + virtual bool ShouldTimeoutClient(int nUserID, float flTimeSinceLastReceived) = 0; + + virtual void PrintStatus(CEntityIndex nPlayerEntityIndex, CBufferString& output) = 0; + + virtual int GetServerGameDLLFlags(void) const = 0; + + // Get the list of cvars that require tags to show differently in the server browser + virtual void GetTaggedConVarList(KeyValues* pCvarTagList) = 0; + + // Give the list of datatable classes to the engine. The engine matches class names from here with + // edict_t::classname to figure out how to encode a class's data for networking + virtual void* GetAllServerClasses(void) = 0; + + virtual const char* GetActiveWorldName(void) const = 0; + + virtual bool IsPaused(void) const = 0; + + virtual bool GetNavMeshData(void* pNavMeshData) = 0; + virtual void SetNavMeshData(const void* navMeshData) = 0; + virtual void RegisterNavListener(void* pNavListener) = 0; + virtual void UnregisterNavListener(void* pNavListener) = 0; + virtual void* GetSpawnDebugInterface(void) = 0; + virtual void* Unknown1(void) = 0; + virtual void* GetToolGameSimulationAPI(void) = 0; + virtual void GetAnimationActivityList(void* activityList) = 0; + virtual void GetAnimationEventList(void* eventList) = 0; + virtual void FilterPlayerCounts(int* pInOutHumans, int* pInOutHumansSlots, int* pInOutBots) = 0; + + // Called after the steam API has been activated post-level startup + virtual void GameServerSteamAPIActivated(void) = 0; + + virtual void GameServerSteamAPIDeactivated(void) = 0; + + virtual void OnHostNameChanged(const char* pHostname) = 0; + virtual void PreFatalShutdown(void) const = 0; + virtual void UpdateWhenNotInGame(float flFrameTime) = 0; + + virtual void GetEconItemNamesForModel(const char* pModelName, bool bExcludeItemSets, bool bExcludeIndividualItems, void* econItemNames) = 0; + virtual void GetEconItemNamesForCharacter(const char* pCharacterName, bool bExcludeItemSets, bool bExcludeIndividualItems, void* econItemNames) = 0; + virtual void GetEconItemsInfoForModel(const char* pModelName, const char* pEconItemName, bool bExcludeItemSets, bool bExcludeIndividualItems, bool bExcludeStockItemSet, void* econInfo) = 0; + virtual void GetEconItemsInfoForCharacter(const char* pCharacterName, const char* pEconItemName, bool bExcludeItemSets, bool bExcludeIndividualItems, bool bExcludeStockItemSet, void* econInfo) = 0; + + virtual void GetDefaultScaleForModel(const char* pModelName, bool bCheckLoadoutScale) = 0; + virtual void GetDefaultScaleForCharacter(const char* pCharacterName, bool bCheckLoadoutScale) = 0; + virtual void GetDefaultControlPointAutoUpdates(const char* pParticleSystemName, void* autoUpdates) = 0; + virtual void GetCharacterNameForModel(const char* pModelName, bool bCheckItemModifiers, CUtlString& characterName) = 0; + virtual void GetModelNameForCharacter(const char* pCharacterNamel, int nIndex, CBufferString& modelName) = 0; + virtual void GetCharacterList(void* characterNames) = 0; + virtual void GetDefaultChoreoDirForModel(const char* pModelName, CBufferString& defaultVCDDir) = 0; + + virtual void* GetEconItemSystem(void) = 0; + + virtual void ServerConVarChanged(const char* pVarName, const char* pValue) = 0; +}; //----------------------------------------------------------------------------- // Purpose: Interface the engine exposes to the game DLL //----------------------------------------------------------------------------- diff --git a/csgo2/sdk_tools.h b/csgo2/sdk_tools.h index 8f8c06e..ea8abee 100644 --- a/csgo2/sdk_tools.h +++ b/csgo2/sdk_tools.h @@ -1,5 +1,7 @@ #pragma once #include "head.h" +inline int PlayerSlot_to_EntityIndex(int PlayerSlot) { return PlayerSlot + 1; } +inline int EntityIndex_to_PlayerSlot(int EntityIndex) { return EntityIndex - 1; } enum _ChatType { kTeam, diff --git a/csgo2/tools.cpp b/csgo2/tools.cpp index 4c5e0eb..588925f 100644 --- a/csgo2/tools.cpp +++ b/csgo2/tools.cpp @@ -1,35 +1,61 @@ #include "tools.h" namespace Tools { - - auto GetFiles(const std::string& path, std::vector& files) - -> void { - std::string pattern(path); - pattern.append("\\*"); - WIN32_FIND_DATAA data; - HANDLE hFind; - if ((hFind = FindFirstFileA(pattern.c_str(), &data)) != - INVALID_HANDLE_VALUE) { - do { - if (data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) { - if (strcmp(data.cFileName, ".") != 0 && - strcmp(data.cFileName, "..") != 0) { - GetFiles((path + "\\" + data.cFileName), files); - } +auto GetDirs(const std::string& path) + -> std::pair, std::vector> { + std::vector dirPaths; + std::vector dirNames; + std::string pattern(path); + pattern.append("\\*"); + WIN32_FIND_DATAA data; + HANDLE hFind; + if ((hFind = FindFirstFileA(pattern.c_str(), &data)) != + INVALID_HANDLE_VALUE) { + do { + if (data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) { + if (strcmp(data.cFileName, ".") != 0 && + strcmp(data.cFileName, "..") != 0) { + dirPaths.push_back(path + "\\" + data.cFileName); + dirNames.push_back(data.cFileName); + auto subDirs = GetDirs(path + "\\" + data.cFileName); + dirPaths.insert(dirPaths.end(), subDirs.first.begin(), + subDirs.first.end()); + dirNames.insert(dirNames.end(), subDirs.second.begin(), + subDirs.second.end()); } - else { - files.push_back(path + "\\" + data.cFileName); + } + } while (FindNextFileA(hFind, &data) != 0); + FindClose(hFind); + } + return {dirPaths, dirNames}; +} +auto GetFiles(const std::string& path, std::vector& files) + -> void { + std::string pattern(path); + pattern.append("\\*"); + WIN32_FIND_DATAA data; + HANDLE hFind; + if ((hFind = FindFirstFileA(pattern.c_str(), &data)) != + INVALID_HANDLE_VALUE) { + do { + if (data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) { + if (strcmp(data.cFileName, ".") != 0 && + strcmp(data.cFileName, "..") != 0) { + GetFiles((path + "\\" + data.cFileName), files); } - } while (FindNextFileA(hFind, &data) != 0); - FindClose(hFind); - } + } else { + files.push_back(path + "\\" + data.cFileName); + } + } while (FindNextFileA(hFind, &data) != 0); + FindClose(hFind); } - auto GetExeFileName() -> std::string { - char buffer[MAX_PATH]; - GetModuleFileNameA(NULL, buffer, MAX_PATH); - return std::string(buffer); - } - auto GetExePath() -> std::string { - std::string f = GetExeFileName(); - return f.substr(0, f.find_last_of("\\/")); - } -} \ No newline at end of file +} +auto GetExeFileName() -> std::string { + char buffer[MAX_PATH]; + GetModuleFileNameA(NULL, buffer, MAX_PATH); + return std::string(buffer); +} +auto GetExePath() -> std::string { + std::string f = GetExeFileName(); + return f.substr(0, f.find_last_of("\\/")); +} +} // namespace Tools diff --git a/csgo2/tools.h b/csgo2/tools.h index bbbb21f..75d194c 100644 --- a/csgo2/tools.h +++ b/csgo2/tools.h @@ -1,7 +1,9 @@ #pragma once #include "head.h" namespace Tools { - auto GetExeFileName() -> std::string; - auto GetExePath() -> std::string; - auto GetFiles(const std::string& path, std::vector& files) -> void; -}; \ No newline at end of file +auto GetExeFileName() -> std::string; +auto GetExePath() -> std::string; +auto GetFiles(const std::string& path, std::vector& files) -> void; +auto GetDirs(const std::string& path) + -> std::pair, std::vector>; +}; // namespace Tools