diff --git a/extensions/tf2/conditions.cpp b/extensions/tf2/conditions.cpp index 32b69ed0..141975fb 100644 --- a/extensions/tf2/conditions.cpp +++ b/extensions/tf2/conditions.cpp @@ -33,125 +33,93 @@ #include "conditions.h" #include "util.h" -typedef int condflags_t; -#define CONDITION_TAUNTING 7 +#include + +typedef uint64_t condflags_t; + +condflags_t g_PlayerConds[MAXPLAYERS+1]; IForward *g_addCondForward = NULL; IForward *g_removeCondForward = NULL; -CDetour *detConditionAdd = NULL; -CDetour *detConditionRemove = NULL; -CDetour *detConditionRemoveAll = NULL; - int playerCondOffset = -1; +int playerCondExOffset = -1; int conditionBitsOffset = -1; bool g_bIgnoreRemove; inline condflags_t GetPlayerConds(CBaseEntity *pPlayer) { - condflags_t cond1 = *(condflags_t *)((intptr_t)pPlayer + playerCondOffset); - condflags_t cond2 = *(condflags_t *)((intptr_t)pPlayer + conditionBitsOffset); - return cond1|cond2; + uint32_t playerCond = *(uint32_t *)((intptr_t)pPlayer + playerCondOffset); + uint32_t condBits = *(uint32_t *)((intptr_t)pPlayer + conditionBitsOffset); + uint32_t playerCondEx = *(uint32_t *)((intptr_t)pPlayer + playerCondExOffset); + + uint64_t playerCondExAdj = playerCondEx; + playerCondExAdj <<= 32; + + return playerCond|condBits|playerCondExAdj; } -DETOUR_DECL_MEMBER2(AddCond, void, int, condition, float, duration) +void Conditions_OnGameFrame(bool simulating) { - CBaseEntity *pPlayer = (CBaseEntity *)((intptr_t)this - playerSharedOffset->actual_offset); - condflags_t oldconds = GetPlayerConds(pPlayer); - - DETOUR_MEMBER_CALL(AddCond)(condition, duration); - - int bit = 1 << condition; - - // The player already had this condition so bug out - if ((oldconds & bit) == bit) + if (!simulating) return; - // The condition wasn't actually added (this can happen!) - if ((GetPlayerConds(pPlayer) & bit) != bit) - return; + condflags_t oldconds; + condflags_t newconds; - int client = gamehelpers->EntityToBCompatRef(pPlayer); + condflags_t addedconds; + condflags_t removedconds; - g_addCondForward->PushCell(client); - g_addCondForward->PushCell(condition); - g_addCondForward->PushFloat(duration); - g_addCondForward->Execute(NULL, NULL); -} - -DETOUR_DECL_MEMBER2(RemoveCond, void, int, condition, bool, bForceRemove) -{ - if (g_bIgnoreRemove) + int maxClients = gpGlobals->maxClients; + for (int i = 1; i <= maxClients; i++) { - DETOUR_MEMBER_CALL(RemoveCond)(condition, bForceRemove); - return; - } - - CBaseEntity *pPlayer = (CBaseEntity *)((intptr_t)this - playerSharedOffset->actual_offset); - - condflags_t oldconds = GetPlayerConds(pPlayer); - int bit = 1 << condition; - - // If we didn't have it, it won't be removed - if ((oldconds & bit) != bit) - { - DETOUR_MEMBER_CALL(RemoveCond)(condition, bForceRemove); - return; - } - - DETOUR_MEMBER_CALL(RemoveCond)(condition, bForceRemove); - - // Calling RemoveCond doesn't necessarily remove the cond... - if ((GetPlayerConds(pPlayer) & bit) == bit) - { - return; - } - - int client = gamehelpers->EntityToBCompatRef(pPlayer); - - DoRemoveCond(client, condition); -} - -DETOUR_DECL_MEMBER1(RemoveAllCond, void, CBaseEntity *, unknown) -{ - // We're going to ignore Remove here since it can be called multiple times for the same cond - g_bIgnoreRemove = true; - - CBaseEntity *pPlayer = (CBaseEntity *)((intptr_t)this - playerSharedOffset->actual_offset); - - int client = gamehelpers->EntityToBCompatRef(pPlayer); - condflags_t conds = GetPlayerConds(pPlayer); - - // I don't know what the unknown is, but it's always NULL - DETOUR_MEMBER_CALL(RemoveAllCond)(unknown); - - int max = sizeof(condflags_t)*8; - for (int i = 0; i < max; i++) - { - // Taunt flag removal doesn't go through RemoveCond nor OnCondRemoved (or was mangled by compiler?) - // Better to "documentedly" give no removes rather than only in some cases - if (i == CONDITION_TAUNTING) + IGamePlayer *pPlayer = playerhelpers->GetGamePlayer(i); + if (!pPlayer || !pPlayer->IsInGame()) continue; - int bit = (1<GetPlayerInfo(); + if (info->IsHLTV() || info->IsReplay()) + continue; + + CBaseEntity *pEntity = gamehelpers->ReferenceToEntity(i); + oldconds = g_PlayerConds[i]; + newconds = GetPlayerConds(pEntity); + + if (oldconds == newconds) + continue; + + addedconds = newconds &~ oldconds; + removedconds = oldconds &~ newconds; + + int j; + condflags_t bit; + + for (j = 0; (bit = ((condflags_t)1 << j)) <= addedconds; j++) { - DoRemoveCond(client, i); + if ((addedconds & bit) == bit) + { + g_addCondForward->PushCell(i); + g_addCondForward->PushCell(j); + g_addCondForward->Execute(NULL, NULL); + } } + + for (j = 0; (bit = ((condflags_t)1 << j)) <= removedconds; j++) + { + if ((removedconds & bit) == bit) + { + g_removeCondForward->PushCell(i); + g_removeCondForward->PushCell(j); + g_removeCondForward->Execute(NULL, NULL); + } + } + + g_PlayerConds[i] = newconds; } - - g_bIgnoreRemove = false; } -void DoRemoveCond(int client, int condition) -{ - g_removeCondForward->PushCell(client); - g_removeCondForward->PushCell(condition); - g_removeCondForward->Execute(NULL, NULL); -} - -bool InitialiseConditionDetours() +bool InitialiseConditionChecks() { sm_sendprop_info_t prop; if (!gamehelpers->FindSendPropInfo("CTFPlayer", "m_nPlayerCond", &prop)) @@ -170,41 +138,39 @@ bool InitialiseConditionDetours() conditionBitsOffset = prop.actual_offset; - if (playerCondOffset == -1 || conditionBitsOffset == -1) - return false; - - detConditionAdd = DETOUR_CREATE_MEMBER(AddCond, "AddCondition"); - if (detConditionAdd == NULL) + if (!gamehelpers->FindSendPropInfo("CTFPlayer", "m_nPlayerCondEx", &prop)) { - g_pSM->LogError(myself, "CTFPlayerShared::AddCond detour failed"); + g_pSM->LogError(myself, "Failed to find m_nPlayerCondEx prop offset"); return false; } + + playerCondExOffset = prop.actual_offset; - detConditionRemove = DETOUR_CREATE_MEMBER(RemoveCond, "RemoveCondition"); - if (detConditionRemove == NULL) - { - g_pSM->LogError(myself, "CTFPlayerShared::RemoveCond detour failed"); + if (playerCondOffset == -1 || playerCondExOffset == -1 || conditionBitsOffset == -1) return false; + + int maxClients = gpGlobals->maxClients; + for (int i = 1; i <= maxClients; i++) + { + IGamePlayer *pPlayer = playerhelpers->GetGamePlayer(i); + if (!pPlayer || !pPlayer->IsInGame()) + continue; + + g_PlayerConds[i] = GetPlayerConds(gamehelpers->ReferenceToEntity(i)); } - detConditionRemoveAll = DETOUR_CREATE_MEMBER(RemoveAllCond, "RemoveAllConditions"); - if (detConditionRemoveAll == NULL) - { - g_pSM->LogError(myself, "CTFPlayerShared::RemoveAllCond detour failed"); - return false; - } - - detConditionAdd->EnableDetour(); - detConditionRemove->EnableDetour(); - detConditionRemoveAll->EnableDetour(); + g_pSM->AddGameFrameHook(Conditions_OnGameFrame); return true; } -void RemoveConditionDetours() +void Conditions_OnClientPutInServer(int client) { - detConditionAdd->Destroy(); - detConditionRemove->Destroy(); - detConditionRemoveAll->Destroy(); + g_PlayerConds[client] = 0; +} + +void RemoveConditionChecks() +{ + g_pSM->RemoveGameFrameHook(Conditions_OnGameFrame); } diff --git a/extensions/tf2/conditions.h b/extensions/tf2/conditions.h index 9fe42b89..f8bf08b2 100644 --- a/extensions/tf2/conditions.h +++ b/extensions/tf2/conditions.h @@ -37,10 +37,12 @@ #include #include "CDetour/detours.h" -bool InitialiseConditionDetours(); -void RemoveConditionDetours(); +bool InitialiseConditionChecks(); +void RemoveConditionChecks(); void DoRemoveCond(int client, int condition); +void Conditions_OnClientPutInServer(int client); + extern IForward *g_addCondForward; extern IForward *g_removeCondForward; diff --git a/extensions/tf2/extension.cpp b/extensions/tf2/extension.cpp index d8472ae1..e08092a1 100644 --- a/extensions/tf2/extension.cpp +++ b/extensions/tf2/extension.cpp @@ -109,10 +109,11 @@ bool TF2Tools::SDK_OnLoad(char *error, size_t maxlength, bool late) plsys->AddPluginsListener(this); playerhelpers->RegisterCommandTargetProcessor(this); + playerhelpers->AddClientListener(this); g_critForward = forwards->CreateForward("TF2_CalcIsAttackCritical", ET_Hook, 4, NULL, Param_Cell, Param_Cell, Param_String, Param_CellByRef); g_getHolidayForward = forwards->CreateForward("TF2_OnGetHoliday", ET_Event, 1, NULL, Param_CellByRef); - g_addCondForward = forwards->CreateForward("TF2_OnConditionAdded", ET_Ignore, 3, NULL, Param_Cell, Param_Cell, Param_Float); + g_addCondForward = forwards->CreateForward("TF2_OnConditionAdded", ET_Ignore, 2, NULL, Param_Cell, Param_Cell); g_removeCondForward = forwards->CreateForward("TF2_OnConditionRemoved", ET_Ignore, 2, NULL, Param_Cell, Param_Cell); g_waitingPlayersStartForward = forwards->CreateForward("TF2_OnWaitingForPlayersStart", ET_Ignore, 0, NULL); g_waitingPlayersEndForward = forwards->CreateForward("TF2_OnWaitingForPlayersEnd", ET_Ignore, 0, NULL); @@ -162,6 +163,7 @@ void TF2Tools::SDK_OnUnload() g_RegNatives.UnregisterAll(); gameconfs->CloseGameConfigFile(g_pGameConf); playerhelpers->UnregisterCommandTargetProcessor(this); + playerhelpers->RemoveClientListener(this); plsys->RemovePluginsListener(this); @@ -307,7 +309,7 @@ bool TF2Tools::ProcessCommandTarget(cmd_target_info_t *info) { UTIL_Format(info->target_name, info->target_name_maxlength, "Blue Team"); } - + return true; } @@ -327,7 +329,7 @@ void TF2Tools::OnPluginLoaded(IPlugin *plugin) && ( g_addCondForward->GetFunctionCount() || g_removeCondForward->GetFunctionCount() ) ) { - m_CondChecksEnabled = InitialiseConditionDetours(); + m_CondChecksEnabled = InitialiseConditionChecks(); } if (!m_RulesDetoursEnabled @@ -354,7 +356,7 @@ void TF2Tools::OnPluginUnloaded(IPlugin *plugin) { if (!g_addCondForward->GetFunctionCount() && !g_removeCondForward->GetFunctionCount()) { - RemoveConditionDetours(); + RemoveConditionChecks(); m_CondChecksEnabled = false; } } @@ -367,6 +369,12 @@ void TF2Tools::OnPluginUnloaded(IPlugin *plugin) } } } + +void TF2Tools::OnClientPutInServer(int client) +{ + Conditions_OnClientPutInServer(client); +} + int FindResourceEntity() { return FindEntityByNetClass(-1, "CTFPlayerResource"); diff --git a/extensions/tf2/extension.h b/extensions/tf2/extension.h index 7f9d3fc3..644723ce 100644 --- a/extensions/tf2/extension.h +++ b/extensions/tf2/extension.h @@ -42,6 +42,8 @@ #include #include +#define MAXPLAYERS 65 + /** * @brief Sample implementation of the SDK Extension. * Note: Uncomment one of the pre-defined virtual functions in order to use it. @@ -51,7 +53,8 @@ class TF2Tools : public ICommandTargetProcessor, public IConCommandBaseAccessor, public IGameEventListener2, - public IPluginsListener + public IPluginsListener, + public IClientListener { public: //SDKExtension /** @@ -99,6 +102,8 @@ public: //IGameEventManager public: //IPluginsListener void OnPluginLoaded(IPlugin *plugin); void OnPluginUnloaded(IPlugin *plugin); +public: //IClientListener + void OnClientPutInServer(int client); public: #if defined SMEXT_CONF_METAMOD /** @@ -138,6 +143,8 @@ extern IBinTools *g_pBinTools; extern IGameConfig *g_pGameConf; extern sm_sendprop_info_t *playerSharedOffset; +extern CGlobalVars *gpGlobals; + void OnServerActivate(edict_t *pEdictList, int edictCount, int clientMax); int FindResourceEntity(); diff --git a/gamedata/sm-tf2.games.txt b/gamedata/sm-tf2.games.txt index 01910e52..9c54b498 100644 --- a/gamedata/sm-tf2.games.txt +++ b/gamedata/sm-tf2.games.txt @@ -120,13 +120,6 @@ "linux" "@_Z21DuelMiniGame_IsInDuelP9CTFPlayer" "mac" "@_Z21DuelMiniGame_IsInDuelP9CTFPlayer" } - "RemoveAllConditions" - { - "library" "server" - "windows" "\x51\x53\x55\x56\x57\x8B\xF9\x8D\x8F\x2A\x2A\x2A\x2A\xE8\x2A\x2A\x2A\x2A\x8D\x87" - "linux" "@_ZN15CTFPlayerShared13RemoveAllCondEP9CTFPlayer" - "mac" "@_ZN15CTFPlayerShared13RemoveAllCondEP9CTFPlayer" - } } "Offsets" { diff --git a/plugins/include/tf2.inc b/plugins/include/tf2.inc index b8965d34..2b7940e9 100644 --- a/plugins/include/tf2.inc +++ b/plugins/include/tf2.inc @@ -292,20 +292,15 @@ native bool:TF2_IsPlayerInDuel(client); /** * Called after a condition is added to a player * - * @note Actual duration can differ based on various events ingame. - * * @param client Index of the client to which the conditon is being added. * @param condition Condition that is being added. - * @param duration Initial duration of the condition or -1 for infinite. * @noreturn */ -forward TF2_OnConditionAdded(client, TFCond:condition, Float:duration); +forward TF2_OnConditionAdded(client, TFCond:condition); /** * Called after a condition is removed from a player * - * @note This will never be called for the removal of TFCond_Taunting. - * * @param client Index of the client to which the condition is being removed. * @param condition Condition that is being removed. * @noreturn