Registering natives //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); } Getting a string and its length //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; } Alternative way to get a string I decided to add this section because i know people are going to be tempted by the simplicity of it, and i think they should know the risks and problems that can come from it. //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; } How to set a string //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; } Casting and returning floats //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); } Passing parameters by reference //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; } Getting and modding array values //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; } Call a callback //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; Calling nati...
atx150w