Skip to content

Mac-Andreas/omp-cmd

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

1 Commit
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

omp-cmd

A fast, batteries-included command processor for SA-MP / open.mp.

The simplicity of zcmd, the permissions/help/aliases of y_commands, and the speed of Pawn.CMD — in one pure-Pawn include with zero dependencies.

license: MIT pure pawn dependencies open.mp


Why omp-cmd?

Every existing command processor makes you trade something:

  • zcmd — dead simple, but no permissions, no help, no aliases, no runtime control. You build all of that by hand.
  • y_commands (ycmd) — has all those features, but needs the entire YSI framework (megabytes of includes that are brittle on modern/open.mp compilers).
  • Pawn.CMD — very fast, but it's a binary plugin (.dll/.so + ABI), not pure Pawn.
  • I-ZCMD — a faster zcmd, but still no built-in permissions/help/aliases.

omp-cmd gives you all the features in a single .inc file, no plugin, no YSI — and it's as fast as the fastest pure-Pawn processors.


Quick start

#include <open.mp>

// tell omp-cmd how to read a player's permission level (optional - default is 0)
#define OMPCMD_LEVEL_PROVIDER  GetPlayerAdminLevel
#include <omp-cmd>

// simple command (zcmd-style) - always allowed
CMD:pos(playerid, params[])
{
    new Float:x, Float:y, Float:z; GetPlayerPos(playerid, x, y, z);
    new m[48]; format(m, sizeof m, "%.1f %.1f %.1f", x, y, z);
    return OReply(playerid, m);
}

// gated command with built-in permission level + self-describing help
OCMD:ban(playerid, params[], help)
{
    if (help) return OHelp(playerid, 3, "/ban <id> <reason> - ban a player");
    Kick(strval(params));
    return OReply(playerid, "Banned.");
}

// alias: /b runs /ban
OALIAS(b, ban)

That's it. OCMD:ban requires level 3 (omp-cmd checks OMPCMD_LEVEL_PROVIDER for you and denies with a message if they're too low). OHelp both returns the level and prints the usage — one line gates and documents the command.

Migrating from y_commands? XCMD:, XHelp, XReply and XALIAS exist as drop-in aliases, so old handlers keep compiling.


Features

omp-cmd zcmd I-ZCMD y_commands Pawn.CMD
Simple CMD: syntax
Permission levels (built-in)
Self-describing help
Aliases
Runtime enable/disable
Pure Pawn (no plugin)
No framework/dependency ❌ (YSI) ❌ (plugin)
Single file

Benchmark

Dispatch cost per command. There are two measurement contexts — modern open.mp on Apple-silicon (mine, reproducible with test/benchmark.pwn) and old SA-MP 32-bit hardware (community-published). Absolute times across the two contexts are not comparable (~20× hardware difference), so each group is shown with its own zcmd-relative figure.

Processor per-command vs zcmd features dependency source
omp-cmd (case-sensitive) 0.60 µs 1.02× ✅ full none measured¹
zcmd 0.61 µs 1.00× none measured¹
I-ZCMD 0.66 µs 0.92× none measured¹
omp-cmd (case-insensitive, default) 0.90 µs 0.68× ✅ full none measured¹
— published numbers below (old SA-MP HW; ratios only) —
zcmd ~14.3 µs 1.0× none community²
Pawn.CMD (with callbacks) ~9.6 µs ~1.5× ⚠️ plugin community²
I-ZCMD ~7.4 µs ~1.9× none community²
Pawn.CMD (no callbacks) ~3.3 µs ~4.3× ⚠️ plugin community²
y_commands (ycmd) ≈ zcmd ~1× ✅ full YSI per author³

¹ open.mp, macOS arm64, 500,000 dispatches of the same command — run test/benchmark.pwn yourself. ² 150,000 calls on old SA-MP 32-bit — I-ZCMD thread, “Faster than I-ZCMD”. ³ “ZCMD and y_commands perform equally well” — Yashas; a live number wasn’t obtainable (YSI doesn’t link cleanly on the modern open.mp toolchain).

