- added ShowActivity2() which has a more convenient display algorithm

- added commandfilters.inc and ProcessTargetString(), an extensible API for processing a command target and getting back formatted replies + player lists.  This makes SearchForClients() look paltry, and base plugins will slowly be moved to support the new functionality
- removed IsPlayerAlive() from SDKTools, added it to Core
- fixed a small bug in re-entrant translations
- core.games.txt is now automatically added to all plugins

--HG--
extra : convert_revision : svn%3A39bc706e-5318-0410-9160-8a85361fbb7c/trunk%401627
This commit is contained in:
David Anderson 2007-10-21 20:06:30 +00:00
parent 744dbdc4c7
commit 02e8977ea0
14 changed files with 676 additions and 30 deletions

View File

@ -45,12 +45,15 @@
#include "HalfLife2.h"
#include <inetchannel.h>
#include <iclient.h>
#include "GameConfigs.h"
PlayerManager g_Players;
bool g_OnMapStarted = false;
IForward *PreAdminCheck = NULL;
IForward *PostAdminCheck = NULL;
const unsigned int *g_NumPlayersToAuth = NULL;
int lifestate_offset = -1;
List<ICommandTargetProcessor *> target_processors;
SH_DECL_HOOK5(IServerGameClients, ClientConnect, SH_NOATTRIB, 0, bool, edict_t *, const char *, const char *, char *, int);
SH_DECL_HOOK2_void(IServerGameClients, ClientPutInServer, SH_NOATTRIB, 0, edict_t *, const char *);
@ -814,6 +817,281 @@ unsigned int PlayerManager::SetReplyTo(unsigned int reply)
return g_ChatTriggers.SetReplyTo(reply);
}
int PlayerManager::FilterCommandTarget(IGamePlayer *pAdmin, IGamePlayer *pTarget, int flags)
{
return InternalFilterCommandTarget((CPlayer *)pAdmin, (CPlayer *)pTarget, flags);
}
void PlayerManager::RegisterCommandTargetProcessor(ICommandTargetProcessor *pHandler)
{
target_processors.push_back(pHandler);
}
void PlayerManager::UnregisterCommandTargetProcessor(ICommandTargetProcessor *pHandler)
{
target_processors.remove(pHandler);
}
int PlayerManager::InternalFilterCommandTarget(CPlayer *pAdmin, CPlayer *pTarget, int flags)
{
if ((flags & COMMAND_FILTER_CONNECTED) == COMMAND_FILTER_CONNECTED
&& !pTarget->IsConnected())
{
return COMMAND_TARGET_NONE;
}
else if ((flags & COMMAND_FILTER_CONNECTED) != COMMAND_FILTER_CONNECTED &&
!pTarget->IsInGame())
{
return COMMAND_TARGET_NOT_IN_GAME;
}
if ((flags & COMMAND_FILTER_NO_BOTS) == COMMAND_FILTER_NO_BOTS
&& pTarget->IsFakeClient())
{
return COMMAND_TARGET_NOT_HUMAN;
}
if (pAdmin != NULL)
{
if ((flags & COMMAND_FILTER_NO_IMMUNITY) != COMMAND_FILTER_NO_IMMUNITY
&& !g_Admins.CanAdminTarget(pAdmin->GetAdminId(), pTarget->GetAdminId()))
{
return COMMAND_TARGET_IMMUNE;
}
}
if ((flags & COMMAND_FILTER_ALIVE) == COMMAND_FILTER_ALIVE
&& pTarget->GetLifeState() != PLAYER_LIFE_ALIVE)
{
return COMMAND_TARGET_NOT_ALIVE;
}
if ((flags & COMMAND_FILTER_DEAD) == COMMAND_FILTER_DEAD
&& pTarget->GetLifeState() != PLAYER_LIFE_DEAD)
{
return COMMAND_TARGET_NOT_DEAD;
}
return COMMAND_TARGET_VALID;
}
void PlayerManager::ProcessCommandTarget(cmd_target_info_t *info)
{
CPlayer *pTarget, *pAdmin;
int max_clients, total = 0;
max_clients = GetMaxClients();
if (info->max_targets < 1)
{
info->reason = COMMAND_TARGET_NONE;
info->num_targets = 0;
}
if (info->admin == 0)
{
pAdmin = NULL;
}
else
{
pAdmin = GetPlayerByIndex(info->admin);
}
if (info->pattern[0] == '#')
{
int userid = atoi(&info->pattern[1]);
int client = GetClientOfUserId(userid);
/* See if a valid userid matched */
if (client > 0)
{
IGamePlayer *pTarget = GetPlayerByIndex(client);
if (pTarget != NULL)
{
if ((info->reason = FilterCommandTarget(pAdmin, pTarget, info->flags)) == COMMAND_TARGET_VALID)
{
info->targets[0] = client;
info->num_targets = 1;
strncopy(info->target_name, pTarget->GetName(), info->target_name_maxlength);
info->target_name_style = COMMAND_TARGETNAME_RAW;
}
else
{
info->num_targets = 0;
}
return;
}
}
/* See if an exact name matches */
for (int i = 1; i <= max_clients; i++)
{
if ((pTarget = GetPlayerByIndex(i)) == NULL)
{
continue;
}
if (!pTarget->IsConnected())
{
continue;
}
if (strcmp(pTarget->GetName(), &info->pattern[1]) == 0)
{
if ((info->reason = FilterCommandTarget(pAdmin, pTarget, info->flags))
== COMMAND_TARGET_VALID)
{
info->targets[0] = i;
info->num_targets = 1;
strncopy(info->target_name, pTarget->GetName(), info->target_name_maxlength);
info->target_name_style = COMMAND_TARGETNAME_RAW;
}
else
{
info->num_targets = 0;
}
return;
}
}
}
if ((info->flags & COMMAND_FILTER_NO_MULTI) != COMMAND_FILTER_NO_MULTI)
{
bool is_multi = false;
bool bots_only = false;
if (strcmp(info->pattern, "@all") == 0)
{
is_multi = true;
strncopy(info->target_name, "all players", info->target_name_maxlength);
info->target_name_style = COMMAND_TARGETNAME_ML;
}
else if (strcmp(info->pattern, "@dead") == 0)
{
is_multi = true;
if ((info->flags & COMMAND_FILTER_ALIVE) == COMMAND_FILTER_ALIVE)
{
info->num_targets = 0;
info->reason = COMMAND_TARGET_NOT_ALIVE;
return;
}
info->flags |= COMMAND_FILTER_DEAD;
strncopy(info->target_name, "all dead players", info->target_name_maxlength);
info->target_name_style = COMMAND_TARGETNAME_ML;
}
else if (strcmp(info->pattern, "@alive") == 0)
{
is_multi = true;
if ((info->flags & COMMAND_FILTER_DEAD) == COMMAND_FILTER_DEAD)
{
info->num_targets = 0;
info->reason = COMMAND_TARGET_NOT_DEAD;
return;
}
strncopy(info->target_name, "all alive players", info->target_name_maxlength);
info->target_name_style = COMMAND_TARGETNAME_ML;
info->flags |= COMMAND_FILTER_ALIVE;
}
else if (strcmp(info->pattern, "@bots") == 0)
{
is_multi = true;
if ((info->flags & COMMAND_FILTER_NO_BOTS) == COMMAND_FILTER_NO_BOTS)
{
info->num_targets = 0;
info->reason = COMMAND_FILTER_NO_BOTS;
return;
}
strncopy(info->target_name, "all bots", info->target_name_maxlength);
info->target_name_style = COMMAND_TARGETNAME_ML;
bots_only = true;
}
else if (strcmp(info->pattern, "@humans") == 0)
{
is_multi = true;
strncopy(info->target_name, "all humans", info->target_name_maxlength);
info->target_name_style = COMMAND_TARGETNAME_ML;
info->flags |= COMMAND_FILTER_NO_BOTS;
}
if (is_multi)
{
for (int i = 1; i <= max_clients && total < info->max_targets; i++)
{
if ((pTarget = GetPlayerByIndex(i)) == NULL)
{
continue;
}
if (FilterCommandTarget(pAdmin, pTarget, info->flags) > 0)
{
if (!bots_only || pTarget->IsFakeClient())
{
info->targets[total++] = i;
}
}
}
info->num_targets = total;
info->reason = (info->num_targets) ? COMMAND_TARGET_VALID : COMMAND_TARGET_EMPTY_FILTER;
return;
}
}
List<ICommandTargetProcessor *>::iterator iter;
for (iter = target_processors.begin(); iter != target_processors.end(); iter++)
{
ICommandTargetProcessor *pProcessor = (*iter);
if (pProcessor->ProcessCommandTarget(info))
{
return;
}
}
/* Check partial names */
int found_client = 0;
CPlayer *pFoundClient = NULL;
for (int i = 1; i <= max_clients; i++)
{
if ((pTarget = GetPlayerByIndex(i)) == NULL)
{
continue;
}
if (stristr(pTarget->GetName(), info->pattern) != NULL)
{
if (found_client)
{
info->num_targets = 0;
info->reason = COMMAND_TARGET_AMBIGUOUS;
return;
}
else
{
found_client = i;
pFoundClient = pTarget;
}
}
}
if (found_client)
{
if ((info->reason = FilterCommandTarget(pAdmin, pFoundClient, info->flags))
== COMMAND_TARGET_VALID)
{
info->targets[0] = found_client;
info->num_targets = 1;
strncopy(info->target_name, pFoundClient->GetName(), info->target_name_maxlength);
info->target_name_style = COMMAND_TARGETNAME_RAW;
}
else
{
info->num_targets = 0;
}
}
else
{
info->num_targets = 0;
info->reason = COMMAND_TARGET_NONE;
}
}
/*******************
*** PLAYER CODE ***
*******************/
@ -1118,3 +1396,45 @@ void CPlayer::MarkAsBeingKicked()
{
m_bIsInKickQueue = true;
}
int CPlayer::GetLifeState()
{
if (lifestate_offset == -1)
{
if (!g_pGameConf->GetOffset("m_lifeState", &lifestate_offset))
{
lifestate_offset = -2;
}
}
if (lifestate_offset < 0)
{
IPlayerInfo *info = GetPlayerInfo();
if (info == NULL)
{
return PLAYER_LIFE_UNKNOWN;
}
return info->IsDead() ? PLAYER_LIFE_DEAD : PLAYER_LIFE_ALIVE;
}
if (m_pEdict == NULL)
{
return PLAYER_LIFE_UNKNOWN;
}
CBaseEntity *pEntity;
IServerUnknown *pUnknown = m_pEdict->GetUnknown();
if (pUnknown == NULL || (pEntity = pUnknown->GetBaseEntity()) == NULL)
{
return PLAYER_LIFE_UNKNOWN;
}
if (*((uint8_t *)pEntity + lifestate_offset) == LIFE_ALIVE)
{
return PLAYER_LIFE_ALIVE;
}
else
{
return PLAYER_LIFE_DEAD;
}
}

