diff --git a/core/smn_entities.cpp b/core/smn_entities.cpp index 3c58a47c..77fa4d89 100644 --- a/core/smn_entities.cpp +++ b/core/smn_entities.cpp @@ -35,6 +35,30 @@ inline edict_t *GetEdict(cell_t num) return pEdict; } +inline edict_t *GetEntity(cell_t num, CBaseEntity **pData) +{ + edict_t *pEdict = engine->PEntityOfEntIndex(num); + if (!pEdict || pEdict->IsFree()) + { + return NULL; + } + if (num > 0 && num < g_Players.GetMaxClients()) + { + CPlayer *pPlayer = g_Players.GetPlayerByIndex(num); + if (!pPlayer || !pPlayer->IsConnected()) + { + return NULL; + } + } + IServerUnknown *pUnk; + if ((pUnk=pEdict->GetUnknown()) == NULL) + { + return NULL; + } + *pData = pUnk->GetBaseEntity(); + return pEdict; +} + static cell_t GetMaxEntities(IPluginContext *pContext, const cell_t *params) { return gpGlobals->maxEntities; @@ -184,11 +208,244 @@ static cell_t GetEntityNetClass(IPluginContext *pContext, const cell_t *params) return 1; } +static cell_t GetEntData(IPluginContext *pContext, const cell_t *params) +{ + CBaseEntity *pEntity; + edict_t *pEdict = GetEntity(params[1], &pEntity); + + if (!pEdict || !pEntity) + { + return pContext->ThrowNativeError("Entity %d is invalid", params[1]); + } + + int offset = params[2]; + if (offset < 0 || offset > 32768) + { + return pContext->ThrowNativeError("Offset %d is invalid", offset); + } + + switch (params[3]) + { + case 4: + return *(int *)((uint8_t *)pEntity + offset); + case 2: + return *(short *)((uint8_t *)pEntity + offset); + case 1: + return *((uint8_t *)pEntity + offset); + default: + return pContext->ThrowNativeError("Integer size %d is invalid", params[3]); + } +} + +static cell_t SetEntData(IPluginContext *pContext, const cell_t *params) +{ + CBaseEntity *pEntity; + edict_t *pEdict = GetEntity(params[1], &pEntity); + + if (!pEdict || !pEntity) + { + return pContext->ThrowNativeError("Entity %d is invalid", params[1]); + } + + int offset = params[2]; + if (offset < 0 || offset > 32768) + { + return pContext->ThrowNativeError("Offset %d is invalid", offset); + } + + switch (params[4]) + { + case 4: + { + *(int *)((uint8_t *)pEntity + offset) = params[3]; + break; + } + case 2: + { + *(short *)((uint8_t *)pEntity + offset) = params[3]; + break; + } + case 1: + { + *((uint8_t *)pEntity + offset) = params[3]; + break; + } + default: + return pContext->ThrowNativeError("Integer size %d is invalid", params[4]); + } + + return 1; +} + +static cell_t GetEntDataFloat(IPluginContext *pContext, const cell_t *params) +{ + CBaseEntity *pEntity; + edict_t *pEdict = GetEntity(params[1], &pEntity); + + if (!pEdict || !pEntity) + { + return pContext->ThrowNativeError("Entity %d is invalid", params[1]); + } + + int offset = params[2]; + if (offset < 0 || offset > 32768) + { + return pContext->ThrowNativeError("Offset %d is invalid", offset); + } + + float f = *(float *)((uint8_t *)pEntity + offset); + + return sp_ftoc(f); +} + +static cell_t SetEntDataFloat(IPluginContext *pContext, const cell_t *params) +{ + CBaseEntity *pEntity; + edict_t *pEdict = GetEntity(params[1], &pEntity); + + if (!pEdict || !pEntity) + { + return pContext->ThrowNativeError("Entity %d is invalid", params[1]); + } + + int offset = params[2]; + if (offset < 0 || offset > 32768) + { + return pContext->ThrowNativeError("Offset %d is invalid", offset); + } + + *(float *)((uint8_t *)pEntity + offset) = sp_ctof(params[3]); + + return 1; +} + +static cell_t GetEntDataVector(IPluginContext *pContext, const cell_t *params) +{ + CBaseEntity *pEntity; + edict_t *pEdict = GetEntity(params[1], &pEntity); + + if (!pEdict || !pEntity) + { + return pContext->ThrowNativeError("Entity %d is invalid", params[1]); + } + + int offset = params[2]; + if (offset < 0 || offset > 32768) + { + return pContext->ThrowNativeError("Offset %d is invalid", offset); + } + + Vector *v = (Vector *)((uint8_t *)pEntity + offset); + + cell_t *vec; + pContext->LocalToPhysAddr(params[3], &vec); + + vec[0] = v->x; + vec[1] = v->y; + vec[2] = v->z; + + return 1; +} + +static cell_t SetEntDataVector(IPluginContext *pContext, const cell_t *params) +{ + CBaseEntity *pEntity; + edict_t *pEdict = GetEntity(params[1], &pEntity); + + if (!pEdict || !pEntity) + { + return pContext->ThrowNativeError("Entity %d is invalid", params[1]); + } + + int offset = params[2]; + if (offset < 0 || offset > 32768) + { + return pContext->ThrowNativeError("Offset %d is invalid", offset); + } + + Vector *v = (Vector *)((uint8_t *)pEntity + offset); + + cell_t *vec; + pContext->LocalToPhysAddr(params[3], &vec); + + v->x = vec[0]; + v->y = vec[1]; + v->z = vec[2]; + + return 1; +} + +static cell_t GetEntDataEnt(IPluginContext *pContext, const cell_t *params) +{ + CBaseEntity *pEntity; + edict_t *pEdict = GetEntity(params[1], &pEntity); + + if (!pEdict || !pEntity) + { + return pContext->ThrowNativeError("Entity %d is invalid", params[1]); + } + + int offset = params[2]; + if (offset < 0 || offset > 32768) + { + return pContext->ThrowNativeError("Offset %d is invalid", offset); + } + + CBaseHandle &hndl = *(CBaseHandle *)((uint8_t *)pEntity + offset); + + if (!hndl.IsValid()) + { + return 0; + } + + /* :TODO: check serial no.? */ + + return hndl.GetEntryIndex(); +} + +static cell_t SetEntDataEnt(IPluginContext *pContext, const cell_t *params) +{ + CBaseEntity *pEntity; + edict_t *pEdict = GetEntity(params[1], &pEntity); + + if (!pEdict || !pEntity) + { + return pContext->ThrowNativeError("Entity %d is invalid", params[1]); + } + + int offset = params[2]; + if (offset < 0 || offset > 32768) + { + return pContext->ThrowNativeError("Offset %d is invalid", offset); + } + + CBaseHandle &hndl = *(CBaseHandle *)((uint8_t *)pEntity + offset); + + if (params[3] == 0) + { + hndl.Set(NULL); + } else { + edict_t *pEdict = GetEdict(params[3]); + if (!pEdict) + { + return pContext->ThrowNativeError("Entity %d is invalid", params[3]); + } + IServerEntity *pEntOther = pEdict->GetIServerEntity(); + hndl.Set(pEntOther); + } + + return 1; +} + REGISTER_NATIVES(entityNatives) { {"CreateEdict", CreateEdict}, {"GetEdictClassname", GetEdictClassname}, {"GetEdictFlags", GetEdictFlags}, + {"GetEntData", GetEntData}, + {"GetEntDataEnt", GetEntDataEnt}, + {"GetEntDataFloat", GetEntDataFloat}, + {"GetEntDataVector", GetEntDataVector}, {"GetEntityNetClass", GetEntityNetClass}, {"GetMaxEntities", GetMaxEntities}, {"IsEntNetworkable", IsEntNetworkable}, @@ -196,4 +453,8 @@ REGISTER_NATIVES(entityNatives) {"IsValidEntity", IsValidEntity}, {"RemoveEdict", RemoveEdict}, {"SetEdictFlags", SetEdictFlags}, + {"SetEntData", SetEntData}, + {"SetEntDataEnt", SetEntDataEnt}, + {"SetEntDataFloat", SetEntDataFloat}, + {"SetEntDataVector", SetEntDataVector}, }; diff --git a/plugins/include/entity.inc b/plugins/include/entity.inc index 6870d62d..9277a6c9 100644 --- a/plugins/include/entity.inc +++ b/plugins/include/entity.inc @@ -129,3 +129,123 @@ native bool:GetEdictClassname(edict, String:clsname[], maxlength); * @error Invalid edict index. */ native bool:GetEntityNetClass(edict, String:clsname[], maxlength); + +/** + * @section Entity offset functions + * + * Offsets should be specified in byte distance from the CBaseEntity + * structure, not short (double byte) or integer (four byte) multiples. + * It is somewhat common practice to use offsets aligned to their final + * type, and thus make sure you are not falling to this error in SourceMod. + * For example, if your "integer-aligned" offset was 119, your byte-aligned + * offset is 119*4, or 476. + + * Specifying incorrect offsets or the incorrect data type for an offset + * can have fatal consequences. If you are hardcoding offsets, and the + * layout of CBaseEntity does not match, you can easily crash the server. + * + * The reasonable bounds for offsets is greater than or equal to 0 and + * below 32768. Offsets out of these bounds will throw an error. However, + * this does not represent any real range, it is simply a sanity check for + * illegal values. Any range outside of the CBaseEntity structure's private + * size will cause undefined behaviour or even crash. + */ + +/** + * Peeks into an entity's object data and retrieves the integer value at + * the given offset. + * + * @param entity Edict index. + * @param offset Offset to use. + * @param size Number of bytes to read (valid values are 1, 2, or 4). + * @return Value at the given memory location. + * @error Invalid entity or offset out of reasonable bounds. + */ +native GetEntData(entity, offset, size=4); + +/** + * Peeks into an entity's object data and sets the integer value at + * the given offset. + * + * @param entity Edict index. + * @param offset Offset to use. + * @param size Number of bytes to write (valid values are 1, 2, or 4). + * @return Value at the given memory location. + * @error Invalid entity or offset out of reasonable bounds. + * @noreturn + */ +native SetEntData(entity, offset, value, size=4); + +/** + * Peeks into an entity's object data and retrieves the float value at + * the given offset. + * + * @param entity Edict index. + * @param offset Offset to use. + * @return Value at the given memory location. + * @error Invalid entity or offset out of reasonable bounds. + */ +native Float:GetEntDataFloat(entity, offset); + +/** + * Peeks into an entity's object data and sets the integer value at + * the given offset. + * + * @param entity Edict index. + * @param offset Offset to use. + * @return Value at the given memory location. + * @error Invalid entity or offset out of reasonable bounds. + * @noreturn + */ +native SetEntDataFloat(entity, offset, Float:value); + +/** + * Peeks into an entity's object data and retrieves the entity handle + * info at the given offset. + * + * @param entity Edict index. + * @param offset Offset to use. + * @return Entity index at the given location, or 0 if none. + * @error Invalid entity or offset out of reasonable bounds. + */ +native GetEntDataEnt(entity, offset); + +/** + * Peeks into an entity's object data and sest the entity handle info + * at the given offset. + * + * @param entity Edict index. + * @param offset Offset to use. + * @param other Entity index to set, or 0 to clear. + * @noreturn + * @error Invalid entity or offset out of reasonable bounds. + */ +native SetEntDataEnt(entity, offset, other); + +/** + * Peeks into an entity's object data and retrieves the vector at the + * given offset. + * @note Both a Vector and a QAngle are three floats. This is a + * convenience function and will work with both types. + * + * @param entity Edict index. + * @param offset Offset to use. + * @param vec Vector buffer to store data in. + * @noreturn + * @error Invalid entity or offset out of reasonable bounds. + */ +native GetEntDataVector(entity, offset, Float:vec[3]); + +/** + * Peeks into an entity's object data and sets the vector at the given + * offset. + * @note Both a Vector and a QAngle are three floats. This is a + * convenience function and will work with both types. + * + * @param entity Edict index. + * @param offset Offset to use. + * @param vec Vector to set. + * @noreturn + * @error Invalid entity or offset out of reasonable bounds. + */ +native SetEntDataVector(entity, offset, const Float:vec[3]);