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:
Borja Ferrer 2007-04-12 00:45:53 +00:00
parent 4ecb7a985f
commit 21923d7871
16 changed files with 757 additions and 33 deletions

View File

@ -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;
}

View File

@ -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;

View File

@ -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);

View File

@ -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;

View File

@ -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"

View File

@ -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}
};

View File

@ -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},
};

View File

@ -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]);

View File

@ -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}
};

View File

@ -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

View File

@ -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:...);

View File

@ -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);
}

View File

@ -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>

View File

@ -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;
};

View File

@ -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;
};
}

View File

@ -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;
};
}