2012-10-06

WIP Rewrite

← Older revision

Revision as of 06:32, 6 October 2012

(5 intermediate revisions by one user not shown)

Line 1:

Line 1:

This guide will give you a basic introduction to writing a [[SourceMod]] plugin.  If you are not familiar with the SourcePawn language, it is recommended that you at least briefly read the [[Introduction to SourcePawn]] article.

This guide will give you a basic introduction to writing a [[SourceMod]] plugin.  If you are not familiar with the SourcePawn language, it is recommended that you at least briefly read the [[Introduction to SourcePawn]] article.



For information on compiling plugins, see [[Compiling SourceMod Plugins]].
The author of this article uses
[http://www.crimsoneditor.com/ Crimson Editor]
to write plugins.  Other possibilities are
[http://www.pspad.com/ PSPad], [http://www.ultraedit.com/ UltraEdit], [http://notepad-plus.sourceforge.net/uk/site.htm Notepad++], [http://www.textpad.com/ TextPad], [http://sourceforge.net/projects/pawnstudio/
SourceMod IDE
] or any other text editor you're comfortable with.

+

For information on compiling plugins, see [[Compiling SourceMod Plugins]].
You can use
[http://www.crimsoneditor.com/ Crimson Editor]
,
[http://www.pspad.com/ PSPad], [http://www.ultraedit.com/ UltraEdit], [http://notepad-plus.sourceforge.net/uk/site.htm Notepad++], [http://www.textpad.com/ TextPad], [http://sourceforge.net/projects/pawnstudio/
Pawn Studio
] or any other text editor you're comfortable with
to write plugins
.



=
Plugin Structure
=

+

=
Starting from scratch
=



Almost all plugins
have the
same three elements:

+

Open your favorite text editor and create a new empty file. When you
have
an empty file you can just start writing code using
the
core language, however, you will not be able to use any of SourceMod features because the compiler does not now about them. This is done deliberately so it is possible to use SourcePawn outside of SourceMod. But since we are writing a SourceMod plugin, it is a good idea to enable access to SourceMod features first. This it done using #include directive. It tells compiler to "paste" the code from another file into yours.



*
'''
Includes
''' -
Allows
you to
access
the SourceMod
API
,
and if you desire, APIs from external
SourceMod
extensions
and
plugins.

+

#include



*
'''
Info
'''
- Public information about
your
plugin.

+

How does this work? First of all, note that we enclosed file name into angle brackets. Angle brackets tell the compiler to look in the default include directory. By default, it is
'''
scripting/include
'''
. You can open it right now and see a lot of inc files there. Those are SourceMod include files that describe various functions, tags and other features available for SourceMod plugins. The files are plain
-
text and
you
are encouraged
to
read them. You will notice however, that there's not much code in there, certainly not enough to implement all
the
great features of
SourceMod,
so where are they? They are implemented inside a
SourceMod
core which is written in C++
and
is compiled into binary files which end up in
'''
bin
'''
directory. So how does
your
SourcePawn code and SM core link together if compiler doesn
'
t know about existence of the latter? SourceMod include files are written specially, so they say that the implementation of functions is
''
somewhere else
''
. Compiler understands that and generate a special code that says that this
function
call is going outside. When SourceMod loads
your plugin
, it inspects these bits of code and substitutes it's own internal functions instead. This is called [http://en.wikipedia.org/wiki/Dynamic_linking dynamic linking]
.



*
'''
Startup
''
' - A
function
which performs start-up routines in
your plugin.

+



A skeletal
plugin
structure looks like
this:

+

=Setting up
plugin
info=



+

Now that we got access to SourceMod features, it is time to setup the information that will be displayed via sm plugins list command. No one likes unnamed plugins. To do that we are going to look inside '''sourcemod.inc''' file and see the format that information should be declared. It's always helpful to look inside SM include files to find out information you don't know. There is also an [http://docs.sourcemod.net/api/ API documentation] but it can be outdated and it only has SM core files so if your plugin are going to use any third party extension or another plugin, you will have to study inc files. So, open '''sourcemod.inc''' and scroll down a bit until you see
this:



#include

+

/**

+

* Plugin public information.

+

*/

+

struct Plugin

+

{

+

const String:name[], /**

+

const String:description[], /**

+

const String:author[], /**

+

const String:version[], /**

+

const String:url[], /**

+

};

+

and this:

+

/**

+

* Declare this as a struct in your plugin to expose its information.

+

* Example:

+

*

+

* public Plugin:myinfo =

+

* {

+

*    name = "My Plugin",

+

*    //etc

+

* };

+

*/

+

public Plugin:myinfo;

+

+

It tells us that we need to create a global public variable myinfo which must be of type Plugin which is a struct with 5 fields which themselves are strings. It may sound complicated for a beginner but it's easy. Let's go ahead and create one:

+

public Plugin:myinfo =

+

{

+

name = "My First Plugin",

+

author = "Me",

+

description = "My first plugin ever",

+

version = "1.0",

+

url = "http://www.sourcemod.net/"

+

};

+

+

The public keyword means that SourceMod will be able to directly access our variable. Plugin: defines a type of our variable. myinfo is, obviously, a name of our variable as required by SourceMod. You see that we initialize it right away. This is preferred way to do when filling out plugin info.

+

+

After that the full code of your plugin should look like this:

+

#include

public Plugin:myinfo =

public Plugin:myinfo =

Line 18:

Line 54:

author = "Me",

author = "Me",

description = "My first plugin ever",

description = "My first plugin ever",



version = "1.0.
0
.0",

+

version = "1.0
",

+

url = "http://www
.
sourcemod.net/"

+

};

+

+

=Getting code to run=

+

We already include SourceMod features and filled up or plugin info. We now have a perfectly well formed plugin which can be compiled and loaded by SourceMod. However, there is one problem - it does nothing. You might be tempted to just start writing a code after myinfo declaration just to see that it will not compile. SourcePawn, unlike other scripting languages like Lua, does not allow a code to be outside of functions. After reading that, you may probably want to just define some function, name it main probably, compile and load a plugin and see that your code never gets called. So how do we make SourceMod call our code? For this exact reason we have forwards. Forwards are function prototypes declared by one party that can be implemented by another party. When a first party starts a forward call, all parties that implemented that forward receive the call. SourceMod declares a plenty of interesting forwards that we can implement. As you can see, forwards are the only way to get our code executed, keep that in mind. So let's implement OnPluginStart forward. As you may have guessed, it is called when our plugin starts. To do that, we'll have to look up the declaration of OnPluginStart. It is declared inside '''sourcemod.inc''', a file we are already familiar with, let's find it:

+

/**

+

* Called when the plugin is fully initialized and all known external references

+

* are resolved. This is only called once in the lifetime of the plugin, and is

+

* paired with OnPluginEnd().

+

*

+

* If any run-time error is thrown during this callback, the plugin will be marked

+

* as failed.

+

*

+

* It is not necessary to close any handles or remove hooks in this function.

+

* SourceMod guarantees that plugin shutdown automatically and correctly releases

+

* all resources.

+

*

+

* @noreturn

+

*/

+

forward OnPluginStart();

+

Empty parentheses tells us that no arguments are passed inside this forward, @noreturn inside documentation tells us that we don't have to return anything, pretty simple forward. So how to implement it? Firstly, our implementation must have the same name, so it's OnPluginStart, secondly, our implementation should have the same number of arguments, none in this case, and lastly, SourceMod needs to be able to call our implementation so it needs to be public. So the implementation looks like this:

+

public OnPluginStart()

+

{

+

}

+

+

Now we can write code inside curly braces and it will be executed when our plugin starts. Let's output "Hello world!" to server console. To do that we are going to use PrintToServer function. It is declared inside '''console.inc''', however, we don't need to manually include '''console.inc''' because it is included automatically as part of '''sourcemod.inc'''.

+

/**

+

* Sends a message to the server console.

+

*

+

* @param format Formatting rules.

+

* @param ... Variable number of format parameters.

+

* @noreturn

+

*/

+

native PrintToServer(const String:format[], any:...);

+

As you can see, this is a native function. It is implemented inside SM core. Judging by it's arguments, we can see that it is a [[Format_Class_Functions_%28SourceMod_Scripting%29|format class function]]. However, we don't need any formatting right now, so let's just pass "Hello world!" string as an only argument:

+

public OnPluginStart()

+

{

+

PrintToServer("Hello world!");

+

}

+

That's it! The full code of your plugin should look like this:

+

#include

+

+

public Plugin:myinfo =

+

{

+

name = "My First Plugin",

+

author = "Me",

+

description = "My first plugin ever",

+

version = "1
.0",

url = "http://www.sourcemod.net/"

url = "http://www.sourcemod.net/"

};

};

Line 24:

Line 108:

public OnPluginStart()

public OnPluginStart()

{

{



// Perform one-time startup tasks ...

+

PrintToServer("Hello world!");

}

}



+

Compile
and
load your plugin on your server
and
see for yourself that
the
message is displayed in server console
.



The information portion is a special syntax construct.  You cannot change any of the keywords, or the public Plugin:myinfo declaration.  The best idea is to copy
and
paste this skeletal structure
and
modify
the
strings to get started
.

+

=Includes=

=Includes=

Show more