Coming from SourceMod

For SourceMod plugin authors — what carries over unchanged, what Source2Mod does differently, and the Source2Mod calls for common SourceMod ones.

Source2Mod scripts are SourcePawn, compiled by SourceMod's own spcomp. The language, the tooling and much of the core library are what you know. What is new is everything that touches the game: CS2 is a Source 2 game, and the API for it is Source2Mod's — the same one the C++ plugins use, not SourceMod's sdktools.

What stays the same

  • The language. #pragma newdecls required, methodmaps, enum structs, typesets, view_as, any, Function — all of current SourcePawn.
  • spcomp. It ships with Source2Mod in addons/source2mod/scripting; see Compiling.
  • The plugin lifecycle. public Plugin myinfo, AskPluginLoad2, OnPluginStart, OnPluginEnd, OnLibraryAdded / OnLibraryRemoved, OnAutoConfigsBuffered, OnConfigsExecuted. Everything a plugin registered is released when it unloads.
  • Handles. Handle, null, delete, Clone().
  • ADT. ArrayList, ArrayStack, StringMap, IntMap, their snapshots, and DataPack, with SourceMod's methods.
  • ConVars. The ConVar methodmap — IntValue, FloatValue, BoolValue, GetString, AddChangeHook with the same ConVarChanged signature.
  • Plugins talking to each other. GlobalForward and PrivateForward, Action with Plugin_Continue / Plugin_Changed / Plugin_Handled / Plugin_Stop, native declarations, SharedPlugin includes with __pl_<name>_SetNTVOptional and MarkNativeAsOptional. See Natives and forwards.
  • Timers. TIMER_FLAG_REPEAT and TIMER_FLAG_NO_MAPCHANGE keep their meaning.

What is different

One include, no extensions

plugin.sp
#include <source2mod/api>

There is no sourcemod.inc, and no sdktools, sdkhooks, dhooks or cstrike. What those do comes from the Source2Mod API:

SourceModSource2Mod
sdktools — entities, props, players, sounds, tracesThe schema classes and their helpers, Source2ModEntities, Source2ModSounds, Source2ModTrace
sdkhooksGame hooks (Source2ModGameHooks.HookTakeDamage, HookCanAcquire, HookPostThink, …) and entity listeners
dhooksRaw hooks (Source2ModHook)
SDKCallSource2ModFunction
cstrikeController and pawn helpers: SwitchTeam, ChangeTeam, Respawn, …

Names follow the C++ API

SourceMod's free natives are static methods of a methodmap named after the C++ interface: Source2ModCommands.RegisterConCommand rather than RegConsoleCmd, Source2ModNatives.CreateNative rather than CreateNative, Source2ModCall.PushCell rather than Call_PushCell. The methods keep the C++ names and argument order, so the C++ reference reads for scripts too.

Players are slots and controllers

There is no client index in the API. A player is a CPlayerSlot (0–63) or their CCSPlayerController; the body in the world is a separate entity, the pawn (player.GetPlayerPawn()). A SourceMod client index is slot + 1. The engine callbacks arrive as forwards named Source2Mod_On…:

SourceModSource2Mod forward
OnClientConnectSource2Mod_OnClientConnect(slot, name, xuid, networkId, unk1, rejectReason, maxlen)
OnClientConnectedSource2Mod_OnClientConnected
OnClientPutInServerSource2Mod_OnClientPutInServer
OnClientAuthorizedSource2Mod_OnClientAuthorized(slot, steamId)
OnClientDisconnect, OnClientDisconnect_PostSource2Mod_OnClientDisconnect, Source2Mod_OnClientDisconnectPost
OnClientSettingsChangedSource2Mod_OnClientSettingsChanged
OnMapStart, OnMapEndSource2Mod_OnLevelInit, Source2Mod_OnLevelShutdown
OnGameFrameSource2Mod_OnGameFrame(simulating, firstTick, lastTick)
OnAllPluginsLoadedSource2Mod_OnAllSource2ModPluginsLoaded
OnClientCookiesCachedSource2Mod_OnClientCookiesCached

