[Tutorial] How to create a plugin
#1

Introduction

A lot of people want to know how to create a plugin using Microsoft Visual C++ 2008/2010. Some people think it's hard but it's actually quite simple if you know how to start.
I will explain step by step below how you create your first native.

Things you need to know before starting
How
  1. Run Microsoft Visual C++ (2008/2010 version).
  2. Go to "File" tab then click "New" and select "Project".

  3. You will see the following dialog

  4. Select "Win32 Project", enter a project name and press "OK" to continue.
  5. The following dialog will appear

  6. Just press "Next" to continue.
  7. In the next dialog you have to select "DLL" under Application type and "Empty project" under additional options and press the "Finish" button.

  8. Now the only thing you're going to see is the solution explorer somewhere on the left side. (If you don't see the solution explorer press the "View" tab then select "Other windows" and click on "Solution explorer")

  9. As you can see, I called my project "Test". Do right click on "Test" in the solution explorer and go select "Properties".
  10. You will see the following dialog

  11. On the left, click "Linker" then select "Input" and write next to "Module Definition File" the name of your project or something else adding the ".def" extension behind and press "OK".

  12. Now refer yourself back to the solution explorer and do right click on your project name again and select "Add", and then click "New Item".
  13. In the dialog that pops up, select "C++ File (.cpp)" and below next to "Name" you write the thing you wrote next to "Module Definition File" (in step 11). And press "Add".

  14. Press right click on your project name, select "Add" and click "New Item" again.
  15. This time you do the same, select "C++ File (.cpp)" but now, you write next to "Name" the name you chose previously but this time using the ".cpp" extension. Then press "Add".

  16. Now you will see 2 tabs opened: "YourProjectName.def" and "YourProjectName.cpp".
  17. Open "YourProjectName.def" and paste the following thing into that:
    Code:
    EXPORTS
    	Supports
    	Load
    	Unload
    	AmxLoad
    	AmxUnload
  18. Now open "YourProjectName.cpp" and paste the following thing into that:
    pawn Code:
    #include "../SDK/plugin.h"

    typedef void
        (*logprintf_t)(char* format, ...)
    ;

    logprintf_t
        logprintf
    ;

    void
        **ppPluginData
    ;

    extern void
        *pAMXFunctions
    ;

    PLUGIN_EXPORT bool PLUGIN_CALL Load(void **ppData)
    {
       pAMXFunctions = ppData[PLUGIN_DATA_AMX_EXPORTS];
       logprintf = (logprintf_t)ppData[PLUGIN_DATA_LOGPRINTF];
       return 1;
    }

    PLUGIN_EXPORT void PLUGIN_CALL Unload()
    {
    }

    AMX_NATIVE_INFO projectNatives[] =
    {
       { 0, 0 }
    };


    PLUGIN_EXPORT unsigned int PLUGIN_CALL Supports()
    {
       return SUPPORTS_VERSION | SUPPORTS_AMX_NATIVES;
    }

    PLUGIN_EXPORT int PLUGIN_CALL AmxLoad(AMX *amx)
    {
       return amx_Register(amx, projectNatives, -1);
    }

    PLUGIN_EXPORT int PLUGIN_CALL AmxUnload(AMX *amx)
    {
       return AMX_ERR_NONE;
    }
  19. Now refer yourself back to Solution explorer and right click your project name again, select "Add", and click on "New Filter". Now you will see that a new file has been created. Change the name to "SDK".
  20. Extract the "SDK" file you have downloaded in "Documents/Visual Studio 2010/Projects/YourProjectName/".
  21. Go back to the solution explorer and right click on the "SDK" filter you have created and select "Add", then click on "Existing Item".
  22. A dialog will appear, now browse to the "SDK" directory you have copied and select "amxplugin.cpp".
  23. Now that's pretty it! Now press F7 and compile it.
  24. You can find your .dll file in the Debug directory.
