Compare commits

..
Author SHA1 Message Date
BotoX 0bda47d85b Added hack to make plugins open a menu with all possible targets on ReplyToTargetError COMMAND_TARGET_AMBIGUOUS.
Explanation:
There are two clients in the server, one named gene, the other one "Ene ~special characters~".
An admin issues "sm_slay Ene" and gets following error message: More than one client matched the given pattern.
What this hack will do is: Use GetCmdArg(0, ...); to get the command name "sm_slay".
Use GetCmdArgString(...); to get the arguments supplied to the command.
Use GetLastProcessTargetString(...); (which was implemented in this commit) to retrieve the arguments that were passed to the last ProcessTargetString call.
It will then pass this data to the DynamicTargeting plugin through its AmbiguousMenu native.
The plugin will open up a menu on the client and list all targets which match the pattern that was supplied to ProcessTargetString.
If the client selects a menu entry, FakeClientCommand will be used to re-execute the command with the correct target.
2019-09-10 11:27:18 +02:00
17 changed files with 494 additions and 315 deletions
+3 -3
View File
@@ -308,7 +308,7 @@ IMenuPanel *MenuManager::RenderMenu(int client, menu_states_t &md, ItemOrder ord
{
ItemDrawInfo &dr = drawItems[foundItems].draw;
/* Is the item valid? */
if (menu->GetItemInfo(i, &dr, client) != NULL)
if (menu->GetItemInfo(i, &dr) != NULL)
{
/* Ask the user to change the style, if necessary */
mh->OnMenuDrawItem(menu, client, i, dr.style);
@@ -398,7 +398,7 @@ IMenuPanel *MenuManager::RenderMenu(int client, menu_states_t &md, ItemOrder ord
}
while (++lastItem < totalItems)
{
if (menu->GetItemInfo(lastItem, &dr, client) != NULL)
if (menu->GetItemInfo(lastItem, &dr) != NULL)
{
mh->OnMenuDrawItem(menu, client, lastItem, dr.style);
if (IsSlotItem(panel, dr.style))
@@ -420,7 +420,7 @@ IMenuPanel *MenuManager::RenderMenu(int client, menu_states_t &md, ItemOrder ord
lastItem--;
while (lastItem != 0)
{
if (menu->GetItemInfo(lastItem, &dr, client) != NULL)
if (menu->GetItemInfo(lastItem, &dr) != NULL)
{
mh->OnMenuDrawItem(menu, client, lastItem, dr.style);
if (IsSlotItem(panel, dr.style))
+3 -64
View File
@@ -633,7 +633,7 @@ bool CBaseMenu::AppendItem(const char *info, const ItemDrawInfo &draw)
return false;
}
CItem item(m_items.length());
CItem item;
item.info = info;
if (draw.display)
@@ -655,7 +655,7 @@ bool CBaseMenu::InsertItem(unsigned int position, const char *info, const ItemDr
if (position >= m_items.length())
return false;
CItem item(position);
CItem item;
item.info = info;
if (draw.display)
item.display = new ke::AString(draw.display);
@@ -679,16 +679,11 @@ void CBaseMenu::RemoveAllItems()
m_items.clear();
}
const char *CBaseMenu::GetItemInfo(unsigned int position, ItemDrawInfo *draw/* =NULL */, int client/* =0 */)
const char *CBaseMenu::GetItemInfo(unsigned int position, ItemDrawInfo *draw/* =NULL */)
{
if (position >= m_items.length())
return NULL;
if (client > 0 && position < m_RandomMaps[client].length())
{
position = m_RandomMaps[client][position];
}
if (draw)
{
draw->display = m_items[position].display->chars();
@@ -698,62 +693,6 @@ const char *CBaseMenu::GetItemInfo(unsigned int position, ItemDrawInfo *draw/* =
return m_items[position].info.chars();
}
void CBaseMenu::ShufflePerClient(int start, int stop)
{
// limit map len to 255 items since it's using uint8
int length = MIN(GetItemCount(), 255);
if (stop >= 0)
length = MIN(length, stop);
for (int i = 1; i < SM_MAXPLAYERS + 1; i++)
{
// populate per-client map ...
m_RandomMaps[i].resize(length);
for (int j = 0; j < length; j++)
m_RandomMaps[i][j] = j;
// ... and random shuffle it
for (int j = length - 1; j > start; j--)
{
int x = rand() % (j - start + 1) + start;
uint8_t tmp = m_RandomMaps[i][x];
m_RandomMaps[i][x] = m_RandomMaps[i][j];
m_RandomMaps[i][j] = tmp;
}
}
}
void CBaseMenu::SetClientMapping(int client, int *array, int length)
{
length = MIN(length, 255);
m_RandomMaps[client].resize(length);
for (int i = 0; i < length; i++)
{
m_RandomMaps[client][i] = array[i];
}
}
bool CBaseMenu::IsPerClientShuffled()
{
for (int i = 1; i < SM_MAXPLAYERS + 1; i++)
{
if(m_RandomMaps[i].length() > 0)
return true;
}
return false;
}
unsigned int CBaseMenu::GetRealItemIndex(int client, unsigned int position)
{
if (client > 0 && position < m_RandomMaps[client].length())
{
position = m_RandomMaps[client][position];
return m_items[position].index;
}
return position;
}
unsigned int CBaseMenu::GetItemCount()
{
return m_items.length();
+2 -11
View File
@@ -44,9 +44,8 @@ using namespace SourceMod;
class CItem
{
public:
CItem(unsigned int index)
CItem()
{
this->index = index;
style = 0;
access = 0;
}
@@ -54,13 +53,11 @@ public:
: info(ke::Move(other.info)),
display(ke::Move(other.display))
{
index = other.index;
style = other.style;
access = other.access;
}
CItem & operator =(CItem &&other)
{
index = other.index;
info = ke::Move(other.info);
display = ke::Move(other.display);
style = other.style;
@@ -69,7 +66,6 @@ public:
}
public:
unsigned int index;
ke::AString info;
ke::AutoPtr<ke::AString> display;
unsigned int style;
@@ -142,7 +138,7 @@ public:
virtual bool InsertItem(unsigned int position, const char *info, const ItemDrawInfo &draw);
virtual bool RemoveItem(unsigned int position);
virtual void RemoveAllItems();
virtual const char *GetItemInfo(unsigned int position, ItemDrawInfo *draw=NULL, int client=0);
virtual const char *GetItemInfo(unsigned int position, ItemDrawInfo *draw=NULL);
virtual unsigned int GetItemCount();
virtual bool SetPagination(unsigned int itemsPerPage);
virtual unsigned int GetPagination();
@@ -156,10 +152,6 @@ public:
virtual unsigned int GetMenuOptionFlags();
virtual void SetMenuOptionFlags(unsigned int flags);
virtual IMenuHandler *GetHandler();
virtual void ShufflePerClient(int start, int stop);
virtual void SetClientMapping(int client, int *array, int length);
virtual bool IsPerClientShuffled();
virtual unsigned int GetRealItemIndex(int client, unsigned int position);
unsigned int GetBaseMemUsage();
private:
void InternalDelete();
@@ -176,7 +168,6 @@ protected:
Handle_t m_hHandle;
IMenuHandler *m_pHandler;
unsigned int m_nFlags;
ke::Vector<uint8_t> m_RandomMaps[SM_MAXPLAYERS+1];
};
#endif //_INCLUDE_MENUSTYLE_BASE_H
+3 -4
View File
@@ -514,16 +514,15 @@ void VoteMenuHandler::OnMenuSelect(IBaseMenu *menu, int client, unsigned int ite
/* Check by our item count, NOT the vote array size */
if (item < m_Items)
{
unsigned int index = menu->GetRealItemIndex(client, item);
m_ClientVotes[client] = index;
m_Votes[index]++;
m_ClientVotes[client] = item;
m_Votes[item]++;
m_NumVotes++;
if (sm_vote_chat.GetBool() || sm_vote_console.GetBool() || sm_vote_client_console.GetBool())
{
static char buffer[1024];
ItemDrawInfo dr;
menu->GetItemInfo(item, &dr, client);
menu->GetItemInfo(item, &dr);
if (sm_vote_console.GetBool())
{
+1 -62
View File
@@ -816,14 +816,8 @@ static cell_t GetMenuItem(IPluginContext *pContext, const cell_t *params)
ItemDrawInfo dr;
const char *info;
cell_t client = (params[0] >= 8) ? params[8] : 0;
if(!client && menu->IsPerClientShuffled())
{
return pContext->ThrowNativeError("This menu has been per-client random shuffled. "
"You have to call GetMenuItem with a client index!");
}
if ((info=menu->GetItemInfo(params[2], &dr, client)) == NULL)
if ((info=menu->GetItemInfo(params[2], &dr)) == NULL)
{
return 0;
}
@@ -838,57 +832,6 @@ static cell_t GetMenuItem(IPluginContext *pContext, const cell_t *params)
return 1;
}
static cell_t MenuShufflePerClient(IPluginContext *pContext, const cell_t *params)
{
Handle_t hndl = (Handle_t)params[1];
HandleError err;
IBaseMenu *menu;
if ((err = ReadMenuHandle(params[1], &menu)) != HandleError_None)
{
return pContext->ThrowNativeError("Menu handle %x is invalid (error %d)", hndl, err);
}
int start = params[2];
int stop = params[3];
if (stop > 0 && !(stop >= start))
{
return pContext->ThrowNativeError("Stop must be -1 or >= start!");
}
menu->ShufflePerClient(start, stop);
return 1;
}
static cell_t MenuSetClientMapping(IPluginContext *pContext, const cell_t *params)
{
Handle_t hndl = (Handle_t)params[1];
HandleError err;
IBaseMenu *menu;
if ((err = ReadMenuHandle(params[1], &menu)) != HandleError_None)
{
return pContext->ThrowNativeError("Menu handle %x is invalid (error %d)", hndl, err);
}
int client = params[2];
if (client < 1 || client > SM_MAXPLAYERS)
{
return pContext->ThrowNativeError("Invalid client index!");
}
cell_t *array;
pContext->LocalToPhysAddr(params[3], &array);
int length = params[4];
menu->SetClientMapping(client, array, length);
return 1;
}
static cell_t SetMenuPagination(IPluginContext *pContext, const cell_t *params)
{
Handle_t hndl = (Handle_t)params[1];
@@ -1702,8 +1645,6 @@ REGISTER_NATIVES(menuNatives)
{"SetPanelKeys", SetPanelKeys},
{"SetVoteResultCallback", SetVoteResultCallback},
{"VoteMenu", VoteMenu},
{"MenuShufflePerClient", MenuShufflePerClient},
{"MenuSetClientMapping", MenuSetClientMapping},
{"SetMenuNoVoteButton", SetMenuNoVoteButton},
// Transitional syntax support.
@@ -1732,8 +1673,6 @@ REGISTER_NATIVES(menuNatives)
{"Menu.ToPanel", CreatePanelFromMenu},
{"Menu.Cancel", CancelMenu},
{"Menu.DisplayVote", VoteMenu},
{"Menu.ShufflePerClient", MenuShufflePerClient},
{"Menu.SetClientMapping", MenuSetClientMapping},
{"Menu.Pagination.get", GetMenuPagination},
{"Menu.Pagination.set", SetMenuPagination},
{"Menu.OptionFlags.get", GetMenuOptionFlags},
+28 -14
View File
@@ -1458,24 +1458,23 @@ static cell_t IsClientInKickQueue(IPluginContext *pContext, const cell_t *params
return pPlayer->IsInKickQueue() ? 1 : 0;
}
cmd_target_info_t g_ProcessTargetString_info;
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];
pContext->LocalToString(params[1], (char **) &g_ProcessTargetString_info.pattern);
g_ProcessTargetString_info.admin = params[2];
pContext->LocalToPhysAddr(params[3], &g_ProcessTargetString_info.targets);
g_ProcessTargetString_info.max_targets = params[4];
g_ProcessTargetString_info.flags = params[5];
pContext->LocalToString(params[6], &g_ProcessTargetString_info.target_name);
g_ProcessTargetString_info.target_name_maxlength = params[7];
cell_t *tn_is_ml;
pContext->LocalToPhysAddr(params[8], &tn_is_ml);
playerhelpers->ProcessCommandTarget(&info);
playerhelpers->ProcessCommandTarget(&g_ProcessTargetString_info);
if (info.target_name_style == COMMAND_TARGETNAME_ML)
if (g_ProcessTargetString_info.target_name_style == COMMAND_TARGETNAME_ML)
{
*tn_is_ml = 1;
}
@@ -1484,16 +1483,30 @@ static cell_t ProcessTargetString(IPluginContext *pContext, const cell_t *params
*tn_is_ml = 0;
}
if (info.num_targets == 0)
if (g_ProcessTargetString_info.num_targets == 0)
{
return info.reason;
return g_ProcessTargetString_info.reason;
}
else
{
return info.num_targets;
return g_ProcessTargetString_info.num_targets;
}
}
static cell_t GetLastProcessTargetString(IPluginContext *pContext, const cell_t *params)
{
cell_t *admin, *flags;
pContext->StringToLocalUTF8(params[1], params[2], g_ProcessTargetString_info.pattern, NULL);
pContext->LocalToPhysAddr(params[3], &admin);
pContext->LocalToPhysAddr(params[4], &flags);
*admin = g_ProcessTargetString_info.admin;
*flags = g_ProcessTargetString_info.flags;
return 0;
}
static cell_t FormatActivitySource(IPluginContext *pContext, const cell_t *params)
{
int value;
@@ -1645,6 +1658,7 @@ REGISTER_NATIVES(playernatives)
{ "NotifyPostAdminCheck", NotifyPostAdminCheck },
{ "IsClientInKickQueue", IsClientInKickQueue },
{ "ProcessTargetString", ProcessTargetString },
{ "GetLastProcessTargetString", GetLastProcessTargetString },
{ "FormatActivitySource", FormatActivitySource },
{ "GetClientSerial", sm_GetClientSerial },
{ "GetClientFromSerial", sm_GetClientFromSerial },
+11
View File
@@ -789,6 +789,16 @@ static cell_t sm_RegAdminCmd(IPluginContext *pContext, const cell_t *params)
return 1;
}
static cell_t sm_IsCommandCallback(IPluginContext *pContext, const cell_t *params)
{
const ICommandArgs *pCmd = g_HL2.PeekCommandStack();
if (!pCmd)
return 0;
return 1;
}
static cell_t sm_GetCmdArgs(IPluginContext *pContext, const cell_t *params)
{
const ICommandArgs *pCmd = g_HL2.PeekCommandStack();
@@ -1467,6 +1477,7 @@ REGISTER_NATIVES(consoleNatives)
{"GetConVarDefault", GetConVarDefault},
{"RegServerCmd", sm_RegServerCmd},
{"RegConsoleCmd", sm_RegConsoleCmd},
{"IsCommandCallback", sm_IsCommandCallback},
{"GetCmdArgString", sm_GetCmdArgString},
{"GetCmdArgs", sm_GetCmdArgs},
{"GetCmdArg", sm_GetCmdArg},
+2 -1
View File
@@ -24,7 +24,8 @@ files = [
'basecommands.sp',
'mapchooser.sp',
'randomcycle.sp',
'sql-admin-manager.sp'
'sql-admin-manager.sp',
'DynamicTargeting.sp'
]
spcomp_argv = [
+266
View File
@@ -0,0 +1,266 @@
#pragma semicolon 1
#define PLUGIN_VERSION "1.0"
#include <sourcemod>
#include <DynamicTargeting>
#pragma newdecls required
public Plugin myinfo =
{
name = "Dynamic Targeting",
author = "BotoX",
description = "",
version = PLUGIN_VERSION,
url = ""
}
char g_PlayerNames[MAXPLAYERS + 1][MAX_NAME_LENGTH];
Handle g_PlayerData[MAXPLAYERS + 1];
public APLRes AskPluginLoad2(Handle myself, bool late, char[] error, int err_max)
{
CreateNative("AmbiguousMenu", Native_AmbiguousMenu);
RegPluginLibrary("DynamicTargeting");
return APLRes_Success;
}
public void OnClientDisconnect(int client)
{
if(g_PlayerData[client] != INVALID_HANDLE)
{
CloseHandle(g_PlayerData[client]);
g_PlayerData[client] = INVALID_HANDLE;
}
}
int CreateAmbiguousMenu(int client, const char[] sCommand, const char[] sArgString, const char[] sPattern, int FilterFlags)
{
Menu menu = new Menu(MenuHandler_AmbiguousMenu, MenuAction_Select|MenuAction_Cancel|MenuAction_End|MenuAction_DrawItem|MenuAction_DisplayItem);
menu.ExitButton = true;
char sTitle[32 + MAX_TARGET_LENGTH];
FormatEx(sTitle, sizeof(sTitle), "Target \"%s\" is ambiguous.", sPattern);
menu.SetTitle(sTitle);
int Players = 0;
int[] aClients = new int[MaxClients + 1];
for(int i = 1; i <= MaxClients; i++)
{
if(!IsClientConnected(i) || i == client)
continue;
if(FilterFlags & COMMAND_FILTER_NO_BOTS && IsFakeClient(i))
continue;
if(!(FilterFlags & COMMAND_FILTER_CONNECTED) && !IsClientInGame(i))
continue;
if(FilterFlags & COMMAND_FILTER_ALIVE && !IsPlayerAlive(i))
continue;
if(FilterFlags & COMMAND_FILTER_DEAD && IsPlayerAlive(i))
continue;
// insert player names into g_PlayerNames array
GetClientName(i, g_PlayerNames[i], sizeof(g_PlayerNames[]));
if(StrContains(g_PlayerNames[i], sPattern, false) != -1)
aClients[Players++] = i;
}
// sort aClients array by player name
SortCustom1D(aClients, Players, SortByPlayerName);
// insert players sorted
char sUserId[12];
char sDisp[MAX_NAME_LENGTH + 16];
for(int i = 0; i < Players; i++)
{
IntToString(GetClientUserId(aClients[i]), sUserId, sizeof(sUserId));
FormatEx(sDisp, sizeof(sDisp), "%s (%s)", g_PlayerNames[aClients[i]], sUserId);
menu.AddItem(sUserId, sDisp);
}
DataPack pack = new DataPack();
pack.WriteString(sCommand);
pack.WriteString(sArgString);
pack.WriteString(sPattern);
pack.WriteCell(FilterFlags);
if(g_PlayerData[client] != INVALID_HANDLE)
{
CloseHandle(g_PlayerData[client]);
g_PlayerData[client] = INVALID_HANDLE;
}
CancelClientMenu(client);
g_PlayerData[client] = pack;
menu.Display(client, MENU_TIME_FOREVER);
return 0;
}
public int MenuHandler_AmbiguousMenu(Menu menu, MenuAction action, int param1, int param2)
{
switch(action)
{
case MenuAction_End:
{
CloseHandle(menu);
}
case MenuAction_Cancel:
{
if(g_PlayerData[param1] != INVALID_HANDLE)
{
CloseHandle(g_PlayerData[param1]);
g_PlayerData[param1] = INVALID_HANDLE;
}
}
case MenuAction_Select:
{
int Style;
char sItem[32];
char sDisp[MAX_NAME_LENGTH + 16];
menu.GetItem(param2, sItem, sizeof(sItem), Style, sDisp, sizeof(sDisp));
int UserId = StringToInt(sItem);
int client = GetClientOfUserId(UserId);
if(!client)
{
PrintToChat(param1, "\x04[DynamicTargeting]\x01 Player no longer available.");
menu.DisplayAt(param1, GetMenuSelectionPosition(), MENU_TIME_FOREVER);
return 0;
}
DataPack pack = view_as<DataPack>(g_PlayerData[param1]);
pack.Reset();
char sCommand[128];
pack.ReadString(sCommand, sizeof(sCommand));
char sArgString[256];
pack.ReadString(sArgString, sizeof(sArgString));
char sPattern[MAX_TARGET_LENGTH];
pack.ReadString(sPattern, sizeof(sPattern));
int Result = ReCallAmbiguous(param1, client, sCommand, sArgString, sPattern);
return Result;
}
case MenuAction_DrawItem:
{
int Style;
char sItem[32];
menu.GetItem(param2, sItem, sizeof(sItem), Style);
int UserId = StringToInt(sItem);
int client = GetClientOfUserId(UserId);
if(!client) // Player disconnected
return ITEMDRAW_DISABLED;
return Style;
}
case MenuAction_DisplayItem:
{
int Style;
char sItem[32];
char sDisp[MAX_NAME_LENGTH + 16];
menu.GetItem(param2, sItem, sizeof(sItem), Style, sDisp, sizeof(sDisp));
if(!sItem[0])
return 0;
char sBuffer[MAX_NAME_LENGTH + 16];
int UserId = StringToInt(sItem);
int client = GetClientOfUserId(UserId);
if(!client) // Player disconnected
return 0;
GetClientName(client, g_PlayerNames[client], sizeof(g_PlayerNames[]));
FormatEx(sBuffer, sizeof(sBuffer), "%s (%d)", g_PlayerNames[client], UserId);
if(!StrEqual(sDisp, sBuffer))
return RedrawMenuItem(sBuffer);
return 0;
}
}
return 0;
}
int ReCallAmbiguous(int client, int newClient, const char[] sCommand, const char[] sArgString, const char[] sPattern)
{
char sTarget[16];
FormatEx(sTarget, sizeof(sTarget), "#%d", GetClientUserId(newClient));
char sNewArgString[256];
strcopy(sNewArgString, sizeof(sNewArgString), sArgString);
char sPart[256];
int CurrentIndex = 0;
int NextIndex = 0;
while(NextIndex != -1 && CurrentIndex < sizeof(sNewArgString))
{
NextIndex = BreakString(sNewArgString[CurrentIndex], sPart, sizeof(sPart));
if(StrEqual(sPart, sPattern))
{
ReplaceStringEx(sNewArgString[CurrentIndex], sizeof(sNewArgString) - CurrentIndex, sPart, sTarget);
break;
}
CurrentIndex += NextIndex;
}
FakeClientCommandEx(client, "%s %s", sCommand, sNewArgString);
return 0;
}
public int Native_AmbiguousMenu(Handle plugin, int numParams)
{
int client = GetNativeCell(1);
if(client > MaxClients || client <= 0)
{
ThrowNativeError(SP_ERROR_NATIVE, "Client is not valid.");
return -1;
}
if(!IsClientInGame(client))
{
ThrowNativeError(SP_ERROR_NATIVE, "Client is not in-game.");
return -1;
}
if(IsFakeClient(client))
{
ThrowNativeError(SP_ERROR_NATIVE, "Client is fake-client.");
return -1;
}
char sCommand[128];
GetNativeString(2, sCommand, sizeof(sCommand));
char sArgString[256];
GetNativeString(3, sArgString, sizeof(sArgString));
char sPattern[MAX_TARGET_LENGTH];
GetNativeString(4, sPattern, sizeof(sPattern));
int FilterFlags = GetNativeCell(5);
return CreateAmbiguousMenu(client, sCommand, sArgString, sPattern, FilterFlags);
}
public int SortByPlayerName(int elem1, int elem2, const int[] array, Handle hndl)
{
return strcmp(g_PlayerNames[elem1], g_PlayerNames[elem2], false);
}
+24
View File
@@ -0,0 +1,24 @@
#if defined _DynamicTargeting_Included
#endinput
#endif
#define _DynamicTargeting_Included
native int AmbiguousMenu(int client, char[] sCommand, char[] sArgString, char[] sPattern, int FilterFlags);
public SharedPlugin __pl_DynamicTargeting =
{
name = "DynamicTargeting",
file = "DynamicTargeting.smx",
#if defined REQUIRE_PLUGIN
required = 1,
#else
required = 0,
#endif
};
#if !defined REQUIRE_PLUGIN
public __pl_DynamicTargeting_SetNTVOptional()
{
MarkNativeAsOptional("AmbiguousMenu");
}
#endif
+48 -1
View File
@@ -84,6 +84,25 @@ native int ProcessTargetString(const char[] pattern,
int tn_maxlength,
bool &tn_is_ml);
/**
* Retrieves arguments that were passed to the last ProcessTargetString call.
*
* @param pattern Buffer to store the pattern.
* @param p_maxlen Maximum length of the pattern buffer.
* @param admin OUTPUT: Admin performing the action, or 0 if the server.
* @param filter_flags OUTPUT: Filter flags.
* @noreturn
*/
native void GetLastProcessTargetString(char[] pattern,
int p_maxlen,
int &admin,
int &filter_flags);
#undef REQUIRE_PLUGIN
#include <DynamicTargeting>
#define REQUIRE_PLUGIN
/**
* Replies to a client with a given message describing a targetting
* failure reason.
@@ -93,7 +112,7 @@ native int ProcessTargetString(const char[] pattern,
* @param client Client index, or 0 for server.
* @param reason COMMAND_TARGET reason.
*/
stock void ReplyToTargetError(int client, int reason)
stock void ReplyToTargetError(int client, int reason, bool dynamic=true)
{
switch (reason)
{
@@ -128,6 +147,34 @@ stock void ReplyToTargetError(int client, int reason)
case COMMAND_TARGET_AMBIGUOUS:
{
ReplyToCommand(client, "[SM] %t", "More than one client matched");
if(dynamic &&
GetFeatureStatus(FeatureType_Native, "GetLastProcessTargetString") == FeatureStatus_Available &&
LibraryExists("DynamicTargeting"))
{
if(GetFeatureStatus(FeatureType_Native, "IsCommandCallback") == FeatureStatus_Available &&
!IsCommandCallback())
{
return;
}
char sCommand[128];
GetCmdArg(0, sCommand, sizeof(sCommand));
char sArgString[256];
GetCmdArgString(sArgString, sizeof(sArgString));
char pattern[MAX_TARGET_LENGTH];
int admin;
int filter_flags;
GetLastProcessTargetString(pattern, sizeof(pattern), admin, filter_flags);
if(!admin || !IsClientInGame(admin) || IsFakeClient(admin))
return;
AmbiguousMenu(admin, sCommand, sArgString, pattern, filter_flags);
}
}
}
}
+8 -1
View File
@@ -402,7 +402,14 @@ native void RegAdminCmd(const char[] cmd,
const char[] description="",
const char[] group="",
int flags=0);
/**
* Returns whether there is a command callback available.
*
* @return True if called from inside a command callback.
*/
native bool IsCommandCallback();
/**
* Returns the number of arguments from the current console or server command.
* @note Unlike the HL2 engine call, this does not include the command itself.
+3
View File
@@ -311,6 +311,9 @@ public void __ext_core_SetNTVOptional()
MarkNativeAsOptional("Protobuf.ReadRepeatedMessage");
MarkNativeAsOptional("Protobuf.AddMessage");
MarkNativeAsOptional("IsCommandCallback");
MarkNativeAsOptional("GetLastProcessTargetString");
VerifyCoreVersion();
}
+2 -37
View File
@@ -307,23 +307,9 @@ methodmap Menu < Handle
// @param style By-reference variable to store drawing flags.
// @param dispBuf Display buffer.
// @param dispBufLen Maximum length of the display buffer.
// @param client Client index. Must be specified if menu is per-client random shuffled, -1 to ignore.
// @return True on success, false if position is invalid.
public native bool GetItem(int position, char[] infoBuf, int infoBufLen,
int &style=0, char[] dispBuf="", int dispBufLen=0, int client=0);
// Generates a per-client random mapping for the current vote options.
//
// @param start Menu item index to start randomizing from.
// @param stop Menu item index to stop randomizing at. -1 = infinite
public native void ShufflePerClient(int start=0, int stop=-1);
// Fills the client vote option mapping with user supplied values.
//
// @param client Client index.
// @param array Integer array with mapping.
// @param length Length of array.
public native void SetClientMapping(int client, int[] array, int length);
int &style=0, char[] dispBuf="", int dispBufLen=0);
// Sets the menu's default title/instruction message.
//
@@ -551,7 +537,6 @@ native void RemoveAllMenuItems(Handle menu);
* @param style By-reference variable to store drawing flags.
* @param dispBuf Display buffer.
* @param dispBufLen Maximum length of the display buffer.
* @param client Client index. Must be specified if menu is per-client random shuffled, -1 to ignore.
* @return True on success, false if position is invalid.
* @error Invalid Handle.
*/
@@ -561,27 +546,7 @@ native bool GetMenuItem(Handle menu,
int infoBufLen,
int &style=0,
char[] dispBuf="",
int dispBufLen=0,
int client=0);
/**
* Generates a per-client random mapping for the current vote options.
*
* @param menu Menu Handle.
* @param start Menu item index to start randomizing from.
* @param stop Menu item index to stop randomizing at. -1 = infinite
*/
native void MenuShufflePerClient(Handle menu, int start=0, int stop=-1);
/*
* Fills the client vote option mapping with user supplied values.
*
* @param menu Menu Handle.
* @param client Client index.
* @param array Integer array with mapping.
* @param length Length of array.
*/
native void MenuSetClientMapping(Handle menu, int client, int[] array, int length);
int dispBufLen=0);
/**
* Returns the first item on the page of a currently selected menu.
+86 -85
View File
@@ -47,6 +47,92 @@ struct Plugin
public const char[] url; /**< Plugin URL */
};
/**
* Returns whether a library exists. This function should be considered
* expensive; it should only be called on plugin to determine availability
* of resources. Use OnLibraryAdded()/OnLibraryRemoved() to detect changes
* in optional resources.
*
* @param name Library name of a plugin or extension.
* @return True if exists, false otherwise.
*/
native bool LibraryExists(const char[] name);
/**
* Feature types.
*/
enum FeatureType
{
/**
* A native function call.
*/
FeatureType_Native,
/**
* A named capability. This is distinctly different from checking for a
* native, because the underlying functionality could be enabled on-demand
* to improve loading time. Thus a native may appear to exist, but it might
* be part of a set of features that are not compatible with the current game
* or version of SourceMod.
*/
FeatureType_Capability
};
/**
* Feature statuses.
*/
enum FeatureStatus
{
/**
* Feature is available for use.
*/
FeatureStatus_Available,
/**
* Feature is not available.
*/
FeatureStatus_Unavailable,
/**
* Feature is not known at all.
*/
FeatureStatus_Unknown
};
/**
* Returns whether "GetFeatureStatus" will work. Using this native
* or this function will not cause SourceMod to fail loading on older versions,
* however, GetFeatureStatus will only work if this function returns true.
*
* @return True if GetFeatureStatus will work, false otherwise.
*/
stock bool CanTestFeatures()
{
return LibraryExists("__CanTestFeatures__");
}
/**
* Returns whether a feature exists, and if so, whether it is usable.
*
* @param type Feature type.
* @param name Feature name.
* @return Feature status.
*/
native FeatureStatus GetFeatureStatus(FeatureType type, const char[] name);
/**
* Requires that a given feature is available. If it is not, SetFailState()
* is called with the given message.
*
* @param type Feature type.
* @param name Feature name.
* @param fmt Message format string, or empty to use default.
* @param ... Message format parameters, if any.
*/
native void RequireFeature(FeatureType type, const char[] name,
const char[] fmt="", any ...);
#include <core>
#include <float>
#include <vector>
@@ -448,17 +534,6 @@ native void AutoExecConfig(bool autoCreate=true, const char[] name="", const cha
*/
native void RegPluginLibrary(const char[] name);
/**
* Returns whether a library exists. This function should be considered
* expensive; it should only be called on plugin to determine availability
* of resources. Use OnLibraryAdded()/OnLibraryRemoved() to detect changes
* in optional resources.
*
* @param name Library name of a plugin or extension.
* @return True if exists, false otherwise.
*/
native bool LibraryExists(const char[] name);
/**
* Returns the status of an extension, by filename.
*
@@ -582,80 +657,6 @@ forward bool OnClientFloodCheck(int client);
*/
forward void OnClientFloodResult(int client, bool blocked);
/**
* Feature types.
*/
enum FeatureType
{
/**
* A native function call.
*/
FeatureType_Native,
/**
* A named capability. This is distinctly different from checking for a
* native, because the underlying functionality could be enabled on-demand
* to improve loading time. Thus a native may appear to exist, but it might
* be part of a set of features that are not compatible with the current game
* or version of SourceMod.
*/
FeatureType_Capability
};
/**
* Feature statuses.
*/
enum FeatureStatus
{
/**
* Feature is available for use.
*/
FeatureStatus_Available,
/**
* Feature is not available.
*/
FeatureStatus_Unavailable,
/**
* Feature is not known at all.
*/
FeatureStatus_Unknown
};
/**
* Returns whether "GetFeatureStatus" will work. Using this native
* or this function will not cause SourceMod to fail loading on older versions,
* however, GetFeatureStatus will only work if this function returns true.
*
* @return True if GetFeatureStatus will work, false otherwise.
*/
stock bool CanTestFeatures()
{
return LibraryExists("__CanTestFeatures__");
}
/**
* Returns whether a feature exists, and if so, whether it is usable.
*
* @param type Feature type.
* @param name Feature name.
* @return Feature status.
*/
native FeatureStatus GetFeatureStatus(FeatureType type, const char[] name);
/**
* Requires that a given feature is available. If it is not, SetFailState()
* is called with the given message.
*
* @param type Feature type.
* @param name Feature name.
* @param fmt Message format string, or empty to use default.
* @param ... Message format parameters, if any.
*/
native void RequireFeature(FeatureType type, const char[] name,
const char[] fmt="", any ...);
/**
* Represents how many bytes we can read from an address with one load
*/
+2 -32
View File
@@ -36,7 +36,7 @@
#include <IHandleSys.h>
#define SMINTERFACE_MENUMANAGER_NAME "IMenuManager"
#define SMINTERFACE_MENUMANAGER_VERSION 17
#define SMINTERFACE_MENUMANAGER_VERSION 16
/**
* @file IMenuManager.h
@@ -485,10 +485,9 @@ namespace SourceMod
*
* @param position Position, starting from 0.
* @param draw Optional pointer to store a draw information.
* @param client Client index. (Important for randomized menus.)
* @return Info string pointer, or NULL if position was invalid.
*/
virtual const char *GetItemInfo(unsigned int position, ItemDrawInfo *draw, int client=0) =0;
virtual const char *GetItemInfo(unsigned int position, ItemDrawInfo *draw) =0;
/**
* @brief Returns the number of items.
@@ -637,35 +636,6 @@ namespace SourceMod
* @return Approximate number of bytes being used.
*/
virtual unsigned int GetApproxMemUsage() =0;
/**
* @brief Generates a per-client random mapping for the current vote options.
* @param start Menu item index to start randomizing from.
* @param stop Menu item index to stop randomizing at. -1 = infinite
*/
virtual void ShufflePerClient(int start=0, int stop=-1) =0;
/**
* @brief Fills the client vote option mapping with user supplied values.
* @param client Client index.
* @param array Integer array with mapping.
* @param length Length of array.
*/
virtual void SetClientMapping(int client, int *array, int length) =0;
/**
* @brief Returns true if the menu has a per-client random mapping.
* @return True on success, false otherwise.
*/
virtual bool IsPerClientShuffled() =0;
/**
* @brief Returns the actual (not shuffled) item index from the client position.
* @param client Client index.
* @param position Position, starting from 0.
* @return Actual item index in menu.
*/
virtual unsigned int GetRealItemIndex(int client, unsigned int position) =0;
};
/**
+2
View File
@@ -324,6 +324,7 @@ CopyFiles('plugins', 'addons/sourcemod/scripting',
'rockthevote.sp',
'sounds.sp',
'sql-admin-manager.sp',
'DynamicTargeting.sp',
]
)
CopyFiles('plugins/include', 'addons/sourcemod/scripting/include',
@@ -394,6 +395,7 @@ CopyFiles('plugins/include', 'addons/sourcemod/scripting/include',
'usermessages.inc',
'vector.inc',
'version.inc',
'DynamicTargeting.inc',
]
)
CopyFiles('translations', 'addons/sourcemod/translations',