Unknown ❤ RESPAWN.ZAFEST.RO | VIP FREE | FREE FOR ALL ❤ 0/32
Romania XPlayZM.CSBlackDevil.COM # Zombie Plague UltimateX [ Respawn + FDL + VIP 22-12PM + POINTS] ® 18/32
Prodvigat server
112 lines 5.8 KiB Markdown Source Rendered Raw Permalink History
title, excerpt, category, tags, image, read_time
title excerpt category tags image read_time
Build a VIP System Plugin (Flags, Menu, MySQL) 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. plugin-dev vip, tutorial 10 min read

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.

1. Pick the VIP flag

Do not invent an auth system — reuse the AMXX flag model. VIP conventionally maps to a custom level flag, most commonly t (ADMIN_LEVEL_H). A user with t in users.ini is a VIP. Read the flag once at load:

#include <amxmodx>
#include <amxmisc>
#include <hamsandwich>
#include <fun>

new const VIP_FLAG = ADMIN_LEVEL_H

bool:is_vip(id) { return (get_user_flags(id) & VIP_FLAG) ? true : false }

2. Give the benefits on spawn

Hook Ham_Spawn and apply bonuses after the engine has finished spawning the player (post = 1):

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") }

client_print_color makes this 1.9+; see colored chat messages for the color codes.

3. Add a /vip menu

Register a say handler and build a menu with the AMXX menu API:

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 }

For a fuller, ready-made version see CSB VIP Menu.

4. Persist expiring VIP in MySQL

Flag-based VIP is permanent and manual. For paid, time-limited VIP you want a database. Use the sqlx module (install it via the MySQL module guide) and always use threaded queries — a blocking query freezes the whole server on a slow database.

#include <sqlx>

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) && SQL_ReadResult(query, 0) > get_systime()) set_user_flags(id, VIP_FLAG) // grant runtime VIP }

Storing the SteamID as the key means VIP follows the player, not a slot. The expires column is a Unix timestamp compared against get_systime(), so lapsed VIP simply never gets the flag set.

Common errors

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

Verification

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