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 --HG-- extra : convert_revision : svn%3A39bc706e-5318-0410-9160-8a85361fbb7c/trunk%40688
This commit is contained in:
parent
4ecb7a985f
commit
21923d7871
@ -17,6 +17,21 @@
|
||||
|
||||
CHalfLife2 g_HL2;
|
||||
|
||||
namespace SourceHook
|
||||
{
|
||||
template<>
|
||||
int HashFunction<datamap_t *>(datamap_t * const &k)
|
||||
{
|
||||
return reinterpret_cast<int>(k);
|
||||
}
|
||||
|
||||
template<>
|
||||
int Compare<datamap_t *>(datamap_t * const &k1, datamap_t * const &k2)
|
||||
{
|
||||
return (k1-k2);
|
||||
}
|
||||
}
|
||||
|
||||
CHalfLife2::CHalfLife2()
|
||||
{
|
||||
m_pClasses = sm_trie_create();
|
||||
@ -36,6 +51,18 @@ CHalfLife2::~CHalfLife2()
|
||||
}
|
||||
|
||||
m_Tables.clear();
|
||||
|
||||
THash<datamap_t *, DataMapTrie>::iterator h_iter;
|
||||
for (h_iter=m_Maps.begin(); h_iter!=m_Maps.end(); h_iter++)
|
||||
{
|
||||
if (h_iter->val.trie)
|
||||
{
|
||||
sm_trie_destroy(h_iter->val.trie);
|
||||
h_iter->val.trie = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
m_Maps.clear();
|
||||
}
|
||||
|
||||
CSharedEdictChangeInfo *g_pSharedChangeInfo = NULL;
|
||||
@ -85,6 +112,31 @@ SendProp *UTIL_FindInSendTable(SendTable *pTable, const char *name)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
typedescription_t *UTIL_FindInDataMap(datamap_t *pMap, const char *name)
|
||||
{
|
||||
while (pMap)
|
||||
{
|
||||
for (int i=0; i<pMap->dataNumFields; i++)
|
||||
{
|
||||
if (strcmp(name, pMap->dataDesc[i].fieldName) == 0)
|
||||
{
|
||||
return &(pMap->dataDesc[i]);
|
||||
}
|
||||
if (pMap->dataDesc[i].td)
|
||||
{
|
||||
typedescription_t *_td;
|
||||
if ((_td=UTIL_FindInDataMap(pMap->dataDesc[i].td, name)) != NULL)
|
||||
{
|
||||
return _td;
|
||||
}
|
||||
}
|
||||
}
|
||||
pMap = pMap->baseMap;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
ServerClass *CHalfLife2::FindServerClass(const char *classname)
|
||||
{
|
||||
DataTableInfo *pInfo = _FindServerClass(classname);
|
||||
@ -111,6 +163,7 @@ DataTableInfo *CHalfLife2::_FindServerClass(const char *classname)
|
||||
pInfo->lookup = sm_trie_create();
|
||||
pInfo->sc = sc;
|
||||
sm_trie_insert(m_pClasses, classname, pInfo);
|
||||
m_Tables.push_back(pInfo);
|
||||
break;
|
||||
}
|
||||
sc = sc->m_pNext;
|
||||
@ -133,7 +186,7 @@ SendProp *CHalfLife2::FindInSendTable(const char *classname, const char *offset)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
SendProp *pProp;
|
||||
SendProp *pProp = NULL;
|
||||
if (!sm_trie_retrieve(pInfo->lookup, offset, (void **)&pProp))
|
||||
{
|
||||
if ((pProp = UTIL_FindInSendTable(pInfo->sc->m_pTable, offset)) != NULL)
|
||||
@ -144,3 +197,23 @@ SendProp *CHalfLife2::FindInSendTable(const char *classname, const char *offset)
|
||||
|
||||
return pProp;
|
||||
}
|
||||
|
||||
typedescription_t *CHalfLife2::FindInDataMap(datamap_t *pMap, const char *offset)
|
||||
{
|
||||
typedescription_t *td = NULL;
|
||||
DataMapTrie &val = m_Maps[pMap];
|
||||
|
||||
if (!val.trie)
|
||||
{
|
||||
val.trie = sm_trie_create();
|
||||
}
|
||||
if (!sm_trie_retrieve(val.trie, offset, (void **)&td))
|
||||
{
|
||||
if ((td = UTIL_FindInDataMap(pMap, offset)) != NULL)
|
||||
{
|
||||
sm_trie_insert(val.trie, offset, td);
|
||||
}
|
||||
}
|
||||
|
||||
return td;
|
||||
}
|
||||
|
@ -16,10 +16,12 @@
|
||||
#define _INCLUDE_SOURCEMOD_CHALFLIFE2_H_
|
||||
|
||||
#include <sh_list.h>
|
||||
#include <sh_tinyhash.h>
|
||||
#include "sm_trie.h"
|
||||
#include "sm_globals.h"
|
||||
#include "dt_send.h"
|
||||
#include "server_class.h"
|
||||
#include "datamap.h"
|
||||
|
||||
using namespace SourceHook;
|
||||
|
||||
@ -29,6 +31,12 @@ struct DataTableInfo
|
||||
Trie *lookup;
|
||||
};
|
||||
|
||||
struct DataMapTrie
|
||||
{
|
||||
DataMapTrie() : trie(NULL) {}
|
||||
Trie *trie;
|
||||
};
|
||||
|
||||
class CHalfLife2 : public SMGlobalClass
|
||||
{
|
||||
public:
|
||||
@ -40,11 +48,13 @@ public:
|
||||
public:
|
||||
SendProp *FindInSendTable(const char *classname, const char *offset);
|
||||
ServerClass *FindServerClass(const char *classname);
|
||||
typedescription_t *FindInDataMap(datamap_t *pMap, const char *offset);
|
||||
private:
|
||||
DataTableInfo *_FindServerClass(const char *classname);
|
||||
private:
|
||||
Trie *m_pClasses;
|
||||
List<DataTableInfo *> m_Tables;
|
||||
THash<datamap_t *, DataMapTrie> m_Maps;
|
||||
};
|
||||
|
||||
extern CHalfLife2 g_HL2;
|
||||
|
@ -31,11 +31,15 @@ PlayerManager::PlayerManager()
|
||||
{
|
||||
m_AuthQueue = NULL;
|
||||
m_FirstPass = true;
|
||||
|
||||
m_UserIdLookUp = new int[USHRT_MAX];
|
||||
memset(m_UserIdLookUp, 0, sizeof(int) * USHRT_MAX);
|
||||
}
|
||||
|
||||
PlayerManager::~PlayerManager()
|
||||
{
|
||||
delete [] m_AuthQueue;
|
||||
delete [] m_UserIdLookUp;
|
||||
}
|
||||
|
||||
void PlayerManager::OnSourceModAllInitialized()
|
||||
@ -212,6 +216,8 @@ bool PlayerManager::OnClientConnect(edict_t *pEntity, const char *pszName, const
|
||||
RETURN_META_VALUE(MRES_SUPERCEDE, false);
|
||||
}
|
||||
|
||||
m_UserIdLookUp[engine->GetPlayerUserId(pEntity)] = client;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -353,6 +359,7 @@ void PlayerManager::OnClientDisconnect(edict_t *pEntity)
|
||||
}
|
||||
|
||||
m_Players[client].Disconnect();
|
||||
m_UserIdLookUp[engine->GetPlayerUserId(pEntity)] = 0;
|
||||
}
|
||||
|
||||
void PlayerManager::OnClientDisconnect_Post(edict_t *pEntity)
|
||||
@ -425,6 +432,11 @@ int PlayerManager::GetNumPlayers()
|
||||
return m_PlayerCount;
|
||||
}
|
||||
|
||||
int PlayerManager::GetClientOfUserId(int userid)
|
||||
{
|
||||
return (userid < 0 || userid > USHRT_MAX) ? 0 : m_UserIdLookUp[userid];
|
||||
}
|
||||
|
||||
void PlayerManager::AddClientListener(IClientListener *listener)
|
||||
{
|
||||
m_hooks.push_back(listener);
|
||||
|
@ -95,6 +95,7 @@ public: //IPlayerManager
|
||||
IGamePlayer *GetGamePlayer(edict_t *pEdict);
|
||||
int GetMaxClients();
|
||||
int GetNumPlayers();
|
||||
int GetClientOfUserId(int userid);
|
||||
public:
|
||||
inline int MaxClients()
|
||||
{
|
||||
@ -117,6 +118,7 @@ private:
|
||||
IForward *m_clauth;
|
||||
IForward *m_onActivate;
|
||||
CPlayer *m_Players;
|
||||
int *m_UserIdLookUp;
|
||||
int m_maxClients;
|
||||
int m_PlayerCount;
|
||||
bool m_FirstPass;
|
||||
|
@ -1,7 +1,7 @@
|
||||
<?xml version="1.0" encoding="Windows-1252"?>
|
||||
<VisualStudioProject
|
||||
ProjectType="Visual C++"
|
||||
Version="8.00"
|
||||
Version="8,00"
|
||||
Name="sourcemod_mm"
|
||||
ProjectGUID="{E39527CD-7CAB-4420-97CC-DA1B93B260BC}"
|
||||
RootNamespace="sourcemod_mm"
|
||||
|
@ -18,6 +18,7 @@
|
||||
#include "server_class.h"
|
||||
#include "PlayerManager.h"
|
||||
#include "HalfLife2.h"
|
||||
#include "GameConfigs.h"
|
||||
|
||||
inline edict_t *GetEdict(cell_t num)
|
||||
{
|
||||
@ -61,6 +62,46 @@ inline edict_t *GetEntity(cell_t num, CBaseEntity **pData)
|
||||
return pEdict;
|
||||
}
|
||||
|
||||
class VEmptyClass {};
|
||||
datamap_t *VGetDataDescMap(CBaseEntity *pThisPtr, int offset)
|
||||
{
|
||||
void **this_ptr = *reinterpret_cast<void ***>(&pThisPtr);
|
||||
void **vtable = *reinterpret_cast<void ***>(pThisPtr);
|
||||
void *vfunc = vtable[offset];
|
||||
|
||||
union
|
||||
{
|
||||
datamap_t *(VEmptyClass::*mfpnew)();
|
||||
#ifndef PLATFORM_POSIX
|
||||
void *addr;
|
||||
} u;
|
||||
u.addr = vfunc;
|
||||
#else
|
||||
struct
|
||||
{
|
||||
void *addr;
|
||||
intptr_t adjustor;
|
||||
} s;
|
||||
} u;
|
||||
u.s.addr = vfunc;
|
||||
u.s.adjustor = 0;
|
||||
#endif
|
||||
|
||||
return (datamap_t *)(reinterpret_cast<VEmptyClass *>(this_ptr)->*u.mfpnew)();
|
||||
}
|
||||
|
||||
inline datamap_t *CBaseEntity_GetDataDescMap(CBaseEntity *pEntity)
|
||||
{
|
||||
int offset;
|
||||
|
||||
if (!g_pGameConf->GetOffset("GetDataDescMap", &offset) || !offset)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return VGetDataDescMap(pEntity, offset);
|
||||
}
|
||||
|
||||
static cell_t GetMaxEntities(IPluginContext *pContext, const cell_t *params)
|
||||
{
|
||||
return gpGlobals->maxEntities;
|
||||
@ -494,6 +535,33 @@ static cell_t FindSendPropOffs(IPluginContext *pContext, const cell_t *params)
|
||||
return pSend->GetOffset();
|
||||
}
|
||||
|
||||
static cell_t FindDataMapOffs(IPluginContext *pContext, const cell_t *params)
|
||||
{
|
||||
CBaseEntity *pEntity;
|
||||
datamap_t *pMap;
|
||||
typedescription_t *td;
|
||||
char *offset;
|
||||
edict_t *pEdict = GetEntity(params[1], &pEntity);
|
||||
|
||||
if (!pEdict || !pEntity)
|
||||
{
|
||||
return pContext->ThrowNativeError("Entity %d is invalid", params[1]);
|
||||
}
|
||||
|
||||
if ((pMap=CBaseEntity_GetDataDescMap(pEntity)) == NULL)
|
||||
{
|
||||
return pContext->ThrowNativeError("Unable to retrieve GetDataDescMap offset");
|
||||
}
|
||||
|
||||
pContext->LocalToString(params[2], &offset);
|
||||
if ((td=g_HL2.FindInDataMap(pMap, offset)) == NULL)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
return td->fieldOffset[TD_OFFSET_NORMAL];
|
||||
}
|
||||
|
||||
REGISTER_NATIVES(entityNatives)
|
||||
{
|
||||
{"ChangeEdictState", ChangeEdictState},
|
||||
@ -517,5 +585,6 @@ REGISTER_NATIVES(entityNatives)
|
||||
{"SetEntDataEnt", SetEntDataEnt},
|
||||
{"SetEntDataFloat", SetEntDataFloat},
|
||||
{"SetEntDataVector", SetEntDataVector},
|
||||
{"FindDataMapOffs", FindDataMapOffs},
|
||||
{NULL, NULL}
|
||||
};
|
||||
|
@ -16,6 +16,18 @@
|
||||
#include "sourcemod.h"
|
||||
#include "sourcemm_api.h"
|
||||
#include "PlayerManager.h"
|
||||
#include "HandleSys.h"
|
||||
|
||||
IServerPluginCallbacks *g_VSP = NULL;
|
||||
|
||||
class HalfLifeNatives : public SMGlobalClass
|
||||
{
|
||||
public: //SMGlobalClass
|
||||
void OnSourceModVSPReceived(IServerPluginCallbacks *iface)
|
||||
{
|
||||
g_VSP = iface;
|
||||
}
|
||||
};
|
||||
|
||||
static cell_t SetRandomSeed(IPluginContext *pContext, const cell_t *params)
|
||||
{
|
||||
@ -229,6 +241,36 @@ static cell_t FakeClientCommand(IPluginContext *pContext, const cell_t *params)
|
||||
return 1;
|
||||
}
|
||||
|
||||
static cell_t smn_CreateDialog(IPluginContext *pContext, const cell_t *params)
|
||||
{
|
||||
KeyValues *pKV;
|
||||
HandleError herr;
|
||||
Handle_t hndl = static_cast<Handle_t>(params[2]);
|
||||
CPlayer *pPlayer = g_Players.GetPlayerByIndex(params[1]);
|
||||
|
||||
if (!pPlayer)
|
||||
{
|
||||
return pContext->ThrowNativeError("Player %d is not a valid player", params[1]);
|
||||
}
|
||||
|
||||
if (!pPlayer->IsConnected())
|
||||
{
|
||||
return pContext->ThrowNativeError("Player %d is not connected", params[1]);
|
||||
}
|
||||
|
||||
pKV = g_SourceMod.ReadKeyValuesHandle(hndl, &herr, true);
|
||||
if (herr != HandleError_None)
|
||||
{
|
||||
return pContext->ThrowNativeError("Invalid key value handle %x (error %d)", hndl, herr);
|
||||
}
|
||||
|
||||
serverpluginhelpers->CreateMessage(pPlayer->GetEdict(), static_cast<DIALOG_TYPE>(params[3]), pKV, g_VSP);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static HalfLifeNatives s_HalfLifeNatives;
|
||||
|
||||
REGISTER_NATIVES(halflifeNatives)
|
||||
{
|
||||
{"CreateFakeClient", CreateFakeClient},
|
||||
@ -252,5 +294,6 @@ REGISTER_NATIVES(halflifeNatives)
|
||||
{"PrecacheSound", PrecacheSound},
|
||||
{"IsSoundPrecached", IsSoundPrecached},
|
||||
{"FakeClientCommand", FakeClientCommand},
|
||||
{"CreateDialog", smn_CreateDialog},
|
||||
{NULL, NULL},
|
||||
};
|
||||
|
@ -48,6 +48,33 @@ public:
|
||||
}
|
||||
};
|
||||
|
||||
KeyValues *SourceModBase::ReadKeyValuesHandle(Handle_t hndl, HandleError *err, bool root)
|
||||
{
|
||||
HandleError herr;
|
||||
HandleSecurity sec;
|
||||
KeyValueStack *pStk;
|
||||
|
||||
sec.pOwner = NULL;
|
||||
sec.pIdentity = g_pCoreIdent;
|
||||
|
||||
if ((herr=g_HandleSys.ReadHandle(hndl, g_KeyValueType, &sec, (void **)&pStk))
|
||||
!= HandleError_None)
|
||||
{
|
||||
if (err)
|
||||
{
|
||||
*err = herr;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (err)
|
||||
{
|
||||
*err = HandleError_None;
|
||||
}
|
||||
|
||||
return (root) ? pStk->pBase : pStk->pCurRoot.front();
|
||||
}
|
||||
|
||||
static cell_t smn_KvSetString(IPluginContext *pCtx, const cell_t *params)
|
||||
{
|
||||
Handle_t hndl = static_cast<Handle_t>(params[1]);
|
||||
|
@ -15,6 +15,7 @@
|
||||
#include "PlayerManager.h"
|
||||
#include "AdminCache.h"
|
||||
#include "sm_stringutil.h"
|
||||
#include <inetchannelinfo.h>
|
||||
|
||||
static cell_t sm_GetClientCount(IPluginContext *pCtx, const cell_t *params)
|
||||
{
|
||||
@ -637,6 +638,182 @@ static cell_t GetHealth(IPluginContext *pContext, const cell_t *params)
|
||||
return pInfo->GetHealth();
|
||||
}
|
||||
|
||||
static cell_t GetTimeConnected(IPluginContext *pContext, const cell_t *params)
|
||||
{
|
||||
int client = params[1];
|
||||
|
||||
CPlayer *pPlayer = g_Players.GetPlayerByIndex(client);
|
||||
if (!pPlayer)
|
||||
{
|
||||
return pContext->ThrowNativeError("Player %d is not a valid client", client);
|
||||
} else if (!pPlayer->IsInGame()) {
|
||||
return pContext->ThrowNativeError("Player %d is not in game", client);
|
||||
} else if (pPlayer->IsFakeClient()) {
|
||||
return pContext->ThrowNativeError("Player %d is a bot", client);
|
||||
}
|
||||
|
||||
INetChannelInfo *pInfo = engine->GetPlayerNetInfo(client);
|
||||
|
||||
return sp_ftoc(pInfo->GetTimeConnected());
|
||||
}
|
||||
|
||||
static cell_t GetDataRate(IPluginContext *pContext, const cell_t *params)
|
||||
{
|
||||
int client = params[1];
|
||||
|
||||
CPlayer *pPlayer = g_Players.GetPlayerByIndex(client);
|
||||
if (!pPlayer)
|
||||
{
|
||||
return pContext->ThrowNativeError("Player %d is not a valid client", client);
|
||||
} else if (!pPlayer->IsInGame()) {
|
||||
return pContext->ThrowNativeError("Player %d is not in game", client);
|
||||
} else if (pPlayer->IsFakeClient()) {
|
||||
return pContext->ThrowNativeError("Player %d is a bot", client);
|
||||
}
|
||||
|
||||
INetChannelInfo *pInfo = engine->GetPlayerNetInfo(client);
|
||||
|
||||
return pInfo->GetDataRate();
|
||||
}
|
||||
|
||||
static cell_t IsTimingOut(IPluginContext *pContext, const cell_t *params)
|
||||
{
|
||||
int client = params[1];
|
||||
|
||||
CPlayer *pPlayer = g_Players.GetPlayerByIndex(client);
|
||||
if (!pPlayer)
|
||||
{
|
||||
return pContext->ThrowNativeError("Player %d is not a valid client", client);
|
||||
} else if (!pPlayer->IsInGame()) {
|
||||
return pContext->ThrowNativeError("Player %d is not in game", client);
|
||||
} else if (pPlayer->IsFakeClient()) {
|
||||
return pContext->ThrowNativeError("Player %d is a bot", client);
|
||||
}
|
||||
|
||||
INetChannelInfo *pInfo = engine->GetPlayerNetInfo(client);
|
||||
|
||||
return pInfo->IsTimingOut() ? 1 : 0;
|
||||
}
|
||||
|
||||
static cell_t GetLatency(IPluginContext *pContext, const cell_t *params)
|
||||
{
|
||||
int client = params[1];
|
||||
|
||||
CPlayer *pPlayer = g_Players.GetPlayerByIndex(client);
|
||||
if (!pPlayer)
|
||||
{
|
||||
return pContext->ThrowNativeError("Player %d is not a valid client", client);
|
||||
} else if (!pPlayer->IsInGame()) {
|
||||
return pContext->ThrowNativeError("Player %d is not in game", client);
|
||||
} else if (pPlayer->IsFakeClient()) {
|
||||
return pContext->ThrowNativeError("Player %d is a bot", client);
|
||||
}
|
||||
|
||||
INetChannelInfo *pInfo = engine->GetPlayerNetInfo(client);
|
||||
|
||||
return sp_ftoc(pInfo->GetLatency(params[2]));
|
||||
}
|
||||
|
||||
static cell_t GetAvgLatency(IPluginContext *pContext, const cell_t *params)
|
||||
{
|
||||
int client = params[1];
|
||||
|
||||
CPlayer *pPlayer = g_Players.GetPlayerByIndex(client);
|
||||
if (!pPlayer)
|
||||
{
|
||||
return pContext->ThrowNativeError("Player %d is not a valid client", client);
|
||||
} else if (!pPlayer->IsInGame()) {
|
||||
return pContext->ThrowNativeError("Player %d is not in game", client);
|
||||
} else if (pPlayer->IsFakeClient()) {
|
||||
return pContext->ThrowNativeError("Player %d is a bot", client);
|
||||
}
|
||||
|
||||
INetChannelInfo *pInfo = engine->GetPlayerNetInfo(client);
|
||||
|
||||
return sp_ftoc(pInfo->GetAvgLatency(params[2]));
|
||||
}
|
||||
|
||||
static cell_t GetAvgLoss(IPluginContext *pContext, const cell_t *params)
|
||||
{
|
||||
int client = params[1];
|
||||
|
||||
CPlayer *pPlayer = g_Players.GetPlayerByIndex(client);
|
||||
if (!pPlayer)
|
||||
{
|
||||
return pContext->ThrowNativeError("Player %d is not a valid client", client);
|
||||
} else if (!pPlayer->IsInGame()) {
|
||||
return pContext->ThrowNativeError("Player %d is not in game", client);
|
||||
} else if (pPlayer->IsFakeClient()) {
|
||||
return pContext->ThrowNativeError("Player %d is a bot", client);
|
||||
}
|
||||
|
||||
INetChannelInfo *pInfo = engine->GetPlayerNetInfo(client);
|
||||
|
||||
return sp_ftoc(pInfo->GetAvgLoss(params[2]));
|
||||
}
|
||||
|
||||
static cell_t GetAvgChoke(IPluginContext *pContext, const cell_t *params)
|
||||
{
|
||||
int client = params[1];
|
||||
|
||||
CPlayer *pPlayer = g_Players.GetPlayerByIndex(client);
|
||||
if (!pPlayer)
|
||||
{
|
||||
return pContext->ThrowNativeError("Player %d is not a valid client", client);
|
||||
} else if (!pPlayer->IsInGame()) {
|
||||
return pContext->ThrowNativeError("Player %d is not in game", client);
|
||||
} else if (pPlayer->IsFakeClient()) {
|
||||
return pContext->ThrowNativeError("Player %d is a bot", client);
|
||||
}
|
||||
|
||||
INetChannelInfo *pInfo = engine->GetPlayerNetInfo(client);
|
||||
|
||||
return sp_ftoc(pInfo->GetAvgChoke(params[2]));
|
||||
}
|
||||
|
||||
static cell_t GetAvgData(IPluginContext *pContext, const cell_t *params)
|
||||
{
|
||||
int client = params[1];
|
||||
|
||||
CPlayer *pPlayer = g_Players.GetPlayerByIndex(client);
|
||||
if (!pPlayer)
|
||||
{
|
||||
return pContext->ThrowNativeError("Player %d is not a valid client", client);
|
||||
} else if (!pPlayer->IsInGame()) {
|
||||
return pContext->ThrowNativeError("Player %d is not in game", client);
|
||||
} else if (pPlayer->IsFakeClient()) {
|
||||
return pContext->ThrowNativeError("Player %d is a bot", client);
|
||||
}
|
||||
|
||||
INetChannelInfo *pInfo = engine->GetPlayerNetInfo(client);
|
||||
|
||||
return sp_ftoc(pInfo->GetAvgData(params[2]));
|
||||
}
|
||||
|
||||
static cell_t GetAvgPackets(IPluginContext *pContext, const cell_t *params)
|
||||
{
|
||||
int client = params[1];
|
||||
|
||||
CPlayer *pPlayer = g_Players.GetPlayerByIndex(client);
|
||||
if (!pPlayer)
|
||||
{
|
||||
return pContext->ThrowNativeError("Player %d is not a valid client", client);
|
||||
} else if (!pPlayer->IsInGame()) {
|
||||
return pContext->ThrowNativeError("Player %d is not in game", client);
|
||||
} else if (pPlayer->IsFakeClient()) {
|
||||
return pContext->ThrowNativeError("Player %d is a bot", client);
|
||||
}
|
||||
|
||||
INetChannelInfo *pInfo = engine->GetPlayerNetInfo(client);
|
||||
|
||||
return sp_ftoc(pInfo->GetAvgPackets(params[2]));
|
||||
}
|
||||
|
||||
static cell_t GetClientOfUserId(IPluginContext *pContext, const cell_t *params)
|
||||
{
|
||||
return g_Players.GetClientOfUserId(params[1]);
|
||||
}
|
||||
|
||||
REGISTER_NATIVES(playernatives)
|
||||
{
|
||||
{"AddUserFlags", AddUserFlags},
|
||||
@ -668,5 +845,15 @@ REGISTER_NATIVES(playernatives)
|
||||
{"GetClientWeapon", GetWeaponName},
|
||||
{"GetClientModel", GetModelName},
|
||||
{"GetClientHealth", GetHealth},
|
||||
{"GetTimeConnected", GetTimeConnected},
|
||||
{"GetDataRate", GetDataRate},
|
||||
{"IsTimingOut", IsTimingOut},
|
||||
{"GetLatency", GetLatency},
|
||||
{"GetAvgLatency", GetAvgLatency},
|
||||
{"GetAvgLoss", GetAvgLoss},
|
||||
{"GetAvgChoke", GetAvgChoke},
|
||||
{"GetAvgData", GetAvgData},
|
||||
{"GetAvgPackets", GetAvgPackets},
|
||||
{"GetClientOfUserId", GetClientOfUserId},
|
||||
{NULL, NULL}
|
||||
};
|
||||
|
@ -93,6 +93,7 @@ public: // ISourceMod
|
||||
IDataPack *CreateDataPack();
|
||||
void FreeDataPack(IDataPack *pack);
|
||||
HandleType_t GetDataPackHandleType(bool readonly=false);
|
||||
KeyValues *ReadKeyValuesHandle(Handle_t hndl, HandleError *err=NULL, bool root=false);
|
||||
private:
|
||||
/**
|
||||
* @brief Loading plugins
|
||||
|
@ -18,6 +18,13 @@
|
||||
#endif
|
||||
#define _clients_included
|
||||
|
||||
enum NetFlow
|
||||
{
|
||||
NetFlow_Outgoing = 0, /**< Outgoing traffic */
|
||||
NetFlow_Incoming, /**< Incoming traffic */
|
||||
NetFlow_Both /**< Incoming and outgoing traffic */
|
||||
};
|
||||
|
||||
/**
|
||||
* Called on client connection.
|
||||
*
|
||||
@ -380,3 +387,110 @@ native GetClientDeaths(client);
|
||||
* @error Invalid client index, client not in game, or no mod support.
|
||||
*/
|
||||
native GetClientFrags(client);
|
||||
|
||||
/**
|
||||
* Returns the client's send data rate in bytes/sec.
|
||||
*
|
||||
* @param client Player's index.
|
||||
* @return Data rate.
|
||||
* @error Invalid client index, client not in game, or fake client.
|
||||
*/
|
||||
native GetDataRate(client);
|
||||
|
||||
/**
|
||||
* Returns if a client is timing out
|
||||
*
|
||||
* @param client Player's index.
|
||||
* @return True if client is timing out, false otherwise.
|
||||
* @error Invalid client index, client not in game, or fake client.
|
||||
*/
|
||||
native bool:IsTimingOut(client);
|
||||
|
||||
/**
|
||||
* Returns the client's connection time in seconds.
|
||||
*
|
||||
* @param client Player's index.
|
||||
* @return Connection time.
|
||||
* @error Invalid client index, client not in game, or fake client.
|
||||
*/
|
||||
native Float:GetTimeConnected(client);
|
||||
|
||||
/**
|
||||
* Returns the client's current latency (RTT), more accurate than GetAvgLatency but jittering.
|
||||
*
|
||||
* @param client Player's index.
|
||||
* @param flow Traffic flowing direction.
|
||||
* @return Latency.
|
||||
* @error Invalid client index, client not in game, or fake client.
|
||||
*/
|
||||
native Float:GetLatency(client, NetFlow:flow);
|
||||
|
||||
/**
|
||||
* Returns the client's average packet latency in seconds.
|
||||
*
|
||||
* @param client Player's index.
|
||||
* @param flow Traffic flowing direction.
|
||||
* @return Average latency.
|
||||
* @error Invalid client index, client not in game, or fake client.
|
||||
*/
|
||||
native Float:GetAvgLatency(client, NetFlow:flow);
|
||||
|
||||
/**
|
||||
* Returns the client's average packet loss, values go from 0 to 1 (for percentages).
|
||||
*
|
||||
* @param client Player's index.
|
||||
* @param flow Traffic flowing direction.
|
||||
* @return Average packet loss.
|
||||
* @error Invalid client index, client not in game, or fake client.
|
||||
*/
|
||||
native Float:GetAvgLoss(client, NetFlow:flow);
|
||||
|
||||
/**
|
||||
* Returns the client's average packet choke, values go from 0 to 1 (for percentages).
|
||||
*
|
||||
* @param client Player's index.
|
||||
* @param flow Traffic flowing direction.
|
||||
* @return Average packet choke.
|
||||
* @error Invalid client index, client not in game, or fake client.
|
||||
*/
|
||||
native Float:GetAvgChoke(client, NetFlow:flow);
|
||||
|
||||
/**
|
||||
* Returns the client's data flow in bytes/sec.
|
||||
*
|
||||
* @param client Player's index.
|
||||
* @param flow Traffic flowing direction.
|
||||
* @return Data flow.
|
||||
* @error Invalid client index, client not in game, or fake client.
|
||||
*/
|
||||
native Float:GetAvgData(client, NetFlow:flow);
|
||||
|
||||
/**
|
||||
* Returns the client's average packet frequency in packets/sec.
|
||||
*
|
||||
* @param client Player's index.
|
||||
* @param flow Traffic flowing direction.
|
||||
* @return Packet frequency.
|
||||
* @error Invalid client index, client not in game, or fake client.
|
||||
*/
|
||||
native Float:GetAvgPackets(client, NetFlow:flow);
|
||||
|
||||
/**
|
||||
* Translates an userid index to the real player index.
|
||||
*
|
||||
* @param userid Userid value.
|
||||
* @return Client value.
|
||||
* @error Returns 0 if invalid userid.
|
||||
*/
|
||||
native GetClientOfUserId(userid);
|
||||
|
||||
/**
|
||||
* Executes a client command on the server without being networked.
|
||||
*
|
||||
* @param client Index of the client.
|
||||
* @param fmt Format of the client command.
|
||||
* @param ... Format parameters
|
||||
* @noreturn
|
||||
* @error Invalid client index, or client not connected.
|
||||
*/
|
||||
native FakeClientCommand(client, const String:fmt[], any:...);
|
||||
|
@ -289,6 +289,16 @@ native SetEntDataVector(entity, offset, const Float:vec[3], bool:changeState=fal
|
||||
*/
|
||||
native FindSendPropOffs(const String:cls[], const String:prop[]);
|
||||
|
||||
/**
|
||||
* Given an entity, finds a datamap property offset.
|
||||
* This information is cached for future calls.
|
||||
*
|
||||
* @param entity Entity index.
|
||||
* @param prop Property name.
|
||||
* @return An offset, or -1 on failure.
|
||||
*/
|
||||
native FindDataMapOffs(entity, const String:prop[]);
|
||||
|
||||
/**
|
||||
* Wrapper function for finding a send property for a particular entity.
|
||||
*
|
||||
@ -320,11 +330,29 @@ stock GetEntSendPropOffs(ent, const String:prop[])
|
||||
*/
|
||||
stock GetEntProp(entity, PropType:type, const String:prop[], size=4)
|
||||
{
|
||||
new offs = GetEntSendPropOffs(entity, prop);
|
||||
new offs;
|
||||
|
||||
switch (type)
|
||||
{
|
||||
case Prop_Send:
|
||||
{
|
||||
offs = GetEntSendPropOffs(entity, prop);
|
||||
}
|
||||
case Prop_Data:
|
||||
{
|
||||
offs = FindDataMapOffs(entity, prop);
|
||||
}
|
||||
default:
|
||||
{
|
||||
ThrowError("Invalid Property type %d", type);
|
||||
}
|
||||
}
|
||||
|
||||
if (offs == -1)
|
||||
{
|
||||
ThrowError("Property \"%s\" not found for entity %d", prop, entity);
|
||||
}
|
||||
|
||||
return GetEntData(entity, offs, size);
|
||||
}
|
||||
|
||||
@ -340,11 +368,29 @@ stock GetEntProp(entity, PropType:type, const String:prop[], size=4)
|
||||
*/
|
||||
stock SetEntProp(entity, PropType:type, const String:prop[], value, size=4)
|
||||
{
|
||||
new offs = GetEntSendPropOffs(entity, prop);
|
||||
new offs;
|
||||
|
||||
switch (type)
|
||||
{
|
||||
case Prop_Send:
|
||||
{
|
||||
offs = GetEntSendPropOffs(entity, prop);
|
||||
}
|
||||
case Prop_Data:
|
||||
{
|
||||
offs = FindDataMapOffs(entity, prop);
|
||||
}
|
||||
default:
|
||||
{
|
||||
ThrowError("Invalid Property type %d", type);
|
||||
}
|
||||
}
|
||||
|
||||
if (offs == -1)
|
||||
{
|
||||
ThrowError("Property \"%s\" not found for entity %d", prop, entity);
|
||||
}
|
||||
|
||||
return SetEntData(entity, offs, value, size, true);
|
||||
}
|
||||
|
||||
@ -359,11 +405,29 @@ stock SetEntProp(entity, PropType:type, const String:prop[], value, size=4)
|
||||
*/
|
||||
stock Float:GetEntPropFloat(entity, PropType:type, const String:prop[])
|
||||
{
|
||||
new offs = GetEntSendPropOffs(entity, prop);
|
||||
new offs;
|
||||
|
||||
switch (type)
|
||||
{
|
||||
case Prop_Send:
|
||||
{
|
||||
offs = GetEntSendPropOffs(entity, prop);
|
||||
}
|
||||
case Prop_Data:
|
||||
{
|
||||
offs = FindDataMapOffs(entity, prop);
|
||||
}
|
||||
default:
|
||||
{
|
||||
ThrowError("Invalid Property type %d", type);
|
||||
}
|
||||
}
|
||||
|
||||
if (offs == -1)
|
||||
{
|
||||
ThrowError("Property \"%s\" not found for entity %d", prop, entity);
|
||||
}
|
||||
|
||||
return GetEntDataFloat(entity, offs);
|
||||
}
|
||||
|
||||
@ -379,11 +443,29 @@ stock Float:GetEntPropFloat(entity, PropType:type, const String:prop[])
|
||||
*/
|
||||
stock SetEntPropFloat(entity, PropType:type, const String:prop[], Float:value)
|
||||
{
|
||||
new offs = GetEntSendPropOffs(entity, prop);
|
||||
new offs;
|
||||
|
||||
switch (type)
|
||||
{
|
||||
case Prop_Send:
|
||||
{
|
||||
offs = GetEntSendPropOffs(entity, prop);
|
||||
}
|
||||
case Prop_Data:
|
||||
{
|
||||
offs = FindDataMapOffs(entity, prop);
|
||||
}
|
||||
default:
|
||||
{
|
||||
ThrowError("Invalid Property type %d", type);
|
||||
}
|
||||
}
|
||||
|
||||
if (offs == -1)
|
||||
{
|
||||
ThrowError("Property \"%s\" not found for entity %d", prop, entity);
|
||||
}
|
||||
|
||||
return SetEntDataFloat(entity, offs, value, true);
|
||||
}
|
||||
|
||||
@ -398,11 +480,29 @@ stock SetEntPropFloat(entity, PropType:type, const String:prop[], Float:value)
|
||||
*/
|
||||
stock GetEntPropEnt(entity, PropType:type, const String:prop[])
|
||||
{
|
||||
new offs = GetEntSendPropOffs(entity, prop);
|
||||
new offs;
|
||||
|
||||
switch (type)
|
||||
{
|
||||
case Prop_Send:
|
||||
{
|
||||
offs = GetEntSendPropOffs(entity, prop);
|
||||
}
|
||||
case Prop_Data:
|
||||
{
|
||||
offs = FindDataMapOffs(entity, prop);
|
||||
}
|
||||
default:
|
||||
{
|
||||
ThrowError("Invalid Property type %d", type);
|
||||
}
|
||||
}
|
||||
|
||||
if (offs == -1)
|
||||
{
|
||||
ThrowError("Property \"%s\" not found for entity %d", prop, entity);
|
||||
}
|
||||
|
||||
return GetEntDataEnt(entity, offs);
|
||||
}
|
||||
|
||||
@ -418,11 +518,29 @@ stock GetEntPropEnt(entity, PropType:type, const String:prop[])
|
||||
*/
|
||||
stock SetEntPropEnt(entity, PropType:type, const String:prop[], other)
|
||||
{
|
||||
new offs = GetEntSendPropOffs(entity, prop);
|
||||
new offs;
|
||||
|
||||
switch (type)
|
||||
{
|
||||
case Prop_Send:
|
||||
{
|
||||
offs = GetEntSendPropOffs(entity, prop);
|
||||
}
|
||||
case Prop_Data:
|
||||
{
|
||||
offs = FindDataMapOffs(entity, prop);
|
||||
}
|
||||
default:
|
||||
{
|
||||
ThrowError("Invalid Property type %d", type);
|
||||
}
|
||||
}
|
||||
|
||||
if (offs == -1)
|
||||
{
|
||||
ThrowError("Property \"%s\" not found for entity %d", prop, entity);
|
||||
}
|
||||
|
||||
return SetEntDataEnt(entity, offs, other, true);
|
||||
}
|
||||
|
||||
@ -440,11 +558,29 @@ stock SetEntPropEnt(entity, PropType:type, const String:prop[], other)
|
||||
*/
|
||||
stock GetEntPropVector(entity, PropType:type, const String:prop[], Float:vec[3])
|
||||
{
|
||||
new offs = GetEntSendPropOffs(entity, prop);
|
||||
new offs;
|
||||
|
||||
switch (type)
|
||||
{
|
||||
case Prop_Send:
|
||||
{
|
||||
offs = GetEntSendPropOffs(entity, prop);
|
||||
}
|
||||
case Prop_Data:
|
||||
{
|
||||
offs = FindDataMapOffs(entity, prop);
|
||||
}
|
||||
default:
|
||||
{
|
||||
ThrowError("Invalid Property type %d", type);
|
||||
}
|
||||
}
|
||||
|
||||
if (offs == -1)
|
||||
{
|
||||
ThrowError("Property \"%s\" not found for entity %d", prop, entity);
|
||||
}
|
||||
|
||||
return GetEntDataVector(entity, offs, vec);
|
||||
}
|
||||
|
||||
@ -462,10 +598,28 @@ stock GetEntPropVector(entity, PropType:type, const String:prop[], Float:vec[3])
|
||||
*/
|
||||
stock SetEntPropVector(entity, PropType:type, const String:prop[], const Float:vec[3])
|
||||
{
|
||||
new offs = GetEntSendPropOffs(entity, prop);
|
||||
new offs;
|
||||
|
||||
switch (type)
|
||||
{
|
||||
case Prop_Send:
|
||||
{
|
||||
offs = GetEntSendPropOffs(entity, prop);
|
||||
}
|
||||
case Prop_Data:
|
||||
{
|
||||
offs = FindDataMapOffs(entity, prop);
|
||||
}
|
||||
default:
|
||||
{
|
||||
ThrowError("Invalid Property type %d", type);
|
||||
}
|
||||
}
|
||||
|
||||
if (offs == -1)
|
||||
{
|
||||
ThrowError("Property \"%s\" not found for entity %d", prop, entity);
|
||||
}
|
||||
|
||||
return SetEntDataVector(entity, offs, vec, true);
|
||||
}
|
||||
|
@ -45,6 +45,14 @@ struct Plugin
|
||||
#include <functions>
|
||||
#include <timers>
|
||||
|
||||
enum DialogType
|
||||
{
|
||||
DialogType_Msg = 0, /**< just an on screen message */
|
||||
DialogType_Menu, /**< an options menu */
|
||||
DialogType_Text, /**< a richtext dialog */
|
||||
DialogType_Entry /**< an entry box */
|
||||
};
|
||||
|
||||
/**
|
||||
* Declare this as a struct in your plugin to expose its information.
|
||||
* Example:
|
||||
@ -388,15 +396,15 @@ native bool:PrecacheSound(const String:sound[], bool:preload=false);
|
||||
native bool:IsSoundPrecached(const String:sound[]);
|
||||
|
||||
/**
|
||||
* Executes a client command on the server without being networked.
|
||||
* Creates different types of ingame messages.
|
||||
*
|
||||
* @param client Index of the client.
|
||||
* @param fmt Format of the client command.
|
||||
* @param ... Format parameters
|
||||
* @param kv KeyValues handle to set the menu keys and options. (Check iserverplugin.h for more information).
|
||||
* @param type Message type to display ingame.
|
||||
* @noreturn
|
||||
* @error Invalid client index, or client not connected.
|
||||
*/
|
||||
native FakeClientCommand(client, const String:fmt[], any:...);
|
||||
native CreateDialog(client, Handle:kv, DialogType:type);
|
||||
|
||||
#include <helpers>
|
||||
#include <entity>
|
||||
|
@ -229,13 +229,13 @@ namespace SourceMod
|
||||
virtual bool AddFunction(IPluginContext *ctx, funcid_t index) =0;
|
||||
|
||||
/**
|
||||
* @brief Removes a function from the call list.
|
||||
* NOTE: Only removes one instance.
|
||||
*
|
||||
* @param ctx Context to use as a look-up.
|
||||
* @param index Function id to add.
|
||||
* @return Whether or not the function was removed.
|
||||
*/
|
||||
* @brief Removes a function from the call list.
|
||||
* NOTE: Only removes one instance.
|
||||
*
|
||||
* @param ctx Context to use as a look-up.
|
||||
* @param index Function id to add.
|
||||
* @return Whether or not the function was removed.
|
||||
*/
|
||||
virtual bool RemoveFunction(IPluginContext *ctx, funcid_t index) =0;
|
||||
};
|
||||
|
||||
|
@ -244,6 +244,14 @@ namespace SourceMod
|
||||
* @return Current number of connected clients.
|
||||
*/
|
||||
virtual int GetNumPlayers() =0;
|
||||
|
||||
/**
|
||||
* @brief Returns the client index by its userid.
|
||||
*
|
||||
* @param userid Userid of the client.
|
||||
* @return Client index, or 0 if invalid userid passed.
|
||||
*/
|
||||
virtual int GetClientOfUserId(int userid) =0;
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -23,13 +23,18 @@
|
||||
* @brief Defines miscellanious helper functions useful to extensions.
|
||||
*/
|
||||
|
||||
#include <IShareSys.h>
|
||||
#include <IHandleSys.h>
|
||||
#include <sp_vm_api.h>
|
||||
#include <IDataPack.h>
|
||||
|
||||
#define SMINTERFACE_SOURCEMOD_NAME "ISourceMod"
|
||||
#define SMINTERFACE_SOURCEMOD_VERSION 1
|
||||
|
||||
/**
|
||||
* @brief Forward declaration of the KeyValues class.
|
||||
*/
|
||||
class KeyValues;
|
||||
|
||||
namespace SourceMod
|
||||
{
|
||||
/**
|
||||
@ -133,18 +138,29 @@ namespace SourceMod
|
||||
*/
|
||||
virtual void FreeDataPack(IDataPack *pack) =0;
|
||||
|
||||
/**
|
||||
* @brief Returns the automated data pack handle type.
|
||||
*
|
||||
* The readonly data type is the parent of the writable type.
|
||||
* Note that calling CloseHandle() on either type will release the data pack.
|
||||
* The readonly type is inheritable, but due to limitations of the Handle System,
|
||||
* the writable type is not.
|
||||
*
|
||||
* @param readonly If true, the readonly type will be returned.
|
||||
* @return The Handle type for storing generic data packs.
|
||||
*/
|
||||
/**
|
||||
* @brief Returns the automated data pack handle type.
|
||||
*
|
||||
* The readonly data type is the parent of the writable type.
|
||||
* Note that calling CloseHandle() on either type will release the data pack.
|
||||
* The readonly type is inheritable, but due to limitations of the Handle System,
|
||||
* the writable type is not.
|
||||
*
|
||||
* @param readonly If true, the readonly type will be returned.
|
||||
* @return The Handle type for storing generic data packs.
|
||||
*/
|
||||
virtual HandleType_t GetDataPackHandleType(bool readonly=false) =0;
|
||||
|
||||
/**
|
||||
* @brief Retrieves a KeyValues pointer from a handle.
|
||||
*
|
||||
* @param hndl Handle_t from which to retrieve contents.
|
||||
* @param err Optional address to store a possible handle error.
|
||||
* @param root If true it will return the root KeyValues pointer for the whole structure.
|
||||
*
|
||||
* @return The KeyValues pointer, or NULL for any error encountered.
|
||||
*/
|
||||
virtual KeyValues *ReadKeyValuesHandle(Handle_t hndl, HandleError *err=NULL, bool root=false) =0;
|
||||
};
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user