SourcePawn
The script API — how the C++ interfaces, schema classes and hooks look from a .sp, and where scripts work differently.
A SourcePawn plugin is a .sp compiled with spcomp into an .smx and
dropped into addons/source2mod/plugins/, next to the C++ plugins. It talks
to the same core through the same API: there is no second, smaller "script
API", only a second language. How to set up, compile and load a first script
is on SourcePawn plugins; this section is
about writing them.
One include
#include <source2mod/api>source2mod/api pulls in everything. The pieces can be included alone
(source2mod/commands, source2mod/schema, …), each named after the area it
covers:
| Include | C++ |
|---|---|
core, functions | The language core, natives, forwards, libraries — ISource2ModNatives |
engine | ISource2ModAPI (Source2ModApi.Log, Format), the engine server, CGlobalVars |
schema, entities | The schema classes, ISource2ModEntities |
convars, commands | ISource2ModConVars, ISource2ModCommands |
events, listener | ISource2ModEvents, the ISource2ModListener callbacks as forwards |
gamehooks, hooks, gameconfig, addresses | ISource2ModGameHooks, raw KHooks, ISource2ModGameConfig, ISource2ModAddresses |
scheduler | ISource2ModScheduler |
adt, datapack | SourceMod's ArrayList, StringMap, ArrayStack, IntMap, DataPack |
netmessages, sounds, transmit, trace, menus, customhud, scripts, translations, targeting, files, paths, json, http, sql, cookies, geoip, maps, features, permissions, clients, utils | The interfaces of the same names (ISource2ModNetworkMessages, ISource2ModSounds, …) |
The include set ships with every Source2Mod build in
addons/source2mod/scripting/include, and lives in the SDK under
public/scripting.
How the C++ API maps
The script API follows the C++ one name for name, so the rest of the documentation reads for both languages:
| C++ | SourcePawn |
|---|---|
An interface: g_pSource2ModCommands->RegisterConCommand(...) | A methodmap of static methods, named without the I and the version: Source2ModCommands.RegisterConCommand(...) |
A schema class: CCSPlayerPawn* | A methodmap of the same name and base class: CCSPlayerPawn |
A field: pPawn->m_iHealth = 150; | A property: pawn.m_iHealth = 150; — a write to a networked field reaches the clients the same way |
| A vector, color or string field | A method of the field's name that fills a buffer, and Set_<name>() to write it |
A helper: pPawn->Teleport(...), pController->GetPlayerPawn() | The same method: pawn.Teleport(...), player.GetPlayerPawn() |
CEntityInstance*, CHandle<T> | An entity handle; NULL_ENTITY for none |
| Services, the scene node, the game rules | A SchemaObject (pawn.m_pItemServices); IsNull for none |
A hook or listener returns a Source2ModHookId | Hook* / Register* / Add* return an int id that the matching Unhook* / Unregister* / Remove* takes |
KHook::Action::Ignore / Override / Supersede | Action_Ignore / Action_Override / Action_Supersede (Source2ModAction) |
ISource2ModListener::OnClientPutInServer, … | Forwards named Source2Mod_On…: declare the function and it is called |
| A lambda | A function of the script; an any data argument carries what the lambda captured |
An object the API hands out (ISource2ModJSONDocument*, a menu, a SQL connection) | A Handle of the same name; delete closes it |
uint64 (a SteamID, a 64-bit field) | Decimal text, or two cells where the include says so |
uint64 player masks | int mask[2] and the PlayerMask_* stocks |
Everything a script registers — commands, hooks, listeners, timers, natives, handles — belongs to it and goes when it unloads.
The same command in both languages:
bool MyPlugin::Load(PluginId id, ISource2ModAPI* api, char* error, size_t maxlen, bool late)
{
SOURCE2MOD_SAVEVARS();
g_pSource2ModCommands->RegisterConCommand("heal", [](const Source2ModCommandContext& context, const Source2ModCommandArgs& args, bool post)
{
CPlayerSlot slot = context.GetPlayerSlot();
CCSPlayerController* pPlayer = slot.IsValid() ? CCSPlayerController::FromSlot(slot) : nullptr;
CCSPlayerPawn* pPawn = pPlayer ? pPlayer->GetPlayerPawn() : nullptr;
if (!pPawn || !pPlayer->m_bPawnIsAlive())
return;
pPawn->m_iHealth = 100;
g_pSource2ModCommands->ReplyToCommand(context, "Healed.");
}, "myplugin.heal");
return true;
}public void OnPluginStart()
{
Source2ModCommands.RegisterConCommand("heal", Cmd_Heal, "myplugin.heal");
}
public void Cmd_Heal(Source2ModCommandContext context, Source2ModCommandArgs args, bool post)
{
CPlayerSlot slot = context.GetPlayerSlot();
CCSPlayerController player = slot.IsValid() ? CCSPlayerController.FromSlot(slot) : NULL_ENTITY;
CCSPlayerPawn pawn = player != NULL_ENTITY ? player.GetPlayerPawn() : NULL_ENTITY;
if (pawn == NULL_ENTITY || !player.m_bPawnIsAlive)
return;
pawn.m_iHealth = 100;
Source2ModCommands.ReplyToCommand(context, "Healed.");
}Two kinds of action
SourcePawn scripts meet two enums that both answer "what now":
| Enum | Values | Answered by |
|---|---|---|
Source2ModAction | Action_Ignore, Action_Override, Action_Supersede | Hooks: game events, game hooks, raw hooks, command listeners, entity outputs — the C++ KHook::Action |
Action | Plugin_Continue, Plugin_Changed, Plugin_Handled, Plugin_Stop | Functions of a GlobalForward / PrivateForward between plugins, as in SourceMod |