added FakeClientCommandEx() to solve a nasty re-entrancy issue

--HG--
extra : convert_revision : svn%3A39bc706e-5318-0410-9160-8a85361fbb7c/trunk%401344
This commit is contained in:
David Anderson 2007-08-15 18:26:15 +00:00
parent ab2a4d5457
commit 290f90a3a3
5 changed files with 73 additions and 1 deletions

View File

@ -33,6 +33,7 @@
#include "sourcemod.h"
#include "sourcemm_api.h"
#include "UserMessages.h"
#include "PlayerManager.h"
CHalfLife2 g_HL2;
bool g_IsOriginalEngine = false;
@ -333,3 +334,38 @@ bool CHalfLife2::ShowVGUIMenu(int client, const char *name, KeyValues *data, boo
return true;
}
void CHalfLife2::AddToFakeCliCmdQueue(int client, int userid, const char *cmd)
{
DelayedFakeCliCmd *pFake;
if (m_FreeCmds.empty())
{
pFake = new DelayedFakeCliCmd;
} else {
pFake = m_FreeCmds.front();
m_FreeCmds.pop();
}
pFake->client = client;
pFake->userid = userid;
pFake->cmd.assign(cmd);
m_CmdQueue.push(pFake);
}
void CHalfLife2::ProcessFakeCliCmdQueue()
{
while (!m_CmdQueue.empty())
{
DelayedFakeCliCmd *pFake = m_CmdQueue.first();
if (g_Players.GetClientOfUserId(pFake->userid) == pFake->client)
{
CPlayer *pPlayer = g_Players.GetPlayerByIndex(pFake->client);
serverpluginhelpers->ClientCommand(pPlayer->GetEdict(), pFake->cmd.c_str());
}
m_CmdQueue.pop();
}
}

View File

@ -33,9 +33,11 @@
#define _INCLUDE_SOURCEMOD_CHALFLIFE2_H_
#include <sh_list.h>
#include <sh_string.h>
#include <sh_tinyhash.h>
#include "sm_trie.h"
#include "sm_globals.h"
#include "sm_queue.h"
#include <IGameHelpers.h>
#include <KeyValues.h>
@ -57,6 +59,13 @@ struct DataMapTrie
Trie *trie;
};
struct DelayedFakeCliCmd
{
String cmd;
int client;
int userid;
};
class CHalfLife2 :
public SMGlobalClass,
public IGameHelpers
@ -77,6 +86,9 @@ public: //IGameHelpers
bool TextMsg(int client, int dest, const char *msg);
bool HintTextMsg(int client, const char *msg);
bool ShowVGUIMenu(int client, const char *name, KeyValues *data, bool show);
public:
void AddToFakeCliCmdQueue(int client, int userid, const char *cmd);
void ProcessFakeCliCmdQueue();
private:
DataTableInfo *_FindServerClass(const char *classname);
private:
@ -86,6 +98,8 @@ private:
int m_MsgTextMsg;
int m_HinTextMsg;
int m_VGUIMenu;
Queue<DelayedFakeCliCmd *> m_CmdQueue;
CStack<DelayedFakeCliCmd *> m_FreeCmds;
};
extern CHalfLife2 g_HL2;

View File

@ -838,7 +838,7 @@ static cell_t FakeClientCommandEx(IPluginContext *pContext, const cell_t *params
return 0;
}
serverpluginhelpers->ClientCommand(pPlayer->GetEdict(), buffer);
g_HL2.AddToFakeCliCmdQueue(params[1], engine->GetPlayerUserId(pPlayer->GetEdict()), buffer);
return 1;
}
@ -1020,5 +1020,6 @@ REGISTER_NATIVES(consoleNatives)
{"GetCommandIterator", GetCommandIterator},
{"ReadCommandIterator", ReadCommandIterator},
{"CheckCommandAccess", CheckCommandAccess},
{"FakeClientCommandEx", FakeClientCommandEx},
{NULL, NULL}
};

View File

@ -49,6 +49,7 @@
#include "MenuStyle_Valve.h"
#include "MenuStyle_Radio.h"
#include "Database.h"
#include "HalfLife2.h"
SH_DECL_HOOK6(IServerGameDLL, LevelInit, SH_NOATTRIB, false, bool, const char *, const char *, const char *, const char *, bool, bool);
SH_DECL_HOOK0_void(IServerGameDLL, LevelShutdown, SH_NOATTRIB, false);
@ -396,6 +397,7 @@ void SimulateTick()
void SourceModBase::GameFrame(bool simulating)
{
g_DBMan.RunFrame();
g_HL2.ProcessFakeCliCmdQueue();
/**
* Note: This is all hardcoded rather than delegated to save

View File

@ -150,6 +150,12 @@ native ClientCommand(client, const String:fmt[], any:...);
/**
* Executes a client command on the server without being networked.
*
* FakeClientCommand() overwrites the command tokenization buffer. This can
* cause undesired effects because future calls to GetCmdArg* will return
* data from the FakeClientCommand(), not the parent command. If you are in
* a hook where this matters (for example, a "say" hook), you should use
* FakeClientCommandEx() instead.
*
* @param client Index of the client.
* @param fmt Format of the client command.
* @param ... Format parameters
@ -158,6 +164,19 @@ native ClientCommand(client, const String:fmt[], any:...);
*/
native FakeClientCommand(client, const String:fmt[], any:...);
/**
* Executes a client command on the server without being networked. The
* execution of the client command is delayed by one frame to prevent any
* re-entrancy issues that might surface with FakeClientCommand().
*
* @param client Index of the client.
* @param fmt Format of the client command.
* @param ... Format parameters
* @noreturn
* @error Invalid client index, or client not connected.
*/
native FakeClientCommandEx(client, const String:fmt[], any:...);
/**
* Sends a message to the server console.
*