Raised This Month: $51 Target: $400
 12% 

[Tutorial] Communicating data across plugins (or how to use natives in general)


Post New Thread Reply   
 
Thread Tools Display Modes
Author Message
sdz
Senior Member
Join Date: Feb 2012
Old 05-27-2016 , 08:06   [Tutorial] Communicating data across plugins (or how to use natives in general)
Reply With Quote #1

Hi, I'm a bad sourcepawn programmer, and I made this because I'm tired of seeing this in plugins and not knowing what the hell I'm looking at!



Three W's:

1. When should I use this? - When your plugin looks like pasketti.

2. Why should I use this? - To organize stuff and consolidate your spaghetti into manageable partitions.

3. Where should I use this? - Anywhere you damn well please or intend to be communicating data across plugins, or exposing your functions to other people.


Ahem, so continuing on.

Well it says here on the wiki that "Natives" are and I quote:
"It is a powerful inter-plugin communication resource that can greatly enhance the functionality you provide."

So I assume we would want to start off with introducing you to some functions we'll be using:
  • CreateNative
    Code:
    native CreateNative(const String:name[], NativeCall:func);
  • ThrowNativeError
    Code:
    native ThrowNativeError(error, const String:fmt[], any:...);
  • SetNativeString
    Code:
    native SetNativeString(param, const String:source[], maxlength, bool:utf8=true, &bytes=0);
  • GetNativeCell
    Code:
    native any:GetNativeCell(param);


To begin we'll want to make our plugin (go figure?). In order to make our natives work, we'll need to use a forward that may be new to many of you: AskPluginLoad2
Code:
forward APLRes:AskPluginLoad2(Handle:myself, bool:late, String:error[], err_max);
It works just like any other forward except it actually returns something so it's not really a far stretch from the good old OnPluginLoad(). Here's how we might want begin to set stuff up.
PHP Code:
#include <sourcemod>

