added datamap property offset native
authorBorja Ferrer <faluco@alliedmods.net>
Thu Apr 12 00:45:53 2007 +0000 (2007-04-12)
changeset 682a70055d6d3b3
parent 681 02f6c1c5fd06
child 683 9b21f0ce2b09
added datamap property offset native
added clientofuserid native for fast userid to client id translation
fixed a memory leek (some tries weren't being deleted)
added createdialog native
added 2 million inetchannelinfo wrappers
core/HalfLife2.cpp
core/HalfLife2.h
core/PlayerManager.cpp
core/PlayerManager.h
core/msvc8/sourcemod_mm.vcproj
core/smn_entities.cpp
core/smn_halflife.cpp
core/smn_keyvalues.cpp
core/smn_player.cpp
core/sourcemod.h
plugins/include/clients.inc
plugins/include/entity.inc
plugins/include/sourcemod.inc
public/IForwardSys.h
public/IPlayerHelpers.h
public/ISourceMod.h
     1.1 --- a/core/HalfLife2.cpp	Wed Apr 11 20:07:20 2007 +0000
     1.2 +++ b/core/HalfLife2.cpp	Thu Apr 12 00:45:53 2007 +0000
     1.3 @@ -17,6 +17,21 @@
     1.4  
     1.5  CHalfLife2 g_HL2;
     1.6  
     1.7 +namespace SourceHook
     1.8 +{
     1.9 +	template<>
    1.10 +	int HashFunction<datamap_t *>(datamap_t * const &k)
    1.11 +	{
    1.12 +		return reinterpret_cast<int>(k);
    1.13 +	}
    1.14 +
    1.15 +	template<>
    1.16 +	int Compare<datamap_t *>(datamap_t * const &k1, datamap_t * const &k2)
    1.17 +	{
    1.18 +		return (k1-k2);
    1.19 +	}
    1.20 +}
    1.21 +
    1.22  CHalfLife2::CHalfLife2()
    1.23  {
    1.24  	m_pClasses = sm_trie_create();
    1.25 @@ -36,6 +51,18 @@
    1.26  	}
    1.27  
    1.28  	m_Tables.clear();
    1.29 +
    1.30 +	THash<datamap_t *, DataMapTrie>::iterator h_iter;
    1.31 +	for (h_iter=m_Maps.begin(); h_iter!=m_Maps.end(); h_iter++)
    1.32 +	{
    1.33 +		if (h_iter->val.trie)
    1.34 +		{
    1.35 +			sm_trie_destroy(h_iter->val.trie);
    1.36 +			h_iter->val.trie = NULL;
    1.37 +		}
    1.38 +	}
    1.39 +
    1.40 +	m_Maps.clear();
    1.41  }
    1.42  
    1.43  CSharedEdictChangeInfo *g_pSharedChangeInfo = NULL;
    1.44 @@ -85,6 +112,31 @@
    1.45  	return NULL;
    1.46  }
    1.47  
    1.48 +typedescription_t *UTIL_FindInDataMap(datamap_t *pMap, const char *name)
    1.49 +{
    1.50 +	while (pMap)
    1.51 +	{
    1.52 +		for (int i=0; i<pMap->dataNumFields; i++)
    1.53 +		{
    1.54 +			if (strcmp(name, pMap->dataDesc[i].fieldName) == 0)
    1.55 +			{
    1.56 +				return &(pMap->dataDesc[i]);
    1.57 +			}
    1.58 +			if (pMap->dataDesc[i].td)
    1.59 +			{
    1.60 +				typedescription_t *_td;
    1.61 +				if ((_td=UTIL_FindInDataMap(pMap->dataDesc[i].td, name)) != NULL)
    1.62 +				{
    1.63 +					return _td;
    1.64 +				}
    1.65 +			}
    1.66 +		}
    1.67 +		pMap = pMap->baseMap;
    1.68 +	}
    1.69 +
    1.70 +	return NULL; 
    1.71 +}
    1.72 +
    1.73  ServerClass *CHalfLife2::FindServerClass(const char *classname)
    1.74  {
    1.75  	DataTableInfo *pInfo = _FindServerClass(classname);
    1.76 @@ -111,6 +163,7 @@
    1.77  				pInfo->lookup = sm_trie_create();
    1.78  				pInfo->sc = sc;
    1.79  				sm_trie_insert(m_pClasses, classname, pInfo);
    1.80 +				m_Tables.push_back(pInfo);
    1.81  				break;
    1.82  			}
    1.83  			sc = sc->m_pNext;
    1.84 @@ -133,7 +186,7 @@
    1.85  		return NULL;
    1.86  	}
    1.87  
    1.88 -	SendProp *pProp;
    1.89 +	SendProp *pProp = NULL;
    1.90  	if (!sm_trie_retrieve(pInfo->lookup, offset, (void **)&pProp))
    1.91  	{
    1.92  		if ((pProp = UTIL_FindInSendTable(pInfo->sc->m_pTable, offset)) != NULL)
    1.93 @@ -144,3 +197,23 @@
    1.94  
    1.95  	return pProp;
    1.96  }
    1.97 +
    1.98 +typedescription_t *CHalfLife2::FindInDataMap(datamap_t *pMap, const char *offset)
    1.99 +{
   1.100 +	typedescription_t *td = NULL;
   1.101 +	DataMapTrie &val = m_Maps[pMap];
   1.102 +
   1.103 +	if (!val.trie)
   1.104 +	{
   1.105 +		val.trie = sm_trie_create();
   1.106 +	}
   1.107 +	if (!sm_trie_retrieve(val.trie, offset, (void **)&td))
   1.108 +	{
   1.109 +		if ((td = UTIL_FindInDataMap(pMap, offset)) != NULL)
   1.110 +		{
   1.111 +			sm_trie_insert(val.trie, offset, td);
   1.112 +		}
   1.113 +	}
   1.114 +
   1.115 +	return td;
   1.116 +}
     2.1 --- a/core/HalfLife2.h	Wed Apr 11 20:07:20 2007 +0000
     2.2 +++ b/core/HalfLife2.h	Thu Apr 12 00:45:53 2007 +0000
     2.3 @@ -16,10 +16,12 @@
     2.4  #define _INCLUDE_SOURCEMOD_CHALFLIFE2_H_
     2.5  
     2.6  #include <sh_list.h>
     2.7 +#include <sh_tinyhash.h>
     2.8  #include "sm_trie.h"
     2.9  #include "sm_globals.h"
    2.10  #include "dt_send.h"
    2.11  #include "server_class.h"
    2.12 +#include "datamap.h"
    2.13  
    2.14  using namespace SourceHook;
    2.15  
    2.16 @@ -29,6 +31,12 @@
    2.17  	Trie *lookup;
    2.18  };
    2.19  
    2.20 +struct DataMapTrie
    2.21 +{
    2.22 +	DataMapTrie() : trie(NULL) {}
    2.23 +	Trie *trie;
    2.24 +};
    2.25 +
    2.26  class CHalfLife2 : public SMGlobalClass
    2.27  {
    2.28  public:
    2.29 @@ -40,11 +48,13 @@
    2.30  public:
    2.31  	SendProp *FindInSendTable(const char *classname, const char *offset);
    2.32  	ServerClass *FindServerClass(const char *classname);
    2.33 +	typedescription_t *FindInDataMap(datamap_t *pMap, const char *offset);
    2.34  private:
    2.35  	DataTableInfo *_FindServerClass(const char *classname);
    2.36  private:
    2.37  	Trie *m_pClasses;
    2.38  	List<DataTableInfo *> m_Tables;
    2.39 +	THash<datamap_t *, DataMapTrie> m_Maps;
    2.40  };
    2.41  
    2.42  extern CHalfLife2 g_HL2;
     3.1 --- a/core/PlayerManager.cpp	Wed Apr 11 20:07:20 2007 +0000
     3.2 +++ b/core/PlayerManager.cpp	Thu Apr 12 00:45:53 2007 +0000
     3.3 @@ -31,11 +31,15 @@
     3.4  {
     3.5  	m_AuthQueue = NULL;
     3.6  	m_FirstPass = true;
     3.7 +
     3.8 +	m_UserIdLookUp = new int[USHRT_MAX];
     3.9 +	memset(m_UserIdLookUp, 0, sizeof(int) * USHRT_MAX);
    3.10  }
    3.11  
    3.12  PlayerManager::~PlayerManager()
    3.13  {
    3.14  	delete [] m_AuthQueue;
    3.15 +	delete [] m_UserIdLookUp;
    3.16  }
    3.17  
    3.18  void PlayerManager::OnSourceModAllInitialized()
    3.19 @@ -212,6 +216,8 @@
    3.20  		RETURN_META_VALUE(MRES_SUPERCEDE, false);
    3.21  	}
    3.22  
    3.23 +	m_UserIdLookUp[engine->GetPlayerUserId(pEntity)] = client;
    3.24 +
    3.25  	return true;
    3.26  }
    3.27  
    3.28 @@ -353,6 +359,7 @@
    3.29  	}
    3.30  
    3.31  	m_Players[client].Disconnect();
    3.32 +	m_UserIdLookUp[engine->GetPlayerUserId(pEntity)] = 0;
    3.33  }
    3.34  
    3.35  void PlayerManager::OnClientDisconnect_Post(edict_t *pEntity)
    3.36 @@ -425,6 +432,11 @@
    3.37  	return m_PlayerCount;
    3.38  }
    3.39  
    3.40 +int PlayerManager::GetClientOfUserId(int userid)
    3.41 +{
    3.42 +	return (userid < 0 || userid > USHRT_MAX) ? 0 : m_UserIdLookUp[userid];
    3.43 +}
    3.44 +
    3.45  void PlayerManager::AddClientListener(IClientListener *listener)
    3.46  {
    3.47  	m_hooks.push_back(listener);
     4.1 --- a/core/PlayerManager.h	Wed Apr 11 20:07:20 2007 +0000
     4.2 +++ b/core/PlayerManager.h	Thu Apr 12 00:45:53 2007 +0000
     4.3 @@ -95,6 +95,7 @@
     4.4  	IGamePlayer *GetGamePlayer(edict_t *pEdict);
     4.5  	int GetMaxClients();
     4.6  	int GetNumPlayers();
     4.7 +	int GetClientOfUserId(int userid);
     4.8  public:
     4.9  	inline int MaxClients()
    4.10  	{
    4.11 @@ -117,6 +118,7 @@
    4.12  	IForward *m_clauth;
    4.13  	IForward *m_onActivate;
    4.14  	CPlayer *m_Players;
    4.15 +	int *m_UserIdLookUp;
    4.16  	int m_maxClients;
    4.17  	int m_PlayerCount;
    4.18  	bool m_FirstPass;
     5.1 --- a/core/msvc8/sourcemod_mm.vcproj	Wed Apr 11 20:07:20 2007 +0000
     5.2 +++ b/core/msvc8/sourcemod_mm.vcproj	Thu Apr 12 00:45:53 2007 +0000
     5.3 @@ -1,7 +1,7 @@
     5.4  <?xml version="1.0" encoding="Windows-1252"?>
     5.5  <VisualStudioProject
     5.6  	ProjectType="Visual C++"
     5.7 -	Version="8.00"
     5.8 +	Version="8,00"
     5.9  	Name="sourcemod_mm"
    5.10  	ProjectGUID="{E39527CD-7CAB-4420-97CC-DA1B93B260BC}"
    5.11  	RootNamespace="sourcemod_mm"
     6.1 --- a/core/smn_entities.cpp	Wed Apr 11 20:07:20 2007 +0000
     6.2 +++ b/core/smn_entities.cpp	Thu Apr 12 00:45:53 2007 +0000
     6.3 @@ -18,6 +18,7 @@
     6.4  #include "server_class.h"
     6.5  #include "PlayerManager.h"
     6.6  #include "HalfLife2.h"
     6.7 +#include "GameConfigs.h"
     6.8  
     6.9  inline edict_t *GetEdict(cell_t num)
    6.10  {
    6.11 @@ -61,6 +62,46 @@
    6.12  	return pEdict;
    6.13  }
    6.14  
    6.15 +class VEmptyClass {};
    6.16 +datamap_t *VGetDataDescMap(CBaseEntity *pThisPtr, int offset)
    6.17 +{
    6.18 +	void **this_ptr = *reinterpret_cast<void ***>(&pThisPtr);
    6.19 +	void **vtable = *reinterpret_cast<void ***>(pThisPtr);
    6.20 +	void *vfunc = vtable[offset];
    6.21 +
    6.22 +	union
    6.23 +	{
    6.24 +		datamap_t *(VEmptyClass::*mfpnew)();
    6.25 +#ifndef PLATFORM_POSIX
    6.26 +		void *addr;
    6.27 +	} u;
    6.28 +	u.addr = vfunc;
    6.29 +#else
    6.30 +		struct  
    6.31 +		{
    6.32 +			void *addr;
    6.33 +			intptr_t adjustor;
    6.34 +		} s;
    6.35 +	} u;
    6.36 +	u.s.addr = vfunc;
    6.37 +	u.s.adjustor = 0;
    6.38 +#endif
    6.39 +
    6.40 +	return (datamap_t *)(reinterpret_cast<VEmptyClass *>(this_ptr)->*u.mfpnew)();
    6.41 +}
    6.42 +
    6.43 +inline datamap_t *CBaseEntity_GetDataDescMap(CBaseEntity *pEntity)
    6.44 +{
    6.45 +	int offset;
    6.46 +
    6.47 +	if (!g_pGameConf->GetOffset("GetDataDescMap", &offset) || !offset)
    6.48 +	{
    6.49 +		return NULL;
    6.50 +	}
    6.51 +
    6.52 +	return VGetDataDescMap(pEntity, offset);
    6.53 +}
    6.54 +
    6.55  static cell_t GetMaxEntities(IPluginContext *pContext, const cell_t *params)
    6.56  {
    6.57  	return gpGlobals->maxEntities;
    6.58 @@ -494,6 +535,33 @@
    6.59  	return pSend->GetOffset();
    6.60  }
    6.61  
    6.62 +static cell_t FindDataMapOffs(IPluginContext *pContext, const cell_t *params)
    6.63 +{
    6.64 +	CBaseEntity *pEntity;
    6.65 +	datamap_t *pMap;
    6.66 +	typedescription_t *td;
    6.67 +	char *offset;
    6.68 +	edict_t *pEdict = GetEntity(params[1], &pEntity);
    6.69 +
    6.70 +	if (!pEdict || !pEntity)
    6.71 +	{
    6.72 +		return pContext->ThrowNativeError("Entity %d is invalid", params[1]);
    6.73 +	}
    6.74 +
    6.75 +	if ((pMap=CBaseEntity_GetDataDescMap(pEntity)) == NULL)
    6.76 +	{
    6.77 +		return pContext->ThrowNativeError("Unable to retrieve GetDataDescMap offset");
    6.78 +	}
    6.79 +
    6.80 +	pContext->LocalToString(params[2], &offset);
    6.81 +	if ((td=g_HL2.FindInDataMap(pMap, offset)) == NULL)
    6.82 +	{
    6.83 +		return -1;
    6.84 +	}
    6.85 +
    6.86 +	return td->fieldOffset[TD_OFFSET_NORMAL];
    6.87 +}
    6.88 +
    6.89  REGISTER_NATIVES(entityNatives)
    6.90  {
    6.91  	{"ChangeEdictState",		ChangeEdictState},
    6.92 @@ -517,5 +585,6 @@
    6.93  	{"SetEntDataEnt",			SetEntDataEnt},
    6.94  	{"SetEntDataFloat",			SetEntDataFloat},
    6.95  	{"SetEntDataVector",		SetEntDataVector},
    6.96 +	{"FindDataMapOffs",			FindDataMapOffs},
    6.97  	{NULL,						NULL}
    6.98  };
     7.1 --- a/core/smn_halflife.cpp	Wed Apr 11 20:07:20 2007 +0000
     7.2 +++ b/core/smn_halflife.cpp	Thu Apr 12 00:45:53 2007 +0000
     7.3 @@ -16,6 +16,18 @@
     7.4  #include "sourcemod.h"
     7.5  #include "sourcemm_api.h"
     7.6  #include "PlayerManager.h"
     7.7 +#include "HandleSys.h"
     7.8 +
     7.9 +IServerPluginCallbacks *g_VSP = NULL;
    7.10 +
    7.11 +class HalfLifeNatives : public SMGlobalClass
    7.12 +{
    7.13 +public: //SMGlobalClass
    7.14 +	void OnSourceModVSPReceived(IServerPluginCallbacks *iface)
    7.15 +	{
    7.16 +		g_VSP = iface;
    7.17 +	}
    7.18 +};
    7.19  
    7.20  static cell_t SetRandomSeed(IPluginContext *pContext, const cell_t *params)
    7.21  {
    7.22 @@ -229,6 +241,36 @@
    7.23  	return 1;
    7.24  }
    7.25  
    7.26 +static cell_t smn_CreateDialog(IPluginContext *pContext, const cell_t *params)
    7.27 +{
    7.28 +	KeyValues *pKV;
    7.29 +	HandleError herr;
    7.30 +	Handle_t hndl = static_cast<Handle_t>(params[2]);
    7.31 +	CPlayer *pPlayer = g_Players.GetPlayerByIndex(params[1]);
    7.32 +
    7.33 +	if (!pPlayer)
    7.34 +	{
    7.35 +		return pContext->ThrowNativeError("Player %d is not a valid player", params[1]);
    7.36 +	}
    7.37 +
    7.38 +	if (!pPlayer->IsConnected())
    7.39 +	{
    7.40 +		return pContext->ThrowNativeError("Player %d is not connected", params[1]);
    7.41 +	}
    7.42 +
    7.43 +	pKV = g_SourceMod.ReadKeyValuesHandle(hndl, &herr, true);
    7.44 +	if (herr != HandleError_None)
    7.45 +	{
    7.46 +		return pContext->ThrowNativeError("Invalid key value handle %x (error %d)", hndl, herr);
    7.47 +	}
    7.48 +
    7.49 +	serverpluginhelpers->CreateMessage(pPlayer->GetEdict(), static_cast<DIALOG_TYPE>(params[3]), pKV, g_VSP);
    7.50 +
    7.51 +	return 1;
    7.52 +}
    7.53 +
    7.54 +static HalfLifeNatives s_HalfLifeNatives;
    7.55 +
    7.56  REGISTER_NATIVES(halflifeNatives)
    7.57  {
    7.58  	{"CreateFakeClient",		CreateFakeClient},
    7.59 @@ -252,5 +294,6 @@
    7.60  	{"PrecacheSound",			PrecacheSound},
    7.61  	{"IsSoundPrecached",		IsSoundPrecached},
    7.62  	{"FakeClientCommand",		FakeClientCommand},
    7.63 +	{"CreateDialog",			smn_CreateDialog},
    7.64  	{NULL,						NULL},
    7.65  };
     8.1 --- a/core/smn_keyvalues.cpp	Wed Apr 11 20:07:20 2007 +0000
     8.2 +++ b/core/smn_keyvalues.cpp	Thu Apr 12 00:45:53 2007 +0000
     8.3 @@ -48,6 +48,33 @@
     8.4  	}
     8.5  };
     8.6  
     8.7 +KeyValues *SourceModBase::ReadKeyValuesHandle(Handle_t hndl, HandleError *err, bool root)
     8.8 +{
     8.9 +	HandleError herr;
    8.10 +	HandleSecurity sec;
    8.11 +	KeyValueStack *pStk;
    8.12 +
    8.13 +	sec.pOwner = NULL;
    8.14 +	sec.pIdentity = g_pCoreIdent;
    8.15 +
    8.16 +	if ((herr=g_HandleSys.ReadHandle(hndl, g_KeyValueType, &sec, (void **)&pStk))
    8.17 +		!= HandleError_None)
    8.18 +	{
    8.19 +		if (err)
    8.20 +		{
    8.21 +			*err = herr;
    8.22 +		}
    8.23 +		return NULL;
    8.24 +	}
    8.25 +
    8.26 +	if (err)
    8.27 +	{
    8.28 +		*err = HandleError_None;
    8.29 +	}
    8.30 +
    8.31 +	return (root) ? pStk->pBase : pStk->pCurRoot.front();
    8.32 +}
    8.33 +
    8.34  static cell_t smn_KvSetString(IPluginContext *pCtx, const cell_t *params)
    8.35  {
    8.36  	Handle_t hndl = static_cast<Handle_t>(params[1]);
     9.1 --- a/core/smn_player.cpp	Wed Apr 11 20:07:20 2007 +0000
     9.2 +++ b/core/smn_player.cpp	Thu Apr 12 00:45:53 2007 +0000
     9.3 @@ -15,6 +15,7 @@
     9.4  #include "PlayerManager.h"
     9.5  #include "AdminCache.h"
     9.6  #include "sm_stringutil.h"
     9.7 +#include <inetchannelinfo.h>
     9.8  
     9.9  static cell_t sm_GetClientCount(IPluginContext *pCtx, const cell_t *params)
    9.10  {
    9.11 @@ -637,6 +638,182 @@
    9.12  	return pInfo->GetHealth();
    9.13  }
    9.14  
    9.15 +static cell_t GetTimeConnected(IPluginContext *pContext, const cell_t *params)
    9.16 +{
    9.17 +	int client = params[1];
    9.18 +
    9.19 +	CPlayer *pPlayer = g_Players.GetPlayerByIndex(client);
    9.20 +	if (!pPlayer)
    9.21 +	{
    9.22 +		return pContext->ThrowNativeError("Player %d is not a valid client", client);
    9.23 +	} else if (!pPlayer->IsInGame()) {
    9.24 +		return pContext->ThrowNativeError("Player %d is not in game", client);
    9.25 +	} else if (pPlayer->IsFakeClient()) {
    9.26 +		return pContext->ThrowNativeError("Player %d is a bot", client);
    9.27 +	}
    9.28 +
    9.29 +	INetChannelInfo *pInfo = engine->GetPlayerNetInfo(client);
    9.30 +
    9.31 +	return sp_ftoc(pInfo->GetTimeConnected());
    9.32 +}
    9.33 +
    9.34 +static cell_t GetDataRate(IPluginContext *pContext, const cell_t *params)
    9.35 +{
    9.36 +	int client = params[1];
    9.37 +
    9.38 +	CPlayer *pPlayer = g_Players.GetPlayerByIndex(client);
    9.39 +	if (!pPlayer)
    9.40 +	{
    9.41 +		return pContext->ThrowNativeError("Player %d is not a valid client", client);
    9.42 +	} else if (!pPlayer->IsInGame()) {
    9.43 +		return pContext->ThrowNativeError("Player %d is not in game", client);
    9.44 +	} else if (pPlayer->IsFakeClient()) {
    9.45 +		return pContext->ThrowNativeError("Player %d is a bot", client);
    9.46 +	}
    9.47 +
    9.48 +	INetChannelInfo *pInfo = engine->GetPlayerNetInfo(client);
    9.49 +
    9.50 +	return pInfo->GetDataRate();
    9.51 +}
    9.52 +
    9.53 +static cell_t IsTimingOut(IPluginContext *pContext, const cell_t *params)
    9.54 +{
    9.55 +	int client = params[1];
    9.56 +
    9.57 +	CPlayer *pPlayer = g_Players.GetPlayerByIndex(client);
    9.58 +	if (!pPlayer)
    9.59 +	{
    9.60 +		return pContext->ThrowNativeError("Player %d is not a valid client", client);
    9.61 +	} else if (!pPlayer->IsInGame()) {
    9.62 +		return pContext->ThrowNativeError("Player %d is not in game", client);
    9.63 +	} else if (pPlayer->IsFakeClient()) {
    9.64 +		return pContext->ThrowNativeError("Player %d is a bot", client);
    9.65 +	}
    9.66 +
    9.67 +	INetChannelInfo *pInfo = engine->GetPlayerNetInfo(client);
    9.68 +
    9.69 +	return pInfo->IsTimingOut() ? 1 : 0;
    9.70 +}
    9.71 +
    9.72 +static cell_t GetLatency(IPluginContext *pContext, const cell_t *params)
    9.73 +{
    9.74 +	int client = params[1];
    9.75 +
    9.76 +	CPlayer *pPlayer = g_Players.GetPlayerByIndex(client);
    9.77 +	if (!pPlayer)
    9.78 +	{
    9.79 +		return pContext->ThrowNativeError("Player %d is not a valid client", client);
    9.80 +	} else if (!pPlayer->IsInGame()) {
    9.81 +		return pContext->ThrowNativeError("Player %d is not in game", client);
    9.82 +	} else if (pPlayer->IsFakeClient()) {
    9.83 +		return pContext->ThrowNativeError("Player %d is a bot", client);
    9.84 +	}
    9.85 +
    9.86 +	INetChannelInfo *pInfo = engine->GetPlayerNetInfo(client);
    9.87 +
    9.88 +	return sp_ftoc(pInfo->GetLatency(params[2]));
    9.89 +}
    9.90 +
    9.91 +static cell_t GetAvgLatency(IPluginContext *pContext, const cell_t *params)
    9.92 +{
    9.93 +	int client = params[1];
    9.94 +
    9.95 +	CPlayer *pPlayer = g_Players.GetPlayerByIndex(client);
    9.96 +	if (!pPlayer)
    9.97 +	{
    9.98 +		return pContext->ThrowNativeError("Player %d is not a valid client", client);
    9.99 +	} else if (!pPlayer->IsInGame()) {
   9.100 +		return pContext->ThrowNativeError("Player %d is not in game", client);
   9.101 +	} else if (pPlayer->IsFakeClient()) {
   9.102 +		return pContext->ThrowNativeError("Player %d is a bot", client);
   9.103 +	}
   9.104 +
   9.105 +	INetChannelInfo *pInfo = engine->GetPlayerNetInfo(client);
   9.106 +
   9.107 +	return sp_ftoc(pInfo->GetAvgLatency(params[2]));
   9.108 +}
   9.109 +
   9.110 +static cell_t GetAvgLoss(IPluginContext *pContext, const cell_t *params)
   9.111 +{
   9.112 +	int client = params[1];
   9.113 +
   9.114 +	CPlayer *pPlayer = g_Players.GetPlayerByIndex(client);
   9.115 +	if (!pPlayer)
   9.116 +	{
   9.117 +		return pContext->ThrowNativeError("Player %d is not a valid client", client);
   9.118 +	} else if (!pPlayer->IsInGame()) {
   9.119 +		return pContext->ThrowNativeError("Player %d is not in game", client);
   9.120 +	} else if (pPlayer->IsFakeClient()) {
   9.121 +		return pContext->ThrowNativeError("Player %d is a bot", client);
   9.122 +	}
   9.123 +
   9.124 +	INetChannelInfo *pInfo = engine->GetPlayerNetInfo(client);
   9.125 +
   9.126 +	return sp_ftoc(pInfo->GetAvgLoss(params[2]));
   9.127 +}
   9.128 +
   9.129 +static cell_t GetAvgChoke(IPluginContext *pContext, const cell_t *params)
   9.130 +{
   9.131 +	int client = params[1];
   9.132 +
   9.133 +	CPlayer *pPlayer = g_Players.GetPlayerByIndex(client);
   9.134 +	if (!pPlayer)
   9.135 +	{
   9.136 +		return pContext->ThrowNativeError("Player %d is not a valid client", client);
   9.137 +	} else if (!pPlayer->IsInGame()) {
   9.138 +		return pContext->ThrowNativeError("Player %d is not in game", client);
   9.139 +	} else if (pPlayer->IsFakeClient()) {
   9.140 +		return pContext->ThrowNativeError("Player %d is a bot", client);
   9.141 +	}
   9.142 +
   9.143 +	INetChannelInfo *pInfo = engine->GetPlayerNetInfo(client);
   9.144 +
   9.145 +	return sp_ftoc(pInfo->GetAvgChoke(params[2]));
   9.146 +}
   9.147 +
   9.148 +static cell_t GetAvgData(IPluginContext *pContext, const cell_t *params)
   9.149 +{
   9.150 +	int client = params[1];
   9.151 +
   9.152 +	CPlayer *pPlayer = g_Players.GetPlayerByIndex(client);
   9.153 +	if (!pPlayer)
   9.154 +	{
   9.155 +		return pContext->ThrowNativeError("Player %d is not a valid client", client);
   9.156 +	} else if (!pPlayer->IsInGame()) {
   9.157 +		return pContext->ThrowNativeError("Player %d is not in game", client);
   9.158 +	} else if (pPlayer->IsFakeClient()) {
   9.159 +		return pContext->ThrowNativeError("Player %d is a bot", client);
   9.160 +	}
   9.161 +
   9.162 +	INetChannelInfo *pInfo = engine->GetPlayerNetInfo(client);
   9.163 +
   9.164 +	return sp_ftoc(pInfo->GetAvgData(params[2]));
   9.165 +}
   9.166 +
   9.167 +static cell_t GetAvgPackets(IPluginContext *pContext, const cell_t *params)
   9.168 +{
   9.169 +	int client = params[1];
   9.170 +
   9.171 +	CPlayer *pPlayer = g_Players.GetPlayerByIndex(client);
   9.172 +	if (!pPlayer)
   9.173 +	{
   9.174 +		return pContext->ThrowNativeError("Player %d is not a valid client", client);
   9.175 +	} else if (!pPlayer->IsInGame()) {
   9.176 +		return pContext->ThrowNativeError("Player %d is not in game", client);
   9.177 +	} else if (pPlayer->IsFakeClient()) {
   9.178 +		return pContext->ThrowNativeError("Player %d is a bot", client);
   9.179 +	}
   9.180 +
   9.181 +	INetChannelInfo *pInfo = engine->GetPlayerNetInfo(client);
   9.182 +
   9.183 +	return sp_ftoc(pInfo->GetAvgPackets(params[2]));
   9.184 +}
   9.185 +
   9.186 +static cell_t GetClientOfUserId(IPluginContext *pContext, const cell_t *params)
   9.187 +{
   9.188 +	return g_Players.GetClientOfUserId(params[1]);
   9.189 +}
   9.190 +
   9.191  REGISTER_NATIVES(playernatives)
   9.192  {
   9.193  	{"AddUserFlags",			AddUserFlags},
   9.194 @@ -668,5 +845,15 @@
   9.195  	{"GetClientWeapon",			GetWeaponName},
   9.196  	{"GetClientModel",			GetModelName},
   9.197  	{"GetClientHealth",			GetHealth},
   9.198 +	{"GetTimeConnected",		GetTimeConnected},
   9.199 +	{"GetDataRate",				GetDataRate},
   9.200 +	{"IsTimingOut",				IsTimingOut},
   9.201 +	{"GetLatency",				GetLatency},
   9.202 +	{"GetAvgLatency",			GetAvgLatency},
   9.203 +	{"GetAvgLoss",				GetAvgLoss},
   9.204 +	{"GetAvgChoke",				GetAvgChoke},
   9.205 +	{"GetAvgData",				GetAvgData},
   9.206 +	{"GetAvgPackets",			GetAvgPackets},
   9.207 +	{"GetClientOfUserId",		GetClientOfUserId},
   9.208  	{NULL,						NULL}
   9.209  };
    10.1 --- a/core/sourcemod.h	Wed Apr 11 20:07:20 2007 +0000
    10.2 +++ b/core/sourcemod.h	Thu Apr 12 00:45:53 2007 +0000
    10.3 @@ -93,6 +93,7 @@
    10.4  	IDataPack *CreateDataPack();
    10.5  	void FreeDataPack(IDataPack *pack);
    10.6  	HandleType_t GetDataPackHandleType(bool readonly=false);
    10.7 +	KeyValues *ReadKeyValuesHandle(Handle_t hndl, HandleError *err=NULL, bool root=false);
    10.8  private:
    10.9  	/**
   10.10  	 * @brief Loading plugins
    11.1 --- a/plugins/include/clients.inc	Wed Apr 11 20:07:20 2007 +0000
    11.2 +++ b/plugins/include/clients.inc	Thu Apr 12 00:45:53 2007 +0000
    11.3 @@ -18,6 +18,13 @@
    11.4  #endif
    11.5  #define _clients_included
    11.6  
    11.7 +enum NetFlow
    11.8 +{
    11.9 +	NetFlow_Outgoing = 0,		/**< Outgoing traffic */
   11.10 +	NetFlow_Incoming,		/**< Incoming traffic */
   11.11 +	NetFlow_Both			/**< Incoming and outgoing traffic */
   11.12 +};
   11.13 +
   11.14  /**
   11.15   * Called on client connection.
   11.16   *
   11.17 @@ -380,3 +387,110 @@
   11.18   * @error				Invalid client index, client not in game, or no mod support.
   11.19   */
   11.20  native GetClientFrags(client);
   11.21 +
   11.22 +/**
   11.23 + * Returns the client's send data rate in bytes/sec.
   11.24 + *
   11.25 + * @param client		Player's index.
   11.26 + * @return				Data rate.
   11.27 + * @error				Invalid client index, client not in game, or fake client.
   11.28 + */
   11.29 +native GetDataRate(client);
   11.30 +
   11.31 +/**
   11.32 + * Returns if a client is timing out
   11.33 + *
   11.34 + * @param client		Player's index.
   11.35 + * @return				True if client is timing out, false otherwise.
   11.36 + * @error				Invalid client index, client not in game, or fake client.
   11.37 + */
   11.38 +native bool:IsTimingOut(client);
   11.39 +
   11.40 +/**
   11.41 + * Returns the client's connection time in seconds.
   11.42 + *
   11.43 + * @param client		Player's index.
   11.44 + * @return				Connection time.
   11.45 + * @error				Invalid client index, client not in game, or fake client.
   11.46 + */
   11.47 +native Float:GetTimeConnected(client);
   11.48 +
   11.49 +/**
   11.50 + * Returns the client's current latency (RTT), more accurate than GetAvgLatency but jittering.
   11.51 + *
   11.52 + * @param client		Player's index.
   11.53 + * @param flow			Traffic flowing direction.
   11.54 + * @return				Latency.
   11.55 + * @error				Invalid client index, client not in game, or fake client.
   11.56 + */
   11.57 +native Float:GetLatency(client, NetFlow:flow);
   11.58 +
   11.59 +/**
   11.60 + * Returns the client's average packet latency in seconds.
   11.61 + *
   11.62 + * @param client		Player's index.
   11.63 + * @param flow			Traffic flowing direction.
   11.64 + * @return				Average latency.
   11.65 + * @error				Invalid client index, client not in game, or fake client.
   11.66 + */
   11.67 +native Float:GetAvgLatency(client, NetFlow:flow);
   11.68 +
   11.69 +/**
   11.70 + * Returns the client's average packet loss, values go from 0 to 1 (for percentages).
   11.71 + *
   11.72 + * @param client		Player's index.
   11.73 + * @param flow			Traffic flowing direction.
   11.74 + * @return				Average packet loss.
   11.75 + * @error				Invalid client index, client not in game, or fake client.
   11.76 + */
   11.77 +native Float:GetAvgLoss(client, NetFlow:flow);
   11.78 +
   11.79 +/**
   11.80 + * Returns the client's average packet choke, values go from 0 to 1 (for percentages).
   11.81 + *
   11.82 + * @param client		Player's index.
   11.83 + * @param flow			Traffic flowing direction.
   11.84 + * @return				Average packet choke.
   11.85 + * @error				Invalid client index, client not in game, or fake client.
   11.86 + */
   11.87 +native Float:GetAvgChoke(client, NetFlow:flow);
   11.88 +
   11.89 +/**
   11.90 + * Returns the client's data flow in bytes/sec.
   11.91 + *
   11.92 + * @param client		Player's index.
   11.93 + * @param flow			Traffic flowing direction.
   11.94 + * @return				Data flow.
   11.95 + * @error				Invalid client index, client not in game, or fake client.
   11.96 + */
   11.97 +native Float:GetAvgData(client, NetFlow:flow);
   11.98 +
   11.99 +/**
  11.100 + * Returns the client's average packet frequency in packets/sec.
  11.101 + *
  11.102 + * @param client		Player's index.
  11.103 + * @param flow			Traffic flowing direction.
  11.104 + * @return				Packet frequency.
  11.105 + * @error				Invalid client index, client not in game, or fake client.
  11.106 + */
  11.107 +native Float:GetAvgPackets(client, NetFlow:flow);
  11.108 +
  11.109 +/**
  11.110 + * Translates an userid index to the real player index.
  11.111 + *
  11.112 + * @param userid		Userid value.
  11.113 + * @return				Client value.
  11.114 + * @error				Returns 0 if invalid userid.
  11.115 + */
  11.116 +native GetClientOfUserId(userid);
  11.117 +
  11.118 +/**
  11.119 + * Executes a client command on the server without being networked.
  11.120 + *
  11.121 + * @param client		Index of the client.
  11.122 + * @param fmt			Format of the client command.
  11.123 + * @param ...			Format parameters
  11.124 + * @noreturn
  11.125 + * @error				Invalid client index, or client not connected.
  11.126 + */
  11.127 +native FakeClientCommand(client, const String:fmt[], any:...);
    12.1 --- a/plugins/include/entity.inc	Wed Apr 11 20:07:20 2007 +0000
    12.2 +++ b/plugins/include/entity.inc	Thu Apr 12 00:45:53 2007 +0000
    12.3 @@ -290,6 +290,16 @@
    12.4  native FindSendPropOffs(const String:cls[], const String:prop[]);
    12.5  
    12.6  /**
    12.7 + * Given an entity, finds a datamap property offset.
    12.8 + * This information is cached for future calls.
    12.9 + *
   12.10 + * @param entity		Entity index.
   12.11 + * @param prop			Property name.
   12.12 + * @return				An offset, or -1 on failure.
   12.13 + */
   12.14 +native FindDataMapOffs(entity, const String:prop[]);
   12.15 +
   12.16 +/**
   12.17   * Wrapper function for finding a send property for a particular entity.
   12.18   *
   12.19   * @param ent			Entity index.
   12.20 @@ -320,11 +330,29 @@
   12.21   */
   12.22  stock GetEntProp(entity, PropType:type, const String:prop[], size=4)
   12.23  {
   12.24 -	new offs = GetEntSendPropOffs(entity, prop);
   12.25 +	new offs;
   12.26 +
   12.27 +	switch (type)
   12.28 +	{
   12.29 +		case Prop_Send:
   12.30 +		{
   12.31 +			offs = GetEntSendPropOffs(entity, prop);
   12.32 +		}
   12.33 +		case Prop_Data:
   12.34 +		{
   12.35 +			offs = FindDataMapOffs(entity, prop);
   12.36 +		}
   12.37 +		default:
   12.38 +		{
   12.39 +			ThrowError("Invalid Property type %d", type);
   12.40 +		}
   12.41 +	}
   12.42 +
   12.43  	if (offs == -1)
   12.44  	{
   12.45  		ThrowError("Property \"%s\" not found for entity %d", prop, entity);
   12.46  	}
   12.47 +
   12.48  	return GetEntData(entity, offs, size);
   12.49  }
   12.50  
   12.51 @@ -340,11 +368,29 @@
   12.52   */
   12.53  stock SetEntProp(entity, PropType:type, const String:prop[], value, size=4)
   12.54  {
   12.55 -	new offs = GetEntSendPropOffs(entity, prop);
   12.56 +	new offs;
   12.57 +
   12.58 +	switch (type)
   12.59 +	{
   12.60 +		case Prop_Send:
   12.61 +		{
   12.62 +			offs = GetEntSendPropOffs(entity, prop);
   12.63 +		}
   12.64 +		case Prop_Data:
   12.65 +		{
   12.66 +			offs = FindDataMapOffs(entity, prop);
   12.67 +		}
   12.68 +		default:
   12.69 +		{
   12.70 +			ThrowError("Invalid Property type %d", type);
   12.71 +		}
   12.72 +	}
   12.73 +
   12.74  	if (offs == -1)
   12.75  	{
   12.76  		ThrowError("Property \"%s\" not found for entity %d", prop, entity);
   12.77  	}
   12.78 +
   12.79  	return SetEntData(entity, offs, value, size, true);
   12.80  }
   12.81  
   12.82 @@ -359,11 +405,29 @@
   12.83   */
   12.84  stock Float:GetEntPropFloat(entity, PropType:type, const String:prop[])
   12.85  {
   12.86 -	new offs = GetEntSendPropOffs(entity, prop);
   12.87 +	new offs;
   12.88 +
   12.89 +	switch (type)
   12.90 +	{
   12.91 +		case Prop_Send:
   12.92 +		{
   12.93 +			offs = GetEntSendPropOffs(entity, prop);
   12.94 +		}
   12.95 +		case Prop_Data:
   12.96 +		{
   12.97 +			offs = FindDataMapOffs(entity, prop);
   12.98 +		}
   12.99 +		default:
  12.100 +		{
  12.101 +			ThrowError("Invalid Property type %d", type);
  12.102 +		}
  12.103 +	}
  12.104 +
  12.105  	if (offs == -1)
  12.106  	{
  12.107  		ThrowError("Property \"%s\" not found for entity %d", prop, entity);
  12.108  	}
  12.109 +
  12.110  	return GetEntDataFloat(entity, offs);
  12.111  }
  12.112  
  12.113 @@ -379,11 +443,29 @@
  12.114   */
  12.115  stock SetEntPropFloat(entity, PropType:type, const String:prop[], Float:value)
  12.116  {
  12.117 -	new offs = GetEntSendPropOffs(entity, prop);
  12.118 +	new offs;
  12.119 +
  12.120 +	switch (type)
  12.121 +	{
  12.122 +		case Prop_Send:
  12.123 +		{
  12.124 +			offs = GetEntSendPropOffs(entity, prop);
  12.125 +		}
  12.126 +		case Prop_Data:
  12.127 +		{
  12.128 +			offs = FindDataMapOffs(entity, prop);
  12.129 +		}
  12.130 +		default:
  12.131 +		{
  12.132 +			ThrowError("Invalid Property type %d", type);
  12.133 +		}
  12.134 +	}
  12.135 +
  12.136  	if (offs == -1)
  12.137  	{
  12.138  		ThrowError("Property \"%s\" not found for entity %d", prop, entity);
  12.139  	}
  12.140 +
  12.141  	return SetEntDataFloat(entity, offs, value, true);
  12.142  }
  12.143  
  12.144 @@ -398,11 +480,29 @@
  12.145   */
  12.146  stock GetEntPropEnt(entity, PropType:type, const String:prop[])
  12.147  {
  12.148 -	new offs = GetEntSendPropOffs(entity, prop);
  12.149 +	new offs;
  12.150 +
  12.151 +	switch (type)
  12.152 +	{
  12.153 +		case Prop_Send:
  12.154 +		{
  12.155 +			offs = GetEntSendPropOffs(entity, prop);
  12.156 +		}
  12.157 +		case Prop_Data:
  12.158 +		{
  12.159 +			offs = FindDataMapOffs(entity, prop);
  12.160 +		}
  12.161 +		default:
  12.162 +		{
  12.163 +			ThrowError("Invalid Property type %d", type);
  12.164 +		}
  12.165 +	}
  12.166 +
  12.167  	if (offs == -1)
  12.168  	{
  12.169  		ThrowError("Property \"%s\" not found for entity %d", prop, entity);
  12.170  	}
  12.171 +
  12.172  	return GetEntDataEnt(entity, offs);
  12.173  }
  12.174  
  12.175 @@ -418,11 +518,29 @@
  12.176   */
  12.177  stock SetEntPropEnt(entity, PropType:type, const String:prop[], other)
  12.178  {
  12.179 -	new offs = GetEntSendPropOffs(entity, prop);
  12.180 +	new offs;
  12.181 +
  12.182 +	switch (type)
  12.183 +	{
  12.184 +		case Prop_Send:
  12.185 +		{
  12.186 +			offs = GetEntSendPropOffs(entity, prop);
  12.187 +		}
  12.188 +		case Prop_Data:
  12.189 +		{
  12.190 +			offs = FindDataMapOffs(entity, prop);
  12.191 +		}
  12.192 +		default:
  12.193 +		{
  12.194 +			ThrowError("Invalid Property type %d", type);
  12.195 +		}
  12.196 +	}
  12.197 +
  12.198  	if (offs == -1)
  12.199  	{
  12.200  		ThrowError("Property \"%s\" not found for entity %d", prop, entity);
  12.201  	}
  12.202 +
  12.203  	return SetEntDataEnt(entity, offs, other, true);
  12.204  }
  12.205  
  12.206 @@ -440,11 +558,29 @@
  12.207   */
  12.208  stock GetEntPropVector(entity, PropType:type, const String:prop[], Float:vec[3])
  12.209  {
  12.210 -	new offs = GetEntSendPropOffs(entity, prop);
  12.211 +	new offs;
  12.212 +
  12.213 +	switch (type)
  12.214 +	{
  12.215 +		case Prop_Send:
  12.216 +		{
  12.217 +			offs = GetEntSendPropOffs(entity, prop);
  12.218 +		}
  12.219 +		case Prop_Data:
  12.220 +		{
  12.221 +			offs = FindDataMapOffs(entity, prop);
  12.222 +		}
  12.223 +		default:
  12.224 +		{
  12.225 +			ThrowError("Invalid Property type %d", type);
  12.226 +		}
  12.227 +	}
  12.228 +
  12.229  	if (offs == -1)
  12.230  	{
  12.231  		ThrowError("Property \"%s\" not found for entity %d", prop, entity);
  12.232  	}
  12.233 +
  12.234  	return GetEntDataVector(entity, offs, vec);
  12.235  }
  12.236  
  12.237 @@ -462,10 +598,28 @@
  12.238   */
  12.239  stock SetEntPropVector(entity, PropType:type, const String:prop[], const Float:vec[3])
  12.240  {
  12.241 -	new offs = GetEntSendPropOffs(entity, prop);
  12.242 +	new offs;
  12.243 +
  12.244 +	switch (type)
  12.245 +	{
  12.246 +		case Prop_Send:
  12.247 +		{
  12.248 +			offs = GetEntSendPropOffs(entity, prop);
  12.249 +		}
  12.250 +		case Prop_Data:
  12.251 +		{
  12.252 +			offs = FindDataMapOffs(entity, prop);
  12.253 +		}
  12.254 +		default:
  12.255 +		{
  12.256 +			ThrowError("Invalid Property type %d", type);
  12.257 +		}
  12.258 +	}
  12.259 +
  12.260  	if (offs == -1)
  12.261  	{
  12.262  		ThrowError("Property \"%s\" not found for entity %d", prop, entity);
  12.263  	}
  12.264 +
  12.265  	return SetEntDataVector(entity, offs, vec, true);
  12.266  }
    13.1 --- a/plugins/include/sourcemod.inc	Wed Apr 11 20:07:20 2007 +0000
    13.2 +++ b/plugins/include/sourcemod.inc	Thu Apr 12 00:45:53 2007 +0000
    13.3 @@ -45,6 +45,14 @@
    13.4  #include <functions>
    13.5  #include <timers>
    13.6  
    13.7 +enum DialogType
    13.8 +{
    13.9 +	DialogType_Msg = 0,		/**< just an on screen message */
   13.10 +	DialogType_Menu, 		/**< an options menu */
   13.11 +	DialogType_Text, 		/**< a richtext dialog */
   13.12 +	DialogType_Entry 		/**< an entry box */
   13.13 +};
   13.14 +
   13.15  /**
   13.16   * Declare this as a struct in your plugin to expose its information.
   13.17   * Example:
   13.18 @@ -388,15 +396,15 @@
   13.19  native bool:IsSoundPrecached(const String:sound[]);
   13.20  
   13.21  /**
   13.22 - * Executes a client command on the server without being networked.
   13.23 + * Creates different types of ingame messages.
   13.24   *
   13.25   * @param client		Index of the client.
   13.26 - * @param fmt			Format of the client command.
   13.27 - * @param ...			Format parameters
   13.28 + * @param kv			KeyValues handle to set the menu keys and options. (Check iserverplugin.h for more information).
   13.29 + * @param type			Message type to display ingame.
   13.30   * @noreturn
   13.31   * @error				Invalid client index, or client not connected.
   13.32   */
   13.33 -native FakeClientCommand(client, const String:fmt[], any:...);
   13.34 +native CreateDialog(client, Handle:kv, DialogType:type);
   13.35  
   13.36  #include <helpers>
   13.37  #include <entity>
    14.1 --- a/public/IForwardSys.h	Wed Apr 11 20:07:20 2007 +0000
    14.2 +++ b/public/IForwardSys.h	Thu Apr 12 00:45:53 2007 +0000
    14.3 @@ -229,13 +229,13 @@
    14.4  		virtual bool AddFunction(IPluginContext *ctx, funcid_t index) =0;
    14.5  
    14.6  		/** 
    14.7 -		* @brief Removes a function from the call list.
    14.8 -		* NOTE: Only removes one instance.
    14.9 -		*
   14.10 -		* @param ctx		Context to use as a look-up.
   14.11 -		* @param index		Function id to add.
   14.12 -		* @return			Whether or not the function was removed.
   14.13 -		*/
   14.14 +		 * @brief Removes a function from the call list.
   14.15 +		 * NOTE: Only removes one instance.
   14.16 +		 *
   14.17 +		 * @param ctx		Context to use as a look-up.
   14.18 +		 * @param index		Function id to add.
   14.19 +		 * @return			Whether or not the function was removed.
   14.20 +		 */
   14.21  		virtual bool RemoveFunction(IPluginContext *ctx, funcid_t index) =0;
   14.22  	};
   14.23  
    15.1 --- a/public/IPlayerHelpers.h	Wed Apr 11 20:07:20 2007 +0000
    15.2 +++ b/public/IPlayerHelpers.h	Thu Apr 12 00:45:53 2007 +0000
    15.3 @@ -244,6 +244,14 @@
    15.4  		 * @return				Current number of connected clients.
    15.5  		 */
    15.6  		virtual int GetNumPlayers() =0;
    15.7 +
    15.8 +		/**
    15.9 +		 * @brief Returns the client index by its userid.
   15.10 +		 *
   15.11 +		 * @param userid		Userid of the client.
   15.12 +		 * @return				Client index, or 0 if invalid userid passed.
   15.13 +		 */
   15.14 +		virtual int GetClientOfUserId(int userid) =0;
   15.15  	};
   15.16  }
   15.17  
    16.1 --- a/public/ISourceMod.h	Wed Apr 11 20:07:20 2007 +0000
    16.2 +++ b/public/ISourceMod.h	Thu Apr 12 00:45:53 2007 +0000
    16.3 @@ -23,13 +23,18 @@
    16.4   * @brief Defines miscellanious helper functions useful to extensions.
    16.5   */
    16.6  
    16.7 -#include <IShareSys.h>
    16.8 +#include <IHandleSys.h>
    16.9  #include <sp_vm_api.h>
   16.10  #include <IDataPack.h>
   16.11  
   16.12  #define SMINTERFACE_SOURCEMOD_NAME		"ISourceMod"
   16.13  #define SMINTERFACE_SOURCEMOD_VERSION	1
   16.14  
   16.15 +/**
   16.16 +* @brief Forward declaration of the KeyValues class.
   16.17 +*/
   16.18 +class KeyValues;
   16.19 +
   16.20  namespace SourceMod
   16.21  {
   16.22  	/**
   16.23 @@ -133,18 +138,29 @@
   16.24  		 */
   16.25  		virtual void FreeDataPack(IDataPack *pack) =0;
   16.26  
   16.27 -		/**
   16.28 -		 * @brief Returns the automated data pack handle type.
   16.29 -		 *
   16.30 -		 * The readonly data type is the parent of the writable type.  
   16.31 -		 * Note that calling CloseHandle() on either type will release the data pack.
   16.32 -		 * The readonly type is inheritable, but due to limitations of the Handle System,
   16.33 -		 * the writable type is not.
   16.34 -		 *
   16.35 -		 * @param readonly	If true, the readonly type will be returned.
   16.36 -		 * @return			The Handle type for storing generic data packs.
   16.37 -		 */
   16.38 +		 /**
   16.39 +		  * @brief Returns the automated data pack handle type.
   16.40 +		  *
   16.41 +		  * The readonly data type is the parent of the writable type.  
   16.42 +		  * Note that calling CloseHandle() on either type will release the data pack.
   16.43 +		  * The readonly type is inheritable, but due to limitations of the Handle System,
   16.44 +		  * the writable type is not.
   16.45 +		  *
   16.46 +		  * @param readonly	If true, the readonly type will be returned.
   16.47 +		  * @return			The Handle type for storing generic data packs.
   16.48 +		  */
   16.49  		 virtual HandleType_t GetDataPackHandleType(bool readonly=false) =0;
   16.50 +
   16.51 +		 /**
   16.52 +		  * @brief Retrieves a KeyValues pointer from a handle.
   16.53 +		  *
   16.54 +		  * @param hndl		Handle_t from which to retrieve contents.
   16.55 +		  * @param err		Optional address to store a possible handle error.
   16.56 +		  * @param root		If true it will return the root KeyValues pointer for the whole structure.
   16.57 +		  *
   16.58 +		  * @return			The KeyValues pointer, or NULL for any error encountered.
   16.59 +		  */
   16.60 +		 virtual KeyValues *ReadKeyValuesHandle(Handle_t hndl, HandleError *err=NULL, bool root=false) =0;
   16.61  	};
   16.62  }
   16.63