Romania XPlayZM.CSBlackDevil.COM # Zombie Plague UltimateX [ Respawn + FDL + VIP 22-12PM + POINTS] ® 20/32
Unknown ❤ RESPAWN.ZAFEST.RO | VIP FREE | FREE FOR ALL ❤ 0/32
France EMIRATES.KiNGS 亗 GAMEAREA ||͇̿P͇̿U͇̿B͇̿L͇̿I͇̿C͇̿| ✪ 32/32
Server bewerben
112 lines 5.8 KiB Markdown Source Rendered Raw Permalink History
---
title: "Build a VIP System Plugin (Flags, Menu, MySQL)"
excerpt: "Design a real VIP plugin for AMX Mod X: gate benefits behind an admin flag, give spawn bonuses, add a /vip menu, and store expiring VIP in MySQL with threaded SQLx queries."
category: "plugin-dev"
tags: "vip, tutorial"
image: ""
read_time: "10 min read"
---
<p>A VIP plugin is a good exercise because it touches every subsystem an admin plugin uses: access flags, spawn hooks, a menu, and persistent storage. This walks through a clean design — flag-gated benefits first, then an optional MySQL layer for VIP that expires. The goal is a plugin you can actually run, not a toy. It targets AMX Mod X 1.9+ and Ham Sandwich for the spawn hook.</p>

<h2>1. Pick the VIP flag</h2>
<p>Do not invent an auth system — reuse the AMXX flag model. VIP conventionally maps to a custom level flag, most commonly <code>t</code> (<code>ADMIN_LEVEL_H</code>). A user with <code>t</code> in <a href="/tutorials/users-ini-explained">users.ini</a> is a VIP. Read the flag once at load:</p>
<pre>#include &lt;amxmodx&gt;
#include &lt;amxmisc&gt;
#include &lt;hamsandwich&gt;
#include &lt;fun&gt;

new const VIP_FLAG = ADMIN_LEVEL_H

bool:is_vip(id) {
    return (get_user_flags(id) &amp; VIP_FLAG) ? true : false
}</pre>

<h2>2. Give the benefits on spawn</h2>
<p>Hook <code>Ham_Spawn</code> and apply bonuses after the engine has finished spawning the player (post = 1):</p>
<pre>public plugin_init() {
    register_plugin("CSB VIP", "1.0", "CSB")
    RegisterHam(Ham_Spawn, "player", "OnSpawnPost", 1)
}

public OnSpawnPost(id) {
    if (!is_user_alive(id) || !is_vip(id))
        return
    set_user_health(id, 125)
    set_user_armor(id, 100)
    client_print_color(id, print_team_default, "^x04[VIP]^x01 Bonus health and armor applied")
}</pre>
<p><code>client_print_color</code> makes this 1.9+; see <a href="/tutorials/colored-chat-messages">colored chat messages</a> for the color codes.</p>

<h2>3. Add a /vip menu</h2>
<p>Register a say handler and build a menu with the AMXX menu API:</p>
<pre>public plugin_init() {
    // ...
    register_clcmd("say /vip", "CmdVipMenu")
}

public CmdVipMenu(id) {
    new menu = menu_create("\\rVIP Menu", "VipMenuHandler")
    menu_additem(menu, "\\wExtra Health", "1")
    menu_additem(menu, "\\wDrop Grenade", "2")
    menu_display(id, menu)
    return PLUGIN_HANDLED
}

public VipMenuHandler(id, menu, item) {
    if (item == MENU_EXIT) {
        menu_destroy(menu)
        return PLUGIN_HANDLED
    }
    if (!is_vip(id)) {
        client_print(id, print_chat, "[VIP] You are not a VIP.")
    } else if (item == 0) {
        set_user_health(id, get_user_health(id) + 25)
    }
    menu_destroy(menu)
    return PLUGIN_HANDLED
}</pre>
<p>For a fuller, ready-made version see <a href="/plugins/csb-vip-menu">CSB VIP Menu</a>.</p>

<h2>4. Persist expiring VIP in MySQL</h2>
<p>Flag-based VIP is permanent and manual. For paid, time-limited VIP you want a database. Use the <code>sqlx</code> module (install it via <a href="/tutorials/install-mysql-module-amxx">the MySQL module guide</a>) and <strong>always</strong> use threaded queries — a blocking query freezes the whole server on a slow database.</p>
<pre>#include &lt;sqlx&gt;

new Handle:g_tuple

public plugin_cfg() {
    g_tuple = SQL_MakeDbTuple("127.0.0.1", "csb", "secret", "csb_vip")
    SQL_ThreadQuery(g_tuple, "IgnoreHandler",
        "CREATE TABLE IF NOT EXISTS vips (steamid VARCHAR(32) PRIMARY KEY, expires INT)")
}

public client_authorized(id) {
    new sid[32], q[128]
    get_user_authid(id, sid, charsmax(sid))
    formatex(q, charsmax(q), "SELECT expires FROM vips WHERE steamid='%s'", sid)
    new data[1]
    data[0] = id
    SQL_ThreadQuery(g_tuple, "OnVipLookup", q, data, 1)
}

public OnVipLookup(failstate, Handle:query, error[], errnum, data[], size, Float:qtime) {
    if (failstate != TQUERY_SUCCESS) {
        log_amx("VIP query failed: %s", error)
        return
    }
    new id = data[0]
    if (SQL_NumResults(query) &amp;&amp; SQL_ReadResult(query, 0) &gt; get_systime())
        set_user_flags(id, VIP_FLAG)   // grant runtime VIP
}</pre>
<p>Storing the SteamID as the key means VIP follows the player, not a slot. The <code>expires</code> column is a Unix timestamp compared against <code>get_systime()</code>, so lapsed VIP simply never gets the flag set.</p>

<h2>Common errors</h2>
<ul>
<li><strong>Server hitches every time a VIP connects</strong> — you used <code>SQL_Execute</code> (blocking) instead of <code>SQL_ThreadQuery</code>. Always thread player-facing queries.</li>
<li><strong>Benefits never apply</strong> — you hooked <code>Ham_Spawn</code> with post = 0. Health set pre-spawn is overwritten by the engine. Use post = 1.</li>
<li><strong>Everyone is a VIP</strong> — your flag check used <code>|</code> instead of <code>&amp;</code>, or you compared to <code>ADMIN_ALL</code> (which is 0). Use <code>get_user_flags(id) &amp; VIP_FLAG</code>.</li>
<li><strong>Module failed to load: sqlx</strong> — the MySQL module is not enabled in <code>modules.ini</code>. See <a href="/tutorials/install-amxx-modules">installing AMXX modules</a>.</li>
<li><strong>VIP granted at runtime is lost on map change</strong> — expected: <code>set_user_flags</code> is per-session. Re-run the lookup on <code>client_authorized</code> every map, as above.</li>
</ul>

<h2>Verification</h2>
<p>Give yourself flag <code>t</code> in users.ini, run <code>amx_reloadadmins</code>, and respawn — you should get the health/armor bonus and the <code>[VIP]</code> line. Type <code>say /vip</code> to open the menu. For the MySQL layer, insert a row with a future <code>expires</code>, reconnect, and confirm the flag is set with <code>amx_who</code>. A row with a past timestamp must leave you as a normal player. Once all three paths behave, compile cleanly per <a href="/tutorials/compile-sma-to-amxx">the compile guide</a> and ship it.</p>