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