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;
};
}