View File

@ -44,6 +44,10 @@
using namespace SourceHook;
#define PLAYER_LIFE_UNKNOWN 0
#define PLAYER_LIFE_ALIVE 1
#define PLAYER_LIFE_DEAD 2
class CPlayer : public IGamePlayer
{
friend class PlayerManager;
@ -70,6 +74,7 @@ public:
void DoBasicAdminChecks();
bool IsInKickQueue();
void MarkAsBeingKicked();
int GetLifeState();
private:
void Initialize(const char *name, const char *ip, edict_t *pEntity);
void Connect();
@ -138,6 +143,11 @@ public: //IPlayerManager
int GetNumPlayers();
int GetClientOfUserId(int userid);
bool IsServerActivated();
int FilterCommandTarget(IGamePlayer *pAdmin, IGamePlayer *pTarget, int flags);
int InternalFilterCommandTarget(CPlayer *pAdmin, CPlayer *pTarget, int flags);
void RegisterCommandTargetProcessor(ICommandTargetProcessor *pHandler);
void UnregisterCommandTargetProcessor(ICommandTargetProcessor *pHandler);
void ProcessCommandTarget(cmd_target_info_t *info);
public:
inline int MaxClients()
{

View File

@ -41,6 +41,7 @@
Translator g_Translator;
CPhraseFile *g_pCorePhrases = NULL;
unsigned int g_pCorePhraseID = 0;
struct trans_t
{
@ -712,10 +713,13 @@ void Translator::OnSourceModAllInitialized()
if (g_LibSys.PathExists(path))
{
id = FindOrAddPhraseFile("core.cfg");
} else {
}
else
{
id = FindOrAddPhraseFile("core.phrases.txt");
}
g_pCorePhraseID = id;
g_pCorePhrases = GetFileByIndex(id);
}

View File

@ -164,6 +164,7 @@ private:
};
extern CPhraseFile *g_pCorePhrases;
extern unsigned int g_pCorePhraseID;
extern Translator g_Translator;
#endif //_INCLUDE_SOURCEMOD_TRANSLATOR_H_

View File

@ -154,6 +154,8 @@ try_serverlang:
if (max_params)
{
cell_t new_params[MAX_TRANSLATE_PARAMS];
/* Check if we're going to over the limit */
if ((*arg) + (max_params - 1) > (size_t)params[0])
{
@ -164,12 +166,18 @@ try_serverlang:
goto error_out;
}
/* Re-order the parameters that this translation requires */
ReorderTranslationParams(&pTrans, const_cast<cell_t *>(&params[*arg]));
}
/* If we need to re-order the parameters, do so with a temporary array.
* Otherwise, we could run into trouble with continual formats, a la ShowActivity().
*/
memcpy(new_params, params, sizeof(cell_t) * (params[0] + 1));
ReorderTranslationParams(&pTrans, &new_params[*arg]);
/* Now, call back into atcprintf() which is re-entrant */
return atcprintf(buffer, maxlen, pTrans.szPhrase, pCtx, new_params, arg);
}
else
{
return atcprintf(buffer, maxlen, pTrans.szPhrase, pCtx, params, arg);
}
error_out:
*error = true;