The full list is in source2mod/listener.inc; see also Listeners.

Entities are handles

An entity is an entity handle — index and serial — never an index. Lookups return NULL_ENTITY (-1) for none, live entities are negative numbers, and a handle kept after its entity is removed is refused by every native. GetEntProp / SetEntProp become properties: pawn.m_iHealth = 50, entity.m_iTeamNum.

Commands, hooks and their answers

  • A command handler is void (Source2ModCommandContext context, Source2ModCommandArgs args, bool post). There is no client parameter (context.GetPlayerSlot()) and nothing to return.
  • Admin flags are permissions: a command takes a permission string ("myplugin.kick") and the core checks it.
  • Hooks — game events, game hooks, raw hooks, command listeners — answer a Source2ModAction: Action_Ignore, Action_Override or Action_Supersede. Action (Plugin_*) is only for forwards between plugins.
  • A game event handler gets (IGameEvent event, bool post, bool &dontBroadcast) and is hooked pre or post; event.GetName gives the name.
  • Hook* returns an int id; Unhook* takes it.

Timers

Source2ModScheduler.AddTimer(interval, callback, data, flags) returns a Source2ModTimer, which is not a Handle. The callback is void (any data): a repeating timer runs until Source2ModScheduler.KillTimer(timer) — from inside its own callback too — not until it returns Plugin_Stop. Source2ModScheduler.NextFrame is RequestFrame.

64-bit values

SteamID64s and other 64-bit integers are decimal text (player.GetSteamID, IGameEvent.GetUint64) or two cells where an include says so.

Common calls

SourceModSource2Mod
RegConsoleCmd, RegAdminCmdSource2ModCommands.RegisterConCommand(name, handler, permission)
AddCommandListenerSource2ModCommands.RegisterConListener(name, handler, post)
ReplyToCommand(client, ...)Source2ModCommands.ReplyToCommand(context, ...)
CheckCommandAccessSource2ModPermissions.CheckAccess, PlayerHasPermission
CreateConVarCConVar.CreateInt / CreateFloat / CreateBool / CreateString / Create
FindConVarCConVarRef.Find
AutoExecConfigSource2ModConVars.AutoExecConfig
HookEventSource2ModEvents.HookGameEvent(name, handler, post)
CreateEvent, Event.FireIGameEventManager2.CreateEvent, IGameEventManager2.FireEvent
CreateTimer, KillTimerSource2ModScheduler.AddTimer, Source2ModScheduler.KillTimer
RequestFrameSource2ModScheduler.NextFrame
LogMessage, LogError, PrintToServerSource2ModApi.Log, Source2ModApi.LogError, Source2ModApi.ConPrintf
Format, VFormatSource2ModApi.Format, Source2ModApi.FormatArgs
ServerCommandSource2ModEngine.ServerCommand
GetEngineTime, GetGameTime, MaxClientsSource2ModEngine.GetEngineTime, Source2ModApi.GetGlobalVars().curtime, .maxClients
GetClientNameplayer.GetPlayerName
GetClientUserId, GetClientOfUserIdplayer.GetUserID, CCSPlayerController.FromUserId
GetClientAuthIdSource2ModClients.GetAuthId, player.GetSteamID
IsClientInGame, IsFakeClientSource2ModClients.IsInGame, Source2ModClients.IsFakeClient / player.IsBot
GetClientCountSource2ModClients.GetClientCount
KickClientSource2ModClients.Kick
PrintToChat, PrintCenterTextplayer.PrintToChat, player.PrintToCenter
ProcessTargetString, FindTargetSource2ModTargeting.ProcessTargetString, Source2ModTargeting.FindTarget
CS_SwitchTeam, ChangeClientTeam, CS_RespawnPlayerplayer.SwitchTeam, player.ChangeTeam, player.Respawn
ForcePlayerSuicidepawn.CommitSuicide(explode, force)
SetEntityHealth, GetEntProp(…, "m_iHealth")pawn.m_iHealth
GetEntProp / SetEntProp (any field)The property, or Source2ModSchema.GetInt / SetInt(entity, class, field, …)
GetEntPropVectorThe field's method, or Source2ModSchema.GetVector
GetEntityClassnameentity.GetClassname
GivePlayerItempawn.m_pItemServices.GiveNamedItem
CreateEntityByName, DispatchSpawnSource2ModEntities.CreateEntityByName, entity.DispatchSpawn
AcceptEntityInput, RemoveEntityentity.AcceptInput, entity.Remove
FindEntityByClassnameSource2ModEntities.FindEntityByClassname(start, classname)
TeleportEntityentity.Teleport(position, angles, velocity)
GetEntPropVector(…, "m_vecOrigin")entity.GetAbsOrigin
HookEntityOutputSource2ModEntities.AddEntityIOListener
SDKHook(…, SDKHook_OnTakeDamage, …)Source2ModGameHooks.HookTakeDamage
OnEntityCreatedSource2ModEntities.AddEntityListener
DHookCreateDetour / DynamicDetourSource2ModHook.FromGameConfig, FromSignature, FromSymbol
StartPrepSDKCall … EndPrepSDKCallSource2ModFunction.FromGameConfig + AddParam
LoadTranslationsSource2ModTranslations.LoadPhraseFile
GetCurrentMap, ForceChangeLevelSource2ModMaps.GetCurrentMap, Source2ModMaps.ChangeLevel
StrEqual, strcopy, StringToIntSource2ModUtils.Equal, Source2ModUtils.Copy, Source2ModUtils.StringToInt
SetFailStateSource2ModPlugins.SetFailState

