SM-Memory
Exposes more memory utility functions for SourceMod plugins.
Installing
Grab a Release and extract to your server directory.
Building
Docker
If using docker to build for Linux, run build_docker.sh in the build folder (git bash works for Windows).
Dockerfile pulls and builds from Debian 10.
Manually
Edit build.bat or build.sh to point to your SM and MM folders and run.
Use Cases
Make Actual, Real Classes
Click
```cpp
// For sanity
enum PlayerData (+= 0x04)
{
_pclient = 0,
_phealth,
_pkills,
_ppoints,
_pdeaths,
_pdamage,
_p_LENGTH
}
#define wf(%1,%2) StoreToAddressFast((%1), (%2))
#define qget(%1) public get() { return Deref(this + (%1)); }
#define qset(%1) public set(int i) { wf(this + (%1), i); }
methodmap PlayerData
{
property int m_iClient
{
qget(_pclient)
qset(_pclient)
}
property int m_iHealth
{
qget(_phealth)
qset(_phealth)
}
property int m_iKills
{
qget(_pkills)
qset(_pkills)
}
property int m_iPoints
{
qget(_ppoints)
qset(_ppoints)
}
property int m_iDeaths
{
qget(_pdeaths)
qset(_pdeaths)
}
property int m_iDamage
{
qget(_pdamage)
qset(_pdamage)
}
public PlayerData(int client)
{
ptr p = malloc(_p_LENGTH);
p.m_iClient = client;
memset(this + 4, 0, _p_LENGTH - 4);
}
}
#undef wf
#undef qget
#undef qset
```
Recreate Valve Classes in SourcePawn
Click
```cpp
enum struct _Vector
{
int x;
int y;
int z;
}
enum Vector {nullvec = 0}
#define qget(%1) public get() { return Deref(this + view_as< any >((%1) * cellbytes)); }
#define qset(%1,%2) public set(%1 i) { WriteVal(this + view_as< any >((%2) * cellbytes), i); }
methodmap Vector
{
property float x
{
qget(_Vector::x)
qset(float, _Vector::x)
}
property float y
{
qget(_Vector::y)
qset(float, _Vector::y)
}
property float z
{
qget(_Vector::z)
qset(float, _Vector::z)
}
public Vector(float x = 0.0, float y = 0.0, float z = 0.0)
{
Vector v = malloc(sizeof(_Vector) * cellbytes);
v.x = x;
v.y = y;
v.z = z;
return v;
}
public static Vector From(ptr address)
{
return view_as< Vector >(address);
}
};
enum EHANDLE
{
NULL_EHANDLE = -1
};
enum ECritType
{
CRIT_NONE = 0,
CRIT_MINI,
CRIT_FULL,
};
enum struct _CTakeDamageInfo
{
_Vector m_vecDamageForce;
_Vector m_vecDamagePosition;
_Vector m_vecReportedPosition; // Position players are told damage is coming from
EHANDLE m_hInflictor;
EHANDLE m_hAttacker;
EHANDLE m_hWeapon;
float m_flDamage;
float m_flMaxDamage;
float m_flBaseDamage; // The damage amount before skill leve adjustments are made. Used to get uniform damage forces.
int m_bitsDamageType;
int m_iDamageCustom;
int m_iDamageStats;
int m_iAmmoType; // AmmoType of the weapon used to cause this damage, if any
int m_iDamagedOtherPlayers;
int m_iPlayerPenetrationCount;
float m_flDamageBonus; // Anything that increases damage (crit) - store the delta
EHANDLE m_hDamageBonusProvider; // Who gave us the ability to do extra damage?
bool m_bForceFriendlyFire; // Ideally this would be a dmg type, but we can't add more
float m_flDamageForForce;
ECritType m_eCritType;
}
methodmap CTakeDamageInfo
{
property Vector m_vecDamageForce
{
public get()
{
return view_as< Vector >(this);
}
public set(Vector v)
{
StoreToAddressFast(this, v.x);
StoreToAddressFast(this + view_as< any >(4), v.y);
StoreToAddressFast(this + view_as< any >(8), v.z);
}
}
property Vector m_vecDamagePosition
{
public get()
{
return view_as< Vector >(this + view_as< any >(12))
}
public set(Vector v)
{
StoreToAddressFast(this + view_as< any >(12), v.x);
StoreToAddressFast(this + view_as< any >(16), v.y);
StoreToAddressFast(this + view_as< any >(20), v.z);
}
}
property Vector m_vecReportedPosition // Position players are told damage is coming from
{
public get()
{
return view_as< Vector >(this + view_as< any >(24))
}
public set(Vector v)
{
StoreToAddressFast(this + view_as< any >(24), v.x);
StoreToAddressFast(this + view_as< any >(28), v.y);
StoreToAddressFast(this + view_as< any >(32), v.z);
}
}
property EHANDLE m_hInflictor
{
qget(_CTakeDamageInfo::m_hInflictor)
qset(EHANDLE, _CTakeDamageInfo::m_hInflictor)
}
property EHANDLE m_hAttacker
{
qget(_CTakeDamageInfo::m_hAttacker)
qset(EHANDLE, _CTakeDamageInfo::m_hAttacker)
}
property EHANDLE m_hWeapon
{
qget(_CTakeDamageInfo::m_hWeapon)
qset(EHANDLE, _CTakeDamageInfo::m_hWeapon)
}
property float m_flDamage
{
qget(_CTakeDamageInfo::m_flDamage)
qset(float, _CTakeDamageInfo::m_flDamage)
}
property float m_flMaxDamage
{
qget(_CTakeDamageInfo::m_flMaxDamage)
qset(float, _CTakeDamageInfo::m_flMaxDamage)
}
property float m_flBaseDamage // The damage amount before skill leve adjustments are made. Used to get uniform damage forces.
{
qget(_CTakeDamageInfo::m_flBaseDamage)
qset(float, _CTakeDamageInfo::m_flBaseDamage)
}
property int m_bitsDamageType
{
qget(_CTakeDamageInfo::m_bitsDamageType)
qset(int, _CTakeDamageInfo::m_bitsDamageType)
}
property int m_iDamageCustom
{
qget(_CTakeDamageInfo::m_iDamageCustom)
qset(int, _CTakeDamageInfo::m_iDamageCustom)
}
property int m_iDamageStats
{
qget(_CTakeDamageInfo::m_iDamageStats)
qset(int, _CTakeDamageInfo::m_iDamageStats)
}
property int m_iAmmoType // AmmoType of the weapon used to cause this damage, if any
{
qget(_CTakeDamageInfo::m_iAmmoType)
qset(int, _CTakeDamageInfo::m_iAmmoType)
}
property int m_iDamagedOtherPlayers
{
qget(_CTakeDamageInfo::m_iDamagedOtherPlayers)
qset(int, _CTakeDamageInfo::m_iDamagedOtherPlayers)
}
property int m_iPlayerPenetrationCount
{
qget(_CTakeDamageInfo::m_iPlayerPenetrationCount)
qset(int, _CTakeDamageInfo::m_iPlayerPenetrationCount)
}
property float m_flDamageBonus // Anything that increases damage (crit) - store the delta
{
qget(_CTakeDamageInfo::m_flDamageBonus)
qset(float, _CTakeDamageInfo::m_flDamageBonus)
}
property EHANDLE m_hDamageBonusProvider // Who gave us the ability to do extra damage?
{
qget(_CTakeDamageInfo::m_hDamageBonusProvider)
qset(EHANDLE, _CTakeDamageInfo::m_hDamageBonusProvider)
}
property bool m_bForceFriendlyFire // Ideally this would be a dmg type, but we can't add more
{
qget(_CTakeDamageInfo::m_bForceFriendlyFire)
qset(bool, _CTakeDamageInfo::m_bForceFriendlyFire)
}
property float m_flDamageForForce
{
qget(_CTakeDamageInfo::m_flDamageForForce)
qset(float, _CTakeDamageInfo::m_flDamageForForce)
}
property ECritType m_eCritType
{
qget(_CTakeDamageInfo::m_eCritType)
qset(ECritType, _CTakeDamageInfo::m_eCritType)
}
public void Init(int pInflictor, int pAttacker, int pWeapon, Vector damageForce,
Vector damagePosition, Vector reportedPosition, float flDamage, int bitsDamageType, int iCustomDamage)
{
this.m_hInflictor = GetEntityHandle(pInflictor);
if ( pAttacker != -1 )
{
this.m_hAttacker = GetEntityHandle(pAttacker);
}
else
{
this.m_hAttacker = GetEntityHandle(pInflictor);
}
this.m_hWeapon = GetEntityHandle(pWeapon);
this.m_flDamage = flDamage;
// This is actually FLT_MAX but I'm lazy
this.m_flBaseDamage = 0.0;
this.m_bitsDamageType = bitsDamageType;
this.m_iDamageCustom = iCustomDamage;
this.m_flMaxDamage = flDamage;
this.m_vecDamageForce = damageForce;
this.m_vecDamagePosition = damagePosition;
this.m_vecReportedPosition = reportedPosition;
this.m_iAmmoType = -1;
this.m_iDamagedOtherPlayers = 0;
this.m_iPlayerPenetrationCount = 0;
this.m_flDamageBonus = 0.0;
this.m_bForceFriendlyFire = false;
this.m_flDamageForForce = 0.0;
this.m_eCritType = CRIT_NONE;
}
public void Set(int pInflictor, int pAttacker, int pWeapon,
const Vector damageForce, const Vector damagePosition, float flDamage, int bitsDamageType, int iKillType, Vector reportedPosition)
{
this.Init(pInflictor, pAttacker, pWeapon, damageForce, damagePosition, reportedPosition, flDamage, bitsDamageType, iKillType)
}
public CTakeDamageInfo(int pInflictor = -1, int pAttacker = -1, int pWeapon = -1,
const Vector damageForce = nullvec, const Vector damagePosition = nullvec, float flDamage = 0.0, int bitsDamageType = 0, int iKillType = 0, Vector reportedPosition = nullvec)
{
CTakeDamageInfo info = malloc(sizeof(_CTakeDamageInfo) * cellbytes);
info.Set( pInflictor, pAttacker, pWeapon, damageForce, damagePosition, flDamage, bitsDamageType, iKillType, reportedPosition );
}
public static CTakeDamageInfo From(ptr p)
{
return view_as< CTakeDamageInfo >(p);
}
public void Free()
{
free(this);
}
public int GetInflictor()
{
return GetEntityFromHandle(this.m_hInflictor);
}
public void SetInflictor( int pInflictor )
{
this.m_hInflictor = GetEntityHandle(pInflictor);
}
public int GetAttacker()
{
return GetEntityFromHandle(this.m_hAttacker);
}
public void SetAttacker( int pAttacker )
{
this.m_hAttacker = GetEntityHandle(pAttacker);
}
public int GetWeapon()
{
return GetEntityFromHandle(this.m_hWeapon);
}
public void SetWeapon( int pWeapon )
{
this.m_hWeapon = GetEntityHandle(pWeapon);
}
public float GetDamage()
{
return this.m_flDamage;
}
public void SetDamage( float flDamage )
{
this.m_flDamage = flDamage;
}
public float GetMaxDamage()
{
return this.m_flMaxDamage;
}
public void SetMaxDamage( float flMaxDamage )
{
this.m_flMaxDamage = flMaxDamage;
}
public void ScaleDamage( float flScaleAmount )
{
this.m_flDamage *= flScaleAmount;
}
public void AddDamage( float flAddAmount )
{
this.m_flDamage += flAddAmount;
}
public void SubtractDamage( float flSubtractAmount )
{
this.m_flDamage -= flSubtractAmount;
}
public float GetDamageBonus()
{
return this.m_flDamageBonus;
}
public int GetDamageBonusProvider()
{
return GetEntityFromHandle(this.m_hDamageBonusProvider);
}
public void SetDamageBonus( float flBonus, int pProvider /*= NULL*/ )
{
this.m_flDamageBonus = flBonus;
this.m_hDamageBonusProvider = GetEntityHandle(pProvider);
}
public bool BaseDamageIsValid()
{
return (this.m_flBaseDamage != 0.0);
}
public float GetBaseDamage()
{
if( this.BaseDamageIsValid() )
return this.m_flBaseDamage;
// No one ever specified a base damage, so just return damage.
return this.m_flDamage;
}
public Vector GetDamageForce()
{
return this.m_vecDamageForce;
}
public void SetDamageForce( const Vector &damageForce )
{
this.m_vecDamageForce = damageForce;
}
public void ScaleDamageForce( float flScaleAmount )
{
StoreToAddressFast(this.m_vecDamageForce, view_as< float >(Deref(this.m_vecDamageForce) * flScaleAmount));
StoreToAddressFast(this.m_vecDamageForce + view_as< any >(4), view_as< float >(Deref(this.m_vecDamageForce + view_as< any >(4)) * flScaleAmount));
StoreToAddressFast(this.m_vecDamageForce + view_as< any >(8), view_as< float >(Deref(this.m_vecDamageForce + view_as< any >(8)) * flScaleAmount));
}
public float GetDamageForForceCalc()
{
return this.m_flDamageForForce;
}
public void SetDamageForForceCalc( float flDamage )
{
this.m_flDamageForForce = flDamage;
}
public Vector GetDamagePosition()
{
return this.m_vecDamagePosition;
}
public void SetDamagePosition( Vector damagePosition )
{
this.m_vecDamagePosition = damagePosition;
}
public Vector GetReportedPosition()
{
return this.m_vecReportedPosition;
}
public void SetReportedPosition( Vector reportedPosition )
{
this.m_vecReportedPosition = reportedPosition;
}
public void SetDamageType( int bitsDamageType )
{
this.m_bitsDamageType = bitsDamageType;
}
public int GetDamageType()
{
return this.m_bitsDamageType;
}
public void AddDamageType( int bitsDamageType )
{
this.m_bitsDamageType |= bitsDamageType;
}
public int GetDamageCustom()
{
return this.m_iDamageCustom;
}
public void SetDamageCustom( int iDamageCustom )
{
this.m_iDamageCustom = iDamageCustom;
}
public int GetDamageStats()
{
return this.m_iDamageCustom;
}
public void SetDamageStats( int iDamageCustom )
{
this.m_iDamageCustom = iDamageCustom;
}
public int GetAmmoType()
{
return this.m_iAmmoType;
}
public void SetAmmoType( int iAmmoType )
{
this.m_iAmmoType = iAmmoType;
}
public void CopyDamageToBaseDamage()
{
this.m_flBaseDamage = this.m_flDamage;
}
};
```
Hook a Function From Any Library
Click
```cpp
#include
#include
public void OnPluginStart()
{
DynLib lib = new DynLib("./tf/addons/sourcemod/bin/sourcemod.logic");
Address func = lib.ResolveSymbol("_ZN6Logger8LogErrorEPKcz");
if (func == Address_Null)
SetFailState("Could not find Logger::LogErrorEx");
DynamicDetour detour = new DynamicDetour(func, CallConv_CDECL, ReturnType_Void);
detour.AddParam(HookParamType_Int);
detour.AddParam(HookParamType_CharPtr);
detour.AddParam(HookParamType_Int);
detour.Enable(Hook_Pre, Logger_LogErrorEx);
detour.Enable(Hook_Post, Logger_LogErrorExPost);
func = lib.ResolveSymbol("_ZN6Logger15LogToOpenFileExEP8_IO_FILEPKcPc");
detour = new DynamicDetour(func, CallConv_CDECL, ReturnType_Void);
detour.AddParam(HookParamType_Int);
detour.AddParam(HookParamType_Int);
detour.AddParam(HookParamType_CharPtr);
detour.AddParam(HookParamType_Int);
detour.Enable(Hook_Pre, Logger_LogToOpenFile);
delete lib;
RegServerCmd("sm_logerror", MakeLogError);
}
public Action MakeLogError(int args)
{
LogError("hi %s %d", "asd", 1);
return Plugin_Handled;
}
static bool g_Start;
public MRESReturn Logger_LogErrorEx(DHookParam hParams)
{
g_Start = true;
}
public MRESReturn Logger_LogErrorExPost(DHookParam hParams)
{
g_Start = false;
}
static bool g_InCall;
public MRESReturn Logger_LogToOpenFile(DHookParam hParams)
{
if (!g_Start)
return MRES_Ignored;
if (g_InCall)
{
PrintToServer("LogHook: Breaking recursion.")
return MRES_Ignored;
}
g_InCall = true;
char buffer[1024];
hParams.GetString(3, buffer, sizeof(buffer));
PrintToServer("%s", buffer);
char buffer2[1024];
VAFormat(buffer2, sizeof(buffer2), buffer, hParams.Get(4));
PrintToServer("%s", buffer2);
g_InCall = false;
return MRES_Ignored;
}
```
Call Library API From a Plugin
Click
```cpp
#include
#include
public void OnPluginStart()
{
DynLib lib = new DynLib("user32.dll");
Address export = lib.ResolveSymbol("MessageBoxA");
PrintToServer("MessageBoxA -> 0x%X", export);
StartPrepSDKCall(SDKCall_Static);
PrepSDKCall_SetAddress(export);
PrepSDKCall_AddParameter(SDKType_PlainOldData, SDKPass_Plain);
PrepSDKCall_AddParameter(SDKType_String, SDKPass_Pointer);
PrepSDKCall_AddParameter(SDKType_String, SDKPass_Pointer);
PrepSDKCall_AddParameter(SDKType_PlainOldData, SDKPass_Plain);
Handle call = EndPrepSDKCall();
// Opens a message box that prints "Hello World!"
SDKCall(call, 0, "Hello World!", "Hi", 0);
delete lib;
delete call;
}
```
Perform a dynamic_cast
Click
```cpp
#include
#include
public void OnEntityCreated(int ent, const char[] classname)
{
any pMeter = DynamicCast(ent, "IHasGenericMeter");
if (pMeter)
{
char typeName[64];
// Can also use GetEntityAddress(ent); either will suffice
GetClassTypeInfoName(pMeter, typeName, sizeof(typeName));
PrintToConsole("%s - %s", classname, typeName);
}
}
```