diff --git a/extensions/tf2/conditions.cpp b/extensions/tf2/conditions.cpp index ea8d7514..0bc18f46 100644 --- a/extensions/tf2/conditions.cpp +++ b/extensions/tf2/conditions.cpp @@ -2,7 +2,7 @@ * vim: set ts=4 : * ============================================================================= * SourceMod Team Fortress 2 Extension - * Copyright (C) 2004-2011 AlliedModders LLC. All rights reserved. + * Copyright (C) 2004-2015 AlliedModders LLC. All rights reserved. * ============================================================================= * * This program is free software; you can redistribute it and/or modify it under @@ -31,152 +31,133 @@ #include "extension.h" #include "conditions.h" -#include "util.h" - -#include - -#include - -const int TF_MAX_CONDITIONS = 128; - -typedef CBitVec condbitvec_t; -condbitvec_t g_PlayerActiveConds[SM_MAXPLAYERS + 1]; IForward *g_addCondForward = NULL; IForward *g_removeCondForward = NULL; -int playerCondOffset = -1; -int playerCondExOffset = -1; -int playerCondEx2Offset = -1; -int playerCondEx3Offset = -1; -int conditionBitsOffset = -1; - -bool g_bIgnoreRemove; - -#define MAX_CONDS (sizeof(uint64_t) * 8) - -inline void GetPlayerConds(CBaseEntity *pPlayer, condbitvec_t *pOut) +struct CondChangeData_t { - uint32_t tmp = *(uint32_t *)((intptr_t)pPlayer + playerCondOffset); - tmp |= *(uint32_t *)((intptr_t)pPlayer + conditionBitsOffset); - pOut->SetDWord(0, tmp); - tmp = *(uint32_t *)((intptr_t)pPlayer + playerCondExOffset); - pOut->SetDWord(1, tmp); - tmp = *(uint32_t *)((intptr_t)pPlayer + playerCondEx2Offset); - pOut->SetDWord(2, tmp); - tmp = *(uint32_t *)((intptr_t) pPlayer + playerCondEx3Offset); - pOut->SetDWord(3, tmp); + CBaseEntity *pPlayer; + PlayerConditionsMgr::CondVar var; + int newConds; +}; + +static void HandleCondChange(void *pData) +{ + auto *pCondData = reinterpret_cast(pData); + g_CondMgr.ProcessCondChange(pCondData); + delete pCondData; } -inline void CondBitVecAndNot(const condbitvec_t &src, const condbitvec_t &addStr, condbitvec_t *out) +void PlayerConditionsMgr::ProcessCondChange(CondChangeData_t *pCondData) { - static_assert(TF_MAX_CONDITIONS == 128, "CondBitVecAndNot hack is hardcoded for 128-bit bitvec."); - - // CBitVec has And and Not, but not a simple, combined AndNot. - // We'll also treat the halves as two 64-bit ints instead of four 32-bit ints - // as a minor optimization (maybe?) that the compiler is not making itself. - uint64 *pDest = (uint64 *)out->Base(); - const uint64 *pOperand1 = (const uint64 *) src.Base(); - const uint64 *pOperand2 = (const uint64 *) addStr.Base(); - - pDest[0] = pOperand1[0] & ~pOperand2[0]; - pDest[1] = pOperand1[1] & ~pOperand2[1]; -} - -void Conditions_OnGameFrame(bool simulating) -{ - if (!simulating) + int client = gamehelpers->EntityToBCompatRef(pCondData->pPlayer); + if (!playerhelpers->GetGamePlayer(client)->IsInGame()) return; - static condbitvec_t newconds; - - static condbitvec_t addedconds; - static condbitvec_t removedconds; + int newConds = 0; + int prevConds = 0; + CondVar var = pCondData->var; - int maxClients = gpGlobals->maxClients; - for (int i = 1; i <= maxClients; i++) + if (var == m_nPlayerCond) { - IGamePlayer *pPlayer = playerhelpers->GetGamePlayer(i); - if (!pPlayer->IsInGame() || pPlayer->IsSourceTV() || pPlayer->IsReplay()) - continue; + prevConds = m_OldConds[client][_condition_bits] | m_OldConds[client][var]; + newConds = m_OldConds[client][_condition_bits] | pCondData->newConds; + } + else if (var == _condition_bits) + { + prevConds = m_OldConds[client][m_nPlayerCond] | m_OldConds[client][var]; + newConds = m_OldConds[client][m_nPlayerCond] | pCondData->newConds; + } + else + { + prevConds = m_OldConds[client][var]; + newConds = pCondData->newConds; + } - CBaseEntity *pEntity = gamehelpers->ReferenceToEntity(i); - condbitvec_t &oldconds = g_PlayerActiveConds[i]; - GetPlayerConds(pEntity, &newconds); + if (prevConds != newConds) + { + int changedConds = newConds ^ prevConds; + int addedConds = changedConds & newConds; + int removedConds = changedConds & prevConds; + m_OldConds[client][var] = newConds; - if (oldconds == newconds) - continue; - - CondBitVecAndNot(newconds, oldconds, &addedconds); - CondBitVecAndNot(oldconds, newconds, &removedconds); - - int bit; - bit = -1; - while ((bit = addedconds.FindNextSetBit(bit + 1)) != -1) + for (int i = 0; i < 32; i++) { - g_addCondForward->PushCell(i); - g_addCondForward->PushCell(bit); - g_addCondForward->Execute(NULL, NULL); + if (addedConds & (1 << i)) + { + g_addCondForward->PushCell(client); + g_addCondForward->PushCell(i + m_CondOffset[var]); + g_addCondForward->Execute(NULL); + } + else if (removedConds & (1 << i)) + { + g_removeCondForward->PushCell(client); + g_removeCondForward->PushCell(i + m_CondOffset[var]); + g_removeCondForward->Execute(NULL); + } } - - bit = -1; - while ((bit = removedconds.FindNextSetBit(bit + 1)) != -1) - { - g_removeCondForward->PushCell(i); - g_removeCondForward->PushCell(bit); - g_removeCondForward->Execute(NULL, NULL); - } - - g_PlayerActiveConds[i] = newconds; } } -bool InitialiseConditionChecks() +template +static void OnPlayerCondChange(const SendProp *pProp, const void *pStructBase, const void *pData, DVariant *pOut, int iElement, int objectID) { - sm_sendprop_info_t prop; - if (!gamehelpers->FindSendPropInfo("CTFPlayer", "m_nPlayerCond", &prop)) + g_CondMgr.OnConVarChange(CondVar, pProp, pStructBase, pData, pOut, iElement, objectID); +} + +void PlayerConditionsMgr::OnConVarChange(CondVar var, const SendProp *pProp, const void *pStructBase, const void *pData, DVariant *pOut, int iElement, int objectID) +{ + auto pCondData = new CondChangeData_t; + pCondData->pPlayer = (CBaseEntity *)((intp)pData - GetPropOffs(var)); + pCondData->var = var; + pCondData->newConds = *(int *)pData; + + g_pSM->AddFrameAction(&HandleCondChange, pCondData); + + if (m_BackupProxyFns[var] != nullptr) + m_BackupProxyFns[var](pProp, pStructBase, pData, pOut, iElement, objectID); +} + +template +bool PlayerConditionsMgr::SetupProp(const char *varname) +{ + if (!gamehelpers->FindSendPropInfo("CTFPlayer", varname, &m_CondVarProps[var])) { - g_pSM->LogError(myself, "Failed to find m_nPlayerCond prop offset"); - return false; - } - - playerCondOffset = prop.actual_offset; - - if (!gamehelpers->FindSendPropInfo("CTFPlayer", "_condition_bits", &prop)) - { - g_pSM->LogError(myself, "Failed to find _condition_bits prop offset"); + g_pSM->LogError(myself, "Failed to find %s prop offset", varname); return false; } - conditionBitsOffset = prop.actual_offset; + m_BackupProxyFns[var] = GetProp(var)->GetProxyFn(); + GetProp(var)->SetProxyFn(OnPlayerCondChange); - if (!gamehelpers->FindSendPropInfo("CTFPlayer", "m_nPlayerCondEx", &prop)) - { - g_pSM->LogError(myself, "Failed to find m_nPlayerCondEx prop offset"); + return true; +} + +PlayerConditionsMgr::PlayerConditionsMgr() +{ + m_CondOffset[m_nPlayerCond] = 0; + m_CondOffset[_condition_bits] = 0; + m_CondOffset[m_nPlayerCondEx] = 32; + m_CondOffset[m_nPlayerCondEx2] = 64; + m_CondOffset[m_nPlayerCondEx3] = 96; +} + +bool PlayerConditionsMgr::Init() +{ + memset(m_BackupProxyFns, 0, sizeof(m_BackupProxyFns)); + + bool bFoundProps = SetupProp("m_nPlayerCond") + && SetupProp<_condition_bits>("_condition_bits") + && SetupProp("m_nPlayerCondEx") + && SetupProp("m_nPlayerCondEx2") + && SetupProp("m_nPlayerCondEx3"); + + if (!bFoundProps) return false; - } - - playerCondExOffset = prop.actual_offset; - if (!gamehelpers->FindSendPropInfo("CTFPlayer", "m_nPlayerCondEx2", &prop)) - { - g_pSM->LogError(myself, "Failed to find m_nPlayerCondEx2 prop offset"); - return false; - } - - playerCondEx2Offset = prop.actual_offset; + playerhelpers->AddClientListener(this); - if (!gamehelpers->FindSendPropInfo("CTFPlayer", "m_nPlayerCondEx3", &prop)) - { - g_pSM->LogError(myself, "Failed to find m_nPlayerCondEx3 prop offset"); - return false; - } - - playerCondEx3Offset = prop.actual_offset; - - if (playerCondOffset == -1 || playerCondExOffset == -1 || conditionBitsOffset == -1 || playerCondEx2Offset == -1 || playerCondEx3Offset == -1) - return false; - int maxClients = gpGlobals->maxClients; for (int i = 1; i <= maxClients; i++) { @@ -184,21 +165,29 @@ bool InitialiseConditionChecks() if (!pPlayer || !pPlayer->IsInGame()) continue; - GetPlayerConds(gamehelpers->ReferenceToEntity(i), &g_PlayerActiveConds[i]); + CBaseEntity *pEntity = gamehelpers->ReferenceToEntity(i); + for (size_t j = 0; j < CondVar_Count; ++j) + { + m_OldConds[i][j] = *(int *)((intp) pEntity + GetPropOffs((CondVar)j)); + } } - g_pSM->AddGameFrameHook(Conditions_OnGameFrame); - return true; } -void Conditions_OnClientPutInServer(int client) +void PlayerConditionsMgr::Shutdown() { - g_PlayerActiveConds[client].ClearAll(); + for (size_t i = 0; i < CondVar_Count; ++i) + { + GetProp((CondVar)i)->SetProxyFn(m_BackupProxyFns[i]); + } + + playerhelpers->RemoveClientListener(this); } -void RemoveConditionChecks() +void PlayerConditionsMgr::OnClientPutInServer(int client) { - g_pSM->RemoveGameFrameHook(Conditions_OnGameFrame); + memset(&m_OldConds[client], 0, sizeof(m_OldConds[0])); } +PlayerConditionsMgr g_CondMgr; diff --git a/extensions/tf2/conditions.h b/extensions/tf2/conditions.h index f8bf08b2..4aa76091 100644 --- a/extensions/tf2/conditions.h +++ b/extensions/tf2/conditions.h @@ -2,7 +2,7 @@ * vim: set ts=4 : * ============================================================================= * SourceMod Team Fortress 2 Extension - * Copyright (C) 2004-2011 AlliedModders LLC. All rights reserved. + * Copyright (C) 2004-2015 AlliedModders LLC. All rights reserved. * ============================================================================= * * This program is free software; you can redistribute it and/or modify it under @@ -33,15 +33,50 @@ #define _INCLUDE_SOURCEMOD_CONDITIONS_H_ #include "extension.h" -#include -#include -#include "CDetour/detours.h" -bool InitialiseConditionChecks(); -void RemoveConditionChecks(); -void DoRemoveCond(int client, int condition); +struct CondChangeData_t; -void Conditions_OnClientPutInServer(int client); +class PlayerConditionsMgr : public IClientListener +{ +public: + PlayerConditionsMgr(); + bool Init(); + void Shutdown(); +public: // IClientListener + void OnClientPutInServer(int client); +public: + enum CondVar : size_t + { + m_nPlayerCond, + _condition_bits, + m_nPlayerCondEx, + m_nPlayerCondEx2, + m_nPlayerCondEx3, + + CondVar_Count + }; + + void OnConVarChange(CondVar var, const SendProp *pProp, const void *pStructBase, const void *pData, DVariant *pOut, int iElement, int objectID); + void ProcessCondChange(CondChangeData_t *pCondData); +private: + inline unsigned int GetPropOffs(CondVar var) + { + return m_CondVarProps[var].actual_offset; + } + inline SendProp *GetProp(CondVar var) + { + return m_CondVarProps[var].prop; + } + template + bool SetupProp(const char *varname); +private: + int m_OldConds[SM_MAXPLAYERS + 1][CondVar_Count]; + sm_sendprop_info_t m_CondVarProps[CondVar_Count]; + int m_CondOffset[CondVar_Count]; + SendVarProxyFn m_BackupProxyFns[CondVar_Count]; +}; + +extern PlayerConditionsMgr g_CondMgr; extern IForward *g_addCondForward; extern IForward *g_removeCondForward; diff --git a/extensions/tf2/extension.cpp b/extensions/tf2/extension.cpp index 074611ad..71f5279f 100644 --- a/extensions/tf2/extension.cpp +++ b/extensions/tf2/extension.cpp @@ -2,7 +2,7 @@ * vim: set ts=4 : * ============================================================================= * SourceMod Team Fortress 2 Extension - * Copyright (C) 2004-2011 AlliedModders LLC. All rights reserved. + * Copyright (C) 2004-2015 AlliedModders LLC. All rights reserved. * ============================================================================= * * This program is free software; you can redistribute it and/or modify it under @@ -116,7 +116,6 @@ 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_addCondForward = forwards->CreateForward("TF2_OnConditionAdded", ET_Ignore, 2, NULL, Param_Cell, Param_Cell); @@ -172,7 +171,6 @@ void TF2Tools::SDK_OnUnload() g_RegNatives.UnregisterAll(); gameconfs->CloseGameConfigFile(g_pGameConf); playerhelpers->UnregisterCommandTargetProcessor(this); - playerhelpers->RemoveClientListener(this); plsys->RemovePluginsListener(this); @@ -357,7 +355,7 @@ void TF2Tools::OnPluginLoaded(IPlugin *plugin) && ( g_addCondForward->GetFunctionCount() || g_removeCondForward->GetFunctionCount() ) ) { - m_CondChecksEnabled = InitialiseConditionChecks(); + m_CondChecksEnabled = g_CondMgr.Init(); } if (!m_RulesDetoursEnabled @@ -384,7 +382,7 @@ void TF2Tools::OnPluginUnloaded(IPlugin *plugin) { if (!g_addCondForward->GetFunctionCount() && !g_removeCondForward->GetFunctionCount()) { - RemoveConditionChecks(); + g_CondMgr.Shutdown(); m_CondChecksEnabled = false; } } @@ -403,11 +401,6 @@ 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 0f6b2089..d9b22334 100644 --- a/extensions/tf2/extension.h +++ b/extensions/tf2/extension.h @@ -2,7 +2,7 @@ * vim: set ts=4 : * ============================================================================= * SourceMod Team Fortress 2 Extension - * Copyright (C) 2004-2011 AlliedModders LLC. All rights reserved. + * Copyright (C) 2004-2015 AlliedModders LLC. All rights reserved. * ============================================================================= * * This program is free software; you can redistribute it and/or modify it under @@ -55,8 +55,7 @@ class TF2Tools : public ICommandTargetProcessor, public IConCommandBaseAccessor, public IGameEventListener2, - public IPluginsListener, - public IClientListener + public IPluginsListener { public: //SDKExtension /** @@ -104,8 +103,6 @@ public: //IGameEventManager public: //IPluginsListener void OnPluginLoaded(IPlugin *plugin); void OnPluginUnloaded(IPlugin *plugin); -public: //IClientListener - void OnClientPutInServer(int client); public: #if defined SMEXT_CONF_METAMOD /**