Calling Game Functions

Functions described once in gamedata and called by name from C++ and SourcePawn — Source2Mod's SDKCall.

A game function the API has no wrapper for is called by name. Its address and its signature live in gamedata, once, and C++ plugins and scripts call the same entry — SourceMod's SDKCall, without the PrepSDKCall_* dance in every plugin.

Describing a function

A gamedata entry becomes callable when it has a "function" object. Where the function is comes from the entry as before: "signatures" (a pattern or an @symbol), or "offsets" for a virtual called on this.

addons/source2mod/gamedata/myplugin.json
{
  "CBaseEntity::CollisionRulesChanged": {
    "offsets": { "windows": 188, "linux": 187 },
    "function": { "this": "entity", "return": "void", "params": [] }
  },
  "GetWeaponCSDataFromKey": {
    "signatures": { "library": "server", "windows": "48 89 5C 24 ? 57 ...", "linux": "48 85 F6 0F 84 ..." },
    "function": { "return": "object", "params": [ "int32", "string" ] }
  },
  "CCSGameRules::TerminateRound": {
    "signatures": { "library": "server", "windows": "...", "linux": "..." },
    "function": {
      "this": "object",
      "return": "void",
      "params": {
        "windows": [ "float", "int32", "pointer" ],
        "linux": [ "int32", "pointer", "float" ]
      }
    }
  }
}
Key
thisentity, object or pointer: a member function, and this is the first argument of a call. Left out for a free function. A virtual needs it.
returnA type; void when left out.
paramsThe types after this, or one list per platform when they differ.

Types: void, bool, int8, int16, int32 (also enums, uint32), int64, float, double, entity (CEntityInstance* and derived), object (a schema object pointer), string (const char*), vector (Vector* / const Vector&), vector_value (Vector by value), pointer (anything else), buffer_string (CBufferString*), int_ref, bool_ref, float_ref (out-parameters).

Every .json file in addons/source2mod/gamedata is read with the core's, so a plugin ships its own entries as a file next to it.

Calling it

#include "source2mod/ISource2ModFunctions.h"

// Find once; the core keeps it until the server shuts down.
ISource2ModFunction* collision = g_pSource2ModFunctions->Find("CBaseEntity::CollisionRulesChanged");
ISource2ModFunction* weaponData = g_pSource2ModFunctions->Find("GetWeaponCSDataFromKey");

// The C++ type of the function, `this` first for a member function.
collision->Call<void(CBaseEntity*)>(pEntity);

auto* ak = weaponData->Call<CCSWeaponBaseVData*(int, const char*)>(-1, "weapon_ak47");

// Past every hook on the function (HookChain::Bypass).
collision->CallOriginal<void(CBaseEntity*)>(pEntity);

Find returns null when the entry is missing, has no "function", or its signature does not match the running game; the reason is on the console.

In C++, Call<Signature> checks the signature against the gamedata on every call (a few compares). When they differ nothing is called: the mismatch is reported once and the result is value-initialised. The call itself goes through a function pointer of the type you wrote, so the compiler handles the calling convention.

Without gamedata

A function you find yourself is described in the call instead:

const Source2ModType params[] = { Source2ModType::Int32, Source2ModType::String };
ISource2ModFunction* fn = g_pSource2ModFunctions->Create(g_PluginID, address, nullptr, -1, false,
                                                         Source2ModType::Pointer, params, 2);
void* result = fn->Call<void*(int, const char*)>(-1, "weapon_awp");
g_pSource2ModFunctions->Destroy(fn);   // or left to the plugin's unload

FromGameConfig, FromSignatureRelative, FromSymbol, FromVirtualTable and FromVirtual find a function the other ways; the table is on Function hooks.

Hooking it

The same ISource2ModFunction is hooked with HookPoint::Function(fn) (Source2ModHooks.HookFunction(fn, ...) in a script) — see Function hooks. A function with a command parameter can be hooked but not called.

Coming from SourceMod

SourceModSource2Mod
StartPrepSDKCall … PrepSDKCall_SetFromConf … EndPrepSDKCalla "function" entry in gamedata + Find
SDKCall(call, entity, ...)Call(entity, ...) (C++: Call<Sig>(entity, ...))
SDKType_CBaseEntity / SDKType_PlainOldData / SDKType_Vector / SDKType_Stringentity / int32, bool, float / vector / string
SDKConf_Virtual / SDKConf_Signature"offsets" / "signatures" in the same entry

On this page