### VM Environment API Source: https://context7.com/alliedmodders/sourcepawn/llms.txt Demonstrates how to create and configure the SourcePawn virtual machine environment, including JIT compilation settings and debug listener setup. ```APIDOC ## VM Environment API ### Description This section covers the initialization and configuration of the SourcePawn virtual machine environment using the `ISourcePawnEnvironment` interface. ### Method C++ API ### Endpoint N/A ### Parameters N/A ### Request Example ```cpp #include // Create a new SourcePawn environment ISourcePawnEnvironment* env = ISourcePawnEnvironment::New(); ISourcePawnEngine2* api = env->APIv2(); // Configure JIT settings api->SetJitEnabled(true); api->InstallWatchdogTimer(5000); // 5 second timeout // Set debug listener for error reporting class MyDebugListener : public IDebugListener { public: void OnDebugSpew(const char* msg, ...) override { va_list ap; va_start(ap, msg); vprintf(msg, ap); va_end(ap); } void ReportError(const IErrorReport& report, IFrameIterator& iter) override { printf("Error: %s\n", report.Message()); for (; !iter.Done(); iter.Next()) { if (iter.IsScriptedFrame()) { printf(" at %s:%d in %s\n", iter.FilePath(), iter.LineNumber(), iter.FunctionName()); } } } }; MyDebugListener listener; api->SetDebugListener(&listener); // Clean shutdown env->Shutdown(); delete env; ``` ### Response N/A ### Response Example N/A ``` -------------------------------- ### SourcePawn Type Declarations and Usage Source: https://context7.com/alliedmodders/sourcepawn/llms.txt Demonstrates various ways to declare variables, arrays, enums, and typedefs in SourcePawn. Includes examples of type casting and using the sizeof operator. Requires including 'sourcemod'. ```c #include // New-style declarations (recommended) int myInt = 42; float myFloat = 3.14; bool myBool = true; char myChar = 'A'; // Arrays int fixedArray[10]; char stringBuffer[256]; float vectors[3] = {1.0, 2.0, 3.0}; // Multi-dimensional arrays int matrix[3][3] = { {1, 2, 3}, {4, 5, 6}, {7, 8, 9} }; // Enumerations enum Color { Color_Red = 0, Color_Green, Color_Blue }; // Enum with explicit values enum Permission { Perm_None = 0, Perm_Read = (1 << 0), Perm_Write = (1 << 1), Perm_Execute = (1 << 2), Perm_All = Perm_Read | Perm_Write | Perm_Execute }; // Typedef for function signatures typedef MenuHandler = function int (Menu menu, MenuAction action, int param1, int param2); typedef SortFunc = function int (int elem1, int elem2, any[] array, Handle hndl); // Typeset for multiple signatures typeset EventCallback { function void (int client); function void (int client, int value); function Action (int client, int value); } // Using 'any' type for generic storage any genericValue; public void OnPluginStart() { // Type casting with view_as int raw = 0x40490FDB; // IEEE 754 representation of pi float pi = view_as(raw); PrintToServer("Pi = %f", pi); // Sizeof operator PrintToServer("Size of int: %d cells", sizeof(myInt)); PrintToServer("Size of fixedArray: %d cells", sizeof(fixedArray)); PrintToServer("Size of matrix: %d cells", sizeof(matrix)); } ``` -------------------------------- ### Create Dynamic Native Functions in SourcePawn Source: https://context7.com/alliedmodders/sourcepawn/llms.txt Implement native functions that can be called by other plugins. Ensure the 'mylib.inc' file is available for clients using this library. The example shows registration of 'MyLib_Add', 'MyLib_PrintMessage', and 'MyLib_GetData' natives. ```c #include // Shared include file (mylib.inc): // native int MyLib_Add(int a, int b); // native void MyLib_PrintMessage(const char[] msg); // native bool MyLib_GetData(int[] buffer, int maxlen); int g_LibraryData[100]; public APLRes AskPluginLoad2(Handle myself, bool late, char[] error, int err_max) { // Register natives CreateNative("MyLib_Add", Native_Add); CreateNative("MyLib_PrintMessage", Native_PrintMessage); CreateNative("MyLib_GetData", Native_GetData); // Register library name RegPluginLibrary("mylib"); return APLRes_Success; } public int Native_Add(Handle plugin, int numParams) { int a = GetNativeCell(1); int b = GetNativeCell(2); return a + b; } public int Native_PrintMessage(Handle plugin, int numParams) { char message[256]; GetNativeString(1, message, sizeof(message)); PrintToServer("[MyLib] %s", message); return 0; } public int Native_GetData(Handle plugin, int numParams) { int maxlen = GetNativeCell(2); // Fill buffer with library data int count = (maxlen < sizeof(g_LibraryData)) ? maxlen : sizeof(g_LibraryData); SetNativeArray(1, g_LibraryData, count); return count; } // In another plugin using the library: // #include // // public void OnPluginStart() // { // if (!LibraryExists("mylib")) { // SetFailState("MyLib not loaded"); // } // int result = MyLib_Add(10, 20); // MyLib_PrintMessage("Hello from client plugin!"); // } ``` -------------------------------- ### Manage Console Variables (ConVars) in SourcePawn Source: https://context7.com/alliedmodders/sourcepawn/llms.txt Create and manage console variables for configurable plugin settings. This example demonstrates creating ConVars with different flags and limits, hooking their change events, and using their values to control plugin behavior like displaying welcome messages. ```c #include ConVar g_cvEnabled; ConVar g_cvMaxPlayers; ConVar g_cvWelcomeMessage; public void OnPluginStart() { // Create ConVars with default values g_cvEnabled = CreateConVar("sm_myplugin_enabled", "1", "Enable/disable the plugin", FCVAR_NOTIFY, true, 0.0, true, 1.0); g_cvMaxPlayers = CreateConVar("sm_myplugin_maxplayers", "32", "Maximum players allowed", FCVAR_NONE, true, 1.0, true, 64.0); g_cvWelcomeMessage = CreateConVar("sm_myplugin_welcome", "Welcome!", "Welcome message to display"); // Hook ConVar changes g_cvEnabled.AddChangeHook(OnEnabledChanged); g_cvMaxPlayers.AddChangeHook(OnMaxPlayersChanged); // Auto-generate config file AutoExecConfig(true, "myplugin"); } public void OnEnabledChanged(ConVar convar, const char[] oldValue, const char[] newValue) { bool enabled = convar.BoolValue; PrintToServer("[MyPlugin] Plugin %s", enabled ? "enabled" : "disabled"); } public void OnMaxPlayersChanged(ConVar convar, const char[] oldValue, const char[] newValue) { PrintToServer("[MyPlugin] Max players changed from %s to %s", oldValue, newValue); } public void OnClientPostAdminCheck(int client) { if (!g_cvEnabled.BoolValue) return; char message[256]; g_cvWelcomeMessage.GetString(message, sizeof(message)); PrintToChat(client, message); } ``` -------------------------------- ### Initialize SourcePawn VM Environment Source: https://context7.com/alliedmodders/sourcepawn/llms.txt Creates a new SourcePawn environment, configures JIT compilation and watchdog timers, and sets up a debug listener for error reporting. Ensure to shut down the environment and delete the object when done. ```cpp #include // Create a new SourcePawn environment ISourcePawnEnvironment* env = ISourcePawnEnvironment::New(); ISourcePawnEngine2* api = env->APIv2(); // Configure JIT settings api->SetJitEnabled(true); api->InstallWatchdogTimer(5000); // 5 second timeout // Set debug listener for error reporting class MyDebugListener : public IDebugListener { public: void OnDebugSpew(const char* msg, ...) override { va_list ap; va_start(ap, msg); vprintf(msg, ap); va_end(ap); } void ReportError(const IErrorReport& report, IFrameIterator& iter) override { printf("Error: %s\n", report.Message()); for (; !iter.Done(); iter.Next()) { if (iter.IsScriptedFrame()) { printf(" at %s:%d in %s\n", iter.FilePath(), iter.LineNumber(), iter.FunctionName()); } } } }; MyDebugListener listener; api->SetDebugListener(&listener); // Clean shutdown env->Shutdown(); delete env; ``` -------------------------------- ### Build SourcePawn from Source Source: https://github.com/alliedmodders/sourcepawn/blob/master/README.md Commands to clone the repository and initiate the build process using AMBuild. ```bash git clone --recursive https://github.com/alliedmodders/sourcepawn cd sourcepawn python3 configure.py --out obj ambuild obj ``` -------------------------------- ### Call Plugin Function with Parameters Source: https://context7.com/alliedmodders/sourcepawn/llms.txt Demonstrates how to push various parameter types (integer, float, string, array, by-reference) onto the stack and execute a SourcePawn function. Includes error handling and cancellation. ```cpp // Find a function that takes parameters IPluginFunction* func = runtime->GetFunctionByName("ProcessData"); if (!func || !func->IsRunnable()) { return; } // Push various parameter types func->PushCell(42); // int parameter func->PushFloat(3.14159f); // float parameter // Push string parameter func->PushString("Hello, SourcePawn!"); // Push array with copyback enabled cell_t myArray[10] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10}; func->PushArray(myArray, 10, SM_PARAM_COPYBACK); // Push by-reference parameter cell_t refValue = 100; func->PushCellByRef(&refValue, SM_PARAM_COPYBACK); // Execute and get result cell_t result; int err = func->Execute(&result); if (err == SP_ERROR_NONE) { printf("Result: %d\n", result); printf("Modified ref value: %d\n", refValue); printf("Modified array[0]: %d\n", myArray[0]); } else { printf("Execution error: %d\n", err); func->Cancel(); // Cancel if needed } ``` -------------------------------- ### Loading and Running Plugins Source: https://context7.com/alliedmodders/sourcepawn/llms.txt Details on how to load compiled SourcePawn plugins (`.smx` files) and execute their public functions using the `IPluginRuntime` interface. ```APIDOC ## Loading and Running Plugins ### Description This section explains how to load compiled SourcePawn plugins from files and interact with them using the `IPluginRuntime` interface. ### Method C++ API ### Endpoint N/A ### Parameters N/A ### Request Example ```cpp #include // Load a compiled plugin char error[256]; IPluginRuntime* runtime = api->LoadBinaryFromFile("myplugin.smx", error, sizeof(error)); if (!runtime) { printf("Failed to load plugin: %s\n", error); return; } // Get the default execution context IPluginContext* ctx = runtime->GetDefaultContext(); // Find and call a public function IPluginFunction* mainFunc = runtime->GetFunctionByName("OnPluginStart"); if (mainFunc) { cell_t result; mainFunc->Execute(&result); printf("OnPluginStart returned: %d\n", result); } // Check plugin properties printf("Plugin: %s\n", runtime->GetFilename()); printf("Memory usage: %zu bytes\n", runtime->GetMemUsage()); printf("Debug mode: %s\n", runtime->IsDebugging() ? "yes" : "no"); // Clean up delete runtime; ``` ### Response N/A ### Response Example N/A ``` -------------------------------- ### Manage Heap Memory in SourcePawn Source: https://context7.com/alliedmodders/sourcepawn/llms.txt Demonstrates manual heap allocation using HeapAlloc and automatic cleanup using AutoEnterHeapScope. ```cpp // Allocate temporary array on heap cell_t localAddr; cell_t* physAddr; int err = ctx->HeapAlloc(100, &localAddr, &physAddr); // 100 cells if (err == SP_ERROR_NONE) { // Fill the array for (int i = 0; i < 100; i++) { physAddr[i] = i; } // Pass to function func->PushCell(localAddr); func->Execute(nullptr); // Free when done ctx->HeapPop(localAddr); } // Using heap scopes for automatic cleanup { AutoEnterHeapScope scope(ctx); // Allocate 2D array cell_t arr2d; ctx->HeapAlloc2dArray(10, 20, &arr2d, nullptr); // 10x20 array // Use array... // Automatically freed when scope exits } ``` -------------------------------- ### Define Basic SourcePawn Plugin Structure Source: https://context7.com/alliedmodders/sourcepawn/llms.txt Outlines the standard plugin information block, global variables, and lifecycle hooks like OnPluginStart and OnPluginEnd. ```c // myplugin.sp - Basic plugin structure #include // Plugin information block public Plugin myinfo = { name = "My Plugin", author = "Developer", description = "Example SourcePawn plugin", version = "1.0.0", url = "https://example.com" }; // Global variables int g_Counter = 0; float g_Multiplier = 1.5; char g_Message[256]; // Called when plugin loads public void OnPluginStart() { // Register console command RegConsoleCmd("sm_hello", Command_Hello, "Says hello"); // Register admin command RegAdminCmd("sm_reset", Command_Reset, ADMFLAG_GENERIC, "Reset counter"); // Initialize g_Counter = 0; strcopy(g_Message, sizeof(g_Message), "Welcome!"); PrintToServer("[MyPlugin] Loaded successfully!"); } // Called when plugin unloads public void OnPluginEnd() { PrintToServer("[MyPlugin] Unloaded. Counter was: %d", g_Counter); } // Console command callback public Action Command_Hello(int client, int args) { g_Counter++; if (client == 0) { PrintToServer("Hello from server! Count: %d", g_Counter); } else { PrintToChat(client, "Hello, player! Count: %d", g_Counter); } return Plugin_Handled; } // Admin command callback public Action Command_Reset(int client, int args) { g_Counter = 0; ReplyToCommand(client, "[SM] Counter reset to zero"); return Plugin_Handled; } ``` -------------------------------- ### Implement Methodmaps for Object-Oriented Syntax Source: https://context7.com/alliedmodders/sourcepawn/llms.txt Shows how to extend enums with methods, properties, and constructors to create object-like structures. ```c // Define an enum for the base type enum WeaponType { Weapon_None = 0, Weapon_Pistol, Weapon_Rifle, Weapon_Shotgun }; // Methodmap extends the enum with methods and properties methodmap Weapon { // Constructor public Weapon(WeaponType type) { return view_as(type); } // Read-only property property WeaponType Type { public get() { return view_as(this); } } // Method to get weapon name public void GetName(char[] buffer, int maxlen) { switch (this.Type) { case Weapon_Pistol: strcopy(buffer, maxlen, "Pistol"); case Weapon_Rifle: strcopy(buffer, maxlen, "Rifle"); case Weapon_Shotgun: strcopy(buffer, maxlen, "Shotgun"); default: strcopy(buffer, maxlen, "None"); } } // Method to check validity public bool IsValid() { return this.Type != Weapon_None; } // Static method public static Weapon FromString(const char[] name) { if (StrEqual(name, "pistol", false)) return Weapon(Weapon_Pistol); if (StrEqual(name, "rifle", false)) return Weapon(Weapon_Rifle); if (StrEqual(name, "shotgun", false)) return Weapon(Weapon_Shotgun); return Weapon(Weapon_None); } } // Usage example public void OnPluginStart() { Weapon pistol = Weapon(Weapon_Pistol); Weapon rifle = Weapon.FromString("rifle"); char name[32]; pistol.GetName(name, sizeof(name)); PrintToServer("Weapon: %s, Valid: %s", name, pistol.IsValid() ? "yes" : "no"); } ``` -------------------------------- ### Load and Run SourcePawn Plugin Source: https://context7.com/alliedmodders/sourcepawn/llms.txt Loads a compiled SourcePawn plugin (`.smx` file) from disk and executes its public functions. Handles potential loading errors and provides access to plugin properties. ```cpp #include // Load a compiled plugin char error[256]; IPluginRuntime* runtime = api->LoadBinaryFromFile("myplugin.smx", error, sizeof(error)); if (!runtime) { printf("Failed to load plugin: %s\n", error); return; } // Get the default execution context IPluginContext* ctx = runtime->GetDefaultContext(); // Find and call a public function IPluginFunction* mainFunc = runtime->GetFunctionByName("OnPluginStart"); if (mainFunc) { cell_t result; mainFunc->Execute(&result); printf("OnPluginStart returned: %d\n", result); } // Check plugin properties printf("Plugin: %s\n", runtime->GetFilename()); printf("Memory usage: %zu bytes\n", runtime->GetMemUsage()); printf("Debug mode: %s\n", runtime->IsDebugging() ? "yes" : "no"); // Clean up delete runtime; ``` -------------------------------- ### Compiling SourcePawn Plugins with spcomp Source: https://context7.com/alliedmodders/sourcepawn/llms.txt Use the spcomp command line tool to compile .sp files into .smx bytecode. Various flags allow for custom include paths, preprocessor definitions, and strict syntax checking. ```bash # Basic compilation spcomp myplugin.sp # Specify output file spcomp myplugin.sp -o=output/myplugin.smx # Add include paths spcomp myplugin.sp -i=include -i=../shared/include # Define preprocessor symbols spcomp myplugin.sp -DDEBUG=1 -DVERSION="1.0.0" # Enable all warnings as errors spcomp myplugin.sp -E # Syntax check only (no output) spcomp myplugin.sp -s # Show included files spcomp myplugin.sp --show-includes # Set verbosity level spcomp myplugin.sp -v=2 # Compile with semicolon requirement spcomp myplugin.sp --require-semicolons # Require new declaration syntax spcomp myplugin.sp --require-newdecls # Full example with multiple options spcomp \ -i=scripting/include \ -o=plugins/myplugin.smx \ -E \ -DRELEASE=1 \ scripting/myplugin.sp ``` -------------------------------- ### Multi-dimensional Array Initialization Source: https://github.com/alliedmodders/sourcepawn/blob/master/docs/upgrading-1.11.md Demonstrates how to resolve compile-time errors when initializing multi-dimensional arrays with varying string lengths. ```SourcePawn char WeaponNames[][] = {"awp", "rocket_launcher"}; char weapon[32] = WeaponNames[0] ``` ```SourcePawn char WeaponNames[][32] = {"awp", "rocket_launcher"}; char weapon[32] = WeaponNames[0] ``` ```SourcePawn char WeaponNames[][] = {"awp", "rocket_launcher"}; char weapon[32] strcopy(weapon, sizeof(weapon), WeaponNames[0]) ``` -------------------------------- ### SourcePawn Timers for Scheduled Events Source: https://context7.com/alliedmodders/sourcepawn/llms.txt Shows how to create one-shot, repeating, and data-carrying timers using SourcePawn's timer functions. Remember to kill timers if they are still active when the plugin ends. ```cpp #include Handle g_RepeatingTimer = null; int g_TimerCount = 0; public void OnPluginStart() { // One-shot timer (executes once after 5 seconds) CreateTimer(5.0, Timer_OneShot); // Repeating timer with data g_RepeatingTimer = CreateTimer(1.0, Timer_Repeating, 42, TIMER_REPEAT); // Timer with DataPack for complex data Handle pack; CreateDataTimer(3.0, Timer_WithData, pack); WritePackCell(pack, 100); WritePackString(pack, "Hello Timer!"); } public Action Timer_OneShot(Handle timer) { PrintToServer("One-shot timer fired!"); return Plugin_Stop; } public Action Timer_Repeating(Handle timer, any data) { g_TimerCount++; PrintToServer("Repeating timer #%d, data: %d", g_TimerCount, data); // Stop after 10 iterations if (g_TimerCount >= 10) { g_RepeatingTimer = null; return Plugin_Stop; } return Plugin_Continue; } public Action Timer_WithData(Handle timer, Handle pack) { ResetPack(pack); int value = ReadPackCell(pack); char message[128]; ReadPackString(pack, message, sizeof(message)); PrintToServer("DataTimer - Value: %d, Message: %s", value, message); return Plugin_Stop; } public void OnPluginEnd() { // Kill timer if still active if (g_RepeatingTimer != null) { KillTimer(g_RepeatingTimer); } } ``` -------------------------------- ### SourcePawn Forwards for Event Systems Source: https://context7.com/alliedmodders/sourcepawn/llms.txt Illustrates creating and using global and private forwards for event handling and inter-plugin communication. Global forwards are registered with parameters and can be called from any plugin. ```cpp #include // Global forward - calls OnCustomEvent in all plugins GlobalForward g_OnCustomEvent; // Private forward - for specific registered callbacks PrivateForward g_OnDataReceived; public void OnPluginStart() { // Create global forward with parameters g_OnCustomEvent = new GlobalForward("OnCustomEvent", ET_Event, Param_Cell, // int eventId Param_String, // const char[] eventName Param_CellByRef // int &result ); // Create private forward g_OnDataReceived = new PrivateForward( ET_Ignore, Param_Cell, // int data Param_Array // int[] buffer ); RegConsoleCmd("sm_fire", Command_FireEvent); } public Action Command_FireEvent(int client, int args) { // Call the global forward int result = 0; Call_StartForward(g_OnCustomEvent); Call_PushCell(100); Call_PushString("TestEvent"); Call_PushCellRef(result); Action action; Call_Finish(action); PrintToServer("Forward returned action: %d, result: %d", action, result); return Plugin_Handled; } // Register a function to the private forward public void RegisterCallback(Function callback) { g_OnDataReceived.AddFunction(null, callback); } // Handler that plugins implement public Action OnCustomEvent(int eventId, const char[] eventName, int &result) { PrintToServer("Received event %d: %s", eventId, eventName); result = 42; return Plugin_Continue; } ``` -------------------------------- ### ArrayList Dynamic Arrays in SourcePawn Source: https://context7.com/alliedmodders/sourcepawn/llms.txt Demonstrates creating and manipulating ArrayLists for integers and strings, including searching, sorting, and cloning. Ensure proper cleanup with 'delete'. ```cpp #include public void OnPluginStart() { // Create ArrayList for integers ArrayList intList = new ArrayList(); intList.Push(10); intList.Push(20); intList.Push(30); PrintToServer("List length: %d", intList.Length); PrintToServer("First element: %d", intList.Get(0)); // Iterate and modify for (int i = 0; i < intList.Length; i++) { int value = intList.Get(i); intList.Set(i, value * 2); } // Create ArrayList for strings (blocksize for string storage) ArrayList strList = new ArrayList(ByteCountToCells(64)); strList.PushString("Alpha"); strList.PushString("Beta"); strList.PushString("Gamma"); char buffer[64]; for (int i = 0; i < strList.Length; i++) { strList.GetString(i, buffer, sizeof(buffer)); PrintToServer("String %d: %s", i, buffer); } // Search in list int idx = strList.FindString("Beta"); if (idx != -1) { PrintToServer("Found 'Beta' at index %d", idx); } // Sort the list strList.Sort(Sort_Ascending, Sort_String); // Clone and manipulate ArrayList clone = intList.Clone(); clone.Erase(0); // Remove first element clone.ShiftUp(1); // Insert space at index 1 clone.Set(1, 999); // Clean up delete intList; delete strList; delete clone; } ``` -------------------------------- ### Calling Plugin Functions with Parameters Source: https://context7.com/alliedmodders/sourcepawn/llms.txt Illustrates how to call plugin functions that accept parameters, including primitive types, strings, arrays, and by-reference values, with support for copyback. ```APIDOC ## Calling Plugin Functions with Parameters ### Description This section details how to call SourcePawn functions from C++ with various parameter types, including push operations for cells, floats, strings, arrays, and by-reference values, along with handling return values and errors. ### Method C++ API ### Endpoint N/A ### Parameters N/A ### Request Example ```cpp // Find a function that takes parameters IPluginFunction* func = runtime->GetFunctionByName("ProcessData"); if (!func || !func->IsRunnable()) { return; } // Push various parameter types func->PushCell(42); // int parameter func->PushFloat(3.14159f); // float parameter // Push string parameter func->PushString("Hello, SourcePawn!"); // Push array with copyback enabled cell_t myArray[10] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10}; func->PushArray(myArray, 10, SM_PARAM_COPYBACK); // Push by-reference parameter cell_t refValue = 100; func->PushCellByRef(&refValue, SM_PARAM_COPYBACK); // Execute and get result cell_t result; int err = func->Execute(&result); if (err == SP_ERROR_NONE) { printf("Result: %d\n", result); printf("Modified ref value: %d\n", refValue); printf("Modified array[0]: %d\n", myArray[0]); } else { printf("Execution error: %d\n", err); func->Cancel(); // Cancel if needed } ``` ### Response N/A ### Response Example N/A ``` -------------------------------- ### Run SourcePawn Corpus Tests Source: https://github.com/alliedmodders/sourcepawn/blob/master/README.md Clone and execute the large corpus of GPL scripts to validate compiler changes. ```bash git clone https://github.com/dvander/sourcepawn-corpus cd sourcepawn python tools/corpus/run.py objdir/path/to/spcomp ../sourcepawn-corpus -j 24 --fail-fast ``` -------------------------------- ### Access SourcePawn Plugin Debug Information Source: https://context7.com/alliedmodders/sourcepawn/llms.txt Retrieve debug metadata like source file mappings, function names, and line numbers using `IPluginDebugInfo`. This is useful for runtime analysis and error reporting. ```cpp IPluginDebugInfo* debug = runtime->GetDebugInfo(); if (debug) { // List all source files printf("Source files (%zu):\n", debug->NumFiles()); for (size_t i = 0; i < debug->NumFiles(); i++) { printf(" %s\n", debug->GetFileName(i)); } // List all functions printf("Functions (%zu):\n", debug->NumFunctions()); for (size_t i = 0; i < debug->NumFunctions(); i++) { const char* file; const char* name = debug->GetFunctionName(i, &file); printf(" %s in %s\n", name, file); } // Lookup by address ucell_t addr = 0x100; const char* funcName; uint32_t line; if (debug->LookupFunction(addr, &funcName) == SP_ERROR_NONE) { debug->LookupLine(addr, &line); printf("Address 0x%x is in %s at line %u\n", addr, funcName, line); } } ``` -------------------------------- ### Array Initialization Syntax Source: https://github.com/alliedmodders/sourcepawn/blob/master/docs/upgrading-1.11.md Corrects the deprecated practice of assigning scalar values to arrays. ```SourcePawn int gNumKills[MAXPLAYERS + 1] = 0; ``` ```SourcePawn int gNumKills[MAXPLAYERS + 1] = {0, ...}; ``` ```SourcePawn int gNumKills[MAXPLAYERS + 1]; ``` -------------------------------- ### SourcePawn Exception Handling Source: https://context7.com/alliedmodders/sourcepawn/llms.txt Use `ExceptionHandler` for scoped try-catch blocks to gracefully handle runtime errors. `DetectExceptions` can be used to simply detect and report errors without catching them. ```cpp #include void SafeExecute(IPluginFunction* func) { // Create exception handler scope ExceptionHandler handler(func->GetParentRuntime()->GetDefaultContext()); cell_t result; bool success = func->Invoke(&result); if (!success || handler.HasException()) { printf("Exception caught: %s\n", handler.Message()); // Exception is automatically cleared when handler goes out of scope return; } printf("Function returned: %d\n", result); } // For detecting exceptions without catching void DetectOnly(IPluginFunction* func) { DetectExceptions detector(func->GetParentRuntime()->GetDefaultContext()); func->Invoke(nullptr); if (detector.HasException()) { printf("Detected exception: %s\n", detector.Message()); detector.Rethrow(); // Propagate to caller } } ``` -------------------------------- ### Hooking Game Events in SourcePawn Source: https://context7.com/alliedmodders/sourcepawn/llms.txt Register and handle game events using HookEvent. Pre-hooks allow modification of event data before it is processed by the game. ```c #include public void OnPluginStart() { // Hook events HookEvent("player_death", Event_PlayerDeath); HookEvent("player_spawn", Event_PlayerSpawn, EventHookMode_Post); HookEvent("round_start", Event_RoundStart, EventHookMode_PostNoCopy); // Hook with pre-check capability HookEvent("player_hurt", Event_PlayerHurt, EventHookMode_Pre); } public void Event_PlayerDeath(Event event, const char[] name, bool dontBroadcast) { int victim = GetClientOfUserId(event.GetInt("userid")); int attacker = GetClientOfUserId(event.GetInt("attacker")); char weapon[64]; event.GetString("weapon", weapon, sizeof(weapon)); if (attacker > 0 && attacker != victim) { PrintToChatAll("%N killed %N with %s", attacker, victim, weapon); } } public void Event_PlayerSpawn(Event event, const char[] name, bool dontBroadcast) { int client = GetClientOfUserId(event.GetInt("userid")); if (IsClientInGame(client) && !IsFakeClient(client)) { CreateTimer(0.1, Timer_PostSpawn, GetClientUserId(client)); } } public Action Timer_PostSpawn(Handle timer, int userid) { int client = GetClientOfUserId(userid); if (client > 0) { PrintToChat(client, "You have spawned!"); } return Plugin_Stop; } public void Event_RoundStart(Event event, const char[] name, bool dontBroadcast) { PrintToChatAll("Round has started!"); } public Action Event_PlayerHurt(Event event, const char[] name, bool dontBroadcast) { int victim = GetClientOfUserId(event.GetInt("userid")); int damage = event.GetInt("dmg_health"); // Modify event data (Pre hook only) if (damage > 50) { event.SetInt("dmg_health", 50); // Cap damage at 50 } return Plugin_Continue; // or Plugin_Handled to block } public void OnPluginEnd() { // Unhook events (optional, done automatically) UnhookEvent("player_death", Event_PlayerDeath); } ``` -------------------------------- ### Configure nginx for Docgen Source: https://github.com/alliedmodders/sourcepawn/blob/master/tools/docgen/README.md Use these rules in your nginx configuration to route requests to index.php. ```nginx location /pawn-docgen/www/ { index index.php; try_files $uri @pawn_docgen_rewrite; } location @pawn_docgen_rewrite { rewrite "^/pawn-docgen/www/(.*)" /pawn-docgen/www/index.php?path=$1 last; } ``` -------------------------------- ### Bind Native Functions in SourcePawn Source: https://context7.com/alliedmodders/sourcepawn/llms.txt Define and bind native C++ functions to SourcePawn scripts using `UpdateNativeBinding`. Ensure functions are found by name before binding. ```cpp static cell_t MyNative_PrintMessage(IPluginContext* ctx, const cell_t* params) { // params[0] = number of parameters // params[1] = first parameter, etc. char* message; ctx->LocalToString(params[1], &message); printf("Plugin says: %s\n", message); return 1; // Return value to script } static cell_t MyNative_AddNumbers(IPluginContext* ctx, const cell_t* params) { cell_t a = params[1]; cell_t b = params[2]; return a + b; } static cell_t MyNative_GetArray(IPluginContext* ctx, const cell_t* params) { cell_t* arr; ctx->LocalToPhysAddr(params[1], &arr); cell_t maxlen = params[2]; // Fill the array for (int i = 0; i < maxlen && i < 5; i++) { arr[i] = i * 10; } return 5; // Return count } // Bind natives after loading plugin uint32_t nativeIndex; if (runtime->FindNativeByName("PrintMessage", &nativeIndex) == SP_ERROR_NONE) { runtime->UpdateNativeBinding(nativeIndex, MyNative_PrintMessage, 0, nullptr); } if (runtime->FindNativeByName("AddNumbers", &nativeIndex) == SP_ERROR_NONE) { runtime->UpdateNativeBinding(nativeIndex, MyNative_AddNumbers, 0, nullptr); } if (runtime->FindNativeByName("GetArray", &nativeIndex) == SP_ERROR_NONE) { runtime->UpdateNativeBinding(nativeIndex, MyNative_GetArray, 0, nullptr); } ``` -------------------------------- ### Correct Syntax for Returning Arrays Source: https://github.com/alliedmodders/sourcepawn/blob/master/docs/upgrading-1.11.md Functions returning arrays in SourcePawn 1.11 require transitional syntax. Use `char[]` instead of `String:` for array return types. ```C char[] GetAString() { return "hello"; } ``` -------------------------------- ### Run Regression Tests Source: https://github.com/alliedmodders/sourcepawn/blob/master/README.md Execute the built-in regression test suite against a specified build directory. ```bash python tests/runtests.py objdir ``` -------------------------------- ### Define Typeset Grammar Source: https://github.com/alliedmodders/sourcepawn/blob/master/docs/specification.md Grammar specification for defining typesets in SourcePawn. ```text typeset ::= "typeset" name "{" typeset-body? "}" typeset-body ::= (typeset-entry newline)* typeset-entry ::= type-expr ``` -------------------------------- ### Define Type Grammar Source: https://github.com/alliedmodders/sourcepawn/blob/master/docs/specification.md Formal grammar rules for defining types and typed declarations in SourcePawn. ```ebnf type-prefix ::= "const"? base-type base-type ::= function-type | primitive-type | identifier type-dims ::= ("[" "]")* fixed-type-dims ::= ("[" integer "]")* type-expr ::= type-prefix | type-prefix type-dims | type-prefix fixed-type-dims typed-decl ::= type-prefix name | type-prefix type-dims name | type-prefix name fixed-type-dims ``` -------------------------------- ### Struct Definition Grammar Source: https://github.com/alliedmodders/sourcepawn/blob/master/docs/specification.md Outlines the syntax for defining structs, which are composite types composed of named fields. Structs are assigned by value, copying field contents. ```sourcepawn struct ::= "struct" name "{" struct-body "}" terminator struct-body ::= (struct-member newline)* struct-member ::= record-field | record-property | record-method record-field ::= record-visibility typed-decl record-property ::= type-expr identifier "{" property-body? "}" property-body ::= property-setter? property-getter | property-getter? property-setter property-getter ::= record-visibility "native" "get" "(" ")" terminator | record-visibility "get" "(" ")" "=" identifier newline | record-visibility "get" "(" ")" method-body newline property-setter ::= record-visibility "native" "set" "(" typed-decl ")" terminator | record-visibility "set" "(" ")" "=" identifier newline | record-visibility "set" "(" typed-decl ")" method-body newline record-method ::= record-visibility record-scope type-expr identifier "(" new-arg-list? ")" method-body newline | record-visibility record-scope "native" type-expr identifier "(" new-arg-list? ")" terminator record-scope ::= "static"? record-visibility ::= "public" new-arg-list ::= new-arg | new-arg "," new-arg new-arg ::= new-arg-base ('=' expression)? new-arg-base ::= type-prefix "&"? identifier | type-prefix type-dims identifier | type-prefix identifier fixed-type-dims ``` -------------------------------- ### Preprocessor Visibility Changes Source: https://github.com/alliedmodders/sourcepawn/blob/master/docs/upgrading-1.11.md Updates preprocessor logic to use explicit macros instead of relying on compiler symbol table visibility. ```SourcePawn enum Blah { }; #if defined Blah // ... #endif ``` ```SourcePawn enum Blah { }; #define BLAH_ENABLED #if defined BLAH_ENABLED // ... #endif ``` -------------------------------- ### Deprecated `do` Keyword and Parenthesis Omission Source: https://github.com/alliedmodders/sourcepawn/blob/master/docs/upgrading-1.11.md SourcePawn 1.11 deprecates the `do` keyword before statements and omitting parentheses in control conditionals. Use standard syntax with parentheses for clarity and compatibility. ```C for int i = 0; i < 10; i++ do { } switch i do { } do { } while i; ``` ```C for (int i = 0; i < 10; i++) { } switch (i) { } do { } while (i); ``` -------------------------------- ### Using StringMap for Key-Value Storage Source: https://context7.com/alliedmodders/sourcepawn/llms.txt StringMap provides efficient storage for various data types using string keys. Always delete snapshots and maps to prevent memory leaks. ```c #include StringMap g_PlayerData; StringMap g_ConfigValues; public void OnPluginStart() { g_PlayerData = new StringMap(); g_ConfigValues = new StringMap(); // Store various value types g_ConfigValues.SetValue("max_health", 100); g_ConfigValues.SetValue("speed_multiplier", 1.5); g_ConfigValues.SetString("server_name", "My Server"); // Store arrays int coords[3] = {100, 200, 300}; g_ConfigValues.SetArray("spawn_point", coords, sizeof(coords)); } public void OnClientAuthorized(int client, const char[] auth) { // Store player data using SteamID as key any data[3]; data[0] = GetTime(); // Join time data[1] = 0; // Score data[2] = 0; // Deaths g_PlayerData.SetArray(auth, data, sizeof(data)); } public void OnClientDisconnect(int client) { char auth[32]; GetClientAuthId(client, AuthId_Steam2, auth, sizeof(auth)); // Retrieve and remove player data any data[3]; if (g_PlayerData.GetArray(auth, data, sizeof(data))) { int playTime = GetTime() - data[0]; PrintToServer("%N played for %d seconds, Score: %d, Deaths: %d", client, playTime, data[1], data[2]); } g_PlayerData.Remove(auth); } // Iterate over StringMap public void ListAllConfigs() { StringMapSnapshot snapshot = g_ConfigValues.Snapshot(); char key[64]; for (int i = 0; i < snapshot.Length; i++) { snapshot.GetKey(i, key, sizeof(key)); int intValue; if (g_ConfigValues.GetValue(key, intValue)) { PrintToServer("Config: %s = %d", key, intValue); } } delete snapshot; } public void OnPluginEnd() { delete g_PlayerData; delete g_ConfigValues; } ``` -------------------------------- ### Enum Definition Grammar Source: https://github.com/alliedmodders/sourcepawn/blob/master/docs/specification.md Defines the syntax for creating named or unnamed enumerations in SourcePawn. Initialized enum entries must use constant expressions. ```sourcepawn enum ::= "enum" enum-name? "{" enum-entry-list? "}" terminator enum-name ::= label | name enum-entry-list ::= enum-entry newline | enum-entry "," newline enum-entry-list enum-entry ::= identifier ("=" expression)? ``` -------------------------------- ### Define Primitive Type Grammar Source: https://github.com/alliedmodders/sourcepawn/blob/master/docs/specification.md Formal grammar rules for primitive types available in SourcePawn. ```ebnf primitive-type ::= bool | char | int8 | uint8 | int16 | uint16 | int32 | uint32 | int64 | uint64 | int | intn | uintn | float | double | any ``` -------------------------------- ### Static Array Declaration Source: https://github.com/alliedmodders/sourcepawn/blob/master/docs/upgrading-1.11.md Corrects the syntax for declaring fixed-length arrays in static scope. ```SourcePawn static float[3] sVector ``` ```SourcePawn static float sVector[3] ``` -------------------------------- ### Methodmap Definition Grammar Source: https://github.com/alliedmodders/sourcepawn/blob/master/docs/specification.md Specifies the grammar for defining methodmaps, which extend enums to allow struct-like usage without adding fields. A methodmap can have at most one per enum. ```sourcepawn methodmap ::= "methodmap" name "__nullable__"? "{" methodmap-body "}" terminator methodmap-body ::= (methodmap-member newline)* methodmap-member ::= record-method | record-property ``` -------------------------------- ### Iterate SourcePawn Call Stack Source: https://context7.com/alliedmodders/sourcepawn/llms.txt Use `IFrameIterator` to traverse the call stack for debugging. It allows inspection of script and native frames, including function names, file paths, and line numbers. ```cpp void PrintStackTrace(IPluginContext* ctx) { IFrameIterator* iter = ctx->CreateFrameIterator(); int frameNum = 0; while (!iter->Done()) { if (iter->IsScriptedFrame()) { printf("#%d: %s() at %s:%u\n", frameNum++, iter->FunctionName() ? iter->FunctionName() : "", iter->FilePath() ? iter->FilePath() : "", iter->LineNumber()); } else if (iter->IsNativeFrame()) { printf("#%d: [native] %s()\n", frameNum++, iter->FunctionName() ? iter->FunctionName() : ""); } iter->Next(); } ctx->DestroyFrameIterator(iter); } ``` -------------------------------- ### Define Function Type Grammar Source: https://github.com/alliedmodders/sourcepawn/blob/master/docs/specification.md Grammar specification for defining function types in SourcePawn. ```text function-type ::= "(" inner-function-type ")" | inner-function-type inner-function-type ::= "function" type-expr "(" function-type-arg-list? ")" function-type-arg-list ::= function-type-arg | function-type-arg (',' function-type-arg) function-type-arg ::= type-expr-prefix "&"? identifier | type-expr-prefix type-dims identifier | type-expr-prefix identifier fixed-type-dims ``` -------------------------------- ### Fixing Undefined Symbol Error in Assignment Source: https://github.com/alliedmodders/sourcepawn/blob/master/docs/upgrading-1.11.md In SourcePawn 1.11, self-assignment like `int i = i;` is invalid because the right-hand side is evaluated first, and `i` is not yet defined. Initialize to zero to resolve this. ```C int i = 0; ``` -------------------------------- ### Fixing Fixed-Size Array Bracket Positioning Source: https://github.com/alliedmodders/sourcepawn/blob/master/docs/upgrading-1.11.md In SourcePawn 1.11, brackets `[]` after a variable name indicate a fixed-size array. Ensure correct bracket positioning to avoid 'error 183' when defining function arguments. ```C stock void DoCommand(int client, char argument[] = "") ``` -------------------------------- ### Correcting Array Size Mismatches Source: https://github.com/alliedmodders/sourcepawn/blob/master/docs/upgrading-1.11.md SourcePawn 1.11 fixes incorrect internal calculations for string array sizes. Ensure allocated array sizes accurately reflect the data being stored to avoid 'error 047'. ```C char a[1] = "0"; // Error: "0" is two bytes ``` ```C char b[5]; b = IsClientInGame(client) ? "true" : "false"; // Error: "false" is 6 bytes ``` ```C char buffer[4]; GeoipCode2(ip, buffer); // Error: char[3] is needed, but user gave char[4] ``` -------------------------------- ### Fixed Dimension Syntax Source: https://github.com/alliedmodders/sourcepawn/blob/master/docs/upgrading-1.11.md Corrects the placement of array dimension brackets in function parameters. ```SourcePawn void GetVector(float[3] pos) {} ``` ```SourcePawn void GetVector(float pos[3]) {} ```