diff --git a/.gitignore b/.gitignore index 0f1bf8c2..cc670aea 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,8 @@ build/ MSVC14/Debug/ MSVC14/Release/ +MSVC14/Binary/ +MSVC14/packages/ MSVC14/*.suo MSVC14/*.user MSVC14/.vs diff --git a/AMBuilder b/AMBuilder index 0af08396..1853d996 100644 --- a/AMBuilder +++ b/AMBuilder @@ -25,6 +25,7 @@ sourceFiles = [ 'prop.cpp', 'util/trace.cpp', 'util/backtrace.cpp', + 'util/rtti.cpp', 'stub/baseentity.cpp', 'stub/gamerules.cpp', 'stub/misc.cpp', @@ -43,9 +44,10 @@ sourceFiles = [ 'mod/mod_stickypusher.cpp', 'mod/mod_robottickle.cpp', 'mod/mod_botmulticlassitem.cpp', - 'mod/debug_airblastbox.cpp', - 'mod/debug_deflectcylinder.cpp', - 'mod/debug_ehsphere.cpp', + 'mod/mod_robotheadnoslide.cpp', + 'mod/visualize_airblastbox.cpp', + 'mod/visualize_deflectcylinder.cpp', + 'mod/visualize_ehsphere.cpp', ] ############### diff --git a/MSVC14/Performance.psess b/MSVC14/Performance.psess index 84a5cb49..8dfaa4fc 100644 --- a/MSVC14/Performance.psess +++ b/MSVC14/Performance.psess @@ -4,7 +4,7 @@ sigsegv.sln Instrumentation None - C:\Profiling\Report.vsp + C:\Profiling\Instrumentation.vsp true true Timestamp @@ -14,6 +14,8 @@ 10 false + + @@ -30,14 +32,26 @@ false false + + + + + + false + + + + + + false - Release\sigsegv.ext.2.tf2.dll + Binary\sigsegv.ext.2.tf2.dll 01/01/0001 00:00:00 true true @@ -54,7 +68,7 @@ IIS InternetExplorer - true + false false false @@ -85,7 +99,7 @@ +exec cheat IIS InternetExplorer - true + false false false diff --git a/MSVC14/hl2.psess b/MSVC14/hl2.psess new file mode 100644 index 00000000..c9077406 --- /dev/null +++ b/MSVC14/hl2.psess @@ -0,0 +1,103 @@ + + + + sigsegv.sln + Sampling + None + C:\Profiling\Sampling.vsp + true + true + Timestamp + Cycles + 10000000 + 10 + 10 + + false + + + + false + 500 + + \(_Total)\ + \(_Total)\ + \\ + + + + true + false + false + + false + + + false + + + + C:\Program Files (x86)\Steam\SteamApps\common\Team Fortress 2\hl2.exe + 01/01/0001 00:00:00 + true + false + false + false + false + false + false + false + true + Executable + C:\Program Files (x86)\Steam\SteamApps\common\Team Fortress 2\hl2.exe + C:\Program Files (x86)\Steam\SteamApps\common\Team Fortress 2\ + -steam -game tf -novid -console -condebug -insecure -windowed -w 1280 -h 720 +maxplayers 32 +developer 1 +map mvm_example + +exec cheat + IIS + InternetExplorer + true + false + + false + + + false + + + + Binary\sigsegv.ext.2.tf2.dll + 01/01/0001 00:00:00 + true + true + false + false + false + false + false + true + false + Executable + .\ + + + IIS + InternetExplorer + true + false + + false + + + false + + {B3E797CF-4E77-4C9D-B8A8-7589B6902206}|sigsegv.vcxproj + sigsegv.vcxproj + sigsegv + + + + + C:\Program Files (x86)\Steam\SteamApps\common\Team Fortress 2\hl2.exe + + + \ No newline at end of file diff --git a/MSVC14/packages.config b/MSVC14/packages.config new file mode 100644 index 00000000..e4a801d0 --- /dev/null +++ b/MSVC14/packages.config @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/MSVC14/sigsegv.vcxproj b/MSVC14/sigsegv.vcxproj index ff1714bb..0e210149 100644 --- a/MSVC14/sigsegv.vcxproj +++ b/MSVC14/sigsegv.vcxproj @@ -15,6 +15,7 @@ sigsegv Win32Proj sigsegv + 8.1 @@ -55,6 +56,12 @@ sigsegv.ext.2.tf2 sigsegv.ext.2.tf2 + + $(SolutionDir)Binary\ + + + $(SolutionDir)Binary\ + /D SE_EPISODEONE=1 /D SE_DARKMESSIAH=2 /D SE_ORANGEBOX=3 /D SE_BLOODYGOODTIME=4 /D SE_EYE=5 /D SE_CSS=6 /D SE_ORANGEBOXVALVE=7 /D SE_LEFT4DEAD=8 /D SE_LEFT4DEAD2=9 /D SE_ALIENSWARM=10 /D SE_PORTAL2=11 /D SE_CSGO=12 @@ -91,8 +98,8 @@ true - copy $(TargetDir)\sigsegv.ext.2.tf2.dll $(SolutionDir)\..\build\package\addons\sourcemod\extensions\ -copy $(TargetDir)\sigsegv.ext.2.tf2.pdb $(SolutionDir)\..\build\package\addons\sourcemod\extensions\ + + @@ -128,8 +135,8 @@ copy $(TargetDir)\sigsegv.ext.2.tf2.pdb $(SolutionDir)\..\build\package\addons\s true - copy $(TargetDir)\sigsegv.ext.2.tf2.dll $(SolutionDir)\..\build\package\addons\sourcemod\extensions\ -copy $(TargetDir)\sigsegv.ext.2.tf2.pdb $(SolutionDir)\..\build\package\addons\sourcemod\extensions\ + + @@ -155,15 +162,16 @@ copy $(TargetDir)\sigsegv.ext.2.tf2.pdb $(SolutionDir)\..\build\package\addons\s - - - + + + + @@ -179,6 +187,7 @@ copy $(TargetDir)\sigsegv.ext.2.tf2.pdb $(SolutionDir)\..\build\package\addons\s + @@ -224,11 +233,22 @@ copy $(TargetDir)\sigsegv.ext.2.tf2.pdb $(SolutionDir)\..\build\package\addons\s + + + + + + + + This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}. + + + \ No newline at end of file diff --git a/MSVC14/sigsegv.vcxproj.filters b/MSVC14/sigsegv.vcxproj.filters index 64126386..1366c1c8 100644 --- a/MSVC14/sigsegv.vcxproj.filters +++ b/MSVC14/sigsegv.vcxproj.filters @@ -74,15 +74,6 @@ Source Files - - Source Files - - - Source Files - - - Source Files - Source Files @@ -134,6 +125,21 @@ Source Files + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + @@ -271,5 +277,11 @@ Header Files + + Header Files + + + + \ No newline at end of file diff --git a/PCH.mak b/PCH.mak index 2ebf1e92..1091d806 100644 --- a/PCH.mak +++ b/PCH.mak @@ -1,2 +1,4 @@ $(OUTFILE): $(INFILE) + #-rm $(OUTFILE) + #-touch $(OUTFILE) $(CXX) -x c++-header $(INFILE) -o $(OUTFILE) diff --git a/PackageScript b/PackageScript index 3fd27540..2842d738 100644 --- a/PackageScript +++ b/PackageScript @@ -40,6 +40,7 @@ def CopyFiles(src, dest, files): # GameData files CopyFiles('gamedata/sigsegv', 'addons/sourcemod/gamedata/sigsegv', [ + 'rtti.txt', 'vtables.txt', 'datamaps.txt', 'globals.txt', diff --git a/abi.h b/abi.h index 67b5671f..dcc7c763 100644 --- a/abi.h +++ b/abi.h @@ -78,5 +78,18 @@ struct __RTTI_CompleteObjectLocator #pragma warning(default:4200) #endif +#if defined _MSC_VER +/* from VC/crt/src/vcruntime/rtti.cpp */ +extern "C" PVOID __CLRCALL_OR_CDECL __RTDynamicCast ( + PVOID inptr, + LONG VfDelta, +// const _TypeDescriptor *SrcType, +// const _TypeDescriptor *TargetType, + PVOID SrcType, + PVOID TargetType, + BOOL isReference + ) throw(...); +#endif + #endif diff --git a/addr/addr.cpp b/addr/addr.cpp index 1dbcca7b..4066ee23 100644 --- a/addr/addr.cpp +++ b/addr/addr.cpp @@ -3,6 +3,14 @@ #include "abi.h" +#if defined __GNUC__ +#warning TODO: refactor prologue-finding code into a shared block somewhere +#warning TODO: make prologue-finding code look for the closest aligned ebp prologue *OR* ebx/ebp prologue +#warning (ensure that ScanResults::FIRST with two scanners will short-circuit properly) +#warning TODO: increase arbitrary 0x1000 limit in prologue-finding code +#endif + + void IAddr::Init() { if (this->m_State != State::INITIAL) { @@ -143,6 +151,7 @@ bool IAddr_VTable::FindAddrLinux(uintptr_t& addr) const bool IAddr_VTable::FindAddrWin(uintptr_t& addr) const { +#if 0 /* STEP 1: get ptr to _TypeDescriptor by finding typeinfo name string */ std::vector scan1_matches; for (auto match : PreScan::WinRTTI_Server()) { @@ -189,6 +198,8 @@ bool IAddr_VTable::FindAddrWin(uintptr_t& addr) const addr = (uintptr_t)p_VT; return true; +#endif + return false; } @@ -200,7 +211,7 @@ bool IAddr_DataDescMap::FindAddrWin(uintptr_t& addr) const return false; } - CSingleScan scan1(ScanDir::FORWARD, CLibSegBounds(this->GetLibrary(), ".data"), 4, new CBasicScanner(ScanResults::ALL, (const void *)&p_str, 0x4)); + CSingleScan scan1(CLibSegBounds(this->GetLibrary(), ".data"), new CBasicScanner(ScanResults::ALL, (const void *)&p_str, 0x4)); struct GetDataDescMap { uint8_t buf[6]; }; std::vector scanners; for (auto match : scan1.Matches()) { @@ -212,7 +223,7 @@ bool IAddr_DataDescMap::FindAddrWin(uintptr_t& addr) const scanners.push_back(new CBasicScanner(ScanResults::ALL, (const void *)&gddm, 0x6)); } - CMultiScan scan2(ScanDir::FORWARD, CLibSegBounds(this->GetLibrary(), ".text"), 0x10, scanners); + CMultiScan scan2(CLibSegBounds(this->GetLibrary(), ".text"), scanners); std::vector results; for (auto scanner : scanners) { if (scanner->Matches().size() == 1) { @@ -307,7 +318,7 @@ bool IAddr_Func_EBPPrologue_UniqueRef::FindAddrWin(uintptr_t& addr) const return false; } - CSingleScan scan1(ScanDir::FORWARD, CLibSegBounds(this->GetLibrary(), ".text"), 1, new CBasicScanner(ScanResults::ALL, (const void *)&p_ref, 0x4)); + CSingleScan scan1(CLibSegBounds(this->GetLibrary(), ".text"), new CBasicScanner(ScanResults::ALL, (const void *)&p_ref, 0x4)); if (scan1.Matches().size() != 1) { DevMsg("IAddr_Func_EBPPrologue_UniqueRef: \"%s\": found %u refs to ostensibly unique symbol\n", this->GetName(), scan1.Matches().size()); return false; @@ -318,7 +329,7 @@ bool IAddr_Func_EBPPrologue_UniqueRef::FindAddrWin(uintptr_t& addr) const 0x55, // +0000 push ebp 0x8b, 0xec, // +0001 mov ebp,esp }; - CSingleScan scan2(ScanDir::REVERSE, CAddrOffBounds(p_in_func, -0x1000), 0x10, new CBasicScanner(ScanResults::FIRST, (const void *)prologue, sizeof(prologue))); + CSingleScan scan2(CAddrOffBounds(p_in_func, -0x1000), new CBasicScanner(ScanResults::FIRST, (const void *)prologue, sizeof(prologue))); if (scan2.Matches().size() != 1) { DevMsg("IAddr_Func_EBPPrologue_UniqueRef: \"%s\": could not locate EBP prologue\n", this->GetName()); return false; @@ -338,7 +349,7 @@ bool IAddr_Func_EBPPrologue_UniqueStr::FindAddrWin(uintptr_t& addr) const return false; } - CSingleScan scan1(ScanDir::FORWARD, CLibSegBounds(this->GetLibrary(), ".text"), 1, new CBasicScanner(ScanResults::ALL, (const void *)&p_str, 0x4)); + CSingleScan scan1(CLibSegBounds(this->GetLibrary(), ".text"), new CBasicScanner(ScanResults::ALL, (const void *)&p_str, 0x4)); if (scan1.Matches().size() != 1) { DevMsg("IAddr_Func_EBPPrologue_UniqueStr: \"%s\": found %u refs to ostensibly unique string\n", this->GetName(), scan1.Matches().size()); return false; @@ -349,7 +360,7 @@ bool IAddr_Func_EBPPrologue_UniqueStr::FindAddrWin(uintptr_t& addr) const 0x55, // +0000 push ebp 0x8b, 0xec, // +0001 mov ebp,esp }; - CSingleScan scan2(ScanDir::REVERSE, CAddrOffBounds(p_in_func, -0x1000), 0x10, new CBasicScanner(ScanResults::FIRST, (const void *)prologue, sizeof(prologue))); + CSingleScan scan2(CAddrOffBounds(p_in_func, -0x10000), new CBasicScanner(ScanResults::FIRST, (const void *)prologue, sizeof(prologue))); if (scan2.Matches().size() != 1) { DevMsg("IAddr_Func_EBPPrologue_UniqueStr: \"%s\": could not locate EBP prologue\n", this->GetName()); return false; @@ -369,7 +380,7 @@ bool IAddr_Func_EBPPrologue_UniqueStr_KnownVTIdx::FindAddrWin(uintptr_t& addr) c return false; } - CSingleScan scan1(ScanDir::FORWARD, CLibSegBounds(this->GetLibrary(), ".text"), 1, new CBasicScanner(ScanResults::ALL, (const void *)&p_str, 0x4)); + CSingleScan scan1(CLibSegBounds(this->GetLibrary(), ".text"), new CBasicScanner(ScanResults::ALL, (const void *)&p_str, 0x4)); if (scan1.Matches().size() != 1) { DevMsg("IAddr_Func_EBPPrologue_UniqueStr_KnownVTIdx: \"%s\": found %u refs to ostensibly unique string\n", this->GetName(), scan1.Matches().size()); return false; @@ -380,7 +391,7 @@ bool IAddr_Func_EBPPrologue_UniqueStr_KnownVTIdx::FindAddrWin(uintptr_t& addr) c 0x55, // +0000 push ebp 0x8b, 0xec, // +0001 mov ebp,esp }; - CSingleScan scan2(ScanDir::REVERSE, CAddrOffBounds(p_in_func, -0x1000), 0x10, new CBasicScanner(ScanResults::FIRST, (const void *)prologue, sizeof(prologue))); + CSingleScan scan2(CAddrOffBounds(p_in_func, -0x10000), new CBasicScanner(ScanResults::FIRST, (const void *)prologue, sizeof(prologue))); if (scan2.Matches().size() != 1) { DevMsg("IAddr_Func_EBPPrologue_UniqueStr_KnownVTIdx: \"%s\": could not locate EBP prologue\n", this->GetName()); return false; @@ -424,7 +435,7 @@ bool IAddr_Func_EBPPrologue_VProf::FindAddrWin(uintptr_t& addr) const }; *(const char **)(vprof + 0x01) = p_name; *(const char **)(vprof + 0x06) = p_group; - CSingleScan scan1(ScanDir::FORWARD, CLibSegBounds(this->GetLibrary(), ".text"), 1, new CBasicScanner(ScanResults::FIRST, (const void *)vprof, sizeof(vprof))); + CSingleScan scan1(CLibSegBounds(this->GetLibrary(), ".text"), new CBasicScanner(ScanResults::FIRST, (const void *)vprof, sizeof(vprof))); if (scan1.Matches().size() != 1) { DevMsg("IAddr_Func_EBPPrologue_VProf: \"%s\": could not locate VPROF_BUDGET\n", this->GetName()); return false; @@ -435,7 +446,7 @@ bool IAddr_Func_EBPPrologue_VProf::FindAddrWin(uintptr_t& addr) const 0x55, // +0000 push ebp 0x8b, 0xec, // +0001 mov ebp,esp }; - CSingleScan scan2(ScanDir::REVERSE, CAddrOffBounds(p_in_func, -0x1000), 0x10, new CBasicScanner(ScanResults::FIRST, (const void *)prologue, sizeof(prologue))); + CSingleScan scan2(CAddrOffBounds(p_in_func, -0x10000), new CBasicScanner(ScanResults::FIRST, (const void *)prologue, sizeof(prologue))); if (scan2.Matches().size() != 1) { DevMsg("IAddr_Func_EBPPrologue_VProf: \"%s\": could not locate EBP prologue\n", this->GetName()); return false; diff --git a/addr/misc.cpp b/addr/misc.cpp index 1bb65ffb..eb3a1305 100644 --- a/addr/misc.cpp +++ b/addr/misc.cpp @@ -85,7 +85,7 @@ class CAddr_pszWpnEntTranslationList : public IAddr_Sym auto strscan3 = new CStringScanner(ScanResults::ALL, "tf_weapon_shotgun_hwg"); auto strscan4 = new CStringScanner(ScanResults::ALL, "tf_weapon_shotgun_pyro"); auto strscan5 = new CStringScanner(ScanResults::ALL, "tf_weapon_shotgun_primary"); - CMultiScan scan1(ScanDir::FORWARD, CLibSegBounds(Library::SERVER, ".rdata"), 1, + CMultiScan scan1(CLibSegBounds(Library::SERVER, ".rdata"), { strscan1, strscan2, strscan3, strscan4, strscan5 }); if (strscan1->Matches().size() != 1) { DevMsg("Fail strscan1\n"); return false; } if (strscan2->Matches().size() != 1) { DevMsg("Fail strscan2\n"); return false; } @@ -100,7 +100,7 @@ class CAddr_pszWpnEntTranslationList : public IAddr_Sym mask.SetDword(0x1c, 0xffffffff); seek.SetDword(0x1c, (uint32_t)strscan3->Matches()[0]); mask.SetDword(0x20, 0xffffffff); seek.SetDword(0x20, (uint32_t)strscan4->Matches()[0]); mask.SetDword(0x28, 0xffffffff); seek.SetDword(0x28, (uint32_t)strscan5->Matches()[0]); - CSingleScan scan2(ScanDir::FORWARD, CLibSegBounds(Library::SERVER, ".data"), 4, new CMaskedScanner(ScanResults::ALL, seek, mask)); + CSingleScan scan2(CLibSegBounds(Library::SERVER, ".data"), new CMaskedScanner(ScanResults::ALL, seek, mask)); if (scan2.Matches().size() != 1) { DevMsg("Fail scan2 %u\n", scan2.Matches().size()); return false; } auto match = (const char **)scan2.Matches()[0]; @@ -171,7 +171,7 @@ class CAddr_CTFPlayer_CanBeForcedToLaugh : public IAddr_Sym // DevMsg("g_pGameRules: %08x\n", (uintptr_t)addr_g_pGameRules); // DevMsg("m_bPlayingMannVsMachine: %08x\n", off_CTFGameRules_m_bPlayingMannVsMachine); - CSingleScan scan1(ScanDir::FORWARD, CLibSegBounds(this->GetLibrary(), ".text"), 0x10, new CMaskedScanner(ScanResults::ALL, seek, mask)); + CSingleScan scan1(CLibSegBounds(this->GetLibrary(), ".text"), new CMaskedScanner(ScanResults::ALL, seek, mask)); if (scan1.Matches().size() != 1) { DevMsg("Fail scan1 %u\n", scan1.Matches().size()); return false; diff --git a/addr/prescan.cpp b/addr/prescan.cpp index 02b9719d..283bdc48 100644 --- a/addr/prescan.cpp +++ b/addr/prescan.cpp @@ -1,7 +1,7 @@ #include "addr/prescan.h" -CSingleScan *PreScan::s_WinRTTI_server = nullptr; +//CSingleScan *PreScan::s_WinRTTI_server = nullptr; void PreScan::DoScans() @@ -9,8 +9,8 @@ void PreScan::DoScans() #if defined _WINDOWS DevMsg("PreScan::DoScans\n"); - s_WinRTTI_server = new CSingleScan(ScanDir::FORWARD, CLibSegBounds(Library::SERVER, ".data"), 1, new CStringPrefixScanner(ScanResults::ALL, ".?AV")); - +// s_WinRTTI_server = new CSingleScan(ScanDir::FORWARD, CLibSegBounds(Library::SERVER, ".data"), 1, new CStringPrefixScanner(ScanResults::ALL, ".?AV")); +// // for (auto match : s_WinRTTI_server->Matches()) { // DevMsg("[PreScan] 0x%08x \"%s\"\n", (uintptr_t)match, (const char *)match); // } diff --git a/addr/prescan.h b/addr/prescan.h index 1941ec1d..97f42859 100644 --- a/addr/prescan.h +++ b/addr/prescan.h @@ -10,10 +10,10 @@ class PreScan public: static void DoScans(); - static const std::vector& WinRTTI_Server() { return s_WinRTTI_server->Matches(); } +// static const std::vector& WinRTTI_Server() { return s_WinRTTI_server->Matches(); } -private: - static CSingleScan *s_WinRTTI_server; +//private: +// static CSingleScan *s_WinRTTI_server; }; diff --git a/common.h b/common.h index e95deb3f..08f8a4b6 100644 --- a/common.h +++ b/common.h @@ -18,6 +18,7 @@ class IEngineTrace; class IStaticPropMgrServer; class IVDebugOverlay; class CGlobalVars; +class CBaseEntityList; extern IVEngineServer *engine; @@ -28,13 +29,17 @@ extern IStaticPropMgrServer *staticpropmgr; extern IVDebugOverlay *debugoverlay; extern CGlobalVars *gpGlobals; +extern CBaseEntityList *g_pEntityList; /* C++ standard library */ #include #include #include +#include +#include #include +#include #include #include #include @@ -47,6 +52,9 @@ extern CGlobalVars *gpGlobals; /* AMTL */ #include +/* Boost */ +//#include + /* Source SDK */ #include "sdk2013/annotations.h" #include "sdk2013/basetypes.h" diff --git a/extension.cpp b/extension.cpp index 9d2dede2..b2047c94 100644 --- a/extension.cpp +++ b/extension.cpp @@ -7,6 +7,7 @@ #include "addr/prescan.h" #include "gameconf.h" #include "prop.h" +#include "util/rtti.h" CExtSigsegv g_Ext; @@ -19,6 +20,7 @@ IStaticPropMgrServer *staticpropmgr; IVDebugOverlay *debugoverlay; CGlobalVars *gpGlobals; +CBaseEntityList *g_pEntityList; //ISDKTools *g_pSDKTools; @@ -28,10 +30,13 @@ bool CExtSigsegv::SDK_OnLoad(char *error, size_t maxlen, bool late) // sharesys->AddDependency(myself, "sdktools.ext", true, true); // SM_GET_IFACE(SDKTOOLS, g_pSDKTools); + g_pEntityList = reinterpret_cast(gamehelpers->GetGlobalEntityList()); + PreScan::DoScans(); if (!g_GCHook.LoadAll(error, maxlen)) goto fail; LibMgr::Load(); + RTTI::PreLoad(); goto fail; // REMOVE ME AddrManager::Load(); if (!Link::InitAll(error, maxlen)) goto fail; diff --git a/gameconf.cpp b/gameconf.cpp index 23904ccb..afc45582 100644 --- a/gameconf.cpp +++ b/gameconf.cpp @@ -5,6 +5,7 @@ static const char *const configs[] = { + "sigsegv/rtti", "sigsegv/vtables", "sigsegv/datamaps", "sigsegv/globals", diff --git a/gamedata/sigsegv/misc.txt b/gamedata/sigsegv/misc.txt index c7edc697..c43ec36f 100644 --- a/gamedata/sigsegv/misc.txt +++ b/gamedata/sigsegv/misc.txt @@ -43,20 +43,20 @@ // uniref "pszWpnEntTranslationList" // } - "CBaseEntity::GetNetworkable" + "CBaseEntity::CalcAbsolutePosition" { type "sym" - sym "_ZN11CBaseEntity14GetNetworkableEv" + sym "_ZN11CBaseEntity20CalcAbsolutePositionEv" } - "CBaseEntity::CalcAbsolutePosition" + "CBaseEntity::GetNetworkable" { type "sym" - sym "_ZN11CBaseEntity20CalcAbsolutePositionEv" + sym "_ZN11CBaseEntity14GetNetworkableEv" } - "CBaseEntity::IsAlive" + "CBaseEntity::IsPlayer" { type "sym" - sym "_ZN11CBaseEntity7IsAliveEv" + sym "_ZNK11CBaseEntity8IsPlayerEv" } "CBasePlayer::IsBot" diff --git a/gamedata/sigsegv/rtti.txt b/gamedata/sigsegv/rtti.txt new file mode 100644 index 00000000..cd50554e --- /dev/null +++ b/gamedata/sigsegv/rtti.txt @@ -0,0 +1,36 @@ +// rtti + +"Games" +{ + "#default" + { + "#supported" + { + engine "tf2" + } + + "sigsegv" + { + "addrs" + { + // TODO: + // remove the need for this file + // and make rtti addr finding automatic + + "[RTTI] CTFPlayer" + { + type "rtti" + sym "_ZTI9CTFPlayer" + winstr ".?AVCTFPlayer@@" + } + + "[RTTI] CTFBot" + { + type "rtti" + sym "_ZTI6CTFBot" + winstr ".?AVCTFBot@@" + } + } + } + } +} diff --git a/gamedata/sigsegv/tfplayer.txt b/gamedata/sigsegv/tfplayer.txt index 5f8da44a..38c26dcc 100644 --- a/gamedata/sigsegv/tfplayer.txt +++ b/gamedata/sigsegv/tfplayer.txt @@ -41,9 +41,22 @@ sym "_ZN9CTFPlayer21SpeakConceptIfAllowedEiPKcPcjP16IRecipientFilter" unistr "disguiseclass:%s" } - // CTFPlayer::TFPlayerThink - // warning! warning! not a straightforward ebp prologue! - // push ebx; mov ebx, esp + "CTFPlayer::TFPlayerThink" + { + // weird function with dual ebx/ebp frame pointers for 16-byte alignment + type "func ebpprologue unistr" + sym "_ZN9CTFPlayer13TFPlayerThinkEv" + unistr "Heal Target: ClipSize Data Limit Exceeded: %d (max 500)\n" + // alt: "disguise_as_dispenser_on_crouch" + // alt: "#TF_Powerup_Supernova_Deploy" + // alt: "respawn_ghost" + // alt: "reviver" + // alt: "sticky_jump_landed" + // alt: "rocket_jump_landed" + // alt: "head_scale" + // alt: "torso_scale" + // alt: "hand_scale" + } } } } diff --git a/gamedata/sigsegv/vtables.txt b/gamedata/sigsegv/vtables.txt index 94a4d5aa..99107f88 100644 --- a/gamedata/sigsegv/vtables.txt +++ b/gamedata/sigsegv/vtables.txt @@ -13,6 +13,10 @@ { "addrs" { + // TODO: + // remove the need for this file + // and make vtable addr finding automatic + "[VT] CGameRules" { type "vtable" diff --git a/library.cpp b/library.cpp index 1686331a..97d7ec88 100644 --- a/library.cpp +++ b/library.cpp @@ -102,6 +102,13 @@ void *LibMgr::FindSym(Library lib, const char *sym) return g_MemUtils.ResolveSymbol(handle, sym); } +void LibMgr::ForEachSym(Library lib, void (*functor)(Symbol *)) +{ + void *handle = s_LibHandles.at(lib); + assert(handle != nullptr); + g_MemUtils.ForEachSymbol(handle, functor); +} + #if defined _LINUX || defined _OSX diff --git a/library.h b/library.h index bea3f42f..5c683fac 100644 --- a/library.h +++ b/library.h @@ -48,6 +48,7 @@ class LibMgr static const LibInfo& GetInfo(Library lib); static void *FindSym(Library lib, const char *sym); + static void ForEachSym(Library lib, void (*functor)(Symbol *)); private: LibMgr() {} diff --git a/mem/extract.h b/mem/extract.h index eac0a196..00043c1f 100644 --- a/mem/extract.h +++ b/mem/extract.h @@ -97,7 +97,7 @@ bool IExtract::Check() uintptr_t addr_min = (uintptr_t)this->m_pFuncAddr + this->m_iFuncOffMin; uintptr_t addr_max = (uintptr_t)this->m_pFuncAddr + this->m_iFuncOffMax + this->m_iLength; - CSingleScan scan(ScanDir::FORWARD, CAddrAddrBounds((void *)addr_min, (void *)addr_max), 1, + CSingleScan scan(CAddrAddrBounds((void *)addr_min, (void *)addr_max), new CMaskedScanner(ScanResults::ALL, this->m_BufExtract, this->m_MaskExtract)); if (scan.Matches().size() != 1) { diff --git a/mem/patch.cpp b/mem/patch.cpp index 5b4050bd..9fc987ed 100644 --- a/mem/patch.cpp +++ b/mem/patch.cpp @@ -29,7 +29,7 @@ bool IPatch::Check() uintptr_t addr_min = (uintptr_t)this->m_pFuncAddr + this->m_iFuncOffMin; uintptr_t addr_max = (uintptr_t)this->m_pFuncAddr + this->m_iFuncOffMax + this->m_iLength; - CSingleScan scan(ScanDir::FORWARD, CAddrAddrBounds((void *)addr_min, (void *)addr_max), 1, + CSingleScan scan(CAddrAddrBounds((void *)addr_min, (void *)addr_max), new CMaskedScanner(ScanResults::ALL, this->m_BufVerify, this->m_MaskVerify)); if (scan.Matches().size() != 1) { diff --git a/mem/scan.cpp b/mem/scan.cpp index 536db3ac..c1fe85e0 100644 --- a/mem/scan.cpp +++ b/mem/scan.cpp @@ -30,14 +30,14 @@ void IScanner::AddMatch(const void *match) void CBasicScanner::CheckOne(const void *where) { - if (memcmp(where, this->m_Seek, this->m_Len) == 0) { + if (memcmp(where, this->m_Seek, this->GetBufLen()) == 0) { this->AddMatch(where); } } void CMaskedScanner::CheckOne(const void *where) { - int len = this->m_Seek.GetSize(); + int len = this->GetBufLen(); for (int i = 0; i < len; ++i) { uint8_t b_mem = *((uint8_t *)where + i); @@ -61,98 +61,17 @@ void CStringScanner::CheckOne(const void *where) void CStringPrefixScanner::CheckOne(const void *where) { - if (strncmp((const char *)where, this->m_Str, strlen(this->m_Str)) == 0) { + if (strncmp((const char *)where, this->m_Str, this->GetBufLen() - 1) == 0) { this->AddMatch(where); } } -void IScan::DoScan() -{ - bool fwd; - switch (this->m_Dir) { - case ScanDir::FORWARD: - fwd = true; - break; - case ScanDir::REVERSE: - fwd = false; - break; - default: - assert(false); - } - - for (auto scanner : this->m_Scanners) { - scanner->Reset(); - } - - int align = this->m_Align; - - const uint8_t *p_low = (const uint8_t *)this->m_Bounds.GetLowerBound(); - const uint8_t *p_high = (const uint8_t *)this->m_Bounds.GetUpperBound(); - - const uint8_t *ptr = (fwd ? p_low : p_high - align); - const uint8_t *end = (fwd ? p_high : p_low - align); - - uintptr_t rem = (uintptr_t)ptr % align; - if (rem != 0) { - if (fwd) { - ptr += (align - rem); - } else { - ptr -= rem; - } - } - - int incr = (fwd ? align : -align); - -// DevMsg("IScan::DoScan: ptr 0x%08x, end 0x%08x, incr %c0x%08x\n", (uintptr_t)ptr, (uintptr_t)end, (fwd ? '+' : '-'), align); - - /* use an array because std::vector is horrendously slow here */ - int num_scanners = this->m_Scanners.size(); - IScanner **scanners = new IScanner *[num_scanners]; - for (int i = 0; i < num_scanners; ++i) { - scanners[i] = this->m_Scanners[i]; - } - - while (fwd ? (ptr < end) : (ptr > end)) { -// bool all_done = false; - - for (int i = 0; i < num_scanners; ++i) { - IScanner *scanner = scanners[i]; - - if (scanner->IsDone()) { - continue; - } - - if (ptr + scanner->GetBufLen() <= p_high) { - scanner->CheckOne(ptr); -#if 0 - - if (scanner->IsDone()) { - scanners.erase(scanner); - if (scanners.empty()) { - all_done = true; - } - } -#endif - } - } - -// if (all_done) { -// break; -// } - - ptr += incr; - } - - delete[] scanners; -} - - namespace Scan { const char *FindUniqueConstStr(const char *str) { - CSingleScan scan(ScanDir::FORWARD, CLibSegBounds(Library::SERVER, ".rdata"), 1, new CStringScanner(ScanResults::ALL, str)); + CSingleScan scan(CLibSegBounds(Library::SERVER, ".rdata"), new CStringScanner(ScanResults::ALL, str)); if (scan.Matches().size() == 1) { return (const char *)scan.Matches()[0]; diff --git a/mem/scan.h b/mem/scan.h index cb753af2..fbb8c88e 100644 --- a/mem/scan.h +++ b/mem/scan.h @@ -93,17 +93,20 @@ class IScanner void Reset() { this->m_Matches.clear(); } bool IsDone() const { return this->m_bDone; } + ScanResults GetResultsType() const { return this->m_RType; } + int GetBufLen() const { return this->m_Len; } + virtual void CheckOne(const void *where) = 0; - virtual int GetBufLen() const = 0; protected: - IScanner(ScanResults rtype) : - m_RType(rtype) {} + IScanner(ScanResults rtype, int len) : + m_RType(rtype), m_Len(len) {} void AddMatch(const void *match); private: ScanResults m_RType; + int m_Len; std::vector m_Matches; bool m_bDone = false; @@ -113,7 +116,7 @@ class CBasicScanner : public IScanner { public: CBasicScanner(ScanResults rtype, const void *seek, int len) : - IScanner(rtype), m_Len(len) + IScanner(rtype, len) { this->m_Seek = new uint8_t[len]; memcpy(this->m_Seek, seek, len); @@ -123,11 +126,9 @@ class CBasicScanner : public IScanner delete[] this->m_Seek; } - virtual int GetBufLen() const override { return this->m_Len; } virtual void CheckOne(const void *where) override; private: - int m_Len; uint8_t *m_Seek; }; @@ -135,12 +136,11 @@ class CMaskedScanner : public IScanner { public: CMaskedScanner(ScanResults rtype, const ByteBuf& seek, const ByteBuf& mask) : - IScanner(rtype), m_Seek(seek), m_Mask(mask) + IScanner(rtype, seek.GetSize()), m_Seek(seek), m_Mask(mask) { assert(this->m_Seek.GetSize() == this->m_Mask.GetSize()); } - virtual int GetBufLen() const override { return this->m_Seek.GetSize(); } virtual void CheckOne(const void *where) override; private: @@ -152,9 +152,8 @@ class CStringScanner : public IScanner { public: CStringScanner(ScanResults rtype, const char *str) : - IScanner(rtype), m_Str(str) {} + IScanner(rtype, strlen(str) + 1), m_Str(str) {} - virtual int GetBufLen() const override { return strlen(this->m_Str) + 1; } virtual void CheckOne(const void *where) override; private: @@ -165,9 +164,8 @@ class CStringPrefixScanner : public IScanner { public: CStringPrefixScanner(ScanResults rtype, const char *str) : - IScanner(rtype), m_Str(str) {} + IScanner(rtype, strlen(str) + 1), m_Str(str) {} - virtual int GetBufLen() const override { return strlen(this->m_Str) + 1; } virtual void CheckOne(const void *where) override; private: @@ -175,11 +173,12 @@ class CStringPrefixScanner : public IScanner }; +template class IScan { protected: - IScan(ScanDir dir, const IBounds& bounds, int align, const std::vector& scanners) : - m_Dir(dir), m_Bounds(bounds), m_Align(align), m_Scanners(scanners) {} + IScan(const IBounds& bounds, const std::vector& scanners) : + m_Bounds(bounds), m_Scanners(scanners) {} virtual ~IScan() { for (auto scanner : this->m_Scanners) { @@ -191,17 +190,19 @@ class IScan void DoScan(); private: - ScanDir m_Dir; + template + void InternalScan(); + const IBounds& m_Bounds; - int m_Align; std::vector m_Scanners; }; -class CSingleScan : public IScan +template +class CSingleScan : public IScan { public: - CSingleScan(ScanDir dir, const IBounds& bounds, int align, IScanner *scanner) : - IScan(dir, bounds, align, { scanner }), m_Scanner(scanner) + CSingleScan(const IBounds& bounds, IScanner *scanner) : + IScan(bounds, { scanner }), m_Scanner(scanner) { this->DoScan(); } @@ -212,16 +213,89 @@ class CSingleScan : public IScan IScanner *m_Scanner; }; -class CMultiScan : public IScan +template +class CMultiScan : public IScan { public: - CMultiScan(ScanDir dir, const IBounds& bounds, int align, const std::vector& scanners) : - IScan(dir, bounds, align, scanners) + CMultiScan(const IBounds& bounds, const std::vector& scanners) : + IScan(bounds, scanners) { this->DoScan(); } }; +template +class CThreadedScan +{ +public: + CThreadedScan(const IBounds& bounds, const std::vector& scanners) : + m_Bounds(bounds) + { + for (auto scanner : scanners) { + this->m_WorkToDo.push_back(scanner); + } + + int n_threads = Min(THREADS, (int)scanners.size()); + + std::vector threads; + for (int i = 0; i < n_threads; ++i) { + DevMsg("CThreadedScan: creating thread #%d\n", i); + threads.emplace_back(&CThreadedScan::Worker, this, i); + } + + for (auto& thread : threads) { + DevMsg("CThreadedScan: join\n"); + thread.join(); + } + DevMsg("CThreadedScan: done\n"); + } + +private: + void Worker(int id) + { +// DevMsg("CThreadedScan::Worker #%d BEGIN\n", id); + + IScanner *scanner; + + while ((scanner = this->GetWork()) != nullptr) { +// DevMsg("CThreadedScan::Worker #%d SCAN BEGIN %08x\n", id, (uintptr_t)scanner); + CSingleScan *scan = new CSingleScan(this->m_Bounds, scanner); +// DevMsg("CThreadedScan::Worker #%d SCAN END %08x\n", id, (uintptr_t)scanner); + this->SubmitResult(scan); + } + +// DevMsg("CThreadedScan::Worker #%d END\n", id); + } + + IScanner *GetWork() + { + std::lock_guard lock(this->m_MutexToDo); + + if (this->m_WorkToDo.empty()) { + return nullptr; + } + + IScanner *work = this->m_WorkToDo.front(); + this->m_WorkToDo.pop_front(); + return work; + } + + void SubmitResult(CSingleScan *result) + { + std::lock_guard lock(this->m_MutexDone); + + this->m_WorkDone.emplace_back(std::unique_ptr>(result)); + } + + const IBounds& m_Bounds; + + std::mutex m_MutexToDo; + std::list m_WorkToDo; + + std::mutex m_MutexDone; + std::vector>> m_WorkDone; +}; + namespace Scan { @@ -229,4 +303,92 @@ namespace Scan } +template +inline void IScan::DoScan() +{ + bool never_done = true; + for (auto scanner : this->m_Scanners) { + if (scanner->GetResultsType() != ScanResults::ALL) { + never_done = false; + break; + } + } + + if (never_done) { + this->InternalScan(); + } else { + this->InternalScan(); + } +} + +template template +inline void IScan::InternalScan() +{ + constexpr bool FWD = (DIR == ScanDir::FORWARD); + + for (auto scanner : this->m_Scanners) { + scanner->Reset(); + } + + const uint8_t *p_low = (const uint8_t *)this->m_Bounds.GetLowerBound(); + const uint8_t *p_high = (const uint8_t *)this->m_Bounds.GetUpperBound(); + + const uint8_t *ptr = (FWD ? p_low : p_high - ALIGN); + const uint8_t *end = (FWD ? p_high : p_low - ALIGN); + + uintptr_t rem = (uintptr_t)ptr % ALIGN; + if (rem != 0) { + if (FWD) { + ptr += (ALIGN - rem); + } else { + ptr -= rem; + } + } + + constexpr int INCR = (FWD ? ALIGN : -ALIGN); + +// DevMsg("IScan::DoScan: ptr 0x%08x, end 0x%08x, incr %c0x%08x\n", (uintptr_t)ptr, (uintptr_t)end, (FWD ? '+' : '-'), ALIGN); + + /* use an array because std::vector is horrendously slow here */ + int num_scanners = this->m_Scanners.size(); + IScanner **scanners = new IScanner *[num_scanners]; + for (int i = 0; i < num_scanners; ++i) { + scanners[i] = this->m_Scanners[i]; + } + + while (FWD ? (ptr < end) : (ptr > end)) { +// bool all_done = false; + + for (int i = 0; i < num_scanners; ++i) { + IScanner *scanner = scanners[i]; + + if (!NEVER_DONE && scanner->IsDone()) { + continue; + } + + if (ptr + scanner->GetBufLen() <= p_high) { + scanner->CheckOne(ptr); +#if 0 + + if (scanner->IsDone()) { + scanners.erase(scanner); + if (scanners.empty()) { + all_done = true; + } + } +#endif + } + } + +// if (all_done) { +// break; +// } + + ptr += INCR; + } + + delete[] scanners; +} + + #endif diff --git a/mod/debug_airblastbox.cpp b/mod/debug_airblastbox.cpp deleted file mode 100644 index da9247ed..00000000 --- a/mod/debug_airblastbox.cpp +++ /dev/null @@ -1,68 +0,0 @@ -#include "mod.h" -#include "sm/detours.h" -#include "stub/stub.h" -#include "util/util.h" - - -#ifdef EXPERIMENTAL - -static ConVar cvar_duration("sigsegv_debug_airblastbox_duration", "0.1", FCVAR_NOTIFY, - "Debug: box draw duration"); - -static ConVar cvar_color_r("sigsegv_debug_airblastbox_color_r", "255", FCVAR_NOTIFY, - "Debug: box color (red)"); -static ConVar cvar_color_g("sigsegv_debug_airblastbox_color_g", "255", FCVAR_NOTIFY, - "Debug: box color (green)"); -static ConVar cvar_color_b("sigsegv_debug_airblastbox_color_b", "255", FCVAR_NOTIFY, - "Debug: box color (blue)"); -static ConVar cvar_color_a("sigsegv_debug_airblastbox_color_a", "255", FCVAR_NOTIFY, - "Debug: box color (alpha)"); - - -static RefCount rc_CTFWeaponBase_DeflectProjectiles; -DETOUR_DECL_MEMBER(bool, CTFWeaponBase_DeflectProjectiles, void) -{ - SCOPED_INCREMENT(rc_CTFWeaponBase_DeflectProjectiles); - return DETOUR_MEMBER_CALL(CTFWeaponBase_DeflectProjectiles)(); -} - -DETOUR_DECL_STATIC(int, UTIL_EntitiesInBox, const Vector& mins, const Vector& maxs, CFlaggedEntitiesEnum *pEnum) -{ - if (rc_CTFWeaponBase_DeflectProjectiles.NonZero()) { - NDebugOverlay::Box(vec3_origin, mins, maxs, - cvar_color_r.GetInt(), - cvar_color_g.GetInt(), - cvar_color_b.GetInt(), - cvar_color_a.GetInt(), - cvar_duration.GetFloat()); - } - - return DETOUR_STATIC_CALL(UTIL_EntitiesInBox)(mins, maxs, pEnum); -} - - -class CMod_Debug_AirblastBox : public IMod -{ -public: - CMod_Debug_AirblastBox() : IMod("Debug_AirblastBox") - { - MOD_ADD_DETOUR_MEMBER(CTFWeaponBase, DeflectProjectiles); - MOD_ADD_DETOUR_GLOBAL(UTIL_EntitiesInBox); - } - - void SetEnabled(bool enable) - { - this->ToggleAllDetours(enable); - } -}; -static CMod_Debug_AirblastBox s_Mod; - - -static ConVar cvar_enable("sigsegv_debug_airblastbox_enable", "0", FCVAR_NOTIFY, - "Debug: draw box used for airblast deflection of projectiles", - [](IConVar *pConVar, const char *pOldValue, float flOldValue) { - ConVarRef var(pConVar); - s_Mod.SetEnabled(var.GetBool()); - }); - -#endif diff --git a/mod/debug_deflectcylinder.cpp b/mod/debug_deflectcylinder.cpp deleted file mode 100644 index be04831f..00000000 --- a/mod/debug_deflectcylinder.cpp +++ /dev/null @@ -1,153 +0,0 @@ -#include "mod.h" -#include "sm/detours.h" -#include "stub/stub.h" -#include "util/util.h" - - -#ifdef EXPERIMENTAL - -static ConVar cvar_duration("sigsegv_debug_deflectcylinder_duration", "0.1", FCVAR_NOTIFY, - "Debug: cylinder draw duration"); - -static ConVar cvar_color_r("sigsegv_debug_deflectcylinder_color_r", "255", FCVAR_NOTIFY, - "Debug: cylinder color (red)"); -static ConVar cvar_color_g("sigsegv_debug_deflectcylinder_color_g", "255", FCVAR_NOTIFY, - "Debug: cylinder color (green)"); -static ConVar cvar_color_b("sigsegv_debug_deflectcylinder_color_b", "255", FCVAR_NOTIFY, - "Debug: cylinder color (blue)"); -static ConVar cvar_color_a("sigsegv_debug_deflectcylinder_color_a", "0", FCVAR_NOTIFY, - "Debug: cylinder color (alpha)"); - -static ConVar cvar_sphere_enable("sigsegv_debug_deflectcylinder_sphere_enable", "0", FCVAR_NOTIFY, - "Debug: sphere enable"); -static ConVar cvar_sphere_color_r("sigsegv_debug_deflectcylinder_sphere_color_r", "255", FCVAR_NOTIFY, - "Debug: sphere color (red)"); -static ConVar cvar_sphere_color_g("sigsegv_debug_deflectcylinder_sphere_color_g", "255", FCVAR_NOTIFY, - "Debug: sphere color (green)"); -static ConVar cvar_sphere_color_b("sigsegv_debug_deflectcylinder_sphere_color_b", "255", FCVAR_NOTIFY, - "Debug: sphere color (blue)"); -static ConVar cvar_sphere_color_a("sigsegv_debug_deflectcylinder_sphere_color_a", "0", FCVAR_NOTIFY, - "Debug: sphere color (alpha)"); - - -static RefCount rc_CTFMinigun_AttackProjectiles; -DETOUR_DECL_MEMBER(void, CTFMinigun_AttackEnemyProjectiles, void) -{ - SCOPED_INCREMENT(rc_CTFMinigun_AttackProjectiles); - DETOUR_MEMBER_CALL(CTFMinigun_AttackEnemyProjectiles)(); -} - -static bool s_IsMiniBoss; -DETOUR_DECL_MEMBER(bool, CTFPlayer_IsMiniBoss, void) -{ - bool result = DETOUR_MEMBER_CALL(CTFPlayer_IsMiniBoss)(); - s_IsMiniBoss = result; - return result; -} - -DETOUR_DECL_STATIC(int, UTIL_EntitiesInSphere, const Vector& center, float radius, CFlaggedEntitiesEnum *pEnum) -{ - if (rc_CTFMinigun_AttackProjectiles.NonZero() && cvar_sphere_enable.GetBool()) { - NDebugOverlay::Sphere(center, QAngle(0.0f, 0.0f, 0.0f), radius, - cvar_sphere_color_r.GetInt(), - cvar_sphere_color_g.GetInt(), - cvar_sphere_color_b.GetInt(), - cvar_sphere_color_a.GetInt(), - false, - cvar_duration.GetFloat()); - - NDebugOverlay::Sphere(center, QAngle(0.0f, 30.0f, 0.0f), radius, - cvar_sphere_color_r.GetInt(), - cvar_sphere_color_g.GetInt(), - cvar_sphere_color_b.GetInt(), - cvar_sphere_color_a.GetInt(), - false, - cvar_duration.GetFloat()); - - NDebugOverlay::Sphere(center, QAngle(0.0f, 60.0f, 0.0f), radius, - cvar_sphere_color_r.GetInt(), - cvar_sphere_color_g.GetInt(), - cvar_sphere_color_b.GetInt(), - cvar_sphere_color_a.GetInt(), - false, - cvar_duration.GetFloat()); - } - - return DETOUR_STATIC_CALL(UTIL_EntitiesInSphere)(center, radius, pEnum); -} - -DETOUR_DECL_STATIC(float, CalcDistanceToLineSegment, const Vector& P, const Vector& vLineA, const Vector& vLineB, float *t) -{ - float dist = DETOUR_STATIC_CALL(CalcDistanceToLineSegment)(P, vLineA, vLineB, t); - - if (rc_CTFMinigun_AttackProjectiles.NonZero()) { - float radius = (s_IsMiniBoss ? 56.0f : 38.0f); - bool is_inside = (dist <= radius); - - NDebugOverlay::Cross3D(P, 10.0f, - (is_inside ? 0 : 255), - (is_inside ? 255 : 0), - 0, - true, - cvar_duration.GetFloat()); - - static int lastframe = -1; - if (gpGlobals->framecount != lastframe) { - lastframe = gpGlobals->framecount; - - /*NDebugOverlay::Line(vLineA, vLineB, - cvar_color_r.GetFloat(), - cvar_color_g.GetFloat(), - cvar_color_b.GetFloat(), - true, - cvar_duration.GetFloat());*/ - - QAngle ang; - VectorAngles(vLineA - vLineB, ang); - - for (int i = 0; i <= 10; ++i) { - float pA = ((float)i / 10.0f); - float pB = (1.0f - pA); - - NDebugOverlay::Circle((pA * vLineA) + (pB * vLineB), ang, radius, - cvar_color_r.GetFloat(), - cvar_color_g.GetFloat(), - cvar_color_b.GetFloat(), - cvar_color_a.GetFloat(), - false, - cvar_duration.GetFloat()); - } - } - } - - return dist; -} - - -class CMod_Debug_DeflectCylinder : public IMod -{ -public: - CMod_Debug_DeflectCylinder() : IMod("Debug_DeflectCylinder") - { - MOD_ADD_DETOUR_MEMBER(CTFMinigun, AttackEnemyProjectiles); - MOD_ADD_DETOUR_MEMBER(CTFPlayer, IsMiniBoss); - MOD_ADD_DETOUR_GLOBAL(UTIL_EntitiesInSphere); - MOD_ADD_DETOUR_GLOBAL(CalcDistanceToLineSegment); - } - - void SetEnabled(bool enable) - { - this->ToggleAllDetours(enable); - } -}; -static CMod_Debug_DeflectCylinder s_Mod; - - -static ConVar cvar_enable("sigsegv_debug_deflectcylinder_enable", "0", FCVAR_NOTIFY, - "Debug: draw cylinder used for heavy's projectile deflection upgrade", - [](IConVar *pConVar, const char *pOldValue, float flOldValue) { - ConVarRef var(pConVar); - s_Mod.SetEnabled(var.GetBool()); - }); - -#endif diff --git a/mod/debug_ehsphere.cpp b/mod/debug_ehsphere.cpp deleted file mode 100644 index e93b4814..00000000 --- a/mod/debug_ehsphere.cpp +++ /dev/null @@ -1,71 +0,0 @@ -#include "mod.h" -#include "sm/detours.h" -#include "stub/stub.h" -#include "util/util.h" - - -#ifdef EXPERIMENTAL - -static ConVar cvar_duration("sigsegv_debug_ehsphere_duration", "0.5", FCVAR_NOTIFY, - "Debug: EH sphere draw duration"); - -static ConVar cvar_color_r("sigsegv_debug_ehsphere_color_r", "255", FCVAR_NOTIFY, - "Debug: EH sphere color (red)"); -static ConVar cvar_color_g("sigsegv_debug_ehsphere_color_g", "255", FCVAR_NOTIFY, - "Debug: EH sphere color (green)"); -static ConVar cvar_color_b("sigsegv_debug_ehsphere_color_b", "255", FCVAR_NOTIFY, - "Debug: EH sphere color (blue)"); -static ConVar cvar_color_a("sigsegv_debug_ehsphere_color_a", "0", FCVAR_NOTIFY, - "Debug: EH sphere color (alpha)"); - - -static RefCount rc_CTFSniperRifle_ExplosiveHeadShot; -DETOUR_DECL_MEMBER(void, CTFSniperRifle_ExplosiveHeadShot, CTFPlayer *player1, CTFPlayer *player2) -{ - SCOPED_INCREMENT(rc_CTFSniperRifle_ExplosiveHeadShot); - DETOUR_MEMBER_CALL(CTFSniperRifle_ExplosiveHeadShot)(player1, player2); -} - -DETOUR_DECL_STATIC(int, UTIL_EntitiesInSphere, const Vector& center, float radius, CFlaggedEntitiesEnum *pEnum) -{ - if (rc_CTFSniperRifle_ExplosiveHeadShot.NonZero()) { - for (int i = 0; i < 8; ++i) { - NDebugOverlay::Sphere(center, QAngle(0.0f, (float)i * 22.5f, 0.0f), radius, - cvar_color_r.GetInt(), - cvar_color_g.GetInt(), - cvar_color_b.GetInt(), - cvar_color_a.GetInt(), - false, - cvar_duration.GetFloat()); - } - } - - return DETOUR_STATIC_CALL(UTIL_EntitiesInSphere)(center, radius, pEnum); -} - - -class CMod_Debug_EHSphere : public IMod -{ -public: - CMod_Debug_EHSphere() : IMod("Debug_EHSphere") - { - MOD_ADD_DETOUR_MEMBER(CTFSniperRifle, ExplosiveHeadShot); - MOD_ADD_DETOUR_GLOBAL(UTIL_EntitiesInSphere); - } - - void SetEnabled(bool enable) - { - this->ToggleAllDetours(enable); - } -}; -static CMod_Debug_EHSphere s_Mod; - - -static ConVar cvar_enable("sigsegv_debug_ehsphere_enable", "0", FCVAR_NOTIFY, - "Debug: draw sphere used for sniper's explosive headshot upgrade", - [](IConVar *pConVar, const char *pOldValue, float flOldValue) { - ConVarRef var(pConVar); - s_Mod.SetEnabled(var.GetBool()); - }); - -#endif diff --git a/mod/mod_autocollectspawncredits.cpp b/mod/mod_autocollectspawncredits.cpp index 76f2acda..d980e334 100644 --- a/mod/mod_autocollectspawncredits.cpp +++ b/mod/mod_autocollectspawncredits.cpp @@ -5,50 +5,53 @@ #include "re/nextbot.h" -static RefCount rc_CCurrencyPack_ComeToRest; -DETOUR_DECL_MEMBER(void, CCurrencyPack_ComeToRest, void) +namespace Mod_AutoCollectSpawnCredits { - SCOPED_INCREMENT(rc_CCurrencyPack_ComeToRest); - return DETOUR_MEMBER_CALL(CCurrencyPack_ComeToRest)(); -} - -DETOUR_DECL_MEMBER(CNavArea *, CNavMesh_GetNavArea, const Vector& v1, float f1) -{ - CNavArea *area = DETOUR_MEMBER_CALL(CNavMesh_GetNavArea)(v1, f1); + RefCount rc_CCurrencyPack_ComeToRest; + DETOUR_DECL_MEMBER(void, CCurrencyPack_ComeToRest) + { + SCOPED_INCREMENT(rc_CCurrencyPack_ComeToRest); + return DETOUR_MEMBER_CALL(CCurrencyPack_ComeToRest)(); + } - if (area != nullptr && rc_CCurrencyPack_ComeToRest.NonZero()) { - TFNavAttributeType attr = reinterpret_cast(area)->GetTFAttributes(); + DETOUR_DECL_MEMBER(CNavArea *, CNavMesh_GetNavArea, const Vector& v1, float f1) + { + CNavArea *area = DETOUR_MEMBER_CALL(CNavMesh_GetNavArea)(v1, f1); - if ((attr & BLUE_SPAWN_ROOM) != 0) { -// DevMsg("CCurrencyPack landed in BLUE_SPAWN_ROOM area; auto-collecting\n"); - return nullptr; + if (area != nullptr && rc_CCurrencyPack_ComeToRest.NonZero()) { + TFNavAttributeType attr = reinterpret_cast(area)->GetTFAttributes(); + + if ((attr & BLUE_SPAWN_ROOM) != 0) { + // DevMsg("CCurrencyPack landed in BLUE_SPAWN_ROOM area; auto-collecting\n"); + return nullptr; + } } + + return area; } - return area; -} - - -class CMod_AutoCollectSpawnCredits : public IMod -{ -public: - CMod_AutoCollectSpawnCredits() : IMod("AutoCollectSpawnCredits") - { - MOD_ADD_DETOUR_MEMBER(CCurrencyPack, ComeToRest); - MOD_ADD_DETOUR_MEMBER(CNavMesh, GetNavArea); - } - void SetEnabled(bool enable) + class CMod : public IMod { - this->ToggleAllDetours(enable); - } -}; -static CMod_AutoCollectSpawnCredits s_Mod; - - -static ConVar cvar_enable("sigsegv_autocollectspawncredits_enable", "0", FCVAR_NOTIFY, - "Mod: auto-collect credits that land in the bots' spawn area", - [](IConVar *pConVar, const char *pOldValue, float flOldValue) { - ConVarRef var(pConVar); - s_Mod.SetEnabled(var.GetBool()); - }); + public: + CMod() : IMod("AutoCollectSpawnCredits") + { + MOD_ADD_DETOUR_MEMBER(CCurrencyPack, ComeToRest); + MOD_ADD_DETOUR_MEMBER(CNavMesh, GetNavArea); + } + + void SetEnabled(bool enable) + { + this->ToggleAllDetours(enable); + } + }; + CMod s_Mod; + + + ConVar cvar_enable("sigsegv_autocollectspawncredits_enable", "0", FCVAR_NOTIFY, + "Mod: auto-collect credits that land in the bots' spawn area", + [](IConVar *pConVar, const char *pOldValue, float flOldValue) { + ConVarRef var(pConVar); + s_Mod.SetEnabled(var.GetBool()); + }); +} diff --git a/mod/mod_botmulticlassitem.cpp b/mod/mod_botmulticlassitem.cpp index aca8045e..30acc0a3 100644 --- a/mod/mod_botmulticlassitem.cpp +++ b/mod/mod_botmulticlassitem.cpp @@ -3,148 +3,151 @@ #include "util/util.h" -/* a less strict version of TranslateWeaponEntForClass (don't return empty strings) */ -static const char *TranslateWeaponEntForClass_improved(const char *name, int classnum) +namespace Mod_BotMultiClassItem { - if (strcasecmp(name, "tf_weapon_shotgun") == 0) { - switch (classnum) { - case TF_CLASS_SOLDIER: - return "tf_weapon_shotgun_soldier"; - case TF_CLASS_PYRO: - return "tf_weapon_shotgun_pyro"; - case TF_CLASS_HEAVYWEAPONS: - return "tf_weapon_shotgun_hwg"; - case TF_CLASS_ENGINEER: - return "tf_weapon_shotgun_primary"; - default: - return "tf_weapon_shotgun_primary"; + /* a less strict version of TranslateWeaponEntForClass (don't return empty strings) */ + const char *TranslateWeaponEntForClass_improved(const char *name, int classnum) + { + if (strcasecmp(name, "tf_weapon_shotgun") == 0) { + switch (classnum) { + case TF_CLASS_SOLDIER: + return "tf_weapon_shotgun_soldier"; + case TF_CLASS_PYRO: + return "tf_weapon_shotgun_pyro"; + case TF_CLASS_HEAVYWEAPONS: + return "tf_weapon_shotgun_hwg"; + case TF_CLASS_ENGINEER: + return "tf_weapon_shotgun_primary"; + default: + return "tf_weapon_shotgun_primary"; + } } - } - - if (strcasecmp(name, "tf_weapon_pistol") == 0) { - switch (classnum) { - case TF_CLASS_SCOUT: - return "tf_weapon_pistol_scout"; - case TF_CLASS_ENGINEER: - return "tf_weapon_pistol"; - default: - return "tf_weapon_pistol"; + + if (strcasecmp(name, "tf_weapon_pistol") == 0) { + switch (classnum) { + case TF_CLASS_SCOUT: + return "tf_weapon_pistol_scout"; + case TF_CLASS_ENGINEER: + return "tf_weapon_pistol"; + default: + return "tf_weapon_pistol"; + } } - } - - if (strcasecmp(name, "tf_weapon_shovel") == 0 || strcasecmp(name, "tf_weapon_bottle") == 0) { - switch (classnum) { - case TF_CLASS_SOLDIER: - return "tf_weapon_shovel"; - case TF_CLASS_DEMOMAN: - return "tf_weapon_bottle"; + + if (strcasecmp(name, "tf_weapon_shovel") == 0 || strcasecmp(name, "tf_weapon_bottle") == 0) { + switch (classnum) { + case TF_CLASS_SOLDIER: + return "tf_weapon_shovel"; + case TF_CLASS_DEMOMAN: + return "tf_weapon_bottle"; + } } - } - - if (strcasecmp(name, "saxxy") == 0) { - switch (classnum) { - case TF_CLASS_SCOUT: - return "tf_weapon_bat"; - case TF_CLASS_SOLDIER: - return "tf_weapon_shovel"; - case TF_CLASS_PYRO: - return "tf_weapon_fireaxe"; - case TF_CLASS_DEMOMAN: - return "tf_weapon_bottle"; - case TF_CLASS_HEAVYWEAPONS: - return "tf_weapon_fireaxe"; - case TF_CLASS_ENGINEER: - return "tf_weapon_wrench"; - case TF_CLASS_MEDIC: - return "tf_weapon_bonesaw"; - case TF_CLASS_SNIPER: - return "tf_weapon_club"; - case TF_CLASS_SPY: - return "tf_weapon_knife"; + + if (strcasecmp(name, "saxxy") == 0) { + switch (classnum) { + case TF_CLASS_SCOUT: + return "tf_weapon_bat"; + case TF_CLASS_SOLDIER: + return "tf_weapon_shovel"; + case TF_CLASS_PYRO: + return "tf_weapon_fireaxe"; + case TF_CLASS_DEMOMAN: + return "tf_weapon_bottle"; + case TF_CLASS_HEAVYWEAPONS: + return "tf_weapon_fireaxe"; + case TF_CLASS_ENGINEER: + return "tf_weapon_wrench"; + case TF_CLASS_MEDIC: + return "tf_weapon_bonesaw"; + case TF_CLASS_SNIPER: + return "tf_weapon_club"; + case TF_CLASS_SPY: + return "tf_weapon_knife"; + } } - } - - if (strcasecmp(name, "tf_weapon_throwable") == 0) { - switch (classnum) { - case TF_CLASS_MEDIC: - return "tf_weapon_throwable_primary"; - default: - return "tf_weapon_throwable_secondary"; + + if (strcasecmp(name, "tf_weapon_throwable") == 0) { + switch (classnum) { + case TF_CLASS_MEDIC: + return "tf_weapon_throwable_primary"; + default: + return "tf_weapon_throwable_secondary"; + } } - } - - if (strcasecmp(name, "tf_weapon_parachute") == 0) { - switch (classnum) { - case TF_CLASS_SOLDIER: - return "tf_weapon_parachute_secondary"; - case TF_CLASS_DEMOMAN: - return "tf_weapon_parachute_primary"; - default: - return "tf_weapon_parachute"; + + if (strcasecmp(name, "tf_weapon_parachute") == 0) { + switch (classnum) { + case TF_CLASS_SOLDIER: + return "tf_weapon_parachute_secondary"; + case TF_CLASS_DEMOMAN: + return "tf_weapon_parachute_primary"; + default: + return "tf_weapon_parachute"; + } } - } - - if (strcasecmp(name, "tf_weapon_revolver") == 0) { - switch (classnum) { - case TF_CLASS_ENGINEER: - return "tf_weapon_revolver_secondary"; - default: - return "tf_weapon_revolver"; + + if (strcasecmp(name, "tf_weapon_revolver") == 0) { + switch (classnum) { + case TF_CLASS_ENGINEER: + return "tf_weapon_revolver_secondary"; + default: + return "tf_weapon_revolver"; + } } + + /* if not handled: return original entity name, not an empty string */ + return name; } - /* if not handled: return original entity name, not an empty string */ - return name; -} - - -static RefCount rc_CTFBot_AddItem; -static int bot_classnum = TF_CLASS_UNDEFINED; -DETOUR_DECL_MEMBER(void, CTFBot_AddItem, const char *item) -{ - SCOPED_INCREMENT(rc_CTFBot_AddItem); - bot_classnum = reinterpret_cast(this)->GetPlayerClass()->GetClassIndex(); - DETOUR_MEMBER_CALL(CTFBot_AddItem)(item); -} - -DETOUR_DECL_STATIC(CBaseEntity *, CreateEntityByName, const char *className, int iForceEdictIndex) -{ - if (rc_CTFBot_AddItem.NonZero()) { - className = TranslateWeaponEntForClass_improved(className, bot_classnum); - } - return DETOUR_STATIC_CALL(CreateEntityByName)(className, iForceEdictIndex); -} - - -// void CTFBot::AddItem(const char *name) -// - CBaseEntity *CItemGeneration::GenerateRandomItem(CItemSelectionCriteria *criteria, const Vector& pos, const QAngle& ang) -// - CBaseEntity *CItemGeneration::SpawnItem(int itemdefidx, const Vector& pos, const QAngle& ang, int, int, const char *class_or_null) -// - CBaseEntity *CreateEntityByName(const char *className, int iForceEdictIndex) - -// const char *TranslateWeaponEntForClass(const char *name, int classnum) - - -class CMod_BotMultiClassItem : public IMod -{ -public: - CMod_BotMultiClassItem() : IMod("BotMultiClassItem") + RefCount rc_CTFBot_AddItem; + int bot_classnum = TF_CLASS_UNDEFINED; + DETOUR_DECL_MEMBER(void, CTFBot_AddItem, const char *item) { - MOD_ADD_DETOUR_MEMBER(CTFBot, AddItem); - MOD_ADD_DETOUR_GLOBAL(CreateEntityByName); + SCOPED_INCREMENT(rc_CTFBot_AddItem); + bot_classnum = reinterpret_cast(this)->GetPlayerClass()->GetClassIndex(); + DETOUR_MEMBER_CALL(CTFBot_AddItem)(item); } - void SetEnabled(bool enable) + DETOUR_DECL_STATIC(CBaseEntity *, CreateEntityByName, const char *className, int iForceEdictIndex) { - this->ToggleAllDetours(enable); + if (rc_CTFBot_AddItem.NonZero()) { + className = TranslateWeaponEntForClass_improved(className, bot_classnum); + } + + return DETOUR_STATIC_CALL(CreateEntityByName)(className, iForceEdictIndex); } -}; -static CMod_BotMultiClassItem s_Mod; - - -static ConVar cvar_enable("sigsegv_botmulticlassitem_enable", "0", FCVAR_NOTIFY, - "Mod: remap item entity names so bots can be given multi-class weapons", - [](IConVar *pConVar, const char *pOldValue, float flOldValue) { - ConVarRef var(pConVar); - s_Mod.SetEnabled(var.GetBool()); - }); + + + // void CTFBot::AddItem(const char *name) + // - CBaseEntity *CItemGeneration::GenerateRandomItem(CItemSelectionCriteria *criteria, const Vector& pos, const QAngle& ang) + // - CBaseEntity *CItemGeneration::SpawnItem(int itemdefidx, const Vector& pos, const QAngle& ang, int, int, const char *class_or_null) + // - CBaseEntity *CreateEntityByName(const char *className, int iForceEdictIndex) + + // const char *TranslateWeaponEntForClass(const char *name, int classnum) + + + class CMod : public IMod + { + public: + CMod() : IMod("BotMultiClassItem") + { + MOD_ADD_DETOUR_MEMBER(CTFBot, AddItem); + MOD_ADD_DETOUR_GLOBAL(CreateEntityByName); + } + + void SetEnabled(bool enable) + { + this->ToggleAllDetours(enable); + } + }; + CMod s_Mod; + + + ConVar cvar_enable("sigsegv_botmulticlassitem_enable", "0", FCVAR_NOTIFY, + "Mod: remap item entity names so bots can be given multi-class weapons", + [](IConVar *pConVar, const char *pOldValue, float flOldValue) { + ConVarRef var(pConVar); + s_Mod.SetEnabled(var.GetBool()); + }); +} diff --git a/mod/mod_engienopush.cpp b/mod/mod_engienopush.cpp index 9dc7771f..bd16d4fe 100644 --- a/mod/mod_engienopush.cpp +++ b/mod/mod_engienopush.cpp @@ -5,71 +5,63 @@ #include "re/nextbot.h" -static RefCount rc_TeleSpawn_Update; -DETOUR_DECL_MEMBER(ActionResult, CTFBotMvMEngineerTeleportSpawn_Update, CTFBot *actor, float dt) +namespace Mod_EngieNoPush { - SCOPED_INCREMENT(rc_TeleSpawn_Update); - return DETOUR_MEMBER_CALL(CTFBotMvMEngineerTeleportSpawn_Update)(actor, dt); -} - -static RefCount rc_BuildSentry_Update; -DETOUR_DECL_MEMBER(ActionResult, CTFBotMvMEngineerBuildSentryGun_Update, CTFBot *actor, float dt) -{ - SCOPED_INCREMENT(rc_BuildSentry_Update); - return DETOUR_MEMBER_CALL(CTFBotMvMEngineerBuildSentryGun_Update)(actor, dt); -} - -static RefCount rc_BuildTele_Update; -DETOUR_DECL_MEMBER(ActionResult, CTFBotMvMEngineerBuildTeleportExit_Update, CTFBot *actor, float dt) -{ - SCOPED_INCREMENT(rc_BuildTele_Update); - return DETOUR_MEMBER_CALL(CTFBotMvMEngineerBuildTeleportExit_Update)(actor, dt); -} - -#if 0 -DETOUR_DECL_MEMBER(void, CTFGameRules_PushAllPlayersAway, const Vector *v1, float f1, float f2, int i1, CUtlVector *vec) -{ - if (rc_TeleSpawn_Update.NonZero() || rc_BuildSentry_Update.NonZero() || rc_BuildTele_Update.NonZero()) { - return; + RefCount rc_TeleSpawn_Update; + DETOUR_DECL_MEMBER(ActionResult, CTFBotMvMEngineerTeleportSpawn_Update, CTFBot *actor, float dt) + { + SCOPED_INCREMENT(rc_TeleSpawn_Update); + return DETOUR_MEMBER_CALL(CTFBotMvMEngineerTeleportSpawn_Update)(actor, dt); } - DETOUR_MEMBER_CALL(CTFGameRules_PushAllPlayersAway)(v1, f1, f2, i1, vec); -} -#endif - -DETOUR_DECL_MEMBER(void, CTFPlayer_ApplyAbsVelocityImpulse, const Vector *v1) -{ - if (rc_TeleSpawn_Update.NonZero() || rc_BuildSentry_Update.NonZero() || rc_BuildTele_Update.NonZero()) { - return; + RefCount rc_BuildSentry_Update; + DETOUR_DECL_MEMBER(ActionResult, CTFBotMvMEngineerBuildSentryGun_Update, CTFBot *actor, float dt) + { + SCOPED_INCREMENT(rc_BuildSentry_Update); + return DETOUR_MEMBER_CALL(CTFBotMvMEngineerBuildSentryGun_Update)(actor, dt); } - DETOUR_MEMBER_CALL(CTFPlayer_ApplyAbsVelocityImpulse)(v1); -} - - -class CMod_EngieNoPush : public IMod -{ -public: - CMod_EngieNoPush() : IMod("EngieNoPush") + RefCount rc_BuildTele_Update; + DETOUR_DECL_MEMBER(ActionResult, CTFBotMvMEngineerBuildTeleportExit_Update, CTFBot *actor, float dt) { - MOD_ADD_DETOUR_MEMBER(CTFBotMvMEngineerTeleportSpawn, Update); - MOD_ADD_DETOUR_MEMBER(CTFBotMvMEngineerBuildSentryGun, Update); - MOD_ADD_DETOUR_MEMBER(CTFBotMvMEngineerBuildTeleportExit, Update); - //MOD_ADD_DETOUR_MEMBER(CTFGameRules, PushAllPlayersAway); - MOD_ADD_DETOUR_MEMBER(CTFPlayer, ApplyAbsVelocityImpulse); + SCOPED_INCREMENT(rc_BuildTele_Update); + return DETOUR_MEMBER_CALL(CTFBotMvMEngineerBuildTeleportExit_Update)(actor, dt); } - void SetEnabled(bool enable) + /* would prefer to detour CTFGameRules::PushAllPlayersAway, but it's hard to reliably locate on Windows */ + DETOUR_DECL_MEMBER(void, CTFPlayer_ApplyAbsVelocityImpulse, const Vector *v1) { - this->ToggleAllDetours(enable); + if (rc_TeleSpawn_Update.NonZero() || rc_BuildSentry_Update.NonZero() || rc_BuildTele_Update.NonZero()) { + return; + } + + DETOUR_MEMBER_CALL(CTFPlayer_ApplyAbsVelocityImpulse)(v1); } -}; -static CMod_EngieNoPush s_Mod; - - -static ConVar cvar_enable("sigsegv_engienopush_enable", "0", FCVAR_NOTIFY, - "Mod: remove engiebot push force when spawning and building", - [](IConVar *pConVar, const char *pOldValue, float flOldValue) { - ConVarRef var(pConVar); - s_Mod.SetEnabled(var.GetBool()); - }); + + + class CMod : public IMod + { + public: + CMod() : IMod("EngieNoPush") + { + MOD_ADD_DETOUR_MEMBER(CTFBotMvMEngineerTeleportSpawn, Update); + MOD_ADD_DETOUR_MEMBER(CTFBotMvMEngineerBuildSentryGun, Update); + MOD_ADD_DETOUR_MEMBER(CTFBotMvMEngineerBuildTeleportExit, Update); + MOD_ADD_DETOUR_MEMBER(CTFPlayer, ApplyAbsVelocityImpulse); + } + + void SetEnabled(bool enable) + { + this->ToggleAllDetours(enable); + } + }; + CMod s_Mod; + + + ConVar cvar_enable("sigsegv_engienopush_enable", "0", FCVAR_NOTIFY, + "Mod: remove engiebot push force when spawning and building", + [](IConVar *pConVar, const char *pOldValue, float flOldValue) { + ConVarRef var(pConVar); + s_Mod.SetEnabled(var.GetBool()); + }); +} diff --git a/mod/mod_robotheadnoslide.cpp b/mod/mod_robotheadnoslide.cpp new file mode 100644 index 00000000..3f68f737 --- /dev/null +++ b/mod/mod_robotheadnoslide.cpp @@ -0,0 +1,56 @@ +#include "mod.h" +#include "util/scope.h" +#include "sm/detours.h" +#include "stub/gamerules.h" +#include "stub/tfplayer.h" + + +namespace Mod_RobotHeadNoSlide +{ + RefCount rc_TFPlayerThink; + DETOUR_DECL_MEMBER(void, CTFPlayer_TFPlayerThink) + { + SCOPED_INCREMENT(rc_TFPlayerThink); + DETOUR_MEMBER_CALL(CTFPlayer_TFPlayerThink)(); + } + + DETOUR_DECL_MEMBER(void, CTFPlayer_ApplyAbsVelocityImpulse, const Vector *v1) + { + if (rc_TFPlayerThink.NonZero() && v1->z == 100.0f && + TFGameRules() && TFGameRules()->IsMannVsMachineMode()) { + CTFPlayer *player = reinterpret_cast(this); + + CBaseEntity *groundent = player->GetGroundEntity(); + if (groundent != nullptr && groundent->IsPlayer()) { + return; + } + } + + DETOUR_MEMBER_CALL(CTFPlayer_ApplyAbsVelocityImpulse)(v1); + } + + + class CMod : public IMod + { + public: + CMod() : IMod("RobotHeadNoSlide") + { + MOD_ADD_DETOUR_MEMBER(CTFPlayer, TFPlayerThink); + // MOD_ADD_DETOUR_MEMBER(CTFPlayer, ApplyAbsVelocityImpulse); + } + + void SetEnabled(bool enable) + { + this->ToggleAllDetours(enable); + } + }; + CMod s_Mod; + + + ConVar cvar_enable("sigsegv_robotheadnoslide_enable", "0", FCVAR_NOTIFY, + "Mod: remove the sliding force that prevents players from standing on robots' heads", + [](IConVar *pConVar, const char *pOldValue, float flOldValue) { + ConVarRef var(pConVar); + s_Mod.SetEnabled(var.GetBool()); + }); +} diff --git a/mod/mod_robottickle.cpp b/mod/mod_robottickle.cpp index 69e8f488..0b90263f 100644 --- a/mod/mod_robottickle.cpp +++ b/mod/mod_robottickle.cpp @@ -1,40 +1,50 @@ #include "mod.h" #include "sm/detours.h" -#include "stub/stub.h" +#include "stub/tfplayer.h" -#ifdef EXPERIMENTAL +#if defined __GNUC__ +#warning TODO: move cvars into class +#endif + -DETOUR_DECL_MEMBER(bool, CTFPlayer_CanBeForcedToLaugh) +namespace Mod_RobotTickle { + ConVar cvar_giants("sigsegv_robottickle_giants", "0", FCVAR_NOTIFY, + "Mod: if set to 0, tickling will only be enabled for non-giant robots"); - return DETOUR_MEMBER_CALL(CTFPlayer_CanBeForcedToLaugh)(); -} - - -class CMod_RobotTickle : public IMod -{ -public: - CMod_RobotTickle() : IMod("RobotTickle") + DETOUR_DECL_MEMBER(bool, CTFPlayer_CanBeForcedToLaugh) { + if (!cvar_giants.GetBool()) { + CTFPlayer *player = reinterpret_cast(this); + return !player->IsMiniBoss(); + } + return true; } - void SetEnabled(bool enable) + + class CMod : public IMod { - this->ToggleAllDetours(enable); - } -}; -static CMod_RobotTickle s_Mod; - - -/*static ConVar cvar_enable("sigsegv_robottickle_enable", "0", FCVAR_NOTIFY, - "Mod: auto-collect credits that land in the bots' spawn area", - [](IConVar *pConVar, const char *pOldValue, float flOldValue) { - ConVarRef var(pConVar); - s_Mod.SetEnabled(var.GetBool()); - }); -*/ - -#endif + public: + CMod() : IMod("RobotTickle") + { + MOD_ADD_DETOUR_MEMBER(CTFPlayer, CanBeForcedToLaugh); + } + + void SetEnabled(bool enable) + { + this->ToggleAllDetours(enable); + } + }; + CMod s_Mod; + + + ConVar cvar_enable("sigsegv_robottickle_enable", "0", FCVAR_NOTIFY, + "Mod: allow robots to be tickled (e.g. by the Holiday Punch)", + [](IConVar *pConVar, const char *pOldValue, float flOldValue) { + ConVarRef var(pConVar); + s_Mod.SetEnabled(var.GetBool()); + }); +} diff --git a/mod/mod_sniperchargeuncap.cpp b/mod/mod_sniperchargeuncap.cpp index 5eacd7f9..8b3bb572 100644 --- a/mod/mod_sniperchargeuncap.cpp +++ b/mod/mod_sniperchargeuncap.cpp @@ -1,131 +1,134 @@ #include "mod.h" -#if defined _LINUX - -static constexpr uint8_t s_Buf[] = { - 0xf3, 0x0f, 0x10, 0x45, 0x00, // +0000 movss xmm0,DWORD PTR [ebp-0xXX] - 0xf3, 0x0f, 0x58, 0x45, 0x00, // +0005 addss xmm0,DWORD PTR [ebp-0xXX] - 0xf3, 0x0f, 0x10, 0xc8, // +000A movss xmm1,xmm0 - 0x0f, 0x28, 0xc1, // +000E movaps xmm0,xmm1 - 0x0f, 0x57, 0xc9, // +0011 xorps xmm1,xmm1 - 0xf3, 0x0f, 0x5f, 0xc1, // +0014 maxss xmm0,xmm1 - 0xf3, 0x0f, 0x5d, 0x05, 0x00, 0x00, 0x00, 0x00, // +0018 minss xmm0,DWORD PTR [XXXXXXXX] - 0xf3, 0x0f, 0x11, 0x45, 0x00, // +0020 movss DWORD PTR [ebp-0xXX],xmm0 - 0xf3, 0x0f, 0x59, 0x40, 0x10, // +0025 mulss xmm0,DWORD PTR [eax+0x10] -}; - -struct CPatch_UncapChargeRate_Common : public IPatch +namespace Mod_SniperChargeUncap { - CPatch_UncapChargeRate_Common() : IPatch(sizeof(s_Buf)) {} +#if defined _LINUX + + constexpr uint8_t s_Buf[] = { + 0xf3, 0x0f, 0x10, 0x45, 0x00, // +0000 movss xmm0,DWORD PTR [ebp-0xXX] + 0xf3, 0x0f, 0x58, 0x45, 0x00, // +0005 addss xmm0,DWORD PTR [ebp-0xXX] + 0xf3, 0x0f, 0x10, 0xc8, // +000A movss xmm1,xmm0 + 0x0f, 0x28, 0xc1, // +000E movaps xmm0,xmm1 + 0x0f, 0x57, 0xc9, // +0011 xorps xmm1,xmm1 + 0xf3, 0x0f, 0x5f, 0xc1, // +0014 maxss xmm0,xmm1 + 0xf3, 0x0f, 0x5d, 0x05, 0x00, 0x00, 0x00, 0x00, // +0018 minss xmm0,DWORD PTR [XXXXXXXX] + 0xf3, 0x0f, 0x11, 0x45, 0x00, // +0020 movss DWORD PTR [ebp-0xXX],xmm0 + 0xf3, 0x0f, 0x59, 0x40, 0x10, // +0025 mulss xmm0,DWORD PTR [eax+0x10] + }; - virtual void GetVerifyInfo(ByteBuf& buf, ByteBuf& mask) const override + struct CPatch_UncapChargeRate_Common : public IPatch { - buf.CopyFrom(s_Buf); + CPatch_UncapChargeRate_Common() : IPatch(sizeof(s_Buf)) {} - mask.SetRange(0x05 + 4, 1, 0x00); - mask.SetRange(0x00 + 4, 1, 0x00); - mask.SetRange(0x18 + 4, 4, 0x00); - mask.SetRange(0x20 + 4, 1, 0x00); - } + virtual void GetVerifyInfo(ByteBuf& buf, ByteBuf& mask) const override + { + buf.CopyFrom(s_Buf); + + mask.SetRange(0x05 + 4, 1, 0x00); + mask.SetRange(0x00 + 4, 1, 0x00); + mask.SetRange(0x18 + 4, 4, 0x00); + mask.SetRange(0x20 + 4, 1, 0x00); + } + + virtual void GetPatchInfo(ByteBuf& buf, ByteBuf& mask) const override + { + /* NOP out the MINSS instruction */ + buf.SetRange(0x18, 8, 0x90); + mask.SetRange(0x18, 8, 0xff); + } + }; - virtual void GetPatchInfo(ByteBuf& buf, ByteBuf& mask) const override + struct CPatch_UncapChargeRate_CTFSniperRifle : public CPatch_UncapChargeRate_Common { - /* NOP out the MINSS instruction */ - buf.SetRange(0x18, 8, 0x90); - mask.SetRange(0x18, 8, 0xff); - } -}; - -struct CPatch_UncapChargeRate_CTFSniperRifle : public CPatch_UncapChargeRate_Common -{ - virtual const char *GetFuncName() const override { return "CTFSniperRifle::ItemPostFrame"; } - virtual uint32_t GetFuncOffMin() const override { return 0x0000; } - virtual uint32_t GetFuncOffMax() const override { return 0x0380; } // @ 0x026a -}; - -struct CPatch_UncapChargeRate_CTFSniperRifleClassic : CPatch_UncapChargeRate_Common -{ - virtual const char *GetFuncName() const override { return "CTFSniperRifleClassic::ItemPostFrame"; } - virtual uint32_t GetFuncOffMin() const override { return 0x0000; } - virtual uint32_t GetFuncOffMax() const override { return 0x0280; } // @ 0x0146 -}; - + virtual const char *GetFuncName() const override { return "CTFSniperRifle::ItemPostFrame"; } + virtual uint32_t GetFuncOffMin() const override { return 0x0000; } + virtual uint32_t GetFuncOffMax() const override { return 0x0380; } // @ 0x026a + }; + + struct CPatch_UncapChargeRate_CTFSniperRifleClassic : CPatch_UncapChargeRate_Common + { + virtual const char *GetFuncName() const override { return "CTFSniperRifleClassic::ItemPostFrame"; } + virtual uint32_t GetFuncOffMin() const override { return 0x0000; } + virtual uint32_t GetFuncOffMax() const override { return 0x0280; } // @ 0x0146 + }; + #elif defined _WINDOWS - -static constexpr uint8_t s_Buf[] = { - 0xa1, 0x00, 0x00, 0x00, 0x00, // +0000 mov eax,DWORD PTR [xxxxxxxx] - 0xf3, 0x0f, 0x10, 0x05, 0x00, 0x00, 0x00, 0x00, // +0005 movss xmm0,DWORD PTR [xxxxxxxx] - 0xf3, 0x0f, 0x10, 0x50, 0x10, // +000D movss xmm2,DWORD PTR [eax+0x10] - 0xd9, 0x5d, 0x00, // +0012 fstp [ebp-0xXX] - 0xf3, 0x0f, 0x10, 0x4d, 0x00, // +0015 movss xmm1,[ebp-0xXX] - 0xf3, 0x0f, 0x5f, 0xc8, // +001A maxss xmm1,xmm0 - 0xf3, 0x0f, 0x10, 0x05, 0x00, 0x00, 0x00, 0x00, // +001E movss xmm0,DWORD PTR [xxxxxxxx] - 0xf3, 0x0f, 0x5d, 0xc8, // +0026 minss xmm1,xmm0 - 0xf3, 0x0f, 0x10, 0x05, 0x00, 0x00, 0x00, 0x00, // +002A movss xmm0,DWORD PTR [xxxxxxxx] -}; - -struct CPatch_UncapChargeRate_Common : public IPatch -{ - CPatch_UncapChargeRate_Common() : IPatch(sizeof(s_Buf)) {} - virtual void GetVerifyInfo(ByteBuf& buf, ByteBuf& mask) const override + constexpr uint8_t s_Buf[] = { + 0xa1, 0x00, 0x00, 0x00, 0x00, // +0000 mov eax,DWORD PTR [xxxxxxxx] + 0xf3, 0x0f, 0x10, 0x05, 0x00, 0x00, 0x00, 0x00, // +0005 movss xmm0,DWORD PTR [xxxxxxxx] + 0xf3, 0x0f, 0x10, 0x50, 0x10, // +000D movss xmm2,DWORD PTR [eax+0x10] + 0xd9, 0x5d, 0x00, // +0012 fstp [ebp-0xXX] + 0xf3, 0x0f, 0x10, 0x4d, 0x00, // +0015 movss xmm1,[ebp-0xXX] + 0xf3, 0x0f, 0x5f, 0xc8, // +001A maxss xmm1,xmm0 + 0xf3, 0x0f, 0x10, 0x05, 0x00, 0x00, 0x00, 0x00, // +001E movss xmm0,DWORD PTR [xxxxxxxx] + 0xf3, 0x0f, 0x5d, 0xc8, // +0026 minss xmm1,xmm0 + 0xf3, 0x0f, 0x10, 0x05, 0x00, 0x00, 0x00, 0x00, // +002A movss xmm0,DWORD PTR [xxxxxxxx] + }; + + struct CPatch_UncapChargeRate_Common : public IPatch { - buf.CopyFrom(s_Buf); + CPatch_UncapChargeRate_Common() : IPatch(sizeof(s_Buf)) {} + + virtual void GetVerifyInfo(ByteBuf& buf, ByteBuf& mask) const override + { + buf.CopyFrom(s_Buf); + + mask.SetRange(0x00 + 1, 4, 0x00); + mask.SetRange(0x05 + 4, 4, 0x00); + mask.SetRange(0x12 + 2, 1, 0x00); + mask.SetRange(0x15 + 4, 1, 0x00); + mask.SetRange(0x1e + 4, 4, 0x00); + mask.SetRange(0x2a + 4, 4, 0x00); + } - mask.SetRange(0x00 + 1, 4, 0x00); - mask.SetRange(0x05 + 4, 4, 0x00); - mask.SetRange(0x12 + 2, 1, 0x00); - mask.SetRange(0x15 + 4, 1, 0x00); - mask.SetRange(0x1e + 4, 4, 0x00); - mask.SetRange(0x2a + 4, 4, 0x00); - } + virtual void GetPatchInfo(ByteBuf& buf, ByteBuf& mask) const override + { + /* NOP out the MINSS instruction */ + buf.SetRange(0x26, 4, 0x90); + mask.SetRange(0x26, 4, 0xff); + } + }; - virtual void GetPatchInfo(ByteBuf& buf, ByteBuf& mask) const override + struct CPatch_UncapChargeRate_CTFSniperRifle : public CPatch_UncapChargeRate_Common { - /* NOP out the MINSS instruction */ - buf.SetRange(0x26, 4, 0x90); - mask.SetRange(0x26, 4, 0xff); - } -}; - -struct CPatch_UncapChargeRate_CTFSniperRifle : public CPatch_UncapChargeRate_Common -{ - virtual const char *GetFuncName() const override { return "CTFSniperRifle::ItemPostFrame"; } - virtual uint32_t GetFuncOffMin() const override { return 0x0000; } - virtual uint32_t GetFuncOffMax() const override { return 0x0200; } // @ 0x16e -}; - -struct CPatch_UncapChargeRate_CTFSniperRifleClassic : CPatch_UncapChargeRate_Common -{ - virtual const char *GetFuncName() const override { return "CTFSniperRifleClassic::ItemPostFrame"; } - virtual uint32_t GetFuncOffMin() const override { return 0x0000; } - virtual uint32_t GetFuncOffMax() const override { return 0x0200; } // @ 0x162 -}; - -#endif - - -class CMod_SniperChargeUncap : public IMod -{ -public: - CMod_SniperChargeUncap() : IMod("SniperChargeUncap") + virtual const char *GetFuncName() const override { return "CTFSniperRifle::ItemPostFrame"; } + virtual uint32_t GetFuncOffMin() const override { return 0x0000; } + virtual uint32_t GetFuncOffMax() const override { return 0x0200; } // @ 0x16e + }; + + struct CPatch_UncapChargeRate_CTFSniperRifleClassic : CPatch_UncapChargeRate_Common { - this->AddPatch(new CPatch_UncapChargeRate_CTFSniperRifle()); - this->AddPatch(new CPatch_UncapChargeRate_CTFSniperRifleClassic()); - } + virtual const char *GetFuncName() const override { return "CTFSniperRifleClassic::ItemPostFrame"; } + virtual uint32_t GetFuncOffMin() const override { return 0x0000; } + virtual uint32_t GetFuncOffMax() const override { return 0x0200; } // @ 0x162 + }; + +#endif - void SetEnabled(bool enable) + + class CMod : public IMod { - this->ToggleAllPatches(enable); - } -}; -static CMod_SniperChargeUncap s_Mod; - - -static ConVar cvar_enable("sigsegv_sniperchargeuncap_enable", "0", FCVAR_NOTIFY, - "Mod: remove the 200 percent upper limit on sniper rifle charge rate", - [](IConVar *pConVar, const char *pOldValue, float flOldValue) { - ConVarRef var(pConVar); - s_Mod.SetEnabled(var.GetBool()); - }); + public: + CMod() : IMod("SniperChargeUncap") + { + this->AddPatch(new CPatch_UncapChargeRate_CTFSniperRifle()); + this->AddPatch(new CPatch_UncapChargeRate_CTFSniperRifleClassic()); + } + + void SetEnabled(bool enable) + { + this->ToggleAllPatches(enable); + } + }; + CMod s_Mod; + + + ConVar cvar_enable("sigsegv_sniperchargeuncap_enable", "0", FCVAR_NOTIFY, + "Mod: remove the 200 percent upper limit on sniper rifle charge rate", + [](IConVar *pConVar, const char *pOldValue, float flOldValue) { + ConVarRef var(pConVar); + s_Mod.SetEnabled(var.GetBool()); + }); +} diff --git a/mod/mod_stickypusher.cpp b/mod/mod_stickypusher.cpp index e3d40726..aeef28d5 100644 --- a/mod/mod_stickypusher.cpp +++ b/mod/mod_stickypusher.cpp @@ -5,139 +5,142 @@ #ifdef EXPERIMENTAL -constexpr int MISSION_PUSHSTICKIES = 7; - - -class CTFBotMissionPushStickies : public Action +namespace Mod_StickyPusher { -public: - CTFBotMissionPushStickies() - { - DevMsg("CTFBotMissionPushStickies::CTFBotMissionPushStickies\n"); - - // TODO - } + constexpr int MISSION_PUSHSTICKIES = 7; - virtual ~CTFBotMissionPushStickies() + + class CTFBotMissionPushStickies : public Action { - DevMsg("CTFBotMissionPushStickies::~CTFBotMissionPushStickies\n"); + public: + CTFBotMissionPushStickies() + { + DevMsg("CTFBotMissionPushStickies::CTFBotMissionPushStickies\n"); + + // TODO + } - // TODO - } + virtual ~CTFBotMissionPushStickies() + { + DevMsg("CTFBotMissionPushStickies::~CTFBotMissionPushStickies\n"); + + // TODO + } + + virtual const char *GetName() const override { return "MissionPushStickies"; } + + virtual ActionResult OnStart(CTFBot *actor, Action *action) override + { + DevMsg("CTFBotMissionPushStickies::OnStart(#%d)\n", ENTINDEX(actor)); + + // TODO + + return ActionResult::Continue(); + } + + virtual ActionResult Update(CTFBot *actor, float dt) override + { + DevMsg("CTFBotMissionPushStickies::Update(#%d)\n", ENTINDEX(actor)); + + CTFBotPathCost cost_func((CTFBot *)0x11111111, RETREAT_ROUTE); + + + // TODO + + return ActionResult::Continue(); + } + + virtual void OnEnd(CTFBot *actor, Action *action) override + { + DevMsg("CTFBotMissionPushStickies::OnEnd(#%d)\n", ENTINDEX(actor)); + + // TODO + } + }; - virtual const char *GetName() const override { return "MissionPushStickies"; } - virtual ActionResult OnStart(CTFBot *actor, Action *action) override + RefCount rc_CMissionPopulator_Parse; + DETOUR_DECL_MEMBER(void, CMissionPopulator_Parse, KeyValues *kv) { - DevMsg("CTFBotMissionPushStickies::OnStart(#%d)\n", ENTINDEX(actor)); - - // TODO - - return ActionResult::Continue(); + SCOPED_INCREMENT(rc_CMissionPopulator_Parse); + return DETOUR_MEMBER_CALL(CMissionPopulator_Parse)(kv); } - virtual ActionResult Update(CTFBot *actor, float dt) override + DETOUR_DECL_MEMBER(bool, CSpawnLocation_Parse, KeyValues *kv) { - DevMsg("CTFBotMissionPushStickies::Update(#%d)\n", ENTINDEX(actor)); - - CTFBotPathCost cost_func((CTFBot *)0x11111111, RETREAT_ROUTE); - - - // TODO + if (rc_CMissionPopulator_Parse.NonZero()) { + if (V_stricmp(kv->GetName(), "Objective") == 0 && + V_stricmp(kv->GetString(), "PushStickies") == 0) { + int *p_m_Objective = (int *)((uintptr_t)this - (offsetof(CMissionPopulator, m_Where) - + offsetof(CMissionPopulator, m_Objective))); + + *p_m_Objective = MISSION_PUSHSTICKIES; + return true; + } + } - return ActionResult::Continue(); + return DETOUR_MEMBER_CALL(CSpawnLocation_Parse)(kv); } - virtual void OnEnd(CTFBot *actor, Action *action) override + + DETOUR_DECL_MEMBER(void, CMissionPopulator_Update) { - DevMsg("CTFBotMissionPushStickies::OnEnd(#%d)\n", ENTINDEX(actor)); + DETOUR_MEMBER_CALL(CMissionPopulator_Update)(); - // TODO - } -}; - - -static RefCount rc_CMissionPopulator_Parse; -DETOUR_DECL_MEMBER(void, CMissionPopulator_Parse, KeyValues *kv) -{ - SCOPED_INCREMENT(rc_CMissionPopulator_Parse); - return DETOUR_MEMBER_CALL(CMissionPopulator_Parse)(kv); -} - -DETOUR_DECL_MEMBER(bool, CSpawnLocation_Parse, KeyValues *kv) -{ - if (rc_CMissionPopulator_Parse.NonZero()) { - if (V_stricmp(kv->GetName(), "Objective") == 0 && - V_stricmp(kv->GetString(), "PushStickies") == 0) { - int *p_m_Objective = (int *)((uintptr_t)this - (offsetof(CMissionPopulator, m_Where) - - offsetof(CMissionPopulator, m_Objective))); - - *p_m_Objective = MISSION_PUSHSTICKIES; - return true; + CMissionPopulator *realthis = reinterpret_cast(this); + + int objective = realthis->m_Objective; + if (objective == MISSION_PUSHSTICKIES) { + realthis->UpdateMission(objective); } } - return DETOUR_MEMBER_CALL(CSpawnLocation_Parse)(kv); -} - - -DETOUR_DECL_MEMBER(void, CMissionPopulator_Update) -{ - DETOUR_MEMBER_CALL(CMissionPopulator_Update)(); - - CMissionPopulator *realthis = reinterpret_cast(this); - - int objective = realthis->m_Objective; - if (objective == MISSION_PUSHSTICKIES) { - realthis->UpdateMission(objective); - } -} - - -// TODO: detour to make CMissionPopulator::Update call CMissionPopulator::UpdateMission for mission numbers above 5 -// TODO: detour to add "PushStickies" as a valid Objective KV for CMissionPopulator::Parse -// TODO: detour to make CTFBotScenarioMonitor::DesiredScenarioAndClassAction select CTFBotMissionPushStickies for MISSION_PUSHSTICKIES - - -DETOUR_DECL_MEMBER(Action *, CTFBotScenarioMonitor_DesiredScenarioAndClassAction, CTFBot *actor) -{ - DevMsg("CTFBotScenarioMonitor::DesiredScenarioAndClassAction\n"); - int mission = actor->GetMission(); - DevMsg("actor->m_nMission = %d\n", mission); + // TODO: detour to make CMissionPopulator::Update call CMissionPopulator::UpdateMission for mission numbers above 5 + // TODO: detour to add "PushStickies" as a valid Objective KV for CMissionPopulator::Parse + // TODO: detour to make CTFBotScenarioMonitor::DesiredScenarioAndClassAction select CTFBotMissionPushStickies for MISSION_PUSHSTICKIES - return new CTFBotMissionPushStickies(); - //DETOUR_MEMBER_CALL(CTFBotScenarioMonitor_DesiredScenarioAndClassAction)(actor); -} - - -class CMod_StickyPusher : public IMod -{ -public: - CMod_StickyPusher() : IMod("StickyPusher") + DETOUR_DECL_MEMBER(Action *, CTFBotScenarioMonitor_DesiredScenarioAndClassAction, CTFBot *actor) { - MOD_ADD_DETOUR_MEMBER(CMissionPopulator, Parse); - MOD_ADD_DETOUR_MEMBER(CSpawnLocation, Parse); + DevMsg("CTFBotScenarioMonitor::DesiredScenarioAndClassAction\n"); + + int mission = actor->GetMission(); + DevMsg("actor->m_nMission = %d\n", mission); - MOD_ADD_DETOUR_MEMBER(CMissionPopulator, Update); + return new CTFBotMissionPushStickies(); - MOD_ADD_DETOUR_MEMBER(CTFBotScenarioMonitor, DesiredScenarioAndClassAction); + //DETOUR_MEMBER_CALL(CTFBotScenarioMonitor_DesiredScenarioAndClassAction)(actor); } - void SetEnabled(bool enable) + + class CMod : public IMod { - this->ToggleAllDetours(enable); - } -}; -static CMod_StickyPusher s_Mod; - - -static ConVar cvar_enable("sigsegv_stickypusher_enable", "0", FCVAR_NOTIFY, - "Mod: add new CTFBotMissionStickyPusher AI behavior", - [](IConVar *pConVar, const char *pOldValue, float flOldValue) { - ConVarRef var(pConVar); - s_Mod.SetEnabled(var.GetBool()); - }); + public: + CMod() : IMod("StickyPusher") + { + MOD_ADD_DETOUR_MEMBER(CMissionPopulator, Parse); + MOD_ADD_DETOUR_MEMBER(CSpawnLocation, Parse); + + MOD_ADD_DETOUR_MEMBER(CMissionPopulator, Update); + + MOD_ADD_DETOUR_MEMBER(CTFBotScenarioMonitor, DesiredScenarioAndClassAction); + } + + void SetEnabled(bool enable) + { + this->ToggleAllDetours(enable); + } + }; + CMod s_Mod; + + + ConVar cvar_enable("sigsegv_stickypusher_enable", "0", FCVAR_NOTIFY, + "Mod: add new CTFBotMissionStickyPusher AI behavior", + [](IConVar *pConVar, const char *pOldValue, float flOldValue) { + ConVarRef var(pConVar); + s_Mod.SetEnabled(var.GetBool()); + }); +} #endif diff --git a/mod/visualize_airblastbox.cpp b/mod/visualize_airblastbox.cpp new file mode 100644 index 00000000..02e9543c --- /dev/null +++ b/mod/visualize_airblastbox.cpp @@ -0,0 +1,76 @@ +#include "mod.h" +#include "sm/detours.h" +#include "stub/stub.h" +#include "util/util.h" + + +#if defined __GNUC__ +#warning TODO: move cvars into class +#endif + + +#ifdef EXPERIMENTAL + +namespace Mod_Visualize_AirblastBox +{ + ConVar cvar_duration("sigsegv_visualize_airblastbox_duration", "0.1", FCVAR_NOTIFY, + "Visualization: box draw duration"); + + ConVar cvar_color_r("sigsegv_visualize_airblastbox_color_r", "255", FCVAR_NOTIFY, + "Visualization: box color (red)"); + ConVar cvar_color_g("sigsegv_visualize_airblastbox_color_g", "255", FCVAR_NOTIFY, + "Visualization: box color (green)"); + ConVar cvar_color_b("sigsegv_visualize_airblastbox_color_b", "255", FCVAR_NOTIFY, + "Visualization: box color (blue)"); + ConVar cvar_color_a("sigsegv_visualize_airblastbox_color_a", "255", FCVAR_NOTIFY, + "Visualization: box color (alpha)"); + + + RefCount rc_CTFWeaponBase_DeflectProjectiles; + DETOUR_DECL_MEMBER(bool, CTFWeaponBase_DeflectProjectiles) + { + SCOPED_INCREMENT(rc_CTFWeaponBase_DeflectProjectiles); + return DETOUR_MEMBER_CALL(CTFWeaponBase_DeflectProjectiles)(); + } + + DETOUR_DECL_STATIC(int, UTIL_EntitiesInBox, const Vector& mins, const Vector& maxs, CFlaggedEntitiesEnum *pEnum) + { + if (rc_CTFWeaponBase_DeflectProjectiles.NonZero()) { + NDebugOverlay::Box(vec3_origin, mins, maxs, + cvar_color_r.GetInt(), + cvar_color_g.GetInt(), + cvar_color_b.GetInt(), + cvar_color_a.GetInt(), + cvar_duration.GetFloat()); + } + + return DETOUR_STATIC_CALL(UTIL_EntitiesInBox)(mins, maxs, pEnum); + } + + + class CMod : public IMod + { + public: + CMod() : IMod("Visualize_AirblastBox") + { + MOD_ADD_DETOUR_MEMBER(CTFWeaponBase, DeflectProjectiles); + MOD_ADD_DETOUR_GLOBAL(UTIL_EntitiesInBox); + } + + void SetEnabled(bool enable) + { + this->ToggleAllDetours(enable); + } + }; + CMod s_Mod; + + + ConVar cvar_enable("sigsegv_visualize_airblastbox_enable", "0", FCVAR_NOTIFY, + "Visualization: draw box used for airblast deflection of projectiles", + [](IConVar *pConVar, const char *pOldValue, float flOldValue) { + ConVarRef var(pConVar); + s_Mod.SetEnabled(var.GetBool()); + }); +} + +#endif diff --git a/mod/visualize_deflectcylinder.cpp b/mod/visualize_deflectcylinder.cpp new file mode 100644 index 00000000..b2f26487 --- /dev/null +++ b/mod/visualize_deflectcylinder.cpp @@ -0,0 +1,161 @@ +#include "mod.h" +#include "sm/detours.h" +#include "stub/stub.h" +#include "util/util.h" + + +#if defined __GNUC__ +#warning TODO: move cvars into class +#endif + + +#ifdef EXPERIMENTAL + +namespace Mod_Visualize_DeflectCylinder +{ + ConVar cvar_duration("sigsegv_visualize_deflectcylinder_duration", "0.1", FCVAR_NOTIFY, + "Visualization: cylinder draw duration"); + + ConVar cvar_color_r("sigsegv_visualize_deflectcylinder_color_r", "255", FCVAR_NOTIFY, + "Visualization: cylinder color (red)"); + ConVar cvar_color_g("sigsegv_visualize_deflectcylinder_color_g", "255", FCVAR_NOTIFY, + "Visualization: cylinder color (green)"); + ConVar cvar_color_b("sigsegv_visualize_deflectcylinder_color_b", "255", FCVAR_NOTIFY, + "Visualization: cylinder color (blue)"); + ConVar cvar_color_a("sigsegv_visualize_deflectcylinder_color_a", "0", FCVAR_NOTIFY, + "Visualization: cylinder color (alpha)"); + + ConVar cvar_sphere_enable("sigsegv_visualize_deflectcylinder_sphere_enable", "0", FCVAR_NOTIFY, + "Visualization: sphere enable"); + ConVar cvar_sphere_color_r("sigsegv_visualize_deflectcylinder_sphere_color_r", "255", FCVAR_NOTIFY, + "Visualization: sphere color (red)"); + ConVar cvar_sphere_color_g("sigsegv_visualize_deflectcylinder_sphere_color_g", "255", FCVAR_NOTIFY, + "Visualization: sphere color (green)"); + ConVar cvar_sphere_color_b("sigsegv_visualize_deflectcylinder_sphere_color_b", "255", FCVAR_NOTIFY, + "Visualization: sphere color (blue)"); + ConVar cvar_sphere_color_a("sigsegv_visualize_deflectcylinder_sphere_color_a", "0", FCVAR_NOTIFY, + "Visualization: sphere color (alpha)"); + + + RefCount rc_CTFMinigun_AttackProjectiles; + DETOUR_DECL_MEMBER(void, CTFMinigun_AttackEnemyProjectiles) + { + SCOPED_INCREMENT(rc_CTFMinigun_AttackProjectiles); + DETOUR_MEMBER_CALL(CTFMinigun_AttackEnemyProjectiles)(); + } + + bool is_MiniBoss; + DETOUR_DECL_MEMBER(bool, CTFPlayer_IsMiniBoss) + { + bool result = DETOUR_MEMBER_CALL(CTFPlayer_IsMiniBoss)(); + is_MiniBoss = result; + return result; + } + + DETOUR_DECL_STATIC(int, UTIL_EntitiesInSphere, const Vector& center, float radius, CFlaggedEntitiesEnum *pEnum) + { + if (rc_CTFMinigun_AttackProjectiles.NonZero() && cvar_sphere_enable.GetBool()) { + NDebugOverlay::Sphere(center, QAngle(0.0f, 0.0f, 0.0f), radius, + cvar_sphere_color_r.GetInt(), + cvar_sphere_color_g.GetInt(), + cvar_sphere_color_b.GetInt(), + cvar_sphere_color_a.GetInt(), + false, + cvar_duration.GetFloat()); + + NDebugOverlay::Sphere(center, QAngle(0.0f, 30.0f, 0.0f), radius, + cvar_sphere_color_r.GetInt(), + cvar_sphere_color_g.GetInt(), + cvar_sphere_color_b.GetInt(), + cvar_sphere_color_a.GetInt(), + false, + cvar_duration.GetFloat()); + + NDebugOverlay::Sphere(center, QAngle(0.0f, 60.0f, 0.0f), radius, + cvar_sphere_color_r.GetInt(), + cvar_sphere_color_g.GetInt(), + cvar_sphere_color_b.GetInt(), + cvar_sphere_color_a.GetInt(), + false, + cvar_duration.GetFloat()); + } + + return DETOUR_STATIC_CALL(UTIL_EntitiesInSphere)(center, radius, pEnum); + } + + DETOUR_DECL_STATIC(float, CalcDistanceToLineSegment, const Vector& P, const Vector& vLineA, const Vector& vLineB, float *t) + { + float dist = DETOUR_STATIC_CALL(CalcDistanceToLineSegment)(P, vLineA, vLineB, t); + + if (rc_CTFMinigun_AttackProjectiles.NonZero()) { + float radius = (is_MiniBoss ? 56.0f : 38.0f); + bool is_inside = (dist <= radius); + + NDebugOverlay::Cross3D(P, 10.0f, + (is_inside ? 0 : 255), + (is_inside ? 255 : 0), + 0, + true, + cvar_duration.GetFloat()); + + static int lastframe = -1; + if (gpGlobals->framecount != lastframe) { + lastframe = gpGlobals->framecount; + + /*NDebugOverlay::Line(vLineA, vLineB, + cvar_color_r.GetFloat(), + cvar_color_g.GetFloat(), + cvar_color_b.GetFloat(), + true, + cvar_duration.GetFloat());*/ + + QAngle ang; + VectorAngles(vLineA - vLineB, ang); + + for (int i = 0; i <= 10; ++i) { + float pA = ((float)i / 10.0f); + float pB = (1.0f - pA); + + NDebugOverlay::Circle((pA * vLineA) + (pB * vLineB), ang, radius, + cvar_color_r.GetFloat(), + cvar_color_g.GetFloat(), + cvar_color_b.GetFloat(), + cvar_color_a.GetFloat(), + false, + cvar_duration.GetFloat()); + } + } + } + + return dist; + } + + + class CMod : public IMod + { + public: + CMod() : IMod("Visualize_DeflectCylinder") + { + MOD_ADD_DETOUR_MEMBER(CTFMinigun, AttackEnemyProjectiles); + MOD_ADD_DETOUR_MEMBER(CTFPlayer, IsMiniBoss); + MOD_ADD_DETOUR_GLOBAL(UTIL_EntitiesInSphere); + MOD_ADD_DETOUR_GLOBAL(CalcDistanceToLineSegment); + } + + void SetEnabled(bool enable) + { + this->ToggleAllDetours(enable); + } + }; + CMod s_Mod; + + + ConVar cvar_enable("sigsegv_visualize_deflectcylinder_enable", "0", FCVAR_NOTIFY, + "Visualization: draw cylinder used for heavy's projectile deflection upgrade", + [](IConVar *pConVar, const char *pOldValue, float flOldValue) { + ConVarRef var(pConVar); + s_Mod.SetEnabled(var.GetBool()); + }); +} + +#endif diff --git a/mod/visualize_ehsphere.cpp b/mod/visualize_ehsphere.cpp new file mode 100644 index 00000000..de462759 --- /dev/null +++ b/mod/visualize_ehsphere.cpp @@ -0,0 +1,79 @@ +#include "mod.h" +#include "sm/detours.h" +#include "stub/stub.h" +#include "util/util.h" + + +#if defined __GNUC__ +#warning TODO: move cvars into class +#endif + + +#ifdef EXPERIMENTAL + +namespace Mod_Visualize_EHSphere +{ + ConVar cvar_duration("sigsegv_visualize_ehsphere_duration", "0.5", FCVAR_NOTIFY, + "Visualization: EH sphere draw duration"); + + ConVar cvar_color_r("sigsegv_visualize_ehsphere_color_r", "255", FCVAR_NOTIFY, + "Visualization: EH sphere color (red)"); + ConVar cvar_color_g("sigsegv_visualize_ehsphere_color_g", "255", FCVAR_NOTIFY, + "Visualization: EH sphere color (green)"); + ConVar cvar_color_b("sigsegv_visualize_ehsphere_color_b", "255", FCVAR_NOTIFY, + "Visualization: EH sphere color (blue)"); + ConVar cvar_color_a("sigsegv_visualize_ehsphere_color_a", "0", FCVAR_NOTIFY, + "Visualization: EH sphere color (alpha)"); + + + RefCount rc_CTFSniperRifle_ExplosiveHeadShot; + DETOUR_DECL_MEMBER(void, CTFSniperRifle_ExplosiveHeadShot, CTFPlayer *player1, CTFPlayer *player2) + { + SCOPED_INCREMENT(rc_CTFSniperRifle_ExplosiveHeadShot); + DETOUR_MEMBER_CALL(CTFSniperRifle_ExplosiveHeadShot)(player1, player2); + } + + DETOUR_DECL_STATIC(int, UTIL_EntitiesInSphere, const Vector& center, float radius, CFlaggedEntitiesEnum *pEnum) + { + if (rc_CTFSniperRifle_ExplosiveHeadShot.NonZero()) { + for (int i = 0; i < 8; ++i) { + NDebugOverlay::Sphere(center, QAngle(0.0f, (float)i * 22.5f, 0.0f), radius, + cvar_color_r.GetInt(), + cvar_color_g.GetInt(), + cvar_color_b.GetInt(), + cvar_color_a.GetInt(), + false, + cvar_duration.GetFloat()); + } + } + + return DETOUR_STATIC_CALL(UTIL_EntitiesInSphere)(center, radius, pEnum); + } + + + class CMod : public IMod + { + public: + CMod() : IMod("Visualize_EHSphere") + { + MOD_ADD_DETOUR_MEMBER(CTFSniperRifle, ExplosiveHeadShot); + MOD_ADD_DETOUR_GLOBAL(UTIL_EntitiesInSphere); + } + + void SetEnabled(bool enable) + { + this->ToggleAllDetours(enable); + } + }; + CMod s_Mod; + + + ConVar cvar_enable("sigsegv_visualize_ehsphere_enable", "0", FCVAR_NOTIFY, + "Visualization: draw sphere used for sniper's explosive headshot upgrade", + [](IConVar *pConVar, const char *pOldValue, float flOldValue) { + ConVarRef var(pConVar); + s_Mod.SetEnabled(var.GetBool()); + }); +} + +#endif diff --git a/prop.h b/prop.h index 84cab089..454df0ba 100644 --- a/prop.h +++ b/prop.h @@ -192,76 +192,97 @@ class CProp_Extract : public IPropTyped // virtual: NOPE // members: NOPE template -class CPropAccessor_Read +class CPropAccessor_Base { public: - CPropAccessor_Read() = delete; -// ~CPropAccessor_Read() = delete; - - operator const T&() const { return this->Get(); } - - /* this is questionable and probably ought to be removed */ - operator T*() const { return this->GetPtr(); } - - const T& operator=(const T& val) = delete; + CPropAccessor_Base() = delete; -protected: T *GetPtr() const { int off = -1; assert(PROP->GetOffset(off)); - return reinterpret_cast((uintptr_t)this + off); } +}; + + +template +class CPropAccessor_Read : public CPropAccessor_Base +{ +public: + CPropAccessor_Read() = delete; - const T& Get() const - { - return *this->GetPtr(); - } + operator const T&() const { return *this->GetPtr(); } + const T& operator=(const T& val) = delete; +}; +/* specialization for CHandle */ +template +class CPropAccessor_Read, IPROP, PROP> : public CPropAccessor_Base, IPROP, PROP> +{ +public: + CPropAccessor_Read() = delete; + + operator U*() const { return *this->GetPtr(); } + U* operator=(U* val) = delete; }; + template class CPropAccessor_Write : public CPropAccessor_Read { public: CPropAccessor_Write() = delete; -// ~CPropAccessor_Write() = delete; - const T& operator=(const T& val) { this->Set(val); return val; } + const T& operator=(const T& val) + { + if (NET) { + /* TODO: update network state */ + assert(false); + } + *this->GetPtr() = val; + return val; + } +}; +/* specialization for CHandle */ +template +class CPropAccessor_Write, IPROP, PROP, NET> : public CPropAccessor_Read, IPROP, PROP> +{ +public: + CPropAccessor_Write() = delete; -protected: - void Set(const T& val) const + U* operator=(U* val) { if (NET) { /* TODO: update network state */ assert(false); } - *this->GetPtr() = val; + return val; } }; #define DEF_SENDPROP(T, P) \ - typedef CProp_SendProp _tp_##P; \ - static _tp_##P s_prop_##P; \ - typedef CPropAccessor_Write _ta_##P; \ - _ta_##P P; \ - static_assert(std::is_empty<_ta_##P>::value, "Prop accessor isn't an empty type") + typedef CProp_SendProp _type_prop_##P; \ + static _type_prop_##P s_prop_##P; \ + /*typedef CPropAccessor_Write _type_accessor_##P;*/ \ + typedef CPropAccessor_Read _type_accessor_##P; \ + _type_accessor_##P P; \ + static_assert(std::is_empty<_type_accessor_##P>::value, "Prop accessor isn't an empty type") #define DEF_DATAMAP(T, P) \ - typedef CProp_DataMap _tp_##P; \ - static _tp_##P s_prop_##P; \ - typedef CPropAccessor_Write _ta_##P; \ - _ta_##P P; \ - static_assert(std::is_empty<_ta_##P>::value, "Prop accessor isn't an empty type") + typedef CProp_DataMap _type_prop_##P; \ + static _type_prop_##P s_prop_##P; \ + typedef CPropAccessor_Write _type_accessor_##P; \ + _type_accessor_##P P; \ + static_assert(std::is_empty<_type_accessor_##P>::value, "Prop accessor isn't an empty type") #define DEF_EXTRACT(T, P) \ - typedef CProp_Extract _tp_##P; \ - static _tp_##P s_prop_##P; \ - typedef CPropAccessor_Write _ta_##P; \ - _ta_##P P; \ - static_assert(std::is_empty<_ta_##P>::value, "Prop accessor isn't an empty type") + typedef CProp_Extract _type_prop_##P; \ + static _type_prop_##P s_prop_##P; \ + typedef CPropAccessor_Write _type_accessor_##P; \ + _type_accessor_##P P; \ + static_assert(std::is_empty<_type_accessor_##P>::value, "Prop accessor isn't an empty type") #define IMPL_SENDPROP(T, C, P, SC) \ diff --git a/sm/MemoryUtils.cpp b/sm/MemoryUtils.cpp index 84123884..41a94709 100644 --- a/sm/MemoryUtils.cpp +++ b/sm/MemoryUtils.cpp @@ -568,3 +568,101 @@ bool MemoryUtils::GetLibraryInfo(const void *libPtr, DynLibInfo &lib) return true; } + +void MemoryUtils::ForEachSymbol(void *handle, void (*functor)(Symbol *)) +{ + /* do a bogus symbol lookup to force everything into the symbol table cache */ + assert(ResolveSymbol(handle, "________________") == nullptr); + +#ifdef PLATFORM_LINUX + struct link_map *dlmap; + LibSymbolTable *libtable; + SymbolTable *table; + + dlmap = (struct link_map *)handle; + table = NULL; + + /* See if we already have a symbol table for this library */ + for (size_t i = 0; i < m_SymTables.size(); i++) + { + libtable = m_SymTables[i]; + if (libtable->lib_base == dlmap->l_addr) + { + table = &libtable->table; + break; + } + } + + /* If we don't have a symbol table for this library, then create one */ + if (table == NULL) + { + libtable = new LibSymbolTable(); + libtable->table.Initialize(); + libtable->lib_base = dlmap->l_addr; + libtable->last_pos = 0; + table = &libtable->table; + m_SymTables.push_back(libtable); + } + + table->ForEachSymbol(functor); +#elif defined PLATFORM_APPLE + uintptr_t dlbase; + uint32_t image_count; + LibSymbolTable *libtable; + SymbolTable *table; + Symbol *symbol_entry; + + dlbase = 0; + image_count = m_ImageList->infoArrayCount; + table = NULL; + + /* Loop through mach-o images in process. + * We can skip index 0 since that is just the executable. + */ + for (uint32_t i = 1; i < image_count; i++) + { + const struct dyld_image_info &info = m_ImageList->infoArray[i]; + + /* "Load" each one until we get a matching handle */ + void *h = dlopen(info.imageFilePath, RTLD_NOLOAD); + if (h == handle) + { + dlbase = (uintptr_t)info.imageLoadAddress; + dlclose(h); + break; + } + + dlclose(h); + } + + if (!dlbase) + { + /* Uh oh, we couldn't find a matching handle */ + return; + } + + /* See if we already have a symbol table for this library */ + for (size_t i = 0; i < m_SymTables.size(); i++) + { + libtable = m_SymTables[i]; + if (libtable->lib_base == dlbase) + { + table = &libtable->table; + break; + } + } + + /* If we don't have a symbol table for this library, then create one */ + if (table == NULL) + { + libtable = new LibSymbolTable(); + libtable->table.Initialize(); + libtable->lib_base = dlbase; + libtable->last_pos = 0; + table = &libtable->table; + m_SymTables.push_back(libtable); + } + + table->ForEachSymbol(functor); +#endif +} diff --git a/sm/MemoryUtils.h b/sm/MemoryUtils.h index 2e7b6f66..a5d91e34 100644 --- a/sm/MemoryUtils.h +++ b/sm/MemoryUtils.h @@ -32,12 +32,12 @@ #include #include -#if defined PLATFORM_LINUX || defined PLATFORM_APPLE +//#if defined PLATFORM_LINUX || defined PLATFORM_APPLE #include #include "sm_symtable.h" using namespace SourceHook; -#endif +//#endif #ifdef PLATFORM_APPLE #include @@ -65,6 +65,7 @@ class MemoryUtils ~MemoryUtils(); void *ResolveSymbol(void *handle, const char *symbol); bool GetLibraryInfo(const void *libPtr, DynLibInfo &lib); + void ForEachSymbol(void *handle, void (*functor)(Symbol *)); #if defined PLATFORM_LINUX || defined PLATFORM_APPLE private: CVector m_SymTables; diff --git a/sm/detours.h b/sm/detours.h index a61982d6..000c91d2 100644 --- a/sm/detours.h +++ b/sm/detours.h @@ -48,7 +48,7 @@ #define DETOUR_STATIC_CALL(name) (Actual_##name) #define DETOUR_DECL_STATIC(ret, name, ...) \ - static CDetour *detour_##name = nullptr; \ + CDetour *detour_##name = nullptr; \ static ret (*Actual_##name)(__VA_ARGS__) = nullptr; \ static ret Detour_##name(__VA_ARGS__) diff --git a/sm/sm_symtable.h b/sm/sm_symtable.h index 1e2e9c40..99f74a6d 100644 --- a/sm/sm_symtable.h +++ b/sm/sm_symtable.h @@ -221,6 +221,19 @@ class SymbolTable return kvs; } + + void ForEachSymbol(void (*functor)(Symbol *)) + { + for (uint32_t i = 0; i < nbuckets; ++i) { + Symbol *sym = buckets[i]; + + while (sym != nullptr) { + functor(sym); + sym = sym->tbl_next; + } + } + } + private: uint32_t nbuckets; uint32_t nused; diff --git a/stub/baseentity.cpp b/stub/baseentity.cpp index d0615c45..1fedf19d 100644 --- a/stub/baseentity.cpp +++ b/stub/baseentity.cpp @@ -4,9 +4,12 @@ IMPL_DATAMAP(int, CBaseEntity, m_iEFlags); IMPL_DATAMAP(Vector, CBaseEntity, m_vecAbsOrigin); -IMPL_SENDPROP(int, CBaseEntity, m_iHealth, CBasePlayer); -IMPL_SENDPROP(char, CBaseEntity, m_lifeState, CBasePlayer); -IMPL_SENDPROP(int, CBaseEntity, m_iTeamNum, CBaseEntity); +IMPL_SENDPROP(int, CBaseEntity, m_iTeamNum, CBaseEntity); +IMPL_SENDPROP(int, CBaseEntity, m_iHealth, CBasePlayer); +IMPL_SENDPROP(char, CBaseEntity, m_lifeState, CBasePlayer); +IMPL_SENDPROP(CHandle, CBaseEntity, m_hGroundEntity, CBasePlayer); FuncThunk CBaseEntity::ft_GetNetworkable( "CBaseEntity::GetNetworkable"); FuncThunk CBaseEntity::ft_CalcAbsolutePosition("CBaseEntity::CalcAbsolutePosition"); + +VFuncThunk CBaseEntity::vt_IsPlayer("[VT] CBaseEntity", "CBaseEntity::IsPlayer"); diff --git a/stub/baseentity.h b/stub/baseentity.h index 9e7567c1..fab6e8d3 100644 --- a/stub/baseentity.h +++ b/stub/baseentity.h @@ -15,23 +15,29 @@ class CBaseEntity bool IsEFlagSet(int nEFlagMask) const; /* getter/setter */ - int GetTeamNumber() const { return this->m_iTeamNum; } - bool IsAlive() { return (this->m_lifeState == LIFE_ALIVE); } + int GetTeamNumber() const { return this->m_iTeamNum; } + int GetHealth() const { return this->m_iHealth; } + bool IsAlive() const { return (this->m_lifeState == LIFE_ALIVE); } + CBaseEntity *GetGroundEntity() const { return this->m_hGroundEntity; } /* thunk */ - IServerNetworkable *GetNetworkable() { return (*ft_GetNetworkable)(this); } - void CalcAbsolutePosition() { (*ft_CalcAbsolutePosition)(this); } + IServerNetworkable *GetNetworkable() { return (*ft_GetNetworkable)(this); } + void CalcAbsolutePosition() { (*ft_CalcAbsolutePosition)(this); } + bool IsPlayer() const { return (vt_IsPlayer.Get(this))(this); } private: DEF_DATAMAP(int, m_iEFlags); DEF_DATAMAP(Vector, m_vecAbsOrigin); - DEF_SENDPROP(int, m_iHealth); - DEF_SENDPROP(char, m_lifeState); - DEF_SENDPROP(int, m_iTeamNum); + DEF_SENDPROP(int, m_iTeamNum); + DEF_SENDPROP(int, m_iHealth); + DEF_SENDPROP(char, m_lifeState); + DEF_SENDPROP(CHandle, m_hGroundEntity); static FuncThunk ft_GetNetworkable; static FuncThunk ft_CalcAbsolutePosition; + + static VFuncThunk vt_IsPlayer; }; inline CBaseEntity *GetContainingEntity(edict_t *pent) diff --git a/stub/tfbot.cpp b/stub/tfbot.cpp index a7e74cb8..dae4a680 100644 --- a/stub/tfbot.cpp +++ b/stub/tfbot.cpp @@ -75,3 +75,22 @@ FuncThunk CTFBot::ft_GetLocomotionInterface(" FuncThunk CTFBot::ft_GetBodyInterface ("CTFBot::GetBodyInterface"); FuncThunk CTFBot::ft_GetVisionInterface ("CTFBot::GetVisionInterface"); FuncThunk CTFBot::ft_GetIntentionInterface ("CTFBot::GetIntentionInterface"); + + +#if defined __GNUC__ +#warning remove me +#endif +#if 0 +#include "re/nextbot.h" +#include "util/dynamic_cast.h" +void TEST_REMOVE_ME(CBaseEntity *ent) +{ + printf("%s", typeid(CTFPlayer).name()); + printf("%s", typeid(Action).name()); + + + //#warning remove me! +// CTFPlayer *player/* = rtti_cast(ent)*/; +// CTFBot *bot = rtti_cast(player); +} +#endif diff --git a/stub/tfbot.h b/stub/tfbot.h index 4a287bf9..f4dedf4d 100644 --- a/stub/tfbot.h +++ b/stub/tfbot.h @@ -18,6 +18,8 @@ class NextBotPlayer : public T {}; class CTFBot : public NextBotPlayer { public: + static const char *GetRTTIAddrName() { return "[RTTI] CTFBot"; } + int GetMission() const { return this->m_nMission; } /* thunk */ @@ -63,4 +65,8 @@ class CTFBot : public NextBotPlayer }; +// TODO: ToTFBot +// (WARNING: DON'T ASSUME THAT DYNAMIC CASTS ARE SAFE!) + + #endif diff --git a/stub/tfplayer.h b/stub/tfplayer.h index fd31b3cf..239f273d 100644 --- a/stub/tfplayer.h +++ b/stub/tfplayer.h @@ -67,8 +67,10 @@ class CTFPlayerShared class CTFPlayer : public CBaseMultiplayerPlayer { public: - CTFPlayerClass *GetPlayerClass() { return m_PlayerClass; } - const CTFPlayerClass *GetPlayerClass() const { return m_PlayerClass; } + static const char *GetRTTIAddrName() { return "[RTTI] CTFPlayer"; } + + CTFPlayerClass *GetPlayerClass() { return m_PlayerClass.GetPtr(); } + const CTFPlayerClass *GetPlayerClass() const { return m_PlayerClass.GetPtr(); } bool IsPlayerClass(int iClass) const; @@ -83,4 +85,9 @@ class CTFPlayer : public CBaseMultiplayerPlayer }; +// TODO: ToBasePlayer +// TODO: ToTFPlayer +// (WARNING: DON'T ASSUME THAT DYNAMIC CASTS ARE SAFE!) + + #endif diff --git a/util/rtti.cpp b/util/rtti.cpp new file mode 100644 index 00000000..9ae7da7d --- /dev/null +++ b/util/rtti.cpp @@ -0,0 +1,279 @@ +#include "util/rtti.h" +#include "library.h" +#include "mem/scan.h" + + +#if defined __GNUC__ +#warning TODO: find/replace all cases of "[VT]" and "[RTTI]" +#warning TODO: remove all cases of GetRTTIAddrName +#endif + + +#if defined _MSC_VER +class Prof +{ +public: + static void Begin() + { + s_bFail = false; + + if (!QueryPerformanceFrequency(&s_Freq)) s_bFail = true; + if (!QueryPerformanceCounter(&s_Count1)) s_bFail = true; + } + static void End(const char *msg) + { + if (!QueryPerformanceCounter(&s_Count2)) s_bFail = true; + + if (s_bFail) { + DevMsg("PROF %s: failed\n", msg); + } else { + int64_t freq = s_Freq.QuadPart; + int64_t delta = s_Count2.QuadPart - s_Count1.QuadPart; + + double sec = (double)delta / (double)freq; + double msec = sec * 1000.0; + + DevMsg("PROF %s: %.3f ms\n", msg, msec); + } + } + +private: + static bool s_bFail; + static LARGE_INTEGER s_Freq; + static LARGE_INTEGER s_Count1; + static LARGE_INTEGER s_Count2; +}; +bool Prof::s_bFail; +LARGE_INTEGER Prof::s_Freq; +LARGE_INTEGER Prof::s_Count1; +LARGE_INTEGER Prof::s_Count2; +#endif + + +namespace RTTI +{ + // Windows: str typeinfo(T).raw_name() => _TypeDescriptor* + // Linux: str typeinfo(T).name() => abi::__class_type_info* + static std::map s_RTTI; + + // Windows: str typeinfo(T).raw_name() => void ** + // Linux: str typeinfo(T).name() => void ** + static std::map s_VT; + + + void PreLoad() + { + DevMsg("RTTI::PreLoad BEGIN\n"); + +#if defined __GNUC__ + + LibMgr::ForEachSym(Library::SERVER, + [](Symbol *sym) + { + const char *buf = sym->buffer(); + char name[4096]; + + if (sym->length >= 4 && memcmp(buf, "_ZT", 3) == 0) { + bool is_rtti = (buf[3] == 'I'); + bool is_vt = (buf[3] == 'V'); + + if (is_rtti || is_vt) { + size_t len = sym->length - 4; + len = Min(len, sizeof(name) - 1); + + memcpy(name, buf + 4, len); + name[sym->length] = '\0'; + + if (is_rtti) { + std::string key(name); + auto addr = (const rtti_t *)(sym->address); + + if (s_RTTI.find(key) != s_RTTI.end()) { + DevWarning("RTTI::PreLoad: duplicate symbol \"_ZTI%s\"\n", name); + } else { + s_RTTI[key] = addr; + // DevMsg("RTTI: %08x \"%s\"\n", (uintptr_t)addr, name); + } + } else if (is_vt) { + std::string key(name); + auto addr = (const void **)((uintptr_t)(sym->address) + offsetof(vtable, vfptrs)); + + if (s_VT.find(key) != s_VT.end()) { + DevWarning("RTTI::PreLoad: duplicate symbol \"_ZTV%s\"\n", name); + } else { + s_VT[key] = addr; + // DevMsg("VT: %08x \"%s\"\n", (uintptr_t)addr, name); + } + } + } + } + } + ); + +#elif defined _MSC_VER + + int n_total = 0; + int n_skip = 0; + int n_add = 0; + + std::vector exclude; + exclude.emplace_back(R"(^.*@CryptoPP@@$)"); + exclude.emplace_back(R"(^.*@protobuf@google@@$)"); + exclude.emplace_back(R"(^.*@GCSDK@@$)"); + exclude.emplace_back(R"(^\.\?AVCGC.*$)"); + exclude.emplace_back(R"(^\.\?AV\?\$CGC.*$)"); + exclude.emplace_back(R"(^\.\?AVCMsg.*$)"); + exclude.emplace_back(R"(^\.\?AVCSO.*$)"); + exclude.emplace_back(R"(^\.\?AV\?\$CUtl\w+DataOps.*$)"); + exclude.emplace_back(R"(^\.\?AV\?\$CEntityFactory@.*$)"); + exclude.emplace_back(R"(^\.\?AVNetworkVar_.*$)"); + + Prof::Begin(); + CSingleScan scan1(CLibSegBounds(Library::SERVER, ".data"), new CStringPrefixScanner(ScanResults::ALL, ".?AV")); + Prof::End("TD scan"); + Prof::Begin(); + for (auto match : scan1.Matches()) { + auto name = (const char *)match; + auto addr = (const rtti_t *)((uintptr_t)match - offsetof(_TypeDescriptor, name)); + + ++n_total; + bool skip = false; + for (auto& filter : exclude) { + if (std::regex_match(name, filter, std::regex_constants::match_any)) { + skip = true; + break; + } + } + if (skip) { + ++n_skip; + continue; + } + ++n_add; + + std::string key(name); + if (s_RTTI.find(key) != s_RTTI.end()) { + // DevWarning("RTTI::PreLoad: duplicate RTTI str \"%s\"\n", name); + } else { + s_RTTI[key] = addr; + // DevMsg("\"%s\" TD @ %08x\n", name, (uintptr_t)s_RTTI[key]); + } + } + DevMsg("RTTI::PreLoad:\n" + "total %d\n" + "skip %d\n" + "add %d\n", + n_total, n_skip, n_add); + Prof::End("TD post"); + + + Prof::Begin(); + std::map scannermap_COL; + std::vector scanners_COL; + for (const auto& pair : s_RTTI) { + auto name = pair.first; + auto p_TD = pair.second; + + __RTTI_CompleteObjectLocator seek_COL = { + 0x00000000, + 0x00000000, + 0x00000000, + const_cast<_TypeDescriptor *>(p_TD), + }; + + IScanner *scanner = new CBasicScanner(ScanResults::FIRST, (const void *)&seek_COL, 0x10); + + scanners_COL.push_back(scanner); + scannermap_COL[scanner] = name; + } + Prof::End("COL pre"); + Prof::Begin(); + //CMultiScan scan_COL(CLibSegBounds(Library::SERVER, ".rdata"), scanners_COL); + CThreadedScan scan_COL(CLibSegBounds(Library::SERVER, ".rdata"), scanners_COL); + Prof::End("COL scan"); + + Prof::Begin(); + std::map results_COL; + for (auto scanner : scanners_COL) { + auto& name = scannermap_COL[scanner]; + + if (scanner->Matches().size() != 1) { + // DevMsg("RTTI::PreLoad: %u TD refs for \"%s\"\n", scanner->Matches().size(), name.c_str()); + continue; + } + + results_COL[name] = (__RTTI_CompleteObjectLocator *)scanner->Matches()[0]; + // DevMsg("\"%s\" COL @ %08x\n", name.c_str(), (uintptr_t)results_COL[name]); + } + Prof::End("COL post"); + + + Prof::Begin(); + std::map scannermap_VT; + std::vector scanners_VT; + for (const auto& pair : results_COL) { + auto name = pair.first; + auto p_COL = pair.second; + + IScanner *scanner = new CBasicScanner(ScanResults::FIRST, (const void *)&p_COL, 0x4); + + scanners_VT.push_back(scanner); + scannermap_VT[scanner] = name; + } + Prof::End("VT pre"); + Prof::Begin(); + //CMultiScan scan_VT(CLibSegBounds(Library::SERVER, ".rdata"), scanners_VT); + CThreadedScan scan_VT(CLibSegBounds(Library::SERVER, ".rdata"), scanners_VT); + Prof::End("VT scan"); + + Prof::Begin(); + for (auto scanner : scanners_VT) { + auto& name = scannermap_VT[scanner]; + + if (scanner->Matches().size() != 1) { + // DevMsg("RTTI::PreLoad: %u COL refs for \"%s\"\n", scanner->Matches().size(), name.c_str()); + continue; + } + + s_VT[name] = (const void **)((uintptr_t)scanner->Matches()[0] + 0x4); + // DevMsg("\"%s\" VT @ %08x\n", name.c_str(), (uintptr_t)s_VT[name]); + } + Prof::End("VT post"); + +#endif + + + DevMsg("RTTI::PreLoad: found %u RTTI\n", s_RTTI.size()); + DevMsg("RTTI::PreLoad: found %u VT\n", s_VT.size()); + + // RTTI + // Linux: find all symbols of format "_ZTI" + // Windows: find typename strings, find refs, subtract 8 bytes + + // VTable + // Linux: find all symbols of format "_ZTV", add 8 bytes + // Windows: do some fancy backtracking from RTTI info + } + + + const rtti_t *GetRTTI(const char *name) + { + auto it = s_RTTI.find(std::string(name)); + if (it == s_RTTI.end()) { + DevMsg("RTTI::GetRTTI FAIL: no RTTI addr for name \"%s\"\n", name); + return nullptr; + } + + return (*it).second; + } + + const void **GetVTable(const char *name) + { + auto it = s_VT.find(std::string(name)); + if (it == s_VT.end()) { + DevMsg("RTTI::GetVTable FAIL: no VT addr for name \"%s\"\n", name); + return nullptr; + } + + return (*it).second; + } +} diff --git a/util/rtti.h b/util/rtti.h new file mode 100644 index 00000000..1dac77d0 --- /dev/null +++ b/util/rtti.h @@ -0,0 +1,55 @@ +#ifndef _INCLUDE_SIGSEGV_UTIL_RTTI_H_ +#define _INCLUDE_SIGSEGV_UTIL_RTTI_H_ + + +#include "abi.h" + + +#if defined __GNUC__ + typedef abi::__class_type_info rtti_t; +#elif defined _MSC_VER + typedef _TypeDescriptor rtti_t; +#endif + + +namespace RTTI +{ + void PreLoad(); + + const rtti_t *GetRTTI(const char *name); + const void **GetVTable(const char *name); + +#if defined __GNUC__ + template inline const rtti_t *GetRTTI() { return RTTI::GetRTTI(typeid(T).name()); } + template inline const void **GetVTable() { return RTTI::GetVTable(typeid(T).name()); } +#elif defined _MSC_VER + template inline const rtti_t *GetRTTI() { return RTTI::GetRTTI(typeid(T).raw_name()); } + template inline const void **GetVTable() { return RTTI::GetVTable(typeid(T).raw_name()); } +#endif +} + + +template +inline TO rtti_cast(const FROM ptr) +{ + if (ptr == nullptr) { + return nullptr; + } + + auto rtti_from = RTTI::GetRTTI::type>(); + auto rtti_to = RTTI::GetRTTI::type>(); + + assert(rtti_from != nullptr); + assert(rtti_to != nullptr); + +#if defined __GNUC__ + void *result = abi::__dynamic_cast(ptr, rtti_from, rtti_to, -1); +#elif defined _MSC_VER + void *result = __RTDynamicCast(ptr, 0, (void *)rtti_from, (void *)rtti_to, false); +#endif + + return reinterpret_cast(result); +} + + +#endif