• 欢迎来到Minecraft插件百科!
  • 对百科编辑一脸懵逼?帮助:快速入门带您快速熟悉百科编辑!
  • 因近日遭受攻击,百科现已限制编辑,有意编辑请加入插件百科企鹅群:223812289

“Bukkit/插件开发教程”的版本间的差异

来自Minecraft插件百科
跳转至: 导航搜索
(翻译完毕)
(翻译)
第1行: 第1行:
{{模板:待翻译}}
 
本页面英文原文内容来源 [http://wiki.bukkit.org/Plugin_Tutorial]
 
== 介绍  ==
 
 
这篇内容丰富的教程旨在帮助你学会如何开发Bukkit插件.这一篇教程不可能完全涉及到Bukkit中所有的可能性,而是最基本的一般概述. 前提是你懂得Java这门语言,并在IDE中设置好你的工作区,此教程介绍了大多数Bukkit插件所必需的语法结构.
 
 
 
== 学习Java  ==
 
== 学习Java  ==
  
'''These tutorials require basic knowledge of the Java programming language.''' If you are just getting started with Java or need a refresher, the below is a non-exhaustive list.
+
'''理解这些教程需要具备Java这门编程语言的基本知识.'''如果你刚开始学习Java或者需要重温一下相关知识,下面有一个并不完整的网站列表可供参考.
 
 
<big>[http://docs.oracle.com/javase/tutorial/ Oracle's Java Tutorials]</big> - Official tutorials
 
*[http://www.java2s.com/Tutorial/Java/CatalogJava.htm Java2s.com] - Tutorials
 
*[http://www.javacoffeebreak.com/java101/java101.html Java 101] - Tutorials
 
*[http://math.hws.edu/javanotes/ JavaNotes] - Free online textbook
 
*[http://thenewboston.org/list.php?cat=31 TheNewBoston] - Video tutorials.
 
 
 
== 开发环境  ==
 
 
 
在研制一个插件前 (或学习Java前) you will need to set up a development environment. This includes but is not limited to installing an IDE (Integrated Development Environment). The following tutorial contains instructions for the Eclipse IDE.
 
 
 
:: ''For further information, see [[Setting Up Your Workspace]]''
 
 
 
You '''MUST''' download the Eclipse build for '''Java''' developers, '''NOT''' the one for '''Java EE''' developers. The version for Java EE developers does not ship Maven support, which is required for this tutorial.
 
 
 
== 新建插件开发项目  ==
 
=== 创建一个新的项目 ===
 
 
 
在开始工作之前,你需要先在Eclipse中配置好工作区和文件. 打开Eclipse,然后依次点击''File -> New -> Project:''来创建一个新的项目.
 
 
 
[[Image:Newproject.png]]
 
 
 
现在,打开''Maven''文件夹, 然后选择''Maven Project''.点击next,之后在下一个菜单中选择''Create a simple project'', 再次点击''Next'':
 
如果你看不到''Maven''文件夹, 那么你需要[http://eclipse.org/m2e/download/ 下载m2eclipse]
 
 
 
[[Image:Newproject2.png]]
 
 
 
现在,你需要给你的组用户命名,就像下面这样:
 
* 如果你拥有一个域名,package则填写逆转的域名地址.
 
** 例如: i-am-a-bukkit-developer.com 你的package地址即是com.i_am_a_bukkit_developer [http://docs.oracle.com/javase/tutorial/java/package/namingpkgs.html source]
 
** 避免使用一个不属于你自己的域名.
 
* 没有域名? 这里有几种方法可供选择。
 
*# 在资源管理站点创建一个用户,比如GitHub或是sourceforge
 
*#* 对于使用GitHub的用户, 请参照[http://pages.github.com/ 这里的说明] 之后你将获得一个子域名,所以你的package地址是io.github.<username><br />
 
*# 使用你的邮箱.  例如: <username>@gmail.com格式的邮箱应输入com.gmail.<username><br />
 
*# 最不济的方法.:  使用独特的组名命名方式,这应当是你最后的解决方法。
 
 
 
以下几个地址不能作为package中的地址前缀:
 
* org.bukkit
 
* net.bukkit
 
* com.bukkit
 
* net.minecraft
 
 
 
完成基础的组名以后,你需要在最后加上插件的名称. 在这里用 GitHub页面作为讲解的实例.  如果你创建了一个名为 ''TestPlugin''的插件,那么完整的组名是''io.github.<username>'', 你的工程名也是 ''TestPlugin''. 至于版本,默认即可,稍后可以修改。
 
完成向导:
 
 
 
[[Image:Newproject3.png]]
 
 
 
如果这是你首次使用Eclipse, 点击右上角的"X" 来关闭Welcome提示页面. 现在,你的窗口视图看起来就像下面的图片这样:
 
 
 
[[Image:Eclipsemain.png]]
 
 
 
点击工程名称右边的箭头来进行下一步,现在我们正式开始。
 
=== 添加Bukkit API ===
 
 
 
在开发插件之前,你需要添加 Bukkit API库文件到你的项目,作为一个dependency, 你也可以添加其他你想实用的API.
 
 
 
找到项目文件夹中部的''pom.xml''并双击进行编辑. 点击''pom.xml''中部的 tab,你将会看到下图所示内容:
 
 
 
[[Image:Pomeditor.png]]
 
 
 
如果你想使用Java 6以上版本的语言特性,你需要指定搭建项目的Java版本.复制并粘贴以下内容(设定项目只能使用Java 7及以下的版本)到 ''</project>''之前:
 
 
 
<code>
 
    <build>
 
      <plugins>
 
          <plugin>
 
              <groupId>org.apache.maven.plugins</groupId>
 
              <artifactId>maven-compiler-plugin</artifactId>
 
              <configuration>
 
                  <nowiki>&lt;</nowiki>source>1.7</source>
 
                  <target>1.7</target>
 
              </configuration>
 
          </plugin>
 
      </plugins>
 
    </build>
 
</code>
 
 
 
你或许想使用其他版本,例如 <code>1.8</code> 使用Java 8. 请注意 [http://mcstats.org/global/ 根据MCStats数据统计], 大多数服主选择了Java 7, 所以使用Java 8会使许多服务器无法运行你的插件. 如果你使用Java 1.7的特性, Eclipse将会在报错代码"error"中建议你更改语言版本.同意即可.
 
 
 
在位于代码中段的''</project>''之前粘贴以下内容(这段代码告诉Eclipse关于Bukkit's repository的地址):
 
 
 
<code>
 
    <repositories>
 
        <repository>
 
            <id>bukkit-repo</id>
 
            <url>http://repo.bukkit.org/content/groups/public/</url>
 
        </repository>
 
    </repositories>
 
</code>
 
 
 
然后继续在 ''</project>''前粘贴以下内容 (这段代码告诉Eclipse搭建插件的Bukkit版本):
 
 
 
<code>
 
    <dependencies>
 
        <dependency>
 
            <groupId>org.bukkit</groupId>
 
            <artifactId>bukkit</artifactId>
 
            <version>1.7.2-R0.3</version>
 
            <type>jar</type>
 
            <scope>provided</scope>
 
        </dependency>
 
    </dependencies>
 
</code>
 
 
 
你也可以修改这里的Bukkit版本.你可以在[http://repo.bukkit.org/content/groups/public/org/bukkit/bukkit/ here]这里通过查看 maven-metadata.xml文件下的版本列表来获取可用的服务端版本号.
 
 
 
当你完成上述步骤后,''pom.xml''中的内容应该是这样:
 
 
 
[[Image:Finishedpom.png]]
 
 
 
通过菜单 ''File -> Save''或者按住 <code>Ctrl + S</code> 来保存''pom.xml''文件 .  之后, 右键项目标题,然后依次选择 ''Maven -> Update Project''.
 
 
 
=== Bukkit Javadocs ===
 
 
 
If you have some experience with Eclipse and Java you will know that when you hover your mouse over any built in class or method a yellow box will pop up, containing the documentation for that class or method. This is known as a Javadoc and can also be accessed online at the [http://download.oracle.com/javase/6/docs/api/ Oracle website]. Bukkit also has documentation which often contains useful descriptions of each method and class provided by the API, which is available [http://jd.bukkit.org/apidocs/ here] (Beta Javadocs can be found [http://jd.bukkit.org/beta/apidocs/ here], and development build Javadocs [http://jd.bukkit.org/dev/apidocs/ here]). In order to have this information available within Eclipse, so that it will pop up whenever you hover over a Bukkit method or class, first right click on the Bukkit jar where it appears under "Maven Dependencies" in the project explorer, and select "Properties". Choose the ''Javadoc Location'' item on the left of the window that pops up, and paste the url '''http://jd.bukkit.org/apidocs/''' (or that of the beta/development Javadocs linked above) into the textbox under "Javadoc URL". It should look like this:
 
 
 
[[Image:Bukkitjavadocs.png]]
 
 
 
Click "Validate", and then click "OK". Done! Now the Bukkit Javadocs are linked to the Bukkit source, and you can access helpful documentation from within Eclipse.
 
 
 
=== Creating a package  ===
 
 
 
Now you need to create a 'package' which will store all the Java class files we will be using. Right click on the folder labelled ''src/main/java'' and select ''New &gt; Package'':
 
 
 
[[Image:Newpackage.png]]
 
 
 
For your package name, put your group name, then a period, and then your artifact name in lowercase. For example, if your group name is ''io.github.name'' and your artifact name is ''TestPlugin'', your package name would be ''io.github.name.testplugin''.
 
 
 
=== Creating the Plugin's Class ===
 
 
 
Now that we have our project set up we can start to add class files and begin making our plugin. The plugin's main class is the class that extends JavaPlugin. There should only ever be one class in your plugin that extends JavaPlugin either directly or indirectly. It's always good practice to create your main class first and give it the same name as your plugin. Right click on the package you created before, and select&nbsp; ''New &gt; Class''.
 
You should have a new class similar to the following
 
<blockquote><source lang="java">package {$GroupName}.{$ArtifactName};
 
 
 
import org.bukkit.plugin.java.JavaPlugin;
 
 
 
public final class {$ArtifactName} extends JavaPlugin {
 
   
 
}</source></blockquote>
 
 
 
{{warning | Plugins should never invoke their constructor and create new instances}}
 
 
 
=== Creating plugin.yml ===
 
 
 
Now you have setup the project and the main class. To allow Bukkit to load your plugin, you must create the '''[[Plugin YAML|plugin.yml]]''' file. This file will contain essential information, and without it your plugin will NOT work. This time we want to right click on ''src/main/resources''. Select ''New &gt; File''. Name the file "'''plugin.yml'''" and click finish. Eclipse will open your currently blank '''plugin.yml''' file in the default text editor. ''(Hint:&nbsp;If you want to keep your workspace organized, close the text editor and drag the '''plugin.yml''' file onto the main workspace(To the right) and you will be able to edit the file inside eclipse.)''
 
 
 
There are three essential attributes that must be declared in the plugin.yml.
 
: name:  the simple name of your plugin.
 
: main: [http://docs.oracle.com/javase/specs/jls/se7/html/jls-6.html#jls-6.7 fully qualified name] of the plugin's main class.
 
: version: the version string of your plugin.
 
 
 
The most simple '''plugin.yml''' file would look like this&nbsp;:
 
<blockquote><source lang="yaml">name: {$PluginName}
 
main: {$PackageName}.{$MainClass}
 
version: {$VersionNumber}</source></blockquote>
 
{{note|  The package name for plugins often includes the plugin name so don't be surprised to see &lt;pluginname&gt;.&lt;pluginname&gt; at the end of the second line!}}
 
{{note| Your main class may or may not be the name of your plugin depending on what you named it earlier, keep in mind this is case-sensitive.}}
 
 
 
'''''For more examples''', see [[#Example_Files_and_Templates]]''
 
 
 
At this point your plugin can be loaded by Bukkit, and will be accompanied with log entries indicating this. But, it will do nothing!
 
 
 
== onEnable() and onDisable()  ==
 
 
 
These methods are called whenever the plugin is enabled and disabled. By default your plugin will automatically enable itself when loaded so you can register your events and provide some debug output here. <code>onEnable()</code> is invoked when the plugin is enabled, and should contain logic to set up your plugin when it is enabled. <code>onDisable()</code> is invoked when a plugin is disabled, and should contain logic to clean up your plugin and associated state. Additionally plugins can override the <code>onLoad()</code> method to perform additional logic when the plugin loads.
 
 
 
=== Introduction to onEnable() and onDisable()  ===
 
 
 
Create the methods <code>onEnable()</code> and <code>onDisable()</code> inside the main class created in the previous section. It will look something like the following
 
<blockquote><source lang="java">package {$TopLevelDomain}.{$Domain}.{$PluginName};
 
 
 
import org.bukkit.plugin.java.JavaPlugin;
 
 
 
public final class {$PluginName} extends JavaPlugin {
 
    @Override
 
    public void onEnable() {
 
        // TODO Insert logic to be performed when the plugin is enabled
 
    }
 
   
 
    @Override
 
    public void onDisable() {
 
        // TODO Insert logic to be performed when the plugin is disabled
 
    }
 
}</source></blockquote>
 
 
 
The methods now exist, but they don't do anything yet.
 
Note: There is no reason to print a message such as "{$PluginName} has been enabled!" as bukkit will do this automatically.
 
For more information about events, click [[Event_API_Reference|here]].
 
 
 
=== Logging a message  ===
 
 
 
A plugin can print a message to the console and the server log. It can accomplish this by invoking the correct method from the plugin's logger. First we must invoke the <code>getLogger()</code> method to retrieve the logger associate with this plugin. Then we can start logging.
 
 
 
We will write to the log when <code>onEnable()</code> method is called. We can do that by inserting the following line into the <code>onEnable()</code> method.
 
<blockquote><source lang="java">getLogger().info("onEnable has been invoked!");</source></blockquote>
 
You can then do the same inside <code>'''onDisable()'''</code>, making sure to change the message.
 
 
 
Your main class should now look something like this:
 
<blockquote><source lang="java">package {$TopLevelDomain}.{$Domain}.{$PluginName};
 
 
 
import org.bukkit.plugin.java.JavaPlugin;
 
 
 
public final class {$PluginName} extends JavaPlugin {
 
@Override
 
public void onEnable() {
 
getLogger().info("onEnable has been invoked!");
 
}
 
 
 
@Override
 
public void onDisable() {
 
getLogger().info("onDisable has been invoked!");
 
}
 
}</source></blockquote>
 
 
 
=== Handling Reloads ===
 
 
 
It is important to remember that this does not only occur on server shutdown and startup, your plugin can also be disabled and enabled by other plugins or through use of the /reload command while the server is running. Assuming that the server has only just been started when the plugin is enabled is therefore a dangerous assumption, as there may well already be players online, additional worlds loaded, additional chunks loaded, and many other unexpected differences.
 
 
 
For example:
 
* You have a plugin that stores information about a player in a HashMap on the PlayerJoinEvent
 
* You rely on having that information available for every player
 
* An operator uses the /reload command
 
* Your plugin is disabled and all data stored is lost
 
* Your plugin is enabled again with several players already online
 
* These players do not have any information stored for them in the HashMap
 
* You try to retrieve information about them but there is none!
 
 
 
For this to work correctly on reload, you would need to find all players currently online during onEnable and store the correct information for that player in the HashMap.
 
<blockquote><source lang="java">for (Player player : Bukkit.getServer().getOnlinePlayers()) {
 
    playerList.put(player.getName(), playerData(player));
 
}</source></blockquote>
 
 
 
== Listeners  ==
 
Listeners are classes whose methods are invoked in response to an event. All listeners implement org.bukkit.event.Listener. For further details about creating listeners,
 
:: '' Please See: [[Event API Reference]]''
 
 
 
== Commands  ==
 
 
 
=== The onCommand() Method  ===
 
 
 
So, you now know how to register events and do something when they happen, but what if you only want something to happen when a command is typed? You use <code>'''onCommand'''</code>. This code is run whenever a player types a command preceded by the "/" character. E.g. typing "/do something" would call the <code>'''onCommand'''</code> method. In this case nothing would happen because no behaviour has been programmed.
 
 
 
Avoid using command names that are the same as those that come with Bukkit, and also consider carefully how unique your commands names will be. E.g. the "give" command is already used by several plugins, and if you implement yet another "give" command, your plugin will become incompatible with those other plugins. You must register your commands in the plugin's '''plugin.yml''' or they will not trigger this method.
 
 
 
The <code>'''onCommand'''</code> method must always return a boolean value - either true or false. If the value returned is true then you won't see any noticable events. However if it returns false then the plugin will revert to your plugin files' 'usage: property' and display a message to the user showing them how to use the command as specified in the '''plugin.yml''' file.
 
 
 
When using <code>'''onCommand'''</code>, you should always register 4 parameters.
 
 
 
*<code>'''CommandSender sender'''</code> - who sent the command
 
 
 
*<code>'''Command cmd'''</code> - the command that was executed
 
 
 
*<code>'''String commandLabel'''</code> - the command alias that was used
 
 
 
*<code>'''String[] args'''</code> - an array of additional arguments, e.g. typing ''/hello abc def'' would put ''abc'' in args[0], and ''def'' in args[1]
 
 
 
==== Setting up the command  ====
 
<blockquote><source lang="java">
 
@Override
 
public boolean onCommand(CommandSender sender, Command cmd, String label, String[] args) {
 
if (cmd.getName().equalsIgnoreCase("basic")) { // If the player typed /basic then do the following...
 
// doSomething
 
return true;
 
} //If this has happened the function will return true.
 
        // If this hasn't happened the value of false will be returned.
 
return false;
 
}</source> </blockquote>
 
When coding the <code>'''onCommand'''</code> function it is always good practice to return false at the very end of the function. Returning false will display the usage dialog set in '''plugin.yml''' (see below). This way if anything goes wrong the help message will be displayed. When returning a value the function will exit so if you return true any code underneath won't be run, unless a return statement is nested in an if statement or similar.
 
 
 
The <code>'''.equalsIgnoreCase("basic")'''</code> just means that it won't distinguish between upper and lower case characters. For example, the string "BAsIc" and "BasiC" would both equal basic and the code would be executed.
 
 
 
Add also these two lines at the top of your file:
 
<blockquote><source lang="java">
 
import org.bukkit.command.Command;
 
import org.bukkit.command.CommandSender;
 
</source> </blockquote>
 
 
 
=== Adding your Command to the Plugin.yml  ===
 
 
 
You will also need to add the command to your '''plugin.yml''' file. Add the following to the end of '''plugin.yml''':
 
<blockquote><code><source lang="yaml">commands:
 
  basic:
 
      description: This is a demo command.
 
      usage: /<command> [player]
 
      permission: <plugin name>.basic
 
      permission-message: You don't have <permission></source> </code> </blockquote>
 
*<code>'''basic'''</code> - the name of the command.
 
 
 
*<code>'''description'''</code> - the description of the command .
 
 
 
*<code>'''usage'''</code> - the help dialog that users will see when you return false in the <code>'''onCommand'''</code> method. Write clearly, so that others can discern what the command is and how to use it.
 
 
 
*<code>'''permission'''</code> - This is used by some help plugins to work out which commands to show to the user.
 
 
 
*<code>'''permission-message'''</code> - This is output when the player attempts but does not have permission to use the command.
 
 
 
Note that yml files use 2 spaces for tabs, as the tab character will cause problems.
 
 
 
=== Console Commands vs. Player Commands  ===
 
 
 
You may have noticed the <code>'''CommandSender sender'''</code> parameter above. <code>'''CommandSender'''</code> is a Bukkit interface which has two useful (for plugin writers) subclasses: <code>'''Player'''</code> and <code>'''ConsoleCommandSender'''</code>.
 
 
 
When you're writing your plugin, it's a very good idea to ensure that commands that ''can'' be run from the console actually work, and that commands that should only be run as a logged-in player really ''are'' only run as a logged-in player. Some plugins simply return if the sender is not a player (i.e. someone tried to use the plugin's commands from the console), even when those commands make perfect sense from the console (e.g. changing the weather on the server).
 
 
 
One way to do this is:
 
<blockquote><source lang="java">
 
@Override
 
public boolean onCommand(CommandSender sender, Command cmd, String label, String[] args) {
 
if (cmd.getName().equalsIgnoreCase("basic")) { // If the player typed /basic then do the following...
 
// do something...
 
return true;
 
} else if (cmd.getName().equalsIgnoreCase("basic2")) {
 
if (!(sender instanceof Player)) {
 
sender.sendMessage("This command can only be run by a player.");
 
} else {
 
Player player = (Player) sender;
 
// do something
 
}
 
return true;
 
}
 
return false;
 
}
 
</source> </blockquote>
 
In this example, the command '''basic''' can be run by anyone - a logged-in player, or the server operator on the console. But the command '''basic2''' can only be run by logged-in players.
 
 
 
In general, you should allow as many commands as possible to work on both the console and for players. Commands that ''need'' a logged-in player can use the mechanism in the example above to check that the <code>'''CommandSender'''</code> is actually a player before continuing. Such commands would generally depend on some attribute of the player, e.g. a teleportation command needs a player to teleport, an item giving command needs a player to give the item to...
 
 
 
If you want to get more advanced, you could do some extra checks on your command arguments so that e.g. a teleportation command could be used from the console ''if and only if'' a player's name is also supplied.
 
 
 
=== Using a separate CommandExecutor class  ===
 
 
 
The examples above just put the <code>'''onCommand()'''</code> method into the plugin's main class. For small plugins, this is fine, but if you're writing something more extensive, it may make sense to put your <code>'''onCommand()'''</code> method into its own class. Fortunately, this isn't too hard:
 
 
 
*Create a new class within your plugin's package. Call it something like '''MyPluginCommandExecutor''' (although of course replacing '''MyPlugin''' with your plugin's actual name). That class ''must'' implement the Bukkit '''CommandExecutor''' interface.
 
 
 
*In your plugin's <code>'''onEnable()'''</code> method, you need to create an instance of your new command executor class, and then make a call like <code>getCommand("basic").setExecutor(myExecutor);</code>, where "basic" is the command we want to handle, and <code>myExecutor</code> is the instance we created.
 
 
 
Best explained by example:
 
 
 
'''MyPlugin.java''' (the main plugin class):
 
<blockquote><source lang="java">
 
@Override
 
public void onEnable() {
 
// This will throw a NullPointerException if you don't have the command defined in your plugin.yml file!
 
this.getCommand("basic").setExecutor(new MyPluginCommandExecutor(this));
 
}
 
</source></blockquote>
 
'''MyPluginCommandExecutor.java''':
 
<blockquote><source lang="java">
 
public class MyPluginCommandExecutor implements CommandExecutor {
 
private final MyPlugin plugin;
 
 
 
public MyPluginCommandExecutor(MyPlugin plugin) {
 
this.plugin = plugin; // Store the plugin in situations where you need it.
 
}
 
 
 
@Override
 
public boolean onCommand(CommandSender sender, Command cmd, String label, String[] args) {
 
// implementation exactly as before...
 
}
 
}
 
</source></blockquote>
 
Notice how we send a reference of the main plugin object to '''MyPluginCommandExecutor'''. This allows us easy access to the main plugin objects's methods if we need to.
 
 
 
By doing this, we can better organise our code - if the main <code>'''onCommand()'''</code> method is large and complex, it can be split into submethods without cluttering up the plugin's main class.
 
 
 
Note that if your plugin has multiple commands, you will need set the command executor for each command individually.
 
 
 
=== Writing a safe onCommand ===
 
When writing an onCommand, it's important that you don't assume any information, such as the sender being a Player. Things to keep in mind:
 
 
 
==== Make sure the sender is a Player before casting ====
 
Using simple code like this makes it possible:
 
<blockquote><source lang="java">
 
@Override
 
public boolean onCommand(CommandSender sender, Command cmd, String label, String[] args) {
 
if (sender instanceof Player) {
 
          Player player = (Player) sender;
 
          // do something
 
        } else {
 
          sender.sendMessage("You must be a player!");
 
          return false;
 
        }
 
        // do something
 
        return false;
 
}</source> </blockquote>
 
 
 
==== Check the arguments length ====
 
Don't always assume the sender typed the correct amount of arguments.
 
<blockquote><source lang="java">
 
@Override
 
public boolean onCommand(CommandSender sender, Command cmd, String label, String[] args) {
 
if (args.length > 4) {
 
          sender.sendMessage("Too many arguments!");
 
          return false;
 
        }
 
        if (args.length < 2) {
 
          sender.sendMessage("Not enough arguments!");
 
          return false;
 
        }
 
}</source> </blockquote>
 
 
 
==== Check if a Player is online before getting them ====
 
Sometimes you want to get another player by the name entered by the player. Always make sure the player is online!
 
<blockquote><source lang="java">
 
@Override
 
public boolean onCommand(CommandSender sender, Command cmd, String label, String[] args) {
 
Player target = (Bukkit.getServer().getPlayer(args[0]));
 
        if (target == null) {
 
          sender.sendMessage(args[0] + " is not online!");
 
          return false;
 
        }
 
        return false;
 
}</source> </blockquote>
 
If you need to modify a Player currently not online, the <code>'''OfflinePlayer'''</code> class provides basic manipulation methods.
 
 
 
== Plugin Configuration/Settings  ==
 
The Bukkit API provides a convenient way for plugins to manage user configurable settings. Additionally it also serves as an easy way to store data.
 
:: ''Please see: [[Configuration API Reference]]''
 
 
 
== Permissions  ==
 
 
 
With the new Bukkit API for permissions, they couldn't be easier. To find out if a player has a particular permission use the following:
 
<blockquote><source lang="java">if (player.hasPermission("some.pointless.permission")) {
 
  //Do something
 
} else {
 
  //Do something else
 
}</source> </blockquote>
 
You can also find if a permission has been set or not (equivalent to Java's '''null''') with the following function:
 
<blockquote><source lang="java">boolean isPermissionSet(String name)</source></blockquote>
 
You may be wondering why there aren't any groups. The answer to that is because they aren't really needed. Previously one of the main uses for groups was to format chat messages. That however can be done just as easily with permissions. Inside your chat plugin's config you would define associations between permissions and prefixes. For example the permission "someChat.prefix.admin" would correspond to the prefix [Admin]. Whenever a player speaks with that permission their name will be prefixed with [Admin].
 
 
 
Another common usage might be to send a message to all users within a group. Again however this can be done with permissions with the following:
 
<blockquote><source lang="java">for (Player player: Bukkit.getServer().getOnlinePlayers()) {
 
    if (player.hasPermission("send.receive.message")) {
 
        player.sendMessage("You were sent a message");
 
    }
 
}</source> </blockquote>
 
Finally you may be asking, well how do I set and organise player's permissions if there are no groups? Although the bukkit API doesn't provide groups itself, you must install a permission provider plugin such as permissionsBukkit to manage the groups for you. '''This API provides the interface, not the implementation.'''
 
 
 
=== Configuring your permissions  ===
 
 
 
If you want more control over your permissions, for example default values or children then you should consider adding them to your ''[[plugin.yml]]''. This is completely optional, however it is advised. Below is an example permissions config that would be appended to the end of your existing ''plugin.yml'':
 
<blockquote><code><source lang="yaml">permissions:
 
    doorman.*:
 
        description: Gives access to all doorman commands
 
        children:
 
            doorman.kick: true
 
            doorman.ban: true
 
            doorman.knock: true
 
            doorman.denied: false
 
    doorman.kick:
 
        description: Allows you to kick a user
 
        default: op
 
    doorman.ban:
 
        description: Allows you to ban a user
 
        default: op
 
    doorman.knock:
 
        description: Knocks on the door!
 
        default: true
 
    doorman.denied:
 
        description: Prevents this user from entering the door</source> </code> </blockquote>
 
Firstly, each permission your plugin uses is defined as a child node of the ''permissions'' node. Each permission can then optionally have a description, a default value, and children.
 
 
 
==== Defaults  ====
 
 
 
By default when a permission isn't defined for a player&nbsp;''hasPermission''&nbsp;will return false. Inside your plugin.yml you can change this by setting the default node to be one of four values:
 
 
 
*'''true''' - The permission will be true by default.
 
*'''false''' - The permission will by false by default.
 
*'''op''' - If the player is an op then this will be true.
 
*'''not op''' - If the player is not an op then this will be true.
 
 
 
==== Children  ====
 
 
 
Before now you will probably be used to the * permission to automatically assign all sub permissions. This has changed with the bukkit API and you can now define the child permissions. This allows for a lot more flexibility. Below is an example of how you do this:
 
<blockquote><source lang="yaml">permissions:
 
    doorman.*:
 
        description: Gives access to all doorman commands
 
        children:
 
            doorman.kick: true
 
            doorman.ban: true
 
            doorman.knock: true
 
            doorman.denied: false</source> </blockquote>
 
Here the ''doorman.*'' permission has several child permissions assigned to it. The way child permissions work is when ''doorman.*'' is set to true, the child permissions are set to their values defined in the ''plugin.yml''. If however ''doorman.*'' was set to false then all child permissions would be inverted.
 
 
 
=== Setting your own permissions  ===
 
 
 
If you wish to know about developing your own permissions plugins (Ones that actually set permissions) then check out the tutorial on&nbsp;[[Developing a permissions plugin]].
 
 
 
== Scheduling Tasks and Background Tasks  ==
 
 
 
Currently, Minecraft servers operate nearly all of the game logic in one thread, so each individual task that happens in the game needs to be kept very short. A complicated piece of code in your plugin has the potential to cause huge delays and lag spikes to the game logic, if not handled properly.
 
 
 
Luckily, Bukkit has support for scheduling code in your plugin. You can submit a Runnable task to occur once in the future, or on a recurring basis, or you can spin off a whole new independent thread that can perform lengthy tasks in parallel with the game logic.
 
 
 
There is a separate [[Scheduler Programming]] tutorial which introduces the Scheduler, and gives more information on using it to schedule synchronous tasks, and on kicking off asynchronous tasks in Bukkit.
 
 
 
== Block Manipulation<br>  ==
 
 
 
The easiest way to create blocks is to get an existing block and modify it. For example, if you want to change the block that is located five blocks above you, you would first have to get your current location, add five to your current y-coordinate, and then change it. For example:
 
<blockquote><source lang="java">
 
@EventHandler
 
public void onPlayerMove(PlayerMoveEvent event) {
 
    // Get the player's location.
 
    Location loc = event.getPlayer().getLocation();
 
    // Sets loc to five above where it used to be. Note that this doesn't change the player's position.
 
    loc.setY(loc.getY() + 5);
 
    // Gets the block at the new location.
 
    Block b = loc.getBlock();
 
    // Sets the block to type id 1 (stone).
 
    b.setType(Material.STONE);
 
}</source> </blockquote>
 
The above code gets the player's location, gets the block five above it, and sets it to stone. Note that once you have a <code>Block</code>, there are other things you can do besides set its type. Consult the JavaDocs for more information.
 
 
 
You can use a similar concept to generate buildings and individual blocks programmatically through the use of algorithms. For example, to generate a solid cube, you could use nested <code>for</code> loops to loop over an entire cube and fill it in.
 
<blockquote><source lang="java">
 
public void generateCube(Location loc, int length) {
 
    // Set one corner of the cube to the given location.
 
    // Uses getBlockN() instead of getN() to avoid casting to an int later.
 
    int x1 = loc.getBlockX();
 
    int y1 = loc.getBlockY();
 
    int z1 = loc.getBlockZ();
 
 
 
    // Figure out the opposite corner of the cube by taking the corner and adding length to all coordinates.
 
    int x2 = x1 + length;
 
    int y2 = y1 + length;
 
    int z2 = z1 + length;
 
 
 
    World world = loc.getWorld();
 
 
 
    // Loop over the cube in the x dimension.
 
    for (int xPoint = x1; xPoint <= x2; xPoint++) {
 
        // Loop over the cube in the y dimension.
 
        for (int yPoint = y1; yPoint <= y2; yPoint++) {
 
            // Loop over the cube in the z dimension.
 
            for (int zPoint = z1; zPoint <= z2; zPoint++) {
 
                // Get the block that we are currently looping over.
 
                Block currentBlock = world.getBlockAt(xPoint, yPoint, zPoint);
 
                // Set the block to type 57 (Diamond block!)
 
                currentBlock.setType(Material.DIAMOND_BLOCK);
 
            }
 
        }
 
    }
 
}</source></blockquote>
 
This method will construct a 3D cube or cuboid with the given length and starting point. As for deleting blocks simply follow the same method for creating them but set the ID to 0 (air).<br>
 
<blockquote></blockquote>
 
 
 
== (Player) Inventory Manipulation  ==
 
 
 
This section mostly covers player inventory manipulation, but the same applies to chest inventory manipulation as well if you find out how to get a chest's inventory&nbsp;:P. Here is a simple example of inventory manipulation:
 
<blockquote><source lang="java">public void onPlayerJoin(PlayerJoinEvent evt) {
 
    Player player = evt.getPlayer(); // The player who joined
 
    PlayerInventory inventory = player.getInventory(); // The player's inventory
 
    ItemStack itemstack = new ItemStack(Material.DIAMOND, 64); // A stack of diamonds
 
       
 
    if (inventory.contains(itemstack)) {
 
        inventory.addItem(itemstack); // Adds a stack of diamonds to the player's inventory
 
        player.sendMessage("Welcome! You seem to be reeeally rich, so we gave you some more diamonds!");
 
    }
 
}</source> </blockquote>
 
So inside onPlayerJoin we first make a few variables to make our job easier: player, inventory and itemstack. Inventory is the player's inventory and itemstack is a ItemStack that has 64 diamonds. After that we check if the player's inventory contains a stack of diamonds. If the player has a stack of diamonds, we give him/her another stack with inventory.addItem(itemstack) and send a message. So inventory manipulation isn't actually that hard, if we wanted we could remove the stack of diamonds by simply replacing inventory.addItem(itemstack) with inventory.remove(itemstack) and change the message a little bit. Hopefully this helped!
 
 
 
== Item Manipulation ==
 
 
 
When dealing with items in the code, you use the ItemStack class for looking up and setting all information on that stack.
 
 
 
=== Enchantments ===
 
 
 
To enchant an item you must first know the [http://www.minecraftwiki.net/wiki/Data_values Item Code] and the [http://www.minecraftwiki.net/wiki/Talk:Enchanting#Effect_IDs Effect ID].  Enchantments themselves cannot be instantiated (new Enchantment() won't work) because they're abstract, so you must use an EnchantmentWrapper. If you want to enchant items that can't be enchanted inside normal SMP, use addUnsafeEnchantment() instead of addEnchantment()
 
 
 
<source lang="java">
 
int itemCode = 280;  //use the item code you want here
 
int effectId = 20;  //use the enchantment code you want here
 
int enchantmentLevel = 100;
 
 
 
ItemStack myItem = new ItemStack(itemCode);  //new item of item code
 
Enchantment myEnchantment = new EnchantmentWrapper(effectId);  //new enchantment of effect id
 
myItem.addEnchantment(myEnchantment, enchantmentLevel);  //enchant the item
 
</source>
 
 
 
=== ItemMeta ===
 
You can set the display name of an item by doing this.
 
<source lang="java">
 
String myDisplayName = "Awesome Sword"; //use the displayname you want here
 
 
ItemStack myItem = new ItemStack(Material.DIAMOND_SWORD);  //your item
 
ItemMeta im = myItem.getItemMeta(); //get the itemmeta of the item
 
im.setDisplayName(myDisplayName); //set the displayname
 
myItem.setItemMeta(im); //give the item the new itemmeta
 
</source>
 
You can also set the lores of an item. The lores are the small annotations on an item, like "+5 attack damage" on a stone sword.
 
<source lang="java">
 
List<String> lores = new ArrayList<String>();
 
lores.add("Example lore", "this one comes on line 2");
 
 
 
ItemStack myItem = new ItemStack(Material.DIAMOND_SWORD);  //your item
 
ItemMeta im = myItem.getItemMeta(); //get the itemmeta of the item again
 
im.setLore(lores); //add the lores of course
 
myItem.setItemMeta(im); //give the item the new itemmeta
 
</source>
 
 
 
== Maps, and Sets, and Lists, Oh My!  ==
 
 
 
Besides the Map/HashMap classes, Java offers many other data structures. They offer these different classes because there are times when a Map is not the most appropriate. Here's a separate page for discussing [[Java data structure classes]] in more detail.
 
 
 
=== HashMaps and How to Use Them  ===
 
<big>
 
'''Keep in mind to never use a player in a hashmap!'''
 
You need to use Strings instead. So use "p.getName()" to add, remove or check if a list contains a player.
 
Saving a player as an object causes huge memory leaks.
 
</big>
 
 
 
When making a plugin you will get to a point where just using single variables to state an event has happened or a condition has been met will be insufficient, due to more than one player performing that action/event.
 
 
 
This was the problem I had with one of my old plugins, Zones, now improved and re-named to Regions. I was getting most of these errors because I didn't consider how the plugin would behave on an actual server with more than one on at any given time. I was using a single boolean variable to check whether players were in the region or not and obviously this wouldn't work as the values for each individual player need to be separate. So if one player was in a region and one was out the variable would constantly be changing which could/would/did cause numerous errors.
 
 
 
A HashMap is an excellent way of doing this. A HashMap is a way of mapping/assigning a value to a key. You could set up the HashMap so that the key is a player and the value could be anything you want, however the useful things with HashMaps is that one key can only contain one value and there can be no duplicate keys. So say for example I put "adam" as the key and assigned a value of "a" to it. That would work as intended, but then say afterwards I wanted to assign the value of "b" to key "adam" I would be able to and would get no errors but the value of "a" assigned to key "adam" in the HashMap would be overwritten because HashMaps cannot contain duplicate values.
 
 
 
==== Defining a HashMap  ====
 
<blockquote><source lang="java">public Map<KeyType, DataType> HashMapName = new HashMap<>(); //Example syntax
 
 
 
// Example Declaration
 
public Map<String, Boolean> pluginEnabled = new HashMap<>();
 
public Map<String, Boolean> isGodMode = new HashMap<>();
 
</source> </blockquote>
 
Keep that code in mind because we will be using it for the rest of the tutorial on HashMaps. So, for example lets create a simple function which will toggle whether the plugin has been enabled or not. Firstly, inside your on command function which I explained earlier you will need to create a function to send the player name to the function and adjust the players state accordingly.
 
 
 
So inside on command you'll need this, the function name can be different but for the sake of simplicity it's best if you keep it the same.
 
<blockquote><source lang="java">Player player = (Player) sender;
 
togglePluginState(player);</source> </blockquote>
 
This code above will cast the value of sender to player and pass that argument to the function togglePluginState(). But now we need to create our togglePluginState() function.
 
<blockquote><source lang="java">public void togglePluginState(Player player) {
 
    // Notice how we use the player name as the key here,
 
    // not the player object
 
    String playerName = player.getName();
 
    if (pluginEnabled.containsKey(playerName)) {
 
        if (pluginEnabled.get(playerName)) {
 
            pluginEnabled.put(playerName, false);
 
            player.sendMessage("Plugin disabled");
 
        } else {
 
            pluginEnabled.put(playerName, true);
 
            player.sendMessage("Plugin enabled");
 
        }
 
    } else {
 
        pluginEnabled.put(playerName, true); //If you want plugin disabled by default change this value to false.
 
        player.sendMessage("Plugin enabled");
 
    }
 
}</source> </blockquote>
 
Now, what this code is doing is checking if the HashMap first contains the key player, so if it has been put into the HashMap, if it is then we check the value of the HashMap key by get(player); if this is true then set value to false and send the player a message, else if the value is false then do the opposite, set the value to true and send a message again. But if the HashMap does not contain the key player then we can assume that this is their first run/use so we change the default value and add the player to the HashMap.
 
 
 
==== More Ideas for HashMaps  ====
 
 
 
A HashMap (or really any kind of Map in Java) is an association. It allows quick and efficient lookup of some sort of '''value''', given a unique '''key'''. Anywhere this happens in your code, a Map may be your solution.
 
 
 
Here are a few other ideas which are ideally suited to using Maps. As you will see, it doesn't have to be data that you store per player, but can be any kind of data that needs to be "translated" from one form to another.
 
 
 
===== Data Value Lookups  =====
 
 
 
<blockquote><source lang="java">
 
public Map<String, Integer> wool_colors = new HashMap<>();
 
 
 
// Run this on plugin startup (ideally reading from a file instead of copied out row by row):
 
wool_colors.put("orange", 1);
 
wool_colors.put("magenta", 2);
 
wool_colors.put("light blue", 3);
 
  ..
 
wool_colors.put("black", 15);
 
 
 
// Run this in response to user commands - turn "green" into 13
 
int datavalue = 0;
 
if (wool_colors.containsKey(argument)) {
 
    datavalue = wool_colors.get(argument);
 
} else {
 
    try { datavalue = Integer.parseInt(argument); }
 
    catch (Exception e) {}
 
}
 
</source> </blockquote>
 
 
 
==== Saving/Loading a HashMap  ====
 
 
 
Once you know how to work with HashMaps, you probably want to know how to save and load the HashMap data. Saving and loading HashMap data is appropriate if
 
 
 
*you don't want an administrator to edit the data manually
 
 
 
*you need to save data in binary format (too complex to organize for YAML)
 
 
 
*you want to avoid parsing block names and/or other objects from freeform text
 
 
 
This is very simple way how to save any HashMap. You can replace HashMap&lt;String, Integer&gt; with any type of HashMap you want. Let's take an example HashMap with code to save it:
 
<blockquote><source lang="java">
 
        HashMap<String, Integer> mapToSave = new HashMap<String,Integer>();
 
        public void save(HashMap<String, Integer> map, String path) {
 
try {
 
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(path));
 
oos.writeObject(map);
 
oos.flush();
 
oos.close();
 
//Handle I/O exceptions
 
} catch(Exception e) {
 
e.printStackTrace();
 
}
 
}
 
 
 
// ...
 
 
 
save(mapToSave, getDataFolder() + File.separator + "example.bin");</source></blockquote>
 
You can see it's really easy. Loading works very very similar but we use ObjectInputStream instead of ObjectOutputStream ,FileInputStream instead of FileOutputStream,readObject() instead of writeObject() and we return the HashMap.
 
<blockquote><source lang="java">public HashMap<String, Integer> load(String path) {
 
try {
 
ObjectInputStream ois = new ObjectInputStream(new FileInputStream(path));
 
Object result = ois.readObject();
 
//you can feel free to cast result to HashMap<String, Integer> if you know there's that HashMap in the file
 
return (HashMap<String, Integer>)result;
 
} catch(Exception e) {
 
e.printStackTrace();
 
}
 
}
 
 
 
// ...
 
 
 
HashMap<Integer, String> loadedMap;
 
 
 
String path = getDataFolder() + File.separator + "example.bin";
 
File file = new File(path);
 
 
 
if (file.exists()) { // check if file exists before loading to avoid errors!
 
loadedMap  = load(path);
 
}</source></blockquote>
 
You can use this "API" for saving/loading HashMaps, ArrayLists, and all Objects which implement Serializable or Externalizable interface.
 
<blockquote><source lang="java">/** SLAPI = Saving/Loading API
 
* API for Saving and Loading Objects.
 
* Everyone has permission to include this code in their plugins as they wish :)
 
* @author Tomsik68<tomsik68@gmail.com>
 
*/
 
public class SLAPI
 
{
 
public static <T extends Object> void save(T obj,String path) throws Exception
 
{
 
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(path));
 
oos.writeObject(obj);
 
oos.flush();
 
oos.close();
 
}
 
public static <T extends Object> T load(String path) throws Exception
 
{
 
ObjectInputStream ois = new ObjectInputStream(new FileInputStream(path));
 
T result = (T)ois.readObject();
 
ois.close();
 
return result;
 
}
 
}</source> </blockquote>
 
Example implementation of this API: '''I'm skipping some part of code in this source'''
 
<blockquote><source lang="java">public class Example extends JavaPlugin {
 
private ArrayList<Object> list = new ArrayList<>();
 
 
 
@Override
 
public void onEnable() {
 
            try {
 
list = SLAPI.load("example.bin");
 
            } catch(Exception e) {
 
                //handle the exception
 
                e.printStackTrace();
 
            }
 
}
 
 
 
@Override
 
public void onDisable() {
 
            try {
 
SLAPI.save(list,"example.bin");
 
            } catch(Exception e) {
 
                e.printStackTrace();
 
            }
 
}
 
}</source> </blockquote>
 
Note #1: This will work un-modified with almost all well-known Java types like Integer, String, HashMap. It will also work for some Bukkit types as well. If you're writing your own data object classes, and you may want to save their state using this technique, you should read about Java's Serializable or Externalizable interface. The only difference between Externalizable and Serializable is, that Serializable automatically takes all of class's fields and tries to serialize them, while Externalizable allows you to define method for reading and writing the Object. It's easy to add to your code, and it will make your data persistent with very little work required. No more parsing!
 
 
 
Note #2: This API doesn't support changes. Once you change something in the class, data files saved with older version of your plugin won't load correctly.
 
 
 
===== Tips & Examples =====
 
 
 
1.) Simplify your save structure
 
 
 
Try to use as much simple types as possible. E.g. if you want to save player, save their UUID instead. If you want to save world, save its UUID. If you want to save location, save x,y,z world UUID. DO NOT DIRECTLY SAVE BUKKIT TYPES!
 
 
 
2.) Save version number along with data
 
 
 
You should always remember, that you don't know what you'll be saving in the same file tomorrow. Will you ever migrate this file because of newer version of your plugin, bukkit, or minecraft? You don't know!
 
 
 
3.) Migrate older files
 
 
 
If your plugin finds older version of some file, it should update the file accordingly and change version number.
 
 
 
== Metadata  ==
 
 
 
Bukkit is trying to make plugin development as easy as possible, so HashMaps with key of type Player, Entity, World or even a Block were replaced by Metadata. Metadata is some kind of alternative to HashMap. It allows you to add custom "fields" to Players, Entities, Worlds and Blocks. These things are all members of Metadatable class(check [http://jd.bukkit.org/doxygen/de/d59/interfaceorg_1_1bukkit_1_1metadata_1_1MetadataValue.html#ab49975fe013a0626dd29d3b85c63a82f])It works very simply. Everything that is Metadatable holds its own HashMap of Metadata which you have access to. That means, for example, if you're creating an economy plugin, you would need a HashMap of Player and Float or Double. With Metadata, you don't have to! You just attach to player new metadata value, and that's it!<br>
 
 
 
=== Why to use Metadata  ===
 
 
 
* Metadata is all handled by Bukkit, which makes it a very good alternative to HashMaps.
 
* Metadata can be used to share information between plugins.
 
 
 
=== Why not use Metadata<br>  ===
 
 
 
* Slightly more difficult to get the value.
 
* It is not saved on shutdown (but then again, neither are any Maps that you create).
 
 
 
=== Getting &amp; Setting Metadata  ===
 
 
 
<source lang="java">
 
public void setMetadata(Metadatable object, String key, Object value, Plugin plugin) {
 
  object.setMetadata(key, new FixedMetadataValue(plugin,value));
 
}
 
 
 
public Object getMetadata(Metadatable object, String key, Plugin plugin) {
 
  List<MetadataValue> values = object.getMetadata(key); 
 
  for (MetadataValue value : values) {
 
    // Plugins are singleton objects, so using == is safe here
 
    if (value.getOwningPlugin() == plugin) {
 
        return value.value();
 
    }
 
  }
 
  return null;
 
}</source>
 
 
 
Note: If you're manipulating with numbers, booleans or strings, use convenient method to get the result. For example, you can use asInt(), asString() or asBoolean() instead of value to find out the value.
 
 
 
== Databases  ==
 
 
 
Sometimes flat files aren't enough for what your looking to do, this is where databases come in. The most common database engines available on Linux/Mac/Windows machines typically run on some flavor of SQL (Structured Query Language).
 
 
 
Software offering SQL allow you to create databases with columns and header to identify to contents of each cell. Think of it as a spreadsheet on steroids, where every column you set up in your database can enforce rules to ensure integrity. Apart from being more organised than a simple custom data file, SQL provides faster access and better searching than flat files.
 
 
 
The SQL standard helps applications like Bukkit implement database storage for their data in a consistent way. Unfortunately, there's more than one SQL-ready database engine, and each has minor differences in how to configure and use it. Which one you choose may depend on your particular needs. (Some plugins even offer configurable options to connect to multiple database engines!)
 
 
 
=== SQLite  ===
 
 
 
Alta189 has written a [http://forums.bukkit.org/threads/lib-tut-mysql-sqlite-bukkit-drivers.33849/ fantastic SQLite tutorial] which I suggest you watch if you're interested in using SQL in your plugins, included with the tutorials is a handy library you can download and import to make using SQL easier. Once you have watched these video tutorials I would suggest you go and learn some SQL syntax, it's very straightforward and shouldn't take you long to pick up. SQL Tutorials [http://www.w3schools.com/sql/default.asp @W3Schools] and [http://www.1keydata.com/sql/sql.html @1Keydata].
 
 
 
SQLite is great for very simple databases, because there's no server concerns to set up. Just make a few calls to create a new database and table. It's easy to back up: just copy the whole database file in one go. SQLite is a little bit weaker at data integrity, flexibility in data types, and it may not be something you would want to trust for huge databases of millions of rows. But for a new plugin in development, it's often easiest and fastest to get the SQL basics squared away with SQLite, even if you "graduate" to a more server-class database engine later.
 
 
 
=== MySQL  ===
 
 
 
Another popular SQL database engine is called MySQL. It is closer to server-grade than SQLite, where many popular companies or websites depend on it for millions of webpage hits every day. With that security comes a little bit steeper learning-curve, because MySQL has more tunable parameters and capabilities.
 
 
 
The coding for plugins accessing MySQL is mostly the same as tiny SQLite or mega-sized Oracle, with only small differences in syntax here or there. But the administration has room to grow. You may want to set up accounts and privileges inside your MySQL setup. You may want to set up SQL scripts that organize your backups and rollback to previous states.
 
 
 
== Deploying your Plugin  ==
 
 
 
Once you have written your plugin, how do you get it from a collection of source files into a working jar file that can be installed on a server? First, set up a CraftBukkit server on your local machine. To do this, visit the wiki page on [http://wiki.bukkit.org/Setting_up_a_server Setting up a server]. Next you have to export your plugin to a .jar so that you can run it on your new server. To do this in Eclipse, right-click the project and click ''Run as > Maven install'':
 
 
 
[[Image:Maveninstall.png]]
 
 
 
In the future, when you make code changes to your plugin, you want to delete the previous JAR by right-clicking the project and clicking ''Run as > Maven clean'' before doing the above. If you're having issues when building your plugin, check if your Java Development Kit (JDK) is properly installed and review [[Setting Up Your Workspace]]. You may need to configure your JDK manually if you see a JDK-related error in the console, as Eclipse may not have detected it correctly. Go to ''Window -> Preferences'', and go to ''Java -> Installed JREs''. Add the latest JDK you've installed as a JRE, tick that one, and untick the active one that was giving you issues:
 
 
 
[[Image:Jrelocation.png]]
 
 
 
If your project built successfully, the JAR file is now under the ''target'' folder in your project's folder under your Eclipse workspace. The JAR file you have exported should now be a working plugin!&nbsp;Assuming of course that there are no errors in your code or your plugin.yml file. You can now drop the jar file you have exported into your Bukkit server's "plugins"&nbsp;folder, reload or relaunch the server, and test away! In order to connect to a server running locally on your computer, simply put "localhost"&nbsp;as the IP&nbsp;address of the server in Minecraft multiplayer. If you run into errors that you can't solve for yourself, try visiting the [http://forums.bukkit.org/forums/plugin-development.5/ plugin development forum], asking in the [http://wiki.bukkit.org/IRC bukkitdev IRC channel], or re-reading this wiki. Once you have a useful working plugin, consider submitting your project to [http://dev.bukkit.org/ dev.bukkit] for consumption by the Bukkit community. From the wizard above, you can see that the JAR file will be by default a compressed archive (JARs are based on the ZIP archive format). As such, it does not make sense to put your JAR into a ZIP archive when uploading to BukkitDev and will only increase the file size. Further, config files can be placed within the JAR and copied into the plugin's data folder if the configuration file does not exist. There is usually no good reason for packaging JAR files into another archive.
 
 
 
== Importing other plugins ==
 
You may wish to edit another plugin that has the source available. If that plugin has a ''pom.xml'' in its folder (most of the popular ones, for example WorldEdit and Essentials, do), you can import it as a project by selecting ''File -> Import'', and then opening the ''Maven'' folder and selecting ''Existing Maven Projects'':
 
 
 
[[Image:Importmaven.png]]
 
 
 
Then select the folder that the ''pom.xml'' is in, and the project should be on your sidebar. Edit it and compile it like you usually would.
 
 
 
== Tips and Tricks  ==
 
 
 
=== Setting a Player on Fire ===
 
 
 
The Bukkit API is capable of a lot of cool stuff. Here are some code snippets for some nice effects!
 
 
 
The following code allows a player to set another player on fire. Running a command like '''/ignite Notch''' would cause Notch to be set on fire!
 
<source lang="java">
 
@Override
 
public boolean onCommand(CommandSender sender, Command cmd, String label, String[] args) {
 
    // Uses equalsIgnoreCase() over equals() to accept "ignite" and "IgNiTe."
 
    if (cmd.getName().equalsIgnoreCase("ignite")) {
 
        // Make sure that the player specified exactly one argument (the name of the player to ignite).
 
        if (args.length != 1) {
 
            // When onCommand() returns false, the help message associated with that command is displayed.
 
            return false;
 
        }
 
 
 
        // Make sure the sender is a player.
 
        if (!(sender instanceof Player)) {
 
            sender.sendMessage("Only players can set other players on fire.");
 
            sender.sendMessage("This is an arbitrary requirement for demonstration purposes only.");
 
            return true;
 
        }
 
 
 
        // Get the player who should be set on fire. Remember that indecies start with 0, not 1.
 
        Player target = Bukkit.getServer().getPlayer(args[0]);
 
 
 
        // Make sure the player is online.
 
        if (target == null) {
 
            sender.sendMessage(args[0] + " is not currently online.");
 
            return true;
 
        }
 
 
 
        // Sets the player on fire for 1,000 ticks (there are ~20 ticks in second, so 50 seconds total).
 
        target.setFireTicks(1000);
 
        return true;
 
    }
 
    return false;
 
}
 
</source>
 
 
 
=== Killing the player  ===
 
 
 
To keep with the theme, here's a way to kill the player.
 
 
 
Use this for your onCommand method:
 
<blockquote><source lang="java">
 
@Override
 
public boolean onCommand(CommandSender sender, Command cmd, String label, String[] args) {
 
    if (cmd.getName().equalsIgnoreCase("KillPlayer")) {
 
        Player target = sender.getServer().getPlayer(args[0]);
 
        // Make sure the player is online.
 
        if (target == null) {
 
            sender.sendMessage(args[0] + " is not currently online.");
 
            return true;
 
        }
 
        target.setHealth(0);
 
    }
 
    return false;
 
}</source></blockquote>
 
Here is an extension to that, that will kill the player with an explosion:
 
<blockquote><source lang="java">
 
float explosionPower = 4F; //This is the explosion power - TNT explosions are 4F by default
 
Player target = sender.getWorld().getPlayer(args[0]);
 
target.getWorld().createExplosion(target.getLocation(), explosionPower);
 
target.setHealth(0);
 
</source> </blockquote>
 
 
 
=== Creating a Fake Explosion  ===
 
 
 
This code produces the TNT/Creeper Visual and Audio effects. However, no explosion damage is dealt to surrounding entities or blocks. This is useful for nerfing explosions while still keeping the aesthetics of them.
 
<blockquote><source lang="java">
 
@EventHandler
 
public void onExplosionPrime(ExplosionPrimeEvent event) {
 
    Entity entity = event.getEntity();
 
 
 
    // If the event is about primed TNT (TNT that is about to explode), then do something
 
    if (entity instanceof TNTPrimed) {
 
        entity.getWorld().createExplosion(entity.getLocation(), 0);
 
    }
 
}
 
</source></blockquote>
 
 
 
=== Hiding a Player From Another Player  ===
 
 
 
This will hide the player who used this command from a specified player. Everyone else will be able to see the player.
 
 
 
<source lang="java">
 
@Override
 
public boolean onCommand(CommandSender sender, Command cmd, String label, String[] args) {
 
    if (cmd.getName().equalsIgnoreCase("HideMe") && args.length == 1) {
 
        if (!(sender instanceof Player)) {
 
            sender.sendMessage("Only players can use this command!");
 
            return true;
 
        }
 
        // After checking to make sure that the sender is a Player, we can safely case it to one.
 
        Player s = (Player) sender;
 
 
 
        // Gets the player who shouldn't see the sender.
 
        Player target = Bukkit.getServer().getPlayer(args[0]);
 
        if (target == null) {
 
            sender.sendMessage("Player " + args[0] + " is not online.");
 
            return true;
 
        }
 
        // Hides a given Player (s) from someone (target).
 
        target.hidePlayer(s);
 
        return true;
 
    }
 
    return false;
 
}</source>
 
 
 
=== Spawn Lightning Bolt Where Player is Looking ===
 
 
 
The code below allows any player with a fishing rod to create a lightning strike by clicking (and aiming somewhere). It's a simple and funny trick.
 
<source lang="java">
 
@EventHandler
 
public void onPlayerInteractBlock(PlayerInteractEvent event) {
 
    Player player = event.getPlayer();
 
    if (player.getItemInHand().getType() == Material.FISHING_ROD) {
 
        // Creates a bolt of lightning at a given location. In this case, that location is where the player is looking.
 
        // Can only create lightning up to 200 blocks away.
 
        player.getWorld().strikeLightning(player.getTargetBlock(null, 200).getLocation());
 
    }
 
}</source>
 
 
 
=== Automatically formatting your code ===
 
 
 
Eclipse provides functionality to automatically format your code to Oracle conventions, fixing unconventional indentations, spacing, and such. Simply select your project in the sidebar, and then select ''Source -> Format''.
 
 
 
== Request Section  ==
 
 
 
http://forums.bukkit.org/forums/plugin-requests.13/
 
 
 
== Example Files and Templates  ==
 
 
 
*[https://github.com/Bukkit/SamplePlugin/ Bukkit/SamplePlugin on GitHub]
 
 
 
*[http://pastebin.com/wpeTPx7N Example.Java]
 
 
 
*[http://pastebin.com/MTwaAVCT ExamplePlayerListener.Java]
 
 
 
*[http://pastebin.com/6FLixfH3 ExampleBlockListener.Java]
 
 
 
*[http://pastebin.com/8UZ6pkWC ExampleEntityListener.Java]
 
 
 
 
 
If you have any more questions on this matter, don't hesitate to visit the BukkitDev [[IRC]] channel and ask!
 
 
 
  
[[Category:教程]]
+
<big>[http://docs.oracle.com/javase/tutorial/ Oracle's Java Tutorials]</big> - 官方教程
 +
*[http://www.java2s.com/Tutorial/Java/CatalogJava.htm Java2s.com] - 教程
 +
*[http://www.javacoffeebreak.com/java101/java101.html Java 101] - 教程
 +
*[http://math.hws.edu/javanotes/ JavaNotes] - 免费的在线教科书
 +
*[http://thenewboston.org/list.php?cat=31 TheNewBoston] - 视频教程.

2014年11月2日 (日) 18:21的版本

学习Java

理解这些教程需要具备Java这门编程语言的基本知识.如果你刚开始学习Java或者需要重温一下相关知识,下面有一个并不完整的网站列表可供参考.

Oracle's Java Tutorials - 官方教程