Compare commits
20
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f3a5c8341d | ||
|
|
ee0fc2230f | ||
|
|
b024ad05bf | ||
|
|
ab218278f7 | ||
|
|
026678d182 | ||
|
|
291b83ed74 | ||
|
|
5d70bb0122 | ||
|
|
eb61b40eaf | ||
|
|
bb87127d92 | ||
|
|
105d41c88d | ||
|
|
9a3cad3b9d | ||
|
|
c4c4cf0085 | ||
|
|
6b3e661b2b | ||
|
|
eda65bb5bb | ||
|
|
4cad93b505 | ||
|
|
98f6ef8173 | ||
|
|
9492337474 | ||
|
|
a51aa290d6 | ||
|
|
f1ca3e8c7f | ||
|
|
68e5c00f48 |
@@ -30,6 +30,7 @@
|
||||
*/
|
||||
|
||||
#include "PlayerManager.h"
|
||||
#include "sourcemod.h"
|
||||
#include "IAdminSystem.h"
|
||||
#include "ConCmdManager.h"
|
||||
#include "MenuStyle_Valve.h"
|
||||
@@ -92,6 +93,12 @@ SH_DECL_EXTERN1_void(ConCommand, Dispatch, SH_NOATTRIB, false, const CCommand &)
|
||||
#else
|
||||
SH_DECL_EXTERN0_void(ConCommand, Dispatch, SH_NOATTRIB, false);
|
||||
#endif
|
||||
SH_DECL_HOOK2_void(IVEngineServer, ClientPrintf, SH_NOATTRIB, 0, edict_t *, const char *);
|
||||
|
||||
static void PrintfBuffer_FrameAction(void *data)
|
||||
{
|
||||
g_Players.OnPrintfFrameAction(reinterpret_cast<unsigned int>(data));
|
||||
}
|
||||
|
||||
ConCommand *maxplayersCmd = NULL;
|
||||
|
||||
@@ -172,6 +179,7 @@ void PlayerManager::OnSourceModAllInitialized()
|
||||
#elif SOURCE_ENGINE > SE_EYE // 2013/orangebox, but not original orangebox.
|
||||
SH_ADD_HOOK(IServerGameDLL, SetServerHibernation, gamedll, SH_MEMBER(this, &PlayerManager::OnServerHibernationUpdate), true);
|
||||
#endif
|
||||
SH_ADD_HOOK(IVEngineServer, ClientPrintf, engine, SH_MEMBER(this, &PlayerManager::OnClientPrintf), false);
|
||||
|
||||
sharesys->AddInterface(NULL, this);
|
||||
|
||||
@@ -225,6 +233,7 @@ void PlayerManager::OnSourceModShutdown()
|
||||
#elif SOURCE_ENGINE > SE_EYE // 2013/orangebox, but not original orangebox.
|
||||
SH_REMOVE_HOOK(IServerGameDLL, SetServerHibernation, gamedll, SH_MEMBER(this, &PlayerManager::OnServerHibernationUpdate), true);
|
||||
#endif
|
||||
SH_REMOVE_HOOK(IVEngineServer, ClientPrintf, engine, SH_MEMBER(this, &PlayerManager::OnClientPrintf), false);
|
||||
|
||||
/* Release forwards */
|
||||
forwardsys->ReleaseForward(m_clconnect);
|
||||
@@ -846,6 +855,88 @@ void PlayerManager::OnClientDisconnect_Post(edict_t *pEntity)
|
||||
}
|
||||
}
|
||||
|
||||
void PlayerManager::OnClientPrintf(edict_t *pEdict, const char *szMsg)
|
||||
{
|
||||
int client = IndexOfEdict(pEdict);
|
||||
|
||||
CPlayer &player = m_Players[client];
|
||||
if (!player.IsConnected())
|
||||
RETURN_META(MRES_IGNORED);
|
||||
|
||||
INetChannel *pNetChan = static_cast<INetChannel *>(engine->GetPlayerNetInfo(client));
|
||||
if (pNetChan == NULL)
|
||||
RETURN_META(MRES_IGNORED);
|
||||
|
||||
size_t nMsgLen = strlen(szMsg);
|
||||
#if SOURCE_ENGINE == SE_EPISODEONE
|
||||
static const int nNumBitsWritten = 0;
|
||||
#else
|
||||
int nNumBitsWritten = pNetChan->GetNumBitsWritten(false); // SVC_Print uses unreliable netchan
|
||||
#endif
|
||||
|
||||
// if the msg is bigger than allowed then just let it fail
|
||||
if (nMsgLen + 1 >= SVC_Print_BufferSize) // +1 for NETMSG_TYPE_BITS
|
||||
RETURN_META(MRES_IGNORED);
|
||||
|
||||
// enqueue msgs if we'd overflow the SVC_Print buffer (+7 as ceil)
|
||||
if (!player.m_PrintfBuffer.empty() || (nNumBitsWritten + NETMSG_TYPE_BITS + 7) / 8 + nMsgLen >= SVC_Print_BufferSize)
|
||||
{
|
||||
// Don't send any more messages for this player until the buffer is empty.
|
||||
// Queue up a gameframe hook to empty the buffer (if we haven't already)
|
||||
if (player.m_PrintfBuffer.empty())
|
||||
g_SourceMod.AddFrameAction(PrintfBuffer_FrameAction, (void *)(uintptr_t)player.GetSerial());
|
||||
|
||||
player.m_PrintfBuffer.append(szMsg);
|
||||
|
||||
RETURN_META(MRES_SUPERCEDE);
|
||||
}
|
||||
|
||||
RETURN_META(MRES_IGNORED);
|
||||
}
|
||||
|
||||
void PlayerManager::OnPrintfFrameAction(unsigned int serial)
|
||||
{
|
||||
int client = GetClientFromSerial(serial);
|
||||
CPlayer &player = m_Players[client];
|
||||
if (!player.IsConnected())
|
||||
{
|
||||
player.ClearNetchannelQueue();
|
||||
return;
|
||||
}
|
||||
|
||||
INetChannel *pNetChan = static_cast<INetChannel *>(engine->GetPlayerNetInfo(client));
|
||||
if (pNetChan == NULL)
|
||||
{
|
||||
player.ClearNetchannelQueue();
|
||||
return;
|
||||
}
|
||||
|
||||
while (!player.m_PrintfBuffer.empty())
|
||||
{
|
||||
#if SOURCE_ENGINE == SE_EPISODEONE
|
||||
static const int nNumBitsWritten = 0;
|
||||
#else
|
||||
int nNumBitsWritten = pNetChan->GetNumBitsWritten(false); // SVC_Print uses unreliable netchan
|
||||
#endif
|
||||
|
||||
ke::AString &string = player.m_PrintfBuffer.front();
|
||||
|
||||
// stop if we'd overflow the SVC_Print buffer (+7 as ceil)
|
||||
if ((nNumBitsWritten + NETMSG_TYPE_BITS + 7) / 8 + string.length() >= SVC_Print_BufferSize)
|
||||
break;
|
||||
|
||||
SH_CALL(engine, &IVEngineServer::ClientPrintf)(player.m_pEdict, string.chars());
|
||||
|
||||
player.m_PrintfBuffer.popFront();
|
||||
}
|
||||
|
||||
if (!player.m_PrintfBuffer.empty())
|
||||
{
|
||||
// continue processing it on the next gameframe as buffer is not empty
|
||||
g_SourceMod.AddFrameAction(PrintfBuffer_FrameAction, (void *)(uintptr_t)player.GetSerial());
|
||||
}
|
||||
}
|
||||
|
||||
void ClientConsolePrint(edict_t *e, const char *fmt, ...)
|
||||
{
|
||||
char buffer[512];
|
||||
@@ -2145,6 +2236,13 @@ void CPlayer::Disconnect()
|
||||
#if SOURCE_ENGINE == SE_CSGO
|
||||
m_LanguageCookie = InvalidQueryCvarCookie;
|
||||
#endif
|
||||
ClearNetchannelQueue();
|
||||
}
|
||||
|
||||
void CPlayer::ClearNetchannelQueue(void)
|
||||
{
|
||||
while (!m_PrintfBuffer.empty())
|
||||
m_PrintfBuffer.popFront();
|
||||
}
|
||||
|
||||
void CPlayer::SetName(const char *name)
|
||||
|
||||
@@ -43,6 +43,7 @@
|
||||
#include <sh_list.h>
|
||||
#include <sh_vector.h>
|
||||
#include <am-string.h>
|
||||
#include <am-deque.h>
|
||||
#include "ConVarManager.h"
|
||||
|
||||
#include <steam/steamclientpublic.h>
|
||||
@@ -123,6 +124,7 @@ private:
|
||||
bool IsAuthStringValidated();
|
||||
bool SetEngineString();
|
||||
bool SetCSteamID();
|
||||
void ClearNetchannelQueue(void);
|
||||
private:
|
||||
bool m_IsConnected = false;
|
||||
bool m_IsInGame = false;
|
||||
@@ -152,6 +154,7 @@ private:
|
||||
#if SOURCE_ENGINE == SE_CSGO
|
||||
QueryCvarCookie_t m_LanguageCookie = InvalidQueryCvarCookie;
|
||||
#endif
|
||||
ke::Deque<ke::AString> m_PrintfBuffer;
|
||||
};
|
||||
|
||||
class PlayerManager :
|
||||
@@ -190,6 +193,8 @@ public:
|
||||
void OnClientSettingsChanged(edict_t *pEntity);
|
||||
//void OnClientSettingsChanged_Pre(edict_t *pEntity);
|
||||
void OnServerHibernationUpdate(bool bHibernating);
|
||||
void OnClientPrintf(edict_t *pEdict, const char *szMsg);
|
||||
void OnPrintfFrameAction(unsigned int serial);
|
||||
public: //IPlayerManager
|
||||
void AddClientListener(IClientListener *listener);
|
||||
void RemoveClientListener(IClientListener *listener);
|
||||
@@ -267,6 +272,9 @@ private:
|
||||
int m_SourceTVUserId;
|
||||
int m_ReplayUserId;
|
||||
bool m_bInCCKVHook;
|
||||
private:
|
||||
static const int NETMSG_TYPE_BITS = 5; // SVC_Print overhead for netmsg type
|
||||
static const int SVC_Print_BufferSize = 2048 - 1; // -1 for terminating \0
|
||||
};
|
||||
|
||||
#if SOURCE_ENGINE >= SE_ORANGEBOX
|
||||
|
||||
+14
-28
@@ -1458,23 +1458,24 @@ 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)
|
||||
{
|
||||
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];
|
||||
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);
|
||||
|
||||
playerhelpers->ProcessCommandTarget(&g_ProcessTargetString_info);
|
||||
playerhelpers->ProcessCommandTarget(&info);
|
||||
|
||||
if (g_ProcessTargetString_info.target_name_style == COMMAND_TARGETNAME_ML)
|
||||
if (info.target_name_style == COMMAND_TARGETNAME_ML)
|
||||
{
|
||||
*tn_is_ml = 1;
|
||||
}
|
||||
@@ -1483,30 +1484,16 @@ static cell_t ProcessTargetString(IPluginContext *pContext, const cell_t *params
|
||||
*tn_is_ml = 0;
|
||||
}
|
||||
|
||||
if (g_ProcessTargetString_info.num_targets == 0)
|
||||
if (info.num_targets == 0)
|
||||
{
|
||||
return g_ProcessTargetString_info.reason;
|
||||
return info.reason;
|
||||
}
|
||||
else
|
||||
{
|
||||
return g_ProcessTargetString_info.num_targets;
|
||||
return 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;
|
||||
@@ -1658,7 +1645,6 @@ REGISTER_NATIVES(playernatives)
|
||||
{ "NotifyPostAdminCheck", NotifyPostAdminCheck },
|
||||
{ "IsClientInKickQueue", IsClientInKickQueue },
|
||||
{ "ProcessTargetString", ProcessTargetString },
|
||||
{ "GetLastProcessTargetString", GetLastProcessTargetString },
|
||||
{ "FormatActivitySource", FormatActivitySource },
|
||||
{ "GetClientSerial", sm_GetClientSerial },
|
||||
{ "GetClientFromSerial", sm_GetClientFromSerial },
|
||||
|
||||
@@ -789,16 +789,6 @@ 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();
|
||||
@@ -1477,7 +1467,6 @@ REGISTER_NATIVES(consoleNatives)
|
||||
{"GetConVarDefault", GetConVarDefault},
|
||||
{"RegServerCmd", sm_RegServerCmd},
|
||||
{"RegConsoleCmd", sm_RegConsoleCmd},
|
||||
{"IsCommandCallback", sm_IsCommandCallback},
|
||||
{"GetCmdArgString", sm_GetCmdArgString},
|
||||
{"GetCmdArgs", sm_GetCmdArgs},
|
||||
{"GetCmdArg", sm_GetCmdArg},
|
||||
|
||||
+1
-2
@@ -24,8 +24,7 @@ files = [
|
||||
'basecommands.sp',
|
||||
'mapchooser.sp',
|
||||
'randomcycle.sp',
|
||||
'sql-admin-manager.sp',
|
||||
'DynamicTargeting.sp'
|
||||
'sql-admin-manager.sp'
|
||||
]
|
||||
|
||||
spcomp_argv = [
|
||||
|
||||
@@ -1,266 +0,0 @@
|
||||
#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);
|
||||
}
|
||||
@@ -1,24 +0,0 @@
|
||||
#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
|
||||
@@ -84,25 +84,6 @@ 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.
|
||||
@@ -112,7 +93,7 @@ native void GetLastProcessTargetString(char[] pattern,
|
||||
* @param client Client index, or 0 for server.
|
||||
* @param reason COMMAND_TARGET reason.
|
||||
*/
|
||||
stock void ReplyToTargetError(int client, int reason, bool dynamic=true)
|
||||
stock void ReplyToTargetError(int client, int reason)
|
||||
{
|
||||
switch (reason)
|
||||
{
|
||||
@@ -147,34 +128,6 @@ stock void ReplyToTargetError(int client, int reason, bool dynamic=true)
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -402,14 +402,7 @@ 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.
|
||||
|
||||
@@ -311,9 +311,6 @@ public void __ext_core_SetNTVOptional()
|
||||
MarkNativeAsOptional("Protobuf.ReadRepeatedMessage");
|
||||
MarkNativeAsOptional("Protobuf.AddMessage");
|
||||
|
||||
MarkNativeAsOptional("IsCommandCallback");
|
||||
MarkNativeAsOptional("GetLastProcessTargetString");
|
||||
|
||||
VerifyCoreVersion();
|
||||
}
|
||||
|
||||
|
||||
@@ -47,92 +47,6 @@ 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>
|
||||
@@ -534,6 +448,17 @@ 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.
|
||||
*
|
||||
@@ -657,6 +582,80 @@ 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
|
||||
*/
|
||||
|
||||
@@ -324,7 +324,6 @@ CopyFiles('plugins', 'addons/sourcemod/scripting',
|
||||
'rockthevote.sp',
|
||||
'sounds.sp',
|
||||
'sql-admin-manager.sp',
|
||||
'DynamicTargeting.sp',
|
||||
]
|
||||
)
|
||||
CopyFiles('plugins/include', 'addons/sourcemod/scripting/include',
|
||||
@@ -395,7 +394,6 @@ CopyFiles('plugins/include', 'addons/sourcemod/scripting/include',
|
||||
'usermessages.inc',
|
||||
'vector.inc',
|
||||
'version.inc',
|
||||
'DynamicTargeting.inc',
|
||||
]
|
||||
)
|
||||
CopyFiles('translations', 'addons/sourcemod/translations',
|
||||
|
||||
Reference in New Issue
Block a user