View File

@ -408,6 +408,34 @@ static cell_t ShowVGUIPanel(IPluginContext *pContext, const cell_t *params)
return 1;
}
static cell_t smn_IsPlayerAlive(IPluginContext *pContext, const cell_t *params)
{
CPlayer *player = g_Players.GetPlayerByIndex(params[1]);
if (player == NULL)
{
return pContext->ThrowNativeError("Invalid client index %d", params[1]);
}
else if (!player->IsInGame())
{
return pContext->ThrowNativeError("Client %d is not in game", params[1]);
}
unsigned int state = player->GetLifeState();
if (state == PLAYER_LIFE_UNKNOWN)
{
return pContext->ThrowNativeError("\"IsPlayerAlive\" not supported by this mod");
}
else if (state == PLAYER_LIFE_ALIVE)
{
return 1;
}
else
{
return 0;
}
}
REGISTER_NATIVES(halflifeNatives)
{
{"CreateFakeClient", CreateFakeClient},
@ -436,5 +464,6 @@ REGISTER_NATIVES(halflifeNatives)
{"PrintCenterText", PrintCenterText},
{"PrintHintText", PrintHintText},
{"ShowVGUIPanel", ShowVGUIPanel},
{"IsPlayerAlive", smn_IsPlayerAlive},
{NULL, NULL},
};

