SourcePawn plugins
The same API as C++, as a SourcePawn script — .smx files compiled with spcomp, loaded next to native plugins.
Source2Mod runs two kinds of plugin side by side, and both are first-class:
| C++ | SourcePawn | |
|---|---|---|
| File | .smx (a native library) | .smx (a compiled SourcePawn script) |
| Built with | The SDK and your compiler (CMake or AMBuild) | spcomp, shipped in addons/source2mod/scripting |
| API | ISource2Mod* interfaces, g_pSource2Mod* globals | Source2Mod* methodmaps, #include <source2mod/api> |
| Hot reload | Yes | Yes |
Both go into addons/source2mod/plugins/. The core tells them apart by their
content, so the extension is the same.
One API, two languages
The script API mirrors the C++ one name for name, so a guide written for one reads for the other:
- Every interface is a methodmap of static methods named like the C++ one,
without the
Iand the version:ISource2ModCommands::RegisterConCommandisSource2ModCommands.RegisterConCommand. - Every schema class is a methodmap of the same name and base class:
CCSPlayerController,CCSPlayerPawn, … Fields are properties with the C++ names (pawn.m_iHealth = 50), and the helper methods carry over (Teleport,GiveNamedItem,Respawn, …). - Services are reached through the entity that owns them:
pawn.m_pItemServices.GiveNamedItem("weapon_ak47"). - Every
Hook*returns an id that the matchingUnhook*takes. - An entity is held as an entity handle: once the entity is gone,
IsValid()is false and natives refuse it instead of touching another entity.
samples/cs2/cpp_sample and samples/cs2/sp_sample in the
Source2Mod repository are the same
plugin in both languages, section for section.
A first script
#include <source2mod/api>
public Plugin myinfo = {
name = "Hello",
author = "You",
description = "Says hello",
version = "1.0.0",
url = ""
};
public void OnPluginStart()
{
Source2ModCommands.RegisterConCommand("hello", Cmd_Hello);
}
public void Cmd_Hello(Source2ModCommandContext context, Source2ModCommandArgs args, bool post)
{
char name[64] = "Console";
CPlayerSlot slot = context.GetPlayerSlot();
if (slot.IsValid())
{
CCSPlayerController player = CCSPlayerController.FromSlot(slot);
if (player != NULL_ENTITY)
player.GetPlayerName(name, sizeof(name));
}
Source2ModCommands.ReplyToCommand(context, "Hello, %s!", name);
}
public void Source2Mod_OnClientPutInServer(CPlayerSlot slot, const char[] name, int type, const char[] xuid)
{
Source2ModApi.Log("%s joined", name);
}The engine callbacks a C++ plugin gets through ISource2ModListener arrive as
forwards named Source2Mod_On… (listener.inc): connects and disconnects,
level init and shutdown, GameFrame, settings changes.
Compiling
spcomp and the include set ship with every Source2Mod build in
addons/source2mod/scripting; the include set itself lives in the SDK under
public/scripting.
cd addons/source2mod/scripting
./spcomp -i include hello.sp -o ../plugins/hello.smxFor a project of its own, samples/cs2/sp_sample builds a script with CMake
or AMBuild — point SOURCE2MOD_SCRIPTING at a Source2Mod build's
addons/source2mod/scripting.
Talking to C++ plugins
A C++ plugin can give scripts natives of its own through
ISource2ModNatives, and scripts can
create natives and forwards for each other. cpp_sample exposes a few and
sp_sample uses them — the library is optional, so the script keeps working
while cpp_sample is not loaded.
Your first C++ plugin
A minimal Source2Mod plugin, from the plugin class to a built .smx on the server — with a command, a game event and logging.
Coming from CounterStrikeSharp
What a CounterStrikeSharp plugin turns into on Source2Mod — the same concepts side by side, a full plugin ported, and what you get for switching.