Create a native
  1. Add this somewhere in your .cpp script:
    pawn Code:
    static cell AMX_NATIVE_CALL YourNativeNameHere(AMX *amx, cell *params)
    {  
            logprintf("I haz made my first plugin!! :)");
            // Your codes go in here
            return 1; // Change the return value if need
    }
  2. Go to:
    pawn Code:
    AMX_NATIVE_INFO projectNatives[] =
    {
            { 0, 0 }
    };
    And add "{ "YourNativeNameHere", YourNativeNameHere }", like this:
    pawn Code:
    AMX_NATIVE_INFO projectNatives[] =
    {
            { "YourNativeNameHere", YourNativeNameHere } // In the first array dimension, you write the name of the native you're going to call in PAWN. In the second one, you write the name in .cpp file. In this case, they're the same!
    };
  3. Compile, and go to the Debug file.
  4. Select your .dll file and copy it to your server files.
  5. Create a new .pwn script and add "native YourNativeNameHere();" at the top. And just call it under "OnFilterScriptInit".
    pawn Code:
    #include <a_samp>

    native YourNativeNameHere();

    public OnFilterScriptInit()
    {
        YourNativeNameHere();
        return 1;
    }
  6. When you run your server, it should print this: "I haz made my first plugin!! ".
  7. This is just the basic. Now you can create other unique things!

    NOTE: If you want natives with parameters, you can use "params[]" starting from index 1.
Note(s)
  • Try to figure out how to do other things. I'm just teaching you the basic on how to get started.
  • You can ask questions below, but I'm not going to answer then all.
Reply
#2

AWESOME!
Reply
#3

haha thanks. i use VS 2010
Reply
#4

Nice tutorial. Althought I hope not to see too many useless plugins by users Good work ryder.
Reply
#5

Awesome, I love that
ty!
Reply
#6

Meh... you could atleast create this tutorial before I broke my ass to learn how to code a plugin...
Reply
#7

Awesome work
Reply
#8

Awesome!
Reply
#9

this tutorials is amazing and a little dificulting.
for people who knows programing C++ and C#.
Reply
#10

sorry to spam, but, THANK YOU SOOO FUKEN MUCH!!!! I REALLY NEEDED THIS! THX MAN!!!!!!!!!!!!!
Reply
#11

This is the bestest thing ever, but still, people will actually still have to learn C++ right?
Also:

i followed all steps, and compiled. cant find DLL

Code:
	
Build started: Project: rxPlugin, Configuration: Debug|Win32

Command Lines
 	 	
Creating temporary file "c:\Users\Justin\Documents\Visual Studio 2008\Projects\rxPlugin\rxPlugin\Debug\RSP00000276886484.rsp" with contents
[
/Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_USRDLL" /D "RXPLUGIN_EXPORTS" /D "_UNICODE" /D "UNICODE" /D "_WINDLL" /Gm /EHsc /RTC1 /MDd /Fo"Debug\\" /Fd"Debug\vc90.pdb" /W3 /c /ZI /TP ".\rxPlugin.cpp"
]
Creating command line "cl.exe @"c:\Users\Justin\Documents\Visual Studio 2008\Projects\rxPlugin\rxPlugin\Debug\RSP00000276886484.rsp" /nologo /errorReport:prompt"
Output Window
 	 	
Compiling...
Skipping... (no relevant changes detected)
rxPlugin.cpp
Results
 	 	
Build log was saved at "file://c:\Users\Justin\Documents\Visual Studio 2008\Projects\rxPlugin\rxPlugin\Debug\BuildLog.htm"
rxPlugin - 0 error(s), 0 warning(s)
Reply
#12

Thanks for this Ryder, might be useful to gain knowledge of C++ in the future. Again thanks.
Reply
#13

Really AMAZING. Wont be forgottable for a long time, and tomorrow I'm gonna test that, cuz I'm lazy now. Even though I've tried before with dev C++. And this is very hard, since C++ has too many functions. But about creative natives it's not so hard, but for execute some advanced functions. Really nice tutorial, congratz
Reply
#14

THIS will make a boost in the plugins -- You are a true SA-MP Member!
Reply
#15

This is really good. But, i want to learn how to make SA-MP functions happen from my include... ex.

