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:myself, bool:late, String: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(int, value);
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:plugin, numParams)
{
//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:myself, bool:late, String:error[], err_max)
{
CreateNative("setAnInt", native_setAnInt);
CreateNative("setAString", native_setAString);
RegPluginLibrary("uselessnatives.inc");
return APLRes_Success;
}
public native_setAString(Handle:plugin, numParams)
{
//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(2, setString, maxlen);
SetNativeString(1, setString, maxlen); //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:myself, bool:late, String:error[], err_max)
{
CreateNative("setAnInt", native_setAnInt);
CreateNative("setAString", native_setAString);
RegPluginLibrary("uselessnatives.inc");
return APLRes_Success;
}
public native_getAString(Handle:plugin, numParams)
{
//getAString(String:buffer[], maxlen);
new maxlen = GetNativeCell(2) + 1;
SetNativeString(1, aString, maxlen);
return;
}
public native_getAnInt(Handle:plugin, numParams)
{
return anInt;
}
public native_setAString(Handle:plugin, numParams)
{
//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(1, aString, maxlen);//This is all we need. We could return nothing or just run the return;
return;
}
public native_setAnInt(Handle:plugin, numParams)
{
//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(client, args)
{
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(getString, sizeof(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.