This site is a testing version, but all data is shared with the live forum.


Raised This Month: $ Target: $400
 0% 

[TUT] Dynamic / Fake Natives


  
 
 
Thread Tools Display Modes
Prev Previous Post   Next Post Next
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
 



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 23:55.


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