- added experimental (so far) support for SourceMod chat triggers
- added ReplyToCommand() for replying to triggers nicely --HG-- extra : convert_revision : svn%3A39bc706e-5318-0410-9160-8a85361fbb7c/trunk%40899
This commit is contained in:
parent
bd15f93d7d
commit
c843825e71
232
core/ChatTriggers.cpp
Normal file
232
core/ChatTriggers.cpp
Normal file
@ -0,0 +1,232 @@
|
|||||||
|
#include "ChatTriggers.h"
|
||||||
|
#include "sm_stringutil.h"
|
||||||
|
#include "TextParsers.h"
|
||||||
|
#include "ConCmdManager.h"
|
||||||
|
|
||||||
|
/* :HACKHACK: We can't SH_DECL here because ConCmdManager.cpp does */
|
||||||
|
extern bool __SourceHook_FHRemoveConCommandDispatch(void *, bool, class fastdelegate::FastDelegate0<void>);
|
||||||
|
extern int __SourceHook_FHAddConCommandDispatch(void *, bool, class fastdelegate::FastDelegate0<void>);
|
||||||
|
|
||||||
|
ChatTriggers g_ChatTriggers;
|
||||||
|
|
||||||
|
ChatTriggers::ChatTriggers() : m_pSayCmd(NULL), m_bWillProcessInPost(false),
|
||||||
|
m_ReplyTo(SM_REPLY_CONSOLE)
|
||||||
|
{
|
||||||
|
m_PubTrigger = sm_strdup("!");
|
||||||
|
m_PrivTrigger = sm_strdup("/");
|
||||||
|
m_PubTriggerSize = 1;
|
||||||
|
m_PrivTriggerSize = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ChatTriggers::OnSourceModGameInitialized()
|
||||||
|
{
|
||||||
|
ConCommandBase *pCmd = icvar->GetCommands();
|
||||||
|
const char *name;
|
||||||
|
while (pCmd)
|
||||||
|
{
|
||||||
|
if (pCmd->IsCommand())
|
||||||
|
{
|
||||||
|
name = pCmd->GetName();
|
||||||
|
if (strcmp(name, "say") == 0)
|
||||||
|
{
|
||||||
|
m_pSayCmd = (ConCommand *)pCmd;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pCmd = const_cast<ConCommandBase *>(pCmd->GetNext());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (m_pSayCmd)
|
||||||
|
{
|
||||||
|
SH_ADD_HOOK_MEMFUNC(ConCommand, Dispatch, m_pSayCmd, this, &ChatTriggers::OnSayCommand_Pre, false);
|
||||||
|
SH_ADD_HOOK_MEMFUNC(ConCommand, Dispatch, m_pSayCmd, this, &ChatTriggers::OnSayCommand_Post, true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ChatTriggers::OnSourceModShutdown()
|
||||||
|
{
|
||||||
|
if (m_pSayCmd)
|
||||||
|
{
|
||||||
|
SH_REMOVE_HOOK_MEMFUNC(ConCommand, Dispatch, m_pSayCmd, this, &ChatTriggers::OnSayCommand_Post, true);
|
||||||
|
SH_REMOVE_HOOK_MEMFUNC(ConCommand, Dispatch, m_pSayCmd, this, &ChatTriggers::OnSayCommand_Pre, false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ChatTriggers::OnSayCommand_Pre()
|
||||||
|
{
|
||||||
|
int client = g_ConCmds.GetCommandClient();
|
||||||
|
|
||||||
|
/* The server console cannot do this */
|
||||||
|
if (client == 0)
|
||||||
|
{
|
||||||
|
RETURN_META(MRES_IGNORED);
|
||||||
|
}
|
||||||
|
|
||||||
|
const char *args = engine->Cmd_Args();
|
||||||
|
|
||||||
|
/* Handle quoted string sets */
|
||||||
|
bool is_quoted = false;
|
||||||
|
if (args[0] == '"')
|
||||||
|
{
|
||||||
|
args++;
|
||||||
|
is_quoted = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool is_trigger = false;
|
||||||
|
bool is_silent = false;
|
||||||
|
|
||||||
|
/* Check for either trigger */
|
||||||
|
if (m_PubTriggerSize && strncmp(args, m_PubTrigger, m_PubTriggerSize) == 0)
|
||||||
|
{
|
||||||
|
is_trigger = true;
|
||||||
|
} else if (m_PrivTriggerSize && strncmp(args, m_PrivTrigger, m_PrivTriggerSize) == 0) {
|
||||||
|
is_trigger = true;
|
||||||
|
is_silent = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!is_trigger)
|
||||||
|
{
|
||||||
|
RETURN_META(MRES_IGNORED);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* If we're a public command, process later */
|
||||||
|
if (!is_silent)
|
||||||
|
{
|
||||||
|
/* We have to process this in _post_ instead. Darn. */
|
||||||
|
m_bWillProcessInPost = true;
|
||||||
|
RETURN_META(MRES_IGNORED);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Otherwise, process now */
|
||||||
|
if (ProcessTrigger(engine->PEntityOfEntIndex(client), &args[m_PrivTriggerSize], is_quoted))
|
||||||
|
{
|
||||||
|
/* If we succeed, block the original say! */
|
||||||
|
RETURN_META(MRES_SUPERCEDE);
|
||||||
|
}
|
||||||
|
|
||||||
|
RETURN_META(MRES_IGNORED);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ChatTriggers::OnSayCommand_Post()
|
||||||
|
{
|
||||||
|
if (m_bWillProcessInPost)
|
||||||
|
{
|
||||||
|
/* Reset this for re-entrancy */
|
||||||
|
m_bWillProcessInPost = false;
|
||||||
|
|
||||||
|
/* Get our arguments */
|
||||||
|
const char *args = engine->Cmd_Args();
|
||||||
|
|
||||||
|
/* Handle quotations */
|
||||||
|
bool is_quoted = false;
|
||||||
|
if (args[0] == '"')
|
||||||
|
{
|
||||||
|
args++;
|
||||||
|
is_quoted = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
int client = g_ConCmds.GetCommandClient();
|
||||||
|
|
||||||
|
ProcessTrigger(engine->PEntityOfEntIndex(client), &args[m_PubTriggerSize], is_quoted);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ChatTriggers::ProcessTrigger(edict_t *pEdict, const char *args, bool is_quoted)
|
||||||
|
{
|
||||||
|
/* Eat up whitespace */
|
||||||
|
while (*args != '\0' && IsWhitespace(args))
|
||||||
|
{
|
||||||
|
args++;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Check if we're still valid */
|
||||||
|
if (*args == '\0')
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Extract a command. This is kind of sloppy. */
|
||||||
|
char cmd_buf[64];
|
||||||
|
size_t cmd_len = 0;
|
||||||
|
const char *inptr = args;
|
||||||
|
while (*inptr != '\0'
|
||||||
|
&& !IsWhitespace(inptr)
|
||||||
|
&& cmd_len < sizeof(cmd_buf) - 1)
|
||||||
|
{
|
||||||
|
cmd_buf[cmd_len++] = *inptr++;
|
||||||
|
}
|
||||||
|
cmd_buf[cmd_len] = '\0';
|
||||||
|
|
||||||
|
/* See if we have this registered */
|
||||||
|
bool prepended = false;
|
||||||
|
if (!g_ConCmds.LookForSourceModCommand(cmd_buf))
|
||||||
|
{
|
||||||
|
/* Check if we had an "sm_" prefix */
|
||||||
|
if (strncmp(cmd_buf, "sm_", 3) == 0)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Now, prepend. Don't worry about the buffers. This will
|
||||||
|
* work because the sizes are limited from earlier.
|
||||||
|
*/
|
||||||
|
char new_buf[80];
|
||||||
|
strcpy(new_buf, "sm_");
|
||||||
|
strncopy(&new_buf[3], cmd_buf, sizeof(new_buf)-3);
|
||||||
|
|
||||||
|
/* Recheck */
|
||||||
|
if (!g_ConCmds.LookForSourceModCommand(new_buf))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
prepended = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* See if we need to do extra string manipulation */
|
||||||
|
if (is_quoted || prepended)
|
||||||
|
{
|
||||||
|
static char buffer[300];
|
||||||
|
size_t len;
|
||||||
|
|
||||||
|
/* Check if we need to prepend sm_ */
|
||||||
|
if (prepended)
|
||||||
|
{
|
||||||
|
len = UTIL_Format(buffer, sizeof(buffer), "sm_%s", args);
|
||||||
|
} else {
|
||||||
|
len = strncopy(buffer, args, sizeof(buffer));
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Check if we need to strip a quote */
|
||||||
|
if (is_quoted)
|
||||||
|
{
|
||||||
|
if (buffer[len-1] == '"')
|
||||||
|
{
|
||||||
|
buffer[--len] = '\0';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
args = buffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Finally, execute! */
|
||||||
|
unsigned int old = SetReplyTo(SM_REPLY_CHAT);
|
||||||
|
serverpluginhelpers->ClientCommand(pEdict, args);
|
||||||
|
SetReplyTo(old);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned int ChatTriggers::SetReplyTo(unsigned int reply)
|
||||||
|
{
|
||||||
|
unsigned int old = m_ReplyTo;
|
||||||
|
|
||||||
|
m_ReplyTo = reply;
|
||||||
|
|
||||||
|
return old;
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned int ChatTriggers::GetReplyTo()
|
||||||
|
{
|
||||||
|
return m_ReplyTo;
|
||||||
|
}
|
37
core/ChatTriggers.h
Normal file
37
core/ChatTriggers.h
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
#ifndef _INCLUDE_SOURCEMOD_CHAT_TRIGGERS_H_
|
||||||
|
#define _INCLUDE_SOURCEMOD_CHAT_TRIGGERS_H_
|
||||||
|
|
||||||
|
#include "sm_globals.h"
|
||||||
|
#include "sourcemm_api.h"
|
||||||
|
|
||||||
|
#define SM_REPLY_CONSOLE 0
|
||||||
|
#define SM_REPLY_CHAT 1
|
||||||
|
|
||||||
|
class ChatTriggers : public SMGlobalClass
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
ChatTriggers();
|
||||||
|
public: //SMGlobalClass
|
||||||
|
void OnSourceModGameInitialized();
|
||||||
|
void OnSourceModShutdown();
|
||||||
|
private: //ConCommand
|
||||||
|
void OnSayCommand_Pre();
|
||||||
|
void OnSayCommand_Post();
|
||||||
|
public:
|
||||||
|
unsigned int GetReplyTo();
|
||||||
|
unsigned int SetReplyTo(unsigned int reply);
|
||||||
|
private:
|
||||||
|
bool ProcessTrigger(edict_t *pEdict, const char *args, bool is_quoted);
|
||||||
|
private:
|
||||||
|
ConCommand *m_pSayCmd;
|
||||||
|
char *m_PubTrigger;
|
||||||
|
size_t m_PubTriggerSize;
|
||||||
|
char *m_PrivTrigger;
|
||||||
|
size_t m_PrivTriggerSize;
|
||||||
|
bool m_bWillProcessInPost;
|
||||||
|
unsigned int m_ReplyTo;
|
||||||
|
};
|
||||||
|
|
||||||
|
extern ChatTriggers g_ChatTriggers;
|
||||||
|
|
||||||
|
#endif //_INCLUDE_SOURCEMOD_CHAT_TRIGGERS_H_
|
@ -58,13 +58,17 @@ void ConCmdManager::OnSourceModAllInitialized()
|
|||||||
SH_ADD_HOOK_MEMFUNC(IServerGameClients, SetCommandClient, serverClients, this, &ConCmdManager::SetCommandClient, false);
|
SH_ADD_HOOK_MEMFUNC(IServerGameClients, SetCommandClient, serverClients, this, &ConCmdManager::SetCommandClient, false);
|
||||||
|
|
||||||
ConCommandBase *pCmd = icvar->GetCommands();
|
ConCommandBase *pCmd = icvar->GetCommands();
|
||||||
|
const char *name;
|
||||||
while (pCmd)
|
while (pCmd)
|
||||||
{
|
{
|
||||||
if (pCmd->IsCommand()
|
if (pCmd->IsCommand())
|
||||||
&& strcmp(pCmd->GetName(), "exec") == 0)
|
|
||||||
{
|
{
|
||||||
m_pExecCmd = (ConCommand *)pCmd;
|
name = pCmd->GetName();
|
||||||
break;
|
if (strcmp(name, "exec") == 0)
|
||||||
|
{
|
||||||
|
m_pExecCmd = (ConCommand *)pCmd;
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
pCmd = const_cast<ConCommandBase *>(pCmd->GetNext());
|
pCmd = const_cast<ConCommandBase *>(pCmd->GetNext());
|
||||||
}
|
}
|
||||||
@ -756,6 +760,18 @@ void ConCmdManager::RemoveConCmd(ConCmdInfo *info)
|
|||||||
delete info;
|
delete info;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool ConCmdManager::LookForSourceModCommand(const char *cmd)
|
||||||
|
{
|
||||||
|
ConCmdInfo *pInfo;
|
||||||
|
|
||||||
|
if (!sm_trie_retrieve(m_pCmds, cmd, (void **)&pInfo))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return pInfo->sourceMod && (pInfo->conhooks.size() > 0);
|
||||||
|
}
|
||||||
|
|
||||||
ConCmdInfo *ConCmdManager::AddOrFindCommand(const char *name, const char *description, int flags)
|
ConCmdInfo *ConCmdManager::AddOrFindCommand(const char *name, const char *description, int flags)
|
||||||
{
|
{
|
||||||
ConCmdInfo *pInfo;
|
ConCmdInfo *pInfo;
|
||||||
|
@ -101,6 +101,7 @@ public:
|
|||||||
ResultType DispatchClientCommand(int client, ResultType type);
|
ResultType DispatchClientCommand(int client, ResultType type);
|
||||||
void UpdateAdminCmdFlags(const char *cmd, OverrideType type, FlagBits bits);
|
void UpdateAdminCmdFlags(const char *cmd, OverrideType type, FlagBits bits);
|
||||||
void NotifyExecDone(const char *file);
|
void NotifyExecDone(const char *file);
|
||||||
|
bool LookForSourceModCommand(const char *cmd);
|
||||||
private:
|
private:
|
||||||
void InternalDispatch();
|
void InternalDispatch();
|
||||||
ResultType RunAdminCommand(ConCmdInfo *pInfo, int client, int args);
|
ResultType RunAdminCommand(ConCmdInfo *pInfo, int client, int args);
|
||||||
|
@ -189,6 +189,10 @@
|
|||||||
RelativePath="..\CDataPack.cpp"
|
RelativePath="..\CDataPack.cpp"
|
||||||
>
|
>
|
||||||
</File>
|
</File>
|
||||||
|
<File
|
||||||
|
RelativePath="..\ChatTriggers.cpp"
|
||||||
|
>
|
||||||
|
</File>
|
||||||
<File
|
<File
|
||||||
RelativePath="..\ConCmdManager.cpp"
|
RelativePath="..\ConCmdManager.cpp"
|
||||||
>
|
>
|
||||||
@ -311,6 +315,10 @@
|
|||||||
RelativePath="..\CellRecipientFilter.h"
|
RelativePath="..\CellRecipientFilter.h"
|
||||||
>
|
>
|
||||||
</File>
|
</File>
|
||||||
|
<File
|
||||||
|
RelativePath="..\ChatTriggers.h"
|
||||||
|
>
|
||||||
|
</File>
|
||||||
<File
|
<File
|
||||||
RelativePath="..\ConCmdManager.h"
|
RelativePath="..\ConCmdManager.h"
|
||||||
>
|
>
|
||||||
|
@ -121,6 +121,13 @@ public:
|
|||||||
virtual void OnSourceModVSPReceived(IServerPluginCallbacks *iface)
|
virtual void OnSourceModVSPReceived(IServerPluginCallbacks *iface)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Called once all MM:S plugins are loaded.
|
||||||
|
*/
|
||||||
|
virtual void OnSourceModGameInitialized()
|
||||||
|
{
|
||||||
|
}
|
||||||
private:
|
private:
|
||||||
SMGlobalClass *m_pGlobalClassNext;
|
SMGlobalClass *m_pGlobalClassNext;
|
||||||
static SMGlobalClass *head;
|
static SMGlobalClass *head;
|
||||||
|
@ -21,6 +21,7 @@
|
|||||||
#include "PluginSys.h"
|
#include "PluginSys.h"
|
||||||
#include "sm_stringutil.h"
|
#include "sm_stringutil.h"
|
||||||
#include "PlayerManager.h"
|
#include "PlayerManager.h"
|
||||||
|
#include "ChatTriggers.h"
|
||||||
|
|
||||||
enum ConVarBounds
|
enum ConVarBounds
|
||||||
{
|
{
|
||||||
@ -28,6 +29,8 @@ enum ConVarBounds
|
|||||||
ConVarBound_Lower
|
ConVarBound_Lower
|
||||||
};
|
};
|
||||||
|
|
||||||
|
#define HUD_PRINTTALK 3
|
||||||
|
|
||||||
static cell_t sm_CreateConVar(IPluginContext *pContext, const cell_t *params)
|
static cell_t sm_CreateConVar(IPluginContext *pContext, const cell_t *params)
|
||||||
{
|
{
|
||||||
char *name, *defaultVal, *helpText;
|
char *name, *defaultVal, *helpText;
|
||||||
@ -632,6 +635,77 @@ static cell_t sm_ClientCommand(IPluginContext *pContext, const cell_t *params)
|
|||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static cell_t FakeClientCommand(IPluginContext *pContext, const cell_t *params)
|
||||||
|
{
|
||||||
|
CPlayer *pPlayer = g_Players.GetPlayerByIndex(params[1]);
|
||||||
|
|
||||||
|
if (!pPlayer)
|
||||||
|
{
|
||||||
|
return pContext->ThrowNativeError("Player %d is not a valid player", params[1]);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!pPlayer->IsConnected())
|
||||||
|
{
|
||||||
|
return pContext->ThrowNativeError("Player %d is not connected", params[1]);
|
||||||
|
}
|
||||||
|
|
||||||
|
char buffer[256];
|
||||||
|
g_SourceMod.FormatString(buffer, sizeof(buffer), pContext, params, 2);
|
||||||
|
|
||||||
|
unsigned int old = g_ChatTriggers.SetReplyTo(SM_REPLY_CONSOLE);
|
||||||
|
serverpluginhelpers->ClientCommand(pPlayer->GetEdict(), buffer);
|
||||||
|
g_ChatTriggers.SetReplyTo(old);
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static cell_t ReplyToCommand(IPluginContext *pContext, const cell_t *params)
|
||||||
|
{
|
||||||
|
/* Build the format string */
|
||||||
|
char buffer[1024];
|
||||||
|
size_t len = g_SourceMod.FormatString(buffer, sizeof(buffer)-2, pContext, params, 2);
|
||||||
|
|
||||||
|
/* If we're printing to the server, shortcut out */
|
||||||
|
if (params[1] == 0)
|
||||||
|
{
|
||||||
|
/* Print */
|
||||||
|
buffer[len++] = '\n';
|
||||||
|
buffer[len] = '\0';
|
||||||
|
META_CONPRINT(buffer);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
CPlayer *pPlayer = g_Players.GetPlayerByIndex(params[1]);
|
||||||
|
|
||||||
|
if (!pPlayer)
|
||||||
|
{
|
||||||
|
return pContext->ThrowNativeError("Client index %d is invalid", params[1]);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!pPlayer->IsConnected())
|
||||||
|
{
|
||||||
|
return pContext->ThrowNativeError("Client %d is not connected", params[1]);
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned int replyto = g_ChatTriggers.GetReplyTo();
|
||||||
|
if (replyto == SM_REPLY_CONSOLE)
|
||||||
|
{
|
||||||
|
buffer[len++] = '\n';
|
||||||
|
buffer[len] = '\0';
|
||||||
|
engine->ClientPrintf(pPlayer->GetEdict(), buffer);
|
||||||
|
} else if (replyto == SM_REPLY_CHAT) {
|
||||||
|
if (len >= 191)
|
||||||
|
{
|
||||||
|
len = 191;
|
||||||
|
}
|
||||||
|
buffer[len] = '\0';
|
||||||
|
g_HL2.TextMsg(params[1], HUD_PRINTTALK, buffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
REGISTER_NATIVES(consoleNatives)
|
REGISTER_NATIVES(consoleNatives)
|
||||||
{
|
{
|
||||||
{"CreateConVar", sm_CreateConVar},
|
{"CreateConVar", sm_CreateConVar},
|
||||||
@ -665,5 +739,7 @@ REGISTER_NATIVES(consoleNatives)
|
|||||||
{"InsertServerCommand", sm_InsertServerCommand},
|
{"InsertServerCommand", sm_InsertServerCommand},
|
||||||
{"ServerExecute", sm_ServerExecute},
|
{"ServerExecute", sm_ServerExecute},
|
||||||
{"ClientCommand", sm_ClientCommand},
|
{"ClientCommand", sm_ClientCommand},
|
||||||
|
{"FakeClientCommand", FakeClientCommand},
|
||||||
|
{"ReplyToCommand", ReplyToCommand},
|
||||||
{NULL, NULL}
|
{NULL, NULL}
|
||||||
};
|
};
|
||||||
|
@ -233,28 +233,6 @@ static cell_t IsSoundPrecached(IPluginContext *pContext, const cell_t *params)
|
|||||||
return enginesound->IsSoundPrecached(sample) ? 1 : 0;
|
return enginesound->IsSoundPrecached(sample) ? 1 : 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static cell_t FakeClientCommand(IPluginContext *pContext, const cell_t *params)
|
|
||||||
{
|
|
||||||
CPlayer *pPlayer = g_Players.GetPlayerByIndex(params[1]);
|
|
||||||
|
|
||||||
if (!pPlayer)
|
|
||||||
{
|
|
||||||
return pContext->ThrowNativeError("Player %d is not a valid player", params[1]);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!pPlayer->IsConnected())
|
|
||||||
{
|
|
||||||
return pContext->ThrowNativeError("Player %d is not connected", params[1]);
|
|
||||||
}
|
|
||||||
|
|
||||||
char buffer[256];
|
|
||||||
g_SourceMod.FormatString(buffer, sizeof(buffer), pContext, params, 2);
|
|
||||||
|
|
||||||
serverpluginhelpers->ClientCommand(pPlayer->GetEdict(), buffer);
|
|
||||||
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
static cell_t smn_CreateDialog(IPluginContext *pContext, const cell_t *params)
|
static cell_t smn_CreateDialog(IPluginContext *pContext, const cell_t *params)
|
||||||
{
|
{
|
||||||
KeyValues *pKV;
|
KeyValues *pKV;
|
||||||
@ -354,7 +332,6 @@ REGISTER_NATIVES(halflifeNatives)
|
|||||||
{"IsGenericPrecached", IsGenericPrecached},
|
{"IsGenericPrecached", IsGenericPrecached},
|
||||||
{"PrecacheSound", PrecacheSound},
|
{"PrecacheSound", PrecacheSound},
|
||||||
{"IsSoundPrecached", IsSoundPrecached},
|
{"IsSoundPrecached", IsSoundPrecached},
|
||||||
{"FakeClientCommand", FakeClientCommand},
|
|
||||||
{"CreateDialog", smn_CreateDialog},
|
{"CreateDialog", smn_CreateDialog},
|
||||||
{"PrintToChat", PrintToChat},
|
{"PrintToChat", PrintToChat},
|
||||||
{"PrintCenterText", PrintCenterText},
|
{"PrintCenterText", PrintCenterText},
|
||||||
|
@ -88,6 +88,7 @@ bool SourceMod_Core::Unpause(char *error, size_t maxlen)
|
|||||||
|
|
||||||
void SourceMod_Core::AllPluginsLoaded()
|
void SourceMod_Core::AllPluginsLoaded()
|
||||||
{
|
{
|
||||||
|
g_SourceMod.AllPluginsLoaded();
|
||||||
}
|
}
|
||||||
|
|
||||||
const char *SourceMod_Core::GetAuthor()
|
const char *SourceMod_Core::GetAuthor()
|
||||||
|
@ -656,6 +656,16 @@ IVirtualMachine *SourceModBase::GetScriptingVM()
|
|||||||
return g_pVM;
|
return g_pVM;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void SourceModBase::AllPluginsLoaded()
|
||||||
|
{
|
||||||
|
SMGlobalClass *base = SMGlobalClass::head;
|
||||||
|
while (base)
|
||||||
|
{
|
||||||
|
base->OnSourceModGameInitialized();
|
||||||
|
base = base->m_pGlobalClassNext;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
SMGlobalClass *SMGlobalClass::head = NULL;
|
SMGlobalClass *SMGlobalClass::head = NULL;
|
||||||
|
|
||||||
SMGlobalClass::SMGlobalClass()
|
SMGlobalClass::SMGlobalClass()
|
||||||
|
@ -97,6 +97,7 @@ public: // ISourceMod
|
|||||||
const char *GetGameFolderName() const;
|
const char *GetGameFolderName() const;
|
||||||
ISourcePawnEngine *GetScriptingEngine();
|
ISourcePawnEngine *GetScriptingEngine();
|
||||||
IVirtualMachine *GetScriptingVM();
|
IVirtualMachine *GetScriptingVM();
|
||||||
|
void AllPluginsLoaded();
|
||||||
private:
|
private:
|
||||||
/**
|
/**
|
||||||
* @brief Loading plugins
|
* @brief Loading plugins
|
||||||
|
@ -133,7 +133,7 @@ native PrintToServer(const String:format[], any:...);
|
|||||||
/**
|
/**
|
||||||
* Sends a message to a client's console.
|
* Sends a message to a client's console.
|
||||||
*
|
*
|
||||||
* @param client Player index.
|
* @param client Client index.
|
||||||
* @param format Formatting rules.
|
* @param format Formatting rules.
|
||||||
* @param ... Variable number of format parameters.
|
* @param ... Variable number of format parameters.
|
||||||
* @noreturn
|
* @noreturn
|
||||||
@ -141,6 +141,21 @@ native PrintToServer(const String:format[], any:...);
|
|||||||
*/
|
*/
|
||||||
native PrintToConsole(client, const String:format[], any:...);
|
native PrintToConsole(client, const String:format[], any:...);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reples to a message in a command.
|
||||||
|
*
|
||||||
|
* A client index of 0 will use PrintToServer().
|
||||||
|
* If the command was from the console, PrintToConsole() is used.
|
||||||
|
* If the command was from chat, PrintToChat() is used.
|
||||||
|
*
|
||||||
|
* @param client Client index, or 0 for server.
|
||||||
|
* @param format Formatting rules.
|
||||||
|
* @param ... Variable number of format parameters.
|
||||||
|
* @noreturn
|
||||||
|
* @error If the client is not connected or invalid.
|
||||||
|
*/
|
||||||
|
native ReplyToCommand(client, const String:fornmat[], any:...);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Called when a server-only command is invoked.
|
* Called when a server-only command is invoked.
|
||||||
*
|
*
|
||||||
|
Loading…
Reference in New Issue
Block a user