AlliedModders

AlliedModders (https://forums.alliedmods.net/index.php)
-   Code Snippets/Tutorials (https://forums.alliedmods.net/forumdisplay.php?f=83)
-   -   [TUT] Plugin API (https://forums.alliedmods.net/showthread.php?t=41241)

Hawk552 07-11-2006 17:16

[TUT] Plugin API
 
Before you get into this, please note that this tutorial assumes you are intermediate level in Pawn or higher. Beginners should avoid this as it may confuse them or not make sense.

API stands for application programming interface. But what is an API? Basically it's an interface that allows multiple programs/scripts to trade data and perform the same functions without having direct access to each other.

There is API functionality in AMXX plugins, implemented through a set of natives. The following natives, built into the core, allow API capabilities:
  • CreateMultiForward
  • CreateOneForward
  • ExecuteForward
  • PrepareArray
  • DestroyForward

But what are they used for? Well, let's take a look at an example and then pull it apart to figure out how it works. Please keep in mind that an API is designed for multiple plugins, so every example will be 2 individual scripts instead of 1 (although it's possible to put them both in one)

Code:
#include <amxmodx> new Float:g_flDelay public plugin_init() {     register_plugin("API Test - Core","1.0","Hawk552")         g_flDelay = random_float(0.1,0.5)     set_task(g_flDelay,"fnDoForward") } public fnDoForward() {     new iForward = CreateMultiForward("plugin_init_delay",ET_IGNORE,FP_FLOAT),iReturn     if(iForward < 0)         return log_amx("Forward could not be created.")         if(!ExecuteForward(iForward,iReturn,g_flDelay))         return log_amx("Could not execute forward.")             return DestroyForward(iForward) }

Code:
#include <amxmodx> public plugin_init()     register_plugin("API Test - Attachment","1.0","Hawk552")     public plugin_init_delay(Float:flDelay)     log_amx("Forward executed %f seconds after plugin_init.",flDelay)

Now, let's pick these two apart.

In the first script, the important part starts at fnDoForward. What we see is:

Code:
new iForward = CreateMultiForward("plugin_init_delay",ET_IGNORE,FP_FLOAT)

CreateMultiForward and CreateOneForward (which will be addressed later) return handles that can be used for ExecuteForward and DestroyForward.
  • NOTE: Difference between CreateMultiForward and CreateOneForward: multi accepts a return value and you must specify upon which condition it'll stop. Multi also sends to all plugins that have a public function after the name of const name[], as shown below. CreateOneForward requires a plugin id, and automatically ignores the return value (although you can still pass it byref)

But as for the parameters:

const name[] - this is the name of the public function you want to call from whatever plugin(s) specified
stop_type - this is what the plugin should obey in terms of stopping. For example, if you register a client command "kill" and then return PLUGIN_HANDLED, it'll stop right there. This parameter allows you to specify whether or not it will stop there, and whether or not the return value from plugins called matters. The possibilities are here:

Code:
#define ET_IGNORE        0    //ignore return val #define ET_STOP            1    //stop on PLUGIN_HANDLED #define ET_STOP2        2    //same, except return biggest #define ET_CONTINUE        3    //no stop, return biggest

In this case, we use ET_IGNORE because plugin_init never stops calling, despite what plugins return (which is why it's useless to return PLUGIN_CONTINUE/_HANDLED in plugin_init), and we want to duplicate that functionality.

... - This is where the parameters of the header of the function called should be specified. In the above example, FP_FLOAT was specified. This is to let the AMXX core know that we want to send a floating point int to the functions called.

Here are the possibilities for the previous section (I will explain how to pass an array/string later):

Code:
#define FP_CELL            0 #define FP_FLOAT        1 #define FP_STRING        2 #define FP_ARRAY        4

Next we check if iForward > 0, or if it's not we stop there and inform the server console. As said in the funcwiki, "Results will be > 0 for success.".

Next, we execute the forward using ExecuteForward. Picking this function apart:

forward_handle - this is the forward to call. It's the handle returned from CreateMultiForward / CreateOneForward.

&ret - this is the return value, passed by reference. It is effectively the value that the function being called returns (ex. return PLUGIN_HANDLED -> &ret == 1) It usually is effected by the stop_type in CreateMultiForward.

... - this is the param(s) where you can input the data that will be passed onto the function header for the function being called. In the example above, g_flDelay is passed and in the second plugin, the plugin_init_delay function recieves it in the header as Float:flDelay. NOTE: You can theoretically have infinite parameters, but they must match with the types passed into CreateOneForward / CreateMultiForward.

If ExecuteForward returns 0 (false) then we stop there and inform the server of this error. Otherwise, we continue onward to DestroyForward.

The next part we find is DestroyForward. The functionality for this is quite obvious, and can be used on any forward but should be used by the time plugin_end is called (or the FM forward for server shutting down) otherwise memory leaks can occur.

Now, what was the point of that? Not really much, that was pretty damn useless. Here's something a little more useful:

Code:
#include <amxmodx> #include <fakemeta> new g_iForward new g_iReturn public plugin_init() {     register_plugin("API Test 2 - Core","1.0","Hawk552")         g_iForward = CreateMultiForward("client_PreThink",ET_IGNORE,FP_CELL)     if(g_iForward < 0)         log_amx("Error creating forward")             register_forward(FM_PlayerPreThink,"fnForwardPlayerPreThink")     register_forward(FM_Sys_Error,"fnForwardSysError") } public fnForwardPlayerPreThink(id)     if(!ExecuteForward(g_iForward,g_iReturn,id))         log_amx("Could not execute forward")         public fnForwardSysError()     plugin_end()     public plugin_end()     DestroyForward(g_iForward)

Code:
#include <amxmodx> public plugin_init()     register_plugin("API Test - Attachment 2","1.0","Hawk552")     public client_PreThink(id)     log_amx("PreThink called on %d",id)

We just allowed a plugin to use client_PreThink using fakemeta without having to even create the fakemeta forward in the other plugin. On top of this, if we added another plugin to the API, it would call client_PreThink for that one too. Be careful though, it'll call it twice if you have engine included.

But one thing remains unanswered: how do you pass an array/string?

A special native has been implemented for this, PrepareArray. Here's an example of how to use it:

Code:
#include <amxmodx> #include <fakemeta> new g_iForward new g_iReturn public plugin_init() {     register_plugin("API Test 2 - Core","1.0","Hawk552")         g_iForward = CreateMultiForward("client_PreThink",ET_IGNORE,FP_ARRAY)     if(g_iForward < 0)         log_amx("Error creating forward")             register_forward(FM_PlayerPreThink,"fnForwardPlayerPreThink")     register_forward(FM_Sys_Error,"fnForwardSysError") } public fnForwardPlayerPreThink(id) {     new iRand = random(5),iArray[2]     iArray[0] = id     iArray[1] = iRand         new iArrayPass = PrepareArray(iArray,2,0)         if(!ExecuteForward(g_iForward,g_iReturn,iArrayPass))         log_amx("Could not execute forward") }         public fnForwardSysError()     plugin_end()     public plugin_end()     DestroyForward(g_iForward)

Code:
#include <amxmodx> public plugin_init()     register_plugin("API Test - Attachment 2","1.0","Hawk552")     public client_PreThink(iArray[2])     log_amx("PreThink called on %d, random value is %d",iArray[0],iArray[1])

Regardless of the fact we could use 2 cells instead of an array, this allows client_PreThink to have a random value from 0-5 packed into the parameters (which is quite useless, again this is just an example).

Now, to pick this apart:

We created an array and then packed it with data. You should know how to already do this.

We then use:

Code:
new iArrayPass = PrepareArray(iArray,2,0)

The usage of PrepareArray is as follows:

array[] - this is the array to be prepared
size - this is the amount of cells in the array (the same amount as when declaring it, not the highest cell)
copyback=0 - this is whether or not changing the array will result in the calling function's array being changed as well. This defaults to 0.

PrepareArray returns a handle as well that can be passed into ExecuteForward under the param of FP_ARRAY or FP_STRING. The difference between these two is that FP_STRING stops reading at the null terminator (and does not need to be prepared using PrepareArray), but FP_ARRAY must be prepared and only stops reading at the "size" cell.

Well, there you have it, if you have any questions or think a statement here is wrong, please post.

Cheap_Suit 07-11-2006 19:14

Re: Plugin API
 
Your very resourcefull Hawk552 :). Thanks for the tutoral.

VEN 08-24-2006 09:02

Re: Plugin API
 
Quote:

until ven gets done with the xp tutoriol
I've never worked on the XP tutorial.

Lord_Destros 08-24-2006 16:34

Re: Plugin API
 
1. Your banned again so this post may be pointless :lol:
2. I think your thinking of v3x

Xanimos 08-24-2006 20:32

Re: Plugin API
 
Speaking of v3x I havn't seen him in a long while. How about you guys?

[EDIT] Yep, hes been missing for over a month now....

Quote:

Originally Posted by v3x's Profile
Last Activity: 07-19-06 02:20 PM


Hawk552 08-25-2006 11:25

Re: Plugin API
 
Quote:

Originally Posted by gangstatothecore (Post 372564)
maybee it was v3x . I could have sworn that it was VEN tht was going to make an xp tutoiol to replace the old one :o

VEN is really beyond that stuff I think, he's more into the fakemeta hardcore stuff.

Hawk552 08-25-2006 20:31

Re: Plugin API
 
Quote:

Originally Posted by nver_been_hit (Post 373090)
OMG did you just diss VEN ? lol. Hey hawk , I was wondering can the natives be used to return wether a plugin is on or off and if it could return cvars as values ? And So i should use the API in my core plugin to comunicate with the other supporting plugins? then have the supporting plugins use natives ?

No, I did not insult VEN, in fact that was more of a compliment.

Why not just use a name for a cvar and then a stock to check whether it's on or off? I suppose you could make a native that other plugins can use to check if your plugin is off, but if it has a cvar it's really better to avoid API / native functions.

Hawk552 08-25-2006 20:55

Re: Plugin API
 
Quote:

Originally Posted by nver_been_hit (Post 373098)
Ok well my cvars arnt for turning the plugins on and off . I hate cvars for that , there to muc of a pain in the ass . my cvars are in my core plugin , they hold values of the ranks , how many kills per rank can be changed by cvar. and i have a menu that has all the info and help of my whole mod . So im going to have my menu , get the wether a plugin is on or off , and the cvars . And if there on or off , it wil or wont display that part of the menu . so if my artilary strike plugin is off , the menu wont display it . also my ranks and values are in the menu too , so i want it to get the cvar values and display the current settings for the ranks . so my menu will be self suficiant auto detecting menu :)

Well what you can do is something like:

Code:
// ... register_native("BF_PointerPrivateKills","_BF_PointerPrivateKills") // ... public _BF_PointerPrivateKills()    return p_PrivateKills // or whatever you call the pointer to the cvar for kills that a private must get

Then in another plugin

Code:
if(get_pcvar_num(BF_PointerPrivateKills()) > 15) // do something

Your question is kinda unclear, but I think that's what you want. You can do the same thing with any cvar like that.

Hawk552 08-25-2006 21:09

Re: Plugin API
 
Quote:

Originally Posted by nver_been_hit (Post 373101)
ok well heres my core plugin (in beta stages now)www.ampaste.net/3390and heres my menu www.ampaste.net/3391 i want them to get values from my core , and return the cvar to display in my menu . also display only items that are on . because it creates some bit of lag and havoc when they do comands and the plugins arnt on . plus its anoying having to tel people that they arnt on 50 milion times day . so i want my menu to be auto detecting.:)

Both of those give errors for me.

Hawk552 08-25-2006 21:35

Re: Plugin API
 
uhh
  1. //battle field mod is not an open source plugins . You do not have permision to alter this code , or you will be sued . All rights are reserverd for Jared "RapHero2000" Chase//
  2. // Jared Chase 6-24-2006 ///
good luck, I'm not helping


All times are GMT -4. The time now is 20:00.

Powered by vBulletin®
Copyright ©2000 - 2024, vBulletin Solutions, Inc.