fixed amb1808 - KickClient() is delayed and does not crash -- KickClientEx() is the old functionality
authorDavid Anderson <dvander@alliedmods.net>
Sun Jul 06 00:45:26 2008 +0000 (2008-07-06)
changeset 216259c5dc056c69
parent 2161 e57e52e272e8
child 2163 e4eaf8851770
fixed amb1808 - KickClient() is delayed and does not crash -- KickClientEx() is the old functionality
core/HalfLife2.cpp
core/HalfLife2.h
core/frame_hooks.cpp
core/smn_player.cpp
plugins/include/clients.inc
     1.1 --- a/core/HalfLife2.cpp	Sun Jul 06 00:02:11 2008 +0000
     1.2 +++ b/core/HalfLife2.cpp	Sun Jul 06 00:45:26 2008 +0000
     1.3 @@ -511,3 +511,31 @@
     1.4  	return m_CommandStack.front().cmd;
     1.5  #endif
     1.6  }
     1.7 +
     1.8 +void CHalfLife2::AddDelayedKick(int client, int userid, const char *msg)
     1.9 +{
    1.10 +	DelayedKickInfo kick;
    1.11 +
    1.12 +	kick.client = client;
    1.13 +	kick.userid = userid;
    1.14 +	UTIL_Format(kick.buffer, sizeof(kick.buffer), "%s", msg);
    1.15 +
    1.16 +	m_DelayedKicks.push(kick);
    1.17 +}
    1.18 +
    1.19 +void CHalfLife2::ProcessDelayedKicks()
    1.20 +{
    1.21 +	while (!m_DelayedKicks.empty())
    1.22 +	{
    1.23 +		DelayedKickInfo info = m_DelayedKicks.first();
    1.24 +		m_DelayedKicks.pop();
    1.25 +
    1.26 +		CPlayer *player = g_Players.GetPlayerByIndex(info.client);
    1.27 +		if (player == NULL || player->GetUserId() != info.userid)
    1.28 +		{
    1.29 +			continue;
    1.30 +		}
    1.31 +
    1.32 +		player->Kick(info.buffer);
    1.33 +	}
    1.34 +}
     2.1 --- a/core/HalfLife2.h	Sun Jul 06 00:02:11 2008 +0000
     2.2 +++ b/core/HalfLife2.h	Sun Jul 06 00:45:26 2008 +0000
     2.3 @@ -79,6 +79,13 @@
     2.4  #endif
     2.5  };
     2.6  
     2.7 +struct DelayedKickInfo
     2.8 +{
     2.9 +	int userid;
    2.10 +	int client;
    2.11 +	char buffer[384];
    2.12 +};
    2.13 +
    2.14  class CHalfLife2 : 
    2.15  	public SMGlobalClass,
    2.16  	public IGameHelpers
    2.17 @@ -110,6 +117,8 @@
    2.18  	void PopCommandStack();
    2.19  	const CCommand *PeekCommandStack();
    2.20  	const char *CurrentCommandName();
    2.21 +	void AddDelayedKick(int client, int userid, const char *msg);
    2.22 +	void ProcessDelayedKicks();
    2.23  #if !defined METAMOD_PLAPI_VERSION
    2.24  	bool IsOriginalEngine();
    2.25  #endif
    2.26 @@ -125,6 +134,7 @@
    2.27  	Queue<DelayedFakeCliCmd *> m_CmdQueue;
    2.28  	CStack<DelayedFakeCliCmd *> m_FreeCmds;
    2.29  	CStack<CachedCommandInfo> m_CommandStack;
    2.30 +	Queue<DelayedKickInfo> m_DelayedKicks;
    2.31  };
    2.32  
    2.33  extern CHalfLife2 g_HL2;
     3.1 --- a/core/frame_hooks.cpp	Sun Jul 06 00:02:11 2008 +0000
     3.2 +++ b/core/frame_hooks.cpp	Sun Jul 06 00:45:26 2008 +0000
     3.3 @@ -44,6 +44,7 @@
     3.4  	/* Frame based hooks */
     3.5  	g_DBMan.RunFrame();
     3.6  	g_HL2.ProcessFakeCliCmdQueue();
     3.7 +	g_HL2.ProcessDelayedKicks();
     3.8  	g_SourceMod.ProcessGameFrameHooks(simulating);
     3.9  
    3.10  	float curtime = *g_pUniversalTime;
     4.1 --- a/core/smn_player.cpp	Sun Jul 06 00:02:11 2008 +0000
     4.2 +++ b/core/smn_player.cpp	Sun Jul 06 00:45:26 2008 +0000
     4.3 @@ -1258,10 +1258,64 @@
     4.4  	if (!pPlayer)
     4.5  	{
     4.6  		return pContext->ThrowNativeError("Client index %d is invalid", client);
     4.7 -	} else if (!pPlayer->IsConnected()) {
     4.8 +	}
     4.9 +	else if (!pPlayer->IsConnected())
    4.10 +	{
    4.11  		return pContext->ThrowNativeError("Client %d is not connected", client);
    4.12  	}
    4.13  
    4.14 +	/* Ignore duplicate kicks */
    4.15 +	if (pPlayer->IsInKickQueue())
    4.16 +	{
    4.17 +		return 1;
    4.18 +	}
    4.19 +
    4.20 +	pPlayer->MarkAsBeingKicked();
    4.21 +
    4.22 +	if (pPlayer->IsFakeClient())
    4.23 +	{
    4.24 +		char kickcmd[40];
    4.25 +		UTIL_Format(kickcmd, sizeof(kickcmd), "kick %s\n", pPlayer->GetName());
    4.26 +
    4.27 +		engine->ServerCommand(kickcmd);
    4.28 +		return 1;
    4.29 +	}
    4.30 +
    4.31 +	g_SourceMod.SetGlobalTarget(client);
    4.32 +
    4.33 +	char buffer[256];
    4.34 +	g_SourceMod.FormatString(buffer, sizeof(buffer), pContext, params, 2);
    4.35 +
    4.36 +	if (pContext->GetContext()->n_err != SP_ERROR_NONE)
    4.37 +	{
    4.38 +		return 0;
    4.39 +	}
    4.40 +
    4.41 +	g_HL2.AddDelayedKick(client, pPlayer->GetUserId(), buffer);
    4.42 +
    4.43 +	return 1;
    4.44 +}
    4.45 +
    4.46 +static cell_t KickClientEx(IPluginContext *pContext, const cell_t *params)
    4.47 +{
    4.48 +	int client = params[1];
    4.49 +
    4.50 +	CPlayer *pPlayer = g_Players.GetPlayerByIndex(client);
    4.51 +	if (!pPlayer)
    4.52 +	{
    4.53 +		return pContext->ThrowNativeError("Client index %d is invalid", client);
    4.54 +	}
    4.55 +	else if (!pPlayer->IsConnected())
    4.56 +	{
    4.57 +		return pContext->ThrowNativeError("Client %d is not connected", client);
    4.58 +	}
    4.59 +
    4.60 +	/* Ignore duplicate kicks */
    4.61 +	if (pPlayer->IsInKickQueue())
    4.62 +	{
    4.63 +		return 1;
    4.64 +	}
    4.65 +
    4.66  	pPlayer->MarkAsBeingKicked();
    4.67  
    4.68  	if (pPlayer->IsFakeClient())
    4.69 @@ -1460,6 +1514,7 @@
    4.70  	{"ShowActivityEx",			ShowActivityEx},
    4.71  	{"ShowActivity2",			ShowActivity2},
    4.72  	{"KickClient",				KickClient},
    4.73 +	{"KickClientEx",			KickClientEx},
    4.74  	{"RunAdminCacheChecks",		RunAdminCacheChecks},
    4.75  	{"NotifyPostAdminCheck",	NotifyPostAdminCheck},
    4.76  	{"IsClientInKickQueue",		IsClientInKickQueue},
     5.1 --- a/plugins/include/clients.inc	Sun Jul 06 00:02:11 2008 +0000
     5.2 +++ b/plugins/include/clients.inc	Sun Jul 06 00:45:26 2008 +0000
     5.3 @@ -634,7 +634,28 @@
     5.4  native GetClientOfUserId(userid);
     5.5  
     5.6  /**
     5.7 - * Disconnects a client from the server.  
     5.8 + * Disconnects a client from the server as soon as the next frame starts.
     5.9 + *
    5.10 + * Note: Originally, KickClient() was immediate.  The delay was introduced 
    5.11 + * because despite warnings, plugins were using it in ways that would crash. 
    5.12 + * The new safe version can break cases that rely on immediate disconnects, 
    5.13 + * but ensures that plugins do not accidentally cause crashes.
    5.14 + *
    5.15 + * If you need immediate disconnects, use KickClientEx().
    5.16 + *
    5.17 + * Note: IsClientInKickQueue() will return true before the kick occurs.
    5.18 + *
    5.19 + * @param client		Client index.
    5.20 + * @param format		Optional formatting rules for disconnect reason.
    5.21 + *                      Note that a period is automatically appended to the string by the engine.
    5.22 + * @param ...			Variable number of format parameters.
    5.23 + * @noreturn
    5.24 + * @error				Invalid client index, or client not connected.
    5.25 + */
    5.26 +native KickClient(client, const String:format[]="", any:...);
    5.27 +
    5.28 +/**
    5.29 + * Immediately disconnects a client from the server.
    5.30   *
    5.31   * Kicking clients from certain events or callbacks may cause crashes.  If in 
    5.32   * doubt, create a short (0.1 second) timer to kick the client in the next 
    5.33 @@ -647,7 +668,7 @@
    5.34   * @noreturn
    5.35   * @error				Invalid client index, or client not connected.
    5.36   */
    5.37 -native KickClient(client, const String:format[]="", any:...);
    5.38 +native KickClientEx(client, const String:format[]="", any:...);
    5.39  
    5.40  /**
    5.41   * Changes a client's team through the mod's generic team changing function.