View File

@ -881,7 +881,11 @@ static cell_t GetClientOfUserId(IPluginContext *pContext, const cell_t *params)
return g_Players.GetClientOfUserId(params[1]);
}
static cell_t _ShowActivity(IPluginContext *pContext, const cell_t *params, const char *tag, cell_t fmt_param)
static cell_t _ShowActivity(IPluginContext *pContext,
const cell_t *params,
const char *tag,
cell_t fmt_param,
unsigned int mode)
{
char message[255];
char buffer[255];
@ -912,13 +916,31 @@ static cell_t _ShowActivity(IPluginContext *pContext, const cell_t *params, cons
{
g_SourceMod.SetGlobalTarget(client);
g_SourceMod.FormatString(buffer, sizeof(buffer), pContext, params, fmt_param);
if (pContext->GetContext()->n_err != SP_ERROR_NONE)
{
return 0;
}
UTIL_Format(message, sizeof(message), "%s%s\n", tag, buffer);
engine->ClientPrintf(pPlayer->GetEdict(), message);
display_in_chat = true;
}
} else {
else if (mode == 2)
{
display_in_chat = true;
}
}
else
{
g_SourceMod.SetGlobalTarget(LANG_SERVER);
g_SourceMod.FormatString(buffer, sizeof(buffer), pContext, params, fmt_param);
if (pContext->GetContext()->n_err != SP_ERROR_NONE)
{
return 0;
}
UTIL_Format(message, sizeof(message), "%s%s\n", tag, buffer);
META_CONPRINT(message);
}
@ -947,15 +969,23 @@ static cell_t _ShowActivity(IPluginContext *pContext, const cell_t *params, cons
if ((value & 1) || (value & 2))
{
const char *newsign = sign;
if (value & 2)
if ((value & 2) || (i == client))
{
newsign = name;
}
g_SourceMod.FormatString(buffer, sizeof(buffer), pContext, params, fmt_param);
if (pContext->GetContext()->n_err != SP_ERROR_NONE)
{
return 0;
}
UTIL_Format(message, sizeof(message), "%s%s: %s", tag, newsign, buffer);
g_HL2.TextMsg(i, HUD_PRINTTALK, message);
}
} else {
}
else
{
/* Treat this as an admin user */
bool is_root = g_Admins.GetAdminFlag(id, Admin_Root, Access_Effective);
if ((value & 4)
@ -963,11 +993,17 @@ static cell_t _ShowActivity(IPluginContext *pContext, const cell_t *params, cons
|| ((value & 16) && is_root))
{
const char *newsign = sign;
if ((value & 8) || ((value & 16) && is_root))
if ((value & 8) || ((value & 16) && is_root) || (i == client))
{
newsign = name;
}
g_SourceMod.FormatString(buffer, sizeof(buffer), pContext, params, fmt_param);
if (pContext->GetContext()->n_err != SP_ERROR_NONE)
{
return 0;
}
UTIL_Format(message, sizeof(message), "%s%s: %s", tag, newsign, buffer);
g_HL2.TextMsg(i, HUD_PRINTTALK, message);
}
@ -979,7 +1015,7 @@ static cell_t _ShowActivity(IPluginContext *pContext, const cell_t *params, cons
static cell_t ShowActivity(IPluginContext *pContext, const cell_t *params)
{
return _ShowActivity(pContext, params, "[SM] ", 2);
return _ShowActivity(pContext, params, "[SM] ", 2, 1);
}
static cell_t ShowActivityEx(IPluginContext *pContext, const cell_t *params)
@ -987,7 +1023,15 @@ static cell_t ShowActivityEx(IPluginContext *pContext, const cell_t *params)
char *str;
pContext->LocalToString(params[2], &str);
return _ShowActivity(pContext, params, str, 3);
return _ShowActivity(pContext, params, str, 3, 1);
}
static cell_t ShowActivity2(IPluginContext *pContext, const cell_t *params)
{
char *str;
pContext->LocalToString(params[2], &str);
return _ShowActivity(pContext, params, str, 3, 2);
}
static cell_t KickClient(IPluginContext *pContext, const cell_t *params)
@ -1036,7 +1080,9 @@ static cell_t ChangeClientTeam(IPluginContext *pContext, const cell_t *params)
if (!pPlayer)
{
return pContext->ThrowNativeError("Client index %d is invalid", client);
} else if (!pPlayer->IsInGame()) {
}
else if (!pPlayer->IsInGame())
{
return pContext->ThrowNativeError("Client %d is not in game", client);
}
@ -1059,9 +1105,13 @@ static cell_t RunAdminCacheChecks(IPluginContext *pContext, const cell_t *params
if (!pPlayer)
{
return pContext->ThrowNativeError("Client index %d is invalid", client);
} else if (!pPlayer->IsInGame()) {
}
else if (!pPlayer->IsInGame())
{
return pContext->ThrowNativeError("Client %d is not in game", client);
} else if (!pPlayer->IsAuthorized()) {
}
else if (!pPlayer->IsAuthorized())
{
return pContext->ThrowNativeError("Client %d is not authorized", client);
}
@ -1079,9 +1129,12 @@ static cell_t NotifyPostAdminCheck(IPluginContext *pContext, const cell_t *param
if (!pPlayer)
{
return pContext->ThrowNativeError("Client index %d is invalid", client);
} else if (!pPlayer->IsInGame()) {
}
else if (!pPlayer->IsInGame()) {
return pContext->ThrowNativeError("Client %d is not in game", client);
} else if (!pPlayer->IsAuthorized()) {
}
else if (!pPlayer->IsAuthorized())
{
return pContext->ThrowNativeError("Client %d is not authorized", client);
}
@ -1108,6 +1161,42 @@ static cell_t IsClientInKickQueue(IPluginContext *pContext, const cell_t *params
return pPlayer->IsInKickQueue() ? 1 : 0;
}
static cell_t ProcessTargetString(IPluginContext *pContext, const cell_t *params)
{
cmd_target_info_t info;
pContext->LocalToString(params[1], (char **)&info.pattern);
info.admin = params[2];
pContext->LocalToPhysAddr(params[3], &info.targets);
info.max_targets = params[4];
info.flags = params[5];
pContext->LocalToString(params[6], &info.target_name);
info.target_name_maxlength = params[7];
cell_t *tn_is_ml;
pContext->LocalToPhysAddr(params[8], &tn_is_ml);
g_Players.ProcessCommandTarget(&info);
if (info.target_name_style == COMMAND_TARGETNAME_ML)
{
*tn_is_ml = 1;
}
else
{
*tn_is_ml = 0;
}
if (info.num_targets == 0)
{
return info.reason;
}
else
{
return info.num_targets;
}
}
REGISTER_NATIVES(playernatives)
{
{"AddUserFlags", AddUserFlags},
@ -1154,10 +1243,12 @@ REGISTER_NATIVES(playernatives)
{"GetClientOfUserId", GetClientOfUserId},
{"ShowActivity", ShowActivity},
{"ShowActivityEx", ShowActivityEx},
{"ShowActivity2", ShowActivity2},
{"KickClient", KickClient},
{"RunAdminCacheChecks", RunAdminCacheChecks},
{"NotifyPostAdminCheck", NotifyPostAdminCheck},
{"IsClientInKickQueue", IsClientInKickQueue},
{"ProcessTargetString", ProcessTargetString},
{NULL, NULL}
};

View File

@ -45,6 +45,7 @@
#include "ConCmdManager.h"
#include "PlayerManager.h"
#include "CoreConfig.h"
#include "Translator.h"
CPluginManager g_PluginSys;
HandleType_t g_PluginType = 0;
@ -1379,6 +1380,9 @@ bool CPluginManager::RunSecondPass(CPlugin *pPlugin, char *error, size_t maxleng
OnLibraryAction((*s_iter).c_str(), true, false);
}
/* Finally, add the core language file */
pPlugin->AddLangFile(g_pCorePhraseID);
return true;
}

View File

@ -255,6 +255,17 @@ native bool:IsFakeClient(client);
*/
native bool:IsClientObserver(client);
/**
* Returns if the client is alive or dead.
*
* Note: This function was originally in SDKTools and was moved to core.
*
* @param client Player's index.
* @return True if the client is alive, false otherwise.
* @error Invalid client index, client not in game, or no mod support.
*/
native bool:IsPlayerAlive(client);
/**
* Retrieves values from client replicated keys.
*

View File

@ -0,0 +1,130 @@
/**
* vim: set ts=4 :
* =============================================================================
* SourceMod (C)2004-2007 AlliedModders LLC. All rights reserved.
* =============================================================================
*
* This file is part of the SourceMod/SourcePawn SDK.
*
* 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 <http://www.gnu.org/licenses/>.
*
* 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 <http://www.sourcemod.net/license.php>.
*
* Version: $Id$
*/
#if defined _commandfilters_included
#endinput
#endif
#define _commandfilters_included
#define MAX_TARGET_LENGTH 64
#define COMMAND_FILTER_ALIVE (1<<0) /* Only allow alive players */
#define COMMAND_FILTER_DEAD (1<<1) /* Only filter dead players */
#define COMMAND_FILTER_CONNECTED (1<<2) /* Allow players not fully in-game */
#define COMMAND_FILTER_NO_IMMUNITY (1<<3) /* Ignore immunity rules */
#define COMMAND_FILTER_NO_MULTI (1<<4) /* Do not allow multiple target patterns */
#define COMMAND_FILTER_NO_BOTS (1<<5) /* Do not allow bots to be targetted */
#define COMMAND_TARGET_NONE 0 /* No target was found */
#define COMMAND_TARGET_NOT_ALIVE -1 /* Single client is not alive */
#define COMMAND_TARGET_NOT_DEAD -2 /* Single client is not dead */
#define COMMAND_TARGET_NOT_IN_GAME -3 /* Single client is not in game */
#define COMMAND_TARGET_IMMUNE -4 /* Single client is immune */
#define COMMAND_TARGET_EMPTY_FILTER -5 /* A multi-filter (such as @all) had no targets */
#define COMMAND_TARGET_NOT_HUMAN -6 /* Target was not human */
#define COMMAND_TARGET_AMBIGUOUS -7 /* Partial name had too many targets */
/**
* Processes a generic command target string, and resolves it to a list
* of clients or one client, based on filtering rules and a pattern.
*
* @param pattern Pattern to find clients against.
* @param admin Admin performing the action, or 0 if the server.
* @param targets Array to hold targets.
* @param max_targets Maximum size of the targets array.
* @param filter_flags Filter flags.
* @param target_name Buffer to store the target name.
* @param tn_maxlength Maximum length of the target name buffer.
* @param tn_is_ml Set to true if the target name buffer is an ML phrase,
* false if it is a normal string.
* @return If a multi-target pattern was used, the number of clients found
* is returned. If a single-target pattern was used, 1 is returned
* if one valid client is found. Otherwise, a CLIENT_TARGET reason
* for failure is returned.
*/
native ProcessTargetString(const String:pattern[],
admin,
targets[],
max_targets,
filter_flags,
String:target_name[],
tn_maxlength,
&bool:tn_is_ml);
/**
* Replies to a client with a given message describing a targetting
* failure reason.
*
* Note: The translation phrases are found in common.phrases.txt.
*
* @param client Client index, or 0 for server.
* @param reason COMMAND_TARGET reason.
* @noreturn
*/
stock ReplyToTargetError(client, reason)
{
switch (reason)
{
case COMMAND_TARGET_NONE:
{
ReplyToCommand(client, "[SM] %t", "No matching client");
}
case COMMAND_TARGET_NOT_ALIVE:
{
ReplyToCommand(client, "[SM] %t", "Target must be alive");
}
case COMMAND_TARGET_NOT_DEAD:
{
ReplyToCommand(client, "[SM] %t", "Target must be dead");
}
case COMMAND_TARGET_NOT_IN_GAME:
{
ReplyToCommand(client, "[SM] %t", "Target is not in game");
}
case COMMAND_TARGET_IMMUNE:
{
ReplyToCommand(client, "[SM] %t", "Unable to target");
}
case COMMAND_TARGET_EMPTY_FILTER:
{
ReplyToCommand(client, "[SM] %t", "No matching clients");
}
case COMMAND_TARGET_NOT_HUMAN:
{
ReplyToCommand(client, "[SM] %t", "Cannot target bot");
}
case COMMAND_TARGET_AMBIGUOUS:
{
ReplyToCommand(client, "[SM] %t", "More than one client matched");
}
}
}

View File

@ -241,9 +241,10 @@ native bool:IsChatTrigger();
/**
* Displays usage of an admin command to users depending on the
* setting of the sm_show_activity cvar. Additionally, the client
* who typed the command will receive the same text without any
* client name prefix (as a confirmation).
* setting of the sm_show_activity cvar.
*
* If the original client did not use a console command, they will
* not see the public display in their chat.
*
* @param client Client index doing the action, or 0 for server.
* @param format Formatting rules.
@ -266,6 +267,19 @@ native ShowActivity(client, const String:format[], any:...);
*/
native ShowActivityEx(client, const String:tag[], const String:format[], any:...);
/**
* Same as ShowActivityEx(), except the text will always be mirrored to the
* client's chat (and console, if the reply source specifies as such).
*
* @param client Client index doing the action, or 0 for server.
* @param tag Tag to display with.
* @param format Formatting rules.
* @param ... Variable number of format parameters.
* @noreturn
* @error
*/
native ShowActivity2(client, const String:tag[], const String:format[], any:...);
/**
* Called when a server-only command is invoked.
*

View File

@ -142,15 +142,6 @@ native FindEntityByClassname(startEnt, const String:classname[]);
*/
native bool:GetClientEyeAngles(client, Float:ang[3]);
/**
* Returns if the client is alive or dead.
*
* @param client Player's index.
* @return True if the client is alive, false otherwise.
* @error Invalid client index, client not in game, or no mod support.
*/
native bool:IsPlayerAlive(client);
/**
* Creates an entity by string name, but does not spawn it (see DispatchSpawn).
* If ForceEdictIndex is not -1, then it will use the edict by that index. If the index is

View File

@ -70,6 +70,7 @@ struct Plugin
#include <halflife>
#include <adt>
#include <banning>
#include <commandfilters>
/**
* Declare this as a struct in your plugin to expose its information.

View File

@ -24,4 +24,36 @@
{
"en" "Previous"
}
"all players"
{
"en" "all players"
}
"all humans"
{
"en" "all humans"
}
"all bots"
{
"en" "all bots"
}
"all dead players"
{
"en" "all dead players"
}
"all alive players"
{
"en" "all alive players"
}
/* This is a special "pass-thru" translation */
"_S"
{
"#format" "{1:s}"
"en" "{1}"
}
}