if(IsPlayerconnected(playerid)
{
print("Player is connected");
}
else
{
print("Player is NOT connected");
}

or something like that... how do i do that?
Reply
#16

Great tutorial, helped me out alot. Thanks.
Reply
#17

@cj101:
Try to compile with F7. Otherwise please try again, all steps are correct.

@sciman001:
So you basicaly want to call a function from SA-MP to your plugin? That's not difficult. Here's the way I'm doing:
In your C++, you create a "function" where you push the parameter and execute the function and retrieve the return the value to return.

Example of IsPlayerConnected in C++. (I used __IsPlayerConnected to prevent collisions)
pawn Code:
int __IsPlayerConnected(int playerid, AMX *amx) // AMX *amx must stay but it's just the playerid parameter that counts in his function.
{
    int
        index
    ;
    if(!amx_FindPublic(amx, "__IsPlayerConnected", &index))
    {
        cell
            retVal
        ;
        amx_Push(amx, playerid);
        amx_Exec(amx, &retVal, index);
        return (int)retVal;
    }
    return -1;
}
Now all you have to do is open PAWN and do a public call where you return IsPlayerConnected with public __IsPlayerConnected.

pawn Code:
#define FunctionCall(%0) \
    forward __%0; public __%0 return %0

FunctionCall(IsPlayerConnected(playerid)); // This will automaticly define the public starting with '__' and return the function.
And you can use "__IsPlayerConnected(playerid, amx)" in your C++ script.

pawn Code:
static cell AMX_NATIVE_CALL __PrintPlayerConnect(AMX *amx, cell *params)
{
    if(__IsPlayerConnected(params[1], amx)) // Never forget to add 'amx'!
    {
        printf("Player %d is connected", params[1]);
    }
    else printf("Player %d is NOT connected", params[1]);
    return 1;
}
Now you can call your native to SA-MP, like I explained in my first post and add this for example under OnPlayerSpawn and see the result!.

Note: You can convert float to cell and vice-versa using "amx_ftoc" and "amx_ctof". Can be useful for pushing float variables.
Reply
#18

i know i cant do it anyways nice ryder
well i'll try
Reply
#19

Compiles fine, but when i load it into my SA-MP server,

Plugin does not conform to architecture! Why ?

pawn Code:
#include "../SDK/plugin.h"

typedef void
    (*logprintf_t)(char* format, ...)
;

logprintf_t
    logprintf
;

void
    **ppPluginData
;

extern void
    *pAMXFunctions
;

PLUGIN_EXPORT bool PLUGIN_CALL Load(void **ppData)
{
   pAMXFunctions = ppData[PLUGIN_DATA_AMX_EXPORTS];
   logprintf = (logprintf_t)ppData[PLUGIN_DATA_LOGPRINTF];
   return 1;
}

PLUGIN_EXPORT void PLUGIN_CALL Unload()
{
}

static cell AMX_NATIVE_CALL MyFunction(AMX *amx, cell *params)
{  
    logprintf("LOVE IT!");
    return 1;
}
static cell AMX_NATIVE_CALL MyFunction2(AMX *amx, cell *params)
{  
    logprintf("LOVE IT!");
    return 1;
}
static cell AMX_NATIVE_CALL MyFunction3(AMX *amx, cell *params)
{  
    logprintf("LOVE IT!");
    return 1;
}

AMX_NATIVE_INFO projectNatives[] =
{
    { "MyFunction", MyFunction },
    { "MyFunction2", MyFunction2 },
    { "MyFunction3", MyFunction3 }
};



PLUGIN_EXPORT unsigned int PLUGIN_CALL Supports()
{
   return SUPPORTS_VERSION | SUPPORTS_AMX_NATIVES;
}

PLUGIN_EXPORT int PLUGIN_CALL AmxLoad(AMX *amx)
{
   return amx_Register(amx, projectNatives, -1);
}

PLUGIN_EXPORT int PLUGIN_CALL AmxUnload(AMX *amx)
{
   return AMX_ERR_NONE;
}
My try...
Reply
#20

You probably missed some steps starting from 9.
Reply


Forum Jump:


Users browsing this thread: 5 Guest(s)