#include "SDK\amx\amx.h"
#include "SDK\plugincommon.h"
typedef void (*logprintf_t)(char* format, ...);
logprintf_t logprintf;
extern void *pAMXFunctions;
cell AMX_NATIVE_CALL HelloWorld(AMX* amx, cell* params)
{
logprintf("This was printed from the Test plugin! Yay!");
return 1;
}
PLUGIN_EXPORT unsigned int PLUGIN_CALL Supports()
{
return SUPPORTS_VERSION | SUPPORTS_AMX_NATIVES;
}
PLUGIN_EXPORT bool PLUGIN_CALL Load(void **ppData)
{
pAMXFunctions = ppData[PLUGIN_DATA_AMX_EXPORTS];
logprintf = (logprintf_t) ppData[PLUGIN_DATA_LOGPRINTF];
logprintf(" * Test plugin was loaded.");
return true;
}
PLUGIN_EXPORT void PLUGIN_CALL Unload()
{
logprintf(" * Test plugin was unloaded.");
}
AMX_NATIVE_INFO PluginNatives[] =
{
{"HelloWorld", HelloWorld},
{0, 0}
};
PLUGIN_EXPORT int PLUGIN_CALL AmxLoad( AMX *amx )
{
return amx_Register(amx, PluginNatives, -1);
}
PLUGIN_EXPORT int PLUGIN_CALL AmxUnload( AMX *amx )
{
return AMX_ERR_NONE;
}
EXPORTS Supports Load Unload AmxLoad AmxUnload
Supports() | This function tells the server what capabilities our plugin will have based on what it returns. Generally we only use 3 support flags in plugins: SUPPORTS_VERSION, SUPPORTS_AMX_NATIVES, and SUPPORTS_PROCESS_TICK. |
Load(void**) | The Load function is pretty straight forward. This is called when the plugin is loaded and gets passed an array of addresses that the plugin will use to function. The two indexes we typically use are PLUGIN_DATA_AMX_EXPORTS, and PLUGIN_DATA_LOGPRINTF. |
Unload() | Unload is called when the plugin is unloaded (server is shutdown). |
AmxLoad(AMX*) | This is called when a new AMX instance is loaded into the server. This will be called for every filterscript/gamemode! Because of this it isn't a good idea to store a single AMX instance for the entire plugin, instead use a queue/list/vector. In this function we also register our custom native functions we wish to provide PAWN with. |
AmxUnload(AMX*) | This function is called when ever an AMX instance is unloaded. If you store AMX instances, make sure you remove them. Otherwise you'll have instances to non-existing gamemodes/filterscripts. |
ProcessTick() | ProcessTick is a function that gets called on every iteration of the server's loop. People generally use this function as a method of managing time by keeping track of the amount of ticks that have passed. The SA-MP server is said to have a sleep time of 5ms, so if 50 ticks go by you have an idea of the elapsed time (5 * 50 = 250ms). Note: Anyone who uses threads in their plugins and require PAWN interaction needs to use this function to ensure PAWN isnt busy doing another task! |
cell | A "cell" is a typedef that is simply there for portability. PAWN offers support for 16bit, 32bit, and 64bit integers. The typedef "cell" will always be the correct size while "int" might not be. Generally with SA-MP plugins, this typedef will always be a 32bit integer. Note: There is also a "ucell" typedef for unsigned integers, but this is hardly used. |
AMX_NATIVE_CALL | This defines the calling convention that our native functions will use. At the moment its defined to nothing, so the default will be used. |
AMX | The name of this structure should be pretty clear, its an AMX instance. This structure has tons of information relating to the data segments along with tons of other amx related information. This structure must be present in all native function declarations along with a cell pointer to the passed parameters; Without this info we dont know what script is calling our native, nor do we know what was passed. |
PLUGIN_EXPORT | This is defined as "PLUGIN_EXTERN_C". |
PLUGIN_EXTERN_C | If using a C++ compiler this is defined as "extern "C"". This is for compatibility with C. C++ offers things like function overloading so additional information like the number/size of arguments is stored with the function's name, this is refered to as "name mangling". When this define is used, it tells C++ to use C-style linkage and thus avoids C++ name mangling. |
PLUGIN_CALL | This define tells C/++ what calling convention to use for our exported functions. If you use a compiler targeting windows, this is defined as __stdcall. Otherwise its defined as nothing and the default convention is used. |
SUPPORTS_VERSION | This define is to be used in a bit mask that is returned by our "Supports()" function. This flag is used to check for compatibility with the server. |
SUPPORTS_AMX_NATIVES | This is yet another define that is to be used by our "Supports()" function. Any plugin that uses AMX functions must use this flag! Without this flag you'll get a run time 19 error due to your natives not registering with the server (amx_Register). |
SUPPORTS_PROCESS_TICK | Our last flag for our "Supports()" function. If you're going to be using the "ProcessTick()" function, you have to add this to our "Supports()" function's returned bit mask. |
PLUGIN_DATA_AMX_EXPORTS | This is used as an index to the multidimensional array that gets passed on Load. This particular index holds the AMX function table. All plugins must use this index to assign the address of the function table to pAMXFunctions. |
PLUGIN_DATA_LOGPRINTF | Yet another index that is to be used with the multidimensional array that gets passed on Load. This index holds the address of the logprintf function which prints information and saves said information to the server's log file. If your plugin uses this function you must use this index to assign the address to the logprintf function pointer. |
AMX_NATIVE_INFO | This struct is used in conjunction with amx_Register. It contains a string that holds your new native's name, and a pointer to it's address. |
enum
{
AMX_ERR_NONE,
/* reserve the first 15 error codes for exit codes of the abstract machine */
AMX_ERR_EXIT, /* forced exit */
AMX_ERR_ASSERT, /* assertion failed */
AMX_ERR_STACKERR, /* stack/heap collision */
AMX_ERR_BOUNDS, /* index out of bounds */
AMX_ERR_MEMACCESS, /* invalid memory access */
AMX_ERR_INVINSTR, /* invalid instruction */
AMX_ERR_STACKLOW, /* stack underflow */
AMX_ERR_HEAPLOW, /* heap underflow */
AMX_ERR_CALLBACK, /* no callback, or invalid callback */
AMX_ERR_NATIVE, /* native function failed */
AMX_ERR_DIVIDE, /* divide by zero */
AMX_ERR_SLEEP, /* go into sleepmode - code can be restarted */
AMX_ERR_INVSTATE, /* invalid state for this access */
AMX_ERR_MEMORY = 16, /* out of memory */
AMX_ERR_FORMAT, /* invalid file format */
AMX_ERR_VERSION, /* file is for a newer version of the AMX */
AMX_ERR_NOTFOUND, /* function not found */
AMX_ERR_INDEX, /* invalid index parameter (bad entry point) */
AMX_ERR_DEBUG, /* debugger cannot run */
AMX_ERR_INIT, /* AMX not initialized (or doubly initialized) */
AMX_ERR_USERDATA, /* unable to set user data field (table full) */
AMX_ERR_INIT_JIT, /* cannot initialize the JIT */
AMX_ERR_PARAMS, /* parameter error */
AMX_ERR_DOMAIN, /* domain error, expression result does not fit in range */
AMX_ERR_GENERAL, /* general error (unknown or unspecific error) */
};
int amx_Allot(AMX *amx, int cells, cell *amx_addr, cell **phys_addr);
amx | The abstract machine. |
cells | The number of cells to reserve. |
amx_addr | The address of the allocated cell as the pawn program (that runs in the abstract machine) can access it. phys_addr The address of the cell for C/C++ programs to access. |
[float] amx_ctof([cell] c);
c | The value to cast from “cell” type to “float”. |
int amx Exec(AMX *amx, long *retval, int index);
amx | The abstract machine from which to call a function. |
retval | Will hold the return value of the called function uponreturn. This parameter may be NULL if you are notinterested in the return value. |
index | An index into the “public function table”; it indicatesthe function to execute. See amx_FindPublic formoreinformation. Use AMX_EXEC_MAIN to start executingat the main function, and AMX_EXEC_CONT to continueexecution from a “sleep state”. |
int amx FindPublic(AMX *amx, char *funcname, int *index);
amx | The abstract machine. |
funcname | The name of the public function to find. |
index | Upon return, this parameter holds the index of therequested public function. |
[cell] amx_ftoc(float] f);
f | The value to cast from “float” type to “cell”. |
int amx_GetAddr(AMX *amx,cell amx_addr,cell **phys_addr);
amx | The abstract machine. |
amx_addr | The address relative to the abstract machine. |
phys_addr | A pointer to the variable that will hold the memoryaddress of the indicated cell. If the amx_addr parame-ter is not a valid address inside the abstract machine,phys_addr will be set to NULL. |
int amx_GetString(char *dest, cell *source, int use_wchar, size_t size);
dest | A pointer to a character array of sufficient size to holdthe converted source string. |
source | A pointer to the source string. Use amx_GetAddr toconvert a string address in the amx to the physicaladdress. |
use_wchar | A non-zero value interprets the dest argument as apointer to “wide characters” —i.e. wchar_t, regard-less of its char type. This allows the function to storeUnicode strings. |
size | The maximum number of characters to store in dest,including the terminating zero byte. If the string inthe source is longer, the string in dest will be trun-cated. |
int amx_Push(AMX *amx, cell value);
amx | The abstract machine. |
value | The value to pass to the public function. |
int amx_PushArray(AMX *amx, cell *amx_addr, cell **phys_addr, const cell array[], int numcells);
amx | The abstract machine. |
amx_addr | The address of the allocated cell as the pawn program (that runs in the abstract machine) can access it, needed to release the memory block. This parameter may be NULL. |
phys_addr | The address of the cell for C/C++ programs to access. This parameter may be NULL. |
array | The array of values to pass to the public function. A single cell that must be passed by-reference is regarded as a single-cell array. |
numcells | The number of elements in the array. |
int amx PushString(AMX *amx, cell *amx_addr, cell **phys_addr, const char *string, int pack, int use_wchar);
amx | The abstract machine. |
amx_addr | The address of the allocated cell as the pawn program (that runs in the abstract machine) can access it, needed to release the memory block. This parameter may be NULL. |
phys_addr | The address of the cell for C/C++ programs to access.This parameter may be NULL. |
string | The string to pass to the public function. |
pack | Non-zero to convert the source string to a packed string in the abstract machine, zero to convert the source string to a cell string. |
use_wchar | A non-zero value interprets the string argument as a pointer to “wide characters” i.e. wchar_t, regardless of its char type. This allows the function to accept Unicode strings. |
int amx Register(AMX *amx, AMX NATIVE INFO *list, int number);
amx | The abstract machine. |
list | An array with structures where each structure holdsa pointer to the name of a native function and a function pointer. The list is optionally terminated with astructure holding two NULL pointers. |
number | The number of structures in the list array, or -1 if thelist ends with a structure holding two NULL pointers. |
int amx Release(AMX *amx,cell amx_addr);
amx | The abstract machine. |
amx_addr | The address of the allocated cell as the pawn program (that runs in the abstract machine) sees it. This value is returned by amx_Allot, amx_PushArray and amx_PushString. |
int amx SetString(cell *dest, char *source, int pack, int use_wchar, size_t size);
dest | A pointer to a character array in the amx where the converted string is stored. Use amx_GetAddr to convert a string address in the amx to the physical address. |
source | A pointer to the source string. |
pack | Non-zero to convert the source string to a packed |
string | in the abstract machine, zero to convert the source string to a cell string. |
use_wchar | A non-zero value interprets the source argument as a pointer to “wide characters” —i.e. wchar_t, regardless of its char type. This allows the function to accept Unicode strings. |
size | The maximum number of cells to store in dest, including the terminating zero byte or cell. If the string in the source is longer than can fit in the number of cells in dest, it will be truncated. |
int amx_StrLen(const cell *cstring, int *length);
cstring | The string in the abstract machine. |
length | This parameter will hold the string length upon return. |
amx_StrParam([AMX*] amx, int] param, [char*] result);
amx | The abstract machine. |
param | The parameter number. |
result | A variable that will hold the result on return. |
//An array of the functions we wish to register with the abstract machine.
AMX_NATIVE_INFO PluginNatives[] =
{
//Here we specify our native functions information and terminate the array with two null values.
{"HelloWorld", HelloWorld},
{0, 0}
};
PLUGIN_EXPORT int PLUGIN_CALL AmxLoad( AMX *amx )
{
//Here we register our natives to the abstract machine. Note how we're using -1. Normally this would have to be the number of
//functions we're registering, but since we terminated the array with two null values, we can specify -1.
return amx_Register(amx, PluginNatives, -1);
}
//This function demonstrates: how to get a string (and its length) from PAWN.
//PAWN native: native PrintPawnString(const str[]);
cell AMX_NATIVE_CALL PrintPawnString(AMX* amx, cell* params)
{
int
len = NULL,
ret = NULL;
cell *addr = NULL;
//Get the address of our string param (str) and then get its length
amx_GetAddr(amx, params[1], &addr);
amx_StrLen(addr, &len);
//if the length of input isnt 0
if(len)
{
//We increase len because we want to make room for the terminating null char when we allocate memory.
//Also because GetString's size parameter counts the null chracter, we have to specify the length
//of the string + 1; otherwise our string will be truncated to make room for the null char (we'd lose 1 character).
len++;
//Allocate memory to hold our string we're passing (str) and then "get" the string using our allocated memory to store it.
char* text = new char[ len ];
amx_GetString(text, addr, 0, len);
//Use logprintf to print out string (text). We dont use std::cout because it doesnt write to the server log (only the console).
logprintf(text);
//Deallocate our memory...
delete[] text;
}
return 1;
}
//This function demonstrates: an alternative method to getting strings from pawn- and the possible risks that come with it.
//PAWN native: native PrintPawnString2(const str[]);
cell AMX_NATIVE_CALL PrintPawnString2(AMX* amx, cell* params)
{
//This method is NOT recomended as the amx_StrParam macro uses the alloca function which is NOT a standard in C, OR C++.
//Using this method comes with risks of overflowing the stack (If you allocate large amounts of memory) and also
//gives you the risk of bugs (this function is machine AND compiler dependent- some implementations are said to be bugged).
char* text = NULL;
amx_StrParam(amx, params[1], text);
//Check if text is null
if(text != NULL)
{
//Use logprintf to print out string (text). We dont use std::cout because it doesnt write to the server log (only the console).
logprintf(text);
}
return 1;
}
//This function demonstrates: how to modify a PAWN string.
//PAWN native: native SetPawnString(str[], len = sizeof(str));
cell AMX_NATIVE_CALL SetPawnString(AMX* amx, cell* params)
{
const string message = "This is a string from C/++!!";
cell* addr = NULL;
//Get the address of our string parameter (str) and store our message
amx_GetAddr(amx, params[1], &addr);
amx_SetString(addr, message.c_str(), 0, 0, params[2]);
return 1;
}
//This function demonstrates: how to cast a float to a PAWN float, and return it.
//PAWN native: native Float:ReturnPawnFloatVal();
cell AMX_NATIVE_CALL ReturnPawnFloatVal(AMX* amx, cell* params)
{
//Since PAWN is a typeless language it stores everything as a 32bit integer and relies on tags to handle special data.
//A floating point number is no exception to this; It's a still 32bit int, but it has a Float tag to show that it shouldnt
//be treated like an regular integer. So how do we convert a float to an 32bit integer (for PAWN) without losing data?
//The answer is the amx_ftoc macro!
//The macro amx_ftoc type casts a float into a cell while preserving its bit pattern (amx_ctof does the inverse).
const float f = 22.624f;
return amx_ftoc(f);
}
//This function demonstrates: How to pass parameters by reference.
//PAWN native: native SetPawnReferenceVars(&value1, &Float:value2);
cell AMX_NATIVE_CALL SetPawnReferenceVars(AMX* amx, cell* params)
{
const int val = 65;
const float val2 = 84.54f;
cell* addr[2] = {NULL, NULL};
//Get the addresses of "value1" and "value2"
amx_GetAddr(amx, params[1], &addr[0]);
amx_GetAddr(amx, params[2], &addr[1]);
//Dereference our pointers and assign our values. Remember to ALWAYS use the macro "amx_ftoc" to convert floats into
//cells (the appropriate float format for PAWN)!
*addr[0] = val;
*addr[1] = amx_ftoc(val2);
return 1;
}
//This function demonstrates: how to get and modify array values.
//PAWN native: native PrintPawnArray(arr[], size = sizeof(arr));
cell AMX_NATIVE_CALL PrintPawnArray(AMX* amx, cell* params)
{
//Make sure there's something to print...
if(params[2] > 0)
{
cell* addr = NULL;
//Get the address of the first value in our PAWN array.
amx_GetAddr(amx, params[1], &addr);
for(int i = 0, l = params[2]; i < l; i++)
{
//This is pretty straight forward: We dereference the addr pointer to get our value to print.
//You should know this already, but arrays and pointers are almost the same thing, so we can use pointer
//arithmetic to add an offset OR just use the subscript operator (in the end *(addr+1) and addr[1] are the same).
logprintf("arr[%d] = %d", i, *(addr + i));
//If you wanted to modify the array you would just change its value by dereferencing addr and assigning a new value.
//You should know this as well, im just adding it in for completeness. Here we change the first value of our array
//to 5 (Note: Since its the first value, no offset it used).
// *(addr) = 5;
}
}
return 1;
}
//This function demonstrates: How to call a callback that is in a PAWN script.
//PAWN native: native EmitPawnCallback();
cell AMX_NATIVE_CALL EmitPawnCallback(AMX* amx, cell* params)
{
int idx;
const cell var = 3;
const cell arr[] = {100, 4, 33};
const string str = "Some random message from C++.";
//Pawn callback: forward OnPawnCallbackEmitted(var, arr[], str[]);
//Find our callback and store its place in the public function table (it's index) into our idx var.
if(!amx_FindPublic(amx, "OnPawnCallbackEmitted", &idx))
{
cell
ret,
addr;
//Here we push our arguments to our function. Note that if the function has multiple arguments you have to push your
//values in reverse order! Thats why we're pushing the string first, then the array, and finally our integer.
amx_PushString(amx, &addr, NULL, str.c_str(), NULL, NULL);
//amx_PushArray(amx, NULL, NULL, arr, sizeof(arr) / sizeof(cell));
cell
amx_addr,
*phys_addr;
//For some reason amx_PushArray seems to be crashing the server, and i have NO idea why. My usage should be completely
//valid judging from the implementers guide, and the code itself. Since the function isnt working we'll have to
//use the old method and allocate the memory, set it, and push it all ourselves. This is pretty straight forward. We
//allocate memory on the heap using amx_Allot (this returns 2 addresses- one of the location in the abstract machine
//(amx_addr), and one relative to the actual server's address space (phsy_addr - which we can use in C++)). Once the
//memory is allocated we use memcpy to copy the memory from our array to our phys_addr address location.
amx_Allot(amx, sizeof(arr) / sizeof(cell), &amx_addr, &phys_addr);
memcpy(phys_addr, arr, sizeof(arr));
amx_Push(amx, amx_addr);
//Push our integer value
amx_Push(amx, var);
//Execute our function using our previously obtained idx var.
//Note: This function's second parameter is what the callback returned (Can be NULL if you're not interested in return values).
amx_Exec(amx, &ret, idx);
//Release our memory that we allocated. The function amx_Alloc allocates memory on the heap in the abstract machine.
//The functions amx_PushString and amx_PushArray both use this function internally so you have to release the memory every time
//you use one of those functions. NOTE: We used both amx_PushString and amx_PushArray, and yet we only have ONE release call.
//This is because memory on the heap is allocated in ascending order! amx_Release release all the memory above a certain point
//(the second parameter, amx_addr - which is our addr variable). Since it does this we ONLY store the address from the amx_PushString
//call, as it'll delete everything from that point on.
amx_Release(amx, addr);
//Print the return value (for completeness).
logprintf("EmitPawnCallback NOTE: OnPawnCallbackEmitted callback returned %d!", ret);
}
return 1;
}
i = integer f = float value s = string v = reference variable (GetPlayerPos, GetPlayerKeys, etc.) p = string var (GetPlayerName etc.)
//This function demonstrates: how to use invoke to call SA-MP natives.
//PAWN native: native WhereIsPlayer(playerid);
cell AMX_NATIVE_CALL WhereIsPlayer(AMX* amx, cell* params)
{
float
x = NULL,
y = NULL,
z = NULL;
//Get the player's position (and check to see if he is even connected).
if(g_Invoke->callNative(&PAWN::GetPlayerPos, params[1], &x, &y, &z))
{
char name[24];
//Get the rest of the player's information (name, interior, and virtualworld) and print it.
g_Invoke->callNative(&PAWN::GetPlayerName, params[1], name);
int interior = g_Invoke->callNative(&PAWN::GetPlayerInterior, params[1]);
int virtualworld = g_Invoke->callNative(&PAWN::GetPlayerVirtualWorld, params[1]);
logprintf("%s is at X: %.2f, Y: %.2f, Z: %.2f (Virtual world: %d, Interior %d).", name, x, y, z, virtualworld, interior);
return 1;
}
return 0;
}
//This function demonstrates: how to write a native to act as a callback.
//PAWN native: native TEST_Hook_OnPlayerConnnect(playerid);
cell AMX_NATIVE_CALL TEST_Hook_OnPlayerConnnect(AMX* amx, cell* params)
{
//Get the players name
char name[24];
g_Invoke->callNative(&PAWN::GetPlayerName, params[1], name);
//Check if his name is "Mario".
if(string("Mario") == name)
{
//If it is send our funny little message and kick him.
g_Invoke->callNative(&PAWN::SendClientMessage, params[1], 0xFFFFFFFF, "Sorry {FF0000}Mario, {FFFFFF}but your princess is in another server.");
g_Invoke->callNative(&PAWN::Kick, params[1]);
}
return 1;
}
public OnPlayerConnect(playerid)
{
TEST_Hook_OnPlayerConnnect(playerid);
//..
if(funcidx("TEST_OnPlayerConnect") != -1)
{
return CallLocalFunction("TEST_OnPlayerConnect", "d", playerid);
}
return 1;
}
#if defined _ALS_OnPlayerConnect
#undef OnPlayerConnect
#else
#define _ALS_OnPlayerConnect
#endif
#define OnPlayerConnect TEST_OnPlayerConnect
forward TEST_OnPlayerConnect(playerid);
//Anything with the prefix "TEST" should be changed if you called your plugin something
//different. This also goes for the plugin code (TEST_Hook_OnPlayerConnect specifically).
#if defined _TEST_INCLUDED
#endinput
#endif
#define _TEST_INCLUDED
#pragma library Test
#include <a_samp>
//Example natives
native PrintPawnString(const str[]);
native PrintPawnString2(const str[]);
native SetPawnString(str[], len = sizeof(str));
native Float:ReturnPawnFloatVal();
native SetPawnReferenceVars(&value1, &Float:value2);
native PrintPawnArray(arr[], size = sizeof(arr));
native EmitPawnCallback();
native WhereIsPlayer(playerid);
native TEST_Hook_OnPlayerConnnect(playerid);
//Invoke native
native Invoke_GetAddresses();
//Callback hooks
public OnPlayerConnect(playerid)
{
TEST_Hook_OnPlayerConnnect(playerid);
//..
if(funcidx("TEST_OnPlayerConnect") != -1)
{
return CallLocalFunction("TEST_OnPlayerConnect", "d", playerid);
}
return 1;
}
#if defined _ALS_OnPlayerConnect
#undef OnPlayerConnect
#else
#define _ALS_OnPlayerConnect
#endif
#define OnPlayerConnect TEST_OnPlayerConnect
forward TEST_OnPlayerConnect(playerid);
public OnGameModeInit()
{
Invoke_GetAddresses();
//..
if(funcidx("TEST_OnGameModeInit") != -1)
{
return CallLocalFunction("TEST_OnGameModeInit", "");
}
return 1;
}
#if defined _ALS_OnGameModeInit
#undef OnGameModeInit
#else
#define _ALS_OnGameModeInit
#endif
#define OnGameModeInit TEST_OnGameModeInit
forward TEST_OnGameModeInit();
public OnFilterScriptInit()
{
Invoke_GetAddresses();
//..
if(funcidx("TEST_OnFilterScriptInit") != -1)
{
return CallLocalFunction("TEST_OnFilterScriptInit", "");
}
return 1;
}
#if defined _ALS_OnFilterScriptInit
#undef OnFilterScriptInit
#else
#define _ALS_OnFilterScriptInit
#endif
#define OnFilterScriptInit TEST_OnFilterScriptInit
forward TEST_OnFilterScriptInit();
//Public function for invoke
forward InvokeFunction();
public InvokeFunction()
{
new Float:fVar;
new Var[ 256 ];
new iVar;
// a_samp.inc
SendClientMessage(0, 0, "");
SendClientMessageToAll(0, "");
SendDeathMessage(0, 0, 0);
GameTextForAll("", 0, 0);
GameTextForPlayer(0, "", 0, 0);
GetTickCount();
GetMaxPlayers();
SetGameModeText("");
SetTeamCount(0);
AddPlayerClass(0, 0.0, 0.0, 0.0, 0.0, 0, 0, 0, 0, 0, 0);
AddPlayerClassEx(0, 0, 0.0, 0.0, 0.0, 0.0, 0, 0, 0, 0, 0, 0);
AddStaticVehicle(0, 0.0, 0.0, 0.0, 0.0, 0, 0);
AddStaticVehicleEx(0, 0.0, 0.0, 0.0, 0.0, 0, 0, 0);
AddStaticPickup(0, 0, 0.0, 0.0, 0.0);
ShowNameTags(0);
ShowPlayerMarkers(0);
GameModeExit();
SetWorldTime(0);
GetWeaponName(0, Var, sizeof( Var ) );
EnableTirePopping(0);
AllowInteriorWeapons(0);
SetWeather(0);
SetGravity(0.0);
AllowAdminTeleport(0);
SetDeathDropAmount(0);
CreateExplosion(0.0, 0.0, 0.0, 0, 0.0);
//SetDisabledWeapons();
EnableZoneNames(0);
IsPlayerAdmin(0);
Kick(0);
Ban(0);
SendRconCommand("");
ShowPlayerDialog(0,0,0,"lol","lol","lol","lol");
// a_players.inc
SetSpawnInfo(0, 0, 0, 0.0, 0.0, 0.0, 0.0, 0, 0, 0, 0, 0,0);
SpawnPlayer(0);
SetPlayerPos(0, 0.0, 0.0, 0.0);
// SetPlayerPosFindZ(0, 0.0, 0.0, 0.0);
GetPlayerPos(0, fVar, fVar, fVar);
SetPlayerFacingAngle(0,0.0);
GetPlayerFacingAngle(0,fVar);
SetPlayerInterior(0,0);
GetPlayerInterior(0);
SetPlayerHealth(0, 0.0);
GetPlayerHealth(0, fVar);
SetPlayerArmour(0, 0.0);
GetPlayerArmour(0, fVar);
SetPlayerAmmo(0, 0,0);
GetPlayerAmmo(0);
SetPlayerTeam(0,0);
GetPlayerTeam(0);
SetPlayerScore(0,0);
GetPlayerScore(0);
SetPlayerColor(0,0);
GetPlayerColor(0);
SetPlayerSkin(0,0);
GivePlayerWeapon(0, 0,0);
ResetPlayerWeapons(0);
GetPlayerWeaponData(0, 0, iVar, iVar );
GivePlayerMoney(0,0);
ResetPlayerMoney(0);
SetPlayerName(0, "");
GetPlayerMoney(0);
GetPlayerState(0);
GetPlayerIp(0, Var, sizeof( Var ));
GetPlayerPing(0);
GetPlayerWeapon(0);
GetPlayerKeys(0,iVar,iVar,iVar);
GetPlayerName(0, Var, sizeof( Var ));
PutPlayerInVehicle(0, 0,0);
GetPlayerVehicleID(0);
RemovePlayerFromVehicle(0);
TogglePlayerControllable(0,0);
PlayerPlaySound(0, 0, 0.0, 0.0,0.0);
SetPlayerCheckpoint(0, 0.0, 0.0, 0.0,0.0);
DisablePlayerCheckpoint(0);
SetPlayerRaceCheckpoint(0, 0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0,0.0);
DisablePlayerRaceCheckpoint(0);
SetPlayerWorldBounds(0,0.0,0.0,0.0,0.0);
SetPlayerMarkerForPlayer(0, 0,0);
ShowPlayerNameTagForPlayer(0, 0,0);
SetPlayerMapIcon(0, 0, 0.0, 0.0, 0.0, 0,0);
RemovePlayerMapIcon(0,0);
SetPlayerCameraPos(0,0.0, 0.0, 0.0);
SetPlayerCameraLookAt(0, 0.0, 0.0, 0.0);
SetCameraBehindPlayer(0);
AllowPlayerTeleport(0,0);
IsPlayerConnected(0);
IsPlayerInVehicle(0,0);
IsPlayerInAnyVehicle(0);
IsPlayerInCheckpoint(0);
IsPlayerInRaceCheckpoint(0);
SetPlayerTime(0, 0,0);
TogglePlayerClock(0,0);
SetPlayerWeather(0,0);
GetPlayerTime(0,iVar,iVar);
SetPlayerVirtualWorld(0,0);
GetPlayerVirtualWorld(0);
// a_vehicle.inc
CreateVehicle(0,0.0,0.0,0.0,0.0,0,0,0);
DestroyVehicle(0);
GetVehiclePos(0,fVar,fVar,fVar);
SetVehiclePos(0,0.0,0.0,0.0);
GetVehicleZAngle(0,fVar);
SetVehicleZAngle(0,0.0);
SetVehicleParamsForPlayer(0,0,0,0);
SetVehicleToRespawn(0);
LinkVehicleToInterior(0,0);
AddVehicleComponent(0,0);
ChangeVehicleColor(0,0,0);
ChangeVehiclePaintjob(0,0);
SetVehicleHealth(0,0.0);
GetVehicleHealth(0,fVar);
AttachTrailerToVehicle(0, 0);
DetachTrailerFromVehicle(0);
IsTrailerAttachedToVehicle(0);
GetVehicleModel(0);
SetVehicleNumberPlate(0,"");
SetVehicleVirtualWorld(0,0);
GetVehicleVirtualWorld(0);
ApplyAnimation(0,"","",1.0,0,0,0,0,0);
// a_objects.inc
CreateObject(0,0.0,0.0,0.0,0.0,0.0,0.0);
SetObjectPos(0,0.0,0.0,0.0);
GetObjectPos(0,fVar,fVar,fVar);
SetObjectRot(0,0.0,0.0,0.0);
GetObjectRot(0,fVar,fVar,fVar);
IsValidObject(0);
DestroyObject(0);
MoveObject(0,0.0,0.0,0.0,0.0);
StopObject(0);
CreatePlayerObject(0,0,0.0,0.0,0.0,0.0,0.0,0.0);
SetPlayerObjectPos(0,0,0.0,0.0,0.0);
GetPlayerObjectPos(0,0,fVar,fVar,fVar);
GetPlayerObjectRot(0,0,fVar,fVar,fVar);
SetPlayerObjectRot(0,0,0.0,0.0,0.0);
IsValidPlayerObject(0,0);
DestroyPlayerObject(0,0);
MovePlayerObject(0,0,0.0,0.0,0.0,0.0);
StopPlayerObject(0,0);
// Menu's
CreateMenu("", 0, 0.0, 0.0, 0.0, 0.0);
DestroyMenu(Menu:0);
AddMenuItem(Menu:0, 0, "");
SetMenuColumnHeader(Menu:0, 0, "");
ShowMenuForPlayer(Menu:0, 0);
HideMenuForPlayer(Menu:0, 0);
IsValidMenu(Menu:0);
DisableMenu(Menu:0);
DisableMenuRow(Menu:0,0);
// Textdraw
TextDrawCreate(0.0,0.0,"");
TextDrawDestroy(Text:0);
TextDrawLetterSize(Text:0, 0.0,0.0);
TextDrawTextSize(Text:0, 0.0,0.0);
TextDrawAlignment(Text:0, 0);
TextDrawColor(Text:0,0);
TextDrawUseBox(Text:0, 0);
TextDrawBoxColor(Text:0, 0);
TextDrawSetShadow(Text:0, 0);
TextDrawSetOutline(Text:0, 0);
TextDrawBackgroundColor(Text:0,0);
TextDrawFont(Text:0, 0);
TextDrawSetProportional(Text:0, 0);
TextDrawShowForPlayer(0, Text:0);
TextDrawHideForPlayer(0, Text:0);
TextDrawShowForAll(Text:0);
TextDrawHideForAll(Text:0);
// Others
funcidx("");
gettime(iVar,iVar,iVar);
getdate(iVar,iVar,iVar);
tickcount(iVar);
return 1;
}
#include "SDK\amx\amx.h"
#include "SDK\plugincommon.h"
#include "Invoke.h"
#include <string>
#include <vector>
#include <cstdlib>
#include <ctime>
typedef void (*logprintf_t)(char* format, ...);
logprintf_t logprintf;
extern void *pAMXFunctions;
using namespace std;
//This function demonstrates: how to get a string (and its length) from PAWN.
//PAWN native: native PrintPawnString(const str[]);
cell AMX_NATIVE_CALL PrintPawnString(AMX* amx, cell* params)
{
int
len = NULL,
ret = NULL;
cell *addr = NULL;
//Get the address of our string param (str) and then get its length
amx_GetAddr(amx, params[1], &addr);
amx_StrLen(addr, &len);
//if the length of input isnt 0
if(len)
{
//We increase len because we want to make room for the terminating null char when we allocate memory.
//Also because GetString's size parameter counts the null chracter, we have to specify the length
//of the string + 1; otherwise our string will be truncated to make room for the null char (we'd lose 1 character).
len++;
//Allocate memory to hold our string we're passing (str) and then "get" the string using our allocated memory to store it.
char* text = new char[ len ];
amx_GetString(text, addr, 0, len);
//Use logprintf to print out string (text). We dont use std::cout because it doesnt write to the server log (only the window).
logprintf(text);
//Deallocate our memory...
delete[] text;
}
return 1;
}
//This function demonstrates: How to call a callback that is in a PAWN script.
//PAWN native: native EmitPawnCallback();
cell AMX_NATIVE_CALL EmitPawnCallback(AMX* amx, cell* params)
{
int idx;
const cell var = 3;
const cell arr[] = {100, 4, 33};
const string str = "Some random message from C++.";
//Pawn callback: forward OnPawnCallbackEmitted(var, arr[], str[]);
//Find our callback and store its place in the public function table (it's index) into our idx var.
if(!amx_FindPublic(amx, "OnPawnCallbackEmitted", &idx))
{
cell
ret,
addr;
//Here we push our arguments to our function. Note that if the function has multiple arguments you have to push your
//values in reverse order! Thats why we're pushing the string first, then the array, and finally our integer.
amx_PushString(amx, &addr, NULL, str.c_str(), NULL, NULL);
//amx_PushArray(amx, NULL, NULL, arr, sizeof(arr) / sizeof(cell));
cell
amx_addr,
*phys_addr;
//For some reason amx_PushArray seems to be crashing the server, and i have NO idea why. My usage should be completely
//valid judging from the implementers guide, and the code itself. Since the function isnt working we'll have to
//use the old method and allocate the memory, set it, and push it all ourselves. This is pretty straight forward. We
//allocate memory on the heap using amx_Allot (this returns 2 addresses- one of the location in the abstract machine
//(amx_addr), and one relative to the actual server's address space (phsy_addr - which we can use in C++)). Once the
//memory is allocated we use memcpy to copy the memory from our array to our phys_addr address location.
amx_Allot(amx, sizeof(arr) / sizeof(cell), &amx_addr, &phys_addr);
memcpy(phys_addr, arr, sizeof(arr));
amx_Push(amx, amx_addr);
//Push our integer value
amx_Push(amx, var);
//Execute our function using our previously obtained idx var.
//Note: This function's second parameter is what the callback returned (Can be NULL if you're not interested in return values).
amx_Exec(amx, &ret, idx);
//Release our memory that we allocated. The function amx_Alloc allocates memory on the heap in the abstract machine.
//The functions amx_PushString and amx_PushArray both use this function internally so you have to release the memory every time
//you use one of those functions. NOTE: We used both amx_PushString and amx_PushArray, and yet we only have ONE release call.
//This is because memory on the heap is allocated in ascending order! amx_Release release all the memory above a certain point
//(the second parameter, amx_addr - which is our addr variable). Since it does this we ONLY store the address from the amx_PushString
//call, as it'll delete everything from that point on.
amx_Release(amx, addr);
//Print the return value (for completeness).
logprintf("EmitPawnCallback NOTE: OnPawnCallbackEmitted callback returned %d!", ret);
}
return 1;
}
//This function demonstrates: an alternative method to getting strings from pawn- and the possible risks that come with it.
//PAWN native: native PrintPawnString2(const str[]);
cell AMX_NATIVE_CALL PrintPawnString2(AMX* amx, cell* params)
{
//This method is NOT recomended as the amx_StrParam macro uses the alloca function which is NOT a standard in C, OR C++.
//Using this method comes with risks of overflowing the stack (If you allocate large amounts of memory) and also
//gives you the risk of bugs (this function is machine AND compiler dependent- some implementations are said to be bugged).
char* text = NULL;
amx_StrParam(amx, params[1], text);
//Check if text is null
if(text != NULL)
{
//Use logprintf to print out string (text). We dont use std::cout because it doesnt write to the server log (only the window).
logprintf(text);
}
return 1;
}
//This function demonstrates: how to modify a PAWN string.
//PAWN native: native SetPawnString(str[], len = sizeof(str));
cell AMX_NATIVE_CALL SetPawnString(AMX* amx, cell* params)
{
const string message = "This is a string from C/++!!";
cell* addr = NULL;
//Get the address of our string parameter (str) and store our message
amx_GetAddr(amx, params[1], &addr);
amx_SetString(addr, message.c_str(), 0, 0, params[2]);
return 1;
}
//This function demonstrates: how to cast a float to a PAWN float.
//PAWN native: native Float:ReturnPawnFloatVal();
cell AMX_NATIVE_CALL ReturnPawnFloatVal(AMX* amx, cell* params)
{
//Since PAWN is a typeless language it stores everything as a 32bit integer and relies on tags to handle special data.
//A floating point number is no exception to this; It's a still 32bit int, but it has a Float tag to show that it shouldnt
//be treated like an regular integer. So how do we convert a float to an 32bit integer (for PAWN) without losing data?
//The answer is the amx_ftoc macro!
//The macro amx_ftoc type casts a float into a cell while preserving its bit pattern (amx_ctof does the inverse).
const float f = 22.624f;
return amx_ftoc(f);
}
//This function demonstrates: How to pass parameters by reference.
//PAWN native: native SetPawnReferenceVars(&value1, &Float:value2);
cell AMX_NATIVE_CALL SetPawnReferenceVars(AMX* amx, cell* params)
{
const int val = 65;
const float val2 = 84.54f;
cell* addr[2] = {NULL, NULL};
//Get the addresses of "value1" and "value2"
amx_GetAddr(amx, params[1], &addr[0]);
amx_GetAddr(amx, params[2], &addr[1]);
//Dereference our pointers and assign our values. Remember to ALWAYS use the macro "amx_ftoc" to convert floats into
//cells (the appropriate float format for PAWN)!
*addr[0] = val;
*addr[1] = amx_ftoc(val2);
return 1;
}
//This function demonstrates: how to get and modify array values.
//PAWN native: native PrintPawnArray(arr[], size = sizeof(arr));
cell AMX_NATIVE_CALL PrintPawnArray(AMX* amx, cell* params)
{
//Make sure there's something to print...
if(params[2] > 0)
{
cell* addr = NULL;
//Get the address of the first value in our PAWN array.
amx_GetAddr(amx, params[1], &addr);
for(int i = 0, l = params[2]; i < l; i++)
{
//This is pretty straight forward: We dereference the addr pointer to get our value to print.
//You should know this already, but arrays and pointers are almost the same thing, so we can use pointer
//arithmetic to add an offset OR just use the subscript operator (in the end *(addr+1) and addr[1] are the same).
logprintf("arr[%d] = %d", i, *(addr + i));
//If you wanted to modify the array you would just change its value by dereferencing addr and assigning a new value.
//You should know this as well, im just adding it in for completeness. Here we change the first value of our array
//to 5 (Note: Since its the first value, no offset it used).
// *(addr) = 5;
}
}
return 1;
}
//This function demonstrates: setting up invoke (fetching the addresses of our natives).
//PAWN native: native Invoke_GetAddresses();
cell AMX_NATIVE_CALL Invoke_GetAddresses(AMX* amx, cell* params)
{
return g_Invoke->getAddresses();
}
//This function demonstrates: how to use invoke to call SA-MP natives.
//PAWN native: native WhereIsPlayer(playerid);
cell AMX_NATIVE_CALL WhereIsPlayer(AMX* amx, cell* params)
{
float
x = NULL,
y = NULL,
z = NULL;
//Get the player's position (and check to see if he is even connected).
if(g_Invoke->callNative(&PAWN::GetPlayerPos, params[1], &x, &y, &z))
{
char name[24];
//Get the rest of the player's information (name, interior, and virtualworld) and print it.
g_Invoke->callNative(&PAWN::GetPlayerName, params[1], name);
int interior = g_Invoke->callNative(&PAWN::GetPlayerInterior, params[1]);
int virtualworld = g_Invoke->callNative(&PAWN::GetPlayerVirtualWorld, params[1]);
logprintf("%s is at X: %.2f, Y: %.2f, Z: %.2f (Virtual world: %d, Interior %d).", name, x, y, z, virtualworld, interior);
return 1;
}
return 0;
}
//This function demonstrates: how to write a native to act as a callback.
//PAWN native: native TEST_Hook_OnPlayerConnnect(playerid);
cell AMX_NATIVE_CALL TEST_Hook_OnPlayerConnnect(AMX* amx, cell* params)
{
//Get the players name
char name[24];
g_Invoke->callNative(&PAWN::GetPlayerName, params[1], name);
//Check if his name is "Mario".
if(string("Mario") == name)
{
//If it is send our funny little message and kick him.
g_Invoke->callNative(&PAWN::SendClientMessage, params[1], 0xFFFFFFFF, "Sorry {FF0000}Mario, {FFFFFF}but your princess is in another server.");
g_Invoke->callNative(&PAWN::Kick, params[1]);
}
return 1;
}
//This function tells the server what capabilities our plugin will have based on what it returns. Generally we only use 3 support flags
//in plugins: SUPPORTS_VERSION, SUPPORTS_AMX_NATIVES, and SUPPORTS_PROCESS_TICK.
PLUGIN_EXPORT unsigned int PLUGIN_CALL Supports()
{
//Note: If you're using the ProccessTick function, remember to export it in the .def file!
return SUPPORTS_VERSION | SUPPORTS_AMX_NATIVES | SUPPORTS_PROCESS_TICK;
}
//The Load function is pretty straight forward. This is called when the plugin is loaded and gets passed an array of addresses that the plugin
//will use to function. The two indexes we typically use are PLUGIN_DATA_AMX_EXPORTS, and PLUGIN_DATA_LOGPRINTF.
PLUGIN_EXPORT bool PLUGIN_CALL Load(void **ppData)
{
//allocate memory for out g_Invoke instance
g_Invoke = new Invoke;
//Assign the addresses of our AMX function table/logprintf function to their corasponding pointers.
pAMXFunctions = ppData[PLUGIN_DATA_AMX_EXPORTS];
logprintf = (logprintf_t) ppData[PLUGIN_DATA_LOGPRINTF];
logprintf("* Test plugin was loaded.");
return true;
}
//Unload is called when the plugin is unloaded (server shutdown).
PLUGIN_EXPORT void PLUGIN_CALL Unload()
{
logprintf("* Test plugin was unloaded.");
}
//Our array of native info for amx_Register (function name and address).
AMX_NATIVE_INFO PluginNatives[] =
{
{"PrintPawnString", PrintPawnString},
{"PrintPawnString2", PrintPawnString2},
{"SetPawnString", SetPawnString},
{"ReturnPawnFloatVal", ReturnPawnFloatVal},
{"SetPawnReferenceVars", SetPawnReferenceVars},
{"PrintPawnArray", PrintPawnArray},
{"EmitPawnCallback", EmitPawnCallback},
{"WhereIsPlayer", WhereIsPlayer},
{"Invoke_GetAddresses", Invoke_GetAddresses},
{"TEST_Hook_OnPlayerConnnect", TEST_Hook_OnPlayerConnnect},
{0, 0}
};
//This function is called when a new AMX instance is loaded into the server. This will be called for every filterscript/gamemode! Because of this it isnt
//a good idea to store a single AMX instance for the entire plugin, instead use a queue/list/vector. In this function we also register our custom
//native functions we wish to provide PAWN with.
PLUGIN_EXPORT int PLUGIN_CALL AmxLoad( AMX *amx )
{
//Any time a script is loaded we want to add it to invoke's AMX list, so we push back the list with the instance.
g_Invoke->amx_list.push_back(amx);
return amx_Register(amx, PluginNatives, -1);
}
//This function is called when every an AMX instance is unloaded. If you store AMX instances, make sure you remove them. Otherwise you'll have instances to
//non-existing gamemodes/filterscripts.
PLUGIN_EXPORT int PLUGIN_CALL AmxUnload( AMX *amx )
{
//Every script that is unloaded needs to get removed from our AMX instance list. So we iterate through our list and find the instance that is being
//unloaded, and we earase it from the list.
for(list<AMX *>::iterator i = g_Invoke->amx_list.begin(); i != g_Invoke->amx_list.end(); ++i)
{
if(*i == amx)
{
g_Invoke->amx_list.erase(i);
break;
}
}
return AMX_ERR_NONE;
}
//ProcessTick is a function that gets called on every iteration of the server's loop. People generally use this function as a method of managing time by
//keeping track of the amount of ticks that have passed. The SA-MP server is said to have a sleep time of 5ms, so if 50 ticks go by you have an idea of
// the elapsed time (5 * 50 = 250ms). Note: Anyone who uses threads in their plugins and require PAWN interaction needs to use this function to ensure
//PAWN isnt busy doing another task!
PLUGIN_EXPORT void PLUGIN_CALL ProcessTick()
{
static int tick = 0;
const int NYAN_COUNT = 100;
tick++;
//Use the modulo operator to see if NYAN_COUNT ticks have passed (100 tick. 5ms sleep time * 100 ticks = 500ms).
if(!(tick % NYAN_COUNT))
{
//If 100 ticks have passed send our funny little message to everyone.
const char nyan[] = "{FF0000}NYAN {FFA500}NYAN {FFFF00}NYAN {00FF00}NYAN {0000FF}NYAN {551A8B}NYAN";
g_Invoke->callNative(&PAWN::SendClientMessageToAll, 0, nyan);
}
}
Nice one! Hopefully this will get me started with plugin development a bit.
![]() |
@BigETI - Make sure your windows file structure matches the filters, or you'll have to change those include lines and omit the folders in the path. I'll make a note of this in the guide later when im more awake :P.
|
Please can you show me how to locate the header files properly? Because I wasn't able to do this even by reading tutorials for dummies O_o (Or my MVC++E got bugged for any reasons)
|
Ok, i'll help you out via PM. To be honest though this is something you should REALLY know how to do.
|
You should consider extending beyond the basics - I figured this all out already, after messing with the SDK. Otherwise, I'm sure this will be helpful - I'd rather have read a tutorial like this (or RyDeR`'s) instead of spending ages playing with the SDK functions.
|