A plugin, ported

A small SourceMod-style plugin — a ConVar, an admin command, a game event — written for Source2Mod. The SourceMod calls it replaces are in the comments:

plugin.sp
#include <source2mod/api>

#pragma semicolon 1
#pragma newdecls required

public Plugin myinfo =
{
	name = "Spawn health",
	author = "You",
	description = "Sets health on spawn, !hp to change it",
	version = "1.0.0",
	url = ""
};

ConVar g_cvHealth;

public void OnPluginStart()
{
	// CreateConVar("spawnhp_health", "100", ...)
	g_cvHealth = CConVar.CreateInt("spawnhp_health", FCVAR_NONE, "Health players spawn with", 100, true, 1, true, 1000);
	Source2ModConVars.AutoExecConfig();

	// RegAdminCmd("sm_hp", Cmd_Hp, ADMFLAG_SLAY) -- "hp" in the console, !hp in chat
	Source2ModCommands.RegisterConCommand("hp", Cmd_Hp, "spawnhp.hp");

	// HookEvent("player_spawn", Event_PlayerSpawn)
	Source2ModEvents.HookGameEvent("player_spawn", Event_PlayerSpawn, true);
}

public Source2ModAction Event_PlayerSpawn(IGameEvent event, bool post, bool &dontBroadcast)
{
	// GetClientOfUserId(event.GetInt("userid"))
	CCSPlayerController player = event.GetPlayerController("userid");
	if (player == NULL_ENTITY)
		return Action_Ignore;

	// The spawn code may still write the health: one frame later, through the handle.
	Source2ModScheduler.NextFrame(ApplyHealth, player);
	return Action_Ignore;
}

public void ApplyHealth(any data)
{
	CCSPlayerController player = data;
	if (!player.IsValid())
		return;

	CCSPlayerPawn pawn = player.GetPlayerPawn();
	if (pawn != NULL_ENTITY)
		pawn.m_iHealth = g_cvHealth.IntValue;      // SetEntityHealth(client, ...)
}

public void Cmd_Hp(Source2ModCommandContext context, Source2ModCommandArgs args, bool post)
{
	if (args.ArgC() < 2)
	{
		Source2ModCommands.ReplyToCommand(context, "Usage: hp <health>");
		return;
	}

	char value[16];
	args.Arg(1, value, sizeof(value));
	g_cvHealth.IntValue = Source2ModUtils.StringToInt(value);
	Source2ModCommands.ReplyToCommand(context, "Players now spawn with %d HP.", g_cvHealth.IntValue);
}

On this page