extensions/sdktools/voice.cpp
author Peace-Maker <januk@freenet.de>
Tue Feb 05 21:45:11 2013 +0000 (2013-02-05)
changeset 3763 336283ba3a38
parent 3211 8dd6da22725b
child 3819 d17aed3d46bf
permissions -rw-r--r--
Updated for CS:S sync (r=asherkin).
     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_ORANGEBOX
    62 SH_DECL_HOOK2_void(IServerGameClients, ClientCommand, SH_NOATTRIB, 0, edict_t *, const CCommand &);
    63 #else
    64 SH_DECL_HOOK1_void(IServerGameClients, ClientCommand, SH_NOATTRIB, 0, edict_t *);
    65 #endif
    66 
    67 bool DecHookCount()
    68 {
    69 	if (g_VoiceHookCount == 0)
    70 	{
    71 		SH_REMOVE_HOOK_MEMFUNC(IVoiceServer, SetClientListening, voiceserver, &g_SdkTools, &SDKTools::OnSetClientListening, false);
    72 		return true;
    73 	}
    74 
    75 	return false;
    76 }
    77 
    78 void IncHookCount()
    79 {
    80 	if (!g_VoiceHookCount++)
    81 	{
    82 		SH_ADD_HOOK_MEMFUNC(IVoiceServer, SetClientListening, voiceserver, &g_SdkTools, &SDKTools::OnSetClientListening, false);
    83 	}
    84 }
    85 
    86 void SDKTools::VoiceInit()
    87 {
    88 	memset(g_VoiceMap, 0, sizeof(g_VoiceMap));
    89 	memset(g_ClientMutes, 0, sizeof(g_ClientMutes));
    90 
    91 	SH_ADD_HOOK_MEMFUNC(IServerGameClients, ClientCommand, serverClients, this, &SDKTools::OnClientCommand, true);
    92 }
    93 
    94 #if SOURCE_ENGINE >= SE_ORANGEBOX
    95 void SDKTools::OnClientCommand(edict_t *pEntity, const CCommand &args)
    96 {
    97 #else
    98 void SDKTools::OnClientCommand(edict_t *pEntity)
    99 {
   100 	CCommand args;
   101 #endif
   102 	int client = IndexOfEdict(pEntity);
   103 
   104 	if ((args.ArgC() > 1) && (stricmp(args.Arg(0), "vban") == 0))
   105 	{
   106 		for (int i = 1; (i < args.ArgC()) && (i < 3); i++)
   107 		{
   108 			unsigned long mask = 0;
   109 			sscanf(args.Arg(i), "%p", (void**)&mask);
   110 			
   111 			for (int j = 0; j < 32; j++)
   112 			{
   113 				g_ClientMutes[client][1 + j + 32 * (i - 1)] = !!(mask & 1 << j);
   114 			}
   115 		}
   116 	}
   117 
   118 	RETURN_META(MRES_IGNORED);
   119 }
   120 
   121 bool SDKTools::OnSetClientListening(int iReceiver, int iSender, bool bListen)
   122 {
   123 	if (g_ClientMutes[iReceiver][iSender])
   124 	{
   125 		RETURN_META_VALUE_NEWPARAMS(MRES_IGNORED, bListen, &IVoiceServer::SetClientListening, (iReceiver, iSender, false));
   126 	}
   127 
   128 	if (g_VoiceFlags[iSender] & SPEAK_MUTED)
   129 	{
   130 		RETURN_META_VALUE_NEWPARAMS(MRES_IGNORED, bListen, &IVoiceServer::SetClientListening, (iReceiver, iSender, false));
   131 	}
   132 
   133 	if (g_VoiceMap[iReceiver][iSender] == Listen_No)
   134 	{
   135 		RETURN_META_VALUE_NEWPARAMS(MRES_IGNORED, bListen, &IVoiceServer::SetClientListening, (iReceiver, iSender, false));
   136 	}
   137 	else if (g_VoiceMap[iReceiver][iSender] == Listen_Yes)
   138 	{
   139 		RETURN_META_VALUE_NEWPARAMS(MRES_IGNORED, bListen, &IVoiceServer::SetClientListening, (iReceiver, iSender, true));
   140 	}
   141 
   142 	if ((g_VoiceFlags[iSender] & SPEAK_ALL) || (g_VoiceFlags[iReceiver] & SPEAK_LISTENALL))
   143 	{
   144 		RETURN_META_VALUE_NEWPARAMS(MRES_IGNORED, bListen, &IVoiceServer::SetClientListening, (iReceiver, iSender, true));
   145 	}
   146 
   147 	if ((g_VoiceFlags[iSender] & SPEAK_TEAM) || (g_VoiceFlags[iReceiver] & SPEAK_LISTENTEAM))
   148 	{
   149 		IGamePlayer *pReceiver = playerhelpers->GetGamePlayer(iReceiver);
   150 		IGamePlayer *pSender = playerhelpers->GetGamePlayer(iSender);
   151 
   152 		if (pReceiver && pSender && pReceiver->IsInGame() && pSender->IsInGame())
   153 		{
   154 			IPlayerInfo *pRInfo = pReceiver->GetPlayerInfo();
   155 			IPlayerInfo *pSInfo = pSender->GetPlayerInfo();
   156 
   157 			if (pRInfo && pSInfo && pRInfo->GetTeamIndex() == pSInfo->GetTeamIndex())
   158 			{
   159 				RETURN_META_VALUE_NEWPARAMS(MRES_IGNORED, bListen, &IVoiceServer::SetClientListening, (iReceiver, iSender, true));
   160 			}
   161 		}
   162 	}
   163 
   164 	RETURN_META_VALUE(MRES_IGNORED, bListen);
   165 }
   166 
   167 void SDKTools::OnClientDisconnecting(int client)
   168 {
   169 	g_Hooks.OnClientDisconnecting(client);
   170 
   171 	int max_clients = playerhelpers->GetMaxClients();
   172 
   173 	if (g_VoiceHookCount == 0)
   174 	{
   175 		return;
   176 	}
   177 
   178 	/* This can probably be optimized more, but I doubt it's that much 
   179 	 * of an actual bottleneck.
   180 	 */
   181 
   182 	/* Reset other clients who receive from or have muted this client */
   183 	for (int i = 1; i <= max_clients; i++)
   184 	{
   185 		if (i == client)
   186 		{
   187 			continue;
   188 		}
   189 
   190 		g_ClientMutes[i][client] = false;
   191 		g_ClientMutes[client][i] = false;
   192 		
   193 		if (g_VoiceMap[i][client] != Listen_Default)
   194 		{
   195 			g_VoiceMap[i][client] = Listen_Default;
   196 			if (DecHookCount())
   197 			{
   198 				break;
   199 			}
   200 		}
   201 		if (g_VoiceMap[client][i] != Listen_Default)
   202 		{
   203 			g_VoiceMap[client][i] = Listen_Default;
   204 			if (DecHookCount())
   205 			{
   206 				break;
   207 			}
   208 		}
   209 	}
   210 
   211 	if (g_VoiceFlags[client])
   212 	{
   213 		g_VoiceFlags[client] = SPEAK_NORMAL;
   214 		DecHookCount();
   215 	}
   216 }
   217 
   218 static cell_t SetClientListeningFlags(IPluginContext *pContext, const cell_t *params)
   219 {
   220 	IGamePlayer *player = playerhelpers->GetGamePlayer(params[1]);
   221 	if (player == NULL)
   222 	{
   223 		return pContext->ThrowNativeError("Client index %d is invalid", params[1]);
   224 	}
   225 	else if (!player->IsConnected())
   226 	{
   227 		return pContext->ThrowNativeError("Client %d is not connected", params[1]);
   228 	}
   229 
   230 	if (!params[2] && g_VoiceFlags[params[1]])
   231 	{
   232 		DecHookCount();
   233 	}
   234 	else if (!g_VoiceFlags[params[1]] && params[2])
   235 	{
   236 		IncHookCount();
   237 	}
   238 
   239 	g_VoiceFlags[params[1]] = params[2];
   240 
   241 	return 1;
   242 }
   243 
   244 static cell_t GetClientListeningFlags(IPluginContext *pContext, const cell_t *params)
   245 {
   246 	IGamePlayer *player = playerhelpers->GetGamePlayer(params[1]);
   247 	if (player == NULL)
   248 	{
   249 		return pContext->ThrowNativeError("Client index %d is invalid", params[1]);
   250 	}
   251 	else if (!player->IsConnected())
   252 	{
   253 		return pContext->ThrowNativeError("Client %d is not connected", params[1]);
   254 	}
   255 
   256 	return g_VoiceFlags[params[1]];
   257 }
   258 
   259 static cell_t SetClientListening(IPluginContext *pContext, const cell_t *params)
   260 {
   261 	int r, s;
   262 	IGamePlayer *player;
   263 	
   264 	player = playerhelpers->GetGamePlayer(params[1]);
   265 	if (player == NULL)
   266 	{
   267 		return pContext->ThrowNativeError("Receiver client index %d is invalid", params[1]);
   268 	}
   269 	else if (!player->IsConnected())
   270 	{
   271 		return pContext->ThrowNativeError("Receiver client %d is not connected", params[1]);
   272 	}
   273 
   274 	player = playerhelpers->GetGamePlayer(params[2]);
   275 	if (player == NULL)
   276 	{
   277 		return pContext->ThrowNativeError("Sender client index %d is invalid", params[2]);
   278 	}
   279 	else if (!player->IsConnected())
   280 	{
   281 		return pContext->ThrowNativeError("Sender client %d is not connected", params[2]);
   282 	}
   283 
   284 	r = params[1];
   285 	s = params[2];
   286 	
   287 	if (g_VoiceMap[r][s] == Listen_Default && params[3] != Listen_Default)
   288 	{
   289 		g_VoiceMap[r][s] = (ListenOverride) params[3];
   290 		IncHookCount();
   291 	}
   292 	else if (g_VoiceMap[r][s] != Listen_Default && params[3] == Listen_Default)
   293 	{
   294 		g_VoiceMap[r][s] = (ListenOverride) params[3];
   295 		DecHookCount();
   296 	}
   297 	else
   298 	{
   299 		g_VoiceMap[r][s] = (ListenOverride) params[3];
   300 	}
   301 
   302 	return 1;
   303 }
   304 
   305 static cell_t GetClientListening(IPluginContext *pContext, const cell_t *params)
   306 {
   307 	IGamePlayer *player;
   308 
   309 	player = playerhelpers->GetGamePlayer(params[1]);
   310 	if (player == NULL)
   311 	{
   312 		return pContext->ThrowNativeError("Receiver client index %d is invalid", params[1]);
   313 	}
   314 	else if (!player->IsConnected())
   315 	{
   316 		return pContext->ThrowNativeError("Receiver client %d is not connected", params[1]);
   317 	}
   318 
   319 	player = playerhelpers->GetGamePlayer(params[2]);
   320 	if (player == NULL)
   321 	{
   322 		return pContext->ThrowNativeError("Sender client index %d is invalid", params[2]);
   323 	}
   324 	else if (!player->IsConnected())
   325 	{
   326 		return pContext->ThrowNativeError("Sender client %d is not connected", params[2]);
   327 	}
   328 
   329 	return g_VoiceMap[params[1]][params[2]];
   330 }
   331 
   332 static cell_t IsClientMuted(IPluginContext *pContext, const cell_t *params)
   333 {
   334 	IGamePlayer *player;
   335 
   336 	player = playerhelpers->GetGamePlayer(params[1]);
   337 	if (player == NULL)
   338 	{
   339 		return pContext->ThrowNativeError("Muter client index %d is invalid", params[1]);
   340 	}
   341 	else if (!player->IsConnected())
   342 	{
   343 		return pContext->ThrowNativeError("Muter client %d is not connected", params[1]);
   344 	}
   345 
   346 	player = playerhelpers->GetGamePlayer(params[2]);
   347 	if (player == NULL)
   348 	{
   349 		return pContext->ThrowNativeError("Mutee client index %d is invalid", params[2]);
   350 	}
   351 	else if (!player->IsConnected())
   352 	{
   353 		return pContext->ThrowNativeError("Mutee client %d is not connected", params[2]);
   354 	}
   355 
   356 	return g_ClientMutes[params[1]][params[2]];
   357 }
   358 
   359 sp_nativeinfo_t g_VoiceNatives[] =
   360 {
   361 	{"SetClientListeningFlags",		SetClientListeningFlags},
   362 	{"GetClientListeningFlags",		GetClientListeningFlags},
   363 	{"SetClientListening",			SetClientListening},
   364 	{"GetClientListening",			GetClientListening},
   365 	{"SetListenOverride",			SetClientListening},
   366 	{"GetListenOverride",			GetClientListening},
   367 	{"IsClientMuted",				IsClientMuted},
   368 	{NULL,							NULL},
   369 };