Well, I guess, this is the 5th or 6th version of PocketMine plugin tutorials from
me?
I must admit that the versions before had not been very successful, so I guess we should start all over
again.
This time, I created the tutorials with GitHub sites. Hopefully more customizable pages will help make
the tutorials better.
So, first of all, let's get to know clearly what we are planning to do.
Wait, aren't we going to code PocketMine plugins? Isn't that clear enough?
Then what exactly does a PocketMine plugin do? The answer could be quite vague.
Basically, a plugin has unlimited power (including root access if you are running the server with root)
on your server. It is the commander of your server. Plugins instruct PocketMine to get everything done.
So I know that the plugin is the god. But how does knowing that help me?
The problem that made most people unable to code is that most people don't know what they want.
I know very clearly. I want to make a hunger games plugin.
But then, what is a hunger games plugin?
+-----------------+ +--------------------------+ | Player tap sign | ==> | Teleport player to lobby | ---+ +-----------------+ +--------------------------+ | | +-------------------------+ +-----------------+ +--------------------------+ | | Timer reached 0 second | +-----------------+ +---------------+ +------------------------+ +------------------------------+ | Player tap sign | ==> | Teleport player to lobby | + ===> +-- WHEN EITHER HAPPENS --+ ===> | Start the match | ===> | Refill chests | ===> | Timer reached 0 second | ===> | Teleport to deathmatch arena | +-----------------+ +--------------------------+ | | Enough players | +-----------------+ +---------------+ +------------------------+ +------------------------------+ | +-------------------------+ | | +-----------------+ +--------------------------+ | +-------------------------------------------------------------------------------------+ | Player tap sign | ==> | Teleport player to lobby | ---+ ===> +-------------------+ +---------------------------------------+ | +-----------------+ +--------------------------+ | When player moves | ==> | Don't let player move (teleport back) | | +-------------------+ +---------------------------------------+ V +----------------------------------+ 。 +----------------+ +-----------------------+ | If there is just one player left | 。 | If player dies | ==> | Teleport him to spawn | ==> | Let that player win (add coins) | 。 +----------------+ +-----------------------+ +----------------------------------+ +--------------+ +------------------------+ | Player joins | ==> | Load player statistics | +--------------+ +------------------------+ +--------------+ +------------------------+ | Player quits | ==> | Save player statistics | +--------------+ +------------------------+ +---------------------------+ +------------------------+ | Player type command /stat | ==> | Show player statistics | +---------------------------+ +------------------------+
The outline for a hunger games plugin actually shows three of the most important components of plugins:
- Commands
- Events (when something happens)
- Timer
Of course, we are nowhere near the complexity of a good hunger games plugin. But at least now we know better what we are going to do.
In Chapters 3-5 of this website, we will go through the principles of these three
components, as well as some fundamental ways of doing various actions on the server.
But before that, we would start by creating a useless plugin that works.
- A PocketMine server that you can directly change its plugin files.
For testing plugins, obviously. -
A virtual/hard keyboard that can let you type symbols like
,.;!?$/\|()[]{}
.
Here is a list of recommended tools:
- Text editor
- Windows
-
If you are thinking about Microsoft Word, get rid of that thought
immediately. Microsoft Word is a rich text editor, and it simply cannot open nor
output any text files. Same applies to WordPad that came in Windows'
Accessories.
- You may be thinking about NotePad that came in the Accessories with Windows. Well, it is the best option if you can't install anything on your computer. (But then how did you install PocketMine?)
- The recommended tool is Notepad++. It is a powerful text editor for many types of files and for different programming languages.
- Some would recommend NetBeans, but I wouldn't want you to waste time doing all the setup stuff. Believe me, setup for a program always makes anything interesting into boring.
- If you have heard of PhpStorm, again, same with NetBeans, setup trouble (although there isn't a lot). Moreover, it is paid software, although there are some ways to legitimately get it for free. Nevertheless, it is an all-powerful IDE that completes the code for you when you have provided a little hint, and can also be used for other things like coding websites, etc. Actually, this website was written using PhpStorm (although there is no PHP here).
- Browser + GitHub The GitHub editor for code is not bad, at least better than Windows Notepad, but it is far not as good as PhpStorm. If you don't like installing anything, this is a good alternative to Notepad++.
-
If you are thinking about Microsoft Word, get rid of that thought
immediately. Microsoft Word is a rich text editor, and it simply cannot open nor
output any text files. Same applies to WordPad that came in Windows'
Accessories.
- OS X
- Some would recommend NetBeans, but I wouldn't want you to waste time doing all the setup stuff. Believe me, setup for a program always makes anything interesting into boring.
- If you have heard of PhpStorm, again, same with NetBeans, setup trouble (although there isn't a lot). Moreover, it is paid software, although there are some ways to legitimately get it for free. Nevertheless, it is an all-powerful IDE that completes the code for you when you have provided a little hint, and can also be used for other things like coding websites, etc. Actually, this website was written using PhpStorm (although there is no PHP here).
- Browser + GitHub The GitHub editor for code is not bad, at least better than Windows Notepad, but it is far not as good as PhpStorm. If you don't like installing anything, this is a good alternative to Notepad++.
- TextMate TextMate (Use the beta!) is an powerful free Editor, with Syntnax highlighing. It has some realy neat functions, and works perfectly with git. It is a powerful text editor for many types of files and for different programming languages.
- iOS
- I have never used iOS, so don't ask me :(
- Android
I would not advise coding on phones or tablets, but if you don't have an alternative, well...- Normal text editors
Whatever editor you like, as long as it disables autocorrect. You can try Text Editor embedded in the File Manager app.
BTW, you need a file explorer since plugins consist of more than one file when you are developing. - Vim Touch
It is an imitation of Vim for Linux. It is an odd choice, but it has good things if you - Chrome + GitHub Many mobile users found GitHub's direct edit interface good, although it isn't at all.
- Normal text editors
- Windows
- A browser
For you to look for documentation (and this website) anytime you have trouble. Believe me, nobody memorizes all the functions in PHP.
Yes, this simple. That's all you need. But again, I strongly encourage you to get a computer and a
hard keyboard, or you can hardly develop anything big without finding all your $this
becoming $thos
.
I would assume that you have PocketMine setup already. I hope I don't need to teach you how to find
that directory.
Fine. For Android it is ~/PocketMine
, where ~
is something
like /mnt/sdcard
, etc.
Navigate to the plugins
directory (a.k.a. folder) in the server directory
and install the DevTools plugin.
For our first plugin, we are going to call it FirstPlugin
. Now, create a
directory in
the plugins
directory called FirstPlugin
. So now
we have /plugins/FirstPlugin
.
For convenience, in the future we are going to call this path ~
. E.g.
~/plugin.yml
refers to /plugins/FirstPlugin/plugin.yml
.
Now, it is time to create the very first file of our plugin. But it is not PHP code. It is a YAML
file that describes your plugin, located at ~/plugin.yml
.
name: FirstPlugin
author: PEMapModder
version: 1.0.0
api: 1.13.0
main: FirstPlugin\FirstPlugin
Before you try to copy, please read my explanation for them, line by line.
name: FirstPlugin
This line, apparently, defines your plugin name. Replace FirstPlugin
with
your plugin name, and you will see it showing up in the startup messages when the server starts.
author: PEMapModder
This line, apparent as well, tells the server who wrote this plugin. Of course, replace
PEMapModder
with your own name. It can contain spaces. However, if you
have
more than one author, change author
to authors
and
put down all the author names, separated by commas ,
, and put square
brackets
[ ]
around them.
version: 1.0.0
This line defines the plugin's version. It is purely internal with no rules, and the server will not try to understand what you are trying to say in the version. Just type whatever you like here as long as users and yourself can understand it.
api: 1.13.0
API means "application program interface". It is basically the things from PocketMine that
lets plugins interact with it. It is like how a microwave oven has buttons to let people adjust
it.
This refers to the minimum API version your plugin supports. You can find the current API version
of your server from the startup messages:
The first number refers to a complete change in API (major version). The plugin and the server must
have the same number here to be loaded.
The second number refers to additions in the API (minor version). The plugin must have the same or
smaller number here to be loaded.
The third number refers to minor API changes (patches). Any numbers work here, but you should use
the number you tested for.
All these information aside, what you have to know is, you'd better use the number you see in
your server's startup.
main: FirstPlugin\FirstPlugin
Here finally comes the part related to the plugin code.
Short and unclear explanation:
This is the fully-qualified name of your plugin's
main class.
This obviously explains nothing to someone who doesn't know what a class is. Let's
just go through step-by-step.
In PHP (especially in PocketMine-related PHP), code is contained in units called "classes". You can
think a class as a "file" (and it is indeed true that we would normally put only one class in one
file).
And what's even more exciting, since we have files, we have directories (folders) that contain the
files. They are called "namespaces". Actually, we normally put a PHP file with the proper filename
in the proper directory according to their namespace and class name. I said "normally" because it
sometimes works if you don't, but just forget it and assume that it doesn't.
Usually, just like you put similar files in the same directory, we put classes of the same plugin in
the same namespace. We can also have "sub-namespaces" just like we have sub-directories,
but that isn't important; just forget about it.
By convention, we use Pascal-case for class names. That is, we would
WriteLikeThis
if we all write in Pascal-Case.
Note that you can only use alphabets, the 10 numbers (0-9) and the underscore (_
) in
class names. Also, class names cannot start with numbers.
For namespaces, the same rule applies. However, to create "sub-namespaces", just like file paths
in Windows, we separate two parts by a backslash \
.
Now, to get a fully-qualified name of the class, which is like the full file path
on Windows, we use NamespaceName\ClassName
. In our example,
FirstPlugin\FirstPlugin
is the fully-qualified name of the namespace
FirstPlugin
and the class name also FirstPlugin
.
So what is a class? We will get it discussed in the next chapter.
I have already explained about the meaning of namespaces and classes in the previous chapter -
namespaces are like directories, and classes are like files. But to what extent are they alike?
Files that contain PHP code (source code) are called "source files". In your plugin, source files should
be located inside a directory called "sources root", which should be at ~/src
.
A source file ends with .php
. In front of that, it is the fully-qualified name
of the class inside the source file.
Now we have the fully-qualified name FirstPlugin\FirstPlugin
, we should create
our source file at ~/src/FirstPlugin/FirstPlugin.php
. Simple. What's not so
simple that gave the magical power to plugins is the things inside the file.
Hold your breath and scroll through all these.
STOP. Don't copy it into your file. Oh, did I say "just copy"? Forget that. Instead, try to understand it and write it yourself when you know what you are writing.
This line, <?php
, seriously does nothing at all. It doesn't help
you in any ways. It is only here to tell people that this is a PHP file.
Sadly, the human race is the best species at finding trouble for themselves. We must put this line at the very beginning of every PHP file, and better on an independent line.
Yes. Honestly. These lines are purely decorative. You can add spaces and tabs and break code
into new lines literally anywhere in your PHP file (but after the
<?php
line!), as long as they don't cut one word into two.
(But it is not reasonable to change function
into
fun ct ion
, is it?)
Nevertheless, programmers have a general habit of talking things in terms of "lines", but when we talk about a "line" of code, we generally refer to a statement. I will explain more about what a statement is later.
This is pretty obvious. When discussing the main
attribute in our
plugin.yml
, we have already mentioned that the namespace of this
class is FirstPlugin
.
We have to declare the namespace in every PHP file, unless it doesn't have a namespace (but this SHOULD NOT happen in a plugin!). As you can observe, the syntax to declare a namespace is:
namespace namespace_for_this_file;
namespace_for_this_file
should be the whole namespace, including the
backslashes (\
) and the subnamespaces.
Don't forget the semicolon at the end. A rule for PHP is that all statements would end with
;
(semicolons), unless they end with a {
,
then enclose other lines of code, and finally a }
. (Of course, except
<?php
, which is not code at all)
'
Keep in mind that the namespace statement must be the very first statement in
every PHP file (of course after <?php
)
There are two types of use
statements:
use Fully\Qualified\ClassName;
use Fully\Qualified\ClassName as AnAlias;
Quite self-expanatory, isn't it? Actually, the first type is just a simplified form of the second type. We can change the first line into:
use Fully\Qualified\ClassName as ClassName;
They basically mean the same thing.
OK. But what are use
statements for?
use
statements tells the PHP compiler that when you later say
AnAlias
in this file, you are talking about
Fully\Qualified\ClassName
. In this way, even though the namespace is
very long, we can use
the class as a short alias. If you don't say
as
what, as in the first type, the PHP compiler will use the
simple class name as the alias. In fact, programmers usually only use the first type to
avoid confusion, unless they are coding something real quickly and short and use
aliases to shorten the class
name (but don't do this in code where you expect other people to read, or yourself to
read after a few months - people will have a hard time understanding it).
In the dummy class above, we used:
use pocketmine\plugin\PluginBase;
On line 7, we mentioned PluginBase
, but because of the
use
statement from line 5, it actually means
pocketmine\plugin\PluginBase
rather than merely
PluginBase
.
Remember that use
statements must be directly after the
namespace
statement.
Pro tip: apart from class names, use statements can also register aliases for namespaces. This is handy when you are using a lot of classes from that namespace.
For convenience, I am going to refer use
statements as "imports" in the future.
Now, we have finally come to the most important part of our code. (Actually, the only part that does something meaningful).
Before I start explaining what this line (line 7) does, let's have an overview on the syntax of the PHP language. Yes, an overview, a very brief and general one. Really general one, you'd probably think that this is useless, but it lets you automatically understand a lot of things in the future.
Let's look at this text. Don't worry, it is written in English :)
When a player moves, if he is not an op, teleport him to jail. Take money from him. Send him a message. When the server stops, if the server owner is not online, notify him. Delete all worlds.
This describes what happens in the plugin in English. I hope that you notice the
ambiguity in this text
I said "Take money from him.", but does this mean take money from him if he is not an op? Or
whenever he moves?
"Delete all worlds.", does this happen only if the server owner is not online, or whenever the
server stops?
To clarify my meaning, let's format my text into this:
When a player moves: { If the player is an op: { Teleport him to jail. Take money from him. } Send him a message. } When the server stops: { If the server owner is not online: { Notify him. } Delete all worlds. }
Now, it is crystal clear that I am referring to the things inside the curly braces ({}
) when I say "if the player is an op". And as a matter of fact,
this is how PHP understands what you say.
In the PHP syntax, it main consists of two compoenents - statements and statement
groups.
A statement is like a sentence saying what to do, like "Teleport him to jail". Just like how
we end every sentence in English with a full stop .
, we end every
PHP statement with a semicolon ;
.
A statement group consists of a line that explains what this statement group is about, then
followed by a group of statements. For example, in the text above, "When the server stops"
explains what the following group of statements is about.
Yes, a statement group is a statement too. Therefore, we can have a statement group inside a
statement group.
Usually, a statement group looks like this:
group_type{ many statements; and nested statement groups{ here; } }
Sometimes, group_type
can contain more information than merely the
group type. Let's go back to our dummy class for an example.
This was our class
statement group:
class FirstPlugin extends PluginBase{ # some code was here }
This declares a class called FirstPlugin
. we have
previously decided that our fully-qualified class name should be FirstPlugin\FirstPlugin
, where the first FirstPlugin
is the namespace and the second FirstPlugin
is the simple class name. We have already declared the namespace for this file on line 3, so we
don't need to mention it again.
The next part of this line says that the class FirstPlugin
extends PluginBase
. What does that mean?
Remember what we did on line 5? PluginBase
actually means
pocketmine\plugin\PluginBase
, which is a class provided by PocketMine
itself.
So what? What does this class do? What is meant by extends
?
The concept of "extend" is more complicated. But for the moment, you can assume that it means that this class is the main class of a plugin.
There are two types of statements inside a class. They are class properties and functions. Class properties are the "memory" of a class, but you can ignore that for the moment. We are going to talk about functions first.
function
is another kind of statement group. It declares a
function.
Have you ever learnt functions from mathematics? OK, if you haven't, learn it now.
A function is like a crafting table. You add ingredients (parameters/arguments)
into it, and it will give you some product (return value). Furthermore, the
order of how you put the input matters.
For example, you have three diamonds and two sticks. If you put the three diamonds on the
top, you get a diamond pickaxe. But if you put one diamond on the side of the middle, you
get a diamond axe.
The same thing goes to functions. A function accepts some arguments (sometimes none,
though). It expects each argument to be something. For example, a function that sets a block
in a world would accept two arguments, the first argument being the position to change, and
the second argument being the type of block to change into.
Functions are also like commands. Say, the /effect
command. You
have to provide a player in the first argument. The second argument is the effect type. You
can optionally also provide the third and fourth argument for duration and amplitude, but
the command will assume default values for you if you didn't provide them.
Just like commands, apart from arguments and return values, a function also has a name,
description and permission, although we instead call description
documentation and permission visibility.
The function's name has the same rules as classes, except that it starts with a
small letter
instead of a capital letter. That is, we use camelCase
rather than
PascalCase
.
There are three types of visibility for a function, namely public,
protected and private. Public functions can be used from anywhere. Private functions
can only be used when you are writing code from that class. As for protected functions, they are
like private functions, but also accessible by subclasses. What are subclasses? You
won't need to know that until you are making really complicated or high-quality plugins (e.g.
SimpleAuth).
When a function is inside a class, we also call it a "class method". But let's call it "function" to avoid confusion.
As you may have already noticed, this is the syntax of declaring a function:
function_visibility function function_name(arguments){ code_inside_the_function }
For instance:
public function myFunction($arg1, $arg2){ // some code here }
You might be asking what the //
means. It is the line comment
symbol. This means that everything on that line after the //
will
be ignored. This is useful when you want other people, or yourself a few months (or a few days)
later to easily understand what you are writing.
Server: chat.freenode.net:6667
Private message: PEMapModder
Channel: #pmplugins
This page is mainly authored by PEMapModder,
This webpage uses the highlight.js
library (and its rainbow theme) for syntax highlighting.