Raised This Month: $32 Target: $400
 8% 

[TUT] Dynamic / Fake Natives


Post New Thread Reply   
 
Thread Tools Display Modes
Author Message
Hawk552
AMX Mod X Moderator
Join Date: Aug 2005
Old 07-11-2006 , 20:43   [TUT] Dynamic / Fake Natives
Reply With Quote #1

Please be aware that this tutorial is geared toward intermediate or higher level scripters. If you are a beginner, this may confuse you or make no sense.

Dynamic / Fake Natives (I will refer to them as dynamic from here onward, I don't like the word fake when they actually do something) allow you to have the functionality of a module (through natives) by having a plugin that acts like a module running. In theory, it is possible to port entire modules to fakemeta + plugins using this principle (fun has already been done, engine can be done with more difficulty). From my experiences, dynamic natives go hand and hand with API design as shown in my "Plugin API" tutorial.

Here are the natives that are used along for dynamic natives:
  • register_native
  • register_library
  • get_param
  • get_param_byref
  • get_param_f
  • get_string
  • get_array
  • get_array_f
  • set_array
  • set_array_f
  • set_string
  • param_convert

Please keep in mind that dynamic natives are pretty useless when not dealing with more than one plugin. As such, the following examples will always be at least 2 scripts, plus one include file.

Here is a simple example to show the basics of dynamic natives:

Code:
#include <amxmodx> #include <fun> public plugin_init()     register_plugin("Dynamic Native Test - Handler","1.0","Hawk552")     public plugin_natives() {     register_library("dyn_test")         register_native("half_user_hp","_half_user_hp") } public _half_user_hp(iPlugin,iParams) {     if(iParams != 1)         return PLUGIN_CONTINUE             new id = get_param(1)     if(!id)         return PLUGIN_CONTINUE             set_user_health(id,get_user_health(id) / 2)         return PLUGIN_HANDLED }

Code:
#include <amxmodx> #include <dyn_test> public plugin_init()     register_plugin("Dynamic Native Test - Caller","1.0","Hawk552")     public client_putinserver(id)     if(is_user_alive(id))         half_user_hp(id)

Code:
// NOTE: This file MUST be called dyn_test.inc (or you have to rename the #include <dyn_test> in script 2) #pragma reqlib "dyn_test" native half_user_hp(id)

Now, what is this doing? Basically, on client_putinserver, if the user is alive (which is only true in mods like sven coop where you spawn right away) then it will slice their HP in half. Let's pick apart each thing.

Script 1

At first we look at the plugin_init, and we notice nothing special. We then look down and see "plugin_natives", a forward most people here have probably never seen before. This is where natives and libraries must be registered. DO NOT try to put them in other forwards, like plugin_init or plugin_precache.

Now, let's look at the first part of plugin_natives, which is register_library.

What does this do? It basically tells AMXX that this plugin provides native functionality to other plugins, and aids with "native not found" errors to check if the plugin is loaded before trying to call the dynamic native. In this case, we're calling our library "dyn_test" for "Dynamic Native Test". There are 2 types of libraries in AMXX core, as of 1.75: libraries and classes. A module is a library, while a plugin is a class. This information is useful for LibraryExists, which will be covered later.

Next, we see register_native.

const native[] - this is the name of the native to register. Pretty obvious.
const handler[] - this is the name of the function that will be called when this native is used. Remember, it must be public (as with almost all functions that are put in quotes or called by outside sources)
[ style = 0 ] - this is will be covered later

Now, as we look at the register_native call, we see the handler is _half_user_hp. So, we look down, and we see this function.

Code:
public _half_user_hp(iPlugin,iParams)

In all dynamic natives, unless registered with style=1, we see something to the effect of (id,params), which I decided to rename in the header. The first param is the plugin id - this is useful in case you want to send it back a callfunc or a CreateOneForward/ExecuteForward, or have an array that stores data for each plugin loaded. The next param is iParams. In the case of the above example, iParams must be 1, because the only param is 1. If it's anything different, someone decided to be stupid and modify the include file.

Next, inside the function we check if iParams == 1, and if it doesn't we stop there. Next, we get_param(1), which allows you to get the first parameter assuming it's a cell (which it is). We then check if id is a counting number, because we can't use 0 with get_user_health. The rest is pretty self explanatory.

In this example, I returned PLUGIN_CONTINUE because that evaluates to false, and PLUGIN_HANDLED because it evaluates to true. This allows you to do simple error checking, ie "if(!half_user_health(id)) log_amx("failed")".

Now that the handler is out of the way, we get down to the plugin that will use this dynamic native.

First, I'd like you to look at the include file (the 3rd example). The first thing we see is #pragma reqlib "dyn_test". This effectively tells AMXX "If dyn_test isn't loaded, error me right here." The register_library function in the first example is what lets you bypass this, because it tells AMXX that dyn_test does exist. Next, in this same file, we see native half_user_hp(id). This basically tells the compiler that half_user_hp will be handled in the VM layer (which is where all the C++ coding is), but the VM layer will pass it down to the function that handles the dynamic native. After it's handled, the VM layer will pass the result back to the calling function.

Onto the script itself, we see that we included this file through #include <dyn_test>. The rest is quite self explanatory.

As for the get_param section, the natives above (get_param_f, get_param_byref, etc.) can be used to replace this, depending upon what arguments are passed into the function.

Now, onto style = 1. This style basically forgets about the iPlugin,iParams part of the header, and then assumes the plugin will pass the parameters correctly. This means _half_user_hp would look like:

Code:
public _half_user_hp(id)

I personally don't like this way as you cannot guarantee that the calling function called it correctly, and also all arrays / strings / byref parameters must be converted using param_convert, however it is easier for simple natives such as the one above.

If you want to set a passed string, passed array, or passed byref parameter, use set_param, set_string, and set_array. This basically allows you to format the parameters like how format does, or in the case of set_param it allows you to set a number (like in cs_get_user_armor).

I don't really want to push this any longer so I'm just going to end it here. Like always, if you have any questions or if anything I said is wrong, feel free to post.

Last edited by Hawk552; 05-01-2010 at 17:36.
Hawk552 is offline
Send a message via AIM to Hawk552
Hawk552
AMX Mod X Moderator
Join Date: Aug 2005
Old 08-24-2006 , 09:01   Re: Dynamic / Fake Natives
Reply With Quote #2

Quote:
Originally Posted by gangsta_kid View Post
Ok well im making a mod (bfm_mod) and its pretty big , it already has 5 seperate plugins ive made in the pack . But id like them to be able to work together, and call functions from one another , instead of adding them all into one huge plugin . Would you recomend using the natives here , or the stuff from your API tutoriol ?
thanks
You really need both for a mod like that. Natives are used to inform the core plugin of new information, while API is used by the core to inform the other plugins of new information. If you need any examples, PM me (I've got something going that heavily uses this) or look at my Clan API plugin.
__________________
Hawk552 is offline
Send a message via AIM to Hawk552
Emp`
AMX Mod X Plugin Approver
Join Date: Aug 2005
Location: Decapod 10
Old 12-17-2006 , 03:12   Re: Dynamic / Fake Natives
Reply With Quote #3

in the include file it needs to be
Code:
#pragma reqlib "dyn_test"
and not
Code:
#pragma reqclass "dyn_test"
Emp` is offline
Send a message via AIM to Emp` Send a message via MSN to Emp` Send a message via Yahoo to Emp` Send a message via Skype™ to Emp`
Orangutanz
Veteran Member
Join Date: Apr 2006
Old 12-17-2006 , 08:46   Re: Dynamic / Fake Natives
Reply With Quote #4

Changed
__________________
|<-- Retired from everything Small/Pawn related -->|
You know when you've been Rango'd
Orangutanz is offline
Old 12-17-2006, 21:44
Hawk552
This message has been deleted by Brad. Reason: immaterial to thread subject
Old 12-17-2006, 22:28
Zenith77
This message has been deleted by Brad. Reason: immaterial to thread subject
Old 12-17-2006, 22:57
Hawk552
This message has been deleted by Brad. Reason: immaterial to thread subject
Drak
Veteran Member
Join Date: Jul 2005
Old 03-16-2007 , 16:07   Re: [TUT] Dynamic / Fake Natives
Reply With Quote #5

I'm a little on confused on this part:
Code:
public _half_user_hp(iPlugin,iParams) {     if(iParams != 1)         return PLUGIN_CONTINUE             new id = get_param(1)     if(!id)         return PLUGIN_CONTINUE             set_user_health(id,get_user_health(id) / 2)         return PLUGIN_HANDLED }
Is this saying that if the ID is zero, continue? And is the "iParams" needed? Or is that only showing if you were gonna add extra perimeters?
Then again, what if you wanted to use a string? Like so:
Code:
public _get_user_rpmoney(iPlugin,where[]) // 1 = Wallet 2 = Balance {     new id = get_param(1)     param_convert(2)     server_print("[AMXX] %s",where)         if(!id) return PLUGIN_CONTINUE         new money,buffer[64],authid[32];     get_user_authid(id,authid,31)     if(equal(where,"wallet")) {         select_string(id,"money","wallet","steamid",authid,buffer)         money = str_to_num(buffer)     }     if(equal(where,"balance")) {         select_string(id,"money","wallet","steamid",authid,buffer)         money = str_to_num(buffer)     }     // does the user have enough money?     /*     switch(where) {         case 1: {             select_string(id,"money","wallet","steamid",authid,buffer)             money = str_to_num(buffer)             server_print("[AMXX] Wallet called")         }         case 2: {             select_string(id,"money","balance","steamid",authid,buffer)             money = str_to_num(buffer)         }     }     */     return money; }
But this just spills out errors when used as so: get_user_rpmoney(id,"wallet")
Last thing, do dynamic natives take any performance away if being used in a large plugin?

Last edited by Drak; 03-16-2007 at 19:06.
Drak is offline
Send a message via MSN to Drak
Hawk552
AMX Mod X Moderator
Join Date: Aug 2005
Old 03-16-2007 , 19:57   Re: [TUT] Dynamic / Fake Natives
Reply With Quote #6

Quote:
Originally Posted by SixTwin View Post
stuff
iParams is necessary, and removing it from the header will cause problems if you try to check it (obviously). You can leave it out if it's never used. Also, if you're using style 1, remove the "iPlugin" parameter from the header. That's probably what's causing your problems. The other thing is that you can't use get_param in style 1. I'm still not sure what style you're using, though, since you mixed them up a lot.

Dynamic natives are extremely slow compared to any calling method other than the callfunc system, of which I'm sure it's either the same or very marginally faster. If possible, you should always use stocks or similar functions rather than dynamic native calls.
__________________
Hawk552 is offline
Send a message via AIM to Hawk552
Drak
Veteran Member
Join Date: Jul 2005
Old 03-16-2007 , 20:08   Re: [TUT] Dynamic / Fake Natives
Reply With Quote #7

Ah, thank you. I was mainly concerned performance wise due to the fact, I would be querying a DB constitly, since i would rather of used dynamic natives instead of making more DB connections.
Drak is offline
Send a message via MSN to Drak
Orangutanz
Veteran Member
Join Date: Apr 2006
Old 03-18-2007 , 12:29   Re: [TUT] Dynamic / Fake Natives
Reply With Quote #8

Dynamic Natives:
Pro:
Can use less memory if the native is used quite a few times in multiple plugins.

Con:
Slower calling time due to passing through Virtual Machine twice.

Stocks:
Pro:
Faster calling times due to passing through Virtual Machine once.

Con:
Can use more memory since the whole function is copied to each plugin that uses it.


As you can see both have a good pro but also a bad con.
I'd personally go for dynamic natives, depending on how you weigh the pro/con from the above up. I prefer less memory being taken and sacrifice the speed, since you still won't really notice any difference.
__________________
|<-- Retired from everything Small/Pawn related -->|
You know when you've been Rango'd
Orangutanz is offline
Zefir
Member
Join Date: Oct 2007
Location: Kiev, Ukraine
Old 05-01-2010 , 07:17   Re: [TUT] Dynamic / Fake Natives
Reply With Quote #9

How get caller plugin_id with dynamic style 1?
__________________

Last edited by Zefir; 05-01-2010 at 09:31.
Zefir is offline
Send a message via ICQ to Zefir
Hawk552
AMX Mod X Moderator
Join Date: Aug 2005
Old 05-01-2010 , 11:41   Re: [TUT] Dynamic / Fake Natives
Reply With Quote #10

Quote:
Originally Posted by Zefir View Post
How get caller plugin_id with dynamic style 1?
I never use style 1, so there may be a way that I'm not aware of, but I don't know of any.
__________________
Hawk552 is offline
Send a message via AIM to Hawk552
Reply


Thread Tools
Display Modes

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is On
HTML code is Off

Forum Jump


All times are GMT -4. The time now is 19:49.


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