//We call this because OnPluginLoad() is apparently called too late. I mean just look at the name of this, it honestly looks like it belongs in here.
public APLRes:AskPluginLoad2(Handle:myselfbool:lateString:error[], err_max)
{
    
CreateNative("setAnInt"native_setAnInt);

    
RegPluginLibrary("uselessnatives.inc");
    return 
APLRes_Success;

Now inside of our uselessnatives.inc, we'll want to make a little something that looks like this:
PHP Code:
//This is double-include prevention. Idk what that does but apparently it's convention so I do this
#if defined _uselessnatives_included
 #endinput
#endif
#define _uselessnatives_included

native native_setAnInt(intvalue);
native setAString(String:set[], const String:value[], maxlen); 
This defines the prototype of our native, if we don't do this we honestly don't really have a way to use the native.

After we've done this, we can move on back to our uselessnatives.sp and write the function of our native!
Here's the callback we'll be using: NativeCall
Code:
 functag public NativeCall(Handle:plugin, numParams);
Thus we find ourself with our sp file looking a little bit like such:
PHP Code:
public native_setAnInt(Handle:pluginnumParams)
{
    
//setAnInt(int, value);
    
new thing GetNativeCell(1); //First param, integer we're going to be setting.
    
new set GetNativeCell(2); //Second param, the value we'll be setting int to.

    
thing set;
    return 
set;

Obviously there's pretty much no point to this, so let's say we wanted to do something that served a purpose, like setting a string.

PHP Code:
#include <sourcemod>

//We call this because OnPluginLoad() is apparently called too late. I mean just look at the name of this, it honestly looks like it belongs in here.
public APLRes:AskPluginLoad2(Handle:myselfbool:lateString:error[], err_max)
{
    
CreateNative("setAnInt"native_setAnInt);
    
CreateNative("setAString"native_setAString);

    
RegPluginLibrary("uselessnatives.inc");
    return 
APLRes_Success;
}

public 
native_setAString(Handle:pluginnumParams)
{
    
//setAString(String:buffer[], const String:source[], maxlen);
    //Caching our results makes everyone's life a little better, although not needed for the purpose of this tutorial.
    
new maxlen GetNativeCell(3) + 1//I don't think most people include a null terminator so we add one.

    
decl String:setString[maxlen];
    
GetNativeString(2setStringmaxlen);
    
SetNativeString(1setStringmaxlen); //This is all we need. We could return nothing or just run the return;

Now if you're thinking "Okay, I got all that but what does this even do for me?", fear not because I have an answer for you. This answer involves making a new file called uselessnatives2.sp (or whatever..) and calling our beloved natives. If you've made it this far I assume you can do that on your own, if not here's one you can rip from me. I've wrote two new plugins and natives this time to show you how it could be used in a realistic setting as far as getters & setters could be replicated.

Plugin with the native functions:
PHP Code:
#include <sourcemod>

new anInt;
new 
aString[16];

//We call this because OnPluginLoad() is apparently called too late. I mean just look at the name of this, it honestly looks like it belongs in here.
public APLRes:AskPluginLoad2(Handle:myselfbool:lateString:error[], err_max)
{
    
CreateNative("setAnInt"native_setAnInt);
    
CreateNative("setAString"native_setAString);

    
RegPluginLibrary("uselessnatives.inc");
    return 
APLRes_Success;
}

public 
native_getAString(Handle:pluginnumParams)
{
    
//getAString(String:buffer[], maxlen);
    
new maxlen GetNativeCell(2) + 1;

    
SetNativeString(1aStringmaxlen);
    return;
}

public 
native_getAnInt(Handle:pluginnumParams)
{
    return 
anInt;
}

public 
native_setAString(Handle:pluginnumParams)
{
    
//setAString(const String:set[], maxlen);
    //Caching our results makes everyone's life a little better, although not needed for the purpose of this tutorial.
    
new maxlen GetNativeCell(2) + 1//I don't think most people include a null terminator so we add one.
    
    /* If we wanted to prevent truncation or whatever.
    if(maxlen >= sizeof(aString) + 1)
    {
        ThrowNativeError(SP_ERROR_NATIVE, "setAString: Array thing invalid, too big for aString.");
        return;
    }
    */

    /*
    We could actually do 
    GetNativeString(1, aString, GetNativeCell(2) + 1), 
    but that looks HELLA ugly. I'm pretty sure people would catch flak for that
    */

    
GetNativeString(1aStringmaxlen);//This is all we need. We could return nothing or just run the return;
    
return;
}

public 
native_setAnInt(Handle:pluginnumParams)
{
    
//setAnInt(value);
    
new set GetNativeCell(2); //First param, the value we'll be setting int to.

    
anInt set;
    return;

Plugin using the natives:
PHP Code:
#include <sourcemod>
#include <uselessnatives>


//We call this because OnPluginStart() is apparently called too late. I mean just look at the name of this, it honestly looks like it belongs in here.
public OnPluginStart()
{
    
RegConsoleCmd("sm_nativeprint"command_printNatives"Print what you set");
}

public 
Action:command_printNatives(clientargs)
{
    
decl String:getString[16];

    
//no args so yolo i guess
    
setAnInt(50);
    
setAString("Animal Crackers"16); //pls dont ever do this in a real setting
    
getAString(getStringsizeof(getString));

    
PrintToChat(client"EXAMPLE:\n  setAnInt: %i\n  setAString: %s"getAnInt(), getString);
    return 
Plugin_Handled;

That's all I can really teach you. You could do this with, say, player data by passing the client index in the native. entity indexes, etc..

Closing statements:
  • If you're going to use getXXX() multiple times without changing the value in the same function, you're best to cache it (assign it to a variable).
  • Make sure your code is better than mine if you intend to release it for other people to use in their plugins.
  • Be cautious and courteous of how you or other people might use them in the future.
  • Use your brain, ask things like do you REALLY need a native for that?
  • Do I suck at making tutorials? Here's some extra-reads: Creating Natives (Saucemod Wiki)
  • For you newdecl elitists: Neuro Toxin's "Dynamic Objects"


I would post some like profiler results showing a cached result vs. repeatedly using GetNativeCell or String repeatedly but I'm too lazy
To you guys to refer to me as "that one bad kid", please do point out some mishaps in the tutorial. I found myself rewriting about every segment in php tags about 4x each, so there's bound to be something I forgot to clean up.
Thanks and good luck in the future!


So now instead of having 30,000 lines in one plugin, you have 19 plugins to be confused about dealing with like me!



pls dont kill me newdecls elitists i use them sometimes
p.s. if theres any admins who see this and think it is good and want to be my friend temporarily if you could please drop the "Eas" out of my username and make it just "Sidezz" that would be sick as fuck.

Last edited by sdz; 05-30-2016 at 00:30.
sdz is offline
Neuro Toxin
Veteran Member
Join Date: Oct 2013
Location: { closing the void; }
Old 05-27-2016 , 19:59   Re: [Tutorial] Communicating data across plugins (or how to use natives in general)
Reply With Quote #2

You forgot to talk about marking natives as optional.

https://github.com/ntoxin66/Dynamic/...namic.inc#L439
__________________
Neuro Toxin is offline
sdz
Senior Member
Join Date: Feb 2012
Old 05-27-2016 , 23:26   Re: [Tutorial] Communicating data across plugins (or how to use natives in general)
Reply With Quote #3

Quote:
Originally Posted by Neuro Toxin View Post
You forgot to talk about marking natives as optional.

https://github.com/ntoxin66/Dynamic/...namic.inc#L439
This was more about cross communication of data, where you know you're gonna need them all. The thought popped in my head but I felt I wasn't able to explain a way well enough on marking natives as optional
sdz is offline
Powerlord
AlliedModders Donor
Join Date: Jun 2008
Location: Seduce Me!
Old 05-29-2016 , 22:58   Re: [Tutorial] Communicating data across plugins (or how to use natives in general)
Reply With Quote #4

Quote:
Originally Posted by EasSideZZ
Code:
//This is double-include prevention. Idk what that does but apparently it's convention so I do this
Include files can include other include files. This prevents you from accidentally including the contents of an include file twice, which would give you errors about a method being already defined.

On a side note, never name a variable int as this isn't a valid variable name in SourcePawn 1.8 or newer.
__________________
Not currently working on SourceMod plugin development.

Last edited by Powerlord; 05-29-2016 at 23:01.
Powerlord is offline
sdz
Senior Member
Join Date: Feb 2012
Old 05-30-2016 , 00:29   Re: [Tutorial] Communicating data across plugins (or how to use natives in general)
Reply With Quote #5

Quote:
Originally Posted by Powerlord View Post
Include files can include other include files. This prevents you from accidentally including the contents of an include file twice, which would give you errors about a method being already defined.

On a side note, never name a variable int as this isn't a valid variable name in SourcePawn 1.8 or newer.
I've just been copying and pasting the same include stuff for about 4 years now not gonna lie to you. Thanks for the heads up though.
sdz is offline
Neuro Toxin
Veteran Member
Join Date: Oct 2013
Location: { closing the void; }
Old 05-30-2016 , 04:09   Re: [Tutorial] Communicating data across plugins (or how to use natives in general)
Reply With Quote #6

I got into the habit of this with includes:

Code:
#if defined _dynamic_included_
  #endinput
#endif
#define _dynamic_included_
It's stops multiple includes.
__________________
Neuro Toxin is offline
ofir753
Senior Member
Join Date: Aug 2012
Old 05-30-2016 , 06:45   Re: [Tutorial] Communicating data across plugins (or how to use natives in general)
Reply With Quote #7

Good job!
ofir753 is offline
Vergil333
Member
Join Date: Apr 2016
Old 06-01-2016 , 12:24   Re: [Tutorial] Communicating data across plugins (or how to use natives in general)
Reply With Quote #8

I hate my stupid brain! :-(
Vergil333 is offline
Vergil333
Member
Join Date: Apr 2016
Old 06-03-2016 , 12:54   Re: [Tutorial] Communicating data across plugins (or how to use natives in general)
Reply With Quote #9

Ok, I think I finally get it!

So we have this first plugin:

PHP Code:
new anInt;

//We call this because OnPluginLoad() is apparently called too late. I mean just look at the name of this, it honestly looks like it belongs in here.
public APLRes:AskPluginLoad2(Handle:myselfbool:lateString:error[], err_max)
{
    
CreateNative("setAnInt"native_setAnInt);

    
RegPluginLibrary("uselessnatives.inc");
    return 
APLRes_Success;
}


public 
native_getAnInt(Handle:pluginnumParams)
{
    return 
anInt;
}


public 
native_setAnInt(Handle:pluginnumParams)
{
    
//setAnInt(value);
    
new set GetNativeCell(2); //First param, the value we'll be setting int to.

    
anInt set;
    return;

then we have this strange include file:
PHP Code:
#if defined _uselessnatives_included
 #endinput
#endif
#define _uselessnatives_included

native native_setAnInt(intvalue); 
and finally second plugin:
PHP Code:
#include <uselessnatives>

public OnPluginStart()
{
    
RegConsoleCmd("sm_nativeprint"command_printNatives"Print what you set");
}

public 
Action:command_printNatives(clientargs)
{
    
setAnInt(50);

    
getAnInt();

    return 
Plugin_Handled;

Sooooooooooooooo... What is happening here?
1) setAnInt(50) from second plugin is actually a function. Function from first plugin called native_setAnInt.
2) it pass the value 50 via uselessnatives.inc
3) uselessnatives.inc communicate with CreateNative("setAnInt", native_setAnInt) from first plugin
4) and by this magic, 50 is set in native_setAnInt function in first plugin. This function than changes value of anInt = 50.

That's setAnInt. Passing data from second plugin to the first plugin.
After it is set (passed) it is called by a function getAnInt. And there is no need for magic? It is just called like this? Directly?

I need to pass an array from first plugin to second. Only one-way. What do I need???

Last edited by Vergil333; 06-03-2016 at 13:52. Reason: I always find some typing errors after I post a post
Vergil333 is offline
WildCard65
Veteran Member
Join Date: Aug 2013
Location: Canada
Old 06-03-2016 , 14:11   Re: [Tutorial] Communicating data across plugins (or how to use natives in general)
Reply With Quote #10

Quote:
Originally Posted by Vergil333 View Post
Sooooooooooooooo... What is happening here?
1) setAnInt(50) from second plugin is actually a function. Function from first plugin called native_setAnInt.
2) it pass the value 50 via uselessnatives.inc
3) uselessnatives.inc communicate with CreateNative("setAnInt", native_setAnInt) from first plugin
4) and by this magic, 50 is set in native_setAnInt function in first plugin. This function than changes value of anInt = 50.

That's setAnInt. Passing data from second plugin to the first plugin.
After it is set (passed) it is called by a function getAnInt. And there is no need for magic? It is just called like this? Directly?

I need to pass an array from first plugin to second. Only one-way. What do I need???
Here is what actually happens:
A) CreateNative tells SourceMod to bind setAnInt native to plugin function native_setAnInt through FakeNatives.
B) 2nd Plugin calls setAnInt and pushes 50 to param stack.
C) Sourcepawn lookups the native binding of setAnInt, discovers it as a fake native and calls the fake native router associated with the native.
D) SourceMod sets up internal variables related to the fake native it's currently executing (in this case, setAnInt).
E) SourceMod calls the bound plugin function.
F) First plugin does logic for native and returns 0 (because SP compiler code injection).
G) SourceMod re-returns 0 back to SP.
H) SP re-returns 0 back to the 2nd plugin.
I) Second plugin discards the returned value after current function is done executing.

Last edited by WildCard65; 06-03-2016 at 14:12.
WildCard65 is offline
Reply



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 20:28.


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