Takeaways (honest):

  • On identical modern hardware, omp-cmd in case-sensitive mode is the fastest pure-Pawn processor measured — edging out zcmd and I-ZCMD — while being the only one with built-in permissions, help, aliases and runtime control.
  • Pawn.CMD can be faster, but it’s a binary plugin. omp-cmd is one pure-Pawn file.
  • Dispatch is never a real bottleneck anyway (a busy server runs maybe a few hundred commands/sec total). omp-cmd’s real value is features per cost.

How the speed is achieved: a single funcidx() lookup; the handler name is built with a native string copy (no per-call format()); params are referenced in place (no copy); and each command’s permission level is cached after the first query, so repeated dispatches are a single call.


Usage

Defining commands

// simple, always allowed (gate it yourself if you want)
CMD:name(playerid, params[]) { ... return 1; }

// permission level + self-help (the `help` flag is set when omp-cmd queries it)
OCMD:name(playerid, params[], help)
{
    if (help) return OHelp(playerid, LEVEL, "/name <args> - what it does");
    ... return 1;
}

Permissions

Tell omp-cmd how to read a level (before including it):

#define OMPCMD_LEVEL_PROVIDER  Admin_GetLevel   // returns the player's level (int)
#include <omp-cmd>

OCMD: commands are auto-denied (with a message, or OnPlayerCommandDenied) when OMPCMD_LEVEL_PROVIDER(playerid) < requiredLevel. Plain CMD: commands are level 0.

Aliases

OALIAS(tp, goto)     // /tp runs /goto, same level + help

Runtime control

OmpCmd_Disable("ban");   OmpCmd_Enable("ban");
OmpCmd_Exists("ban");    OmpCmd_LevelOf("ban");

Building a /cmds menu

omp-cmd dispatch needs no registry, but to list commands for a help dialog, register the ones you want shown:

OmpCmd_List_Add("ban", 3, "/ban <id> <reason> - ban a player");
// then iterate OmpCmd_List_Count() / OmpCmd_List_Get(i, name, level, help, len)

See examples/example.pwn for a complete /cmds dialog.

Optional callbacks

public OnPlayerCommandReceived(playerid, cmdtext[]) { return 1; } // 0 = block
public OnPlayerCommandPerformed(playerid, cmdtext[], success) { return success; }
public OnPlayerCommandDenied(playerid, const cmd[], reason) { return 1; }
// reason: OMPCMD_DENY_UNKNOWN / OMPCMD_DENY_LEVEL / OMPCMD_DENY_DISABLED

Options (define before #include <omp-cmd>)

Define Default Effect
OMPCMD_LEVEL_PROVIDER returns 0 your permission-level function
OMPCMD_CASE_SENSITIVE off skip lowercasing → fastest dispatch
OMPCMD_MAX_NAME 28 longest command name

Install

Drop omp-cmd.inc into your pawno/qawno include/ folder and:

#include <omp-cmd>

omp-cmd hooks OnPlayerCommandText (via the ALS chain), so it coexists with other includes. Nothing else to install.


Migrating from zcmd

omp-cmd is CMD:-compatible — your existing zcmd commands work unchanged. Add permissions/help by switching a command to OCMD: when you want them. (Don’t use zcmd and omp-cmd at the same time — both hook OnPlayerCommandText.)

Coming from y_commands? XCMD:, XHelp, XReply and XALIAS are kept as aliases of OCMD:, OHelp, OReply and OALIAS, so existing handlers compile as-is.


License

MIT — use it anywhere. Author: Xyranaut (Mac Andreas).

Inspired by zcmd (Zeex), I-ZCMD (Yashas), y_commands (Y-Less) and Pawn.CMD (katursis) — no code reused. Independent clean-room implementation.

About

Fast, batteries-included command processor for SA-MP / open.mp — best of zcmd + y_commands + Pawn.CMD in one pure-Pawn include, zero dependencies.

Resources

License

Stars

Watchers

Forks

Packages

 
 
 

Contributors

Languages