diff --git a/core/logic/AMBuilder b/core/logic/AMBuilder index 80972250..f02693c0 100644 --- a/core/logic/AMBuilder +++ b/core/logic/AMBuilder @@ -31,6 +31,7 @@ files = [ 'Profiler.cpp', 'smn_functions.cpp', 'smn_timers.cpp', + 'smn_players.cpp', 'sm_crc32.cpp' ] if AMBuild.target['platform'] == 'windows': diff --git a/core/logic/AutoHandleRooter.h b/core/logic/AutoHandleRooter.h new file mode 100644 index 00000000..3233c8a8 --- /dev/null +++ b/core/logic/AutoHandleRooter.h @@ -0,0 +1,92 @@ +/** + * vim: set ts=4 sw=4 tw=99 noet : + * ============================================================================= + * SourceMod + * Copyright (C) 2004-2009 AlliedModders LLC. All rights reserved. + * ============================================================================= + * + * This program is free software; you can redistribute it and/or modify it under + * the terms of the GNU General Public License, version 3.0, as published by the + * Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + * + * As a special exception, AlliedModders LLC gives you permission to link the + * code of this program (as well as its derivative works) to "Half-Life 2," the + * "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software + * by the Valve Corporation. You must obey the GNU General Public License in + * all respects for all other code used. Additionally, AlliedModders LLC grants + * this exception to all derivative works. AlliedModders LLC defines further + * exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007), + * or . + * + * Version: $Id$ + */ + +#ifndef _INCLUDE_SOURCEMOD_AUTO_HANDLE_ROOTER_H_ +#define _INCLUDE_SOURCEMOD_AUTO_HANDLE_ROOTER_H_ + +#include "common_logic.h" +#include + +class AutoHandleRooter +{ +public: + AutoHandleRooter(Handle_t hndl) + { + if (hndl != BAD_HANDLE) + this->hndl = handlesys->FastCloneHandle(hndl); + else + this->hndl = BAD_HANDLE; + } + + ~AutoHandleRooter() + { + if (hndl != BAD_HANDLE) + { + HandleSecurity sec(g_pCoreIdent, g_pCoreIdent); + handlesys->FreeHandle(hndl, &sec); + } + } + + Handle_t getHandle() + { + return hndl; + } +private: + Handle_t hndl; +}; + +class AutoHandleCloner +{ + Handle_t original; + HandleSecurity sec; + AutoHandleRooter ahr; +public: + AutoHandleCloner(Handle_t original, HandleSecurity sec) + : original(original), sec(sec), ahr(original) + { + } + + ~AutoHandleCloner() + { + if (original != BAD_HANDLE) + { + handlesys->FreeHandle(original, &sec); + } + } + + Handle_t getClone() + { + return ahr.getHandle(); + } +}; + +#endif /* _INCLUDE_SOURCEMOD_AUTO_HANDLE_ROOTER_H_ */ + diff --git a/core/logic/Makefile b/core/logic/Makefile index fb257245..d59319c9 100644 --- a/core/logic/Makefile +++ b/core/logic/Makefile @@ -27,7 +27,8 @@ OBJECTS = \ Profiler.cpp \ smn_functions.cpp \ sm_crc32.cpp \ - smn_timers.cpp + smn_timers.cpp \ + smn_players.cpp ############################################## ### CONFIGURE ANY OTHER FLAGS/OPTIONS HERE ### diff --git a/core/logic/common_logic.cpp b/core/logic/common_logic.cpp index c9259692..0b03101e 100644 --- a/core/logic/common_logic.cpp +++ b/core/logic/common_logic.cpp @@ -52,6 +52,7 @@ IPluginManager *pluginsys; IForwardManager *forwardsys; ITimerSystem *timersys; ServerGlobals serverGlobals; +IPlayerManager *playerhelpers; static sm_logic_t logic = { @@ -79,6 +80,7 @@ static void logic_init(const sm_core_t* core, sm_logic_t* _logic) pluginsys = core->pluginsys; forwardsys = core->forwardsys; timersys = core->timersys; + playerhelpers = core->playerhelpers; } PLATFORM_EXTERN_C ITextParsers *get_textparsers() diff --git a/core/logic/common_logic.h b/core/logic/common_logic.h index 604050e0..07edaaa3 100644 --- a/core/logic/common_logic.h +++ b/core/logic/common_logic.h @@ -49,6 +49,7 @@ extern IPluginManager *pluginsys; extern IForwardManager *forwardsys; extern ITimerSystem *timersys; extern ServerGlobals serverGlobals; +extern IPlayerManager *playerhelpers; #endif /* _INCLUDE_SOURCEMOD_COMMON_LOGIC_H_ */ diff --git a/core/logic/intercom.h b/core/logic/intercom.h index f79b33c3..8b1a73e9 100644 --- a/core/logic/intercom.h +++ b/core/logic/intercom.h @@ -42,7 +42,7 @@ using namespace SourceMod; * Add 1 to the RHS of this expression to bump the intercom file * This is to prevent mismatching core/logic binaries */ -#define SM_LOGIC_MAGIC (0x0F47C0DE - 4) +#define SM_LOGIC_MAGIC (0x0F47C0DE - 5) #if defined SM_LOGIC class IVEngineServer @@ -64,6 +64,7 @@ namespace SourceMod class IPluginManager; class IForwardManager; class ITimerSystem; + class IPlayerManager; } class IVEngineServer; @@ -89,6 +90,7 @@ struct sm_core_t IPluginManager *pluginsys; IForwardManager *forwardsys; ITimerSystem *timersys; + IPlayerManager *playerhelpers; /* Functions */ void (*AddNatives)(sp_nativeinfo_t* nlist); ConVar * (*FindConVar)(const char*); diff --git a/core/logic/smn_players.cpp b/core/logic/smn_players.cpp new file mode 100644 index 00000000..45c995c4 --- /dev/null +++ b/core/logic/smn_players.cpp @@ -0,0 +1,235 @@ +/** + * vim: set ts=4 sw=4 tw=99 noet : + * ============================================================================= + * SourceMod + * Copyright (C) 2004-2008 AlliedModders LLC. All rights reserved. + * ============================================================================= + * + * This program is free software; you can redistribute it and/or modify it under + * the terms of the GNU General Public License, version 3.0, as published by the + * Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + * + * As a special exception, AlliedModders LLC gives you permission to link the + * code of this program (as well as its derivative works) to "Half-Life 2," the + * "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software + * by the Valve Corporation. You must obey the GNU General Public License in + * all respects for all other code used. Additionally, AlliedModders LLC grants + * this exception to all derivative works. AlliedModders LLC defines further + * exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007), + * or . + * + * Version: $Id$ + */ + +#include "common_logic.h" +#include +#include +#include +#include +#include "AutoHandleRooter.h" +#include "CellArray.h" + +using namespace SourceHook; +using namespace SourceMod; + +class PlayerLogicHelpers : + public SMGlobalClass, + public IPluginsListener, + public ICommandTargetProcessor +{ + struct SimpleMultiTargetFilter + { + IPlugin *plugin; + SourceHook::String pattern; + IPluginFunction *fun; + SourceHook::String phrase; + bool phraseIsML; + + SimpleMultiTargetFilter(IPlugin *plugin, const char *pattern, IPluginFunction *fun, + const char *phrase, bool phraseIsML) + : plugin(plugin), pattern(pattern), fun(fun), phrase(phrase), phraseIsML(phraseIsML) + { + } + }; + + List simpleMultis; + bool filterEnabled; + +public: + void AddMultiTargetFilter(IPlugin *plugin, const char *pattern, IPluginFunction *fun, + const char *phrase, bool phraseIsML) + { + SimpleMultiTargetFilter *smtf = new SimpleMultiTargetFilter(plugin, pattern, fun, phrase, + phraseIsML); + + simpleMultis.push_back(smtf); + + if (!filterEnabled) { + playerhelpers->RegisterCommandTargetProcessor(this); + filterEnabled = true; + } + } + + void RemoveMultiTargetFilter(const char *pattern, IPluginFunction *fun) + { + List::iterator iter = simpleMultis.begin(); + + while (iter != simpleMultis.end()) { + if ((*iter)->fun == fun && strcmp((*iter)->pattern.c_str(), pattern) == 0) { + delete (*iter); + iter = simpleMultis.erase(iter); + break; + } + iter++; + } + } + + PlayerLogicHelpers() + : filterEnabled(false) + { + } + +public: //ICommandTargetProcessor + bool ProcessCommandTarget(cmd_target_info_t *info) + { + List::iterator iter; + + for (iter = simpleMultis.begin(); iter != simpleMultis.end(); iter++) { + SimpleMultiTargetFilter *smtf = (*iter); + if (strcmp(smtf->pattern.c_str(), info->pattern) == 0) { + CellArray *array = new CellArray(1); + HandleSecurity sec(g_pCoreIdent, g_pCoreIdent); + Handle_t hndl = handlesys->CreateHandleEx(htCellArray, array, &sec, NULL, NULL); + AutoHandleCloner ahc(hndl, sec); + if (ahc.getClone() == BAD_HANDLE) { + smcore.LogError("[SM] Could not allocate a handle (%s, %d)", __FILE__, __LINE__); + delete array; + return false; + } + + smtf->fun->PushString(info->pattern); + smtf->fun->PushCell(ahc.getClone()); + cell_t result = 0; + if (smtf->fun->Execute(&result) != SP_ERROR_NONE || !result) + return false; + + IGamePlayer *pAdmin = info->admin + ? playerhelpers->GetGamePlayer(info->admin) + : NULL; + + info->num_targets = 0; + for (size_t i = 0; i < array->size(); i++) { + cell_t client = *array->at(i); + IGamePlayer *pClient = playerhelpers->GetGamePlayer(client); + if (pClient == NULL || !pClient->IsConnected()) + continue; + if (playerhelpers->FilterCommandTarget(pAdmin, pClient, info->flags) == + COMMAND_TARGET_VALID) + { + info->targets[info->num_targets++] = client; + if (info->num_targets >= unsigned(info->max_targets)) + break; + } + } + + info->reason = info->num_targets > 0 + ? COMMAND_TARGET_VALID + : COMMAND_TARGET_EMPTY_FILTER; + if (info->num_targets) { + smcore.strncopy(info->target_name, smtf->phrase.c_str(), info->target_name_maxlength); + info->target_name_style = smtf->phraseIsML + ? COMMAND_TARGETNAME_ML + : COMMAND_TARGETNAME_RAW; + } + + return true; + } + } + + return false; + } + +public: //SMGlobalClass + void OnSourceModAllInitialized() + { + pluginsys->AddPluginsListener(this); + } + + void OnSourceModShutdown() + { + pluginsys->RemovePluginsListener(this); + if (filterEnabled) { + playerhelpers->UnregisterCommandTargetProcessor(this); + filterEnabled = false; + } + } + +public: //IPluginsListener + + void OnPluginDestroyed(IPlugin *plugin) + { + List::iterator iter = simpleMultis.begin(); + + while (iter != simpleMultis.end()) { + if ((*iter)->plugin != plugin) { + iter++; + } else { + delete (*iter); + iter = simpleMultis.erase(iter); + } + } + } +} s_PlayerLogicHelpers; + +static cell_t +AddMultiTargetFilter(IPluginContext *ctx, const cell_t *params) +{ + IPluginFunction *fun = ctx->GetFunctionById(funcid_t(params[2])); + if (fun == NULL) + return ctx->ThrowNativeError("Invalid function id (%X)", params[2]); + + char *pattern; + char *phrase; + + ctx->LocalToString(params[1], &pattern); + ctx->LocalToString(params[3], &phrase); + + bool phraseIsML = !!params[4]; + IPlugin *plugin = pluginsys->FindPluginByContext(ctx->GetContext()); + + s_PlayerLogicHelpers.AddMultiTargetFilter(plugin, pattern, fun, phrase, phraseIsML); + + return 1; +} + +static cell_t +RemoveMultiTargetFilter(IPluginContext *ctx, const cell_t *params) +{ + IPluginFunction *fun = ctx->GetFunctionById(funcid_t(params[2])); + if (fun == NULL) + return ctx->ThrowNativeError("Invalid function id (%X)", params[2]); + + char *pattern; + + ctx->LocalToString(params[1], &pattern); + + s_PlayerLogicHelpers.RemoveMultiTargetFilter(pattern, fun); + + return 1; +} + +REGISTER_NATIVES(playernatives) +{ + {"AddMultiTargetFilter", AddMultiTargetFilter}, + {"RemoveMultiTargetFilter", RemoveMultiTargetFilter}, + {NULL, NULL} +}; + diff --git a/core/logic_bridge.cpp b/core/logic_bridge.cpp index 4fb0fa16..bde7fb47 100644 --- a/core/logic_bridge.cpp +++ b/core/logic_bridge.cpp @@ -46,6 +46,7 @@ #include "TimerSys.h" #include "logic_bridge.h" #include "DebugReporter.h" +#include "PlayerManager.h" static ILibrary *g_pLogic = NULL; static LogicInitFunction logic_init_fn; @@ -113,6 +114,7 @@ static sm_core_t core_bridge = &g_PluginSys, &g_Forwards, &g_Timers, + &g_Players, /* Functions */ add_natives, find_convar, diff --git a/plugins/include/commandfilters.inc b/plugins/include/commandfilters.inc index 825ba5ce..7b40b92f 100644 --- a/plugins/include/commandfilters.inc +++ b/plugins/include/commandfilters.inc @@ -132,3 +132,34 @@ stock ReplyToTargetError(client, reason) } } } + +/** + * Adds clients to a multi-target filter. + * + * @param pattern Pattern name. + * @param clients Array to fill with unique, valid client indexes. + * @return True if pattern was recognized, false otherwise. + */ +functag public bool:MultiTargetFilter(const String:pattern[], Handle:clients); + +/** + * Adds a multi-target filter function for ProcessTargetString(). + * + * @param pattern Pattern to match (case sensitive). + * @param filter Filter function. + * @param phrase Descriptive phrase to display on successful match. + * @param phraseIsML True if phrase is multi-lingual, false otherwise. + * @noreturn + */ +native AddMultiTargetFilter(const String:pattern[], MultiTargetFilter:filter, + const String:phrase[], bool:phraseIsML); + +/** + * Removes a multi-target filter function from ProcessTargetString(). + * + * @param pattern Pattern to match (case sensitive). + * @param filter Filter function. + * @noreturn + */ +native RemoveMultiTargetFilter(const String:pattern[], MultiTargetFilter:filter); + diff --git a/plugins/testsuite/ptstest.sp b/plugins/testsuite/ptstest.sp new file mode 100644 index 00000000..cb5280c1 --- /dev/null +++ b/plugins/testsuite/ptstest.sp @@ -0,0 +1,26 @@ +#include + +public Plugin:myinfo = +{ + name = "Test target filters", + author = "AlliedModders LLC", + description = "Tests target filters", + version = "1.0.0.0", + url = "http://www.sourcemod.net/" +}; + +public OnPluginStart() +{ + AddMultiTargetFilter("@crab", filter, "all players", true) +} + +public bool:filter(const String:pattern[], Handle:clients) +{ + for (new i = 1; i <= MaxClients; i++) { + if (IsPlayerInGame(i)) + PushArrayCell(clients, i) + } + + return true +} + diff --git a/public/IHandleSys.h b/public/IHandleSys.h index 78c3c338..48ae46fd 100644 --- a/public/IHandleSys.h +++ b/public/IHandleSys.h @@ -52,7 +52,7 @@ #include #define SMINTERFACE_HANDLESYSTEM_NAME "IHandleSys" -#define SMINTERFACE_HANDLESYSTEM_VERSION 3 +#define SMINTERFACE_HANDLESYSTEM_VERSION 4 /** Specifies no Identity */ #define DEFAULT_IDENTITY NULL @@ -357,6 +357,13 @@ namespace SourceMod const HandleSecurity *pSec, const HandleAccess *pAccess, HandleError *err) =0; + + /** + * @brief Clones a handle, bypassing security checks. + * + * @return A new Handle_t, or 0 on failure. + */ + virtual Handle_t FastCloneHandle(Handle_t hndl) =0; }; }