Fakemeta (module fakemeta) gives AMXX plugins the same low-level access to the engine that a Metamod plugin written in C++ would have: it reads and writes the entvars_t struct of any entity, calls engine and game-DLL functions directly, and hooks the engine's forwards. It is fast and it is everywhere, but it is also unforgiving — there are no named members and no safety rails. This guide covers the three things you will use constantly: pev/set_pev, engfunc/dllfunc, and register_forward.
1. Reading entity variables with pev
Every entity — players, weapons, grenades, doors — has an entvars_t block. pev(index, member) reads a field. Integer fields return directly; vector fields are read into a passed array; string fields into a buffer:
#include <amxmodx>
#include <fakemeta>
public show_state(id)
{
new Float:health = pev(id, pev_health)
new flags = pev(id, pev_flags)
new team = pev(id, pev_team)
new Float:origin[3]
pev(id, pev_origin, origin)
new classname[32]
pev(id, pev_classname, classname, charsmax(classname))
server_print("hp=%.0f onground=%d team=%d at %.0f %.0f %.0f",
health, (flags & FL_ONGROUND) ? 1 : 0, team,
origin[0], origin[1], origin[2])
}
The pev_* constants are the field names. The ones you will reach for most are pev_origin, pev_velocity, pev_health, pev_maxspeed, pev_flags, pev_button, pev_team, pev_frags, pev_deadflag, and the generic user fields pev_iuser1..pev_iuser4.
2. Writing with set_pev
set_pev mirrors pev. Set a scalar directly, a vector from an array:
public buff_player(id)
{
set_pev(id, pev_health, 150.0)
set_pev(id, pev_maxspeed, 320.0)
new Float:vel[3]
pev(id, pev_velocity, vel)
vel[2] = 300.0 // launch upward
set_pev(id, pev_velocity, vel)
}
Note the types: pev_health and pev_maxspeed are floats, so pass 150.0, not 150. Writing an integer into a float member is a tag-mismatch warning and produces nonsense in-game. When you move a player, prefer setting velocity or using the engine's SetOrigin (below) over writing pev_origin directly, which can push a player into a wall.
3. engfunc and dllfunc
engfunc calls an engine function; dllfunc calls a game-DLL function. Both take a constant naming the function followed by its arguments:
// teleport safely through the engine
new Float:dest[3] = { 0.0, 0.0, 128.0 }
engfunc(EngFunc_SetOrigin, id, dest)
// give the player a weapon through the game DLL
dllfunc(DLLFunc_GiveNamedItem, id, "weapon_ak47")
// slay a player cleanly
dllfunc(DLLFunc_ClientKill, id)
These call the same functions the game itself calls, so side effects (sounds, prediction, physics) happen correctly — unlike poking entvars_t by hand. The full list of EngFunc_* and DLLFunc_* constants is in fakemeta.inc; read the include for exact argument orders rather than guessing.
4. Engine forwards
The real power of fakemeta is register_forward: it hooks an engine function so your code runs every time the engine calls it. FM_PlayerPreThink runs each frame per player, FM_Touch when two entities touch, FM_EmitSound whenever a sound plays:
public plugin_init()
{
register_plugin("Fakemeta Demo", "1.0", "CSB")
register_forward(FM_PlayerPreThink, "fw_prethink")
register_forward(FM_EmitSound, "fw_emitsound")
}
public fw_prethink(id)
{
if (!is_user_alive(id))
return FMRES_IGNORED
// enforce a speed cap every frame
set_pev(id, pev_maxspeed, 300.0)
return FMRES_IGNORED
}
public fw_emitsound(id, channel, const sample[])
{
// block footstep sounds
if (containi(sample, "player/pl_step") != -1)
return FMRES_SUPERCEDE
return FMRES_IGNORED
}
The return value controls what happens next. FMRES_IGNORED lets the engine proceed normally. FMRES_SUPERCEDE blocks the original call entirely — that is how you silence a sound or cancel a touch. FMRES_HANDLED marks it handled without blocking. Registering a forward as a post hook (pass 1 as the last argument to register_forward) runs your code after the engine, when you want the result rather than the chance to block it.
Common errors
- Tag mismatch on
pev_health/pev_origin— you used an integer where a float is required. Vector and health fields areFloat:. FM_PlayerPreThinktanks the server — it fires every frame for every player. Do the absolute minimum inside it, and bail early withis_user_alive. Heavy work here is the classic cause of low server FPS.- Blocking a forward does nothing — you returned
FMRES_IGNORED. OnlyFMRES_SUPERCEDEstops the original call. - Player stuck in geometry after moving — you wrote
pev_origindirectly. Useengfunc(EngFunc_SetOrigin, ...)so the engine relinks the entity.
Verification
Print a value you set and read it back the next frame to confirm the write stuck: set pev_maxspeed in FM_PlayerPreThink, then check in-game that movement speed actually changed. For blocking hooks, trigger the sound or touch and confirm FMRES_SUPERCEDE suppressed it. When you need to modify damage or spawning rather than engine calls, Ham Sandwich is the better tool — see the Ham Sandwich guide — and on ReGameDLL, ReAPI replaces most raw fakemeta with named members.









