extensions/sdktools/voice.cpp
author Nicholas Hastings <skamonkey@gmail.com>
Fri Feb 07 22:11:31 2014 -0500 (2014-02-07)
changeset 4231 84b709f4103a
parent 4153 5768ce963643
child 4268 3c383c5b1aaa
permissions -rw-r--r--
Fix SDKHooks load issue and reliance on gEntList ptr on CS:GO and Dota 2 on Windows.
     1 /**
     2  * vim: set ts=4 :
     3  * =============================================================================
     4  * SourceMod SDKTools Extension
     5  * Copyright (C) 2004-2008 AlliedModders LLC.  All rights reserved.
     6  * =============================================================================
     7  *
     8  * This program is free software; you can redistribute it and/or modify it under
     9  * the terms of the GNU General Public License, version 3.0, as published by the
    10  * Free Software Foundation.
    11  *
    12  * This program is distributed in the hope that it will be useful, but WITHOUT
    13  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
    14  * FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
    15  * details.
    16  *
    17  * You should have received a copy of the GNU General Public License along with
    18  * this program.  If not, see <http://www.gnu.org/licenses/>.
    19  *
    20  * As a special exception, AlliedModders LLC gives you permission to link the
    21  * code of this program (as well as its derivative works) to "Half-Life 2," the
    22  * "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software
    23  * by the Valve Corporation.  You must obey the GNU General Public License in
    24  * all respects for all other code used.  Additionally, AlliedModders LLC grants
    25  * this exception to all derivative works.  AlliedModders LLC defines further
    26  * exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007),
    27  * or <http://www.sourcemod.net/license.php>.
    28  *
    29  * Version: $Id$
    30  */
    31 
    32 #include <extension.h>
    33 #include <hooks.h>
    34 
    35 #define SPEAK_NORMAL		0
    36 #define SPEAK_MUTED			1
    37 #define SPEAK_ALL			2
    38 #define SPEAK_LISTENALL		4
    39 #define SPEAK_TEAM			8
    40 #define SPEAK_LISTENTEAM	16
    41 #define LISTEN_DEFAULT		0
    42 #define LISTEN_NO			1
    43 #define LISTEN_YES			2
    44 
    45 /* this enum needs to be in the order as the LISTEN_ defines above to keep
    46    the old broken SetClientListening functionality untouched */
    47 enum ListenOverride
    48 {
    49 	Listen_Default,		/**< Leave it up to the game */
    50 	Listen_No,			/**< Can't hear */
    51 	Listen_Yes,			/**< Can hear */
    52 };
    53 
    54 size_t g_VoiceFlags[65];
    55 size_t g_VoiceHookCount = 0;
    56 ListenOverride g_VoiceMap[65][65];
    57 bool g_ClientMutes[65][65];
    58 
    59 SH_DECL_HOOK3(IVoiceServer, SetClientListening, SH_NOATTRIB, 0, bool, int, int, bool);
    60 
    61 #if SOURCE_ENGINE == SE_DOTA
    62 SH_DECL_HOOK2_void(IServerGameClients, ClientCommand, SH_NOATTRIB, 0, CEntityIndex, const CCommand &);
    63 #elif SOURCE_ENGINE >= SE_ORANGEBOX
    64 SH_DECL_HOOK2_void(IServerGameClients, ClientCommand, SH_NOATTRIB, 0, edict_t *, const CCommand &);
    65 #else
    66 SH_DECL_HOOK1_void(IServerGameClients, ClientCommand, SH_NOATTRIB, 0, edict_t *);
    67 #endif
    68 
    69 bool DecHookCount()
    70 {
    71 	if (--g_VoiceHookCount == 0)
    72 	{
    73 		SH_REMOVE_HOOK(IVoiceServer, SetClientListening, voiceserver, SH_MEMBER(&g_SdkTools, &SDKTools::OnSetClientListening), false);
    74 		return true;
    75 	}
    76 
    77 	return false;
    78 }
    79 
    80 void IncHookCount()
    81 {
    82 	if (!g_VoiceHookCount++)
    83 	{
    84 		SH_ADD_HOOK(IVoiceServer, SetClientListening, voiceserver, SH_MEMBER(&g_SdkTools, &SDKTools::OnSetClientListening), false);
    85 	}
    86 }
    87 
    88 void SDKTools::VoiceInit()
    89 {
    90 	memset(g_VoiceMap, 0, sizeof(g_VoiceMap));
    91 	memset(g_ClientMutes, 0, sizeof(g_ClientMutes));
    92 
    93 	SH_ADD_HOOK(IServerGameClients, ClientCommand, serverClients, SH_MEMBER(this, &SDKTools::OnClientCommand), true);
    94 }
    95 
    96 #if SOURCE_ENGINE == SE_DOTA
    97 void SDKTools::OnClientCommand(CEntityIndex index, const CCommand &args)
    98 {
    99 	int client = index.Get();
   100 #elif SOURCE_ENGINE >= SE_ORANGEBOX
   101 void SDKTools::OnClientCommand(edict_t *pEntity, const CCommand &args)
   102 {
   103 	int client = IndexOfEdict(pEntity);
   104 #else
   105 void SDKTools::OnClientCommand(edict_t *pEntity)
   106 {
   107 	CCommand args;
   108 	int client = IndexOfEdict(pEntity);
   109 #endif
   110 
   111 	if ((args.ArgC() > 1) && (stricmp(args.Arg(0), "vban") == 0))
   112 	{
   113 		for (int i = 1; (i < args.ArgC()) && (i < 3); i++)
   114 		{
   115 			unsigned long mask = 0;
   116 			sscanf(args.Arg(i), "%p", (void**)&mask);
   117 			
   118 			for (int j = 0; j < 32; j++)
   119 			{
   120 				g_ClientMutes[client][1 + j + 32 * (i - 1)] = !!(mask & 1 << j);
   121 			}
   122 		}
   123 	}
   124 
   125 	RETURN_META(MRES_IGNORED);
   126 }
   127 
   128 bool SDKTools::OnSetClientListening(int iReceiver, int iSender, bool bListen)
   129 {
   130 	if (g_ClientMutes[iReceiver][iSender])
   131 	{
   132 		RETURN_META_VALUE_NEWPARAMS(MRES_IGNORED, bListen, &IVoiceServer::SetClientListening, (iReceiver, iSender, false));
   133 	}
   134 
   135 	if (g_VoiceFlags[iSender] & SPEAK_MUTED)
   136 	{
   137 		RETURN_META_VALUE_NEWPARAMS(MRES_IGNORED, bListen, &IVoiceServer::SetClientListening, (iReceiver, iSender, false));
   138 	}
   139 
   140 	if (g_VoiceMap[iReceiver][iSender] == Listen_No)
   141 	{
   142 		RETURN_META_VALUE_NEWPARAMS(MRES_IGNORED, bListen, &IVoiceServer::SetClientListening, (iReceiver, iSender, false));
   143 	}
   144 	else if (g_VoiceMap[iReceiver][iSender] == Listen_Yes)
   145 	{
   146 		RETURN_META_VALUE_NEWPARAMS(MRES_IGNORED, bListen, &IVoiceServer::SetClientListening, (iReceiver, iSender, true));
   147 	}
   148 
   149 	if ((g_VoiceFlags[iSender] & SPEAK_ALL) || (g_VoiceFlags[iReceiver] & SPEAK_LISTENALL))
   150 	{
   151 		RETURN_META_VALUE_NEWPARAMS(MRES_IGNORED, bListen, &IVoiceServer::SetClientListening, (iReceiver, iSender, true));
   152 	}
   153 
   154 	if ((g_VoiceFlags[iSender] & SPEAK_TEAM) || (g_VoiceFlags[iReceiver] & SPEAK_LISTENTEAM))
   155 	{
   156 		IGamePlayer *pReceiver = playerhelpers->GetGamePlayer(iReceiver);
   157 		IGamePlayer *pSender = playerhelpers->GetGamePlayer(iSender);
   158 
   159 		if (pReceiver && pSender && pReceiver->IsInGame() && pSender->IsInGame())
   160 		{
   161 			IPlayerInfo *pRInfo = pReceiver->GetPlayerInfo();
   162 			IPlayerInfo *pSInfo = pSender->GetPlayerInfo();
   163 
   164 			if (pRInfo && pSInfo && pRInfo->GetTeamIndex() == pSInfo->GetTeamIndex())
   165 			{
   166 				RETURN_META_VALUE_NEWPARAMS(MRES_IGNORED, bListen, &IVoiceServer::SetClientListening, (iReceiver, iSender, true));
   167 			}
   168 		}
   169 	}
   170 
   171 	RETURN_META_VALUE(MRES_IGNORED, bListen);
   172 }
   173 
   174 void SDKTools::OnClientDisconnecting(int client)
   175 {
   176 	g_Hooks.OnClientDisconnecting(client);
   177 
   178 	int max_clients = playerhelpers->GetMaxClients();
   179 
   180 	if (g_VoiceHookCount == 0)
   181 	{
   182 		return;
   183 	}
   184 
   185 	/* This can probably be optimized more, but I doubt it's that much 
   186 	 * of an actual bottleneck.
   187 	 */
   188 
   189 	/* Reset other clients who receive from or have muted this client */
   190 	for (int i = 1; i <= max_clients; i++)
   191 	{
   192 		if (i == client)
   193 		{
   194 			continue;
   195 		}
   196 
   197 		g_ClientMutes[i][client] = false;
   198 		g_ClientMutes[client][i] = false;
   199 		
   200 		if (g_VoiceMap[i][client] != Listen_Default)
   201 		{
   202 			g_VoiceMap[i][client] = Listen_Default;
   203 			if (DecHookCount())
   204 			{
   205 				break;
   206 			}
   207 		}
   208 		if (g_VoiceMap[client][i] != Listen_Default)
   209 		{
   210 			g_VoiceMap[client][i] = Listen_Default;
   211 			if (DecHookCount())
   212 			{
   213 				break;
   214 			}
   215 		}
   216 	}
   217 
   218 	if (g_VoiceFlags[client])
   219 	{
   220 		g_VoiceFlags[client] = SPEAK_NORMAL;
   221 		DecHookCount();
   222 	}
   223 }
   224 
   225 static cell_t SetClientListeningFlags(IPluginContext *pContext, const cell_t *params)
   226 {
   227 	IGamePlayer *player = playerhelpers->GetGamePlayer(params[1]);
   228 	if (player == NULL)
   229 	{
   230 		return pContext->ThrowNativeError("Client index %d is invalid", params[1]);
   231 	}
   232 	else if (!player->IsConnected())
   233 	{
   234 		return pContext->ThrowNativeError("Client %d is not connected", params[1]);
   235 	}
   236 
   237 	if (!params[2] && g_VoiceFlags[params[1]])
   238 	{
   239 		DecHookCount();
   240 	}
   241 	else if (!g_VoiceFlags[params[1]] && params[2])
   242 	{
   243 		IncHookCount();
   244 	}
   245 
   246 	g_VoiceFlags[params[1]] = params[2];
   247 
   248 	return 1;
   249 }
   250 
   251 static cell_t GetClientListeningFlags(IPluginContext *pContext, const cell_t *params)
   252 {
   253 	IGamePlayer *player = playerhelpers->GetGamePlayer(params[1]);
   254 	if (player == NULL)
   255 	{
   256 		return pContext->ThrowNativeError("Client index %d is invalid", params[1]);
   257 	}
   258 	else if (!player->IsConnected())
   259 	{
   260 		return pContext->ThrowNativeError("Client %d is not connected", params[1]);
   261 	}
   262 
   263 	return g_VoiceFlags[params[1]];
   264 }
   265 
   266 static cell_t SetClientListening(IPluginContext *pContext, const cell_t *params)
   267 {
   268 	int r, s;
   269 	IGamePlayer *player;
   270 	
   271 	player = playerhelpers->GetGamePlayer(params[1]);
   272 	if (player == NULL)
   273 	{
   274 		return pContext->ThrowNativeError("Receiver client index %d is invalid", params[1]);
   275 	}
   276 	else if (!player->IsConnected())
   277 	{
   278 		return pContext->ThrowNativeError("Receiver client %d is not connected", params[1]);
   279 	}
   280 
   281 	player = playerhelpers->GetGamePlayer(params[2]);
   282 	if (player == NULL)
   283 	{
   284 		return pContext->ThrowNativeError("Sender client index %d is invalid", params[2]);
   285 	}
   286 	else if (!player->IsConnected())
   287 	{
   288 		return pContext->ThrowNativeError("Sender client %d is not connected", params[2]);
   289 	}
   290 
   291 	r = params[1];
   292 	s = params[2];
   293 	
   294 	if (g_VoiceMap[r][s] == Listen_Default && params[3] != Listen_Default)
   295 	{
   296 		g_VoiceMap[r][s] = (ListenOverride) params[3];
   297 		IncHookCount();
   298 	}
   299 	else if (g_VoiceMap[r][s] != Listen_Default && params[3] == Listen_Default)
   300 	{
   301 		g_VoiceMap[r][s] = (ListenOverride) params[3];
   302 		DecHookCount();
   303 	}
   304 	else
   305 	{
   306 		g_VoiceMap[r][s] = (ListenOverride) params[3];
   307 	}
   308 
   309 	return 1;
   310 }
   311 
   312 static cell_t GetClientListening(IPluginContext *pContext, const cell_t *params)
   313 {
   314 	IGamePlayer *player;
   315 
   316 	player = playerhelpers->GetGamePlayer(params[1]);
   317 	if (player == NULL)
   318 	{
   319 		return pContext->ThrowNativeError("Receiver client index %d is invalid", params[1]);
   320 	}
   321 	else if (!player->IsConnected())
   322 	{
   323 		return pContext->ThrowNativeError("Receiver client %d is not connected", params[1]);
   324 	}
   325 
   326 	player = playerhelpers->GetGamePlayer(params[2]);
   327 	if (player == NULL)
   328 	{
   329 		return pContext->ThrowNativeError("Sender client index %d is invalid", params[2]);
   330 	}
   331 	else if (!player->IsConnected())
   332 	{
   333 		return pContext->ThrowNativeError("Sender client %d is not connected", params[2]);
   334 	}
   335 
   336 	return g_VoiceMap[params[1]][params[2]];
   337 }
   338 
   339 static cell_t IsClientMuted(IPluginContext *pContext, const cell_t *params)
   340 {
   341 	IGamePlayer *player;
   342 
   343 	player = playerhelpers->GetGamePlayer(params[1]);
   344 	if (player == NULL)
   345 	{
   346 		return pContext->ThrowNativeError("Muter client index %d is invalid", params[1]);
   347 	}
   348 	else if (!player->IsConnected())
   349 	{
   350 		return pContext->ThrowNativeError("Muter client %d is not connected", params[1]);
   351 	}
   352 
   353 	player = playerhelpers->GetGamePlayer(params[2]);
   354 	if (player == NULL)
   355 	{
   356 		return pContext->ThrowNativeError("Mutee client index %d is invalid", params[2]);
   357 	}
   358 	else if (!player->IsConnected())
   359 	{
   360 		return pContext->ThrowNativeError("Mutee client %d is not connected", params[2]);
   361 	}
   362 
   363 	return g_ClientMutes[params[1]][params[2]];
   364 }
   365 
   366 sp_nativeinfo_t g_VoiceNatives[] =
   367 {
   368 	{"SetClientListeningFlags",		SetClientListeningFlags},
   369 	{"GetClientListeningFlags",		GetClientListeningFlags},
   370 	{"SetClientListening",			SetClientListening},
   371 	{"GetClientListening",			GetClientListening},
   372 	{"SetListenOverride",			SetClientListening},
   373 	{"GetListenOverride",			GetClientListening},
   374 	{"IsClientMuted",				IsClientMuted},
   375 	{NULL,							NULL},
   376 };