Romania XPlayZM.CSBlackDevil.COM # Zombie Plague UltimateX [ Respawn + FDL + VIP 22-12PM + POINTS] ® 16/32
Unknown ❤ RESPAWN.ZAFEST.RO | VIP FREE | FREE FOR ALL ❤ 0/32
France EMIRATES.KiNGS 亗 GAMEAREA ||͇̿P͇̿U͇̿B͇̿L͇̿I͇̿C͇̿| ✪ 32/32
United Kingdom BALKAN.KiNGS.EU ||͇̿P͇̿U͇̿B͇̿L͇̿I͇̿C͇̿| ✪ 24/32
Promoveaza serverul
81 lines 6.7 KiB Markdown Source Rendered Raw Permalink History
---
title: "register_event and register_event_ex: The Complete Guide"
excerpt: "How AMXX game events work, the read-flags and condition strings that filter them, and why register_event_ex is the modern, type-checked replacement."
category: "plugin-dev"
tags: "register_event, events"
image: ""
read_time: "6 min read"
---
<p><code>register_event</code> is the oldest and still the most widely used hook in AMX Mod X. It listens for network messages the game DLL sends to clients &mdash; <code>DeathMsg</code>, <code>CurWeapon</code>, <code>Health</code>, <code>DamageEnd</code>, and dozens more &mdash; and fires your function when one arrives. It hooks nothing inside the engine and needs no external module beyond core AMXX, which is exactly why it survives on every stack from HLDS to ReHLDS. This guide covers the flag string, the condition arguments that trip up most people, how to read message arguments, and where <code>register_event_ex</code> improves on it.</p>

<h2>1. The signature and where to register</h2>
<p>Register events once, in <code>plugin_init()</code>. The call takes the event name, your callback, a flag string, and any number of condition strings:</p>
<pre>#include &lt;amxmodx&gt;

public plugin_init()
{
    register_plugin("Event Demo", "1.0", "CSB")
    register_event("DeathMsg", "on_death", "a")
    register_event("CurWeapon", "on_curweapon", "be", "1=1")
}</pre>
<p>The event names are the message names the game DLL emits. You can list them live with <code>message_begin</code> logging, but the common ones are stable across CS 1.6.</p>

<h2>2. The flag string</h2>
<p>The third argument is a string of single-character flags. The letters you will actually use:</p>
<table>
<thead><tr><th>Flag</th><th>Meaning</th></tr></thead>
<tbody>
<tr><td><code>a</code></td><td>Fire only when the message is broadcast globally (sent to all players)</td></tr>
<tr><td><code>b</code></td><td>Fire per receiving client &mdash; the message is directed at one player, and the first parameter of your handler is that player's index</td></tr>
<tr><td><code>d</code></td><td>Only fire when the receiving player is dead</td></tr>
<tr><td><code>e</code></td><td>Only fire when the receiving player is alive</td></tr>
</tbody>
</table>
<p>A broadcast event like <code>DeathMsg</code> uses <code>a</code>. A per-client event like <code>CurWeapon</code> (the engine tells each player about their own weapon) uses <code>b</code>, usually with <code>e</code> so it only runs for living players. That is why the classic speed/weapon idiom is <code>"be"</code>.</p>

<h2>3. Condition strings</h2>
<p>Everything after the flag string is a condition on a numbered message argument. The number is the argument index (1-based), followed by a comparison operator and a value:</p>
<pre>// only fire when arg 1 equals 1
register_event("CurWeapon", "on_curweapon", "be", "1=1")

// only fire when arg 2 (weapon id) is the AWP
register_event("CurWeapon", "on_awp", "be", "1=1", "2=19")

// fire when arg 1 is greater than 0
register_event("Health", "on_low_hp", "be", "1&gt;0")</pre>
<p>Multiple conditions are ANDed together. Operators are <code>=</code>, <code>!</code> (not equal), <code>&lt;</code>, <code>&gt;</code>, and <code>&amp;</code> (bitwise-and non-zero). For <code>CurWeapon</code>, arg&nbsp;1 is whether the weapon is active and arg&nbsp;2 is the weapon id &mdash; filtering in the condition string is far cheaper than filtering with <code>if</code> inside a handler that fires on every weapon switch.</p>

<h2>4. Reading message arguments in the handler</h2>
<p>Inside the callback, pull argument values with <code>read_data</code>. Integer args use <code>read_data(n)</code>, floats use <code>read_data(n, Float:...)</code> via the float form, and strings use the buffer form:</p>
<pre>public on_death()
{
    new killer  = read_data(1)   // attacker index
    new victim  = read_data(2)   // victim index
    new headshot = read_data(3)  // 1 if headshot

    if (killer == victim || !killer)
        return

    new name[32]
    get_user_name(killer, name, charsmax(name))
    client_print(0, print_chat, "[CSB] %s got a kill%s",
        name, headshot ? " (headshot)" : "")
}</pre>
<p>With flag <code>b</code>, the affected player is passed as the handler's first parameter, so declare it: <code>public on_curweapon(id)</code>. With <code>a</code>, there is no per-player id &mdash; you read indices out of the message data instead, as above.</p>

<h2>5. register_event_ex</h2>
<p>The letter-flag string is compact but easy to get wrong, and a typo in it is silent. <code>register_event_ex</code> is the newer form: it takes the same event name and callback, but the flags argument is a bitmask built from named <code>RegisterEvent_*</code> constants instead of a cryptic string, so a mistake is a compile error rather than a runtime surprise:</p>
<pre>register_event_ex("CurWeapon", "on_curweapon",
    RegisterEvent_OnePlayer | RegisterEvent_Alive)</pre>
<p>The exact constant names are defined in <code>amxmodx.inc</code> &mdash; open the include and read them rather than guessing, because they are the authoritative list. Functionally it is the same hook; prefer it in new code for readability. Condition strings still work the same way as trailing arguments.</p>

<h2>Common errors</h2>
<ul>
<li><strong>Handler never fires</strong> &mdash; wrong flag. A per-client event registered with <code>a</code> (or vice versa) simply never matches. Try registering with no flags first to confirm the event name is right, then add filters.</li>
<li><strong>Handler fires but <code>id</code> is 0</strong> &mdash; you used flag <code>a</code> and declared <code>public handler(id)</code>. Global events do not pass a player id; read indices from the data instead.</li>
<li><strong>Condition never matches</strong> &mdash; argument indices are 1-based and per-event. Log <code>read_data(0)</code> (the argument count) and each argument to discover the real layout before hard-coding a condition.</li>
<li><strong>Reads return garbage</strong> &mdash; you called <code>read_data</code> with the wrong type. Use the float form for float args; an integer read of a float argument returns its bit pattern.</li>
</ul>

<h2>Verification</h2>
<p>Add a temporary <code>server_print("event fired, args=%d", read_data(0))</code> to the top of the handler, load the plugin, and trigger the event on the server. The console line proves the registration matches. Once it fires reliably, tighten it with condition strings and remove the print. If you are new to the language used here, start with <a href="/tutorials/pawn-basics-for-amxx">Pawn basics for AMXX</a>; to understand when an event hook is the wrong tool, read <a href="/tutorials/register-event-vs-ham-vs-reapi">register_event vs Hamsandwich vs ReAPI</a>.</p>